Passed
Push — master ( 6f8cb0...cccadf )
by Julito
09:47
created

Statistics::makeSizeString()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 8
nc 5
nop 1
dl 0
loc 13
rs 8.8333
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
/**
5
 * This class provides some functions for statistics.
6
 *
7
 * @package chamilo.statistics
8
 */
9
class Statistics
10
{
11
    /**
12
     * Converts a number of bytes in a formatted string.
13
     *
14
     * @param int $size
15
     *
16
     * @return string Formatted file size
17
     */
18
    public static function makeSizeString($size)
19
    {
20
        if ($size < pow(2, 10)) {
21
            return $size." bytes";
22
        }
23
        if ($size >= pow(2, 10) && $size < pow(2, 20)) {
24
            return round($size / pow(2, 10), 0)." KB";
25
        }
26
        if ($size >= pow(2, 20) && $size < pow(2, 30)) {
27
            return round($size / pow(2, 20), 1)." MB";
28
        }
29
        if ($size > pow(2, 30)) {
30
            return round($size / pow(2, 30), 2)." GB";
31
        }
32
    }
33
34
    /**
35
     * Count courses.
36
     *
37
     * @param string $categoryCode Code of a course category.
38
     *                             Default: count all courses.
39
     *
40
     * @return int Number of courses counted
41
     */
42
    public static function countCourses($categoryCode = null)
43
    {
44
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
45
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
46
        $urlId = api_get_current_access_url_id();
47
        if (api_is_multiple_url_enabled()) {
48
            $sql = "SELECT COUNT(*) AS number
49
                    FROM ".$course_table." as c, $access_url_rel_course_table as u
50
                    WHERE u.c_id = c.id AND access_url_id='".$urlId."'";
51
            if (isset($categoryCode)) {
52
                $sql .= " AND category_code = '".Database::escape_string($categoryCode)."'";
53
            }
54
        } else {
55
            $sql = "SELECT COUNT(*) AS number
56
                    FROM $course_table";
57
            if (isset($categoryCode)) {
58
                $sql .= " WHERE category_code = '".Database::escape_string($categoryCode)."'";
59
            }
60
        }
61
62
        $res = Database::query($sql);
63
        $obj = Database::fetch_object($res);
64
65
        return $obj->number;
66
    }
67
68
    /**
69
     * Count courses by visibility.
70
     *
71
     * @param int $visibility visibility (0 = closed, 1 = private, 2 = open, 3 = public) all courses
72
     *
73
     * @return int Number of courses counted
74
     */
75
    public static function countCoursesByVisibility($visibility = null)
76
    {
77
        if (!isset($visibility)) {
78
            return 0;
79
        }
80
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
81
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
82
        $urlId = api_get_current_access_url_id();
83
        if (api_is_multiple_url_enabled()) {
84
            $sql = "SELECT COUNT(*) AS number
85
                    FROM $course_table as c, $access_url_rel_course_table as u
86
                    WHERE u.c_id = c.id AND access_url_id='".$urlId."'";
87
            if (isset($visibility)) {
88
                $sql .= " AND visibility = ".intval($visibility);
89
            }
90
        } else {
91
            $sql = "SELECT COUNT(*) AS number FROM $course_table ";
92
            if (isset($visibility)) {
93
                $sql .= " WHERE visibility = ".intval($visibility);
94
            }
95
        }
96
        $res = Database::query($sql);
97
        $obj = Database::fetch_object($res);
98
99
        return $obj->number;
100
    }
101
102
    /**
103
     * Count users.
104
     *
105
     * @param int    $status                user status (COURSEMANAGER or STUDENT) if not setted it'll count all users
106
     * @param string $categoryCode          course category code. Default: count only users without filtering category
107
     * @param bool   $countInvisibleCourses Count invisible courses (todo)
108
     * @param bool   $onlyActive            Count only active users (false to only return currently active users)
109
     *
110
     * @return int Number of users counted
111
     */
112
    public static function countUsers(
113
        $status = null,
114
        $categoryCode = null,
115
        $countInvisibleCourses = true,
116
        $onlyActive = false
117
    ) {
118
        // Database table definitions
119
        $course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
120
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
121
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
122
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
123
        $urlId = api_get_current_access_url_id();
124
        $active_filter = $onlyActive ? ' AND active=1' : '';
125
        $status_filter = isset($status) ? ' AND status = '.intval($status) : '';
126
127
        if (api_is_multiple_url_enabled()) {
128
            $sql = "SELECT COUNT(DISTINCT(u.user_id)) AS number
129
                    FROM $user_table as u, $access_url_rel_user_table as url
130
                    WHERE
131
                        u.user_id = url.user_id AND
132
                        access_url_id = '".$urlId."'
133
                        $status_filter $active_filter";
134
            if (isset($categoryCode)) {
135
                $sql = "SELECT COUNT(DISTINCT(cu.user_id)) AS number
136
                        FROM $course_user_table cu, $course_table c, $access_url_rel_user_table as url
137
                        WHERE
138
                            c.id = cu.c_id AND
139
                            c.category_code = '".Database::escape_string($categoryCode)."' AND
140
                            cu.user_id = url.user_id AND
141
                            access_url_id='".$urlId."'
142
                            $status_filter $active_filter";
143
            }
144
        } else {
145
            $sql = "SELECT COUNT(DISTINCT(user_id)) AS number
146
                    FROM $user_table
147
                    WHERE 1=1 $status_filter $active_filter";
148
            if (isset($categoryCode)) {
149
                $status_filter = isset($status) ? ' AND status = '.intval($status) : '';
150
                $sql = "SELECT COUNT(DISTINCT(cu.user_id)) AS number
151
                        FROM $course_user_table cu, $course_table c
152
                        WHERE
153
                            c.id = cu.c_id AND
154
                            c.category_code = '".Database::escape_string($categoryCode)."'
155
                            $status_filter
156
                            $active_filter
157
                        ";
158
            }
159
        }
160
161
        $res = Database::query($sql);
162
        $obj = Database::fetch_object($res);
163
164
        return $obj->number;
165
    }
166
167
    /**
168
     * @param string $startDate
169
     * @param string $endDate
170
     *
171
     * @return array
172
     */
173
    public static function getCoursesWithActivity($startDate, $endDate)
174
    {
175
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
176
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
177
        $startDate = Database::escape_string($startDate);
178
        $endDate = Database::escape_string($endDate);
179
180
        $urlId = api_get_current_access_url_id();
181
182
        if (api_is_multiple_url_enabled()) {
183
            $sql = "SELECT DISTINCT(t.c_id) FROM $table t , $access_url_rel_course_table a
184
                    WHERE
185
                        t.c_id = a.c_id AND
186
                        access_url_id='".$urlId."' AND
187
                        access_date BETWEEN '$startDate' AND '$endDate'
188
                    ";
189
        } else {
190
            $sql = "SELECT DISTINCT(t.c_id) FROM $table t
191
                   access_date BETWEEN '$startDate' AND '$endDate' ";
192
        }
193
194
        $result = Database::query($sql);
195
196
        return Database::store_result($result);
197
    }
198
199
    /**
200
     * Count activities from track_e_default_table.
201
     *
202
     * @return int Number of activities counted
203
     */
204
    public static function getNumberOfActivities($courseId = 0, $sessionId = 0)
205
    {
206
        // Database table definitions
207
        $track_e_default = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
208
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
209
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
210
        $urlId = api_get_current_access_url_id();
211
        if (api_is_multiple_url_enabled()) {
212
            $sql = "SELECT count(default_id) AS total_number_of_items
213
                    FROM $track_e_default, $table_user user, $access_url_rel_user_table url
214
                    WHERE
215
                        default_user_id = user.user_id AND
216
                        user.user_id=url.user_id AND
217
                        access_url_id = '".$urlId."'";
218
        } else {
219
            $sql = "SELECT count(default_id) AS total_number_of_items
220
                    FROM $track_e_default, $table_user user
221
                    WHERE default_user_id = user.user_id ";
222
        }
223
224
        if (!empty($courseId)) {
225
            $courseId = (int) $courseId;
226
            $sql .= " AND c_id = $courseId";
227
            $sql .= api_get_session_condition($sessionId);
228
        }
229
230
        if (isset($_GET['keyword'])) {
231
            $keyword = Database::escape_string(trim($_GET['keyword']));
232
            $sql .= " AND (
233
                        user.username LIKE '%".$keyword."%' OR 
234
                        default_event_type LIKE '%".$keyword."%' OR 
235
                        default_value_type LIKE '%".$keyword."%' OR 
236
                        default_value LIKE '%".$keyword."%') ";
237
        }
238
239
        $res = Database::query($sql);
240
        $obj = Database::fetch_object($res);
241
242
        return $obj->total_number_of_items;
243
    }
