midcom_helper_toolbar::get_label()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
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
 * This class is a generic toolbar class. It supports enabling
11
 * and disabling of buttons, icons and hover-helptexts (currently
12
 * rendered using TITLE tags).
13
 *
14
 * A single button in the toolbar is represented using an associative
15
 * array with the following elements:
16
 *
17
 * <code>
18
 * $item = [
19
 *     MIDCOM_TOOLBAR_URL => $url,
20
 *     MIDCOM_TOOLBAR_LABEL => $label,
21
 *     MIDCOM_TOOLBAR_HELPTEXT => $helptext,
22
 *     MIDCOM_TOOLBAR_ICON => $icon,
23
 *     MIDCOM_TOOLBAR_ENABLED => $enabled,
24
 *     MIDCOM_TOOLBAR_HIDDEN => $hidden
25
 *     MIDCOM_TOOLBAR_OPTIONS => array $options,
26
 *     MIDCOM_TOOLBAR_SUBMENU => midcom_helper_toolbar $submenu,
27
 *     MIDCOM_TOOLBAR_ACCESSKEY => (char) 'a',
28
 *     MIDCOM_TOOLBAR_POST => true,
29
 *     MIDCOM_TOOLBAR_POST_HIDDENARGS => array $args,
30
 * ];
31
 * </code>
32
 *
33
 * The URL parameter can be interpreted in three different ways:
34
 * If it is a relative URL (not starting with 'http[s]://' or at least
35
 * a '/') it will be interpreted relative to the current Anchor
36
 * Prefix as defined in the active MidCOM context. Otherwise, the URL
37
 * is used as-is. Note, that the Anchor-Prefix is appended immediately
38
 * when the item is added, not when the toolbar is rendered.
39
 *
40
 * The original URL (before prepending anything) is stored internally;
41
 * so in all places where you reference an element by-URL, you can use
42
 * the original URL if you wish (actually, both URLs are recognized
43
 * during the translation into an id).
44
 *
45
 * The label is the text shown as the button, the helptext is used as
46
 * TITLE value to the anchor, and will be shown when hovering over the
47
 * link therefore. Set it to null, to suppress this feature (this is the
48
 * default).
49
 *
50
 * The icon is a relative URL within the static MidCOM tree, for example
51
 * 'stock-icons/16x16/attach.png'. Set it to null, to suppress the display
52
 * of an icon (this is the default)
53
 *
54
 * By default, as shown below, the toolbar system renders a standard Hyperlink.
55
 * If you set MIDCOM_TOOLBAR_POST to true however, a form is used instead.
56
 * This is important if you want to provide operations directly behind the
57
 * toolbar entries - you'd run into problems with HTTP Link Prefetching
58
 * otherwise. It is also useful if you want to pass complex operations
59
 * to the URL target, as the option MIDCOM_TOOLBAR_POST_HIDDENARGS allows
60
 * you to add HIDDEN variables to the form. These arguments will be automatically
61
 * run through htmlspecialchars when rendering. By default, standard links will
62
 * be rendered, POST versions will only be used if explicitly requested.
63
 *
64
 * Note, that while this should prevent link prefetching on the POST entries,
65
 * this is a big should. Due to its lack of standardization, it is strongly
66
 * recommended to check for a POST request when processing such toolbar
67
 * targets, using something like this:
68
 *
69
 * <code>
70
 * if ($_SERVER['REQUEST_METHOD'] != 'post')
71
 * {
72
 *     throw new midcom_error_forbidden('Only POST requests are allowed here.');
73
 * }
74
 * </code>
75
 *
76
 * The enabled boolean flag is set to true (the default) if the link should
77
 * be clickable, or to false otherwise.
78
 *
79
 * The hidden boolean flag is very similar to the enabled one: Instead of
80
 * having unclickable links, it just hides the toolbar button entirely.
81
 * This is useful for access control checks, where you want to completely
82
 * hide items without access. The difference from just not adding the
83
 * corresponding variable is that you can have a consistent set of
84
 * toolbar options in a "template" which you just need to tweak by
85
 * setting this flag. (Note, that there is no explicit access
86
 * control checks in the toolbar helper itself, as this would mean that
87
 * the corresponding content objects need to be passed into the toolbar,
88
 * which is not feasible with the large number of toolbars in use in NAP
89
 * for example.)
90
 *
