Project

General

Profile

1 827 doc
<?php
2 1293 Luisehahne
/**
3
 *
4
 * @category        module
5
 * @package         show_menu2
6
 * @author          WebsiteBaker Project
7
 * @copyright       2004-2009, Ryan Djurovich
8 1349 Luisehahne
 * @copyright       2009-2011, Website Baker Org. e.V.
9 1293 Luisehahne
 * @link			http://www.websitebaker2.org/
10
 * @license         http://www.gnu.org/licenses/gpl.html
11
 * @platform        WebsiteBaker 2.7.0 | 2.8.x
12 1349 Luisehahne
 * @requirements    PHP 5.2.2 and higher
13 1293 Luisehahne
 * @version         $Id$
14
 * @filesource		$HeadURL$
15
 * @lastmodified    $Date$
16
 *
17
 */
18 860 Ruebenwurz
19 1183 Luisehahne
define('SM2_ROOT',       -1000);
20
define('SM2_CURR',       -2000);
21
define('SM2_ALLMENU',       -1);
22
define('SM2_START',       1000);
23
define('SM2_MAX',         2000);
24
define('SM2_ALL',       0x0001); // bit 0 (group 1) (Note: also used for max level!)
25
define('SM2_TRIM',      0x0002); // bit 1 (group 1)
26
define('SM2_CRUMB',     0x0004); // bit 2 (group 1)
27
define('SM2_SIBLING',   0x0008); // bit 3 (group 1)
28
define('SM2_NUMCLASS',  0x0010); // bit 4
29
define('SM2_ALLINFO',   0x0020); // bit 5
30
define('SM2_NOCACHE',   0x0040); // bit 6
31
define('SM2_PRETTY',    0x0080); // bit 7
32
define('SM2_ESCAPE',    0x0100); // bit 8
33
define('SM2_NOESCAPE',       0); // NOOP, unnecessary with WB 2.6.7+
34
define('SM2_BUFFER',    0x0200); // bit 9
35
define('SM2_CURRTREE',  0x0400); // bit 10
36 1293 Luisehahne
define('SM2_SHOWHIDDEN', 0x0800); // bit 11
37 827 doc
38
define('_SM2_GROUP_1',  0x000F); // exactly one flag from group 1 is required
39
40
41
// Implement support for page_menu and show_menu using show_menu2. If you remove
42
// the comments characters from the beginning of the following include, all menu
43
// functions in Website Baker will be implemented using show_menu2. While it is
44
// commented out, the original WB functions will be used.
45
//include('legacy.php');
46
47
// This class is the default menu formatter for sm2. If desired, you can
48
// create your own formatter class and pass the object into show_menu2
49
// as $aItemFormat.
50
define('SM2_CONDITIONAL','if\s*\(([^\)]+)\)\s*{([^}]*)}\s*(?:else\s*{([^}]*)}\s*)?');
51
define('SM2_COND_TERM','\s*(\w+)\s*(<|<=|==|=|=>|>|!=)\s*([\w\-]+)\s*');
52
class SM2_Formatter
53
{
54
    var $output;
55
    var $flags;
56
    var $itemOpen;
57
    var $itemClose;
58
    var $menuOpen;
59
    var $menuClose;
60
    var $topItemOpen;
61
    var $topMenuOpen;
62
63
    var $isFirst;
64
    var $page;
65
    var $url;
66
    var $currSib;
67
    var $sibCount;
68
    var $currClass;
69
    var $prettyLevel;
70
71
    // output the data
72
    function output($aString) {
73
        if ($this->flags & SM2_BUFFER) {
74
            $this->output .= $aString;
75
        }
76
        else {
77
            echo $aString;
78
        }
79
    }
80
81
    // set the default values for all of our formatting items
82
    function set($aFlags, $aItemOpen, $aItemClose, $aMenuOpen, $aMenuClose, $aTopItemOpen, $aTopMenuOpen) {
83
        $this->flags        = $aFlags;
84
        $this->itemOpen     = is_string($aItemOpen)    ? $aItemOpen    : '[li][a][menu_title]</a>';
85
        $this->itemClose    = is_string($aItemClose)   ? $aItemClose   : '</li>';
86
        $this->menuOpen     = is_string($aMenuOpen)    ? $aMenuOpen    : '[ul]';
87
        $this->menuClose    = is_string($aMenuClose)   ? $aMenuClose   : '</ul>';
88
        $this->topItemOpen  = is_string($aTopItemOpen) ? $aTopItemOpen : $this->itemOpen;
89
        $this->topMenuOpen  = is_string($aTopMenuOpen) ? $aTopMenuOpen : $this->menuOpen;
90
    }
91
92
    // initialize the state of the formatter before anything is output
93
    function initialize() {
94
        $this->output = '';
95
        $this->prettyLevel = 0;
96
        if ($this->flags & SM2_PRETTY) {
97
            $this->output("\n<!-- show_menu2 -->");
98
        }
99
    }
100
101
    // start a menu
102
    function startList(&$aPage, &$aUrl) {
103
        $currClass = '';
104
        $currItem = $this->menuOpen;
105
106
        // use the top level menu open if this is the first menu
107
        if ($this->topMenuOpen) {
108
            $currItem = $this->topMenuOpen;
109
            $currClass .= ' menu-top';
110
            $this->topMenuOpen = false;
111
        }
112
113
        // add the numbered menu class only if requested
114
        if (($this->flags & SM2_NUMCLASS) == SM2_NUMCLASS) {
115
            $currClass .= ' menu-'.$aPage['level'];
116
        }
117
118
        $this->prettyLevel += 1;
119
120
        // replace all keywords in the output
121
        if ($this->flags & SM2_PRETTY) {
122
            $this->output("\n".str_repeat(' ',$this->prettyLevel).
123
                $this->format($aPage, $aUrl, $currItem, $currClass));
124
        }
125
        else {
126
            $this->output($this->format($aPage, $aUrl, $currItem, $currClass));
127
        }
128
129
        $this->prettyLevel += 3;
130
    }
131
132
    // start an item within the menu
133
    function startItem(&$aPage, &$aUrl, $aCurrSib, $aSibCount) {
134
        // generate our class list
135
        $currClass = '';
136
        if (($this->flags & SM2_NUMCLASS) == SM2_NUMCLASS) {
137
            $currClass .= ' menu-'.$aPage['level'];
138
        }
139
        if (array_key_exists('sm2_has_child', $aPage)) {
140
            // not set if false, so existence = true
141
            $currClass .= ' menu-expand';
142
        }
143 845 doc
        if (array_key_exists('sm2_is_curr', $aPage)) {
144 827 doc
            $currClass .= ' menu-current';
145
        }
146
        elseif (array_key_exists('sm2_is_parent', $aPage)) {
147
            // not set if false, so existence = true
148
            $currClass .= ' menu-parent';
149
        }
150
        elseif (array_key_exists('sm2_is_sibling', $aPage)) {
151
            // not set if false, so existence = true
152
            $currClass .= ' menu-sibling';
153
        }
154
        elseif (array_key_exists('sm2_child_level',$aPage)) {
155
            // not set if not a child
156
            $currClass .= ' menu-child';
157
            if (($this->flags & SM2_NUMCLASS) == SM2_NUMCLASS) {
158
                $currClass .= ' menu-child-'.($aPage['sm2_child_level']-1);
159
            }
160
        }
161
        if ($aCurrSib == 1) {
162
            $currClass .= ' menu-first';
163
        }
164
        if ($aCurrSib == $aSibCount) {
165
            $currClass .= ' menu-last';
166
        }
167
168
        // use the top level item if this is the first item
169
        $currItem = $this->itemOpen;
170
        if ($this->topItemOpen) {
171
            $currItem = $this->topItemOpen;
172
            $this->topItemOpen = false;
173
        }
174
175
        // replace all keywords in the output
176
        if ($this->flags & SM2_PRETTY) {
177
            $this->output("\n".str_repeat(' ',$this->prettyLevel));
178
        }
179
        $this->output($this->format($aPage, $aUrl, $currItem, $currClass, $aCurrSib, $aSibCount));
180
    }
181
182
    // find and replace all keywords, setting the state variables first
183
    function format(&$aPage, &$aUrl, &$aCurrItem, &$aCurrClass,
184
        $aCurrSib = 0, $aSibCount = 0)
185
    {
186
        $this->page      = &$aPage;
187
        $this->url       = &$aUrl;
188
        $this->currClass = trim($aCurrClass);
189
        $this->currSib   = $aCurrSib;
190
        $this->sibCount  = $aSibCount;
191
192
        $item = $this->format2($aCurrItem);
193
194
        unset($this->page);
195
        unset($this->url);
196
        unset($this->currClass);
197
198
        return $item;
199
    }
200
201
    // find and replace all keywords
202
    function format2(&$aCurrItem) {
203
        if (!is_string($aCurrItem)) return '';
204
        return preg_replace(
205
            '@\[('.
206
                'a|ac|/a|li|/li|ul|/ul|menu_title|page_title|url|target|page_id|'.
207
                'parent|level|sib|sibCount|class|description|keywords|'.
208
                SM2_CONDITIONAL.
209
            ')\]@e',
210
            '$this->replace("\1")', $aCurrItem);
211
    }
212
213
    // replace the keywords
214
    function replace($aMatch) {
215
        switch ($aMatch) {
216
        case 'a':
217
            return '<a href="'.$this->url.'" target="'.$this->page['target'].'">';
218
        case 'ac':
219
            return '<a href="'.$this->url.'" target="'.$this->page['target'].'" class="'.$this->currClass.'">';
220
        case '/a':
221
            return '</a>';
222
        case 'li':
223
            return '<li class="'.$this->currClass.'">';
224
        case '/li':
225
            return '</li>';
226
        case 'ul':
227
            return '<ul class="'.$this->currClass.'">';
228
        case '/ul':
229
            return '</ul>';
230
        case 'url':
231
            return $this->url;
232
        case 'sib':
233
            return $this->currSib;
234
        case 'sibCount':
235
            return $this->sibCount;
236
        case 'class':
237
            return $this->currClass;
238
        default:
239
            if (array_key_exists($aMatch, $this->page)) {
240
                if ($this->flags & SM2_ESCAPE) {
241
                    return htmlspecialchars($this->page[$aMatch], ENT_QUOTES);
242
                }
243
                else {
244
                    return $this->page[$aMatch];
245
                }
246
            }
247
            if (preg_match('/'.SM2_CONDITIONAL.'/', $aMatch, $rgMatches)) {
248
                return $this->replaceIf($rgMatches[1], $rgMatches[2], $rgMatches[3]);
249
            }
250
        }
251
        return "[$aMatch=UNKNOWN]";
252
    }
253
254
    // conditional replacement
255
    function replaceIf(&$aExpression, &$aIfValue, &$aElseValue) {
256
        // evaluate all of the tests in the conditional (we don't do short-circuit
257
        // evaluation) and replace the string test with the boolean result
258
        $rgTests = preg_split('/(\|\||\&\&)/', $aExpression, -1, PREG_SPLIT_DELIM_CAPTURE);
259
        for ($n = 0; $n < count($rgTests); $n += 2) {
260
            if (preg_match('/'.SM2_COND_TERM.'/', $rgTests[$n], $rgMatches)) {
261
                $rgTests[$n] = $this->ifTest($rgMatches[1], $rgMatches[2], $rgMatches[3]);
262
            }
263
            else {
264 1186 Luisehahne
                @error_logs("show_menu2 error: conditional expression is invalid!");
265 827 doc
                $rgTests[$n] = false;
266
            }
267
        }
268
269
        // combine all test results for a final result
270
        $ok = $rgTests[0];
271
        for ($n = 1; $n+1 < count($rgTests); $n += 2) {
272
            if ($rgTests[$n] == '||') {
273
                $ok = $ok || $rgTests[$n+1];
274
            }
275
            else {
276
                $ok = $ok && $rgTests[$n+1];
277
            }
278
        }
279
280
        // return the formatted expression if the test succeeded
281
        return $ok ? $this->format2($aIfValue) : $this->format2($aElseValue);
282
    }
283
284
    // conditional test
285
    function ifTest(&$aKey, &$aOperator, &$aValue) {
286
        global $wb;
287
288
        // find the correct operand
289
        $operand = false;
290
        switch($aKey) {
291
        case 'class':
292
            // we need to wrap the class names in spaces so we can test for a unique
293
            // class name that will not match prefixes or suffixes. Same must be done
294
            // for the value we are testing.
295
            $operand = " $this->currClass ";
296
            break;
297
        case 'sib':
298
            $operand = $this->currSib;
299
            if ($aValue == 'sibCount') {
300
                $aValue = $this->sibCount;
301
            }
302
            break;
303
        case 'sibCount':
304
            $operand = $this->sibCount;
305
            break;
306
        case 'level':
307
            $operand = $this->page['level'];
308
            switch ($aValue) {
309
            case 'root':    $aValue = 0; break;
310
            case 'granny':  $aValue = $wb->page['level']-2; break;
311
            case 'parent':  $aValue = $wb->page['level']-1; break;
312
            case 'current': $aValue = $wb->page['level'];   break;
313
            case 'child':   $aValue = $wb->page['level']+1; break;
314
            }
315
            if ($aValue < 0) $aValue = 0;
316
            break;
317
        case 'id':
318
            $operand = $this->page['page_id'];
319
            switch ($aValue) {
320
            case 'parent':  $aValue = $wb->page['parent'];  break;
321
            case 'current': $aValue = $wb->page['page_id']; break;
322
            }
323
            break;
324
        default:
325
            return '';
326
        }
327
328
        // do the test
329
        $ok = false;
330
        switch ($aOperator) {
331
        case '<':
332
            $ok = ($operand < $aValue);
333
            break;
334
        case '<=':
335
            $ok = ($operand <= $aValue);
336
            break;
337
        case '=':
338
        case '==':
339
        case '!=':
340
            if ($aKey == 'class') {
341
                $ok = strstr($operand, " $aValue ") !== FALSE;
342
            }
343
            else {
344
                $ok = ($operand == $aValue);
345
            }
346
            if ($aOperator == '!=') {
347
                $ok = !$ok;
348
            }
349
            break;
350
        case '>=':
351
            $ok = ($operand >= $aValue);
352
        case '>':
353
            $ok = ($operand > $aValue);
354
        }
355
356
        return $ok;
357
    }
358
359
    // finish the current menu item
360
    function finishItem() {
361
        if ($this->flags & SM2_PRETTY) {
362
            $this->output(str_repeat(' ',$this->prettyLevel).$this->itemClose);
363
        }
364
        else {
365
            $this->output($this->itemClose);
366
        }
367
    }
368 1313 Luisehahne
369 827 doc
    // finish the current menu
370
    function finishList() {
371
        $this->prettyLevel -= 3;
372
373
        if ($this->flags & SM2_PRETTY) {
374
            $this->output("\n".str_repeat(' ',$this->prettyLevel).$this->menuClose."\n");
375
        }
376
        else {
377
            $this->output($this->menuClose);
378
        }
379
380
        $this->prettyLevel -= 1;
381
    }
382
383
    // cleanup the state of the formatter after everything has been output
384
    function finalize() {
385
        if ($this->flags & SM2_PRETTY) {
386
            $this->output("\n");
387
        }
388
    }
389
390
    // return the output
391
    function getOutput() {
392
        return $this->output;
393
    }
394
};
395
396 1186 Luisehahne
function error_logs($error_str)
397
{
398
                $log_error = true;
399
                if ( ! function_exists('error_log') )
400
                        $log_error = false;
401
402
                $log_file = @ini_get('error_log');
403
                if ( !empty($log_file) && ('syslog' != $log_file) && !@is_writable($log_file) )
404
                        $log_error = false;
405
406
                if ( $log_error )
407
                        @error_log($error_str, 0);
408
}
409
410 827 doc
function show_menu2(
411
    $aMenu          = 0,
412
    $aStart         = SM2_ROOT,
413
    $aMaxLevel      = -1999, // SM2_CURR+1
414 859 Ruebenwurz
    $aOptions       = SM2_TRIM,
415 827 doc
    $aItemOpen      = false,
416
    $aItemClose     = false,
417
    $aMenuOpen      = false,
418
    $aMenuClose     = false,
419
    $aTopItemOpen   = false,
420
    $aTopMenuOpen   = false
421
    )
