1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* BB's Zend Framework 2 Components |
4
|
|
|
* |
5
|
|
|
* UI Components |
6
|
|
|
* |
7
|
|
|
* @package [MyApplication] |
8
|
|
|
* @subpackage BB's Zend Framework 2 Components |
9
|
|
|
* @subpackage UI Components |
10
|
|
|
* @author Björn Bartels <[email protected]> |
11
|
|
|
* @link https://gitlab.bjoernbartels.earth/groups/zf2 |
12
|
|
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
13
|
|
|
* @copyright copyright (c) 2016 Björn Bartels <[email protected]> |
14
|
|
|
*/ |
15
|
|
|
|
16
|
|
|
namespace UIComponents\View\Helper\Traits; |
17
|
|
|
|
18
|
|
|
use \Zend\Navigation\AbstractContainer; |
19
|
|
|
use \Zend\Navigation\Page\AbstractPage; |
20
|
|
|
use \Admin\View\Helper\Isallowed; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* |
24
|
|
|
* @method \Admin\View\Helper\Isallowed isAllowed($resource) |
25
|
|
|
* @author bba |
26
|
|
|
* |
27
|
|
|
*/ |
28
|
|
|
trait ComponentNavigationTrait { |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* AbstractContainer to operate on by default |
32
|
|
|
* |
33
|
|
|
* @var Navigation\AbstractContainer |
34
|
|
|
*/ |
35
|
|
|
protected $container; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* The minimum depth a page must have to be included when rendering |
39
|
|
|
* |
40
|
|
|
* @var int |
41
|
|
|
*/ |
42
|
|
|
protected $minDepth; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* The maximum depth a page can have to be included when rendering |
46
|
|
|
* |
47
|
|
|
* @var int |
48
|
|
|
*/ |
49
|
|
|
protected $maxDepth; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Indentation string |
53
|
|
|
* |
54
|
|
|
* @var string |
55
|
|
|
*/ |
56
|
|
|
protected $indent = ''; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Whether invisible items should be rendered by this helper |
60
|
|
|
* |
61
|
|
|
* @var bool |
62
|
|
|
*/ |
63
|
|
|
protected $renderInvisible = false; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Sets navigation container the helper operates on by default |
67
|
|
|
* |
68
|
|
|
* Implements {@link HelperInterface::setContainer()}. |
69
|
|
|
* |
70
|
|
|
* @param string|Navigation\AbstractContainer $container Default is null, meaning container will be reset. |
71
|
|
|
* @return self |
72
|
|
|
*/ |
73
|
|
|
public function setContainer($container = null) |
74
|
|
|
{ |
75
|
|
|
//$this->parseContainer($container); |
76
|
|
|
$this->container = $container; |
|
|
|
|
77
|
|
|
|
78
|
|
|
return $this; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Returns the navigation container helper operates on by default |
83
|
|
|
* |
84
|
|
|
* Implements {@link HelperInterface::getContainer()}. |
85
|
|
|
* |
86
|
|
|
* If no container is set, a new container will be instantiated and |
87
|
|
|
* stored in the helper. |
88
|
|
|
* |
89
|
|
|
* @return Navigation\AbstractContainer|Components navigation container |
90
|
|
|
*/ |
91
|
|
|
public function getContainer() |
92
|
|
|
{ |
93
|
|
|
if (null === $this->container) { |
94
|
|
|
$this->container = new \UIComponents\View\Helper\Components(); |
|
|
|
|
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
return $this->container; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Checks if the helper has a container |
102
|
|
|
* |
103
|
|
|
* Implements {@link HelperInterface::hasContainer()}. |
104
|
|
|
* |
105
|
|
|
* @return bool |
106
|
|
|
*/ |
107
|
|
|
public function hasContainer() |
108
|
|
|
{ |
109
|
|
|
return null !== $this->container; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Retrieve whitespace representation of $indent |
114
|
|
|
* |
115
|
|
|
* @param int|string $indent |
116
|
|
|
* @return string |
117
|
|
|
*/ |
118
|
|
|
protected function getWhitespace($indent) |
119
|
|
|
{ |
120
|
|
|
if (is_int($indent)) { |
121
|
|
|
$indent = str_repeat(' ', $indent); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
return (string) $indent; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Set the indentation string for using in {@link render()}, optionally a |
129
|
|
|
* number of spaces to indent with |
130
|
|
|
* |
131
|
|
|
* @param string|int $indent |
132
|
|
|
* @return self |
133
|
|
|
*/ |
134
|
|
|
public function setIndent($indent) |
135
|
|
|
{ |
136
|
|
|
$this->indent = $this->getWhitespace($indent); |
137
|
|
|
return $this; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Returns indentation |
142
|
|
|
* |
143
|
|
|
* @return string |
144
|
|
|
*/ |
145
|
|
|
public function getIndent() |
146
|
|
|
{ |
147
|
|
|
return $this->indent; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Sets the maximum depth a page can have to be included when rendering |
152
|
|
|
* |
153
|
|
|
* @param int $maxDepth Default is null, which sets no maximum depth. |
154
|
|
|
* @return self |
155
|
|
|
*/ |
156
|
|
|
public function setMaxDepth($maxDepth = null) |
157
|
|
|
{ |
158
|
|
|
if (null === $maxDepth || is_int($maxDepth)) { |
159
|
|
|
$this->maxDepth = $maxDepth; |
160
|
|
|
} else { |
161
|
|
|
$this->maxDepth = (int) $maxDepth; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
return $this; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* Returns maximum depth a page can have to be included when rendering |
169
|
|
|
* |
170
|
|
|
* @return int|null |
171
|
|
|
*/ |
172
|
|
|
public function getMaxDepth() |
173
|
|
|
{ |
174
|
|
|
return $this->maxDepth; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Sets the minimum depth a page must have to be included when rendering |
179
|
|
|
* |
180
|
|
|
* @param int $minDepth Default is null, which sets no minimum depth. |
181
|
|
|
* @return self |
182
|
|
|
*/ |
183
|
|
|
public function setMinDepth($minDepth = null) |
184
|
|
|
{ |
185
|
|
|
if (null === $minDepth || is_int($minDepth)) { |
186
|
|
|
$this->minDepth = $minDepth; |
187
|
|
|
} else { |
188
|
|
|
$this->minDepth = (int) $minDepth; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
return $this; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* Returns minimum depth a page must have to be included when rendering |
196
|
|
|
* |
197
|
|
|
* @return int|null |
198
|
|
|
*/ |
199
|
|
|
public function getMinDepth() |
200
|
|
|
{ |
201
|
|
|
if (!is_int($this->minDepth) || $this->minDepth < 0) { |
202
|
|
|
return 0; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
return $this->minDepth; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Render invisible items? |
210
|
|
|
* |
211
|
|
|
* @param bool $renderInvisible |
212
|
|
|
* @return self |
213
|
|
|
*/ |
214
|
|
|
public function setRenderInvisible($renderInvisible = true) |
215
|
|
|
{ |
216
|
|
|
$this->renderInvisible = (bool) $renderInvisible; |
217
|
|
|
return $this; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Return renderInvisible flag |
222
|
|
|
* |
223
|
|
|
* @return bool |
224
|
|
|
*/ |
225
|
|
|
public function getRenderInvisible() |
226
|
|
|
{ |
227
|
|
|
return $this->renderInvisible; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* Finds the deepest active page in the given container |
232
|
|
|
* |
233
|
|
|
* @param Navigation\AbstractContainer $container container to search |
234
|
|
|
* @param int|null $minDepth [optional] minimum depth |
235
|
|
|
* required for page to be |
236
|
|
|
* valid. Default is to use |
237
|
|
|
* {@link getMinDepth()}. A |
238
|
|
|
* null value means no minimum |
239
|
|
|
* depth required. |
240
|
|
|
* @param int|null $maxDepth [optional] maximum depth |
241
|
|
|
* a page can have to be |
242
|
|
|
* valid. Default is to use |
243
|
|
|
* {@link getMaxDepth()}. A |
244
|
|
|
* null value means no maximum |
245
|
|
|
* depth required. |
246
|
|
|
* @return array an associative array with |
247
|
|
|
* the values 'depth' and |
248
|
|
|
* 'page', or an empty array |
249
|
|
|
* if not found |
250
|
|
|
*/ |
251
|
|
|
public function findActive($container, $minDepth = null, $maxDepth = -1) |
252
|
|
|
{ |
253
|
|
|
|
254
|
|
|
if (!is_int($minDepth)) { |
255
|
|
|
$minDepth = $this->getMinDepth(); |
256
|
|
|
} |
257
|
|
|
if ((!is_int($maxDepth) || $maxDepth < 0) && null !== $maxDepth) { |
258
|
|
|
$maxDepth = $this->getMaxDepth(); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
$found = null; |
262
|
|
|
$foundDepth = -1; |
263
|
|
|
$iterator = new RecursiveIteratorIterator( |
264
|
|
|
$container, |
265
|
|
|
RecursiveIteratorIterator::CHILD_FIRST |
266
|
|
|
); |
267
|
|
|
|
268
|
|
|
/** @var \Zend\Navigation\Page\AbstractPage $page */ |
269
|
|
|
foreach ($iterator as $page) { |
270
|
|
|
$currDepth = $iterator->getDepth(); |
271
|
|
|
if ($currDepth < $minDepth || !$this->accept($page)) { |
272
|
|
|
// page is not accepted |
273
|
|
|
continue; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
if ($page->isActive(false) && $currDepth > $foundDepth) { |
277
|
|
|
// found an active page at a deeper level than before |
278
|
|
|
$found = $page; |
279
|
|
|
$foundDepth = $currDepth; |
280
|
|
|
} |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
if (is_int($maxDepth) && $foundDepth > $maxDepth) { |
284
|
|
|
while ($foundDepth > $maxDepth) { |
285
|
|
|
if (--$foundDepth < $minDepth) { |
286
|
|
|
$found = null; |
287
|
|
|
break; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
$found = $found->getParent(); |
291
|
|
|
if (!$found instanceof AbstractPage) { |
292
|
|
|
$found = null; |
293
|
|
|
break; |
294
|
|
|
} |
295
|
|
|
} |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
if ($found) { |
299
|
|
|
return ['page' => $found, 'depth' => $foundDepth]; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
return []; |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
/** |
306
|
|
|
* Verifies container and eventually fetches it from service locator if it is a string |
307
|
|
|
* |
308
|
|
|
* @param Navigation\AbstractContainer|string|null $container |
309
|
|
|
* @throws Exception\InvalidArgumentException |
310
|
|
|
*/ |
311
|
|
|
protected function parseContainer(&$container = null) |
312
|
|
|
{ |
313
|
|
|
if (null === $container) { |
314
|
|
|
return; |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
if (is_string($container)) { |
318
|
|
|
if (!$this->getServiceLocator()) { |
319
|
|
|
throw new Exception\InvalidArgumentException(sprintf( |
320
|
|
|
'Attempted to set container with alias "%s" but no ServiceLocator was set', |
321
|
|
|
$container |
322
|
|
|
)); |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* Load the navigation container from the root service locator |
327
|
|
|
* |
328
|
|
|
* The navigation container is probably located in Zend\ServiceManager\ServiceManager |
329
|
|
|
* and not in the View\HelperPluginManager. If the set service locator is a |
330
|
|
|
* HelperPluginManager, access the navigation container via the main service locator. |
331
|
|
|
*/ |
332
|
|
|
$sl = $this->getServiceLocator(); |
333
|
|
|
if ($sl instanceof \Zend\View\HelperPluginManager) { |
334
|
|
|
$sl = $sl->getServiceLocator(); |
335
|
|
|
} |
336
|
|
|
$container = $sl->get($container); |
337
|
|
|
return; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
if (!$container instanceof \Zend\Navigation\AbstractContainer) { |
341
|
|
|
throw new Exception\InvalidArgumentException( |
342
|
|
|
'Container must be a string alias or an instance of ' |
343
|
|
|
. 'Zend\Navigation\AbstractContainer' |
344
|
|
|
); |
345
|
|
|
} |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
// Iterator filter methods: |
349
|
|
|
|
350
|
|
|
/** |
351
|
|
|
* Determines whether a page should be accepted when iterating |
352
|
|
|
* |
353
|
|
|
* Default listener may be 'overridden' by attaching listener to 'isAllowed' |
354
|
|
|
* method. Listener must be 'short circuited' if overriding default ACL |
355
|
|
|
* listener. |
356
|
|
|
* |
357
|
|
|
* Rules: |
358
|
|
|
* - If a page is not visible it is not accepted, unless RenderInvisible has |
359
|
|
|
* been set to true |
360
|
|
|
* - If $useAcl is true (default is true): |
361
|
|
|
* - Page is accepted if listener returns true, otherwise false |
362
|
|
|
* - If page is accepted and $recursive is true, the page |
363
|
|
|
* will not be accepted if it is the descendant of a non-accepted page |
364
|
|
|
* |
365
|
|
|
* @param AbstractPage $page page to check |
366
|
|
|
* @param bool $recursive [optional] if true, page will not be |
367
|
|
|
* accepted if it is the descendant of |
368
|
|
|
* a page that is not accepted. Default |
369
|
|
|
* is true |
370
|
|
|
* |
371
|
|
|
* @return bool Whether page should be accepted |
372
|
|
|
*/ |
373
|
|
|
public function accept(AbstractPage $page, $recursive = true) |
374
|
|
|
{ |
375
|
|
|
$accept = true; |
376
|
|
|
|
377
|
|
|
if (!$page->isVisible(false) && !$this->getRenderInvisible()) { |
378
|
|
|
$accept = false; |
379
|
|
|
} elseif ($this->getUseAcl()) { |
380
|
|
|
$acl = $this->getAcl(); |
381
|
|
|
$role = $this->getRole(); |
382
|
|
|
$params = ['acl' => $acl, 'page' => $page, 'role' => $role]; |
383
|
|
|
$accept = $this->isAllowed($params); |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
if ($accept && $recursive) { |
387
|
|
|
$parent = $page->getParent(); |
388
|
|
|
|
389
|
|
|
if ($parent instanceof AbstractPage) { |
390
|
|
|
$accept = $this->accept($parent, true); |
391
|
|
|
} |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
return $accept; |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
/** |
398
|
|
|
* Get the service locator. |
399
|
|
|
* |
400
|
|
|
* @abstract |
401
|
|
|
* @return ServiceLocatorInterface |
402
|
|
|
*/ |
403
|
|
|
abstract public function getServiceLocator(); |
404
|
|
|
|
405
|
|
|
/** |
406
|
|
|
* Returns ACL or null if it isn't set using {@link setAcl()} or |
407
|
|
|
* {@link setDefaultAcl()} |
408
|
|
|
* |
409
|
|
|
* Implements {@link HelperInterface::getAcl()}. |
410
|
|
|
* |
411
|
|
|
* @return Acl\AclInterface|null ACL object or null |
412
|
|
|
*/ |
413
|
|
|
abstract public function getAcl(); |
414
|
|
|
|
415
|
|
|
/** |
416
|
|
|
* Returns whether ACL should be used |
417
|
|
|
* |
418
|
|
|
* Implements {@link HelperInterface::getUseAcl()}. |
419
|
|
|
* |
420
|
|
|
* @return bool |
421
|
|
|
*/ |
422
|
|
|
abstract public function getUseAcl(); |
423
|
|
|
|
424
|
|
|
/** |
425
|
|
|
* Returns ACL role to use when iterating pages, or null if it isn't set |
426
|
|
|
* using {@link setRole()} or {@link setDefaultRole()} |
427
|
|
|
* |
428
|
|
|
* Implements {@link HelperInterface::getRole()}. |
429
|
|
|
* |
430
|
|
|
* @return string|Acl\Role\RoleInterface|null |
431
|
|
|
*/ |
432
|
|
|
abstract public function getRole(); |
433
|
|
|
|
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
?> |
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.