Passed
Branch feature/code_clean (75ffa1)
by Nils
06:31
created

handleVisibleNode()   B

Complexity

Conditions 7
Paths 3

Size

Total Lines 49
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 25
nc 3
nop 10
dl 0
loc 49
rs 8.5866
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Teampass - a collaborative passwords manager.
7
 * ---
8
 * This library is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * @project   Teampass
13
 * @file      tree.php
14
 *
15
 * @author    Nils Laumaillé ([email protected])
16
 * @copyright 2009-2024 Teampass.net
17
 * @license   https://spdx.org/licenses/GPL-3.0-only.html#licenseText GPL-3.0
18
 *
19
 * @see       https://www.teampass.net
20
 */
21
22
23
use TeampassClasses\SessionManager\SessionManager;
24
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
25
use TeampassClasses\Language\Language;
26
use TeampassClasses\PerformChecks\PerformChecks;
27
use TeampassClasses\ConfigManager\ConfigManager;
28
use TeampassClasses\NestedTree\NestedTree;
29
30
// Load functions
31
require_once 'main.functions.php';
32
33
// init
34
loadClasses('DB');
35
$session = SessionManager::getSession();
36
$request = SymfonyRequest::createFromGlobals();
37
$lang = new Language($session->get('user-language') ?? 'english');
38
39
// Load config
40
$configManager = new ConfigManager();
41
$SETTINGS = $configManager->getAllSettings();
42
43
// Do checks
44
// Instantiate the class with posted data
45
$checkUserAccess = new PerformChecks(
46
    dataSanitizer(
47
        [
48
            'type' => null !== $request->request->get('type') ? htmlspecialchars($request->request->get('type')) : '',
49
        ],
50
        [
51
            'type' => 'trim|escape',
52
        ],
53
    ),
54
    [
55
        'user_id' => returnIfSet($session->get('user-id'), null),
56
        'user_key' => returnIfSet($session->get('key'), null),
57
    ]
58
);
59
// Handle the case
60
echo $checkUserAccess->caseHandler();
61
if (
62
    $checkUserAccess->userAccessPage('items') === false ||
63
    $checkUserAccess->checkSession() === false
64
) {
65
    // Not allowed page
66
    $session->set('system-error_code', ERR_NOT_ALLOWED);
67
    include $SETTINGS['cpassman_dir'] . '/error.php';
68
    exit;
69
}
70
71
// Define Timezone
72
date_default_timezone_set(isset($SETTINGS['timezone']) === true ? $SETTINGS['timezone'] : 'UTC');
73
74
// Set header properties
75
header('Content-type: text/html; charset=utf-8');
76
header('Cache-Control: no-cache, no-store, must-revalidate');
77
78
// --------------------------------- //
79
80
// Load tree
81
$tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
82
83
// Prepare sanitization
84
$data = [
85
    'forbidenPfs' => null !== $session->get('user-forbiden_personal_folders') ? json_encode($session->get('user-forbiden_personal_folders')) : '{}',
86
    'visibleFolders' => null !== $session->get('user-accessible_folders') ? json_encode($session->get('user-accessible_folders')) : '{}',
87
    'userId' => null !== $session->get('user-id') ? $session->get('user-id') : '',
88
    'userLogin' => null !== $session->get('user-login') ? $session->get('user-login') : '',
89
    'userReadOnly' => null !== $session->get('user-read_only') ? $session->get('user-read_only') : '',
90
    'limitedFolders' => null !== $session->get('user-list_folders_limited') ? json_encode($session->get('user-list_folders_limited')) : '{}',
91
    'readOnlyFolders' => null !== $session->get('user-read_only_folders') ? json_encode($session->get('user-read_only_folders')) : '{}',
92
    'personalVisibleFolders' => null !== $session->get('user-personal_visible_folders') ? json_encode($session->get('user-personal_visible_folders')) : '{}',
93
    'userTreeLastRefresh' => null !== $session->get('user-tree_last_refresh_timestamp') ? $session->get('user-tree_last_refresh_timestamp') : '',
94
    'forceRefresh' => null !== $request->query->get('force_refresh') ? $request->query->get('force_refresh') : '',
95
    'nodeId' => null !== $request->query->get('id') ? $request->query->get('id') : '',
96
    'restrictedFoldersForItems' => null !== $request->query->get('list_restricted_folders_for_items') ? json_encode($request->query->get('list_restricted_folders_for_items')) : '{}',
97
    'noAccessFolders' => null !== $session->get('user-no_access_folders') ? json_encode($session->get('user-no_access_folders')) : '{}',
98
    'personalFolders' => null !== $session->get('user-personal_folders') ? json_encode($session->get('user-personal_folders')) : '{}',
99
    'userCanCreateRootFolder' => null !== $session->get('user-can_create_root_folder') ? json_encode($session->get('user-can_create_root_folder')) : '{}',
100
    'userTreeLoadStrategy' => null !== $session->get('user-tree_load_strategy') ? $session->get('user-tree_load_strategy') : '',
101
];
102
103
$filters = [
104
    'forbidenPfs' => 'cast:array',
105
    'visibleFolders' => 'cast:array',
106
    'userId' => 'cast:integer',
107
    'userLogin' => 'trim|escape',
108
    'userReadOnly' => 'cast:boolean',
109
    'limitedFolders' => 'cast:array',
110
    'readOnlyFolders' => 'cast:array',
111
    'personalVisibleFolders' => 'cast:array',
112
    'userTreeLastRefresh' => 'cast:integer',
113
    'forceRefresh' => 'cast:integer',
114
    'nodeId' => 'cast:integer',
115
    'restrictedFoldersForItems' => 'cast:array',
116
    'noAccessFolders' => 'cast:array',
117
    'personalFolders' => 'cast:array',
118
    'userCanCreateRootFolder' => 'cast:array',
119
    'userTreeLoadStrategy' => 'trim|escape',
120
];
121
122
$inputData = dataSanitizer(
123
    $data,
124
    $filters
125
);
126
127
$lastFolderChange = DB::queryfirstrow(
128
    'SELECT valeur FROM ' . prefixTable('misc') . '
129
    WHERE type = %s AND intitule = %s',
130
    'timestamp',
131
    'last_folder_change'
132
);
133
if (DB::count() === 0) {
134
    $lastFolderChange['valeur'] = 0;
135
}
136
137
// Should we use a cache or refresh the tree
138
$goTreeRefresh = loadTreeStrategy(
139
    (int) $lastFolderChange['valeur'],
140
    (int) $inputData['userTreeLastRefresh'],
141
    (null === $session->get('user-tree_structure') || empty($session->get('user-tree_structure')) === true) ? [] : $session->get('user-tree_structure'),
142
    (int) $inputData['userId'],
143
    (int) $inputData['forceRefresh']
144
);
145
// We don't use the cache if an ID of folder is provided
146
if ($goTreeRefresh['state'] === true || empty($inputData['nodeId']) === false || $inputData['userTreeLoadStrategy'] === 'sequential') {
147
    // Build tree
148
    $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
149
150
    if (
151
        isset($inputData['limitedFolders']) === true
152
        && is_array($inputData['limitedFolders']) === true
153
        && count($inputData['limitedFolders']) > 0
154
    ) {
155
        $listFoldersLimitedKeys = array_keys($inputData['limitedFolders']);
156
    } else {
157
        $listFoldersLimitedKeys = array();
158
    }
159
    
160
    // list of items accessible but not in an allowed folder
161
    if (
162
        isset($inputData['restrictedFoldersForItems']) === true
163
        && count($inputData['restrictedFoldersForItems']) > 0
164
    ) {
165
        $listRestrictedFoldersForItemsKeys = @array_keys($inputData['restrictedFoldersForItems']);
166
    } else {
167
        $listRestrictedFoldersForItemsKeys = array();
168
    }
169
    
170
    $ret_json = array();
171
    $last_visible_parent = -1;
172
    $last_visible_parent_level = 1;
173
    $nodeId = isset($inputData['nodeId']) === true && is_int($inputData['nodeId']) === true ? $inputData['nodeId'] : 0;
174
175
    // build the tree to be displayed
176
    if (showFolderToUser(
177
        $nodeId,
178
        $inputData['forbidenPfs'],
179
        $inputData['visibleFolders'],
180
        $listFoldersLimitedKeys,
181
        $listRestrictedFoldersForItemsKeys
182
    ) === true)
183
    {
184
        if ($inputData['userTreeLoadStrategy'] === 'sequential') {
185
            // SEQUENTIAL MODE
186
            $completTree = $tree->getDescendants(empty($nodeId) === true ? 0 : (int) $nodeId, false, true, false);
187
            foreach ($completTree as $child) {
188
                recursiveTree(
189
                    (int) $child->id,
190
                    $child,
191
                    /** @scrutinizer ignore-type */ $tree,
192
                    $listFoldersLimitedKeys,
193
                    $listRestrictedFoldersForItemsKeys,
194
                    $last_visible_parent,
195
                    $last_visible_parent_level,
196
                    $SETTINGS,
197
                    $inputData,
198
                    $ret_json
199
                );
200
            }
201
202
        } else {
203
            // FULL MODE
204
            $completTree = $tree->getTreeWithChildren();
205
            foreach ($completTree[0]->children as $child) {
206
                recursiveTree(
207
                    (int) $child,
208
                    $completTree[$child],
209
                    /** @scrutinizer ignore-type */ $tree,
210
                    $listFoldersLimitedKeys,
211
                    $listRestrictedFoldersForItemsKeys,
212
                    $last_visible_parent,
213
                    $last_visible_parent_level,
214
                    $SETTINGS,
215
                    $inputData,
216
                    $ret_json
217
                );
218
            }
219
        }
220
    }
221
222
    // Save in SESSION
223
    $session->set('user-tree_structure', $ret_json);
224
    $session->set('user-tree_last_refresh_timestamp', time());
225
226
    $ret_json = json_encode($ret_json);
227
228
    // Save user folders tree
229
    cacheTreeUserHandler(
230
        (int) $inputData['userId'],
231
        $ret_json,
232
        $SETTINGS
233
    );
234
235
    // Add new process
236
    DB::insert(
237
        prefixTable('background_tasks'),
238
        array(
239
            'created_at' => time(),
240
            'process_type' => 'user_build_cache_tree',
241
            'arguments' => json_encode([
242
                'user_id' => (int) $inputData['userId'],
243
            ], JSON_HEX_QUOT | JSON_HEX_TAG),
244
            'updated_at' => '',
245
            'finished_at' => '',
246
            'output' => '',
247
        )
248
    );
249
250
    // Send back
251
    echo $ret_json;
252
} else {
253
    echo $goTreeRefresh['data'];
254
}
255
256
257
/**
258
 * Check if user can see this folder based upon rights
259
 *
260
 * @param integer $nodeId
261
 * @param array $session_forbiden_pfs
262
 * @param array $session_groupes_visibles
263
 * @param array $listFoldersLimitedKeys
264
 * @param array $listRestrictedFoldersForItemsKeys
265
 * @return boolean
266
 */
