Completed
Push — master ( 624b69...a6a42f )
by Andreas
18:32
created

midcom_helper_nav::get_leaf_uplink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package midcom.helper
4
 * @author The Midgard Project, http://www.midgard-project.org
5
 * @copyright The Midgard Project, http://www.midgard-project.org
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
/**
10
 * Main Navigation interface class.
11
 *
12
 * Basically, this class proxies all requests to a midcom_helper_nav_backend
13
 * class. See the interface definition of it for further details.
14
 *
15
 * Additionally this class implements a couple of helper functions to make
16
 * common NAP tasks easier.
17
 *
18
 * <b>Important note:</b> Whenever you add new code to this class, or extend it through
19
 * inheritance, never call the proxy-functions of the backend directly, this is strictly
20
 * forbidden.
21
 *
22
 * @todo End-User documentation of node and leaf data, as the one in the backend is incomplete too.
23
 * @package midcom.helper
24
 * @see midcom_helper_nav_backend
25
 */
26
class midcom_helper_nav
27
{
28
    /**
29
     * The backend instance in use.
30
     *
31
     * @var midcom_helper_nav_backend
32
     */
33
    private $_backend;
34
35
    /**
36
     * The cache of instantiated NAP backends
37
     *
38
     * @var array
39
     */
40
    private static $_backends = [];
41
42
    /**
43
     * The context ID we're associated with.
44
     *
45
     * @var midcom_core_context
46
     */
47
    private $context;
48
49
    /**
50
     * Create a NAP instance for the currently active context
51
     */
52 422
    public function __construct()
53
    {
54 422
        $this->context = midcom_core_context::get();
55 422
        $this->_backend = $this->_get_backend();
56 422
    }
57
58
    /**
59
     * This function maintains one NAP Class per context. Usually this is enough,
60
     * since you mostly will access it in context 0, the default. The problem is, that
61
     * this is not 100% efficient: If you instantiate two different NAP Classes in
62
     * different contexts both referring to the same root node, you will get two
63
     * different instances.
64
     *
65
     * @see midcom_helper_nav
66
     */
67 422
    private function _get_backend() : midcom_helper_nav_backend
68
    {
69 422
        if (!isset(self::$_backends[$this->context->id])) {
70 278
            $root = $this->context->get_key(MIDCOM_CONTEXT_ROOTTOPIC);
71 278
            $urltopics = $this->context->get_key(MIDCOM_CONTEXT_URLTOPICS);
72 278
            self::$_backends[$this->context->id] = new midcom_helper_nav_backend($root, $urltopics);
0 ignored issues
show
Bug introduced by
It seems like $urltopics can also be of type false; however, parameter $urltopics of midcom_helper_nav_backend::__construct() does only seem to accept array, 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

72
            self::$_backends[$this->context->id] = new midcom_helper_nav_backend($root, /** @scrutinizer ignore-type */ $urltopics);
Loading history...
Bug introduced by
It seems like $root can also be of type false; however, parameter $root of midcom_helper_nav_backend::__construct() does only seem to accept midcom_db_topic, 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

72
            self::$_backends[$this->context->id] = new midcom_helper_nav_backend(/** @scrutinizer ignore-type */ $root, $urltopics);
Loading history...
73
        }
74
75 422
        return self::$_backends[$this->context->id];
76
    }
77
78
    /* The following methods are just interfaces to midcom_helper_nav_backend */
79
80
    /**
81
     * Retrieve the ID of the currently displayed node. Defined by the topic of
82
     * the component that declared able to handle the request.
83
     *
84
     * @return int    The ID of the node in question.
85
     * @see midcom_helper_nav_backend::get_current_node()
86
     */
87 230
    public function get_current_node()
88
    {
89 230
        return $this->_backend->get_current_node();
90
    }
91
92
    /**
93
     * Retrieve the ID of the currently displayed leaf. This is a leaf that is
94
     * displayed by the handling topic. If no leaf is active, this function
95
     * returns false. (Remember to make a type sensitive check, e.g.
96
     * nav::get_current_leaf() !== false to distinguish '0' and 'false'.)
97
     *
98
     * @return int    The ID of the leaf in question or false on failure.
99
     * @see midcom_helper_nav_backend::get_current_leaf()
100
     */
101 250
    public function get_current_leaf()
102
    {
103 250
        return $this->_backend->get_current_leaf();
104
    }
105
106
    /**
107
     * Retrieve the ID of the root node. Note that this ID is dependent from the
108
     * ID of the MidCOM Root topic and therefore will change as easily as the
109
     * root topic ID might. The MIDCOM_NAV_URL entry of the root node's data will
110
     * always be empty.
111
     *
112
     * @see midcom_helper_nav_backend::get_root_node()
113
     */
114 89
    public function get_root_node() : int
115
    {
116 89
        return $this->_backend->get_root_node();
117
    }
118
119
    /**
120
     * Lists all Sub-nodes of $parent_node. If there are no subnodes you will get
121
     * an empty array, if there was an error (for instance an unknown parent node
122
     * ID) you will get false.
123
     *
124
     * @param int $parent_node    The id of the node of which the subnodes are searched.
125
     * @param boolean $show_noentry Show all objects on-site which have the noentry flag set.
126
     *     This defaults to false.
127
     * @see midcom_helper_nav_backend::list_nodes()
128
     */
129 22
    public function list_nodes($parent_node, bool $show_noentry = false) : array
130
    {
131 22
        return $this->_backend->list_nodes($parent_node, $show_noentry);
132
    }
133
134
    /**
135
     * Lists all leaves of $parent_node. If there are no leaves you will get an
136
     * empty array, if there was an error (for instance an unknown parent node ID)
137
     * you will get false.
138
     *
139
     * @param int $parent_node    The ID of the node of which the leaves are searched.
140
     * @param boolean $show_noentry Show all objects on-site which have the noentry flag set.
141
     *     This defaults to false.
142
     * @see midcom_helper_nav_backend::list_leaves()
143
     */
144 19
    public function list_leaves($parent_node, $show_noentry = false) : array
145
    {
146 19
        return $this->_backend->list_leaves($parent_node, $show_noentry);
147
    }
148
149
    /**
150
     * This will give you a key-value pair describing the node with the ID
151
     * $node_id. The defined keys are described above in Node data interchange
152
     * format. You will get false if the node ID is invalid.
153
     *
154
     * @param int $node_id    The node ID to be retrieved.
155
     * @return Array        The node data as outlined in the class introduction, false on failure
156
     * @see midcom_helper_nav_backend::get_node()
157
     */
158 294
    public function get_node($node_id)
159
    {
160 294
        return $this->_backend->get_node($node_id);
161
    }
162
163
    /**
164
     * This will give you a key-value pair describing the leaf with the ID
165
     * $node_id. The defined keys are described above in leaf data interchange
166
     * format. You will get false if the leaf ID is invalid.
167
     *
168
     * @param string $leaf_id    The leaf-id to be retrieved.
169
     * @return Array        The leaf-data as outlined in the class introduction, false on failure
170
     * @see midcom_helper_nav_backend::get_leaf()
171
     */
172 36
    public function get_leaf($leaf_id)
173
    {
174 36
        return $this->_backend->get_leaf($leaf_id);
175
    }
176
177
    /**
178
     * Returns the ID of the node to which $leaf_id is associated to, false
179
     * on failure.
180
     *
181
     * @param string $leaf_id    The Leaf-ID to search an uplink for.
182
     * @return int             The ID of the Node for which we have a match, or false on failure.
183
     * @see midcom_helper_nav_backend::get_leaf_uplink()
184
     */
185
    function get_leaf_uplink($leaf_id)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
186
    {
187
        return $this->_backend->get_leaf_uplink($leaf_id);
188
    }
189
190
    /**
191
     * Returns the ID of the node to which $node_id is associated to, false
192
     * on failure. The root node's uplink is -1.
193
     *
194
     * @param int $node_id    The Leaf-ID to search an uplink for.
195
     * @return int             The ID of the Node for which we have a match, -1 for the root node, or false on failure.
196
     * @see midcom_helper_nav_backend::get_node_uplink()
197
     */
198 67
    public function get_node_uplink($node_id)
199
    {
200 67
        return $this->_backend->get_node_uplink($node_id);
201
    }
202
203
    /**
204
     * Checks if the given node is within the tree of another node.
205
     *
206
     * @param int    $node_id    The node in question.
207
     * @param int    $root_id    The root node to use.
208
     */
209 67
    public function is_node_in_tree($node_id, $root_id) : bool
210
    {
211 67
        $uplink = $this->get_node_uplink($node_id);
212 67
        if ($uplink == $root_id) {
213 18
            return true;
214
        }
215 52
        if (in_array($uplink, [false, -1])) {
216 52
            return false;
217
        }
218 11
        return $this->is_node_in_tree($uplink, $root_id);
219
    }
220
221
    /**
222
     * List all child elements, nodes and leaves alike, of the node with ID
223
     * $parent_node_id. For every child element, an array of ID and type (node/leaf)
224
     * is given as
225
     *
226
     * - MIDCOM_NAV_ID => 0,
227
     * - MIDCOM_NAV_TYPE => 'node'
228
     *
229
     * If there are no child elements at all the method will return an empty array,
230
     * in case of an error false.  NOTE: This method should be quite slow, there's
231
     * room for improvement... :-)
232
     *
233
     * @param int $parent_node_id    The ID of the parent node.
234
     * @return Array                A list of found elements, or null on failure.
235
     */
236 11
    public function list_child_elements($parent_node_id) : ?array
237
    {
238 11
        $parent_node = $this->get_node($parent_node_id);
239 11
        if (!$parent_node) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parent_node of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
240
            return null;
241
        }
242
243 11
        $guid = $parent_node[MIDCOM_NAV_GUID];
244 11
        $navorder = (int) midcom_db_parameter::get_by_objectguid($guid, 'midcom.helper.nav', 'navorder');
245 11
        if ($navorder == MIDCOM_NAVORDER_ARTICLESFIRST) {
246
            $navorder = 'articlesfirst';
247 11
        } elseif ($navorder == MIDCOM_NAVORDER_SCORE) {
248
            $navorder = 'score';
249
        } else {
250 11
            $navorder = 'topicsfirst';
251
        }
252
253 11
        $nav_object = midcom_helper_nav_itemlist::factory($navorder, $this, $parent_node_id);
254 11
        return $nav_object->get_sorted_list();
255
    }
