github.com/elliott5/community@v0.14.1-0.20160709191136-823126fb026a/documize/api/endpoint/document_endpoint.go (about)

     1  // Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
     2  //
     3  // This software (Documize Community Edition) is licensed under
     4  // GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
     5  //
     6  // You can operate outside the AGPL restrictions by purchasing
     7  // Documize Enterprise Edition and obtaining a commercial license
     8  // by contacting <sales@documize.com>.
     9  //
    10  // https://documize.com
    11  
    12  package endpoint
    13  
    14  import (
    15  	"database/sql"
    16  	"encoding/json"
    17  	"fmt"
    18  	"io/ioutil"
    19  	"net/http"
    20  	"net/url"
    21  
    22  	"github.com/documize/community/documize/api/entity"
    23  	"github.com/documize/community/documize/api/plugins"
    24  	"github.com/documize/community/documize/api/request"
    25  	"github.com/documize/community/documize/api/store"
    26  	"github.com/documize/community/wordsmith/log"
    27  	"github.com/documize/community/wordsmith/utility"
    28  
    29  	"github.com/gorilla/mux"
    30  )
    31  
    32  // SearchDocuments endpoint takes a list of keywords and returns a list of document references matching those keywords.
    33  func SearchDocuments(w http.ResponseWriter, r *http.Request) {
    34  	method := "SearchDocuments"
    35  	p := request.GetPersister(r)
    36  
    37  	query := r.URL.Query()
    38  	keywords := query.Get("keywords")
    39  	decoded, err := url.QueryUnescape(keywords)
    40  	log.IfErr(err)
    41  
    42  	results, err := p.SearchDocument(decoded)
    43  
    44  	if err != nil {
    45  		writeServerError(w, method, err)
    46  		return
    47  	}
    48  
    49  	// Put in slugs for easy UI display of search URL
    50  	for key, result := range results {
    51  		result.DocumentSlug = utility.MakeSlug(result.DocumentTitle)
    52  		result.FolderSlug = utility.MakeSlug(result.LabelName)
    53  		results[key] = result
    54  	}
    55  
    56  	if len(results) == 0 {
    57  		results = []entity.DocumentSearch{}
    58  	}
    59  
    60  	data, err := json.Marshal(results)
    61  
    62  	if err != nil {
    63  		writeJSONMarshalError(w, method, "search", err)
    64  		return
    65  	}
    66  
    67  	writeSuccessBytes(w, data)
    68  }
    69  
    70  // GetDocument is an endpoint that returns the document-level information for a given documentID.
    71  func GetDocument(w http.ResponseWriter, r *http.Request) {
    72  	method := "GetDocument"
    73  	p := request.GetPersister(r)
    74  
    75  	params := mux.Vars(r)
    76  	id := params["documentID"]
    77  
    78  	if len(id) == 0 {
    79  		writeMissingDataError(w, method, "documentID")
    80  		return
    81  	}
    82  
    83  	document, err := p.GetDocument(id)
    84  
    85  	if err == sql.ErrNoRows {
    86  		writeNotFoundError(w, method, id)
    87  		return
    88  	}
    89  
    90  	if err != nil {
    91  		writeGeneralSQLError(w, method, err)
    92  		return
    93  	}
    94  
    95  	if !p.CanViewDocumentInFolder(document.LabelID) {
    96  		writeForbiddenError(w)
    97  		return
    98  	}
    99  
   100  	json, err := json.Marshal(document)
   101  
   102  	if err != nil {
   103  		writeJSONMarshalError(w, method, "document", err)
   104  		return
   105  	}
   106  
   107  	writeSuccessBytes(w, json)
   108  }
   109  
   110  // GetDocumentMeta is an endpoint returning the metadata for a document.
   111  func GetDocumentMeta(w http.ResponseWriter, r *http.Request) {
   112  	method := "GetDocumentMeta"
   113  	p := request.GetPersister(r)
   114  
   115  	params := mux.Vars(r)
   116  	id := params["documentID"]
   117  
   118  	if len(id) == 0 {
   119  		writeMissingDataError(w, method, "documentID")
   120  		return
   121  	}
   122  
   123  	meta, err := p.GetDocumentMeta(id)
   124  
   125  	if err == sql.ErrNoRows {
   126  		writeNotFoundError(w, method, id)
   127  		return
   128  	}
   129  
   130  	if err != nil {
   131  		writeGeneralSQLError(w, method, err)
   132  		return
   133  	}
   134  
   135  	json, err := json.Marshal(meta)
   136  
   137  	if err != nil {
   138  		writeJSONMarshalError(w, method, "document", err)
   139  		return
   140  	}
   141  
   142  	writeSuccessBytes(w, json)
   143  }
   144  
   145  // GetDocumentsByFolder is an endpoint that returns the documents in a given folder.
   146  func GetDocumentsByFolder(w http.ResponseWriter, r *http.Request) {
   147  	method := "GetDocumentsByFolder"
   148  	p := request.GetPersister(r)
   149  
   150  	query := r.URL.Query()
   151  	folderID := query.Get("folder")
   152  
   153  	if len(folderID) == 0 {
   154  		writeMissingDataError(w, method, "folder")
   155  		return
   156  	}
   157  
   158  	if !p.CanViewFolder(folderID) {
   159  		writeForbiddenError(w)
   160  		return
   161  	}
   162  
   163  	documents, err := p.GetDocumentsByFolder(folderID)
   164  
   165  	if err != nil && err != sql.ErrNoRows {
   166  		writeServerError(w, method, err)
   167  		return
   168  	}
   169  
   170  	json, err := json.Marshal(documents)
   171  
   172  	if err != nil {
   173  		writeJSONMarshalError(w, method, "document", err)
   174  		return
   175  	}
   176  
   177  	writeSuccessBytes(w, json)
   178  }
   179  
   180  // GetDocumentsByTag is an endpoint that returns the documents with a given tag.
   181  func GetDocumentsByTag(w http.ResponseWriter, r *http.Request) {
   182  	method := "GetDocumentsByTag"
   183  	p := request.GetPersister(r)
   184  
   185  	query := r.URL.Query()
   186  	tag := query.Get("tag")
   187  
   188  	if len(tag) == 0 {
   189  		writeMissingDataError(w, method, "tag")
   190  		return
   191  	}
   192  
   193  	documents, err := p.GetDocumentsByTag(tag)
   194  
   195  	if err != nil && err != sql.ErrNoRows {
   196  		writeServerError(w, method, err)
   197  		return
   198  	}
   199  
   200  	json, err := json.Marshal(documents)
   201  
   202  	if err != nil {
   203  		writeJSONMarshalError(w, method, "document", err)
   204  		return
   205  	}
   206  
   207  	writeSuccessBytes(w, json)
   208  }
   209  
   210  // DeleteDocument is an endpoint that deletes a document specified by documentID.
   211  func DeleteDocument(w http.ResponseWriter, r *http.Request) {
   212  	method := "DeleteDocument"
   213  	p := request.GetPersister(r)
   214  
   215  	params := mux.Vars(r)
   216  	documentID := params["documentID"]
   217  
   218  	if len(documentID) == 0 {
   219  		writeMissingDataError(w, method, "documentID")
   220  		return
   221  	}
   222  
   223  	if !p.CanChangeDocument(documentID) {
   224  		writeForbiddenError(w)
   225  		return
   226  	}
   227  
   228  	tx, err := request.Db.Beginx()
   229  
   230  	if err != nil {
   231  		writeTransactionError(w, method, err)
   232  		return
   233  	}
   234  
   235  	p.Context.Transaction = tx
   236  
   237  	_, err = p.DeleteDocument(documentID)
   238  
   239  	if err != nil {
   240  		log.IfErr(tx.Rollback())
   241  		writeGeneralSQLError(w, method, err)
   242  		return
   243  	}
   244  
   245  	log.IfErr(tx.Commit())
   246  
   247  	writeSuccessEmptyJSON(w)
   248  }
   249  
   250  // GetDocumentAsDocx returns a Word document.
   251  func GetDocumentAsDocx(w http.ResponseWriter, r *http.Request) {
   252  	method := "GetDocumentAsDocx"
   253  	p := request.GetPersister(r)
   254  
   255  	params := mux.Vars(r)
   256  	documentID := params["documentID"]
   257  
   258  	if len(documentID) == 0 {
   259  		writeMissingDataError(w, method, "documentID")
   260  		return
   261  	}
   262  
   263  	document, err := p.GetDocument(documentID)
   264  
   265  	if err == sql.ErrNoRows {
   266  		writeNotFoundError(w, method, documentID)
   267  		return
   268  	}
   269  
   270  	if err != nil {
   271  		writeServerError(w, method, err)
   272  		return
   273  	}
   274  
   275  	if !p.CanViewDocumentInFolder(document.LabelID) {
   276  		writeForbiddenError(w)
   277  		return
   278  	}
   279  
   280  	pages, err := p.GetPages(documentID)
   281  
   282  	if err != nil {
   283  		writeServerError(w, method, err)
   284  		return
   285  	}
   286  
   287  	xtn := "html"
   288  	actions, err := plugins.Lib.Actions("Export")
   289  	if err == nil {
   290  		for _, x := range actions {
   291  			if x == "docx" { // only actually export a docx if we have the plugin
   292  				xtn = x
   293  				break
   294  			}
   295  		}
   296  	}
   297  
   298  	estLen := 0
   299  	for _, page := range pages {
   300  		estLen += len(page.Title) + len(page.Body)
   301  	}
   302  	html := make([]byte, 0, estLen*2) // should be far bigger than we need
   303  	html = append(html, []byte("<html><head></head><body>")...)
   304  	for _, page := range pages {
   305  		html = append(html, []byte(fmt.Sprintf("<h%d>", page.Level))...)
   306  		html = append(html, utility.EscapeHTMLcomplexCharsByte([]byte(page.Title))...)
   307  		html = append(html, []byte(fmt.Sprintf("</h%d>", page.Level))...)
   308  		html = append(html, utility.EscapeHTMLcomplexCharsByte([]byte(page.Body))...)
   309  	}
   310  	html = append(html, []byte("</body></html>")...)
   311  
   312  	export, err := store.ExportAs(xtn, string(html))
   313  	log.Error("store.ExportAs()", err)
   314  
   315  	w.Header().Set("Content-Disposition", "attachment; filename="+utility.MakeSlug(document.Title)+"."+xtn)
   316  	w.Header().Set("Content-Type", "application/octet-stream")
   317  	w.Header().Set("Content-Length", fmt.Sprintf("%d", len(export.File)))
   318  
   319  	writeSuccessBytes(w, export.File)
   320  }
   321  
   322  // UpdateDocument updates an existing document using the
   323  // format described in NewDocumentModel() encoded as JSON in the request.
   324  func UpdateDocument(w http.ResponseWriter, r *http.Request) {
   325  	method := "UpdateDocument"
   326  	p := request.GetPersister(r)
   327  
   328  	if !p.Context.Editor {
   329  		w.WriteHeader(http.StatusForbidden)
   330  		return
   331  	}
   332  
   333  	params := mux.Vars(r)
   334  	documentID := params["documentID"]
   335  
   336  	if len(documentID) == 0 {
   337  		writeMissingDataError(w, method, "documentID")
   338  		return
   339  	}
   340  
   341  	if !p.CanChangeDocument(documentID) {
   342  		writeForbiddenError(w)
   343  		return
   344  	}
   345  
   346  	defer utility.Close(r.Body)
   347  	body, err := ioutil.ReadAll(r.Body)
   348  
   349  	if err != nil {
   350  		writePayloadError(w, method, err)
   351  		return
   352  	}
   353  
   354  	d := entity.Document{}
   355  	err = json.Unmarshal(body, &d)
   356  
   357  	if err != nil {
   358  		writeBadRequestError(w, method, "document")
   359  		return
   360  	}
   361  
   362  	d.RefID = documentID
   363  
   364  	tx, err := request.Db.Beginx()
   365  
   366  	if err != nil {
   367  		writeTransactionError(w, method, err)
   368  		return
   369  	}
   370  
   371  	p.Context.Transaction = tx
   372  
   373  	err = p.UpdateDocument(d)
   374  
   375  	if err != nil {
   376  		log.IfErr(tx.Rollback())
   377  		writeGeneralSQLError(w, method, err)
   378  		return
   379  	}
   380  
   381  	log.IfErr(tx.Commit())
   382  
   383  	writeSuccessEmptyJSON(w)
   384  }