github.com/gophish/gophish@v0.12.2-0.20230915144530-8e7929441393/static/js/src/app/templates.js (about) 1 var templates = [] 2 var icons = { 3 "application/vnd.ms-excel": "fa-file-excel-o", 4 "text/plain": "fa-file-text-o", 5 "image/gif": "fa-file-image-o", 6 "image/png": "fa-file-image-o", 7 "application/pdf": "fa-file-pdf-o", 8 "application/x-zip-compressed": "fa-file-archive-o", 9 "application/x-gzip": "fa-file-archive-o", 10 "application/vnd.openxmlformats-officedocument.presentationml.presentation": "fa-file-powerpoint-o", 11 "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "fa-file-word-o", 12 "application/octet-stream": "fa-file-o", 13 "application/x-msdownload": "fa-file-o" 14 } 15 16 // Save attempts to POST to /templates/ 17 function save(idx) { 18 var template = { 19 attachments: [] 20 } 21 template.name = $("#name").val() 22 template.subject = $("#subject").val() 23 template.envelope_sender = $("#envelope-sender").val() 24 template.html = CKEDITOR.instances["html_editor"].getData(); 25 // Fix the URL Scheme added by CKEditor (until we can remove it from the plugin) 26 template.html = template.html.replace(/https?:\/\/{{\.URL}}/gi, "{{.URL}}") 27 // If the "Add Tracker Image" checkbox is checked, add the tracker 28 if ($("#use_tracker_checkbox").prop("checked")) { 29 if (template.html.indexOf("{{.Tracker}}") == -1 && 30 template.html.indexOf("{{.TrackingUrl}}") == -1) { 31 template.html = template.html.replace("</body>", "{{.Tracker}}</body>") 32 } 33 } else { 34 // Otherwise, remove the tracker 35 template.html = template.html.replace("{{.Tracker}}</body>", "</body>") 36 } 37 template.text = $("#text_editor").val() 38 // Add the attachments 39 $.each($("#attachmentsTable").DataTable().rows().data(), function (i, target) { 40 template.attachments.push({ 41 name: unescapeHtml(target[1]), 42 content: target[3], 43 type: target[4], 44 }) 45 }) 46 47 if (idx != -1) { 48 template.id = templates[idx].id 49 api.templateId.put(template) 50 .success(function (data) { 51 successFlash("Template edited successfully!") 52 load() 53 dismiss() 54 }) 55 .error(function (data) { 56 modalError(data.responseJSON.message) 57 }) 58 } else { 59 // Submit the template 60 api.templates.post(template) 61 .success(function (data) { 62 successFlash("Template added successfully!") 63 load() 64 dismiss() 65 }) 66 .error(function (data) { 67 modalError(data.responseJSON.message) 68 }) 69 } 70 } 71 72 function dismiss() { 73 $("#modal\\.flashes").empty() 74 $("#attachmentsTable").dataTable().DataTable().clear().draw() 75 $("#name").val("") 76 $("#subject").val("") 77 $("#text_editor").val("") 78 $("#html_editor").val("") 79 $("#modal").modal('hide') 80 } 81 82 var deleteTemplate = function (idx) { 83 Swal.fire({ 84 title: "Are you sure?", 85 text: "This will delete the template. This can't be undone!", 86 type: "warning", 87 animation: false, 88 showCancelButton: true, 89 confirmButtonText: "Delete " + escapeHtml(templates[idx].name), 90 confirmButtonColor: "#428bca", 91 reverseButtons: true, 92 allowOutsideClick: false, 93 preConfirm: function () { 94 return new Promise(function (resolve, reject) { 95 api.templateId.delete(templates[idx].id) 96 .success(function (msg) { 97 resolve() 98 }) 99 .error(function (data) { 100 reject(data.responseJSON.message) 101 }) 102 }) 103 } 104 }).then(function (result) { 105 if(result.value) { 106 Swal.fire( 107 'Template Deleted!', 108 'This template has been deleted!', 109 'success' 110 ); 111 } 112 $('button:contains("OK")').on('click', function () { 113 location.reload() 114 }) 115 }) 116 } 117 118 function deleteTemplate(idx) { 119 if (confirm("Delete " + templates[idx].name + "?")) { 120 api.templateId.delete(templates[idx].id) 121 .success(function (data) { 122 successFlash(data.message) 123 load() 124 }) 125 } 126 } 127 128 function attach(files) { 129 attachmentsTable = $("#attachmentsTable").DataTable({ 130 destroy: true, 131 "order": [ 132 [1, "asc"] 133 ], 134 columnDefs: [{ 135 orderable: false, 136 targets: "no-sort" 137 }, { 138 sClass: "datatable_hidden", 139 targets: [3, 4] 140 }] 141 }); 142 $.each(files, function (i, file) { 143 var reader = new FileReader(); 144 /* Make this a datatable */ 145 reader.onload = function (e) { 146 var icon = icons[file.type] || "fa-file-o" 147 // Add the record to the modal 148 attachmentsTable.row.add([ 149 '<i class="fa ' + icon + '"></i>', 150 escapeHtml(file.name), 151 '<span class="remove-row"><i class="fa fa-trash-o"></i></span>', 152 reader.result.split(",")[1], 153 file.type || "application/octet-stream" 154 ]).draw() 155 } 156 reader.onerror = function (e) { 157 console.log(e) 158 } 159 reader.readAsDataURL(file) 160 }) 161 } 162 163 function edit(idx) { 164 $("#modalSubmit").unbind('click').click(function () { 165 save(idx) 166 }) 167 $("#attachmentUpload").unbind('click').click(function () { 168 this.value = null 169 }) 170 $("#html_editor").ckeditor() 171 setupAutocomplete(CKEDITOR.instances["html_editor"]) 172 $("#attachmentsTable").show() 173 attachmentsTable = $('#attachmentsTable').DataTable({ 174 destroy: true, 175 "order": [ 176 [1, "asc"] 177 ], 178 columnDefs: [{ 179 orderable: false, 180 targets: "no-sort" 181 }, { 182 sClass: "datatable_hidden", 183 targets: [3, 4] 184 }] 185 }); 186 var template = { 187 attachments: [] 188 } 189 if (idx != -1) { 190 $("#templateModalLabel").text("Edit Template") 191 template = templates[idx] 192 $("#name").val(template.name) 193 $("#subject").val(template.subject) 194 $("#envelope-sender").val(template.envelope_sender) 195 $("#html_editor").val(template.html) 196 $("#text_editor").val(template.text) 197 attachmentRows = [] 198 $.each(template.attachments, function (i, file) { 199 var icon = icons[file.type] || "fa-file-o" 200 // Add the record to the modal 201 attachmentRows.push([ 202 '<i class="fa ' + icon + '"></i>', 203 escapeHtml(file.name), 204 '<span class="remove-row"><i class="fa fa-trash-o"></i></span>', 205 file.content, 206 file.type || "application/octet-stream" 207 ]) 208 }) 209 attachmentsTable.rows.add(attachmentRows).draw() 210 if (template.html.indexOf("{{.Tracker}}") != -1) { 211 $("#use_tracker_checkbox").prop("checked", true) 212 } else { 213 $("#use_tracker_checkbox").prop("checked", false) 214 } 215 216 } else { 217 $("#templateModalLabel").text("New Template") 218 } 219 // Handle Deletion 220 $("#attachmentsTable").unbind('click').on("click", "span>i.fa-trash-o", function () { 221 attachmentsTable.row($(this).parents('tr')) 222 .remove() 223 .draw(); 224 }) 225 } 226 227 function copy(idx) { 228 $("#modalSubmit").unbind('click').click(function () { 229 save(-1) 230 }) 231 $("#attachmentUpload").unbind('click').click(function () { 232 this.value = null 233 }) 234 $("#html_editor").ckeditor() 235 $("#attachmentsTable").show() 236 attachmentsTable = $('#attachmentsTable').DataTable({ 237 destroy: true, 238 "order": [ 239 [1, "asc"] 240 ], 241 columnDefs: [{ 242 orderable: false, 243 targets: "no-sort" 244 }, { 245 sClass: "datatable_hidden", 246 targets: [3, 4] 247 }] 248 }); 249 var template = { 250 attachments: [] 251 } 252 template = templates[idx] 253 $("#name").val("Copy of " + template.name) 254 $("#subject").val(template.subject) 255 $("#envelope-sender").val(template.envelope_sender) 256 $("#html_editor").val(template.html) 257 $("#text_editor").val(template.text) 258 $.each(template.attachments, function (i, file) { 259 var icon = icons[file.type] || "fa-file-o" 260 // Add the record to the modal 261 attachmentsTable.row.add([ 262 '<i class="fa ' + icon + '"></i>', 263 escapeHtml(file.name), 264 '<span class="remove-row"><i class="fa fa-trash-o"></i></span>', 265 file.content, 266 file.type || "application/octet-stream" 267 ]).draw() 268 }) 269 // Handle Deletion 270 $("#attachmentsTable").unbind('click').on("click", "span>i.fa-trash-o", function () { 271 attachmentsTable.row($(this).parents('tr')) 272 .remove() 273 .draw(); 274 }) 275 if (template.html.indexOf("{{.Tracker}}") != -1) { 276 $("#use_tracker_checkbox").prop("checked", true) 277 } else { 278 $("#use_tracker_checkbox").prop("checked", false) 279 } 280 } 281 282 function importEmail() { 283 raw = $("#email_content").val() 284 convert_links = $("#convert_links_checkbox").prop("checked") 285 if (!raw) { 286 modalError("No Content Specified!") 287 } else { 288 api.import_email({ 289 content: raw, 290 convert_links: convert_links 291 }) 292 .success(function (data) { 293 $("#text_editor").val(data.text) 294 $("#html_editor").val(data.html) 295 $("#subject").val(data.subject) 296 // If the HTML is provided, let's open that view in the editor 297 if (data.html) { 298 CKEDITOR.instances["html_editor"].setMode('wysiwyg') 299 $('.nav-tabs a[href="#html"]').click() 300 } 301 $("#importEmailModal").modal("hide") 302 }) 303 .error(function (data) { 304 modalError(data.responseJSON.message) 305 }) 306 } 307 } 308 309 function load() { 310 $("#templateTable").hide() 311 $("#emptyMessage").hide() 312 $("#loading").show() 313 api.templates.get() 314 .success(function (ts) { 315 templates = ts 316 $("#loading").hide() 317 if (templates.length > 0) { 318 $("#templateTable").show() 319 templateTable = $("#templateTable").DataTable({ 320 destroy: true, 321 columnDefs: [{ 322 orderable: false, 323 targets: "no-sort" 324 }] 325 }); 326 templateTable.clear() 327 templateRows = [] 328 $.each(templates, function (i, template) { 329 templateRows.push([ 330 escapeHtml(template.name), 331 moment(template.modified_date).format('MMMM Do YYYY, h:mm:ss a'), 332 "<div class='pull-right'><span data-toggle='modal' data-backdrop='static' data-target='#modal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Edit Template' onclick='edit(" + i + ")'>\ 333 <i class='fa fa-pencil'></i>\ 334 </button></span>\ 335 <span data-toggle='modal' data-target='#modal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Copy Template' onclick='copy(" + i + ")'>\ 336 <i class='fa fa-copy'></i>\ 337 </button></span>\ 338 <button class='btn btn-danger' data-toggle='tooltip' data-placement='left' title='Delete Template' onclick='deleteTemplate(" + i + ")'>\ 339 <i class='fa fa-trash-o'></i>\ 340 </button></div>" 341 ]) 342 }) 343 templateTable.rows.add(templateRows).draw() 344 $('[data-toggle="tooltip"]').tooltip() 345 } else { 346 $("#emptyMessage").show() 347 } 348 }) 349 .error(function () { 350 $("#loading").hide() 351 errorFlash("Error fetching templates") 352 }) 353 } 354 355 $(document).ready(function () { 356 // Setup multiple modals 357 // Code based on http://miles-by-motorcycle.com/static/bootstrap-modal/index.html 358 $('.modal').on('hidden.bs.modal', function (event) { 359 $(this).removeClass('fv-modal-stack'); 360 $('body').data('fv_open_modals', $('body').data('fv_open_modals') - 1); 361 }); 362 $('.modal').on('shown.bs.modal', function (event) { 363 // Keep track of the number of open modals 364 if (typeof ($('body').data('fv_open_modals')) == 'undefined') { 365 $('body').data('fv_open_modals', 0); 366 } 367 // if the z-index of this modal has been set, ignore. 368 if ($(this).hasClass('fv-modal-stack')) { 369 return; 370 } 371 $(this).addClass('fv-modal-stack'); 372 // Increment the number of open modals 373 $('body').data('fv_open_modals', $('body').data('fv_open_modals') + 1); 374 // Setup the appropriate z-index 375 $(this).css('z-index', 1040 + (10 * $('body').data('fv_open_modals'))); 376 $('.modal-backdrop').not('.fv-modal-stack').css('z-index', 1039 + (10 * $('body').data('fv_open_modals'))); 377 $('.modal-backdrop').not('fv-modal-stack').addClass('fv-modal-stack'); 378 }); 379 $.fn.modal.Constructor.prototype.enforceFocus = function () { 380 $(document) 381 .off('focusin.bs.modal') // guard against infinite focus loop 382 .on('focusin.bs.modal', $.proxy(function (e) { 383 if ( 384 this.$element[0] !== e.target && !this.$element.has(e.target).length 385 // CKEditor compatibility fix start. 386 && 387 !$(e.target).closest('.cke_dialog, .cke').length 388 // CKEditor compatibility fix end. 389 ) { 390 this.$element.trigger('focus'); 391 } 392 }, this)); 393 }; 394 // Scrollbar fix - https://stackoverflow.com/questions/19305821/multiple-modals-overlay 395 $(document).on('hidden.bs.modal', '.modal', function () { 396 $('.modal:visible').length && $(document.body).addClass('modal-open'); 397 }); 398 $('#modal').on('hidden.bs.modal', function (event) { 399 dismiss() 400 }); 401 $("#importEmailModal").on('hidden.bs.modal', function (event) { 402 $("#email_content").val("") 403 }) 404 CKEDITOR.on('dialogDefinition', function (ev) { 405 // Take the dialog name and its definition from the event data. 406 var dialogName = ev.data.name; 407 var dialogDefinition = ev.data.definition; 408 409 // Check if the definition is from the dialog window you are interested in (the "Link" dialog window). 410 if (dialogName == 'link') { 411 dialogDefinition.minWidth = 500 412 dialogDefinition.minHeight = 100 413 414 // Remove the linkType field 415 var infoTab = dialogDefinition.getContents('info'); 416 infoTab.get('linkType').hidden = true; 417 } 418 }); 419 load() 420 421 })