267
function showFolderToUser(
268
    int $nodeId,
269
    array $session_forbiden_pfs,
270
    array $session_groupes_visibles,
271
    array $listFoldersLimitedKeys,
272
    array $listRestrictedFoldersForItemsKeys
273
): bool
274
{
275
    $big_array = array_diff(
276
        array_unique(
277
            array_merge(
278
                $session_groupes_visibles, 
279
                $listFoldersLimitedKeys, 
280
                $listRestrictedFoldersForItemsKeys
281
            ), 
282
            SORT_NUMERIC
283
        ), 
284
        $session_forbiden_pfs
285
    );
286
    if ($nodeId === 0 || in_array($nodeId, $big_array) === true) {
287
        return true;
288
    }
289
    return false;
290
}
291
292
293
294
/**
295
 * Get through complete tree
296
 *
297
 * @param int     $nodeId                            Id
298
 * @param stdClass   $currentNode                       Tree info
299
 * @param NestedTree   $tree                              The tree
300
 * @param array   $listFoldersLimitedKeys            Limited
301
 * @param array   $listRestrictedFoldersForItemsKeys Restricted
302
 * @param int     $last_visible_parent               Visible parent
303
 * @param int     $last_visible_parent_level         Parent level
304
 * @param array   $SETTINGS                          Teampass settings
305
 * @param array   $inputData,
306
 * @param array   $ret_json
307
 *
308
 * @return array
309
 */