244
245
    /**
246
     * Get activities data to display.
247
     *
248
     * @param int    $from
249
     * @param int    $numberOfItems
250
     * @param int    $column
251
     * @param string $direction
252
     * @param int    $courseId
253
     * @param int    $sessionId
254
     *
255
     * @return array
256
     */
257
    public static function getActivitiesData(
258
        $from,
259
        $numberOfItems,
260
        $column,
261
        $direction,
262
        $courseId = 0,
263
        $sessionId = 0
264
    ) {
265
        $track_e_default = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
266
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
267
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
268
        $urlId = api_get_current_access_url_id();
269
        $column = intval($column);
270
        $from = intval($from);
271
        $numberOfItems = intval($numberOfItems);
272
        $direction = strtoupper($direction);
273
274
        if (!in_array($direction, ['ASC', 'DESC'])) {
275
            $direction = 'DESC';
276
        }
277
278
        if (api_is_multiple_url_enabled()) {
279
            $sql = "SELECT
280
                        default_event_type  as col0,
281
                        default_value_type    as col1,
282
                        default_value        as col2,
283
                        c_id         as col3,
284
                        session_id as col4,
285
                        user.username         as col5,
286
                        user.user_id         as col6,
287
                        default_date         as col7
288
                    FROM $track_e_default as track_default,
289
                    $table_user as user,
290
                    $access_url_rel_user_table as url
291
                    WHERE
292
                        track_default.default_user_id = user.user_id AND
293
                        url.user_id = user.user_id AND
294
                        access_url_id= $urlId ";
295
        } else {
296
            $sql = "SELECT
297
                       default_event_type  as col0,
298
                       default_value_type    as col1,
299
                       default_value        as col2,
300
                       c_id         as col3,
301
                       session_id as col4,
302
                       user.username         as col5,
303
                       user.user_id         as col6,
304
                       default_date         as col7
305
                   FROM $track_e_default track_default, $table_user user
306
                   WHERE track_default.default_user_id = user.user_id ";
307
        }
308
309
        if (!empty($_GET['keyword'])) {
310
            $keyword = Database::escape_string(trim($_GET['keyword']));
311
            $sql .= " AND (user.username LIKE '%".$keyword."%' OR
312
                        default_event_type LIKE '%".$keyword."%' OR
313
                        default_value_type LIKE '%".$keyword."%' OR
314
                        default_value LIKE '%".$keyword."%') ";
315
        }
316
317
        if (!empty($courseId)) {
318
            $courseId = (int) $courseId;
319
            $sql .= " AND c_id = $courseId";
320
            $sql .= api_get_session_condition($sessionId);
321
        }
322
323
        if (!empty($column) && !empty($direction)) {
324
            $sql .= " ORDER BY col$column $direction";
325
        } else {
326
            $sql .= " ORDER BY col7 DESC ";
327
        }
328
        $sql .= " LIMIT $from, $numberOfItems ";
329
330
        $res = Database::query($sql);
331
        $activities = [];
332
        while ($row = Database::fetch_row($res)) {
333
            if (strpos($row[1], '_object') === false &&
334
                strpos($row[1], '_array') === false
335
            ) {
336
                $row[2] = $row[2];
337
            } else {
338
                if (!empty($row[2])) {
339
                    $originalData = str_replace('\\', '', $row[2]);
340
                    $row[2] = UnserializeApi::unserialize('not_allowed_classes', $originalData);
341
                    if (is_array($row[2]) && !empty($row[2])) {
342
                        $row[2] = implode_with_key(', ', $row[2]);
343
                    } else {
344
                        $row[2] = $originalData;
345
                    }
346
                }
347
            }
348
349
            if (!empty($row['default_date'])) {
350
                $row['default_date'] = api_get_local_time($row['default_date']);
351
            } else {
352
                $row['default_date'] = '-';
353
            }
354
355
            if (!empty($row[5])) {
356
                // Course
357
                if (!empty($row[3])) {
358
                    $row[3] = Display::url(
359
                        $row[3],
360
                        api_get_path(WEB_CODE_PATH).'admin/course_edit.php?id='.$row[3]
361
                    );
362
                } else {
363
                    $row[3] = '-';
364
                }
365
366
                // session
367
                if (!empty($row[4])) {
368
                    $row[4] = Display::url(
369
                        $row[4],
370
                        api_get_path(WEB_CODE_PATH).'session/resume_session.php?id_session='.$row[4]
371
                    );
372
                } else {
373
                    $row[4] = '-';
374
                }
375
376
                // User id.
377
                $row[5] = Display::url(
378
                    $row[5],
379
                    api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$row[6],
380
                    ['class' => 'ajax']
381
                );
382
383
                $row[6] = Tracking::get_ip_from_user_event(
384
                    $row[6],
385
                    $row[7],
386
                    true
387
                );
388
                if (empty($row[6])) {
389
                    $row[6] = get_lang('Unknown');
390
                }
391
            }
392
            $activities[] = $row;
393
        }
