Project

General

Profile

1
<?php
2
/**
3
 *
4
 * @category        module
5
 * @package         show_menu2
6
 * @author          WebsiteBaker Project
7
 * @copyright       Brodie Thiesfield
8
 * @copyright       2009-2013, WebsiteBaker Org. e.V.
9
 * @link            http://www.websitebaker.org/
10
 * @license         http://www.gnu.org/licenses/gpl.html
11
 * @platform        WebsiteBaker 2.7.0 | 2.8.x
12
 * @requirements    PHP 5.2.2 and higher
13
 * @version         $Id: include.php 1917 2013-06-07 04:12:10Z Luisehahne $
14
 * @filesource      $HeadURL: svn://isteam.dynxs.de/wb-archiv/branches/2.8.x/wb/modules/show_menu2/include.php $
15
 * @lastmodified    $Date: 2013-06-07 06:12:10 +0200 (Fri, 07 Jun 2013) $
16
 *
17
 */
18

    
19
// Must include code to stop this file being access directly
20
if(defined('WB_PATH') == false) { die("Cannot access this file directly"); }
21

    
22
define('SM2_ROOT',          -1000);
23
define('SM2_CURR',          -2000);
24
define('SM2_ALLMENU',          -1);
25
define('SM2_START',          1000);
26
define('SM2_MAX',            2000);
27
define('SM2_ALL',          0x0001); // bit 0 (group 1) (Note: also used for max level!)
28
define('SM2_TRIM',         0x0002); // bit 1 (group 1)
29
define('SM2_CRUMB',        0x0004); // bit 2 (group 1)
30
define('SM2_SIBLING',      0x0008); // bit 3 (group 1)
31
define('SM2_NUMCLASS',     0x0010); // bit 4
32
define('SM2_ALLINFO',      0x0020); // bit 5
33
define('SM2_NOCACHE',      0x0040); // bit 6
34
define('SM2_PRETTY',       0x0080); // bit 7
35
define('SM2_ESCAPE',       0x0100); // bit 8
36
define('SM2_NOESCAPE',          0); // NOOP, unnecessary with WB 2.6.7+
37
define('SM2_BUFFER',       0x0200); // bit 9
38
define('SM2_CURRTREE',     0x0400); // bit 10
39
define('SM2_SHOWHIDDEN',   0x0800); // bit 11
40
define('SM2_XHTML_STRICT', 0x1000); // bit 12
41
define('SM2_NO_TITLE',     0x1001); // bit 13
42

    
43
define('_SM2_GROUP_1',  0x000F); // exactly one flag from group 1 is required
44

    
45

    
46
// Implement support for page_menu and show_menu using show_menu2. If you remove
47
// the comments characters from the beginning of the following include, all menu
48
// functions in Website Baker will be implemented using show_menu2. While it is
49
// commented out, the original WB functions will be used.
50
//include('legacy.php');
51

    
52
// This class is the default menu formatter for sm2. If desired, you can 
53
// create your own formatter class and pass the object into show_menu2 
54
// as $aItemFormat.
55
define('SM2_CONDITIONAL','if\s*\(([^\)]+)\)\s*{([^}]*)}\s*(?:else\s*{([^}]*)}\s*)?');
56
define('SM2_COND_TERM','\s*(\w+)\s*(<|<=|==|=|=>|>|!=)\s*([\w\-]+)\s*');
57
class SM2_Formatter
58
{
59
    var $output;
60
    var $flags;
61
    var $itemOpen;
62
    var $itemClose;
63
    var $menuOpen;
64
    var $menuClose;
65
    var $topItemOpen;
66
    var $topMenuOpen;
67
    
68
    var $isFirst;
69
    var $page;
70
    var $url;
71
    var $currSib;
72
    var $sibCount;
73
    var $currClass;
74
    var $prettyLevel;
75

    
76
    // output the data
77
    function output($aString) {
78
        if ($this->flags & SM2_BUFFER) {
79
            $this->output .= $aString;
80
        }
81
        else {
82
            echo $aString;
83
        }
84
    }
85
    
86
    // set the default values for all of our formatting items
87
    function set($aFlags, $aItemOpen, $aItemClose, $aMenuOpen, $aMenuClose, $aTopItemOpen, $aTopMenuOpen) {
88
        $this->flags        = $aFlags;
89
        $this->itemOpen     = is_string($aItemOpen)    ? $aItemOpen    : '[li][a][menu_title]</a>';
90
        $this->itemClose    = is_string($aItemClose)   ? $aItemClose   : '</li>';
91
        $this->menuOpen     = is_string($aMenuOpen)    ? $aMenuOpen    : '[ul]';
92
        $this->menuClose    = is_string($aMenuClose)   ? $aMenuClose   : '</ul>';
93
        $this->topItemOpen  = is_string($aTopItemOpen) ? $aTopItemOpen : $this->itemOpen;
94
        $this->topMenuOpen  = is_string($aTopMenuOpen) ? $aTopMenuOpen : $this->menuOpen;
95
    }
96

    
97
    // initialize the state of the formatter before anything is output
98
    function initialize() {
99
        $this->output = '';
100
        $this->prettyLevel = 0;
101
        if ($this->flags & SM2_PRETTY) {
102
            $this->output("\n<!-- show_menu2 -->");
103
        }
104
    }
105

    
106
    // start a menu     
107
    function startList(&$aPage, &$aUrl) {
108
        $currClass = '';
109
        $currItem = $this->menuOpen;
110
        
111
        // use the top level menu open if this is the first menu
112
        if ($this->topMenuOpen) {
113
            $currItem = $this->topMenuOpen;
114
            $currClass .= ' menu-top';
115
            $this->topMenuOpen = false;
116
        }
117
        
118
        // add the numbered menu class only if requested
119
        if (($this->flags & SM2_NUMCLASS) == SM2_NUMCLASS) {
120
            $currClass .= ' menu-'.$aPage['level'];
121
        }
122
        
123
        $this->prettyLevel += 1;
124
        
125
        // replace all keywords in the output
126
        if ($this->flags & SM2_PRETTY) {
127
            $this->output("\n".str_repeat(' ',$this->prettyLevel).
128
                $this->format($aPage, $aUrl, $currItem, $currClass));
129
        }
130
        else {
131
            $this->output($this->format($aPage, $aUrl, $currItem, $currClass));
132
        }
133
        
134
        $this->prettyLevel += 3;
135
    }
136
    
137
    // start an item within the menu
138
    function startItem(&$aPage, &$aUrl, $aCurrSib, $aSibCount) {
139
        // generate our class list
140
        $currClass = '';
141
        if (($this->flags & SM2_NUMCLASS) == SM2_NUMCLASS) {
142
            $currClass .= ' menu-'.$aPage['level'];
143
        }
144
        if (array_key_exists('sm2_has_child', $aPage)) {
145
            // not set if false, so existence = true
146
            $currClass .= ' menu-expand';
147
        }
148
        if (array_key_exists('sm2_is_curr', $aPage)) { 
149
            $currClass .= ' menu-current';
150
        }
151
        elseif (array_key_exists('sm2_is_parent', $aPage)) { 
152
            // not set if false, so existence = true
153
            $currClass .= ' menu-parent';
154
        }
155
        elseif (array_key_exists('sm2_is_sibling', $aPage)) {
156
            // not set if false, so existence = true
157
            $currClass .= ' menu-sibling';
158
        }
159
        elseif (array_key_exists('sm2_child_level',$aPage)) {
160
            // not set if not a child
161
            $currClass .= ' menu-child';
162
            if (($this->flags & SM2_NUMCLASS) == SM2_NUMCLASS) {
163
                $currClass .= ' menu-child-'.($aPage['sm2_child_level']-1);
164
            }
165
        }
166
        if ($aCurrSib == 1) {
167
            $currClass .= ' menu-first';
168
        }
169
        if ($aCurrSib == $aSibCount) {
170
            $currClass .= ' menu-last';
171
        }
172

    
173
        // use the top level item if this is the first item
174
        $currItem = $this->itemOpen;
175
        if ($this->topItemOpen) {
176
            $currItem = $this->topItemOpen;
177
            $this->topItemOpen = false;
178
        }
179

    
180
        // replace all keywords in the output
181
        if ($this->flags & SM2_PRETTY) {
182
            $this->output("\n".str_repeat(' ',$this->prettyLevel));
183
        }
184
        $this->output($this->format($aPage, $aUrl, $currItem, $currClass, $aCurrSib, $aSibCount));
185
    }
186
    
187
    // find and replace all keywords, setting the state variables first
188
    function format(&$aPage, &$aUrl, &$aCurrItem, &$aCurrClass, 
189
        $aCurrSib = 0, $aSibCount = 0) 
190
    {
191
        $this->page      = &$aPage;
192
        $this->url       = &$aUrl;
193
        $this->currClass = trim($aCurrClass);
194
        $this->currSib   = $aCurrSib;
195
        $this->sibCount  = $aSibCount;
196
        
197
        $item = $this->format2($aCurrItem);
198
        
199
        unset($this->page);
200
        unset($this->url);
201
        unset($this->currClass);
202
        
203
        return $item;
204
    }
205
    
206
    // find and replace all keywords
207
    function format2(&$aCurrItem) {
208
        if (!is_string($aCurrItem)) return '';
209
        return preg_replace_callback(
210
            '@\[('.
211
                'a|ac|/a|li|/li|ul|/ul|menu_title|menu_icon_0|menu_icon_1|'.
212
		        'page_title|page_icon|url|target|page_id|tooltip|'.
213
                'parent|level|sib|sibCount|class|description|keywords|'.
214
                SM2_CONDITIONAL.
215
            ')\]@',
216
            array($this, 'replace'),
217
            $aCurrItem);
218
    }
219
    
220
    // replace the keywords
221
    function replace($aMatches) {
222
        $aMatch = $aMatches[1];
223
        $retval = '['.$aMatch.'=UNKNOWN]';
224
        switch ($aMatch) {
225
        case 'a':
226
            $retval = '<a href="'.$this->url.'"';
227
			// break; // ignore 'break' to add the rest of <a>-tag
228
		case 'ac':
229
			if( substr($retval, 0, 2) != '<a'){
230
				$retval = '<a href="'.$this->url.'" class="'.$this->currClass.'"';
231
			}
232
			if(($this->flags & SM2_NO_TITLE)) {
233
				$retval .= ' title="'.$this->page['tooltip'].'"';
234
			}
235
			if(!($this->flags & SM2_XHTML_STRICT)) {
236
				$retval .= ' target="'.$this->page['target'].'"';
237
			}
238
			$retval .= '>';
239
			break;
240
        case '/a':
241
            $retval = '</a>'; break;
242
        case 'li':
243
            $retval = '<li class="'.$this->currClass.'">'; break;
244
        case '/li':
245
            $retval = '</li>'; break;
246
        case 'ul':
247
            $retval = '<ul class="'.$this->currClass.'">'; break;
248
        case '/ul':
249
            $retval = '</ul>'; break;
250
        case 'url':
251
            $retval = $this->url; break;
252
        case 'sib':
253
            $retval = $this->currSib; break;
254
        case 'sibCount':
255
            $retval = $this->sibCount; break;
256
        case 'class':
257
            $retval = $this->currClass; break;
258
        default:
259
            if (array_key_exists($aMatch, $this->page)) {
260
                if ($this->flags & SM2_ESCAPE) {
261
                    $retval = htmlspecialchars($this->page[$aMatch], ENT_QUOTES);
262
                }
263
                else {
264
                    $retval = $this->page[$aMatch];
265
                }
266
            }
267
            if (preg_match('/'.SM2_CONDITIONAL.'/', $aMatch, $rgMatches)) {
268
                $retval = $this->replaceIf($rgMatches[1], $rgMatches[2], $rgMatches[3]);
269
            }
270
        }
271
        return $retval;
272
    }
273
    
274
    // conditional replacement
275
    function replaceIf(&$aExpression, &$aIfValue, &$aElseValue) {
276
        // evaluate all of the tests in the conditional (we don't do short-circuit
277
        // evaluation) and replace the string test with the boolean result
278
        $rgTests = preg_split('/(\|\||\&\&)/', $aExpression, -1, PREG_SPLIT_DELIM_CAPTURE);
279
        for ($n = 0; $n < count($rgTests); $n += 2) {
280
            if (preg_match('/'.SM2_COND_TERM.'/', $rgTests[$n], $rgMatches)) {
281
                $rgTests[$n] = $this->ifTest($rgMatches[1], $rgMatches[2], $rgMatches[3]);
282
            }
283
            else {
284
                @error_logs("show_menu2 error: conditional expression is invalid!");
285
                $rgTests[$n] = false;
286
            }
287
        }
288

    
289
        // combine all test results for a final result
290
        $ok = $rgTests[0];
291
        for ($n = 1; $n+1 < count($rgTests); $n += 2) {
292
            if ($rgTests[$n] == '||') {
293
                $ok = $ok || $rgTests[$n+1];
294
            }
295
            else {
296
                $ok = $ok && $rgTests[$n+1];
297
            }
298
        }
299
        
300
        // return the formatted expression if the test succeeded
301
        return $ok ? $this->format2($aIfValue) : $this->format2($aElseValue);
302
    }
303

    
304
    // conditional test
305
    function ifTest(&$aKey, &$aOperator, &$aValue) {
306
        global $wb;
307
        
308
        // find the correct operand
309
        $operand = false;
310
        switch($aKey) {
311
        case 'class':
312
            // we need to wrap the class names in spaces so we can test for a unique
313
            // class name that will not match prefixes or suffixes. Same must be done
314
            // for the value we are testing.
315
            $operand = " $this->currClass "; 
316
            break;
317
		case 'target':
318
			$operand = $this->page['target'];
319
			break;
320
        case 'sib':
321
            $operand = $this->currSib;
322
            if ($aValue == 'sibCount') {
323
                $aValue = $this->sibCount;
324
            }
325
            break;
326
        case 'sibCount':
327
            $operand = $this->sibCount;
328
            break;
329
        case 'level':
330
            $operand = $this->page['level'];
331
            switch ($aValue) {
332
            case 'root':    $aValue = 0; break;
333
            case 'granny':  $aValue = $wb->page['level']-2; break;
334
            case 'parent':  $aValue = $wb->page['level']-1; break;
335
            case 'current': $aValue = $wb->page['level'];   break;
336
            case 'child':   $aValue = $wb->page['level']+1; break;
337
            }
338
            if ($aValue < 0) $aValue = 0;
339
            break;
340
        case 'id':
341
            $operand = $this->page['page_id'];
342
            switch ($aValue) {
343
            case 'parent':  $aValue = $wb->page['parent'];  break;
344
            case 'current': $aValue = $wb->page['page_id']; break;
345
            }
346
            break;
347
        default:
348
            return '';
349
        }
350

    
351
        // do the test        
352
        $ok = false;
353
        switch ($aOperator) { 
354
        case '<':
355
            $ok = ($operand < $aValue); 
356
            break;
357
        case '<=':
358
            $ok = ($operand <= $aValue); 
359
            break;
360
        case '=':
361
        case '==':
362
        case '!=':
363
            if ($aKey == 'class') {
364
                $ok = strstr($operand, " $aValue ") !== FALSE;
365
            }
366
            else {
367
                $ok = ($operand == $aValue); 
368
            }
369
            if ($aOperator == '!=') {
370
                $ok = !$ok;
371
            }
372
            break;
373
        case '>=':
374
            $ok = ($operand >= $aValue); 
375
        case '>':
376
            $ok = ($operand > $aValue); 
377
        }
378
        
379
        return $ok;
380
    }
381
    
382
    // finish the current menu item
383
    function finishItem() {
384
        if ($this->flags & SM2_PRETTY) {
385
            $this->output(str_repeat(' ',$this->prettyLevel).$this->itemClose);
386
        }
387
        else {
388
            $this->output($this->itemClose);
389
        }
390
    }
391

    
392
    // finish the current menu
393
    function finishList() {
394
        $this->prettyLevel -= 3;
395
        
396
        if ($this->flags & SM2_PRETTY) {
397
            $this->output("\n".str_repeat(' ',$this->prettyLevel).$this->menuClose."\n");
398
        }
399
        else {
400
            $this->output($this->menuClose);
401
        }
402
        
403
        $this->prettyLevel -= 1;
404
    }
405
    
406
    // cleanup the state of the formatter after everything has been output
407
    function finalize() {
408
        if ($this->flags & SM2_PRETTY) {
409
            $this->output("\n");
410
        }
411
    }
412

    
413
    // return the output
414
    function getOutput() {
415
        return $this->output;
416
    }
417
};
418

    
419
function error_logs($error_str)
420
{
421
                $log_error = true;
422
                if ( ! function_exists('error_log') )
423
                        $log_error = false;
424

    
425
                $log_file = @ini_get('error_log');
426
                if ( !empty($log_file) && ('syslog' != $log_file) && !@is_writable($log_file) )
427
                        $log_error = false;
428

    
429
                if ( $log_error )
430
                        @error_log($error_str, 0);
431
}
432

    
433
function show_menu2(
434
    $aMenu          = 0,
435
    $aStart         = SM2_ROOT,
436
    $aMaxLevel      = -1999, // SM2_CURR+1
437
    $aOptions       = SM2_TRIM,
438
    $aItemOpen      = false,
439
    $aItemClose     = false,
440
    $aMenuOpen      = false,
441
    $aMenuClose     = false,
442
    $aTopItemOpen   = false,
443
    $aTopMenuOpen   = false
444
    )
