diff --git a/controllers/api/util.go b/controllers/api/util.go index 9ccdc7ed..154c08b1 100644 --- a/controllers/api/util.go +++ b/controllers/api/util.go @@ -3,6 +3,7 @@ package api import ( "encoding/json" "net/http" + "net/mail" ctx "github.com/gophish/gophish/context" log "github.com/gophish/gophish/logger" @@ -93,7 +94,19 @@ func (as *Server) SendTestEmail(w http.ResponseWriter, r *http.Request) { } s.SMTP = smtp } - s.FromAddress = s.SMTP.FromAddress + + _, err = mail.ParseAddress(s.Template.EnvelopeSender) + if err != nil { + _, err = mail.ParseAddress(s.SMTP.FromAddress) + if err != nil { + JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) + return + } else { + s.FromAddress = s.SMTP.FromAddress + } + } else { + s.FromAddress = s.Template.EnvelopeSender + } // Validate the given request if err = s.Validate(); err != nil { diff --git a/db/db_mysql/migrations/20220321133237_0.4.1_envelope_sender.sql b/db/db_mysql/migrations/20220321133237_0.4.1_envelope_sender.sql new file mode 100644 index 00000000..ca874545 --- /dev/null +++ b/db/db_mysql/migrations/20220321133237_0.4.1_envelope_sender.sql @@ -0,0 +1,8 @@ + +-- +goose Up +-- SQL in section 'Up' is executed when this migration is applied +ALTER TABLE templates ADD COLUMN envelope_sender varchar(255); + +-- +goose Down +-- SQL section 'Down' is executed when this migration is rolled back + diff --git a/db/db_sqlite3/migrations/20220321133237_0.4.1_envelope_sender.sql b/db/db_sqlite3/migrations/20220321133237_0.4.1_envelope_sender.sql new file mode 100644 index 00000000..ca874545 --- /dev/null +++ b/db/db_sqlite3/migrations/20220321133237_0.4.1_envelope_sender.sql @@ -0,0 +1,8 @@ + +-- +goose Up +-- SQL in section 'Up' is executed when this migration is applied +ALTER TABLE templates ADD COLUMN envelope_sender varchar(255); + +-- +goose Down +-- SQL section 'Down' is executed when this migration is rolled back + diff --git a/mailer/mailer.go b/mailer/mailer.go index 99a3f38a..e7842362 100644 --- a/mailer/mailer.go +++ b/mailer/mailer.go @@ -55,6 +55,7 @@ type Mail interface { Success() error Generate(msg *gomail.Message) error GetDialer() (Dialer, error) + GetSmtpFrom() (string, error) } // MailWorker is the worker that receives slices of emails @@ -160,7 +161,14 @@ func sendMail(ctx context.Context, dialer Dialer, ms []Mail) { m.Error(err) continue } - err = gomail.Send(sender, message) + + smtp_from, err := m.GetSmtpFrom() + if err != nil { + m.Error(err) + continue + } + + err = gomail.SendCustomFrom(sender, smtp_from, message) if err != nil { if te, ok := err.(*textproto.Error); ok { switch { @@ -215,6 +223,8 @@ func sendMail(ctx context.Context, dialer Dialer, ms []Mail) { } } log.WithFields(logrus.Fields{ + "smtp_from": smtp_from, + "envelope_from": message.GetHeader("From")[0], "email": message.GetHeader("To")[0], }).Info("Email sent") m.Success() diff --git a/mailer/mockmailer.go b/mailer/mockmailer.go index c39cb305..8a54e12b 100644 --- a/mailer/mockmailer.go +++ b/mailer/mockmailer.go @@ -162,6 +162,10 @@ func (mm *mockMessage) Generate(message *gomail.Message) error { return nil } +func (mm *mockMessage) GetSmtpFrom() (string, error) { + return mm.from, nil +} + func (mm *mockMessage) Success() error { mm.finished = true return nil diff --git a/models/email_request.go b/models/email_request.go index 35d2e6b9..09440fcd 100644 --- a/models/email_request.go +++ b/models/email_request.go @@ -77,6 +77,10 @@ func (s *EmailRequest) Success() error { return nil } +func (s *EmailRequest) GetSmtpFrom() (string, error) { + return s.SMTP.FromAddress, nil +} + // PostEmailRequest stores a SendTestEmailRequest in the database. func PostEmailRequest(s *EmailRequest) error { // Generate an ID to be used in the underlying Result object @@ -99,7 +103,7 @@ func GetEmailRequestByResultId(id string) (EmailRequest, error) { // Generate fills in the details of a gomail.Message with the contents // from the SendTestEmailRequest. func (s *EmailRequest) Generate(msg *gomail.Message) error { - f, err := mail.ParseAddress(s.FromAddress) + f, err := mail.ParseAddress(s.getFromAddress()) if err != nil { return err } diff --git a/models/email_request_test.go b/models/email_request_test.go index a954d988..c21a5039 100644 --- a/models/email_request_test.go +++ b/models/email_request_test.go @@ -106,6 +106,37 @@ func (s *ModelsSuite) TestEmailRequestGenerate(ch *check.C) { } } +func (s *ModelsSuite) TestGetSmtpFrom(ch *check.C) { + smtp := SMTP{ + FromAddress: "from@example.com", + } + template := Template{ + Name: "Test Template", + Subject: "{{.FirstName}} - Subject", + Text: "{{.Email}} - Text", + HTML: "{{.Email}} - HTML", + } + req := &EmailRequest{ + SMTP: smtp, + Template: template, + URL: "http://127.0.0.1/{{.Email}}", + BaseRecipient: BaseRecipient{ + FirstName: "First", + LastName: "Last", + Email: "firstlast@example.com", + }, + FromAddress: smtp.FromAddress, + RId: fmt.Sprintf("%s-foobar", PreviewPrefix), + } + + msg := gomail.NewMessage() + err := req.Generate(msg) + smtp_from, err := req.GetSmtpFrom() + + ch.Assert(err, check.Equals, nil) + ch.Assert(smtp_from, check.Equals, "from@example.com") +} + func (s *ModelsSuite) TestEmailRequestURLTemplating(ch *check.C) { smtp := SMTP{ FromAddress: "from@example.com", diff --git a/models/maillog.go b/models/maillog.go index d55730df..d87a5d0d 100644 --- a/models/maillog.go +++ b/models/maillog.go @@ -149,6 +149,16 @@ func (m *MailLog) CacheCampaign(campaign *Campaign) error { return nil } +func (m *MailLog) GetSmtpFrom() (string, error) { + c, err := GetCampaign(m.CampaignId, m.UserId) + if err != nil { + return "", err + } + + f, err := mail.ParseAddress(c.SMTP.FromAddress) + return f.Address, err +} + // Generate fills in the details of a gomail.Message instance with // the correct headers and body from the campaign and recipient listed in // the maillog. We accept the gomail.Message as an argument so that the caller @@ -167,9 +177,12 @@ func (m *MailLog) Generate(msg *gomail.Message) error { c = &campaign } - f, err := mail.ParseAddress(c.SMTP.FromAddress) + f, err := mail.ParseAddress(c.Template.EnvelopeSender) if err != nil { - return err + f, err = mail.ParseAddress(c.SMTP.FromAddress) + if err != nil { + return err + } } msg.SetAddressHeader("From", f.Address, f.Name) diff --git a/models/maillog_test.go b/models/maillog_test.go index 0c8c757c..495e973c 100644 --- a/models/maillog_test.go +++ b/models/maillog_test.go @@ -213,15 +213,51 @@ func (s *ModelsSuite) TestGenerateMailLog(ch *check.C) { ch.Assert(m.Processing, check.Equals, false) } +func (s *ModelsSuite) TestMailLogGetSmtpFrom(ch *check.C) { + template := Template{ + Name: "OverrideSmtpFrom", + UserId: 1, + Text: "dummytext", + HTML: "Dummyhtml", + Subject: "Dummysubject", + EnvelopeSender: "spoofing@example.com", + } + ch.Assert(PostTemplate(&template), check.Equals, nil) + campaign := s.createCampaignDependencies(ch) + campaign.Template = template + + ch.Assert(PostCampaign(&campaign, campaign.UserId), check.Equals, nil) + result := campaign.Results[0] + + m := &MailLog{} + err := db.Where("r_id=? AND campaign_id=?", result.RId, campaign.Id). + Find(m).Error + ch.Assert(err, check.Equals, nil) + + msg := gomail.NewMessage() + err = m.Generate(msg) + ch.Assert(err, check.Equals, nil) + + msgBuff := &bytes.Buffer{} + _, err = msg.WriteTo(msgBuff) + ch.Assert(err, check.Equals, nil) + + got, err := email.NewEmailFromReader(msgBuff) + ch.Assert(err, check.Equals, nil) + ch.Assert(got.From, check.Equals, "spoofing@example.com") +} + func (s *ModelsSuite) TestMailLogGenerate(ch *check.C) { campaign := s.createCampaign(ch) result := campaign.Results[0] expected := &email.Email{ + From: "test@test.com", // Default smtp.FromAddress Subject: fmt.Sprintf("%s - Subject", result.RId), Text: []byte(fmt.Sprintf("%s - Text", result.RId)), HTML: []byte(fmt.Sprintf("%s - HTML", result.RId)), } got := s.emailFromFirstMailLog(campaign, ch) + ch.Assert(got.From, check.Equals, expected.From) ch.Assert(got.Subject, check.Equals, expected.Subject) ch.Assert(string(got.Text), check.Equals, string(expected.Text)) ch.Assert(string(got.HTML), check.Equals, string(expected.HTML)) diff --git a/models/template.go b/models/template.go index 4010b9e4..f7e3ac71 100644 --- a/models/template.go +++ b/models/template.go @@ -2,6 +2,7 @@ package models import ( "errors" + "net/mail" "time" log "github.com/gophish/gophish/logger" @@ -13,6 +14,7 @@ type Template struct { Id int64 `json:"id" gorm:"column:id; primary_key:yes"` UserId int64 `json:"-" gorm:"column:user_id"` Name string `json:"name"` + EnvelopeSender string `json:"envelope_sender"` Subject string `json:"subject"` Text string `json:"text"` HTML string `json:"html" gorm:"column:html"` @@ -33,6 +35,11 @@ func (t *Template) Validate() error { return ErrTemplateNameNotSpecified case t.Text == "" && t.HTML == "": return ErrTemplateMissingParameter + case t.EnvelopeSender != "": + _, err := mail.ParseAddress(t.EnvelopeSender) + if err != nil { + return err + } } if err := ValidateTemplate(t.HTML); err != nil { return err diff --git a/static/js/dist/app/templates.min.js b/static/js/dist/app/templates.min.js index 3c291cdd..049676c4 100644 --- a/static/js/dist/app/templates.min.js +++ b/static/js/dist/app/templates.min.js @@ -1 +1 @@ -var templates=[],icons={"application/vnd.ms-excel":"fa-file-excel-o","text/plain":"fa-file-text-o","image/gif":"fa-file-image-o","image/png":"fa-file-image-o","application/pdf":"fa-file-pdf-o","application/x-zip-compressed":"fa-file-archive-o","application/x-gzip":"fa-file-archive-o","application/vnd.openxmlformats-officedocument.presentationml.presentation":"fa-file-powerpoint-o","application/vnd.openxmlformats-officedocument.wordprocessingml.document":"fa-file-word-o","application/octet-stream":"fa-file-o","application/x-msdownload":"fa-file-o"};function save(t){var a={attachments:[]};a.name=$("#name").val(),a.subject=$("#subject").val(),a.html=CKEDITOR.instances.html_editor.getData(),a.html=a.html.replace(/https?:\/\/{{\.URL}}/gi,"{{.URL}}"),$("#use_tracker_checkbox").prop("checked")?-1==a.html.indexOf("{{.Tracker}}")&&-1==a.html.indexOf("{{.TrackingUrl}}")&&(a.html=a.html.replace("","{{.Tracker}}")):a.html=a.html.replace("{{.Tracker}}",""),a.text=$("#text_editor").val(),$.each($("#attachmentsTable").DataTable().rows().data(),function(t,e){a.attachments.push({name:unescapeHtml(e[1]),content:e[3],type:e[4]})}),-1!=t?(a.id=templates[t].id,api.templateId.put(a).success(function(t){successFlash("Template edited successfully!"),load(),dismiss()}).error(function(t){modalError(t.responseJSON.message)})):api.templates.post(a).success(function(t){successFlash("Template added successfully!"),load(),dismiss()}).error(function(t){modalError(t.responseJSON.message)})}function dismiss(){$("#modal\\.flashes").empty(),$("#attachmentsTable").dataTable().DataTable().clear().draw(),$("#name").val(""),$("#subject").val(""),$("#text_editor").val(""),$("#html_editor").val(""),$("#modal").modal("hide")}var deleteTemplate=function(t){Swal.fire({title:"Are you sure?",text:"This will delete the template. This can't be undone!",type:"warning",animation:!1,showCancelButton:!0,confirmButtonText:"Delete "+escapeHtml(templates[t].name),confirmButtonColor:"#428bca",reverseButtons:!0,allowOutsideClick:!1,preConfirm:function(){return new Promise(function(e,a){api.templateId.delete(templates[t].id).success(function(t){e()}).error(function(t){a(t.responseJSON.message)})})}}).then(function(t){t.value&&Swal.fire("Template Deleted!","This template has been deleted!","success"),$('button:contains("OK")').on("click",function(){location.reload()})})};function deleteTemplate(t){confirm("Delete "+templates[t].name+"?")&&api.templateId.delete(templates[t].id).success(function(t){successFlash(t.message),load()})}function attach(t){attachmentsTable=$("#attachmentsTable").DataTable({destroy:!0,order:[[1,"asc"]],columnDefs:[{orderable:!1,targets:"no-sort"},{sClass:"datatable_hidden",targets:[3,4]}]}),$.each(t,function(t,a){var o=new FileReader;o.onload=function(t){var e=icons[a.type]||"fa-file-o";attachmentsTable.row.add(['',escapeHtml(a.name),'',o.result.split(",")[1],a.type||"application/octet-stream"]).draw()},o.onerror=function(t){console.log(t)},o.readAsDataURL(a)})}function edit(t){$("#modalSubmit").unbind("click").click(function(){save(t)}),$("#attachmentUpload").unbind("click").click(function(){this.value=null}),$("#html_editor").ckeditor(),setupAutocomplete(CKEDITOR.instances.html_editor),$("#attachmentsTable").show(),attachmentsTable=$("#attachmentsTable").DataTable({destroy:!0,order:[[1,"asc"]],columnDefs:[{orderable:!1,targets:"no-sort"},{sClass:"datatable_hidden",targets:[3,4]}]});var e={attachments:[]};-1!=t&&(e=templates[t],$("#name").val(e.name),$("#subject").val(e.subject),$("#html_editor").val(e.html),$("#text_editor").val(e.text),attachmentRows=[],$.each(e.attachments,function(t,e){var a=icons[e.type]||"fa-file-o";attachmentRows.push(['',escapeHtml(e.name),'',e.content,e.type||"application/octet-stream"])}),attachmentsTable.rows.add(attachmentRows).draw(),-1!=e.html.indexOf("{{.Tracker}}")?$("#use_tracker_checkbox").prop("checked",!0):$("#use_tracker_checkbox").prop("checked",!1)),$("#attachmentsTable").unbind("click").on("click","span>i.fa-trash-o",function(){attachmentsTable.row($(this).parents("tr")).remove().draw()})}function copy(t){$("#modalSubmit").unbind("click").click(function(){save(-1)}),$("#attachmentUpload").unbind("click").click(function(){this.value=null}),$("#html_editor").ckeditor(),$("#attachmentsTable").show(),attachmentsTable=$("#attachmentsTable").DataTable({destroy:!0,order:[[1,"asc"]],columnDefs:[{orderable:!1,targets:"no-sort"},{sClass:"datatable_hidden",targets:[3,4]}]});var e={attachments:[]};e=templates[t],$("#name").val("Copy of "+e.name),$("#subject").val(e.subject),$("#html_editor").val(e.html),$("#text_editor").val(e.text),$.each(e.attachments,function(t,e){var a=icons[e.type]||"fa-file-o";attachmentsTable.row.add(['',escapeHtml(e.name),'',e.content,e.type||"application/octet-stream"]).draw()}),$("#attachmentsTable").unbind("click").on("click","span>i.fa-trash-o",function(){attachmentsTable.row($(this).parents("tr")).remove().draw()}),-1!=e.html.indexOf("{{.Tracker}}")?$("#use_tracker_checkbox").prop("checked",!0):$("#use_tracker_checkbox").prop("checked",!1)}function importEmail(){raw=$("#email_content").val(),convert_links=$("#convert_links_checkbox").prop("checked"),raw?api.import_email({content:raw,convert_links:convert_links}).success(function(t){$("#text_editor").val(t.text),$("#html_editor").val(t.html),$("#subject").val(t.subject),t.html&&(CKEDITOR.instances.html_editor.setMode("wysiwyg"),$('.nav-tabs a[href="#html"]').click()),$("#importEmailModal").modal("hide")}).error(function(t){modalError(t.responseJSON.message)}):modalError("No Content Specified!")}function load(){$("#templateTable").hide(),$("#emptyMessage").hide(),$("#loading").show(),api.templates.get().success(function(t){templates=t,$("#loading").hide(),0\t\t "])}),templateTable.rows.add(templateRows).draw(),$('[data-toggle="tooltip"]').tooltip()):$("#emptyMessage").show()}).error(function(){$("#loading").hide(),errorFlash("Error fetching templates")})}$(document).ready(function(){$(".modal").on("hidden.bs.modal",function(t){$(this).removeClass("fv-modal-stack"),$("body").data("fv_open_modals",$("body").data("fv_open_modals")-1)}),$(".modal").on("shown.bs.modal",function(t){void 0===$("body").data("fv_open_modals")&&$("body").data("fv_open_modals",0),$(this).hasClass("fv-modal-stack")||($(this).addClass("fv-modal-stack"),$("body").data("fv_open_modals",$("body").data("fv_open_modals")+1),$(this).css("z-index",1040+10*$("body").data("fv_open_modals")),$(".modal-backdrop").not(".fv-modal-stack").css("z-index",1039+10*$("body").data("fv_open_modals")),$(".modal-backdrop").not("fv-modal-stack").addClass("fv-modal-stack"))}),$.fn.modal.Constructor.prototype.enforceFocus=function(){$(document).off("focusin.bs.modal").on("focusin.bs.modal",$.proxy(function(t){this.$element[0]===t.target||this.$element.has(t.target).length||$(t.target).closest(".cke_dialog, .cke").length||this.$element.trigger("focus")},this))},$(document).on("hidden.bs.modal",".modal",function(){$(".modal:visible").length&&$(document.body).addClass("modal-open")}),$("#modal").on("hidden.bs.modal",function(t){dismiss()}),$("#importEmailModal").on("hidden.bs.modal",function(t){$("#email_content").val("")}),CKEDITOR.on("dialogDefinition",function(t){var e=t.data.name,a=t.data.definition;"link"==e&&(a.minWidth=500,a.minHeight=100,a.getContents("info").get("linkType").hidden=!0)}),load()}); \ No newline at end of file +var templates=[],icons={"application/vnd.ms-excel":"fa-file-excel-o","text/plain":"fa-file-text-o","image/gif":"fa-file-image-o","image/png":"fa-file-image-o","application/pdf":"fa-file-pdf-o","application/x-zip-compressed":"fa-file-archive-o","application/x-gzip":"fa-file-archive-o","application/vnd.openxmlformats-officedocument.presentationml.presentation":"fa-file-powerpoint-o","application/vnd.openxmlformats-officedocument.wordprocessingml.document":"fa-file-word-o","application/octet-stream":"fa-file-o","application/x-msdownload":"fa-file-o"};function save(e){var a={attachments:[]};a.name=$("#name").val(),a.subject=$("#subject").val(),a.envelope_sender=$("#envelope-sender").val(),a.html=CKEDITOR.instances.html_editor.getData(),a.html=a.html.replace(/https?:\/\/{{\.URL}}/gi,"{{.URL}}"),$("#use_tracker_checkbox").prop("checked")?-1==a.html.indexOf("{{.Tracker}}")&&-1==a.html.indexOf("{{.TrackingUrl}}")&&(a.html=a.html.replace("","{{.Tracker}}")):a.html=a.html.replace("{{.Tracker}}",""),a.text=$("#text_editor").val(),$.each($("#attachmentsTable").DataTable().rows().data(),function(e,t){a.attachments.push({name:unescapeHtml(t[1]),content:t[3],type:t[4]})}),-1!=e?(a.id=templates[e].id,api.templateId.put(a).success(function(e){successFlash("Template edited successfully!"),load(),dismiss()}).error(function(e){modalError(e.responseJSON.message)})):api.templates.post(a).success(function(e){successFlash("Template added successfully!"),load(),dismiss()}).error(function(e){modalError(e.responseJSON.message)})}function dismiss(){$("#modal\\.flashes").empty(),$("#attachmentsTable").dataTable().DataTable().clear().draw(),$("#name").val(""),$("#subject").val(""),$("#text_editor").val(""),$("#html_editor").val(""),$("#modal").modal("hide")}var deleteTemplate=function(e){Swal.fire({title:"Are you sure?",text:"This will delete the template. This can't be undone!",type:"warning",animation:!1,showCancelButton:!0,confirmButtonText:"Delete "+escapeHtml(templates[e].name),confirmButtonColor:"#428bca",reverseButtons:!0,allowOutsideClick:!1,preConfirm:function(){return new Promise(function(t,a){api.templateId.delete(templates[e].id).success(function(e){t()}).error(function(e){a(e.responseJSON.message)})})}}).then(function(e){e.value&&Swal.fire("Template Deleted!","This template has been deleted!","success"),$('button:contains("OK")').on("click",function(){location.reload()})})};function deleteTemplate(e){confirm("Delete "+templates[e].name+"?")&&api.templateId.delete(templates[e].id).success(function(e){successFlash(e.message),load()})}function attach(e){attachmentsTable=$("#attachmentsTable").DataTable({destroy:!0,order:[[1,"asc"]],columnDefs:[{orderable:!1,targets:"no-sort"},{sClass:"datatable_hidden",targets:[3,4]}]}),$.each(e,function(e,a){var o=new FileReader;o.onload=function(e){var t=icons[a.type]||"fa-file-o";attachmentsTable.row.add(['',escapeHtml(a.name),'',o.result.split(",")[1],a.type||"application/octet-stream"]).draw()},o.onerror=function(e){console.log(e)},o.readAsDataURL(a)})}function edit(e){$("#modalSubmit").unbind("click").click(function(){save(e)}),$("#attachmentUpload").unbind("click").click(function(){this.value=null}),$("#html_editor").ckeditor(),setupAutocomplete(CKEDITOR.instances.html_editor),$("#attachmentsTable").show(),attachmentsTable=$("#attachmentsTable").DataTable({destroy:!0,order:[[1,"asc"]],columnDefs:[{orderable:!1,targets:"no-sort"},{sClass:"datatable_hidden",targets:[3,4]}]});var t={attachments:[]};-1!=e&&(t=templates[e],$("#name").val(t.name),$("#subject").val(t.subject),$("#envelope-sender").val(t.envelope_sender),$("#html_editor").val(t.html),$("#text_editor").val(t.text),attachmentRows=[],$.each(t.attachments,function(e,t){var a=icons[t.type]||"fa-file-o";attachmentRows.push(['',escapeHtml(t.name),'',t.content,t.type||"application/octet-stream"])}),attachmentsTable.rows.add(attachmentRows).draw(),-1!=t.html.indexOf("{{.Tracker}}")?$("#use_tracker_checkbox").prop("checked",!0):$("#use_tracker_checkbox").prop("checked",!1)),$("#attachmentsTable").unbind("click").on("click","span>i.fa-trash-o",function(){attachmentsTable.row($(this).parents("tr")).remove().draw()})}function copy(e){$("#modalSubmit").unbind("click").click(function(){save(-1)}),$("#attachmentUpload").unbind("click").click(function(){this.value=null}),$("#html_editor").ckeditor(),$("#attachmentsTable").show(),attachmentsTable=$("#attachmentsTable").DataTable({destroy:!0,order:[[1,"asc"]],columnDefs:[{orderable:!1,targets:"no-sort"},{sClass:"datatable_hidden",targets:[3,4]}]});var t={attachments:[]},t=templates[e];$("#name").val("Copy of "+t.name),$("#subject").val(t.subject),$("#envelope-sender").val(t.envelope_sender),$("#html_editor").val(t.html),$("#text_editor").val(t.text),$.each(t.attachments,function(e,t){var a=icons[t.type]||"fa-file-o";attachmentsTable.row.add(['',escapeHtml(t.name),'',t.content,t.type||"application/octet-stream"]).draw()}),$("#attachmentsTable").unbind("click").on("click","span>i.fa-trash-o",function(){attachmentsTable.row($(this).parents("tr")).remove().draw()}),-1!=t.html.indexOf("{{.Tracker}}")?$("#use_tracker_checkbox").prop("checked",!0):$("#use_tracker_checkbox").prop("checked",!1)}function importEmail(){raw=$("#email_content").val(),convert_links=$("#convert_links_checkbox").prop("checked"),raw?api.import_email({content:raw,convert_links:convert_links}).success(function(e){$("#text_editor").val(e.text),$("#html_editor").val(e.html),$("#subject").val(e.subject),e.html&&(CKEDITOR.instances.html_editor.setMode("wysiwyg"),$('.nav-tabs a[href="#html"]').click()),$("#importEmailModal").modal("hide")}).error(function(e){modalError(e.responseJSON.message)}):modalError("No Content Specified!")}function load(){$("#templateTable").hide(),$("#emptyMessage").hide(),$("#loading").show(),api.templates.get().success(function(e){templates=e,$("#loading").hide(),0\t\t "])}),templateTable.rows.add(templateRows).draw(),$('[data-toggle="tooltip"]').tooltip()):$("#emptyMessage").show()}).error(function(){$("#loading").hide(),errorFlash("Error fetching templates")})}$(document).ready(function(){$(".modal").on("hidden.bs.modal",function(e){$(this).removeClass("fv-modal-stack"),$("body").data("fv_open_modals",$("body").data("fv_open_modals")-1)}),$(".modal").on("shown.bs.modal",function(e){void 0===$("body").data("fv_open_modals")&&$("body").data("fv_open_modals",0),$(this).hasClass("fv-modal-stack")||($(this).addClass("fv-modal-stack"),$("body").data("fv_open_modals",$("body").data("fv_open_modals")+1),$(this).css("z-index",1040+10*$("body").data("fv_open_modals")),$(".modal-backdrop").not(".fv-modal-stack").css("z-index",1039+10*$("body").data("fv_open_modals")),$(".modal-backdrop").not("fv-modal-stack").addClass("fv-modal-stack"))}),$.fn.modal.Constructor.prototype.enforceFocus=function(){$(document).off("focusin.bs.modal").on("focusin.bs.modal",$.proxy(function(e){this.$element[0]===e.target||this.$element.has(e.target).length||$(e.target).closest(".cke_dialog, .cke").length||this.$element.trigger("focus")},this))},$(document).on("hidden.bs.modal",".modal",function(){$(".modal:visible").length&&$(document.body).addClass("modal-open")}),$("#modal").on("hidden.bs.modal",function(e){dismiss()}),$("#importEmailModal").on("hidden.bs.modal",function(e){$("#email_content").val("")}),CKEDITOR.on("dialogDefinition",function(e){var t=e.data.name,e=e.data.definition;"link"==t&&(e.minWidth=500,e.minHeight=100,e.getContents("info").get("linkType").hidden=!0)}),load()}); \ No newline at end of file diff --git a/static/js/src/app/templates.js b/static/js/src/app/templates.js index c9190de6..cd92b3a0 100644 --- a/static/js/src/app/templates.js +++ b/static/js/src/app/templates.js @@ -20,6 +20,7 @@ function save(idx) { } template.name = $("#name").val() template.subject = $("#subject").val() + template.envelope_sender = $("#envelope-sender").val() template.html = CKEDITOR.instances["html_editor"].getData(); // Fix the URL Scheme added by CKEditor (until we can remove it from the plugin) template.html = template.html.replace(/https?:\/\/{{\.URL}}/gi, "{{.URL}}") @@ -189,6 +190,7 @@ function edit(idx) { template = templates[idx] $("#name").val(template.name) $("#subject").val(template.subject) + $("#envelope-sender").val(template.envelope_sender) $("#html_editor").val(template.html) $("#text_editor").val(template.text) attachmentRows = [] @@ -247,6 +249,7 @@ function copy(idx) { template = templates[idx] $("#name").val("Copy of " + template.name) $("#subject").val(template.subject) + $("#envelope-sender").val(template.envelope_sender) $("#html_editor").val(template.html) $("#text_editor").val(template.text) $.each(template.attachments, function (i, file) { diff --git a/templates/sending_profiles.html b/templates/sending_profiles.html index 0959f830..d68cc867 100644 --- a/templates/sending_profiles.html +++ b/templates/sending_profiles.html @@ -50,7 +50,8 @@ - + @@ -140,4 +141,4 @@ {{end}} {{define "scripts"}} -{{end}} \ No newline at end of file +{{end}} diff --git a/templates/templates.html b/templates/templates.html index 6ef478b5..2a348019 100644 --- a/templates/templates.html +++ b/templates/templates.html @@ -55,6 +55,10 @@ class="fa fa-envelope"> Import Email + +
+ +
@@ -137,4 +141,4 @@ -{{end}} \ No newline at end of file +{{end}}