github.com/elliott5/community@v0.14.1-0.20160709191136-823126fb026a/documize/api/endpoint/templates_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  	"encoding/json"
    16  	"errors"
    17  	"fmt"
    18  	"io/ioutil"
    19  	"net/http"
    20  
    21  	"database/sql"
    22  
    23  	"github.com/gorilla/mux"
    24  
    25  	"github.com/documize/community/documize/api/convert"
    26  	"github.com/documize/community/documize/api/endpoint/models"
    27  	"github.com/documize/community/documize/api/entity"
    28  	"github.com/documize/community/documize/api/request"
    29  	"github.com/documize/community/documize/api/util"
    30  	"github.com/documize/community/wordsmith/api"
    31  	"github.com/documize/community/wordsmith/log"
    32  	"github.com/documize/community/wordsmith/utility"
    33  
    34  	uuid "github.com/nu7hatch/gouuid"
    35  )
    36  
    37  // SaveAsTemplate saves existing document as a template.
    38  func SaveAsTemplate(w http.ResponseWriter, r *http.Request) {
    39  	method := "SaveAsTemplate"
    40  	p := request.GetPersister(r)
    41  
    42  	var model struct {
    43  		DocumentID string
    44  		Name       string
    45  		Excerpt    string
    46  	}
    47  
    48  	defer utility.Close(r.Body)
    49  	body, err := ioutil.ReadAll(r.Body)
    50  
    51  	if err != nil {
    52  		writeBadRequestError(w, method, "Bad payload")
    53  		return
    54  	}
    55  
    56  	err = json.Unmarshal(body, &model)
    57  
    58  	if err != nil {
    59  		writePayloadError(w, method, err)
    60  		return
    61  	}
    62  
    63  	if !p.CanChangeDocument(model.DocumentID) {
    64  		writeForbiddenError(w)
    65  		return
    66  	}
    67  
    68  	// DB transaction
    69  	tx, err := request.Db.Beginx()
    70  
    71  	if err != nil {
    72  		writeTransactionError(w, method, err)
    73  		return
    74  	}
    75  
    76  	p.Context.Transaction = tx
    77  
    78  	// Duplicate document
    79  	doc, err := p.GetDocument(model.DocumentID)
    80  
    81  	if err != nil {
    82  		writeServerError(w, method, err)
    83  		return
    84  	}
    85  
    86  	docID := util.UniqueID()
    87  	doc.Template = true
    88  	doc.Title = model.Name
    89  	doc.Excerpt = model.Excerpt
    90  	doc.RefID = docID
    91  	doc.ID = 0
    92  	doc.Template = true
    93  
    94  	// Duplicate pages and associated meta
    95  	pages, err := p.GetPages(model.DocumentID)
    96  	var pageModel []models.PageModel
    97  
    98  	if err != nil {
    99  		writeServerError(w, method, err)
   100  		return
   101  	}
   102  
   103  	for _, page := range pages {
   104  		page.DocumentID = docID
   105  		page.ID = 0
   106  
   107  		meta, err2 := p.GetPageMeta(page.RefID)
   108  
   109  		if err2 != nil {
   110  			writeServerError(w, method, err2)
   111  			return
   112  		}
   113  
   114  		pageID := util.UniqueID()
   115  		page.RefID = pageID
   116  
   117  		meta.PageID = pageID
   118  		meta.DocumentID = docID
   119  
   120  		m := models.PageModel{}
   121  
   122  		m.Page = page
   123  		m.Meta = meta
   124  
   125  		pageModel = append(pageModel, m)
   126  	}
   127  
   128  	// Duplicate attachments
   129  	attachments, err := p.GetAttachments(model.DocumentID)
   130  
   131  	for i, a := range attachments {
   132  		a.DocumentID = docID
   133  		a.RefID = util.UniqueID()
   134  		a.ID = 0
   135  		attachments[i] = a
   136  	}
   137  
   138  	// Now create the template: document, attachments, pages and their meta
   139  	err = p.AddDocument(doc)
   140  
   141  	if err != nil {
   142  		log.IfErr(tx.Rollback())
   143  		writeGeneralSQLError(w, method, err)
   144  		return
   145  	}
   146  
   147  	for _, a := range attachments {
   148  		err = p.AddAttachment(a)
   149  
   150  		if err != nil {
   151  			log.IfErr(tx.Rollback())
   152  			writeGeneralSQLError(w, method, err)
   153  			return
   154  		}
   155  	}
   156  
   157  	for _, m := range pageModel {
   158  		err = p.AddPage(m)
   159  
   160  		if err != nil {
   161  			log.IfErr(tx.Rollback())
   162  			writeGeneralSQLError(w, method, err)
   163  			return
   164  		}
   165  	}
   166  
   167  	// Commit and return new document template
   168  	log.IfErr(tx.Commit())
   169  
   170  	doc, err = p.GetDocument(docID)
   171  
   172  	if err != nil {
   173  		writeGeneralSQLError(w, method, err)
   174  		return
   175  	}
   176  
   177  	d, err := json.Marshal(doc)
   178  
   179  	if err != nil {
   180  		writeJSONMarshalError(w, method, "document", err)
   181  		return
   182  	}
   183  
   184  	writeSuccessBytes(w, d)
   185  }
   186  
   187  // GetSavedTemplates returns all templates saved by the user
   188  func GetSavedTemplates(w http.ResponseWriter, r *http.Request) {
   189  	method := "GetSavedTemplates"
   190  	p := request.GetPersister(r)
   191  
   192  	documents, err := p.GetDocumentTemplates()
   193  
   194  	if err != nil {
   195  		writeGeneralSQLError(w, method, err)
   196  		return
   197  	}
   198  
   199  	templates := []entity.Template{}
   200  
   201  	for _, d := range documents {
   202  
   203  		var template = entity.Template{}
   204  
   205  		template.ID = d.RefID
   206  		template.Title = d.Title
   207  		template.Description = d.Excerpt
   208  		template.Author = ""
   209  		template.Dated = d.Created
   210  		template.Type = entity.TemplateTypePrivate
   211  
   212  		templates = append(templates, template)
   213  	}
   214  
   215  	data, err := json.Marshal(templates)
   216  
   217  	if err != nil {
   218  		writeJSONMarshalError(w, method, "template", err)
   219  		return
   220  	}
   221  
   222  	writeSuccessBytes(w, data)
   223  }
   224  
   225  // GetStockTemplates returns available templates from the public Documize repository.
   226  func GetStockTemplates(w http.ResponseWriter, r *http.Request) {
   227  	method := "GetStockTemplates"
   228  
   229  	var templates = fetchStockTemplates()
   230  
   231  	json, err := json.Marshal(templates)
   232  
   233  	if err != nil {
   234  		writeJSONMarshalError(w, method, "template", err)
   235  		return
   236  	}
   237  
   238  	writeSuccessBytes(w, json)
   239  }
   240  
   241  // StartDocumentFromStockTemplate creates new document using one of the stock templates
   242  func StartDocumentFromStockTemplate(w http.ResponseWriter, r *http.Request) {
   243  	method := "StartDocumentFromStockTemplate"
   244  	p := request.GetPersister(r)
   245  
   246  	if !p.Context.Editor {
   247  		writeForbiddenError(w)
   248  		return
   249  	}
   250  
   251  	params := mux.Vars(r)
   252  	folderID := params["folderID"]
   253  
   254  	if len(folderID) == 0 {
   255  		writeMissingDataError(w, method, "folderID")
   256  		return
   257  	}
   258  
   259  	templateID := params["templateID"]
   260  
   261  	if len(templateID) == 0 {
   262  		writeMissingDataError(w, method, "templateID")
   263  		return
   264  	}
   265  
   266  	filename, template, err := fetchStockTemplate(templateID)
   267  
   268  	if err != nil {
   269  		writeServerError(w, method, err)
   270  		return
   271  	}
   272  
   273  	if len(template) == 0 {
   274  		writeBadRequestError(w, method, "No data found in template")
   275  	}
   276  
   277  	fileRequest := api.DocumentConversionRequest{}
   278  	fileRequest.Filedata = template
   279  	fileRequest.Filename = fmt.Sprintf("%s.docx", filename)
   280  	fileRequest.PageBreakLevel = 4
   281  	//fileRequest.Job = templateID
   282  	//fileRequest.OrgID = p.Context.OrgID
   283  
   284  	//	fileResult, err := store.RunConversion(fileRequest)
   285  	//fileResultI, err := plugins.Lib.Run(nil, "Convert", "docx", fileRequest)
   286  	fileResult, err := convert.Convert(nil, "docx", &fileRequest)
   287  	if err != nil {
   288  		writeServerError(w, method, err)
   289  		return
   290  	}
   291  
   292  	model, err := processDocument(p, fileRequest.Filename, templateID, folderID, fileResult)
   293  
   294  	if err != nil {
   295  		writeServerError(w, method, err)
   296  		return
   297  	}
   298  
   299  	json, err := json.Marshal(model)
   300  
   301  	if err != nil {
   302  		writeJSONMarshalError(w, method, "stockTemplate", err)
   303  		return
   304  	}
   305  
   306  	writeSuccessBytes(w, json)
   307  }
   308  
   309  // StartDocumentFromSavedTemplate creates new document using a saved document as a template.
   310  // If template ID is ZERO then we provide an Empty Document as the new document.
   311  func StartDocumentFromSavedTemplate(w http.ResponseWriter, r *http.Request) {
   312  	method := "StartDocumentFromSavedTemplate"
   313  	p := request.GetPersister(r)
   314  
   315  	params := mux.Vars(r)
   316  	folderID := params["folderID"]
   317  
   318  	if len(folderID) == 0 {
   319  		writeMissingDataError(w, method, "folderID")
   320  		return
   321  	}
   322  
   323  	templateID := params["templateID"]
   324  
   325  	// We are OK with zero valued template ID because it signals 'give me empty document'
   326  	if len(templateID) == 0 {
   327  		writeMissingDataError(w, method, "templateID")
   328  		return
   329  	}
   330  
   331  	// Define an empty document just in case user wanted one.
   332  	var err error
   333  	var d = entity.Document{}
   334  	d.Title = "New Document"
   335  	d.Location = fmt.Sprintf("template-%s", templateID)
   336  	d.Excerpt = "A new document"
   337  	d.Slug = utility.MakeSlug(d.Title)
   338  	d.Tags = ""
   339  	d.LabelID = folderID
   340  	documentID := util.UniqueID()
   341  	d.RefID = documentID
   342  
   343  	var pages = []entity.Page{}
   344  	//var pages = make([]entity.Page, 1, 1)
   345  	//pages[0] = entity.Page{}
   346  	//pages[0].Title = "Heading"
   347  	//pages[0].Body = "<p>Some content here.</p>"
   348  	//pages[0].Level = 1
   349  	//pages[0].Sequence = 1
   350  
   351  	var attachments = []entity.Attachment{}
   352  
   353  	// Fetch document and associated pages, attachments if we have template ID
   354  	if templateID != "0" {
   355  		d, err = p.GetDocument(templateID)
   356  
   357  		if err == sql.ErrNoRows {
   358  			api.WriteError(w, errors.New("NotFound"))
   359  			return
   360  		}
   361  
   362  		if err != nil {
   363  			api.WriteError(w, err)
   364  			return
   365  		}
   366  
   367  		pages, err = p.GetPages(templateID)
   368  		attachments, err = p.GetAttachmentsWithData(templateID)
   369  	}
   370  
   371  	// create new document
   372  	tx, err := request.Db.Beginx()
   373  
   374  	if err != nil {
   375  		writeTransactionError(w, method, err)
   376  		return
   377  	}
   378  
   379  	p.Context.Transaction = tx
   380  
   381  	// Prepare new document
   382  	documentID = util.UniqueID()
   383  	d.RefID = documentID
   384  	d.Template = false
   385  	d.LabelID = folderID
   386  	d.UserID = p.Context.UserID
   387  
   388  	err = p.AddDocument(d)
   389  
   390  	if err != nil {
   391  		log.IfErr(tx.Rollback())
   392  		writeGeneralSQLError(w, method, err)
   393  		return
   394  	}
   395  
   396  	for _, page := range pages {
   397  		page.DocumentID = documentID
   398  		pageID := util.UniqueID()
   399  		page.RefID = pageID
   400  
   401  		meta := entity.PageMeta{}
   402  		meta.PageID = pageID
   403  		meta.RawBody = page.Body
   404  
   405  		model := models.PageModel{}
   406  		model.Page = page
   407  		model.Meta = meta
   408  
   409  		err = p.AddPage(model)
   410  
   411  		if err != nil {
   412  			log.IfErr(tx.Rollback())
   413  			writeGeneralSQLError(w, method, err)
   414  			return
   415  		}
   416  	}
   417  
   418  	newUUID, err := uuid.NewV4()
   419  
   420  	if err != nil {
   421  		log.IfErr(tx.Rollback())
   422  		writeServerError(w, method, err)
   423  		return
   424  	}
   425  
   426  	for _, a := range attachments {
   427  		a.DocumentID = documentID
   428  		a.Job = newUUID.String()
   429  		random := util.GenerateSalt()
   430  		a.FileID = random[0:9]
   431  		attachmentID := util.UniqueID()
   432  		a.RefID = attachmentID
   433  
   434  		err = p.AddAttachment(a)
   435  
   436  		if err != nil {
   437  			log.IfErr(tx.Rollback())
   438  			writeServerError(w, method, err)
   439  			return
   440  		}
   441  	}
   442  
   443  	log.IfErr(tx.Commit())
   444  
   445  	newDocument, err := p.GetDocument(documentID)
   446  
   447  	if err != nil {
   448  		writeServerError(w, method, err)
   449  		return
   450  	}
   451  
   452  	data, err := json.Marshal(newDocument)
   453  
   454  	if err != nil {
   455  		writeJSONMarshalError(w, method, "document", err)
   456  		return
   457  	}
   458  
   459  	writeSuccessBytes(w, data)
   460  }
   461  
   462  type templateConfig struct {
   463  	ID          string `json:"id"`
   464  	Description string `json:"description"`
   465  	Author      string `json:"author"`
   466  	Title       string `json:"title"`
   467  }
   468  
   469  func fetchStockTemplates() (templates []entity.Template) {
   470  	path := "./templates"
   471  	templates = make([]entity.Template, 0)
   472  
   473  	folders, err := ioutil.ReadDir(path)
   474  	log.IfErr(err)
   475  	for _, folder := range folders {
   476  
   477  		if folder.IsDir() {
   478  			files, err := ioutil.ReadDir(path + "/" + folder.Name())
   479  			log.IfErr(err)
   480  
   481  			for _, file := range files {
   482  				if !file.IsDir() && file.Name() == "template.json" {
   483  					data, err := ioutil.ReadFile(path + "/" + folder.Name() + "/" + file.Name())
   484  
   485  					if err != nil {
   486  						log.Error("error reading template.json", err)
   487  					} else {
   488  						var config = templateConfig{}
   489  						err = json.Unmarshal(data, &config)
   490  
   491  						if err != nil {
   492  							log.Error("error parsing template.json", err)
   493  						} else {
   494  							var template = entity.Template{}
   495  
   496  							template.ID = config.ID
   497  							template.Title = config.Title
   498  							template.Description = config.Description
   499  							template.Author = config.Author
   500  							template.Type = entity.TemplateTypePublic
   501  
   502  							templates = append(templates, template)
   503  						}
   504  					}
   505  				}
   506  			}
   507  		}
   508  	}
   509  
   510  	return
   511  }
   512  
   513  func fetchStockTemplate(ID string) (filename string, template []byte, err error) {
   514  
   515  	path := "./templates"
   516  	folders, err := ioutil.ReadDir(path)
   517  	if err != nil {
   518  		log.Error("error reading template directory", err)
   519  		return "", nil, err
   520  	}
   521  
   522  	for _, folder := range folders {
   523  		if folder.IsDir() {
   524  			files, err := ioutil.ReadDir(path + "/" + folder.Name())
   525  			if err != nil {
   526  				log.Error("error reading template sub-dir", err)
   527  				return "", nil, err
   528  			}
   529  
   530  			for _, file := range files {
   531  				if !file.IsDir() && file.Name() == "template.json" {
   532  					data, err := ioutil.ReadFile(path + "/" + folder.Name() + "/template.json")
   533  
   534  					if err != nil {
   535  						log.Error("error reading template.json", err)
   536  						return "", nil, err
   537  					}
   538  
   539  					var config = templateConfig{}
   540  					err = json.Unmarshal(data, &config)
   541  
   542  					if err != nil {
   543  						log.Error("error parsing template.json", err)
   544  						return "", nil, err
   545  					}
   546  
   547  					if config.ID == ID {
   548  						template, err = ioutil.ReadFile(path + "/" + folder.Name() + "/template.docx")
   549  						return folder.Name(), template, err
   550  					}
   551  				}
   552  			}
   553  		}
   554  	}
   555  
   556  	return
   557  }