310
function recursiveTree(
311
    int $nodeId,
312
    stdClass $currentNode,
313
    NestedTree $tree,
314
    array $listFoldersLimitedKeys,
315
    array $listRestrictedFoldersForItemsKeys,
316
    int $last_visible_parent,
317
    int $last_visible_parent_level,
318
    array $SETTINGS,
319
    array $inputData,
320
    array &$ret_json = array()
321
) {
322
    // Initialize variables
323
    $text = '';    
324
    $displayThisNode = false;
325
    $nbItemsInSubfolders = $nbSubfolders = $nbItemsInFolder = 0;
326
    $nodeDescendants = $tree->getDescendants($nodeId, true, false, false);
327
    // On combine les tableaux une seule fois pour optimiser
328
    $allowedFolders = array_merge($inputData['personalFolders'], $inputData['visibleFolders']);
329
    
330
    foreach ($nodeDescendants as $node) {
331
        if (!in_array($node->id, $allowedFolders)) {
332
            continue;
333
        }
334
335
        // If it is a personal folder, we check the specific conditions
336
        if (
337
            (int) $node->personal_folder === 1 
338
            && (int) $SETTINGS['enable_pf_feature'] === 1 
339
            && !in_array($node->id, $inputData['personalFolders'])
340
        ) {
341
            continue;
342
        }
343
        
344
        // If we are here, the node must be displayed
345
        $displayThisNode = true;
346
        $nbItemsInSubfolders = (int) $node->nb_items_in_subfolders;
347
        $nbItemsInFolder = (int) $node->nb_items_in_folder;
348
        $nbSubfolders = (int) $node->nb_subfolders;
349
        break;  // Get out as soon as we find a valid node.
350
    }
351
    
352
    if ($displayThisNode === true) {
353
        handleNode(
354
            (int) $nodeId,
355
            $currentNode,
356
            $tree,
357
            $listFoldersLimitedKeys,
358
            $listRestrictedFoldersForItemsKeys,
359
            $last_visible_parent,
360
            $last_visible_parent_level,
361
            $SETTINGS,
362
            $inputData,
363
            $text,
364
            $nbItemsInSubfolders,
365
            $nbSubfolders,
366
            $nbItemsInFolder,
367
            $ret_json
368
        );
369
    }
370
    
371
    return $ret_json;
372
}
373
374
/**
375
 * Permits to get the Node definition
376
 *
377
 * @param integer $nodeId
378
 * @param stdClass $currentNode
379
 * @param NestedTree $tree
380
 * @param array $listFoldersLimitedKeys
381
 * @param array $listRestrictedFoldersForItemsKeys
382
 * @param integer $last_visible_parent
383
 * @param integer $last_visible_parent_level
384
 * @param array $SETTINGS
385
 * @param array $inputData
386
 * @param string $text
387
 * @param integer $nbItemsInSubfolders
388
 * @param integer $nbSubfolders
389
 * @param integer $nbItemsInFolder
390
 * @param array $ret_json
391
 * @return void
392
 */