394
395
        return $activities;
396
    }
397
398
    /**
399
     * Get all course categories.
400
     *
401
     * @return array All course categories (code => name)
402
     */
403
    public static function getCourseCategories()
404
    {
405
        $categoryTable = Database::get_main_table(TABLE_MAIN_CATEGORY);
406
        $sql = "SELECT code, name 
407
                FROM $categoryTable
408
                ORDER BY tree_pos";
409
        $res = Database::query($sql);
410
        $categories = [];
411
        while ($category = Database::fetch_object($res)) {
412
            $categories[$category->code] = $category->name;
413
        }
414
415
        return $categories;
416
    }
417
418
    /**
419
     * Rescale data.
420
     *
421
     * @param array $data The data that should be rescaled
422
     * @param int   $max  The maximum value in the rescaled data (default = 500);
423
     *
424
     * @return array The rescaled data, same key as $data
425
     */
426
    public static function rescale($data, $max = 500)
427
    {
428
        $data_max = 1;
429
        foreach ($data as $index => $value) {
430
            $data_max = ($data_max < $value ? $value : $data_max);
431
        }
432
        reset($data);
433
        $result = [];
434
        $delta = $max / $data_max;
435
        foreach ($data as $index => $value) {
436
            $result[$index] = (int) round($value * $delta);
437
        }
438
439
        return $result;
440
    }
441
442
    /**
443
     * Show statistics.
444
     *
445
     * @param string $title      The title
446
     * @param array  $stats
447
     * @param bool   $showTotal
448
     * @param bool   $isFileSize
449
     */
450
    public static function printStats(
451
        $title,
452
        $stats,
453
        $showTotal = true,
454
        $isFileSize = false
455
    ) {
456
        $total = 0;
457
        $data = self::rescale($stats);
458
        echo '<table class="data_table" cellspacing="0" cellpadding="3">
459
                <tr><th colspan="'.($showTotal ? '4' : '3').'">'.$title.'</th></tr>';
460
        $i = 0;
461
        foreach ($stats as $subtitle => $number) {
462
            $total += $number;
463
        }
464
465
        foreach ($stats as $subtitle => $number) {
466
            if (!$isFileSize) {
467
                $number_label = number_format($number, 0, ',', '.');
468
            } else {
469
                $number_label = self::makeSizeString($number);
470
            }
471
            $percentage = ($total > 0 ? number_format(100 * $number / $total, 1, ',', '.') : '0');
472
473
            echo '<tr class="row_'.($i % 2 == 0 ? 'odd' : 'even').'">
474
                    <td width="150">'.$subtitle.'</td>
475
                    <td width="550">'.Display::bar_progress($percentage, false).'</td>
476
                    <td align="right">'.$number_label.'</td>';
477
            if ($showTotal) {
478
                echo '<td align="right"> '.$percentage.'%</td>';
479
            }
480
            echo '</tr>';
481
            $i++;
482
        }
483
        if ($showTotal) {
484
            if (!$isFileSize) {
485
                $total_label = number_format($total, 0, ',', '.');
486
            } else {
487
                $total_label = self::makeSizeString($total);
488
            }
489
            echo '<tr><th colspan="4" align="right">'.get_lang('Total').': '.$total_label.'</td></tr>';
490
        }
491
        echo '</table>';
492
    }
493
494
    /**
495
     * Show some stats about the number of logins.
496
     *
497
     * @param string $type month, hour or day
498
     */
499
    public static function printLoginStats($type)
500
    {
501
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
502
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
503
        $urlId = api_get_current_access_url_id();
504
505
        $table_url = null;
506
        $where_url = null;
507
        $now = api_get_utc_datetime();
508
        $where_url_last = ' WHERE login_date > DATE_SUB("'.$now.'",INTERVAL 1 %s)';
509
        if (api_is_multiple_url_enabled()) {
510
            $table_url = ", $access_url_rel_user_table";
511
            $where_url = " WHERE login_user_id=user_id AND access_url_id='".$urlId."'";
512
            $where_url_last = ' AND login_date > DATE_SUB("'.$now.'",INTERVAL 1 %s)';
513
        }
514
515
        $period = get_lang('PeriodMonth');
516
        $periodCollection = api_get_months_long();
517
        $sql = "SELECT 
518
                DATE_FORMAT( login_date, '%Y-%m' ) AS stat_date , 
519
                count( login_id ) AS number_of_logins
520
                FROM $table $table_url $where_url
521
                GROUP BY stat_date
522
                ORDER BY login_date DESC";
523
        $sql_last_x = null;
524
525
        switch ($type) {
526
            case 'hour':
527
                $period = get_lang('PeriodHour');
528
                $sql = "SELECT 
529
                          DATE_FORMAT( login_date, '%H') AS stat_date, 
530
                          count( login_id ) AS number_of_logins 
531
                        FROM $table $table_url $where_url 
532
                        GROUP BY stat_date 
533
                        ORDER BY stat_date ";
534
                $sql_last_x = "SELECT 
535
                                DATE_FORMAT( login_date, '%H' ) AS stat_date, 
536
                                count( login_id ) AS number_of_logins 
537
                               FROM $table $table_url $where_url ".sprintf($where_url_last, 'DAY')." 
538
                               GROUP BY stat_date 
539
                               ORDER BY stat_date ";
540
                break;
541
            case 'day':
542
                $periodCollection = api_get_week_days_long();
543
                $period = get_lang('PeriodDay');
544
                $sql = "SELECT DATE_FORMAT( login_date, '%w' ) AS stat_date , 
545
                        count( login_id ) AS number_of_logins 
546
                        FROM  $table $table_url $where_url 
547
                        GROUP BY stat_date 
548
                        ORDER BY DATE_FORMAT( login_date, '%w' ) ";
549
                $sql_last_x = "SELECT 
550
                                DATE_FORMAT( login_date, '%w' ) AS stat_date, 
551
                                count( login_id ) AS number_of_logins 
552
                               FROM $table $table_url $where_url ".sprintf($where_url_last, 'WEEK')." 
553
                               GROUP BY stat_date 
554
                               ORDER BY DATE_FORMAT( login_date, '%w' ) ";
555
                break;
556
        }
557
558
        if ($sql_last_x) {
559
            $res_last_x = Database::query($sql_last_x);
560
            $result_last_x = [];
561
            while ($obj = Database::fetch_object($res_last_x)) {
562
                $stat_date = ($type === 'day') ? $periodCollection[$obj->stat_date] : $obj->stat_date;
563
                $result_last_x[$stat_date] = $obj->number_of_logins;
564
            }
565
            self::printStats(get_lang('LastLogins').' ('.$period.')', $result_last_x, true);
566
            flush(); //flush web request at this point to see something already while the full data set is loading
567
            echo '<br />';
568
        }
569
        $res = Database::query($sql);
570
        $result = [];
571
        while ($obj = Database::fetch_object($res)) {
572
            $stat_date = $obj->stat_date;
573
            switch ($type) {
574
                case 'month':
575
                    $stat_date = explode('-', $stat_date);
576
                    $stat_date[1] = $periodCollection[$stat_date[1] - 1];
577
                    $stat_date = implode(' ', $stat_date);
578
                    break;
579
                case 'day':
580
                    $stat_date = $periodCollection[$stat_date];
581
                    break;
582
            }
583
            $result[$stat_date] = $obj->number_of_logins;
584
        }
585
        self::printStats(get_lang('AllLogins').' ('.$period.')', $result, true);
586
    }
