github.com/lunarobliq/gophish@v0.8.1-0.20230523153303-93511002234d/static/js/src/app/campaigns.js (about)

     1  // labels is a map of campaign statuses to
     2  // CSS classes
     3  var labels = {
     4      "In progress": "label-primary",
     5      "Queued": "label-info",
     6      "Completed": "label-success",
     7      "Emails Sent": "label-success",
     8      "Error": "label-danger"
     9  }
    10  
    11  var campaigns = []
    12  var campaign = {}
    13  
    14  // Launch attempts to POST to /campaigns/
    15  function launch() {
    16      Swal.fire({
    17          title: "Are you sure?",
    18          text: "This will schedule the campaign to be launched.",
    19          type: "question",
    20          animation: false,
    21          showCancelButton: true,
    22          confirmButtonText: "Launch",
    23          confirmButtonColor: "#428bca",
    24          reverseButtons: true,
    25          allowOutsideClick: false,
    26          showLoaderOnConfirm: true,
    27          preConfirm: function () {
    28              return new Promise(function (resolve, reject) {
    29                  groups = []
    30                  $("#users").select2("data").forEach(function (group) {
    31                      groups.push({
    32                          name: group.text
    33                      });
    34                  })
    35                  // Validate our fields
    36                  var send_by_date = $("#send_by_date").val()
    37                  if (send_by_date != "") {
    38                      send_by_date = moment(send_by_date, "MMMM Do YYYY, h:mm a").utc().format()
    39                  }
    40                  campaign = {
    41                      name: $("#name").val(),
    42                      template: {
    43                          name: $("#template").select2("data")[0].text
    44                      },
    45                      url: $("#url").val(),
    46                      page: {
    47                          name: $("#page").select2("data")[0].text
    48                      },
    49                      smtp: {
    50                          name: $("#profile").select2("data")[0].text
    51                      },
    52                      launch_date: moment($("#launch_date").val(), "MMMM Do YYYY, h:mm a").utc().format(),
    53                      send_by_date: send_by_date || null,
    54                      groups: groups,
    55                  }
    56                  // Submit the campaign
    57                  api.campaigns.post(campaign)
    58                      .success(function (data) {
    59                          resolve()
    60                          campaign = data
    61                      })
    62                      .error(function (data) {
    63                          $("#modal\\.flashes").empty().append("<div style=\"text-align:center\" class=\"alert alert-danger\">\
    64              <i class=\"fa fa-exclamation-circle\"></i> " + data.responseJSON.message + "</div>")
    65                          Swal.close()
    66                      })
    67              })
    68          }
    69      }).then(function (result) {
    70          if (result.value){
    71              Swal.fire(
    72                  'Campaign Scheduled!',
    73                  'This campaign has been scheduled for launch!',
    74                  'success'
    75              );
    76          }
    77          $('button:contains("OK")').on('click', function () {
    78              window.location = "/campaigns/" + campaign.id.toString()
    79          })
    80      })
    81  }
    82  
    83  // Attempts to send a test email by POSTing to /campaigns/
    84  function sendTestEmail() {
    85      var test_email_request = {
    86          template: {
    87              name: $("#template").select2("data")[0].text
    88          },
    89          first_name: $("input[name=to_first_name]").val(),
    90          last_name: $("input[name=to_last_name]").val(),
    91          email: $("input[name=to_email]").val(),
    92          position: $("input[name=to_position]").val(),
    93          url: $("#url").val(),
    94          page: {
    95              name: $("#page").select2("data")[0].text
    96          },
    97          smtp: {
    98              name: $("#profile").select2("data")[0].text
    99          }
   100      }
   101      btnHtml = $("#sendTestModalSubmit").html()
   102      $("#sendTestModalSubmit").html('<i class="fa fa-spinner fa-spin"></i> Sending')
   103      // Send the test email
   104      api.send_test_email(test_email_request)
   105          .success(function (data) {
   106              $("#sendTestEmailModal\\.flashes").empty().append("<div style=\"text-align:center\" class=\"alert alert-success\">\
   107              <i class=\"fa fa-check-circle\"></i> Email Sent!</div>")
   108              $("#sendTestModalSubmit").html(btnHtml)
   109          })
   110          .error(function (data) {
   111              $("#sendTestEmailModal\\.flashes").empty().append("<div style=\"text-align:center\" class=\"alert alert-danger\">\
   112              <i class=\"fa fa-exclamation-circle\"></i> " + data.responseJSON.message + "</div>")
   113              $("#sendTestModalSubmit").html(btnHtml)
   114          })
   115  }
   116  
   117  function dismiss() {
   118      $("#modal\\.flashes").empty();
   119      $("#name").val("");
   120      $("#template").val("").change();
   121      $("#page").val("").change();
   122      $("#url").val("");
   123      $("#profile").val("").change();
   124      $("#users").val("").change();
   125      $("#modal").modal('hide');
   126  }
   127  
   128  function deleteCampaign(idx) {
   129      Swal.fire({
   130          title: "Are you sure?",
   131          text: "This will delete the campaign. This can't be undone!",
   132          type: "warning",
   133          animation: false,
   134          showCancelButton: true,
   135          confirmButtonText: "Delete " + campaigns[idx].name,
   136          confirmButtonColor: "#428bca",
   137          reverseButtons: true,
   138          allowOutsideClick: false,
   139          preConfirm: function () {
   140              return new Promise(function (resolve, reject) {
   141                  api.campaignId.delete(campaigns[idx].id)
   142                      .success(function (msg) {
   143                          resolve()
   144                      })
   145                      .error(function (data) {
   146                          reject(data.responseJSON.message)
   147                      })
   148              })
   149          }
   150      }).then(function (result) {
   151          if (result.value){
   152              Swal.fire(
   153                  'Campaign Deleted!',
   154                  'This campaign has been deleted!',
   155                  'success'
   156              );
   157          }
   158          $('button:contains("OK")').on('click', function () {
   159              location.reload()
   160          })
   161      })
   162  }
   163  
   164  function setupOptions() {
   165      api.groups.summary()
   166          .success(function (summaries) {
   167              groups = summaries.groups
   168              if (groups.length == 0) {
   169                  modalError("No groups found!")
   170                  return false;
   171              } else {
   172                  var group_s2 = $.map(groups, function (obj) {
   173                      obj.text = obj.name
   174                      obj.title = obj.num_targets + " targets"
   175                      return obj
   176                  });
   177                  console.log(group_s2)
   178                  $("#users.form-control").select2({
   179                      placeholder: "Select Groups",
   180                      data: group_s2,
   181                  });
   182              }
   183          });
   184      api.templates.get()
   185          .success(function (templates) {
   186              if (templates.length == 0) {
   187                  modalError("No templates found!")
   188                  return false
   189              } else {
   190                  var template_s2 = $.map(templates, function (obj) {
   191                      obj.text = obj.name
   192                      return obj
   193                  });
   194                  var template_select = $("#template.form-control")
   195                  template_select.select2({
   196                      placeholder: "Select a Template",
   197                      data: template_s2,
   198                  });
   199                  if (templates.length === 1) {
   200                      template_select.val(template_s2[0].id)
   201                      template_select.trigger('change.select2')
   202                  }
   203              }
   204          });
   205      api.pages.get()
   206          .success(function (pages) {
   207              if (pages.length == 0) {
   208                  modalError("No pages found!")
   209                  return false
   210              } else {
   211                  var page_s2 = $.map(pages, function (obj) {
   212                      obj.text = obj.name
   213                      return obj
   214                  });
   215                  var page_select = $("#page.form-control")
   216                  page_select.select2({
   217                      placeholder: "Select a Landing Page",
   218                      data: page_s2,
   219                  });
   220                  if (pages.length === 1) {
   221                      page_select.val(page_s2[0].id)
   222                      page_select.trigger('change.select2')
   223                  }
   224              }
   225          });
   226      api.SMTP.get()
   227          .success(function (profiles) {
   228              if (profiles.length == 0) {
   229                  modalError("No profiles found!")
   230                  return false
   231              } else {
   232                  var profile_s2 = $.map(profiles, function (obj) {
   233                      obj.text = obj.name
   234                      return obj
   235                  });
   236                  var profile_select = $("#profile.form-control")
   237                  profile_select.select2({
   238                      placeholder: "Select a Sending Profile",
   239                      data: profile_s2,
   240                  }).select2("val", profile_s2[0]);
   241                  if (profiles.length === 1) {
   242                      profile_select.val(profile_s2[0].id)
   243                      profile_select.trigger('change.select2')
   244                  }
   245              }
   246          });
   247  }
   248  
   249  function edit(campaign) {
   250      setupOptions();
   251  }
   252  
   253  function copy(idx) {
   254      setupOptions();
   255      // Set our initial values
   256      api.campaignId.get(campaigns[idx].id)
   257          .success(function (campaign) {
   258              $("#name").val("Copy of " + campaign.name)
   259              if (!campaign.template.id) {
   260                  $("#template").val("").change();
   261                  $("#template").select2({
   262                      placeholder: campaign.template.name
   263                  });
   264              } else {
   265                  $("#template").val(campaign.template.id.toString());
   266                  $("#template").trigger("change.select2")
   267              }
   268              if (!campaign.page.id) {
   269                  $("#page").val("").change();
   270                  $("#page").select2({
   271                      placeholder: campaign.page.name
   272                  });
   273              } else {
   274                  $("#page").val(campaign.page.id.toString());
   275                  $("#page").trigger("change.select2")
   276              }
   277              if (!campaign.smtp.id) {
   278                  $("#profile").val("").change();
   279                  $("#profile").select2({
   280                      placeholder: campaign.smtp.name
   281                  });
   282              } else {
   283                  $("#profile").val(campaign.smtp.id.toString());
   284                  $("#profile").trigger("change.select2")
   285              }
   286              $("#url").val(campaign.url)
   287          })
   288          .error(function (data) {
   289              $("#modal\\.flashes").empty().append("<div style=\"text-align:center\" class=\"alert alert-danger\">\
   290              <i class=\"fa fa-exclamation-circle\"></i> " + data.responseJSON.message + "</div>")
   291          })
   292  }
   293  
   294  $(document).ready(function () {
   295      $("#launch_date").datetimepicker({
   296          "widgetPositioning": {
   297              "vertical": "bottom"
   298          },
   299          "showTodayButton": true,
   300          "defaultDate": moment(),
   301          "format": "MMMM Do YYYY, h:mm a"
   302      })
   303      $("#send_by_date").datetimepicker({
   304          "widgetPositioning": {
   305              "vertical": "bottom"
   306          },
   307          "showTodayButton": true,
   308          "useCurrent": false,
   309          "format": "MMMM Do YYYY, h:mm a"
   310      })
   311      // Setup multiple modals
   312      // Code based on http://miles-by-motorcycle.com/static/bootstrap-modal/index.html
   313      $('.modal').on('hidden.bs.modal', function (event) {
   314          $(this).removeClass('fv-modal-stack');
   315          $('body').data('fv_open_modals', $('body').data('fv_open_modals') - 1);
   316      });
   317      $('.modal').on('shown.bs.modal', function (event) {
   318          // Keep track of the number of open modals
   319          if (typeof ($('body').data('fv_open_modals')) == 'undefined') {
   320              $('body').data('fv_open_modals', 0);
   321          }
   322          // if the z-index of this modal has been set, ignore.
   323          if ($(this).hasClass('fv-modal-stack')) {
   324              return;
   325          }
   326          $(this).addClass('fv-modal-stack');
   327          // Increment the number of open modals
   328          $('body').data('fv_open_modals', $('body').data('fv_open_modals') + 1);
   329          // Setup the appropriate z-index
   330          $(this).css('z-index', 1040 + (10 * $('body').data('fv_open_modals')));
   331          $('.modal-backdrop').not('.fv-modal-stack').css('z-index', 1039 + (10 * $('body').data('fv_open_modals')));
   332          $('.modal-backdrop').not('fv-modal-stack').addClass('fv-modal-stack');
   333      });
   334      // Scrollbar fix - https://stackoverflow.com/questions/19305821/multiple-modals-overlay
   335      $(document).on('hidden.bs.modal', '.modal', function () {
   336          $('.modal:visible').length && $(document.body).addClass('modal-open');
   337      });
   338      $('#modal').on('hidden.bs.modal', function (event) {
   339          dismiss()
   340      });
   341      api.campaigns.summary()
   342          .success(function (data) {
   343              campaigns = data.campaigns
   344              $("#loading").hide()
   345              if (campaigns.length > 0) {
   346                  $("#campaignTable").show()
   347                  $("#campaignTableArchive").show()
   348  
   349                  activeCampaignsTable = $("#campaignTable").DataTable({
   350                      columnDefs: [{
   351                          orderable: false,
   352                          targets: "no-sort"
   353                      }],
   354                      order: [
   355                          [1, "desc"]
   356                      ]
   357                  });
   358                  archivedCampaignsTable = $("#campaignTableArchive").DataTable({
   359                      columnDefs: [{
   360                          orderable: false,
   361                          targets: "no-sort"
   362                      }],
   363                      order: [
   364                          [1, "desc"]
   365                      ]
   366                  });
   367                  rows = {
   368                      'active': [],
   369                      'archived': []
   370                  }
   371                  $.each(campaigns, function (i, campaign) {
   372                      label = labels[campaign.status] || "label-default";
   373  
   374                      //section for tooltips on the status of a campaign to show some quick stats
   375                      var launchDate;
   376                      if (moment(campaign.launch_date).isAfter(moment())) {
   377                          launchDate = "Scheduled to start: " + moment(campaign.launch_date).format('MMMM Do YYYY, h:mm:ss a')
   378                          var quickStats = launchDate + "<br><br>" + "Number of recipients: " + campaign.stats.total
   379                      } else {
   380                          launchDate = "Launch Date: " + moment(campaign.launch_date).format('MMMM Do YYYY, h:mm:ss a')
   381                          var quickStats = launchDate + "<br><br>" + "Number of recipients: " + campaign.stats.total + "<br><br>" + "Emails opened: " + campaign.stats.opened + "<br><br>" + "Emails clicked: " + campaign.stats.clicked + "<br><br>" + "Submitted Credentials: " + campaign.stats.submitted_data + "<br><br>" + "Errors : " + campaign.stats.error + "<br><br>" + "Reported : " + campaign.stats.email_reported
   382                      }
   383  
   384                      var row = [
   385                          escapeHtml(campaign.name),
   386                          moment(campaign.created_date).format('MMMM Do YYYY, h:mm:ss a'),
   387                          "<span class=\"label " + label + "\" data-toggle=\"tooltip\" data-placement=\"right\" data-html=\"true\" title=\"" + quickStats + "\">" + campaign.status + "</span>",
   388                          "<div class='pull-right'><a class='btn btn-primary' href='/campaigns/" + campaign.id + "' data-toggle='tooltip' data-placement='left' title='View Results'>\
   389                      <i class='fa fa-bar-chart'></i>\
   390                      </a>\
   391              <span data-toggle='modal' data-backdrop='static' data-target='#modal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Copy Campaign' onclick='copy(" + i + ")'>\
   392                      <i class='fa fa-copy'></i>\
   393                      </button></span>\
   394                      <button class='btn btn-danger' onclick='deleteCampaign(" + i + ")' data-toggle='tooltip' data-placement='left' title='Delete Campaign'>\
   395                      <i class='fa fa-trash-o'></i>\
   396                      </button></div>"
   397                      ]
   398                      if (campaign.status == 'Completed') {
   399                          rows['archived'].push(row)
   400                      } else {
   401                          rows['active'].push(row)
   402                      }
   403                  })
   404                  activeCampaignsTable.rows.add(rows['active']).draw()
   405                  archivedCampaignsTable.rows.add(rows['archived']).draw()
   406                  $('[data-toggle="tooltip"]').tooltip()
   407              } else {
   408                  $("#emptyMessage").show()
   409              }
   410          })
   411          .error(function () {
   412              $("#loading").hide()
   413              errorFlash("Error fetching campaigns")
   414          })
   415      // Select2 Defaults
   416      $.fn.select2.defaults.set("width", "100%");
   417      $.fn.select2.defaults.set("dropdownParent", $("#modal_body"));
   418      $.fn.select2.defaults.set("theme", "bootstrap");
   419      $.fn.select2.defaults.set("sorter", function (data) {
   420          return data.sort(function (a, b) {
   421              if (a.text.toLowerCase() > b.text.toLowerCase()) {
   422                  return 1;
   423              }
   424              if (a.text.toLowerCase() < b.text.toLowerCase()) {
   425                  return -1;
   426              }
   427              return 0;
   428          });
   429      })
   430  })