393
function handleNode(
394
    int $nodeId,
395
    stdClass $currentNode,
396
    NestedTree $tree,
397
    array $listFoldersLimitedKeys,
398
    array $listRestrictedFoldersForItemsKeys,
399
    int $last_visible_parent,
400
    int $last_visible_parent_level,
401
    array $SETTINGS,
402
    array $inputData,
403
    string $text,
404
    int $nbItemsInSubfolders,
405
    int $nbSubfolders,
406
    int $nbItemsInFolder,
407
    array &$ret_json = array()
408
)
409
{
410
    // If personal Folder, convert id into user name
411
    if ((int) $currentNode->title === (int) $inputData['userId'] && (int) $currentNode->nlevel === 1) {
412
        $currentNode->title = $inputData['userLogin'];
413
    }
414
415
    // Decode if needed
416
    $currentNode->title = htmlspecialchars_decode($currentNode->title, ENT_QUOTES);
417
418
    $nodeData = prepareNodeData(
419
        (int) $nodeId,
420
        $inputData['visibleFolders'],
421
        $inputData['readOnlyFolders'],
422
        $inputData['personalVisibleFolders'],
423
        (int) $nbItemsInFolder,
424
        (int) $nbItemsInSubfolders,
425
        (int) $nbSubfolders,
426
        $inputData['limitedFolders'],
427
        isset($SETTINGS['tree_counters']) === true && isset($SETTINGS['enable_tasks_manager']) === true && (int) $SETTINGS['enable_tasks_manager'] === 1 && (int) $SETTINGS['tree_counters'] === 1 ? 1 : 0,
428
        (bool) $inputData['userReadOnly'],
429
        $listFoldersLimitedKeys,
430
        $listRestrictedFoldersForItemsKeys,
431
        $inputData['restrictedFoldersForItems'],
432
        $inputData['personalFolders'],
433
        $tree
434
    );
435
436
    // Prepare JSON 
437
    $tmpRetArray = prepareNodeJson(
438
        $currentNode,
439
        $nodeData,
440
        $inputData,
441
        $ret_json,
442
        $last_visible_parent,
443
        $last_visible_parent_level,
444
        $nodeId,
445
        $text,
446
        $nbSubfolders,
447
        $SETTINGS
448
    );    
449
    $last_visible_parent = $tmpRetArray['last_visible_parent'];
450
    $last_visible_parent_level = $tmpRetArray['last_visible_parent_level'];
451
    $ret_json = $tmpRetArray['ret_json'];
452
453
    // ensure we get the children of the folder
454
    if (isset($currentNode->children) === false) {
455
        $currentNode->children = $tree->getDescendants($nodeId, false, true, true);
456
    }
457
    if ($inputData['userTreeLoadStrategy'] === 'full' && isset($currentNode->children) === true) {
458
        foreach ($currentNode->children as $child) {
459
            recursiveTree(
460
                (int) $child,
461
                $tree->getNode($child),// get node info for this child
462
                /** @scrutinizer ignore-type */ $tree,
463
                $listFoldersLimitedKeys,
464
                $listRestrictedFoldersForItemsKeys,
465
                $last_visible_parent,
466
                $last_visible_parent_level,
467
                $SETTINGS,
468
                $inputData,
469
                $ret_json
470
            );
471
        }
472
    }
473
}
474
475
/**
476
 * Permits to prepare the node data
477
 *
478
 * @param stdClass $currentNode
479
 * @param array $nodeData
480
 * @param array $inputData
481
 * @param array $ret_json
482
 * @param integer $last_visible_parent
483
 * @param integer $last_visible_parent_level
484
 * @param integer $nodeId
485
 * @param string $text
486
 * @param integer $nbSubfolders
487
 * @param array $SETTINGS
488
 * @return array
489
 */