587
588
    /**
589
     * Print the number of recent logins.
590
     *
591
     * @param bool $distinct        whether to only give distinct users stats, or *all* logins
592
     * @param int  $sessionDuration
593
     */
594
    public static function printRecentLoginStats($distinct = false, $sessionDuration = 0)
595
    {
596
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
597
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
598
        $urlId = api_get_current_access_url_id();
599
        $table_url = '';
600
        $where_url = '';
601
        if (api_is_multiple_url_enabled()) {
602
            $table_url = ", $access_url_rel_user_table";
603
            $where_url = " AND login_user_id=user_id AND access_url_id='".$urlId."'";
604
        }
605
606
        $now = api_get_utc_datetime();
607
        $field = 'login_id';
608
        if ($distinct) {
609
            $field = 'DISTINCT(login_user_id)';
610
        }
611
612
        $days = [1, 7, 15, 31];
613
        $sqlList = [];
614
615
        $sessionDuration = (int) $sessionDuration;
616
        foreach ($days as $day) {
617
            $date = new DateTime($now);
618
            $startDate = $date->format('Y-m-d').' 00:00:00';
619
            $endDate = $date->format('Y-m-d').' 23:59:59';
620
621
            if ($day > 1) {
622
                $startDate = $date->sub(new DateInterval('P'.$day.'D'));
623
                $startDate = $startDate->format('Y-m-d').' 00:00:00';
624
            }
625
626
            $localDate = api_get_local_time($startDate, null, null, false, false);
627
            $localEndDate = api_get_local_time($endDate, null, null, false, false);
628
629
            $label = sprintf(get_lang('LastXDays'), $day);
630
            if ($day == 1) {
631
                $label = get_lang('Today');
632
            }
633
            $label .= " <br /> $localDate - $localEndDate";
634
            $sql = "SELECT count($field) AS number 
635
                    FROM $table $table_url 
636
                    WHERE 
637
                        UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration AND
638
                        login_date BETWEEN '$startDate' AND '$endDate'  
639
                        $where_url";
640
            $sqlList[$label] = $sql;
641
        }
642
643
        $sql = "SELECT count($field) AS number 
644
                FROM $table $table_url                
645
                WHERE UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration $where_url
646
               ";
647
        $sqlList[get_lang('Total')] = $sql;
648
        $totalLogin = [];
649
        foreach ($sqlList as $label => $query) {
650
            $res = Database::query($query);
651
            $obj = Database::fetch_object($res);
652
            $totalLogin[$label] = $obj->number;
653
        }
654
        if ($distinct) {
655
            self::printStats(get_lang('DistinctUsersLogins'), $totalLogin, false);
656
        } else {
657
            self::printStats(get_lang('Logins'), $totalLogin, false);
658
        }
659
    }
660
661
    public static function getLoginCount($startDate, $endDate)
662
    {
663
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
664
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
665
        $urlId = api_get_current_access_url_id();
666
        $table_url = '';
667
        $where_url = '';
668
        if (api_is_multiple_url_enabled()) {
669
            $table_url = ", $access_url_rel_user_table";
670
            $where_url = " AND login_user_id=user_id AND access_url_id='".$urlId."'";
671
        }
672
        $startDate = Database::escape_string($startDate);
673
        $endDate = Database::escape_string($endDate);
674
675
        $sql = "
676
                SELECT count(logins) count FROM (
677
                    SELECT count(login_user_id) AS logins
678
                    FROM $table $table_url 
679
                    WHERE 
680
                    login_date BETWEEN '$startDate' AND '$endDate'  
681
                    $where_url 
682
                    GROUP BY login_user_id
683
                ) as t
684
                ";
685
686
        $res = Database::query($sql);
687
        $totalLogin = 0;
688
        $row = Database::fetch_array($res, 'ASSOC');
689
        if ($row) {
690
            $totalLogin = $row['count'];
691
        }
692
693
        return $totalLogin;
694
    }
695
696
    /**
697
     * get the number of recent logins.
698
     *
699
     * @param bool $distinct            Whether to only give distinct users stats, or *all* logins
700
     * @param int  $sessionDuration
701
     * @param bool $completeMissingDays Whether to fill the daily gaps (if any) when getting a list of logins
702
     *
703
     * @return array
704
     */
705
    public static function getRecentLoginStats($distinct = false, $sessionDuration = 0, $completeMissingDays = true)
706
    {
707
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
708
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
709
        $urlId = api_get_current_access_url_id();
710
        $table_url = '';
711
        $where_url = '';
712
        if (api_is_multiple_url_enabled()) {
713
            $table_url = ", $access_url_rel_user_table";
714
            $where_url = " AND login_user_id=user_id AND access_url_id='".$urlId."'";
715
        }
716
717
        $now = api_get_utc_datetime();
718
        $date = new DateTime($now);
719
        $date->sub(new DateInterval('P15D'));
720
        $newDate = $date->format('Y-m-d h:i:s');
721
        $totalLogin = self::buildDatesArray($newDate, $now, true);
722
723
        $field = 'login_id';
724
        if ($distinct) {
725
            $field = 'DISTINCT(login_user_id)';
726
        }
727
        $sessionDuration = (int) $sessionDuration;
728
729
        $sql = "SELECT count($field) AS number, date(login_date) as login_date
730
                FROM $table $table_url 
731
                WHERE 
732
                UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration AND
733
                login_date >= '$newDate' $where_url 
734
                GROUP BY date(login_date)";
735
736
        $res = Database::query($sql);
737
        while ($row = Database::fetch_array($res, 'ASSOC')) {
738
            $monthAndDay = substr($row['login_date'], 5, 5);
739
            $totalLogin[$monthAndDay] = $row['number'];
740
        }
741
742
        return $totalLogin;
743
    }
