Skip to content
This repository was archived by the owner on Sep 19, 2022. It is now read-only.

Commit c1f3f1b

Browse files
author
Dominik František Bučík
authored
Merge pull request #26 from dBucik/proxystatistics
feat: 🎸 Displaying statistics
2 parents ee29b35 + 75f43c3 commit c1f3f1b

24 files changed

+1422
-247
lines changed

composer.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,20 @@
1212
{
1313
"name": "Michal Prochazka",
1414
"email": "michalp@ics.muni.cz"
15+
},
16+
{
17+
"name": "Dominik Frantisek Bucik",
18+
"email": "bucik@ics.muni.cz"
1519
}
1620
],
1721
"require": {
1822
"simplesamlphp/composer-module-installer": "~1.0",
1923
"simplesamlphp/simplesamlphp": "~v1.19.0",
2024
"cesnet/simplesamlphp-module-perun": "^v7.2.0",
25+
"cesnet/simplesamlphp-module-proxystatistics": "^v7.0.2",
2126
"ext-json": "*",
22-
"ext-curl": "*"
27+
"ext-curl": "*",
28+
"ext-pdo": "*"
2329
},
2430
"require-dev": {
2531
"symplify/easy-coding-standard": "^9.4"

composer.lock

Lines changed: 293 additions & 238 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/stats/Templates.php

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace SimpleSAML\Module\elixir\stats;
4+
5+
use SimpleSAML\Auth\Simple;
6+
use SimpleSAML\Configuration;
7+
use SimpleSAML\Logger;
8+
use SimpleSAML\Module;
9+
use SimpleSAML\Module\proxystatistics\Config;
10+
use SimpleSAML\Module\proxystatistics\DatabaseCommand;
11+
use SimpleSAML\XHTML\Template;
12+
13+
class Templates
14+
{
15+
private const INSTANCE_NAME = 'instance_name';
16+
17+
public static function showProviders($side, $tab)
18+
{
19+
assert(in_array($side, ['identity', 'service'], true));
20+
21+
$t = new Template(Configuration::getInstance(), 'proxystatistics:providers-tpl.php');
22+
$t->data['side'] = $side;
23+
$t->data['tab'] = $tab;
24+
$t->show();
25+
}
26+
27+
public static function pieChart($id)
28+
{
29+
?>
30+
<!-- <canvas id="--><?php //echo $id; ?><!--" class="pieChart chart---><?php //echo $id; ?><!--"></canvas>-->
31+
<div class="pie-chart-container row">
32+
<div class="canvas-container">
33+
<canvas id="<?php echo $id; ?>" class="pieChart chart-<?php echo $id; ?>"></canvas>
34+
</div>
35+
</div>
36+
<?php
37+
}
38+
39+
public static function timeRange($vars = [])
40+
{
41+
$t = new Template(Configuration::getInstance(), 'proxystatistics:timeRange-tpl.php');
42+
$t->data['lastDays'] = self::getSelectedTimeRange();
43+
foreach ($vars as $var => $value) {
44+
$t->data[$var] = $value;
45+
}
46+
47+
$t->show();
48+
}
49+
50+
public static function loginsDashboard()
51+
{
52+
$t = new Template(Configuration::getInstance(), 'proxystatistics:loginsDashboard-tpl.php');
53+
$t->show();
54+
}
55+
56+
public static function showDetail($side)
57+
{
58+
$t = new Template(Configuration::getInstance(), 'proxystatistics:detail-tpl.php');
59+
60+
$lastDays = self::getSelectedTimeRange();
61+
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT, [
62+
'options' => [
63+
'min_range' => 0,
64+
],
65+
]);
66+
$t->data['id'] = $id;
67+
68+
$t->data['detailGraphClass'] = '';
69+
if (Config::getInstance()->getMode() === Utils::theOther(Config::SIDES, $side)) {
70+
$t->data['detailGraphClass'] = 'hidden';
71+
}
72+
73+
self::loadIncludes($t);
74+
75+
$dbCmd = new DatabaseCommand();
76+
$t->data['head'] .= Utils::metaData(
77+
'loginCountPerDay',
78+
$dbCmd->getLoginCountPerDay($lastDays, [
79+
$side => $id,
80+
])
81+
);
82+
$t->data['head'] .= Utils::metaData(
83+
'accessCounts',
84+
$dbCmd->getAccessCount(Utils::theOther(Config::SIDES, $side), $lastDays, [
85+
$side => $id,
86+
])
87+
);
88+
89+
$translations = [
90+
'count' => $t->t('{proxystatistics:stats:count}'),
91+
'other' => $t->t('{proxystatistics:stats:other}'),
92+
'of_logins' => $t->t('{proxystatistics:stats:of_logins}'),
93+
'of_users' => $t->t('{proxystatistics:stats:of_users}'),
94+
];
95+
foreach (Config::SIDES as $s) {
96+
$translations['tables_' . $s] = $t->t('{proxystatistics:stats:side_' . $s . '}');
97+
}
98+
$t->data['head'] .= Utils::metaData('translations', $translations);
99+
100+
$name = $dbCmd->getNameById($side, $id);
101+
$t->data['header'] = $t->t('{proxystatistics:stats:' . $side . 'Detail_header_name}') . $name;
102+
103+
$t->data['side'] = $side;
104+
$t->data['other_side'] = Utils::theOther(Config::SIDES, $side);
105+
106+
$t->show();
107+
}
108+
109+
public static function showIndex()
110+
{
111+
$config = Config::getInstance();
112+
113+
$authSource = $config->getRequiredAuthSource();
114+
if ($authSource) {
115+
$as = new Simple($authSource);
116+
$as->requireAuth();
117+
}
118+
119+
$t = new Template(Configuration::getInstance(), 'proxystatistics:index-tpl.php');
120+
$lastDays = self::getSelectedTimeRange();
121+
122+
$t->data['tab'] = filter_input(
123+
INPUT_GET,
124+
'tab',
125+
FILTER_VALIDATE_INT,
126+
[
127+
'options' => [
128+
'default' => 0,
129+
'min_range' => 0,
130+
'max_range' => 2,
131+
],
132+
]
133+
); // indexed from 0
134+
135+
$t->data['tabsAttributes'] = [
136+
'PROXY' => 'id="tab-1" href="' . Module::getModuleURL('elixir/summary.php') . '?lastDays=' . $lastDays . '"',
137+
'IDP' => 'id="tab-2" href="' . Module::getModuleURL('elixir/identityProviders.php') . '?lastDays=' . $lastDays . '"',
138+
'SP' => 'id="tab-3" href="' . Module::getModuleURL('elixir/serviceProviders.php') . '?lastDays=' . $lastDays . '"',
139+
];
140+
$mode = $config->getMode();
141+
if (Config::MODE_PROXY !== $mode) {
142+
$t->data['tabsAttributes'][$mode] = 'class="hidden" ' . $t->data['tabsAttributes'][$mode];
143+
}
144+
145+
$t->data['header'] = $t->t('{proxystatistics:stats:statistics_header}');
146+
$instanceName = Configuration::getInstance()->getString(self::INSTANCE_NAME, null);
147+
if (null !== $instanceName) {
148+
$t->data['header'] = $instanceName . ' ' . $t->data['header'];
149+
} else {
150+
Logger::warning('Missing configuration: config.php - instance_name is not set.');
151+
}
152+
153+
self::loadIncludes($t);
154+
155+
$dbCmd = new DatabaseCommand();
156+
$t->data['head'] .= Utils::metaData('loginCountPerDay', $dbCmd->getLoginCountPerDay($lastDays));
157+
158+
$translations = [
159+
'count' => $t->t('{proxystatistics:stats:count}'),
160+
'other' => $t->t('{proxystatistics:stats:other}'),
161+
'of_logins' => $t->t('{proxystatistics:stats:of_logins}'),
162+
'of_users' => $t->t('{proxystatistics:stats:of_users}'),
163+
];
164+
foreach (Config::SIDES as $side) {
165+
$otherSide = Utils::theOther(Config::SIDES, $side);
166+
$t->data['head'] .= Utils::metaData(
167+
'loginCountPer' . $side,
168+
$dbCmd->getAccessCount($side, $lastDays, [
169+
$otherSide => null,
170+
])
171+
);
172+
$translations['tables_' . $side] = $t->t('{proxystatistics:stats:side_' . $side . '}');
173+
}
174+
175+
$t->data['head'] .= Utils::metaData('translations', $translations);
176+
177+
$t->show();
178+
}
179+
180+
public static function showLegend($t, $side)
181+
{
182+
$mode = Config::getInstance()->getMode();
183+
echo $t->t(
184+
'{proxystatistics:stats:chart_legend}',
185+
[
186+
'!side_of' => $t->t('{proxystatistics:stats:chart_legend_side_of_' . $side . '}'),
187+
'!side_on' => $t->t('{proxystatistics:stats:chart_legend_side_on_' . $side . '}'),
188+
]
189+
);
190+
if (Config::MODE_SP === $side && Config::MODE_SP !== $mode) {
191+
echo ' ';
192+
echo $t->t(
193+
'{proxystatistics:stats:first_access_only}',
194+
[
195+
'!through_mode' => $t->t('{proxystatistics:stats:through_mode_' . $mode . '}'),
196+
]
197+
);
198+
}
199+
}
200+
201+
public static function showSummary()
202+
{
203+
$t = new Template(Configuration::getInstance(), 'proxystatistics:summary-tpl.php');
204+
$t->data['tab'] = 0;
205+
206+
$mode = Config::getInstance()->getMode();
207+
$t->data['mode'] = $mode;
208+
$t->data['summaryGraphs'] = [];
209+
if (Config::MODE_PROXY === $mode || Config::MODE_MULTI_IDP === $mode) {
210+
foreach (Config::SIDES as $side) {
211+
$t->data['summaryGraphs'][$side] = [];
212+
$t->data['summaryGraphs'][$side]['Providers'] = 'col-md-6 graph';
213+
$t->data['summaryGraphs'][$side]['ProvidersLegend'] = 'col-md-12';
214+
$t->data['summaryGraphs'][$side]['ProvidersGraph'] = 'col-md-12';
215+
}
216+
} else {
217+
$side = $mode;
218+
$t->data['summaryGraphs'][$side] = [];
219+
$t->data['summaryGraphs'][$side]['Providers'] = 'hidden';
220+
$t->data['summaryGraphs'][$side]['ProvidersLegend'] = '';
221+
$t->data['summaryGraphs'][$side]['ProvidersGraph'] = '';
222+
$otherSide = Utils::theOther(Config::SIDES, $side);
223+
$t->data['summaryGraphs'][$otherSide] = [];
224+
$t->data['summaryGraphs'][$otherSide]['Providers'] = 'col-md-12 graph';
225+
$t->data['summaryGraphs'][$otherSide]['ProvidersLegend'] = 'col-md-6';
226+
$t->data['summaryGraphs'][$otherSide]['ProvidersGraph'] = 'col-md-6 col-md-offset-3';
227+
}
228+
229+
$t->show();
230+
}
231+
232+
private static function getSelectedTimeRange()
233+
{
234+
return filter_input(
235+
INPUT_GET,
236+
'lastDays',
237+
FILTER_VALIDATE_INT,
238+
[
239+
'options' => [
240+
'default' => 0,
241+
'min_range' => 0,
242+
],
243+
]
244+
);
245+
}
246+
247+
private static function loadIncludes($t)
248+
{
249+
if (empty($t->data['head'])) {
250+
$t->data['head'] = '';
251+
}
252+
$t->data['head'] .= '<link rel="stylesheet" media="screen" type="text/css" href="' .
253+
Module::getModuleUrl('elixir/res/css/jquery-ui.min.css') . '" />';
254+
$t->data['head'] .= '<link rel="stylesheet" media="screen" type="text/css" href="' .
255+
MOdule::getModuleURL('elixir/res/css/stats.css') . '" />';
256+
257+
if (empty($t->data['scripts'])) {
258+
$t->data['scripts'] = '';
259+
}
260+
$t->data['scripts'] .= '<script type="text/javascript" src="' .
261+
Module::getModuleUrl('elixir/res/js/jquery-ui.min.js') . '"></script>';
262+
$t->data['scripts'] .= '<script type="text/javascript" src="' .
263+
self::getFullUrl('assets/js/moment-with-locales.min.js') . '"></script>';
264+
$t->data['scripts'] .= '<script type="text/javascript" src="' .
265+
self::getFullUrl('assets/js/chart.min.js') . '"></script>';
266+
$t->data['scripts'] .= '<script type="text/javascript" src="' .
267+
self::getFullUrl('assets/js/hammer.min.js') . '"></script>';
268+
$t->data['scripts'] .= '<script type="text/javascript" src="' .
269+
self::getFullUrl('assets/js/chartjs-plugin-zoom.min.js') . '"></script>';
270+
$t->data['scripts'] .= '<script type="text/javascript" src="' .
271+
self::getFullUrl('assets/js/chartjs-adapter-moment.js') . '"></script>';
272+
$t->data['scripts'] .= '<script type="text/javascript" src="' .
273+
Module::getModuleUrl('elixir/res/js/charts.js') . '"></script>';
274+
275+
$t->data['head'] .= Utils::metaData('module_url_base', Module::getModuleUrl('elixir/'));
276+
}
277+
278+
private static function getFullUrl($path = ''): string
279+
{
280+
return Module::getModuleUrl('proxystatistics/') . $path;
281+
}
282+
}

