Issues (15)

Security Analysis    no vulnerabilities found

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

sources/logs.datatables.php (1 issue)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Teampass - a collaborative passwords manager.
7
 * ---
8
 * This file is part of the TeamPass project.
9
 * 
10
 * TeamPass is free software: you can redistribute it and/or modify it
11
 * under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, version 3 of the License.
13
 * 
14
 * TeamPass is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU General Public License for more details.
18
 * 
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21
 * 
22
 * Certain components of this file may be under different licenses. For
23
 * details, see the `licenses` directory or individual file headers.
24
 * ---
25
 * @file      logs.datatables.php
26
 * @author    Nils Laumaillé ([email protected])
27
 * @copyright 2009-2025 Teampass.net
28
 * @license   GPL-3.0
29
 * @see       https://www.teampass.net
30
 */
31
32
use TeampassClasses\SessionManager\SessionManager;
33
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
34
use TeampassClasses\Language\Language;
35
use EZimuel\PHPSecureSession;
36
use TeampassClasses\PerformChecks\PerformChecks;
37
use TeampassClasses\ConfigManager\ConfigManager;
38
use TeampassClasses\NestedTree\NestedTree;
39
use voku\helper\AntiXSS;
40
41
// Load functions
42
require_once 'main.functions.php';
43
44
// init
45
loadClasses('DB');
46
$session = SessionManager::getSession();
47
$request = SymfonyRequest::createFromGlobals();
48
$lang = new Language($session->get('user-language') ?? 'english');
49
$antiXss = new AntiXSS();
50
51
// Load config
52
$configManager = new ConfigManager();
53
$SETTINGS = $configManager->getAllSettings();
54
55
// Do checks
56
// Instantiate the class with posted data
57
$checkUserAccess = new PerformChecks(
58
    dataSanitizer(
59
        [
60
            'type' => htmlspecialchars($request->request->get('type', ''), ENT_QUOTES, 'UTF-8'),
61
        ],
62
        [
63
            'type' => 'trim|escape',
64
        ],
65
    ),
66
    [
67
        'user_id' => returnIfSet($session->get('user-id'), null),
68
        'user_key' => returnIfSet($session->get('key'), null),
69
    ]
70
);
71
// Handle the case
72
echo $checkUserAccess->caseHandler();
73
if (
74
    $checkUserAccess->userAccessPage('utilities.logs') === false ||
75
    $checkUserAccess->checkSession() === false
76
) {
77
    // Not allowed page
78
    $session->set('system-error_code', ERR_NOT_ALLOWED);
79
    include $SETTINGS['cpassman_dir'] . '/error.php';
80
    exit;
81
}
82
83
// Define Timezone
84
date_default_timezone_set($SETTINGS['timezone'] ?? 'UTC');
85
86
// Set header properties
87
header('Content-type: text/html; charset=utf-8');
88
header('Cache-Control: no-cache, no-store, must-revalidate');
89
90
// --------------------------------- //
91
92
// Configure AntiXSS to keep double-quotes
93
$antiXss->removeEvilAttributes(['style', 'onclick', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'onmousemove', 'onkeydown', 'onkeyup', 'onkeypress', 'onchange', 'onblur', 'onfocus', 'onabort', 'onerror', 'onscroll']);
94
$antiXss->removeEvilHtmlTags(['script', 'iframe', 'embed', 'object', 'applet', 'link', 'style']);
95
96
// Load tree
97
$tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
98
99
// Get the data
100
$params = $request->query->all();
101
102
// Init
103
$searchValue = $sWhere = $sOrder = $sOutput = '';
104
$aSortTypes = ['ASC', 'DESC'];
105
$sLimitStart = $request->query->has('start') 
106
    ? $request->query->filter('start', 0, FILTER_VALIDATE_INT, ['options' => ['default' => 0, 'min_range' => 0]]) 
107
    : 0;
108
$sLimitLength = $request->query->has('length') 
109
    ? $request->query->filter('length', 0, FILTER_VALIDATE_INT, ['options' => ['default' => 0, 'min_range' => 0]]) 
110
    : 10;
111
112
// Check search parameters
113
if (isset($params['search']['value'])) {
114
    // Case 1: search[value]
115
    $searchValue = (string) $params['search']['value'];
116
} elseif (isset($params['sSearch'])) {
117
    // Case 2: sSearch
118
    $searchValue = (string) $params['sSearch'];
119
}
120
121
// Ordering
122
$order = strtoupper($params['order'][0]['dir'] ?? null);
0 ignored issues
show
It seems like $params['order'][0]['dir'] ?? null can also be of type null; however, parameter $string of strtoupper() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

122
$order = strtoupper(/** @scrutinizer ignore-type */ $params['order'][0]['dir'] ?? null);
Loading history...
123
$orderDirection = in_array($order, $aSortTypes, true) ? $order : 'DESC';
124
    