490
function prepareNodeJson(
491
    stdClass $currentNode,
492
    array $nodeData,
493
    array $inputData,
494
    array &$ret_json,
495
    int &$last_visible_parent,
496
    int &$last_visible_parent_level,
497
    int $nodeId,
498
    string $text,
499
    int $nbSubfolders,
500
    array $SETTINGS
501
): array
502
{
503
    $session = SessionManager::getSession();
504
    // Load user's language
505
    $lang = new Language($session->get('user-language') ?? 'english');
506
507
    // prepare json return for current node
508
    $parent = $currentNode->parent_id === '0' ? '#' : 'li_' . $currentNode->parent_id;
509
510
    // handle displaying
511
    if (isKeyExistingAndEqual('show_only_accessible_folders', 1, $SETTINGS) === true) {
512
        if ($nodeData['hide_node'] === true) {
513
            $last_visible_parent = (int) $parent;
514
            $last_visible_parent_level = $currentNode->nlevel--;
515
        } elseif ($currentNode->nlevel < $last_visible_parent_level) {
516
            $last_visible_parent = -1;
517
        }
518
    }
519
520
    // json
521
    if ($nodeData['hide_node'] === false && $nodeData['show_but_block'] === false) {
522
        array_push(
523
            $ret_json,
524
            array(
525
                'id' => 'li_' . $nodeId,
526
                'parent' => $last_visible_parent === -1 ? $parent : $last_visible_parent,
527
                'text' => '<i class="'.$currentNode->fa_icon.' tree-folder mr-2" data-folder="'.$currentNode->fa_icon.'"  data-folder-selected="'.$currentNode->fa_icon_selected.'"></i>'.$text.htmlspecialchars($currentNode->title).$nodeData['html'],
528
                'li_attr' => array(
529
                    'class' => 'jstreeopen',
530
                    'title' => 'ID [' . $nodeId . '] ' . $nodeData['title'],
531
                ),
532
                'a_attr' => array(
533
                    'id' => 'fld_' . $nodeId,
534
                    'class' => $nodeData['folderClass'],
535
                    'onclick' => 'ListerItems(' . $nodeId . ', ' . $nodeData['restricted'] . ', 0, 1)',
536
                    'data-title' => htmlspecialchars($currentNode->title),
537
                ),
538
                'is_pf' => in_array($nodeId, $inputData['personalFolders']) === true ? 1 : 0,
539
                'can_edit' => (int) $inputData['userCanCreateRootFolder'],
540
            )
541
        );
542
        
543
        if ($inputData['userTreeLoadStrategy'] === 'sequential') {
544
            $ret_json[count($ret_json) - 1]['children'] = $nbSubfolders > 0 ? true : false;
545
        }
546
547
    } elseif ($nodeData['show_but_block'] === true) {
548
        array_push(
549
            $ret_json,
550
            array(
551
                'id' => 'li_' . $nodeId,
552
                'parent' => $last_visible_parent === -1 ? $parent : $last_visible_parent,
553
                'text' => '<i class="'.$currentNode->fa_icon.' tree-folder mr-2" data-folder="'.$currentNode->fa_icon.'"  data-folder-selected="'.$currentNode->fa_icon_selected.'"></i>'.'<i class="fas fa-times fa-xs text-danger mr-1 ml-1"></i>'.$text.htmlspecialchars($currentNode->title).$nodeData['html'],
554
                'li_attr' => array(
555
                    'class' => '',
556
                    'title' => 'ID [' . $nodeId . '] ' . $lang->get('no_access'),
557
                ),
558
            )
559
        );
560
    }
561
562
    return [
563
        'last_visible_parent' => (int) $last_visible_parent,
564
        'last_visible_parent_level' => (int) $last_visible_parent_level,
565
        'ret_json' => $ret_json
566
    ];
567
}
568
569
570
/**
571
 * Prepares the context of a folder based on user session and folder data.
572
 *
573
 * @param int $nodeId
574
 * @param array $session_groupes_visibles
575
 * @param array $session_read_only_folders
576
 * @param array $session_personal_visible_groups
577
 * @param int $nbItemsInFolder
578
 * @param int $nbItemsInSubfolders
579
 * @param int $nbSubfolders
580
 * @param array $session_list_folders_limited
581
 * @param int $tree_counters
582
 * @param bool $session_user_read_only
583
 * @param array $listFoldersLimitedKeys
584
 * @param array $listRestrictedFoldersForItemsKeys
585
 * @param array $session_list_restricted_folders_for_items
586
 * @param array $session_personal_folder
587
 * @param NestedTree $tree
588
 * @return array
589
 */
