Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<?php
# Lifter010: TODO
/*
* Navigation.php - Stud.IP navigation base class
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* @author Elmar Ludwig
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
* @category Stud.IP
*/
/**
* This is the navigation base class that maintains the global
* navigation structure. All navigation objects are stored in a
* tree and can be accessed by their "path names", just like file
* names in a normal file system. The "root" of the tree is '/'.
*
* So you can do for example:
*
* $navigation = new Navigation('Home', 'index.php');
* $profilenav = new Navigation('Profile', 'profile.php');
*
* $navigation->addSubNavigation('profile', $profilenav);
*
* Navigation::addItem('/home', $navigation);
* Navigation::activateItem('/home/profile');
*/
class Navigation implements IteratorAggregate
{
private static $root;
protected $active;
protected $enabled;
protected $initialized = false;
protected $active_image;
protected $badgeNumber;
protected $badgeTimestamp;
protected $description;
protected $image;
protected $link_attributes = [];
protected $params;
protected $subnav;
protected $title;
protected $url;
/**
* Mark the navigation item at the given path as active.
* This is just a shortcut for doing:
*
* Navigation::getItem($path)->setActive(true)
*
* @param string $path path of navigation item
*/
public static function activateItem($path)
{
self::getItem($path)->setActive(true);
NotificationCenter::postNotification('NavigationDidActivateItem', $path);
}
/**
* Add a new navigation item at the given path. If there is
* already an item with this path, the old one is replaced
* by the new item.
*
* @param string $path path of new navigation item
* @param object $navigation navigation item to add
*/
public static function addItem($path, Navigation $navigation)
{
$nav = self::getItem(strtr(dirname($path), '\\', '/'));
$nav->addSubNavigation(basename($path), $navigation);
}
/**
* Add a new navigation item at the given path. The new
* item is inserted immediately before the item with the
* name given by $where (at the same level in the tree).
*
* @param string $path path of new navigation item
* @param object $navigation navigation item to add
* @param string $where insert it before this item
*/
public static function insertItem($path, Navigation $navigation, $where)
{
$nav = self::getItem(strtr(dirname($path), '\\', '/'));
$nav->insertSubNavigation(basename($path), $navigation, $where);
}
/**
* Remove the navigation item at the given path (if there
* is an item with this path).
*
* @param string $path path of item to remove
*/
public static function removeItem($path)
{
$nav = self::getItem(strtr(dirname($path), '\\', '/'));
$nav->removeSubNavigation(basename($path));
}
/**
* Return the navigation item at the given path.
*
* @param string $path path of navigation item
* @throws InvalidArgumentException if the item cannot be found
*/
public static function getItem($path)
{
$node = self::$root;
foreach (explode('/', $path) as $name) {
if ($name === '') {
continue;
}
$subnav = $node->getSubNavigation();
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
throw new InvalidArgumentException("navigation item '$path' not found");
}
}
return $node;
}
/**
* Test whether there is a navigation item at the given path.
*
* @param string $path path of navigation item
*/
public static function hasItem($path)
{
try {
return self::getItem($path) instanceOf Navigation;
} catch (InvalidArgumentException $ex) {
return false;
}
}
/**
* Set the root of the navigation tree. Must be called before
* any further items can be added to the tree.
*
* @param object $navigation root navigation item
*/
public static function setRootNavigation(Navigation $navigation)
{
self::$root = $navigation;
}
/**
* Initialize a new Navigation instance with the given title
* and URL (optional).
*/
public function __construct($title, $url = NULL, $params = NULL)
{
$this->setTitle($title);
$this->setURL($url, $params);
$this->setEnabled(true);
}
/**
* used to defer initialization of item metadata, override in subclasses
* if initialization is costly
*/
public function initItem()
{
$this->initialized = true;
}
/**
* Return the current image associated with this navigation item.
*
* @return Icon an instance of class Icon depicting this item or NULL
*/
public function getImage()
{
if ($this->initialized === false) {
$this->initItem();
}
if (isset($this->active_image) && $this->isActive()) {
return $this->active_image;
} else {
return $this->image;
}
}
/**
* Shorthand method for creating an appropriate image tag for display.
*
* @return string HTML tag snippet for the image
*/
public function getImageTag()
{
if ($image = $this->getImage()) {
return $image->asImg($this->getLinkAttributes());
} else {
return '';
}
}
/**
* Return the current title associated with this navigation item.
*
* @return string title of item or NULL (no title set)
*/
public function getTitle()
{
if ($this->initialized === false) {
$this->initItem();
}
return $this->title;
}
/**
* Return the description associated with this navigation item.
*
* @return string description of item or NULL (no description set)
*/
public function getDescription()
{
if ($this->initialized === false) {
$this->initItem();
}
return $this->description;
}
/**
* Return the current URL associated with this navigation item.
* If not URL is set but there are subnavigation items, the URL
* of the first visible subnavigation item is returned.
*
* @return string url of item or NULL (no URL set)
*/
public function getURL()
{
if ($this->initialized === false) {
$this->initItem();
}
if (isset($this->url)) {
if (isset($this->params)) {
return URLHelper::getURL($this->url, $this->params);
} else {
return $this->url;
}
}
foreach ($this->getSubNavigation() as $nav) {
$url = $nav->getURL();
if (isset($url)) {
return $url;
}
}
return NULL;
}
/**
* Return the badge number of this navigation item.
*
* @return int the badge number
*/
public function getBadgeNumber()
{
return $this->badgeNumber;
}
/**
* Return the badge number of this navigation item.
*
* @return int the badge number
*/
public function getBadgeTimestamp()
{
return $this->badgeTimestamp;
}
/**
* Determines whether this navigation item has a badge number.
*/
public function hasBadgeNumber()
{
return (boolean) $this->badgeNumber;
}
/**
* Determine whether this navigation item is active.
*/
public function isActive()
{
if (isset($this->active)) {
return $this->active;
}
return (boolean) $this->activeSubNavigation();
}
/**
* Return whether this navigation item is enabled.
*/
public function isEnabled()
{
return $this->enabled;
}
/**
* Return whether this navigation item is visible.
*
* @param boolean $needs_image requires an image
*/
public function isVisible($needs_image = false)
{
if ($needs_image && !$this->getImage()) {
return false;
}
$url = $this->getURL();
return isset($url);
}
/**
* Set the active status of this item. This can be used to
* override heuristics used by the class to determine this
* automatically.
*
* @param boolean $active new active status
*/
public function setActive($active)
{
$this->active = $active;
}
/**
* Set the enabled status of this item. Disabled items are
* still visible but cannot be clicked.
*
* @param boolean $enabled new enabled status
*/
public function setEnabled($enabled)
{
$this->enabled = $enabled;
}
/**
* @param \Icon|null $image an instance of class Icon depicting this item
* or null to remove the image
{
$this->image = $image;
}
/**
* Set the image for the active state of this navigation item.
* If no active image is set, the normal image is used for the
* @param \Icon|null $image an instance of class Icon depicting this item
* or null to remove the image
{
$this->active_image = $image;
}
/**
* Set the attributes that should be used in the link element
* surrounding this item's icon image.
*
* @param array $attributes the attributes to pass to the surrounding link element
*/
public function setLinkAttributes($attributes)
{
$this->link_attributes = $attributes;
}
/**
* Return the attributes that should be used in the link element
* surrounding this item's icon image.
*
* @return array the attributes to pass to the surrounding link element
*/
public function getLinkAttributes()
{
$attributes = $this->link_attributes;
if ($this->isActive()) {
$attributes['aria-current'] = 'page';
}
return $attributes;
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
}
/**
* Set the title of this navigation item.
*
* @param string $title display title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* Set the description of this navigation item.
*
* @param string $description description text
*/
public function setDescription($description)
{
$this->description = $description;
}
/**
* Set the URL of this navigation item. Additional URL
* parameters can be passed using the (optional) second
* parameter.
*
* @param string $title display title
* @param array $params additional URL parameters
*/
public function setURL($url, $params = NULL)
{
$this->url = $url;
$this->params = $params;
}
/**
* Set the badge number of this navigation item.
*
* @param string $badgeNumber the badge number
*/
public function setBadgeNumber($badgeNumber)
{
$this->badgeNumber = $badgeNumber;
}
/**
* Set the badge number of this navigation item.
*
* @param string $badgeNumber the badge number
*/
public function setBadgeTimestamp($badgeTimestamp)
{
$this->badgeTimestamp = $badgeTimestamp;
}
/**
* Get the active subnavigation item of this navigation
* (if there is one). Returns NULL if the subnavigation
* has no active item.
*/
public function activeSubNavigation()
{
if (isset($this->subnav)) {
foreach ($this->subnav as $nav) {
if ($nav->isActive()) {
return $nav;
}
}
}
return NULL;
}
/**
* Initialize the subnavigation of this item. This method
* is called once before the first item is added or removed.
*/
protected function initSubNavigation()
{
$this->subnav = [];
}
/**
* Add the given item to the subnavigation of this object.
* This also assigns a name to this subnavigation item. If
* there is already a subitem with this name, the old one
* is replaced by the new item.
*
* @param string $name name of new navigation item
* @param object $navigation navigation item to add
*/
public function addSubNavigation($name, Navigation $navigation)
{
if (!isset($this->subnav)) {
$this->initSubNavigation();
}
$this->subnav[$name] = $navigation;
}
/**
* Add the given item to the subnavigation of this object.
* The new item is inserted immediately before the item with
* the name given by $where (if there is one, it is appended
* to the end otherwise). This also assigns a name to this
* subnavigation item.
*
* @param string $name name of new navigation item
* @param object $navigation navigation item to add
* @param string $where insert it before this item
*/
public function insertSubNavigation($name, Navigation $navigation, $where)
{
$subnav = [];
$done = false;
foreach ($this->getSubNavigation() as $key => $nav) {
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
$subnav[$name] = $navigation;
$done = true;
}
$subnav[$key] = $nav;
}
if (!$done) {
$subnav[$name] = $navigation;
}
$this->subnav = $subnav;
}
/**
* Return the list of subnavigation items of this object.
*/
public function getSubNavigation()
{
if (!isset($this->subnav)) {
$this->initSubNavigation();
}
return $this->subnav;
}
/**
* Remove the given item from the subnavigation of this
* object (if there is an item with this name).
*
* @param string $name name of item to remove
*/
public function removeSubNavigation($name)
{
if (!isset($this->subnav)) {
$this->initSubNavigation();
}
unset($this->subnav[$name]);
}
/**
* IteratorAggregate: Create interator for request parameters.
*
* @todo Add Traversable return type when Stud.IP requires PHP8 minimal
public function getIterator()
{
return new ArrayIterator($this->getSubNavigation());
}
}