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 }