422
{
423
    global $wb;
424
425 859 Ruebenwurz
    // extract the flags and set $aOptions to an array
426
    $flags = 0;
427
    if (is_int($aOptions)) {
428
        $flags = $aOptions;
429
        $aOptions = array();
430
    }
431
    else if (isset($aOptions['flags'])) {
432
        $flags = $aOptions['flags'];
433
    }
434
    else {
435
        $flags = SM2_TRIM;
436
        $aOptions = array();
437 1186 Luisehahne
        @error_logs('show_menu2 error: $aOptions is invalid. No flags supplied!');
438 859 Ruebenwurz
    }
439
440 1185 Luisehahne
    // ensure we have our group 1 flag, we don't check for the "exactly 1" part, but
441 827 doc
    // we do ensure that they provide at least one.
442 859 Ruebenwurz
    if (0 == ($flags & _SM2_GROUP_1)) {
443 1186 Luisehahne
        @error_logs('show_menu2 error: $aOptions is invalid. No flags from group 1 supplied!');
444 859 Ruebenwurz
        $flags |= SM2_TRIM; // default to TRIM
445 827 doc
    }
446
447 845 doc
    // search page results don't have any of the page data loaded by WB, so we load it
448
    // ourselves using the referrer ID as the current page
449
    $CURR_PAGE_ID = defined('REFERRER_ID') ? REFERRER_ID : PAGE_ID;
450
    if (count($wb->page) == 0 && defined('REFERRER_ID') && REFERRER_ID > 0) {
451
        global $database;
452 1313 Luisehahne
        $sql = 'SELECT * FROM `'.TABLE_PREFIX.'pages` WHERE `page_id` = '.REFERRER_ID.'';
453 845 doc
        $result = $database->query($sql);
454
        if ($result->numRows() == 1) {
455
            $wb->page = $result->fetchRow();
456
        }
457
        unset($result);
458
    }
459
460 827 doc
    // fix up the menu number to default to the menu number
461
    // of the current page if no menu has been supplied
462
    if ($aMenu == 0) {
463
        $aMenu = $wb->page['menu'] == '' ? 1 : $wb->page['menu'];
464
    }
465
466
    // Set some of the $wb->page[] settings to defaults if not set
467 845 doc
    $pageLevel  = $wb->page['level']  == '' ? 0 : $wb->page['level'];
468 827 doc
    $pageParent = $wb->page['parent'] == '' ? 0 : $wb->page['parent'];
469
470
    // adjust the start level and start page ID as necessary to
471
    // handle the special values that can be passed in as $aStart
472
    $aStartLevel = 0;
473
    if ($aStart < SM2_ROOT) {   // SM2_CURR+N
474
        if ($aStart == SM2_CURR) {
475
            $aStartLevel = $pageLevel;
476 845 doc
            $aStart = $pageParent;
477 827 doc
        }
478
        else {
479
            $aStartLevel = $pageLevel + $aStart - SM2_CURR;
480 845 doc
            $aStart = $CURR_PAGE_ID;
481 827 doc
        }
482
    }
483
    elseif ($aStart < 0) {   // SM2_ROOT+N
484
        $aStartLevel = $aStart - SM2_ROOT;
485
        $aStart = 0;
486
    }
487
488
    // we get the menu data once and store it in a global variable. This allows
489
    // multiple calls to show_menu2 in a single page with only a single call to
490
    // the database. If this variable exists, then we have already retrieved all
491
    // of the information and processed it, so we don't need to do it again.
492 859 Ruebenwurz
    if (($flags & SM2_NOCACHE) != 0
493 1183 Luisehahne
        || !array_key_exists('show_menu2_data', $GLOBALS)
494 827 doc
        || !array_key_exists($aMenu, $GLOBALS['show_menu2_data']))
495
    {
496
        global $database;
497
498
        // create an array of all parents of the current page. As the page_trail
499
        // doesn't include the theoretical root element 0, we add it ourselves.
500 1189 Luisehahne
        $rgCurrParents = explode(",", '0,'.$wb->page['page_trail']);
501 827 doc
        array_pop($rgCurrParents); // remove the current page
502
        $rgParent = array();
503
504
        // if the caller wants all menus gathered together (e.g. for a sitemap)
505
        // then we don't limit our SQL query
506
        $menuLimitSql = ' AND menu = ' .$aMenu;
507
        if ($aMenu == SM2_ALLMENU) {
508
            $menuLimitSql = '';
509
        }
510
511
        // we only load the description and keywords if we have been told to,
512
        // this cuts the memory load for pages that don't use them. Note that if
513
        // we haven't been told to load these fields the *FIRST TIME* show_menu2
514
        // is called (i.e. where the database is loaded) then the info won't
515
        // exist anyhow.
516
        $fields = 'parent,page_id,menu_title,page_title,link,target,level,visibility,viewing_groups';
517 845 doc
        if (version_compare(WB_VERSION, '2.7', '>=')) { // WB 2.7+
518 827 doc
            $fields .= ',viewing_users';
519
        }
520 859 Ruebenwurz
        if ($flags & SM2_ALLINFO) {
521 827 doc
            $fields = '*';
522
        }
523 1313 Luisehahne
524
        // we request all matching rows from the database for the menu that we
525
        // are about to create it is cheaper for us to get everything we need
526 827 doc
        // from the database once and create the menu from memory then make
527
        // multiple calls to the database.
528
        $sql = "SELECT $fields FROM ".TABLE_PREFIX.
529
               "pages WHERE $wb->extra_where_sql $menuLimitSql ".
530
               'ORDER BY level ASC, position ASC';
531 971 Ruebenwurz
        $sql = str_replace('hidden', 'IGNOREME', $sql); // we want the hidden pages
532 827 doc
        $oRowset = $database->query($sql);
533
        if (is_object($oRowset) && $oRowset->numRows() > 0) {
534
            // create an in memory array of the database data based on the item's parent.
535
            // The array stores all elements in the correct display order.
536
            while ($page = $oRowset->fetchRow()) {
537 845 doc
                // ignore all pages that the current user is not permitted to view
538 827 doc
                if(version_compare(WB_VERSION, '2.7', '>=')) { // WB >= 2.7
539 971 Ruebenwurz
                    // 1. hidden pages aren't shown unless they are on the current page
540
                    if ($page['visibility'] == 'hidden') {
541
                        $page['sm2_hide'] = true;
542 827 doc
                    }
543 971 Ruebenwurz
544
                    // 2. all pages with no active sections (unless it is the top page) are ignored
545
                    else if (!$wb->page_is_active($page) && $page['link'] != $wb->default_link && !INTRO_PAGE) {
546 827 doc
                        continue;
547
                    }
548 971 Ruebenwurz
549
                    // 3. all pages not visible to this user (unless always visible to registered users) are ignored
550
                    else if (!$wb->page_is_visible($page) && $page['visibility'] != 'registered') {
551
                        continue;
552
                    }
553 827 doc
                }
554
555
                // ensure that we have an array entry in the table to add this to
556
                $idx = $page['parent'];
557
                if (!array_key_exists($idx, $rgParent)) {
558
                    $rgParent[$idx] = array();
559
                }
560
561
                // mark our current page as being on the current path
562 845 doc
                if ($page['page_id'] == $CURR_PAGE_ID) {
563
                    $page['sm2_is_curr'] = true;
564 827 doc
                    $page['sm2_on_curr_path'] = true;
565 1293 Luisehahne
                    if ($flags & SM2_SHOWHIDDEN)
566
					{
567
                        // show hidden pages if active and SHOWHIDDEN flag supplied
568
                        unset($page['sm2_hide']);
569
                    }
570 827 doc
                }
571
572
                // mark parents of the current page as such
573
                if (in_array($page['page_id'], $rgCurrParents)) {
574
                    $page['sm2_is_parent'] = true;
575
                    $page['sm2_on_curr_path'] = true;
576 1293 Luisehahne
                    if ($flags & SM2_SHOWHIDDEN)
577
					{
578
                        // show hidden pages if active and SHOWHIDDEN flag supplied
579
						unset($page['sm2_hide']); // don't hide a parent page
580
                    }
581 827 doc
                }
582
583
                // add the entry to the array
584
                $rgParent[$idx][] = $page;
585
            }
586
        }
587
        unset($oRowset);
588
589
        // mark all elements that are siblings of any element on the current path
590
        foreach ($rgCurrParents as $x) {
591
            if (array_key_exists($x, $rgParent)) {
592
                foreach (array_keys($rgParent[$x]) as $y) {
593
                    $mark =& $rgParent[$x][$y];
594
                    $mark['sm2_path_sibling'] = true;
595
                    unset($mark);
596
                }
597
            }
598
        }
599
600
        // mark all elements that have children and are siblings of the current page
601
        $parentId = $pageParent;
602
        foreach (array_keys($rgParent) as $x) {
603
            $childSet =& $rgParent[$x];
604
            foreach (array_keys($childSet) as $y) {
605
                $mark =& $childSet[$y];
606
                if (array_key_exists($mark['page_id'], $rgParent)) {
607
                    $mark['sm2_has_child'] = true;
608
                }
609 845 doc
                if ($mark['parent'] == $parentId && $mark['page_id'] != $CURR_PAGE_ID) {
610 827 doc
                    $mark['sm2_is_sibling'] = true;
611
                }
612
                unset($mark);
613
            }
614
            unset($childSet);
615
        }
616
617
        // mark all children of the current page. We don't do this when
618 845 doc
        // $CURR_PAGE_ID is 0, as 0 is the parent of everything.
619
        // $CURR_PAGE_ID == 0 occurs on special pages like search results
620
        // when no referrer is available.s
621
        if ($CURR_PAGE_ID != 0) {
622
            sm2_mark_children($rgParent, $CURR_PAGE_ID, 1);
623 827 doc
        }
624
625
        // store the complete processed menu data as a global. We don't
626
        // need to read this from the database anymore regardless of how
627
        // many menus are displayed on the same page.
628
        if (!array_key_exists('show_menu2_data', $GLOBALS)) {
629
            $GLOBALS['show_menu2_data'] = array();
630
        }
631
        $GLOBALS['show_menu2_data'][$aMenu] =& $rgParent;
632
        unset($rgParent);
633
    }
634
635
    // adjust $aMaxLevel to the level number of the final level that
636
    // will be displayed. That is, we display all levels <= aMaxLevel.
637
    if ($aMaxLevel == SM2_ALL) {
638
        $aMaxLevel = 1000;
639
    }
640
    elseif ($aMaxLevel < 0) {   // SM2_CURR+N
641
        $aMaxLevel += $pageLevel - SM2_CURR;
642
    }
643
    elseif ($aMaxLevel >= SM2_MAX) { // SM2_MAX+N
644
        $aMaxLevel += $aStartLevel - SM2_MAX;
645
        if ($aMaxLevel > $pageLevel) {
646
            $aMaxLevel = $pageLevel;
647
        }
648
    }
649
    else {  // SM2_START+N
650
        $aMaxLevel += $aStartLevel - SM2_START;
651
    }
652
653
    // generate the menu
654
    $retval = false;
655
    if (array_key_exists($aStart, $GLOBALS['show_menu2_data'][$aMenu])) {
656
        $formatter = $aItemOpen;
657
        if (!is_object($aItemOpen)) {
658
            static $sm2formatter;
659
            if (!isset($sm2formatter)) {
660
                $sm2formatter = new SM2_Formatter;
661
            }
662
            $formatter = $sm2formatter;
663 859 Ruebenwurz
            $formatter->set($flags, $aItemOpen, $aItemClose,
664 827 doc
                $aMenuOpen, $aMenuClose, $aTopItemOpen, $aTopMenuOpen);
665
        }
666
667 859 Ruebenwurz
        // adjust the level until we show everything and ignore the SM2_TRIM flag.
668
        // Usually this will be less than the start level to disable it.
669
        $showAllLevel = $aStartLevel - 1;
670
        if (isset($aOptions['notrim'])) {
671
            $showAllLevel = $aStartLevel + $aOptions['notrim'];
672
        }
673
674 827 doc
        // display the menu
675
        $formatter->initialize();
676
        sm2_recurse(
677
            $GLOBALS['show_menu2_data'][$aMenu],
678
            $aStart,    // parent id to start displaying sub-menus
679 859 Ruebenwurz
            $aStartLevel, $showAllLevel, $aMaxLevel, $flags,
680 827 doc
            $formatter);
681
        $formatter->finalize();
682
683
        // if we are returning something, get the data
684 859 Ruebenwurz
        if (($flags & SM2_BUFFER) != 0) {
685 827 doc
            $retval = $formatter->getOutput();
686
        }
687
    }
688
689
    // clear the data if we aren't caching it
690 859 Ruebenwurz
    if (($flags & SM2_NOCACHE) != 0) {
691 827 doc
        unset($GLOBALS['show_menu2_data'][$aMenu]);
692
    }
693
694
    return $retval;
695
}
696
697
function sm2_mark_children(&$rgParent, $aStart, $aChildLevel)
698
{
699
    if (array_key_exists($aStart, $rgParent)) {
700
        foreach (array_keys($rgParent[$aStart]) as $y) {
701
            $mark =& $rgParent[$aStart][$y];
702
            $mark['sm2_child_level'] = $aChildLevel;
703
            $mark['sm2_on_curr_path'] = true;
704
            sm2_mark_children($rgParent, $mark['page_id'], $aChildLevel+1);
705
        }
706
    }
707
}
708
709
function sm2_recurse(
710
    &$rgParent, $aStart,
711 859 Ruebenwurz
    $aStartLevel, $aShowAllLevel, $aMaxLevel, $aFlags,
712 827 doc
    &$aFormatter
713
    )