590
function prepareNodeData(
591
    int $nodeId,
592
    array $session_groupes_visibles,
593
    array $session_read_only_folders,
594
    array $session_personal_visible_groups,
595
    int $nbItemsInFolder,
596
    int $nbItemsInSubfolders,
597
    int $nbSubfolders,
598
    array $session_list_folders_limited,
599
    int $tree_counters,
600
    bool $session_user_read_only,
601
    array $listFoldersLimitedKeys,
602
    array $listRestrictedFoldersForItemsKeys,
603
    array $session_list_restricted_folders_for_items,
604
    array $session_personal_folder,
605
    NestedTree $tree
606
): array {
607
    $session = SessionManager::getSession();
608
    $lang = new Language($session->get('user-language') ?? 'english');
609
    
610
    // Determine if the folder is personal
611
    $isPersonalFolder = in_array($nodeId, $session_personal_folder) === true ? 1 : 0;
612
613
    // Case 1: Node is visible in user's groups
614
    if (in_array($nodeId, $session_groupes_visibles)) {
615
        return handleVisibleNode(
616
            $nodeId,
617
            $session_read_only_folders,
618
            $session_personal_visible_groups,
619
            $nbItemsInFolder,
620
            $nbItemsInSubfolders,
621
            $nbSubfolders,
622
            $tree_counters,
623
            $session_user_read_only,
624
            $isPersonalFolder,
625
            $lang
626
        );
627
    }
628
629
    // Case 2: Node is limited in user's folders
630
    if (in_array($nodeId, $listFoldersLimitedKeys)) {
631
        return createNodeData(
632
            $session_user_read_only ? '<i class="far fa-eye fa-xs mr-1"></i>' : '',
633
            $tree_counters === 1 ? '<span class="badge badge-pill badge-light ml-2 items_count" id="itcount_' . $nodeId . '">' . count($session_list_folders_limited[$nodeId]) . '</span>' : '',
634
            1, // restricted
635
            'folder',
636
            false,
637
            false,
638
            $isPersonalFolder
639
        );
640
    }
641
642
    // Case 3: Node is restricted for items
643
    if (in_array($nodeId, $listRestrictedFoldersForItemsKeys)) {
644
        return createNodeData(
645
            $session_user_read_only ? '<i class="far fa-eye fa-xs mr-1"></i>' : '',
646
            '<span class="badge badge-pill badge-light ml-2 items_count" id="itcount_' . $nodeId . '">' . count($session_list_restricted_folders_for_items[$nodeId]) . '</span>',
647
            1, // restricted
648
            'folder',
649
            false,
650
            false,
651
            $isPersonalFolder
652
        );
653
    }
654
655
    // Case 4: Node has no subfolders and no accessible descendants
656
    if ((int)$nbSubfolders === 0) {
657
        $nodeDirectDescendants = $tree->getDescendants($nodeId, false, false, true);
658
        $accessibleDescendants = array_merge(
659
            $session_groupes_visibles,
660
            array_keys($session_list_restricted_folders_for_items)
661
        );
662
        $hasAccessibleDescendants = count(array_diff($nodeDirectDescendants, $accessibleDescendants)) !== count($nodeDirectDescendants);
663
664
        if ($hasAccessibleDescendants) {
665
            // Show node but block it
666
            return createNodeData('', '', 1, 'folder_not_droppable', true, false, $isPersonalFolder);
667
        }
668
669
        // Hide node
670
        return createNodeData('', '', 1, 'folder_not_droppable', false, true, $isPersonalFolder);
671
    }
672
673
    // Default case: Node is restricted and blocked
674
    return createNodeData('', '', 1, 'folder_not_droppable', true, false, $isPersonalFolder);
675
}
676
677
/**
678
 * Handles the logic for visible nodes.
679
 * 
680
 * @param int $nodeId
681
 * @param array $session_read_only_folders
682
 * @param array $session_personal_visible_groups
683
 * @param int $nbItemsInFolder
684
 * @param int $nbItemsInSubfolders
685
 * @param int $nbSubfolders
686
 * @param int $tree_counters
687
 * @param bool $session_user_read_only
688
 * @param int $isPersonalFolder
689
 * @param Language $lang
690
 * @return array
691
 */
