<?php // Contains all the common php functions that most pages need. // Some common/global settings session_start(); // retain session variables across page requests // The settings below display error on the screen, instead of giving blank pages. error_reporting(E_ALL ^ E_NOTICE); ini_set('display_errors', true); // Todo: get rid of these globals $XCATROOT = '/opt/xcat/bin'; $CURRDIR = '/opt/xcat/web'; /*----------------------------------------------------------------------------------------------- Function to insert the header part of the HTML and the top part of the page ------------------------------------------------------------------------------------------------*/ function insertHeader($title, $stylesheets, $javascripts, $currents) { global $TOPDIR; if (!$TOPDIR) $TOPDIR = '..'; echo <<<EOS <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 Strict//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>$title</title> <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1"> <link href="$TOPDIR/lib/style.css" rel="stylesheet"> <script src="$TOPDIR/lib/functions.js" type="text/javascript"></script> EOS; // These are only needed for popup windows, so only need it for specific pages like groups & dsh //echo "<script src='$TOPDIR/lib/GroupNodeTableUpdater.js' type='text/javascript'> </script>\n"; //echo "<script src='$TOPDIR/js/prototype.js' type='text/javascript'></script>\n"; //echo "<script src='$TOPDIR/js/scriptaculous.js?load=effects' type='text/javascript'></script>\n"; //echo "<script src='$TOPDIR/js/effects.js' type='text/javascript'> </script>\n"; //echo "<script src='$TOPDIR/js/window.js' type='text/javascript'></script>\n"; //echo "<link href='$TOPDIR/themes/default.css' rel='stylesheet' type='text/css'/>\n"; //echo "<link href='$TOPDIR/css/xcattop.css' rel='stylesheet'>\n"; //echo "<link href='$TOPDIR/css/xcat.css' rel='stylesheet'>\n"; if ($stylesheets) { foreach ($stylesheets as $s) { echo "<LINK rel=stylesheet href='$s' type='text/css'>\n"; } } if ($javascripts) { foreach ($javascripts as $j) { echo "<script type='text/javascript' src='$j'></script>\n"; } } echo "</head><body>\n"; echo <<<EOS <table id=headingTable border=0 cellspacing=0 cellpadding=0> <tr valign=top> <td><img src='$TOPDIR/images/topl.jpg'></td> <td class=TopMiddle><img id=xcatImage src='$TOPDIR/images/xCAT_icon-l.gif' height=40px></td> <td class=TopMiddle width='100%'> EOS; //echo "<div id=top><img id=xcatImage src='$TOPDIR/images/xCAT_icon.gif'><div id=menuDiv>\n"; insertMenus($currents); echo "</td><td><img src='$TOPDIR/images/topr.jpg'></td></tr></table>\n"; //echo "</div></div>\n"; // end the top div } // end insertHeader // Insert the menus at the top of the page // $currents is an array of the current menu choice tree function insertMenus($currents) { global $TOPDIR; echo "<table border=0 cellspacing=0 cellpadding=0>\n"; insertMenuRow($currents[0], 1, array( 'machines' => array('Machines', "$TOPDIR/machines/groups.php"), 'jobs' => array('Jobs', "$TOPDIR/jobs/overview.php"), 'deploy' => array('Deploy', "$TOPDIR/deploy/osimages.php"), 'config' => array('Configuration', "$TOPDIR/config/site.php"), 'support' => array('Support', "$TOPDIR/support/diagnose.php") )); if ($currents[0] == 'machines') { insertMenuRow($currents[1], 0, array( 'lab' => array('Lab Floor', "$TOPDIR/machines/lab.php"), 'frames' => array('Frames', "$TOPDIR/machines/frames.php"), 'groups' => array('Groups', "$TOPDIR/machines/groups.php"), 'nodes' => array('Nodes', "$TOPDIR/machines/nodes.php"), 'layout' => array('Layout', "$TOPDIR/machines/layout.php") )); } elseif ($currents[0] == 'jobs') { insertMenuRow($currents[1], 0, array( 'overview' => array('Overview', "$TOPDIR/jobs/overview.php"), //todo: Vallard fill in rest )); } elseif ($currents[0] == 'deploy') { insertMenuRow($currents[1], 0, array( 'osimages' => array('OS Images', "$TOPDIR/deploy/osimages.php"), 'prepare' => array('Prepare', "$TOPDIR/deploy/prepare.php"), 'deploy' => array('Deploy', "$TOPDIR/deploy/deploy.php"), 'monitor' => array('Monitor', "$TOPDIR/deploy/monitor.php"), )); } elseif ($currents[0] == 'config') { insertMenuRow($currents[1], 0, array( 'prefs' => array('Preferences', "$TOPDIR/config/prefs.php"), 'site' => array('Cluster Settings', "$TOPDIR/config/site.php"), 'mgmtnode' => array('Mgmt Node', "$TOPDIR/config/mgmtnode.php"), 'monitor' => array('Monitor Setup', "$TOPDIR/config/monitor.php"), 'eventlog' => array('Event Log', "$TOPDIR/config/eventlog.php"), )); } elseif ($currents[0] == 'support') { insertMenuRow($currents[1], 0, array( 'diagnose' => array('Diagnose', "$TOPDIR/support/diagnose.php"), 'update' => array('Update', "$TOPDIR/support/update.php"), 'howtos' => array('HowTos', "$TOPDIR/support/howtos.php"), 'manpages' => array('Man Pages', "$TOPDIR/support/manpages.php"), 'maillist' => array('Mail List', "http://xcat.org/mailman/listinfo/xcat-user"), 'wiki' => array('Wiki', "http://xcat.wiki.sourceforge.net/"), 'suggest' => array('Suggestions', "$TOPDIR/support/suggest.php"), 'about' => array('About', "$TOPDIR/support/about.php"), )); } echo "</table>\n"; } // Insert one set of choices under a main choice. function insertMenuRow($current, $isTop, $items) { global $TOPDIR; //$img = "$TOPDIR/images/h3bg_new.gif"; $menuRowClass = $isTop ? '' : 'class=MenuRowBottom'; $menuItemClass = $isTop ? 'class=MenuItemTop' : ''; $currentClass = $isTop ? 'class=CurrentMenuItemTop' : ''; //echo "<TABLE class=MenuTable id=mainNav cellpadding=0 cellspacing=0 border=0><tr>\n"; //echo "<div class=$menuRowClass><ul id=mainNav>\n"; echo "<tr><td $menuRowClass><ul id=mainNav>\n"; foreach ($items as $key => $link) { if ($key == $current){ //echo "<TD><a id=$key href='$link[1]'>$link[0]</a></TD>\n"; echo "<li><p $currentClass>$link[0]</p></li>"; } else { //echo "<TD><a class=NavItem id=$key href='$link[1]'>$link[0]</a></TD>\n"; echo "<li><a $menuItemClass id=$key href='$link[1]'>$link[0]</a></li>"; } } //echo "</TR></TABLE>\n"; //echo "</ul></div>\n"; echo "</td></tr></ul>\n"; } /** ---------------------------------------------------------------------------------------------- Function to run the commands on the remote nodes. Four arguments: 1. The command 2. Mode: 0: If successful, return output to a reference variable in the caller function, with the newlines removed. Otherwise, print the error msg to the screen 2: Like mode 0, if successful, return output to a reference variable in the caller function, with the newlines removed. But error msgs are output to reference variable in the caller function 1: Long running cmd, intermediate results/errors are ouput as the command is executed -1: Like mode 1 3: Long running cmd. Results/errors are output to a file and return a file handle to the caller function 3. Reference variable to hold the output returned to caller function 4. Reference to an options hash, e.g. { NoVerbose => 1, NoRedirectStderr => 1 } Return status: 0 - successful, error - 1 ------------------------------------------------------------------------------------------------*/ function runcmd ($cmd, $mode, &$output, $options=NULL){ //Set error output to the same source as standard output (on Linux) if (strstr($cmd,'2>&1') == FALSE && !$options["NoRedirectStdErr"]) { $cmd .= ' 2>&1'; } $cmd = "/bin/sudo $cmd"; //todo: change this when move to xcat 2 $ret_stat = ""; $arr_output = NULL; if ($mode == 3){ $handle = popen($cmd, "r"); if($handle){ $output = $handle; //return file handle to caller return 0; //successful }else{ echo "Piping command into a file failed"; return 1; } }elseif ($mode == 0 || $mode == 2 ){ exec($cmd,$arr_output,$ret_stat); if ($ret_stat == 0){ $output = $arr_output; } else { //output the error msg to the screen if ($mode == 0) echo $arr_output[0]; //output error msg to the caller function elseif ($mode == 2) $output = $arr_output[0]; } }elseif ($mode == 1 || $mode == -1){ system($cmd,$ret_stat); } return $ret_stat; } // Send the keys and values in the primary global arrays function dumpGlobals() { //------------------------------------ trace('$_SERVER:'); foreach ($_SERVER as $key => $val) { trace("$key = $val"); } trace('<br>$_ENV:'); foreach ($_ENV as $key => $val) { trace("$key = $val"); } trace('<br>$_REQUEST:'); foreach ($_REQUEST as $key => $val) { trace("$key = $val"); } if (isset($_SESSION)) { trace('<br>$_SESSION:'); foreach ($_SESSION as $key => $val) { trace("$key = $val"); } } } # Returns true if the given rpm file is already installed at this version or higher. function isInstalled($rpmfile) { //------------------------------------ $aixrpmopt = isAIX() ? '--ignoreos' : ''; $lang = isWindows() ? '' : 'LANG=C'; //todo: add this back in $out = array(); $rc = runcmd("rpm -U $aixrpmopt --test $rpmfile", 2, $out); # The rc is not reliable in this case because it will be 1 if it is already installed # of if there is some other problem like a dependency is not satisfied. So we parse the # output instead. if (preg_grep('/package .* already installed/', $out)) { return 1; } else { return 0; } } // Debug output ------------------------------------ define("TRACE", "1"); function trace($str) { if (TRACE) { echo "<p class=Trace>$str</p>\n"; } } // Take in a hostname or ip address and return the fully qualified primary hostname. If resolution fails, // it just returns what it was given. function resolveHost($host) { //------------------------------------ if (isIPAddr($host)) { // IP address $hostname = gethostbyaddr($host); return $hostname; } else { //todo: implement resolution of hostname to full primary hostname with just one call $ip = gethostbyname($host); if (!isIPAddr($ip)) { return $host; } $hostname = gethostbyaddr($ip); return $hostname; } } //------------------------------------ function isIPAddr ($host) { return preg_match('/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/', $host); } // Returns the URL for the requested documentation. This provides 1 level of indirection in case the location // of some of the documentation changes. function getDocURL($book, $section) { //------------------------------------ $web = array ( 'docs' => "http://xcat.org/doc", 'forum' => "http://xcat.org/mailman/listinfo/xcat-user", 'updates' => "http://www.alphaworks.ibm.com/tech/xCAT", 'opensrc' => "http://www-rcf.usc.edu/~garrick", 'wiki' => "http://www.asianlinux.org/xcat", ); $man = array (); $howto = array (); $rsctadmin = array ( 0 => "http://publib.boulder.ibm.com/infocenter/clresctr/vxrx/topic/com.ibm.cluster.rsct.doc/rsct_aix5l53", 1 => "$rsctadmin[0]/bl5adm1002.html", 'sqlExpressions' => "$rsctadmin[0]/bl5adm1042.html#ussexp", 'conditions' => "$rsctadmin[0]/bl5adm1041.html#cmrcond", 'responses' => "$rsctadmin[0]/bl5adm1041.html#cmrresp", 'resourceClasses' => "$rsctadmin[0]/bl5adm1039.html#lavrc", ); $rsctref = array ( 0 => "http://publib.boulder.ibm.com/infocenter/clresctr/vxrx/topic/com.ibm.cluster.rsct.doc/rsct_linux151", 1 => "$rsctref[0]/bl5trl1002.html", 'errm' => "$rsctref[0]/bl5trl1067.html#errmcmd", 'errmScripts' => "$rsctref[0]/bl5trl1081.html#errmscr", 'sensor' => "$rsctref[0]/bl5trl1088.html#srmcmd", 'auditlog' => "$rsctref[0]/bl5trl1095.html#audcmd", 'lscondresp' => "$rsctref[0]/bl5trl1071.html#lscondresp", 'startcondresp' => "$rsctref[0]/bl5trl1079.html#startcondresp", ); if ($book) { if (!$section) { $section = 1; } // link to whole book if no section specified $url = &$$book; return $url[$section]; } else { // produce html for a page that contains all the links above, for testing purposes return ''; //todo: } } // This returns important display info about each type of hardware, so we can easily add new hw types. function getHWTypeInfo($hwtype, $attr) { //------------------------------------ //todo: get the aliases to be keys in this hash too $hwhash = array ( 'x335' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'8676' ), 'x336' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'8837' ), 'x306' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'8836' ), 'x306m' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'8849,8491' ), 'x3550' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'7978' ), 'e325' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'8835' ), 'e326' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'8848' ), 'e326m' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'7969' ), 'e327' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'' ), 'x340' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'8656' ), 'x342' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'8669' ), 'x345' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'8670' ), 'x346' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'8840' ), 'x3650' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'7979' ), 'x360' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'8686' ), 'x365' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'8862' ), 'x366' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'8863' ), 'x3850' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'8863' ), 'x445' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'' ), 'x450' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'' ), 'x455' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'' ), 'x460' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'' ), 'x3950' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'' ), 'hs20' => array ( 'image'=>'blade.gif', 'rackimage'=>'blade-front', 'u'=>7, 'aliases'=>'8678,8843,7981' ), # removed 8832 because it is older and it made this entry to wide in the drop down boxes 'js20' => array ( 'image'=>'blade.gif', 'rackimage'=>'blade-front', 'u'=>7, 'aliases'=>'8842' ), 'js21' => array ( 'image'=>'blade.gif', 'rackimage'=>'blade-front', 'u'=>7, 'aliases'=>'8844' ), 'ls20' => array ( 'image'=>'blade.gif', 'rackimage'=>'blade-front', 'u'=>7, 'aliases'=>'8850' ), 'hs40' => array ( 'image'=>'blade2.gif', 'rackimage'=>'blade2-front', 'u'=>7, 'aliases'=>'8839' ), 'p5-505' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'9115' ), 'p5-505Q' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'9115' ), 'p5-510' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'9110' ), 'p5-510Q' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'9110' ), 'p5-575' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'9118' ), 'p5-520' => array ( 'image'=>'520.gif', 'rackimage'=>'p5-520-front', 'u'=>4, 'aliases'=>'9111' ), 'p5-520Q' => array ( 'image'=>'520.gif', 'rackimage'=>'p5-520-front', 'u'=>4, 'aliases'=>'9131' ), 'p5-550' => array ( 'image'=>'520.gif', 'rackimage'=>'p5-520-front', 'u'=>4, 'aliases'=>'9113' ), 'p5-550Q' => array ( 'image'=>'520.gif', 'rackimage'=>'p5-520-front', 'u'=>4, 'aliases'=>'9133' ), 'p5-560' => array ( 'image'=>'520.gif', 'rackimage'=>'p5-520-front', 'u'=>4, 'aliases'=>'9116' ), 'p5-560Q' => array ( 'image'=>'520.gif', 'rackimage'=>'p5-520-front', 'u'=>4, 'aliases'=>'9116' ), 'p5-570' => array ( 'image'=>'520.gif', 'rackimage'=>'p5-520-front', 'u'=>4, 'aliases'=>'9117' ), 'p5-590' => array ( 'image'=>'590.gif', 'rackimage'=>'p5-590-front', 'u'=>42, 'aliases'=>'9119' ), 'p5-595' => array ( 'image'=>'590.gif', 'rackimage'=>'p5-590-front', 'u'=>42, 'aliases'=>'9119' ), 'p710' => array ( 'image'=>'342.gif', 'rackimage'=>'x335-front', 'u'=>2, 'aliases'=>'9123' ), 'p720' => array ( 'image'=>'520.gif', 'rackimage'=>'x345-front', 'u'=>4, 'aliases'=>'9124' ), 'p610' => array ( 'image'=>'520.gif', 'rackimage'=>'x335-front', 'u'=>5, 'aliases'=>'7028' ), 'p615' => array ( 'image'=>'520.gif', 'rackimage'=>'x335-front', 'u'=>4, 'aliases'=>'7029' ), 'p630' => array ( 'image'=>'520.gif', 'rackimage'=>'x335-front', 'u'=>4, 'aliases'=>'7026' ), 'p640' => array ( 'image'=>'520.gif', 'rackimage'=>'x335-front', 'u'=>5, 'aliases'=>'7026' ), 'p650' => array ( 'image'=>'520.gif', 'rackimage'=>'x335-front', 'u'=>8, 'aliases'=>'7038' ), 'p655' => array ( 'image'=>'590.gif', 'rackimage'=>'x335-front', 'u'=>42, 'aliases'=>'7039' ), 'p670' => array ( 'image'=>'590.gif', 'rackimage'=>'x335-front', 'u'=>42, 'aliases'=>'7040' ), 'p690' => array ( 'image'=>'590.gif', 'rackimage'=>'x335-front', 'u'=>42, 'aliases'=>'7040' ), ); $info = $hwhash[strtolower($hwtype)]; if (isset($attr)) { return $info[$attr]; } else { return $info; } } // Returns the image that should be displayed for this type of hw. Gets this from getHWTypeInfo() function getHWTypeImage($hwtype, $powermethod) { //------------------------------------ # 1st try to match the hw type $info = getHWTypeInfo($hwtype, 'image'); if ($info) { return $info['image']; } # No matches yet. Use the power method to get a generic type. if (isset($powermethod)) { $powermethod = strtolower($powermethod); if ($powermethod == 'blade') { return getHWTypeInfo('hs20', 'image'); } elseif ($powermethod == 'hmc') { return getHWTypeInfo('p5-520', 'image'); } elseif ($powermethod == 'bmc') { return getHWTypeInfo('x335','image'); } elseif ($powermethod == 'xseries') { return getHWTypeInfo('x335', 'image'); } } # As a last resort, return the most common node image return getHWTypeInfo('x335', 'image'); } // Return the image that represents the status string passed in function getStatusImage($status) { global $TOPDIR; if ($status == 'good') { return "$TOPDIR/images/green-ball-m.gif"; } elseif ($status == 'warning') { return "$TOPDIR/images/yellow-ball-m.gif"; } elseif ($status == 'bad') { return "$TOPDIR/images/red-ball-m.gif"; } else { return "$TOPDIR/images/blue-ball-m.gif"; } } // Returns the specified user preference value. Not finished. function getPref($key) { //------------------------------------ if ($key == 'MaxNodesDisplayed') { return 50; } return ''; } // Returns a list of some or all of the nodes in the cluster and some of their attributes. // Pass in a node range (or NULL to get all nodes) and an array of attribute names (or NULL for none). // Returns an array where each key is the node name and each value is an array of attr/value pairs. function getNodes($noderange, $attrs) { //my ($hostname, $type, $osname, $distro, $version, $mode, $status, $conport, $hcp, $nodeid, $pmethod, $location, $comment) = split(/:\|:/, $na); //$nodes[] = array('hostname'=>"node$i.cluster.com", 'type'=>'x3655', 'osname'=>'Linux', 'distro'=>'RedHat', 'version'=>'4.5', 'status'=>1, 'conport'=>$i, 'hcp'=>"node$i-bmc.cluster.com", 'nodeid'=>'', 'pmethod'=>'bmc', 'location'=>"frame=1 u=$", 'comment'=>''); $nodes = array(); foreach ($attrs as $a) { $output = array(); //echo "<p>nodels $noderange $a</p>\n"; runcmd("nodels $noderange $a", 2, $output); foreach ($output as $line) { $vals = preg_split('/: */', $line); // vals[0] will be the node name if (!$nodes[$vals[0]]) { $nodes[$vals[0]] = array(); } $attributes = & $nodes[$vals[0]]; if ($a == 'type') { $types = preg_split('/-/', $vals[1]); $attributes['osversion'] = $types[0]; $attributes['arch'] = $types[1]; $attributes['type'] = $types[2]; } } } return $nodes; } // Returns the node groups defined in the cluster. function getGroups() { $groups = array(); $output = array(); runcmd("listattr", 2, $output); foreach ($output as $grp) { $groups[] = $grp; } return $groups; } // Returns the aggregate status of each node group in the cluster. The return value is a // hash in which the key is the group name and the value is the status as returned by nodestat. function getGroupStatus() { $groups = array(); $output = array(); runcmd("grpattr", 2, $output); foreach ($output as $line) { //echo "<p>line=$line</p>"; $vals = preg_split('/: */', $line); if (count($vals) == 2) { $groups[$vals[0]] = $vals[1]; } } return $groups; } // Returns true if we are running on AIX ------------------------------------ function isAIX() { } //todo: implement // Returns true if we are running on Linux ------------------------------------ function isLinux() { } //todo: implement // Returns true if we are running on Windows ------------------------------------ function isWindows() { return array_key_exists('WINDIR', $_SERVER); } // Create file folder-like tabs. Tablist is an array of label/url pairs. function insertTabs ($tablist, $currentTabIndex) { //------------------------------------ echo "<TABLE cellpadding=4 cellspacing=0 width='100%' summary=Tabs><TBODY><TR>"; foreach ($tablist as $key => $tab) { if ($key != 0) { echo "<TD width=2></TD>"; } if ($currentTabIndex == $key) { echo "<TD align=center background='$TOPDIR/images/tab-current.gif'><b>$tab[0]</b></TD>"; } else { echo "<TD align=center background='$TOPDIR/images/tab.gif'><A href='$tab[1]'>$tab[0]</A></TD>"; } } echo "</TR><TR><TD colspan=7 height=7 bgcolor='#CBCBCB'></TD></TR></TBODY></TABLE>\n"; } // Create the Action buttons in a table. Buttonlist is an array of arrays of button attribute strings. function insertButtons ($buttonsets) { //------------------------------------ foreach ($buttonsets as $buttonlist) { echo "<TABLE cellpadding=0 cellspacing=2><TR>"; foreach ($buttonlist as $button) { //echo "<td><INPUT type=submit class=but $button ></td>"; echo "<td><a class=button href=''><span>$button</span></a></td>"; } echo "</TR></TABLE>\n"; } } function insertNotDoneYet() { echo "<p class=NotDone>This page is not done yet.</p>\n"; } ?>