125
// Start building the query and output depending on the action
126
if (isset($params['action']) && $params['action'] === 'connections') {
127
    //Columns name
128
    $aColumns = ['l.date', 'l.label', 'l.qui', 'u.login', 'u.name', 'u.lastname'];
129
130
    // Ordering
131
    $orderColumn = $aColumns[0];
132
    if (isset($aColumns[$params['order'][0]['column']]) === true) {
133
        $orderColumn = $aColumns[$params['order'][0]['column']];
134
    }
135
    
136
    // Filtering
137
    $sWhere = new WhereClause('AND');
138
    if ($searchValue !== '') {        
139
        $subclause = $sWhere->addClause('OR');
140
        foreach ($aColumns as $column) {
141
            $subclause->add($column.' LIKE %ss', $searchValue);
142
        }
143
    }
144
    $sWhere->add('l.type = %s', 'user_connection');
145
146
    // Get the total number of records
147
    $iTotal = DB::queryFirstField(
148
        'SELECT COUNT(*)
149
        FROM '.prefixTable('log_system').' as l
150
        INNER JOIN '.prefixTable('users').' as u ON (l.qui=u.id) 
151
        WHERE %l ORDER BY %l %l',
152
        $sWhere,
153
        $orderColumn,
154
        $orderDirection
155
    );
156
157
    // Prepare the SQL query
158
    $sql = 'SELECT l.date as date, l.label as label, l.qui as who, 
159
    u.login as login, u.name AS name, u.lastname AS lastname
160
    FROM '.prefixTable('log_system').' as l
161
    INNER JOIN '.prefixTable('users').' as u ON (l.qui=u.id)
162
    WHERE %l ORDER BY %l %l LIMIT %i, %i';
163
    $params = [$sWhere, $orderColumn, $orderDirection, $sLimitStart, $sLimitLength];
164
165
    // Get the records
166
    $rows = DB::query($sql, ...$params);
167
    $iFilteredTotal = DB::count();
168
    
169
    // Output
170
    $sOutput = '{';
171
    $sOutput .= '"sEcho": '. $request->query->filter('draw', FILTER_SANITIZE_NUMBER_INT) . ', ';
172
    $sOutput .= '"iTotalRecords": '.$iTotal.', ';
173
    $sOutput .= '"iTotalDisplayRecords": '.$iTotal.', ';
174
    $sOutput .= '"aaData": ';
175
    if ($iFilteredTotal > 0) {
176
        $sOutput .= '[';
177
    }
178
    foreach ($rows as $record) {
179
        $sOutput .= '[';
180
        //col1
181
        $sOutput .= '"'.date($SETTINGS['date_format'].' '.$SETTINGS['time_format'], (int) $record['date']).'", ';
182
        //col2
183
        $sOutput .= '"'.str_replace([chr(10), chr(13)], [' ', ' '], htmlspecialchars(stripslashes((string) $record['label']), ENT_QUOTES)).'", ';
184
        //col3
185
        $sOutput .= '"'.htmlspecialchars(stripslashes((string) $record['name']), ENT_QUOTES).' '.htmlspecialchars(stripslashes((string) $record['lastname']), ENT_QUOTES).' ['.htmlspecialchars(stripslashes((string) $record['login']), ENT_QUOTES).']"';
186
        //Finish the line
187
        $sOutput .= '],';
188
    }
189
190
    if (count($rows) > 0) {
191
        $sOutput = substr_replace($sOutput, '', -1);
192
        $sOutput .= '] }';
193
    } else {
194
        $sOutput .= '[] }';
195
    }
196
197
    /* ERRORS LOG */
198
} elseif (isset($params['action']) && $params['action'] === 'access') {
199
    //Columns name
200
    $aColumns = ['l.date', 'i.label', 'u.login'];
201
202
    // Ordering
203
    $orderColumn = $aColumns[0];
204
    if (isset($aColumns[$params['order'][0]['column']]) === true) {
205
        $orderColumn = $aColumns[$params['order'][0]['column']];
206
    }
207
208
    // Filtering
209
    $sWhere = new WhereClause('AND');
210
    if ($searchValue !== '') {        
211
        $subclause = $sWhere->addClause('OR');
212
        foreach ($aColumns as $column) {
213
            $subclause->add($column.' LIKE %ss', $searchValue);
214
        }
215
    }
216
    $sWhere->add('l.action = %s', 'at_shown');
217
218
    // Get the total number of records
219
    $iTotal = DB::queryFirstField(
220
        'SELECT COUNT(*)
221
        FROM '.prefixTable('log_items').' as l
222
        INNER JOIN '.prefixTable('items').' as i ON (l.id_item=i.id)
223
        INNER JOIN '.prefixTable('users').' as u ON (l.id_user=u.id)
224
        WHERE %l ORDER BY %l %l',
225
        $sWhere,
226
        $orderColumn,
227
        $orderDirection
228
    );
229
230
    // Prepare the SQL query
231
    $sql = 'SELECT l.date as date, u.login as login, i.label as label
232
    FROM '.prefixTable('log_items').' as l
233
    INNER JOIN '.prefixTable('items').' as i ON (l.id_item=i.id)
234
    INNER JOIN '.prefixTable('users').' as u ON (l.id_user=u.id)
235
    WHERE %l ORDER BY %l %l LIMIT %i, %i';
236
    $params = [$sWhere, $orderColumn, $orderDirection, $sLimitStart, $sLimitLength];
237
238
    // Get the records
239
    $rows = DB::query($sql, ...$params);
240
    $iFilteredTotal = DB::count();
241
242
    // Output
243
    $sOutput = '{';
244
    $sOutput .= '"sEcho": '. $request->query->filter('draw', FILTER_SANITIZE_NUMBER_INT) . ', ';
245
    $sOutput .= '"iTotalRecords": '.$iTotal.', ';
246
    $sOutput .= '"iTotalDisplayRecords": '.$iTotal.', ';
247
    $sOutput .= '"aaData": ';
248
    if ($iFilteredTotal > 0) {
249
        $sOutput .= '[';
250
    }
251
    foreach ($rows as $record) {
252
        $sOutput .= '[';
253
        //col1
254
        $sOutput .= '"'.date($SETTINGS['date_format'].' '.$SETTINGS['time_format'], (int) $record['date']).'", ';
255
        //col2
256
        $sOutput .= '"'.str_replace([chr(10), chr(13)], [' ', ' '], htmlspecialchars(stripslashes((string) $record['label']), ENT_QUOTES)).'", ';
257
        //col3
258
        $sOutput .= '"'.htmlspecialchars(stripslashes((string) $record['login']), ENT_QUOTES).'"';
259
        //Finish the line
260
        $sOutput .= '],';
261
    }
262
263
    if (count($rows) > 0) {
264
        $sOutput = substr_replace($sOutput, '', -1);
265
        $sOutput .= '] }';
266
    } else {
267
        $sOutput .= '[] }';
268
    }
269
270
    /* COPY LOG */
271
} elseif (isset($params['action']) && $params['action'] === 'copy') {
272
    //Columns name
273
    $aColumns = ['l.date', 'i.label', 'u.login'];
274
275
    // Ordering
276
    $orderColumn = $aColumns[0];
277
    if (isset($aColumns[$params['order'][0]['column']]) === true) {
278
        $orderColumn = $aColumns[$params['order'][0]['column']];
279
    }
280
    
281
    // Filtering
282
    $sWhere = new WhereClause('AND');
283
    if ($searchValue !== '') {        
284
        $subclause = $sWhere->addClause('OR');
285
        foreach ($aColumns as $column) {
286
            $subclause->add($column.' LIKE %ss', $searchValue);
287
        }
288
    }
289
    $sWhere->add('l.action = %s', 'at_copy');
290
291
    // Get the total number of records
292
    $iTotal = DB::queryFirstField(
293
        'SELECT COUNT(*)
294
        FROM '.prefixTable('log_items').' as l
295
        INNER JOIN '.prefixTable('items').' as i ON (l.id_item=i.id)
296
        INNER JOIN '.prefixTable('users').' as u ON (l.id_user=u.id)
297
        WHERE %l ORDER BY %l %l',
298
        $sWhere,
299
        $orderColumn,
300
        $orderDirection
301
    );
302
303
    // Prepare the SQL query
304
    $sql = 'SELECT l.date as date, u.login as login, u.name AS name, u.lastname AS lastname, i.label as label
305
    FROM '.prefixTable('log_items').' as l
306
    INNER JOIN '.prefixTable('items').' as i ON (l.id_item=i.id)
307
    INNER JOIN '.prefixTable('users').' as u ON (l.id_user=u.id)
308
    WHERE %l ORDER BY %l %l LIMIT %i, %i';
309
    $params = [$sWhere, $orderColumn, $orderDirection, $sLimitStart, $sLimitLength];
310
311
    // Get the records
312
    $rows = DB::query($sql, ...$params);
313
    $iFilteredTotal = DB::count();
314
315
    // Output
316
    $sOutput = '{';
317
    $sOutput .= '"sEcho": '. (int) $request->query->filter('draw', FILTER_SANITIZE_NUMBER_INT) . ', ';
318
    $sOutput .= '"iTotalRecords": '.$iTotal.', ';
319
    $sOutput .= '"iTotalDisplayRecords": '.$iTotal.', ';
320
    $sOutput .= '"aaData": ';
321
    if ($iFilteredTotal > 0) {
322
        $sOutput .= '[';
323
    }
324
    foreach ($rows as $record) {
325
        $sOutput .= '[';
326
        //col1
327
        $sOutput .= '"'.date($SETTINGS['date_format'].' '.$SETTINGS['time_format'], (int) $record['date']).'", ';
328
        //col2
329
        $sOutput .= '"'.trim(htmlspecialchars(stripslashes((string) $record['label']), ENT_QUOTES)).'", ';
330
        //col3
331
        $sOutput .= '"'.trim(htmlspecialchars(stripslashes((string) $record['login']), ENT_QUOTES)).'"';
332
        //Finish the line
333
        $sOutput .= '],';
334
    }
335
336
    if (count($rows) > 0) {
337
        $sOutput = substr_replace($sOutput, '', -1);
338
        $sOutput .= '] }';
339
    } else {
340
        $sOutput .= '[] }';
341
    }
342
343
    /*
344
    * ADMIN LOG
345
     */
346
} elseif (isset($params['action']) && $params['action'] === 'admin') {
347
    //Columns name
348
    $aColumns = ['l.date', 'u.login', 'l.label', 'l.field_1'];
349
350
    // Ordering
351
    $orderColumn = $aColumns[0];
352
    if (isset($aColumns[$params['order'][0]['column']]) === true) {
353
        $orderColumn = $aColumns[$params['order'][0]['column']];
354
    }
355
356
    // Filtering
357
    $sWhere = new WhereClause('AND');
358
    if ($searchValue !== '') {        
359
        $subclause = $sWhere->addClause('OR');
360
        foreach ($aColumns as $column) {
361
            $subclause->add($column.' LIKE %ss', $searchValue);
362
        }
363
    }
364
    $sWhere->add('l.type IN %ls', ['admin_action', 'user_mngt']);
365
366
    // Get the total number of records
367
    $iTotal = DB::queryFirstField(
368
        'SELECT COUNT(*)
369
        FROM '.prefixTable('log_system').' as l
370
        INNER JOIN '.prefixTable('users').' as u ON (l.qui=u.id)
371
        WHERE %l ORDER BY %l %l',
372
        $sWhere,
373
        $orderColumn,
374
        $orderDirection
375
    );
376
377
    // Prepare the SQL query
378
    $sql = 'SELECT l.date as date, u.login as login, u.name AS name, u.lastname AS lastname, l.label as label, l.field_1 as field_1
379
    FROM '.prefixTable('log_system').' as l
380
    INNER JOIN '.prefixTable('users').' as u ON (l.qui=u.id)
381
    WHERE %l ORDER BY %l %l LIMIT %i, %i';
382
    $params = [$sWhere, $orderColumn, $orderDirection, $sLimitStart, $sLimitLength];
383
384
    // Get the records
385
    $rows = DB::query($sql, ...$params);    
386
    $iFilteredTotal = DB::count();
387
388
    // Output
389
    $sOutput = '{';
390
    $sOutput .= '"sEcho": '. (int) $request->query->filter('draw', FILTER_SANITIZE_NUMBER_INT) . ', ';
391
    $sOutput .= '"iTotalRecords": '.$iTotal.', ';
392
    $sOutput .= '"iTotalDisplayRecords": '.$iTotal.', ';
393
    $sOutput .= '"aaData": [ ';
394
    foreach ($rows as $record) {
395
        $get_item_in_list = true;
396
        $sOutput_item = '[';
397
        //col1
398
        $sOutput_item .= '"'.date($SETTINGS['date_format'].' '.$SETTINGS['time_format'], (int) $record['date']).'", ';
399
        //col2
400
        $sOutput_item .= '"'.htmlspecialchars(stripslashes((string) $record['login']), ENT_QUOTES).'", ';
401
        //col3
402
        if ($record['label'] === 'at_user_added') {
403
            $cell = $lang->get('user_creation');
404
        } elseif ($record['label'] === 'at_user_deleted' || $record['label'] === 'user_deleted') {
405
            $cell = $lang->get('user_deletion');
406
        } elseif ($record['label'] === 'at_user_updated') {
407
            $cell = $lang->get('user_updated');
408
        } elseif (strpos($record['label'], 'at_user_email_changed') !== false) {
409
            $change = explode(':', $record['label']);
410
            $cell = $lang->get('log_user_email_changed').' '.$change[1];
411
        } elseif ($record['label'] === 'at_user_new_keys') {
412
            $cell = $lang->get('new_keys_generated');
413
        } elseif ($record['label'] === 'at_user_keys_download') {
414
            $cell = $lang->get('user_keys_downloaded');
415
        } elseif ($record['label'] === 'at_2fa_google_code_send_by_email') {
416
            $cell = $lang->get('mfa_code_send_by_email');
417
        } else {
418
            $cell = htmlspecialchars(stripslashes((string) $record['label']), ENT_QUOTES);
419
        }
420
        $sOutput_item .= '"'.$cell.'" ';
421
        //col4
422
        if (empty($record['field_1']) === false) {
423
            // get user name
424
            $info = DB::queryFirstRow(
425
                'SELECT u.login as login, u.name AS name, u.lastname AS lastname
426
                    FROM '.prefixTable('users').' as u
427
                    WHERE u.id = %i',
428
                    $record['field_1']
429
            );
430
            $sOutput_item .= ', "'.(empty($info['name']) === false ? htmlspecialchars(stripslashes((string) $info['name'].' '.$info['lastname']), ENT_QUOTES) : 'Removed user ('.$record['field_1'].')').'" ';
431
        } else {
432
            $sOutput_item .= ', "" ';
433
        }
434
        //Finish the line
435
        $sOutput_item .= '], ';
436
        if ($get_item_in_list === true) {
437
            $sOutput .= $sOutput_item;
438
        }
439
    }
440
    if ($iFilteredTotal > 0) {
441
        $sOutput = substr_replace($sOutput, '', -2);
442
    }
443
    $sOutput .= '] }';
444
/* ITEMS */
445
} elseif (isset($params['action']) && $params['action'] === 'items') {
446
    require_once $SETTINGS['cpassman_dir'].'/sources/main.functions.php';
447
    //Columns name
448
    $aColumns = ['l.date', 'i.label', 'u.login', 'l.action', 'i.perso', 'i.id', 't.title'];
449
450
    // Ordering
451
    $orderColumn = $aColumns[0];
452
    if (isset($aColumns[$params['order'][0]['column']]) === true) {
453
        $orderColumn = $aColumns[$params['order'][0]['column']];
454
    }
455
456
    // Filtering
457
    $sWhere = new WhereClause('OR');
458
    if ($searchValue !== '') {        
459
        foreach ($aColumns as $column) {
460
            $sWhere->add($column.' LIKE %ss', $searchValue);
461
        }
462
    }
463
464
    // Get the total number of records
465
    $iTotal = DB::queryFirstField(
466
        'SELECT COUNT(*)
467
        FROM '.prefixTable('log_items').' AS l
468
        INNER JOIN '.prefixTable('items').' AS i ON (l.id_item=i.id)
469
        INNER JOIN '.prefixTable('users').' AS u ON (l.id_user=u.id)
470
        INNER JOIN '.prefixTable('nested_tree').' AS t ON (i.id_tree=t.id)
471
        WHERE %l ORDER BY %l %l',
472
        $sWhere,
473
        $orderColumn,
474
        $orderDirection
475
    );
476
477
    // Prepare the SQL query
478
    $sql = 'SELECT l.date AS date, u.login AS login, u.name AS name, u.lastname AS lastname, i.label AS label,
479
    i.perso AS perso, l.action AS action, t.title AS folder, i.id AS id
480
    FROM '.prefixTable('log_items').' AS l
481
    INNER JOIN '.prefixTable('items').' AS i ON (l.id_item=i.id)
482
    INNER JOIN '.prefixTable('users').' AS u ON (l.id_user=u.id)
483
    INNER JOIN '.prefixTable('nested_tree').' AS t ON (i.id_tree=t.id)
484
    WHERE %l ORDER BY %l %l LIMIT %i, %i';
485
    $params = [$sWhere, $orderColumn, $orderDirection, $sLimitStart, $sLimitLength];
486
487
    // Get the records
488
    $rows = DB::query($sql, ...$params);
489
    $iFilteredTotal = DB::count();
490
491
    // Output
492
    $sOutput = '{';
493
    $sOutput .= '"sEcho": '. (int) $request->query->filter('draw', FILTER_SANITIZE_NUMBER_INT) . ', ';
494
    $sOutput .= '"iTotalRecords": '.$iTotal.', ';
495
    $sOutput .= '"iTotalDisplayRecords": '.$iTotal.', ';
496
    $sOutput .= '"aaData": [ ';
497
    foreach ($rows as $record) {
498
        $get_item_in_list = true;
499
        $sOutput_item = '[';
500
        //col1
501
        $sOutput_item .= '"'.date($SETTINGS['date_format'].' '.$SETTINGS['time_format'], (int) $record['date']).'", ';
502
        //col3
503
        $sOutput_item .= '"'.trim(htmlspecialchars(stripslashes((string) $record['id']), ENT_QUOTES)).'", ';
504
        //col3
505
        $sOutput_item .= '"'.trim(htmlspecialchars(stripslashes((string) $record['label']), ENT_QUOTES)).'", ';
506
        //col2
507
        $sOutput_item .= '"'.trim(htmlspecialchars(stripslashes((string) $record['folder']), ENT_QUOTES)).'", ';
508
        //col2
509
        $sOutput_item .= '"'.trim(htmlspecialchars(stripslashes((string) $record['name']), ENT_QUOTES)).' '.trim(htmlspecialchars(stripslashes((string) $record['lastname']), ENT_QUOTES)).' ['.trim(htmlspecialchars(stripslashes((string) $record['login']), ENT_QUOTES)).']", ';
510
        //col4
511
        $sOutput_item .= '"'.trim(htmlspecialchars(stripslashes($lang->get($record['action'])), ENT_QUOTES)).'", ';
512
        //col5
513
        if ($record['perso'] === 1) {
514
            $sOutput_item .= '"'.trim(htmlspecialchars(stripslashes($lang->get('yes')), ENT_QUOTES)).'"';
515
        } else {
516
            $sOutput_item .= '"'.trim(htmlspecialchars(stripslashes($lang->get('no')), ENT_QUOTES)).'"';
517
        }
518
519
        //Finish the line
520
        $sOutput_item .= '], ';
521
        if ($get_item_in_list === true) {
522
            $sOutput .= $sOutput_item;
523
        }
524
    }
525
    if ($iFilteredTotal > 0) {
526
        $sOutput = substr_replace($sOutput, '', -2);
527
    }
528
    $sOutput .= '] }';
529
/* FAILED AUTHENTICATION */
530
} elseif (isset($params['action']) && $params['action'] === 'failed_auth') {
531
    //Columns name
532
    $aColumns = ['l.date', 'l.label', 'l.qui', 'l.field_1'];
533
534
    // Ordering
535
    $orderColumn = $aColumns[0];
536
    if (isset($aColumns[$params['order'][0]['column']]) === true) {
537
        $orderColumn = $aColumns[$params['order'][0]['column']];
538
    }
539
540
    // Filtering
541
    $sWhere = new WhereClause('AND');
542
    if ($searchValue !== '') {        
543
        $subclause = $sWhere->addClause('OR');
544
        foreach ($aColumns as $column) {
545
            $subclause->add($column.' LIKE %ss', $searchValue);
546
        }
547
    }
548
    $sWhere->add('l.type = %s', 'failed_auth');
549
550
    // Get the total number of records
551
    $iTotal = DB::queryFirstField(
552
        'SELECT COUNT(*)
553
        FROM '.prefixTable('log_system').' as l
554
        WHERE %l ORDER BY %l %l',
555
        $sWhere,
556
        $orderColumn,
557
        $orderDirection
558
    );
559
560
    // Prepare the SQL query
561
    $sql = 'SELECT l.date as auth_date, l.label as label, l.qui as who, l.field_1
562
    FROM '.prefixTable('log_system').' as l
563
    WHERE %l ORDER BY %l %l LIMIT %i, %i';
564
    $params = [$sWhere, $orderColumn, $orderDirection, $sLimitStart, $sLimitLength];
565
566
    // Get the records
567
    $rows = DB::query($sql, ...$params);
568
    $iFilteredTotal = DB::count(); 
569
570
    // Output
571
    if ($iTotal === '') {
572
        $iTotal = 0;
573
    }
574
    $sOutput = '{';
575
    $sOutput .= '"sEcho": '. (int) $request->query->filter('draw', FILTER_SANITIZE_NUMBER_INT) . ', ';
576
    $sOutput .= '"iTotalRecords": '.$iTotal.', ';
577
    $sOutput .= '"iTotalDisplayRecords": '.$iTotal.', ';
578
    $sOutput .= '"aaData": ';
579
    if ($iFilteredTotal > 0) {
580
        $sOutput .= '[';
581
    }
582
    foreach ($rows as $record) {
583
        $sOutput .= '[';
584
        //col1
585
        $sOutput .= '"'.date($SETTINGS['date_format'].' '.$SETTINGS['time_format'], (int) $record['auth_date']).'", ';
586
        //col2 - 3
587
        if ($record['label'] === 'password_is_not_correct' || $record['label'] === 'user_not_exists') {
588
            $sOutput .= '"'.$lang->get($record['label']).'", "'.$record['field_1'].'", ';
589
        } else {
590
            $sOutput .= '"'.$lang->get($record['label']).'", "", ';
591
        }
592
593
        //col3
594
        $sOutput .= '"'.htmlspecialchars(stripslashes((string) $record['who']), ENT_QUOTES).'"';
595
        //Finish the line
596
        $sOutput .= '],';
597
    }
598
599
    if (count($rows) > 0) {
600
        $sOutput = substr_replace($sOutput, '', -1);
601
        $sOutput .= '] }';
602
    } else {
603
        $sOutput .= '[] }';
604
    }
605
} elseif (isset($params['action']) && $params['action'] === 'errors') {
606
    //Columns name
607
    $aColumns = ['l.date', 'l.label', 'l.qui', 'u.login', 'u.name', 'u.lastname'];
608
609
    // Ordering
610
    $orderColumn = $aColumns[0];
611
    if (isset($aColumns[$params['order'][0]['column']]) === true) {
612
        $orderColumn = $aColumns[$params['order'][0]['column']];
613
    }
614
615
    // Filtering
616
    $sWhere = new WhereClause('AND');
617
    if ($searchValue !== '') {        
618
        $subclause = $sWhere->addClause('OR');
619
        foreach ($aColumns as $column) {
620
            $subclause->add($column.' LIKE %ss', $searchValue);
621
        }
622
    }
623
    $sWhere->add('l.type = %s', 'error');
624
625
    // Get the total number of records
626
    $iTotal = DB::queryFirstField(
627
        'SELECT COUNT(*)
628
            FROM '.prefixTable('log_system').' as l
629
            INNER JOIN '.prefixTable('users').' as u ON (l.qui=u.id) 
630
            WHERE %l ORDER BY %l %l',
631
            $sWhere,
632
            $orderColumn,
633
            $orderDirection
634
    );
635
    $iTotal = DB::count();
636
637
    // Prepare the SQL query
638
    $sql = 'SELECT l.date as date, l.label as label, l.qui as who,
639
    u.login as login, u.name AS name, u.lastname AS lastname
640
    FROM '.prefixTable('log_system').' as l
641
    INNER JOIN '.prefixTable('users').' as u ON (l.qui=u.id) 
642
    WHERE %l ORDER BY %l %l LIMIT %i, %i';
643
    $params = [$sWhere, $orderColumn, $orderDirection, $sLimitStart, $sLimitLength];
644
645
    // Get the records
646
    $rows = DB::query($sql, ...$params);
647
    $iFilteredTotal = DB::count();
648
649
    // Output
650
    $sOutput = '{';
651
    $sOutput .= '"sEcho": '. (int) $request->query->filter('draw', FILTER_SANITIZE_NUMBER_INT) . ', ';
652
    $sOutput .= '"iTotalRecords": '.$iTotal.', ';
653
    $sOutput .= '"iTotalDisplayRecords": '.$iTotal.', ';
654
    $sOutput .= '"aaData": ';
655
    if ($iFilteredTotal > 0) {
656
        $sOutput .= '[';
657
    }
658
    foreach ($rows as $record) {
659
        $sOutput .= '[';
660
        //col1
661
        $sOutput .= '"'.date($SETTINGS['date_format'].' '.$SETTINGS['time_format'], (int) $record['date']).'", ';
662
        //col2
663
        $sOutput .= '"'.addslashes(str_replace([chr(10), chr(13), '`', '<br />@', "'"], ['<br>', '<br>', "'", '', '&#39;'], $record['label'])).'", ';
664
        //col3
665
        $sOutput .= '"'.htmlspecialchars(stripslashes((string) $record['name']), ENT_QUOTES).' '.htmlspecialchars(stripslashes((string) $record['lastname']), ENT_QUOTES).' ['.htmlspecialchars(stripslashes((string) $record['login']), ENT_QUOTES).']"';
666
        //Finish the line
667
        $sOutput .= '],';
668
    }
669
670
    if (count($rows) > 0) {
671
        $sOutput = substr_replace($sOutput, '', -1);
672
        $sOutput .= '] }';
673
    } else {
674
        $sOutput .= '[] }';
675
    }
676
} elseif (isset($params['action']) && $params['action'] === 'items_in_edition') {
677
    //Columns name
678
    $aColumns = ['e.timestamp', 'u.login', 'i.label', 'u.name', 'u.lastname'];
679
680
    // Ordering
681
    $orderColumn = $aColumns[0];
682
    if (isset($aColumns[$params['order'][0]['column']]) === true) {
683
        $orderColumn = $aColumns[$params['order'][0]['column']];
684
    }
685
686
    // Filtering
687
    $sWhere = new WhereClause('OR');
688
    if ($searchValue !== '') {        
689
        foreach ($aColumns as $column) {
690
            $sWhere->add($column.' LIKE %ss', $searchValue);
691
        }
692
    }
693
694
    // Get the total number of records
695
    $iTotal = DB::queryFirstField(
696
        'SELECT COUNT(*)
697
        FROM '.prefixTable('items_edition').' AS e
698
        INNER JOIN '.prefixTable('items').' as i ON (e.item_id=i.id)
699
        INNER JOIN '.prefixTable('users').' as u ON (e.user_id=u.id)
700
        WHERE %l ORDER BY %l %l',
701
        $sWhere,
702
        $orderColumn,
703
        $orderDirection
704
    );
705
706
    // Prepare the SQL query
707
    $sql = 'SELECT e.timestamp, e.item_id, e.user_id, u.login, u.name, u.lastname, i.label
708
    FROM '.prefixTable('items_edition').' AS e
709
    INNER JOIN '.prefixTable('items').' as i ON (e.item_id=i.id)
710
    INNER JOIN '.prefixTable('users').' as u ON (e.user_id=u.id)
711
    WHERE %l ORDER BY %l %l LIMIT %i, %i';
712
    $params = [$sWhere, $orderColumn, $orderDirection, $sLimitStart, $sLimitLength];
713
714
    // Get the records
715
    $rows = DB::query($sql, ...$params);
716
    $iFilteredTotal = DB::count();
717
718
    // Output
719
    $sOutput = '{';
720
    $sOutput .= '"sEcho": '. (int) $request->query->filter('draw', FILTER_SANITIZE_NUMBER_INT) . ', ';
721
    $sOutput .= '"iTotalRecords": '.$iTotal.', ';
722
    $sOutput .= '"iTotalDisplayRecords": '.$iTotal.', ';
723
    $sOutput .= '"aaData": ';
724
    if ($iFilteredTotal > 0) {
725
        $sOutput .= '[';
726
    }
727
    foreach ($rows as $record) {
728
        $sOutput .= '[';
729
        //col1
730
        $sOutput .= '"<span data-id=\"'.$record['item_id'].'\">", ';
731
        //col2
732
        $time_diff = intval(time() - $record['timestamp']);
733
        $hoursDiff = round($time_diff / 3600, 0, PHP_ROUND_HALF_DOWN);
734
        $minutesDiffRemainder = floor($time_diff % 3600 / 60);
735
        $sOutput .= '"'.$hoursDiff.'h '.$minutesDiffRemainder.'m'.'", ';
736
        //col3
737
        $sOutput .= '"'.htmlspecialchars(stripslashes((string) $record['name']), ENT_QUOTES).' '.htmlspecialchars(stripslashes((string) $record['lastname']), ENT_QUOTES).' ['.htmlspecialchars(stripslashes((string) $record['login']), ENT_QUOTES).']", ';
738
        //col5 - TAGS
739
        $sOutput .= '"'.htmlspecialchars(stripslashes((string) $record['label']), ENT_QUOTES).' ['.$record['item_id'].']"';
740
        //Finish the line
741
        $sOutput .= '],';
742
    }
743
744
    if (count($rows) > 0) {
745
        $sOutput = substr_replace($sOutput, '', -1);
746
        $sOutput .= '] }';
747
    } else {
748
        $sOutput .= '[] }';
749
    }
750
} elseif (isset($params['action']) && $params['action'] === 'users_logged_in') {
751
    //Columns name
752
    $aColumns = ['login', 'name', 'lastname', 'timestamp', 'last_connexion'];
753
754
    // Ordering
755
    $orderColumn = $aColumns[0];
756
    if (isset($aColumns[$params['order'][0]['column']]) === true) {
757
        $orderColumn = $aColumns[$params['order'][0]['column']];
758
    }    
759
760
    // Filtering
761
    $sWhere = new WhereClause('AND');
762
    if ($searchValue !== '') {        
763
        $subclause = $sWhere->addClause('OR');
764
        foreach ($aColumns as $column) {
765
            $subclause->add($column.' LIKE %ss', $searchValue);
766
        }
767
    }
768
    $subclause2 = $sWhere->addClause('OR');
769
    $subclause2->add('session_end >= %i', time());
770
771
    // Get the total number of records
772
    $iTotal = DB::queryFirstField(
773
        'SELECT COUNT(*)
774
        FROM '.prefixTable('users').'
775
        WHERE %l ORDER BY %l %l',
776
        $sWhere,
777
        $orderColumn,
778
        $orderDirection
779
    );
780
781
    // Prepare the SQL query
782
    $sql = 'SELECT *
783
    FROM '.prefixTable('users').'
784
    WHERE %l ORDER BY %l %l LIMIT %i, %i';
785
    $params = [$sWhere, $orderColumn, $orderDirection, $sLimitStart, $sLimitLength];
786
787
    // Get the records
788
    $rows = DB::query($sql, ...$params);
789
    $iFilteredTotal = DB::count();
790
791
    // Output
792
    $sOutput = '{';
793
    $sOutput .= '"sEcho": '. (int) $request->query->filter('draw', FILTER_SANITIZE_NUMBER_INT) . ', ';
794
    $sOutput .= '"iTotalRecords": '.$iTotal.', ';
795
    $sOutput .= '"iTotalDisplayRecords": '.$iTotal.', ';
796
    $sOutput .= '"aaData": ';
797
    if ($iFilteredTotal > 0) {
798
        $sOutput .= '[';
799
    }
800
    foreach ($rows as $record) {
801
        $sOutput .= '[';
802
        //col1
803
        $sOutput .= '"<span data-id=\"'.$record['id'].'\">", ';
804
        //col2
805
        $sOutput .= '"'.htmlspecialchars(stripslashes((string) $record['name']), ENT_QUOTES).' '.htmlspecialchars(stripslashes((string) $record['lastname']), ENT_QUOTES).' ['.htmlspecialchars(stripslashes((string) $record['login']), ENT_QUOTES).']", ';
806
        //col3
807
        if ($record['admin'] === '1') {
808
            $user_role = $lang->get('god');
809
        } elseif ($lang->get('gestionnaire') === 1) {
810
            $user_role = $lang->get('gestionnaire');
811
        } else {
812
            $user_role = $lang->get('user');
813
        }
814
        $sOutput .= '"'.$user_role.'", ';
815
        //col4
816
        $time_diff = time() - (int) $record['timestamp'];
817
        $hoursDiff = round($time_diff / 3600, 0, PHP_ROUND_HALF_DOWN);
818
        $minutesDiffRemainder = floor($time_diff % 3600 / 60);
819
        $sOutput .= '"'.$hoursDiff.'h '.$minutesDiffRemainder.'m" ';
820
        //Finish the line
821
        $sOutput .= '],';
822
    }
823
824
    if (count($rows) > 0) {
825
        $sOutput = substr_replace($sOutput, '', -1);
826
        $sOutput .= '] }';
827
    } else {
828
        $sOutput .= '[] }';
829
    }
830
} elseif (isset($params['action']) && $params['action'] === 'tasks_in_progress') {
831
    //Columns name
832
    $aColumns = ['p.increment_id', 'p.created_at', 'p.updated_at', 'p.process_type', 'p.is_in_progress'];
833
834
    // Ordering
835
    $orderColumn = $aColumns[0];
836
    if (isset($aColumns[$params['order'][0]['column']]) === true) {
837
        $orderColumn = $aColumns[$params['order'][0]['column']];
838
    }    
839
840
    // Filtering
841
    $sWhere = new WhereClause('AND');
842
    if ($searchValue !== '') {        
843
        $subclause = $sWhere->addClause('OR');
844
        foreach ($aColumns as $column) {
845
            $subclause->add($column.' LIKE %ss', $searchValue);
846
        }
847
    }
848
    $subclause2 = $sWhere->addClause('OR');
849
    $subclause2->add('p.finished_at = ""');
850
    $subclause2->add('p.finished_at IS NULL');
851
852
    // Get the total number of records
853
    $iTotal = DB::queryFirstField(
854
        'SELECT COUNT(*)
855
        FROM '.prefixTable('background_tasks').' AS p 
856
        LEFT JOIN '.prefixTable('users').' AS u ON %l
857
        WHERE %l ORDER BY %l %l',
858
        'u.id = json_extract(p.arguments, "$[0]")',
859
        $sWhere,
860
        $orderColumn,
861
        $orderDirection
862
    );
863
864
    // Prepare the SQL query
865
    $sql = 'SELECT p.increment_id, p.created_at, p.updated_at, p.process_type,
866
                p.is_in_progress, p.arguments
867
            FROM '.prefixTable('background_tasks').' AS p 
868
            LEFT JOIN '.prefixTable('users').' AS u ON %l
869
            WHERE %l ORDER BY %l %l LIMIT %i, %i';
870
    $params = ['u.id = json_extract(p.arguments, "$[0]")',$sWhere, $orderColumn, $orderDirection, $sLimitStart, $sLimitLength];
871
872
    // Get the records
873
    $rows = DB::query($sql, ...$params);
874
    $iFilteredTotal = DB::count();
875
876
    // Output
877
    $sOutput = '{';
878
    $sOutput .= '"sEcho": '. (int) $request->query->filter('draw', FILTER_SANITIZE_NUMBER_INT) . ', ';
879
    $sOutput .= '"iTotalRecords": '.$iTotal.', ';
880
    $sOutput .= '"iTotalDisplayRecords": '.$iTotal.', ';
881
    $sOutput .= '"aaData": ';
882
    if ($iFilteredTotal > 0) {
883
        $sOutput .= '[';
884
    }
885
    foreach ($rows as $record) {
886
        // Get subtask progress
887
        $subtaskProgress = getSubtaskProgress($record['increment_id']);        
888
889
        $sOutput .= '[';
890
        //col1
891
        $sOutput .= '"<span data-done=\"'.$record['is_in_progress'].'\" data-type=\"'.$record['process_type'].'\" data-process-id=\"'.$record['increment_id'].'\"></span>", ';
892
        //col2
893
        $sOutput .= '"'.date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['created_at']).'", ';
894
        //col3
895
        //$sOutput .= '"'.($record['updated_at'] === '' ? '-' : date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['updated_at'])).'", ';
896
        $sOutput .= '"<div class=\"progress mt-2\"><div class=\"progress-bar\" style=\"width: '.$subtaskProgress.'\">'.$subtaskProgress.'</div></div>", ';
897
        //col4
898
        $sOutput .= '"'.$record['process_type'].'", ';
899
900
        // col5
901
        if (in_array($record['process_type'], array('create_user_keys', 'item_copy')) === true) {
902
            $data_user = DB::queryFirstRow(
903
                'SELECT name, lastname FROM ' . prefixTable('users') . '
904
                WHERE id = %i',
905
                json_decode($record['arguments'], true)['new_user_id']
906
            );
907
            $sOutput .= '"'.$data_user['name'].' '.$data_user['lastname'].'", ';
908
        } elseif ($record['process_type'] === 'send_email') {
909
            $sOutput .= '"'.json_decode($record['arguments'], true)['receiver_name'].'", ';
910
        }
911
        // col6
912
        $sOutput .= '""';
913
        //Finish the line
914
        $sOutput .= '],';
915
    }
