Completed
Push — master ( 843223...7f5174 )
by
unknown
08:52
created

SecondaryMenuWidget::addGroup()   C

Complexity

Conditions 12
Paths 37

Size

Total Lines 60
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 37
nc 37
nop 2
dl 0
loc 60
rs 6.366
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Charcoal\Admin\Widget;
4
5
use ArrayIterator;
6
use RuntimeException;
7
use InvalidArgumentException;
8
9
// From Pimple
10
use Pimple\Container;
11
12
// From PSR-7
13
use Psr\Http\Message\RequestInterface;
14
use Psr\Http\Message\UriInterface;
15
16
// From 'charcoal-factory'
17
use Charcoal\Factory\FactoryInterface;
18
19
// From 'charcoal-admin'
20
use Charcoal\Admin\AdminWidget;
21
use Charcoal\Admin\Support\HttpAwareTrait;
22
use Charcoal\Admin\Ui\ActionContainerTrait;
23
use Charcoal\Admin\Ui\SecondaryMenu\SecondaryMenuGroupInterface;
24
25
/**
26
 * Admin Secondary Menu Widget
27
 */
28
class SecondaryMenuWidget extends AdminWidget implements
29
    SecondaryMenuWidgetInterface
30
{
31
    use ActionContainerTrait;
0 ignored issues
show
Bug introduced by
The trait Charcoal\Admin\Ui\ActionContainerTrait requires the property $currentObj which is not provided by Charcoal\Admin\Widget\SecondaryMenuWidget.
Loading history...
32
    use HttpAwareTrait;
33
34
    /**
35
     * Default sorting priority for an action.
36
     *
37
     * @const integer
38
     */
39
    const DEFAULT_ACTION_PRIORITY = 10;
40
41
    /**
42
     * Store the secondary menu actions.
43
     *
44
     * @var array|null
45
     */
46
    protected $secondaryMenuActions;
47
48
    /**
49
     * Store the default list actions.
50
     *
51
     * @var array|null
52
     */
53
    protected $defaultSecondaryMenuActions;
54
55
    /**
56
     * Keep track if secondary menu actions are finalized.
57
     *
58
     * @var boolean
59
     */
60
    protected $parsedSecondaryMenuActions = false;
61
62
    /**
63
     * The secondary menu's display type.
64
     *
65
     * @var string
66
     */
67
    protected $displayType;
68
69
    /**
70
     * The secondary menu's display options.
71
     *
72
     * @var array
73
     */
74
    protected $displayOptions;
75
76
    /**
77
     * Whether the group is collapsed or not.
78
     *
79
     * @var boolean
80
     */
81
    private $collapsed = false;
82
83
    /**
84
     * Whether the group has siblings or not.
85
     *
86
     * @var boolean
87
     */
88
    private $parented = false;
89
90
    /**
91
     * The title is displayed by default.
92
     *
93
     * @var boolean
94
     */
95
    private $showTitle = true;
96
97
    /**
98
     * The description is displayed by default.
99
     *
100
     * @var boolean
101
     */
102
    private $showDescription = true;
103
104
    /**
105
     * The currently highlighted item.
106
     *
107
     * @var mixed
108
     */
109
    protected $currentItem;
110
111
    /**
112
     * The admin's current route.
113
     *
114
     * @var UriInterface
115
     */
116
    protected $adminRoute;
117
118
    /**
119
     * The secondary menu's title.
120
     *
121
     * @var \Charcoal\Translator\Translation|string|null
122
     */
123
    protected $title;
124
125
    /**
126
     * The secondary menu's links.
127
     *
128
     * @var array
129
     */
130
    protected $links;
131
132
    /**
133
     * The secondary menu's groups.
134
     *
135
     * @var SecondaryMenuGroupInterface[]
136
     */
137
    protected $groups;
138
139
    /**
140
     * The secondary menu's description.
141
     *
142
     * @var \Charcoal\Translator\Translation|string|null
143
     */
144
    protected $description;
145
146
    /**
147
     * Store the factory instance for the current class.
148
     *
149
     * @var FactoryInterface
150
     */
151
    protected $secondaryMenu;
152
153
    /**
154
     * @param  array $data Class data.
155
     * @return self
156
     */
157
    public function setData(array $data)
158
    {
159
160
        if (isset($data['actions'])) {
161
            $this->setSecondaryMenuActions($data['actions']);
162
            unset($data['actions']);
163
        }
164
165
        if (isset($data['current_item'])) {
166
            $this->setCurrentItem($data['current_item']);
167
            unset($data['current_item']);
168
        }
169
170
        if (isset($data['is_current'])) {
171
            $this->setIsCurrent($data['is_current']);
172
            unset($data['is_current']);
173
        }
174
175
        parent::setData($data);
176
177
        return $this;
178
    }
179
180
    /**
181
     * Determine if the secondary menu has anything.
182
     *
183
     * @return boolean
184
     */
185
    public function hasSecondaryMenu()
186
    {
187
        $ident    = $this->ident();
188
        $metadata = $this->adminSecondaryMenu();
189
190
        if (isset($metadata[$ident])) {
191
            return $this->hasLinks() ||
192
                   $this->hasGroups() ||
193
                   $this->hasActions() ||
194
                   $this->showTitle() ||
195
                   $this->showDescription();
196
        }
197
198
        return false;
199
    }
200
201
    /**
202
     * Determine if the secondary menu is accessible via a tab.
203
     *
204
     * @return boolean
205
     */
206
    public function isTabbed()
207
    {
208
        $ident    = $this->ident();
209
        $metadata = $this->adminSecondaryMenu();
210
211
        if (isset($metadata[$ident])) {
212
            return $this->hasLinks() ||
213
                   $this->hasGroups() ||
214
                   $this->hasActions();
215
        }
216
217
        return false;
218
    }
219
220
    /**
221
     * Retrieve the metadata for the secondary menu.
222
     *
223
     * @return array
224
     */
225
    public function adminSecondaryMenu()
226
    {
227
        return $this->adminConfig('secondary_menu', []);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->adminConfi...condary_menu', array()) also could return the type Charcoal\Admin\Config which is incompatible with the documented return type array.
Loading history...
228
    }
229
230
    /**
231
     * Retrieve the current route path.
232
     *
233
     * @return string|null
234
     */
235
    public function adminRoute()
236
    {
237
        if ($this->adminRoute === null) {
238
            $requestUri = (string)$this->httpRequest()->getUri();
239
            $requestUri = str_replace($this->adminUrl(), '', $requestUri);
240
241
            $this->adminRoute = $requestUri;
0 ignored issues
show
Documentation Bug introduced by
It seems like $requestUri of type string is incompatible with the declared type Psr\Http\Message\UriInterface of property $adminRoute.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
242
        }
243
244
        return $this->adminRoute;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->adminRoute also could return the type Psr\Http\Message\UriInterface which is incompatible with the documented return type null|string.
Loading history...
245
    }
246
247
    /**
248
     * @param  string $ident The ident for the current item to highlight.
249
     * @return self
250
     */
251
    public function setCurrentItem($ident)
252
    {
253
        $this->currentItem = $ident;
254
        return $this;
255
    }
256
257
    /**
258
     * @return string
259
     */
260
    public function currentItem()
261
    {
262
        return $this->currentItem;
263
    }
264
265
    /**
266
     * Computes the intersection of values to determine if the link is the current item.
267
     *
268
     * @param  mixed $linkIdent The link's value(s) to check.
269
     * @return boolean
270
     */
271
    public function isCurrentItem($linkIdent)
272
    {
273
        $context = array_filter([
274
            $this->currentItem(),
275
            $this->objType(),
276
            $this->adminRoute(),
277
        ]);
278
279
        $matches = array_intersect((array)$linkIdent, $context);
280
281
        return !!$matches;
282
    }
283
284
    /**
285
     * Retrieve the current object type from the GET parameters.
286
     *
287
     * @return string|null
288
     */
289
    public function objType()
290
    {
291
        return $this->httpRequest()->getParam('obj_type');
0 ignored issues
show
Bug introduced by
The method getParam() does not exist on Psr\Http\Message\RequestInterface. It seems like you code against a sub-type of Psr\Http\Message\RequestInterface such as Slim\Http\Request. ( Ignorable by Annotation )

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

291
        return $this->httpRequest()->/** @scrutinizer ignore-call */ getParam('obj_type');
Loading history...
292
    }
293
294
    /**
295
     * Show/hide the widget's title.
296
     *
297
     * @param  boolean $show Show (TRUE) or hide (FALSE) the title.
298
     * @return self
299
     */
300
    public function setShowTitle($show)
301
    {
302
        $this->showTitle = !!$show;
303
304
        return $this;
305
    }
306
307
    /**
308
     * Determine if the title is to be displayed.
309
     *
310
     * @return boolean If TRUE or unset, check if there is a title.
311
     */
312
    public function showTitle()
313
    {
314
        if ($this->showTitle === false) {
315
            return false;
316
        } else {
317
            return !!$this->title();
318
        }
319
    }
320
321
    /**
322
     * Set the title of the secondary menu.
323
     *
324
     * @param  mixed $title A title for the secondary menu.
325
     * @return self
326
     */
327
    public function setTitle($title)
328
    {
329
        $this->title = $this->translator()->translation($title);
330
331
        return $this;
332
    }
333
334
    /**
335
     * Retrieve the title of the secondary menu.
336
     *
337
     * @return \Charcoal\Translator\Translation|string|null
338
     */
339
    public function title()
340
    {
341
        if ($this->title === null) {
342
            $ident    = $this->ident();
343
            $metadata = $this->adminSecondaryMenu();
344
345
            $this->title = '';
346
347
            if (isset($metadata[$ident]['title'])) {
348
                $this->setTitle($metadata[$ident]['title']);
349
            }
350
        }
351
352
        return $this->title;
353
    }
354
355
    /**
356
     * Set the secondary menu links.
357
     *
358
     * @param  array $links A collection of link objects.
359
     * @return self
360
     */
361
    public function setLinks(array $links)
362
    {
363
        $this->links = new ArrayIterator();
0 ignored issues
show
Documentation Bug introduced by
It seems like new ArrayIterator() of type ArrayIterator is incompatible with the declared type array of property $links.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
364
365
        foreach ($links as $linkIdent => $link) {
366
            $this->addLink($linkIdent, $link);
367
        }
368
369
        return $this;
370
    }
371
372
    /**
373
     * Set the secondary menu links.
374
     *
375
     * @param  string       $linkIdent The link identifier.
376
     * @param  array|object $link      The link object or structure.
377
     * @throws InvalidArgumentException If the link is invalid.
378
     * @return self
379
     */
380
    public function addLink($linkIdent, $link)
381
    {
382
        if (!is_string($linkIdent) && !is_numeric($linkIdent)) {
0 ignored issues
show
introduced by
The condition is_string($linkIdent) is always true.
Loading history...
383
            throw new InvalidArgumentException(
384
                'Link identifier must be a string or '
385
            );
386
        }
387
388
        if (is_array($link)) {
389
            $active = true;
390
            $name   = null;
391
            $url    = null;
392
            $permissions = [];
393
394
            if (isset($link['ident'])) {
395
                $linkIdent = $link['ident'];
396
            } else {
397
                $link['ident'] = $linkIdent;
398
            }
399
400
            if (isset($link['active'])) {
401
                $active = !!$link['active'];
402
            }
403
404
            if (isset($link['name'])) {
405
                $name = $this->translator()->translation($link['name']);
406
            }
407
408
            if (isset($link['url'])) {
409
                $url = $this->translator()->translation($link['url']);
410
            }
411
412
            if (isset($link['required_acl_permissions'])) {
413
                $permissions = $link['required_acl_permissions'];
414
            }
415
416
            if ($name === null && $url === null) {
417
                return $this;
418
            }
419
420
            $this->links[$linkIdent] = [
421
                'active'   => $active,
422
                'name'     => $name,
423
                'url'      => $url,
424
                'selected' => $this->isCurrentItem([ $linkIdent, (string)$url ]),
425
                'required_acl_permissions' => $permissions
426
            ];
427
        } else {
428
            throw new InvalidArgumentException(sprintf(
429
                'Link must be an associative array, received %s',
430
                (is_object($link) ? get_class($link) : gettype($link))
431
            ));
432
        }
433
434
        return $this;
435
    }
436
437
    /**
438
     * Retrieve the secondary menu links.
439
     *
440
     * @return array
441
     */
442
    public function links()
443
    {
444
        if ($this->links === null) {
445
            $ident    = $this->ident();
446
            $metadata = $this->adminSecondaryMenu();
447
448
            $this->links = [];
449
            if (isset($metadata[$ident]['links'])) {
450
                $links = $metadata[$ident]['links'];
451
452
                if (is_array($links)) {
453
                    $this->setLinks($links);
454
                }
455
            }
456
        }
457
458
        $out = [];
459
460
        foreach ($this->links as $link) {
461
            if (isset($link['active']) && !$link['active']) {
462
                continue;
463
            }
464
465
            if (isset($link['required_acl_permissions'])) {
466
                $link['permissions'] = $link['required_acl_permissions'];
467
                unset($link['required_acl_permissions']);
468
            }
469
470
            if (isset($link['permissions'])) {
471
                if ($this->hasPermissions($link['permissions']) === false) {
472
                    continue;
473
                }
474
            }
475
476
            $out[] = $link;
477
        }
478
479
        $this->links = $out;
480
        return $this->links;
481
    }
482
483
    /**
484
     * Set the display type of the secondary menu's contents.
485
     *
486
     * @param  mixed $type The display type.
487
     * @throws InvalidArgumentException If the display type is invalid.
488
     * @return self
489
     */
490
    public function setDisplayType($type)
491
    {
492
        if (!is_string($type)) {
493
            throw new InvalidArgumentException('The display type must be a string.');
494
        }
495
496
        $this->displayType = $type;
497
498
        return $this;
499
    }
500
501
    /**
502
     * Retrieve the display type of the secondary menu's contents.
503
     *
504
     * @return string|null
505
     */
506
    public function displayType()
507
    {
508
        if ($this->displayType === null) {
509
            $ident    = $this->ident();
510
            $metadata = $this->adminSecondaryMenu();
511
512
            if (isset($metadata[$ident]['display_type'])) {
513
                $this->setDisplayType($metadata[$ident]['display_type']);
514
            } else {
515
                $this->displayType = '';
516
            }
517
        }
518
519
        return $this->displayType;
520
    }
521
522
    /**
523
     * Determine if the secondary menu groups should be displayed as panels.
524
     *
525
     * @return boolean
526
     */
527
    public function displayAsPanel()
528
    {
529
        return in_array($this->displayType(), [ 'panel', 'collapsible' ]);
530
    }
531
532
    /**
533
     * Determine if the display type is "collapsible".
534
     *
535
     * @return boolean
536
     */
537
    public function collapsible()
538
    {
539
        return ($this->displayType() === 'collapsible');
540
    }
541
542
    /**
543
     * Set the display options for the secondary menu.
544
     *
545
     * @param  array $options Display configuration.
546
     * @throws InvalidArgumentException If the display options are not an associative array.
547
     * @return self
548
     */
549
    public function setDisplayOptions(array $options)
550
    {
551
        $this->displayOptions = $options;
552
553
        return $this;
554
    }
555
556
    /**
557
     * Retrieve the display options for the secondary menu.
558
     *
559
     * @throws RuntimeException If the display options are not an associative array.
560
     * @return array
561
     */
562
    public function displayOptions()
563
    {
564
        if ($this->displayOptions === null) {
565
            $this->setDisplayOptions($this->defaultDisplayOptions());
566
567
            $ident    = $this->ident();
568
            $metadata = $this->adminSecondaryMenu();
569
570
            if (isset($metadata[$ident]['display_options'])) {
571
                $options = $metadata[$ident]['display_options'];
572
573
                if (!is_array($options)) {
574
                    throw new RuntimeException('The display options must be an associative array.');
575
                }
576
577
                $this->setDisplayOptions(array_merge($this->displayOptions, $options));
578
            }
579
        }
580
581
        return $this->displayOptions;
582
    }
583
584
    /**
585
     * Retrieve the default display options for the secondary menu.
586
     *
587
     * @return array
588
     */
589
    public function defaultDisplayOptions()
590
    {
591
        return [
592
            'parented'  => false,
593
            'collapsed' => $this->collapsible()
594
        ];
595
    }
596
597
    /**
598
     * @return mixed
599
     */
600
    public function parented()
601
    {
602
        if ($this->parented) {
603
            return $this->parented;
604
        }
605
606
        return $this->displayOptions()['parented'];
607
    }
608
609
    /**
610
     * @return mixed
611
     */
612
    public function collapsed()
613
    {
614
        if ($this->collapsed) {
615
            return $this->collapsed;
616
        }
617
618
        return $this->displayOptions()['collapsed'];
619
    }
620
621
    /**
622
     * Set the secondary menu's groups.
623
     *
624
     * @param  array $groups A collection of group structures.
625
     * @return self
626
     */
627
    public function setGroups(array $groups)
628
    {
629
        $this->groups = [];
630
631
        foreach ($groups as $groupIdent => $group) {
632
            $this->addGroup($groupIdent, $group);
633
        }
634
635
        uasort($this->groups, [ $this, 'sortGroupsByPriority' ]);
636
637
        // Remove items that are not active.
638
        $this->groups = array_filter($this->groups, function($item) {
639
            return ($item->active());
640
        });
641
642
        return $this;
643
    }
644
645
    /**
646
     * Add a secondary menu group.
647
     *
648
     * @param  string                       $groupIdent The group identifier.
649
     * @param  array|SecondaryMenuGroupInterface $group      The group object or structure.
650
     * @throws InvalidArgumentException If the identifier is not a string or the group is invalid.
651
     * @return self
652
     */
653
    public function addGroup($groupIdent, $group)
654
    {
655
        if (!is_string($groupIdent)) {
0 ignored issues
show
introduced by
The condition is_string($groupIdent) is always true.
Loading history...
656
            throw new InvalidArgumentException(
657
                'Group identifier must be a string'
658
            );
659
        }
660
661
        if ($group instanceof SecondaryMenuGroupInterface) {
662
            $group->setSecondaryMenu($this);
663
            $group->setIdent($groupIdent);
664
665
            $this->groups[] = $group;
666
        } elseif (is_array($group)) {
0 ignored issues
show
introduced by
The condition is_array($group) is always true.
Loading history...
667
            if (isset($group['ident'])) {
668
                $groupIdent = $group['ident'];
0 ignored issues
show
Unused Code introduced by
The assignment to $groupIdent is dead and can be removed.
Loading history...
669
            } else {
670
                $group['ident'] = $groupIdent;
671
            }
672
673
            $displayOptions = $this->displayOptions();
674
            if (isset($group['display_options'])) {
675
                $displayOptions = array_merge($displayOptions, $group['display_options']);
676
            }
677
678
            $group['collapsed'] = $displayOptions['collapsed'];
679
            $group['parented']  = $displayOptions['parented'];
680
681
            if (!isset($group['display_type'])) {
682
                $group['display_type'] = $this->displayType();
683
            }
684
685
            $collapsible = ($group['display_type'] === 'collapsible');
686
687
            if ($collapsible) {
688
                $group['group_id'] = uniqid('collapsible_');
689
            }
690
691
            $g = $this->secondaryMenu()->create($this->defaultGroupType());
692
            $g->setSecondaryMenu($this);
693
            $g->setData($group);
694
695
            $group = $g;
0 ignored issues
show
Unused Code introduced by
The assignment to $group is dead and can be removed.
Loading history...
696
        } elseif ($group === false || $group === null) {
697
            return $this;
698
        } else {
699
            throw new InvalidArgumentException(sprintf(
700
                'Group must be an instance of %s or an array of form group options, received %s',
701
                'SecondaryMenuGroupInterface',
702
                (is_object($group) ? get_class($group) : gettype($group))
703
            ));
704
        }
705
706
        if ($g->isAuthorized() === false) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $g does not seem to be defined for all execution paths leading up to this point.
Loading history...
707
            return $this;
708
        }
709
710
        $this->groups[] = $g;
711
712
        return $this;
713
    }