91
 * The midcom_toolbar_submenu can be used to create nested submenus by adding a pointer
92
 * to a new toolbar object.
93
 *
94
 * The toolbar gets rendered as an unordered list, letting you define the
95
 * CSS id and/or class tags of the list itself. The default class for
96
 * example used the well-known horizontal-UL approach to transform this
97
 * into a real toolbar. The output of the draw call therefore looks like
98
 * this:
99
 *
100
 * The <b>accesskey</b> option is used to assign an accesskey to the toolbar item.
101
 * It will be rendered in the toolbar text as either underlining the key or stated in
102
 * parentheses behind the text.
103
 *
104
 * <pre>
105
 * &lt;ul [class="$class"] [id="$id"]&gt;
106
 *   &lt;li class="(enabled|disabled)"&gt;
107
 *     [&lt;a href="$url" [title="$helptext"] [ $options as $key =&gt; $val ]&gt;]
108
 *       [&lt;img src="$calculated_image_url"&gt;]
109
 *       $label
110
 *      [new submenu here]
111
 *     [&lt;/a&gt;]
112
 *   &lt;/li&gt;
113
 * &lt;/ul&gt;
114
 * </pre>
115
 *
116
 * Both class and id can be null, indicating no style should be selected.
117
 * By default, the class will use "midcom_toolbar" and no id style, which
118
 * will yield a traditional MidCOM toolbar. Of course, the
119
 * style sheet must be loaded to support this. Note, that this style assumes
120
 * 16x16 height icons in its toolbar rendering. Larger or smaller icons
121
 * will look ugly in the layout.
122
 *
123
 * The options array. You can use the options array to make simple changes to the toolbar items.
124
 * Here's a quick example to remove the underlining.
125
 * <code>
126
 * foreach ($toolbar->items as $index => $item) {
127
 *     $toolbar->items[$index][MIDCOM_TOOLBAR_OPTIONS] = [ "style" => "text-decoration:none;"];
128
 * }
129
 * </code>
130
 * This will add style="text-decoration:none;" to all the links in the toolbar.
131
 *
132
 * @package midcom.helper
133
 */