692
function handleVisibleNode(
693
    int $nodeId,
694
    array $session_read_only_folders,
695
    array $session_personal_visible_groups,
696
    int $nbItemsInFolder,
697
    int $nbItemsInSubfolders,
698
    int $nbSubfolders,
699
    int $tree_counters,
700
    bool $session_user_read_only,
701
    int $isPersonalFolder,
702
    Language $lang
703
): array {
704
    // Check if the folder is read-only
705
    if (in_array($nodeId, $session_read_only_folders)) {
706
        return createNodeData(
707
            '<i class="far fa-eye fa-xs mr-1 ml-1"></i>',
708
            $tree_counters === 1 ? '<span class="badge badge-pill badge-light ml-2 items_count" id="itcount_' . $nodeId . '">' . $nbItemsInFolder . '/' . $nbItemsInSubfolders . '/' . $nbSubfolders . '</span>' : '',
709
            1, // restricted
710
            'folder_not_droppable',
711
            false,
712
            false,
713
            $isPersonalFolder,
714
            $lang->get('read_only_account')
715
        );
716
    }
717
718
    // Check if user is read-only and folder is not personal
719
    if ($session_user_read_only && !in_array($nodeId, $session_personal_visible_groups)) {
720
        return createNodeData(
721
            '<i class="far fa-eye fa-xs mr-1"></i>',
722
            $tree_counters === 1 ? '<span class="badge badge-pill badge-light ml-2 items_count" id="itcount_' . $nodeId . '">' . $nbItemsInFolder . '/' . $nbItemsInSubfolders . '/' . $nbSubfolders . '</span>' : '',
723
            0, // not restricted
724
            'folder',
725
            false,
726
            false,
727
            $isPersonalFolder,
728
            $lang->get('read_only_account')
729
        );
730
    }
731
732
    // Default case for visible nodes
733
    return createNodeData(
734
        '',
735
        $tree_counters === 1 ? '<span class="badge badge-pill badge-light ml-2 items_count" id="itcount_' . $nodeId . '">' . $nbItemsInFolder . '/' . $nbItemsInSubfolders . '/' . $nbSubfolders . '</span>' : '',
736
        0, // not restricted
737
        'folder',
738
        false,
739
        false,
740
        $isPersonalFolder
741
    );
742
}
743
744
/**
745
 * Creates a standardized array for node data.
746
 * 
747
 * @param string $html The HTML content for the node
748
 * @param string $badgeHtml The HTML content for the badge
749
 * @param int $restricted The restricted status
750
 * @param string $folderClass The folder class
751
 * @param bool $showButBlock The show but block status
752
 * @param bool $hideNode The hide node status
753
 * @param int $isPersonalFolder The personal folder status
754
 * @param string $title The title
755
 * @return array
756
 */
