From 2ab0b5e222c658f8ef4731a7785358f1bd1ef5ea Mon Sep 17 00:00:00 2001 From: Eike Kettner Date: Sat, 11 Jul 2020 11:38:57 +0200 Subject: [PATCH] Rename space -> folder --- .../scala/docspell/backend/BackendApp.scala | 6 +- .../scala/docspell/backend/ops/OFolder.scala | 110 ++++++++ .../scala/docspell/backend/ops/OSpace.scala | 110 -------- .../src/main/resources/docspell-openapi.yml | 84 +++--- .../docspell/restserver/RestServer.scala | 2 +- .../restserver/routes/FolderRoutes.scala | 113 ++++++++ .../restserver/routes/SpaceRoutes.scala | 112 -------- ...V1.8.0__spaces.sql => V1.8.0__folders.sql} | 12 +- .../docspell/store/queries/QFolder.scala | 249 ++++++++++++++++++ .../scala/docspell/store/queries/QSpace.scala | 249 ------------------ .../records/{RSpace.scala => RFolder.scala} | 32 +-- .../store/records/RFolderMember.scala | 61 +++++ .../scala/docspell/store/records/RItem.scala | 12 +- .../docspell/store/records/RSpaceMember.scala | 61 ----- modules/webapp/src/main/elm/Api.elm | 60 ++--- .../{SpaceDetail.elm => FolderDetail.elm} | 82 +++--- .../{SpaceManage.elm => FolderManage.elm} | 78 +++--- .../Comp/{SpaceTable.elm => FolderTable.elm} | 12 +- modules/webapp/src/main/elm/Data/Icons.elm | 14 +- .../src/main/elm/Page/ManageData/Data.elm | 10 +- .../src/main/elm/Page/ManageData/Update.elm | 16 +- .../src/main/elm/Page/ManageData/View.elm | 24 +- 22 files changed, 755 insertions(+), 754 deletions(-) create mode 100644 modules/backend/src/main/scala/docspell/backend/ops/OFolder.scala delete mode 100644 modules/backend/src/main/scala/docspell/backend/ops/OSpace.scala create mode 100644 modules/restserver/src/main/scala/docspell/restserver/routes/FolderRoutes.scala delete mode 100644 modules/restserver/src/main/scala/docspell/restserver/routes/SpaceRoutes.scala rename modules/store/src/main/resources/db/migration/postgresql/{V1.8.0__spaces.sql => V1.8.0__folders.sql} (68%) create mode 100644 modules/store/src/main/scala/docspell/store/queries/QFolder.scala delete mode 100644 modules/store/src/main/scala/docspell/store/queries/QSpace.scala rename modules/store/src/main/scala/docspell/store/records/{RSpace.scala => RFolder.scala} (64%) create mode 100644 modules/store/src/main/scala/docspell/store/records/RFolderMember.scala delete mode 100644 modules/store/src/main/scala/docspell/store/records/RSpaceMember.scala rename modules/webapp/src/main/elm/Comp/{SpaceDetail.elm => FolderDetail.elm} (80%) rename modules/webapp/src/main/elm/Comp/{SpaceManage.elm => FolderManage.elm} (68%) rename modules/webapp/src/main/elm/Comp/{SpaceTable.elm => FolderTable.elm} (88%) diff --git a/modules/backend/src/main/scala/docspell/backend/BackendApp.scala b/modules/backend/src/main/scala/docspell/backend/BackendApp.scala index d62dddd1..72ce0138 100644 --- a/modules/backend/src/main/scala/docspell/backend/BackendApp.scala +++ b/modules/backend/src/main/scala/docspell/backend/BackendApp.scala @@ -35,7 +35,7 @@ trait BackendApp[F[_]] { def mail: OMail[F] def joex: OJoex[F] def userTask: OUserTask[F] - def space: OSpace[F] + def folder: OFolder[F] } object BackendApp { @@ -68,7 +68,7 @@ object BackendApp { JavaMailEmil(blocker, Settings.defaultSettings.copy(debug = cfg.mailDebug)) mailImpl <- OMail(store, javaEmil) userTaskImpl <- OUserTask(utStore, queue, joexImpl) - spaceImpl <- OSpace(store) + folderImpl <- OFolder(store) } yield new BackendApp[F] { val login: Login[F] = loginImpl val signup: OSignup[F] = signupImpl @@ -86,7 +86,7 @@ object BackendApp { val mail = mailImpl val joex = joexImpl val userTask = userTaskImpl - val space = spaceImpl + val folder = folderImpl } def apply[F[_]: ConcurrentEffect: ContextShift]( diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OFolder.scala b/modules/backend/src/main/scala/docspell/backend/ops/OFolder.scala new file mode 100644 index 00000000..e93b7d5d --- /dev/null +++ b/modules/backend/src/main/scala/docspell/backend/ops/OFolder.scala @@ -0,0 +1,110 @@ +package docspell.backend.ops + +import cats.effect._ + +import docspell.common._ +import docspell.store.{AddResult, Store} +import docspell.store.records.{RFolder, RUser} +import docspell.store.queries.QFolder + +trait OFolder[F[_]] { + + def findAll( + account: AccountId, + ownerLogin: Option[Ident], + nameQuery: Option[String] + ): F[Vector[OFolder.FolderItem]] + + def findById(id: Ident, account: AccountId): F[Option[OFolder.FolderDetail]] + + /** Adds a new folder. If `login` is non-empty, the `folder.user` + * property is ignored and the user-id is determined by the given + * login name. + */ + def add(folder: RFolder, login: Option[Ident]): F[AddResult] + + def changeName( + folder: Ident, + account: AccountId, + name: String + ): F[OFolder.FolderChangeResult] + + def addMember( + folder: Ident, + account: AccountId, + member: Ident + ): F[OFolder.FolderChangeResult] + + def removeMember( + folder: Ident, + account: AccountId, + member: Ident + ): F[OFolder.FolderChangeResult] + + def delete(id: Ident, account: AccountId): F[OFolder.FolderChangeResult] +} + +object OFolder { + + type FolderChangeResult = QFolder.FolderChangeResult + val FolderChangeResult = QFolder.FolderChangeResult + + type FolderItem = QFolder.FolderItem + val FolderItem = QFolder.FolderItem + + type FolderDetail = QFolder.FolderDetail + val FolderDetail = QFolder.FolderDetail + + def apply[F[_]: Effect](store: Store[F]): Resource[F, OFolder[F]] = + Resource.pure[F, OFolder[F]](new OFolder[F] { + def findAll( + account: AccountId, + ownerLogin: Option[Ident], + nameQuery: Option[String] + ): F[Vector[FolderItem]] = + store.transact(QFolder.findAll(account, None, ownerLogin, nameQuery)) + + def findById(id: Ident, account: AccountId): F[Option[FolderDetail]] = + store.transact(QFolder.findById(id, account)) + + def add(folder: RFolder, login: Option[Ident]): F[AddResult] = { + val insert = login match { + case Some(n) => + for { + user <- RUser.findByAccount(AccountId(folder.collectiveId, n)) + s = user.map(u => folder.copy(owner = u.uid)).getOrElse(folder) + n <- RFolder.insert(s) + } yield n + + case None => + RFolder.insert(folder) + } + val exists = RFolder.existsByName(folder.collectiveId, folder.name) + store.add(insert, exists) + } + + def changeName( + folder: Ident, + account: AccountId, + name: String + ): F[FolderChangeResult] = + store.transact(QFolder.changeName(folder, account, name)) + + def addMember( + folder: Ident, + account: AccountId, + member: Ident + ): F[FolderChangeResult] = + store.transact(QFolder.addMember(folder, account, member)) + + def removeMember( + folder: Ident, + account: AccountId, + member: Ident + ): F[FolderChangeResult] = + store.transact(QFolder.removeMember(folder, account, member)) + + def delete(id: Ident, account: AccountId): F[FolderChangeResult] = + store.transact(QFolder.delete(id, account)) + }) +} diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OSpace.scala b/modules/backend/src/main/scala/docspell/backend/ops/OSpace.scala deleted file mode 100644 index 25ec9e20..00000000 --- a/modules/backend/src/main/scala/docspell/backend/ops/OSpace.scala +++ /dev/null @@ -1,110 +0,0 @@ -package docspell.backend.ops - -import cats.effect._ - -import docspell.common._ -import docspell.store.{AddResult, Store} -import docspell.store.records.{RSpace, RUser} -import docspell.store.queries.QSpace - -trait OSpace[F[_]] { - - def findAll( - account: AccountId, - ownerLogin: Option[Ident], - nameQuery: Option[String] - ): F[Vector[OSpace.SpaceItem]] - - def findById(id: Ident, account: AccountId): F[Option[OSpace.SpaceDetail]] - - /** Adds a new space. If `login` is non-empty, the `space.user` - * property is ignored and the user-id is determined by the given - * login name. - */ - def add(space: RSpace, login: Option[Ident]): F[AddResult] - - def changeName( - space: Ident, - account: AccountId, - name: String - ): F[OSpace.SpaceChangeResult] - - def addMember( - space: Ident, - account: AccountId, - member: Ident - ): F[OSpace.SpaceChangeResult] - - def removeMember( - space: Ident, - account: AccountId, - member: Ident - ): F[OSpace.SpaceChangeResult] - - def delete(id: Ident, account: AccountId): F[OSpace.SpaceChangeResult] -} - -object OSpace { - - type SpaceChangeResult = QSpace.SpaceChangeResult - val SpaceChangeResult = QSpace.SpaceChangeResult - - type SpaceItem = QSpace.SpaceItem - val SpaceItem = QSpace.SpaceItem - - type SpaceDetail = QSpace.SpaceDetail - val SpaceDetail = QSpace.SpaceDetail - - def apply[F[_]: Effect](store: Store[F]): Resource[F, OSpace[F]] = - Resource.pure[F, OSpace[F]](new OSpace[F] { - def findAll( - account: AccountId, - ownerLogin: Option[Ident], - nameQuery: Option[String] - ): F[Vector[SpaceItem]] = - store.transact(QSpace.findAll(account, None, ownerLogin, nameQuery)) - - def findById(id: Ident, account: AccountId): F[Option[SpaceDetail]] = - store.transact(QSpace.findById(id, account)) - - def add(space: RSpace, login: Option[Ident]): F[AddResult] = { - val insert = login match { - case Some(n) => - for { - user <- RUser.findByAccount(AccountId(space.collectiveId, n)) - s = user.map(u => space.copy(owner = u.uid)).getOrElse(space) - n <- RSpace.insert(s) - } yield n - - case None => - RSpace.insert(space) - } - val exists = RSpace.existsByName(space.collectiveId, space.name) - store.add(insert, exists) - } - - def changeName( - space: Ident, - account: AccountId, - name: String - ): F[SpaceChangeResult] = - store.transact(QSpace.changeName(space, account, name)) - - def addMember( - space: Ident, - account: AccountId, - member: Ident - ): F[SpaceChangeResult] = - store.transact(QSpace.addMember(space, account, member)) - - def removeMember( - space: Ident, - account: AccountId, - member: Ident - ): F[SpaceChangeResult] = - store.transact(QSpace.removeMember(space, account, member)) - - def delete(id: Ident, account: AccountId): F[SpaceChangeResult] = - store.transact(QSpace.delete(id, account)) - }) -} diff --git a/modules/restapi/src/main/resources/docspell-openapi.yml b/modules/restapi/src/main/resources/docspell-openapi.yml index 75ed64ad..bcdeef5c 100644 --- a/modules/restapi/src/main/resources/docspell-openapi.yml +++ b/modules/restapi/src/main/resources/docspell-openapi.yml @@ -795,14 +795,14 @@ paths: application/json: schema: $ref: "#/components/schemas/BasicResult" - /sec/space: + /sec/folder: get: - tags: [ Space ] - summary: Get a list of spaces. + tags: [ Folder ] + summary: Get a list of folders. description: | - Return a list of spaces for the current collective. + Return a list of folders for the current collective. - All spaces are returned, including those not owned by the + All folders are returned, including those not owned by the current user. It is possible to restrict the results by a substring match of @@ -818,12 +818,12 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/SpaceList" + $ref: "#/components/schemas/FolderList" post: - tags: [ Space ] - summary: Create a new space + tags: [ Folder ] + summary: Create a new folder description: | - Create a new space owned by the current user. If a space with + Create a new folder owned by the current user. If a folder with the same name already exists, an error is thrown. security: - authTokenHeader: [] @@ -831,7 +831,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/NewSpace" + $ref: "#/components/schemas/NewFolder" responses: 200: description: Ok @@ -839,12 +839,12 @@ paths: application/json: schema: $ref: "#/components/schemas/IdResult" - /sec/space/{id}: + /sec/folder/{id}: get: - tags: [ Space ] - summary: Get space details. + tags: [ Folder ] + summary: Get folder details. description: | - Return details about a space. + Return details about a folder. security: - authTokenHeader: [] parameters: @@ -855,12 +855,12 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/SpaceDetail" + $ref: "#/components/schemas/FolderDetail" put: - tags: [ Space ] - summary: Change the name of a space + tags: [ Folder ] + summary: Change the name of a folder description: | - Changes the name of a space. The new name must not exists. + Changes the name of a folder. The new name must not exists. security: - authTokenHeader: [] parameters: @@ -869,7 +869,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/NewSpace" + $ref: "#/components/schemas/NewFolder" responses: 200: description: Ok @@ -878,10 +878,10 @@ paths: schema: $ref: "#/components/schemas/BasicResult" delete: - tags: [ Space ] - summary: Delete a space by its id. + tags: [ Folder ] + summary: Delete a folder by its id. description: | - Deletes a space. + Deletes a folder. security: - authTokenHeader: [] parameters: @@ -893,12 +893,12 @@ paths: application/json: schema: $ref: "#/components/schemas/BasicResult" - /sec/space/{id}/member/{userId}: + /sec/folder/{id}/member/{userId}: put: - tags: [ Space ] - summary: Add a member to this space + tags: [ Folder ] + summary: Add a member to this folder description: | - Adds a member to this space (identified by `id`). + Adds a member to this folder (identified by `id`). security: - authTokenHeader: [] parameters: @@ -912,10 +912,10 @@ paths: schema: $ref: "#/components/schemas/BasicResult" delete: - tags: [ Space ] - summary: Removes a member from this space. + tags: [ Folder ] + summary: Removes a member from this folder. description: | - Removes a member from this space. + Removes a member from this folder. security: - authTokenHeader: [] parameters: @@ -984,7 +984,7 @@ paths: summary: Get some insights regarding your items. description: | Returns some information about how many items there are, how - much space they occupy etc. + much folder they occupy etc. security: - authTokenHeader: [] responses: @@ -2492,19 +2492,19 @@ paths: components: schemas: - SpaceList: + FolderList: description: | - A list of spaces with their member counts. + A list of folders with their member counts. required: - items properties: items: type: array items: - $ref: "#/components/schemas/SpaceItem" - SpaceItem: + $ref: "#/components/schemas/FolderItem" + FolderItem: description: | - An item in a space list. + An item in a folder list. required: - id - name @@ -2528,17 +2528,17 @@ components: memberCount: type: integer format: int32 - NewSpace: + NewFolder: description: | - Data required to create a new space. + Data required to create a new folder. required: - name properties: name: type: string - SpaceDetail: + FolderDetail: description: | - Details about a space. + Details about a folder. required: - id - name @@ -2567,9 +2567,9 @@ components: type: array items: $ref: "#/components/schemas/IdName" - SpaceMember: + FolderMember: description: | - Information to add or remove a space member. + Information to add or remove a folder member. required: - userId properties: @@ -4001,7 +4001,7 @@ components: owning: name: full in: query - description: Whether to get owning spaces + description: Whether to get owning folders required: false schema: type: boolean diff --git a/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala b/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala index d31becf3..501628ea 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala @@ -82,7 +82,7 @@ object RestServer { "usertask/scanmailbox" -> ScanMailboxRoutes(restApp.backend, token), "calevent/check" -> CalEventCheckRoutes(), "fts" -> FullTextIndexRoutes.secured(cfg, restApp.backend, token), - "space" -> SpaceRoutes(restApp.backend, token) + "folder" -> FolderRoutes(restApp.backend, token) ) def openRoutes[F[_]: Effect](cfg: Config, restApp: RestApp[F]): HttpRoutes[F] = diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/FolderRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/FolderRoutes.scala new file mode 100644 index 00000000..0a9305bc --- /dev/null +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/FolderRoutes.scala @@ -0,0 +1,113 @@ +package docspell.restserver.routes + +import cats.data.OptionT +import cats.effect._ +import cats.implicits._ + +import docspell.backend.BackendApp +import docspell.backend.auth.AuthToken +import docspell.backend.ops.OFolder +import docspell.common._ +import docspell.restapi.model._ +import docspell.restserver.conv.Conversions +import docspell.restserver.http4s._ +import docspell.store.records.RFolder + +import org.http4s.HttpRoutes +import org.http4s.circe.CirceEntityDecoder._ +import org.http4s.circe.CirceEntityEncoder._ +import org.http4s.dsl.Http4sDsl + +object FolderRoutes { + + def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = { + val dsl = new Http4sDsl[F] with ResponseGenerator[F] {} + import dsl._ + + HttpRoutes.of { + case GET -> Root :? QueryParam.QueryOpt(q) :? QueryParam.OwningOpt(owning) => + val login = + owning.filter(identity).map(_ => user.account.user) + for { + all <- backend.folder.findAll(user.account, login, q.map(_.q)) + resp <- Ok(FolderList(all.map(mkFolder).toList)) + } yield resp + + case req @ POST -> Root => + for { + data <- req.as[NewFolder] + nfolder <- newFolder(data, user.account) + res <- backend.folder.add(nfolder, Some(user.account.user)) + resp <- + Ok(Conversions.idResult(res, nfolder.id, "Folder successfully created.")) + } yield resp + + case GET -> Root / Ident(id) => + (for { + folder <- OptionT(backend.folder.findById(id, user.account)) + resp <- OptionT.liftF(Ok(mkFolderDetail(folder))) + } yield resp).getOrElseF(NotFound()) + + case req @ PUT -> Root / Ident(id) => + for { + data <- req.as[NewFolder] + res <- backend.folder.changeName(id, user.account, data.name) + resp <- Ok(mkFolderChangeResult(res)) + } yield resp + + case DELETE -> Root / Ident(id) => + for { + res <- backend.folder.delete(id, user.account) + resp <- Ok(mkFolderChangeResult(res)) + } yield resp + + case PUT -> Root / Ident(id) / "member" / Ident(userId) => + for { + res <- backend.folder.addMember(id, user.account, userId) + resp <- Ok(mkFolderChangeResult(res)) + } yield resp + + case DELETE -> Root / Ident(id) / "member" / Ident(userId) => + for { + res <- backend.folder.removeMember(id, user.account, userId) + resp <- Ok(mkFolderChangeResult(res)) + } yield resp + } + } + + private def newFolder[F[_]: Sync](ns: NewFolder, account: AccountId): F[RFolder] = + RFolder.newFolder(ns.name, account) + + private def mkFolder(item: OFolder.FolderItem): FolderItem = + FolderItem( + item.id, + item.name, + Conversions.mkIdName(item.owner), + item.created, + item.member, + item.memberCount + ) + + private def mkFolderDetail(item: OFolder.FolderDetail): FolderDetail = + FolderDetail( + item.id, + item.name, + Conversions.mkIdName(item.owner), + item.created, + item.member, + item.memberCount, + item.members.map(Conversions.mkIdName) + ) + + private def mkFolderChangeResult(r: OFolder.FolderChangeResult): BasicResult = + r match { + case OFolder.FolderChangeResult.Success => + BasicResult(true, "Successfully changed folder.") + case OFolder.FolderChangeResult.NotFound => + BasicResult(false, "Folder or user not found.") + case OFolder.FolderChangeResult.Forbidden => + BasicResult(false, "Not allowed to edit folder.") + case OFolder.FolderChangeResult.Exists => + BasicResult(false, "The member already exists.") + } +} diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/SpaceRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/SpaceRoutes.scala deleted file mode 100644 index 71c0a916..00000000 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/SpaceRoutes.scala +++ /dev/null @@ -1,112 +0,0 @@ -package docspell.restserver.routes - -import cats.data.OptionT -import cats.effect._ -import cats.implicits._ - -import docspell.backend.BackendApp -import docspell.backend.auth.AuthToken -import docspell.backend.ops.OSpace -import docspell.common._ -import docspell.restapi.model._ -import docspell.restserver.conv.Conversions -import docspell.restserver.http4s._ -import docspell.store.records.RSpace - -import org.http4s.HttpRoutes -import org.http4s.circe.CirceEntityDecoder._ -import org.http4s.circe.CirceEntityEncoder._ -import org.http4s.dsl.Http4sDsl - -object SpaceRoutes { - - def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = { - val dsl = new Http4sDsl[F] with ResponseGenerator[F] {} - import dsl._ - - HttpRoutes.of { - case GET -> Root :? QueryParam.QueryOpt(q) :? QueryParam.OwningOpt(owning) => - val login = - owning.filter(identity).map(_ => user.account.user) - for { - all <- backend.space.findAll(user.account, login, q.map(_.q)) - resp <- Ok(SpaceList(all.map(mkSpace).toList)) - } yield resp - - case req @ POST -> Root => - for { - data <- req.as[NewSpace] - nspace <- newSpace(data, user.account) - res <- backend.space.add(nspace, Some(user.account.user)) - resp <- Ok(Conversions.idResult(res, nspace.id, "Space successfully created.")) - } yield resp - - case GET -> Root / Ident(id) => - (for { - space <- OptionT(backend.space.findById(id, user.account)) - resp <- OptionT.liftF(Ok(mkSpaceDetail(space))) - } yield resp).getOrElseF(NotFound()) - - case req @ PUT -> Root / Ident(id) => - for { - data <- req.as[NewSpace] - res <- backend.space.changeName(id, user.account, data.name) - resp <- Ok(mkSpaceChangeResult(res)) - } yield resp - - case DELETE -> Root / Ident(id) => - for { - res <- backend.space.delete(id, user.account) - resp <- Ok(mkSpaceChangeResult(res)) - } yield resp - - case PUT -> Root / Ident(id) / "member" / Ident(userId) => - for { - res <- backend.space.addMember(id, user.account, userId) - resp <- Ok(mkSpaceChangeResult(res)) - } yield resp - - case DELETE -> Root / Ident(id) / "member" / Ident(userId) => - for { - res <- backend.space.removeMember(id, user.account, userId) - resp <- Ok(mkSpaceChangeResult(res)) - } yield resp - } - } - - private def newSpace[F[_]: Sync](ns: NewSpace, account: AccountId): F[RSpace] = - RSpace.newSpace(ns.name, account) - - private def mkSpace(item: OSpace.SpaceItem): SpaceItem = - SpaceItem( - item.id, - item.name, - Conversions.mkIdName(item.owner), - item.created, - item.member, - item.memberCount - ) - - private def mkSpaceDetail(item: OSpace.SpaceDetail): SpaceDetail = - SpaceDetail( - item.id, - item.name, - Conversions.mkIdName(item.owner), - item.created, - item.member, - item.memberCount, - item.members.map(Conversions.mkIdName) - ) - - private def mkSpaceChangeResult(r: OSpace.SpaceChangeResult): BasicResult = - r match { - case OSpace.SpaceChangeResult.Success => - BasicResult(true, "Successfully changed space.") - case OSpace.SpaceChangeResult.NotFound => - BasicResult(false, "Space or user not found.") - case OSpace.SpaceChangeResult.Forbidden => - BasicResult(false, "Not allowed to edit space.") - case OSpace.SpaceChangeResult.Exists => - BasicResult(false, "The member already exists.") - } -} diff --git a/modules/store/src/main/resources/db/migration/postgresql/V1.8.0__spaces.sql b/modules/store/src/main/resources/db/migration/postgresql/V1.8.0__folders.sql similarity index 68% rename from modules/store/src/main/resources/db/migration/postgresql/V1.8.0__spaces.sql rename to modules/store/src/main/resources/db/migration/postgresql/V1.8.0__folders.sql index 8a43c097..19fdd8a3 100644 --- a/modules/store/src/main/resources/db/migration/postgresql/V1.8.0__spaces.sql +++ b/modules/store/src/main/resources/db/migration/postgresql/V1.8.0__folders.sql @@ -1,4 +1,4 @@ -CREATE TABLE "space" ( +CREATE TABLE "folder" ( "id" varchar(254) not null primary key, "name" varchar(254) not null, "cid" varchar(254) not null, @@ -9,15 +9,15 @@ CREATE TABLE "space" ( foreign key ("owner") references "user_"("uid") ); -CREATE TABLE "space_member" ( +CREATE TABLE "folder_member" ( "id" varchar(254) not null primary key, - "space_id" varchar(254) not null, + "folder_id" varchar(254) not null, "user_id" varchar(254) not null, "created" timestamp not null, - unique ("space_id", "user_id"), - foreign key ("space_id") references "space"("id"), + unique ("folder_id", "user_id"), + foreign key ("folder_id") references "folder"("id"), foreign key ("user_id") references "user_"("uid") ); ALTER TABLE "item" -ADD COLUMN "space_id" varchar(254) NULL; +ADD COLUMN "folder_id" varchar(254) NULL; diff --git a/modules/store/src/main/scala/docspell/store/queries/QFolder.scala b/modules/store/src/main/scala/docspell/store/queries/QFolder.scala new file mode 100644 index 00000000..1495d1b0 --- /dev/null +++ b/modules/store/src/main/scala/docspell/store/queries/QFolder.scala @@ -0,0 +1,249 @@ +package docspell.store.queries + +import cats.data.OptionT +import cats.implicits._ + +import docspell.common._ +import docspell.store.impl.Implicits._ +import docspell.store.records._ + +import doobie._ +import doobie.implicits._ + +object QFolder { + + final case class FolderItem( + id: Ident, + name: String, + owner: IdRef, + created: Timestamp, + member: Boolean, + memberCount: Int + ) { + def withMembers(members: List[IdRef]): FolderDetail = + FolderDetail(id, name, owner, created, member, memberCount, members) + } + + final case class FolderDetail( + id: Ident, + name: String, + owner: IdRef, + created: Timestamp, + member: Boolean, + memberCount: Int, + members: List[IdRef] + ) + + sealed trait FolderChangeResult + object FolderChangeResult { + case object Success extends FolderChangeResult + def success: FolderChangeResult = Success + case object NotFound extends FolderChangeResult + def notFound: FolderChangeResult = NotFound + case object Forbidden extends FolderChangeResult + def forbidden: FolderChangeResult = Forbidden + case object Exists extends FolderChangeResult + def exists: FolderChangeResult = Exists + } + + def delete(id: Ident, account: AccountId): ConnectionIO[FolderChangeResult] = { + def tryDelete = + for { + _ <- RItem.removeFolder(id) + _ <- RFolderMember.deleteAll(id) + _ <- RFolder.delete(id) + } yield FolderChangeResult.success + + (for { + uid <- OptionT(findUserId(account)) + folder <- OptionT(RFolder.findById(id)) + res <- OptionT.liftF( + if (folder.owner == uid) tryDelete + else FolderChangeResult.forbidden.pure[ConnectionIO] + ) + } yield res).getOrElse(FolderChangeResult.notFound) + } + + def changeName( + folder: Ident, + account: AccountId, + name: String + ): ConnectionIO[FolderChangeResult] = { + def tryUpdate(ns: RFolder): ConnectionIO[FolderChangeResult] = + for { + n <- RFolder.update(ns) + res = + if (n == 0) FolderChangeResult.notFound + else FolderChangeResult.Success + } yield res + + (for { + uid <- OptionT(findUserId(account)) + folder <- OptionT(RFolder.findById(folder)) + res <- OptionT.liftF( + if (folder.owner == uid) tryUpdate(folder.copy(name = name)) + else FolderChangeResult.forbidden.pure[ConnectionIO] + ) + } yield res).getOrElse(FolderChangeResult.notFound) + } + + def removeMember( + folder: Ident, + account: AccountId, + member: Ident + ): ConnectionIO[FolderChangeResult] = { + def tryRemove: ConnectionIO[FolderChangeResult] = + for { + n <- RFolderMember.delete(member, folder) + res = + if (n == 0) FolderChangeResult.notFound + else FolderChangeResult.Success + } yield res + + (for { + uid <- OptionT(findUserId(account)) + folder <- OptionT(RFolder.findById(folder)) + res <- OptionT.liftF( + if (folder.owner == uid) tryRemove + else FolderChangeResult.forbidden.pure[ConnectionIO] + ) + } yield res).getOrElse(FolderChangeResult.notFound) + } + + def addMember( + folder: Ident, + account: AccountId, + member: Ident + ): ConnectionIO[FolderChangeResult] = { + def tryAdd: ConnectionIO[FolderChangeResult] = + for { + spm <- RFolderMember.findByUserId(member, folder) + mem <- RFolderMember.newMember[ConnectionIO](folder, member) + res <- + if (spm.isDefined) FolderChangeResult.exists.pure[ConnectionIO] + else RFolderMember.insert(mem).map(_ => FolderChangeResult.Success) + } yield res + + (for { + uid <- OptionT(findUserId(account)) + folder <- OptionT(RFolder.findById(folder)) + res <- OptionT.liftF( + if (folder.owner == uid) tryAdd + else FolderChangeResult.forbidden.pure[ConnectionIO] + ) + } yield res).getOrElse(FolderChangeResult.notFound) + } + + def findById(id: Ident, account: AccountId): ConnectionIO[Option[FolderDetail]] = { + val mUserId = RFolderMember.Columns.user.prefix("m") + val mFolderId = RFolderMember.Columns.folder.prefix("m") + val uId = RUser.Columns.uid.prefix("u") + val uLogin = RUser.Columns.login.prefix("u") + val sColl = RFolder.Columns.collective.prefix("s") + val sId = RFolder.Columns.id.prefix("s") + + val from = RFolderMember.table ++ fr"m INNER JOIN" ++ + RUser.table ++ fr"u ON" ++ mUserId.is(uId) ++ fr"INNER JOIN" ++ + RFolder.table ++ fr"s ON" ++ mFolderId.is(sId) + + val memberQ = selectSimple( + Seq(uId, uLogin), + from, + and(mFolderId.is(id), sColl.is(account.collective)) + ).query[IdRef].to[Vector] + + (for { + folder <- OptionT(findAll(account, Some(id), None, None).map(_.headOption)) + memb <- OptionT.liftF(memberQ) + } yield folder.withMembers(memb.toList)).value + } + + def findAll( + account: AccountId, + idQ: Option[Ident], + ownerLogin: Option[Ident], + nameQ: Option[String] + ): ConnectionIO[Vector[FolderItem]] = { +// with memberlogin as +// (select m.folder_id,u.login +// from folder_member m +// inner join user_ u on u.uid = m.user_id +// inner join folder s on s.id = m.folder_id +// where s.cid = 'eike' +// union all +// select s.id,u.login +// from folder s +// inner join user_ u on u.uid = s.owner +// where s.cid = 'eike') +// select s.id +// ,s.name +// ,s.owner +// ,u.login +// ,s.created +// ,(select count(*) > 0 from memberlogin where folder_id = s.id and login = 'eike') as member +// ,(select count(*) - 1 from memberlogin where folder_id = s.id) as member_count +// from folder s +// inner join user_ u on u.uid = s.owner +// where s.cid = 'eike'; + + val uId = RUser.Columns.uid.prefix("u") + val uLogin = RUser.Columns.login.prefix("u") + val sId = RFolder.Columns.id.prefix("s") + val sOwner = RFolder.Columns.owner.prefix("s") + val sName = RFolder.Columns.name.prefix("s") + val sColl = RFolder.Columns.collective.prefix("s") + val mUser = RFolderMember.Columns.user.prefix("m") + val mFolder = RFolderMember.Columns.folder.prefix("m") + + //CTE + val cte: Fragment = { + val from1 = RFolderMember.table ++ fr"m INNER JOIN" ++ + RUser.table ++ fr"u ON" ++ uId.is(mUser) ++ fr"INNER JOIN" ++ + RFolder.table ++ fr"s ON" ++ sId.is(mFolder) + + val from2 = RFolder.table ++ fr"s INNER JOIN" ++ + RUser.table ++ fr"u ON" ++ uId.is(sOwner) + + withCTE( + "memberlogin" -> + (selectSimple(Seq(mFolder, uLogin), from1, sColl.is(account.collective)) ++ + fr"UNION ALL" ++ + selectSimple(Seq(sId, uLogin), from2, sColl.is(account.collective))) + ) + } + + val isMember = + fr"SELECT COUNT(*) > 0 FROM memberlogin WHERE" ++ mFolder.prefix("").is(sId) ++ + fr"AND" ++ uLogin.prefix("").is(account.user) + + val memberCount = + fr"SELECT COUNT(*) - 1 FROM memberlogin WHERE" ++ mFolder.prefix("").is(sId) + + //Query + val cols = Seq( + sId.f, + sName.f, + sOwner.f, + uLogin.f, + RFolder.Columns.created.prefix("s").f, + fr"(" ++ isMember ++ fr") as mem", + fr"(" ++ memberCount ++ fr") as cnt" + ) + + val from = RFolder.table ++ fr"s INNER JOIN" ++ + RUser.table ++ fr"u ON" ++ uId.is(sOwner) + + val where = + sColl.is(account.collective) :: idQ.toList + .map(id => sId.is(id)) ::: nameQ.toList.map(q => + sName.lowerLike(s"%${q.toLowerCase}%") + ) ::: ownerLogin.toList.map(login => uLogin.is(login)) + + (cte ++ selectSimple(commas(cols), from, and(where) ++ orderBy(sName.asc))) + .query[FolderItem] + .to[Vector] + } + + 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/QSpace.scala b/modules/store/src/main/scala/docspell/store/queries/QSpace.scala deleted file mode 100644 index 8a840506..00000000 --- a/modules/store/src/main/scala/docspell/store/queries/QSpace.scala +++ /dev/null @@ -1,249 +0,0 @@ -package docspell.store.queries - -import cats.data.OptionT -import cats.implicits._ - -import docspell.common._ -import docspell.store.impl.Implicits._ -import docspell.store.records._ - -import doobie._ -import doobie.implicits._ - -object QSpace { - - final case class SpaceItem( - id: Ident, - name: String, - owner: IdRef, - created: Timestamp, - member: Boolean, - memberCount: Int - ) { - def withMembers(members: List[IdRef]): SpaceDetail = - SpaceDetail(id, name, owner, created, member, memberCount, members) - } - - final case class SpaceDetail( - id: Ident, - name: String, - owner: IdRef, - created: Timestamp, - member: Boolean, - memberCount: Int, - members: List[IdRef] - ) - - sealed trait SpaceChangeResult - object SpaceChangeResult { - case object Success extends SpaceChangeResult - def success: SpaceChangeResult = Success - case object NotFound extends SpaceChangeResult - def notFound: SpaceChangeResult = NotFound - case object Forbidden extends SpaceChangeResult - def forbidden: SpaceChangeResult = Forbidden - case object Exists extends SpaceChangeResult - def exists: SpaceChangeResult = Exists - } - - def delete(id: Ident, account: AccountId): ConnectionIO[SpaceChangeResult] = { - def tryDelete = - for { - _ <- RItem.removeSpace(id) - _ <- RSpaceMember.deleteAll(id) - _ <- RSpace.delete(id) - } yield SpaceChangeResult.success - - (for { - uid <- OptionT(findUserId(account)) - space <- OptionT(RSpace.findById(id)) - res <- OptionT.liftF( - if (space.owner == uid) tryDelete - else SpaceChangeResult.forbidden.pure[ConnectionIO] - ) - } yield res).getOrElse(SpaceChangeResult.notFound) - } - - def changeName( - space: Ident, - account: AccountId, - name: String - ): ConnectionIO[SpaceChangeResult] = { - def tryUpdate(ns: RSpace): ConnectionIO[SpaceChangeResult] = - for { - n <- RSpace.update(ns) - res = - if (n == 0) SpaceChangeResult.notFound - else SpaceChangeResult.Success - } yield res - - (for { - uid <- OptionT(findUserId(account)) - space <- OptionT(RSpace.findById(space)) - res <- OptionT.liftF( - if (space.owner == uid) tryUpdate(space.copy(name = name)) - else SpaceChangeResult.forbidden.pure[ConnectionIO] - ) - } yield res).getOrElse(SpaceChangeResult.notFound) - } - - def removeMember( - space: Ident, - account: AccountId, - member: Ident - ): ConnectionIO[SpaceChangeResult] = { - def tryRemove: ConnectionIO[SpaceChangeResult] = - for { - n <- RSpaceMember.delete(member, space) - res = - if (n == 0) SpaceChangeResult.notFound - else SpaceChangeResult.Success - } yield res - - (for { - uid <- OptionT(findUserId(account)) - space <- OptionT(RSpace.findById(space)) - res <- OptionT.liftF( - if (space.owner == uid) tryRemove - else SpaceChangeResult.forbidden.pure[ConnectionIO] - ) - } yield res).getOrElse(SpaceChangeResult.notFound) - } - - def addMember( - space: Ident, - account: AccountId, - member: Ident - ): ConnectionIO[SpaceChangeResult] = { - def tryAdd: ConnectionIO[SpaceChangeResult] = - for { - spm <- RSpaceMember.findByUserId(member, space) - mem <- RSpaceMember.newMember[ConnectionIO](space, member) - res <- - if (spm.isDefined) SpaceChangeResult.exists.pure[ConnectionIO] - else RSpaceMember.insert(mem).map(_ => SpaceChangeResult.Success) - } yield res - - (for { - uid <- OptionT(findUserId(account)) - space <- OptionT(RSpace.findById(space)) - res <- OptionT.liftF( - if (space.owner == uid) tryAdd - else SpaceChangeResult.forbidden.pure[ConnectionIO] - ) - } yield res).getOrElse(SpaceChangeResult.notFound) - } - - def findById(id: Ident, account: AccountId): ConnectionIO[Option[SpaceDetail]] = { - val mUserId = RSpaceMember.Columns.user.prefix("m") - val mSpaceId = RSpaceMember.Columns.space.prefix("m") - val uId = RUser.Columns.uid.prefix("u") - val uLogin = RUser.Columns.login.prefix("u") - val sColl = RSpace.Columns.collective.prefix("s") - val sId = RSpace.Columns.id.prefix("s") - - val from = RSpaceMember.table ++ fr"m INNER JOIN" ++ - RUser.table ++ fr"u ON" ++ mUserId.is(uId) ++ fr"INNER JOIN" ++ - RSpace.table ++ fr"s ON" ++ mSpaceId.is(sId) - - val memberQ = selectSimple( - Seq(uId, uLogin), - from, - and(mSpaceId.is(id), sColl.is(account.collective)) - ).query[IdRef].to[Vector] - - (for { - space <- OptionT(findAll(account, Some(id), None, None).map(_.headOption)) - memb <- OptionT.liftF(memberQ) - } yield space.withMembers(memb.toList)).value - } - - def findAll( - account: AccountId, - idQ: Option[Ident], - ownerLogin: Option[Ident], - nameQ: Option[String] - ): ConnectionIO[Vector[SpaceItem]] = { -// with memberlogin as -// (select m.space_id,u.login -// from space_member m -// inner join user_ u on u.uid = m.user_id -// inner join space s on s.id = m.space_id -// where s.cid = 'eike' -// union all -// select s.id,u.login -// from space s -// inner join user_ u on u.uid = s.owner -// where s.cid = 'eike') -// select s.id -// ,s.name -// ,s.owner -// ,u.login -// ,s.created -// ,(select count(*) > 0 from memberlogin where space_id = s.id and login = 'eike') as member -// ,(select count(*) - 1 from memberlogin where space_id = s.id) as member_count -// from space s -// inner join user_ u on u.uid = s.owner -// where s.cid = 'eike'; - - val uId = RUser.Columns.uid.prefix("u") - val uLogin = RUser.Columns.login.prefix("u") - val sId = RSpace.Columns.id.prefix("s") - val sOwner = RSpace.Columns.owner.prefix("s") - val sName = RSpace.Columns.name.prefix("s") - val sColl = RSpace.Columns.collective.prefix("s") - val mUser = RSpaceMember.Columns.user.prefix("m") - val mSpace = RSpaceMember.Columns.space.prefix("m") - - //CTE - val cte: Fragment = { - val from1 = RSpaceMember.table ++ fr"m INNER JOIN" ++ - RUser.table ++ fr"u ON" ++ uId.is(mUser) ++ fr"INNER JOIN" ++ - RSpace.table ++ fr"s ON" ++ sId.is(mSpace) - - val from2 = RSpace.table ++ fr"s INNER JOIN" ++ - RUser.table ++ fr"u ON" ++ uId.is(sOwner) - - withCTE( - "memberlogin" -> - (selectSimple(Seq(mSpace, uLogin), from1, sColl.is(account.collective)) ++ - fr"UNION ALL" ++ - selectSimple(Seq(sId, uLogin), from2, sColl.is(account.collective))) - ) - } - - val isMember = - fr"SELECT COUNT(*) > 0 FROM memberlogin WHERE" ++ mSpace.prefix("").is(sId) ++ - fr"AND" ++ uLogin.prefix("").is(account.user) - - val memberCount = - fr"SELECT COUNT(*) - 1 FROM memberlogin WHERE" ++ mSpace.prefix("").is(sId) - - //Query - val cols = Seq( - sId.f, - sName.f, - sOwner.f, - uLogin.f, - RSpace.Columns.created.prefix("s").f, - fr"(" ++ isMember ++ fr") as mem", - fr"(" ++ memberCount ++ fr") as cnt" - ) - - val from = RSpace.table ++ fr"s INNER JOIN" ++ - RUser.table ++ fr"u ON" ++ uId.is(sOwner) - - val where = - sColl.is(account.collective) :: idQ.toList - .map(id => sId.is(id)) ::: nameQ.toList.map(q => - sName.lowerLike(s"%${q.toLowerCase}%") - ) ::: ownerLogin.toList.map(login => uLogin.is(login)) - - (cte ++ selectSimple(commas(cols), from, and(where) ++ orderBy(sName.asc))) - .query[SpaceItem] - .to[Vector] - } - - private def findUserId(account: AccountId): ConnectionIO[Option[Ident]] = - RUser.findByAccount(account).map(_.map(_.uid)) -} diff --git a/modules/store/src/main/scala/docspell/store/records/RSpace.scala b/modules/store/src/main/scala/docspell/store/records/RFolder.scala similarity index 64% rename from modules/store/src/main/scala/docspell/store/records/RSpace.scala rename to modules/store/src/main/scala/docspell/store/records/RFolder.scala index 00bc802f..47401984 100644 --- a/modules/store/src/main/scala/docspell/store/records/RSpace.scala +++ b/modules/store/src/main/scala/docspell/store/records/RFolder.scala @@ -9,7 +9,7 @@ import docspell.store.impl.Implicits._ import doobie._ import doobie.implicits._ -case class RSpace( +case class RFolder( id: Ident, name: String, collectiveId: Ident, @@ -17,15 +17,15 @@ case class RSpace( created: Timestamp ) -object RSpace { +object RFolder { - def newSpace[F[_]: Sync](name: String, account: AccountId): F[RSpace] = + def newFolder[F[_]: Sync](name: String, account: AccountId): F[RFolder] = for { nId <- Ident.randomId[F] now <- Timestamp.current[F] - } yield RSpace(nId, name, account.collective, account.user, now) + } yield RFolder(nId, name, account.collective, account.user, now) - val table = fr"space" + val table = fr"folder" object Columns { @@ -40,7 +40,7 @@ object RSpace { import Columns._ - def insert(value: RSpace): ConnectionIO[Int] = { + def insert(value: RFolder): ConnectionIO[Int] = { val sql = insertRow( table, all, @@ -49,37 +49,37 @@ object RSpace { sql.update.run } - def update(v: RSpace): ConnectionIO[Int] = + def update(v: RFolder): ConnectionIO[Int] = updateRow( table, and(id.is(v.id), collective.is(v.collectiveId), owner.is(v.owner)), name.setTo(v.name) ).update.run - def existsByName(coll: Ident, spaceName: String): ConnectionIO[Boolean] = - selectCount(id, table, and(collective.is(coll), name.is(spaceName))) + def existsByName(coll: Ident, folderName: String): ConnectionIO[Boolean] = + selectCount(id, table, and(collective.is(coll), name.is(folderName))) .query[Int] .unique .map(_ > 0) - def findById(spaceId: Ident): ConnectionIO[Option[RSpace]] = { - val sql = selectSimple(all, table, id.is(spaceId)) - sql.query[RSpace].option + def findById(folderId: Ident): ConnectionIO[Option[RFolder]] = { + val sql = selectSimple(all, table, id.is(folderId)) + sql.query[RFolder].option } def findAll( coll: Ident, nameQ: Option[String], order: Columns.type => Column - ): ConnectionIO[Vector[RSpace]] = { + ): ConnectionIO[Vector[RFolder]] = { val q = Seq(collective.is(coll)) ++ (nameQ match { case Some(str) => Seq(name.lowerLike(s"%${str.toLowerCase}%")) case None => Seq.empty }) val sql = selectSimple(all, table, and(q)) ++ orderBy(order(Columns).f) - sql.query[RSpace].to[Vector] + sql.query[RFolder].to[Vector] } - def delete(spaceId: Ident): ConnectionIO[Int] = - deleteFrom(table, id.is(spaceId)).update.run + def delete(folderId: Ident): ConnectionIO[Int] = + deleteFrom(table, id.is(folderId)).update.run } diff --git a/modules/store/src/main/scala/docspell/store/records/RFolderMember.scala b/modules/store/src/main/scala/docspell/store/records/RFolderMember.scala new file mode 100644 index 00000000..cb7b5f21 --- /dev/null +++ b/modules/store/src/main/scala/docspell/store/records/RFolderMember.scala @@ -0,0 +1,61 @@ +package docspell.store.records + +import cats.effect._ +import cats.implicits._ + +import docspell.common._ +import docspell.store.impl.Column +import docspell.store.impl.Implicits._ + +import doobie._ +import doobie.implicits._ + +case class RFolderMember( + id: Ident, + folderId: Ident, + userId: Ident, + created: Timestamp +) + +object RFolderMember { + + def newMember[F[_]: Sync](folder: Ident, user: Ident): F[RFolderMember] = + for { + nId <- Ident.randomId[F] + now <- Timestamp.current[F] + } yield RFolderMember(nId, folder, user, now) + + val table = fr"folder_member" + + object Columns { + + val id = Column("id") + val folder = Column("folder_id") + val user = Column("user_id") + val created = Column("created") + + val all = List(id, folder, user, created) + } + + import Columns._ + + def insert(value: RFolderMember): ConnectionIO[Int] = { + val sql = insertRow( + table, + all, + fr"${value.id},${value.folderId},${value.userId},${value.created}" + ) + sql.update.run + } + + def findByUserId(userId: Ident, folderId: Ident): ConnectionIO[Option[RFolderMember]] = + selectSimple(all, table, and(folder.is(folderId), user.is(userId))) + .query[RFolderMember] + .option + + def delete(userId: Ident, folderId: Ident): ConnectionIO[Int] = + deleteFrom(table, and(folder.is(folderId), user.is(userId))).update.run + + def deleteAll(folderId: Ident): ConnectionIO[Int] = + deleteFrom(table, folder.is(folderId)).update.run +} diff --git a/modules/store/src/main/scala/docspell/store/records/RItem.scala b/modules/store/src/main/scala/docspell/store/records/RItem.scala index a8b33509..b0c197ce 100644 --- a/modules/store/src/main/scala/docspell/store/records/RItem.scala +++ b/modules/store/src/main/scala/docspell/store/records/RItem.scala @@ -28,7 +28,7 @@ case class RItem( created: Timestamp, updated: Timestamp, notes: Option[String], - spaceId: Option[Ident] + folderId: Option[Ident] ) {} object RItem { @@ -82,7 +82,7 @@ object RItem { val created = Column("created") val updated = Column("updated") val notes = Column("notes") - val space = Column("space_id") + val folder = Column("folder_id") val all = List( id, cid, @@ -100,7 +100,7 @@ object RItem { created, updated, notes, - space + folder ) } import Columns._ @@ -111,7 +111,7 @@ object RItem { all, fr"${v.id},${v.cid},${v.name},${v.itemDate},${v.source},${v.direction},${v.state}," ++ fr"${v.corrOrg},${v.corrPerson},${v.concPerson},${v.concEquipment},${v.inReplyTo},${v.dueDate}," ++ - fr"${v.created},${v.updated},${v.notes},${v.spaceId}" + fr"${v.created},${v.updated},${v.notes},${v.folderId}" ).update.run def getCollective(itemId: Ident): ConnectionIO[Option[Ident]] = @@ -300,8 +300,8 @@ object RItem { def findByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Option[RItem]] = selectSimple(all, table, and(id.is(itemId), cid.is(coll))).query[RItem].option - def removeSpace(spaceId: Ident): ConnectionIO[Int] = { + def removeFolder(folderId: Ident): ConnectionIO[Int] = { val empty: Option[Ident] = None - updateRow(table, space.is(spaceId), space.setTo(empty)).update.run + updateRow(table, folder.is(folderId), folder.setTo(empty)).update.run } } diff --git a/modules/store/src/main/scala/docspell/store/records/RSpaceMember.scala b/modules/store/src/main/scala/docspell/store/records/RSpaceMember.scala deleted file mode 100644 index 839c6267..00000000 --- a/modules/store/src/main/scala/docspell/store/records/RSpaceMember.scala +++ /dev/null @@ -1,61 +0,0 @@ -package docspell.store.records - -import cats.effect._ -import cats.implicits._ - -import docspell.common._ -import docspell.store.impl.Column -import docspell.store.impl.Implicits._ - -import doobie._ -import doobie.implicits._ - -case class RSpaceMember( - id: Ident, - spaceId: Ident, - userId: Ident, - created: Timestamp -) - -object RSpaceMember { - - def newMember[F[_]: Sync](space: Ident, user: Ident): F[RSpaceMember] = - for { - nId <- Ident.randomId[F] - now <- Timestamp.current[F] - } yield RSpaceMember(nId, space, user, now) - - val table = fr"space_member" - - object Columns { - - val id = Column("id") - val space = Column("space_id") - val user = Column("user_id") - val created = Column("created") - - val all = List(id, space, user, created) - } - - import Columns._ - - def insert(value: RSpaceMember): ConnectionIO[Int] = { - val sql = insertRow( - table, - all, - fr"${value.id},${value.spaceId},${value.userId},${value.created}" - ) - sql.update.run - } - - def findByUserId(userId: Ident, spaceId: Ident): ConnectionIO[Option[RSpaceMember]] = - selectSimple(all, table, and(space.is(spaceId), user.is(userId))) - .query[RSpaceMember] - .option - - def delete(userId: Ident, spaceId: Ident): ConnectionIO[Int] = - deleteFrom(table, and(space.is(spaceId), user.is(userId))).update.run - - def deleteAll(spaceId: Ident): ConnectionIO[Int] = - deleteFrom(table, space.is(spaceId)).update.run -} diff --git a/modules/webapp/src/main/elm/Api.elm b/modules/webapp/src/main/elm/Api.elm index e2adb8c9..b23fa1dd 100644 --- a/modules/webapp/src/main/elm/Api.elm +++ b/modules/webapp/src/main/elm/Api.elm @@ -6,16 +6,17 @@ module Api exposing , addMember , addTag , cancelJob + , changeFolderName , changePassword - , changeSpaceName , checkCalEvent , createImapSettings , createMailSettings - , createNewSpace + , createNewFolder , createNotifyDueItems , createScanMailbox , deleteAttachment , deleteEquip + , deleteFolder , deleteImapSettings , deleteItem , deleteMailSettings @@ -24,7 +25,6 @@ module Api exposing , deletePerson , deleteScanMailbox , deleteSource - , deleteSpace , deleteTag , deleteUser , getAttachmentMeta @@ -32,6 +32,8 @@ module Api exposing , getCollectiveSettings , getContacts , getEquipments + , getFolderDetail + , getFolders , getImapSettings , getInsights , getItemProposals @@ -46,8 +48,6 @@ module Api exposing , getScanMailbox , getSentMails , getSources - , getSpaceDetail - , getSpaces , getTags , getUsers , itemDetail @@ -108,6 +108,8 @@ import Api.Model.EmailSettings exposing (EmailSettings) import Api.Model.EmailSettingsList exposing (EmailSettingsList) import Api.Model.Equipment exposing (Equipment) import Api.Model.EquipmentList exposing (EquipmentList) +import Api.Model.FolderDetail exposing (FolderDetail) +import Api.Model.FolderList exposing (FolderList) import Api.Model.GenInvite exposing (GenInvite) import Api.Model.IdResult exposing (IdResult) import Api.Model.ImapSettings exposing (ImapSettings) @@ -122,7 +124,7 @@ import Api.Model.ItemSearch exposing (ItemSearch) import Api.Model.ItemUploadMeta exposing (ItemUploadMeta) import Api.Model.JobQueueState exposing (JobQueueState) import Api.Model.MoveAttachment exposing (MoveAttachment) -import Api.Model.NewSpace exposing (NewSpace) +import Api.Model.NewFolder exposing (NewFolder) import Api.Model.NotificationSettings exposing (NotificationSettings) import Api.Model.NotificationSettingsList exposing (NotificationSettingsList) import Api.Model.OptionalDate exposing (OptionalDate) @@ -141,8 +143,6 @@ import Api.Model.SentMails exposing (SentMails) import Api.Model.SimpleMail exposing (SimpleMail) import Api.Model.Source exposing (Source) import Api.Model.SourceList exposing (SourceList) -import Api.Model.SpaceDetail exposing (SpaceDetail) -import Api.Model.SpaceList exposing (SpaceList) import Api.Model.Tag exposing (Tag) import Api.Model.TagList exposing (TagList) import Api.Model.User exposing (User) @@ -161,13 +161,13 @@ import Util.Http as Http2 ---- Spaces +--- Folders -deleteSpace : Flags -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg -deleteSpace flags id receive = +deleteFolder : Flags -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg +deleteFolder flags id receive = Http2.authDelete - { url = flags.config.baseUrl ++ "/api/v1/sec/space/" ++ id + { url = flags.config.baseUrl ++ "/api/v1/sec/folder/" ++ id , account = getAccount flags , expect = Http.expectJson receive Api.Model.BasicResult.decoder } @@ -176,7 +176,7 @@ deleteSpace flags id receive = removeMember : Flags -> String -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg removeMember flags id user receive = Http2.authDelete - { url = flags.config.baseUrl ++ "/api/v1/sec/space/" ++ id ++ "/member/" ++ user + { url = flags.config.baseUrl ++ "/api/v1/sec/folder/" ++ id ++ "/member/" ++ user , account = getAccount flags , expect = Http.expectJson receive Api.Model.BasicResult.decoder } @@ -185,48 +185,48 @@ removeMember flags id user receive = addMember : Flags -> String -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg addMember flags id user receive = Http2.authPut - { url = flags.config.baseUrl ++ "/api/v1/sec/space/" ++ id ++ "/member/" ++ user + { url = flags.config.baseUrl ++ "/api/v1/sec/folder/" ++ id ++ "/member/" ++ user , account = getAccount flags , body = Http.emptyBody , expect = Http.expectJson receive Api.Model.BasicResult.decoder } -changeSpaceName : Flags -> String -> NewSpace -> (Result Http.Error BasicResult -> msg) -> Cmd msg -changeSpaceName flags id ns receive = +changeFolderName : Flags -> String -> NewFolder -> (Result Http.Error BasicResult -> msg) -> Cmd msg +changeFolderName flags id ns receive = Http2.authPut - { url = flags.config.baseUrl ++ "/api/v1/sec/space/" ++ id + { url = flags.config.baseUrl ++ "/api/v1/sec/folder/" ++ id , account = getAccount flags - , body = Http.jsonBody (Api.Model.NewSpace.encode ns) + , body = Http.jsonBody (Api.Model.NewFolder.encode ns) , expect = Http.expectJson receive Api.Model.BasicResult.decoder } -createNewSpace : Flags -> NewSpace -> (Result Http.Error IdResult -> msg) -> Cmd msg -createNewSpace flags ns receive = +createNewFolder : Flags -> NewFolder -> (Result Http.Error IdResult -> msg) -> Cmd msg +createNewFolder flags ns receive = Http2.authPost - { url = flags.config.baseUrl ++ "/api/v1/sec/space" + { url = flags.config.baseUrl ++ "/api/v1/sec/folder" , account = getAccount flags - , body = Http.jsonBody (Api.Model.NewSpace.encode ns) + , body = Http.jsonBody (Api.Model.NewFolder.encode ns) , expect = Http.expectJson receive Api.Model.IdResult.decoder } -getSpaceDetail : Flags -> String -> (Result Http.Error SpaceDetail -> msg) -> Cmd msg -getSpaceDetail flags id receive = +getFolderDetail : Flags -> String -> (Result Http.Error FolderDetail -> msg) -> Cmd msg +getFolderDetail flags id receive = Http2.authGet - { url = flags.config.baseUrl ++ "/api/v1/sec/space/" ++ id + { url = flags.config.baseUrl ++ "/api/v1/sec/folder/" ++ id , account = getAccount flags - , expect = Http.expectJson receive Api.Model.SpaceDetail.decoder + , expect = Http.expectJson receive Api.Model.FolderDetail.decoder } -getSpaces : Flags -> String -> Bool -> (Result Http.Error SpaceList -> msg) -> Cmd msg -getSpaces flags query owningOnly receive = +getFolders : Flags -> String -> Bool -> (Result Http.Error FolderList -> msg) -> Cmd msg +getFolders flags query owningOnly receive = Http2.authGet { url = flags.config.baseUrl - ++ "/api/v1/sec/space?q=" + ++ "/api/v1/sec/folder?q=" ++ Url.percentEncode query ++ (if owningOnly then "&owning=true" @@ -235,7 +235,7 @@ getSpaces flags query owningOnly receive = "" ) , account = getAccount flags - , expect = Http.expectJson receive Api.Model.SpaceList.decoder + , expect = Http.expectJson receive Api.Model.FolderList.decoder } diff --git a/modules/webapp/src/main/elm/Comp/SpaceDetail.elm b/modules/webapp/src/main/elm/Comp/FolderDetail.elm similarity index 80% rename from modules/webapp/src/main/elm/Comp/SpaceDetail.elm rename to modules/webapp/src/main/elm/Comp/FolderDetail.elm index b6e9c571..f3e44abc 100644 --- a/modules/webapp/src/main/elm/Comp/SpaceDetail.elm +++ b/modules/webapp/src/main/elm/Comp/FolderDetail.elm @@ -1,4 +1,4 @@ -module Comp.SpaceDetail exposing +module Comp.FolderDetail exposing ( Model , Msg , init @@ -9,10 +9,10 @@ module Comp.SpaceDetail exposing import Api import Api.Model.BasicResult exposing (BasicResult) +import Api.Model.FolderDetail exposing (FolderDetail) import Api.Model.IdName exposing (IdName) import Api.Model.IdResult exposing (IdResult) -import Api.Model.NewSpace exposing (NewSpace) -import Api.Model.SpaceDetail exposing (SpaceDetail) +import Api.Model.NewFolder exposing (NewFolder) import Api.Model.User exposing (User) import Api.Model.UserList exposing (UserList) import Comp.FixedDropdown @@ -28,7 +28,7 @@ import Util.Maybe type alias Model = { result : Maybe BasicResult - , space : SpaceDetail + , folder : FolderDetail , name : Maybe String , members : List IdName , users : List User @@ -43,10 +43,10 @@ type Msg = SetName String | MemberDropdownMsg (Comp.FixedDropdown.Msg IdName) | SaveName - | NewSpaceResp (Result Http.Error IdResult) - | ChangeSpaceResp (Result Http.Error BasicResult) + | NewFolderResp (Result Http.Error IdResult) + | ChangeFolderResp (Result Http.Error BasicResult) | ChangeNameResp (Result Http.Error BasicResult) - | SpaceDetailResp (Result Http.Error SpaceDetail) + | FolderDetailResp (Result Http.Error FolderDetail) | AddMember | RemoveMember IdName | RequestDelete @@ -55,16 +55,16 @@ type Msg | GoBack -init : List User -> SpaceDetail -> Model -init users space = +init : List User -> FolderDetail -> Model +init users folder = { result = Nothing - , space = space - , name = Util.Maybe.fromString space.name - , members = space.members + , folder = folder + , name = Util.Maybe.fromString folder.name + , members = folder.members , users = users , memberDropdown = Comp.FixedDropdown.initMap .name - (makeOptions users space) + (makeOptions users folder) , selectedMember = Nothing , loading = False , deleteDimmer = Comp.YesNoDimmer.emptyModel @@ -73,17 +73,17 @@ init users space = initEmpty : List User -> Model initEmpty users = - init users Api.Model.SpaceDetail.empty + init users Api.Model.FolderDetail.empty -makeOptions : List User -> SpaceDetail -> List IdName -makeOptions users space = +makeOptions : List User -> FolderDetail -> List IdName +makeOptions users folder = let toIdName u = IdName u.id u.login notMember idn = - List.member idn (space.owner :: space.members) |> not + List.member idn (folder.owner :: folder.members) |> not in List.map toIdName users |> List.filter notMember @@ -129,13 +129,13 @@ update flags msg model = Just name -> let cmd = - if model.space.id == "" then - Api.createNewSpace flags (NewSpace name) NewSpaceResp + if model.folder.id == "" then + Api.createNewFolder flags (NewFolder name) NewFolderResp else - Api.changeSpaceName flags - model.space.id - (NewSpace name) + Api.changeFolderName flags + model.folder.id + (NewFolder name) ChangeNameResp in ( { model @@ -149,9 +149,9 @@ update flags msg model = Nothing -> ( model, Cmd.none, False ) - NewSpaceResp (Ok ir) -> + NewFolderResp (Ok ir) -> if ir.success then - ( model, Api.getSpaceDetail flags ir.id SpaceDetailResp, False ) + ( model, Api.getFolderDetail flags ir.id FolderDetailResp, False ) else ( { model @@ -162,7 +162,7 @@ update flags msg model = , False ) - NewSpaceResp (Err err) -> + NewFolderResp (Err err) -> ( { model | loading = False , result = Just (BasicResult False (Util.Http.errorToString err)) @@ -171,10 +171,10 @@ update flags msg model = , False ) - ChangeSpaceResp (Ok r) -> + ChangeFolderResp (Ok r) -> if r.success then ( model - , Api.getSpaceDetail flags model.space.id SpaceDetailResp + , Api.getFolderDetail flags model.folder.id FolderDetailResp , False ) @@ -184,7 +184,7 @@ update flags msg model = , False ) - ChangeSpaceResp (Err err) -> + ChangeFolderResp (Err err) -> ( { model | loading = False , result = Just (BasicResult False (Util.Http.errorToString err)) @@ -209,10 +209,10 @@ update flags msg model = , False ) - SpaceDetailResp (Ok sd) -> + FolderDetailResp (Ok sd) -> ( init model.users sd, Cmd.none, False ) - SpaceDetailResp (Err err) -> + FolderDetailResp (Err err) -> ( { model | loading = False , result = Just (BasicResult False (Util.Http.errorToString err)) @@ -225,7 +225,7 @@ update flags msg model = case model.selectedMember of Just mem -> ( { model | loading = True } - , Api.addMember flags model.space.id mem.id ChangeSpaceResp + , Api.addMember flags model.folder.id mem.id ChangeFolderResp , False ) @@ -234,7 +234,7 @@ update flags msg model = RemoveMember idname -> ( { model | loading = True } - , Api.removeMember flags model.space.id idname.id ChangeSpaceResp + , Api.removeMember flags model.folder.id idname.id ChangeFolderResp , False ) @@ -252,7 +252,7 @@ update flags msg model = cmd = if flag then - Api.deleteSpace flags model.space.id DeleteResp + Api.deleteFolder flags model.folder.id DeleteResp else Cmd.none @@ -278,23 +278,23 @@ view flags model = let isOwner = Maybe.map .user flags.account - |> Maybe.map ((==) model.space.owner.name) + |> Maybe.map ((==) model.folder.owner.name) |> Maybe.withDefault False in div [] ([ Html.map DeleteMsg (Comp.YesNoDimmer.view model.deleteDimmer) - , if model.space.id == "" then + , if model.folder.id == "" then div [] - [ text "Create a new space. You are automatically set as owner of this new space." + [ text "Create a new folder. You are automatically set as owner of this new folder." ] else div [] - [ text "Modify this space by changing the name or add/remove members." + [ text "Modify this folder by changing the name or add/remove members." ] - , if model.space.id /= "" && not isOwner then + , if model.folder.id /= "" && not isOwner then div [ class "ui info message" ] - [ text "You are not the owner of this space and therefore are not allowed to edit it." + [ text "You are not the owner of this folder and therefore are not allowed to edit it." ] else @@ -315,7 +315,7 @@ view flags model = [ text "Owner" ] , div [ class "" ] - [ text model.space.owner.name + [ text model.folder.owner.name ] , div [ class "ui header" ] [ text "Name" @@ -361,7 +361,7 @@ viewButtons _ = viewMembers : Model -> List (Html Msg) viewMembers model = - if model.space.id == "" then + if model.folder.id == "" then [] else diff --git a/modules/webapp/src/main/elm/Comp/SpaceManage.elm b/modules/webapp/src/main/elm/Comp/FolderManage.elm similarity index 68% rename from modules/webapp/src/main/elm/Comp/SpaceManage.elm rename to modules/webapp/src/main/elm/Comp/FolderManage.elm index d050d6d6..6ffad0d6 100644 --- a/modules/webapp/src/main/elm/Comp/SpaceManage.elm +++ b/modules/webapp/src/main/elm/Comp/FolderManage.elm @@ -1,4 +1,4 @@ -module Comp.SpaceManage exposing +module Comp.FolderManage exposing ( Model , Msg , empty @@ -8,13 +8,13 @@ module Comp.SpaceManage exposing ) import Api -import Api.Model.SpaceDetail exposing (SpaceDetail) -import Api.Model.SpaceItem exposing (SpaceItem) -import Api.Model.SpaceList exposing (SpaceList) +import Api.Model.FolderDetail exposing (FolderDetail) +import Api.Model.FolderItem exposing (FolderItem) +import Api.Model.FolderList exposing (FolderList) import Api.Model.User exposing (User) import Api.Model.UserList exposing (UserList) -import Comp.SpaceDetail -import Comp.SpaceTable +import Comp.FolderDetail +import Comp.FolderTable import Data.Flags exposing (Flags) import Html exposing (..) import Html.Attributes exposing (..) @@ -23,9 +23,9 @@ import Http type alias Model = - { tableModel : Comp.SpaceTable.Model - , detailModel : Maybe Comp.SpaceDetail.Model - , spaces : List SpaceItem + { tableModel : Comp.FolderTable.Model + , detailModel : Maybe Comp.FolderDetail.Model + , folders : List FolderItem , users : List User , query : String , owningOnly : Bool @@ -34,21 +34,21 @@ type alias Model = type Msg - = TableMsg Comp.SpaceTable.Msg - | DetailMsg Comp.SpaceDetail.Msg + = TableMsg Comp.FolderTable.Msg + | DetailMsg Comp.FolderDetail.Msg | UserListResp (Result Http.Error UserList) - | SpaceListResp (Result Http.Error SpaceList) - | SpaceDetailResp (Result Http.Error SpaceDetail) + | FolderListResp (Result Http.Error FolderList) + | FolderDetailResp (Result Http.Error FolderDetail) | SetQuery String - | InitNewSpace + | InitNewFolder | ToggleOwningOnly empty : Model empty = - { tableModel = Comp.SpaceTable.init + { tableModel = Comp.FolderTable.init , detailModel = Nothing - , spaces = [] + , folders = [] , users = [] , query = "" , owningOnly = True @@ -61,7 +61,7 @@ init flags = ( empty , Cmd.batch [ Api.getUsers flags UserListResp - , Api.getSpaces flags empty.query empty.owningOnly SpaceListResp + , Api.getFolders flags empty.query empty.owningOnly FolderListResp ] ) @@ -76,14 +76,14 @@ update flags msg model = TableMsg lm -> let ( tm, action ) = - Comp.SpaceTable.update lm model.tableModel + Comp.FolderTable.update lm model.tableModel cmd = case action of - Comp.SpaceTable.EditAction item -> - Api.getSpaceDetail flags item.id SpaceDetailResp + Comp.FolderTable.EditAction item -> + Api.getFolderDetail flags item.id FolderDetailResp - Comp.SpaceTable.NoAction -> + Comp.FolderTable.NoAction -> Cmd.none in ( { model | tableModel = tm }, cmd ) @@ -93,11 +93,11 @@ update flags msg model = Just detail -> let ( dm, dc, back ) = - Comp.SpaceDetail.update flags lm detail + Comp.FolderDetail.update flags lm detail cmd = if back then - Api.getSpaces flags model.query model.owningOnly SpaceListResp + Api.getFolders flags model.query model.owningOnly FolderListResp else Cmd.none @@ -121,7 +121,7 @@ update flags msg model = SetQuery str -> ( { model | query = str } - , Api.getSpaces flags str model.owningOnly SpaceListResp + , Api.getFolders flags str model.owningOnly FolderListResp ) ToggleOwningOnly -> @@ -130,7 +130,7 @@ update flags msg model = not model.owningOnly in ( { model | owningOnly = newOwning } - , Api.getSpaces flags model.query newOwning SpaceListResp + , Api.getFolders flags model.query newOwning FolderListResp ) UserListResp (Ok ul) -> @@ -139,24 +139,24 @@ update flags msg model = UserListResp (Err err) -> ( model, Cmd.none ) - SpaceListResp (Ok sl) -> - ( { model | spaces = sl.items }, Cmd.none ) + FolderListResp (Ok sl) -> + ( { model | folders = sl.items }, Cmd.none ) - SpaceListResp (Err err) -> + FolderListResp (Err err) -> ( model, Cmd.none ) - SpaceDetailResp (Ok sd) -> - ( { model | detailModel = Comp.SpaceDetail.init model.users sd |> Just } + FolderDetailResp (Ok sd) -> + ( { model | detailModel = Comp.FolderDetail.init model.users sd |> Just } , Cmd.none ) - SpaceDetailResp (Err err) -> + FolderDetailResp (Err err) -> ( model, Cmd.none ) - InitNewSpace -> + InitNewFolder -> let sd = - Comp.SpaceDetail.initEmpty model.users + Comp.FolderDetail.initEmpty model.users in ( { model | detailModel = Just sd } , Cmd.none @@ -177,10 +177,10 @@ view flags model = viewTable model -viewDetail : Flags -> Comp.SpaceDetail.Model -> Html Msg +viewDetail : Flags -> Comp.FolderDetail.Model -> Html Msg viewDetail flags detailModel = div [] - [ Html.map DetailMsg (Comp.SpaceDetail.view flags detailModel) + [ Html.map DetailMsg (Comp.FolderDetail.view flags detailModel) ] @@ -209,7 +209,7 @@ viewTable model = , checked model.owningOnly ] [] - , label [] [ text "Show owning spaces only" ] + , label [] [ text "Show owning folders only" ] ] ] , div [ class "right menu" ] @@ -217,15 +217,15 @@ viewTable model = [ a [ class "ui primary button" , href "#" - , onClick InitNewSpace + , onClick InitNewFolder ] [ i [ class "plus icon" ] [] - , text "New Space" + , text "New Folder" ] ] ] ] - , Html.map TableMsg (Comp.SpaceTable.view model.tableModel model.spaces) + , Html.map TableMsg (Comp.FolderTable.view model.tableModel model.folders) , div [ classList [ ( "ui dimmer", True ) diff --git a/modules/webapp/src/main/elm/Comp/SpaceTable.elm b/modules/webapp/src/main/elm/Comp/FolderTable.elm similarity index 88% rename from modules/webapp/src/main/elm/Comp/SpaceTable.elm rename to modules/webapp/src/main/elm/Comp/FolderTable.elm index 19f7fb3e..a44f5e59 100644 --- a/modules/webapp/src/main/elm/Comp/SpaceTable.elm +++ b/modules/webapp/src/main/elm/Comp/FolderTable.elm @@ -1,4 +1,4 @@ -module Comp.SpaceTable exposing +module Comp.FolderTable exposing ( Action(..) , Model , Msg @@ -7,7 +7,7 @@ module Comp.SpaceTable exposing , view ) -import Api.Model.SpaceItem exposing (SpaceItem) +import Api.Model.FolderItem exposing (FolderItem) import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onClick) @@ -20,12 +20,12 @@ type alias Model = type Msg - = EditItem SpaceItem + = EditItem FolderItem type Action = NoAction - | EditAction SpaceItem + | EditAction FolderItem init : Model @@ -40,7 +40,7 @@ update msg model = ( model, EditAction item ) -view : Model -> List SpaceItem -> Html Msg +view : Model -> List FolderItem -> Html Msg view _ items = div [] [ table [ class "ui very basic center aligned table" ] @@ -58,7 +58,7 @@ view _ items = ] -viewItem : SpaceItem -> Html Msg +viewItem : FolderItem -> Html Msg viewItem item = tr [] [ td [ class "collapsing" ] diff --git a/modules/webapp/src/main/elm/Data/Icons.elm b/modules/webapp/src/main/elm/Data/Icons.elm index 77badc14..86999931 100644 --- a/modules/webapp/src/main/elm/Data/Icons.elm +++ b/modules/webapp/src/main/elm/Data/Icons.elm @@ -15,12 +15,12 @@ module Data.Icons exposing , editNotesIcon , equipment , equipmentIcon + , folder + , folderIcon , organization , organizationIcon , person , personIcon - , space - , spaceIcon , tag , tagIcon , tags @@ -31,14 +31,14 @@ import Html exposing (Html, i) import Html.Attributes exposing (class) -space : String -space = +folder : String +folder = "folder outline icon" -spaceIcon : String -> Html msg -spaceIcon classes = - i [ class (space ++ " " ++ classes) ] [] +folderIcon : String -> Html msg +folderIcon classes = + i [ class (folder ++ " " ++ classes) ] [] concerned : String diff --git a/modules/webapp/src/main/elm/Page/ManageData/Data.elm b/modules/webapp/src/main/elm/Page/ManageData/Data.elm index 8ab230a4..69178dac 100644 --- a/modules/webapp/src/main/elm/Page/ManageData/Data.elm +++ b/modules/webapp/src/main/elm/Page/ManageData/Data.elm @@ -6,9 +6,9 @@ module Page.ManageData.Data exposing ) import Comp.EquipmentManage +import Comp.FolderManage import Comp.OrgManage import Comp.PersonManage -import Comp.SpaceManage import Comp.TagManage import Data.Flags exposing (Flags) @@ -19,7 +19,7 @@ type alias Model = , equipManageModel : Comp.EquipmentManage.Model , orgManageModel : Comp.OrgManage.Model , personManageModel : Comp.PersonManage.Model - , spaceManageModel : Comp.SpaceManage.Model + , folderManageModel : Comp.FolderManage.Model } @@ -30,7 +30,7 @@ init _ = , equipManageModel = Comp.EquipmentManage.emptyModel , orgManageModel = Comp.OrgManage.emptyModel , personManageModel = Comp.PersonManage.emptyModel - , spaceManageModel = Comp.SpaceManage.empty + , folderManageModel = Comp.FolderManage.empty } , Cmd.none ) @@ -41,7 +41,7 @@ type Tab | EquipTab | OrgTab | PersonTab - | SpaceTab + | FolderTab type Msg @@ -50,4 +50,4 @@ type Msg | EquipManageMsg Comp.EquipmentManage.Msg | OrgManageMsg Comp.OrgManage.Msg | PersonManageMsg Comp.PersonManage.Msg - | SpaceMsg Comp.SpaceManage.Msg + | FolderMsg Comp.FolderManage.Msg diff --git a/modules/webapp/src/main/elm/Page/ManageData/Update.elm b/modules/webapp/src/main/elm/Page/ManageData/Update.elm index d50f0042..f229e2ad 100644 --- a/modules/webapp/src/main/elm/Page/ManageData/Update.elm +++ b/modules/webapp/src/main/elm/Page/ManageData/Update.elm @@ -1,9 +1,9 @@ module Page.ManageData.Update exposing (update) import Comp.EquipmentManage +import Comp.FolderManage import Comp.OrgManage import Comp.PersonManage -import Comp.SpaceManage import Comp.TagManage import Data.Flags exposing (Flags) import Page.ManageData.Data exposing (..) @@ -30,12 +30,12 @@ update flags msg model = PersonTab -> update flags (PersonManageMsg Comp.PersonManage.LoadPersons) m - SpaceTab -> + FolderTab -> let ( sm, sc ) = - Comp.SpaceManage.init flags + Comp.FolderManage.init flags in - ( { m | spaceManageModel = sm }, Cmd.map SpaceMsg sc ) + ( { m | folderManageModel = sm }, Cmd.map FolderMsg sc ) TagManageMsg m -> let @@ -65,11 +65,11 @@ update flags msg model = in ( { model | personManageModel = m2 }, Cmd.map PersonManageMsg c2 ) - SpaceMsg lm -> + FolderMsg lm -> let ( m2, c2 ) = - Comp.SpaceManage.update flags lm model.spaceManageModel + Comp.FolderManage.update flags lm model.folderManageModel in - ( { model | spaceManageModel = m2 } - , Cmd.map SpaceMsg c2 + ( { model | folderManageModel = m2 } + , Cmd.map FolderMsg c2 ) diff --git a/modules/webapp/src/main/elm/Page/ManageData/View.elm b/modules/webapp/src/main/elm/Page/ManageData/View.elm index 0829c920..b7d853fb 100644 --- a/modules/webapp/src/main/elm/Page/ManageData/View.elm +++ b/modules/webapp/src/main/elm/Page/ManageData/View.elm @@ -1,9 +1,9 @@ module Page.ManageData.View exposing (view) import Comp.EquipmentManage +import Comp.FolderManage import Comp.OrgManage import Comp.PersonManage -import Comp.SpaceManage import Comp.TagManage import Data.Flags exposing (Flags) import Data.Icons as Icons @@ -53,11 +53,11 @@ view flags settings model = , text "Person" ] , div - [ classActive (model.currentTab == Just SpaceTab) "link icon item" - , onClick (SetTab SpaceTab) + [ classActive (model.currentTab == Just FolderTab) "link icon item" + , onClick (SetTab FolderTab) ] - [ Icons.spaceIcon "" - , text "Space" + [ Icons.folderIcon "" + , text "Folder" ] ] ] @@ -77,8 +77,8 @@ view flags settings model = Just PersonTab -> viewPerson settings model - Just SpaceTab -> - viewSpace flags settings model + Just FolderTab -> + viewFolder flags settings model Nothing -> [] @@ -87,19 +87,19 @@ view flags settings model = ] -viewSpace : Flags -> UiSettings -> Model -> List (Html Msg) -viewSpace flags _ model = +viewFolder : Flags -> UiSettings -> Model -> List (Html Msg) +viewFolder flags _ model = [ h2 [ class "ui header" ] - [ Icons.spaceIcon "" + [ Icons.folderIcon "" , div [ class "content" ] - [ text "Spaces" + [ text "Folders" ] ] - , Html.map SpaceMsg (Comp.SpaceManage.view flags model.spaceManageModel) + , Html.map FolderMsg (Comp.FolderManage.view flags model.folderManageModel) ]