diff --git a/app/app/components/document/document-toolbar.js b/app/app/components/document/document-toolbar.js index bbd26949..253deb2a 100644 --- a/app/app/components/document/document-toolbar.js +++ b/app/app/components/document/document-toolbar.js @@ -22,6 +22,11 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { description: "" }, + didReceiveAttrs() { + this.set('saveTemplate.name', this.get('document.name')); + this.set('saveTemplate.description', this.get('document.excerpt')); + }, + didRender() { if (this.get('isEditor')) { this.addTooltip(document.getElementById("attachment-button")); @@ -99,30 +104,21 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { }, saveTemplate() { - var templateName = this.get('saveTemplate.name'); - var templateDescription = this.get('saveTemplate.description'); + var name = this.get('saveTemplate.name'); + var excerpt = this.get('saveTemplate.description'); - if (is.empty(templateName)) { + if (is.empty(name)) { $("#new-template-name").addClass("error").focus(); return false; } - if (is.empty(templateDescription)) { + if (is.empty(excerpt)) { $("#new-template-desc").addClass("error").focus(); return false; } - - let doc = this.get('document'); - doc.set('template', true); - doc.set('name', templateName); - doc.set('excerpt', templateDescription); - - this.set('saveTemplate.name', ""); - this.set('saveTemplate.description', ""); - - this.showNotification('Templated'); - this.attrs.onSaveTemplate(doc); + this.showNotification('Template saved'); + this.attrs.onSaveTemplate(name, excerpt); return true; }, diff --git a/app/app/pods/document/index/controller.js b/app/app/pods/document/index/controller.js index fb3712e3..de491176 100644 --- a/app/app/pods/document/index/controller.js +++ b/app/app/pods/document/index/controller.js @@ -3,6 +3,7 @@ import NotifierMixin from '../../../mixins/notifier'; export default Ember.Controller.extend(NotifierMixin, { documentService: Ember.inject.service('document'), + templateService: Ember.inject.service('template'), queryParams: ['page'], page: null, @@ -170,6 +171,11 @@ export default Ember.Controller.extend(NotifierMixin, { } }, + onSaveTemplate(name, desc) { + this.get('templateService').saveAsTemplate(this.model.get('id'), name, desc).then(function() { + }); + }, + onDocumentChange(doc) { let self = this; this.get('documentService').save(doc).then(function() { diff --git a/app/app/pods/document/index/template.hbs b/app/app/pods/document/index/template.hbs index 7673b41f..2c036ac4 100644 --- a/app/app/pods/document/index/template.hbs +++ b/app/app/pods/document/index/template.hbs @@ -2,7 +2,7 @@ {{header/message-box message=model.excerpt}} {{/header/page-navigation}} -{{document/document-toolbar document=model pages=pages meta=meta folder=folder isEditor=isEditor users=users owner=owner onSaveTemplate=(action 'onDocumentChange') onDocumentChange=(action 'onDocumentChange') onAttachmentUpload=(action +{{document/document-toolbar document=model pages=pages meta=meta folder=folder isEditor=isEditor users=users owner=owner onSaveTemplate=(action 'onSaveTemplate') onDocumentChange=(action 'onDocumentChange') onAttachmentUpload=(action 'onAttachmentUpload') onDocumentDelete=(action 'onDocumentDelete')}}
diff --git a/app/app/services/template.js b/app/app/services/template.js index 86b48c9e..ec99a59b 100644 --- a/app/app/services/template.js +++ b/app/app/services/template.js @@ -62,5 +62,20 @@ export default Ember.Service.extend({ return this.get('ajax').request(url, { method: 'GET' }); - } + }, + + saveAsTemplate(documentId, name, excerpt) { + let url = this.get('sessionService').appMeta.getUrl("templates"); + let payload = { + DocumentID: documentId, + Name: name, + Excerpt: excerpt + }; + + return this.get('ajax').request(url, { + method: 'POST', + data: JSON.stringify(payload) + }).then(() => { + }); + } }); diff --git a/app/app/styles/view/document/content.scss b/app/app/styles/view/document/content.scss index a66bf2dc..a24d3f31 100644 --- a/app/app/styles/view/document/content.scss +++ b/app/app/styles/view/document/content.scss @@ -69,10 +69,12 @@ } .is-template { - color: $color-green; + color: $color-gray; font-weight: bold; - font-size: 1.3em; - margin-bottom: 20px; + font-size: 1.5em; + margin-bottom: 50px; + padding-bottom: 5px; + @include border-bottom(1px); } > .pages { diff --git a/app/app/templates/components/document/document-toolbar.hbs b/app/app/templates/components/document/document-toolbar.hbs index 26ae373f..fedacb44 100644 --- a/app/app/templates/components/document/document-toolbar.hbs +++ b/app/app/templates/components/document/document-toolbar.hbs @@ -77,15 +77,15 @@

Are you sure you want to delete this document?

There is no undo!

{{/dropdown-dialog}} - {{#dropdown-dialog target="save-template-button" position="bottom right" button="Save" color="flat-green" onAction=(action 'saveTemplate') focusOn="new-template-name"}} + {{#dropdown-dialog target="save-template-button" position="bottom right" button="Save as Template" color="flat-green" onAction=(action 'saveTemplate') focusOn="new-template-name"}}
- -
Short title for this type of document
+ +
Short name for this type of document
{{input type='text' id="new-template-name" value=saveTemplate.name}}
- +
Explain use case for this template
{{textarea value=saveTemplate.description rows="3" id="new-template-desc"}}
diff --git a/app/tests/index.html b/app/tests/index.html index 87c5d442..a955841e 100644 --- a/app/tests/index.html +++ b/app/tests/index.html @@ -11,6 +11,14 @@ + + {{content-for "head"}} {{content-for "test-head"}} diff --git a/documize/api/endpoint/router.go b/documize/api/endpoint/router.go index 9bb4d255..f050cff2 100644 --- a/documize/api/endpoint/router.go +++ b/documize/api/endpoint/router.go @@ -145,8 +145,8 @@ func buildSecureRoutes() *mux.Router { router.HandleFunc("/api/documents/{documentID}", GetDocument).Methods("GET", "OPTIONS") router.HandleFunc("/api/documents/{documentID}", UpdateDocument).Methods("PUT", "OPTIONS") router.HandleFunc("/api/documents/{documentID}", DeleteDocument).Methods("DELETE", "OPTIONS") - // Document Meta + // Document Meta router.HandleFunc("/api/documents/{documentID}/meta", GetDocumentMeta).Methods("GET", "OPTIONS") // Document Page @@ -198,6 +198,7 @@ func buildSecureRoutes() *mux.Router { router.HandleFunc("/api/search", SearchDocuments).Methods("GET", "OPTIONS") // Templates + router.HandleFunc("/api/templates", SaveAsTemplate).Methods("POST", "OPTIONS") router.HandleFunc("/api/templates", GetSavedTemplates).Methods("GET", "OPTIONS") router.HandleFunc("/api/templates/stock", GetStockTemplates).Methods("GET", "OPTIONS") router.HandleFunc("/api/templates/{templateID}/folder/{folderID}", StartDocumentFromStockTemplate).Methods("POST", "OPTIONS").Queries("type", "stock") diff --git a/documize/api/endpoint/templates_endpoint.go b/documize/api/endpoint/templates_endpoint.go index 78c6b05b..4974b235 100644 --- a/documize/api/endpoint/templates_endpoint.go +++ b/documize/api/endpoint/templates_endpoint.go @@ -34,6 +34,156 @@ import ( uuid "github.com/nu7hatch/gouuid" ) +// SaveAsTemplate saves existing document as a template. +func SaveAsTemplate(w http.ResponseWriter, r *http.Request) { + method := "SaveAsTemplate" + p := request.GetPersister(r) + + var model struct { + DocumentID string + Name string + Excerpt string + } + + defer utility.Close(r.Body) + body, err := ioutil.ReadAll(r.Body) + + if err != nil { + writeBadRequestError(w, method, "Bad payload") + return + } + + err = json.Unmarshal(body, &model) + + if err != nil { + writePayloadError(w, method, err) + return + } + + if !p.CanChangeDocument(model.DocumentID) { + writeForbiddenError(w) + return + } + + // DB transaction + tx, err := request.Db.Beginx() + + if err != nil { + writeTransactionError(w, method, err) + return + } + + p.Context.Transaction = tx + + // Duplicate document + doc, err := p.GetDocument(model.DocumentID) + + if err != nil { + writeServerError(w, method, err) + return + } + + docID := util.UniqueID() + doc.Template = true + doc.Title = model.Name + doc.Excerpt = model.Excerpt + doc.RefID = docID + doc.ID = 0 + doc.Template = true + + // Duplicate pages and associated meta + pages, err := p.GetPages(model.DocumentID) + var pageModel []models.PageModel + + if err != nil { + writeServerError(w, method, err) + return + } + + for _, page := range pages { + page.DocumentID = docID + page.ID = 0 + + meta, err2 := p.GetPageMeta(page.RefID) + + if err2 != nil { + writeServerError(w, method, err2) + return + } + + pageID := util.UniqueID() + page.RefID = pageID + + meta.PageID = pageID + meta.DocumentID = docID + + m := models.PageModel{} + + m.Page = page + m.Meta = meta + + pageModel = append(pageModel, m) + } + + // Duplicate attachments + attachments, err := p.GetAttachments(model.DocumentID) + + for i, a := range attachments { + a.DocumentID = docID + a.RefID = util.UniqueID() + a.ID = 0 + attachments[i] = a + } + + // Now create the template: document, attachments, pages and their meta + err = p.AddDocument(doc) + + if err != nil { + log.IfErr(tx.Rollback()) + writeGeneralSQLError(w, method, err) + return + } + + for _, a := range attachments { + err = p.AddAttachment(a) + + if err != nil { + log.IfErr(tx.Rollback()) + writeGeneralSQLError(w, method, err) + return + } + } + + for _, m := range pageModel { + err = p.AddPage(m) + + if err != nil { + log.IfErr(tx.Rollback()) + writeGeneralSQLError(w, method, err) + return + } + } + + // Commit and return new document template + log.IfErr(tx.Commit()) + + doc, err = p.GetDocument(docID) + + if err != nil { + writeGeneralSQLError(w, method, err) + return + } + + d, err := json.Marshal(doc) + + if err != nil { + writeJSONMarshalError(w, method, "document", err) + return + } + + writeSuccessBytes(w, d) +} + // GetSavedTemplates returns all templates saved by the user func GetSavedTemplates(w http.ResponseWriter, r *http.Request) { method := "GetSavedTemplates" diff --git a/documize/api/request/document.go b/documize/api/request/document.go index 761da709..dc95a25e 100644 --- a/documize/api/request/document.go +++ b/documize/api/request/document.go @@ -1,11 +1,11 @@ // Copyright 2016 Documize Inc. . All rights reserved. // -// This software (Documize Community Edition) is licensed under +// 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 . +// by contacting . // // https://documize.com @@ -29,7 +29,7 @@ func (p *Persister) AddDocument(document entity.Document) (err error) { document.Created = time.Now().UTC() document.Revised = document.Created // put same time in both fields - stmt, err := p.Context.Transaction.Preparex("INSERT INTO document (refId, orgid, labelid, userid, job, location, title, excerpt, slug, tags, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + stmt, err := p.Context.Transaction.Preparex("INSERT INTO document (refId, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") defer utility.Close(stmt) if err != nil { @@ -37,7 +37,7 @@ func (p *Persister) AddDocument(document entity.Document) (err error) { return } - _, err = stmt.Exec(document.RefID, document.OrgID, document.LabelID, document.UserID, document.Job, document.Location, document.Title, document.Excerpt, document.Slug, document.Tags, document.Created, document.Revised) + _, err = stmt.Exec(document.RefID, document.OrgID, document.LabelID, document.UserID, document.Job, document.Location, document.Title, document.Excerpt, document.Slug, document.Tags, document.Template, document.Created, document.Revised) if err != nil { log.Error("Unable to execute insert for document", err)