744
745
    /**
746
     * Get course tools usage statistics for the whole platform (by URL if multi-url).
747
     */
748
    public static function getToolsStats()
749
    {
750
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
751
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
752
        $urlId = api_get_current_access_url_id();
753
754
        $tools = [
755
            'announcement',
756
            'assignment',
757
            'calendar_event',
758
            'chat',
759
            'course_description',
760
            'document',
761
            'dropbox',
762
            'group',
763
            'learnpath',
764
            'link',
765
            'quiz',
766
            'student_publication',
767
            'user',
768
            'forum',
769
        ];
770
        $tool_names = [];
771
        foreach ($tools as $tool) {
772
            $tool_names[$tool] = get_lang(ucfirst($tool), '');
773
        }
774
        if (api_is_multiple_url_enabled()) {
775
            $sql = "SELECT access_tool, count( access_id ) AS number_of_logins
776
                    FROM $table t , $access_url_rel_course_table a
777
                    WHERE
778
                        access_tool IN ('".implode("','", $tools)."') AND
779
                        t.c_id = a.c_id AND
780
                        access_url_id='".$urlId."'
781
                        GROUP BY access_tool
782
                    ";
783
        } else {
784
            $sql = "SELECT access_tool, count( access_id ) AS number_of_logins
785
                    FROM $table
786
                    WHERE access_tool IN ('".implode("','", $tools)."')
787
                    GROUP BY access_tool ";
788
        }
789
790
        $res = Database::query($sql);
791
        $result = [];
792
        while ($obj = Database::fetch_object($res)) {
793
            $result[$tool_names[$obj->access_tool]] = $obj->number_of_logins;
794
        }
795
796
        return $result;
797
    }
798
799
    /**
800
     * Show some stats about the accesses to the different course tools.
801
     *
802
     * @param array $result If defined, this serves as data. Otherwise, will get the data from getToolsStats()
803
     */
804
    public static function printToolStats($result = null)
805
    {
806
        if (empty($result)) {
807
            $result = self::getToolsStats();
808
        }
809
        self::printStats(get_lang('PlatformToolAccess'), $result, true);
810
    }
811
812
    /**
813
     * Show some stats about the number of courses per language.
814
     */
815
    public static function printCourseByLanguageStats()
816
    {
817
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
818
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
819
        $urlId = api_get_current_access_url_id();
820
        if (api_is_multiple_url_enabled()) {
821
            $sql = "SELECT course_language, count( c.code ) AS number_of_courses
822
                    FROM $table as c, $access_url_rel_course_table as u
823
                    WHERE u.c_id = c.id AND access_url_id='".$urlId."'
824
                    GROUP BY course_language
825
                    ORDER BY number_of_courses DESC";
826
        } else {
827
            $sql = "SELECT course_language, count( code ) AS number_of_courses
828
                   FROM $table GROUP BY course_language
829
                   ORDER BY number_of_courses DESC";
830
        }
831
        $res = Database::query($sql);
832
        $result = [];
833
        while ($obj = Database::fetch_object($res)) {
834
            $result[$obj->course_language] = $obj->number_of_courses;
835
        }
836
837
        return $result;
838
    }
839
840
    /**
841
     * Shows the number of users having their picture uploaded in Dokeos.
842
     */
843
    public static function printUserPicturesStats()
844
    {
845
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
846
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
847
        $urlId = api_get_current_access_url_id();
848
        $url_condition = null;
849
        $url_condition2 = null;
850
        $table = null;
851
        if (api_is_multiple_url_enabled()) {
852
            $url_condition = ", $access_url_rel_user_table as url WHERE url.user_id=u.user_id AND access_url_id='".$urlId."'";
853
            $url_condition2 = " AND url.user_id=u.user_id AND access_url_id='".$urlId."'";
854
            $table = ", $access_url_rel_user_table as url ";
855
        }
856
        $sql = "SELECT COUNT(*) AS n FROM $user_table as u ".$url_condition;
857
        $res = Database::query($sql);
858
        $count1 = Database::fetch_object($res);
859
        $sql = "SELECT COUNT(*) AS n FROM $user_table as u $table ".
860
               "WHERE LENGTH(picture_uri) > 0 $url_condition2";
861
        $res = Database::query($sql);
862
        $count2 = Database::fetch_object($res);
863
        // #users without picture
864
        $result[get_lang('No')] = $count1->n - $count2->n;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.
Loading history...
865
        $result[get_lang('Yes')] = $count2->n; // #users with picture
866
867
        self::printStats(get_lang('CountUsers').' ('.get_lang('UserPicture').')', $result, true);
868
    }
869
870
    /**
871
     * Important activities.
872
     */
873
    public static function printActivitiesStats()
874
    {
875
        echo '<h4>'.get_lang('ImportantActivities').'</h4>';
876
        // Create a search-box
877
        $form = new FormValidator(
878
            'search_simple',
879
            'get',
880
            api_get_path(WEB_CODE_PATH).'admin/statistics/index.php',
881
            '',
882
            'width=200px',
883
            false
884
        );
885
        $renderer = &$form->defaultRenderer();
886
        $renderer->setCustomElementTemplate('<span>{element}</span> ');
887
        $form->addHidden('report', 'activities');
888
        $form->addHidden('activities_direction', 'DESC');
889
        $form->addHidden('activities_column', '4');
890
        $form->addElement('text', 'keyword', get_lang('Keyword'));
891
        $form->addButtonSearch(get_lang('Search'), 'submit');
892
        echo '<div class="actions">';
893
        $form->display();
894
        echo '</div>';
895
896
        $table = new SortableTable(
897
            'activities',
898
            ['Statistics', 'getNumberOfActivities'],
899
            ['Statistics', 'getActivitiesData'],
900
            7,
901
            50,
902
            'DESC'
903
        );
904
        $parameters = [];
905
906
        $parameters['report'] = 'activities';
907
        if (isset($_GET['keyword'])) {
908
            $parameters['keyword'] = Security::remove_XSS($_GET['keyword']);
909
        }
910
911
        $table->set_additional_parameters($parameters);
912
        $table->set_header(0, get_lang('EventType'));
913
        $table->set_header(1, get_lang('DataType'));
914
        $table->set_header(2, get_lang('Value'));
915
        $table->set_header(3, get_lang('Course'));
916
        $table->set_header(4, get_lang('Session'));
917
        $table->set_header(5, get_lang('UserName'));
918
        $table->set_header(6, get_lang('IPAddress'));
919
        $table->set_header(7, get_lang('Date'));
920
        $table->display();
921
    }