134
class midcom_helper_toolbar
135
{
136
    /**
137
     * The CSS ID-Style rule that should be used for the toolbar.
138
     * Set to null if none should be used.
139
     */
140
    public ?string $id_style;
141
142
    /**
143
     * The CSS class-Style rule that should be used for the toolbar.
144
     * Set to null if none should be used.
145
     */
146
    public string $class_style;
147
148
    /**
149
     * The toolbar's label
150
     */
151
    protected string $label = '';
152
153
    /**
154
     * The items in the toolbar.
155
     *
156
     * The array consists of Arrays outlined in the class introduction.
157
     * You can modify existing items in this collection but you should use
158
     * the class methods to add or delete existing items. Also note that
159
     * relative URLs are processed upon the invocation of add_item(), if
160
     * you change URL manually, you have to ensure a valid URL by yourself
161
     * or use update_item_url, which is recommended.
162
     */
163
    public array $items = [];
164
165
    /**
166
     * Allow our users to add arbitrary data to the toolbar.
167
     *
168
     * This is for example used to track which items have been added to a toolbar
169
     * when it is possible that the adders are called repeatedly.
170
     *
171
     * The entries should be namespaced according to the usual MidCOM
172
     * Namespacing rules.
173
     */
174
    public array $customdata = [];
175
176
    private bool $rendered = false;
177
178
    /**
179
     * Basic constructor, initializes the class and sets defaults for the
180
     * CSS style if omitted.
181
     *
182
     * Note that the styles can be changed after construction by updating
183
     * the id_style and class_style members.
184
     */
185 353
    public function __construct(string $class_style = 'midcom_toolbar', ?string $id_style = null)
186
    {
187 353
        $this->id_style = $id_style;
188 353
        $this->class_style = $class_style;
189
    }
190
191 7
    public function is_rendered() : bool
192
    {
193 7
        return $this->rendered;
194
    }
195
196 7
    public function get_label() : string
197
    {
198 7
        return $this->label;
199
    }
200
201 52
    public function set_label(string $label)
202
    {
203 52
        $this->label = $label;
204
    }
205
206
    /**
207
     * Add an item to the toolbar.
208
     *
209
     * Set before to the index of the element before which you want to insert
210
     * the item or use -1 if you want to append an item. Alternatively,
211
     * instead of specifying an index, you can specify a URL instead.
212
     *
213
     * This member will process the URL and append the anchor prefix in case
214
     * the URL is a relative one.
215
     *
216
     * Invalid positions will result in a MidCOM Error.
217
     *
218
     * @param string|int $before The index before which the item should be inserted.
219
     *     Use -1 for appending at the end, use a string to insert
220
     *     it before a URL, an integer will insert it before a
221
     *     given index.
222
     * @see midcom_helper_toolbar::get_index_from_url()
223
     * @see midcom_helper_toolbar::_check_index()
224
     * @see midcom_helper_toolbar::clean_item()
225
     */
226 188
    public function add_item(array $item, string|int $before = -1)
227
    {
228 188
        if ($before != -1) {
229 1
            $before = $this->_check_index($before, false);
230
        }
231 188
        $item = $this->clean_item($item);
232
233 188
        if ($before == -1) {
234 188
            $this->items[] = $item;
235 1
        } elseif ($before == 0) {
236 1
            array_unshift($this->items, $item);
237
        } else {
238 1
            $start = array_slice($this->items, 0, $before);
239 1
            $start[] = $item;
240 1
            $this->items = array_merge($start, array_slice($this->items, $before));
241
        }
242
    }
243
244
    /**
245
     * Convenience shortcut to add multiple buttons at the same item
246
     *
247
     * @param string|int $before The index before which the item should be inserted.
248
     *     Use -1 for appending at the end, use a string to insert
249
     *     it before a URL, an integer will insert it before a
250
     *     given index.
251
     */
252 117
    public function add_items(array $items, string|int $before = -1)
253
    {
254 117
        foreach ($items as $item) {
255 112
            $this->add_item($item, $before);
256
        }
257
    }
258
259
    /**
260
     * Clean up an item that is added, making sure that the item has all the
261
     * needed options and indexes.
262
     */
263 189
    public function clean_item(array $item) : array
264
    {
265 189
        static $used_access_keys = [];
266
267 189
        $defaults = [
268 189
            MIDCOM_TOOLBAR_URL => './',
269 189
            MIDCOM_TOOLBAR_OPTIONS => [],
270 189
            MIDCOM_TOOLBAR_HIDDEN => false,
271 189
            MIDCOM_TOOLBAR_HELPTEXT => '',
272 189
            MIDCOM_TOOLBAR_ICON => null,
273 189
            MIDCOM_TOOLBAR_GLYPHICON => null,
274 189
            MIDCOM_TOOLBAR_ENABLED => true,
275 189
            MIDCOM_TOOLBAR_POST => false,
276 189
            MIDCOM_TOOLBAR_POST_HIDDENARGS => [],
277 189
            MIDCOM_TOOLBAR_ACCESSKEY => null
278 189
        ];
279
280 189
        $item = array_replace($defaults, $item);
281
282 189
        if (   !empty($item[MIDCOM_TOOLBAR_ACCESSKEY])
283 189
            && !array_key_exists($item[MIDCOM_TOOLBAR_ACCESSKEY], $used_access_keys)) {
284
            // We have valid access key, add it to help text
285 6
            $prefix = 'Alt-';
286 6
            if (str_contains($_SERVER['HTTP_USER_AGENT'] ?? '', 'Macintosh')) {
287
                // Mac users
288
                $prefix = 'Ctrl-Alt-';
289
            }
290 6
            $hotkey = $prefix . strtoupper($item[MIDCOM_TOOLBAR_ACCESSKEY]);
291
292 6
            if ($item[MIDCOM_TOOLBAR_HELPTEXT] == '') {
293 6
                $item[MIDCOM_TOOLBAR_HELPTEXT] = $hotkey;
294
            } else {
295
                $item[MIDCOM_TOOLBAR_HELPTEXT] .= " ({$hotkey})";
296
            }
297 6
            $used_access_keys[$item[MIDCOM_TOOLBAR_ACCESSKEY]] = true;
298
        }
299
300 189
        $this->set_url($item, $item[MIDCOM_TOOLBAR_URL]);
301 189
        return $item;
302
    }
303
304 189
    private function set_url(array &$item, string $url)
305
    {
306 189
        $item[MIDCOM_TOOLBAR__ORIGINAL_URL] = $url;
307 189
        if (   (   empty($item[MIDCOM_TOOLBAR_OPTIONS]["rel"])
308
                // Some items may want to keep their links unmutilated
309 189
                || $item[MIDCOM_TOOLBAR_OPTIONS]["rel"] != "directlink")
310 189
            && !str_starts_with($url, '/')
311 189
            && !preg_match('|^https?://|', $url)) {
312 161
            $url = midcom_core_context::get()->get_key(MIDCOM_CONTEXT_ANCHORPREFIX) . $url;
0 ignored issues
show
Bug introduced by
Are you sure midcom_core_context::get...M_CONTEXT_ANCHORPREFIX) of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

312
            $url = /** @scrutinizer ignore-type */ midcom_core_context::get()->get_key(MIDCOM_CONTEXT_ANCHORPREFIX) . $url;
Loading history...
313
        }
314 189
        $item[MIDCOM_TOOLBAR_URL] = $url;
315
    }
