<?php if (!defined('PmWiki')) exit() ; /****************************************************************** parseMenu.php version 2022-12-29 A Wiki-Page Menu Building Recipe for PmWiki skins Written for PmWiki in 2022 by Kirk Siqveland - www.SnippetsPub.com Copyright 2022 Kirk Siqveland - Edmonds, WA USA Kirk@SnippetsPub.com www.SnippetsPub.com This recipe (software) is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. *******************************************************************/ ## FEATURES: # - Uses editable Wiki Pages to build Navigation Menus # - Self-Compiles menu html code from wiki pages # -- Pages can be decorated without changing the Menus # -- allowing the page itself to be used for navigation. # - Hides secondary level Menus # - Navigation Wiki Pages May be used for Responsive design # - - when building the menus Markup ignores non-menu text. # - One page Works for both Horizontal and Vertical Menus. # - - layout is all in the CSS (see included code) # - Allows Splicing of Site and Group Navigation Pages ## This file requires CSS files to establish function ## and to provide the decorative style to match your skin ## The Responsive parseMenu project requires three files: ## # parseMenu.php (this file) ## # navbar_function.css ## # navbar_style.css.php (CSS using PHP for Variables) ## ## For instructions see the included HowTo.txt or the ## www.pmwiki.org/wiki/Cookbook/ResponsiveMenus page ## ## This recipe allows a PmWiki Skin to easily be modified to use ## Menus built from particular wiki pages i.e. {Group}.{Page} ## ## Like other functions in PmWiki, you may specify a Group Specific ## Menu, or if that page does not exist, a default Site-Wide Menu ## will be used. /****************************************************************** H O W T O : Modify your Skin template, inserting a new div where you want the menu, setting - class="NavMenu" - then add Code inside the <div> to use this MarkUp including at least one wiki-page address, the second is used in case the first wiki-page doesn't exist. * Example CODE: <div class="NavMenu"> <!--markup:(:parseMenu $Group.NavBar $SiteGroup.NavBar :)--> </div> *****************************************************************/ function parseMenu($args){ global $SiteGroup ; $fluff = true ; ## Example Navigation page entry: // * [[$SiteGroup/NavBar?GroupNav={$Group}/GroupNav | Nav Menu ]] #- The Asterix flags it as a visible menu item w/ level one #- Normal pmwiki markup makes it a link named "Nav Menu". #- the link passes the $_GET var "GroupNav" with a second #- page address to be appended to the first when building #- this menu. Here the "{$Group}" Markup-Variable looks for #- the "GroupNav" page in whatever Group you are currently in. #- If we can't find that, we use /$SiteGroup/GroupNav instead. // $uri = explode("/",$_SERVER['REQUEST_URI']) ; // $Group = $uri[1] ; // $pagename=$GLOBALS['MarkupToHTML']['pagename']; // $group = PageVar($pagename, '$Group'); ## Split the provided args so we can extract each/any $ArgsArr = explode(" ", $args[1]) ; ## Sanitize: $WikiPage = strtok($ArgsArr[0], "?") ; if(!PageExists($WikiPage)){ //look for Group Page $WikiPage = strtok($ArgsArr[1], "?") ; // Sanitize } $arrayPage = ReadPage($WikiPage) ; ## Allow bypass of Menu Builder using Markup -- (:Splash ... :) ## to display an image instead of a menu $Splash = substr(trim($arrayPage['text']), 0,8) ; if( $Splash === '(:Splash' || $Splash === '(:splash'){ $trimString = substr(trim($arrayPage['text']), 9) ; return rtrim( $trimString, ":)") ; }else{ ## Otherwise we break down the page line by line and look for menu items. ## Split the line into an array using New-Line as the delimiter $pullText = str_replace("\r", "", $arrayPage['text']) ;//drop Chr(13) $LineItems = explode("\n", $pullText) ; ## Mark the end position with a dummy marker: array_push($LineItems,"@ EOA"); ## Start Building our Menu structure: $FullBar = "<ul class='menu'><li>" ; ## Now we handle each array item to find its Menu Level: foreach($LineItems as $LI){ ## Allow Decoration Text etc. before and after our Menu if(stripos($LI, ":Menu:")!== false) {$fluff = false ; } if($fluff) continue ; if(stripos($LI, ":MenuEnd:")!== false) break ; ## Skip Comment or Markup Lines while building the Menu $firstChar = substr($LI, 1,1); if ( $firstChar === '!'|| $firstChar === '('|| $firstChar === '%'){ continue; } ## Skip Markup line but with special handling if ( $firstChar === ':'){ if(strpos($LI, ':include') !== false){ if(strpos($LI, '{$Group}') !== false){ preg_match('/\\(:include (.*?):\\)/', $LI, $matches); $testDir = str_replace("{\$Group}", "$Group", $matches[1]) ; if(!PageExists($testDir)){ $Group = "$SiteGroup" ; } } } continue; } ## Peel off the List Level Markup e.g. "** " for second level List Items ## This requires the use of a space between the Asterisks and the menu Item. $exLI = explode(" ",trim($LI)) ; if(count($exLI) > 1){ $liLevel = $exLI[0] ; ## Now remove the Markup and put the rest back together to get the Menu Item $liTag = implode(" ", array_slice($exLI,1)) ; ## insert a horizontal rule if indicated with "----" if(trim($liTag)==="----"){ $liTag = '<hr class="hrli"/>' ; } if(trim($liTag)==="____"){ $liTag = '<div class="spacer"></div>' ; } if(trim($liTag)==="(:hr:)"){ $liTag = '<hr class="hrli"/>' ; } if(trim($liTag)==="(:spacer:)"){ $liTag = '<div class="spacer"></div>' ; } ##--------------------------------------------------------------- ## Replace the Variable '{$Group}' with the actual Group Name. ## If that Page Doesn't exist, replace '{$Group}' with "Site" if(strpos($LI, '{$Group}') !== false){ preg_match('/{\$Group}(.*?) /', $LI, $matches); if(!PageExists("$Group$matches[1]")){ $Group = "Site" ; } $liTag = str_replace("{\$Group}", "$Group", $liTag) ; } ##------------------------------------------------------------- ## Define our DropLevel, 0 = top level Menu item $DropLI = 0 ; ## Don't process until we have read in the first and second items to compare. if(isset($prevLevel)){ ## calculate the Menu level #- For Simplicity This currently only works with One Drop-Level ## Start by identifying the level * = 1, ** = 2 $urCT = strlen(trim($prevLevel)) ; $liCT = strlen(trim($liLevel)) ; ## Parse the line and build the Menu String: ## we are actually working with the previous list item if( $liTag === "EOA" ){ while($DropLI > 0){ $FullBar .= "</ul></li>" ; } $FullBar .= $urTag . "</li></ul>" ; }elseif( $liCT == $urCT ){ $FullBar .= $urTag . "</li><li>" ; }elseif( $liCT > $urCT ){ $FullBar .= $urTag . "<ul class='Dropli'><li>" ; $DropLI++ ; }elseif( $liCT < $urCT ){ $FullBar .= $urTag . "</li></ul>" ; while($DropLI > 0){ $FullBar .= "</li></ul>" ; } $FullBar .= "<li>" ; } } ## Roll current item into the "Previous" position $urTag = $liTag ; ## Roll current drop-level to the "Previous" level $prevLevel = $liLevel ; } } // for each return $FullBar; } } // MakeFlatMenu() ## the Markup itself: Markup( 'parseMenu', 'inline', '/\\(:parseMenu(?s).(.*?):\\)/', "parseMenu" ); ## This hides the "Splash" markup which bypasses the Parse function. ## For example to insert a picture instead of a Menu for certain groups: ## the wiki page would use the Splash markup to bypass the Menu builder. /* (:Splash https://www.mydomain.org/pmwiki/media/welcome_image.png :) */ Markup( 'SplashMenu', 'inline', '/\\(:Splash (.*?):\\)/', "$1" ); ## Menu Horizontal-Rule Markup: Markup( 'menuHR', 'inline', '/\*(.*?)\\(:hr:\\)/', "<hr class='hrli'/>" ); ## Menu Empty-line or "Spacer" Markup: Markup( 'menuSpacer', 'inline', '/\*(.*?)\\(:spacer:\\)/', "" ); ## By Using MenuStart and MenuEnd Markup ## We can have decorated pages which include the actual menu as well Markup( 'menuStart', 'style', '/\\(:Menu:\\)/', "<div class='menublock'>" ); Markup( 'menuEnd', 'style', '/\\(:MenuEnd:\\)/', "</div>" ); ## The SpliceNav Markup allows the Splicing of Menu Pages so ## you can have a Site Navigation Menu page and also have ## {Group} Navigation Pages, which can be combined. ## Using the (:SpliceNav:) Markup you can add/splice the ## {Group} Navigation page to the bottom of the ## Site Navigation page ( for Responsive Themes) ## NB!: This requires passing the spliced page link as part of the URL ## using a $_GET var ## e.g.: ?GroupNav=Group/Page with the link address. ## For example I have a {Group}/MiniNav page with one Link - /* // * [[Site/NavBar?GroupNav={$Group}/GroupNav | Nav Menu ]] ## This splices the current {Group}/GroupNav page onto Site/NavBar page function SpliceNav(){ if(isset($_GET['GroupNav'])){ if(PageExists($_GET['GroupNav'])){ return "(:include ".$_GET['GroupNav']." :)" ; }else{ $outLink = str_replace($Group, "Site", $_GET['GroupNav']) ; if(PageExists($outLink)){ return "(:include $outLink:)" ; } } } } Markup( 'SpliceNav', 'fulltext', '/\\(:SpliceNav:\\)/', "SpliceNav" ); ## And finally menu-page markup to insert a menu horizontal rule: ## (applied late to only change list items) Markup('lihrrule','style','/----/','<hr class="lihr" />'); /* CSS Added to your skin, or to your pub/css/local.css .hrli, .lihr { margin-left: 0px ; width: 80% ; max-width: 180px ; border: 0 ; height: 0 ; border-bottom: 1px solid ; } .hrli { margin-left: 40px ;} /*