916
917
    if (count($rows) > 0) {
918
        $sOutput = substr_replace($sOutput, '', -1);
919
        $sOutput .= '] }';
920
    } else {
921
        $sOutput .= '[] }';
922
    }
923
} elseif (isset($params['action']) && $params['action'] === 'tasks_finished') {
924
    //Columns name
925
    $aColumns = ['p.created_at', 'p.finished_at', 'p.process_type', 'u.name'];
926
927
    // Ordering
928
    $orderColumn = $aColumns[0];
929
    if (isset($aColumns[$params['order'][0]['column']]) === true) {
930
        $orderColumn = $aColumns[$params['order'][0]['column']];
931
    }
932
933
    // Filtering
934
    $sWhere = new WhereClause('AND');
935
    if ($searchValue !== '') {        
936
        $subclause = $sWhere->addClause('OR');
937
        foreach ($aColumns as $column) {
938
            $subclause->add($column.' LIKE %ss', $searchValue);
939
        }
940
    }
941
    $sWhere->add('finished_at != ""');
942
943
    // Get the total number of records
944
    $iTotal = DB::queryFirstField(
945
        'SELECT COUNT(*)
946
        FROM '.prefixTable('background_tasks').' AS p 
947
        LEFT JOIN '.prefixTable('users').' AS u ON u.id = json_extract(p.arguments, "$[0]")
948
        WHERE %l ORDER BY %l %l',
949
        $sWhere,
950
        $orderColumn,
951
        $orderDirection
952
    );
953
954
    // Prepare the SQL query
955
    $sql = 'SELECT p.*
956
    FROM '.prefixTable('background_tasks').' AS p 
957
    LEFT JOIN '.prefixTable('users').' AS u ON %l
958
    WHERE %l ORDER BY %l %l LIMIT %i, %i';
959
    $params = ['u.id = json_extract(p.arguments, "$[0]")',$sWhere, $orderColumn, $orderDirection, $sLimitStart, $sLimitLength];
960
961
    // Get the records
962
    $rows = DB::query($sql, ...$params);
963
    $iFilteredTotal = DB::count();
964
965
    // Output
966
    $sOutput = '{';
967
    $sOutput .= '"sEcho": '. (int) $request->query->filter('draw', FILTER_SANITIZE_NUMBER_INT) . ', ';
968
    $sOutput .= '"iTotalRecords": '.$iTotal.', ';
969
    $sOutput .= '"iTotalDisplayRecords": '.$iTotal.', ';
970
    $sOutput .= '"aaData": ';
971
    if ($iFilteredTotal > 0) {
972
        $sOutput .= '[';
973
    }
974
    foreach ($rows as $record) {
975
        // play with dates
976
        $start = strtotime(date('Y-m-d H:i:s', (int) $record['created_at']));
977
        $end = strtotime(date('Y-m-d H:i:s', (int) $record['finished_at']));
978
        
979
        $sOutput .= '[';
980
        //col1
981
        $sOutput .= '"'.(is_null($record['error_message']) ? '' : addslashes($record['error_message'])).'", ';
982
        //col2
983
        $sOutput .= '"'.date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['created_at']).'", ';
984
        //col3
985
        $sOutput .= is_null($record['started_at']) === false ?
986
            ('"'.date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['started_at']).'", ') :
