From 53ca6d11998da32866a74d9efd0aa09feebeeb18 Mon Sep 17 00:00:00 2001
From: Julian Lam <julian@nodebb.org>
Date: Thu, 24 Aug 2017 12:37:22 -0400
Subject: [PATCH] closes #5889

---
 .../en-GB/admin/manage/ip-blacklist.json      |  5 +-
 public/src/admin/manage/ip-blacklist.js       | 96 ++++++++++++++++++-
 src/analytics.js                              |  6 ++
 src/controllers/admin/blacklist.js            | 25 ++---
 src/views/admin/manage/ip-blacklist.tpl       | 19 ++++
 5 files changed, 138 insertions(+), 13 deletions(-)

diff --git a/public/language/en-GB/admin/manage/ip-blacklist.json b/public/language/en-GB/admin/manage/ip-blacklist.json
index 5106434351..cd79294266 100644
--- a/public/language/en-GB/admin/manage/ip-blacklist.json
+++ b/public/language/en-GB/admin/manage/ip-blacklist.json
@@ -11,5 +11,8 @@
 	"validate.x-valid": "<strong>%1</strong> out of <strong>%2</strong> rule(s) valid.",
 	"validate.x-invalid": "The following <strong>%1</strong> rules are invalid:",
 
-	"alerts.applied-success": "Blacklist Applied"
+	"alerts.applied-success": "Blacklist Applied",
+
+	"analytics.blacklist-hourly": "<strong>Figure 1</strong> &ndash; Blacklist hits per hour",
+	"analytics.blacklist-daily": "<strong>Figure 2</strong> &ndash; Blacklist hits per day"
 }
\ No newline at end of file
diff --git a/public/src/admin/manage/ip-blacklist.js b/public/src/admin/manage/ip-blacklist.js
index 0b565325d7..4b5b37ce11 100644
--- a/public/src/admin/manage/ip-blacklist.js
+++ b/public/src/admin/manage/ip-blacklist.js
@@ -1,7 +1,7 @@
 'use strict';
 
 
-define('admin/manage/ip-blacklist', [], function () {
+define('admin/manage/ip-blacklist', ['Chart'], function (Chart) {
 	var Blacklist = {};
 
 	Blacklist.init = function () {
@@ -37,6 +37,100 @@ define('admin/manage/ip-blacklist', [], function () {
 				});
 			});
 		});
+
+		Blacklist.setupAnalytics();
+	};
+
+	Blacklist.setupAnalytics = function () {
+		var hourlyCanvas = document.getElementById('blacklist:hourly');
+		var	dailyCanvas = document.getElementById('blacklist:daily');
+		var	hourlyLabels = utils.getHoursArray().map(function (text, idx) {
+			return idx % 3 ? '' : text;
+		});
+		var	dailyLabels = utils.getDaysArray().map(function (text, idx) {
+			return idx % 3 ? '' : text;
+		});
+
+		// Only 7 days displayed in this chart
+		dailyLabels.length = 7;
+
+		if (utils.isMobile()) {
+			Chart.defaults.global.tooltips.enabled = false;
+		}
+
+		var data = {
+			'blacklist:hourly': {
+				labels: hourlyLabels,
+				datasets: [
+					{
+						label: '',
+						backgroundColor: 'rgba(186,139,175,0.2)',
+						borderColor: 'rgba(186,139,175,1)',
+						pointBackgroundColor: 'rgba(186,139,175,1)',
+						pointHoverBackgroundColor: '#fff',
+						pointBorderColor: '#fff',
+						pointHoverBorderColor: 'rgba(186,139,175,1)',
+						data: ajaxify.data.analytics.hourly,
+					},
+				],
+			},
+			'blacklist:daily': {
+				labels: dailyLabels,
+				datasets: [
+					{
+						label: '',
+						backgroundColor: 'rgba(151,187,205,0.2)',
+						borderColor: 'rgba(151,187,205,1)',
+						pointBackgroundColor: 'rgba(151,187,205,1)',
+						pointHoverBackgroundColor: '#fff',
+						pointBorderColor: '#fff',
+						pointHoverBorderColor: 'rgba(151,187,205,1)',
+						data: ajaxify.data.analytics.daily,
+					},
+				],
+			},
+		};
+
+		hourlyCanvas.width = $(hourlyCanvas).parent().width();
+		dailyCanvas.width = $(dailyCanvas).parent().width();
+
+		new Chart(hourlyCanvas.getContext('2d'), {
+			type: 'line',
+			data: data['blacklist:hourly'],
+			options: {
+				responsive: true,
+				animation: false,
+				legend: {
+					display: false,
+				},
+				scales: {
+					yAxes: [{
+						ticks: {
+							beginAtZero: true,
+						},
+					}],
+				},
+			},
+		});
+
+		new Chart(dailyCanvas.getContext('2d'), {
+			type: 'line',
+			data: data['blacklist:daily'],
+			options: {
+				responsive: true,
+				animation: false,
+				legend: {
+					display: false,
+				},
+				scales: {
+					yAxes: [{
+						ticks: {
+							beginAtZero: true,
+						},
+					}],
+				},
+			},
+		});
 	};
 
 	return Blacklist;
