Project

General

Profile

1
<?php
2
/**
3
 *
4
 * @category        module
5
 * @package         show_menu2
6
 * @author          WebsiteBaker Project
7
 * @copyright       Ryan Djurovich
8
 * @copyright       WebsiteBaker Org. e.V.
9
 * @link            http://websitebaker.org/
10
 * @license         http://www.gnu.org/licenses/gpl.html
11
 * @platform        WebsiteBaker 2.8.3
12
 * @requirements    PHP 5.3.6 and higher
13
 * @version         $Id: include.php 2 2017-07-02 15:14:29Z Manuela $
14
 * @filesource      $HeadURL: svn://isteam.dynxs.de/wb/2.10.x/branches/main/modules/show_menu2/include.php $
15
 * @lastmodified    $Date: 2017-07-02 17:14:29 +0200 (Sun, 02 Jul 2017) $
16
 *
17
 */
18

    
19
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
define('SM2_SHOWHIDDEN',   0x0800); // bit 11
37
define('SM2_XHTML_STRICT', 0x1000); // bit 12
38
define('SM2_NO_TITLE',     0x2000); // bit 13
39

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

    
42

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

    
49
// This class is the default menu formatter for sm2. If desired, you can
50
// create your own formatter class and pass the object into show_menu2
51
// as $aItemFormat.
52
define('SM2_CONDITIONAL','if\s*\(([^\)]+)\)\s*{([^}]*)}\s*(?:else\s*{([^}]*)}\s*)?');
53
define('SM2_COND_TERM','\s*(\w+)\s*(<|<=|==|=|=>|>|!=)\s*([\w\-]+)\s*');
54
class SM2_Formatter
55
{
56
    public $output;
57
    public $flags;
58
    public $itemOpen;
59
    public $itemClose;
60
    public $menuOpen;
61
    public $menuClose;
62
    public $topItemOpen;
63
    public $topMenuOpen;
64

    
65
    public $isFirst;
66
    public $page;
67
    public $url;
68
    public $currSib;
69
    public $sibCount;
70
    public $currClass;
71
    public $prettyLevel;
72

    
73
    public function __construct() {
74

    
75
    }
76

    
77
    // output the data
78
    function output($aString) {
79
        if ($this->flags & SM2_BUFFER) {
80
            $this->output .= $aString;
81
        }
82
        else {
83
            echo $aString;
84
        }
85
    }
86

    
87
    // set the default values for all of our formatting items
88
    function set($aFlags, $aItemOpen, $aItemClose, $aMenuOpen, $aMenuClose, $aTopItemOpen, $aTopMenuOpen) {
89
        $this->flags        = $aFlags;
90
        $this->itemOpen     = is_string($aItemOpen)    ? $aItemOpen    : '[li][a][menu_title]</a>';
91
        $this->itemClose    = is_string($aItemClose)   ? $aItemClose   : '</li>';
92
        $this->menuOpen     = is_string($aMenuOpen)    ? $aMenuOpen    : '[ul]';
93
        $this->menuClose    = is_string($aMenuClose)   ? $aMenuClose   : '</ul>';
94
        $this->topItemOpen  = is_string($aTopItemOpen) ? $aTopItemOpen : $this->itemOpen;
95
        $this->topMenuOpen  = is_string($aTopMenuOpen) ? $aTopMenuOpen : $this->menuOpen;
96
    }
97

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

    
107
    // start a menu
108
    function startList(&$aPage, &$aUrl) {
109
        $currClass = '';
110
        $currItem = $this->menuOpen;
111

    
112
        // use the top level menu open if this is the first menu
113
        if ($this->topMenuOpen) {
114
            $currItem = $this->topMenuOpen;
115
            $currClass .= ' menu-top';
116
            $this->topMenuOpen = false;
117
        }
118

    
119
        // add the numbered menu class only if requested
120
        if (($this->flags & SM2_NUMCLASS) == SM2_NUMCLASS) {
121
            $currClass .= ' menu-'.$aPage['level'];
122
        }
123

    
124
        $this->prettyLevel += 1;
125

    
126
        // replace all keywords in the output
127
        if ($this->flags & SM2_PRETTY) {
128
            $this->output("\n".str_repeat(' ',$this->prettyLevel).
129
                $this->format($aPage, $aUrl, $currItem, $currClass));
130
        }
131
        else {
132
            $this->output($this->format($aPage, $aUrl, $currItem, $currClass));
133
        }
134

    
135
        $this->prettyLevel += 3;
136
    }
137

    
138
    // start an item within the menu
139
    function startItem(&$aPage, &$aUrl, $aCurrSib, $aSibCount) {
140
        // generate our class list
141
        $currClass = '';
142
        if (($this->flags & SM2_NUMCLASS) == SM2_NUMCLASS) {
143
            $currClass .= ' menu-'.$aPage['level'];
144
        }
145
        if (array_key_exists('sm2_has_child', $aPage) &&
146
            !array_key_exists('sm2_is_max_level', $aPage)
147
        ) {
148
        // if item has child(ren) and is not topmost level
149
            $currClass .= ' menu-expand';
150
        }
151
        if (array_key_exists('sm2_is_curr', $aPage)) {
152
            $currClass .= ' menu-current';
153
        }
154
        elseif (array_key_exists('sm2_is_parent', $aPage)) {
155
            // not set if false, so existence = true
156
            $currClass .= ' menu-parent';
157
        }
158
        elseif (array_key_exists('sm2_is_sibling', $aPage)) {
159
            // not set if false, so existence = true
160
            $currClass .= ' menu-sibling';
161
        }
162
        elseif (array_key_exists('sm2_child_level',$aPage)) {
163
            // not set if not a child
164
            $currClass .= ' menu-child';
165
            if (($this->flags & SM2_NUMCLASS) == SM2_NUMCLASS) {
166
                $currClass .= ' menu-child-'.($aPage['sm2_child_level']-1);
167
            }
168
        }
169
        if ($aCurrSib == 1) {
170
            $currClass .= ' menu-first';
171
        }
172
        if ($aCurrSib == $aSibCount) {
173
            $currClass .= ' menu-last';
174
        }
175

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

    
183
        // replace all keywords in the output
184
        if ($this->flags & SM2_PRETTY) {
185
            $this->output("\n".str_repeat(' ',$this->prettyLevel));
186
        }
187
        $this->output($this->format($aPage, $aUrl, $currItem, $currClass, $aCurrSib, $aSibCount));
188
    }
189

    
190
    // find and replace all keywords, setting the state variables first
191
    function format(&$aPage, &$aUrl, &$aCurrItem, &$aCurrClass,
192
        $aCurrSib = 0, $aSibCount = 0)
193
    {
194
        $this->page      = &$aPage;
195
        $this->url       = &$aUrl;
196
        $this->currClass = trim($aCurrClass);
197
        $this->currSib   = $aCurrSib;
198
        $this->sibCount  = $aSibCount;
199

    
200
        $item = $this->format2($aCurrItem);
201

    
202
        unset($this->page);
203
        unset($this->url);
204
        unset($this->currClass);
205

    
206
        return $item;
207
    }
208

    
209
    // find and replace all keywords
210
    function format2(&$aCurrItem) {
211
        if (!is_string($aCurrItem)) return '';
212
        return preg_replace_callback(
213
            '@\[('.
214
                'a|ac|/a|li|/li|ul|/ul|menu_title|menu_icon_0|menu_icon_1|'.
215
                'page_title|page_icon|url|target|page_id|tooltip|'.
216
                'parent|level|sib|sibCount|class|description|keywords|'.
217
                SM2_CONDITIONAL.
218
            ')\]@',
219
            array($this, 'replace'),
220
            $aCurrItem);
221
    }
222

    
223
    // replace the keywords
224
    function replace($aMatches) {
225
        $aMatch = $aMatches[1];
226
        $retval = '['.$aMatch.'=UNKNOWN]';
227
        $retval_1 = '';
228
        switch ($aMatch) {
229
        case 'a':
230
            $retval_1 = '<a href="'.$this->url.'"';
231
        case 'ac':
232
            $retval = '<a href="'.$this->url.'" class="'.$this->currClass.'"';
233
            $retval = ($retval_1 == '') ? $retval : $retval_1;
234
            if(($this->flags & SM2_XHTML_STRICT)) {
235
                $retval .= ' title="'.(($this->flags & SM2_NO_TITLE) ? '&nbsp;' : $this->page['tooltip']).'"';
236
            }
237
            else {
238
                $retval .= ' target="'.$this->page['target'].'"';
239
                $retval .= ($this->flags & SM2_NO_TITLE) ? '' : ' title="'.$this->page['tooltip'].'"';
240
            }
241
            $retval .= '>';
242
            break;
243
        case '/a':
244
            $retval = '</a>'; break;
245
        case 'li':
246
            $retval = '<li class="'.$this->currClass.'">'; break;
247
        case '/li':
248
            $retval = '</li>'; break;
249
        case 'ul':
250
            $retval = '<ul class="'.$this->currClass.'">'; break;
251
        case '/ul':
252
            $retval = '</ul>'; break;
253
        case 'url':
254
            $retval = $this->url; break;
255
        case 'sib':
256
            $retval = $this->currSib; break;
257
        case 'sibCount':
258
            $retval = $this->sibCount; break;
259
        case 'class':
260
            $retval = $this->currClass; break;
261
        default:
262
            if (array_key_exists($aMatch, $this->page)) {
263
                if ($this->flags & SM2_ESCAPE) {
264
                    $retval = htmlspecialchars($this->page[$aMatch], ENT_QUOTES);
265
                }
266
                else {
267
                    $retval = $this->page[$aMatch];
268
                }
269
            }
270
            if (preg_match('/'.SM2_CONDITIONAL.'/', $aMatch, $rgMatches)) {
271
                $retval = $this->replaceIf($rgMatches[1], $rgMatches[2], $rgMatches[3]);
272
            }
273
        }
274
        return $retval;
275
    }
276

    
277
    // conditional replacement
278
    function replaceIf(&$aExpression, &$aIfValue, &$aElseValue) {
279
        // evaluate all of the tests in the conditional (we don't do short-circuit
280
        // evaluation) and replace the string test with the boolean result
281
        $rgTests = preg_split('/(\|\||\&\&)/', $aExpression, -1, PREG_SPLIT_DELIM_CAPTURE);
282
        for ($n = 0; $n < count($rgTests); $n += 2) {
283
            if (preg_match('/'.SM2_COND_TERM.'/', $rgTests[$n], $rgMatches)) {
284
                $rgTests[$n] = $this->ifTest($rgMatches[1], $rgMatches[2], $rgMatches[3]);
285
            }
286
            else {
287
                @SM2_error_logs("show_menu2 error: conditional expression is invalid!");
288
                $rgTests[$n] = false;
289
            }
290
        }
291

    
292
        // combine all test results for a final result
293
        $ok = $rgTests[0];
294
        for ($n = 1; $n+1 < count($rgTests); $n += 2) {
295
            if ($rgTests[$n] == '||') {
296
                $ok = $ok || $rgTests[$n+1];
297
            }
298
            else {
299
                $ok = $ok && $rgTests[$n+1];
300
            }
301
        }
302

    
303
        // return the formatted expression if the test succeeded
304
        return $ok ? $this->format2($aIfValue) : $this->format2($aElseValue);
305
    }
306

    
307
    // conditional test
308
    function ifTest(&$aKey, &$aOperator, &$aValue) {
309
        global $wb;
310

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

    
354
        // do the test
355
        $ok = false;
356
        switch ($aOperator) {
357
        case '<':
358
            $ok = ($operand < $aValue);
359
            break;
360
        case '<=':
361
            $ok = ($operand <= $aValue);
362
            break;
363
        case '=':
364
        case '==':
365
        case '!=':
366
            if ($aKey == 'class') {
367
                $ok = strstr($operand, " $aValue ") !== FALSE;
368
            }
369
            else {
370
                $ok = ($operand == $aValue);
371
            }
372
            if ($aOperator == '!=') {
373
                $ok = !$ok;
374
            }
375
            break;
376
        case '>=':
377
            $ok = ($operand >= $aValue);
378
        case '>':
379
            $ok = ($operand > $aValue);
380
        }
381

    
382
        return $ok;
383
    }
384

    
385
    // finish the current menu item
386
    function finishItem() {
387
        if ($this->flags & SM2_PRETTY) {
388
            $this->output(str_repeat(' ',$this->prettyLevel).$this->itemClose);
389
        }
390
        else {
391
            $this->output($this->itemClose);
392
        }
393
    }
394

    
395
    // finish the current menu
396
    function finishList() {
397
        $this->prettyLevel -= 3;
398

    
399
        if ($this->flags & SM2_PRETTY) {
400
            $this->output("\n".str_repeat(' ',$this->prettyLevel).$this->menuClose."\n");
401
        }
402
        else {
403
            $this->output($this->menuClose);
404
        }
405

    
406
        $this->prettyLevel -= 1;
407
    }
408

    
409
    // cleanup the state of the formatter after everything has been output
410
    function finalize() {
411
        if ($this->flags & SM2_PRETTY) {
412
            $this->output("\n");
413
        }
414
    }
415

    
416
    // return the output
417
    function getOutput() {
418
        return $this->output;
419
    }
420
};
421

    
422
function sm2_error_logs($error_str)
423
{
424
                $log_error = true;
425
                if ( ! function_exists('error_log') )
426
                        $log_error = false;
427

    
428
                $log_file = @ini_get('error_log');
429
                if ( !empty($log_file) && ('syslog' != $log_file) && !@is_writable($log_file) )
430
                        $log_error = false;
431

    
432
                if ( $log_error )
433
                        @error_log($error_str, 0);
434
}
435

    
436
function show_menu2(
437
    $aMenu          = 0,
438
    $aStart         = SM2_ROOT,
439
    $aMaxLevel      = -1999, // SM2_CURR+1
440
    $aOptions       = SM2_TRIM,
441
    $aItemOpen      = false,
442
    $aItemClose     = false,
443
    $aMenuOpen      = false,
444
    $aMenuClose     = false,
445
    $aTopItemOpen   = false,
446
    $aTopMenuOpen   = false
447
    ){
448
    global $wb;
449
    // extract the flags and set $aOptions to an array
450
    $flags = SM2_TRIM;
451
    if (is_int($aOptions)) {
452
        $flags = $aOptions;
453
        $aOptions = array();
454
    } elseif (isset($aOptions['flags'])) {
455
        $flags = $aOptions['flags'];
456
    } else {
457
        $flags = SM2_TRIM;
458
        $aOptions = array();
459
        @sm2_error_logs('show_menu2 error: $aOptions is invalid. No flags supplied!');
460
    }
461
    if ($flags & 0xF == 0) { $flags |= SM2_TRIM; }
462
    // ensure we have our group 1 flag, we don't check for the "exactly 1" part, but
463
    // we do ensure that they provide at least one.
464
    if (0 == ($flags & _SM2_GROUP_1)) {
465
        @sm2_error_logs('show_menu2 error: $aOptions is invalid. No flags from group 1 supplied!');
466
        $flags |= SM2_TRIM; // default to TRIM
467
    }
468

    
469
    // search page results don't have any of the page data loaded by WB, so we load it
470
    // ourselves using the referrer ID as the current page
471
    $CURR_PAGE_ID = defined('REFERRER_ID') ? REFERRER_ID : PAGE_ID;
472
    if (count($wb->page) == 0 && defined('REFERRER_ID') && REFERRER_ID > 0) {
473
        global $database;
474
        $sql = 'SELECT * FROM `'.TABLE_PREFIX.'pages` WHERE `page_id` = '.REFERRER_ID.'';
475
        $result = $database->query($sql);
476
        if ($result->numRows() == 1) {
477
            $wb->page = $result->fetchRow();
478
        }
479
        unset($result);
480
    }
481

    
482
    // fix up the menu number to default to the menu number
483
    // of the current page if no menu has been supplied
484
    if ($aMenu == 0) {
485
        $aMenu = $wb->page['menu'] == '' ? 1 : $wb->page['menu'];
486
    }
487

    
488
    // Set some of the $wb->page[] settings to defaults if not set
489
    $pageLevel  = $wb->page['level']  == '' ? 0 : $wb->page['level'];
490
    $pageParent = $wb->page['parent'] == '' ? 0 : $wb->page['parent'];
491

    
492
    // adjust the start level and start page ID as necessary to
493
    // handle the special values that can be passed in as $aStart
494
    $aStartLevel = 0;
495
    if ($aStart < SM2_ROOT) {   // SM2_CURR+N
496
        if ($aStart == SM2_CURR) {
497
            $aStartLevel = $pageLevel;
498
            $aStart = $pageParent;
499
        }
500
        else {
501
            $aStartLevel = $pageLevel + $aStart - SM2_CURR;
502
            $aStart = $CURR_PAGE_ID;
503
        }
504
    }
505
    elseif ($aStart < 0) {   // SM2_ROOT+N
506
        $aStartLevel = $aStart - SM2_ROOT;
507
        $aStart = 0;
508
    }
509

    
510
    // adjust $aMaxLevel to the level number of the final level that
511
    // will be displayed. That is, we display all levels <= aMaxLevel.
512
    if ($aMaxLevel == SM2_ALL) {
513
        $aMaxLevel = 1000;
514
    }
515
    elseif ($aMaxLevel < 0) {   // SM2_CURR+N
516
        $aMaxLevel += $pageLevel - SM2_CURR;
517
    }
518
    elseif ($aMaxLevel >= SM2_MAX) { // SM2_MAX+N
519
        $aMaxLevel += $aStartLevel - SM2_MAX;
520
        if ($aMaxLevel > $pageLevel) {
521
            $aMaxLevel = $pageLevel;
522
        }
523
    }
524
    else {  // SM2_START+N
525
        $aMaxLevel += $aStartLevel - SM2_START;
526
    }
527

    
528
    // we get the menu data once and store it in a global variable. This allows
529
    // multiple calls to show_menu2 in a single page with only a single call to
530
    // the database. If this variable exists, then we have already retrieved all
531
    // of the information and processed it, so we don't need to do it again.
532
    if (($flags & SM2_NOCACHE) != 0
533
        || !array_key_exists('show_menu2_data', $GLOBALS)
534
        || !array_key_exists($aMenu, $GLOBALS['show_menu2_data']))
535
    {
536
        global $database;
537

    
538
        // create an array of all parents of the current page. As the page_trail
539
        // doesn't include the theoretical root element 0, we add it ourselves.
540
        $rgCurrParents = explode(",", '0,'.$wb->page['page_trail']);
541
        array_pop($rgCurrParents); // remove the current page
542
        $rgParent = array();
543

    
544
        // if the caller wants all menus gathered together (e.g. for a sitemap)
545
        // then we don't limit our SQL query
546
        $menuLimitSql = ' AND `menu`='.$aMenu;
547
        if ($aMenu == SM2_ALLMENU) {
548
            $menuLimitSql = '';
549
        }
550

    
551
        // we only load the description and keywords if we have been told to,
552
        // this cuts the memory load for pages that don't use them. Note that if
553
        // we haven't been told to load these fields the *FIRST TIME* show_menu2
554
        // is called (i.e. where the database is loaded) then the info won't
555
        // exist anyhow.
556
        $fields  = '`parent`,`page_id`,`menu_title`,`page_title`,`link`,`target`,';
557
        $fields .= '`level`,`visibility`,`viewing_groups`';
558
        if (version_compare(WB_VERSION, '2.7', '>=')) { // WB 2.7+
559
            $fields .= ',`viewing_users`';
560
        }
561
        if(version_compare(WB_VERSION, '2.8.3', '>=')) {
562
            $fields .= ',`menu_icon_0`,`menu_icon_1`,`page_icon`,`tooltip`';
563
        }
564
        if ($flags & SM2_ALLINFO) {
565
            $fields = '*';
566
        }
567

    
568
        // we request all matching rows from the database for the menu that we
569
        // are about to create it is cheaper for us to get everything we need
570
        // from the database once and create the menu from memory then make
571
        // multiple calls to the database.
572
        $sql  = 'SELECT '.$fields.' FROM `'.TABLE_PREFIX.'pages` ';
573
        $sql .= 'WHERE '.$wb->extra_where_sql.' '.$menuLimitSql.' ';
574
        $sql .= 'ORDER BY `level` ASC, `position` ASC';
575
        $sql = str_replace('hidden', 'IGNOREME', $sql); // we want the hidden pages
576
        $oRowset = $database->query($sql);
577
        if (is_object($oRowset) && $oRowset->numRows() > 0) {
578
            // create an in memory array of the database data based on the item's parent.
579
            // The array stores all elements in the correct display order.
580
            while ($page = $oRowset->fetchRow(MYSQLI_ASSOC)) {
581
                // ignore all pages that the current user is not permitted to view
582
                if(version_compare(WB_VERSION, '2.7', '>=')) { // WB >= 2.7
583
                    // 1. hidden pages aren't shown unless they are on the current page
584
                    if ($page['visibility'] == 'hidden') {
585
                        if (($flags & SM2_SHOWHIDDEN)==false) {
586
                            // show hidden pages if SHOWHIDDEN flag supplied
587
                            $page['sm2_hide'] = true;
588
                        }
589
                    }
590

    
591
                    // 2. all pages with no active sections (unless it is the top page) are ignored
592
                    else if (!$wb->page_is_active($page) && $page['link'] != $wb->default_link && !INTRO_PAGE) {
593
                        continue;
594
                    }
595

    
596
                    // 3. all pages not visible to this user (unless always visible to registered users) are ignored
597
                    else if (!$wb->page_is_visible($page) && $page['visibility'] != 'registered') {
598
                        continue;
599
                    }
600
                }
601
                if(isset($page['page_icon']) && $page['page_icon'] != '') {
602
                    $page['page_icon'] = WB_URL.$page['page_icon'];
603
                }
604
                if(isset($page['menu_icon_0']) && $page['menu_icon_0'] != '') {
605
                    $page['menu_icon_0'] = WB_URL.$page['menu_icon_0'];
606
                }
607
                if(isset($page['menu_icon_1']) && $page['menu_icon_1'] != '') {
608
                    $page['menu_icon_1'] = WB_URL.$page['menu_icon_1'];
609
                }
610

    
611
                if(!isset($page['tooltip'])) { $page['tooltip'] = $page['page_title']; }
612
                // ensure that we have an array entry in the table to add this to
613
                $idx = $page['parent'];
614
                if (!array_key_exists($idx, $rgParent)) {
615
                    $rgParent[$idx] = array();
616
                }
617

    
618
                // mark our current page as being on the current path
619
                if ($page['page_id'] == $CURR_PAGE_ID) {
620
                    $page['sm2_is_curr'] = true;
621
                    $page['sm2_on_curr_path'] = true;
622
                    if ($flags & SM2_SHOWHIDDEN)
623
                    {
624
                        // show hidden pages if active and SHOWHIDDEN flag supplied
625
                        unset($page['sm2_hide']);
626
                    }
627
                }
628
                // mark our current page as being on the maximum level to show
629
                if ($page['level'] == $aMaxLevel) {
630
                    $page['sm2_is_max_level'] = true;
631
                }
632

    
633
                // mark parents of the current page as such
634
                if (in_array($page['page_id'], $rgCurrParents)) {
635
                    $page['sm2_is_parent'] = true;
636
                    $page['sm2_on_curr_path'] = true;
637
                    if ($flags & SM2_SHOWHIDDEN)
638
                    {
639
                        // show hidden pages if active and SHOWHIDDEN flag supplied
640
                        unset($page['sm2_hide']); // don't hide a parent page
641
                    }
642
                }
643

    
644
                // add the entry to the array
645
                $rgParent[$idx][] = $page;
646
            }
647
        }
648
        unset($oRowset);
649

    
650
        // mark all elements that are siblings of any element on the current path
651
        foreach ($rgCurrParents as $x) {
652
            if (array_key_exists($x, $rgParent)) {
653
                foreach (array_keys($rgParent[$x]) as $y) {
654
                    $mark =& $rgParent[$x][$y];
655
                    $mark['sm2_path_sibling'] = true;
656
                    unset($mark);
657
                }
658
            }
659
        }
660

    
661
        // mark all elements that have children and are siblings of the current page
662
        $parentId = $pageParent;
663
        foreach (array_keys($rgParent) as $x) {
664
            $childSet =& $rgParent[$x];
665
            foreach (array_keys($childSet) as $y) {
666
                $mark =& $childSet[$y];
667
                if (array_key_exists($mark['page_id'], $rgParent)) {
668
                    $mark['sm2_has_child'] = true;
669
                }
670
                if ($mark['parent'] == $parentId && $mark['page_id'] != $CURR_PAGE_ID) {
671
                    $mark['sm2_is_sibling'] = true;
672
                }
673
                unset($mark);
674
            }
675
            unset($childSet);
676
        }
677

    
678
        // mark all children of the current page. We don't do this when
679
        // $CURR_PAGE_ID is 0, as 0 is the parent of everything.
680
        // $CURR_PAGE_ID == 0 occurs on special pages like search results
681
        // when no referrer is available.s
682
        if ($CURR_PAGE_ID != 0) {
683
            sm2_mark_children($rgParent, $CURR_PAGE_ID, 1);
684
        }
685

    
686
        // store the complete processed menu data as a global. We don't
687
        // need to read this from the database anymore regardless of how
688
        // many menus are displayed on the same page.
689
        if (!array_key_exists('show_menu2_data', $GLOBALS)) {
690
            $GLOBALS['show_menu2_data'] = array();
691
        }
692
        $GLOBALS['show_menu2_data'][$aMenu] =& $rgParent;
693
        unset($rgParent);
694
    }
695
/*
696
    // adjust $aMaxLevel to the level number of the final level that
697
    // will be displayed. That is, we display all levels <= aMaxLevel.
698
    if ($aMaxLevel == SM2_ALL) {
699
        $aMaxLevel = 1000;
700
    }
701
    elseif ($aMaxLevel < 0) {   // SM2_CURR+N
702
        $aMaxLevel += $pageLevel - SM2_CURR;
703
    }
704
    elseif ($aMaxLevel >= SM2_MAX) { // SM2_MAX+N
705
        $aMaxLevel += $aStartLevel - SM2_MAX;
706
        if ($aMaxLevel > $pageLevel) {
707
            $aMaxLevel = $pageLevel;
708
        }
709
    }
710
    else {  // SM2_START+N
711
        $aMaxLevel += $aStartLevel - SM2_START;
712
    }
713
*/
714
    // generate the menu
715
    $retval = false;
716
    if (array_key_exists($aStart, $GLOBALS['show_menu2_data'][$aMenu])) {
717
        $formatter = $aItemOpen;
718
        if (!is_object($aItemOpen)) {
719
            static $sm2formatter;
720
            if (!isset($sm2formatter)) {
721
                $sm2formatter = new SM2_Formatter();
722
            }
723
            $formatter = $sm2formatter;
724
            $formatter->set($flags, $aItemOpen, $aItemClose,
725
                $aMenuOpen, $aMenuClose, $aTopItemOpen, $aTopMenuOpen);
726
        }
727

    
728
        // adjust the level until we show everything and ignore the SM2_TRIM flag.
729
        // Usually this will be less than the start level to disable it.
730
        $showAllLevel = $aStartLevel - 1;
731
        if (isset($aOptions['notrim'])) {
732
            $showAllLevel = $aStartLevel + $aOptions['notrim'];
733
        }
734

    
735
        // display the menu
736
        $formatter->initialize();
737
        sm2_recurse(
738
            $GLOBALS['show_menu2_data'][$aMenu],
739
            $aStart,    // parent id to start displaying sub-menus
740
            $aStartLevel, $showAllLevel, $aMaxLevel, $flags,
741
            $formatter);
742
        $formatter->finalize();
743

    
744

    
745
        // if we are returning something, get the data
746
        if (($flags & SM2_BUFFER) != 0) {
747
            $retval = $formatter->getOutput();
748
        }
749
    }
750

    
751
    // clear the data if we aren't caching it
752
    if (($flags & SM2_NOCACHE) != 0) {
753
        unset($GLOBALS['show_menu2_data'][$aMenu]);
754
    }
755

    
756
    return $retval;
757
}
758

    
759
function sm2_mark_children(&$rgParent, $aStart, $aChildLevel)
760
{
761
    if (array_key_exists($aStart, $rgParent)) {
762
        foreach (array_keys($rgParent[$aStart]) as $y) {
763
            $mark =& $rgParent[$aStart][$y];
764
            $mark['sm2_child_level'] = $aChildLevel;
765
            $mark['sm2_on_curr_path'] = true;
766
            sm2_mark_children($rgParent, $mark['page_id'], $aChildLevel+1);
767
        }
768
    }
769
}
770

    
771
function sm2_recurse(
772
    &$rgParent, $aStart,
773
    $aStartLevel, $aShowAllLevel, $aMaxLevel, $aFlags,
774
    &$aFormatter
775
    )