714
715
    /**
716
     * Retrieve the secondary menu groups.
717
     *
718
     * @return array
719
     */
720
    public function groups()
721
    {
722
        if ($this->groups === null) {
723
            $ident    = $this->ident();
724
            $metadata = $this->adminSecondaryMenu();
725
726
            $this->groups = [];
727
            if (isset($metadata[$ident]['groups'])) {
728
                $groups = $metadata[$ident]['groups'];
729
730
                if (is_array($groups)) {
731
                    $this->setGroups($groups);
732
                }
733
            }
734
        }
735
736
        return $this->groups;
737
    }
738
739
    /**
740
     * Retrieve the default secondary menu group class name.
741
     *
742
     * @return string
743
     */
744
    public function defaultGroupType()
745
    {
746
        return 'charcoal/ui/secondary-menu/generic';
747
    }
748
749
    /**
750
     * Determine if the secondary menu has any links.
751
     *
752
     * @return boolean
753
     */
754
    public function hasLinks()
755
    {
756
        return !!$this->numLinks();
757
    }
758
759
    /**
760
     * Count the number of secondary menu links.
761
     *
762
     * @return integer
763
     */
764
    public function numLinks()
765
    {
766
        if (!is_array($this->links()) && !($this->links() instanceof \Traversable)) {
0 ignored issues
show
introduced by
The condition is_array($this->links()) is always true.
Loading history...
767
            return 0;
768
        }
769
770
        $links = array_filter($this->links, function ($link) {
771
            if (isset($link['active']) && !$link['active']) {
772
                return false;
773
            }
774
775
            if (isset($link['required_acl_permissions'])) {
776
                $link['permissions'] = $link['required_acl_permissions'];
777
                unset($link['required_acl_permissions']);
778
            }
779
780
            if (isset($link['permissions'])) {
781
                if ($this->hasPermissions($link['permissions']) === false) {
782
                    return false;
783
                }
784
            }
785
786
            return true;
787
        });
788
789
        return count($links);
790
    }
