1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-08-08 06:55:28 +02:00

major code repair from old to new API -- WIP

This commit is contained in:
Harvey Kandola 2017-07-24 16:24:21 +01:00
parent 25b576f861
commit 792c3e2ce8
46 changed files with 3403 additions and 171 deletions

View file

@ -12,3 +12,757 @@
// Package space handles API calls and persistence for spaces.
// Spaces in Documize contain documents.
package space
import (
"database/sql"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"github.com/documize/api/wordsmith/log"
"github.com/documize/community/core/api/mail"
"github.com/documize/community/core/request"
"github.com/documize/community/core/response"
"github.com/documize/community/core/secrets"
"github.com/documize/community/core/streamutil"
"github.com/documize/community/core/stringutil"
"github.com/documize/community/core/uniqueid"
"github.com/documize/community/domain"
"github.com/documize/community/domain/account"
"github.com/documize/community/domain/document"
"github.com/documize/community/domain/eventing"
"github.com/documize/community/domain/organization"
"github.com/documize/community/domain/pin"
"github.com/documize/community/domain/user"
)
// Add creates a new space.
func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
method := "AddSpace"
ctx, s := domain.NewContexts(h.Runtime, r)
if !h.Runtime.Product.License.IsValid() {
response.WriteBadLicense(w)
return
}
if !ctx.Editor {
response.WriteForbiddenError(w)
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
return
}
var space = Space{}
err = json.Unmarshal(body, &space)
if err != nil {
response.WriteServerError(w, method, err)
return
}
if len(space.Name) == 0 {
response.WriteMissingDataError(w, method, "name")
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
space.RefID = uniqueid.Generate()
space.OrgID = ctx.OrgID
err = addSpace(s, space)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
eventing.Record(s, eventing.EventTypeSpaceAdd)
ctx.Transaction.Commit()
space, _ = Get(s, space.RefID)
response.WriteJSON(w, space)
}
// Get returns the requested space.
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
method := "Get"
_, s := domain.NewContexts(h.Runtime, r)
id := request.Param(r, "folderID")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "folderID")
return
}
sp, err := Get(s, id)
if err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, id)
return
}
if err != nil {
response.WriteServerError(w, method, err)
return
}
response.WriteJSON(w, sp)
}
// GetAll returns spaces the user can see.
func (h *Handler) GetAll(w http.ResponseWriter, r *http.Request) {
method := "GetAll"
_, s := domain.NewContexts(h.Runtime, r)
sp, err := GetAll(s)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
return
}
if len(sp) == 0 {
sp = []Space{}
}
response.WriteJSON(w, sp)
}
// GetSpaceViewers returns the users that can see the shared spaces.
func (h *Handler) GetSpaceViewers(w http.ResponseWriter, r *http.Request) {
method := "GetSpaceViewers"
_, s := domain.NewContexts(h.Runtime, r)
v, err := Viewers(s)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
return
}
if len(v) == 0 {
v = []Viewer{}
}
response.WriteJSON(w, v)
}
// Update processes request to save space object to the database
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
method := "space.Update"
ctx, s := domain.NewContexts(h.Runtime, r)
if !ctx.Editor {
response.WriteForbiddenError(w)
return
}
folderID := request.Param(r, "folderID")
if len(folderID) == 0 {
response.WriteMissingDataError(w, method, "folderID")
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
return
}
var sp Space
err = json.Unmarshal(body, &sp)
if err != nil {
response.WriteBadRequestError(w, method, "marshal")
return
}
if len(sp.Name) == 0 {
response.WriteMissingDataError(w, method, "name")
return
}
sp.RefID = folderID
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
err = Update(s, sp)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
eventing.Record(s, eventing.EventTypeSpaceUpdate)
ctx.Transaction.Commit()
response.WriteJSON(w, sp)
}
// Remove moves documents to another folder before deleting it
func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) {
method := "space.Remove"
ctx, s := domain.NewContexts(h.Runtime, r)
if !h.Runtime.Product.License.IsValid() {
response.WriteBadLicense(w)
return
}
if !ctx.Editor {
response.WriteForbiddenError(w)
return
}
id := request.Param(r, "folderID")
move := request.Param(r, "moveToId")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "folderID")
return
}
if len(move) == 0 {
response.WriteMissingDataError(w, method, "moveToId")
return
}
var err error
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
_, err = Delete(s, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
err = document.MoveDocumentSpace(s, id, move)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
err = MoveSpaceRoles(s, id, move)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
_, err = pin.DeletePinnedSpace(s, id)
if err != nil && err != sql.ErrNoRows {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
eventing.Record(s, eventing.EventTypeSpaceDelete)
ctx.Transaction.Commit()
response.WriteEmpty(w)
}
// Delete deletes empty space.
func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
method := "space.Delete"
ctx, s := domain.NewContexts(h.Runtime, r)
if !h.Runtime.Product.License.IsValid() {
response.WriteBadLicense(w)
return
}
if !ctx.Editor {
response.WriteForbiddenError(w)
return
}
id := request.Param(r, "folderID")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "folderID")
return
}
var err error
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
_, err = Delete(s, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
_, err = DeleteSpaceRoles(s, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
_, err = pin.DeletePinnedSpace(s, id)
if err != nil && err != sql.ErrNoRows {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
eventing.Record(s, eventing.EventTypeSpaceDelete)
ctx.Transaction.Commit()
response.WriteEmpty(w)
}
// SetPermissions persists specified spac3 permissions
func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
method := "space.SetPermissions"
ctx, s := domain.NewContexts(h.Runtime, r)
if !ctx.Editor {
response.WriteForbiddenError(w)
return
}
id := request.Param(r, "folderID")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "folderID")
return
}
sp, err := Get(s, id)
if err != nil {
response.WriteNotFoundError(w, method, "No such space")
return
}
if sp.UserID != s.Context.UserID {
response.WriteForbiddenError(w)
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
return
}
var model = RolesModel{}
err = json.Unmarshal(body, &model)
if err != nil {
response.WriteServerError(w, method, err)
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
// We compare new permisions to what we had before.
// Why? So we can send out folder invitation emails.
previousRoles, err := GetRoles(s, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
// Store all previous roles as map for easy querying
previousRoleUsers := make(map[string]bool)
for _, v := range previousRoles {
previousRoleUsers[v.UserID] = true
}
// Who is sharing this folder?
inviter, err := user.Get(s, s.Context.UserID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
// Nuke all previous permissions for this folder
_, err = DeleteSpaceRoles(s, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
me := false
hasEveryoneRole := false
roleCount := 0
url := s.Context.GetAppURL(fmt.Sprintf("s/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
for _, role := range model.Roles {
role.OrgID = s.Context.OrgID
role.LabelID = id
// Ensure the folder owner always has access!
if role.UserID == s.Context.UserID {
me = true
role.CanView = true
role.CanEdit = true
}
if len(role.UserID) == 0 && (role.CanView || role.CanEdit) {
hasEveryoneRole = true
}
// Only persist if there is a role!
if role.CanView || role.CanEdit {
roleID := uniqueid.Generate()
role.RefID = roleID
err = AddRole(s, role)
roleCount++
log.IfErr(err)
// We send out folder invitation emails to those users
// that have *just* been given permissions.
if _, isExisting := previousRoleUsers[role.UserID]; !isExisting {
// we skip 'everyone' (user id != empty string)
if len(role.UserID) > 0 {
var existingUser user.User
existingUser, err = user.Get(s, role.UserID)
if err == nil {
go mail.ShareFolderExistingUser(existingUser.Email, inviter.Fullname(), url, sp.Name, model.Message)
h.Runtime.Log.Info(fmt.Sprintf("%s is sharing space %s with existing user %s", inviter.Email, sp.Name, existingUser.Email))
} else {
response.WriteServerError(w, method, err)
}
}
}
}
}
// Do we need to ensure permissions for space owner when shared?
if !me {
role := Role{}
role.LabelID = id
role.OrgID = s.Context.OrgID
role.UserID = s.Context.UserID
role.CanEdit = true
role.CanView = true
roleID := uniqueid.Generate()
role.RefID = roleID
err = AddRole(s, role)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
}
// Mark up folder type as either public, private or restricted access.
if hasEveryoneRole {
sp.Type = ScopePublic
} else {
if roleCount > 1 {
sp.Type = ScopeRestricted
} else {
sp.Type = ScopePrivate
}
}
err = Update(s, sp)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
eventing.Record(s, eventing.EventTypeSpacePermission)
ctx.Transaction.Commit()
response.WriteEmpty(w)
}
// GetPermissions returns user permissions for the requested folder.
func (h *Handler) GetPermissions(w http.ResponseWriter, r *http.Request) {
method := "space.GetPermissions"
_, s := domain.NewContexts(h.Runtime, r)
folderID := request.Param(r, "folderID")
if len(folderID) == 0 {
response.WriteMissingDataError(w, method, "folderID")
return
}
roles, err := GetRoles(s, folderID)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
return
}
if len(roles) == 0 {
roles = []Role{}
}
response.WriteJSON(w, roles)
}
// AcceptInvitation records the fact that a user has completed space onboard process.
func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
method := "space.AcceptInvitation"
ctx, s := domain.NewContexts(h.Runtime, r)
folderID := request.Param(r, "folderID")
if len(folderID) == 0 {
response.WriteMissingDataError(w, method, "folderID")
return
}
org, err := organization.GetOrganizationByDomain(s, ctx.Subdomain)
if err != nil {
response.WriteServerError(w, method, err)
return
}
// AcceptShare does not authenticate the user hence the context needs to set up
ctx.OrgID = org.RefID
s.Context.OrgID = org.RefID
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
return
}
var model = AcceptShareModel{}
err = json.Unmarshal(body, &model)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
return
}
if len(model.Serial) == 0 || len(model.Firstname) == 0 || len(model.Lastname) == 0 || len(model.Password) == 0 {
response.WriteMissingDataError(w, method, "Serial, Firstname, Lastname, Password")
return
}
u, err := user.GetBySerial(s, model.Serial)
if err != nil && err == sql.ErrNoRows {
response.WriteDuplicateError(w, method, "user")
return
}
// AcceptShare does not authenticate the user hence the context needs to set up
ctx.UserID = u.RefID
s.Context.UserID = u.RefID
u.Firstname = model.Firstname
u.Lastname = model.Lastname
u.Initials = stringutil.MakeInitials(u.Firstname, u.Lastname)
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
err = user.UpdateUser(s, u)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
salt := secrets.GenerateSalt()
err = user.UpdateUserPassword(s, u.RefID, salt, secrets.GeneratePassword(model.Password, salt))
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
eventing.Record(s, eventing.EventTypeSpaceJoin)
ctx.Transaction.Commit()
response.WriteJSON(w, u)
}
// Invite sends users folder invitation emails.
func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
method := "space.Invite"
ctx, s := domain.NewContexts(h.Runtime, r)
id := request.Param(r, "folderID")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "folderID")
return
}
sp, err := Get(s, id)
if err != nil {
response.WriteServerError(w, method, err)
return
}
if sp.UserID != s.Context.UserID {
response.WriteForbiddenError(w)
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, "body")
return
}
var model = InvitationModel{}
err = json.Unmarshal(body, &model)
if err != nil {
response.WriteBadRequestError(w, method, "json")
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
inviter, err := user.Get(s, ctx.UserID)
if err != nil {
response.WriteServerError(w, method, err)
return
}
for _, email := range model.Recipients {
u, err := user.GetByEmail(s, email)
if err != nil && err != sql.ErrNoRows {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
if len(u.RefID) > 0 {
// Ensure they have access to this organization
accounts, err2 := account.GetUserAccounts(s, u.RefID)
if err2 != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
// we create if they c
hasAccess := false
for _, a := range accounts {
if a.OrgID == s.Context.OrgID {
hasAccess = true
}
}
if !hasAccess {
var a account.Account
a.UserID = u.RefID
a.OrgID = s.Context.OrgID
a.Admin = false
a.Editor = false
a.Active = true
accountID := uniqueid.Generate()
a.RefID = accountID
err = account.Add(s, a)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
}
// Ensure they have space roles
DeleteUserSpaceRoles(s, sp.RefID, u.RefID)
role := Role{}
role.LabelID = sp.RefID
role.OrgID = ctx.OrgID
role.UserID = u.RefID
role.CanEdit = false
role.CanView = true
roleID := uniqueid.Generate()
role.RefID = roleID
err = AddRole(s, role)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
url := ctx.GetAppURL(fmt.Sprintf("s/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
go mail.ShareFolderExistingUser(email, inviter.Fullname(), url, sp.Name, model.Message)
h.Runtime.Log.Info(fmt.Sprintf("%s is sharing space %s with existing user %s", inviter.Email, sp.Name, email))
} else {
// On-board new user
if strings.Contains(email, "@") {
url := ctx.GetAppURL(fmt.Sprintf("auth/share/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
err = inviteNewUserToSharedSpace(s, email, inviter, url, sp, model.Message)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
h.Runtime.Log.Info(fmt.Sprintf("%s is sharing space %s with new user %s", inviter.Email, sp.Name, email))
}
}
}
// We ensure that the folder is marked as restricted as a minimum!
if len(model.Recipients) > 0 && sp.Type == ScopePrivate {
sp.Type = ScopeRestricted
err = Update(s, sp)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
}
eventing.Record(s, eventing.EventTypeSpaceInvite)
ctx.Transaction.Commit()
response.WriteEmpty(w)
}

View file

@ -12,3 +12,92 @@
// Package space handles API calls and persistence for spaces.
// Spaces in Documize contain documents.
package space
import (
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
)
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime env.Runtime
}
// Space defines a container for documents.
type Space struct {
domain.BaseEntity
Name string `json:"name"`
OrgID string `json:"orgId"`
UserID string `json:"userId"`
Type Scope `json:"folderType"`
}
// Scope determines folder visibility.
type Scope int
const (
// ScopePublic can be seen by anyone
ScopePublic Scope = 1
// ScopePrivate can only be seen by the person who owns it
ScopePrivate Scope = 2
// ScopeRestricted can be seen by selected users
ScopeRestricted Scope = 3
)
// IsPublic means the folder can be seen by anyone.
func (l *Space) IsPublic() bool {
return l.Type == ScopePublic
}
// IsPrivate means the folder can only be seen by the person who owns it.
func (l *Space) IsPrivate() bool {
return l.Type == ScopePrivate
}
// IsRestricted means the folder can be seen by selected users.
func (l *Space) IsRestricted() bool {
return l.Type == ScopeRestricted
}
// Role determines user permissions for a folder.
type Role struct {
domain.BaseEntityObfuscated
OrgID string `json:"-"`
LabelID string `json:"folderId"`
UserID string `json:"userId"`
CanView bool `json:"canView"`
CanEdit bool `json:"canEdit"`
}
// Viewer details who can see a particular space
type Viewer struct {
Name string `json:"name"`
LabelID string `json:"folderId"`
Type int `json:"folderType"`
UserID string `json:"userId"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
Email string `json:"email"`
}
// RolesModel details which users have what permissions on a given space.
type RolesModel struct {
Message string
Roles []Role
}
// AcceptShareModel is used to setup a user who has accepted a shared space.
type AcceptShareModel struct {
Serial string `json:"serial"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
Password string `json:"password"`
}
// InvitationModel details which users have been invited to a space.
type InvitationModel struct {
Message string
Recipients []string
}

View file

@ -12,3 +12,51 @@
// Package space handles API calls and persistence for spaces.
// Spaces in Documize contain documents.
package space
import (
"database/sql"
"fmt"
"github.com/documize/community/domain"
)
// CanViewSpace returns if the user has permission to view the given spaceID.
func CanViewSpace(s domain.StoreContext, spaceID string) (hasPermission bool) {
roles, err := GetRoles(s, spaceID)
if err == sql.ErrNoRows {
err = nil
}
if err != nil {
s.Runtime.Log.Error(fmt.Sprintf("check space permissions %s", spaceID), err)
return false
}
for _, role := range roles {
if role.LabelID == spaceID && (role.CanView || role.CanEdit) {
return true
}
}
return false
}
// CanViewSpaceDocuments returns if the user has permission to view a document within the specified space.
func CanViewSpaceDocuments(s domain.StoreContext, spaceID string) (hasPermission bool) {
roles, err := GetRoles(s, spaceID)
if err == sql.ErrNoRows {
err = nil
}
if err != nil {
s.Runtime.Log.Error(fmt.Sprintf("check space permissions %s", spaceID), err)
return false
}
for _, role := range roles {
if role.LabelID == spaceID && (role.CanView || role.CanEdit) {
return true
}
}
return false
}

103
domain/space/space.go Normal file
View file

@ -0,0 +1,103 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package space
import (
"fmt"
"github.com/documize/community/core/api/mail"
"github.com/documize/community/core/secrets"
"github.com/documize/community/core/uniqueid"
"github.com/documize/community/domain"
"github.com/documize/community/domain/account"
"github.com/documize/community/domain/user"
)
// addSpace prepares and creates space record.
func addSpace(s domain.StoreContext, sp Space) (err error) {
sp.Type = ScopePrivate
sp.UserID = s.Context.UserID
err = Add(s, sp)
if err != nil {
return
}
role := Role{}
role.LabelID = sp.RefID
role.OrgID = sp.OrgID
role.UserID = s.Context.UserID
role.CanEdit = true
role.CanView = true
role.RefID = uniqueid.Generate()
err = AddRole(s, role)
return
}
// Invite new user to a folder that someone has shared with them.
// We create the user account with default values and then take them
// through a welcome process designed to capture profile data.
// We add them to the organization and grant them view-only folder access.
func inviteNewUserToSharedSpace(s domain.StoreContext, email string, invitedBy user.User,
baseURL string, sp Space, invitationMessage string) (err error) {
var u = user.User{}
u.Email = email
u.Firstname = email
u.Lastname = ""
u.Salt = secrets.GenerateSalt()
requestedPassword := secrets.GenerateRandomPassword()
u.Password = secrets.GeneratePassword(requestedPassword, u.Salt)
userID := uniqueid.Generate()
u.RefID = userID
err = user.Add(s, u)
if err != nil {
return
}
// Let's give this user access to the organization
var a account.Account
a.UserID = userID
a.OrgID = s.Context.OrgID
a.Admin = false
a.Editor = false
a.Active = true
accountID := uniqueid.Generate()
a.RefID = accountID
err = account.Add(s, a)
if err != nil {
return
}
role := Role{}
role.LabelID = sp.RefID
role.OrgID = s.Context.OrgID
role.UserID = userID
role.CanEdit = false
role.CanView = true
roleID := uniqueid.Generate()
role.RefID = roleID
err = AddRole(s, role)
if err != nil {
return
}
url := fmt.Sprintf("%s/%s", baseURL, u.Salt)
go mail.ShareFolderNewUser(u.Email, invitedBy.Fullname(), url, sp.Name, invitationMessage)
return
}

286
domain/space/store.go Normal file
View file

@ -0,0 +1,286 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
// Package space handles API calls and persistence for spaces.
// Spaces in Documize contain documents.
package space
import (
"database/sql"
"fmt"
"time"
"github.com/documize/community/core/streamutil"
"github.com/documize/community/domain"
"github.com/documize/community/domain/store/mysql"
"github.com/pkg/errors"
)
// Add adds new folder into the store.
func Add(s domain.StoreContext, sp Space) (err error) {
sp.UserID = s.Context.UserID
sp.Created = time.Now().UTC()
sp.Revised = time.Now().UTC()
stmt, err := s.Context.Transaction.Preparex("INSERT INTO label (refid, label, orgid, userid, type, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?)")
defer streamutil.Close(stmt)
if err != nil {
err = errors.Wrap(err, "unable to prepare insert for label")
return
}
_, err = stmt.Exec(sp.RefID, sp.Name, sp.OrgID, sp.UserID, sp.Type, sp.Created, sp.Revised)
if err != nil {
err = errors.Wrap(err, "unable to execute insert for label")
return
}
return
}
// Get returns a space from the store.
func Get(s domain.StoreContext, id string) (sp Space, err error) {
stmt, err := s.Runtime.Db.Preparex("SELECT id,refid,label as name,orgid,userid,type,created,revised FROM label WHERE orgid=? and refid=?")
defer streamutil.Close(stmt)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to prepare select for label %s", id))
return
}
err = stmt.Get(&sp, s.Context.OrgID, id)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute select for label %s", id))
return
}
return
}
// PublicSpaces returns folders that anyone can see.
func PublicSpaces(s domain.StoreContext, orgID string) (sp []Space, err error) {
sql := "SELECT id,refid,label as name,orgid,userid,type,created,revised FROM label a where orgid=? AND type=1"
err = s.Runtime.Db.Select(&sp, sql, orgID)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("Unable to execute GetPublicFolders for org %s", orgID))
return
}
return
}
// GetAll returns folders that the user can see.
// Also handles which folders can be seen by anonymous users.
func GetAll(s domain.StoreContext) (sp []Space, err error) {
sql := `
(SELECT id,refid,label as name,orgid,userid,type,created,revised from label WHERE orgid=? AND type=2 AND userid=?)
UNION ALL
(SELECT id,refid,label as name,orgid,userid,type,created,revised FROM label a where orgid=? AND type=1 AND refid in
(SELECT labelid from labelrole WHERE orgid=? AND userid='' AND (canedit=1 OR canview=1)))
UNION ALL
(SELECT id,refid,label as name,orgid,userid,type,created,revised FROM label a where orgid=? AND type=3 AND refid in
(SELECT labelid from labelrole WHERE orgid=? AND userid=? AND (canedit=1 OR canview=1)))
ORDER BY name`
err = s.Runtime.Db.Select(&sp, sql,
s.Context.OrgID,
s.Context.UserID,
s.Context.OrgID,
s.Context.OrgID,
s.Context.OrgID,
s.Context.OrgID,
s.Context.UserID)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("Unable to execute select labels for org %s", s.Context.OrgID))
return
}
return
}
// Update saves space changes.
func Update(s domain.StoreContext, sp Space) (err error) {
sp.Revised = time.Now().UTC()
stmt, err := s.Context.Transaction.PrepareNamed("UPDATE label SET label=:name, type=:type, userid=:userid, revised=:revised WHERE orgid=:orgid AND refid=:refid")
defer streamutil.Close(stmt)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to prepare update for label %s", sp.RefID))
return
}
_, err = stmt.Exec(&sp)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute update for label %s", sp.RefID))
return
}
return
}
// ChangeOwner transfer space ownership.
func ChangeOwner(s domain.StoreContext, currentOwner, newOwner string) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE label SET userid=? WHERE userid=? AND orgid=?")
defer streamutil.Close(stmt)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to prepare change space owner for %s", currentOwner))
return
}
_, err = stmt.Exec(newOwner, currentOwner, s.Context.OrgID)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute change space owner for %s", currentOwner))
return
}
return
}
// Viewers returns the list of people who can see shared folders.
func Viewers(s domain.StoreContext) (v []Viewer, err error) {
sql := `
SELECT a.userid,
COALESCE(u.firstname, '') as firstname,
COALESCE(u.lastname, '') as lastname,
COALESCE(u.email, '') as email,
a.labelid,
b.label as name,
b.type
FROM labelrole a
LEFT JOIN label b ON b.refid=a.labelid
LEFT JOIN user u ON u.refid=a.userid
WHERE a.orgid=? AND b.type != 2
GROUP BY a.labelid,a.userid
ORDER BY u.firstname,u.lastname`
err = s.Runtime.Db.Select(&v, sql, s.Context.OrgID)
return
}
// Delete removes space from the store.
func Delete(s domain.StoreContext, id string) (rows int64, err error) {
b := mysql.BaseQuery{}
return b.DeleteConstrained(s.Context.Transaction, "label", s.Context.OrgID, id)
}
// AddRole inserts the given record into the labelrole database table.
func AddRole(s domain.StoreContext, r Role) (err error) {
r.Created = time.Now().UTC()
r.Revised = time.Now().UTC()
stmt, err := s.Context.Transaction.Preparex("INSERT INTO labelrole (refid, labelid, orgid, userid, canview, canedit, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
defer streamutil.Close(stmt)
if err != nil {
err = errors.Wrap(err, "unable to prepare insert for space role")
return
}
_, err = stmt.Exec(r.RefID, r.LabelID, r.OrgID, r.UserID, r.CanView, r.CanEdit, r.Created, r.Revised)
if err != nil {
err = errors.Wrap(err, "unable to execute insert for space role")
return
}
return
}
// GetRoles returns a slice of labelrole records, for the given labelID in the client's organization, grouped by user.
func GetRoles(s domain.StoreContext, labelID string) (r []Role, err error) {
query := `SELECT id, refid, labelid, orgid, userid, canview, canedit, created, revised FROM labelrole WHERE orgid=? AND labelid=?` // was + "GROUP BY userid"
err = s.Runtime.Db.Select(&r, query, s.Context.OrgID, labelID)
if err == sql.ErrNoRows {
err = nil
}
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute select for space roles %s", labelID))
return
}
return
}
// GetUserRoles returns a slice of role records, for both the client's user and organization, and
// those space roles that exist for all users in the client's organization.
func GetUserRoles(s domain.StoreContext) (r []Role, err error) {
err = s.Runtime.Db.Select(&r, `
SELECT id, refid, labelid, orgid, userid, canview, canedit, created, revised FROM labelrole WHERE orgid=? and userid=?
UNION ALL
SELECT id, refid, labelid, orgid, userid, canview, canedit, created, revised FROM labelrole WHERE orgid=? AND userid=''`,
s.Context.OrgID, s.Context.UserID, s.Context.OrgID)
if err == sql.ErrNoRows {
err = nil
}
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute select for user space roles %s", s.Context.UserID))
return
}
return
}
// DeleteRole deletes the labelRoleID record from the labelrole table.
func DeleteRole(s domain.StoreContext, roleID string) (rows int64, err error) {
b := mysql.BaseQuery{}
sql := fmt.Sprintf("DELETE FROM labelrole WHERE orgid='%s' AND refid='%s'", s.Context.OrgID, roleID)
return b.DeleteWhere(s.Context.Transaction, sql)
}
// DeleteSpaceRoles deletes records from the labelrole table which have the given space ID.
func DeleteSpaceRoles(s domain.StoreContext, spaceID string) (rows int64, err error) {
b := mysql.BaseQuery{}
sql := fmt.Sprintf("DELETE FROM labelrole WHERE orgid='%s' AND labelid='%s'", s.Context.OrgID, spaceID)
return b.DeleteWhere(s.Context.Transaction, sql)
}
// DeleteUserSpaceRoles removes all roles for the specified user, for the specified space.
func DeleteUserSpaceRoles(s domain.StoreContext, spaceID, userID string) (rows int64, err error) {
b := mysql.BaseQuery{}
sql := fmt.Sprintf("DELETE FROM labelrole WHERE orgid='%s' AND labelid='%s' AND userid='%s'",
s.Context.OrgID, spaceID, userID)
return b.DeleteWhere(s.Context.Transaction, sql)
}
// MoveSpaceRoles changes the space ID for space role records from previousLabel to newLabel.
func MoveSpaceRoles(s domain.StoreContext, previousLabel, newLabel string) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE labelrole SET labelid=? WHERE labelid=? AND orgid=?")
defer streamutil.Close(stmt)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to prepare move space roles for label %s", previousLabel))
return
}
_, err = stmt.Exec(newLabel, previousLabel, s.Context.OrgID)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute move space roles for label %s", previousLabel))
}
return
}

View file

@ -1,14 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
// Package space handles API calls and persistence for spaces.
// Spaces in Documize contain documents.
package space

View file

@ -1,14 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
// Package space handles API calls and persistence for spaces.
// Spaces in Documize contain documents.
package space