Commit ec0d1b3787cb74a93ddfac948839da26f09d0936
1 parent
f226ca53
Exists in
master
and in
1 other branch
Grosse amélioration des stats de connexion (pages /stats)
v4.106.3-3.7.9
Showing
8 changed files
with
263 additions
and
54 deletions
Show diff stats
CHANGES.txt
... | ... | @@ -134,12 +134,12 @@ Outre ces changements, voici d'autres changements importants : |
134 | 134 | ======= CHANGES ======= |
135 | 135 | |
136 | 136 | ------- |
137 | -22/10/2020 v4.106.1-3.7.9 | |
138 | - - (b) Bugfix vues pour les stats | |
137 | +22/10/2020 v4.106.3-3.7.9 | |
138 | + - (e) Grosse amélioration des stats de connexion (pages /stats) | |
139 | 139 | |
140 | 140 | ------- |
141 | 141 | 21/10/2020 v4.106.0-3.7.9 |
142 | - - (i) Ajout d'une nouvelle table "stats" qui maintient pour chaque utilisateur son nb de connexions et sa durée de connexion par année | |
142 | + - (i) Ajout d'une nouvelle table "stats" (/stats) qui maintient pour chaque utilisateur son nb de connexions et sa durée de connexion par année | |
143 | 143 | - (i) Correction d'un TRÈS VIEUX problème qui faisait que lorsqu'on POST des données (bouton Submit) |
144 | 144 | puis qu'on revient en arrière, on avait une page blanche avec message erreur "Le document a expiré..." |
145 | 145 | C'est enfin réglé ! | ... | ... |
README.md
... | ... | @@ -43,7 +43,7 @@ Logiciel testé et validé sur les configurations suivantes : |
43 | 43 | -------------------------------------------------------------------------------------------- |
44 | 44 | |
45 | 45 | Date: 22/10/2020 |
46 | -Version: 4.106.1-3.7.9 | |
46 | +Version: 4.106.3-3.7.9 | |
47 | 47 | |
48 | 48 | |
49 | 49 | HISTORIQUE DES CHANGEMENTS DE VERSION : voir le fichier CHANGES.txt (ou la page web /pages/changes) | ... | ... |
config/bootstrap.php
1 | - <?php | |
1 | +<?php | |
2 | + | |
3 | +//header('Cache-Control: private_no_expire, must-revalidate'); | |
4 | + | |
2 | 5 | /** |
3 | 6 | * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) |
4 | 7 | * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) |
... | ... | @@ -277,3 +280,7 @@ Type::build('datetime') |
277 | 280 | //Cake\Core\Plugin::load('BootstrapUI'); |
278 | 281 | //Plugin::load('BootstrapUI'); |
279 | 282 | Plugin::load('BootstrapUI', ['autoload' => true]); |
283 | + | |
284 | +// (EP20201022) Pour gérer le timeout auto | |
285 | +DispatcherFactory::add('SessionTimeout'); | |
286 | + | ... | ... |
... | ... | @@ -0,0 +1,8 @@ |
1 | +use database; | |
2 | + | |
3 | +-- | |
4 | +-- Ajout du champ stats.last_connex_dur | |
5 | +-- | |
6 | +ALTER TABLE stats | |
7 | + ADD last_connex_dur INT NOT NULL DEFAULT 0 AFTER last_login_time COMMENT 'Temps de connexion de la derniere session', | |
8 | + CHANGE connex_dur connex_dur_tot INT(11) NOT NULL DEFAULT 0 COMMENT "Total temps connexion cumulé sur l'année (sec)"; | ... | ... |
src/Controller/UsersController.php
... | ... | @@ -208,7 +208,7 @@ class UsersController extends AppController |
208 | 208 | ] |
209 | 209 | */ |
210 | 210 | |
211 | - $this->_updateStatsForUserLogin($user); | |
211 | + $this->_updateStatsForCurrentUserOnLogin(); | |
212 | 212 | //exit; |
213 | 213 | |
214 | 214 | // On va maintenant à la page qui etait demandée |
... | ... | @@ -220,45 +220,63 @@ class UsersController extends AppController |
220 | 220 | } |
221 | 221 | } |
222 | 222 | |
223 | - public function logout() | |
224 | - { | |
223 | + public function logout() { | |
224 | + //debug("LOGOUT !"); | |
225 | 225 | //debug($this->u); |
226 | - $this->_updateStatsForUserLogout($this->u); | |
227 | - //exit; | |
226 | + $this->_updateStatsForCurrentUserOnLogout(); | |
228 | 227 | //$this->Flash->success('You are now logged out.'); |
229 | 228 | return $this->redirect($this->LdapAuth->logout()); |
230 | 229 | // Puis ça va sur /users/login |
231 | 230 | } |
232 | 231 | |
232 | + | |
233 | + /* | |
234 | + * Renvoie les infos de l'utilisateur qui sont dans la session | |
235 | + * | |
236 | + * Voici ce que contient $_SESSION['Auth']['User'] par exemple : | |
237 | + [ | |
238 | + 'sn' => [ | |
239 | + (int) 0 => 'Pallier' | |
240 | + ], | |
241 | + 'mail' => [ | |
242 | + (int) 0 => 'Etienne.Pallier@irap.omp.eu' | |
243 | + ], | |
244 | + 'givenname' => [ | |
245 | + (int) 0 => 'Etienne' | |
246 | + ], | |
247 | + 'uid' => [ | |
248 | + (int) 0 => 'epallier' | |
249 | + ], | |
250 | + 'userpassword' => [ | |
251 | + (int) 0 => 'mot-de-passe-crypté' | |
252 | + ] | |
253 | + ] | |
254 | + */ | |
255 | + private function _getCurrentUserInfosFromSession() { | |
256 | + // Current user infos | |
257 | + return isset($_SESSION['Auth']['User']) ? $_SESSION['Auth']['User'] : []; | |
258 | + } | |
259 | + private function _getCurrentUserFullNameFromSession($user_infos = null) { | |
260 | + if (!$user_infos) $user_infos = $this->_getCurrentUserInfosFromSession(); | |
261 | + // "Pallier Etienne" | |
262 | + return $user_infos ? $user_infos['sn'][0].' '.$user_infos['givenname'][0] : 'Name Firstname'; | |
263 | + } | |
264 | + private function _getCurrentUserEntityFromSession($session_user = null) { | |
265 | + $user_fullname = $this->_getCurrentUserFullNameFromSession($session_user); | |
266 | + //debug($user_fullname); | |
267 | + // "Pallier Etienne" | |
268 | + return $this->Users->find()->where(['nom'=>$user_fullname])->first(); | |
269 | + } | |
270 | + | |
271 | + | |
233 | 272 | /* |
234 | 273 | * Mise à jour des stats pour ce user (et pour l'année courante) au moment du login |
235 | 274 | * => on enregistre son heure de connexion |
236 | 275 | * => on incrémente le nombre de connexions |
237 | - * | |
238 | - * Voici ce que contient $user_infos par exemple : | |
239 | - [ | |
240 | - 'sn' => [ | |
241 | - (int) 0 => 'Pallier' | |
242 | - ], | |
243 | - 'mail' => [ | |
244 | - (int) 0 => 'Etienne.Pallier@irap.omp.eu' | |
245 | - ], | |
246 | - 'givenname' => [ | |
247 | - (int) 0 => 'Etienne' | |
248 | - ], | |
249 | - 'uid' => [ | |
250 | - (int) 0 => 'epallier' | |
251 | - ], | |
252 | - 'userpassword' => [ | |
253 | - (int) 0 => 'mot-de-passe-crypté' | |
254 | - ] | |
255 | - ] | |
256 | 276 | */ |
257 | - private function _updateStatsForUserLogin($user_infos) { | |
277 | + private function _updateStatsForCurrentUserOnLogin() { | |
258 | 278 | |
259 | - // "Pallier Etienne" | |
260 | - $user_fullname = $user_infos['sn'][0].' '.$user_infos['givenname'][0]; | |
261 | - $user = $this->Users->find()->where(['nom'=>$user_fullname])->first(); | |
279 | + $user_id = $this->_getCurrentUserEntityFromSession()->id; | |
262 | 280 | //debug($user); |
263 | 281 | |
264 | 282 | $now = new FrozenTime(); |
... | ... | @@ -266,8 +284,9 @@ class UsersController extends AppController |
266 | 284 | $year = $now->format('yy'); |
267 | 285 | //debug($year); |
268 | 286 | |
287 | + // On recup la Stat du user courant | |
269 | 288 | $table_stats = $this->Users->Stats; |
270 | - $stat_id = [$year,$user->id]; | |
289 | + $stat_id = [$year,$user_id]; | |
271 | 290 | $current_user_stat = null; |
272 | 291 | try { |
273 | 292 | // Si un enregistrement existe pour ce user (et cette année) => on le récupère |
... | ... | @@ -291,20 +310,32 @@ class UsersController extends AppController |
291 | 310 | //$stat_entity = $table_stats->newEntity($stat); |
292 | 311 | $current_user_stat = $table_stats->newEntity(); |
293 | 312 | $current_user_stat->year = $year; |
294 | - $current_user_stat->user_id = $user->id; | |
313 | + $current_user_stat->user_id = $user_id; | |
295 | 314 | } |
296 | 315 | $last_login_time = $current_user_stat->last_login_time; |
297 | 316 | $last_logout_time = $current_user_stat->last_logout_time; |
317 | + | |
298 | 318 | /* |
299 | 319 | * Check le cas où le logout n'a pas été enregistré |
300 | 320 | * (le user n'a pas fait logout, ça s'est fait tout seul par timeout) |
301 | - * => Dans ce cas, on ajoute un temps de connexion FORFAITAIRE de 10 minutes à la durée totale | |
321 | + * => Dans ce cas, on ajoute à la durée totale le dernier temps de connexion last_connex_dur | |
302 | 322 | */ |
303 | - if ($last_logout_time < $last_login_time) | |
304 | - $current_user_stat->connex_dur += 10*60; | |
305 | - // On met à jour le champ last_login_time | |
323 | + if ($last_logout_time < $last_login_time) { | |
324 | + $last_connex_dur = $current_user_stat->last_connex_dur; | |
325 | + /* | |
326 | + * (Optimisation): Si la dernière session a été très courte (<10s), on ne le comptabilise pas | |
327 | + * => on décrémente le nb de connexions (passées) | |
328 | + */ | |
329 | + if ($current_user_stat->connex_nb>0 && $last_connex_dur<10) | |
330 | + $current_user_stat->connex_nb--; | |
331 | + else | |
332 | + $current_user_stat->connex_dur_tot += $last_connex_dur; | |
333 | + } | |
334 | + | |
335 | + // On met à jour le champs last_login_time, RAZ last_connex_dur, et incrémente le nb de connexions | |
306 | 336 | $current_user_stat->last_login_time = $now; |
307 | - // et 1 connexion de plus, 1 ! | |
337 | + $current_user_stat->last_connex_dur = 0; | |
338 | + // et 1 NOUVELLE connexion de plus, 1 ! | |
308 | 339 | $current_user_stat->connex_nb++; |
309 | 340 | //debug($current_user_stat); |
310 | 341 | // On sauvegarde en table |
... | ... | @@ -324,15 +355,58 @@ class UsersController extends AppController |
324 | 355 | debug("yes"); |
325 | 356 | } |
326 | 357 | */ |
327 | - } // _updateStatsForUserLogin() | |
358 | + } // _updateStatsForCurrentUserOnLogin() | |
328 | 359 | |
360 | + /* | |
361 | + * Mise à jour des stats pour ce user (et pour l'année courante) durant la session, à chaque action | |
362 | + * => on met à jour son temps de connexion | |
363 | + */ | |
364 | + public function updateStatsForCurrentUserDuringSession($session_user = null) { | |
365 | + | |
366 | + //debug("ici"); | |
367 | + //exit; | |
368 | + // Current user | |
369 | + //$user_id = $this->u->id; | |
370 | + //debug($session_user); | |
371 | + $user_id = $this->_getCurrentUserEntityFromSession($session_user)->id; | |
372 | + //debug($user_id); | |
373 | + | |
374 | + // On récupère la Stat de ce user | |
375 | + $now = new FrozenTime(); | |
376 | + //debug($last_login_time); | |
377 | + $year = $now->format('yy'); | |
378 | + //debug($year); | |
379 | + $table_stats = $this->Users->Stats; | |
380 | + $stat_id = [$year,$user_id]; | |
381 | + $current_user_stat = $table_stats->get($stat_id); | |
382 | + | |
383 | + // On met à jour le temps de connexion | |
384 | + $connex_duration = $now->diff($current_user_stat->last_login_time); | |
385 | + //debug($connex_duration); | |
386 | + //exit; | |
387 | + $current_user_stat->last_connex_dur = $connex_duration->h*3600 + $connex_duration->i*60 + $connex_duration->s; | |
388 | + //debug($current_user_stat->last_connex_dur); | |
389 | + | |
390 | + // On sauvegarde en table | |
391 | + if (! $table_stats->save($current_user_stat)) { | |
392 | + echo "Impossible de sauvegarder une stat durant la session !"; | |
393 | + debug($current_user_stat->getErrors()); | |
394 | + debug($current_user_stat); | |
395 | + exit; | |
396 | + } | |
397 | + | |
398 | + } // updateStatsForCurrentUserDuringSession() | |
329 | 399 | |
330 | 400 | /* |
331 | 401 | * Mise à jour des stats pour ce user (et pour l'année courante) au moment du logout |
332 | 402 | * => on enregistre son heure de dé-connexion |
333 | - * => on calcule son temps de connexion et on l'ajoute au champ qui totalise le temps de connexion : connex_dur | |
403 | + * => on calcule son temps de connexion et on l'ajoute au champ qui totalise le temps de connexion : connex_dur_tot | |
334 | 404 | */ |
335 | - private function _updateStatsForUserLogout(Entity $user) { | |
405 | + private function _updateStatsForCurrentUserOnLogout() { | |
406 | + | |
407 | + // Current user | |
408 | + $user_id = $this->u->id; | |
409 | + | |
336 | 410 | $last_logout_time = new FrozenTime(); |
337 | 411 | //debug($last_login_time); |
338 | 412 | $year = $last_logout_time->format('yy'); |
... | ... | @@ -342,7 +416,7 @@ class UsersController extends AppController |
342 | 416 | // Normalement, elle doit exister puisque il y a déjà eu login ! |
343 | 417 | // TODO: Cas particulier où le login se fait le 31 décembre, et le logout l'année suivante => BUG !!! |
344 | 418 | $table_stats = $this->Users->Stats; |
345 | - $stat_id = [$year,$user->id]; | |
419 | + $stat_id = [$year,$user_id]; | |
346 | 420 | $current_user_stat = $table_stats->get($stat_id); |
347 | 421 | $last_login_time = $current_user_stat->last_login_time; |
348 | 422 | |
... | ... | @@ -355,12 +429,12 @@ class UsersController extends AppController |
355 | 429 | $connex_duration = $connex_duration->h*3600 + $connex_duration->i*60 + $connex_duration->s; |
356 | 430 | //debug($last_login_time); |
357 | 431 | //debug($last_logout_time); |
358 | - //debug($connex_duration); | |
432 | + //debug($connex_dur_totation); | |
359 | 433 | // - on cumule ce temps au temps total |
360 | - //debug($current_user_stat->connex_dur); | |
361 | - //debug($connex_duration); | |
362 | - $current_user_stat->connex_dur += $connex_duration; | |
363 | - //debug($current_user_stat->connex_dur); | |
434 | + //debug($current_user_stat->connex_dur_tot); | |
435 | + //debug($connex_dur_totation); | |
436 | + $current_user_stat->connex_dur_tot += $connex_duration; | |
437 | + //debug($current_user_stat->connex_dur_tot); | |
364 | 438 | |
365 | 439 | // On sauvegarde en table |
366 | 440 | if (! $table_stats->save($current_user_stat)) { |
... | ... | @@ -369,7 +443,7 @@ class UsersController extends AppController |
369 | 443 | debug($current_user_stat); |
370 | 444 | exit; |
371 | 445 | } |
372 | - } // _updateStatsForUserLogout() | |
446 | + } // _updateStatsForCurrentUserOnLogout() | |
373 | 447 | |
374 | 448 | |
375 | 449 | ... | ... |
... | ... | @@ -0,0 +1,77 @@ |
1 | +<?php | |
2 | + | |
3 | +/* | |
4 | + * (EP20201022) | |
5 | + * | |
6 | + * Pour pouvoir mettre à jour les stats de connexion | |
7 | + * au moment d'un timeout automatique (qui fait un logout auto sans appeler Users.logout()) | |
8 | + * | |
9 | + * https://stackoverflow.com/questions/32298817/how-to-prevent-cakephp-3-0-from-extending-session-timeout-with-ajax-requests | |
10 | + * | |
11 | + */ | |
12 | + | |
13 | +namespace App\Routing\Filter; | |
14 | + | |
15 | +use Cake\Core\Configure; | |
16 | +use Cake\Event\Event; | |
17 | +use Cake\Routing\DispatcherFilter; | |
18 | +use App\Controller\UsersController; | |
19 | + | |
20 | +class SessionTimeoutFilter extends DispatcherFilter | |
21 | +{ | |
22 | + | |
23 | + public function beforeDispatch(Event $event) { | |
24 | + | |
25 | + //debug("iciii1"); | |
26 | + // Si déjà TIMEOUT => on ne peut plus rien faire, donc on quitte | |
27 | + //debug($_SESSION); | |
28 | + $request = $event->data['request']; | |
29 | + $session = $request->session(); | |
30 | + $session_user = $session->read('Auth.User'); | |
31 | + if (! $session_user) return; | |
32 | + // MARCHE PAS, WHY ??????? | |
33 | + //if (! isset($_SESSION['Auth']['User']) ) return; | |
34 | + //debug("iciii2"); | |
35 | + | |
36 | + // Sinon, on met à jour les stats de connexion | |
37 | + //(new UsersController())->updateStatsForCurrentUserDuringSession(); | |
38 | + (new UsersController())->updateStatsForCurrentUserDuringSession($session_user); | |
39 | + //exit; | |
40 | + | |
41 | + | |
42 | + /* | |
43 | + //debug($event->data['request']); | |
44 | + //debug($event); | |
45 | + | |
46 | + /S @var $request \Cake\Network\Request S/ | |
47 | + $request = $event->data['request']; | |
48 | + $session = $request->session(); | |
49 | + $lastAccess = $session->read('SessionTimeoutFilter.lastAccess'); // en sec | |
50 | + //debug($lastAccess); | |
51 | + | |
52 | + if ($lastAccess !== null) { | |
53 | + // On calcule le temps d'inactivité de l'utilisateur | |
54 | + $inactivity_duration = time() - $lastAccess; | |
55 | + //debug($inactivity_duration); | |
56 | + //debug(Configure::read('Session.timeout')*60); | |
57 | + //debug("avant"); | |
58 | + /S | |
59 | + $sess = $_SESSION['Auth']['User']; | |
60 | + $user_fullname = $sess['sn'][0].' '.$sess['givenname'][0]; | |
61 | + //debug($user_fullname); | |
62 | + S/ | |
63 | + //debug("after"); | |
64 | + // Si ce temps d'inactivité est supérieur au timeout, on détruit la session, | |
65 | + if ($inactivity_duration > Configure::read('Session.timeout') * 60) { | |
66 | + // Sans doute inutile vu que logout() l'a déjà fait normalement... | |
67 | + $request->session()->destroy(); | |
68 | + } | |
69 | + } | |
70 | + | |
71 | + // On écrit l'heure actuelle dans la variable de session lastAccess | |
72 | + if (! $request->is('ajax') ) | |
73 | + $session->write('SessionTimeoutFilter.lastAccess', time()); // en sec | |
74 | + */ | |
75 | + } | |
76 | + | |
77 | +} | |
0 | 78 | \ No newline at end of file | ... | ... |
src/Template/Stats/index.ctp
... | ... | @@ -3,6 +3,22 @@ |
3 | 3 | * @var \App\View\AppView $this |
4 | 4 | * @var \App\Model\Entity\Stat[]|\Cake\Collection\CollectionInterface $stats |
5 | 5 | */ |
6 | + | |
7 | +// Variables passées par le controleur | |
8 | +$stats = $stats; | |
9 | + | |
10 | + | |
11 | +function getHourMnSecForDuration($duration_sec) { | |
12 | + //debug($duration_sec); | |
13 | + $h = intdiv($duration_sec,3600); | |
14 | + $m = intdiv($duration_sec-$h*3600 , 60); | |
15 | + $s = $duration_sec-$h*3600-$m*60; | |
16 | + return [$h,$m,$s]; | |
17 | +} | |
18 | + | |
19 | + | |
20 | + | |
21 | + | |
6 | 22 | ?> |
7 | 23 | <nav class="large-3 medium-4 columns" id="actions-sidebar"> |
8 | 24 | <ul class="side-nav"> |
... | ... | @@ -20,9 +36,11 @@ |
20 | 36 | <th scope="col"><?= $this->Paginator->sort('year') ?></th> |
21 | 37 | <th scope="col"><?= $this->Paginator->sort('user_id') ?></th> |
22 | 38 | <th scope="col"><?= $this->Paginator->sort('last_login_time') ?></th> |
39 | + <th scope="col"><?= $this->Paginator->sort('last_connex_dur', 'Durée connexion (mn)') ?></th> | |
23 | 40 | <th scope="col"><?= $this->Paginator->sort('last_logout_time') ?></th> |
24 | - <th scope="col"><?= $this->Paginator->sort('connex_nb') ?></th> | |
25 | - <th scope="col"><?= $this->Paginator->sort('connex_dur') ?></th> | |
41 | + <th scope="col"><?= $this->Paginator->sort('connex_dur_tot', "Durée connexion cumulée (sur année) (mn)") ?></th> | |
42 | + <th scope="col"><?= $this->Paginator->sort('connex_nb', "Nb connexions (sur année)") ?></th> | |
43 | + <th scope="col"><?= "Durée connexion moyenne (sur année)" ?></th> | |
26 | 44 | <th scope="col" class="actions"><?= __('Actions') ?></th> |
27 | 45 | </tr> |
28 | 46 | </thead> |
... | ... | @@ -32,9 +50,33 @@ |
32 | 50 | <td><?= h($stat->year) ?></td> |
33 | 51 | <td><?= $stat->has('user') ? $this->Html->link($stat->user->nom, ['controller' => 'Users', 'action' => 'view', $stat->user->id]) : '' ?></td> |
34 | 52 | <td><?= h($stat->last_login_time) ?></td> |
53 | + | |
54 | + <?php | |
55 | + list($h,$m,$s) = getHourMnSecForDuration($stat->last_connex_dur); | |
56 | + ?> | |
57 | + <td><?="$h h $m mn $s sec"?></td> | |
58 | + <!-- | |
59 | + <td><= h($stat->last_connex_dur) ?></td> | |
60 | + --> | |
61 | + | |
35 | 62 | <td><?= h($stat->last_logout_time) ?></td> |
63 | + | |
64 | + <?php | |
65 | + list($h,$m,$s) = getHourMnSecForDuration($stat->connex_dur_tot); | |
66 | + ?> | |
67 | + <td><?="$h h $m mn $s sec"?></td> | |
68 | + <!-- | |
69 | + <td><= $this->Number->format($stat->connex_dur_tot) ?></td> | |
70 | + --> | |
71 | + | |
36 | 72 | <td><?= $this->Number->format($stat->connex_nb) ?></td> |
37 | - <td><?= $this->Number->format($stat->connex_dur) ?></td> | |
73 | + | |
74 | + <?php | |
75 | + $connex_dur_avg = intdiv($stat->connex_dur_tot , $stat->connex_nb); | |
76 | + list($h,$m,$s) = getHourMnSecForDuration($connex_dur_avg); | |
77 | + ?> | |
78 | + <td><?="$h h $m mn $s sec"?></td> | |
79 | + | |
38 | 80 | <td class="actions"> |
39 | 81 | <?= $this->Html->link(__('View'), ['action' => 'view', $stat->year,$stat->user_id]) ?> |
40 | 82 | <?= $this->Html->link(__('Edit'), ['action' => 'edit', $stat->year,$stat->user_id]) ?> | ... | ... |
tests/Fixture/UsersFixture.php
... | ... | @@ -58,7 +58,8 @@ class UsersFixture extends TestFixture |
58 | 58 | [ |
59 | 59 | //'id' => 1, |
60 | 60 | // Nom Prénom |
61 | - 'nom' => 'user1 SUPER', | |
61 | + /////'nom' => 'user1 SUPER', | |
62 | + 'nom' => 'SUPER user1', | |
62 | 63 | //'nom' => 'test1 test2', |
63 | 64 | //'username' => 'testa', |
64 | 65 | 'username' => 'user1_SUPER', | ... | ... |