Rename space -> folder

This commit is contained in:
Eike Kettner 2020-07-11 11:38:57 +02:00
parent 0365c1980a
commit 2ab0b5e222
22 changed files with 755 additions and 754 deletions

View File

@ -35,7 +35,7 @@ trait BackendApp[F[_]] {
def mail: OMail[F] def mail: OMail[F]
def joex: OJoex[F] def joex: OJoex[F]
def userTask: OUserTask[F] def userTask: OUserTask[F]
def space: OSpace[F] def folder: OFolder[F]
} }
object BackendApp { object BackendApp {
@ -68,7 +68,7 @@ object BackendApp {
JavaMailEmil(blocker, Settings.defaultSettings.copy(debug = cfg.mailDebug)) JavaMailEmil(blocker, Settings.defaultSettings.copy(debug = cfg.mailDebug))
mailImpl <- OMail(store, javaEmil) mailImpl <- OMail(store, javaEmil)
userTaskImpl <- OUserTask(utStore, queue, joexImpl) userTaskImpl <- OUserTask(utStore, queue, joexImpl)
spaceImpl <- OSpace(store) folderImpl <- OFolder(store)
} yield new BackendApp[F] { } yield new BackendApp[F] {
val login: Login[F] = loginImpl val login: Login[F] = loginImpl
val signup: OSignup[F] = signupImpl val signup: OSignup[F] = signupImpl
@ -86,7 +86,7 @@ object BackendApp {
val mail = mailImpl val mail = mailImpl
val joex = joexImpl val joex = joexImpl
val userTask = userTaskImpl val userTask = userTaskImpl
val space = spaceImpl val folder = folderImpl
} }
def apply[F[_]: ConcurrentEffect: ContextShift]( def apply[F[_]: ConcurrentEffect: ContextShift](

View File

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

View File

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

View File

@ -795,14 +795,14 @@ paths:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/BasicResult" $ref: "#/components/schemas/BasicResult"
/sec/space: /sec/folder:
get: get:
tags: [ Space ] tags: [ Folder ]
summary: Get a list of spaces. summary: Get a list of folders.
description: | 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. current user.
It is possible to restrict the results by a substring match of It is possible to restrict the results by a substring match of
@ -818,12 +818,12 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/SpaceList" $ref: "#/components/schemas/FolderList"
post: post:
tags: [ Space ] tags: [ Folder ]
summary: Create a new space summary: Create a new folder
description: | 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. the same name already exists, an error is thrown.
security: security:
- authTokenHeader: [] - authTokenHeader: []
@ -831,7 +831,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/NewSpace" $ref: "#/components/schemas/NewFolder"
responses: responses:
200: 200:
description: Ok description: Ok
@ -839,12 +839,12 @@ paths:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/IdResult" $ref: "#/components/schemas/IdResult"
/sec/space/{id}: /sec/folder/{id}:
get: get:
tags: [ Space ] tags: [ Folder ]
summary: Get space details. summary: Get folder details.
description: | description: |
Return details about a space. Return details about a folder.
security: security:
- authTokenHeader: [] - authTokenHeader: []
parameters: parameters:
@ -855,12 +855,12 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/SpaceDetail" $ref: "#/components/schemas/FolderDetail"
put: put:
tags: [ Space ] tags: [ Folder ]
summary: Change the name of a space summary: Change the name of a folder
description: | 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: security:
- authTokenHeader: [] - authTokenHeader: []
parameters: parameters:
@ -869,7 +869,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/NewSpace" $ref: "#/components/schemas/NewFolder"
responses: responses:
200: 200:
description: Ok description: Ok
@ -878,10 +878,10 @@ paths:
schema: schema:
$ref: "#/components/schemas/BasicResult" $ref: "#/components/schemas/BasicResult"
delete: delete:
tags: [ Space ] tags: [ Folder ]
summary: Delete a space by its id. summary: Delete a folder by its id.
description: | description: |
Deletes a space. Deletes a folder.
security: security:
- authTokenHeader: [] - authTokenHeader: []
parameters: parameters:
@ -893,12 +893,12 @@ paths:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/BasicResult" $ref: "#/components/schemas/BasicResult"
/sec/space/{id}/member/{userId}: /sec/folder/{id}/member/{userId}:
put: put:
tags: [ Space ] tags: [ Folder ]
summary: Add a member to this space summary: Add a member to this folder
description: | description: |
Adds a member to this space (identified by `id`). Adds a member to this folder (identified by `id`).
security: security:
- authTokenHeader: [] - authTokenHeader: []
parameters: parameters:
@ -912,10 +912,10 @@ paths:
schema: schema:
$ref: "#/components/schemas/BasicResult" $ref: "#/components/schemas/BasicResult"
delete: delete:
tags: [ Space ] tags: [ Folder ]
summary: Removes a member from this space. summary: Removes a member from this folder.
description: | description: |
Removes a member from this space. Removes a member from this folder.
security: security:
- authTokenHeader: [] - authTokenHeader: []
parameters: parameters:
@ -984,7 +984,7 @@ paths:
summary: Get some insights regarding your items. summary: Get some insights regarding your items.
description: | description: |
Returns some information about how many items there are, how Returns some information about how many items there are, how
much space they occupy etc. much folder they occupy etc.
security: security:
- authTokenHeader: [] - authTokenHeader: []
responses: responses:
@ -2492,19 +2492,19 @@ paths:
components: components:
schemas: schemas:
SpaceList: FolderList:
description: | description: |
A list of spaces with their member counts. A list of folders with their member counts.
required: required:
- items - items
properties: properties:
items: items:
type: array type: array
items: items:
$ref: "#/components/schemas/SpaceItem" $ref: "#/components/schemas/FolderItem"
SpaceItem: FolderItem:
description: | description: |
An item in a space list. An item in a folder list.
required: required:
- id - id
- name - name
@ -2528,17 +2528,17 @@ components:
memberCount: memberCount:
type: integer type: integer
format: int32 format: int32
NewSpace: NewFolder:
description: | description: |
Data required to create a new space. Data required to create a new folder.
required: required:
- name - name
properties: properties:
name: name:
type: string type: string
SpaceDetail: FolderDetail:
description: | description: |
Details about a space. Details about a folder.
required: required:
- id - id
- name - name
@ -2567,9 +2567,9 @@ components:
type: array type: array
items: items:
$ref: "#/components/schemas/IdName" $ref: "#/components/schemas/IdName"
SpaceMember: FolderMember:
description: | description: |
Information to add or remove a space member. Information to add or remove a folder member.
required: required:
- userId - userId
properties: properties:
@ -4001,7 +4001,7 @@ components:
owning: owning:
name: full name: full
in: query in: query
description: Whether to get owning spaces description: Whether to get owning folders
required: false required: false
schema: schema:
type: boolean type: boolean

View File

@ -82,7 +82,7 @@ object RestServer {
"usertask/scanmailbox" -> ScanMailboxRoutes(restApp.backend, token), "usertask/scanmailbox" -> ScanMailboxRoutes(restApp.backend, token),
"calevent/check" -> CalEventCheckRoutes(), "calevent/check" -> CalEventCheckRoutes(),
"fts" -> FullTextIndexRoutes.secured(cfg, restApp.backend, token), "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] = def openRoutes[F[_]: Effect](cfg: Config, restApp: RestApp[F]): HttpRoutes[F] =

View File

@ -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.")
}
}

View File

@ -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.")
}
}

View File

@ -1,4 +1,4 @@
CREATE TABLE "space" ( CREATE TABLE "folder" (
"id" varchar(254) not null primary key, "id" varchar(254) not null primary key,
"name" varchar(254) not null, "name" varchar(254) not null,
"cid" varchar(254) not null, "cid" varchar(254) not null,
@ -9,15 +9,15 @@ CREATE TABLE "space" (
foreign key ("owner") references "user_"("uid") foreign key ("owner") references "user_"("uid")
); );
CREATE TABLE "space_member" ( CREATE TABLE "folder_member" (
"id" varchar(254) not null primary key, "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, "user_id" varchar(254) not null,
"created" timestamp not null, "created" timestamp not null,
unique ("space_id", "user_id"), unique ("folder_id", "user_id"),
foreign key ("space_id") references "space"("id"), foreign key ("folder_id") references "folder"("id"),
foreign key ("user_id") references "user_"("uid") foreign key ("user_id") references "user_"("uid")
); );
ALTER TABLE "item" ALTER TABLE "item"
ADD COLUMN "space_id" varchar(254) NULL; ADD COLUMN "folder_id" varchar(254) NULL;

View File

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

View File

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

View File

@ -9,7 +9,7 @@ import docspell.store.impl.Implicits._
import doobie._ import doobie._
import doobie.implicits._ import doobie.implicits._
case class RSpace( case class RFolder(
id: Ident, id: Ident,
name: String, name: String,
collectiveId: Ident, collectiveId: Ident,
@ -17,15 +17,15 @@ case class RSpace(
created: Timestamp 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 { for {
nId <- Ident.randomId[F] nId <- Ident.randomId[F]
now <- Timestamp.current[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 { object Columns {
@ -40,7 +40,7 @@ object RSpace {
import Columns._ import Columns._
def insert(value: RSpace): ConnectionIO[Int] = { def insert(value: RFolder): ConnectionIO[Int] = {
val sql = insertRow( val sql = insertRow(
table, table,
all, all,
@ -49,37 +49,37 @@ object RSpace {
sql.update.run sql.update.run
} }
def update(v: RSpace): ConnectionIO[Int] = def update(v: RFolder): ConnectionIO[Int] =
updateRow( updateRow(
table, table,
and(id.is(v.id), collective.is(v.collectiveId), owner.is(v.owner)), and(id.is(v.id), collective.is(v.collectiveId), owner.is(v.owner)),
name.setTo(v.name) name.setTo(v.name)
).update.run ).update.run
def existsByName(coll: Ident, spaceName: String): ConnectionIO[Boolean] = def existsByName(coll: Ident, folderName: String): ConnectionIO[Boolean] =
selectCount(id, table, and(collective.is(coll), name.is(spaceName))) selectCount(id, table, and(collective.is(coll), name.is(folderName)))
.query[Int] .query[Int]
.unique .unique
.map(_ > 0) .map(_ > 0)
def findById(spaceId: Ident): ConnectionIO[Option[RSpace]] = { def findById(folderId: Ident): ConnectionIO[Option[RFolder]] = {
val sql = selectSimple(all, table, id.is(spaceId)) val sql = selectSimple(all, table, id.is(folderId))
sql.query[RSpace].option sql.query[RFolder].option
} }
def findAll( def findAll(
coll: Ident, coll: Ident,
nameQ: Option[String], nameQ: Option[String],
order: Columns.type => Column order: Columns.type => Column
): ConnectionIO[Vector[RSpace]] = { ): ConnectionIO[Vector[RFolder]] = {
val q = Seq(collective.is(coll)) ++ (nameQ match { val q = Seq(collective.is(coll)) ++ (nameQ match {
case Some(str) => Seq(name.lowerLike(s"%${str.toLowerCase}%")) case Some(str) => Seq(name.lowerLike(s"%${str.toLowerCase}%"))
case None => Seq.empty case None => Seq.empty
}) })
val sql = selectSimple(all, table, and(q)) ++ orderBy(order(Columns).f) 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] = def delete(folderId: Ident): ConnectionIO[Int] =
deleteFrom(table, id.is(spaceId)).update.run deleteFrom(table, id.is(folderId)).update.run
} }

View File

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

View File

@ -28,7 +28,7 @@ case class RItem(
created: Timestamp, created: Timestamp,
updated: Timestamp, updated: Timestamp,
notes: Option[String], notes: Option[String],
spaceId: Option[Ident] folderId: Option[Ident]
) {} ) {}
object RItem { object RItem {
@ -82,7 +82,7 @@ object RItem {
val created = Column("created") val created = Column("created")
val updated = Column("updated") val updated = Column("updated")
val notes = Column("notes") val notes = Column("notes")
val space = Column("space_id") val folder = Column("folder_id")
val all = List( val all = List(
id, id,
cid, cid,
@ -100,7 +100,7 @@ object RItem {
created, created,
updated, updated,
notes, notes,
space folder
) )
} }
import Columns._ import Columns._
@ -111,7 +111,7 @@ object RItem {
all, all,
fr"${v.id},${v.cid},${v.name},${v.itemDate},${v.source},${v.direction},${v.state}," ++ 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.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 ).update.run
def getCollective(itemId: Ident): ConnectionIO[Option[Ident]] = def getCollective(itemId: Ident): ConnectionIO[Option[Ident]] =
@ -300,8 +300,8 @@ object RItem {
def findByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Option[RItem]] = def findByIdAndCollective(itemId: Ident, coll: Ident): ConnectionIO[Option[RItem]] =
selectSimple(all, table, and(id.is(itemId), cid.is(coll))).query[RItem].option 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 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
} }
} }

View File

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

View File

@ -6,16 +6,17 @@ module Api exposing
, addMember , addMember
, addTag , addTag
, cancelJob , cancelJob
, changeFolderName
, changePassword , changePassword
, changeSpaceName
, checkCalEvent , checkCalEvent
, createImapSettings , createImapSettings
, createMailSettings , createMailSettings
, createNewSpace , createNewFolder
, createNotifyDueItems , createNotifyDueItems
, createScanMailbox , createScanMailbox
, deleteAttachment , deleteAttachment
, deleteEquip , deleteEquip
, deleteFolder
, deleteImapSettings , deleteImapSettings
, deleteItem , deleteItem
, deleteMailSettings , deleteMailSettings
@ -24,7 +25,6 @@ module Api exposing
, deletePerson , deletePerson
, deleteScanMailbox , deleteScanMailbox
, deleteSource , deleteSource
, deleteSpace
, deleteTag , deleteTag
, deleteUser , deleteUser
, getAttachmentMeta , getAttachmentMeta
@ -32,6 +32,8 @@ module Api exposing
, getCollectiveSettings , getCollectiveSettings
, getContacts , getContacts
, getEquipments , getEquipments
, getFolderDetail
, getFolders
, getImapSettings , getImapSettings
, getInsights , getInsights
, getItemProposals , getItemProposals
@ -46,8 +48,6 @@ module Api exposing
, getScanMailbox , getScanMailbox
, getSentMails , getSentMails
, getSources , getSources
, getSpaceDetail
, getSpaces
, getTags , getTags
, getUsers , getUsers
, itemDetail , itemDetail
@ -108,6 +108,8 @@ import Api.Model.EmailSettings exposing (EmailSettings)
import Api.Model.EmailSettingsList exposing (EmailSettingsList) import Api.Model.EmailSettingsList exposing (EmailSettingsList)
import Api.Model.Equipment exposing (Equipment) import Api.Model.Equipment exposing (Equipment)
import Api.Model.EquipmentList exposing (EquipmentList) 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.GenInvite exposing (GenInvite)
import Api.Model.IdResult exposing (IdResult) import Api.Model.IdResult exposing (IdResult)
import Api.Model.ImapSettings exposing (ImapSettings) 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.ItemUploadMeta exposing (ItemUploadMeta)
import Api.Model.JobQueueState exposing (JobQueueState) import Api.Model.JobQueueState exposing (JobQueueState)
import Api.Model.MoveAttachment exposing (MoveAttachment) 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.NotificationSettings exposing (NotificationSettings)
import Api.Model.NotificationSettingsList exposing (NotificationSettingsList) import Api.Model.NotificationSettingsList exposing (NotificationSettingsList)
import Api.Model.OptionalDate exposing (OptionalDate) 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.SimpleMail exposing (SimpleMail)
import Api.Model.Source exposing (Source) import Api.Model.Source exposing (Source)
import Api.Model.SourceList exposing (SourceList) 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.Tag exposing (Tag)
import Api.Model.TagList exposing (TagList) import Api.Model.TagList exposing (TagList)
import Api.Model.User exposing (User) 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 deleteFolder : Flags -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg
deleteSpace flags id receive = deleteFolder flags id receive =
Http2.authDelete Http2.authDelete
{ url = flags.config.baseUrl ++ "/api/v1/sec/space/" ++ id { url = flags.config.baseUrl ++ "/api/v1/sec/folder/" ++ id
, account = getAccount flags , account = getAccount flags
, expect = Http.expectJson receive Api.Model.BasicResult.decoder , 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 -> String -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg
removeMember flags id user receive = removeMember flags id user receive =
Http2.authDelete 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 , account = getAccount flags
, expect = Http.expectJson receive Api.Model.BasicResult.decoder , 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 -> String -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg
addMember flags id user receive = addMember flags id user receive =
Http2.authPut 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 , account = getAccount flags
, body = Http.emptyBody , body = Http.emptyBody
, expect = Http.expectJson receive Api.Model.BasicResult.decoder , expect = Http.expectJson receive Api.Model.BasicResult.decoder
} }
changeSpaceName : Flags -> String -> NewSpace -> (Result Http.Error BasicResult -> msg) -> Cmd msg changeFolderName : Flags -> String -> NewFolder -> (Result Http.Error BasicResult -> msg) -> Cmd msg
changeSpaceName flags id ns receive = changeFolderName flags id ns receive =
Http2.authPut Http2.authPut
{ url = flags.config.baseUrl ++ "/api/v1/sec/space/" ++ id { url = flags.config.baseUrl ++ "/api/v1/sec/folder/" ++ id
, account = getAccount flags , 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 , expect = Http.expectJson receive Api.Model.BasicResult.decoder
} }
createNewSpace : Flags -> NewSpace -> (Result Http.Error IdResult -> msg) -> Cmd msg createNewFolder : Flags -> NewFolder -> (Result Http.Error IdResult -> msg) -> Cmd msg
createNewSpace flags ns receive = createNewFolder flags ns receive =
Http2.authPost Http2.authPost
{ url = flags.config.baseUrl ++ "/api/v1/sec/space" { url = flags.config.baseUrl ++ "/api/v1/sec/folder"
, account = getAccount flags , 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 , expect = Http.expectJson receive Api.Model.IdResult.decoder
} }
getSpaceDetail : Flags -> String -> (Result Http.Error SpaceDetail -> msg) -> Cmd msg getFolderDetail : Flags -> String -> (Result Http.Error FolderDetail -> msg) -> Cmd msg
getSpaceDetail flags id receive = getFolderDetail flags id receive =
Http2.authGet Http2.authGet
{ url = flags.config.baseUrl ++ "/api/v1/sec/space/" ++ id { url = flags.config.baseUrl ++ "/api/v1/sec/folder/" ++ id
, account = getAccount flags , 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 getFolders : Flags -> String -> Bool -> (Result Http.Error FolderList -> msg) -> Cmd msg
getSpaces flags query owningOnly receive = getFolders flags query owningOnly receive =
Http2.authGet Http2.authGet
{ url = { url =
flags.config.baseUrl flags.config.baseUrl
++ "/api/v1/sec/space?q=" ++ "/api/v1/sec/folder?q="
++ Url.percentEncode query ++ Url.percentEncode query
++ (if owningOnly then ++ (if owningOnly then
"&owning=true" "&owning=true"
@ -235,7 +235,7 @@ getSpaces flags query owningOnly receive =
"" ""
) )
, account = getAccount flags , account = getAccount flags
, expect = Http.expectJson receive Api.Model.SpaceList.decoder , expect = Http.expectJson receive Api.Model.FolderList.decoder
} }

View File

@ -1,4 +1,4 @@
module Comp.SpaceDetail exposing module Comp.FolderDetail exposing
( Model ( Model
, Msg , Msg
, init , init
@ -9,10 +9,10 @@ module Comp.SpaceDetail exposing
import Api import Api
import Api.Model.BasicResult exposing (BasicResult) import Api.Model.BasicResult exposing (BasicResult)
import Api.Model.FolderDetail exposing (FolderDetail)
import Api.Model.IdName exposing (IdName) import Api.Model.IdName exposing (IdName)
import Api.Model.IdResult exposing (IdResult) import Api.Model.IdResult exposing (IdResult)
import Api.Model.NewSpace exposing (NewSpace) import Api.Model.NewFolder exposing (NewFolder)
import Api.Model.SpaceDetail exposing (SpaceDetail)
import Api.Model.User exposing (User) import Api.Model.User exposing (User)
import Api.Model.UserList exposing (UserList) import Api.Model.UserList exposing (UserList)
import Comp.FixedDropdown import Comp.FixedDropdown
@ -28,7 +28,7 @@ import Util.Maybe
type alias Model = type alias Model =
{ result : Maybe BasicResult { result : Maybe BasicResult
, space : SpaceDetail , folder : FolderDetail
, name : Maybe String , name : Maybe String
, members : List IdName , members : List IdName
, users : List User , users : List User
@ -43,10 +43,10 @@ type Msg
= SetName String = SetName String
| MemberDropdownMsg (Comp.FixedDropdown.Msg IdName) | MemberDropdownMsg (Comp.FixedDropdown.Msg IdName)
| SaveName | SaveName
| NewSpaceResp (Result Http.Error IdResult) | NewFolderResp (Result Http.Error IdResult)
| ChangeSpaceResp (Result Http.Error BasicResult) | ChangeFolderResp (Result Http.Error BasicResult)
| ChangeNameResp (Result Http.Error BasicResult) | ChangeNameResp (Result Http.Error BasicResult)
| SpaceDetailResp (Result Http.Error SpaceDetail) | FolderDetailResp (Result Http.Error FolderDetail)
| AddMember | AddMember
| RemoveMember IdName | RemoveMember IdName
| RequestDelete | RequestDelete
@ -55,16 +55,16 @@ type Msg
| GoBack | GoBack
init : List User -> SpaceDetail -> Model init : List User -> FolderDetail -> Model
init users space = init users folder =
{ result = Nothing { result = Nothing
, space = space , folder = folder
, name = Util.Maybe.fromString space.name , name = Util.Maybe.fromString folder.name
, members = space.members , members = folder.members
, users = users , users = users
, memberDropdown = , memberDropdown =
Comp.FixedDropdown.initMap .name Comp.FixedDropdown.initMap .name
(makeOptions users space) (makeOptions users folder)
, selectedMember = Nothing , selectedMember = Nothing
, loading = False , loading = False
, deleteDimmer = Comp.YesNoDimmer.emptyModel , deleteDimmer = Comp.YesNoDimmer.emptyModel
@ -73,17 +73,17 @@ init users space =
initEmpty : List User -> Model initEmpty : List User -> Model
initEmpty users = initEmpty users =
init users Api.Model.SpaceDetail.empty init users Api.Model.FolderDetail.empty
makeOptions : List User -> SpaceDetail -> List IdName makeOptions : List User -> FolderDetail -> List IdName
makeOptions users space = makeOptions users folder =
let let
toIdName u = toIdName u =
IdName u.id u.login IdName u.id u.login
notMember idn = notMember idn =
List.member idn (space.owner :: space.members) |> not List.member idn (folder.owner :: folder.members) |> not
in in
List.map toIdName users List.map toIdName users
|> List.filter notMember |> List.filter notMember
@ -129,13 +129,13 @@ update flags msg model =
Just name -> Just name ->
let let
cmd = cmd =
if model.space.id == "" then if model.folder.id == "" then
Api.createNewSpace flags (NewSpace name) NewSpaceResp Api.createNewFolder flags (NewFolder name) NewFolderResp
else else
Api.changeSpaceName flags Api.changeFolderName flags
model.space.id model.folder.id
(NewSpace name) (NewFolder name)
ChangeNameResp ChangeNameResp
in in
( { model ( { model
@ -149,9 +149,9 @@ update flags msg model =
Nothing -> Nothing ->
( model, Cmd.none, False ) ( model, Cmd.none, False )
NewSpaceResp (Ok ir) -> NewFolderResp (Ok ir) ->
if ir.success then if ir.success then
( model, Api.getSpaceDetail flags ir.id SpaceDetailResp, False ) ( model, Api.getFolderDetail flags ir.id FolderDetailResp, False )
else else
( { model ( { model
@ -162,7 +162,7 @@ update flags msg model =
, False , False
) )
NewSpaceResp (Err err) -> NewFolderResp (Err err) ->
( { model ( { model
| loading = False | loading = False
, result = Just (BasicResult False (Util.Http.errorToString err)) , result = Just (BasicResult False (Util.Http.errorToString err))
@ -171,10 +171,10 @@ update flags msg model =
, False , False
) )
ChangeSpaceResp (Ok r) -> ChangeFolderResp (Ok r) ->
if r.success then if r.success then
( model ( model
, Api.getSpaceDetail flags model.space.id SpaceDetailResp , Api.getFolderDetail flags model.folder.id FolderDetailResp
, False , False
) )
@ -184,7 +184,7 @@ update flags msg model =
, False , False
) )
ChangeSpaceResp (Err err) -> ChangeFolderResp (Err err) ->
( { model ( { model
| loading = False | loading = False
, result = Just (BasicResult False (Util.Http.errorToString err)) , result = Just (BasicResult False (Util.Http.errorToString err))
@ -209,10 +209,10 @@ update flags msg model =
, False , False
) )
SpaceDetailResp (Ok sd) -> FolderDetailResp (Ok sd) ->
( init model.users sd, Cmd.none, False ) ( init model.users sd, Cmd.none, False )
SpaceDetailResp (Err err) -> FolderDetailResp (Err err) ->
( { model ( { model
| loading = False | loading = False
, result = Just (BasicResult False (Util.Http.errorToString err)) , result = Just (BasicResult False (Util.Http.errorToString err))
@ -225,7 +225,7 @@ update flags msg model =
case model.selectedMember of case model.selectedMember of
Just mem -> Just mem ->
( { model | loading = True } ( { model | loading = True }
, Api.addMember flags model.space.id mem.id ChangeSpaceResp , Api.addMember flags model.folder.id mem.id ChangeFolderResp
, False , False
) )
@ -234,7 +234,7 @@ update flags msg model =
RemoveMember idname -> RemoveMember idname ->
( { model | loading = True } ( { model | loading = True }
, Api.removeMember flags model.space.id idname.id ChangeSpaceResp , Api.removeMember flags model.folder.id idname.id ChangeFolderResp
, False , False
) )
@ -252,7 +252,7 @@ update flags msg model =
cmd = cmd =
if flag then if flag then
Api.deleteSpace flags model.space.id DeleteResp Api.deleteFolder flags model.folder.id DeleteResp
else else
Cmd.none Cmd.none
@ -278,23 +278,23 @@ view flags model =
let let
isOwner = isOwner =
Maybe.map .user flags.account Maybe.map .user flags.account
|> Maybe.map ((==) model.space.owner.name) |> Maybe.map ((==) model.folder.owner.name)
|> Maybe.withDefault False |> Maybe.withDefault False
in in
div [] div []
([ Html.map DeleteMsg (Comp.YesNoDimmer.view model.deleteDimmer) ([ Html.map DeleteMsg (Comp.YesNoDimmer.view model.deleteDimmer)
, if model.space.id == "" then , if model.folder.id == "" then
div [] 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 else
div [] 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" ] 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 else
@ -315,7 +315,7 @@ view flags model =
[ text "Owner" [ text "Owner"
] ]
, div [ class "" ] , div [ class "" ]
[ text model.space.owner.name [ text model.folder.owner.name
] ]
, div [ class "ui header" ] , div [ class "ui header" ]
[ text "Name" [ text "Name"
@ -361,7 +361,7 @@ viewButtons _ =
viewMembers : Model -> List (Html Msg) viewMembers : Model -> List (Html Msg)
viewMembers model = viewMembers model =
if model.space.id == "" then if model.folder.id == "" then
[] []
else else

View File

@ -1,4 +1,4 @@
module Comp.SpaceManage exposing module Comp.FolderManage exposing
( Model ( Model
, Msg , Msg
, empty , empty
@ -8,13 +8,13 @@ module Comp.SpaceManage exposing
) )
import Api import Api
import Api.Model.SpaceDetail exposing (SpaceDetail) import Api.Model.FolderDetail exposing (FolderDetail)
import Api.Model.SpaceItem exposing (SpaceItem) import Api.Model.FolderItem exposing (FolderItem)
import Api.Model.SpaceList exposing (SpaceList) import Api.Model.FolderList exposing (FolderList)
import Api.Model.User exposing (User) import Api.Model.User exposing (User)
import Api.Model.UserList exposing (UserList) import Api.Model.UserList exposing (UserList)
import Comp.SpaceDetail import Comp.FolderDetail
import Comp.SpaceTable import Comp.FolderTable
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
@ -23,9 +23,9 @@ import Http
type alias Model = type alias Model =
{ tableModel : Comp.SpaceTable.Model { tableModel : Comp.FolderTable.Model
, detailModel : Maybe Comp.SpaceDetail.Model , detailModel : Maybe Comp.FolderDetail.Model
, spaces : List SpaceItem , folders : List FolderItem
, users : List User , users : List User
, query : String , query : String
, owningOnly : Bool , owningOnly : Bool
@ -34,21 +34,21 @@ type alias Model =
type Msg type Msg
= TableMsg Comp.SpaceTable.Msg = TableMsg Comp.FolderTable.Msg
| DetailMsg Comp.SpaceDetail.Msg | DetailMsg Comp.FolderDetail.Msg
| UserListResp (Result Http.Error UserList) | UserListResp (Result Http.Error UserList)
| SpaceListResp (Result Http.Error SpaceList) | FolderListResp (Result Http.Error FolderList)
| SpaceDetailResp (Result Http.Error SpaceDetail) | FolderDetailResp (Result Http.Error FolderDetail)
| SetQuery String | SetQuery String
| InitNewSpace | InitNewFolder
| ToggleOwningOnly | ToggleOwningOnly
empty : Model empty : Model
empty = empty =
{ tableModel = Comp.SpaceTable.init { tableModel = Comp.FolderTable.init
, detailModel = Nothing , detailModel = Nothing
, spaces = [] , folders = []
, users = [] , users = []
, query = "" , query = ""
, owningOnly = True , owningOnly = True
@ -61,7 +61,7 @@ init flags =
( empty ( empty
, Cmd.batch , Cmd.batch
[ Api.getUsers flags UserListResp [ 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 -> TableMsg lm ->
let let
( tm, action ) = ( tm, action ) =
Comp.SpaceTable.update lm model.tableModel Comp.FolderTable.update lm model.tableModel
cmd = cmd =
case action of case action of
Comp.SpaceTable.EditAction item -> Comp.FolderTable.EditAction item ->
Api.getSpaceDetail flags item.id SpaceDetailResp Api.getFolderDetail flags item.id FolderDetailResp
Comp.SpaceTable.NoAction -> Comp.FolderTable.NoAction ->
Cmd.none Cmd.none
in in
( { model | tableModel = tm }, cmd ) ( { model | tableModel = tm }, cmd )
@ -93,11 +93,11 @@ update flags msg model =
Just detail -> Just detail ->
let let
( dm, dc, back ) = ( dm, dc, back ) =
Comp.SpaceDetail.update flags lm detail Comp.FolderDetail.update flags lm detail
cmd = cmd =
if back then if back then
Api.getSpaces flags model.query model.owningOnly SpaceListResp Api.getFolders flags model.query model.owningOnly FolderListResp
else else
Cmd.none Cmd.none
@ -121,7 +121,7 @@ update flags msg model =
SetQuery str -> SetQuery str ->
( { model | query = str } ( { model | query = str }
, Api.getSpaces flags str model.owningOnly SpaceListResp , Api.getFolders flags str model.owningOnly FolderListResp
) )
ToggleOwningOnly -> ToggleOwningOnly ->
@ -130,7 +130,7 @@ update flags msg model =
not model.owningOnly not model.owningOnly
in in
( { model | owningOnly = newOwning } ( { model | owningOnly = newOwning }
, Api.getSpaces flags model.query newOwning SpaceListResp , Api.getFolders flags model.query newOwning FolderListResp
) )
UserListResp (Ok ul) -> UserListResp (Ok ul) ->
@ -139,24 +139,24 @@ update flags msg model =
UserListResp (Err err) -> UserListResp (Err err) ->
( model, Cmd.none ) ( model, Cmd.none )
SpaceListResp (Ok sl) -> FolderListResp (Ok sl) ->
( { model | spaces = sl.items }, Cmd.none ) ( { model | folders = sl.items }, Cmd.none )
SpaceListResp (Err err) -> FolderListResp (Err err) ->
( model, Cmd.none ) ( model, Cmd.none )
SpaceDetailResp (Ok sd) -> FolderDetailResp (Ok sd) ->
( { model | detailModel = Comp.SpaceDetail.init model.users sd |> Just } ( { model | detailModel = Comp.FolderDetail.init model.users sd |> Just }
, Cmd.none , Cmd.none
) )
SpaceDetailResp (Err err) -> FolderDetailResp (Err err) ->
( model, Cmd.none ) ( model, Cmd.none )
InitNewSpace -> InitNewFolder ->
let let
sd = sd =
Comp.SpaceDetail.initEmpty model.users Comp.FolderDetail.initEmpty model.users
in in
( { model | detailModel = Just sd } ( { model | detailModel = Just sd }
, Cmd.none , Cmd.none
@ -177,10 +177,10 @@ view flags model =
viewTable model viewTable model
viewDetail : Flags -> Comp.SpaceDetail.Model -> Html Msg viewDetail : Flags -> Comp.FolderDetail.Model -> Html Msg
viewDetail flags detailModel = viewDetail flags detailModel =
div [] 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 , checked model.owningOnly
] ]
[] []
, label [] [ text "Show owning spaces only" ] , label [] [ text "Show owning folders only" ]
] ]
] ]
, div [ class "right menu" ] , div [ class "right menu" ]
@ -217,15 +217,15 @@ viewTable model =
[ a [ a
[ class "ui primary button" [ class "ui primary button"
, href "#" , href "#"
, onClick InitNewSpace , onClick InitNewFolder
] ]
[ i [ class "plus icon" ] [] [ 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 , div
[ classList [ classList
[ ( "ui dimmer", True ) [ ( "ui dimmer", True )

View File

@ -1,4 +1,4 @@
module Comp.SpaceTable exposing module Comp.FolderTable exposing
( Action(..) ( Action(..)
, Model , Model
, Msg , Msg
@ -7,7 +7,7 @@ module Comp.SpaceTable exposing
, view , view
) )
import Api.Model.SpaceItem exposing (SpaceItem) import Api.Model.FolderItem exposing (FolderItem)
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Html.Events exposing (onClick) import Html.Events exposing (onClick)
@ -20,12 +20,12 @@ type alias Model =
type Msg type Msg
= EditItem SpaceItem = EditItem FolderItem
type Action type Action
= NoAction = NoAction
| EditAction SpaceItem | EditAction FolderItem
init : Model init : Model
@ -40,7 +40,7 @@ update msg model =
( model, EditAction item ) ( model, EditAction item )
view : Model -> List SpaceItem -> Html Msg view : Model -> List FolderItem -> Html Msg
view _ items = view _ items =
div [] div []
[ table [ class "ui very basic center aligned table" ] [ table [ class "ui very basic center aligned table" ]
@ -58,7 +58,7 @@ view _ items =
] ]
viewItem : SpaceItem -> Html Msg viewItem : FolderItem -> Html Msg
viewItem item = viewItem item =
tr [] tr []
[ td [ class "collapsing" ] [ td [ class "collapsing" ]

View File

@ -15,12 +15,12 @@ module Data.Icons exposing
, editNotesIcon , editNotesIcon
, equipment , equipment
, equipmentIcon , equipmentIcon
, folder
, folderIcon
, organization , organization
, organizationIcon , organizationIcon
, person , person
, personIcon , personIcon
, space
, spaceIcon
, tag , tag
, tagIcon , tagIcon
, tags , tags
@ -31,14 +31,14 @@ import Html exposing (Html, i)
import Html.Attributes exposing (class) import Html.Attributes exposing (class)
space : String folder : String
space = folder =
"folder outline icon" "folder outline icon"
spaceIcon : String -> Html msg folderIcon : String -> Html msg
spaceIcon classes = folderIcon classes =
i [ class (space ++ " " ++ classes) ] [] i [ class (folder ++ " " ++ classes) ] []
concerned : String concerned : String

View File

@ -6,9 +6,9 @@ module Page.ManageData.Data exposing
) )
import Comp.EquipmentManage import Comp.EquipmentManage
import Comp.FolderManage
import Comp.OrgManage import Comp.OrgManage
import Comp.PersonManage import Comp.PersonManage
import Comp.SpaceManage
import Comp.TagManage import Comp.TagManage
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
@ -19,7 +19,7 @@ type alias Model =
, equipManageModel : Comp.EquipmentManage.Model , equipManageModel : Comp.EquipmentManage.Model
, orgManageModel : Comp.OrgManage.Model , orgManageModel : Comp.OrgManage.Model
, personManageModel : Comp.PersonManage.Model , personManageModel : Comp.PersonManage.Model
, spaceManageModel : Comp.SpaceManage.Model , folderManageModel : Comp.FolderManage.Model
} }
@ -30,7 +30,7 @@ init _ =
, equipManageModel = Comp.EquipmentManage.emptyModel , equipManageModel = Comp.EquipmentManage.emptyModel
, orgManageModel = Comp.OrgManage.emptyModel , orgManageModel = Comp.OrgManage.emptyModel
, personManageModel = Comp.PersonManage.emptyModel , personManageModel = Comp.PersonManage.emptyModel
, spaceManageModel = Comp.SpaceManage.empty , folderManageModel = Comp.FolderManage.empty
} }
, Cmd.none , Cmd.none
) )
@ -41,7 +41,7 @@ type Tab
| EquipTab | EquipTab
| OrgTab | OrgTab
| PersonTab | PersonTab
| SpaceTab | FolderTab
type Msg type Msg
@ -50,4 +50,4 @@ type Msg
| EquipManageMsg Comp.EquipmentManage.Msg | EquipManageMsg Comp.EquipmentManage.Msg
| OrgManageMsg Comp.OrgManage.Msg | OrgManageMsg Comp.OrgManage.Msg
| PersonManageMsg Comp.PersonManage.Msg | PersonManageMsg Comp.PersonManage.Msg
| SpaceMsg Comp.SpaceManage.Msg | FolderMsg Comp.FolderManage.Msg

View File

@ -1,9 +1,9 @@
module Page.ManageData.Update exposing (update) module Page.ManageData.Update exposing (update)
import Comp.EquipmentManage import Comp.EquipmentManage
import Comp.FolderManage
import Comp.OrgManage import Comp.OrgManage
import Comp.PersonManage import Comp.PersonManage
import Comp.SpaceManage
import Comp.TagManage import Comp.TagManage
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Page.ManageData.Data exposing (..) import Page.ManageData.Data exposing (..)
@ -30,12 +30,12 @@ update flags msg model =
PersonTab -> PersonTab ->
update flags (PersonManageMsg Comp.PersonManage.LoadPersons) m update flags (PersonManageMsg Comp.PersonManage.LoadPersons) m
SpaceTab -> FolderTab ->
let let
( sm, sc ) = ( sm, sc ) =
Comp.SpaceManage.init flags Comp.FolderManage.init flags
in in
( { m | spaceManageModel = sm }, Cmd.map SpaceMsg sc ) ( { m | folderManageModel = sm }, Cmd.map FolderMsg sc )
TagManageMsg m -> TagManageMsg m ->
let let
@ -65,11 +65,11 @@ update flags msg model =
in in
( { model | personManageModel = m2 }, Cmd.map PersonManageMsg c2 ) ( { model | personManageModel = m2 }, Cmd.map PersonManageMsg c2 )
SpaceMsg lm -> FolderMsg lm ->
let let
( m2, c2 ) = ( m2, c2 ) =
Comp.SpaceManage.update flags lm model.spaceManageModel Comp.FolderManage.update flags lm model.folderManageModel
in in
( { model | spaceManageModel = m2 } ( { model | folderManageModel = m2 }
, Cmd.map SpaceMsg c2 , Cmd.map FolderMsg c2
) )

View File

@ -1,9 +1,9 @@
module Page.ManageData.View exposing (view) module Page.ManageData.View exposing (view)
import Comp.EquipmentManage import Comp.EquipmentManage
import Comp.FolderManage
import Comp.OrgManage import Comp.OrgManage
import Comp.PersonManage import Comp.PersonManage
import Comp.SpaceManage
import Comp.TagManage import Comp.TagManage
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Data.Icons as Icons import Data.Icons as Icons
@ -53,11 +53,11 @@ view flags settings model =
, text "Person" , text "Person"
] ]
, div , div
[ classActive (model.currentTab == Just SpaceTab) "link icon item" [ classActive (model.currentTab == Just FolderTab) "link icon item"
, onClick (SetTab SpaceTab) , onClick (SetTab FolderTab)
] ]
[ Icons.spaceIcon "" [ Icons.folderIcon ""
, text "Space" , text "Folder"
] ]
] ]
] ]
@ -77,8 +77,8 @@ view flags settings model =
Just PersonTab -> Just PersonTab ->
viewPerson settings model viewPerson settings model
Just SpaceTab -> Just FolderTab ->
viewSpace flags settings model viewFolder flags settings model
Nothing -> Nothing ->
[] []
@ -87,19 +87,19 @@ view flags settings model =
] ]
viewSpace : Flags -> UiSettings -> Model -> List (Html Msg) viewFolder : Flags -> UiSettings -> Model -> List (Html Msg)
viewSpace flags _ model = viewFolder flags _ model =
[ h2 [ h2
[ class "ui header" [ class "ui header"
] ]
[ Icons.spaceIcon "" [ Icons.folderIcon ""
, div , div
[ class "content" [ class "content"
] ]
[ text "Spaces" [ text "Folders"
] ]
] ]
, Html.map SpaceMsg (Comp.SpaceManage.view flags model.spaceManageModel) , Html.map FolderMsg (Comp.FolderManage.view flags model.folderManageModel)
] ]