Sfoglia il codice sorgente

CSP for statistics

https://github.com/FreshRSS/FreshRSS/issues/1075
Alexandre Alapetite 10 anni fa
parent
commit
264d05297c

+ 10 - 10
app/Models/StatsDAO.php

@@ -55,9 +55,9 @@ SQL;
 
 	/**
 	 * Calculates entry count per day on a 30 days period.
-	 * Returns the result as a JSON string.
+	 * Returns the result as a JSON object.
 	 *
-	 * @return string
+	 * @return JSON object
 	 */
 	public function calculateEntryCount() {
 		$count = $this->initEntryCountArray();
@@ -257,9 +257,9 @@ SQL;
 
 	/**
 	 * Calculates feed count per category.
-	 * Returns the result as a JSON string.
+	 * Returns the result as a JSON object.
 	 *
-	 * @return string
+	 * @return JSON object
 	 */
 	public function calculateFeedByCategory() {
 		$sql = <<<SQL
@@ -282,7 +282,7 @@ SQL;
 	 * Calculates entry count per category.
 	 * Returns the result as a JSON string.
 	 *
-	 * @return string
+	 * @return JSON object
 	 */
 	public function calculateEntryByCategory() {
 		$sql = <<<SQL
@@ -357,7 +357,7 @@ SQL;
 			$serie[] = array($key, $value);
 		}
 
-		return json_encode($serie);
+		return $serie;
 	}
 
 	protected function convertToPieSerie($data) {
@@ -368,7 +368,7 @@ SQL;
 			$serie[] = $value;
 		}
 
-		return json_encode($serie);
+		return $serie;
 	}
 
 	/**
@@ -411,17 +411,17 @@ SQL;
 	}
 
 	/**
-	 * Translates array content and encode it as JSON
+	 * Translates array content
 	 *
 	 * @param array $data
-	 * @return string
+	 * @return JSON object
 	 */
 	private function convertToTranslatedJson($data = array()) {
 		$translated = array_map(function($a) {
 			return _t('gen.date.' . $a);
 		}, $data);
 
-		return json_encode($translated);
+		return $translated;
 	}
 
 }

+ 2 - 2
app/Models/StatsDAOSQLite.php

@@ -4,9 +4,9 @@ class FreshRSS_StatsDAOSQLite extends FreshRSS_StatsDAO {
 
 	/**
 	 * Calculates entry count per day on a 30 days period.
-	 * Returns the result as a JSON string.
+	 * Returns the result as a JSON object.
 	 *
-	 * @return string
+	 * @return JSON object
 	 */
 	public function calculateEntryCount() {
 		$count = $this->initEntryCountArray();

+ 9 - 55
app/views/stats/index.phtml

@@ -82,58 +82,12 @@
 	</div>
 </div>
 
-<script>
-"use strict";
-function initStats() {
-	if (!window.Flotr) {
-		if (window.console) {
-			console.log('FreshRSS waiting for Flotr…');
-		}
-		window.setTimeout(initStats, 50);
-		return;
-	}
-	// Entry per day
-	var avg = [];
-	for (var i = -31; i <= 0; i++) {
-		avg.push([i, <?php echo $this->average?>]);
-	}
-	Flotr.draw(document.getElementById('statsEntryPerDay'),
-		[{
-			data: <?php echo $this->count ?>,
-			bars: {horizontal: false, show: true}
-		},{
-			data: avg,
-			lines: {show: true},
-			label: "<?php echo $this->average?>"
-		}],
-		{
-			grid: {verticalLines: false},
-			xaxis: {noTicks: 6, showLabels: false, tickDecimals: 0, min: -30.75, max: -0.25},
-			yaxis: {min: 0},
-			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
-		});
-	// Feed per category
-	Flotr.draw(document.getElementById('statsFeedPerCategory'),
-		<?php echo $this->feedByCategory ?>,
-		{
-			grid: {verticalLines: false, horizontalLines: false},
-			pie: {explode: 10, show: true, labelFormatter: function(){return '';}},
-			xaxis: {showLabels: false},
-			yaxis: {showLabels: false},
-			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ numberFormat(obj.y) + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}},
-			legend: {container: document.getElementById('statsFeedPerCategoryLegend'), noColumns: 3}
-		});
-	// Entry per category
-	Flotr.draw(document.getElementById('statsEntryPerCategory'),
-		<?php echo $this->entryByCategory ?>,
-		{
-			grid: {verticalLines: false, horizontalLines: false},
-			pie: {explode: 10, show: true, labelFormatter: function(){return '';}},
-			xaxis: {showLabels: false},
-			yaxis: {showLabels: false},
-			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ numberFormat(obj.y) + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}},
-			legend: {container: document.getElementById('statsEntryPerCategoryLegend'), noColumns: 3}
-		});
-}
-initStats();
-</script>
+<script id="jsonStats" type="application/json"><?php
+echo htmlspecialchars(json_encode(array(
+	'average' => $this->average,
+	'dataCount' => $this->count,
+	'feedByCategory' => $this->feedByCategory,
+	'entryByCategory' => $this->entryByCategory,
+), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES);
+?></script>
+<script src="../scripts/stats.js?<?php echo @filemtime(PUBLIC_PATH . '/scripts/stats.js'); ?>"></script>

+ 20 - 83
app/views/stats/repartition.phtml

@@ -30,20 +30,20 @@
 	<?php }?>
 
 	<div class="stat">