791
792
    /**
793
     * Determine if the secondary menu has any groups of links.
794
     *
795
     * @return boolean
796
     */
797
    public function hasGroups()
798
    {
799
        return !!$this->numGroups();
800
    }
801
802
    /**
803
     * Count the number of secondary menu groups.
804
     *
805
     * @return integer
806
     */
807
    public function numGroups()
808
    {
809
        return count($this->groups());
810
    }
811
812
    /**
813
     * Alias for {@see self::showSecondaryMenuActions()}
814
     *
815
     * @return boolean
816
     */
817
    public function hasActions()
818
    {
819
        return $this->showSecondaryMenuActions();
820
    }
821
822
    /**
823
     * Determine if the secondary menu's actions should be shown.
824
     *
825
     * @return boolean
826
     */
827
    public function showSecondaryMenuActions()
828
    {
829
        $actions = $this->secondaryMenuActions();
830
831
        return count($actions);
0 ignored issues
show
Bug Best Practice introduced by
The expression return count($actions) returns the type integer which is incompatible with the documented return type boolean.
Loading history...
832
    }
833
834
    /**
835
     * Retrieve the secondary menu's actions.
836
     *
837
     * @return array
838
     */
839
    public function secondaryMenuActions()
840
    {
841
        if ($this->secondaryMenuActions === null) {
842
            $ident    = $this->ident();
843
            $metadata = $this->adminSecondaryMenu();
844
            if (isset($metadata[$ident]['actions'])) {
845
                $actions = $metadata[$ident]['actions'];
846
            } else {
847
                $actions = [];
848
            }
849
            $this->setSecondaryMenuActions($actions);
850
        }
851
852
        if ($this->parsedSecondaryMenuActions === false) {
853
            $this->parsedSecondaryMenuActions = true;
854
            $this->secondaryMenuActions = $this->createSecondaryMenuActions($this->secondaryMenuActions);
855
        }
856
857
        return $this->secondaryMenuActions;
858
    }
