Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
85.71% |
96 / 112 |
|
50.00% |
1 / 2 |
CRAP | |
0.00% |
0 / 1 |
NavBuilder | |
85.71% |
96 / 112 |
|
50.00% |
1 / 2 |
95.74 | |
0.00% |
0 / 1 |
build | |
79.49% |
62 / 78 |
|
0.00% |
0 / 1 |
73.45 | |||
prepare | |
100.00% |
34 / 34 |
|
100.00% |
1 / 1 |
27 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Nav; |
15 | |
16 | use Pop\Dom\Child; |
17 | |
18 | /** |
19 | * Nav builder class |
20 | * |
21 | * @category Pop |
22 | * @package Pop\Nav |
23 | * @author Nick Sagona, III <dev@nolainteractive.com> |
24 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
25 | * @license http://www.popphp.org/license New BSD License |
26 | * @version 3.3.0 |
27 | */ |
28 | class NavBuilder |
29 | { |
30 | |
31 | /** |
32 | * Build the navigation from the nav object |
33 | * |
34 | * @param Nav $navObject |
35 | * @param array $tree |
36 | * @param int $depth |
37 | * @param ?string $parentHref |
38 | * @throws Exception|\Pop\Acl\Exception |
39 | * @return Child |
40 | */ |
41 | public static function build(Nav $navObject, array $tree, int $depth = 1, ?string $parentHref = null): Child |
42 | { |
43 | $config = $navObject->getConfig(); |
44 | [$nav, $child] = self::prepare($navObject, $config, $depth); |
45 | |
46 | $navObject->incrementParentLevel(); |
47 | $depth++; |
48 | |
49 | // Recursively loop through the nodes |
50 | foreach ($tree as $node) { |
51 | $allowed = true; |
52 | if (isset($node['acl'])) { |
53 | if ($navObject->getAcl() === null) { |
54 | throw new Exception('The access control object is not set.'); |
55 | } |
56 | if (empty($navObject->getRoles())) { |
57 | $allowed = false; |
58 | } else { |
59 | $resource = (isset($node['acl']['resource'])) ? $node['acl']['resource'] : null; |
60 | $permission = (isset($node['acl']['permission'])) ? $node['acl']['permission'] : null; |
61 | $allowed = ($navObject->isAclStrict()) ? |
62 | $navObject->getAcl()->isAllowedManyStrict($navObject->getRoles(), $resource, $permission) : |
63 | $navObject->getAcl()->isAllowedMany($navObject->getRoles(), $resource, $permission); |
64 | } |
65 | } |
66 | if (($allowed) && isset($node['name']) && isset($node['href'])) { |
67 | // Create child node and child link node |
68 | $a = new Child('a', $node['name']); |
69 | |
70 | if ((str_starts_with($node['href'], '#')) || (str_ends_with($node['href'], '#')) || |
71 | (str_starts_with($node['href'], 'http')) || (str_starts_with($node['href'], 'mailto:'))) { |
72 | $href = $node['href']; |
73 | } else if (str_starts_with($node['href'], '/')) { |
74 | $href = $navObject->getBaseUrl() . $node['href']; |
75 | } else { |
76 | if (str_ends_with($parentHref, '/')) { |
77 | $href = $parentHref . $node['href']; |
78 | } else { |
79 | $href = $parentHref . '/' . $node['href']; |
80 | } |
81 | } |
82 | |
83 | $a->setAttribute('href', $href); |
84 | |
85 | if (($navObject->isReturnFalse()) && (($href == '#') || (str_ends_with($href, '#')))) { |
86 | $a->setAttribute('onclick', 'return false;'); |
87 | } |
88 | $url = $_SERVER['REQUEST_URI'] ?? null; |
89 | if (str_contains($url, '?')) { |
90 | $url = substr($url, strpos($url, '?')); |
91 | } |
92 | |
93 | $linkClass = null; |
94 | if ($href == $url) { |
95 | if (isset($config['on'])) { |
96 | $linkClass = $config['on']; |
97 | } |
98 | } else { |
99 | if (isset($config['off'])) { |
100 | $linkClass = $config['off']; |
101 | } |
102 | } |
103 | |
104 | // If the node has any attributes |
105 | if (isset($node['attributes'])) { |
106 | foreach ($node['attributes'] as $attrib => $value) { |
107 | $value = (($attrib == 'class') && ($linkClass !== null)) ? $value . ' ' . $linkClass : $value; |
108 | $a->setAttribute($attrib, $value); |
109 | } |
110 | } else if ($linkClass !== null) { |
111 | $a->setAttribute('class', $linkClass); |
112 | } |
113 | |
114 | if ($child !== null) { |
115 | $navChild = new Child($child); |
116 | |
117 | // Set child attributes if they exist |
118 | if (isset($config['child']) && isset($config['child']['id'])) { |
119 | $navChild->setAttribute('id', $config['child']['id'] . '-' . $navObject->getChildLevel()); |
120 | } |
121 | if (isset($config['child']) && isset($config['child']['class'])) { |
122 | $navChild->setAttribute('class', $config['child']['class'] . '-' . ($depth - 1)); |
123 | } |
124 | if (isset($config['child']['attributes'])) { |
125 | foreach ($config['child']['attributes'] as $attrib => $value) { |
126 | $navChild->setAttribute($attrib, $value); |
127 | } |
128 | } |
129 | |
130 | // Add link node |
131 | $navChild->addChild($a); |
132 | $navObject->incrementChildLevel(); |
133 | |
134 | // If there are children, loop through and add them |
135 | if (isset($node['children']) && is_array($node['children']) && (count($node['children']) > 0)) { |
136 | $childrenAllowed = true; |
137 | // Check if the children are allowed |
138 | |
139 | $i = 0; |
140 | foreach ($node['children'] as $nodeChild) { |
141 | if (isset($nodeChild['acl'])) { |
142 | if ($navObject->getAcl() === null) { |
143 | throw new Exception('The access control object is not set.'); |
144 | } |
145 | if (empty($navObject->getRoles())) { |
146 | $childrenAllowed = false; |
147 | } else { |
148 | $resource = (isset($nodeChild['acl']['resource'])) ? $nodeChild['acl']['resource'] : null; |
149 | $permission = (isset($nodeChild['acl']['permission'])) ? $nodeChild['acl']['permission'] : null; |
150 | $method = ($navObject->isAclStrict()) ? 'isAllowedManyStrict' : 'isAllowedMany'; |
151 | if (!($navObject->getAcl()->{$method}($navObject->getRoles(), $resource, $permission))) { |
152 | $i++; |
153 | } |
154 | } |
155 | } |
156 | } |
157 | if ($i == count($node['children'])) { |
158 | $childrenAllowed = false; |
159 | } |
160 | if ($childrenAllowed) { |
161 | $nextChild = self::build($navObject, $node['children'], $depth, $href); |
162 | if (($nextChild->hasChildren()) || ($nextChild->getNodeValue() !== null)) { |
163 | $navChild->addChild($nextChild); |
164 | } |
165 | } |
166 | } |
167 | // Add child node |
168 | $nav->addChild($navChild); |
169 | } else { |
170 | $nav->addChild($a); |
171 | } |
172 | } |
173 | } |
174 | |
175 | return $nav; |
176 | } |
177 | |
178 | /** |
179 | * Prepare nav node |
180 | * |
181 | * @param Nav $navObject |
182 | * @param array $config |
183 | * @param int $depth |
184 | * @return array |
185 | */ |
186 | public static function prepare(Nav $navObject, array $config, int $depth = 1): array |
187 | { |
188 | // Create overriding top level parent, if set |
189 | if (($depth == 1) && isset($config['top'])) { |
190 | $parent = (isset($config['top']) && isset($config['top']['node'])) ? $config['top']['node'] : 'nav'; |
191 | $child = null; |
192 | if (isset($config['child']) && isset($config['child']['node'])) { |
193 | $child = $config['child']['node']; |
194 | } else if ($parent == 'nav') { |
195 | $child = 'nav'; |
196 | } |
197 | |
198 | // Create parent node |
199 | $nav = new Child($parent); |
200 | if ($navObject->getIndent() !== null) { |
201 | $nav->setIndent(str_repeat($navObject->getIndent(), $depth)); |
202 | } |
203 | |
204 | // Set top attributes if they exist |
205 | if (isset($config['top']) && isset($config['top']['id'])) { |
206 | $nav->setAttribute('id', $config['top']['id']); |
207 | } |
208 | if (isset($config['top']) && isset($config['top']['class'])) { |
209 | $nav->setAttribute('class', $config['top']['class']); |
210 | } |
211 | if (isset($config['top']['attributes'])) { |
212 | foreach ($config['top']['attributes'] as $attrib => $value) { |
213 | $nav->setAttribute($attrib, $value); |
214 | } |
215 | } |
216 | } else { |
217 | // Set up parent/child node names |
218 | $parent = (isset($config['parent']) && isset($config['parent']['node'])) ? $config['parent']['node'] : 'nav'; |
219 | $child = null; |
220 | if (isset($config['child']) && isset($config['child']['node'])) { |
221 | $child = $config['child']['node']; |
222 | } else if ($parent == 'nav') { |
223 | $child = 'nav'; |
224 | } |
225 | |
226 | // Create parent node |
227 | $nav = new Child($parent); |
228 | if ($navObject->getIndent() !== null) { |
229 | $nav->setIndent(str_repeat($navObject->getIndent(), $depth)); |
230 | } |
231 | |
232 | // Set parent attributes if they exist |
233 | if (isset($config['parent']) && isset($config['parent']['id'])) { |
234 | $nav->setAttribute('id', $config['parent']['id'] . '-' . $navObject->getParentLevel()); |
235 | } |
236 | if (isset($config['parent']) && isset($config['parent']['class'])) { |
237 | $nav->setAttribute('class', $config['parent']['class'] . '-' . $depth); |
238 | } |
239 | if (isset($config['parent']['attributes'])) { |
240 | foreach ($config['parent']['attributes'] as $attrib => $value) { |
241 | $nav->setAttribute($attrib, $value); |
242 | } |
243 | } |
244 | } |
245 | |
246 | return [$nav, $child]; |
247 | } |
248 | |
249 | } |