316
317
    /**
318
     * Removes a toolbar item based on its index or its URL
319
     *
320
     * It will trigger a MidCOM Error upon an invalid index.
321
     *
322
     * @param string|int $index The (integer) index or URL to remove.
323
     * @see midcom_helper_toolbar::get_index_from_url()
324
     * @see midcom_helper_toolbar::_check_index()
325
     */
326 1
    public function remove_item(string|int $index)
327
    {
328 1
        $index = $this->_check_index($index);
329
330 1
        if ($index == 0) {
331
            array_shift($this->items);
332 1
        } elseif ($index == count($this->items) -1) {
333
            array_pop($this->items);
334
        } else {
335 1
            $this->items = array_merge(array_slice($this->items, 0, $index),
336 1
                array_slice($this->items, $index + 1));
337
        }
338
    }
339
340
    /**
341
     * Clears the complete toolbar.
342
     */
343
    public function remove_all_items()
344
    {
345
        $this->items = [];
346
    }
347
348
    /**
349
     * Set's an item's enabled flag to true.
350
     *
351
     * @param string|int $index The integer index or URL of the item to enable.
352
     */
353
    public function enable_item(string|int $index)
354
    {
355
        $index = $this->_check_index($index);
356
        $this->items[$index][MIDCOM_TOOLBAR_ENABLED] = true;
357
    }
358
359
    /**
360
     * Set's an item's enabled flag to false.
361
     *
362
     * @param string|int $index The integer index or URL of the item to disable.
363
     */
364 12
    public function disable_item(string|int $index)
365
    {
366 12
        $index = $this->_check_index($index, false);
367
368 12
        if ($index !== null) {
369 12
            $this->items[$index][MIDCOM_TOOLBAR_ENABLED] = false;
370
        }
371
    }
372
373
    /**
374
     * Set's an item's hidden flag to true.
375
     *
376
     * @param string|int $index The integer index or URL of the item to hide.
377
     */
378 5
    public function hide_item(string|int $index)
379
    {
380 5
        $index = $this->_check_index($index, false);
381
382 5
        if ($index !== null) {
383 5
            $this->items[$index][MIDCOM_TOOLBAR_HIDDEN] = true;
384
        }
385
    }
386
387
    /**
388
     * Set's an item's hidden flag to false.
389
     *
390
     * @param string|int $index The integer index or URL of the item to show.
391
     */
392
    public function show_item(string|int $index)
393
    {
394
        $index = $this->_check_index($index);
395
        $this->items[$index][MIDCOM_TOOLBAR_HIDDEN] = false;
396
    }
397
398
    /**
399
     * Updates an items URL using the same rules as in add_item.
400
     *
401
     * @param mixed $index The integer index or URL of the item to update.
402
     * @see midcom_helper_toolbar::get_index_from_url()
403
     * @see midcom_helper_toolbar::_check_index()
404
     * @see midcom_helper_toolbar::add_item()
405
     */
406
    public function update_item_url(string|int $index, string $url)
407
    {
408
        $index = $this->_check_index($index);
409
        $this->set_url($this->items[$index], $url);
410
    }
411
412
    /**
413
     * Renders the toolbar and returns it as a string.
414
     */
415 20
    public function render() : string