859
860
    /**
861
     * Set the description of the secondary menu.
862
     *
863
     * @param  mixed $description A description for the secondary menu.
864
     * @return self
865
     */
866
    public function setDescription($description)
867
    {
868
        $this->description = $this->translator()->translation($description);
869
870
        return $this;
871
    }
872
873
    /**
874
     * Retrieve the description of the secondary menu.
875
     *
876
     * @return \Charcoal\Translator\Translation|string|null
877
     */
878
    public function description()
879
    {
880
        if ($this->description === null) {
881
            $ident    = $this->ident();
882
            $metadata = $this->adminSecondaryMenu();
883
884
            $this->description = '';
885
886
            if (isset($metadata[$ident]['description'])) {
887
                $this->setDescription($metadata[$ident]['description']);
888
            }
889
        }
890
891
        return $this->description;
892
    }
893
    /**
894
     * Determine if the description is to be displayed.
895
     *
896
     * @return boolean If TRUE or unset, check if there is a description.
897
     */
898
    public function setShowDescription($show)
899
    {
900
        $this->showDescription = !!$show;
901
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Charcoal\Admin\Widget\SecondaryMenuWidget which is incompatible with the documented return type boolean.
Loading history...
902
    }
903
904
    /**
905
     * Show/hide the widget's description.
906
     *
907
     * @param  boolean $show Show (TRUE) or hide (FALSE) the description.
908
     * @return self
909
     */