757
function createNodeData(
758
    string $html,
759
    string $badgeHtml,
760
    int $restricted,
761
    string $folderClass,
762
    bool $showButBlock,
763
    bool $hideNode,
764
    int $isPersonalFolder,
765
    string $title = ''
766
): array {
767
    return [
768
        'html' => $html . $badgeHtml,
769
        'title' => $title,
770
        'restricted' => $restricted,
771
        'folderClass' => $folderClass,
772
        'show_but_block' => $showButBlock,
773
        'hide_node' => $hideNode,
774
        'is_pf' => $isPersonalFolder,
775
    ];
776
}
777
778
779
/**
780
 * Permits to check if we can user a cache instead of loading from DB
781
 *
782
 * @param integer $lastTreeChange
783
 * @param integer $userTreeLastRefresh
784
 * @param array $userSessionTreeStructure
785
 * @param integer $userId
786
 * @param integer $forceRefresh
787
 * @param array $SETTINGS
788
 * @return array
789
 */
790
function loadTreeStrategy(
791
    int $lastTreeChange,
792
    int $userTreeLastRefresh,
793
    array $userSessionTreeStructure,
794
    int $userId,
795
    int $forceRefresh
796
): array
797
{
798
    // Case when refresh is EXPECTED / MANDATORY
799
    if ((int) $forceRefresh === 1) {
800
        return [
801
            'state' => true,
802
            'data' => [],
803
        ];
804
    }
805
806
    // Case when an update in the tree has been done
807
    // Refresh is then mandatory
808
    if ((int) $lastTreeChange > (int) $userTreeLastRefresh) {
809
        return [
810
            'state' => true,
811
            'data' => [],
812
        ];
813
    }
814
815
    // Does this user has the tree structure in session?
816
    // If yes then use it
817
    if (count($userSessionTreeStructure) > 0) {
818
        return [
819
            'state' => false,
820
            'data' => json_encode($userSessionTreeStructure),
821
        ];
822
    }
823
824
    // Does this user has a tree cache
825
    $userCacheTree = DB::queryfirstrow(
826
        'SELECT data
827
        FROM ' . prefixTable('cache_tree') . '
828
        WHERE user_id = %i',
829
        $userId
830
    );
831
    if (empty($userCacheTree['data']) === false && $userCacheTree['data'] !== '[]') {
832
        return [
833
            'state' => false,
834
            'data' => $userCacheTree['data'],
835
        ];
836
    }
837
838
    return [
839
        'state' => true,
840
        'data' => [],
841
    ];
842
}