mirror of
https://github.com/documize/community.git
synced 2025-07-19 13:19:43 +02:00
document level permissions
This commit is contained in:
parent
f4f32bcfcb
commit
b9394a4967
28 changed files with 983 additions and 355 deletions
|
@ -4,10 +4,6 @@
|
|||
ALTER TABLE document ADD COLUMN `protection` INT NOT NULL DEFAULT 0 AFTER `template`;
|
||||
ALTER TABLE document ADD COLUMN `approval` INT NOT NULL DEFAULT 0 AFTER `protection`;
|
||||
|
||||
-- page needs proection and approval columns
|
||||
ALTER TABLE page ADD COLUMN `protection` INT NOT NULL DEFAULT 0 AFTER `revisions`;
|
||||
ALTER TABLE page ADD COLUMN `approval` INT NOT NULL DEFAULT 0 AFTER `protection`;
|
||||
|
||||
-- data migration clean up from previous releases
|
||||
DROP TABLE IF EXISTS `audit`;
|
||||
DROP TABLE IF EXISTS `search_old`;
|
||||
|
|
|
@ -414,6 +414,17 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
record := pm.DecodeUserPermissions(perms)
|
||||
|
||||
roles, err := h.Store.Permission.GetUserDocumentPermissions(ctx, document.RefID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
if len(roles) == 0 {
|
||||
roles = []pm.Permission{}
|
||||
}
|
||||
|
||||
rolesRecord := pm.DecodeUserDocumentPermissions(roles)
|
||||
|
||||
// links
|
||||
l, err := h.Store.Link.GetDocumentOutboundLinks(ctx, id)
|
||||
if len(l) == 0 {
|
||||
|
@ -439,6 +450,7 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
|
|||
data := documentData{}
|
||||
data.Document = document
|
||||
data.Permissions = record
|
||||
data.Roles = rolesRecord
|
||||
data.Links = l
|
||||
data.Spaces = sp
|
||||
|
||||
|
@ -469,8 +481,9 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
|
|||
// documentData represents all data associated for a single document.
|
||||
// Used by FetchDocumentData() bulk data load call.
|
||||
type documentData struct {
|
||||
Document doc.Document `json:"document"`
|
||||
Permissions pm.Record `json:"permissions"`
|
||||
Spaces []space.Space `json:"folders"`
|
||||
Links []link.Link `json:"link"`
|
||||
Document doc.Document `json:"document"`
|
||||
Permissions pm.Record `json:"permissions"`
|
||||
Roles pm.DocumentRecord `json:"roles"`
|
||||
Spaces []space.Space `json:"folders"`
|
||||
Links []link.Link `json:"link"`
|
||||
}
|
||||
|
|
98
domain/mail/document-approver.html
Normal file
98
domain/mail/document-approver.html
Normal file
|
@ -0,0 +1,98 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>{{.Subject}}</title>
|
||||
<style type="text/css">
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6;
|
||||
}
|
||||
body {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
@media only screen and (max-width: 640px) {
|
||||
h1 {
|
||||
font-weight: 600 !important; margin: 20px 0 5px !important;
|
||||
}
|
||||
h2 {
|
||||
font-weight: 600 !important; margin: 20px 0 5px !important;
|
||||
}
|
||||
h3 {
|
||||
font-weight: 600 !important; margin: 20px 0 5px !important;
|
||||
}
|
||||
h4 {
|
||||
font-weight: 600 !important; margin: 20px 0 5px !important;
|
||||
}
|
||||
h1 {
|
||||
font-size: 22px !important;
|
||||
}
|
||||
h2 {
|
||||
font-size: 18px !important;
|
||||
}
|
||||
h3 {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
.container {
|
||||
width: 100% !important;
|
||||
}
|
||||
.content {
|
||||
padding: 10px !important;
|
||||
}
|
||||
.content-wrap {
|
||||
padding: 10px !important;
|
||||
}
|
||||
.invoice {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6; background: #f6f6f6; margin: 0; padding: 0;">
|
||||
|
||||
<table class="body-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background: #f6f6f6; margin: 0; padding: 0;">
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
<td style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0;" valign="top"></td>
|
||||
<td class="container" width="600" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto; padding: 0;" valign="top">
|
||||
<div class="content" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;">
|
||||
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
|
||||
Document Approval Role Granted
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
|
||||
<td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top">
|
||||
<table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
|
||||
<p>You are requested to approve all changes to the following document:</p>
|
||||
<p style="font-weight: bold;">{{.Document}}</p>
|
||||
<p>{{.Inviter}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
|
||||
<a href="{{.Url}}" class="btn-primary" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">View document</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px; color: #7a8184;" valign="top">
|
||||
Have any questions? <a href="mailto:team@documize.com" style="color: #7a8184;">Contact Documize</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
<td style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0;" valign="top"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
70
domain/mail/document.go
Normal file
70
domain/mail/document.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
// 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
|
||||
|
||||
// jshint ignore:start
|
||||
|
||||
package mail
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
|
||||
"github.com/documize/community/server/web"
|
||||
)
|
||||
|
||||
// DocumentApprover notifies user who has just been granted document approval rights.
|
||||
func (m *Mailer) DocumentApprover(recipient, inviter, url, document string) {
|
||||
method := "DocumentApprover"
|
||||
m.LoadCredentials()
|
||||
|
||||
file, err := web.ReadFile("mail/document-approver.html")
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to load email template", method), err)
|
||||
return
|
||||
}
|
||||
|
||||
emailTemplate := string(file)
|
||||
|
||||
// check inviter name
|
||||
if inviter == "Hello You" || len(inviter) == 0 {
|
||||
inviter = "Your colleague"
|
||||
}
|
||||
|
||||
subject := fmt.Sprintf("%s has granted you document approval", inviter)
|
||||
|
||||
e := NewEmail()
|
||||
e.From = m.Credentials.SMTPsender
|
||||
e.To = []string{recipient}
|
||||
e.Subject = subject
|
||||
|
||||
parameters := struct {
|
||||
Subject string
|
||||
Inviter string
|
||||
Url string
|
||||
Document string
|
||||
}{
|
||||
subject,
|
||||
inviter,
|
||||
url,
|
||||
document,
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
t := template.Must(template.New("emailTemplate").Parse(emailTemplate))
|
||||
t.Execute(buffer, ¶meters)
|
||||
e.HTML = buffer.Bytes()
|
||||
|
||||
err = e.Send(m.GetHost(), m.GetAuth())
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to send email", method), err)
|
||||
}
|
||||
}
|
|
@ -14,15 +14,11 @@
|
|||
package mail
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
|
||||
"github.com/documize/community/core/env"
|
||||
"github.com/documize/community/domain"
|
||||
"github.com/documize/community/server/web"
|
||||
)
|
||||
|
||||
// Mailer provides emailing facilities
|
||||
|
@ -33,241 +29,6 @@ type Mailer struct {
|
|||
Credentials Credentials
|
||||
}
|
||||
|
||||
// InviteNewUser invites someone new providing credentials, explaining the product and stating who is inviting them.
|
||||
func (m *Mailer) InviteNewUser(recipient, inviter, url, username, password string) {
|
||||
method := "InviteNewUser"
|
||||
m.LoadCredentials()
|
||||
|
||||
file, err := web.ReadFile("mail/invite-new-user.html")
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to load email template", method), err)
|
||||
return
|
||||
}
|
||||
|
||||
emailTemplate := string(file)
|
||||
|
||||
// check inviter name
|
||||
if inviter == "Hello You" || len(inviter) == 0 {
|
||||
inviter = "Your colleague"
|
||||
}
|
||||
|
||||
subject := fmt.Sprintf("%s has invited you to Documize", inviter)
|
||||
|
||||
e := NewEmail()
|
||||
e.From = m.Credentials.SMTPsender
|
||||
e.To = []string{recipient}
|
||||
e.Subject = subject
|
||||
|
||||
parameters := struct {
|
||||
Subject string
|
||||
Inviter string
|
||||
Url string
|
||||
Username string
|
||||
Password string
|
||||
}{
|
||||
subject,
|
||||
inviter,
|
||||
url,
|
||||
recipient,
|
||||
password,
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
t := template.Must(template.New("emailTemplate").Parse(emailTemplate))
|
||||
t.Execute(buffer, ¶meters)
|
||||
e.HTML = buffer.Bytes()
|
||||
|
||||
err = e.Send(m.GetHost(), m.GetAuth())
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to send email", method), err)
|
||||
}
|
||||
}
|
||||
|
||||
// InviteExistingUser invites a known user to an organization.
|
||||
func (m *Mailer) InviteExistingUser(recipient, inviter, url string) {
|
||||
method := "InviteExistingUser"
|
||||
m.LoadCredentials()
|
||||
|
||||
file, err := web.ReadFile("mail/invite-existing-user.html")
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to load email template", method), err)
|
||||
return
|
||||
}
|
||||
|
||||
emailTemplate := string(file)
|
||||
|
||||
// check inviter name
|
||||
if inviter == "Hello You" || len(inviter) == 0 {
|
||||
inviter = "Your colleague"
|
||||
}
|
||||
|
||||
subject := fmt.Sprintf("%s has invited you to their Documize account", inviter)
|
||||
|
||||
e := NewEmail()
|
||||
e.From = m.Credentials.SMTPsender
|
||||
e.To = []string{recipient}
|
||||
e.Subject = subject
|
||||
|
||||
parameters := struct {
|
||||
Subject string
|
||||
Inviter string
|
||||
Url string
|
||||
}{
|
||||
subject,
|
||||
inviter,
|
||||
url,
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
t := template.Must(template.New("emailTemplate").Parse(emailTemplate))
|
||||
t.Execute(buffer, ¶meters)
|
||||
e.HTML = buffer.Bytes()
|
||||
|
||||
err = e.Send(m.GetHost(), m.GetAuth())
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to send email", method), err)
|
||||
}
|
||||
}
|
||||
|
||||
// PasswordReset sends a reset email with an embedded token.
|
||||
func (m *Mailer) PasswordReset(recipient, url string) {
|
||||
method := "PasswordReset"
|
||||
m.LoadCredentials()
|
||||
|
||||
file, err := web.ReadFile("mail/password-reset.html")
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to load email template", method), err)
|
||||
return
|
||||
}
|
||||
|
||||
emailTemplate := string(file)
|
||||
|
||||
subject := "Documize password reset request"
|
||||
|
||||
e := NewEmail()
|
||||
e.From = m.Credentials.SMTPsender //e.g. "Documize <hello@documize.com>"
|
||||
e.To = []string{recipient}
|
||||
e.Subject = subject
|
||||
|
||||
parameters := struct {
|
||||
Subject string
|
||||
Url string
|
||||
}{
|
||||
subject,
|
||||
url,
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
t := template.Must(template.New("emailTemplate").Parse(emailTemplate))
|
||||
t.Execute(buffer, ¶meters)
|
||||
e.HTML = buffer.Bytes()
|
||||
|
||||
err = e.Send(m.GetHost(), m.GetAuth())
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to send email", method), err)
|
||||
}
|
||||
}
|
||||
|
||||
// ShareSpaceExistingUser provides an existing user with a link to a newly shared space.
|
||||
func (m *Mailer) ShareSpaceExistingUser(recipient, inviter, url, folder, intro string) {
|
||||
method := "ShareSpaceExistingUser"
|
||||
m.LoadCredentials()
|
||||
|
||||
file, err := web.ReadFile("mail/share-space-existing-user.html")
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to load email template", method), err)
|
||||
return
|
||||
}
|
||||
|
||||
emailTemplate := string(file)
|
||||
|
||||
// check inviter name
|
||||
if inviter == "Hello You" || len(inviter) == 0 {
|
||||
inviter = "Your colleague"
|
||||
}
|
||||
|
||||
subject := fmt.Sprintf("%s has shared %s with you", inviter, folder)
|
||||
|
||||
e := NewEmail()
|
||||
e.From = m.Credentials.SMTPsender
|
||||
e.To = []string{recipient}
|
||||
e.Subject = subject
|
||||
|
||||
parameters := struct {
|
||||
Subject string
|
||||
Inviter string
|
||||
Url string
|
||||
Folder string
|
||||
Intro string
|
||||
}{
|
||||
subject,
|
||||
inviter,
|
||||
url,
|
||||
folder,
|
||||
intro,
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
t := template.Must(template.New("emailTemplate").Parse(emailTemplate))
|
||||
t.Execute(buffer, ¶meters)
|
||||
e.HTML = buffer.Bytes()
|
||||
|
||||
err = e.Send(m.GetHost(), m.GetAuth())
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to send email", method), err)
|
||||
}
|
||||
}
|
||||
|
||||
// ShareSpaceNewUser invites new user providing Credentials, explaining the product and stating who is inviting them.
|
||||
func (m *Mailer) ShareSpaceNewUser(recipient, inviter, url, space, invitationMessage string) {
|
||||
method := "ShareSpaceNewUser"
|
||||
m.LoadCredentials()
|
||||
|
||||
file, err := web.ReadFile("mail/share-space-new-user.html")
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to load email template", method), err)
|
||||
return
|
||||
}
|
||||
|
||||
emailTemplate := string(file)
|
||||
|
||||
// check inviter name
|
||||
if inviter == "Hello You" || len(inviter) == 0 {
|
||||
inviter = "Your colleague"
|
||||
}
|
||||
|
||||
subject := fmt.Sprintf("%s has shared %s with you on Documize", inviter, space)
|
||||
|
||||
e := NewEmail()
|
||||
e.From = m.Credentials.SMTPsender
|
||||
e.To = []string{recipient}
|
||||
e.Subject = subject
|
||||
|
||||
parameters := struct {
|
||||
Subject string
|
||||
Inviter string
|
||||
Url string
|
||||
Invitation string
|
||||
Folder string
|
||||
}{
|
||||
subject,
|
||||
inviter,
|
||||
url,
|
||||
invitationMessage,
|
||||
space,
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
t := template.Must(template.New("emailTemplate").Parse(emailTemplate))
|
||||
t.Execute(buffer, ¶meters)
|
||||
e.HTML = buffer.Bytes()
|
||||
|
||||
err = e.Send(m.GetHost(), m.GetAuth())
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to send email", method), err)
|
||||
}
|
||||
}
|
||||
|
||||
// Credentials holds SMTP endpoint and authentication methods
|
||||
type Credentials struct {
|
||||
SMTPuserid string
|
||||
|
|
122
domain/mail/space.go
Normal file
122
domain/mail/space.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
// 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
|
||||
|
||||
// jshint ignore:start
|
||||
|
||||
package mail
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
|
||||
"github.com/documize/community/server/web"
|
||||
)
|
||||
|
||||
// ShareSpaceExistingUser provides an existing user with a link to a newly shared space.
|
||||
func (m *Mailer) ShareSpaceExistingUser(recipient, inviter, url, folder, intro string) {
|
||||
method := "ShareSpaceExistingUser"
|
||||
m.LoadCredentials()
|
||||
|
||||
file, err := web.ReadFile("mail/share-space-existing-user.html")
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to load email template", method), err)
|
||||
return
|
||||
}
|
||||
|
||||
emailTemplate := string(file)
|
||||
|
||||
// check inviter name
|
||||
if inviter == "Hello You" || len(inviter) == 0 {
|
||||
inviter = "Your colleague"
|
||||
}
|
||||
|
||||
subject := fmt.Sprintf("%s has shared %s with you", inviter, folder)
|
||||
|
||||
e := NewEmail()
|
||||
e.From = m.Credentials.SMTPsender
|
||||
e.To = []string{recipient}
|
||||
e.Subject = subject
|
||||
|
||||
parameters := struct {
|
||||
Subject string
|
||||
Inviter string
|
||||
Url string
|
||||
Folder string
|
||||
Intro string
|
||||
}{
|
||||
subject,
|
||||
inviter,
|
||||
url,
|
||||
folder,
|
||||
intro,
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
t := template.Must(template.New("emailTemplate").Parse(emailTemplate))
|
||||
t.Execute(buffer, ¶meters)
|
||||
e.HTML = buffer.Bytes()
|
||||
|
||||
err = e.Send(m.GetHost(), m.GetAuth())
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to send email", method), err)
|
||||
}
|
||||
}
|
||||
|
||||
// ShareSpaceNewUser invites new user providing Credentials, explaining the product and stating who is inviting them.
|
||||
func (m *Mailer) ShareSpaceNewUser(recipient, inviter, url, space, invitationMessage string) {
|
||||
method := "ShareSpaceNewUser"
|
||||
m.LoadCredentials()
|
||||
|
||||
file, err := web.ReadFile("mail/share-space-new-user.html")
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to load email template", method), err)
|
||||
return
|
||||
}
|
||||
|
||||
emailTemplate := string(file)
|
||||
|
||||
// check inviter name
|
||||
if inviter == "Hello You" || len(inviter) == 0 {
|
||||
inviter = "Your colleague"
|
||||
}
|
||||
|
||||
subject := fmt.Sprintf("%s has shared %s with you on Documize", inviter, space)
|
||||
|
||||
e := NewEmail()
|
||||
e.From = m.Credentials.SMTPsender
|
||||
e.To = []string{recipient}
|
||||
e.Subject = subject
|
||||
|
||||
parameters := struct {
|
||||
Subject string
|
||||
Inviter string
|
||||
Url string
|
||||
Invitation string
|
||||
Folder string
|
||||
}{
|
||||
subject,
|
||||
inviter,
|
||||
url,
|
||||
invitationMessage,
|
||||
space,
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
t := template.Must(template.New("emailTemplate").Parse(emailTemplate))
|
||||
t.Execute(buffer, ¶meters)
|
||||
e.HTML = buffer.Bytes()
|
||||
|
||||
err = e.Send(m.GetHost(), m.GetAuth())
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to send email", method), err)
|
||||
}
|
||||
}
|
157
domain/mail/user.go
Normal file
157
domain/mail/user.go
Normal file
|
@ -0,0 +1,157 @@
|
|||
// 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
|
||||
|
||||
// jshint ignore:start
|
||||
|
||||
package mail
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
|
||||
"github.com/documize/community/server/web"
|
||||
)
|
||||
|
||||
// InviteNewUser invites someone new providing credentials, explaining the product and stating who is inviting them.
|
||||
func (m *Mailer) InviteNewUser(recipient, inviter, url, username, password string) {
|
||||
method := "InviteNewUser"
|
||||
m.LoadCredentials()
|
||||
|
||||
file, err := web.ReadFile("mail/invite-new-user.html")
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to load email template", method), err)
|
||||
return
|
||||
}
|
||||
|
||||
emailTemplate := string(file)
|
||||
|
||||
// check inviter name
|
||||
if inviter == "Hello You" || len(inviter) == 0 {
|
||||
inviter = "Your colleague"
|
||||
}
|
||||
|
||||
subject := fmt.Sprintf("%s has invited you to Documize", inviter)
|
||||
|
||||
e := NewEmail()
|
||||
e.From = m.Credentials.SMTPsender
|
||||
e.To = []string{recipient}
|
||||
e.Subject = subject
|
||||
|
||||
parameters := struct {
|
||||
Subject string
|
||||
Inviter string
|
||||
Url string
|
||||
Username string
|
||||
Password string
|
||||
}{
|
||||
subject,
|
||||
inviter,
|
||||
url,
|
||||
recipient,
|
||||
password,
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
t := template.Must(template.New("emailTemplate").Parse(emailTemplate))
|
||||
t.Execute(buffer, ¶meters)
|
||||
e.HTML = buffer.Bytes()
|
||||
|
||||
err = e.Send(m.GetHost(), m.GetAuth())
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to send email", method), err)
|
||||
}
|
||||
}
|
||||
|
||||
// InviteExistingUser invites a known user to an organization.
|
||||
func (m *Mailer) InviteExistingUser(recipient, inviter, url string) {
|
||||
method := "InviteExistingUser"
|
||||
m.LoadCredentials()
|
||||
|
||||
file, err := web.ReadFile("mail/invite-existing-user.html")
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to load email template", method), err)
|
||||
return
|
||||
}
|
||||
|
||||
emailTemplate := string(file)
|
||||
|
||||
// check inviter name
|
||||
if inviter == "Hello You" || len(inviter) == 0 {
|
||||
inviter = "Your colleague"
|
||||
}
|
||||
|
||||
subject := fmt.Sprintf("%s has invited you to their Documize account", inviter)
|
||||
|
||||
e := NewEmail()
|
||||
e.From = m.Credentials.SMTPsender
|
||||
e.To = []string{recipient}
|
||||
e.Subject = subject
|
||||
|
||||
parameters := struct {
|
||||
Subject string
|
||||
Inviter string
|
||||
Url string
|
||||
}{
|
||||
subject,
|
||||
inviter,
|
||||
url,
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
t := template.Must(template.New("emailTemplate").Parse(emailTemplate))
|
||||
t.Execute(buffer, ¶meters)
|
||||
e.HTML = buffer.Bytes()
|
||||
|
||||
err = e.Send(m.GetHost(), m.GetAuth())
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to send email", method), err)
|
||||
}
|
||||
}
|
||||
|
||||
// PasswordReset sends a reset email with an embedded token.
|
||||
func (m *Mailer) PasswordReset(recipient, url string) {
|
||||
method := "PasswordReset"
|
||||
m.LoadCredentials()
|
||||
|
||||
file, err := web.ReadFile("mail/password-reset.html")
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to load email template", method), err)
|
||||
return
|
||||
}
|
||||
|
||||
emailTemplate := string(file)
|
||||
|
||||
subject := "Documize password reset request"
|
||||
|
||||
e := NewEmail()
|
||||
e.From = m.Credentials.SMTPsender //e.g. "Documize <hello@documize.com>"
|
||||
e.To = []string{recipient}
|
||||
e.Subject = subject
|
||||
|
||||
parameters := struct {
|
||||
Subject string
|
||||
Url string
|
||||
}{
|
||||
subject,
|
||||
url,
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
t := template.Must(template.New("emailTemplate").Parse(emailTemplate))
|
||||
t.Execute(buffer, ¶meters)
|
||||
e.HTML = buffer.Bytes()
|
||||
|
||||
err = e.Send(m.GetHost(), m.GetAuth())
|
||||
if err != nil {
|
||||
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to send email", method), err)
|
||||
}
|
||||
}
|
|
@ -54,8 +54,8 @@ func (s Scope) Add(ctx domain.RequestContext, model page.NewPage) (err error) {
|
|||
model.Page.Sequence = maxSeq * 2
|
||||
}
|
||||
|
||||
_, err = ctx.Transaction.Exec("INSERT INTO page (refid, orgid, documentid, userid, contenttype, pagetype, level, title, body, revisions, sequence, blockid, protected, approval, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
model.Page.RefID, model.Page.OrgID, model.Page.DocumentID, model.Page.UserID, model.Page.ContentType, model.Page.PageType, model.Page.Level, model.Page.Title, model.Page.Body, model.Page.Revisions, model.Page.Sequence, model.Page.BlockID, model.Page.Protection, model.Page.Approval, model.Page.Created, model.Page.Revised)
|
||||
_, err = ctx.Transaction.Exec("INSERT INTO page (refid, orgid, documentid, userid, contenttype, pagetype, level, title, body, revisions, sequence, blockid, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
model.Page.RefID, model.Page.OrgID, model.Page.DocumentID, model.Page.UserID, model.Page.ContentType, model.Page.PageType, model.Page.Level, model.Page.Title, model.Page.Body, model.Page.Revisions, model.Page.Sequence, model.Page.BlockID, model.Page.Created, model.Page.Revised)
|
||||
|
||||
_, err = ctx.Transaction.Exec("INSERT INTO pagemeta (pageid, orgid, userid, documentid, rawbody, config, externalsource, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
model.Meta.PageID, model.Meta.OrgID, model.Meta.UserID, model.Meta.DocumentID, model.Meta.RawBody, model.Meta.Config, model.Meta.ExternalSource, model.Meta.Created, model.Meta.Revised)
|
||||
|
@ -69,7 +69,7 @@ func (s Scope) Add(ctx domain.RequestContext, model page.NewPage) (err error) {
|
|||
|
||||
// Get returns the pageID page record from the page table.
|
||||
func (s Scope) Get(ctx domain.RequestContext, pageID string) (p page.Page, err error) {
|
||||
err = s.Runtime.Db.Get(&p, "SELECT a.id, a.refid, a.orgid, a.documentid, a.userid, a.contenttype, a.pagetype, a.level, a.sequence, a.title, a.body, a.revisions, a.blockid, a.protection, a.approval, a.created, a.revised FROM page a WHERE a.orgid=? AND a.refid=?",
|
||||
err = s.Runtime.Db.Get(&p, "SELECT a.id, a.refid, a.orgid, a.documentid, a.userid, a.contenttype, a.pagetype, a.level, a.sequence, a.title, a.body, a.revisions, a.blockid, a.created, a.revised FROM page a WHERE a.orgid=? AND a.refid=?",
|
||||
ctx.OrgID, pageID)
|
||||
|
||||
if err != nil {
|
||||
|
@ -81,7 +81,7 @@ func (s Scope) Get(ctx domain.RequestContext, pageID string) (p page.Page, err e
|
|||
|
||||
// GetPages returns a slice containing all the page records for a given documentID, in presentation sequence.
|
||||
func (s Scope) GetPages(ctx domain.RequestContext, documentID string) (p []page.Page, err error) {
|
||||
err = s.Runtime.Db.Select(&p, "SELECT a.id, a.refid, a.orgid, a.documentid, a.userid, a.contenttype, a.pagetype, a.level, a.sequence, a.title, a.body, a.revisions, a.blockid, a.protection, a.approval, a.created, a.revised FROM page a WHERE a.orgid=? AND a.documentid=? ORDER BY a.sequence", ctx.OrgID, documentID)
|
||||
err = s.Runtime.Db.Select(&p, "SELECT a.id, a.refid, a.orgid, a.documentid, a.userid, a.contenttype, a.pagetype, a.level, a.sequence, a.title, a.body, a.revisions, a.blockid, a.created, a.revised FROM page a WHERE a.orgid=? AND a.documentid=? ORDER BY a.sequence", ctx.OrgID, documentID)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "execute get pages")
|
||||
|
@ -93,7 +93,7 @@ func (s Scope) GetPages(ctx domain.RequestContext, documentID string) (p []page.
|
|||
// GetPagesWithoutContent returns a slice containing all the page records for a given documentID, in presentation sequence,
|
||||
// but without the body field (which holds the HTML content).
|
||||
func (s Scope) GetPagesWithoutContent(ctx domain.RequestContext, documentID string) (pages []page.Page, err error) {
|
||||
err = s.Runtime.Db.Select(&pages, "SELECT id, refid, orgid, documentid, userid, contenttype, pagetype, sequence, level, title, revisions, blockid, protection, approval, created, revised FROM page WHERE orgid=? AND documentid=? ORDER BY sequence", ctx.OrgID, documentID)
|
||||
err = s.Runtime.Db.Select(&pages, "SELECT id, refid, orgid, documentid, userid, contenttype, pagetype, sequence, level, title, revisions, blockid, created, revised FROM page WHERE orgid=? AND documentid=? ORDER BY sequence", ctx.OrgID, documentID)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, fmt.Sprintf("Unable to execute select pages for org %s and document %s", ctx.OrgID, documentID))
|
||||
|
@ -119,7 +119,7 @@ func (s Scope) Update(ctx domain.RequestContext, page page.Page, refID, userID s
|
|||
}
|
||||
|
||||
// Update page
|
||||
_, err = ctx.Transaction.NamedExec("UPDATE page SET documentid=:documentid, level=:level, title=:title, body=:body, revisions=:revisions, sequence=:sequence, protection=:protection, approval=:approval, revised=:revised WHERE orgid=:orgid AND refid=:refid",
|
||||
_, err = ctx.Transaction.NamedExec("UPDATE page SET documentid=:documentid, level=:level, title=:title, body=:body, revisions=:revisions, sequence=:sequence, revised=:revised WHERE orgid=:orgid AND refid=:refid",
|
||||
&page)
|
||||
|
||||
if err != nil {
|
||||
|
|
|
@ -69,7 +69,7 @@ func (h *Handler) SetSpacePermissions(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
var model = permission.PermissionsModel{}
|
||||
var model = permission.SpaceRequestModel{}
|
||||
err = json.Unmarshal(body, &model)
|
||||
if err != nil {
|
||||
response.WriteServerError(w, method, err)
|
||||
|
@ -398,3 +398,192 @@ func (h *Handler) SetCategoryPermissions(w http.ResponseWriter, r *http.Request)
|
|||
|
||||
response.WriteEmpty(w)
|
||||
}
|
||||
|
||||
// GetDocumentPermissions returns permissions for all users for given document.
|
||||
func (h *Handler) GetDocumentPermissions(w http.ResponseWriter, r *http.Request) {
|
||||
method := "space.GetDocumentPermissions"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
documentID := request.Param(r, "documentID")
|
||||
if len(documentID) == 0 {
|
||||
response.WriteMissingDataError(w, method, "documentID")
|
||||
return
|
||||
}
|
||||
|
||||
perms, err := h.Store.Permission.GetDocumentPermissions(ctx, documentID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
if len(perms) == 0 {
|
||||
perms = []permission.Permission{}
|
||||
}
|
||||
|
||||
userPerms := make(map[string][]permission.Permission)
|
||||
for _, p := range perms {
|
||||
userPerms[p.WhoID] = append(userPerms[p.WhoID], p)
|
||||
}
|
||||
|
||||
records := []permission.DocumentRecord{}
|
||||
for _, up := range userPerms {
|
||||
records = append(records, permission.DecodeUserDocumentPermissions(up))
|
||||
}
|
||||
|
||||
response.WriteJSON(w, records)
|
||||
}
|
||||
|
||||
// GetUserDocumentPermissions returns permissions for the requested space, for current user.
|
||||
func (h *Handler) GetUserDocumentPermissions(w http.ResponseWriter, r *http.Request) {
|
||||
method := "space.GetUserDocumentPermissions"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
documentID := request.Param(r, "documentID")
|
||||
if len(documentID) == 0 {
|
||||
response.WriteMissingDataError(w, method, "documentID")
|
||||
return
|
||||
}
|
||||
|
||||
perms, err := h.Store.Permission.GetUserDocumentPermissions(ctx, documentID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
if len(perms) == 0 {
|
||||
perms = []permission.Permission{}
|
||||
}
|
||||
|
||||
record := permission.DecodeUserDocumentPermissions(perms)
|
||||
response.WriteJSON(w, record)
|
||||
}
|
||||
|
||||
// SetDocumentPermissions persists specified document permissions
|
||||
// These permissions override document permissions
|
||||
func (h *Handler) SetDocumentPermissions(w http.ResponseWriter, r *http.Request) {
|
||||
method := "space.SetDocumentPermissions"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
id := request.Param(r, "documentID")
|
||||
if len(id) == 0 {
|
||||
response.WriteMissingDataError(w, method, "documentID")
|
||||
return
|
||||
}
|
||||
|
||||
doc, err := h.Store.Document.Get(ctx, id)
|
||||
if err != nil {
|
||||
response.WriteNotFoundError(w, method, "document not found")
|
||||
return
|
||||
}
|
||||
|
||||
sp, err := h.Store.Space.Get(ctx, doc.LabelID)
|
||||
if err != nil {
|
||||
response.WriteNotFoundError(w, method, "space not found")
|
||||
return
|
||||
}
|
||||
|
||||
// if !HasPermission(ctx, *h.Store, doc.LabelID, permission.SpaceManage, permission.SpaceOwner) {
|
||||
// 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 = []permission.DocumentRecord{}
|
||||
err = json.Unmarshal(body, &model)
|
||||
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
|
||||
}
|
||||
|
||||
// We compare new permisions to what we had before.
|
||||
// Why? So we can send out space invitation emails.
|
||||
previousRoles, err := h.Store.Permission.GetDocumentPermissions(ctx, id)
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
response.WriteServerError(w, method, err)
|
||||
h.Runtime.Log.Error(method, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Store all previous approval roles as map for easy querying
|
||||
previousRoleUsers := make(map[string]bool)
|
||||
for _, v := range previousRoles {
|
||||
if v.Action == permission.DocumentApprove {
|
||||
previousRoleUsers[v.WhoID] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Get user who is setting document permissions so we can send out emails with context
|
||||
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
|
||||
}
|
||||
|
||||
// Nuke all previous permissions for this document
|
||||
_, err = h.Store.Permission.DeleteDocumentPermissions(ctx, id)
|
||||
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/d/%s/%s",
|
||||
sp.RefID, stringutil.MakeSlug(sp.Name), doc.RefID, stringutil.MakeSlug(doc.Title)))
|
||||
|
||||
for _, perm := range model {
|
||||
perm.OrgID = ctx.OrgID
|
||||
perm.DocumentID = id
|
||||
|
||||
// Only persist if there is a role!
|
||||
if permission.HasAnyDocumentPermission(perm) {
|
||||
r := permission.EncodeUserDocumentPermissions(perm)
|
||||
|
||||
for _, p := range r {
|
||||
err = h.Store.Permission.AddPermission(ctx, p)
|
||||
if err != nil {
|
||||
h.Runtime.Log.Error("set document permission", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Send email notification to users who have been given document approver role
|
||||
if _, isExisting := previousRoleUsers[perm.UserID]; !isExisting {
|
||||
|
||||
// we skip 'everyone' (user id != empty string)
|
||||
if perm.UserID != "0" && perm.UserID != "" && perm.DocumentRoleApprove {
|
||||
existingUser, err := h.Store.User.Get(ctx, perm.UserID)
|
||||
if err != nil {
|
||||
response.WriteServerError(w, method, err)
|
||||
break
|
||||
}
|
||||
|
||||
mailer := mail.Mailer{Runtime: h.Runtime, Store: h.Store, Context: ctx}
|
||||
go mailer.DocumentApprover(existingUser.Email, inviter.Fullname(), url, doc.Title)
|
||||
h.Runtime.Log.Info(fmt.Sprintf("%s has made %s document approver for: %s", inviter.Email, existingUser.Email, doc.Title))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h.Store.Audit.Record(ctx, audit.EventTypeDocumentPermission)
|
||||
|
||||
ctx.Transaction.Commit()
|
||||
|
||||
response.WriteEmpty(w)
|
||||
}
|
||||
|
|
|
@ -212,3 +212,55 @@ func (s Scope) GetUserCategoryPermissions(ctx domain.RequestContext, userID stri
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
// GetUserDocumentPermissions returns document permissions for user.
|
||||
// Context is used to for user ID.
|
||||
func (s Scope) GetUserDocumentPermissions(ctx domain.RequestContext, documentID string) (r []permission.Permission, err error) {
|
||||
err = s.Runtime.Db.Select(&r, `
|
||||
SELECT id, orgid, who, whoid, action, scope, location, refid
|
||||
FROM permission WHERE orgid=? AND location='document' AND refid=? AND who='user' AND (whoid=? OR whoid='0')
|
||||
UNION ALL
|
||||
SELECT p.id, p.orgid, p.who, p.whoid, p.action, p.scope, p.location, p.refid
|
||||
FROM permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.location='document' AND refid=?
|
||||
AND p.who='role' AND (r.userid=? OR r.userid='0')`,
|
||||
ctx.OrgID, documentID, ctx.UserID, ctx.OrgID, documentID, ctx.OrgID)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, fmt.Sprintf("unable to execute select user document permissions %s", ctx.UserID))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetDocumentPermissions returns documents permissions for all users.
|
||||
func (s Scope) GetDocumentPermissions(ctx domain.RequestContext, documentID string) (r []permission.Permission, err error) {
|
||||
err = s.Runtime.Db.Select(&r, `
|
||||
SELECT id, orgid, who, whoid, action, scope, location, refid
|
||||
FROM permission WHERE orgid=? AND location='document' AND refid=? AND who='user'
|
||||
UNION ALL
|
||||
SELECT p.id, p.orgid, p.who, p.whoid, p.action, p.scope, p.location, p.refid
|
||||
FROM permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.location='document' AND p.refid=?
|
||||
AND p.who='role'`,
|
||||
ctx.OrgID, documentID, ctx.OrgID, documentID)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, fmt.Sprintf("unable to execute select document permissions %s", ctx.UserID))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteDocumentPermissions removes records from permissions table for given document.
|
||||
func (s Scope) DeleteDocumentPermissions(ctx domain.RequestContext, documentID string) (rows int64, err error) {
|
||||
b := mysql.BaseQuery{}
|
||||
|
||||
sql := fmt.Sprintf("DELETE FROM permission WHERE orgid='%s' AND location='document' AND refid='%s'", ctx.OrgID, documentID)
|
||||
|
||||
return b.DeleteWhere(ctx.Transaction, sql)
|
||||
}
|
||||
|
|
|
@ -94,6 +94,9 @@ type PermissionStorer interface {
|
|||
GetCategoryPermissions(ctx RequestContext, catID string) (r []permission.Permission, err error)
|
||||
GetCategoryUsers(ctx RequestContext, catID string) (u []user.User, err error)
|
||||
GetUserCategoryPermissions(ctx RequestContext, userID string) (r []permission.Permission, err error)
|
||||
GetUserDocumentPermissions(ctx RequestContext, documentID string) (r []permission.Permission, err error)
|
||||
GetDocumentPermissions(ctx RequestContext, documentID string) (r []permission.Permission, err error)
|
||||
DeleteDocumentPermissions(ctx RequestContext, documentID string) (rows int64, err error)
|
||||
}
|
||||
|
||||
// UserStorer defines required methods for user management
|
||||
|
|
|
@ -12,8 +12,9 @@
|
|||
import { setProperties } from '@ember/object';
|
||||
import Component from '@ember/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import ModalMixin from '../../mixins/modal';
|
||||
|
||||
export default Component.extend({
|
||||
export default Component.extend(ModalMixin, {
|
||||
folderService: service('folder'),
|
||||
userService: service('user'),
|
||||
appMeta: service(),
|
||||
|
@ -120,8 +121,7 @@ export default Component.extend({
|
|||
}
|
||||
|
||||
this.get('folderService').savePermissions(folder.get('id'), payload).then(() => {
|
||||
$('#space-permission-modal').modal('hide');
|
||||
$('#space-permission-modal').modal('dispose');
|
||||
this.modalClose('#space-permission-modal');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
25
gui/app/models/document-role.js
Normal file
25
gui/app/models/document-role.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
// 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
|
||||
|
||||
import Model from 'ember-data/model';
|
||||
import attr from 'ember-data/attr';
|
||||
|
||||
export default Model.extend({
|
||||
orgId: attr('string'),
|
||||
documentId: attr('string'),
|
||||
userId: attr('string'),
|
||||
fullname: attr('string'), // client-side usage only, not from API
|
||||
|
||||
documentEdit: attr('boolean'), // space level setting
|
||||
documentApprove: attr('boolean'), // space level setting
|
||||
documentRoleEdit: attr('boolean'), // document level setting
|
||||
documentRoleApprove: attr('boolean') // document level setting
|
||||
});
|
|
@ -28,8 +28,6 @@ export default Model.extend({
|
|||
body: attr('string'),
|
||||
rawBody: attr('string'),
|
||||
meta: attr(),
|
||||
protection: attr('number', { defaultValue: 0 }),
|
||||
approval: attr('number', { defaultValue: 0 }),
|
||||
|
||||
tagName: computed('level', function () {
|
||||
return "h2";
|
||||
|
|
|
@ -32,7 +32,8 @@ export default Route.extend(AuthenticatedRouteMixin, {
|
|||
pages: this.get('documentService').getPages(this.modelFor('document').document.get('id')),
|
||||
links: this.modelFor('document').links,
|
||||
sections: this.modelFor('document').sections,
|
||||
permissions: this.modelFor('document').permissions
|
||||
permissions: this.modelFor('document').permissions,
|
||||
roles: this.modelFor('document').roles
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -44,5 +45,6 @@ export default Route.extend(AuthenticatedRouteMixin, {
|
|||
controller.set('links', model.links);
|
||||
controller.set('sections', model.sections);
|
||||
controller.set('permissions', model.permissions);
|
||||
controller.set('roles', model.roles);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
{{toolbar/nav-bar}}
|
||||
|
||||
{{toolbar/for-document document=document spaces=folders space=folder permissions=permissions
|
||||
onDocumentDelete=(action 'onDocumentDelete') onSaveTemplate=(action 'onSaveTemplate')}}
|
||||
{{toolbar/for-document document=document spaces=folders space=folder permissions=permissions roles=roles
|
||||
onDocumentDelete=(action 'onDocumentDelete')
|
||||
onSaveTemplate=(action 'onSaveTemplate')
|
||||
onSaveDocument=(action 'onSaveDocument')}}
|
||||
|
||||
<div id="doc-view" class="container">
|
||||
<div class="row">
|
||||
|
@ -15,7 +17,8 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
{{document/document-toc document=document folder=folder pages=pages page=page permissions=permissions currentPageId=pageId tab=tab
|
||||
{{document/document-toc document=document folder=folder pages=pages page=page
|
||||
permissions=permissions roles=roles currentPageId=pageId tab=tab
|
||||
onPageSequenceChange=(action 'onPageSequenceChange') onPageLevelChange=(action 'onPageLevelChange') onGotoPage=(action 'onGotoPage')}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -31,6 +31,7 @@ export default Route.extend(AuthenticatedRouteMixin, {
|
|||
this.set('folders', data.folders);
|
||||
this.set('folder', data.folder);
|
||||
this.set('permissions', data.permissions);
|
||||
this.set('roles', data.roles);
|
||||
this.set('links', data.links);
|
||||
resolve();
|
||||
});
|
||||
|
@ -44,6 +45,7 @@ export default Route.extend(AuthenticatedRouteMixin, {
|
|||
document: this.get('document'),
|
||||
page: this.get('pageId'),
|
||||
permissions: this.get('permissions'),
|
||||
roles: this.get('roles'),
|
||||
links: this.get('links'),
|
||||
sections: this.get('sectionService').getAll()
|
||||
});
|
||||
|
|
13
gui/app/serializers/document-role.js
Normal file
13
gui/app/serializers/document-role.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import ApplicationSerializer from './application';
|
||||
|
||||
export default ApplicationSerializer.extend({
|
||||
normalize(modelClass, resourceHash) {
|
||||
return {
|
||||
data: {
|
||||
id: resourceHash.userId ? resourceHash.userId : 0,
|
||||
type: modelClass.modelName,
|
||||
attributes: resourceHash
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
|
@ -328,6 +328,7 @@ export default Service.extend({
|
|||
let data = {
|
||||
document: {},
|
||||
permissions: {},
|
||||
roles: {},
|
||||
folders: [],
|
||||
folder: {},
|
||||
links: [],
|
||||
|
@ -337,9 +338,12 @@ export default Service.extend({
|
|||
doc = this.get('store').push(doc);
|
||||
|
||||
let perms = this.get('store').normalize('space-permission', response.permissions);
|
||||
perms= this.get('store').push(perms);
|
||||
perms = this.get('store').push(perms);
|
||||
this.get('folderService').set('permissions', perms);
|
||||
|
||||
let roles = this.get('store').normalize('document-role', response.roles);
|
||||
roles = this.get('store').push(roles);
|
||||
|
||||
let folders = response.folders.map((obj) => {
|
||||
let data = this.get('store').normalize('folder', obj);
|
||||
return this.get('store').push(data);
|
||||
|
@ -347,6 +351,7 @@ export default Service.extend({
|
|||
|
||||
data.document = doc;
|
||||
data.permissions = perms;
|
||||
data.roles = roles;
|
||||
data.folders = folders;
|
||||
data.folder = folders.findBy('id', doc.get('folderId'));
|
||||
data.links = response.links;
|
||||
|
|
|
@ -23,6 +23,36 @@
|
|||
color: $color-dark;
|
||||
}
|
||||
}
|
||||
|
||||
> .protection-table {
|
||||
> tbody, > thead {
|
||||
> tr, > th {
|
||||
> td, > th {
|
||||
margin: 0;
|
||||
padding: 10px 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
> td:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> thead {
|
||||
> tr {
|
||||
> th {
|
||||
background-color: $color-off-white;
|
||||
color: $color-gray;
|
||||
}
|
||||
|
||||
> th:first-child {
|
||||
background-color: $color-white !important;
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section-divider {
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<div class="document-customfields">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-3 heading">Categories</div>
|
||||
<div class="col-9 value">
|
||||
<div class="col-12 col-sm-3 heading">Categories</div>
|
||||
<div class="col-12 col-sm-9 value">
|
||||
{{#each selectedCategories as |cat|}}
|
||||
{{#link-to 'folder' folder.id folder.slug (query-params category=cat.id)}}
|
||||
{{cat.category}}
|
||||
|
@ -27,8 +27,8 @@
|
|||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-3 heading">Tags</div>
|
||||
<div class="col-9 value">
|
||||
<div class="col-12 col-sm-3 heading">Tags</div>
|
||||
<div class="col-12 col-sm-9 value">
|
||||
{{#each tagz as |t index|}}
|
||||
{{#link-to 'search' (query-params filter=t matchTag=true)}}
|
||||
{{concat '#' t}}
|
||||
|
|
|
@ -164,8 +164,6 @@ let PageModel = BaseModel.extend({
|
|||
title: "",
|
||||
body: "",
|
||||
rawBody: "",
|
||||
protection: constants.ProtectionType.None,
|
||||
approval: constants.ApprovalType.None,
|
||||
meta: {},
|
||||
|
||||
tagName: computed('level', function () {
|
||||
|
|
|
@ -34,6 +34,7 @@ const (
|
|||
EventTypeDocumentUpdate EventType = "updated-document"
|
||||
EventTypeDocumentDelete EventType = "removed-document"
|
||||
EventTypeDocumentRevisions EventType = "viewed-document-revisions"
|
||||
EventTypeDocumentPermission EventType = "changed-document-permissions"
|
||||
EventTypeSpaceAdd EventType = "added-space"
|
||||
EventTypeSpaceUpdate EventType = "updated-space"
|
||||
EventTypeSpaceDelete EventType = "removed-space"
|
||||
|
|
|
@ -16,26 +16,23 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/documize/community/model"
|
||||
"github.com/documize/community/model/workflow"
|
||||
)
|
||||
|
||||
// Page represents a section within a document.
|
||||
type Page struct {
|
||||
model.BaseEntity
|
||||
OrgID string `json:"orgId"`
|
||||
DocumentID string `json:"documentId"`
|
||||
UserID string `json:"userId"`
|
||||
ContentType string `json:"contentType"`
|
||||
PageType string `json:"pageType"`
|
||||
BlockID string `json:"blockId"`
|
||||
Level uint64 `json:"level"`
|
||||
Sequence float64 `json:"sequence"`
|
||||
Numbering string `json:"numbering"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
Revisions uint64 `json:"revisions"`
|
||||
Protection workflow.Protection `json:"protection"`
|
||||
Approval workflow.Approval `json:"approval"`
|
||||
OrgID string `json:"orgId"`
|
||||
DocumentID string `json:"documentId"`
|
||||
UserID string `json:"userId"`
|
||||
ContentType string `json:"contentType"`
|
||||
PageType string `json:"pageType"`
|
||||
BlockID string `json:"blockId"`
|
||||
Level uint64 `json:"level"`
|
||||
Sequence float64 `json:"sequence"`
|
||||
Numbering string `json:"numbering"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
Revisions uint64 `json:"revisions"`
|
||||
}
|
||||
|
||||
// SetDefaults ensures no blank values.
|
||||
|
|
76
model/permission/common.go
Normal file
76
model/permission/common.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
// 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 permission
|
||||
|
||||
import "time"
|
||||
|
||||
// Permission represents a permission for a space and is persisted to the database.
|
||||
type Permission struct {
|
||||
ID uint64 `json:"id"`
|
||||
OrgID string `json:"orgId"`
|
||||
Who string `json:"who"` // user, role
|
||||
WhoID string `json:"whoId"` // either a user or role ID
|
||||
Action Action `json:"action"` // view, edit, delete
|
||||
Scope string `json:"scope"` // object, table
|
||||
Location string `json:"location"` // table name
|
||||
RefID string `json:"refId"` // id of row in table / blank when scope=table
|
||||
Created time.Time `json:"created"`
|
||||
}
|
||||
|
||||
// Action details type of action
|
||||
type Action string
|
||||
|
||||
const (
|
||||
// SpaceView action means you can view a space and documents therein
|
||||
SpaceView Action = "view"
|
||||
|
||||
// SpaceManage action means you can add, remove users, set permissions, but not delete that space
|
||||
SpaceManage Action = "manage"
|
||||
|
||||
// SpaceOwner action means you can delete a space and do all SpaceManage functions
|
||||
SpaceOwner Action = "own"
|
||||
|
||||
// DocumentAdd action means you can create/upload documents to a space
|
||||
DocumentAdd Action = "doc-add"
|
||||
|
||||
// DocumentEdit action means you can edit documents in a space
|
||||
DocumentEdit Action = "doc-edit"
|
||||
|
||||
// DocumentDelete means you can delete documents in a space
|
||||
DocumentDelete Action = "doc-delete"
|
||||
|
||||
// DocumentMove means you can move documents between spaces
|
||||
DocumentMove Action = "doc-move"
|
||||
|
||||
// DocumentCopy means you can copy documents within and between spaces
|
||||
DocumentCopy Action = "doc-copy"
|
||||
|
||||
// DocumentTemplate means you can create, edit and delete document templates and content blocks
|
||||
DocumentTemplate Action = "doc-template"
|
||||
|
||||
// DocumentApprove means you can approve a change to a document
|
||||
DocumentApprove Action = "doc-approve"
|
||||
|
||||
// CategoryView action means you can view a category and documents therein
|
||||
CategoryView Action = "view"
|
||||
)
|
||||
|
||||
// ContainsPermission checks if action matches one of the required actions?
|
||||
func ContainsPermission(action Action, actions ...Action) bool {
|
||||
for _, a := range actions {
|
||||
if action == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
78
model/permission/document.go
Normal file
78
model/permission/document.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
// 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 permission
|
||||
|
||||
// DocumentRecord represents space permissions for a user on a document.
|
||||
// This data structure is made from database permission records for the document,
|
||||
// and it is designed to be sent to HTTP clients (web, mobile).
|
||||
type DocumentRecord struct {
|
||||
OrgID string `json:"orgId"`
|
||||
DocumentID string `json:"documentId"`
|
||||
UserID string `json:"userId"`
|
||||
DocumentRoleEdit bool `json:"documentRoleEdit"`
|
||||
DocumentRoleApprove bool `json:"documentRoleApprove"`
|
||||
}
|
||||
|
||||
// DecodeUserDocumentPermissions returns a flat, usable permission summary record
|
||||
// from multiple user permission records for a given document.
|
||||
func DecodeUserDocumentPermissions(perm []Permission) (r DocumentRecord) {
|
||||
r = DocumentRecord{}
|
||||
|
||||
if len(perm) > 0 {
|
||||
r.OrgID = perm[0].OrgID
|
||||
r.UserID = perm[0].WhoID
|
||||
r.DocumentID = perm[0].RefID
|
||||
}
|
||||
|
||||
for _, p := range perm {
|
||||
switch p.Action {
|
||||
case DocumentEdit:
|
||||
r.DocumentRoleEdit = true
|
||||
case DocumentApprove:
|
||||
r.DocumentRoleApprove = true
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeUserDocumentPermissions returns multiple user permission records
|
||||
// for a given document, using flat permission summary record.
|
||||
func EncodeUserDocumentPermissions(r DocumentRecord) (perm []Permission) {
|
||||
if r.DocumentRoleEdit {
|
||||
perm = append(perm, EncodeDocumentRecord(r, DocumentEdit))
|
||||
}
|
||||
if r.DocumentRoleApprove {
|
||||
perm = append(perm, EncodeDocumentRecord(r, DocumentApprove))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// HasAnyDocumentPermission returns true if user has at least one permission.
|
||||
func HasAnyDocumentPermission(p DocumentRecord) bool {
|
||||
return p.DocumentRoleEdit || p.DocumentRoleApprove
|
||||
}
|
||||
|
||||
// EncodeDocumentRecord creates standard permission record representing user permissions for a document.
|
||||
func EncodeDocumentRecord(r DocumentRecord, a Action) (p Permission) {
|
||||
p = Permission{}
|
||||
p.OrgID = r.OrgID
|
||||
p.Who = "user"
|
||||
p.WhoID = r.UserID
|
||||
p.Location = "document"
|
||||
p.RefID = r.DocumentID
|
||||
p.Action = a
|
||||
p.Scope = "object" // default to row level permission
|
||||
|
||||
return
|
||||
}
|
|
@ -11,59 +11,6 @@
|
|||
|
||||
package permission
|
||||
|
||||
import "time"
|
||||
|
||||
// Permission represents a permission for a space and is persisted to the database.
|
||||
type Permission struct {
|
||||
ID uint64 `json:"id"`
|
||||
OrgID string `json:"orgId"`
|
||||
Who string `json:"who"` // user, role
|
||||
WhoID string `json:"whoId"` // either a user or role ID
|
||||
Action Action `json:"action"` // view, edit, delete
|
||||
Scope string `json:"scope"` // object, table
|
||||
Location string `json:"location"` // table name
|
||||
RefID string `json:"refId"` // id of row in table / blank when scope=table
|
||||
Created time.Time `json:"created"`
|
||||
}
|
||||
|
||||
// Action details type of action
|
||||
type Action string
|
||||
|
||||
const (
|
||||
// SpaceView action means you can view a space and documents therein
|
||||
SpaceView Action = "view"
|
||||
|
||||
// SpaceManage action means you can add, remove users, set permissions, but not delete that space
|
||||
SpaceManage Action = "manage"
|
||||
|
||||
// SpaceOwner action means you can delete a space and do all SpaceManage functions
|
||||
SpaceOwner Action = "own"
|
||||
|
||||
// DocumentAdd action means you can create/upload documents to a space
|
||||
DocumentAdd Action = "doc-add"
|
||||
|
||||
// DocumentEdit action means you can edit documents in a space
|
||||
DocumentEdit Action = "doc-edit"
|
||||
|
||||
// DocumentDelete means you can delete documents in a space
|
||||
DocumentDelete Action = "doc-delete"
|
||||
|
||||
// DocumentMove means you can move documents between spaces
|
||||
DocumentMove Action = "doc-move"
|
||||
|
||||
// DocumentCopy means you can copy documents within and between spaces
|
||||
DocumentCopy Action = "doc-copy"
|
||||
|
||||
// DocumentTemplate means you can create, edit and delete document templates and content blocks
|
||||
DocumentTemplate Action = "doc-template"
|
||||
|
||||
// DocumentApprove means you can approve a change to a document
|
||||
DocumentApprove Action = "doc-approve"
|
||||
|
||||
// CategoryView action means you can view a category and documents therein
|
||||
CategoryView Action = "view"
|
||||
)
|
||||
|
||||
// Record represents space permissions for a user on a space.
|
||||
// This data structure is made from database permission records for the space,
|
||||
// and it is designed to be sent to HTTP clients (web, mobile).
|
||||
|
@ -123,23 +70,6 @@ func DecodeUserPermissions(perm []Permission) (r Record) {
|
|||
return
|
||||
}
|
||||
|
||||
// PermissionsModel details which users have what permissions on a given space.
|
||||
type PermissionsModel struct {
|
||||
Message string
|
||||
Permissions []Record
|
||||
}
|
||||
|
||||
// ContainsPermission checks if action matches one of the required actions?
|
||||
func ContainsPermission(action Action, actions ...Action) bool {
|
||||
for _, a := range actions {
|
||||
if action == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// EncodeUserPermissions returns multiple user permission records
|
||||
// for a given space, using flat permission summary record.
|
||||
func EncodeUserPermissions(r Record) (perm []Permission) {
|
||||
|
@ -205,3 +135,9 @@ type CategoryViewRequestModel struct {
|
|||
CategoryID string `json:"categoryID"`
|
||||
UserID string `json:"userId"`
|
||||
}
|
||||
|
||||
// SpaceRequestModel details which users have what permissions on a given space.
|
||||
type SpaceRequestModel struct {
|
||||
Message string
|
||||
Permissions []Record
|
||||
}
|
|
@ -89,6 +89,9 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) {
|
|||
Add(rt, RoutePrefixPrivate, "documents/{documentID}", []string{"GET", "OPTIONS"}, nil, document.Get)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}", []string{"PUT", "OPTIONS"}, nil, document.Update)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}", []string{"DELETE", "OPTIONS"}, nil, document.Delete)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/permissions", []string{"GET", "OPTIONS"}, nil, permission.GetDocumentPermissions)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/permissions", []string{"PUT", "OPTIONS"}, nil, permission.SetDocumentPermissions)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/permissions/user", []string{"GET", "OPTIONS"}, nil, permission.GetUserDocumentPermissions)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/activity", []string{"GET", "OPTIONS"}, nil, document.Activity)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages/level", []string{"POST", "OPTIONS"}, nil, page.ChangePageLevel)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue