github.com/topsteplocal/gophish@v0.6.0/static/js/src/app/dashboard.js (about) 1 var campaigns = [] 2 // statuses is a helper map to point result statuses to ui classes 3 var statuses = { 4 "Email Sent": { 5 color: "#1abc9c", 6 label: "label-success", 7 icon: "fa-envelope", 8 point: "ct-point-sent" 9 }, 10 "Emails Sent": { 11 color: "#1abc9c", 12 label: "label-success", 13 icon: "fa-envelope", 14 point: "ct-point-sent" 15 }, 16 "In progress": { 17 label: "label-primary" 18 }, 19 "Queued": { 20 label: "label-info" 21 }, 22 "Completed": { 23 label: "label-success" 24 }, 25 "Email Opened": { 26 color: "#f9bf3b", 27 label: "label-warning", 28 icon: "fa-envelope", 29 point: "ct-point-opened" 30 }, 31 "Email Reported": { 32 color: "#45d6ef", 33 label: "label-warning", 34 icon: "fa-bullhorne", 35 point: "ct-point-reported" 36 }, 37 "Clicked Link": { 38 color: "#F39C12", 39 label: "label-clicked", 40 icon: "fa-mouse-pointer", 41 point: "ct-point-clicked" 42 }, 43 "Success": { 44 color: "#f05b4f", 45 label: "label-danger", 46 icon: "fa-exclamation", 47 point: "ct-point-clicked" 48 }, 49 "Error": { 50 color: "#6c7a89", 51 label: "label-default", 52 icon: "fa-times", 53 point: "ct-point-error" 54 }, 55 "Error Sending Email": { 56 color: "#6c7a89", 57 label: "label-default", 58 icon: "fa-times", 59 point: "ct-point-error" 60 }, 61 "Submitted Data": { 62 color: "#f05b4f", 63 label: "label-danger", 64 icon: "fa-exclamation", 65 point: "ct-point-clicked" 66 }, 67 "Unknown": { 68 color: "#6c7a89", 69 label: "label-default", 70 icon: "fa-question", 71 point: "ct-point-error" 72 }, 73 "Sending": { 74 color: "#428bca", 75 label: "label-primary", 76 icon: "fa-spinner", 77 point: "ct-point-sending" 78 }, 79 "Campaign Created": { 80 label: "label-success", 81 icon: "fa-rocket" 82 } 83 } 84 85 var statsMapping = { 86 "sent": "Email Sent", 87 "opened": "Email Opened", 88 "email_reported": "Email Reported", 89 "clicked": "Clicked Link", 90 "submitted_data": "Submitted Data", 91 } 92 93 function deleteCampaign(idx) { 94 if (confirm("Delete " + campaigns[idx].name + "?")) { 95 api.campaignId.delete(campaigns[idx].id) 96 .success(function (data) { 97 successFlash(data.message) 98 location.reload() 99 }) 100 } 101 } 102 103 /* Renders a pie chart using the provided chartops */ 104 function renderPieChart(chartopts) { 105 return Highcharts.chart(chartopts['elemId'], { 106 chart: { 107 type: 'pie', 108 events: { 109 load: function () { 110 var chart = this, 111 rend = chart.renderer, 112 pie = chart.series[0], 113 left = chart.plotLeft + pie.center[0], 114 top = chart.plotTop + pie.center[1]; 115 this.innerText = rend.text(chartopts['data'][0].count, left, top). 116 attr({ 117 'text-anchor': 'middle', 118 'font-size': '16px', 119 'font-weight': 'bold', 120 'fill': chartopts['colors'][0], 121 'font-family': 'Helvetica,Arial,sans-serif' 122 }).add(); 123 }, 124 render: function () { 125 this.innerText.attr({ 126 text: chartopts['data'][0].count 127 }) 128 } 129 } 130 }, 131 title: { 132 text: chartopts['title'] 133 }, 134 plotOptions: { 135 pie: { 136 innerSize: '80%', 137 dataLabels: { 138 enabled: false 139 } 140 } 141 }, 142 credits: { 143 enabled: false 144 }, 145 tooltip: { 146 formatter: function () { 147 if (this.key == undefined) { 148 return false 149 } 150 return '<span style="color:' + this.color + '">\u25CF</span>' + this.point.name + ': <b>' + this.y + '%</b><br/>' 151 } 152 }, 153 series: [{ 154 data: chartopts['data'], 155 colors: chartopts['colors'], 156 }] 157 }) 158 } 159 160 function generateStatsPieCharts(campaigns) { 161 var stats_data = [] 162 var stats_series_data = {} 163 var total = 0 164 165 $.each(campaigns, function (i, campaign) { 166 $.each(campaign.stats, function (status, count) { 167 if (status == "total") { 168 total += count 169 return true 170 } 171 if (!stats_series_data[status]) { 172 stats_series_data[status] = count; 173 } else { 174 stats_series_data[status] += count; 175 } 176 }) 177 }) 178 $.each(stats_series_data, function (status, count) { 179 // I don't like this, but I guess it'll have to work. 180 // Turns submitted_data into Submitted Data 181 if (!(status in statsMapping)) { 182 return true 183 } 184 status_label = statsMapping[status] 185 stats_data.push({ 186 name: status_label, 187 y: Math.floor((count / total) * 100), 188 count: count 189 }) 190 stats_data.push({ 191 name: '', 192 y: 100 - Math.floor((count / total) * 100) 193 }) 194 var stats_chart = renderPieChart({ 195 elemId: status + '_chart', 196 title: status_label, 197 name: status, 198 data: stats_data, 199 colors: [statuses[status_label].color, "#dddddd"] 200 }) 201 202 stats_data = [] 203 }); 204 } 205 206 function generateTimelineChart(campaigns) { 207 var overview_data = [] 208 $.each(campaigns, function (i, campaign) { 209 var campaign_date = moment.utc(campaign.created_date).local() 210 // Add it to the chart data 211 campaign.y = 0 212 // Clicked events also contain our data submitted events 213 campaign.y += campaign.stats.clicked 214 campaign.y = Math.floor((campaign.y / campaign.stats.total) * 100) 215 // Add the data to the overview chart 216 overview_data.push({ 217 campaign_id: campaign.id, 218 name: campaign.name, 219 x: campaign_date.valueOf(), 220 y: campaign.y 221 }) 222 }) 223 Highcharts.chart('overview_chart', { 224 chart: { 225 zoomType: 'x', 226 type: 'areaspline' 227 }, 228 title: { 229 text: 'Phishing Success Overview' 230 }, 231 xAxis: { 232 type: 'datetime', 233 dateTimeLabelFormats: { 234 second: '%l:%M:%S', 235 minute: '%l:%M', 236 hour: '%l:%M', 237 day: '%b %d, %Y', 238 week: '%b %d, %Y', 239 month: '%b %Y' 240 } 241 }, 242 yAxis: { 243 min: 0, 244 max: 100, 245 title: { 246 text: "% of Success" 247 } 248 }, 249 tooltip: { 250 formatter: function () { 251 return Highcharts.dateFormat('%A, %b %d %l:%M:%S %P', new Date(this.x)) + 252 '<br>' + this.point.name + '<br>% Success: <b>' + this.y + '%</b>' 253 } 254 }, 255 legend: { 256 enabled: false 257 }, 258 plotOptions: { 259 series: { 260 marker: { 261 enabled: true, 262 symbol: 'circle', 263 radius: 3 264 }, 265 cursor: 'pointer', 266 point: { 267 events: { 268 click: function (e) { 269 window.location.href = "/campaigns/" + this.campaign_id 270 } 271 } 272 } 273 } 274 }, 275 credits: { 276 enabled: false 277 }, 278 series: [{ 279 data: overview_data, 280 color: "#f05b4f", 281 fillOpacity: 0.5 282 }] 283 }) 284 } 285 286 $(document).ready(function () { 287 Highcharts.setOptions({ 288 global: { 289 useUTC: false 290 } 291 }) 292 api.campaigns.summary() 293 .success(function (data) { 294 $("#loading").hide() 295 campaigns = data.campaigns 296 if (campaigns.length > 0) { 297 $("#dashboard").show() 298 // Create the overview chart data 299 campaignTable = $("#campaignTable").DataTable({ 300 columnDefs: [{ 301 orderable: false, 302 targets: "no-sort" 303 }, 304 { 305 className: "color-sent", 306 targets: [2] 307 }, 308 { 309 className: "color-opened", 310 targets: [3] 311 }, 312 { 313 className: "color-clicked", 314 targets: [4] 315 }, 316 { 317 className: "color-success", 318 targets: [5] 319 }, 320 { 321 className: "color-reported", 322 targets: [6] 323 } 324 ], 325 order: [ 326 [1, "desc"] 327 ] 328 }); 329 $.each(campaigns, function (i, campaign) { 330 var campaign_date = moment(campaign.created_date).format('MMMM Do YYYY, h:mm:ss a') 331 var label = statuses[campaign.status].label || "label-default"; 332 //section for tooltips on the status of a campaign to show some quick stats 333 var launchDate; 334 if (moment(campaign.launch_date).isAfter(moment())) { 335 launchDate = "Scheduled to start: " + moment(campaign.launch_date).format('MMMM Do YYYY, h:mm:ss a') 336 var quickStats = launchDate + "<br><br>" + "Number of recipients: " + campaign.stats.total 337 } else { 338 launchDate = "Launch Date: " + moment(campaign.launch_date).format('MMMM Do YYYY, h:mm:ss a') 339 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 340 } 341 // Add it to the table 342 campaignTable.row.add([ 343 escapeHtml(campaign.name), 344 campaign_date, 345 campaign.stats.sent, 346 campaign.stats.opened, 347 campaign.stats.clicked, 348 campaign.stats.submitted_data, 349 campaign.stats.email_reported, 350 "<span class=\"label " + label + "\" data-toggle=\"tooltip\" data-placement=\"right\" data-html=\"true\" title=\"" + quickStats + "\">" + campaign.status + "</span>", 351 "<div class='pull-right'><a class='btn btn-primary' href='/campaigns/" + campaign.id + "' data-toggle='tooltip' data-placement='left' title='View Results'>\ 352 <i class='fa fa-bar-chart'></i>\ 353 </a>\ 354 <button class='btn btn-danger' onclick='deleteCampaign(" + i + ")' data-toggle='tooltip' data-placement='left' title='Delete Campaign'>\ 355 <i class='fa fa-trash-o'></i>\ 356 </button></div>" 357 ]).draw() 358 $('[data-toggle="tooltip"]').tooltip() 359 }) 360 // Build the charts 361 generateStatsPieCharts(campaigns) 362 generateTimelineChart(campaigns) 363 } else { 364 $("#emptyMessage").show() 365 } 366 }) 367 .error(function () { 368 errorFlash("Error fetching campaigns") 369 }) 370 })