1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-24 07:39:43 +02:00
documize/domain/space/endpoint.go
HarveyKandola 8a65567169 Implement PostgreSQL Full Text Search++
1. Full text search supports MySQL, MariaDB, Percona and now PostgreSQL.
2. Changed SQL Variant to typed enum.
3. Changed doc.Versioned from INT to BOOL.
4. Search Reindexer now parses all documents and attachments.
5. Site meta API call returns storage provider type.
6. README prep'ed for PostgreSQL support.
7. DELETE SQL statements ignore zero rows affected.

Closes #100 !!!

Co-Authored-By: Saul S <sauls8t@users.noreply.github.com>
Co-Authored-By: McMatts <matt@documize.com>
2018-09-28 16:33:15 +01:00

949 lines
24 KiB
Go

// 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"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"github.com/documize/community/core/env"
"github.com/documize/community/core/event"
"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/mail"
"github.com/documize/community/domain/organization"
"github.com/documize/community/domain/store"
"github.com/documize/community/model/account"
"github.com/documize/community/model/activity"
"github.com/documize/community/model/audit"
"github.com/documize/community/model/doc"
"github.com/documize/community/model/page"
"github.com/documize/community/model/permission"
"github.com/documize/community/model/space"
"github.com/documize/community/model/user"
wf "github.com/documize/community/model/workflow"
uuid "github.com/nu7hatch/gouuid"
)
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
Store *store.Store
}
// Add creates a new space.
func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
method := "space.add"
ctx := domain.GetRequestContext(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())
h.Runtime.Log.Error(method, err)
return
}
var model = space.NewSpaceRequest{}
err = json.Unmarshal(body, &model)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
model.Name = strings.TrimSpace(model.Name)
model.CloneID = strings.TrimSpace(model.CloneID)
if len(model.Name) == 0 {
response.WriteMissingDataError(w, method, "name")
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
var sp space.Space
sp.Name = model.Name
sp.RefID = uniqueid.Generate()
sp.OrgID = ctx.OrgID
sp.UserID = ctx.UserID
sp.Type = space.ScopePrivate
sp.Lifecycle = wf.LifecycleLive
err = h.Store.Space.Add(ctx, sp)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
perm := permission.Permission{}
perm.OrgID = sp.OrgID
perm.Who = permission.UserPermission
perm.WhoID = ctx.UserID
perm.Scope = permission.ScopeRow
perm.Location = permission.LocationSpace
perm.RefID = sp.RefID
perm.Action = "" // we send array for actions below
err = h.Store.Permission.AddPermissions(ctx, perm, permission.SpaceOwner, permission.SpaceManage, permission.SpaceView,
permission.DocumentAdd, permission.DocumentCopy, permission.DocumentDelete, permission.DocumentEdit, permission.DocumentMove,
permission.DocumentTemplate, permission.DocumentApprove, permission.DocumentVersion, permission.DocumentLifecycle)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: sp.RefID,
SourceType: activity.SourceTypeSpace,
ActivityType: activity.TypeCreated})
if err != nil {
ctx.Transaction.Rollback()
h.Runtime.Log.Error(method, err)
}
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeSpaceAdd)
// Get back new space
sp, _ = h.Store.Space.Get(ctx, sp.RefID)
// clone existing space?
if model.CloneID != "" && (model.CopyDocument || model.CopyPermission || model.CopyTemplate) {
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
if model.CopyPermission {
spCloneRoles, err := h.Store.Permission.GetSpacePermissions(ctx, model.CloneID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
for _, r := range spCloneRoles {
r.RefID = sp.RefID
err = h.Store.Permission.AddPermission(ctx, r)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
}
}
toCopy := []doc.Document{}
spCloneTemplates, err := h.Store.Document.TemplatesBySpace(ctx, model.CloneID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
toCopy = append(toCopy, spCloneTemplates...)
if model.CopyDocument {
docs, err := h.Store.Document.GetBySpace(ctx, model.CloneID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
toCopy = append(toCopy, docs...)
}
// documemt.GroupID groups versioned documents together
// and must be reassigned a new value when being copied
// to avoid conflicts.
groupChange := make(map[string]string)
if len(toCopy) > 0 {
for _, t := range toCopy {
origID := t.RefID
documentID := uniqueid.Generate()
t.RefID = documentID
t.SpaceID = sp.RefID
// Reassign group ID
if len(t.GroupID) > 0 {
if len(groupChange[t.GroupID]) > 0 {
t.GroupID = groupChange[t.GroupID]
} else {
groupChange[t.GroupID] = uniqueid.Generate()
t.GroupID = groupChange[t.GroupID]
}
}
err = h.Store.Document.Add(ctx, t)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
pages, _ := h.Store.Page.GetPages(ctx, origID)
for _, p := range pages {
meta, err2 := h.Store.Page.GetPageMeta(ctx, p.RefID)
if err2 != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
p.DocumentID = documentID
pageID := uniqueid.Generate()
p.RefID = pageID
meta.SectionID = pageID
meta.DocumentID = documentID
model := page.NewPage{}
model.Page = p
model.Meta = meta
err = h.Store.Page.Add(ctx, model)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
}
newUUID, _ := uuid.NewV4()
attachments, _ := h.Store.Attachment.GetAttachmentsWithData(ctx, origID)
for _, a := range attachments {
attachmentID := uniqueid.Generate()
a.RefID = attachmentID
a.DocumentID = documentID
a.Job = newUUID.String()
random := secrets.GenerateSalt()
a.FileID = random[0:9]
err = h.Store.Attachment.Add(ctx, a)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
}
}
}
if model.CopyTemplate {
blocks, err := h.Store.Block.GetBySpace(ctx, model.CloneID)
for _, b := range blocks {
b.RefID = uniqueid.Generate()
b.SpaceID = sp.RefID
b.UserID = ctx.UserID
err = h.Store.Block.Add(ctx, b)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
}
}
// update space to reflect it's type (public/protected/private)
toClone, err := h.Store.Space.Get(ctx, model.CloneID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
sp.Type = toClone.Type
err = h.Store.Space.Update(ctx, sp)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
ctx.Transaction.Commit()
}
event.Handler().Publish(string(event.TypeAddSpace))
response.WriteJSON(w, sp)
}
// Get returns the requested space.
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
method := "space.get"
ctx := domain.GetRequestContext(r)
id := request.Param(r, "spaceID")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "spaceID")
return
}
sp, err := h.Store.Space.Get(ctx, id)
if err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, id)
h.Runtime.Log.Error(method, err)
return
}
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: sp.RefID,
SourceType: activity.SourceTypeSpace,
ActivityType: activity.TypeRead})
if err != nil {
ctx.Transaction.Rollback()
h.Runtime.Log.Error(method, err)
}
ctx.Transaction.Commit()
response.WriteJSON(w, sp)
}
// GetViewable returns spaces the user can see.
func (h *Handler) GetViewable(w http.ResponseWriter, r *http.Request) {
method := "space.GetViewable"
ctx := domain.GetRequestContext(r)
sp, err := h.Store.Space.GetViewable(ctx)
if err != nil {
h.Runtime.Log.Error(method, err)
}
response.WriteJSON(w, sp)
}
// GetAll returns every space for documize admin users to manage
func (h *Handler) GetAll(w http.ResponseWriter, r *http.Request) {
method := "space.getAll"
ctx := domain.GetRequestContext(r)
if !ctx.Administrator {
response.WriteForbiddenError(w)
h.Runtime.Log.Info("rejected non-admin user request for all spaces")
return
}
sp, err := h.Store.Space.GetAll(ctx)
if err != nil {
h.Runtime.Log.Error(method, err)
}
response.WriteJSON(w, sp)
}
// Update processes request to save space object to the database
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
method := "space.update"
ctx := domain.GetRequestContext(r)
if !ctx.Editor {
response.WriteForbiddenError(w)
return
}
spaceID := request.Param(r, "spaceID")
if len(spaceID) == 0 {
response.WriteMissingDataError(w, method, "spaceID")
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
h.Runtime.Log.Error(method, err)
return
}
var sp space.Space
err = json.Unmarshal(body, &sp)
if err != nil {
response.WriteBadRequestError(w, method, "marshal")
h.Runtime.Log.Error(method, err)
return
}
if len(sp.Name) == 0 {
response.WriteMissingDataError(w, method, "name")
h.Runtime.Log.Error(method, err)
return
}
sp.RefID = spaceID
// Retreive previous record for comparison later.
prev, err := h.Store.Space.Get(ctx, spaceID)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
err = h.Store.Space.Update(ctx, sp)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
// If newly marked Everyone space, ensure everyone has permission
if prev.Type != space.ScopePublic && sp.Type == space.ScopePublic {
_, err = h.Store.Permission.DeleteUserSpacePermissions(ctx, sp.RefID, user.EveryoneUserID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
perm := permission.Permission{}
perm.OrgID = sp.OrgID
perm.Who = permission.UserPermission
perm.WhoID = user.EveryoneUserID
perm.Scope = permission.ScopeRow
perm.Location = permission.LocationSpace
perm.RefID = sp.RefID
perm.Action = "" // we send array for actions below
err = h.Store.Permission.AddPermissions(ctx, perm, permission.SpaceView)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
}
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeSpaceUpdate)
response.WriteJSON(w, sp)
}
// Remove moves documents to another space before deleting it
func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) {
method := "space.remove"
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.License.IsValid() {
response.WriteBadLicense(w)
return
}
if !ctx.Editor {
response.WriteForbiddenError(w)
return
}
id := request.Param(r, "spaceID")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "spaceID")
return
}
move := request.Param(r, "moveToId")
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)
h.Runtime.Log.Error(method, err)
return
}
err = h.Store.Document.MoveDocumentSpace(ctx, id, move)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
_, err = h.Store.Category.RemoveSpaceCategoryMemberships(ctx, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
_, err = h.Store.Space.Delete(ctx, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
_, err = h.Store.Permission.DeleteSpacePermissions(ctx, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
_, err = h.Store.Pin.DeletePinnedSpace(ctx, id)
if err != nil && err != sql.ErrNoRows {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: id,
SourceType: activity.SourceTypeSpace,
ActivityType: activity.TypeDeleted})
if err != nil {
ctx.Transaction.Rollback()
h.Runtime.Log.Error(method, err)
}
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeSpaceDelete)
event.Handler().Publish(string(event.TypeRemoveSpace))
response.WriteEmpty(w)
}
// Delete removes space.
func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
method := "space.delete"
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.License.IsValid() {
response.WriteBadLicense(w)
return
}
if !ctx.Editor {
response.WriteForbiddenError(w)
return
}
id := request.Param(r, "spaceID")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "spaceID")
return
}
var err error
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
_, err = h.Store.Document.DeleteBySpace(ctx, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
_, err = h.Store.Permission.DeleteSpacePermissions(ctx, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
// remove category permissions
_, err = h.Store.Permission.DeleteSpaceCategoryPermissions(ctx, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
_, err = h.Store.Pin.DeletePinnedSpace(ctx, id)
if err != nil && err != sql.ErrNoRows {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
// remove category and members for space
_, err = h.Store.Category.DeleteBySpace(ctx, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
_, err = h.Store.Space.Delete(ctx, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: id,
SourceType: activity.SourceTypeSpace,
ActivityType: activity.TypeDeleted})
if err != nil {
ctx.Transaction.Rollback()
h.Runtime.Log.Error(method, err)
}
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeSpaceDelete)
event.Handler().Publish(string(event.TypeRemoveSpace))
response.WriteEmpty(w)
}
// 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 := domain.GetRequestContext(r)
ctx.Subdomain = organization.GetSubdomainFromHost(r)
spaceID := request.Param(r, "spaceID")
if len(spaceID) == 0 {
response.WriteMissingDataError(w, method, "spaceID")
return
}
org, err := h.Store.Organization.GetOrganizationByDomain(ctx.Subdomain)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
// AcceptShare does not authenticate the user hence the context needs to set up
ctx.OrgID = org.RefID
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
h.Runtime.Log.Error(method, err)
return
}
var model = space.AcceptShareModel{}
err = json.Unmarshal(body, &model)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
h.Runtime.Log.Error(method, err)
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 := h.Store.User.GetBySerial(ctx, model.Serial)
if err != nil && err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, "user")
h.Runtime.Log.Error(method, err)
return
}
// AcceptShare does not authenticate the user hence the context needs to set up
ctx.UserID = u.RefID
// Prepare user data
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)
h.Runtime.Log.Error(method, err)
return
}
err = h.Store.User.UpdateUser(ctx, u)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
salt := secrets.GenerateSalt()
err = h.Store.User.UpdateUserPassword(ctx, u.RefID, salt, secrets.GeneratePassword(model.Password, salt))
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeSpaceJoin)
// We send back POJO and not fully authenticated user object as
// SSO should take place thereafter
response.WriteJSON(w, u)
}
// Invite sends users space invitation emails.
func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
method := "space.Invite"
ctx := domain.GetRequestContext(r)
id := request.Param(r, "spaceID")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "spaceID")
return
}
sp, err := h.Store.Space.Get(ctx, id)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
// if sp.UserID != ctx.UserID {
// response.WriteForbiddenError(w)
// return
// }
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, "body")
h.Runtime.Log.Error(method, err)
return
}
var model = space.InvitationModel{}
err = json.Unmarshal(body, &model)
if err != nil {
response.WriteBadRequestError(w, method, "json")
h.Runtime.Log.Error(method, err)
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
inviter, err := h.Store.User.Get(ctx, ctx.UserID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
for _, email := range model.Recipients {
u, err := h.Store.User.GetByEmail(ctx, email)
if err != nil && err != sql.ErrNoRows {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
if len(u.RefID) > 0 {
// Ensure they have access to this organization
accounts, err2 := h.Store.Account.GetUserAccounts(ctx, u.RefID)
if err2 != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
// we create if they c
hasAccess := false
for _, a := range accounts {
if a.OrgID == ctx.OrgID {
hasAccess = true
}
}
if !hasAccess {
var a account.Account
a.UserID = u.RefID
a.OrgID = ctx.OrgID
a.Admin = false
a.Editor = false
a.Users = false
a.Active = true
accountID := uniqueid.Generate()
a.RefID = accountID
err = h.Store.Account.Add(ctx, a)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
}
// Ensure they have space roles
h.Store.Permission.DeleteUserSpacePermissions(ctx, sp.RefID, u.RefID)
perm := permission.Permission{}
perm.OrgID = sp.OrgID
perm.Who = permission.UserPermission
perm.WhoID = u.RefID
perm.Scope = permission.ScopeRow
perm.Location = permission.LocationSpace
perm.RefID = sp.RefID
perm.Action = "" // we send array for actions below
err = h.Store.Permission.AddPermissions(ctx, perm, permission.SpaceView)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
url := ctx.GetAppURL(fmt.Sprintf("s/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
mailer := mail.Mailer{Runtime: h.Runtime, Store: h.Store, Context: ctx}
go mailer.ShareSpaceExistingUser(email, inviter.Fullname(), inviter.Email, 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(ctx, h.Runtime, h.Store, email, inviter, url, sp, model.Message)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(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 space is marked as restricted as a minimum!
if len(model.Recipients) > 0 && sp.Type == space.ScopePrivate {
sp.Type = space.ScopeRestricted
err = h.Store.Space.Update(ctx, sp)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
}
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeSpaceInvite)
response.WriteEmpty(w)
}