Knightnet Site Design - Automatic menu generator class

You are in section: Home > Site Design > Template Files

This is the automatic menu class.

It produces a menu for the left hand side of the page based on the structure of the files and folders relative to the current page. It also produces the "breadcrumb" list at the top of the page, a fully automatic site map and is able to retrieve "meta" information from pages giving information such as titles, last updated dates and so on.

<?php // A class for producing an automatic navigation menu # Uses the OS folder/file structure to produce the menu # ----------------------------------------------------------------- # Useage: # require_once('automenu.class.inc'); # $AutoMenu=new AutoMenu(); # # Set any required vars, e.g. # $AutoMenu->setVar('folderStart','/myDir/'); # # OR: # $AutoMenu->setVar('folderStart',$AutoMenu->currPath()); # # & produce output # $content=$AutoMenu->output(); # # Can also create vars containing details on the current scripts path: # $content.=$AutoMenu->currPath(); # ----------------------------------------------------------------- # Author and copyright: J.Knight. All rights reserved. # Contact: http://www.knightnet.org.uk/contacting.htm # Version: 2.0 - 4/Aug/03 - Rewrite as CLASS # ----------------------------------------------------------------- class AutoMenu { // Class Variables ## see initvars() // Class constructor function AutoMenu() { # Init default variables: $this->initVars(); } // Generic constructor for class variables (use to set variables in object instance) # Easily extended to do validation, etc. function setVar($var,$val) { $this->$var=$val; if ($var=='folderStart') $this->folderStart=$this->chkFolder(); } // Generic output for class variables function getVar($var) { return $this->$var; } // Initialise 1 or all class variables # ** NEEDS enhancing to allow individual vars to be reset to defaults ** function initVars($var='') { # Start folder (where to start the menu from) $this->folderStart='/'; # Additional folders [directories] (manually add to include in list) $this->folderExtra=''; # Folder types/names to ignore (use regex expressions) # These will never show up in the nav menus nor in the site map $this->folderIgnore=Array('/^\..*/', # Folder names begining with . '/^_.*/', # Folder names begining with _ '/^cgi-bin$/i', # /cgi-bin/ folder '/^common$/i', # /common/ folder '/^images$/i', # /images/folder '/^worship$/i', # /worship/ folder (placeholder for sub-domain) '/Music$/', # /Worship Resources/Music ); # Additional pages [files] (manually add to include in list) $this->pageExtra=''; # File types to include (regex, add "|ext" to add another extension) # NB: Also used to remove ext for display $this->pageTypes='/(.*)\.(php|php3|php4|htm|html|shtm|shtml)$/'; # File types/names to ignore (use regex expressions) $this->pageIgnore=Array('/^\..*/', # File names begining with . '/^_.*/', # File names begining with _ '/^index.*/i', # index files (as they are covered by folders) '/^robots.txt$/i', ); # Check var so fns can know that lists have been created $this->_doneLists=FALSE; # Final list of folders $this->folderList=Array(); # Final list of pages $this->pageList=Array(); # Var controlling whether location line is shown at start $this->showLocation=FALSE; # Separator for breadcrumb trail $this->tSep = ' > '; # Location of Table of Contents page (set to '' to not show) $this->toc='/Site_Index.htm'; # --- Set by currPath() dont change externally! --- $this->currPath=''; # The path of the current script $this->currPathArray=Array(); # Array of current path elements } // Check that a folder name is valid and correct missing '/'s # Defaults to $this->folderStart # Returns corrected folder name or dies in the process function chkFolder($fName='') { if ($fName=='') $fName=$this->folderStart; # Check folder name does not contain 2 or more . (folder traversal hack) if (preg_match('/.*(\.{2,}).*/',$fName)) die("<p><b>AutoMenu.class ERROR</b><br>\nFolder: '$fName'<br>\nFolder name may NOT contain .., ..., etc. to prevent folder traversal hacks (chkFolder)</p>"); # replace all \ with / (works w/ both win an unix) $fName=strtr($fName,'\\','/'); # Ensure it starts and ends with a / if (substr($fName,0,1)!='/') $fName='/'.$fName; if (substr($fName,-1)!='/') $fName.='/'; # Check if it exists if (!is_dir($_SERVER['DOCUMENT_ROOT'].$fName)) die("<p><b>AutoMenu Class ERROR:</b><br />\nFolder $fName does not exist (chkFolder)</p>\n"); return $fName; } // Folder operation funcs, allows easy folder manipulation from calling func if reqd # Open a folder (defaults to the start folder but can be overriden) # ONLY ALLOWS FOLDERS IN THE WEB DOCUMENT ROOT PATH TO BE OPENED # Opens a dir & returns a handle ($this->handle) and corrected folder name ($fName) function folderOpen($fName='') { if ($fName=='') $fName=$this->folderStart; # Check folder is valid (either dies or returns corrected folder name) $fName=$this->chkFolder($fName); $this->folderClose(); $this->handle=opendir($_SERVER['DOCUMENT_ROOT'].$fName); return $fName; } # Close the currently open folder function folderClose() { @closedir($this->handle); } # Rewind the currently open folder function folderRewind() { if ($this->handle!='') rewinddir($this->handle); } // Create list of folders and pages # Names of vars to write to can be passed as parms function createLists($fName='',$fListName='folderList',$pListName='pageList') { # Default to $this->folderStart if ($fName=='') $fName=$this->folderStart; # Check that folder has been opened $fName=$this->folderOpen($fName); while ($dir=readdir($this->handle)) { # block items in folderIgnore if (preg_replace($this->folderIgnore,'',$dir)!='') { # Save folder list if (is_dir($_SERVER['DOCUMENT_ROOT'].$fName.$dir)) $this->{$fListName}[]=$dir; } # block items in pageIgnore if (preg_replace($this->pageIgnore,'',$dir)!='') { # Save page list (only for valid files with valid types if (is_file($_SERVER['DOCUMENT_ROOT'].$fName.$dir) AND preg_match($this->pageTypes,$dir)) $this->{$pListName}[]=$dir; } } # Sort the lists @natcasesort($this->$fListName);@natcasesort($this->$pListName); # Close the directory $this->folderClose(); # Set check var so other fns can know that this has been called ## Sorry, no can do as this fn is now generic # return corrected folder name return $fName; } // Add a slash to every element of a path # WARNING: ASSUMES NO TRAILING SLASH function pathAddSlash($path) { if (is_array($path)) { $i=-1; foreach ($path as $j) { ++$i; $path[$i].='/'; } } else $path.='/'; return $path; } // Replace _ with " " in a string & make proper case # Use for showing folder/page names on screen function UsToSp($name) { return ucwords(str_replace("_"," ",$name)); } // Stores the folder path of the current script # Can be used to generate a breadcrumb trail # Main use is to feed current calling scripts path to this class # (see useage example @ top of file) function currPath() { # Script path/name $path=explode("/",$_SERVER['PHP_SELF']); # drop the name $pL=array_pop($path); # Store the whole path $this->currPath=join('/',$path); # For each element, add a / to the end for consistancy (e.g. root has just a /) $path=$this->pathAddSlash($path); # Store the path elements in an array $this->currPathArray=$path; # Return the current scripts path return $this->currPath; } // Get file details (Last update, title) # $file is relative to server root # $qDir=TRUE if "file" is really a folder (looks for index page) # $qRetTitle=TRUE to return the title of each detail prepended to the data # Returns an array function getFileDetails($file,$qDir=FALSE,$qRetTitle=FALSE) { # Array of meta data to pull from file (does not include update date) # NB: Make sure case is correct $fields=Array( 'title', 'metaDescription', ); $fldsCount=count($fields); # field titles required? if ($qRetTitle) { $updTitle='LAST UPDATE: '; $fldTitles=Array('TITLE: ','DESCRIPTION: '); } else { $updTitle=''; $fldTitles=Array('',''); } # Real file name $fname=$_SERVER['DOCUMENT_ROOT'].$file; # If a folder, find the matching index page (only looks for index.htm & index.php) ### could do this better with apache_lookup_uri w/ just folder name ??? if ($qDir) { # ensure last char is / if (substr($fname,-1)!='/') $fname.='/'; if (file_exists($fname.'index.htm')) $fname=$fname.'index.htm'; else if (file_exists($fname.'index.php')) $fname=$fname.'index.php'; # if not found, return last mod date of folder else return array($updTitle.date('d/M/Y H:i',filemtime($fname)),''); } # Blank return array $ret=array(); # Get last update date $ret[]=$updTitle.date('d/M/Y H:i',filemtime($fname)); # Read in the whole file! $f=file_get_contents($fname); # Find each required meta field for ($j = 0; $j <= $fldsCount-1; $j++) { # Search for where the title var is set & retrieve the title $res=@preg_match('/^\s*\$\w+->'.$fields[$j].'\s*=\s*(\'|")(.+)(\'|");\s*$/m',$f,$matches); if ($res!=1) $res=@preg_match('/^\s*\$\w+->setVar\s*\(\''.$fields[$j].'\'\s*\,\s*(\'|")(.+)(\'|")\);\s*$/m',$f,$matches); if ($res==1) if ($matches[2]!='') $ret[]=$fldTitles[$j].$matches[2]; } # return $ret; } // Output automenu list function outputMenu() { $r=''; # Check if lists are populated, if not call createLists to do so ## Oops, no can do if createLists is for generic use $this->createLists(); # Optionally display start location if ($this->showLocation) $r.="<div><b>Location:</b> <a href='$this->folderStart'>$this->folderStart</a></div>\n"; # Display sections (folders/directories) if (count($this->folderList)>0) { $r.="<h5>Sections:</h5>\n"; foreach ($this->folderList as $dir) { $d=implode(', ',$this->getFileDetails($this->folderStart.$dir,TRUE)); # NB: No br as class is defined as block display $r.="<a href='$this->folderStart$dir' title='$d'>"; $r.=$this->UsToSp($dir)."</a>\n"; } $r.="</p>\n"; } # Display pages if (count($this->pageList)>0) { $r.="<h5>Pages:</h5>\n"; foreach ($this->pageList as $file) { $d=implode(', ',$this->getFileDetails($this->folderStart.$file)); $r.="<a href='$this->folderStart$file' title='$d'>"; $r.=$this->fmtPName($file); $r.="</a>\n"; } $r.="</p>\n"; } # return $r; } // Remove page extension & _ & change to proper case for display function fmtPName($pname) { return $this->UsToSp(preg_replace($this->pageTypes,'$1',$pname)); } // Create a breadcrumb trail from root to $this->folderStart function outputBreadcrumb() { $fname=$this->chkFolder($this->folderStart); $path=explode('/',$fname); # folderStart always has trailing slash so drop last element $pL=array_pop($path); # For each element, add a / to the end for consistancy (e.g. root has just a /) $path=$this->pathAddSlash($path); # Start the output $r='<div style="float:left" title="Click on underlined entries to go to that section of the site">'; $r.='You are in section: '; # How many entries $numElem=count($path); # 1st element is always web root $d=implode(', ',$this->getFileDetails($path[0],TRUE)); $r.="<a href='$path[0]' title='$d'>Home</a>"; # Remaining elements $tPath=$path[0]; for ($i=1;$i<=count($path)-1;$i++) { $tPath.=$path[$i]; $d=implode(', ',$this->getFileDetails($tPath,TRUE)); $r.="$this->tSep<a href='$tPath' title='$d'>".$this->UsToSp(rtrim($path[$i],'/')).'</a>'; } # Add Table of contents if ($this->toc!='') $r.='</div><div style="text-align:right"><a href="'; $r.=$this->toc; $r.='" title="Table of Contents">Site Map</a></div>'; # return $r; } // Create site map # $fName=Folder name # $level=The number of the current level (used for recursion only) # $qShowP=TRUE Show pages as well as folders # $qShowD=TRUE Show folder/pages meta data (has to read EVERY file so can be slow!) function createSiteMap($fName='/',$level=0,$qShowP=TRUE, $qShowD=TRUE) { $level++; # If recursion level is too great, just return if ($level>20) return; # reset lists $this->smfList=array();$this->smpList=array(); # Get list of folders and pages for folder name # NB: this only includes certain files and folders $fname=$this->createLists($fName,'smfList','smpList'); # Save lists locally as they will be overwritten at next recurse $fList=$this->smfList; $pList=$this->smpList; # Exit fn now if nothing to do if (count($fList)==0 & count($pList)==0) return; # Folder heading $r='<div style="padding-left:1em;">'.NL; if ($fName=='/') $name='Home'; else $name=$this->UsToSp($fName); $r.='<b><a href="'.$fName.'">'.$name.'</a></b> (s)'.NL; if ($qShowD) $d=implode("<br>\n",$this->getFileDetails($fName,TRUE)); else $d=''; $r.='<div style="padding-left:2em; font-size:70%;">'.$d.'</div>'; # List the pages for this folder if ($qShowP) foreach ($pList as $page) { # strip .xxx from name + proper case + _ to space (Useful function) $r.='<a href="'.$fName.$page.'" style="padding-left:1em;">'.$this->fmtPName($page).'</a> (p)'.NL; # get file details if ($qShowD) $d=implode("<br>\n",$this->getFileDetails($fName.$page,FALSE)); else $d=''; $r.='<div style="padding-left:2em; font-size:70%;">'.$d.'</div>'.NL; } # Now recurse for each sub-folder foreach ($fList as $dir) $r.=$this->createSiteMap($fName.$dir.'/',$level,$qShowP,$qShowD); # Close the div for this folder $r.='</div>'.NL; # return $r; } // ---------- END OF AutoMenu CLASS ------- } ?>

Pages:

Valid HTML 4.01 iconValid CSS icon
© Copyright Julian Knight, July 2008 All rights reserved.
Page: Updated 2008-07-10 08:50:07, Author Julian Knight