416
    {
417 20
        $visible_items = array_filter($this->items, function ($item) {
418 15
            return !$item[MIDCOM_TOOLBAR_HIDDEN];
419 20
        });
420 20
        $this->rendered = true;
421
422 20
        if (empty($visible_items)) {
423 12
            debug_add('Tried to render an empty toolbar, returning an empty string.');
424 12
            return '';
425
        }
426
427
        // List header
428 15
        $output = '<ul';
429 15
        if ($this->class_style !== null) {
430 15
            $output .= " class='{$this->class_style}'";
431
        }
432 15
        if ($this->id_style !== null) {
433
            $output .= " id='{$this->id_style}'";
434
        }
435 15
        $output .= '>';
436
437 15
        $last = count($visible_items);
438 15
        $first_class = ($last === 1) ? 'only_item' : 'first_item';
439
        // List items
440 15
        foreach ($visible_items as $i => $item) {
441 15
            $output .= '<li class="';
442 15
            if ($i == 0) {
443 15
                $output .= $first_class . ' ';
444 13
            } elseif ($i == $last) {
445
                $output .= 'last_item ';
446
            }
447
448 15
            if ($item[MIDCOM_TOOLBAR_ENABLED]) {
449 15
                $output .= 'enabled">';
450
            } else {
451 11
                $output .= 'disabled">';
452
            }
453
454 15
            if ($item[MIDCOM_TOOLBAR_POST]) {
455
                $output .= $this->_render_post_item($item);
456
            } else {
457 15
                $output .= $this->_render_link_item($item);
458
            }
459 15
            if (!empty($item[MIDCOM_TOOLBAR_SUBMENU])) {
460
                $output .= $item[MIDCOM_TOOLBAR_SUBMENU]->render();
461
            }
462
463 15
            $output .= '</li>';
464
        }
465
466
        // List footer
467 15
        $output .= '</ul>';
468
469 15
        return $output;
470
    }
471
472
    /**
473
     * Generate a label for the item that includes its accesskey
474
     */
475 15
    private function _generate_item_label(array $item) : string
476
    {
477 15
        $label = htmlentities($item[MIDCOM_TOOLBAR_LABEL], ENT_COMPAT, "UTF-8");
478
479 15
        if (!empty($item[MIDCOM_TOOLBAR_ACCESSKEY])) {
480
            // Try finding uppercase version of the accesskey first
481 8
            $accesskey = strtoupper($item[MIDCOM_TOOLBAR_ACCESSKEY]);
482 8
            $position = strpos($label, $accesskey);
483 8
            if (   $position === false
484 8
                && midcom::get()->i18n->get_current_language() == 'en') {
485
                // Try lowercase, too
486 7
                $accesskey = strtolower($accesskey);
487 7
                $position = strpos($label, $accesskey);
488
            }
489 8
            if ($position !== false) {
490 8
                $label = substr_replace($label, "<span style=\"text-decoration: underline;\">{$accesskey}</span>", $position, 1);
491
            }
492
        }
493
494 15
        return $label;
495
    }
496
497
    /**
498
     * Render a regular a href... based link target.
499
     */
500 15
    private function _render_link_item(array $item) : string
501
    {
502 15
        $attributes = $this->get_item_attributes($item);
503
504 15
        if ($item[MIDCOM_TOOLBAR_ENABLED]) {
505 15
            $tagname = 'a';
506 15
            $attributes['href'] = $item[MIDCOM_TOOLBAR_URL];
507
        } else {
508 11
            $tagname = !empty($attributes['title']) ? 'abbr' : 'span';
509
        }
510
511 15
        $output = '<' . $tagname;
512 15
        foreach ($attributes as $key => $val) {
513 15
            $output .= ' ' . $key . '="' . htmlspecialchars($val) . '"';
514
        }
515 15
        $output .= '>';
516
517 15
        $output .= $this->render_icon($item);
518 15
        $output .= '&nbsp;<span class="toolbar_label">' . $this->_generate_item_label($item) . "</span>";
519 15
        return $output . '</' . $tagname . '>';
520
    }
521
522 15
    private function render_icon(array $item) : string
523
    {
524 15
        if ($item[MIDCOM_TOOLBAR_GLYPHICON] !== null) {
525 15
            $class = 'fa fa-' . $item[MIDCOM_TOOLBAR_GLYPHICON];
526 15
            return "<i class='{$class}'></i>";
527
        }
528
        if ($item[MIDCOM_TOOLBAR_ICON] !== null) {
529
            $url = MIDCOM_STATIC_URL . '/' . $item[MIDCOM_TOOLBAR_ICON];
530
            return "<img src='{$url}' alt=\"{$item[MIDCOM_TOOLBAR_HELPTEXT]}\" />";
531
        }
532
        return '';
533
    }
