source: trunk/lib/Navigation.inc.php

Last change on this file was 810, checked in by anonymous, 2 months ago

Enable setting db_timezone during runtime. Refactor setParam() in App, DB, and PDO.

File size: 17.3 KB
Line 
1<?php
2/**
3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
5 * Copyright 2001-2012 Strangecode, LLC
6 *
7 * This file is part of The Strangecode Codebase.
8 *
9 * The Strangecode Codebase is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your option)
12 * any later version.
13 *
14 * The Strangecode Codebase is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * The Strangecode Codebase. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/**
24 * Navigation.inc.php
25 *
26 * The Nav class provides a system for working with navigation elements.
27 * It supports storing page titles and URLs for printing breadcrumbs
28 * and titles, as well as setting page params such as hiding the page title on
29 * some pages but not others, and storing vars like the page title itself.
30 *
31 * Note: this class was renamed from "Nav" because of the change in API and to be more descriptive.
32 *
33 * @author  Quinn Comendant <quinn@strangecode.com>
34 * @version 2.0
35 */
36class Navigation
37{
38
39    // Configuration parameters for this object.
40    protected $_params = array(
41        'head_title' => true,
42        'body_title' => true,
43        'title' => true,
44        'path' => true,
45        'breadcrumbs' => true,
46        'chop_breadcrumbs' => 0,
47        'chop_breadcrumb_links' => 1,
48        'path_delimiter' => ' / ',
49        'last_crumb_format' => '%s',
50        'current_page_url' => null, // This should be set at runtime using, e.g., $_SERVER['REQUEST_URI']
51    );
52    public $pages = array();
53
54    /**
55     * Navigation constructor.
56     */
57    public function __construct(Array $params=[])
58    {
59        $app =& App::getInstance();
60
61        // Define current_page_url here because _SERVER, not a static scalar, cannot be defined in the defaults above.
62        // Using PHP_SELF for legacy compatibility, but it might make sense to override this with REQUEST_URI.
63        // This could be overwritten by passed params.
64        $this->_params['current_page_url'] = $_SERVER['PHP_SELF'];
65
66        if (isset($params) && is_array($params)) {
67            // Merge new parameters with old overriding only those passed.
68            $this->_params = array_merge($this->_params, $params);
69        }
70    }
71
72    /**
73     * Add a page to the internal pages array. Pages must be added sequentially
74     * as they are to be printed. The root page must be added first, and the
75     * current page added last. Vars can be specified for any page, but only vars
76     * from the "current" page will be accessed with Nav::get.
77     *
78     * @access  public
79     * @param   string  $title      The title of the page.
80     * @param   string  $url        The URL to the page. Set to null to use REQUEST_URI.
81     * @param   array   $vars       Additional page variables.
82     */
83    public function add($title, $url=null, $vars=array())
84    {
85        $page = array(
86            'title' => $title,
87            'head_title' => $title,
88            'body_title' => $title,
89            'url' => is_null($url) ? $this->_params['current_page_url'] : $url,
90        );
91        // An "unformed page element" has settings applied (via ->set()) but no page added (via ->add()).
92        if (empty($this->pages) || isset(end($this->pages)['title'])) {
93            // There are no unformed page elements; add a whole new page.
94            $this->pages[] = array_merge($page, $vars);
95        } else {
96            // Append the new page to the unformed page element.
97            $curr_page =& $this->pages[key($this->pages)];
98            $curr_page = array_merge($curr_page, $page, $vars);
99        }
100    }
101
102    /**
103     * Set (or overwrite existing) parameters by passing an array of new parameters.
104     *
105     * @access public
106     * @param  array    $params     Array of parameters (key => val pairs).
107     */
108    public function setParam($params)
109    {
110        $app =& App::getInstance();
111
112        if (isset($params) && is_array($params)) {
113            // Merge new parameters with old overriding only those passed.
114            $this->_params = array_merge($this->_params, $params);
115        } else {
116            $app->logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__);
117        }
118    }
119
120    /**
121     * Return the value of a parameter, if it exists.
122     *
123     * @access public
124     * @param string $param        Which parameter to return.
125     * @return mixed               Configured parameter value.
126     */
127    public function getParam($param)
128    {
129        $app =& App::getInstance();
130
131        if (array_key_exists($param, $this->_params)) {
132            return $this->_params[$param];
133        } else {
134            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
135            return null;
136        }
137    }
138
139    /**
140     * Unsets all pages.
141     *
142     * @access  public
143     */
144    public function clear()
145    {
146        $this->pages = array();
147    }
148
149    /**
150     * Sets a variable into the current page.
151     *
152     * @access public
153     * @param mixed $key      Which value to set.
154     * @param mixed $val      Value to set variable to.
155     */
156    public function set($key, $val)
157    {
158        // Set params of current page.
159        if (empty($this->pages)) {
160            // If we're setting a value on an empty pages array, we need to add one "unformed" element first.
161            $this->pages[] = array();
162        }
163        end($this->pages);
164        $curr_page =& $this->pages[key($this->pages)];
165        $curr_page[$key] = $val;
166    }
167
168    /**
169     * Returns a specified value from the current page.
170     *
171     * @access public
172     * @param mixed $key      Which value to return.
173     * @param mixed $default  Value to return if key not found in user_data.
174     * @return mixed          Value stored in session.
175     */
176    public function get($key, $default='')
177    {
178        end($this->pages);
179        $curr_page =& $this->pages[key($this->pages)];
180
181        switch ($key) {
182        case 'title' :
183            if ($this->getParam('title') && isset($curr_page['title'])) {
184                return $curr_page['title'];
185            }
186            break;
187
188        case 'head_title' :
189            if ($this->getParam('head_title') && $this->getParam('title') && isset($curr_page['head_title'])) {
190                return $curr_page['head_title'];
191            }
192            break;
193
194        case 'body_title' :
195            if ($this->getParam('body_title') && $this->getParam('title') && isset($curr_page['body_title'])) {
196                return $curr_page['body_title'];
197            }
198            break;
199
200        case 'path' :
201            if ($this->getParam('path')) {
202                return $this->getPath();
203            }
204            break;
205
206        case 'breadcrumbs' :
207            if ($this->getParam('breadcrumbs')) {
208                return $this->getBreadcrumbs();
209            }
210            break;
211
212        default :
213            return isset($curr_page[$key]) ? $curr_page[$key] : $default;
214            break;
215        }
216
217        return $default;
218    }
219
220    /**
221     * Returns the path from root up to the current page as an array.
222     *
223     * @access  public
224     * @param   string   $key   Which value to use in the path (usually head_title or body_title or just title).
225     * @return  mixed           Path (string) or false if path param is not set.
226     */
227    public function getPathArray($key='title')
228    {
229        $path = array();
230        if ($this->getParam('path')) {
231            foreach ($this->pages as $page) {
232                $path[] = strip_tags($page[$key]);
233            }
234        }
235        return $path;
236    }
237
238    /**
239     * Returns the text path from root up to the current page, separated by the
240     * path_delimiter.
241     *
242     * @access  public
243     * @param   string   $key   Which value to use in the path (usually head_title or body_title or just title).
244     * @return  mixed           Path (string) or false if path param is not set.
245     */
246    public function getPath($key='title')
247    {
248
249        $path = $this->getPathArray($key);
250        return empty($path) ? '' : join(oTxt($this->getParam('path_delimiter'), true), $path);
251    }
252
253    /**
254     * Returns the breadcrumbs from the root page to the current page.
255     * Breadcrumbs are the text path with pages titles linked to that page.
256     *
257     * @access  public
258     * @return  string   Breadcrumbs or empty string if breadcrumbs param not set.
259     */
260    public function getBreadcrumbsArray($carry_args=null)
261    {
262        $app =& App::getInstance();
263
264        if ($this->getParam('breadcrumbs')) {
265            $breadcrumbs = array();
266            $crumb_count = sizeof($this->pages);
267            foreach ($this->pages as $page) {
268                if ($crumb_count <= $this->getParam('chop_breadcrumbs')) {
269                    // Stop gathering crumbs.
270                    break;
271                }
272                if (!is_null($carry_args)) {
273                    // If this function is called with $carry_args, employ the ohref() method.
274                    $url = $app->ohref($page['url'], $carry_args);
275                } else {
276                    // Otherwise let the URL pass through as-is (the wise developer must have provided the correct URL when calling $nav->add(
)).
277                    $url = $page['url'];
278                }
279                if ($crumb_count <= 1) {
280                    // The last crumb.
281                    if ('' == trim($page['url']) || $crumb_count <= $this->getParam('chop_breadcrumb_links')) {
282                        // A last crumb with no link.
283                        $breadcrumbs[] = array(
284                            'url' => false,
285                            'title' => sprintf($this->getParam('last_crumb_format'), $page['title']),
286                            'class' => 'current'
287                        );
288                    } else if ($crumb_count > $this->getParam('chop_breadcrumb_links')) {
289                        // A last linked crumb.
290                        $breadcrumbs[] = array(
291                            'url' => $url,
292                            'title' => sprintf($this->getParam('last_crumb_format'), $page['title']),
293                            'class' => '',
294                        );
295                    }
296                } else {
297                    if ('' == trim($page['url'])) {
298                        // A crumb with no link.
299                        $breadcrumbs[] = array(
300                            'url' => false,
301                            'title' => $page['title'],
302                            'class' => 'unavailable',
303                        );
304                    } else {
305                        // A normal linked crumb.
306                        $breadcrumbs[] = array(
307                            'url' => $url,
308                            'title' => $page['title'],
309                            'class' => '',
310                        );
311                    }
312                }
313                $crumb_count--;
314            }
315            return $breadcrumbs;
316        } else {
317            return array();
318        }
319    }
320
321    /**
322     * Returns the breadcrumbs from the root page to the current page.
323     * Breadcrumbs are the text path with pages titles linked to that page.
324     *
325     * @access  public
326     * @return  string   Breadcrumbs or empty string if breadcrumbs param not set.
327     */
328    public function getBreadcrumbs($carry_args=null)
329    {
330        $app =& App::getInstance();
331
332        if ($this->getParam('breadcrumbs')) {
333            $breadcrumbs = array();
334            $pathmark = '';
335            $crumb_count = sizeof($this->pages);
336            foreach ($this->pages as $page) {
337                if ($crumb_count <= $this->getParam('chop_breadcrumbs')) {
338                    // Stop gathering crumbs.
339                    break;
340                }
341                if (!is_null($carry_args)) {
342                    // If this function is called with $carry_args, employ the ohref() method.
343                    $url = $app->ohref($page['url'], $carry_args);
344                } else {
345                    // Otherwise let the URL pass through as-is (the wise developer must have provided the correct URL when calling $nav->add(
)).
346                    $url = $page['url'];
347                }
348                if ($crumb_count <= 1) {
349                    // The last crumb.
350                    if ('' == trim($page['url']) || $crumb_count <= $this->getParam('chop_breadcrumb_links')) {
351                        // A last crumb with no link.
352                        $breadcrumbs[] =  sprintf($this->getParam('last_crumb_format'), oTxt($page['title'], true));
353                    } else if ($crumb_count > $this->getParam('chop_breadcrumb_links')) {
354                        // A last linked crumb.
355                        $breadcrumbs[] =  '<a href="' . $url . '">' . sprintf($this->getParam('last_crumb_format'), oTxt($page['title'], true)) . '</a>';
356                    }
357                } else {
358                    if ('' == trim($page['url'])) {
359                        // A crumb with no link.
360                        $breadcrumbs[] = oTxt($pathmark . $page['title'], true);
361                    } else {
362                        // A normal linked crumb.
363                        $breadcrumbs[] = '<a href="' . $url . '">' . oTxt($page['title'], true) . '</a>';
364                    }
365                }
366                $pathmark = $this->getParam('path_delimiter');
367                $crumb_count--;
368            }
369            return join(oTxt($pathmark, true), $breadcrumbs);
370        } else {
371            return '';
372        }
373    }
374
375    /*
376    *
377    *
378    * @access   public
379    * @param
380    * @return
381    * @author   Quinn Comendant <quinn@strangecode.com>
382    * @version  1.0
383    * @since    07 Sep 2014 12:22:19
384    */
385    public function getBreadcrumbsUL($carry_args=null, $ul_open_tag='<ul class="breadcrumbs">')
386    {
387        $app =& App::getInstance();
388
389        $out = '';
390        $breadcrumbs = $this->getBreadcrumbsArray($carry_args);
391        if (!empty($breadcrumbs)) {
392            $out = $ul_open_tag;
393            foreach ($breadcrumbs as $b) {
394                $printclass = '' != $b['class'] ? sprintf(' class="%s"', $b['class']) : '';
395                if ('' == trim($b['url'])) {
396                    // A crumb with no link.
397                    $out .= sprintf('<li%s>%s</li>', $printclass, $b['title']);
398                } else {
399                    // A normal linked crumb.
400                    $out .= sprintf('<li%s><a href="%s">%s</a></li>', $printclass, $b['url'], $b['title']);
401                }
402            }
403            $out .= '</ul>';
404        }
405        return $out;
406    }
407
408    /**
409     * Test if the given URI matches the URL of the current page. By default the URI is tested
410     * without concern
411     * One use is to change the returned value for a positive match
412     * so a css class prints for an element representing the current page:
413     *   echo $nav->currentPage('/script.php?op=info', ' class="current"', '', true);
414     * The above will match only if the current page (REQUEST_URI) is also '/script.php?op=info',
415     * and will return the string ' class="current"' if it is.
416     *
417     * @access  public
418     *
419     * @param   mixed   $test_uri       A URI, or an array of URIs, to test against the current page.
420     * @param   mixed   $true_return    The value to return if the current page matches the test URI.
421     * @param   mixed   $false_return   The value to return if the current page does not match the test URI.
422     * @param   bool    $include_query  If set true, include the URI query string in the test.
423     *
424     * @return  mixed   If the test URI matches the current page URI, the value given for $true_return
425     *                  is returned (true by default), otherwise the value given for $false_return is
426     *                  returned (false by default).
427     */
428    public function currentPage($test_uri, $true_return=true, $false_return=false, $include_query=false)
429    {
430        $app =& App::getInstance();
431
432        // If given an array, test each URI recursively returning TRUE on a first match, or FALSE if none match.
433        if (is_array($test_uri)) {
434            foreach ($test_uri as $uri) {
435                if ($this->currentPage($uri, $true_return, $false_return, $include_query)) {
436                    return true;
437                }
438            }
439            return false;
440        }
441
442        $actual_uri = $include_query ? $_SERVER['REQUEST_URI'] : (strstr(getenv('REQUEST_URI'), '?', true) ?: getenv('REQUEST_URI')); // strstr() returns false if '?' is not found, so use a shorthand ternary operator.
443        $test_uri = $include_query ? $test_uri : (strstr($test_uri, '?', true) ?: $test_uri); // strstr() returns false if '?' is not found, so use a shorthand ternary operator.
444        if (mb_strtolower($test_uri) == mb_strtolower($actual_uri)) {
445            // $app->logMsg(sprintf('Current page (%s) == test URI (%s)', $actual_uri, $test_uri), LOG_DEBUG, __FILE__, __LINE__);
446            return $true_return;
447        }
448        // $app->logMsg(sprintf('Current page (%s) != test URI (%s)', $actual_uri, $test_uri), LOG_DEBUG, __FILE__, __LINE__);
449        return $false_return;
450    }
451
452}
453// End of class.
Note: See TracBrowser for help on using the repository browser.