Passed
Push — master ( eed2f4...e44814 )
by Andreas
32:42
created

fi_protie_navigation::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 7
ccs 4
cts 5
cp 0.8
crap 2.032
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package fi.protie.navigation
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
 * Versatile class for drawing dynamically navigation elements according to
11
 * user preferences.
12
 *
13
 * <code>
14
 * // Initializes the class
15
 * $navigation = new fi_protie_navigation();
16
 *
17
 * // Display only nodes (folders)
18
 * $navigation->list_leaves = false;
19
 *
20
 * // Expand the whole site tree instead of the active path
21
 * $navigation->follow_all = true;
22
 *
23
 * // Skip 1 level from the beginning of the active path
24
 * $navigation->skip_levels = 1;
25
 *
26
 * // Finally draw the navigation
27
 * $navigation->draw();
28
 * </code>
29
 *
30
 * See the attributes of this class for additional customizing options.
31
 *
32
 * @package fi.protie.navigation
33
 */
34
class fi_protie_navigation
35
{
36
    /**
37
     * MidCOM helper class for navigation subsystem. Uses class 'midcom.helper.nav'
38
     *
39
     * @var midcom_helper_nav
40
     */
41
    private $_nap;
42
43
    /**
44
     * Stores the navigation access point history or in other words path to the current point.
45
     *
46
     * @var Array
47
     */
48
    private $node_path = [];
49
50
    /**
51
     * ID for the folder to get the navigation
52
     *
53
     * @var integer
54
     */
55
    public $root_id;
56
57
    /**
58
     * Number of the parsed level
59
     *
60
     * @var integer
61
     */
62
    private $_level = 1;
63
64
    /**
65
     * The amount of lowest level elements to be skipped.
66
     *
67
     * @var integer
68
     */
69
    public $skip_levels = 0;
70
71
    /**
72
     * Switch to determine if navigation should display leaves or pages.
73
     *
74
     * @var boolean
75
     */
76
    public $list_leaves = true;
77
78
    /**
79
     * List only the leaf elements or pages
80
     *
81
     * @var boolean
82
     */
83
    public $list_nodes = true;
84
85
    /**
86
     * Switch to determine if navigation should follow node path (on true) or stop on the
87
     * spot.
88
     *
89
     * @var boolean
90
     */
91
    public $follow_selected = true;
92
93
    /**
94
     * Switch to determine if navigation should follow all the nodes or only the current
95
     *
96
     * @var boolean
97
     */
98
    public $follow_all = false;
99
100
    /**
101
     * Switch to determine if navigation should show only the information of the currently selected node.
102
     *
103
     * @var boolean
104
     */
105
    public $show_only_current = false;
106
107
    /**
108
     * Restrict the amount of levels listed.
109
     *
110
     * @var integer
111
     */
112
    public $list_levels = 0;
113
114
    /**
115
     * ID of the root level list object
116
     *
117
     * @var integer
118
     */
119
    public $root_object_id;
120
121
    /**
122
     * CSS class for styling the lists
123
     *
124
     * @var string
125
     */
126
    public $css_list_style = 'fi_protie_navigation';
127
128
    /**
129
     * Add component name to list item ul class name
130
     *
131
     * @var boolean
132
     */
133
    public $component_name_to_class = false;
134
135
    /**
136
     * Check if item has children and if so, add node/leaf class to list item
137
     *
138
     * @var boolean
139
     */
140
    public $has_children_to_class = false;
141
142
    /**
143
     * Should the object's status be added to list item ul class names
144
     * Since this forces us to load the entire object, set it to false if you don't need it
145
     *
146
     * @var boolean
147
     */
148
    public $object_status_to_class = false;
149
150
    /**
151
     * CSS class for nodes
152
     *
153
     * @var string
154
     */
155
    public $css_node = 'node';
156
157
    /**
158
     * CSS class for leaves
159
     *
160
     * @var string
161
     */
162
    public $css_leaf = 'leaf';
163
164
    /**
165
     * CSS class for the elements in node path. All the elements in node path will have this class.
166
     *
167
     * @var string
168
     */
169
    public $css_selected = 'selected';
170
171
    /**
172
     * CSS class for the current, active node or leaf. There can be only one active element.
173
     *
174
     * @var string
175
     */
176
    public $css_active = 'active';
177
178
    /**
179
     * CSS class for links
180
     *
181
     * @var string
182
     */
183
    public $css_link = 'link';
184
185
    /**
186
     * Here we initialize the classes and variables needed through the class.
187
     */
188 11
    public function __construct(int $id = null)
189
    {
190 11
        $this->_nap = new midcom_helper_nav();
191 11
        $this->get_node_path();
192
193 11
        if ($id !== null) {
194
            $this->root_id = $id;
195
        }
196 11
    }
197
198
    /**
199
     * Traverses through the node path to fetch the location of the current navigation access point.
200
     */
201 11
    private function get_node_path()
202
    {
203
        // Get nodes
204 11
        $this->node_path = $this->_nap->get_node_path();
205
206
        // If NAP offers a leaf it should be stored in the node path
207 11
        if ($leaf = $this->_nap->get_current_leaf()) {
208
            $this->node_path[] = $leaf;
209
        }
210 11
    }
211
212
    /**
213
     * Traverse the child nodes starting from the requested node id
214
     */
215
    private function _list_child_nodes(int $id)
216
    {
217
        $children = $this->_nap->get_nodes($id);
218
219
        // Stop traversing the path if there are no children
220
        if (empty($children)) {
221
            return;
222
        }
223
224
        // Add ID property to the first unordered list ever called
225
        $element_id = '';
226
        if ($this->root_object_id) {
227
            $element_id = " id=\"{$this->root_object_id}\"";
228
            $this->root_object_id = null;
229
        }
230
231
        echo "<ul class=\"{$this->css_list_style} node-{$id}\"{$element_id}>";
232
233
        // Draw each child element
234
        foreach ($children as $child) {
235
            $this->_display_element($child);
236
        }
237
        echo "</ul>";
238
    }
239
240
    /**
241
     * Traverse the child elements starting from the requested node id
242
     */
243 11
    private function _list_child_elements(int $id)
244
    {
245
        // If only nodes are to be listed use the appropriate NAP call
246 11
        if (!$this->list_leaves) {
247
            $this->_list_child_nodes($id);
248
            return;
249
        }
250
251 11
        $children = $this->_nap->list_child_elements($id);
252
253
        // Stop traversing the path if there are no children
254 11
        if (empty($children)) {
255 11
            return;
256
        }
257
258
        // Add ID property to the first unordered list ever called
259
        $element_id = '';
260
        if ($this->root_object_id) {
261
            $element_id = " id=\"{$this->root_object_id}\"";
262
            $this->root_object_id = null;
263
        }
264
265
        echo "<ul class=\"{$this->css_list_style} node-{$id}\"{$element_id}>";
266
267
        // Draw each child element
268
        foreach ($children as $child) {
269
            if ($child[MIDCOM_NAV_TYPE] === 'node' && $this->list_nodes === false) {
270
                // If the listing of nodes is set to false, skip this item and proceed to the next
271
                continue;
272
            }
273
            $this->_display_element($child);
274
        }
275
276
        echo "</ul>";
277
    }
278
279
    private function _get_css_classes(array $item) : string
280
    {
281
        $classes = [];
282
283
        if ($item[MIDCOM_NAV_TYPE] === 'node') {
284
            if (   $item[MIDCOM_NAV_ID] === $this->_nap->get_current_node()
285
                && (   !$this->_nap->get_current_leaf()
286
                    || !$this->_nap->get_leaf($this->_nap->get_current_leaf()))) {
287
                $classes[] = $this->css_active;
288
            }
289
290
            if (in_array($item[MIDCOM_NAV_ID], $this->node_path, true)) {
291
                $classes[] = $this->css_selected;
292
            }
293
294
            if ($this->component_name_to_class) {
295
                $classes[] = str_replace('.', '_', $item[MIDCOM_NAV_COMPONENT]);
296
            }
297
        } elseif ($item[MIDCOM_NAV_ID] === $this->_nap->get_current_leaf()) {
298
            // Place the corresponding css class for the currently active leaf)
299
            $classes[] = $this->css_active;
300
            $classes[] = $this->css_selected;
301
        }
302
303
        if ($this->has_children_to_class) {
304
            if (!$this->list_leaves) {
305
                $children = $this->_nap->get_nodes($item[MIDCOM_NAV_ID]);
306
            } elseif ($item[MIDCOM_NAV_TYPE] == 'node') {
307
                $children = $this->_nap->list_child_elements($item[MIDCOM_NAV_ID]);
308
            } else {
309
                $children = false;
310
            }
311
            $classes[] = $children ? $this->css_node : $this->css_leaf;
312
        }
313
314
        // Add information about the object's status
315
        if (   $this->object_status_to_class
316
            && isset($item[MIDCOM_NAV_OBJECT])
317
            && $css_status_class = midcom::get()->metadata->get_object_classes($item[MIDCOM_NAV_OBJECT])) {
318
            $classes[] = $css_status_class;
319
        }
320
321
        return implode(' ', $classes);
322
    }
323
324
    private function _display_element(array $item)
325
    {
326
        $css_classes = $this->_get_css_classes($item);
327
        // Finalize the class naming
328
        $class = ($css_classes !== '') ? ' class="' . $css_classes . '"' : '';
329
        $link_class = $this->css_link ? ' class="' . $this->css_link . '"' : '';
330
331
        echo "<li{$class}>";
332
        echo "<a href=\"{$item[MIDCOM_NAV_ABSOLUTEURL]}\"{$link_class}>" . htmlspecialchars($item[MIDCOM_NAV_NAME]) . "</a>";
333
        // If either of the follow nodes switches is on, follow all the nodes
334
335
        if (   $item[MIDCOM_NAV_TYPE] === 'node'
336
            && !$this->show_only_current
337
            && (   $this->list_levels === 0
338
                || $this->_level < $this->list_levels)) {
339
            if (   $this->follow_all
340
                || (   $this->follow_selected
341
                    && in_array($item[MIDCOM_NAV_ID], $this->node_path, true))) {
342
                $this->_level++;
343
                $this->_list_child_elements($item[MIDCOM_NAV_ID]);
344
                $this->_level--;
345
            }
346
        }
347
348
        echo "</li>";
349
    }
350
351
    /**
352
     * Draw the navigation.
353
     */
354 11
    public function draw()
355
    {
356 11
        if (!$this->root_id) {
357 11
            $this->root_id = $this->_nap->get_root_node();
358
        }
359
360 11
        if ($this->skip_levels !== 0) {
361
            if (!array_key_exists($this->skip_levels, $this->node_path)) {
362
                return;
363
            }
364
365
            $this->root_id = $this->node_path[$this->skip_levels];
366
        }
367
368 11
        if ($this->show_only_current) {
369
            $this->root_id = $this->_nap->get_current_node();
370
        }
371
372 11
        $this->_list_child_elements($this->root_id);
373 11
    }
374
375
    /**
376
     * Set the root element id
377
     *
378
     * @param int $id root ul id
379
     */
380
    public function set_root_element_id(int $id)
381
    {
382
        $this->root_object_id = $id;
383
    }
384
}
385