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