714
{
715
    global $wb;
716
717
    // on entry to this function we know that there are entries for this
718
    // parent and all entries for that parent are being displayed. We also
719
    // need to check if any of the children need to be displayed too.
720
    $isListOpen = false;
721
    $currentLevel = $wb->page['level'] == '' ? 0 : $wb->page['level'];
722 972 Ruebenwurz
723
    // get the number of siblings skipping the hidden pages so we can pass
724
    // this in and check if the item is first or last
725
    $sibCount = 0;
726
    foreach ($rgParent[$aStart] as $page) {
727
        if (!array_key_exists('sm2_hide', $page)) $sibCount++;
728
    }
729 827 doc
730
    $currSib = 0;
731
    foreach ($rgParent[$aStart] as $page) {
732 972 Ruebenwurz
        // skip all hidden pages
733
        if (array_key_exists('sm2_hide', $page)) { // not set if false, so existence = true
734
            continue;
735
        }
736
737 827 doc
        $currSib++;
738
739
        // skip any elements that are lower than the maximum level
740
        $pageLevel = $page['level'];
741
        if ($pageLevel > $aMaxLevel) {
742
            continue;
743
        }
744
745
        // this affects ONLY the top level
746
        if ($aStart == 0 && ($aFlags & SM2_CURRTREE)) {
747
            if (!array_key_exists('sm2_on_curr_path', $page)) { // not set if false, so existence = true
748
                continue;
749
            }
750
            $sibCount = 1;
751
        }
752
753
        // trim the tree as appropriate
754
        if ($aFlags & SM2_SIBLING) {
755
            // parents, and siblings and children of current only
756
            if (!array_key_exists('sm2_on_curr_path', $page)      // not set if false, so existence = true
757
                && !array_key_exists('sm2_is_sibling', $page)     // not set if false, so existence = true
758
                && !array_key_exists('sm2_child_level', $page)) { // not set if false, so existence = true
759
                continue;
760
            }
761
        }
762
        else if ($aFlags & SM2_TRIM) {
763
            // parents and siblings of parents
764 859 Ruebenwurz
            if ($pageLevel > $aShowAllLevel  // permit all levels to be shown
765
                && !array_key_exists('sm2_on_curr_path', $page)    // not set if false, so existence = true
766 827 doc
                && !array_key_exists('sm2_path_sibling', $page)) {  // not set if false, so existence = true
767
                continue;
768
            }
769
        }
770
        elseif ($aFlags & SM2_CRUMB) {
771
            // parents only
772
            if (!array_key_exists('sm2_on_curr_path', $page)    // not set if false, so existence = true
773
                || array_key_exists('sm2_child_level', $page)) {  // not set if false, so existence = true
774
                continue;
775
            }
776
        }
777
778
        // depth first traverse
779
        $nextParent = $page['page_id'];
780
781
        // display the current element if we have reached the start level
782
        if ($pageLevel >= $aStartLevel) {
783
            // massage the link into the correct form
784
            if(!INTRO_PAGE && $page['link'] == $wb->default_link) {
785
                $url = WB_URL;
786
            }
787
            else {
788
                $url = $wb->page_link($page['link']);
789
            }
790
791
            // we open the list only when we absolutely need to
792
            if (!$isListOpen) {
793
                $aFormatter->startList($page, $url);
794
                $isListOpen = true;
795
            }
796
797
            $aFormatter->startItem($page, $url, $currSib, $sibCount);
798
        }
799
800
        // display children as appropriate
801
        if ($pageLevel + 1 <= $aMaxLevel
802
            && array_key_exists('sm2_has_child', $page)) {  // not set if false, so existence = true
803
            sm2_recurse(
804
                $rgParent, $nextParent, // parent id to start displaying sub-menus
805 859 Ruebenwurz
                $aStartLevel, $aShowAllLevel, $aMaxLevel, $aFlags,
806 827 doc
                $aFormatter);
807
        }
808
809
        // close the current element if appropriate
810
        if ($pageLevel >= $aStartLevel) {
811
            $aFormatter->finishItem($pageLevel, $page);
812
        }
813
    }
814
815
    // close the list if we opened one
816
    if ($isListOpen) {
817
        $aFormatter->finishList();
818
    }
819
}
820
821
?>