<?php
/*
Copyright (C) 2001-2004 ZZOSS GbR, http://www.zzoss.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
@version $Id: Plugin.php,v 1.18 2004/04/07 06:11:57 ordnas Exp $
@copyright Copyright © 2001-2004 ZZ/OSS GbR, http://www.zzoss.com
@license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
*/
require_once 'ZZOSS_Installer/Utils.php';
define('ZZOSS_PLUGIN_STATUS_UNDEFINED', 0);
define('ZZOSS_PLUGIN_STATUS_BUILTIN', 1);
define('ZZOSS_PLUGIN_STATUS_INCLUDED', 2);
/**
* Methods for installer plugins
*/
class ZZOSS_Plugin
{
var $application_settings = NULL;
var $build;
/**
* Directory where plugins are moved to.
*/
var $dest_dir;
/**
* Paths where plugins reside.
*/
var $sources = array();
var $output;
/**
* Data directory where build packages can store data.
*/
var $data_dir;
/**
* Available package descriptions.
*/
var $packages = array();
/**
* Relating file name to its baseinstalldir.
*/
var $file_baseinstalldir = array();
/**
* Track dirs of filelist.
*/
var $filelist_dirs = array();
/**
* Track roles of dirs of filelist.
*/
var $filelist_dirs_roles = array();
/**
* Debug mode on/off.
*/
var $is_debug = false;
function setDebug($is_debug)
{
$this->is_debug = $is_debug;
}
function setDestDir($dest_dir)
{
$this->dest_dir = $dest_dir;
}
/**
* Sources are processed in the order they have been added.
* First things first!
*/
function setSource($source)
{
if(strlen($source)){
$this->sources[] = $source;
}
}
/**
* Sources are processed in the order they have been added.
* First things first!
*/
function setDataDir($data)
{
$this->data_dir = $data;
}
function setQueue($queue)
{
$this->queue = $queue;
return is_array($this->queue);
}
function setRegistry(&$registry)
{
$this->registry = &$registry;
$this->initPlugins();
$this->initPackages();
$this->initApplicationPackages();
}
/**
* Using this function, one can execute a plugin on
* a certain package.
*/
function loadPackage($package_xml, $action, $step = 0)
{
if(!$xml = $this->registry->getPackageDescr($package_xml)){
trigger_error('Cannot read \''.$package_xml.'\'');
return false;
}
$pkg_name = $xml["name"].'-'.$xml['release']['version'];
$this->queue = array($step => array($pkg_name => $action));
$this->packages[$pkg_name] = $xml;
}
function exec($role, $step = 0, $mode = NULL)
{
$this->role = $role;
$this->log = "\n## Plugin handler '".$role."'\n";
$this->mode = $mode;
$this->log .= "## Plugin mode '".$mode."'\n";
$this->log .= "\n# Step ".($step)."\n";
$queue = $this->queue[$step];
// Now we deal with the packages
foreach($queue as $pkg_name => $action) {
$this->execPackage($action, $pkg_name);
}
}
function execPackage($action, $pkg_name)
{
// Pre-processing
switch($action) {
case 'I':
$this->action = 'install';
$this->plugin_action = 'add';
break;
case 'U':
$this->action = 'update';
$this->plugin_action = 'add';
break;
case 'R':
$this->action = 'remove';
$this->plugin_action = 'remove';
break;
}
$this->package = $this->packages[$pkg_name];
$this->log .= "\n[$pkg_name]\n\n";
$this->log .= "Action: ".$this->action."\n";
// compose absolute baseinstalldir
$this->pkg_root = '';
if(isset($this->packages_application[$pkg_name]["@"]["baseinstalldir"])){
$this->pkg_root = ZZOSS_InstallerUtils::fixPath($this->packages_application[$pkg_name]["@"]["baseinstalldir"]);
}
$this->pkg_root .= DIRECTORY_SEPARATOR;
// execute plugins
switch($this->role){
case 'role':
if($this->execRole()){
// register new plugins only, if fileroles have been executed
$this->registerPlugins();
$this->registry->setPlugins($this->plugins);
}
break;
case 'build':
$this->execBuild();
break;
}
}
function initPackages()
{
$this->packages = $this->registry->getPackages();
}
function initApplicationPackages()
{
$this->packages_application = $this->registry->getApplicationPackages();
}
function initPlugins()
{
$this->plugins = $this->registry->getPlugins();
}
function execRole()
{
// Upon install or update, we check if the src package is newer then
// the installed package, only then we perform the filerole routines
$pkg_installed_dir = $this->registry->getPkgInstalledDir($this->package["name"].'-'.$this->package["release"]["version"]);
//$this->log .= "Check if $pkg_installed_dir exists.\n";
if(is_dir($pkg_installed_dir)){
$pkg_src_dir = ZZOSS_InstallerUtils::fixPath($this->package["install"]["path"].DIRECTORY_SEPARATOR);
//$this->log .= "Check if $pkg_src_dir is newer then $pkg_installed_dir.\n";
if(filemtime($pkg_src_dir) < filemtime($pkg_installed_dir)){
$this->log .= "Package has not changed since last installation.\n";
return false;
}
}
//$this->log .= 'Files: '.var_export($files, true);
if(isset($this->package["release"]["filelist"])){
// clean up previous filelist
$this->filelist_dirs = array();
$this->filelist_dirs_roles = array();
$this->execRoleFilelist($this->package["release"]["filelist"]);
}
switch($this->action) {
case 'install':
case 'update':
$this->registry->installPackage($this->package["name"].'-'.$this->package["release"]["version"]);
$this->log .= $this->registry->getLog();
break;
case 'remove':
// remove directory of installed package
if(!isset($this->package['_specified'])){
$this->package['_specified'] = true;
}
$this->registry->removePackage($this->package["name"].'-'.$this->package["release"]["version"], $this->package['_specified']);
$this->log .= $this->registry->getLog();
break;
}
return true;
}
function execRoleFilelist($filelist, $role = null)
{
if(!is_array($filelist) || !count($filelist)){
$this->log .= "No files.\n";
return false;
} else {
// path of previous dir
$path_prev = '';
if(isset($filelist['@']['baseinstalldir'])){
$path_prev = $filelist['@']['baseinstalldir'];
}
if(isset($filelist['@']['name']) && $filelist['@']['name'] != '/' && $filelist['@']['name'] != '\\'){
$path_prev .= DIRECTORY_SEPARATOR.$filelist['@']['name'];
}
foreach($filelist as $type => $items){
switch($type){
case 'dir':
foreach($items as $dir){
$path = '';
$path = null;
$role = null;
if(isset($dir['@']['baseinstalldir'])){
$path = $dir['@']['baseinstalldir'];
}
if(isset($dir['@']['name']) && $dir['@']['name'] != '/' && $dir['@']['name'] != '\\'){
$path .= DIRECTORY_SEPARATOR.$dir['@']['name'];
}
if(isset($dir['@']['role'])){
$role = $dir['@']['role'];
}
$this->updateDirsStack($path_prev);
array_push($this->filelist_dirs, $path);
$this->execRoleFilelist($dir, $role);
}
break;
case 'file':
$this->updateDirsStack($path_prev);
$dir_path = '';
if(count($this->filelist_dirs)){
$dir_path = ZZOSS_InstallerUtils::fixPath(implode(DIRECTORY_SEPARATOR,$this->filelist_dirs));
//echo "Path: $dir_path <br/>";
}
foreach($items as $key => $file) {
if(!is_array($file)){
$this->file["@"]["name"] = $file;
if(!is_null($role)){
$this->file["@"]["role"] = $role;
}
} else {
$this->file = $file;
}
if(!isset($this->file["@"]["name"])){
$this->file["@"]["name"] = $this->file["#"];
}
if(isset($this->file["@"]["baseinstalldir"])){
$this->file_baseinstalldir[$this->file["@"]["name"]] = $dir_path.$this->file["@"]["baseinstalldir"];
} else {
$this->file_baseinstalldir[$this->file["@"]["name"]] = $dir_path;
}
if(!isset($this->file["@"]["role"])){
// check if we have a role association defined by the directory
if(!is_null($role)){
$this->file["@"]["role"] = $role;
} else {
$this->file["@"]["role"] = '';
}
}
//echo "Filename: {$this->file["@"]["name"]} <br/>";
//echo "Role: {$this->file["@"]["role"]} <br/>";
//echo "Path: {$this->file_baseinstalldir[$this->file["@"]["name"]]}".DIRECTORY_SEPARATOR."{$this->file["@"]["name"]} <br/>";
// execute plugins
$this->log .= "* Dealing with role '".$this->file["@"]["role"]."'\n";
//$this->log .= " performing on '".var_export($this->plugins['role']['role'], true)."'\n";
if(!isset($this->plugins['role']['role'][$this->file["@"]["role"]])){
$this->execPluginRole();
} else {
if(!$this->includePlugins($this->plugins['role']['role'][$this->file["@"]["role"]])){
$this->log .= "* ERROR: A handler for this role does not exist.\n";
}
}
}
break;
}
}
}
return true;
}
function updateDirsStack($path_prev)
{
// if the current dir is within previous dirs, we truncate the array,
// but only if the current directory is not the last one in our stack
if(strlen($path_prev) && end($this->filelist_dirs) != $path_prev){
//reset($this->fielist_dirs);
$filelist_dirs = array();
/*
echo '<pre>1';
print_r($this->filelist_dirs);
echo '</pre>';
*/
// rearrange the array with values only until we hit
// the current directory
foreach($this->filelist_dirs as $filelist_dir){
$filelist_dirs[] = $filelist_dir;
if($filelist_dir == $path_prev){
break;
}
}
/*
echo '<pre>2';
print_r($filelist_dirs);
echo '</pre>';
*/
$this->filelist_dirs = $filelist_dirs;
}
}
function execPluginRole()
{
$GLOBALS['ZI']['plugin_log'] = '';
// if we are supposed to process the filelist,
// but no plugin is available,
// then we simply copy the file to the specified
// location.
ob_start();
$this->log .= "* Using build in plugin to move file\n";
$this->initGlobalVariables();
$status = ZZOSS_PLUGIN_STATUS_BUILTIN;
switch($GLOBALS['ZI']['package_action']){
case 'remove':
unlink($GLOBALS['ZI']['file_dest']);
$GLOBALS['ZI']['plugin_log'] .= 'deleted '.$GLOBALS['ZI']['file_dest'];
break;
case 'update':
case 'install':
if(!is_dir(dirname($GLOBALS['ZI']['file_dest']))){
ZZOSS_InstallerUtils::mkDir(dirname($GLOBALS['ZI']['file_dest']));
$GLOBALS['ZI']['plugin_log'] .= 'Created directory '.dirname($GLOBALS['ZI']['file_dest'])."\n";
}
if(substr(PHP_OS, 0, 3) == 'WIN'){
// Windows case insensitivity problem:
// We get a probem with files that have the same name as an
// already existing directory, copied to the same root
// directory where the already existing dir is located
// (e.g. dir 'PEAR', file 'pear').
if(is_dir($GLOBALS['ZI']['file_dest'])){
if(!$this->is_debug){
// don't procede
break;
} else {
trigger_error(
"Windows case insensitivity problem: ".
"The file '".$GLOBALS['ZI']['file_dest']."' conflicts with already existing directory '".$GLOBALS['ZI']['file_dest']."'. ".
"We get a probem with files that have the same name as an ".
"already existing directory, copied to the same root ".
"directory where the already existing dir is located. "
);
}
}
}
//ZZOSS_InstallerUtils::mkdir($pathinfo["dirname"]);
//$log .= 'src: '.$src. 'dest: '.$dest."\n";
//echo 'src: '.$GLOBALS['ZI']['file_src']. 'dest: '.$GLOBALS['ZI']['file_dest']."\n<br>";
if(!copy($GLOBALS['ZI']['file_src'], $GLOBALS['ZI']['file_dest'])) {
$GLOBALS['ZI']['plugin_log'] .=
'Cannot copy '.$GLOBALS['ZI']['file_src']."\n".
' to '.$GLOBALS['ZI']['file_dest']."\n";
} else {
$GLOBALS['ZI']['plugin_log'] .=
'Copied '.$GLOBALS['ZI']['file_src']."\n".
' to '.$GLOBALS['ZI']['file_dest']."\n";
}
break;
}
$this->showPluginMessage();
$this->output .= ob_get_contents();
ob_end_clean();
return $status;
}
function includePlugins($plugins, $build = null)
{
$status = ZZOSS_PLUGIN_STATUS_UNDEFINED;
ob_start();
$this->log .= "* Processing plugins.\n";
/*
reset($this->sources);*/
if(is_array($plugins)){
foreach($plugins as $name => $params){/*
// Iterate available sources
if(!count($this->sources)){
PEAR::raiseError('No plugin directory defined.');
} else {*/
if(!$build || ($name == $build["@"]["plugin"].'-'.$build["@"]["version"])) {
$status = $this->includePlugin($name, $params['file']);
}
/*}
*/}
}
/*
echo '<pre>';
echo $this->log;
print_r($this->plugins);
echo '<pre>';
die();
*/
$this->output .= ob_get_contents();
ob_end_clean();
return $status;
}
function includePlugin($name, $include)
{
$status = NULL;
$GLOBALS['ZI']['plugin_log'] = '';
$include = ZZOSS_InstallerUtils::fixPath($include);
$this->log .= "* Trying to include '".$include."'\n";
if(file_exists($include)){
// START Plugins Runtime Environment
// remember include path
$inc_path_old = ini_get('include_path');
// set include path to allow only relative includes,
// which will immediately throw an error if a plugin
// relies on a class external to the plugins runtime
// environment (e.g. a class provided by the installer).
ini_set('include_path', '.');
// set global vars ($GLOBALS['ZI'])
$this->initGlobalVariables();
//print_r(get_included_files());
// include plugin
include($include);
// reset include path
ini_set('include_path', $inc_path_old);
// END Plugins Runtime Environment
$status = ZZOSS_PLUGIN_STATUS_INCLUDED;
}
if($status == ZZOSS_PLUGIN_STATUS_UNDEFINED){
$this->log .= "* Plugin '$name': Could not find '".$include."' in specified sources.\n";
} elseif($status == ZZOSS_PLUGIN_STATUS_INCLUDED) {
$this->log .= "* Including $include\n";
$this->showPluginMessage();
}
return $status;
}
function showPluginMessage()
{
if(strlen($GLOBALS['ZI']['plugin_log'])){
$this->log .= "--- Plugin Message ---\n";
$this->log .= $GLOBALS['ZI']['plugin_log'];
$this->log .= "-----------------------\n";
}
}
function initApplicationSettings()
{
if(!is_array($this->application_settings)){
$this->application_settings = $this->registry->getApplicationSettings();
}
}
function getOutput()
{
return $this->output;
}
function getLog()
{
return $this->log;
}
function execBuild()
{
// iterate builds
if(!isset($this->package['release']['builds']['build'])){
$this->log .= "No build definition.\n";
} else {
$pkg_builds = $this->package['release']['builds']['build'];
foreach($pkg_builds as $build) {
$this->build = $build;
// execute plugins
$this->log .= "* Dealing with action '".$this->action."'\n";
if(!$this->includePlugins($this->plugins[$this->action], $build)){
$this->log .= "* ERROR: A handler for this role does not exist.\n";
}
}
}
}
function isStep($step)
{
if(!isset($this->queue[$step])){
return false;
} else {
return is_array($this->queue[$step]);
}
}
function registerPlugins()
{
// register new plugins of package
$files = array();
// This is here to be downward compatible
if(isset($this->package["release"]["plugin"]["filelist"]["file"])){
$files = $this->package["release"]["plugin"]["filelist"]["file"];
} elseif(isset($this->package["release"]["plugin"]["file"])) {
$files = $this->package["release"]["plugin"]["file"];
}
if(count($files)) {
$plugin_param = array();
$plugins_params = array();
$plugins_params['add'] = NULL;
$plugins_params['remove'] = NULL;
//$plugins_install['default'][$this->package['name'].'-'.$this->package['release']['version']] = strlen($this->package["release"]["plugin"]['@']['default']);
foreach($files as $file) {
if(!isset($this->file_baseinstalldir[$file["@"]["name"]])){
$file["@"]["baseinstalldir"] = '';
} else {
$file["@"]["baseinstalldir"] = $this->file_baseinstalldir[$file["@"]["name"]];
}
// remember parameters to later register plugin files
$file_path = ZZOSS_InstallerUtils::fixPath($this->dest_dir.DIRECTORY_SEPARATOR.$this->pkg_root.$file["@"]["baseinstalldir"].DIRECTORY_SEPARATOR.$file["@"]["name"]);
$plugin_params[$this->package['name'].'-'.$this->package['release']['version']]['file'] = $file_path;
$plugin_params[$this->package['name'].'-'.$this->package['release']['version']]['name'] = $this->package['name'];
if(!isset($file['@']['role'])){
$role = '';
$plugins_params[$this->plugin_action]['_undefined'] = $plugin_params;
} else if(!isset($file['@']['attr'])){
$role = $file['@']['role'];
$plugins_params[$this->plugin_action][$file['@']['role']] = $plugin_params;
} else {
$role = $file['@']['attr'];
$plugins_params[$this->plugin_action][$file['@']['role']][$file["@"]["attr"]][$file["@"]["val"]] = $plugin_params;
}
// Log info
$this->log .= $this->plugin_action." plugin file ".$file_path." with role '".$role."'\n";
}
$this->plugins = $this->registry->composePlugins($plugins_params['add'], $plugins_params['remove'], $this->plugins);
}
}
function initGlobalVariables()
{
$this->log .= "* Initializing global plugin variables.\n";
$this->initApplicationSettings();
// application
$GLOBALS['ZI']['application_root'] =
$GLOBALS['ZI']['application_baseinstalldir'] =
$this->application_settings['application_root'];
$GLOBALS['ZI']['application_data_dir'] = $this->registry->getApplicationPath();
$GLOBALS['ZI']['application_data_dir_rel'] = $this->registry->getApplicationPathRel();
$GLOBALS['ZI']['application_url_rel'] = $this->application_settings['application_url_rel'];
$GLOBALS['ZI']['application_url'] = $this->application_settings['application_url'];
$GLOBALS['ZI']['application_demo'] = (isset($this->application_settings['application_demo']) && $this->application_settings['application_demo'] == 'on');
$GLOBALS['ZI']['application_profile'] = $this->application_settings['application_profile'];
// package
$directory =
$GLOBALS['ZI']['package_root'] =
$GLOBALS['ZI']['package_baseinstalldir'] =
ZZOSS_InstallerUtils::fixPath($this->dest_dir.$this->pkg_root);
$GLOBALS['ZI']['package_src'] =
ZZOSS_InstallerUtils::fixPath($this->package["install"]["path"].DIRECTORY_SEPARATOR);
$GLOBALS['ZI']['package_build_dir'] = $GLOBALS['ZI']['package_src'].$this->build['@']['dir'];
$GLOBALS['ZI']['package_action'] = $this->action;
$GLOBALS['ZI']['package_name'] = $this->package['name'];
$GLOBALS['ZI']['package_summary'] = $this->package['summary'];
$GLOBALS['ZI']['package_version'] = $this->package['release']['version'];
$GLOBALS['ZI']['plugin_mode'] = $this->mode;
$GLOBALS['ZI']['plugin_data_dir'] = $this->data_dir.DIRECTORY_SEPARATOR;
$GLOBALS['ZI']['plugins_re'] = $this->registry->getApplicationPath().'plugins_re'.DIRECTORY_SEPARATOR;
// GUI
$GLOBALS['ZI']['gui_submitted'] = (isset($_REQUEST['ZI_BUTTON_NEXT']) || isset($_REQUEST['ZI_BUTTON_NEXT_x']));
// compose file params only if we are in role "role"
switch($this->role){
case 'role':
$this->initGlobalVariablesRole();
break;
case 'build':
$this->initGlobalVariablesBuild();
break;
}
}
function initGlobalVariablesRole()
{
if(!isset($this->file['@']["baseinstalldir"])){
$this->file['@']["baseinstalldir"] = '';
}
$path = ZZOSS_InstallerUtils::fixPath($GLOBALS['ZI']['package_baseinstalldir'].$this->file['@']["baseinstalldir"].DIRECTORY_SEPARATOR.$this->file['@']["name"]);
$pathinfo = pathinfo($path);
$GLOBALS['ZI']['file_name'] = $this->file['@']["name"];
/*$GLOBALS['ZI']['file_root'] =*/
$GLOBALS['ZI']['file_baseinstalldir'] =
ZZOSS_InstallerUtils::fixPath($this->file['@']["baseinstalldir"].DIRECTORY_SEPARATOR);
$GLOBALS['ZI']['file_src'] = ZZOSS_InstallerUtils::fixPath($GLOBALS['ZI']['package_src'].$this->file['@']['name']);
$GLOBALS['ZI']['file_name_dest'] = $this->file['@']["name"];
if(isset($this->file['@']["save-as"])){
$GLOBALS['ZI']['file_name_dest'] = $this->file['@']["save-as"];
}
if(isset($this->file['@']["install-as"])){
$GLOBALS['ZI']['file_name_dest'] = $this->file['@']["install-as"];
}
$GLOBALS['ZI']['file_dest'] = ZZOSS_InstallerUtils::fixPath($GLOBALS['ZI']['package_baseinstalldir'].$this->file['@']["baseinstalldir"].DIRECTORY_SEPARATOR.$GLOBALS['ZI']['file_name_dest']);
if(isset($this->file['@']['role'])){
$GLOBALS['ZI']['file_role'] = $this->file['@']['role'];
}
}
function initGlobalVariablesBuild()
{
$GLOBALS['ZI']['build_dir'] =
$GLOBALS['ZI']['plugin_root'] =
$GLOBALS['ZI']['plugin_baseinstalldir'] =
$this->package['install']['path'].$this->build["@"]["dir"];
if(isset($this->build['params'])){
$GLOBALS['ZI']['build_params'] =
$GLOBALS['ZI']['plugin_params'] =
$this->build['params'];
}
}
}
?>