From e387b5513f85f26c291365482d51ead21ff1c8af Mon Sep 17 00:00:00 2001
From: Eike Kettner <eike.kettner@posteo.de>
Date: Sat, 11 Jul 2020 22:16:09 +0200
Subject: [PATCH] Remove items in non-member folders from sql search results

---
 .../joex/notify/NotifyDueItemsTask.scala      |  2 +-
 .../docspell/store/queries/QFolder.scala      | 26 +++++++++++++++++++
 .../scala/docspell/store/queries/QItem.scala  | 23 ++++++++++------
 3 files changed, 42 insertions(+), 9 deletions(-)

diff --git a/modules/joex/src/main/scala/docspell/joex/notify/NotifyDueItemsTask.scala b/modules/joex/src/main/scala/docspell/joex/notify/NotifyDueItemsTask.scala
index 218b4d0d..1eb24a75 100644
--- a/modules/joex/src/main/scala/docspell/joex/notify/NotifyDueItemsTask.scala
+++ b/modules/joex/src/main/scala/docspell/joex/notify/NotifyDueItemsTask.scala
@@ -71,7 +71,7 @@ object NotifyDueItemsTask {
       now <- Timestamp.current[F]
       q =
         QItem.Query
-          .empty(ctx.args.account.collective)
+          .empty(ctx.args.account)
           .copy(
             states = ItemState.validStates.toList,
             tagsInclude = ctx.args.tagsInclude,
diff --git a/modules/store/src/main/scala/docspell/store/queries/QFolder.scala b/modules/store/src/main/scala/docspell/store/queries/QFolder.scala
index 1495d1b0..8f8b50a8 100644
--- a/modules/store/src/main/scala/docspell/store/queries/QFolder.scala
+++ b/modules/store/src/main/scala/docspell/store/queries/QFolder.scala
@@ -244,6 +244,32 @@ object QFolder {
       .to[Vector]
   }
 
+  /** Select all folder_id where the given account is member or owner. */
+  def findMemberFolderIds(account: AccountId): Fragment = {
+    val fId     = RFolder.Columns.id.prefix("f")
+    val fOwner  = RFolder.Columns.owner.prefix("f")
+    val fColl   = RFolder.Columns.collective.prefix("f")
+    val uId     = RUser.Columns.uid.prefix("u")
+    val uLogin  = RUser.Columns.login.prefix("u")
+    val mFolder = RFolderMember.Columns.folder.prefix("m")
+    val mUser   = RFolderMember.Columns.user.prefix("m")
+
+    selectSimple(
+      Seq(fId),
+      RFolder.table ++ fr"f INNER JOIN" ++ RUser.table ++ fr"u ON" ++ fOwner.is(uId),
+      and(fColl.is(account.collective), uLogin.is(account.user))
+    ) ++
+      fr"UNION ALL" ++
+      selectSimple(
+        Seq(mFolder),
+        RFolderMember.table ++ fr"m INNER JOIN" ++ RFolder.table ++ fr"f ON" ++ fId.is(
+          mFolder
+        ) ++
+          fr"INNER JOIN" ++ RUser.table ++ fr"u ON" ++ uId.is(mUser),
+        and(fColl.is(account.collective), uLogin.is(account.user))
+      )
+  }
+
   private def findUserId(account: AccountId): ConnectionIO[Option[Ident]] =
     RUser.findByAccount(account).map(_.map(_.uid))
 }
diff --git a/modules/store/src/main/scala/docspell/store/queries/QItem.scala b/modules/store/src/main/scala/docspell/store/queries/QItem.scala
index e4618772..99415125 100644
--- a/modules/store/src/main/scala/docspell/store/queries/QItem.scala
+++ b/modules/store/src/main/scala/docspell/store/queries/QItem.scala
@@ -273,17 +273,20 @@ object QItem {
       ) ++ moreCols
     )
 
-    val withItem   = selectSimple(itemCols, RItem.table, IC.cid.is(q.account.collective))
-    val withPerson = selectSimple(personCols, RPerson.table, PC.cid.is(q.account.collective))
-    val withOrgs   = selectSimple(orgCols, ROrganization.table, OC.cid.is(q.account.collective))
-    val withEquips = selectSimple(equipCols, REquipment.table, EC.cid.is(q.account.collective))
+    val withItem = selectSimple(itemCols, RItem.table, IC.cid.is(q.account.collective))
+    val withPerson =
+      selectSimple(personCols, RPerson.table, PC.cid.is(q.account.collective))
+    val withOrgs =
+      selectSimple(orgCols, ROrganization.table, OC.cid.is(q.account.collective))
+    val withEquips =
+      selectSimple(equipCols, REquipment.table, EC.cid.is(q.account.collective))
     val withFolder =
       selectSimple(folderCols, RFolder.table, FC.collective.is(q.account.collective))
     val withAttach = fr"SELECT COUNT(" ++ AC.id.f ++ fr") as num, " ++ AC.itemId.f ++
       fr"from" ++ RAttachment.table ++ fr"GROUP BY (" ++ AC.itemId.f ++ fr")"
 
     val selectKW = if (distinct) fr"SELECT DISTINCT" else fr"SELECT"
-    val query = withCTE(
+    withCTE(
       (Seq(
         "items"   -> withItem,
         "persons" -> withPerson,
@@ -302,7 +305,6 @@ object QItem {
       .prefix("i")
       .is(EC.eid.prefix("e1")) ++
       fr"LEFT JOIN folders f1 ON" ++ IC.folder.prefix("i").is(FC.id.prefix("f1"))
-    query
   }
 
   def findItems(q: Query, batch: Batch): Stream[ConnectionIO, ListItem] = {
@@ -334,6 +336,7 @@ object QItem {
           RTagItem.Columns.tagId.isOneOf(q.tagsExclude)
         )
 
+    val iFolder  = IC.folder.prefix("i")
     val name     = q.name.map(_.toLowerCase).map(queryWildcard)
     val allNames = q.allNames.map(_.toLowerCase).map(queryWildcard)
     val cond = and(
@@ -385,7 +388,8 @@ object QItem {
             .map(nel => IC.id.prefix("i").isIn(nel))
             .getOrElse(IC.id.prefix("i").is(""))
         )
-        .getOrElse(Fragment.empty)
+        .getOrElse(Fragment.empty),
+      or(iFolder.isNull, iFolder.isIn(QFolder.findMemberFolderIds(q.account)))
     )
 
     val order = q.orderAsc match {
@@ -476,7 +480,10 @@ object QItem {
       n  <- store.transact(RItem.deleteByIdAndCollective(itemId, collective))
     } yield tn + rn + n + mn
 
-  private def findByFileIdsQuery(fileMetaIds: NonEmptyList[Ident], limit: Option[Int]): Fragment = {
+  private def findByFileIdsQuery(
+      fileMetaIds: NonEmptyList[Ident],
+      limit: Option[Int]
+  ): Fragment = {
     val IC      = RItem.Columns.all.map(_.prefix("i"))
     val aItem   = RAttachment.Columns.itemId.prefix("a")
     val aId     = RAttachment.Columns.id.prefix("a")