1<?php 2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 3// 4// All Rights Reserved. See copyright.txt for details and a complete list of authors. 5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 6// $Id$ 7 8//this script may only be included - so its better to die if called directly. 9if (strpos($_SERVER["SCRIPT_NAME"], basename(__FILE__)) !== false) { 10 header("location: index.php"); 11 exit; 12} 13 14class LogsLib extends TikiLib 15{ 16 17 function add_log($type, $message, $who = '', $ip = '', $client = '', $time = '') 18 { 19 global $user; 20 if (empty($who)) { 21 if (! empty($user)) { 22 $who = $user; 23 } else { 24 $who = 'Anonymous'; 25 } 26 } 27 if (empty($ip)) { 28 $ip = $this->get_ip_address(); 29 } 30 if (empty($client)) { 31 if (empty($_SERVER['HTTP_USER_AGENT'])) { 32 $client = 'NO USER AGENT'; 33 } else { 34 $client = $_SERVER['HTTP_USER_AGENT']; 35 } 36 } 37 if (empty($time)) { 38 $time = $this->now; 39 } 40 $this->add_action($type, 'system', 'system', $message, $who, $ip, $client, $time); 41 } 42 43 function list_logs($type = '', $user = '', $offset = 0, $maxRecords = -1, $sort_mode = 'lastModif_desc', $find = '', $min = 0, $max = 0) 44 { 45 $actions = $this->list_actions($type, 'system', $user, $offset, $maxRecords, $sort_mode, $find, $min, $max, '', true); 46 return $actions; 47 } 48 49 function old_list_logs($type = '', $user = '', $offset = 0, $maxRecords = -1, $sort_mode = 'logtime_desc', $find = '', $min = 0, $max = 0) 50 { 51 $bindvars = []; 52 $amid = []; 53 $mid = ''; 54 55 if ($find) { 56 $findesc = '%' . $find . '%'; 57 $amid[] = "`logmessage` like ? or `loguser` like ? or 'logip' like ?"; 58 $bindvars[] = $findesc; 59 $bindvars[] = $findesc; 60 $bindvars[] = $findesc; 61 } 62 63 if ($type) { 64 $amid[] = "`logtype` = ?"; 65 $bindvars[] = $type; 66 } 67 68 if ($user) { 69 if (is_array($user)) { 70 $amid[] = '`loguser` in (' . implode(',', array_fill(0, count($user), '?')) . ')'; 71 foreach ($user as $u) { 72 $bindvars[] = $u; 73 } 74 } else { 75 $amid[] = "`loguser` = ?"; 76 $bindvars[] = $user ; 77 } 78 } 79 80 if ($min) { 81 $amid[] = "`logtime` > ?"; 82 $bindvars[] = $min; 83 } 84 85 if ($max) { 86 $amid[] = "`logtime` < ?"; 87 $bindvars[] = $max; 88 } 89 90 if (count($amid)) { 91 $mid = " where " . implode(" and ", $amid) . " "; 92 } 93 94 $query = "select `logId`,`loguser`,`logtype`,`logmessage`,`logtime`,`logip`,`logclient` "; 95 $query .= " from `tiki_logs` $mid order by " . $this->convertSortMode($sort_mode); 96 $query_cant = "select count(*) from `tiki_logs` $mid"; 97 $ret = $this->fetchAll($query, $bindvars, $maxRecords, $offset); 98 $cant = $this->getOne($query_cant, $bindvars); 99 $retval = []; 100 $retval["data"] = $ret; 101 $retval["cant"] = $cant; 102 return $retval; 103 } 104 105 function clean_logs($date) 106 { 107 $query = "delete from `tiki_actionlog` where `objectType`='system' and `lastModif`<=?"; 108 $this->query($query, [(int)$date]); 109 } 110 111 /** 112 * action = "Updated", "Created", "Removed", "Viewed", "Removed version $version", "Changed actual version to $version" 113 * type = 'wiki page', 'category', 'article', 'image gallery', 'tracker', 'forum thread' 114 * TODO: merge $param and $contributions together into a hash and but everything in actionlog_params 115 */ 116 function object_must_be_logged($action, $object, $objectType) 117 { 118 global $prefs; 119 120 if ($objectType == 'wiki page' && $action != 'Viewed') { 121 $logObject = true; // to have the tiki_my_edit, history and mod-last_modif_pages 122 } else { 123 $logObject = $this->action_must_be_logged($action, $objectType); 124 } 125 126 $logCateg = $prefs['feature_categories'] == 'y' ? $this->action_must_be_logged('*', 'category') : false; 127 128 if (! $logObject && ! $logCateg) { 129 return 0; 130 } 131 132 if ($logCateg) { 133 $categlib = TikiLib::lib('categ'); 134 if ($objectType == 'comment') { 135 preg_match('/type=([^&]*)/', $param, $matches); 136 $categs = $categlib->get_object_categories($matches[1], $object); 137 } else { 138 $categs = $categlib->get_object_categories($objectType, $object); 139 } 140 } 141 } 142 function add_action($action, $object, $objectType = 'wiki page', $param = '', $who = '', $ip = '', $client = '', $date = '', $contributions = '', $hash = '', $log = '') 143 { 144 global $user, $prefs; 145 146 if (is_array($param)) { 147 $param = http_build_query($param, '', '&'); 148 } 149 150 if ($objectType == 'wiki page' && $action != 'Viewed') { 151 $logObject = true; // to have the tiki_my_edit, history and mod-last_modif_pages 152 } else { 153 $logObject = $this->action_must_be_logged($action, $objectType); 154 } 155 156 $logCateg = false; 157 if (isset($prefs['feature_categories'])) { 158 $logCateg = $prefs['feature_categories'] == 'y' ? $this->action_must_be_logged('*', 'category') : false; 159 } 160 161 if (! $logObject && ! $logCateg) { 162 return 0; 163 } 164 165 if ($date == '') { 166 $date = $this->now; 167 } 168 169 if ($who == '') { 170 global $tokenlib; 171 if ($prefs['auth_token_access'] == 'y' && empty($user) && ! empty($tokenlib) && $tokenlib->ok) { 172 $user = '§TOKEN§'; 173 } else { 174 $who = $user; 175 } 176 } 177 178 if ($ip == '') { 179 $ip = $this->get_ip_address(); 180 } 181 182 if ($client == '') { 183 if (! empty($_SERVER['HTTP_USER_AGENT'])) { 184 $client = substr($_SERVER['HTTP_USER_AGENT'], 0, 200); 185 } else { 186 $client = null; 187 } 188 } else { 189 $client = substr($client, 0, 200); 190 } 191 192 if ($logCateg) { 193 $categlib = TikiLib::lib('categ'); 194 if ($objectType == 'comment') { 195 preg_match('/type=([^&]*)/', $param, $matches); 196 $categs = $categlib->get_object_categories($matches[1], $object); 197 } else { 198 $categs = $categlib->get_object_categories($objectType, $object); 199 } 200 } 201 202 if (! empty($log)) { 203 $log = serialize($log); 204 } 205 206 $actions = []; 207 $nottostore = [ 208 'auth_ldap_adminpass', 209 'auth_ldap_group_adminpass', 210 'shipping_fedex_password', 211 'shipping_ups_password', 212 'auth_phpbb_dbpasswd', 213 'zend_mail_smtp_pass', 214 'unified_elastic_url', 215 'proxy_pass' 216 ]; 217 if ($logObject) { 218 if (function_exists('mb_strcut')) { 219 $param = mb_strcut($param, 0, 200); 220 } else { 221 $param = substr($param, 0, 200); 222 } 223 if ($logCateg && count($categs) > 0) { 224 foreach ($categs as $categ) { 225 $query = "insert into `tiki_actionlog` " . 226 " (`action`, `object`, `lastModif`, `user`, `ip`, `comment`, `objectType`, `categId`, `client`, `log`) " . 227 " values(?,?,?,?,?,?,?,?,?,?)" 228 ; 229 230 $this->query($query, [$action, $object, (int)$date, $who, $ip, $param, $objectType, $categ, $client, $log]); 231 $actions[] = $this->lastInsertId(); 232 } 233 } elseif (! in_array($object, $nottostore)) { 234 // It's possible that this action is being added during upgrade to 18.x before the `log` field has been added 235 // to the database. To avoid error on doc/devtools/svnup.php, do not use the field here if $log is null 236 if ($log != null) { 237 $query = "insert into `tiki_actionlog`" . 238 " (`action`, `object`, `lastModif`, `user`, `ip`, `comment`, `objectType`, `client`, `log`)" . 239 " values(?,?,?,?,?,?,?,?,?)" 240 ; 241 242 $this->query($query, [$action, $object, (int)$date, $who, $ip, $param, $objectType, $client, $log]); 243 } else { 244 $query = "insert into `tiki_actionlog`" . 245 " (`action`, `object`, `lastModif`, `user`, `ip`, `comment`, `objectType`, `client`)" . 246 " values(?,?,?,?,?,?,?,?)" 247 ; 248 249 $this->query($query, [$action, $object, (int)$date, $who, $ip, $param, $objectType, $client]); 250 } 251 $actions[] = $this->lastInsertId(); 252 } 253 } 254 255 if (! empty($contributions)) { 256 foreach ($actions as $a) { 257 $query = "insert into `tiki_actionlog_params` (`actionId`, `name`, `value`) values(?,?,?)"; 258 foreach ($contributions as $contribution) { 259 $this->query($query, [$a, 'contribution', $contribution]); 260 } 261 } 262 } 263 264 if (! empty($hash)) { 265 $query = "insert into `tiki_actionlog_params` (`actionId`, `name`, `value`) values(?,?,?)"; 266 foreach ($actions as $a) { 267 foreach ($hash as $h) { 268 foreach ($h as $param => $val) { 269 $this->query($query, [$a, $param, $val]); 270 } 271 } 272 } 273 } 274 275 return isset($actions[0]) ? $actions[0] : 0; 276 } 277 278 function action_must_be_logged($action, $objectType) 279 { 280 global $prefs; 281 282 return $this->action_is_viewed($action, $objectType, true); 283 } 284 285 function action_is_viewed($action, $objectType, $logged = false) 286 { 287 global $prefs; 288 static $is_viewed; 289 290 // for previous compatibility 291 // the new action are added with a if ($feature..) 292 if (isset($prefs['feature_actionlog'])) { 293 if ($prefs['feature_actionlog'] != 'y') { 294 return true; 295 } 296 } 297 298 299 if (! isset($is_viewed)) { 300 $logActions = $this->get_all_actionlog_conf(); 301 $is_viewed = []; 302 foreach ($logActions as $conf) { 303 if ($logged) { 304 $is_viewed[$conf['objectType']][$conf['action']] = $conf['status'] == 'v' || $conf['status'] == 'y'; 305 } else { 306 $is_viewed[$conf['objectType']][$conf['action']] = $conf['status'] == 'v'; 307 } 308 } 309 } 310 311 if (isset($is_viewed[$objectType][$action])) { 312 return $is_viewed[$objectType][$action]; 313 } elseif (isset($is_viewed[$objectType]['*'])) { 314 return $is_viewed[$objectType]['*']; 315 } else { 316 return false; 317 } 318 } 319 320 function set_actionlog_conf($action, $objectType, $status) 321 { 322 global $actionlogConf; 323 $this->delete_actionlog_conf($action, $objectType); 324 $action = str_replace('*', '%', $action); 325 $query = "insert into `tiki_actionlog_conf` (`action`, `objectType`, `status`) values(?, ?, ?)"; 326 $this->query($query, [$action, $objectType, $status]); 327 unset($actionlogConf); 328 } 329 330 function delete_actionlog_conf($action, $objectType) 331 { 332 if ($action === '*') { 333 $action = '%'; 334 } 335 $query = "delete from `tiki_actionlog_conf` where `action`=? and `objectType`= ?"; 336 $this->query($query, [$action, $objectType]); 337 } 338 339 function get_all_actionlog_conf() 340 { 341 global $actionlogConf; 342 343 if (! isset($actionlogConf)) { 344 $actionlogConf = self::get_actionlog_conf(); 345 } 346 347 return $actionlogConf; 348 } 349 350 function get_actionlog_conf($type = '%', $action = '%') 351 { 352 $actionlogconf = []; 353 $query = "select * from `tiki_actionlog_conf`" . 354 " where `objectType` like ? and `action` like ?" . 355 " order by `objectType` desc, `action` asc" 356 ; 357 $result = $this->query($query, [$type, $action]); 358 359 while ($res = $result->fetchRow()) { 360 if ($res['action'] == '%') { 361 $res['action'] = '*'; 362 } 363 $res['code'] = self::encode_actionlog_conf($res['action'], $res['objectType']); 364 $actionlogconf[] = $res; 365 } 366 367 return $actionlogconf; 368 } 369 370 function get_actionlog_types() 371 { 372 $actionlogtype = []; 373 $query = "select distinct `objectType` from `tiki_actionlog_conf` order by `objectType`"; 374 $result = $this->query($query, []); 375 while ($res = $result->fetchRow()) { 376 $actionlogtypes[] = $res['objectType']; 377 } 378 return $actionlogtypes; 379 } 380 381 function get_actionlog_actions() 382 { 383 $actionlogactions = []; 384 $query = "select distinct `action` from `tiki_actionlog_conf` order by `action`"; 385 $result = $this->query($query, []); 386 while ($res = $result->fetchRow()) { 387 if ($res['action'] != '%') { 388 $actionlogactions[] = $res['action']; 389 } 390 } 391 return $actionlogactions; 392 } 393 394 function encode_actionlog_conf($action, $objectType) 395 { 396 return str_replace(' ', '0', $action . '_' . $objectType); 397 } 398 399 function decode_actionlog_conf($string) 400 { 401 return explode('_', str_replace('0', ' ', $conf)); 402 } 403 404 function list_actions($action = '', $objectType = '', $user = '', $offset = 0, $maxRecords = -1, $sort_mode = 'lastModif_desc', $find = '', $start = 0, $end = 0, $categId = '', $all = false 405 ) 406 { 407 global $prefs, $section; 408 $tikilib = TikiLib::lib('tiki'); 409 $contributionlib = TikiLib::lib('contribution'); 410 411 $bindvars = []; 412 $bindvarsU = []; 413 $amid = []; 414 $mid1 = ''; 415 $mid2 = ''; 416 if ($find) { 417 $findesc = '%' . $find . '%'; 418 $amid[] = "(`comment` like ? or a.`action` like ? or `object` like ?)"; 419 $bindvars[] = $findesc; 420 $bindvars[] = $findesc; 421 $bindvars[] = $findesc; 422 } 423 if ($action) { 424 $amid[] = "a.`action` = ?"; 425 $bindvars[] = $action; 426 } 427 if ($objectType) { 428 $amid[] = "c.`objectType` = ?"; 429 $bindvars[] = $objectType; 430 } 431 if ($user == 'Anonymous') { 432 $amid[] = "`user` = ?"; 433 $bindvars[] = '' ; 434 } elseif ($user == 'Registered') { 435 $amid[] = "`user` != ?"; 436 $bindvars[] = '' ; 437 } elseif ($user) { 438 if (is_array($user)) { 439 $mid1 = '`user` in (' . implode(',', array_fill(0, count($user), '?')) . ')'; 440 $mid2 = 'ap.`value` in (' . implode(',', array_fill(0, count($user), '?')) . ') and ap.`name`=? and ap.`actionId`=a.`actionId`'; 441 foreach ($user as $u) { 442 $bindvarsU[] = $u; 443 } 444 445 foreach ($user as $u) { 446 $bindvarsU[] = $tikilib->get_user_id($u); 447 } 448 449 $bindvarsU[] = 'contributor'; 450 } else { 451 $mid1 = '`user` = ?'; 452 $mid2 = 'ap.`value`=? and ap.`name`=? and ap.`actionId`=a.`actionId`'; 453 $bindvarsU[] = $user ; 454 $bindvarsU[] = $tikilib->get_user_id($user); 455 $bindvarsU[] = 'contributor'; 456 } 457 } 458 459 if ($start) { 460 $amid[] = "`lastModif` > ?"; 461 $bindvars[] = $start; 462 } 463 464 if ($end) { 465 $amid[] = "`lastModif` < ?"; 466 $bindvars[] = $end; 467 } 468 469 if ($categId && $categId != 0) { 470 if (is_array($categId)) { 471 $amid[] = "`categId`in (?)"; 472 $bindvars[] = implode(",", $categId); 473 } else { 474 $amid[] = "`categId` = ?"; 475 $bindvars[] = $categId; 476 } 477 } 478 479 $amid[] = " a.`action` like c.`action` and a.`objectType` = c.`objectType`" . ($all ? "" : " and (c.`status` = 'v')"); 480 481 if (count($amid)) { 482 $mid = implode(" and ", $amid); 483 } 484 485 if (! empty($bindvarsU)) { 486 $bindvars = array_merge($bindvars, $bindvarsU, $bindvars); 487 $query = "(select distinct a.* from `tiki_actionlog` a ,`tiki_actionlog_conf` c where $mid and $mid1)"; 488 $query .= "union (select distinct a.* from `tiki_actionlog` a ,`tiki_actionlog_conf` c,`tiki_actionlog_params` ap where $mid2 and $mid)"; 489 490 $query_cant = "select count(distinct `actionId`) from `tiki_actionlog`" . 491 " where `actionId` in" . 492 " (select distinct a.`actionId` from `tiki_actionlog` a ,`tiki_actionlog_conf` c" . 493 " where $mid and $mid1 union select distinct a.`actionId`" . 494 " from `tiki_actionlog` a ,`tiki_actionlog_conf` c,`tiki_actionlog_params` ap where $mid2 and $mid)" 495 ; 496 } else { 497 $query = "select distinct a.* from `tiki_actionlog` a ,`tiki_actionlog_conf` c where $mid"; 498 $query_cant = "select count(distinct actionId) from `tiki_actionlog` a ,`tiki_actionlog_conf` c where $mid"; 499 } 500 501 $query .= " order by " . $this->convertSortMode($sort_mode); 502 $result = $this->query($query, $bindvars, $maxRecords, $offset); 503 $cant = $this->getOne($query_cant, $bindvars); 504 $ret = []; 505 506 while ($res = $result->fetchRow()) { 507 if ($prefs['feature_contribution'] == 'y' && 508 ($res['action'] == 'Created' || $res['action'] == 'Updated' || $res['action'] == 'Posted' || $res['action'] == 'Replied')) { 509 if ($res['objectType'] == 'wiki page') { 510 $res['contributions'] = $this->get_action_contributions($res['actionId']); 511 } elseif ($id = $this->get_comment_action($res)) { 512 $res['contributions'] = $this->get_action_contributions($res['actionId']); 513 } else { 514 $res['contributions'] = $contributionlib->get_assigned_contributions($res['object'], $res['objectType']); // todo: do a left join 515 } 516 } 517 518 if ($prefs['feature_contributor_wiki'] == 'y' && $res['objectType'] == 'wiki page') { 519 $res['contributors'] = $this->get_contributors($res['actionId']); 520 $res['nbContributors'] = 1 + count($res['contributors']); 521 } 522 523 if ($res['objectType'] == 'comment' && empty($res['categId'])) { 524 $categlib = TikiLib::lib('categ'); 525 preg_match('/type=([^&]*)/', $res['comment'], $matches); 526 $categs = $categlib->get_object_categories($matches[1], $res['object']); 527 $i = 0; 528 529 foreach ($categs as $categId) { 530 $res['categId'] = $categId; 531 if ($i++ > 0) { 532 $ret[] = $res; 533 } 534 } 535 } 536 537 // For tiki logs 538 if ($res['objectType'] === 'system') { 539 $what = $res['object'] === 'system' ? '' : $res['object'] . ' : '; 540 $res['object'] = $res['action']; 541 $res['action'] = $what . $res['comment']; 542 } 543 $ret[] = $res; 544 } 545 546 return ['data' => $ret, 'cant' => $cant]; 547 } 548 549 function sort_by_date($action1, $action2) 550 { 551 return ($action1['lastModif'] - $action2['lastModif']); 552 } 553 554 function get_login_time($logins, $startDate, $endDate, $actions) 555 { 556 //FIXME 557 if ($endDate > $this->now) { 558 $endDate = $this->now; 559 } 560 561 $logTimes = []; 562 563 foreach ($logins as $login) { 564 if (! array_key_exists($login['user'], $logTimes)) { 565 if ($login['action'] == 'timeout' || $login['action'] == 'logged out') { 566 $logTimes[$login['user']]['last'] = $startDate; 567 } else { 568 $logTimes[$login['user']]['last'] = 0; 569 } 570 $logTimes[$login['user']]['time'] = 0; 571 $logTimes[$login['user']]['nbLogins'] = 0; 572 } 573 574 if (strstr($login['action'], 'logged from') || $login['action'] == 'back') { 575 if (strstr($login['action'], 'logged from')) { 576 ++$logTimes[$login['user']]['nbLogins']; 577 } 578 // can be already log in 579 if ($logTimes[$login['user']]['last'] == 0) { 580 $logTimes[$login['user']]['last'] = $login['lastModif']; 581 } 582 } elseif (($login['action'] == 'timeout' || $login['action'] == 'logged out') && $logTimes[$login['user']]['last'] > 0) { 583 $logTimes[$login['user']]['time'] += $login['lastModif'] - $logTimes[$login['user']]['last']; 584 $logTimes[$login['user']]['last'] = 0; 585 } 586 } 587 588 // update time for those still logged in 589 foreach ($logTimes as $user => $logTime) { 590 if ($logTime['last']) { 591 $logTimes[$user]['time'] += $endDate - $logTime['last']; 592 } 593 } 594 595 // update time for those who were always logged in 596 foreach ($actions as $action) { 597 if ($action['user'] && ! array_key_exists($action['user'], $logTimes)) { 598 $logTimes[$action['user']]['time'] = $endDate - $startDate; 599 } 600 } 601 602 foreach ($logTimes as $user => $login) { 603 $nbMin = floor($login['time'] / 60); 604 $nbHour = floor($nbMin / 60); 605 $nbDay = floor($nbHour / 24); 606 $logTimes[$user]['secs'] = $login['time'] - $nbMin * 60; 607 $logTimes[$user]['mins'] = $nbMin - $nbHour * 60; 608 $logTimes[$user]['hours'] = $nbHour - $nbDay * 24; 609 $logTimes[$user]['days'] = $nbDay; 610 } 611 612 return $logTimes; 613 } 614 615 function get_volume_action($action) 616 { 617 $bytes = []; 618 619 if (preg_match('/bytes=([0-9\-+]+)/', $action['comment'], $matches)) {//old syntax 620 if (preg_match('/\+([0-9]+)/', $matches[1], $m)) { 621 $bytes['add'] = $m[1]; 622 } 623 if (preg_match('/\-([0-9]+)/', $matches[1], $m)) { 624 $bytes['del'] = $m[1]; 625 } 626 } else { 627 if (preg_match('/add=([0-9\-+]+)/', $action['comment'], $matches)) { 628 $bytes['add'] = $matches[1]; 629 } 630 if (preg_match('/del=([0-9\-+]+)/', $action['comment'], $matches)) { 631 $bytes['del'] = $matches[1]; 632 } 633 } 634 635 return $bytes; 636 } 637 638 function get_comment_action($action) 639 { 640 if (preg_match('/comments_parentId=([0-9\-+]+)/', $action['comment'], $matches)) { 641 return $matches[1]; 642 } elseif (preg_match('/#threadId=?([0-9\-+]+)/', $action['comment'], $matches)) { 643 return $matches[1]; 644 } elseif (preg_match('/sheetId=([0-9]+)/', $action['comment'], $matches)) { 645 return $matches[1]; 646 } elseif (preg_match('/postId=([0-9]+)/', $action['comment'], $matches)) { 647 return $matches[1]; 648 } else { 649 return ''; 650 } 651 } 652 653 function get_stat_actions_per_user($actions) 654 { 655 $stats = $this->get_stat_actions_per_field($actions, 'user'); 656 657 return $stats; 658 } 659 660 function get_stat_actions_per_field($actions, $field = 'user') 661 { 662 $stats = []; 663 $actions_name = []; 664 665 $actionlogConf = $this->get_all_actionlog_conf(); 666 667 foreach ($actions as $action) { 668 if (strpos($action['action'], 'logged from') === 0) { 669 $action['action'] = 'login'; 670 } 671 if (strpos($action['action'], 'logged out') === 0) { 672 $action['action'] = 'login'; 673 } 674 $name = $action['action'] . '/' . $action['objectType']; 675 $sort = $action['objectType'] . '/' . $action['action']; 676 if ($this->action_is_viewed($action['action'], $action['objectType']) and ! in_array($name, $actions_name)) { 677 $actions_name[$sort] = $name; 678 } 679 } 680 681 ksort($actions_name); 682 683 foreach ($actions as $action) { 684 $key = $action[$field]; 685 if (! isset($stats[$key])) { 686 $stats[$key] = array_fill_keys($actions_name, 0); 687 $stats[$key][$field] = $action[$field]; 688 } 689 $name = $action['action'] . '/' . $action['objectType']; 690 if (($index = array_search($name, $actions_name)) !== false) { 691 if ($field == 'object') { 692 $stats[$key]['link'] = isset($action['link']) ? $action['link'] : null; 693 } 694 ++$stats[$key][$name]; 695 } 696 } 697 698 sort($stats, SORT_STRING); // will sort on the first field 699 700 return $stats; 701 } 702 703 function get_stat_contributions_per_group($actions, $selectedGroups) 704 { 705 $tikilib = TikiLib::lib('tiki'); 706 $statGroups = []; 707 foreach ($actions as $action) { 708 if (! empty($previousAction) && 709 $action['lastModif'] == $previousAction['lastModif'] && 710 $action['user'] == $previousAction['user'] && 711 $action['object'] == $previousAction['object'] && 712 $action['objectType'] == $previousAction['objectType']) { 713 // differ only by the categories 714 continue; 715 } 716 717 if (strpos($action['action'], 'logged from') === 0) { 718 $action['action'] = 'login'; 719 } 720 721 if (strpos($action['action'], 'logged out') === 0) { 722 $action['action'] = 'login'; 723 } 724 725 $previousAction = $action; 726 727 if (empty($action['user'])) { 728 $groups = ['Anonymous']; 729 } else { 730 $groups = $tikilib->get_user_groups($action['user']); 731 $groups = array_diff($groups, ['Anonymous']); 732 } 733 734 foreach ($groups as $key => $group) { 735 if (isset($selectedGroups) && $selectedGroups[$group] != 'y') { 736 continue; 737 } 738 739 if (empty($action['contributions'])) { 740 continue; 741 } 742 743 foreach ($action['contributions'] as $contribution) { 744 if (! isset($statGroups[$group])) { 745 $statGroups[$group][$contribution['name']]['add'] = 0; 746 $statGroups[$group][$contribution['name']]['del'] = 0; 747 $statGroups[$group][$contribution['name']]['dif'] = 0; 748 } 749 $statGroups[$group][$contribution['name']]['add'] += $action['contributorAdd']; 750 $statGroups[$group][$contribution['name']]['del'] += $action['contributorDel']; 751 $statGroups[$group][$contribution['name']]['dif'] += $action['contributorAdd'] - $action['contributorDel']; 752 } 753 } 754 } 755 ksort($statGroups); 756 757 return $statGroups; 758 } 759 760 function get_action_stat_categ($actions, $categNames) 761 { 762 $stats = []; 763 $actionlogConf = $this->get_all_actionlog_conf(); 764 765 foreach ($actions as $action) { 766 //if ($action['categId'] == 0) print also stat for non categ object 767 // continue; 768 769 if (strpos($action['action'], 'logged from') === 0) { 770 $action['action'] = 'login'; 771 } 772 773 if (strpos($action['action'], 'logged out') === 0) { 774 $action['action'] = 'login'; 775 } 776 $key = $action['categId']; 777 778 if (! array_key_exists($key, $stats)) { 779 $stats[$key]['category'] = $key ? $categNames[$key] : ''; 780 foreach ($actionlogConf as $conf) { 781 // don't take category 782 if ($conf['status'] == 'v' && $conf['action'] != '*') { 783 $stats[$key][$conf['action'] . '/' . $conf['objectType']] = 0; 784 } 785 } 786 } 787 ++$stats[$key][$action['action'] . '/' . $action['objectType']]; 788 } 789 sort($stats); //sort on the first field category 790 791 return $stats; 792 } 793 794 function get_action_vol_categ($actions, $categNames) 795 { 796 $stats = []; 797 $actionlogConf = $this->get_all_actionlog_conf(); 798 799 foreach ($actions as $action) { 800 //if ($action['categId'] == 0) print also stat for non categ object 801 // continue; 802 803 if (strpos($action['action'], 'logged from') === 0) { 804 $action['action'] = 'login'; 805 } 806 807 if (strpos($action['action'], 'logged out') === 0) { 808 $action['action'] = 'login'; 809 } 810 811 if (! ($bytes = $this->get_volume_action($action))) { 812 continue; 813 } 814 815 $key = $action['categId']; 816 if (! array_key_exists($key, $stats)) { 817 $stats[$key]['category'] = $key ? $categNames[$key] : ''; 818 } 819 820 if (! isset($stats[$key][$action['objectType']]['add'])) { 821 $stats[$key][$action['objectType']]['add'] = 0; 822 $stats[$key][$action['objectType']]['del'] = 0; 823 $stats[$key][$action['objectType']]['dif'] = 0; 824 } 825 $dif = 0; 826 827 if (isset($bytes['add'])) { 828 $stats[$key][$action['objectType']]['add'] += $bytes['add']; 829 $dif = $bytes['add']; 830 } 831 832 if (isset($bytes['del'])) { 833 $stats[$key][$action['objectType']]['del'] += $bytes['del']; 834 $dif -= $bytes['del']; 835 } 836 837 $stats[$key][$action['objectType']]['dif'] += $dif; 838 } 839 sort($stats); //sort on the first field category 840 841 return $stats; 842 } 843 844 function get_action_vol_user_categ($actions, $categNames) 845 { 846 $stats = []; 847 $actionlogConf = $this->get_all_actionlog_conf(); 848 849 foreach ($actions as $action) { 850 //if ($action['categId'] == 0) print also stat for non categ object 851 // continue; 852 853 if (strpos($action['action'], 'logged from') === 0) { 854 $action['action'] = 'login'; 855 } 856 857 if (strpos($action['action'], 'logged out') === 0) { 858 $action['action'] = 'login'; 859 } 860 861 if ($action['user'] == '' 862 || ! ($bytes = $this->get_volume_action($action))) { 863 continue; 864 } 865 866 $key = $action['categId'] . '/' . $action['user']; 867 if (! array_key_exists($key, $stats)) { 868 $stats[$key]['category'] = $action['categId'] ? $categNames[$action['categId']] : ''; 869 $stats[$key]['user'] = $action['user']; 870 } 871 872 if (! isset($stats[$key][$action['objectType']]['add'])) { 873 $stats[$key][$action['objectType']]['add'] = 0; 874 $stats[$key][$action['objectType']]['del'] = 0; 875 $stats[$key][$action['objectType']]['dif'] = 0; 876 } 877 878 $dif = 0; 879 if (isset($bytes['add'])) { 880 $stats[$key][$action['objectType']]['add'] += $bytes['add']; 881 $dif = $bytes['add']; 882 } 883 884 if (isset($bytes['del'])) { 885 $stats[$key][$action['objectType']]['del'] += $bytes['del']; 886 $dif -= $bytes['del']; 887 } 888 $stats[$key][$action['objectType']]['dif'] += $dif; 889 } 890 sort($stats); //sort on the first field category 891 892 return $stats; 893 } 894 895 function get_action_vol_type($vols) 896 { 897 $types = []; 898 foreach ($vols as $vol) { 899 foreach ($vol as $key => $value) { 900 if ($key != 'category' && $key != 'user' && ! in_array($key, $types)) { 901 $types[] = $key; 902 } 903 } 904 } 905 906 return $types; 907 } 908 909 function get_actions_per_user_categ($actions, $categNames) 910 { 911 $stats = []; 912 $actionlogConf = $this->get_all_actionlog_conf(); 913 foreach ($actions as $action) { 914 if (empty($action['categId'])) { 915 continue; 916 } 917 918 if (strpos($action['action'], 'logged from') === 0) { 919 $action['action'] = 'login'; 920 } 921 922 if (strpos($action['action'], 'logged out') === 0) { 923 $action['action'] = 'login'; 924 } 925 926 $key = $action['categId'] . '/' . $action['user']; 927 ; 928 929 if (! array_key_exists($key, $stats)) { 930 $stats[$key]['category'] = $categNames[$action['categId']]; 931 $stats[$key]['user'] = $action['user']; 932 foreach ($actionlogConf as $conf) { 933 // don't take category 934 if ($conf['status'] == 'v' && $conf['action'] != '*') { 935 $stats[$key][$conf['action'] . '/' . $conf['objectType']] = 0; 936 } 937 } 938 } 939 ++$stats[$key][$action['action'] . '/' . $action['objectType']]; 940 } 941 sort($stats); // sort on the first fields categ , then user 942 943 return $stats; 944 } 945 946 function in_kb($vol) 947 { 948 for ($i = count($vol) - 1; $i >= 0; --$i) { 949 foreach ($vol[$i] as $k => $v) { 950 if ($k != 'category' && $k != 'user') { 951 $vol[$i][$k]['add'] = round($vol[$i][$k]['add'] / 1024); 952 $vol[$i][$k]['del'] = round($vol[$i][$k]['del'] / 1024); 953 $vol[$i][$k]['dif'] = round($vol[$i][$k]['dif'] / 1024); 954 } 955 } 956 } 957 return $vol; 958 } 959 960 function export($actionlogs, $unit = 'b') 961 { 962 foreach ($actionlogs as $action) { 963 if (! isset($action['object'])) { 964 $action['object'] = ''; 965 } 966 967 if (! isset($action['categName'])) { 968 $action['categName'] = ''; 969 $action['categId'] = ''; 970 } 971 972 if (! isset($action['add'])) { 973 $action['add'] = ''; 974 } 975 976 if (! isset($action['del'])) { 977 $action['del'] = ''; 978 } 979 980 if (! isset($action['ip'])) { 981 $action['ip'] = ''; 982 } 983 984 $csv .= '"' . $action['user'] 985 . '","' . $this->date_format("%y%m%d", $action['lastModif']) 986 . '","' . $this->date_format("%H:%M", $action['lastModif']) 987 . '","' . $action['action'] 988 . '","' . $action['objectType'] 989 . '","' . $action['object'] 990 . '","' . $action['categName'] 991 . '","' . $action['categId'] 992 . '","' . $action['ip'] 993 . '","' . $unit 994 . '","' . $action['add'] 995 . '","' . $action['del'] 996 . '","' 997 ; 998 999 if (isset($action['contributions'])) { 1000 $i = 0; 1001 foreach ($action['contributions'] as $contribution) { 1002 if ($i++) { 1003 $csv .= ','; 1004 } 1005 $csv .= $contribution['name']; 1006 } 1007 } 1008 $csv .= "\"\n"; 1009 } 1010 1011 return $csv; 1012 } 1013 1014 function get_action_params($actionId, $name = '') 1015 { 1016 if (empty($name)) { 1017 $query = "select * from `tiki_actionlog_params` where `actionId`=?"; 1018 $ret = $this->fetchAll($query, [$actionId]); 1019 } else { 1020 $query = "select `value` from `tiki_actionlog_params` where `actionId`=? and `name`=?"; 1021 $result = $this->query($query, [$actionId, $name]); 1022 $ret = []; 1023 while ($res = $result->fetchRow()) { 1024 $ret[] = $res['value']; 1025 } 1026 } 1027 1028 return $ret; 1029 } 1030 1031 function get_action_contributions($actionId) 1032 { 1033 $query = "select tc.* from `tiki_contributions` tc, `tiki_actionlog_params` tp where tp.`actionId`=? and tp.`name`=? and tp.`value`=tc.`contributionId`"; 1034 1035 return $this->fetchAll($query, [$actionId, 'contribution']); 1036 } 1037 1038 function rename($objectType, $oldName, $newName) 1039 { 1040 $query = "update `tiki_actionlog`set `comment`= concat(?, `comment`) where `object`=? and (`objectType`=? or `objectType`= ?) and `comment` not like ?"; 1041 $this->query($query, ["old=$oldName&", $oldName, $objectType, 'comment' , '%old=%']); 1042 $query = "update `tiki_actionlog`set `object`=? where `object`=? and (`objectType`=? or `objectType`= ?)"; 1043 $this->query($query, [$newName, $oldName, $objectType, 'comment']); 1044 } 1045 1046 function update_category($actionId, $categId) 1047 { 1048 $query = "update `tiki_actionlog` set `categId`=? where `actionId`=?"; 1049 $this->query($query, [$categId, $actionId]); 1050 } 1051 1052 function get_info_action($actionId) 1053 { 1054 $query = "select * from `tiki_actionlog`where `actionId`= ?"; 1055 $result = $this->query($query, [$actionId]); 1056 if ($res = $result->fetchRow()) { 1057 return $res; 1058 } else { 1059 return null; 1060 } 1061 } 1062 1063 function get_user_registration_action($user) 1064 { 1065 $actions = $this->list_actions('register', 'system', '', 0, -1, 'lastModif_desc', 'created account', 0, 0, '', true); 1066 $n_actions = count($actions['data']); 1067 for ($al = 0; $al <= $n_actions - 1; $al++) { // $al = action listed, from the list of actions found matching these previous criteria 1068 $string = $actions['data'][$al]['action']; 1069 if (strpos($string, $user)) { 1070 return $actions['data'][$al]; 1071 } 1072 } 1073 return ''; 1074 } 1075 1076 function delete_params($actionId, $name = '') 1077 { 1078 $bindvars = [$actionId]; 1079 if (! empty($name)) { 1080 $mid = 'and `name`= ?'; 1081 $bindvars[] = $name; 1082 } 1083 $query = "delete from `tiki_actionlog_params` where `actionId`=? $mid"; 1084 $this->query($query, $bindvars); 1085 } 1086 1087 function insert_params($actionId, $param, $values) 1088 { 1089 $query = "insert into `tiki_actionlog_params` (`actionId`, `name`, `value`) values(?,?,?)"; 1090 1091 foreach ($values as $val) { 1092 $this->query($query, [$actionId, $param, $val]); 1093 } 1094 } 1095 1096 function get_stat_contribution($actions, $startDate, $endDate, $unit = 'w') 1097 { 1098 $contributions = []; 1099 $nbCols = ceil(($endDate - $startDate) / (60 * 60 * 24)); 1100 if ($unit != 'd') { 1101 $nbCols = ceil($nbCols / 7); 1102 } 1103 foreach ($actions as $action) { 1104 if (isset($action['contributions'])) { 1105 if (! empty($previousAction) 1106 && $action['lastModif'] == $previousAction['lastModif'] 1107 && $action['user'] == $previousAction['user'] 1108 && $action['object'] == $previousAction['object'] 1109 && $action['objectType'] == $previousAction['objectType'] 1110 ) { 1111 // differ only by the categories 1112 continue; 1113 } 1114 1115 $previousAction = $action; 1116 1117 foreach ($action['contributions'] as $contrib) { 1118 $i = floor(($action['lastModif'] - $startDate) / (60 * 60 * 24)); 1119 1120 if ($unit != 'd') { 1121 $i = floor($i / 7); 1122 } 1123 1124 if (empty($contributions[$contrib['contributionId']])) { 1125 $contributions[$contrib['contributionId']]['name'] = $contrib['name']; 1126 for ($j = 0; $j < $nbCols; ++$j) { 1127 $contributions[$contrib['contributionId']]['stat'][$j]['add'] = 0; 1128 $contributions[$contrib['contributionId']]['stat'][$j]['del'] = 0; 1129 $contributions[$contrib['contributionId']]['stat'][$j]['nbAdd'] = 0; 1130 $contributions[$contrib['contributionId']]['stat'][$j]['nbDel'] = 0; 1131 $contributions[$contrib['contributionId']]['stat'][$j]['nbUpdate'] = 0; 1132 } 1133 } 1134 1135 if (! empty($action['add'])) { 1136 $contributions[$contrib['contributionId']]['stat'][$i]['add'] += $action['add']; 1137 if (empty($action['del'])) { 1138 ++$contributions[$contrib['contributionId']]['stat'][$i]['nbAdd']; 1139 } 1140 } 1141 1142 if (! empty($action['del'])) { 1143 $contributions[$contrib['contributionId']]['stat'][$i]['del'] += $action['del']; 1144 if (empty($action['add'])) { 1145 ++$contributions[$contrib['contributionId']]['stat'][$i]['nbDel']; 1146 } 1147 } 1148 1149 if (! empty($action['add']) && ! empty($action['del'])) { 1150 ++$contributions[$contrib['contributionId']]['stat'][$i]['nbUpdate']; 1151 } 1152 } 1153 } 1154 } 1155 1156 return (['nbCols' => $nbCols, 'data' => $contributions]); 1157 } 1158 1159 function get_stat_contributions_per_user($actions) 1160 { 1161 $tab = []; 1162 1163 foreach ($actions as $action) { 1164 if (strpos($action['action'], 'logged from') === 0) { 1165 $action['action'] = 'login'; 1166 } 1167 1168 if (strpos($action['action'], 'logged out') === 0) { 1169 $action['action'] = 'login'; 1170 } 1171 1172 if (isset($action['contributions'])) { 1173 if (! empty($previousAction) 1174 && $action['lastModif'] == $previousAction['lastModif'] 1175 && $action['object'] == $previousAction['object'] 1176 && $action['objectType'] == $previousAction['objectType'] 1177 && $action['categId'] != $previousAction['categId'] 1178 ) { 1179 // differ only by the categories 1180 continue; 1181 } 1182 1183 $previousAction = $action; 1184 1185 foreach ($action['contributions'] as $contrib) { 1186 if (empty($tab[$action['user']]) or empty($tab[$action['user']]['stat'][$contrib['contributionId']])) { 1187 $tab[$action['user']][$contrib['contributionId']]['name'] = $contrib['name']; 1188 $tab[$action['user']][$contrib['contributionId']]['stat']['add'] = 0; 1189 $tab[$action['user']][$contrib['contributionId']]['stat']['del'] = 0; 1190 $tab[$action['user']][$contrib['contributionId']]['stat']['nbAdd'] = 0; 1191 $tab[$action['user']][$contrib['contributionId']]['stat']['nbDel'] = 0; 1192 $tab[$action['user']][$contrib['contributionId']]['stat']['nbUpdate'] = 0; 1193 } 1194 1195 if ($action['contributorAdd']) { 1196 $tab[$action['user']][$contrib['contributionId']]['stat']['add'] += $action['contributorAdd']; 1197 if (! $action['contributorDel']) { 1198 ++$tab[$action['user']][$contrib['contributionId']]['stat']['nbAdd']; 1199 } 1200 } 1201 1202 if ($action['contributorDel']) { 1203 $tab[$action['user']][$contrib['contributionId']]['stat']['del'] += $action['contributorDel']; 1204 if (! $action['contributorAdd']) { 1205 ++$tab[$action['user']][$contrib['contributionId']]['stat']['nbDel']; 1206 } 1207 } 1208 1209 if ($action['contributorAdd'] && $action['contributorDel']) { 1210 ++$tab[$action['user']][$contrib['contributionId']]['stat']['nbUpdate']; 1211 } 1212 } 1213 } 1214 } 1215 ksort($tab); 1216 1217 return ['data' => $tab, 'nbCols' => count($tab)]; 1218 ; 1219 } 1220 1221 function get_colors($nb) 1222 { 1223 $colors[] = 'red'; 1224 if (! --$nb) { 1225 return $colors; 1226 } 1227 $colors[] = 'yellow'; 1228 if (! --$nb) { 1229 return $colors; 1230 } 1231 $colors[] = 'blue'; 1232 if (! --$nb) { 1233 return $colors; 1234 } 1235 $colors[] = 'gray'; 1236 if (! --$nb) { 1237 return $colors; 1238 } 1239 $colors[] = 'green'; 1240 if (! --$nb) { 1241 return $colors; 1242 } 1243 $colors[] = 'aqua'; 1244 if (! --$nb) { 1245 return $colors; 1246 } 1247 $colors[] = 'lime'; 1248 if (! --$nb) { 1249 return $colors; 1250 } 1251 $colors[] = 'maroon'; 1252 if (! --$nb) { 1253 return $colors; 1254 } 1255 $colors[] = 'navy'; 1256 if (! --$nb) { 1257 return $colors; 1258 } 1259 $colors[] = 'black'; 1260 if (! --$nb) { 1261 return $colors; 1262 } 1263 $colors[] = 'purple'; 1264 if (! --$nb) { 1265 return $colors; 1266 } 1267 $colors[] = 'silver'; 1268 if (! --$nb) { 1269 return $colors; 1270 } 1271 $colors[] = 'teal'; 1272 if (! --$nb) { 1273 return $colors; 1274 } 1275 1276 if ($nb > 0) { 1277 while (--$nb) { 1278 $colors[] = rand(1, 999999); 1279 } 1280 } 1281 1282 return $colors; 1283 } 1284 1285 function draw_contribution_vol($contributionStat, $type = 'add', $contributions) 1286 { 1287 $ret = []; 1288 $ret['totalVol'] = 0; 1289 $ret['x'][] = tra('Contributions'); 1290 $ret['color'] = $this->get_colors($contributions['cant']); 1291 $iy = 0; 1292 1293 foreach ($contributions['data'] as $contribution) { 1294 $ret['label'][] = utf8_decode($contribution['name']); 1295 $vol = 0; 1296 for ($ix = 0; $ix < $contributionStat['nbCols']; ++$ix) { 1297 if (! empty($contributionStat['data'][$contribution['contributionId']]['stat'][$ix])) { 1298 $vol += $contributionStat['data'][$contribution['contributionId']]['stat'][$ix][$type]; 1299 } 1300 } 1301 1302 $ret["y$iy"][] = $vol; 1303 $ret['totalVol'] += $vol; 1304 ++$iy; 1305 } 1306 1307 return $ret; 1308 } 1309 1310 function draw_week_contribution_vol($contributionStat, $type = 'add', $contributions) 1311 { 1312 $ret = []; 1313 $ret['totalVol'] = 0; 1314 1315 for ($i = 1, $nb = $contributionStat['nbCols']; $nb; --$nb) { 1316 $ret['x'][] = $i++; 1317 } 1318 1319 $ret['color'] = $this->get_colors($contributions['cant']); 1320 $iy = 0; 1321 1322 foreach ($contributions['data'] as $contribution) { 1323 $ret['label'][] = utf8_decode($contribution['name']); 1324 for ($ix = 0; $ix < $contributionStat['nbCols']; ++$ix) { 1325 if (empty($contributionStat['data'][$contribution['contributionId']]) || 1326 empty($contributionStat['data'][$contribution['contributionId']]['stat'][$ix])) { 1327 $ret["y$iy"][] = 0; 1328 } else { 1329 $ret["y$iy"][] = $contributionStat['data'][$contribution['contributionId']]['stat'][$ix][$type]; 1330 $ret['totalVol'] += $contributionStat['data'][$contribution['contributionId']]['stat'][$ix][$type]; 1331 } 1332 } 1333 ++$iy; 1334 } 1335 1336 return $ret; 1337 } 1338 1339 function draw_contribution_user($userStat, $type = 'add', $contributions) 1340 { 1341 $ret = []; 1342 $ret['totalVol'] = 0; 1343 1344 foreach ($userStat['data'] as $user => $stats) { 1345 $ret['x'][] = utf8_decode($user); 1346 } 1347 1348 $ret['color'] = $this->get_colors($contributions['cant']); 1349 $iy = 0; 1350 1351 foreach ($contributions['data'] as $contribution) { 1352 $ret['label'][] = utf8_decode($contribution['name']); 1353 foreach ($userStat['data'] as $user => $stats) { 1354 if (empty($stats[$contribution['contributionId']])) { 1355 $ret["y$iy"][] = 0; 1356 } else { 1357 $ret["y$iy"][] = $stats[$contribution['contributionId']]['stat']["$type"]; 1358 $ret['totalVol'] += $stats[$contribution['contributionId']]['stat']["$type"]; 1359 } 1360 } 1361 ++$iy; 1362 } 1363 1364 return $ret; 1365 } 1366 1367 function draw_contribution_group($groupContributions, $type = 'add', $contributions) 1368 { 1369 $ret = []; 1370 $ret['totalVol'] = 0; 1371 1372 foreach ($groupContributions as $group => $stats) { 1373 $ret['x'][] = utf8_decode($group); 1374 } 1375 1376 $ret['color'] = $this->get_colors($contributions['cant']); 1377 $iy = 0; 1378 1379 foreach ($contributions['data'] as $contribution) { 1380 $ret['label'][] = utf8_decode($contribution['name']); 1381 foreach ($groupContributions as $group => $stats) { 1382 if (empty($stats[$contribution['name']])) { 1383 $ret["y$iy"][] = 0; 1384 } else { 1385 $ret["y$iy"][] = $stats[$contribution['name']][$type]; 1386 $ret['totalVol'] += $stats[$contribution['name']][$type]; 1387 } 1388 } 1389 ++$iy; 1390 } 1391 1392 return $ret; 1393 } 1394 1395 function get_contributors($actionId) 1396 { 1397 $query = 'select uu.`login` from `tiki_actionlog_params` tap, `users_users` uu where tap.`actionId`=? and tap.`name`=? and uu.`userId`=tap.`value`'; 1398 return $this->fetchAll($query, [$actionId, 'contributor']); 1399 } 1400 1401 /* 1402 * get the contributors of the last update of a wiki page 1403 * 1404 */ 1405 function get_wiki_contributors($page_info) 1406 { 1407 $query = 'select distinct(uu.`login`), uu.`userId` ' . 1408 ' from `tiki_actionlog_params` tap, `users_users` uu , `tiki_actionlog` ta' . 1409 ' where tap.`actionId`= ta.`actionId` ' . 1410 ' and tap.`name`=? ' . 1411 ' and uu.`userId`=tap.`value` ' . 1412 ' and ta.`object`=? ' . 1413 ' and ta.`objectType`=? ' . 1414 ' and ta.`lastModif`=? ' . 1415 ' order by `login` asc' 1416 ; 1417 1418 return $this->fetchAll($query, ['contributor', $page_info['pageName'], 'wiki page', $page_info['lastModif']]); 1419 } 1420 1421 function split_actions_per_contributors($actions, $users) 1422 { 1423 $contributorActions = []; 1424 1425 foreach ($actions as $action) { 1426 $bytes = $this->get_volume_action($action); 1427 1428 if (strpos($action['action'], 'logged from') === 0) { 1429 $action['action'] = 'login'; 1430 } 1431 1432 if (strpos($action['action'], 'logged out') === 0) { 1433 $action['action'] = 'login'; 1434 } 1435 1436 $nbC = isset($action['nbContributors']) ? $action['nbContributors'] : 1; 1437 1438 if (isset($bytes['add'])) { 1439 $action['add'] = $bytes['add']; 1440 $action['contributorAdd'] = round($bytes['add'] / $nbC); 1441 $action['comment'] = 'add=' . $action['contributorAdd']; 1442 } 1443 1444 if (isset($bytes['del'])) { 1445 $action['del'] = $bytes['del']; 1446 $action['contributorDel'] = round($bytes['del'] / $nbC); 1447 if (! empty($action['comment'])) { 1448 $action['comment'] .= '&del=' . $action['contributorDel']; 1449 } else { 1450 $action['comment'] = 'del=' . $action['contributorDel']; 1451 } 1452 } 1453 1454 if (empty($users) || in_array($action['user'], $users)) { 1455 $contributorActions[] = $action; 1456 } 1457 1458 if (isset($action['contributors'])) { 1459 foreach ($action['contributors'] as $contributor) { 1460 if (empty($users) || in_array($contributor['login'], $users)) { 1461 $action['user'] = $contributor['login']; 1462 $contributorActions[] = $action; 1463 } 1464 } 1465 } 1466 } 1467 return $contributorActions; 1468 } 1469 1470 function list_logsql($sort_mode = 'created_desc', $offset = 0, $maxRecords = -1, $find = '') 1471 { 1472 global $prefs; 1473 $bindvars = []; 1474 1475 if (! empty($find)) { 1476 $findesc = '%' . $find . '%'; 1477 $amid = '`sql1` like ? or `params` like ? or `tracer` like ?'; 1478 $bindvars[] = $findesc; 1479 $bindvars[] = $findesc; 1480 $bindvars[] = $findesc; 1481 } 1482 1483 $query = 'select * from `adodb_logsql`' . ($find ? " where $amid" : '') . ' order by ' . $this->convertSortMode($sort_mode); 1484 $ret = $this->fetchAll($query, $bindvars, $maxRecords, $offset); 1485 $query_cant = 'select count(*) from `adodb_logsql`' . ($find ? " where $amid" : ''); 1486 $cant = $this->getOne($query_cant, $bindvars); 1487 $retval = []; 1488 $retval['data'] = $ret; 1489 $retval['cant'] = $cant; 1490 1491 return $retval; 1492 } 1493 1494 function clean_logsql() 1495 { 1496 $query = 'delete from `adodb_logsql`'; 1497 $this->query($query, []); 1498 } 1499 1500 function graph_to_jpgraph(&$jpgraph, $series, $accumulated = false, $color = 'whitesmoke', $colorLegend = 'white') 1501 { 1502 $jpgraph->SetScale('textlin'); 1503 $jpgraph->setMarginColor($color); 1504 $jpgraph->xaxis->SetTickLabels($series['x']); 1505 $plot = []; 1506 1507 for ($i = 0; isset($series["y$i"]); ++$i) { 1508 $plot[$i] = new BarPlot($series["y$i"]); 1509 $plot[$i]->SetFillColor($series['color'][$i]); 1510 $plot[$i]->SetLegend($series['label'][$i]); 1511 } 1512 1513 if ($accumulated) { 1514 $gbplot = new AccBarPlot($plot); 1515 } else { 1516 $gbplot = new GroupBarPlot($plot); 1517 } 1518 1519 $jpgraph->legend->SetFillColor($colorLegend); 1520 $jpgraph->Add($gbplot); 1521 } 1522 1523 function insert_image($galleryId, $graph, $ext, $title, $period) 1524 { 1525 global $prefs, $user; 1526 $imagegallib = TikiLib::lib('imagegal'); 1527 1528 $filename = $prefs['tmpDir'] . '/' . md5(rand() . time()) . '.' . $ext; 1529 $graph->Stroke($filename); 1530 $info = getimagesize($filename); 1531 $size = filesize($filename); 1532 $fp = fopen($filename, "rb"); 1533 $data = fread($fp, $size); 1534 fclose($fp); 1535 $imagegallib->insert_image( 1536 $_REQUEST['galleryId'], 1537 $title . $period, 1538 '', 1539 $title . $period . '.' . $ext, 1540 'image/' . $ext, 1541 $data, 1542 $size, 1543 $info[0], 1544 $info[1], 1545 $user, 1546 '', 1547 '' 1548 ); 1549 } 1550 1551 function get_more_info($actions, $categNames = []) 1552 { 1553 global $tikilib, $prefs; 1554 1555 foreach ($actions as &$action) { 1556 if (empty($action['user'])) { 1557 $action['user'] = 'Anonymous'; 1558 } 1559 1560 if ($action['categId'] && $categNames) { 1561 $action['categName'] = $categNames[$action['categId']]; 1562 } 1563 1564 if ($bytes = $this->get_volume_action($action)) { 1565 if (isset($bytes['add'])) { 1566 $action['add'] = $bytes['add']; 1567 } 1568 if (isset($bytes['del'])) { 1569 $action['del'] = $bytes['del']; 1570 } 1571 } 1572 1573 switch ($action['objectType']) { 1574 case 'wiki page': 1575 if (preg_match("/old=(.*)/", $action['comment'], $matches)) { 1576 $action['link'] = 'tiki-index.php?page=' . $action['object'] . '&old=' . $matches[1]; 1577 } else { 1578 $action['link'] = 'tiki-index.php?page=' . $action['object']; 1579 } 1580 break; 1581 1582 case 'article': 1583 $action['link'] = 'tiki-read_article.php?articleId=' . $action['object']; 1584 1585 if (! isset($articleNames)) { 1586 $artlib = TikiLib::lib('art'); 1587 $objects = $artlib->list_articles(0, -1, 'title_asc', '', 0, 0, ''); 1588 $articleNames = []; 1589 foreach ($objects['data'] as $object) { 1590 $articleNames[$object['articleId']] = $object['title']; 1591 } 1592 } 1593 1594 if (! empty($articleNames[$action['object']])) { 1595 $action['object'] = $articleNames[$action['object']]; 1596 } 1597 break; 1598 1599 case 'category': 1600 $action['link'] = 'tiki-browse_categories.php?parentId=' . $action['object']; 1601 if ($categNames && ! empty($categNames[$action['object']])) { 1602 $action['object'] = $categNames[$action['object']]; 1603 } 1604 break; 1605 1606 case 'forum': 1607 if ($action['action'] == 'Removed') { 1608 $action['link'] = 'tiki-view_forum.php?forumId=' . $action['object'] . '&' . $action['comment'];// threadId dded for debug info 1609 } else { 1610 $action['link'] = 'tiki-view_forum_thread.php?' . $action['comment']; 1611 } 1612 1613 if (! isset($forumNames)) { 1614 $objects = TikiLib::lib('comments')->list_forums(0, -1, 'name_asc', ''); 1615 $forumNames = []; 1616 foreach ($objects['data'] as $object) { 1617 $forumNames[$object['forumId']] = $object['name']; 1618 } 1619 } 1620 1621 if (! empty($forumNames[$action['object']])) { 1622 $action['object'] = $forumNames[$action['object']]; 1623 } 1624 break; 1625 1626 case 'image gallery': 1627 if ($action['action'] == 'Uploaded') { 1628 $action['link'] = 'tiki-browse_image.php?galleryId=' . $action['object'] . '&' . $action['comment']; 1629 } else { 1630 $action['link'] = 'tiki-browse_gallery.php?galleryId=' . $action['object']; 1631 } 1632 1633 if (! isset($imageGalleryNames)) { 1634 $imagegallib = TikiLib::lib('imagegal'); 1635 $objects = $imagegallib->list_galleries(0, -1, 'name_asc', 'admin'); 1636 foreach ($objects['data'] as $object) { 1637 $imageGalleryNames[$object['galleryId']] = $object['name']; 1638 } 1639 } 1640 1641 if (! empty($imageGalleryNames[$action['object']])) { 1642 $action['object'] = $imageGalleryNames[$action['object']]; 1643 } 1644 break; 1645 1646 case 'file gallery': 1647 if ($action['action'] == 'Uploaded' || $action['action'] == 'Downloaded') { 1648 $action['link'] = 'tiki-upload_file.php?galleryId=' . $action['object'] . '&' . $action['comment']; 1649 } else { 1650 $action['link'] = 'tiki-list_file_gallery.php?galleryId=' . $action['object']; 1651 } 1652 1653 if (! isset($fileGalleryNames)) { 1654 $filegallib = TikiLib::lib('filegal'); 1655 $objects = $filegallib->list_file_galleries(0, -1, 'name_asc', 'admin', '', $prefs['fgal_root_id']); 1656 foreach ($objects['data'] as $object) { 1657 $fileGalleryNames[$object['id']] = $object['name']; 1658 } 1659 } 1660 1661 if (! empty($fileGalleryNames[$action['object']])) { 1662 $action['object'] = $fileGalleryNames[$action['object']]; 1663 } 1664 break; 1665 1666 case 'comment': 1667 preg_match('/type=([^&]*)(&.*)/', $action['comment'], $matches); 1668 1669 switch ($matches[1]) { 1670 case 'wiki page': 1671 case 'wiki+page': 1672 case 'wiki%20page': 1673 $action['link'] = 'tiki-index.php?page=' . $action['object']; 1674 if (preg_match("/old=(.*)&/", $action['comment'], $ms)) { 1675 $action['link'] .= '&old=' . $ms[1]; 1676 } 1677 $action['link'] .= $matches[2]; 1678 break; 1679 1680 case 'file gallery': 1681 $action['link'] = 'tiki-list_file_gallery.php?galleryId=' . $action['object'] . $matches[2]; 1682 break; 1683 1684 case 'image gallery': 1685 $action['link'] = 'tiki-browse_gallery.php?galleryId=' . $action['object'] . $matches[2]; 1686 break; 1687 } 1688 1689 break; 1690 1691 case 'sheet': 1692 if (! isset($sheetNames)) { 1693 $sheetlib = TikiLib::lib('sheet'); 1694 $objects = $sheetlib->list_sheets(); 1695 foreach ($objects['data'] as $object) { 1696 $sheetNames[$object['sheetId']] = $object['title']; 1697 } 1698 } 1699 1700 if (! empty($sheetNames[$action['object']])) { 1701 $action['object'] = $sheetNames[$action['object']]; 1702 } 1703 1704 $action['link'] = 'tiki-view_sheets.php?sheetId=' . $action['object']; 1705 break; 1706 1707 case 'blog': 1708 if (! isset($blogNames)) { 1709 $bloglib = TikiLib::lib('blog'); 1710 $objects = $bloglib->list_blogs(); 1711 foreach ($objects['data'] as $object) { 1712 $blogNames[$object['blogId']] = $object['title']; 1713 } 1714 } 1715 1716 $action['link'] = 'tiki-view_blog.php?' . $action['comment']; 1717 1718 if (! empty($blogNames[$action['object']])) { 1719 $action['object'] = $blogNames[$action['object']]; 1720 } 1721 break; 1722 } 1723 } 1724 1725 return $actions; 1726 } 1727 1728 function remove_action($actionId) 1729 { 1730 $query = 'delete from `tiki_actionlog_params` where `actionId`=?'; 1731 $this->query($query, [$actionId]); 1732 $query = 'delete from `tiki_actionlog` where `actionId`=?'; 1733 return $this->query($query, [$actionId]); 1734 } 1735 1736 function get_who_viewed($mystuff, $anonymous = true) 1737 { 1738 if (! $mystuff) { 1739 return false; 1740 } 1741 1742 global $prefs; 1743 $bindvars = []; 1744 $mid = ''; 1745 foreach ($mystuff as $obj) { 1746 // If changing type, compose rest of partial filter immediately 1747 if (isset($objectType) && $obj["objectType"] != $objectType) { 1748 $mid .= ' and `object` in (' . implode(',', array_fill(0, $thistype, '?')) . ')'; 1749 // add comments filter if needed 1750 if ($comments) { 1751 $bindvars = array_merge($bindvars, $comments); 1752 $mid .= ' and `comment` in (' . implode(',', array_fill(0, count($comments), '?')) . ')'; 1753 } 1754 } 1755 1756 // If starting out, or changing type, to start new sub filter 1757 if (! isset($objectType) || $obj["objectType"] != $objectType) { 1758 $objectType = $obj["objectType"]; 1759 if (! $mid) { 1760 $mid .= ' (`objectType` = ?'; 1761 } else { 1762 $mid .= ' or `objectType` = ?'; 1763 } 1764 1765 $bindvars[] = $objectType; 1766 // reset comment detection and counter 1767 $comments = []; 1768 $thistype = 0; 1769 } 1770 1771 // Just keep adding while objectType remain unchanged 1772 $bindvars[] = $obj["object"]; 1773 if ($obj["comment"]) { 1774 // i.e. this objectType filters by comments also, not just on object (id) 1775 $comments[] = $obj["comment"]; 1776 } 1777 $thistype++; 1778 } 1779 1780 // compose rest of filter for last type 1781 $mid .= ' and `object` in (' . implode(',', array_fill(0, $thistype, '?')) . ')'; 1782 // add comments filter if needed 1783 if ($comments) { 1784 $bindvars = array_merge($bindvars, $comments); 1785 $mid .= ' and `comment` in (' . implode(',', array_fill(0, count($comments), '?')) . ')'; 1786 } 1787 // add date filter 1788 if ($prefs['user_who_viewed_my_stuff_days']) { 1789 $firsttime = $this->now - 3600 * 24 * $prefs['user_who_viewed_my_stuff_days']; 1790 $mid .= ") and `lastModif` > $firsttime"; 1791 } 1792 1793 if (! $anonymous) { 1794 $mid .= " and `user` != 'Anonymous'"; 1795 } 1796 1797 $mid .= " and `action` = 'Viewed'"; 1798 $mid .= " and `user` IS NOT NULL"; // just to avoid those strange null entries 1799 $query = "select *, max(`lastModif`) as `lastViewed` " . 1800 " from `tiki_actionlog` where $mid " . 1801 " group by `user`, `object`, `objectType`, `comment`, `actionId`, `action`, `ip`, `categId`, `client`, `lastModif` order by `lastViewed` desc" 1802 ; 1803 1804 $ret = $this->fetchAll($query, $bindvars); 1805 $ret = $this->get_more_info($ret); 1806 1807 return $ret; 1808 } 1809 1810 function get_log_count($objectType, $action) 1811 { 1812 $query = "SELECT m.user,m.object,m.action 1813 FROM tiki_actionlog AS m 1814 INNER JOIN ( 1815 SELECT MAX(i.lastModif) lastModif, i.user 1816 FROM tiki_actionlog i 1817 where objectType = ? 1818 GROUP BY i.user, i.object 1819 ) AS j ON (j.lastModif = m.lastModif AND j.user = m.user)"; 1820 return $this->fetchAll($query, [$objectType]); 1821 } 1822 1823 function get_bigblue_login_time($logins, $startDate, $endDate, $actions) 1824 { 1825 if ($endDate > $this->now) { 1826 $endDate = $this->now; 1827 } 1828 $logTimes = []; 1829 1830 foreach ($logins as $login) { 1831 if ($login['objectType'] == 'bigbluebutton') { 1832 if ($login['action'] == 'Joined Room') { 1833 if (! isset($logTimes[$login['user']][$login['object']]['starttime'])) { 1834 $logTimes[$login['user']][$login['object']]['starttime'] = $login['lastModif']; 1835 } 1836 } 1837 1838 if ($login['action'] == 'Left Room') { 1839 if (isset($logTimes[$login['user']][$login['object']]['starttime'])) { 1840 $logTimes[$login['user']][$login['object']]['total'][] = $login['lastModif'] - $logTimes[$login['user']][$login['object']]['starttime']; 1841 unset($logTimes[$login['user']][$login['object']]['starttime']); 1842 } 1843 } 1844 } 1845 } 1846 1847 foreach ($logTimes as $user => $object) { 1848 foreach ($object as $room => $times) { 1849 foreach ($times['total'] as $key => $time) { 1850 $nbMin = floor($time / 60); 1851 $nbHour = floor($nbMin / 60); 1852 $nbDay = floor($nbHour / 24); 1853 $log[$user][$room][$key] = floor($time / 60); 1854 } 1855 } 1856 } 1857 return $log; 1858 } 1859 1860 function export_bbb($actionlogs) 1861 { 1862 foreach ($actionlogs as $user => $room) { 1863 foreach ($room as $room_name => $values) { 1864 foreach ($values as $value) { 1865 $csv .= '"' . $user 1866 . '","' . $room_name 1867 . '","' . $value 1868 . '","' 1869 ; 1870 $csv .= "\"\n"; 1871 } 1872 } 1873 } 1874 return $csv; 1875 } 1876 1877 function delete_action($action, $object, $objectType, $comment) 1878 { 1879 $query = "delete from `tiki_actionlog` where `action` = ? and `object` = ? and `objectType` = ? and `comment` = ?"; 1880 $this->query($query, [$action, $object, $objectType, $comment]); 1881 } 1882 1883 /** 1884 * Log a revert action from another log 1885 * 1886 * @param int $actionId 1887 * @param string $object 1888 * @param string $page 1889 * @param array $logInfo 1890 * return null 1891 */ 1892 function revert_action($actionId, $object, $page, $logInfo) 1893 { 1894 if (! isset($logInfo['reverted'])) { 1895 $logInfoReverted = array_merge(['reverted' => true], $logInfo); 1896 $logInfoReverted = serialize($logInfoReverted); 1897 $query = "update `tiki_actionlog` set `log`= ? where `actionId`=?"; 1898 $this->query($query, [$logInfoReverted, $actionId]); 1899 $type = $page . ' apply reverted'; 1900 $message = $page . ' ' . tra('reverted'); 1901 $this->add_action($type, $object, 'system', $message, '', '', '', '', '', '', $logInfo); 1902 } 1903 } 1904} 1905