diff --git a/src/analytics.js b/src/analytics.js
index 711eadc865..b4922cf184 100644
--- a/src/analytics.js
+++ b/src/analytics.js
@@ -211,3 +211,9 @@ Analytics.getErrorAnalytics = function (callback) {
 	}, callback);
 };
 
+Analytics.getBlacklistAnalytics = function (callback) {
+	async.parallel({
+		daily: async.apply(Analytics.getDailyStatsForSet, 'analytics:blacklist', Date.now(), 7),
+		hourly: async.apply(Analytics.getHourlyStatsForSet, 'analytics:blacklist', Date.now(), 24),
+	}, callback);
+};
diff --git a/src/controllers/admin/blacklist.js b/src/controllers/admin/blacklist.js
index fb92f377b5..a5f8136463 100644
--- a/src/controllers/admin/blacklist.js
+++ b/src/controllers/admin/blacklist.js
@@ -2,19 +2,22 @@
 
 var async = require('async');
 var meta = require('../../meta');
+var analytics = require('../../analytics');
 
 var blacklistController = module.exports;
 
 blacklistController.get = function (req, res, next) {
-	async.waterfall([
-		function (next) {
-			meta.blacklist.get(next);
-		},
-		function (rules) {
-			res.render('admin/manage/ip-blacklist', {
-				rules: rules,
-				title: '[[pages:ip-blacklist]]',
-			});
-		},
-	], next);
+	// Analytics.getBlacklistAnalytics
+	async.parallel({
+		rules: async.apply(meta.blacklist.get),
+		analytics: async.apply(analytics.getBlacklistAnalytics),
+	}, function (err, data) {
+		if (err) {
+			return next(err);
+		}
+
+		res.render('admin/manage/ip-blacklist', Object.assign(data, {
+			title: '[[pages:ip-blacklist]]',
+		}));
+	});
 };
diff --git a/src/views/admin/manage/ip-blacklist.tpl b/src/views/admin/manage/ip-blacklist.tpl
index 8ec7e72733..e2378e5e5c 100644
--- a/src/views/admin/manage/ip-blacklist.tpl
+++ b/src/views/admin/manage/ip-blacklist.tpl
@@ -7,6 +7,25 @@
 			[[admin/manage/ip-blacklist:description]]
 		</p>
 
+		<div class="row">
+			<div class="col-sm-6">
+				<div class="panel panel-default">
+					<div class="panel-body">
+						<div><canvas id="blacklist:hourly" height="250"></canvas></div>
+					</div>
+					<div class="panel-footer"><small>[[admin/manage/ip-blacklist:analytics.blacklist-hourly]]</small></div>
+				</div>
+			</div>
+			<div class="col-sm-6">
+				<div class="panel panel-default">
+					<div class="panel-body">
+						<div><canvas id="blacklist:daily" height="250"></canvas></div>
+					</div>
+					<div class="panel-footer"><small>[[admin/manage/ip-blacklist:analytics.blacklist-daily]]</small></div>
+				</div>
+			</div>
+		</div>
+
 		<div class="row">
 			<div class="col-sm-6">
 				<textarea id="blacklist-rules">{rules}</textarea>