256
257
    /**
258
     * Try to resolve a guid into a NAP object.
259
     *
260
     * The code is optimized trying to avoid a full-scan if possible. To do this it
261
     * will treat topic and article guids specially: In both cases the system will
262
     * translate it using the topic id into a node id and scan only that part of the
263
     * tree non-recursively.
264
     *
265
     * A full scan of the NAP data is only done if another MidgardObject is used.
266
     *
267
     * Note: If you want to resolve a GUID you got from a Permalink, use the Permalinks
268
     * service within MidCOM, as it covers more objects than the NAP listings.
269
     *
270
     * @param string $guid The GUID of the object to be looked up.
271
     * @param boolean $node_is_sufficient if we could return a good guess of correct parent node but said node does not list the $guid in leaves return the node or try to do a full (and very expensive) NAP scan ?
272
     * @return mixed Either a node or leaf structure, distinguishable by MIDCOM_NAV_TYPE, or false on failure.
273
     * @see midcom_services_permalinks
274
     */
275 28
    public function resolve_guid($guid, $node_is_sufficient = false)
276
    {
277
        // First, check if the GUID is already known by the backend:
278 28
        if ($cached_result = $this->_backend->get_loaded_object_by_guid($guid)) {
279 4
            debug_add('The GUID was already known by the backend instance, returning the cached copy directly.');
280 4
            return $cached_result;
281
        }
282
283
        // Fetch the object in question for a start, so that we know what to do (tm)
284
        // Note, that objects that cannot be resolved will still be processed using a full-scan of
285
        // the tree. This is, for example, used by the on-delete cache invalidation.
286
        try {
287 27
            $object = midcom::get()->dbfactory->get_object_by_guid($guid);
288
        } catch (midcom_error $e) {
289
            debug_add("Could not load GUID {$guid}, trying to continue anyway. Last error was: " . $e->getMessage(), MIDCOM_LOG_WARN);
290
        }
291 27
        if (!empty($object)) {
292 27
            if ($object instanceof midcom_db_topic) {
293
                // Ok. This topic should be within the content tree,
294
                // we check this and return the node if everything is ok.
295 20
                if (!$this->is_node_in_tree($object->id, $this->get_root_node())) {
296 18
                    debug_add("The GUID {$guid} leads to an unknown topic not in our tree.", MIDCOM_LOG_WARN);
297 18
                    return false;
298
                }
299
300 2
                return $this->get_node($object->id);
301
            }
302
303 8
            if ($object instanceof midcom_db_article) {
304
                // Ok, let's try to find the article using the topic in the tree.
305
                if (!$this->is_node_in_tree($object->topic, $this->get_root_node())) {
306
                    debug_add("The GUID {$guid} leads to an unknown topic not in our tree.", MIDCOM_LOG_WARN);
307
                    return false;
308
                }
309
                if ($leaf = $this->_find_leaf_in_topic($object->topic, $guid)) {
310
                    return $leaf;
311
                }
312
313
                debug_add("The Article GUID {$guid} is somehow hidden from the NAP data in its topic, no results shown.", MIDCOM_LOG_INFO);
314
                return false;
315
            }
316
317
            // Ok, unfortunately, this is not an immediate topic. We try to traverse
318
            // upwards in the object chain to find a topic.
319 8
            if ($topic = $this->find_closest_topic($object)) {
320
                debug_add("Found topic #{$topic->id}, searching the leaves");
321
                if ($leaf = $this->_find_leaf_in_topic($topic->id, $guid)) {
322
                    return $leaf;
323
                }
324
                if ($node_is_sufficient) {
325
                    debug_add("Could not find guid in leaves (maybe not listed?), but node is sufficient, returning node");
326
                    return $this->get_node($topic->id);
327
                }
328
            }
329
        }
330
331
        // this is the rest of the lot, we need to traverse everything, unfortunately.
332
        // First, we traverse a list of nodes to be checked on by one, avoiding a recursive
333
        // function call.
334 8
        $unprocessed_node_ids = [$this->get_root_node()];
335
336 8
        while (!empty($unprocessed_node_ids)) {
337 8
            $node_id = array_shift($unprocessed_node_ids);
338
339
            // Check leaves of this node first.
340 8
            if ($leaf = $this->_find_leaf_in_topic($node_id, $guid)) {
341
                return $leaf;
342
            }
343
344
            // Ok, append all subnodes to the queue.
345 8
            $unprocessed_node_ids = array_merge($unprocessed_node_ids, $this->list_nodes($node_id));
346
        }
347
348 8
        debug_add("We were unable to find the GUID {$guid} in the MidCOM tree even with a full scan.", MIDCOM_LOG_INFO);
349 8
        return false;
350
    }