922
923
    /**
924
     * Shows statistics about the time of last visit to each course.
925
     */
926
    public static function printCourseLastVisit()
927
    {
928
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
929
        $urlId = api_get_current_access_url_id();
930
931
        $columns[0] = 't.c_id';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$columns was never initialized. Although not strictly required by PHP, it is generally a good practice to add $columns = array(); before regardless.
Loading history...
932
        $columns[1] = 'access_date';
933
        $sql_order[SORT_ASC] = 'ASC';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sql_order was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sql_order = array(); before regardless.
Loading history...
934
        $sql_order[SORT_DESC] = 'DESC';
935
        $per_page = isset($_GET['per_page']) ? intval($_GET['per_page']) : 10;
936
        $page_nr = isset($_GET['page_nr']) ? intval($_GET['page_nr']) : 1;
937
        $column = isset($_GET['column']) ? intval($_GET['column']) : 0;
938
        $direction = isset($_GET['direction']) ? $_GET['direction'] : SORT_ASC;
939
940
        if (!in_array($direction, [SORT_ASC, SORT_DESC])) {
941
            $direction = SORT_ASC;
942
        }
943
        $form = new FormValidator('courselastvisit', 'get');
944
        $form->addElement('hidden', 'report', 'courselastvisit');
945
        $form->addText('date_diff', get_lang('Days'), true);
946
        $form->addRule('date_diff', 'InvalidNumber', 'numeric');
947
        $form->addButtonSearch(get_lang('Search'), 'submit');
948
        if (!isset($_GET['date_diff'])) {
949
            $defaults['date_diff'] = 60;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$defaults was never initialized. Although not strictly required by PHP, it is generally a good practice to add $defaults = array(); before regardless.
Loading history...
950
        } else {
951
            $defaults['date_diff'] = Security::remove_XSS($_GET['date_diff']);
952
        }
953
        $form->setDefaults($defaults);
954
        $form->display();
955
        $values = $form->exportValues();
956
        $date_diff = $values['date_diff'];
957
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
958
        if (api_is_multiple_url_enabled()) {
959
            $sql = "SELECT * FROM $table t , $access_url_rel_course_table a
960
                   WHERE
961
                        t.c_id = a.c_id AND
962
                        access_url_id='".$urlId."'
963
                   GROUP BY t.c_id
964
                   HAVING t.c_id <> ''
965
                   AND DATEDIFF( '".api_get_utc_datetime()."' , access_date ) <= ".$date_diff;
966
        } else {
967
            $sql = "SELECT * FROM $table t
968
                   GROUP BY t.c_id
969
                   HAVING t.c_id <> ''
970
                   AND DATEDIFF( '".api_get_utc_datetime()."' , access_date ) <= ".$date_diff;
971
        }
972
        $sql .= ' ORDER BY '.$columns[$column].' '.$sql_order[$direction];
973
        $from = ($page_nr - 1) * $per_page;
974
        $sql .= ' LIMIT '.$from.','.$per_page;
975
976
        echo '<p>'.get_lang('LastAccess').' &gt;= '.$date_diff.' '.get_lang('Days').'</p>';
977
        $res = Database::query($sql);
978
        if (Database::num_rows($res) > 0) {
979
            $courses = [];
980
            while ($obj = Database::fetch_object($res)) {
981
                $courseInfo = api_get_course_info_by_id($obj->c_id);
982
                $course = [];
983
                $course[] = '<a href="'.api_get_path(WEB_COURSE_PATH).$courseInfo['code'].'">'.$courseInfo['code'].' <a>';
984
                // Allow sort by date hiding the numerical date
985
                $course[] = '<span style="display:none;">'.$obj->access_date.'</span>'.api_convert_and_format_date($obj->access_date);
986
                $courses[] = $course;
987
            }
988
            $parameters['date_diff'] = $date_diff;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$parameters was never initialized. Although not strictly required by PHP, it is generally a good practice to add $parameters = array(); before regardless.
Loading history...
989
            $parameters['report'] = 'courselastvisit';
990
            $table_header[] = [get_lang("CourseCode"), true];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$table_header was never initialized. Although not strictly required by PHP, it is generally a good practice to add $table_header = array(); before regardless.
Loading history...
991
            $table_header[] = [get_lang("LastAccess"), true];
992
            Display:: display_sortable_table(
993
                $table_header,
994
                $courses,
995
                ['column' => $column, 'direction' => $direction],
996
                [],
997
                $parameters
998
            );
999
        } else {
1000
            echo get_lang('NoSearchResults');
1001
        }
1002
    }
1003
1004
    /**
1005
     * Displays the statistics of the messages sent and received by each user in the social network.
1006
     *
1007
     * @param string $messageType Type of message: 'sent' or 'received'
1008
     *
1009
     * @return array Message list
1010
     */
1011
    public static function getMessages($messageType)
1012
    {
1013
        $message_table = Database::get_main_table(TABLE_MESSAGE);
1014
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1015
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1016
1017
        $urlId = api_get_current_access_url_id();
1018
1019
        switch ($messageType) {
1020
            case 'sent':
1021
                $field = 'user_sender_id';
1022
                break;
1023
            case 'received':
1024
                $field = 'user_receiver_id';
1025
                break;
1026
        }
1027
1028
        if (api_is_multiple_url_enabled()) {
1029
            $sql = "SELECT lastname, firstname, username, COUNT($field) AS count_message 
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $field does not seem to be defined for all execution paths leading up to this point.
Loading history...
1030
                FROM $access_url_rel_user_table as url, $message_table m 
1031
                LEFT JOIN $user_table u ON m.$field = u.user_id 
1032
                WHERE  url.user_id = m.$field AND  access_url_id='".$urlId."' 
1033
                GROUP BY m.$field 
1034
                ORDER BY count_message DESC ";
1035
        } else {
1036
            $sql = "SELECT lastname, firstname, username, COUNT($field) AS count_message 
1037
                FROM $message_table m 
1038
                LEFT JOIN $user_table u ON m.$field = u.user_id 
1039
                GROUP BY m.$field ORDER BY count_message DESC ";
1040
        }
1041
        $res = Database::query($sql);
1042
        $messages_sent = [];
1043
        while ($messages = Database::fetch_array($res)) {
1044
            if (empty($messages['username'])) {
1045
                $messages['username'] = get_lang('Unknown');
1046
            }
1047
            $users = api_get_person_name(
1048
                $messages['firstname'],
1049
                $messages['lastname']
1050
            ).'<br />('.$messages['username'].')';
1051
            $messages_sent[$users] = $messages['count_message'];
1052
        }
1053
1054
        return $messages_sent;
1055
    }