987
                ('"'.date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['created_at']).'", ');
988
        //col4
989
        $sOutput .= '"'.date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['finished_at']).'", '; 
990
        // col7
991
        $sOutput .= '"'.gmdate('H:i:s', (int) $record['finished_at'] - (is_null($record['started_at']) === false ? (int) $record['started_at'] : (int) $record['created_at'])).'",';
992
        //col5
993
        if ($record['process_type'] === 'create_user_keys') {
994
            $processIcon = '<i class=\"fa-solid fa-user-plus infotip\" style=\"cursor: pointer;\" title=\"'.$lang->get('user_creation').'\"></i>';
995
        } else if ($record['process_type'] === 'send_email') {
996
            $processIcon = '<i class=\"fa-solid fa-envelope-circle-check infotip\" style=\"cursor: pointer;\" title=\"'.$lang->get('send_email_to_user').'\"></i>';
997
        } else if ($record['process_type'] === 'user_build_cache_tree') {
998
            $processIcon = '<i class=\"fa-solid fa-folder-tree infotip\" style=\"cursor: pointer;\" title=\"'.$lang->get('reload_user_cache_table').'\"></i>';
999
        } else if ($record['process_type'] === 'item_copy') {
1000
            $processIcon = '<i class=\"fa-solid fa-copy infotip\" style=\"cursor: pointer;\" title=\"'.$lang->get('item_copied').'\"></i>';
1001
        } else if ($record['process_type'] === 'item_update_create_keys') {
1002
            $processIcon = '<i class=\"fa-solid fa-pencil infotip\" style=\"cursor: pointer;\" title=\"'.$lang->get('item_updated').'\"></i>';
1003
        } else if ($record['process_type'] === 'new_item') {
1004
            $processIcon = '<i class=\"fa-solid fa-square-plus infotip\" style=\"cursor: pointer;\" title=\"'.$lang->get('new_item').'\"></i>';
1005
        } else {
1006
            $processIcon = '<i class=\"fa-solid fa-question\"></i> ('.$record['process_type'].')';
1007
        }