910
    public function showDescription()
911
    {
912
        if ($this->showDescription === false) {
913
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type Charcoal\Admin\Widget\SecondaryMenuWidget.
Loading history...
914
        } else {
915
            return !!$this->description();
0 ignored issues
show
Bug Best Practice introduced by
The expression return ! ! $this->description() returns the type boolean which is incompatible with the documented return type Charcoal\Admin\Widget\SecondaryMenuWidget.
Loading history...
916
        }
917
    }
918
919
    /**
920
     * @return string
921
     */
922
    public function jsActionPrefix()
923
    {
924
        return 'js-secondary-menu';
925
    }
926
927
    /**
928
     * Inject dependencies from a DI Container.
929
     *
930
     * @param  Container $container A dependencies container instance.
931
     * @return void
932
     */
933
    protected function setDependencies(Container $container)
934
    {
935
        parent::setDependencies($container);
936
937
        // Satisfies HttpAwareTrait dependencies
938
        $this->setHttpRequest($container['request']);
939
940
        $this->setSecondaryMenuGroupFactory($container['secondary-menu/group/factory']);
941
    }
942
943
    /**
944
     * Retrieve the widget's display state.
945
     *
946
     * @return boolean
947
     */
948
    public function isCurrent()
949
    {
950
        return $this->isCurrent;
951
    }