1056
1057
    /**
1058
     * Count the number of friends for social network users.
1059
     */
1060
    public static function getFriends()
1061
    {
1062
        $user_friend_table = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
1063
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1064
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1065
        $urlId = api_get_current_access_url_id();
1066
1067
        if (api_is_multiple_url_enabled()) {
1068
            $sql = "SELECT lastname, firstname, username, COUNT(friend_user_id) AS count_friend 
1069
                    FROM $access_url_rel_user_table as url, $user_friend_table uf 
1070
                    LEFT JOIN $user_table u 
1071
                    ON (uf.user_id = u.user_id) 
1072
                    WHERE 
1073
                        uf.relation_type <> '".USER_RELATION_TYPE_RRHH."' AND 
1074
                        uf.user_id = url.user_id AND  
1075
                        access_url_id = '".$urlId."' 
1076
                    GROUP BY uf.user_id 
1077
                    ORDER BY count_friend DESC ";
1078
        } else {
1079
            $sql = "SELECT lastname, firstname, username, COUNT(friend_user_id) AS count_friend 
1080
                    FROM $user_friend_table uf 
1081
                    LEFT JOIN $user_table u 
1082
                    ON (uf.user_id = u.user_id) 
1083
                    WHERE uf.relation_type <> '".USER_RELATION_TYPE_RRHH."' 
1084
                    GROUP BY uf.user_id 
1085
                    ORDER BY count_friend DESC ";
1086
        }
1087
        $res = Database::query($sql);
1088
        $list_friends = [];
1089
        while ($friends = Database::fetch_array($res)) {
1090
            $users = api_get_person_name($friends['firstname'], $friends['lastname']).'<br />('.$friends['username'].')';
1091
            $list_friends[$users] = $friends['count_friend'];
1092
        }
1093
1094
        return $list_friends;
1095
    }
1096
1097
    /**
1098
     * Print the number of users that didn't login for a certain period of time.
1099
     */
1100
    public static function printUsersNotLoggedInStats()
1101
    {
1102
        $totalLogin = [];
1103
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1104
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1105
        $urlId = api_get_current_access_url_id();
1106
        $total = self::countUsers();
1107
        if (api_is_multiple_url_enabled()) {
1108
            $table_url = ", $access_url_rel_user_table";
1109
            $where_url = " AND login_user_id=user_id AND access_url_id='".$urlId."'";
1110
        } else {
1111
            $table_url = '';
1112
            $where_url = '';
1113
        }
1114
        $now = api_get_utc_datetime();
1115
        $sql[get_lang('ThisDay')] =
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sql was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sql = array(); before regardless.
Loading history...
1116
            "SELECT count(distinct(login_user_id)) AS number ".
1117
            " FROM $table $table_url ".
1118
            " WHERE DATE_ADD(login_date, INTERVAL 1 DAY) >= '$now' $where_url";
1119
        $sql[get_lang('Last7days')] =
1120
            "SELECT count(distinct(login_user_id)) AS number ".
1121
            " FROM $table $table_url ".
1122
            " WHERE DATE_ADD(login_date, INTERVAL 7 DAY) >= '$now' $where_url";
1123
        $sql[get_lang('Last31days')] =
1124
            "SELECT count(distinct(login_user_id)) AS number ".
1125
            " FROM $table $table_url ".
1126
            " WHERE DATE_ADD(login_date, INTERVAL 31 DAY) >= '$now' $where_url";
1127
        $sql[sprintf(get_lang('LastXMonths'), 6)] =
1128
            "SELECT count(distinct(login_user_id)) AS number ".
1129
            " FROM $table $table_url ".
1130
            " WHERE DATE_ADD(login_date, INTERVAL 6 MONTH) >= '$now' $where_url";
1131
        $sql[get_lang('NeverConnected')] =
1132
            "SELECT count(distinct(login_user_id)) AS number ".
1133
            " FROM $table $table_url WHERE 1=1 $where_url";
1134
        foreach ($sql as $index => $query) {
1135
            $res = Database::query($query);
1136
            $obj = Database::fetch_object($res);
1137
            $r = $total - $obj->number;
1138
            $totalLogin[$index] = $r < 0 ? 0 : $r;
1139
        }
1140
        self::printStats(
1141
            get_lang('StatsUsersDidNotLoginInLastPeriods'),
1142
            $totalLogin,
1143
            false
1144
        );
1145
    }
1146
1147
    /**
1148
     * Returns an array with indexes as the 'yyyy-mm-dd' format of each date
1149
     * within the provided range (including limits). Dates are assumed to be
1150
     * given in UTC.
1151
     *
1152
     * @param string $startDate  Start date, in Y-m-d or Y-m-d h:i:s format
1153
     * @param string $endDate    End date, in Y-m-d or Y-m-d h:i:s format
1154
     * @param bool   $removeYear Whether to remove the year in the results (for easier reading)
1155
     *
1156
     * @return array|bool False on error in the params, array of [date1 => 0, date2 => 0, ...] otherwise
1157
     */
1158
    public static function buildDatesArray($startDate, $endDate, $removeYear = false)
1159
    {
1160
        if (strlen($startDate) > 10) {
1161
            $startDate = substr($startDate, 0, 10);
1162
        }
1163
        if (strlen($endDate) > 10) {
1164
            $endDate = substr($endDate, 0, 10);
1165
        }
1166
        if (!preg_match('/\d\d\d\d-\d\d-\d\d/', $startDate)) {
1167
            return false;
1168
        }
1169
        if (!preg_match('/\d\d\d\d-\d\d-\d\d/', $startDate)) {
1170
            return false;
1171
        }
1172
        $startTimestamp = strtotime($startDate);
1173
        $endTimestamp = strtotime($endDate);
1174
        $list = [];
1175
        for ($time = $startTimestamp; $time < $endTimestamp; $time += 86400) {
1176
            $datetime = api_get_utc_datetime($time);
1177
            if ($removeYear) {
1178
                $datetime = substr($datetime, 5, 5);
1179
            } else {
1180
                $dateTime = substr($datetime, 0, 10);
1181
            }
1182
            $list[$datetime] = 0;
1183
        }
1184
1185
        return $list;
1186
    }