-	    <table>
+		<table>
 		<tr>
-		    <th><?php echo _t('admin.stats.status_total'); ?></th>
-		    <th><?php echo _t('admin.stats.status_read'); ?></th>
-		    <th><?php echo _t('admin.stats.status_unread'); ?></th>
-		    <th><?php echo _t('admin.stats.status_favorites'); ?></th>
+			<th><?php echo _t('admin.stats.status_total'); ?></th>
+			<th><?php echo _t('admin.stats.status_read'); ?></th>
+			<th><?php echo _t('admin.stats.status_unread'); ?></th>
+			<th><?php echo _t('admin.stats.status_favorites'); ?></th>
 		</tr>
 		<tr>
-		    <td class="numeric"><?php echo $this->repartition['total']; ?></td>
-		    <td class="numeric"><?php echo $this->repartition['read']; ?></td>
-		    <td class="numeric"><?php echo $this->repartition['unread']; ?></td>
-		    <td class="numeric"><?php echo $this->repartition['favorite']; ?></td>
+			<td class="numeric"><?php echo $this->repartition['total']; ?></td>
+			<td class="numeric"><?php echo $this->repartition['read']; ?></td>
+			<td class="numeric"><?php echo $this->repartition['unread']; ?></td>
+			<td class="numeric"><?php echo $this->repartition['favorite']; ?></td>
 		</tr>
-	    </table>
+		</table>
 	</div>
 
 	<div class="stat">
@@ -62,76 +62,13 @@
 	</div>
 </div>
 