952
953
    /**
954
     * Set the widget's display state.
955
     *
956
     * @param  boolean $flag A truthy state.
957
     * @return self
958
     */
959
    protected function setIsCurrent($flag)
960
    {
961
        $this->isCurrent = boolval($flag);
0 ignored issues
show
Bug Best Practice introduced by
The property isCurrent does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
962
963
        return $this;
964
    }
965
966
    /**
967
     * Retrieve the secondary menu group factory.
968
     *
969
     * @throws RuntimeException If the secondary menu group factory was not previously set.
970
     * @return FactoryInterface
971
     */
972
    protected function secondaryMenu()
973
    {
974
        if (!isset($this->secondaryMenu)) {
975
            throw new RuntimeException(sprintf(
976
                'Secondary Menu Group Factory is not defined for "%s"',
977
                get_class($this)
978
            ));
979
        }
980
981
        return $this->secondaryMenu;
982
    }
983
984
    /**
985
     * Set the secondary menu's actions.
986
     *
987
     * @param  array $actions One or more actions.
988
     * @return self
989
     */
990
    protected function setSecondaryMenuActions(array $actions)
991
    {
992
        $this->parsedSecondaryMenuActions = false;
993
994
        $this->secondaryMenuActions = $this->mergeActions($this->defaultSecondaryMenuActions(), $actions);
995
996
        return $this;
997
    }