1187
1188
    /**
1189
     * Prepare the JS code to load a chart.
1190
     *
1191
     * @param string $url     URL for AJAX data generator
1192
     * @param string $type    bar, line, pie, etc
1193
     * @param string $options Additional options to the chart (see chart-specific library)
1194
     * @param string A JS code for loading the chart together with a call to AJAX data generator
1195
     */
1196
    public static function getJSChartTemplate($url, $type = 'pie', $options = '', $elementId = 'canvas')
1197
    {
1198
        $chartCode = '
1199
        <script>
1200
        $(function() {
1201
            $.ajax({
1202
                url: "'.$url.'",
1203
                type: "POST",
1204
                success: function(data) {
1205
                    Chart.defaults.global.responsive = true;
1206
                    var ctx = document.getElementById("'.$elementId.'").getContext("2d");
1207
                    var myLoginChart = new Chart(ctx, {
1208
                        type: "'.$type.'",
1209
                        data: data,
1210
                        options: {'.$options.'}
1211
                    });
1212
                }
1213
            });
1214
        });
1215
        </script>';
1216
1217
        return $chartCode;
1218
    }
1219
1220
    /**
1221
     * Display the Logins By Date report and allow export its result to XLS.
1222
     */
1223
    public static function printLoginsByDate()
1224
    {
1225
        if (isset($_GET['export']) && 'xls' === $_GET['export']) {
1226
            $result = self::getLoginsByDate($_GET['start'], $_GET['end']);
1227
            $data = [[get_lang('Username'), get_lang('FirstName'), get_lang('LastName'), get_lang('TotalTime')]];
1228
1229
            foreach ($result as $i => $item) {
1230
                $data[] = [
1231
                    $item['username'],
1232
                    $item['firstname'],
1233
                    $item['lastname'],
1234
                    api_time_to_hms($item['time_count']),
1235
                ];
1236
            }
1237
1238
            Export::arrayToXls($data);
1239
1240
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1241
        }
1242
1243
        echo Display::page_header(get_lang('LoginsByDate'));
1244
1245
        $actions = '';
1246
        $content = '';
1247
1248
        $form = new FormValidator('frm_logins_by_date', 'get');
1249
        $form->addDateRangePicker(
1250
            'daterange',
1251
            get_lang('DateRange'),
1252
            true,
1253
            ['format' => 'YYYY-MM-DD', 'timePicker' => 'false', 'validate_format' => 'Y-m-d']
1254
        );
1255
        $form->addHidden('report', 'logins_by_date');
1256
        $form->addButtonFilter(get_lang('Search'));
1257
1258
        if ($form->validate()) {
1259
            $values = $form->exportValues();
1260
1261
            $result = self::getLoginsByDate($values['daterange_start'], $values['daterange_end']);
1262
1263
            if (!empty($result)) {
1264
                $actions = Display::url(
1265
                    Display::return_icon('excel.png', get_lang('ExportToXls'), [], ICON_SIZE_MEDIUM),
1266
                    api_get_self().'?'.http_build_query(
1267
                        [
1268
                            'report' => 'logins_by_date',
1269
                            'export' => 'xls',
1270
                            'start' => Security::remove_XSS($values['daterange_start']),
1271
                            'end' => Security::remove_XSS($values['daterange_end']),
1272
                        ]
1273
                    )
1274
                );
1275
            }
1276
1277
            $table = new HTML_Table(['class' => 'data_table']);
1278
            $table->setHeaderContents(0, 0, get_lang('Username'));
1279
            $table->setHeaderContents(0, 1, get_lang('FirstName'));
1280
            $table->setHeaderContents(0, 2, get_lang('LastName'));
1281
            $table->setHeaderContents(0, 3, get_lang('TotalTime'));
1282
1283
            foreach ($result as $i => $item) {
1284
                $table->setCellContents($i + 1, 0, $item['username']);
1285
                $table->setCellContents($i + 1, 1, $item['firstname']);
1286
                $table->setCellContents($i + 1, 2, $item['lastname']);
1287
                $table->setCellContents($i + 1, 3, api_time_to_hms($item['time_count']));
1288
            }
1289
1290
            $table->setColAttributes(0, ['class' => 'text-center']);
1291
            $table->setColAttributes(3, ['class' => 'text-center']);
1292
            $content = $table->toHtml();
1293
        }
1294
1295
        $form->display();
1296
1297
        if (!empty($actions)) {
1298
            echo  Display::toolbarAction('logins_by_date_toolbar', [$actions]);
1299
        }
1300
1301
        echo $content;
1302
    }
1303
1304
    /**
1305
     * @param string $startDate
1306
     * @param string $endDate
1307
     *
1308
     * @return array
1309
     */
1310
    private static function getLoginsByDate($startDate, $endDate)
1311
    {
1312
        /** @var DateTime $startDate */
1313
        $startDate = api_get_utc_datetime("$startDate 00:00:00");
1314
        /** @var DateTime $endDate */
1315
        $endDate = api_get_utc_datetime("$endDate 23:59:59");
1316
1317
        if (empty($startDate) || empty($endDate)) {
1318
            return [];
1319
        }
1320
1321
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
1322
        $tblLogin = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1323
        $urlJoin = '';
1324
        $urlWhere = '';
1325
1326
        if (api_is_multiple_url_enabled()) {
1327
            $tblUrlUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1328
1329
            $urlJoin = "INNER JOIN $tblUrlUser au ON u.id = au.user_id";
1330
            $urlWhere = 'AND au.access_url_id = '.api_get_current_access_url_id();
1331
        }
1332
1333
        $sql = "SELECT u.id,
1334
                    u.firstname,
1335
                    u.lastname,
1336
                    u.username,
1337
                    SUM(TIMESTAMPDIFF(SECOND, l.login_date, l.logout_date)) AS time_count
1338
                FROM $tblUser u
1339
                INNER JOIN $tblLogin l ON u.id = l.login_user_id
1340
                $urlJoin
1341
                WHERE l.login_date BETWEEN '$startDate' AND '$endDate'
1342
                $urlWhere
1343
                GROUP BY u.id";
1344
1345
        $stmt = Database::query($sql);
1346
        $result = Database::store_result($stmt, 'ASSOC');
1347
1348
        return $result;
1349
    }
1350
}
1351