mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 03:26:04 +01:00
fixes #4463
This commit is contained in:
109
public/src/admin/manage/category-analytics.js
Normal file
109
public/src/admin/manage/category-analytics.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
"use strict";
|
||||||
|
/*global config, define, app, socket, ajaxify, bootbox, templates, Chart, utils */
|
||||||
|
|
||||||
|
define('admin/manage/category-analytics', [], function() {
|
||||||
|
var CategoryAnalytics = {};
|
||||||
|
|
||||||
|
CategoryAnalytics.init = function() {
|
||||||
|
var hourlyCanvas = document.getElementById('pageviews:hourly'),
|
||||||
|
dailyCanvas = document.getElementById('pageviews:daily'),
|
||||||
|
topicsCanvas = document.getElementById('topics:daily'),
|
||||||
|
postsCanvas = document.getElementById('posts:daily'),
|
||||||
|
hourlyLabels = utils.getHoursArray().map(function(text, idx) {
|
||||||
|
return idx % 3 ? '' : text;
|
||||||
|
}),
|
||||||
|
dailyLabels = utils.getDaysArray().map(function(text, idx) {
|
||||||
|
return idx % 3 ? '' : text;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (utils.isMobile()) {
|
||||||
|
Chart.defaults.global.showTooltips = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
'pageviews:hourly': {
|
||||||
|
labels: hourlyLabels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "",
|
||||||
|
fillColor: "rgba(186,139,175,0.2)",
|
||||||
|
strokeColor: "rgba(186,139,175,1)",
|
||||||
|
pointColor: "rgba(186,139,175,1)",
|
||||||
|
pointStrokeColor: "#fff",
|
||||||
|
pointHighlightFill: "#fff",
|
||||||
|
pointHighlightStroke: "rgba(186,139,175,1)",
|
||||||
|
data: ajaxify.data.analytics['pageviews:hourly']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'pageviews:daily': {
|
||||||
|
labels: dailyLabels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "",
|
||||||
|
fillColor: "rgba(151,187,205,0.2)",
|
||||||
|
strokeColor: "rgba(151,187,205,1)",
|
||||||
|
pointColor: "rgba(151,187,205,1)",
|
||||||
|
pointStrokeColor: "#fff",
|
||||||
|
pointHighlightFill: "#fff",
|
||||||
|
pointHighlightStroke: "rgba(151,187,205,1)",
|
||||||
|
data: ajaxify.data.analytics['pageviews:daily']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'topics:daily': {
|
||||||
|
labels: dailyLabels.slice(-7),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "",
|
||||||
|
fillColor: "rgba(171,70,66,0.2)",
|
||||||
|
strokeColor: "rgba(171,70,66,1)",
|
||||||
|
pointColor: "rgba(171,70,66,1)",
|
||||||
|
pointStrokeColor: "#fff",
|
||||||
|
pointHighlightFill: "#fff",
|
||||||
|
pointHighlightStroke: "rgba(171,70,66,1)",
|
||||||
|
data: ajaxify.data.analytics['topics:daily']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'posts:daily': {
|
||||||
|
labels: dailyLabels.slice(-7),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "",
|
||||||
|
fillColor: "rgba(161,181,108,0.2)",
|
||||||
|
strokeColor: "rgba(161,181,108,1)",
|
||||||
|
pointColor: "rgba(161,181,108,1)",
|
||||||
|
pointStrokeColor: "#fff",
|
||||||
|
pointHighlightFill: "#fff",
|
||||||
|
pointHighlightStroke: "rgba(161,181,108,1)",
|
||||||
|
data: ajaxify.data.analytics['posts:daily']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
hourlyCanvas.width = $(hourlyCanvas).parent().width();
|
||||||
|
dailyCanvas.width = $(dailyCanvas).parent().width();
|
||||||
|
topicsCanvas.width = $(topicsCanvas).parent().width();
|
||||||
|
postsCanvas.width = $(postsCanvas).parent().width();
|
||||||
|
new Chart(hourlyCanvas.getContext('2d')).Line(data['pageviews:hourly'], {
|
||||||
|
responsive: true,
|
||||||
|
animation: false
|
||||||
|
});
|
||||||
|
new Chart(dailyCanvas.getContext('2d')).Line(data['pageviews:daily'], {
|
||||||
|
responsive: true,
|
||||||
|
animation: false
|
||||||
|
});
|
||||||
|
new Chart(topicsCanvas.getContext('2d')).Line(data['topics:daily'], {
|
||||||
|
responsive: true,
|
||||||
|
animation: false
|
||||||
|
});
|
||||||
|
new Chart(postsCanvas.getContext('2d')).Line(data['posts:daily'], {
|
||||||
|
responsive: true,
|
||||||
|
animation: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return CategoryAnalytics;
|
||||||
|
});
|
||||||
@@ -154,12 +154,6 @@ define('admin/manage/category', [
|
|||||||
});
|
});
|
||||||
|
|
||||||
Category.setupPrivilegeTable();
|
Category.setupPrivilegeTable();
|
||||||
|
|
||||||
if (window.location.hash === '#analytics') {
|
|
||||||
Category.setupGraphs();
|
|
||||||
} else {
|
|
||||||
$('a[href="#analytics"]').on('shown.bs.tab', Category.setupGraphs);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Category.setupPrivilegeTable = function() {
|
Category.setupPrivilegeTable = function() {
|
||||||
@@ -360,106 +354,5 @@ define('admin/manage/category', [
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Category.setupGraphs = function() {
|
|
||||||
var hourlyCanvas = document.getElementById('pageviews:hourly'),
|
|
||||||
dailyCanvas = document.getElementById('pageviews:daily'),
|
|
||||||
topicsCanvas = document.getElementById('topics:daily'),
|
|
||||||
postsCanvas = document.getElementById('posts:daily'),
|
|
||||||
hourlyLabels = utils.getHoursArray().map(function(text, idx) {
|
|
||||||
return idx % 3 ? '' : text;
|
|
||||||
}),
|
|
||||||
dailyLabels = utils.getDaysArray().map(function(text, idx) {
|
|
||||||
return idx % 3 ? '' : text;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (utils.isMobile()) {
|
|
||||||
Chart.defaults.global.showTooltips = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var data = {
|
|
||||||
'pageviews:hourly': {
|
|
||||||
labels: hourlyLabels,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: "",
|
|
||||||
fillColor: "rgba(186,139,175,0.2)",
|
|
||||||
strokeColor: "rgba(186,139,175,1)",
|
|
||||||
pointColor: "rgba(186,139,175,1)",
|
|
||||||
pointStrokeColor: "#fff",
|
|
||||||
pointHighlightFill: "#fff",
|
|
||||||
pointHighlightStroke: "rgba(186,139,175,1)",
|
|
||||||
data: ajaxify.data.analytics['pageviews:hourly']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'pageviews:daily': {
|
|
||||||
labels: dailyLabels,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: "",
|
|
||||||
fillColor: "rgba(151,187,205,0.2)",
|
|
||||||
strokeColor: "rgba(151,187,205,1)",
|
|
||||||
pointColor: "rgba(151,187,205,1)",
|
|
||||||
pointStrokeColor: "#fff",
|
|
||||||
pointHighlightFill: "#fff",
|
|
||||||
pointHighlightStroke: "rgba(151,187,205,1)",
|
|
||||||
data: ajaxify.data.analytics['pageviews:daily']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'topics:daily': {
|
|
||||||
labels: dailyLabels.slice(-7),
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: "",
|
|
||||||
fillColor: "rgba(171,70,66,0.2)",
|
|
||||||
strokeColor: "rgba(171,70,66,1)",
|
|
||||||
pointColor: "rgba(171,70,66,1)",
|
|
||||||
pointStrokeColor: "#fff",
|
|
||||||
pointHighlightFill: "#fff",
|
|
||||||
pointHighlightStroke: "rgba(171,70,66,1)",
|
|
||||||
data: ajaxify.data.analytics['topics:daily']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'posts:daily': {
|
|
||||||
labels: dailyLabels.slice(-7),
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: "",
|
|
||||||
fillColor: "rgba(161,181,108,0.2)",
|
|
||||||
strokeColor: "rgba(161,181,108,1)",
|
|
||||||
pointColor: "rgba(161,181,108,1)",
|
|
||||||
pointStrokeColor: "#fff",
|
|
||||||
pointHighlightFill: "#fff",
|
|
||||||
pointHighlightStroke: "rgba(161,181,108,1)",
|
|
||||||
data: ajaxify.data.analytics['posts:daily']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
hourlyCanvas.width = $(hourlyCanvas).parent().width();
|
|
||||||
dailyCanvas.width = $(dailyCanvas).parent().width();
|
|
||||||
topicsCanvas.width = $(topicsCanvas).parent().width();
|
|
||||||
postsCanvas.width = $(postsCanvas).parent().width();
|
|
||||||
new Chart(hourlyCanvas.getContext('2d')).Line(data['pageviews:hourly'], {
|
|
||||||
responsive: true,
|
|
||||||
animation: false
|
|
||||||
});
|
|
||||||
new Chart(dailyCanvas.getContext('2d')).Line(data['pageviews:daily'], {
|
|
||||||
responsive: true,
|
|
||||||
animation: false
|
|
||||||
});
|
|
||||||
new Chart(topicsCanvas.getContext('2d')).Line(data['topics:daily'], {
|
|
||||||
responsive: true,
|
|
||||||
animation: false
|
|
||||||
});
|
|
||||||
new Chart(postsCanvas.getContext('2d')).Line(data['posts:daily'], {
|
|
||||||
responsive: true,
|
|
||||||
animation: false
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return Category;
|
return Category;
|
||||||
});
|
});
|
||||||
@@ -14,31 +14,38 @@ var categoriesController = {};
|
|||||||
categoriesController.get = function(req, res, next) {
|
categoriesController.get = function(req, res, next) {
|
||||||
async.parallel({
|
async.parallel({
|
||||||
category: async.apply(categories.getCategories, [req.params.category_id], req.user.uid),
|
category: async.apply(categories.getCategories, [req.params.category_id], req.user.uid),
|
||||||
privileges: async.apply(privileges.categories.list, req.params.category_id),
|
privileges: async.apply(privileges.categories.list, req.params.category_id)
|
||||||
analytics: async.apply(analytics.getCategoryAnalytics, req.params.category_id)
|
|
||||||
}, function(err, data) {
|
}, function(err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.fireHook('filter:admin.category.get', { req: req, res: res, category: data.category[0], privileges: data.privileges, analytics: data.analytics }, function(err, data) {
|
plugins.fireHook('filter:admin.category.get', { req: req, res: res, category: data.category[0], privileges: data.privileges }, function(err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
data.category.name = translator.escape(data.category.name);
|
data.category.name = translator.escape(data.category.name);
|
||||||
res.render('admin/manage/category', {
|
res.render('admin/manage/category', {
|
||||||
category: data.category,
|
category: data.category,
|
||||||
privileges: data.privileges,
|
privileges: data.privileges
|
||||||
analytics: data.analytics
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
categoriesController.getAll = function(req, res, next) {
|
categoriesController.getAll = function(req, res, next) {
|
||||||
//Categories list will be rendered on client side with recursion, etc.
|
// Categories list will be rendered on client side with recursion, etc.
|
||||||
res.render('admin/manage/categories', {});
|
res.render('admin/manage/categories', {});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
categoriesController.getAnalytics = function(req, res, next) {
|
||||||
|
async.parallel({
|
||||||
|
name: async.apply(categories.getCategoryField, req.params.category_id, 'name'),
|
||||||
|
analytics: async.apply(analytics.getCategoryAnalytics, req.params.category_id)
|
||||||
|
}, function(err, data) {
|
||||||
|
res.render('admin/manage/category-analytics', data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
module.exports = categoriesController;
|
module.exports = categoriesController;
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ function addRoutes(router, middleware, controllers) {
|
|||||||
|
|
||||||
router.get('/manage/categories', middlewares, controllers.admin.categories.getAll);
|
router.get('/manage/categories', middlewares, controllers.admin.categories.getAll);
|
||||||
router.get('/manage/categories/:category_id', middlewares, controllers.admin.categories.get);
|
router.get('/manage/categories/:category_id', middlewares, controllers.admin.categories.get);
|
||||||
|
router.get('/manage/categories/:category_id/analytics', middlewares, controllers.admin.categories.getAnalytics);
|
||||||
|
|
||||||
router.get('/manage/tags', middlewares, controllers.admin.tags.get);
|
router.get('/manage/tags', middlewares, controllers.admin.tags.get);
|
||||||
router.get('/manage/flags', middlewares, controllers.admin.flags.get);
|
router.get('/manage/flags', middlewares, controllers.admin.flags.get);
|
||||||
|
|||||||
53
src/views/admin/manage/category-analytics.tpl
Normal file
53
src/views/admin/manage/category-analytics.tpl
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<a class="btn btn-primary" href="{config.relative_path}/admin/manage/categories"><i class="fa fa-fw fa-chevron-left"></i> Back to Categories List</a>
|
||||||
|
|
||||||
|
<h3>Analytics for "{name}" category</h3>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6 text-center">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<div><canvas id="pageviews:hourly" height="250"></canvas></div>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer"><small><strong>Figure 1</strong> – Hourly page views for this category</small></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 text-center">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<div><canvas id="pageviews:daily" height="250"></canvas></div>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer"><small><strong>Figure 2</strong> – Daily page views for this category</small></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6 text-center">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<div><canvas id="topics:daily" height="250"></canvas></div>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer"><small><strong>Figure 3</strong> – Daily topics created in this category</small></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 text-center">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<div><canvas id="posts:daily" height="250"></canvas></div>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer"><small><strong>Figure 4</strong> – Daily posts made in this category</small></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
<ul class="nav nav-pills">
|
<ul class="nav nav-pills">
|
||||||
<li class="active"><a href="#category-settings" data-toggle="tab">Category Settings</a></li>
|
<li class="active"><a href="#category-settings" data-toggle="tab">Category Settings</a></li>
|
||||||
<li><a href="#privileges" data-toggle="tab">Privileges</a></li>
|
<li><a href="#privileges" data-toggle="tab">Privileges</a></li>
|
||||||
<li><a href="#analytics" data-toggle="tab">Analytics</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<br />
|
<br />
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
@@ -128,37 +127,6 @@
|
|||||||
<!-- IMPORT admin/partials/categories/privileges.tpl -->
|
<!-- IMPORT admin/partials/categories/privileges.tpl -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane fade col-xs-12" id="analytics">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-6 text-center">
|
|
||||||
<div><canvas id="pageviews:hourly" height="250"></canvas></div>
|
|
||||||
<p>
|
|
||||||
<small><strong>Figure 1</strong> – Hourly page views for this category</small>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 text-center">
|
|
||||||
<div><canvas id="pageviews:daily" height="250"></canvas></div>
|
|
||||||
<p>
|
|
||||||
<small><strong>Figure 2</strong> – Daily page views for this category</small>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-6 text-center">
|
|
||||||
<div><canvas id="topics:daily" height="250"></canvas></div>
|
|
||||||
<p>
|
|
||||||
<small><strong>Figure 3</strong> – Daily topics created in this category</small>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 text-center">
|
|
||||||
<div><canvas id="posts:daily" height="250"></canvas></div>
|
|
||||||
<p>
|
|
||||||
<small><strong>Figure 4</strong> – Daily posts made in this category</small>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
<button data-cid="{categories.cid}" data-action="toggle" data-disabled="{categories.disabled}" class="btn <!-- IF categories.disabled -->btn-primary<!-- ELSE -->btn-danger<!-- ENDIF categories.disabled -->">
|
<button data-cid="{categories.cid}" data-action="toggle" data-disabled="{categories.disabled}" class="btn <!-- IF categories.disabled -->btn-primary<!-- ELSE -->btn-danger<!-- ENDIF categories.disabled -->">
|
||||||
<!-- IF categories.disabled -->Enable<!-- ELSE -->Disable<!-- ENDIF categories.disabled -->
|
<!-- IF categories.disabled -->Enable<!-- ELSE -->Disable<!-- ENDIF categories.disabled -->
|
||||||
</button>
|
</button>
|
||||||
|
<a href="./categories/{categories.cid}/analytics" class="btn btn-default"><i class="fa fa-line-chart"></i></a>
|
||||||
<a href="./categories/{categories.cid}" class="btn btn-default">Edit</a>
|
<a href="./categories/{categories.cid}" class="btn btn-default">Edit</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user