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  })