351
352 8
    private function _find_leaf_in_topic(int $topic, string $guid) : ?array
353
    {
354 8
        foreach ($this->list_leaves($topic, true) as $leafid) {
355
            $leaf = $this->get_leaf($leafid);
356
            if ($leaf[MIDCOM_NAV_GUID] == $guid) {
357
                return $leaf;
358
            }
359
        }
360 8
        return null;
361
    }
362
363 253
    public function find_closest_topic(midcom_core_dbaobject $object) : ?midcom_db_topic
364
    {
365 253
        debug_add('Looking for a topic to use via get_parent()');
366 253
        while ($parent = $object->get_parent()) {
0 ignored issues
show
Bug introduced by
The method get_parent() does not exist on null. ( Ignorable by Annotation )

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

366
        while ($parent = $object->/** @scrutinizer ignore-call */ get_parent()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
367 123
            if (is_a($parent, midcom_db_topic::class)) {
368
                // Verify that this topic is within the current sites tree, if it is not,
369
                // we ignore it.
370 47
                if ($this->is_node_in_tree($parent->id, $this->get_root_node())) {
371 16
                    return $parent;
372
                }
373
            }
374 118
            $object = $parent;
375
        }
376 240
        return null;
377
    }
378
379
    /* The more complex interface methods starts here */
380
381
    /**
382
     * Construct a breadcrumb line.
383
     *
384
     * Gives you a line like 'Start > Topic1 > Topic2 > Article' using NAP to
385
     * traverse upwards till the root node. $separator is inserted between the
386
     * pairs, $class, if non-null, will be used as CSS-class for the A-Tags.
387
     *
388
     * The parameter skip_levels indicates how much nodes should be skipped at
389
     * the beginning of the current path. Default is to show the complete path. A
390
     * value of 1 will skip the home link, 2 will skip the home link and the first
391
     * subtopic and so on. If a leaf or node is selected, that normally would be
392
     * hidden, only its name will be shown.
393
     *
394
     * @param string    $separator        The separator to use between the elements.
395
     * @param string    $class            If not-null, it will be assigned to all A tags.
396
     * @param int       $skip_levels      The number of topic levels to skip before starting to work (use this to skip 'Home' links etc.).
397
     * @param string    $current_class    The class that should be assigned to the currently active element.
398
     * @param array     $skip_guids       Array of guids that are skipped.
399
     */
400 9
    public function get_breadcrumb_line($separator = ' &gt; ', $class = null, $skip_levels = 0, $current_class = null, $skip_guids = []) : string
401
    {
402 9
        $breadcrumb_data = $this->get_breadcrumb_data();
403 9
        $result = '';
404
405
        // Detect real starting Node
406 9
        if ($skip_levels > 0) {
407
            if ($skip_levels >= count($breadcrumb_data)) {
408
                debug_add('We were asked to skip all breadcrumb elements that were present (or even more). Returning an empty breadcrumb line therefore.', MIDCOM_LOG_INFO);
409
                return $result;
410
            }
411
            $breadcrumb_data = array_slice($breadcrumb_data, $skip_levels);
412
        }
413
414 9
        $class = $class === null ? '' : ' class="' . $class . '"';
415 9
        while (current($breadcrumb_data) !== false) {
416 9
            $data = current($breadcrumb_data);
417 9
            $entry = htmlspecialchars($data[MIDCOM_NAV_NAME]);
418
419
            // Add the next element sensitive to the fact whether we are at the end or not.
420 9
            if (next($breadcrumb_data) === false) {
421 9
                if ($current_class !== null) {
422 9
                    $entry = "<span class=\"{$current_class}\">{$entry}</span>";
423
                }
424
            } else {
425 8
                if (   !empty($data['napobject'][MIDCOM_NAV_GUID])
426 8
                    && in_array($data['napobject'][MIDCOM_NAV_GUID], $skip_guids)) {
427
                    continue;
428
                }
429
430 8
                $entry = "<a href=\"{$data[MIDCOM_NAV_URL]}\"{$class}>{$entry}</a>{$separator}";
431
            }
432 9
            $result .= $entry;
433
        }
434
435 9
        return $result;
436
    }
437
438
    /**
439
     * Construct source data for a breadcrumb line.
440
     *
441
     * Gives you the data needed to construct a line like
442
     * 'Start > Topic1 > Topic2 > Article' using NAP to
443
     * traverse upwards till the root node. The components custom breadcrumb
444
     * data is inserted at the end of the computed breadcrumb line after any
445
     * set NAP leaf.
446
     *
447
     * See get_breadcrumb_line for a more end-user oriented way of life.
448
     *
449
     * <b>Return Value</b>
450
     *
451
     * The breadcrumb data will be returned as a list of associative arrays each
452
     * containing these keys:
453
     *
454
     * - MIDCOM_NAV_URL The fully qualified URL to the node.
455
     * - MIDCOM_NAV_NAME The clear-text name of the node.
456
     * - MIDCOM_NAV_TYPE One of 'node', 'leaf', 'custom' indicating what type of entry
457
     *   this is.
458
     * - MIDCOM_NAV_ID The Identifier of the structure used to build this entry, this is
459
     *   either a NAP node/leaf ID or the list key set by the component for custom data.
460
     * - 'napobject' This contains the original NAP object retrieved by the function.
461
     *   Just in case you need more information than is available directly.
462
     *
463
     * The entry of every level is indexed by its MIDCOM_NAV_ID, where custom keys preserve
464
     * their original key (as passed by the component) and prefixing it with 'custom-'. This
465
     * allows you to easily check if a given node/leave is within the current breadcrumb-line
466
     * by checking with array_key_exists.
467
     *
468
     * <b>Adding custom data</b>
469
     *
470
     * Custom elements are added to this array by using the MidCOM custom component context
471
     * at this time. You need to add a list with the same structure as above into the
472
     * custom component context key <i>midcom.helper.nav.breadcrumb</i>. (This needs
473
     * to be an array always, even if you return only one element.)
474
     *
475
     * Note, that the URL you pass in that list is always prepended with the current anchor
476
     * prefix. It is not possible to specify absolute URLs there. No leading slash is required.
477
     *
478
     * Example:
479
     *
480
     * <code>
481
     * $tmp = [
482
     *     [
483
     *         MIDCOM_NAV_URL => "list/{$this->_category}/{$this->_mode}/1/",
484
     *         MIDCOM_NAV_NAME => $this->_category_name,
485
     *     ],
486
     * ];
487
     * midcom_core_context::get()->set_custom_key('midcom.helper.nav.breadcrumb', $tmp);
488
     * </code>
489
     */
490 9
    public function get_breadcrumb_data($id = null) : array
491
    {
492 9
        $prefix = $this->context->get_key(MIDCOM_CONTEXT_ANCHORPREFIX);
493 9
        $result = [];
494
495 9
        if (!$id) {
496 9
            $curr_leaf = $this->get_current_leaf();
497 9
            $curr_node = $this->get_current_node();
498
        } else {
499
            $curr_leaf = false;
500
            $curr_node = -1;
501
502
            if ($leaf = $this->get_leaf($id)) {
503
                $curr_leaf = $leaf[MIDCOM_NAV_ID];
504
                $curr_node = $leaf[MIDCOM_NAV_NODEID];
505
            } elseif ($node = $this->get_node($id)) {
506
                $curr_node = $node[MIDCOM_NAV_ID];
507
            }
508
        }
509 9
        foreach ($this->get_node_path($curr_node) as $node_id) {
510 9
            $node = $this->get_node($node_id);
511 9
            $result[$node[MIDCOM_NAV_ID]] = [
512 9
                MIDCOM_NAV_URL => $node[MIDCOM_NAV_ABSOLUTEURL],
513
                MIDCOM_NAV_NAME => $node[MIDCOM_NAV_NAME],
514 9
                MIDCOM_NAV_TYPE => 'node',
515 9
                MIDCOM_NAV_ID => $node_id,
516 9
                'napobject' => $node,
517
            ];
518
        }
519 9
        if ($curr_leaf !== false && $leaf = $this->get_leaf($curr_leaf)) {
520
            // Ignore Index Article Leaves
521
            if ($leaf[MIDCOM_NAV_URL] != '') {
522
                $result[$leaf[MIDCOM_NAV_ID]] = [
523
                    MIDCOM_NAV_URL => $leaf[MIDCOM_NAV_ABSOLUTEURL],
524
                    MIDCOM_NAV_NAME => $leaf[MIDCOM_NAV_NAME],
525
                    MIDCOM_NAV_TYPE => 'leaf',
526
                    MIDCOM_NAV_ID => $curr_leaf,
527
                    'napobject' => $leaf,
528
                ];
529
            }
530
        }
531
532 9
        if (midcom_core_context::get()->has_custom_key('midcom.helper.nav.breadcrumb')) {
533 5
            $customdata = midcom_core_context::get()->get_custom_key('midcom.helper.nav.breadcrumb');
534 5
            if (is_array($customdata)) {
535 5
                foreach ($customdata as $key => $entry) {
536 5
                    $id = "custom-{$key}";
537
538 5
                    $url = "{$prefix}{$entry[MIDCOM_NAV_URL]}";
539 5
                    if (   str_starts_with($entry[MIDCOM_NAV_URL], '/')
540 5
                        || preg_match('|^https?://|', $entry[MIDCOM_NAV_URL])) {
541 5
                        $url = $entry[MIDCOM_NAV_URL];
542
                    }
543
544 5
                    $result[$id] = [
545 5
                        MIDCOM_NAV_URL => $url,
546
                        MIDCOM_NAV_NAME => $entry[MIDCOM_NAV_NAME],
547 5
                        MIDCOM_NAV_TYPE => 'custom',
548 5
                        MIDCOM_NAV_ID => $id,
549 5
                        'napobject' => $entry,
550
                    ];
551
                }
552
            }
553
        }
554 9
        return $result;
555
    }
556
557
    /**
558
     * Retrieve the IDs of the nodes from the URL. First value at key 0 is
559
     * the root node ID, possible second value is the first subnode ID etc.
560
     * Contains only visible nodes (nodes which can be loaded).
561
     */
562 11
    public function get_node_path($node_id = null) : array
563
    {
564 11
        if ($node_id === null) {
565 11
            return $this->_backend->get_node_path();
566
        }
567 9
        $path = [];
568 9
        $node = $this->get_node($node_id);
569 9
        while ($node) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $node of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
570 9
            $path[] = $node[MIDCOM_NAV_ID];
571 9
            if ($node[MIDCOM_NAV_NODEID] === -1) {
572 9
                break;
573
            }
574 5
            $node = $this->get_node($node[MIDCOM_NAV_NODEID]);
575
        }
576 9
        return array_reverse($path);
577
    }
578
579
    /**
580
     * Retrieve the ID of the upper node of the currently displayed node.
581
     *
582
     * @return mixed    The ID of the node in question.
583
     */
584 1
    public function get_current_upper_node()
585
    {
586 1
        return $this->_backend->get_current_upper_node();
587
    }
588
}
589