776
{
777
    global $wb;
778

    
779
    // on entry to this function we know that there are entries for this
780
    // parent and all entries for that parent are being displayed. We also
781
    // need to check if any of the children need to be displayed too.
782
    $isListOpen = false;
783
    $currentLevel = $wb->page['level'] == '' ? 0 : $wb->page['level'];
784

    
785
    // get the number of siblings skipping the hidden pages so we can pass
786
    // this in and check if the item is first or last
787
    $sibCount = 0;
788
    foreach ($rgParent[$aStart] as $page) {
789
        if (!array_key_exists('sm2_hide', $page)) $sibCount++;
790
    }
791

    
792
    $currSib = 0;
793
    foreach ($rgParent[$aStart] as $mKey => $page) {
794
        // skip all hidden pages
795
        if (array_key_exists('sm2_hide', $page)) { // not set if false, so existence = true
796
            continue;
797
        }
798

    
799
        $currSib++;
800

    
801
        // skip any elements that are lower than the maximum level
802
        $pageLevel = $page['level'];
803

    
804
        if ($pageLevel > $aMaxLevel) {
805
            continue;
806
        }
807

    
808
        // this affects ONLY the top level
809
        if ($aStart == 0 && ($aFlags & SM2_CURRTREE)) {
810
            if (!array_key_exists('sm2_on_curr_path', $page)) { // not set if false, so existence = true
811
                continue;
812
            }
813
            $sibCount = 1;
814
        }
815

    
816
        // trim the tree as appropriate
817
        if ($aFlags & SM2_SIBLING) {
818
            // parents, and siblings and children of current only
819
            if (!array_key_exists('sm2_on_curr_path', $page)      // not set if false, so existence = true
820
                && !array_key_exists('sm2_is_sibling', $page)     // not set if false, so existence = true
821
                && !array_key_exists('sm2_child_level', $page)) { // not set if false, so existence = true
822
                continue;
823
            }
824
        }
825
        else if ($aFlags & SM2_TRIM) {
826
            // parents and siblings of parents
827
            if ($pageLevel > $aShowAllLevel  // permit all levels to be shown
828
                && !array_key_exists('sm2_on_curr_path', $page)    // not set if false, so existence = true
829
                && !array_key_exists('sm2_path_sibling', $page)) {  // not set if false, so existence = true
830
                continue;
831
            }
832
        }
833
        elseif ($aFlags & SM2_CRUMB) {
834
            // parents only
835
            if (!array_key_exists('sm2_on_curr_path', $page)    // not set if false, so existence = true
836
                || array_key_exists('sm2_child_level', $page)) {  // not set if false, so existence = true
837
                continue;
838
            }
839
        }
840

    
841
        // depth first traverse
842
        $nextParent = $page['page_id'];
843

    
844
        // display the current element if we have reached the start level
845
        if ($pageLevel >= $aStartLevel) {
846
            // massage the link into the correct form
847
            if(!INTRO_PAGE && $page['link'] == $wb->default_link) {
848
                $url = WB_URL;
849
            }
850
            else {
851
                $url = $wb->page_link($page['link']);
852
            }
853

    
854
            // we open the list only when we absolutely need to
855
            if (!$isListOpen) {
856
                $aFormatter->startList($page, $url);
857
                $isListOpen = true;
858
            }
859

    
860
            $aFormatter->startItem($page, $url, $currSib, $sibCount);
861
        }
862

    
863
        // display children as appropriate
864
        if ($pageLevel + 1 <= $aMaxLevel
865
            && array_key_exists('sm2_has_child', $page)) {  // not set if false, so existence = true
866
            sm2_recurse(
867
                $rgParent, $nextParent, // parent id to start displaying sub-menus
868
                $aStartLevel, $aShowAllLevel, $aMaxLevel, $aFlags,
869
                $aFormatter);
870
        }
871

    
872
        // close the current element if appropriate
873
        if ($pageLevel >= $aStartLevel) {
874
            $aFormatter->finishItem($pageLevel, $page);
875
        }
876
    }
877

    
878
    // close the list if we opened one
879
    if ($isListOpen) {
880
        $aFormatter->finishList();
881
    }
882
}
883

    
(3-3/8)