lib/stats/Utils.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace SimpleSAML\Module\elixir\stats;
4+
5+
use SimpleSAML\Auth\Simple;
6+
use SimpleSAML\Configuration;
7+
use SimpleSAML\Logger;
8+
use SimpleSAML\Module;
9+
use SimpleSAML\XHTML\Template;
10+
11+
class Utils
12+
{
13+
public static function theOther($arr, $val)
14+
{
15+
return current(array_diff($arr, [$val]));
16+
}
17+
18+
public static function metaData($id, $data)
19+
{
20+
return '<meta name="' . $id . '" id="' . $id . '" ' .
21+
'content="' . htmlspecialchars(json_encode($data, JSON_NUMERIC_CHECK)) . '">';
22+
}
23+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php declare(strict_types=1);
2+
3+
use SimpleSAML\Module;
4+
5+
if (!empty($this->data['htmlinject']['htmlContentPost'])) {
6+
foreach ($this->data['htmlinject']['htmlContentPost'] as $c) {
7+
echo $c;
8+
}
9+
}
10+
11+
?>
12+
13+
</div> <!-- ENDCOL -->
14+
</div> <!-- ENDROW -->
15+
<footer>
16+
<div class="footer offset-1 col-10 offset-sm-1 col-sm-10 offset-md-2 col-md-8 offset-lg-3 col-lg-6 offset-xl-3 col-xl-6">
17+
<div class="footer-contact">
18+
<a class="contact-link" href="mailto:support@aai.lifescience-ri.eu">Contact us</a>
19+
</div>
20+
<div class="footer-policy">
21+
<a class="footer-policy-link" href="https://lifescience-ri.eu/ls-login/ls-aai-aup.html">Privacy Policy</a>
22+
</div>
23+
</div>
24+
</footer>
25+
<script type="text/javascript" src="<?php echo Module::getModuleURL('elixir/res/js/jquery-3.5.1.min.js'); ?>"></script>
26+
<script type="text/javascript" src="<?php echo Module::getModuleURL('elixir/res/js/bootstrap.min.js'); ?>"></script>
27+
<script type="text/javascript" src="<?php echo Module::getModuleURL('elixir/res/js/cmservice.js'); ?>"></script>
28+
<?php
29+
if (array_key_exists('scripts', $this->data)) {
30+
echo $this->data['scripts'];
31+
}
32+
?>
33+
</body>
34+
</html>

themes/elixir/default/includes/footer.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,10 @@
2727
<script type="text/javascript" src="<?php echo Module::getModuleURL('elixir/res/js/jquery-3.5.1.min.js'); ?>"></script>
2828
<script type="text/javascript" src="<?php echo Module::getModuleURL('elixir/res/js/bootstrap.min.js'); ?>"></script>
2929
<script type="text/javascript" src="<?php echo Module::getModuleURL('elixir/res/js/cmservice.js'); ?>"></script>
30+
<?php
31+
if (array_key_exists('scripts', $this->data)) {
32+
echo $this->data['scripts'];
33+
}
34+
?>
3035
</body>
3136
</html>

0 commit comments

Comments
 (0)