-<script>
-"use strict";
-function initStats() {
-	if (!window.Flotr) {
-		if (window.console) {
-			console.log('FreshRSS waiting for Flotr…');
-		}
-		window.setTimeout(initStats, 50);
-		return;
-	}
-	// Entry per hour
-	Flotr.draw(document.getElementById('statsEntryPerHour'),
-		[{
-			data: <?php echo $this->repartitionHour ?>,
-			bars: {horizontal: false, show: true}
-		}],
-		{
-			grid: {verticalLines: false},
-			xaxis: {noTicks: 23,
-				tickFormatter: function(x) {
-					var x = parseInt(x);
-					return x + 1;
-				},
-				min: -0.9,
-				max: 23.9,
-				tickDecimals: 0},
-			yaxis: {min: 0},
-			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
-		});
-	// Entry per day of week
-	Flotr.draw(document.getElementById('statsEntryPerDayOfWeek'),
-		[{
-			data: <?php echo $this->repartitionDayOfWeek ?>,
-			bars: {horizontal: false, show: true}
-		}],
-		{
-			grid: {verticalLines: false},
-			xaxis: {noTicks: 6,
-				tickFormatter: function(x) {
-					var x = parseInt(x),
-					    days = <?php echo $this->days?>;
-					return days[x];
-				},
-				min: -0.9,
-				max: 6.9,
-				tickDecimals: 0},
-			yaxis: {min: 0},
-			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
-		});
-	// Entry per month
-	Flotr.draw(document.getElementById('statsEntryPerMonth'),
-		[{
-			data: <?php echo $this->repartitionMonth ?>,
-			bars: {horizontal: false, show: true}
-		}],
-		{
-			grid: {verticalLines: false},
-			xaxis: {noTicks: 12,
-				tickFormatter: function(x) {
-					var x = parseInt(x),
-					    months = <?php echo $this->months?>;
-					return months[(x - 1)];
-				},
-				min: 0.1,
-				max: 12.9,
-				tickDecimals: 0},
-			yaxis: {min: 0},
-			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
-		});
-
-}
-initStats();
-</script>
+<script id="jsonRepartition" type="application/json"><?php
+echo htmlspecialchars(json_encode(array(
+	'repartitionHour' => $this->repartitionHour,
+	'repartitionDayOfWeek' => $this->repartitionDayOfWeek,
+	'days' => $this->days,
+	'repartitionMonth' => $this->repartitionMonth,
+	'months' => $this->months,
+), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES);
+?></script>
+<script src="../scripts/repartition.js?<?php echo @filemtime(PUBLIC_PATH . '/scripts/repartition.js'); ?>"></script>

+ 72 - 0
p/scripts/repartition.js

@@ -0,0 +1,72 @@
+"use strict";
+function initStats() {
+	if (!window.Flotr) {
+		if (window.console) {
+			console.log('FreshRSS waiting for Flotr…');
+		}
+		window.setTimeout(initStats, 50);
+		return;
+	}
+	var jsonRepartition = document.getElementById('jsonRepartition'),
+		stats = JSON.parse(jsonRepartition.innerHTML);
+	jsonRepartition.outerHTML = '';
+	// Entry per hour
+	Flotr.draw(document.getElementById('statsEntryPerHour'),
+		[{
+			data: stats.repartitionHour,
+			bars: {horizontal: false, show: true}
+		}],
+		{
+			grid: {verticalLines: false},
+			xaxis: {noTicks: 23,
+				tickFormatter: function(x) {
+					var x = parseInt(x);
+					return x + 1;
+				},
+				min: -0.9,
+				max: 23.9,
+				tickDecimals: 0},
+			yaxis: {min: 0},
+			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
+		});
+	// Entry per day of week
+	Flotr.draw(document.getElementById('statsEntryPerDayOfWeek'),
+		[{
+			data: stats.repartitionDayOfWeek,
+			bars: {horizontal: false, show: true}
+		}],
+		{
+			grid: {verticalLines: false},
+			xaxis: {noTicks: 6,
+				tickFormatter: function(x) {
+					var x = parseInt(x);
+					return stats.days[x];
+				},
+				min: -0.9,
+				max: 6.9,
+				tickDecimals: 0},
+			yaxis: {min: 0},
+			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
+		});
+	// Entry per month
+	Flotr.draw(document.getElementById('statsEntryPerMonth'),
+		[{
+			data: stats.repartitionMonth,
+			bars: {horizontal: false, show: true}
+		}],
+		{
+			grid: {verticalLines: false},
+			xaxis: {noTicks: 12,
+				tickFormatter: function(x) {
+					var x = parseInt(x);
+					return stats.months[(x - 1)];
+				},
+				min: 0.1,
+				max: 12.9,
+				tickDecimals: 0},
+			yaxis: {min: 0},
+			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
+		});
+
+}
+initStats();

+ 56 - 0
p/scripts/stats.js

@@ -0,0 +1,56 @@
+"use strict";
+function initStats() {
+	if (!window.Flotr) {
+		if (window.console) {
+			console.log('FreshRSS waiting for Flotr…');
+		}
+		window.setTimeout(initStats, 50);
+		return;
+	}
+	var jsonStats = document.getElementById('jsonStats'),
+		stats = JSON.parse(jsonStats.innerHTML);
+	jsonStats.outerHTML = '';
+	// Entry per day
+	var avg = [];
+	for (var i = -31; i <= 0; i++) {
+		avg.push([i, stats.average]);
+	}
+	Flotr.draw(document.getElementById('statsEntryPerDay'),
+		[{
+			data: stats.dataCount,
+			bars: {horizontal: false, show: true}
+		},{
+			data: avg,
+			lines: {show: true},
+			label: stats.average,
+		}],
+		{
+			grid: {verticalLines: false},
+			xaxis: {noTicks: 6, showLabels: false, tickDecimals: 0, min: -30.75, max: -0.25},
+			yaxis: {min: 0},
+			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
+		});
+	// Feed per category
+	Flotr.draw(document.getElementById('statsFeedPerCategory'),
+		stats.feedByCategory,
+		{
+			grid: {verticalLines: false, horizontalLines: false},
+			pie: {explode: 10, show: true, labelFormatter: function(){return '';}},
+			xaxis: {showLabels: false},
+			yaxis: {showLabels: false},
+			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ numberFormat(obj.y) + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}},
+			legend: {container: document.getElementById('statsFeedPerCategoryLegend'), noColumns: 3}
+		});
+	// Entry per category
+	Flotr.draw(document.getElementById('statsEntryPerCategory'),
+		stats.entryByCategory,
+		{
+			grid: {verticalLines: false, horizontalLines: false},
+			pie: {explode: 10, show: true, labelFormatter: function(){return '';}},
+			xaxis: {showLabels: false},
+			yaxis: {showLabels: false},
+			mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ numberFormat(obj.y) + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}},
+			legend: {container: document.getElementById('statsEntryPerCategoryLegend'), noColumns: 3}
+		});
+}
+initStats();