mirror of
https://github.com/documize/community.git
synced 2025-07-22 06:39:43 +02:00
1. handle long username overflow on space permissions screen 2. only show document history to editors 3. removed redundant document editing permission check 4. Ensure subdomain is detected when accepting space invitation
242 lines
6.3 KiB
Go
242 lines
6.3 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 attachment
|
|
|
|
import (
|
|
"bytes"
|
|
"database/sql"
|
|
"fmt"
|
|
"io"
|
|
"mime"
|
|
"net/http"
|
|
|
|
"github.com/documize/community/core/env"
|
|
"github.com/documize/community/core/request"
|
|
"github.com/documize/community/core/response"
|
|
"github.com/documize/community/core/secrets"
|
|
"github.com/documize/community/core/uniqueid"
|
|
"github.com/documize/community/domain"
|
|
"github.com/documize/community/domain/document"
|
|
"github.com/documize/community/domain/organization"
|
|
indexer "github.com/documize/community/domain/search"
|
|
"github.com/documize/community/model/attachment"
|
|
"github.com/documize/community/model/audit"
|
|
uuid "github.com/nu7hatch/gouuid"
|
|
)
|
|
|
|
// Handler contains the runtime information such as logging and database.
|
|
type Handler struct {
|
|
Runtime *env.Runtime
|
|
Store *domain.Store
|
|
Indexer indexer.Indexer
|
|
}
|
|
|
|
// Download is the end-point that responds to a request for a particular attachment
|
|
// by sending the requested file to the client.
|
|
func (h *Handler) Download(w http.ResponseWriter, r *http.Request) {
|
|
method := "attachment.Download"
|
|
ctx := domain.GetRequestContext(r)
|
|
ctx.Subdomain = organization.GetSubdomainFromHost(r)
|
|
|
|
a, err := h.Store.Attachment.GetAttachment(ctx, request.Param(r, "orgID"), request.Param(r, "attachmentID"))
|
|
|
|
if err == sql.ErrNoRows {
|
|
response.WriteNotFoundError(w, method, request.Param(r, "fileID"))
|
|
return
|
|
}
|
|
if err != nil {
|
|
h.Runtime.Log.Error("get attachment", err)
|
|
response.WriteServerError(w, method, err)
|
|
return
|
|
}
|
|
|
|
typ := mime.TypeByExtension("." + a.Extension)
|
|
if typ == "" {
|
|
typ = "application/octet-stream"
|
|
}
|
|
|
|
w.Header().Set("Content-Type", typ)
|
|
w.Header().Set("Content-Disposition", `Attachment; filename="`+a.Filename+`" ; `+`filename*="`+a.Filename+`"`)
|
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(a.Data)))
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
_, err = w.Write(a.Data)
|
|
if err != nil {
|
|
h.Runtime.Log.Error("write attachment", err)
|
|
return
|
|
}
|
|
|
|
h.Store.Audit.Record(ctx, audit.EventTypeAttachmentDownload)
|
|
}
|
|
|
|
// Get is an end-point that returns all of the attachments of a particular documentID.
|
|
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
|
|
method := "attachment.GetAttachments"
|
|
ctx := domain.GetRequestContext(r)
|
|
|
|
documentID := request.Param(r, "documentID")
|
|
if len(documentID) == 0 {
|
|
response.WriteMissingDataError(w, method, "documentID")
|
|
return
|
|
}
|
|
|
|
if !document.CanViewDocument(ctx, *h.Store, documentID) {
|
|
response.WriteForbiddenError(w)
|
|
return
|
|
}
|
|
|
|
a, err := h.Store.Attachment.GetAttachments(ctx, documentID)
|
|
if err != nil && err != sql.ErrNoRows {
|
|
h.Runtime.Log.Error("get attachment", err)
|
|
response.WriteServerError(w, method, err)
|
|
return
|
|
}
|
|
|
|
if len(a) == 0 {
|
|
a = []attachment.Attachment{}
|
|
}
|
|
|
|
response.WriteJSON(w, a)
|
|
}
|
|
|
|
// Delete is an endpoint that deletes a particular document attachment.
|
|
func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
|
method := "attachment.DeleteAttachment"
|
|
ctx := domain.GetRequestContext(r)
|
|
|
|
documentID := request.Param(r, "documentID")
|
|
if len(documentID) == 0 {
|
|
response.WriteMissingDataError(w, method, "documentID")
|
|
return
|
|
}
|
|
|
|
attachmentID := request.Param(r, "attachmentID")
|
|
if len(attachmentID) == 0 {
|
|
response.WriteMissingDataError(w, method, "attachmentID")
|
|
return
|
|
}
|
|
|
|
if !document.CanChangeDocument(ctx, *h.Store, documentID) {
|
|
response.WriteForbiddenError(w)
|
|
return
|
|
}
|
|
|
|
var err error
|
|
ctx.Transaction, err = h.Runtime.Db.Beginx()
|
|
if err != nil {
|
|
h.Runtime.Log.Error("transaction", err)
|
|
response.WriteServerError(w, method, err)
|
|
return
|
|
}
|
|
|
|
_, err = h.Store.Attachment.Delete(ctx, attachmentID)
|
|
if err != nil {
|
|
ctx.Transaction.Rollback()
|
|
response.WriteServerError(w, method, err)
|
|
h.Runtime.Log.Error("delete attachment", err)
|
|
return
|
|
}
|
|
|
|
// Mark references to this document as orphaned
|
|
err = h.Store.Link.MarkOrphanAttachmentLink(ctx, attachmentID)
|
|
if err != nil {
|
|
ctx.Transaction.Rollback()
|
|
response.WriteServerError(w, method, err)
|
|
h.Runtime.Log.Error("delete attachment links", err)
|
|
return
|
|
}
|
|
|
|
h.Store.Audit.Record(ctx, audit.EventTypeAttachmentDelete)
|
|
|
|
ctx.Transaction.Commit()
|
|
|
|
a, _ := h.Store.Attachment.GetAttachments(ctx, documentID)
|
|
d, _ := h.Store.Document.Get(ctx, documentID)
|
|
go h.Indexer.IndexDocument(ctx, d, a)
|
|
|
|
response.WriteEmpty(w)
|
|
}
|
|
|
|
// Add stores files against a document.
|
|
func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
|
method := "attachment.Add"
|
|
ctx := domain.GetRequestContext(r)
|
|
|
|
documentID := request.Param(r, "documentID")
|
|
if len(documentID) == 0 {
|
|
response.WriteMissingDataError(w, method, "documentID")
|
|
return
|
|
}
|
|
|
|
if !document.CanChangeDocument(ctx, *h.Store, documentID) {
|
|
response.WriteForbiddenError(w)
|
|
return
|
|
}
|
|
|
|
filedata, filename, err := r.FormFile("attachment")
|
|
if err != nil {
|
|
response.WriteMissingDataError(w, method, "attachment")
|
|
return
|
|
}
|
|
|
|
b := new(bytes.Buffer)
|
|
_, err = io.Copy(b, filedata)
|
|
if err != nil {
|
|
response.WriteServerError(w, method, err)
|
|
h.Runtime.Log.Error("add attachment", err)
|
|
return
|
|
}
|
|
|
|
var job = "some-uuid"
|
|
newUUID, err := uuid.NewV4()
|
|
if err != nil {
|
|
h.Runtime.Log.Error("uuid", err)
|
|
response.WriteServerError(w, method, err)
|
|
return
|
|
}
|
|
job = newUUID.String()
|
|
|
|
var a attachment.Attachment
|
|
refID := uniqueid.Generate()
|
|
a.RefID = refID
|
|
a.DocumentID = documentID
|
|
a.Job = job
|
|
random := secrets.GenerateSalt()
|
|
a.FileID = random[0:9]
|
|
a.Filename = filename.Filename
|
|
a.Data = b.Bytes()
|
|
|
|
ctx.Transaction, err = h.Runtime.Db.Beginx()
|
|
if err != nil {
|
|
response.WriteServerError(w, method, err)
|
|
h.Runtime.Log.Error("transaction", err)
|
|
return
|
|
}
|
|
|
|
err = h.Store.Attachment.Add(ctx, a)
|
|
if err != nil {
|
|
ctx.Transaction.Rollback()
|
|
response.WriteServerError(w, method, err)
|
|
h.Runtime.Log.Error("add attachment", err)
|
|
return
|
|
}
|
|
|
|
h.Store.Audit.Record(ctx, audit.EventTypeAttachmentAdd)
|
|
|
|
ctx.Transaction.Commit()
|
|
|
|
all, _ := h.Store.Attachment.GetAttachments(ctx, documentID)
|
|
d, _ := h.Store.Document.Get(ctx, documentID)
|
|
go h.Indexer.IndexDocument(ctx, d, all)
|
|
|
|
response.WriteEmpty(w)
|
|
}
|