/** * Checks to see if the locales for a given module are up to date * @param string $shortname The module * @param string $file -- defaults to null which means use the currently loaded file location for the config file * @returns booolean. True if all is up to date. */ public function checkLocalesUptoDate($shortname, $file = null) { if ($file == null) { if (!$this->config->setIfIsSet($file, "data/{$shortname}/file")) { return false; // the module has never been loaded } $r_file = I2CE_FileSearch::realPath($file); if (!file_exists($r_file) || !is_readable($r_file)) { return false; // the friggin file does not even exist. why are you asking me? } } else { $r_file = I2CE_FileSearch::realPath($file); if (!$r_file) { return false; //invalid file was set. } if (!$this->config->setIfIsSet($saved_file, "data/{$shortname}/file")) { return false; // the module has never been loaded } if (I2CE_FileSearch::realPath($saved_file) != $r_file) { //the loaded config file is different than the one we are looking at return false; } } $locales = I2CE_Locales::getAvailableLocales(); $dirs = array(); if (!$this->config->setIfIsSet($dirs, "data/{$shortname}/paths/CONFIGS", true)) { return true; } $localized = array(); $this->config->setIfIsSet($localized, "status/localized/{$shortname}", true); $basename = basename($r_file); $dirname = dirname($r_file); //I2CE::raiseError("Checking $shortname in " . implode(',',$dirs) . " for locales " . implode(',',$locales) ); foreach ($dirs as $dir) { foreach ($locales as $locale) { if (I2CE_FileSearch::isAbsolut($dir)) { //the config path in theconfig file is absolut. $t_file = $dir . DIRECTORY_SEPARATOR . $locale . DIRECTORY_SEPARATOR . $basename; $localized_file = I2CE_FileSearch::realPath($t_file); $dir = I2CE_FileSearch::relativePath($dir); } else { //the config path in theconfig file is relative to the module file $t_file = $dirname . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR . $locale . DIRECTORY_SEPARATOR . $basename; $localized_file = I2CE_FileSearch::realPath($t_file); } if (!$localized_file || !is_file($localized_file) || !is_readable($localized_file)) { continue; } if (!array_key_exists($locale, $localized) || !is_array($localized[$locale])) { I2CE::raiseError("Localization data {$locale} for module {$shortname} has never been loaded"); return false; //we have never examined this one before } foreach (array('file', 'mtime', 'hash') as $key) { if (!array_key_exists($key, $localized[$locale])) { return false; } } //we have examined this one before. $l_file = I2CE_FileSearch::realPath($localized[$locale]['file']); if ($localized_file !== $l_file) { return false; } @($mtime = filemtime($localized_file)); if (!$mtime || !$localized[$locale]['mtime']) { return false; } if ($mtime > $localized[$locale]['mtime']) { return false; } $contents = file_get_contents($localized_file); if (!$contents) { return false; } if ($localized[$locale]['hash'] != md5($contents)) { return false; } } } return true; }
protected static function _updateModules($updates, $removals = array(), $optional_excludes = array(), $disables = array()) { I2CE::raiseError("Updating Modules"); //make sure everything is nice and fresh clearstatcache(); $mod_factory = I2CE_ModuleFactory::instance(); $exec = array('max_execution_time' => 20 * 60, 'memory_limit' => 256 * 1048576); I2CE::longExecution($exec); if (!is_array($updates)) { $updates = array($updates); } if (!is_array($removals)) { $removals = array($removals); } $msg = "Will attempt to update:\n"; foreach (array('Updates' => $updates, 'Removals' => $removals, 'Disables' => $disables) as $k => $v) { if (count($v) > 0) { $msg .= "\t{$k}:\n\t\t" . implode(',', $v) . "\n"; } } I2CE::raiseError($msg); $storage = I2CE::getConfig(); $tmp_storage = I2CE_MagicData::instance("temp_ModuleFactory"); $configurator = new I2CE_Configurator($tmp_storage); if ($storage->setIfIsSet($sitemodule, "config/site/module")) { I2CE::raiseError("Site is set at " . $storage->getPath() . ' to be ' . $sitemodule); //make sure the site direcotry is added in to the config path. $data = $configurator->checkRequirements($updates, $disables, $removals, $mod_factory->getEnabled(), $sitemodule); } else { I2CE::raiseError("Site is not set at " . $storage->getPath()); $data = $configurator->checkRequirements($updates, $disables, $removals, $mod_factory->getEnabled()); } //note that checkRequirements has the result of putting _all_ valid config module metadata under /config/data of $tmp_storage if (isset($data['failure'])) { $storage->clearCache(); I2CE::raiseError("Installation failed: " . $data['failure']); return false; } foreach (array_keys($data['removals']) as $shortname) { if (!$mod_factory->disable($shortname)) { $storage->clearCache(); I2CE::raiseError("Unable to disable {$shortname}", E_USER_NOTICE); return false; } } //now we remove from the requirements list anything that is already enabled and up-to-date if (count($data['moved']) > 0) { I2CE::raiseError("Found the following in another location. Attempting to move:" . implode(',', array_keys($data['moved']))); I2CE::setupFileSearch(array(), true); //reset the file search and clear its cache I2CE::getFileSearch()->addPath('MODULES', dirname(dirname(__FILE__)), 'EVEN_HIGHER'); } $skipped = array(); $moved = array(); foreach ($data['requirements'] as $shortname => $file) { if (!$mod_factory->isEnabled($shortname)) { continue; } if ($mod_factory->isUpToDate($shortname, $file) && $mod_factory->isUpToDateModule($shortname)) { //everything is in the correct place and up to date. $skipped[] = $shortname; $mod_factory->loadPaths($shortname, null, true); //for the loading of all categories for this module $storage->config->data->{$shortname}->file = $data['requirements'][$shortname]; I2CE::raiseError("Updated {$shortname} config file to be " . $data['requirements'][$shortname]); unset($data['requirements'][$shortname]); //this module is enabled and the config is up-to-date so we dont need to do anything continue; } //let us see if this module has been moved if (!$storage->__isset("config/data/{$shortname}")) { continue; } if (!$tmp_storage->__isset("config/data/{$shortname}")) { continue; } $meta = $storage->config->data->{$shortname}; $tmp_meta = $tmp_storage->config->data->{$shortname}; foreach (array("hash", "last_access") as $key) { if (!isset($meta->{$key}) || !isset($tmp_meta->{$key}) || $tmp_meta->{$key} !== $meta->{$key}) { continue 2; } } $class_file = null; $tmp_meta->setIfIsSet($class_file, "class/file"); if ($class_file && ($class_file = I2CE_FileSearch::realPath($class_file))) { if (!$meta->__isset("class/hash")) { continue; } if (!is_readable($class_file)) { continue; } $contents = file_get_contents($class_file); if (!$contents) { continue; } if ($meta->class->hash !== md5($contents)) { continue; } } $mtimes = array(); foreach (array("class/file", "file") as $f) { if (!isset($tmp_meta->{$f})) { continue; } @($mtimes[$f] = filemtime(I2CE_FileSearch::realPath($tmp_meta->{$f}))); if (!$mtimes[$f]) { continue 2; } } if (false == $mod_factory->checkLocalesUpToDate($shortname, $class_file)) { //the locales for this module are not up to date continue; } //we made it here. we can skip the update. I2CE::raiseError("Able to move config file for {$shortname} from:\n " . $meta->file . "\nto:\n " . $tmp_meta->file); foreach (array("class/file" => "class/last_access", "file" => "last_access") as $f => $a) { $val = null; $tmp_meta->setIfIsSet($val, $f); if ($val === null) { continue; } $meta->{$f} = $val; $meta->{$a} = $mtimes[$f]; $tmp_meta->{$a} = $mtimes[$f]; } unset($data['requirements'][$shortname]); //this module is enabled and the config is up-to-date so we dont need to do anything $mod_factory->loadPaths($shortname, null, true); //for the loading of all categories for this module $moved[] = $shortname; } if (count($skipped) > 0) { I2CE::raiseError("Skipping update on the following up-to-date modules:" . implode(',', $skipped)); } if (count($moved) > 0) { I2CE::raiseError("Moved the following modules:" . implode(',', $moved)); } I2CE::raiseError("Attempting to update/enable the following out of date modules: " . implode(',', array_keys($data['requirements']))); //make sure all of our class paths for existing moduels are loaded. $good_modules = array_diff($mod_factory->getEnabled(), $data['removals'], array_keys($data['requirements'])); I2CE::raiseError("The following modules class paths are being added:\n\t" . implode(',', $good_modules)); $mod_factory->loadPaths($good_modules, 'CLASSES', true); if (!array_key_exists('optional', $data) || !is_array($data['optional'])) { $data['optional'] = array(); } if (is_string($optional_excludes)) { $optional_excludes = array($optional_excludes); } if (!is_array($optional_excludes)) { $optional_excludes = array(); } $to_enable = array_merge($data['requirements'], $data['optional']); //while (count ($data['requirements']) > 0) { I2CE::raiseError("Trying to enable the following required:\n" . implode(" ", array_keys($data['requirements']))); I2CE::raiseError("Trying to enable the following optional:\n" . implode(" ", array_keys($data['optional']))); I2CE::raiseError("Trying to enable the following:\n" . implode(" ", array_keys($to_enable))); while (count($to_enable) > 0) { $shortname = key($to_enable); // reset ($data['requirements']); // $shortname = key($data['requirements']); if (!is_string($shortname) || strlen($shortname) == 0) { I2CE::raiseError("Invalid Shortname"); continue; } $file = array_shift($to_enable); if (array_key_exists($shortname, $data['optional']) && in_array($shortname, $optional_excludes)) { continue; } $old_vers = '0'; $storage->setIfIsSet($old_vers, "/config/data/{$shortname}/version"); $new_vers = null; $tmp_storage->setIfIsSet($new_vers, "/config/data/{$shortname}/version"); $mod_config = $tmp_storage->config->data->{$shortname}; $storage->__unset("/config/data/{$shortname}"); //set the module's metadata to the new stuff. $storage->config->data->{$shortname} = $mod_config; //keep the old version set around until we know that the module was upgraded $storage->config->data->{$shortname}->version = $old_vers; if (!$tmp_storage->__isset("/config/data/{$shortname}/class/name")) { //there is no class associated in the new version of this module. if ($storage->__isset("/config/data/{$shortname}/class/name")) { //there was a class previously assoicated to this module -- remove its hooks/fuzzy methods, $mod_factory->removeHooks($shortname); unset($storage->config->data->{$shortname}->class); } } foreach (array('conflict' => 'conflict_external', 'requirement' => 'requirement_external') as $type => $key) { if ($mod_config->is_parent($key)) { foreach ($mod_config->{$key} as $ext => $req_data) { if ($req_data instanceof I2CE_MagicDataNode) { $req_data = $req_data->getAsArray(); } else { $req_data = array(); } foreach ($req_data as $req_d) { if (!is_array($req_d) || !array_key_exists('eval', $req_d) || !$req_d['eval']) { continue; } $eval = null; @eval('$eval = ' . $req_d['eval'] . ';'); if (is_bool($eval) && !$eval) { if (self::failedRequiredUpdate($shortname, $data, "Could not verify external {$type} {$ext} for {$shortname}", $configurator)) { return false; } else { continue 4; } } } } } } $mod_storage = I2CE_MagicData::instance("temp_ModuleFactory_" . $shortname); I2CE::getFileSearch()->addPath('MODULES', dirname(dirname(__FILE__)), 'EVEN_HIGHER'); $r_file = I2CE_FileSearch::realPath($file); $mod_configurator = new I2CE_Configurator($mod_storage); $s = $mod_configurator->processConfigFile($r_file, false, true, true, false); if (!is_string($s)) { if (self::failedRequiredUpdate($shortname, $data, "Could load configuration file", $configurator)) { return false; } else { continue; } } if ($s != $shortname) { //be super safe if (self::failedRequiredUpdate($shortname, $data, "Configuration shortname mismatch ({$s}/{$shortname})", $configurator)) { return false; } else { continue; } } self::processErasers($mod_config, $old_vers); $loaded = self::loadModuleMagicData($shortname, $r_file, $old_vers, $new_vers, $mod_configurator); if ($loaded === false) { if (self::failedRequiredUpdate($shortname, $data, "Could not load magic data", $configurator)) { return false; } else { continue; } } $loaded_mod_config = $mod_storage->config->data->{$shortname}; self::processErasers($loaded_mod_config, $old_vers); if (!self::preUpgradeModule($shortname, $old_vers, $new_vers, $mod_storage)) { if (self::failedRequiredUpdate($shortname, $data, "Could not pre-update module", $configurator)) { return false; } else { continue; } } //if $loaded === true, then there was no magic data to update, so we can skip the store. if (is_array($loaded) && !self::storeModuleMagicData($shortname, $old_vers, $new_vers, $mod_configurator, $loaded)) { if (self::failedRequiredUpdate($shortname, $data, "Could not store magic data", $configurator)) { return false; } else { continue; } } if (!self::upgradeModule($shortname, $old_vers, $new_vers)) { if (self::failedRequiredUpdate($shortname, $data, "Could not upgrade module", $configurator)) { return false; } else { continue; } } if (!self::postUpdateModule($shortname, $old_vers, $new_vers)) { if (self::failedRequiredUpdate($shortname, $data, "Could not post update module", $configurator)) { return false; } else { continue; } } $mod_factory->setModuleHash($shortname); $mod_factory->setModuleClassHash($shortname, false); $mod_configurator->__destruct(); $mod_configurator = null; $mod_storage->erase(); $mod_storage = null; $storage = I2CE::getConfig(); //just to make sure that any upgrades did not change the storage. this happens with i2ce install for example $storage->config->data->{$shortname}->version = $new_vers; //we updated this module. update the permanent modules config data with the temporary $mod_factory->loadPaths($shortname, null, true); //for the loading of all categories for this module } I2CE::raiseError("Enabled Modules: " . implode(',', $mod_factory->getEnabled())); return true; }
public function importLocalizedTemplates($localized = array()) { $imported = array(); $i2ceConfigNodeList = $this->template->query('/I2CEConfiguration'); if ($i2ceConfigNodeList->length != 1) { I2CE::raiseError("No configuration template loaded"); return $imported; } else { $configNode = $i2ceConfigNodeList->item(0); } $shortname = trim($configNode->getAttribute('name')); if (!$shortname) { I2CE::raiseError("Could not find module name"); return $imported; } $config = $this->storage->config->data->{$shortname}; $file = false; $config->setIfIsSet($file, "file"); if (!$file) { I2CE::raiseError("Could not source file"); return $imported; } $dirs = array(); $basedir = dirname($file); $basename = basename($file); $config->setIfIsSet($dirs, "paths/CONFIGS", true); if (!is_array($localized)) { $localized = array(); } foreach ($dirs as $dir) { foreach ($this->locales as $locale) { if (array_key_exists($locale, $imported)) { continue; } if (I2CE_FileSearch::isAbsolut($dir)) { //the config path in theconfig file is absolut. $localized_file = I2CE_FileSearch::realPath($dir . DIRECTORY_SEPARATOR . $locale . DIRECTORY_SEPARATOR . $basename); $dir = I2CE_FileSearch::relativePath($dir); } else { //the config path in theconfig file is relative to the module file $localized_file = I2CE_FileSearch::realPath($basedir . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR . $locale . DIRECTORY_SEPARATOR . $basename); } if (!$localized_file || !is_file($localized_file) || !is_readable($localized_file)) { continue; } I2CE::raiseError("Loading {$localized_file}"); $loc_template = new I2CE_MagicDataTemplate(); $loc_template->loadRootFile($localized_file); $loc_node = $loc_template->doc->documentElement; if (!$loc_node instanceof DOMNODE) { continue; } $results = $loc_template->query('./configurationGroup', $loc_node); $localenode = null; if ($results->length == 1) { $localenode = $results->item(0); if ($localenode->getAttribute('locale') !== $locale) { I2CE::raiseError("Locale mismatch on {$localized_file}"); continue; } } $results = $loc_template->query('./metadata/version', $loc_node); if ($results->length != 1) { I2CE::raiseError("No version on {$localized_file}"); continue; } $new_vers = trim($results->item(0)->textContent); if (!array_key_exists($locale, $localized) || !is_array($localized[$locale]) || !array_key_exists('vers', $localized[$locale]) || !is_string($localized[$locale]['vers']) || strlen($localized[$locale]['vers']) == 0) { $old_vers = '0'; } else { $old_vers = $localized[$locale]['vers']; } if ($localenode) { $localenode_imported = $this->template->doc->importNode($localenode, true); I2CE::raiseError("Adding in localizations of {$shortname} in {$locale} to process"); $configNode->appendChild($localenode_imported); } $data = array('file' => I2CE_FileSearch::relativePath($localized_file), 'mtime' => @filemtime($localized_file), 'hash' => md5(file_get_contents($localized_file)), 'old_vers' => $old_vers, 'vers' => $new_vers); $imported[$locale] = $data; } } if (count($imported) > 0) { I2CE::raiseError("Found localized config files for " . $shortname . ": " . implode(',', array_keys($imported))); } return $imported; }
/** * Get the system status. http://www.ursula1000.com/ * @returns string 'gogo' means we are good. 'needs_installation' means we need to initialize. 'needs_upgrade' */ public static function allSystemsAreGoGo($site_module_file, $check_time = false) { $site_module_file = I2CE_FileSearch::realPath($site_module_file); $config = self::getConfig(); $site_module = ''; $config->setIfIsSet($site_module, '/config/site/module'); $mod_factory = I2CE_ModuleFactory::instance(); $installed = ''; $config->setIfIsSet($installed, "/config/site/installation"); if ($installed == '') { return 'needs_install'; } if (!$site_module) { self::raiseError("Cannot determine what your site is. This is bad.\n({$site_module_file})", E_USER_ERROR); return 'no_site'; } if (substr($installed, 0, 11) == 'in_progress') { if (array_key_exists('HTTP_HOST', $_SERVER)) { self::raiseError("Warning: Trying to access the site while update is in progress. Do you know what you are doing?"); } else { self::raiseError("Warning: Trying to access the site while update is in progress. Run --update=1 --force-restart=1 to restart"); } return $installed; } else { if ($installed == 'done') { $previous_site_file = ''; if ($config->setIfIsSet($previous_site_file, '/config/data/' . $site_module . '/file')) { $previous_site_file = I2CE_FileSearch::realPath($previous_site_file); if ($previous_site_file != $site_module_file) { I2CE::raiseError("Need reinstall ({$previous_site_file}) != ({$site_module_file}) for site module {$site_module}"); return 'needs_reinstall'; } } $mod_factory = I2CE_ModuleFactory::instance(); if (!$mod_factory->isEnabled($site_module)) { return 'needs_reenable'; } //$installed == 'done' and the site module/site module file are good. This should be the 'usual state of affairs' $times = array(); $config->setIfIsSet($times, '/I2CE/update/times', true); if (!isset($times['stale'])) { $times['stale'] = 10; } if (!$check_time) { if ($times['stale'] < 0 || !isset($times['last'])) { $check_time = true; } else { if (isset($times['last'])) { $check_time = time() - $times['last'] > $times['stale']; } } } if ($check_time) { I2CE::longExecution(null, false); //we are due to check the config files/modules for updates $config->__set("/I2CE/update/times/last", time()); $updates = $mod_factory->getOutOfDateConfigFiles(); $config->__set("/I2CE/update/times/last", time()); if (count($updates['updates']) + count($updates['removals']) > 0) { return 'needs_upgrade'; } } return 'done'; } else { self::raiseError("Unknown installation status:" . $installed, E_USER_ERROR); return 'unknown'; } } }
/** * @param mixed $obj. The calling object * @param string $script The php script * @param mixed $cl_args. String or array of string, the command line arguments. Defualts to empty arra. * @param string $wd. Workking directory. Default to null */ public static function launchBackgroundPHPScript($obj, $script, $cl_args = array(), $wd = null) { if (strlen($script) == 0) { I2CE::raiseError("No background script provided"); return false; } $script = escapeshellarg($script); $php = 'php'; if (!I2CE_FileSearch::isUnixy()) { I2CE::getConfig()->setIfIsSet($php, "/modules/BackgroundProcess/php_executable/windows"); } else { I2CE::getConfig()->setIfIsSet($php, "/modules/BackgroundProcess/php_executable/unix"); } $php = I2CE_FileSearch::realPath($php); if (!is_executable($php) && !array_key_exists('HTTP_HOST', $_SERVER) && array_key_exists('_', $_SERVER)) { //this is CLI -- try using the current running interpreter. $php = $_SERVER['_']; //this is the php executable from the CLI } $php = escapeshellarg($php); self::launchBackgroundProcess($obj, "{$php} {$script}", $cl_args, $wd); }