534
535 15
    private function get_item_attributes(array $item) : array
536
    {
537 15
        $attributes = ($item[MIDCOM_TOOLBAR_ENABLED]) ? $item[MIDCOM_TOOLBAR_OPTIONS] : [];
538
539 15
        if ($item[MIDCOM_TOOLBAR_HELPTEXT] !== null) {
540 15
            $attributes['title'] = $item[MIDCOM_TOOLBAR_HELPTEXT];
541
        }
542
543 15
        if (   $item[MIDCOM_TOOLBAR_ENABLED]
544 15
            && $item[MIDCOM_TOOLBAR_ACCESSKEY] !== null) {
545 8
            $attributes['class'] = 'accesskey';
546 8
            $attributes['accesskey'] = $item[MIDCOM_TOOLBAR_ACCESSKEY];
547
        }
548 15
        return $attributes;
549
    }
550
551
    /**
552
     * Render a form based link target.
553
     */
554
    private function _render_post_item(array $item) : string
555
    {
556
        $output = '';
557
558
        if ($item[MIDCOM_TOOLBAR_ENABLED]) {
559
            $output .= "<form method=\"post\" action=\"{$item[MIDCOM_TOOLBAR_URL]}\">";
560
            $output .= "<div><button type=\"submit\" name=\"midcom_helper_toolbar_submit\"";
561
562
            foreach ($this->get_item_attributes($item) as $key => $val) {
563
                $output .= ' ' . $key . '="' . htmlspecialchars($val) . '"';
564
            }
565
            $output .= '>';
566
        }
567
568
        $output .= $this->render_icon($item);
569
        $output .= ' ' . $this->_generate_item_label($item);
570
571
        if ($item[MIDCOM_TOOLBAR_ENABLED]) {
572
            $output .= '</button>';
573
            foreach ($item[MIDCOM_TOOLBAR_POST_HIDDENARGS] as $key => $value) {
574
                $key = htmlspecialchars($key);
575
                $value = htmlspecialchars($value);
576
                $output .= "<input type=\"hidden\" name=\"{$key}\" value=\"{$value}\"/>";
577
            }
578
            $output .= '</div></form>';
579
        }
580
581
        return $output;
582
    }
583
584
    /**
585
     * Traverse all available items and return the first
586
     * element whose URL matches the value passed to the function.
587
     *
588
     * Note, that if two items point to the same URL, only the first one
589
     * will be reported.
590
     */
591 17
    public function get_index_from_url(string $url) : ?int
592
    {
593 17
        foreach ($this->items as $i => $item) {
594 17
            if (   $item[MIDCOM_TOOLBAR_URL] == $url
595 17
                || $item[MIDCOM_TOOLBAR__ORIGINAL_URL] == $url) {
596 17
                return $i;
597
            }
598
        }
599
        return null;
600
    }
601
602
    /**
603
     * Check an index for validity.
604
     *
605
     * It will automatically convert a string-based URL into an
606
     * Index (if possible); if the URL can't be found, it will
607
     * also trigger an error. The translated URL is returned by the
608
     * function.
609
     *
610
     * @param string|int $index The integer index or URL to check
611
     */
612 19
    protected function _check_index(string|int $index, bool $raise_error = true) :?int
613
    {
614 19
        if (is_string($index)) {
615 17
            $url = $index;
616 17
            debug_add("Translating the URL '{$url}' into an index.");
617 17
            $index = $this->get_index_from_url($url);
618 17
            if ($index === null) {
619
                debug_add("Invalid URL '{$url}', URL not found.", MIDCOM_LOG_ERROR);
620
621
                if ($raise_error) {
622
                    throw new midcom_error("Invalid URL '{$url}', URL not found.");
623
                }
624
                return null;
625
            }
626
        }
627 19
        if ($index >= count($this->items)) {
628
            throw new midcom_error("Invalid index {$index}, it is off-the-end.");
629
        }
630 19
        if ($index < 0) {
631
            throw new midcom_error("Invalid index {$index}, it is negative.");
632
        }
633 19
        return $index;
634
    }
635
}
636