998
999
    /**
1000
     * Build the secondary menu's actions.
1001
     *
1002
     * Secondary menu actions should come from the form settings defined by the "secondary menus".
1003
     * It is still possible to completly override those externally by setting the "actions"
1004
     * with the {@see self::setSecondaryMenuActions()} method.
1005
     *
1006
     * @param  array $actions Actions to resolve.
1007
     * @return array Secondary menu actions.
1008
     */
1009
    protected function createSecondaryMenuActions(array $actions)
1010
    {
1011
        $secondaryMenuActions = $this->parseActions($actions);
1012
1013
        return $secondaryMenuActions;
1014
    }
1015
1016
    /**
1017
     * Retrieve the secondary menu's default actions.
1018
     *
1019
     * @return array
1020
     */
1021
    protected function defaultSecondaryMenuActions()
1022
    {
1023
        if ($this->defaultSecondaryMenuActions === null) {
1024
            $this->defaultSecondaryMenuActions = [];
1025
        }
1026
1027
        return $this->defaultSecondaryMenuActions;
1028
    }
1029
1030
    /**
1031
     * To be called with {@see uasort()}.
1032
     *
1033
     * @param  SecondaryMenuGroupInterface $a Sortable entity A.
1034
     * @param  SecondaryMenuGroupInterface $b Sortable entity B.
1035
     * @return integer Sorting value: -1, 0, or 1
1036
     */
1037
    protected function sortGroupsByPriority(
1038
        SecondaryMenuGroupInterface $a,
1039
        SecondaryMenuGroupInterface $b
1040
    ) {
1041
        $a = $a->priority();
1042
        $b = $b->priority();
1043
1044
        if ($a === $b) {
1045
            return 0;
1046
        }
1047
        return ($a < $b) ? (-1) : 1;
1048
    }
1049
1050
    /**
1051
     * Set a secondary menu group factory.
1052
     *
1053
     * @param FactoryInterface $factory The group factory, to create objects.
1054
     * @return void
1055
     */
1056
    private function setSecondaryMenuGroupFactory(FactoryInterface $factory)
1057
    {
1058
        $this->secondaryMenu = $factory;
1059
    }
1060
}
1061