445
{
446
    global $wb;
447
    $database = WbDatabase::getInstance();
448
	$iQueryStart = $database->getQueryCount;
449
    // extract the flags and set $aOptions to an array
450
    $flags = 0;
451
    if (is_int($aOptions)) {
452
        $flags = $aOptions;
453
        $aOptions = array();
454
    }
455
    else if (isset($aOptions['flags'])) {
456
        $flags = $aOptions['flags'];
457
    }
458
    else {
459
        $flags = SM2_TRIM;
460
        $aOptions = array();
461
        @error_logs('show_menu2 error: $aOptions is invalid. No flags supplied!');
462
    }
463
    
464
    // ensure we have our group 1 flag, we don't check for the "exactly 1" part, but
465
    // we do ensure that they provide at least one.
466
    if (0 == ($flags & _SM2_GROUP_1)) {
467
        @error_logs('show_menu2 error: $aOptions is invalid. No flags from group 1 supplied!');
468
        $flags |= SM2_TRIM; // default to TRIM
469
    }
470
    
471
    // search page results don't have any of the page data loaded by WB, so we load it 
472
    // ourselves using the referrer ID as the current page
473
    $CURR_PAGE_ID = defined('REFERRER_ID') ? REFERRER_ID : PAGE_ID;
474
    if (count($wb->page) == 0 && defined('REFERRER_ID') && REFERRER_ID > 0) {
475
        global $database;
476
        $sql = 'SELECT * FROM `'.TABLE_PREFIX.'pages` WHERE `page_id` = '.REFERRER_ID.'';
477
        $result = $database->query($sql);
478
        if ($result->numRows() == 1) {
479
            $wb->page = $result->fetchRow();
480
        }
481
        unset($result);
482
    }
483
    
484
    // fix up the menu number to default to the menu number
485
    // of the current page if no menu has been supplied
486
    if ($aMenu == 0) {
487
        $aMenu = $wb->page['menu'] == '' ? 1 : $wb->page['menu'];
488
    } 
489

    
490
    // Set some of the $wb->page[] settings to defaults if not set
491
    $pageLevel  = $wb->page['level']  == '' ? 0 : $wb->page['level'];
492
    $pageParent = $wb->page['parent'] == '' ? 0 : $wb->page['parent'];
493
    
494
    // adjust the start level and start page ID as necessary to
495
    // handle the special values that can be passed in as $aStart
496
    $aStartLevel = 0;
497
    if ($aStart < SM2_ROOT) {   // SM2_CURR+N
498
        if ($aStart == SM2_CURR) {
499
            $aStartLevel = $pageLevel;
500
            $aStart = $pageParent;
501
        }
502
        else {
503
            $aStartLevel = $pageLevel + $aStart - SM2_CURR;
504
            $aStart = $CURR_PAGE_ID; 
505
        }
506
    }
507
    elseif ($aStart < 0) {   // SM2_ROOT+N
508
        $aStartLevel = $aStart - SM2_ROOT;
509
        $aStart = 0;
510
    }
511

    
512
    // we get the menu data once and store it in a global variable. This allows 
513
    // multiple calls to show_menu2 in a single page with only a single call to 
514
    // the database. If this variable exists, then we have already retrieved all
515
    // of the information and processed it, so we don't need to do it again.
516
    if (($flags & SM2_NOCACHE) != 0
517
        || !array_key_exists('show_menu2_data', $GLOBALS)
518
        || !array_key_exists($aMenu, $GLOBALS['show_menu2_data'])) 
519
    {
520
        global $database;
521

    
522
        // create an array of all parents of the current page. As the page_trail
523
        // doesn't include the theoretical root element 0, we add it ourselves.
524
        $rgCurrParents = explode(",", '0,'.$wb->page['page_trail']);
525
        array_pop($rgCurrParents); // remove the current page
526
        $rgParent = array();
527

    
528
        // if the caller wants all menus gathered together (e.g. for a sitemap)
529
        // then we don't limit our SQL query
530
        $menuLimitSql = ' AND `menu`='.$aMenu;
531
        if ($aMenu == SM2_ALLMENU) {
532
            $menuLimitSql = '';
533
        }
534

    
535
        // we only load the description and keywords if we have been told to,
536
        // this cuts the memory load for pages that don't use them. Note that if
537
        // we haven't been told to load these fields the *FIRST TIME* show_menu2
538
        // is called (i.e. where the database is loaded) then the info won't
539
        // exist anyhow.
540
        $fields  = '`parent`,`page_id`,`menu_title`,`page_title`,`link`,`target`,';
541
		$fields .= '`level`,`visibility`,`viewing_groups`,`viewing_users`,';
542
		$fields .= '`menu_icon_0`,`menu_icon_1`,`page_icon`,`tooltip`';
543
        if ($flags & SM2_ALLINFO) {
544
            $fields = '*';
545
        }
546

    
547
        // we request all matching rows from the database for the menu that we
548
        // are about to create it is cheaper for us to get everything we need
549
        // from the database once and create the menu from memory then make 
550
        // multiple calls to the database. 
551
        $sql  = 'SELECT '.$fields.' FROM `'.TABLE_PREFIX.'pages` ';
552
		$sql .= 'WHERE '.$wb->extra_where_sql.' '.$menuLimitSql.' ';
553
		$sql .= 'ORDER BY `level` ASC, `position` ASC';
554
        $sql = str_replace('hidden', 'IGNOREME', $sql); // we want the hidden pages
555
        $oRowset = $database->query($sql);
556
        if (is_object($oRowset) && $oRowset->numRows() > 0) {
557
            // create an in memory array of the database data based on the item's parent. 
558
            // The array stores all elements in the correct display order.
559
            while ($page = $oRowset->fetchRow()) {
560
                // ignore all pages that the current user is not permitted to view
561
                if(version_compare(WB_VERSION, '2.7', '>=')) { // WB >= 2.7
562
                    // 1. hidden pages aren't shown unless they are on the current page
563
                    if ($page['visibility'] == 'hidden') {
564
                        $page['sm2_hide'] = true;
565
                    }
566
                    
567
                    // 2. all pages with no active sections (unless it is the top page) are ignored
568
                    else if (!$wb->page_is_active($page) && $page['link'] != $wb->default_link && !INTRO_PAGE) {
569
                        continue;
570
                    }
571

    
572
                    // 3. all pages not visible to this user (unless always visible to registered users) are ignored
573
                    else if (!$wb->page_is_visible($page) && $page['visibility'] != 'registered') {
574
                        continue;
575
                    }
576
                }
577
				if(isset($page['page_icon']) && $page['page_icon'] != '') {
578
					$page['page_icon'] = WB_URL.$page['page_icon'];
579
				}
580
				if(isset($page['menu_icon_0']) && $page['menu_icon_0'] != '') {
581
					$page['menu_icon_0'] = WB_URL.$page['menu_icon_0'];
582
				}
583
				if(isset($page['menu_icon_1']) && $page['menu_icon_1'] != '') {
584
					$page['menu_icon_1'] = WB_URL.$page['menu_icon_1'];
585
				}
586

    
587
				if(!isset($page['tooltip'])) { $page['tooltip'] = $page['page_title']; }
588
                // ensure that we have an array entry in the table to add this to
589
                $idx = $page['parent'];
590
                if (!array_key_exists($idx, $rgParent)) {
591
                    $rgParent[$idx] = array();
592
                }
593

    
594
                // mark our current page as being on the current path
595
                if ($page['page_id'] == $CURR_PAGE_ID) {
596
                    $page['sm2_is_curr'] = true;
597
                    $page['sm2_on_curr_path'] = true;
598
                    if ($flags & SM2_SHOWHIDDEN) 
599
					{ 
600
                        // show hidden pages if active and SHOWHIDDEN flag supplied
601
                        unset($page['sm2_hide']); 
602
                    }
603
                }
604

    
605
                // mark parents of the current page as such
606
                if (in_array($page['page_id'], $rgCurrParents)) {
607
                    $page['sm2_is_parent'] = true;
608
                    $page['sm2_on_curr_path'] = true;
609
                    if ($flags & SM2_SHOWHIDDEN) 
610
					{
611
                        // show hidden pages if active and SHOWHIDDEN flag supplied
612
						unset($page['sm2_hide']); // don't hide a parent page                
613
                    }
614
                }
615
                
616
                // add the entry to the array                
617
                $rgParent[$idx][] = $page;
618
            }
619
        }    
620
        unset($oRowset);
621

    
622
        // mark all elements that are siblings of any element on the current path
623
        foreach ($rgCurrParents as $x) {
624
            if (array_key_exists($x, $rgParent)) {
625
                foreach (array_keys($rgParent[$x]) as $y) {
626
                    $mark =& $rgParent[$x][$y];
627
                    $mark['sm2_path_sibling'] = true;
628
                    unset($mark);
629
                }
630
            }
631
        }
632

    
633
        // mark all elements that have children and are siblings of the current page
634
        $parentId = $pageParent;
635
        foreach (array_keys($rgParent) as $x) {
636
            $childSet =& $rgParent[$x];
637
            foreach (array_keys($childSet) as $y) {
638
                $mark =& $childSet[$y];
639
                if (array_key_exists($mark['page_id'], $rgParent)) {
640
                    $mark['sm2_has_child'] = true;
641
                }
642
                if ($mark['parent'] == $parentId && $mark['page_id'] != $CURR_PAGE_ID) {
643
                    $mark['sm2_is_sibling'] = true;
644
                }
645
                unset($mark);
646
            }
647
            unset($childSet);
648
        }
649
        
650
        // mark all children of the current page. We don't do this when 
651
        // $CURR_PAGE_ID is 0, as 0 is the parent of everything. 
652
        // $CURR_PAGE_ID == 0 occurs on special pages like search results
653
        // when no referrer is available.s
654
        if ($CURR_PAGE_ID != 0) {
655
            sm2_mark_children($rgParent, $CURR_PAGE_ID, 1);
656
        }
657
        
658
        // store the complete processed menu data as a global. We don't 
659
        // need to read this from the database anymore regardless of how 
660
        // many menus are displayed on the same page.
661
        if (!array_key_exists('show_menu2_data', $GLOBALS)) {
662
            $GLOBALS['show_menu2_data'] = array();
663
        }
664
        $GLOBALS['show_menu2_data'][$aMenu] =& $rgParent;
665
        unset($rgParent);
666
    }
667

    
668
    // adjust $aMaxLevel to the level number of the final level that 
669
    // will be displayed. That is, we display all levels <= aMaxLevel.
670
    if ($aMaxLevel == SM2_ALL) {
671
        $aMaxLevel = 1000;
672
    }
673
    elseif ($aMaxLevel < 0) {   // SM2_CURR+N
674
        $aMaxLevel += $pageLevel - SM2_CURR;
675
    }
676
    elseif ($aMaxLevel >= SM2_MAX) { // SM2_MAX+N
677
        $aMaxLevel += $aStartLevel - SM2_MAX;
678
        if ($aMaxLevel > $pageLevel) {
679
            $aMaxLevel = $pageLevel;
680
        }
681
    }
682
    else {  // SM2_START+N
683
        $aMaxLevel += $aStartLevel - SM2_START;
684
    }
685

    
686
    // generate the menu
687
    $retval = false;
688
    if (array_key_exists($aStart, $GLOBALS['show_menu2_data'][$aMenu])) {
689
        $formatter = $aItemOpen;
690
        if (!is_object($aItemOpen)) {
691
            static $sm2formatter;
692
            if (!isset($sm2formatter)) {
693
                $sm2formatter = new SM2_Formatter;
694
            }
695
            $formatter = $sm2formatter;
696
            $formatter->set($flags, $aItemOpen, $aItemClose, 
697
                $aMenuOpen, $aMenuClose, $aTopItemOpen, $aTopMenuOpen);
698
        }
699
        
700
        // adjust the level until we show everything and ignore the SM2_TRIM flag.
701
        // Usually this will be less than the start level to disable it.
702
        $showAllLevel = $aStartLevel - 1;
703
        if (isset($aOptions['notrim'])) {
704
            $showAllLevel = $aStartLevel + $aOptions['notrim'];
705
        }
706
        
707
        // display the menu
708
        $formatter->initialize();
709
        sm2_recurse(
710
            $GLOBALS['show_menu2_data'][$aMenu],
711
            $aStart,    // parent id to start displaying sub-menus
712
            $aStartLevel, $showAllLevel, $aMaxLevel, $flags, 
713
            $formatter);
714
        $formatter->finalize();
715
        
716
        // if we are returning something, get the data
717
        if (($flags & SM2_BUFFER) != 0) {
718
            $retval = $formatter->getOutput();
719
        }
720
    }
721

    
722
    // clear the data if we aren't caching it
723
    if (($flags & SM2_NOCACHE) != 0) {
724
        unset($GLOBALS['show_menu2_data'][$aMenu]);
725
    }
726
	if(defined('DEBUG') && (DEBUG)) {
727
	    $iQueriesDone = $database->getQueryCount - $iQueryStart;
728
	    return $retval."\n".'<!-- Queries: '.$iQueriesDone.' -->'."\n";
729
	}
730
    return $retval;
731
}
732

    
733
function sm2_mark_children(&$rgParent, $aStart, $aChildLevel)
734
{
735
    if (array_key_exists($aStart, $rgParent)) {
736
        foreach (array_keys($rgParent[$aStart]) as $y) {
737
            $mark =& $rgParent[$aStart][$y];
738
            $mark['sm2_child_level'] = $aChildLevel;
739
            $mark['sm2_on_curr_path'] = true;
740
            sm2_mark_children($rgParent, $mark['page_id'], $aChildLevel+1);
741
        }
742
    }
743
}
744

    
745
function sm2_recurse(
746
    &$rgParent, $aStart, 
747
    $aStartLevel, $aShowAllLevel, $aMaxLevel, $aFlags, 
748
    &$aFormatter
749
    )
