diff options
| -rwxr-xr-x | BitSystem.php | 256 | ||||
| -rw-r--r-- | admin/admin_packages_inc.php | 25 | ||||
| -rw-r--r-- | admin/schema_inc.php | 10 | ||||
| -rw-r--r-- | admin/upgrades/2.0.0.php | 10 | ||||
| -rw-r--r-- | dependency_graph.php | 7 | ||||
| -rw-r--r-- | templates/admin_packages.tpl | 146 |
6 files changed, 397 insertions, 57 deletions
diff --git a/BitSystem.php b/BitSystem.php index eef2c32..c37b5ed 100755 --- a/BitSystem.php +++ b/BitSystem.php @@ -3,7 +3,7 @@ * Main bitweaver systems functions * * @package kernel - * @version $Header: /cvsroot/bitweaver/_bit_kernel/BitSystem.php,v 1.199 2008/10/29 22:06:35 squareing Exp $ + * @version $Header: /cvsroot/bitweaver/_bit_kernel/BitSystem.php,v 1.200 2008/10/30 22:02:20 squareing Exp $ * @author spider <spider@steelsun.com> */ // +----------------------------------------------------------------------+ @@ -1803,6 +1803,252 @@ die; } /** + * registerDependencies + * + * @param array $pParams + * @param array $pDepHash + * @access public + * @return void + */ + function registerDependencies( $pPackage, $pDepHash ) { + if( !empty( $pPackage ) && $this->verifyDependencies( $pDepHash )) { + $this->mDependencies[strtolower( $pPackage )]['dependencies'] = $pDepHash; + } + } + + /** + * verifyDependencies + * + * @param array $pDepHash + * @access public + * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure + */ + function verifyDependencies( &$pDepHash ) { + if( !empty( $pDepHash ) && is_array( $pDepHash )) { + foreach( $pDepHash as $pkg => $versions ) { + if( empty( $versions['min'] )) { + $this->mErrors['version_min'] = "You have to provide a minimum version number for the $pkg dependency. If you just want the required package to be present, please use 0.0.0 as minimum version."; + } elseif( !$this->validateVersion( $versions['min'] )) { + $this->mErrors['version_min'] = "Please make sure you use a valid minimum version number for the $pkg dependency."; + } elseif( !empty( $versions['max'] )) { + if( !$this->validateVersion( $versions['max'] )) { + $this->mErrors['version_max'] = "Please make sure you use a valid maximum version number for the $pkg dependency."; + } elseif( version_compare( $versions['min'], $versions['max'], '>=' )) { + $this->mErrors['version_max'] = "Please make sure the maximum version is greater than the minimum version for the $pkg dependency."; + } + } + } + } else { + $this->mErrors['deps'] = "If you want to register dependencies, please do so with a valid dependency hash."; + } + + // since this should only show up when devs are working, we'll simply display the output: + if( !empty( $this->mErrors )) { + vd( $this->mErrors ); + bt(); + } + + return( count( $this->mErrors ) == 0 ); + } + + /** + * getDependencies + * + * @param array $pPackage + * @access public + * @return array of package dependencies + */ + function getDependencies( $pPackage ) { + $ret = array(); + if( !empty( $pPackage )) { + $pPackage = strtolower( $pPackage ); + if( !empty( $this->mDependencies[$pPackage]['dependencies'] )) { + return $this->mDependencies[$pPackage]['dependencies']; + } + } + return $ret; + } + + /** + * calculateDependencies will calculate all dependencies and return a hash of the results + * + * @access public + * @return array of calculated dependencies + */ + function calculateDependencies() { + $ret = array(); + // first we gather all version information. + foreach( array_keys( $this->mPackages ) as $package ) { + if( $this->isPackageActive( $package )) { + + // get the latest upgrade version, since this is the version the package will be at after install + if( !$version = $this->getLatestUpgradeVersion( $package )) { + $version = $this->getVersion( $package ); + } + $installed[$package] = $version; + + if( $deps = $this->getDependencies( $package )) { + $dependencies[$package] = $deps; + } + } + } + + if( !empty( $dependencies )) { + foreach( $dependencies as $package => $deps ) { + foreach( $deps as $depPackage => $depVersion ) { + $hash = array( + 'package' => $package, + 'package_version' => $installed[$package], + 'requires' => $depPackage, + 'required_version' => $depVersion, + ); + + if( !empty( $installed[$depPackage] )) { + $hash['version']['available'] = $installed[$depPackage]; + } + + if( empty( $installed[$depPackage] )) { + $hash['result'] = 'missing'; + } elseif( version_compare( $depVersion['min'], $installed[$depPackage], '>' )) { + $hash['result'] = 'min_dep'; + } elseif( !empty( $depVersion['max'] ) && version_compare( $depVersion['max'], $installed[$depPackage], '<' )) { + $hash['result'] = 'max_dep'; + } else { + $hash['result'] = 'ok'; + } + + $ret[] = $hash; + } + } + } + + return $ret; + } + + /** + * drawDependencyGraph + * + * @access public + * @return image + */ + function drawDependencyGraph( $pFormat = 'png', $pCommand = 'dot' ) { + global $gBitSmarty; + + // only do this if we can load PEAR GraphViz interface + if( include_once( 'Image/GraphViz.php' )) { + ksort( $this->mPackages ); + $deps = $this->calculateDependencies(); + $delKeys = $matches = array(); + + // crazy manipulation of hash to remove duplicate version matches. + // we do this that we can use double headed arrows in the graph below. + foreach( $deps as $key => $dep ) { + foreach( $deps as $k => $d ) { + if( $dep['requires'] == $d['package'] && $dep['package'] == $d['requires'] && $dep['result'] == 'ok' && $d['result'] == 'ok' ) { + $deps[$key]['dir'] = 'both'; + $matches[$key] = $k; + } + } + } + + // get duplicates + foreach( $matches as $key => $match ) { + unset( $delKeys[$match] ); + $delKeys[$key] = $match; + } + + // remove dupes from hash + foreach( $delKeys as $key ) { + unset( $deps[$key] ); + } + + // start drawing stuff + $graphattributes = array( + 'fontname' => 'mono', + 'fontsize' => 11, + 'overlap' => 'scale', + 'size' => '7,10', + 'ratio' => 'auto', + ); + $graph = new Image_GraphViz( TRUE, $graphattributes, 'Dependencies', TRUE ); + + $fromattributes = $toattributes = array( + 'overlap' => 'scale', + 'fontname' => 'mono', + 'fontsize' => 11, + 'style' => 'filled', + 'fillcolor' => 'palegreen', + ); + + foreach( $deps as $node ) { + //$fromNode = ucfirst( $node['package'] )."\n".$node['package_version']; + //$toNode = ucfirst( $node['requires'] )."\n".$node['required_version']['min']; + + $fromNode = ucfirst( $node['package'] ); + $toNode = ucfirst( $node['requires'] ); + + switch( $node['result'] ) { + case 'max_dep': + $edgecolor = 'chocolate3'; + $headlabel = 'Maximum version\nexceeded'; + $toNode .= "\n".$node['required_version']['min']." - ".$node['required_version']['max']; + $toattributes['fillcolor'] = 'khaki'; + break; + case 'min_dep': + $edgecolor = 'crimson'; + $headlabel = 'Minimum version\nnot met'; + $toNode .= "\n".$node['required_version']['min']; + if( !empty( $node['required_version']['max'] )) { + $toNode .= " - ".$node['required_version']['max']; + } + $toattributes['fillcolor'] = 'pink'; + break; + case 'missing': + $edgecolor = 'crimson'; + $headlabel = 'Missing package'; + $toNode .= "\n".$node['required_version']['min']; + if( !empty( $node['required_version']['max'] )) { + $toNode .= " - ".$node['required_version']['max']; + } + $toattributes['fillcolor'] = 'pink'; + break; + default: + $edgecolor = 'darkgreen'; + $headlabel = ''; + $toattributes['fillcolor'] = 'palegreen'; + break; + } + + $fromattributes['URL'] = "http://www.bitweaver.org/wiki/".ucfirst( $node['package'] )."Package"; + $graph->addNode( $fromNode, $fromattributes ); + + $toattributes['URL'] = "http://www.bitweaver.org/wiki/".ucfirst( $node['requires'] )."Package"; + $graph->addNode( $toNode, $toattributes ); + + $graph->addEdge( + array( $fromNode => $toNode ), + array( + 'dir' => ( !empty( $node['dir'] ) ? $node['dir'] : '' ), + 'color' => $edgecolor, + 'fontcolor' => $edgecolor, + 'label' => $headlabel, + 'fontname' => 'mono', + 'fontsize' => 11 + ) + ); + } + + if( preg_match( "#(png|gif|jpe?g|bmp|svg|tif)#i", $pFormat )) { + $graph->image( $pFormat, $pCommand ); + } else { + return $graph->fetch( $pFormat, $pCommand ); + } + } else { + return FALSE; + } + } + + /** * verifyInstalledPackages scan all available packages * * @param string $ pScanFile file to be looked for @@ -1886,8 +2132,7 @@ die; } // }}} - //********************* DATE AND TIME METHODS **************************// - + // {{{=========================== Date and time methods ============================== /** * Retrieve a current UTC timestamp * Simple map to BitDate object allowing tidy display elsewhere @@ -1995,6 +2240,7 @@ die; function get_long_datetime( $pTimestamp, $pUser = FALSE ) { return $this->mServerTimestamp->strftime( $this->get_long_datetime_format(), $pTimestamp, $pUser ); } + // }}} /** * checkBitVersion Check for new version of bitweaver @@ -2115,7 +2361,7 @@ die; - // ==================== deprecated methods - will be removed soon ==================== + // {{{ ==================== deprecated methods - will be removed soon ==================== // deprecated method saved compatibility until all getPreference calls have been eliminated /** * @deprecated deprecated since version 2.1.0-beta @@ -2243,6 +2489,7 @@ die; $this->setRenderFormat( "html" ); } } + // }}} } /* Function for sorting AppMenu by menu_position */ @@ -2256,4 +2503,5 @@ function bit_system_menu_sort( $a, $b ) { return $pb - $pa; } +/* vim: :set fdm=marker : */ ?> diff --git a/admin/admin_packages_inc.php b/admin/admin_packages_inc.php index e805624..87de1f3 100644 --- a/admin/admin_packages_inc.php +++ b/admin/admin_packages_inc.php @@ -1,6 +1,6 @@ <?php -// $Header: /cvsroot/bitweaver/_bit_kernel/admin/admin_packages_inc.php,v 1.9 2006/10/11 07:45:46 spiderr Exp $ +// $Header: /cvsroot/bitweaver/_bit_kernel/admin/admin_packages_inc.php,v 1.10 2008/10/30 22:02:20 squareing Exp $ // Copyright (c) 2002-2003, Luis Argerich, Garland Foster, Eduardo Polidor, et. al. // All Rights Reserved. See copyright.txt for details and a complete list of authors. @@ -13,25 +13,23 @@ $fPackage = &$_REQUEST['fPackage']; // emulate register_globals $gBitSystem->scanPackages( 'bit_setup_inc.php', TRUE, 'all', TRUE, TRUE ); -ksort($gBitSystem->mPackages); // So packages will be listed in alphabetical order // make a copy of mPackages - expensive, but this is low use code -if(!empty( $_REQUEST['features'] ) ) { +if( !empty( $_REQUEST['features'] ) ) { $pkgArray = $gBitSystem->mPackages; foreach( array_keys( $pkgArray ) as $pkgKey ) { $pkg = $pkgArray[$pkgKey]; - if( isset( $pkg['name'] ) ) { + if( !empty( $pkg['name'] )) { $pkgName = strtolower( $pkg['name'] ); - #can only change already installed packages that are not required - if ($gBitSystem->isPackageInstalled($pkgName) && empty($pkg['required']) ) { - if( isset( $_REQUEST['fPackage'][$pkgName] ) ) { - #mark installed and active + // can only change already installed packages that are not required + if( $gBitSystem->isPackageInstalled( $pkgName ) && empty( $pkg['required'] )) { + if( isset( $_REQUEST['fPackage'][$pkgName] )) { + // mark installed and active $gBitSystem->storeConfig( 'package_'.$pkgName, 'y', $pkgName ); unset( $pkgArray[$pkgKey] ); - } - else { - #mark installed but not active + } else { + // mark installed but not active $gBitSystem->storeConfig( 'package_'.$pkgName, 'i', $pkgName ); unset( $pkgArray[$pkgKey] ); } @@ -43,4 +41,9 @@ if(!empty( $_REQUEST['features'] ) ) { global $gBitInstaller; $gBitInstaller = &$gBitSystem; $gBitSystem->verifyInstalledPackages(); +$gBitSmarty->assign( 'dependencies', $gBitInstaller->calculateDependencies() ); +$gBitSmarty->assign( 'dependencymap', $gBitSystem->drawDependencyGraph( 'cmapx' )); + +// So packages will be listed in alphabetical order +ksort( $gBitSystem->mPackages ); ?> diff --git a/admin/schema_inc.php b/admin/schema_inc.php index 1625ae2..f9be513 100644 --- a/admin/schema_inc.php +++ b/admin/schema_inc.php @@ -135,6 +135,14 @@ $gBitInstaller->registerModules( $moduleHash ); $gBitInstaller->registerUserPermissions( KERNEL_PKG_NAME, array( array('p_admin', 'Can manage users groups and permissions and all aspects of site management', 'admin', KERNEL_PKG_NAME ), array('p_access_closed_site', 'Can access site when closed', 'admin', KERNEL_PKG_NAME) -) ); +)); + +// Package dependencies +$gBitInstaller->registerDependencies( KERNEL_PKG_NAME, array( + 'liberty' => array( 'min' => '2.1.0' ), + 'users' => array( 'min' => '2.1.0' ), + 'themes' => array( 'min' => '2.0.0' ), + 'languages' => array( 'min' => '2.0.0' ), +)); ?> diff --git a/admin/upgrades/2.0.0.php b/admin/upgrades/2.0.0.php index 516e0e5..d86f031 100644 --- a/admin/upgrades/2.0.0.php +++ b/admin/upgrades/2.0.0.php @@ -1,6 +1,6 @@ <?php /** - * @version $Header: /cvsroot/bitweaver/_bit_kernel/admin/upgrades/2.0.0.php,v 1.2 2008/10/29 22:05:18 squareing Exp $ + * @version $Header: /cvsroot/bitweaver/_bit_kernel/admin/upgrades/2.0.0.php,v 1.3 2008/10/30 22:02:20 squareing Exp $ */ global $gBitInstaller; @@ -9,13 +9,5 @@ $infoHash = array( 'version' => str_replace( '.php', '', basename( __FILE__ )), 'description' => "Set core package version number.", ); - $gBitInstaller->registerPackageUpgrade( $infoHash ); - -$gBitInstaller->registerPackageDependencies( $infoHash, array( - 'liberty' => array( 'min' => '2.1.0' ), - 'users' => array( 'min' => '2.1.0' ), - 'themes' => array( 'min' => '2.0.0' ), - 'languages' => array( 'min' => '2.0.0' ), -)); ?> diff --git a/dependency_graph.php b/dependency_graph.php new file mode 100644 index 0000000..023f054 --- /dev/null +++ b/dependency_graph.php @@ -0,0 +1,7 @@ +<?php +require_once( '../bit_setup_inc.php' ); +global $gBitInstaller; +$gBitInstaller = &$gBitSystem; +$gBitInstaller->verifyInstalledPackages(); +$gBitInstaller->drawDependencyGraph(( !empty( $_REQUEST['format'] ) ? $_REQUEST['format'] : 'png' ), ( !empty( $_REQUEST['command'] ) ? $_REQUEST['command'] : 'dot' )); +?> diff --git a/templates/admin_packages.tpl b/templates/admin_packages.tpl index 10fe7f0..d34bdfa 100644 --- a/templates/admin_packages.tpl +++ b/templates/admin_packages.tpl @@ -73,67 +73,149 @@ {/jstab} - - {jstab title="Not Installed"} - {legend legend="bitweaver packages available for installation"} - - <div class="row"> - <div class="formlabel"> - {biticon ipackage=install iname="pkg_install" iexplain="install" iforce=icon} - </div> - {forminput} - <p><strong><a class="warning" href='{$smarty.const.INSTALL_PKG_URL}install.php?step=3'>{tr}Click here to install more Packages{/tr} …</a></strong></p> - - {$install_unavailable} - {/forminput} - </div> - - <hr style="clear:both" /> - + {jstab title="Required"} + {legend legend="Required bitweaver packages installed on your system"} {foreach key=name item=package from=$gBitSystem->mPackages} - {if ((1 or $package.tables) && !$package.required && !$package.installed) } + {if $package.installed && !$package.service && $package.required} <div class="row"> <div class="formlabel"> {biticon ipackage=$name iname="pkg_`$name`" iexplain="$name" iforce=icon} </div> {forminput} - {$name|capitalize} - {formhelp note=`$package.info` package=$name} + <strong>{$name|capitalize}</strong> + {formhelp note=`$package.info` package=$name} {/forminput} </div> {/if} {/foreach} {/legend} - {/jstab} - - - {jstab title="Required"} - {legend legend="Required bitweaver packages installed on your system"} + {legend legend="Required bitweaver services installed on your system"} {foreach key=name item=package from=$gBitSystem->mPackages} - {if $package.installed && !$package.service && $package.required} + {if $package.installed && $package.service && $package.required} <div class="row"> <div class="formlabel"> {biticon ipackage=$name iname="pkg_`$name`" iexplain="$name" iforce=icon} </div> {forminput} + <label> <strong>{$name|capitalize}</strong> - {formhelp note=`$package.info` package=$name} + </label> + {formhelp note=`$package.info` package=$name} {/forminput} </div> {/if} {/foreach} {/legend} - {legend legend="Required bitweaver services installed on your system"} + {/jstab} + + + {if $dependencymap || $dependencies} + {jstab title="Dependencies"} + {if $dependencymap} + <h2>{tr}Dependencies{/tr}</h2> + <p class="help">{tr}Below you will find an illustration of how the packages depend on each other.{/tr}</p> + <div style="text-align:center; overflow:auto;"> + <img alt="A graphical representation of package dependencies" title="Dependency graph" src="{$smarty.const.KERNEL_PKG_URL}dependency_graph.php?format={$smarty.request.format}&command={$smarty.request.command}" usemap="#Dependencies" /> + {$dependencymap} + </div> + {/if} + + {if $dependencies} + <h2>{tr}Dependency Table{/tr}</h2> + <p class="help">{tr}Below you will find a detailed table with package dependencies. If not all package dependencies are met, consider trying to meet all package dependencies. If you don't meet them, you may continue at your own peril.{/tr}</p> + <table id="dependencies"> + <caption>Package Dependencies</caption> + <tr> + <th style="width:16%;">Requirement</th> + <th style="width:16%;">Min Version</th> + <th style="width:16%;">Max Version</th> + <th style="width:16%;">Available</th> + <th style="width:36%;">Result</th> + </tr> + {foreach from=$dependencies item=dep} + {if $pkg != $dep.package} + <tr><th colspan="5">{$dep.package|ucfirst} requirements</th></tr> + {assign var=pkg value=$dep.package} + {/if} + + {if $dep.result == 'ok'} + {assign var=class value=success} + {elseif $dep.result == 'missing'} + {assign var=class value=error} + {elseif $dep.result == 'min_dep'} + {assign var=class value=error} + {elseif $dep.result == 'max_dep'} + {assign var=class value=warning} + {/if} + + <tr class="{$class}"> + <td>{$dep.requires|ucfirst}</td> + <td>{$dep.required_version.min}</td> + <td>{$dep.required_version.max}</td> + <td>{$dep.required_version.available}</td> + <td> + {if $dep.result == 'ok'} + OK + {elseif $dep.result == 'missing'} + Package missing + {assign var=missing value=true} + {elseif $dep.result == 'min_dep'} + Minimum version not met + {assign var=min_dep value=true} + {elseif $dep.result == 'max_dep'} + Maximum version exceeded + {assign var=max_dep value=true} + {/if} + </td> + </tr> + {/foreach} + </table> + + {if $missing} + <p class="warning">At least one required package is missing. Please install the missing package.</p> + {/if} + + {if $min_dep} + <p class="warning">At least one package did not meet the minimum version requirement in our calculations. If possible, please get a newer version of those packages.</p> + {/if} + + {if $max_dep} + <p class="warning">At least one package recommend a version lower to the one you have installed or are about to upgrade to.</p> + {/if} + + {if !$min_dep && !$max_dep && !$missing} + <p class="success">All package dependencies have been met.</p> + {/if} + {/if} + + {/jstab} + {/if} + + + {jstab title="Not Installed"} + {legend legend="bitweaver packages available for installation"} + + <div class="row"> + <div class="formlabel"> + {biticon ipackage=install iname="pkg_install" iexplain="install" iforce=icon} + </div> + {forminput} + <p><strong><a class="warning" href='{$smarty.const.INSTALL_PKG_URL}install.php?step=3'>{tr}Click here to install more Packages{/tr} …</a></strong></p> + + {$install_unavailable} + {/forminput} + </div> + + <hr style="clear:both" /> + {foreach key=name item=package from=$gBitSystem->mPackages} - {if $package.installed && $package.service && $package.required} + {if ((1 or $package.tables) && !$package.required && !$package.installed) } <div class="row"> <div class="formlabel"> {biticon ipackage=$name iname="pkg_`$name`" iexplain="$name" iforce=icon} </div> {forminput} - <label> - <strong>{$name|capitalize}</strong> - </label> + {$name|capitalize} {formhelp note=`$package.info` package=$name} {/forminput} </div> |