1008
        $sOutput .= '"'.$processIcon.'", ';
1009
        // col6
1010
        $arguments = json_decode($record['arguments'], true);
1011
        $newUserId = array_key_exists('new_user_id', $arguments) ? 
1012
            $arguments['new_user_id'] : 
1013
            (array_key_exists('user_id', $arguments) ? $arguments['user_id'] : null);
1014
        if ($record['process_type'] === 'create_user_keys' && is_null($newUserId) === false && empty($newUserId) === false) {
1015
            $data_user = DB::queryFirstRow(
1016
                'SELECT name, lastname, login FROM ' . prefixTable('users') . '
1017
                WHERE id = %i',
1018
                $newUserId
1019
            );
1020
            if (DB::count() > 0) {
1021
                $txt = (isset($data_user['name']) === true ? $data_user['name'] : '').(isset($data_user['lastname']) === true ? ' '.$data_user['lastname'] : '');
1022
                $sOutput .= '"'.(empty($txt) === false ? $txt : $data_user['login']).'"';
1023
            } else {
1024
                $sOutput .= '"<i class=\"fa-solid fa-user-slash\"></i>"';
1025
            }
1026
        } elseif ($record['process_type'] === 'send_email') {
1027
            $user = json_decode($record['arguments'], true)['receiver_name'];
1028
            $sOutput .= '"'.(is_null($user) === true || empty($user) === true ? '<i class=\"fa-solid fa-user-slash\"></i>' : $user).'"';
1029
        } elseif ($record['process_type'] === 'user_build_cache_tree') {
1030
            $user = json_decode($record['arguments'], true)['user_id'];
1031
            $data_user = DB::queryFirstRow(
1032
                'SELECT name, lastname, login FROM ' . prefixTable('users') . '
1033
                WHERE id = %i',
1034
                $user
1035
            );
1036
            if (DB::count() > 0) {
1037
                $txt = (isset($data_user['name']) === true ? $data_user['name'] : '').(isset($data_user['lastname']) === true ? ' '.$data_user['lastname'] : '');
1038
                $sOutput .= '"'.(empty($txt) === false ? $txt : $data_user['login']).'"';
1039
            } else {
1040
                $sOutput .= '"<i class=\"fa-solid fa-user-slash\"></i>"';
1041
            }
1042
        } else {
1043
            $sOutput .= '"<i class=\"fa-solid fa-user-slash\"></i>"';
1044
        }
1045
        //Finish the line
1046
        $sOutput .= '],';
1047
    }
1048
1049
    if (count($rows) > 0) {
1050
        $sOutput = substr_replace($sOutput, '', -1);
1051
        $sOutput .= '] }';
1052
    } else {
1053
        $sOutput .= '[] }';
1054
    }
1055
}
1056
1057
// deepcode ignore XSS: data comes from database. Before being stored it is clean with feature antiXss->xss_clean
1058
echo (string) $sOutput;
1059
1060
1061
1062
function getSubtaskProgress($id)
1063
{
1064
    $subtasks = DB::query(
1065
        'SELECT *
1066
        FROM ' . prefixTable('background_subtasks') . '
1067
        WHERE task_id = %i',
1068
        $id
1069
    );
1070
1071
    $i = 0;
1072
    $nb = count($subtasks);
1073
    $finished_nb = 0;
1074
    foreach ($subtasks as $task) {
1075
        if (is_null($task['finished_at']) === false) {
1076
            $finished_nb++;
1077
        }
1078
1079
        $i++;
1080
    }
1081
1082
    return ($finished_nb !== 0 ? pourcentage($finished_nb, $nb, 100) : 0) .'%';
1083
}