750
{
751
    global $wb;
752

    
753
    // on entry to this function we know that there are entries for this 
754
    // parent and all entries for that parent are being displayed. We also 
755
    // need to check if any of the children need to be displayed too.
756
    $isListOpen = false;
757
    $currentLevel = $wb->page['level'] == '' ? 0 : $wb->page['level'];
758

    
759
    // get the number of siblings skipping the hidden pages so we can pass 
760
    // this in and check if the item is first or last
761
    $sibCount = 0;
762
    foreach ($rgParent[$aStart] as $page) {
763
        if (!array_key_exists('sm2_hide', $page)) $sibCount++;
764
    }
765
    
766
    $currSib = 0;
767
    foreach ($rgParent[$aStart] as $page) {
768
        // skip all hidden pages 
769
        if (array_key_exists('sm2_hide', $page)) { // not set if false, so existence = true
770
            continue;
771
        }
772
        
773
        $currSib++;
774

    
775
        // skip any elements that are lower than the maximum level
776
        $pageLevel = $page['level'];
777
        if ($pageLevel > $aMaxLevel) {
778
            continue;
779
        }
780
        
781
        // this affects ONLY the top level
782
        if ($aStart == 0 && ($aFlags & SM2_CURRTREE)) {
783
            if (!array_key_exists('sm2_on_curr_path', $page)) { // not set if false, so existence = true
784
                continue;
785
            }
786
            $sibCount = 1;
787
        }
788
        
789
        // trim the tree as appropriate
790
        if ($aFlags & SM2_SIBLING) {
791
            // parents, and siblings and children of current only
792
            if (!array_key_exists('sm2_on_curr_path', $page)      // not set if false, so existence = true
793
                && !array_key_exists('sm2_is_sibling', $page)     // not set if false, so existence = true
794
                && !array_key_exists('sm2_child_level', $page)) { // not set if false, so existence = true
795
                continue;
796
            }
797
        }
798
        else if ($aFlags & SM2_TRIM) {
799
            // parents and siblings of parents
800
            if ($pageLevel > $aShowAllLevel  // permit all levels to be shown
801
                && !array_key_exists('sm2_on_curr_path', $page)    // not set if false, so existence = true
802
                && !array_key_exists('sm2_path_sibling', $page)) {  // not set if false, so existence = true
803
                continue;
804
            }
805
        }
806
        elseif ($aFlags & SM2_CRUMB) {
807
            // parents only
808
            if (!array_key_exists('sm2_on_curr_path', $page)    // not set if false, so existence = true
809
                || array_key_exists('sm2_child_level', $page)) {  // not set if false, so existence = true
810
                continue;
811
            }
812
        }
813

    
814
        // depth first traverse
815
        $nextParent = $page['page_id'];
816

    
817
        // display the current element if we have reached the start level
818
        if ($pageLevel >= $aStartLevel) {
819
            // massage the link into the correct form
820
            if(!INTRO_PAGE && $page['link'] == $wb->default_link) {
821
                $url = WB_URL.'/';
822
            }
823
            else {
824
                $url = $wb->page_link($page['link']);
825
            }
826
                    
827
            // we open the list only when we absolutely need to
828
            if (!$isListOpen) {
829
                $aFormatter->startList($page, $url);
830
                $isListOpen = true;
831
            }
832

    
833
            $aFormatter->startItem($page, $url, $currSib, $sibCount);
834
        }
835
        
836
        // display children as appropriate
837
        if ($pageLevel + 1 <= $aMaxLevel 
838
            && array_key_exists('sm2_has_child', $page)) {  // not set if false, so existence = true
839
            sm2_recurse(
840
                $rgParent, $nextParent, // parent id to start displaying sub-menus
841
                $aStartLevel, $aShowAllLevel, $aMaxLevel, $aFlags, 
842
                $aFormatter);
843
        }
844
        
845
        // close the current element if appropriate
846
        if ($pageLevel >= $aStartLevel) {
847
            $aFormatter->finishItem($pageLevel, $page);
848
        }
849
    }
850

    
851
    // close the list if we opened one
852
    if ($isListOpen) {
853
        $aFormatter->finishList();
854
    }
855
}
(4-4/10)