public function testBrokenErase() { $a = self::$c->getAvailableModules(array(self::data_dir(__CLASS__) . "/OneModuleWithClass")); TestMagicDataTearDown::brokenTearDown(); I2CE::resetFileSearch(); $this->assertEquals(array(), self::$not_errors); $this->assertEquals(array(), self::$errors); $this->setUp(); $a = self::$c->getAvailableModules(array(self::data_dir(__CLASS__) . "/OneModuleWithClass")); $b = array(); $this->assertEquals($b, $a); $this->assertRegExp("{Trying to get property of non-object}", self::$not_errors[0]); $this->assertContains("Got a NULL value for storage.", self::$errors); self::$errors = array(); TestMagicDataTearDown::tearDown(); I2CE::resetFileSearch(); $this->assertEquals(array(), self::$errors); }
function tearDown() { I2CE_MagicData::tearDown(); I2CE::resetFileSearch(); }
protected function tearDown() { I2CE_MagicData::tearDown(); I2CE::resetFileSearch(); I2CE::popErrorHandler(array(__CLASS__, "errorHandler")); }
/** * Checks the requirements. if all requirements are met and * there are no conflicts, it returns a list of those that * need to be installed, an empty array() if none are needed. * On failure returns null. * * @param array $shortnames an array of shortnames * * @param mixed $disables a shortname or an array of * shortnames of modules to disable. Defaults to empty array * * @param array $remove an array of potential shortnames to * remove -- if we did not find them somplace else. (it may * have just been moved) defaults to an empty array. * * @param array $enabled_modules An array of shortnames of * enabled modules -- we check for conflicts against this * array * * @param mixed $required_modules A shortname or an array of * shortnames of required modules -- if a module is set to be * updated and it is not required by on of the modules in this * array, it will be set as an optional modules. Defaults to * the empty array * * @returns array. Keys are: * 'failure' - string with the reason for failture * 'requirements' - array which has as keys shortnames and * values files for the requirements. * 'removals' - array of shortnames that need to be removed. * 'optional' - array with key shortnames and value true * 'moved' - array with key shortnames and value true */ public function checkRequirements($updates, $disables = array(), $removals = array(), $enabled_modules = array(), $required_modules = array(), $reset_moved = true) { if ($reset_moved) { $this->moved = array(); } if ($this->storage->setIfIsSet($i2ce_config_file, "config/data/I2CE/file") && $i2ce_config_file != ($new_i2ce_config_file = rtrim(dirname(dirname(__FILE__)), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'I2CE_Configuration.xml')) { //we have moved location of I2CE. Other things have likely gotten moved around as well and we do not want to use the stored paths. //the file search has been set in I2CE_Update->_updateSite()->needs_upgrade to I2CE::setupFileSearch( array( "CLASSES" => "./", 'MODULES'=>array(dirname($site_module_file),dirname(dirname(__FILE__)))), true); $this->resetCheckedPaths(); I2CE::resetFileSearch(); $file_search = I2CE::getFileSearch(); if (null === $file_search) { I2CE::raiseError("Resetting file search"); I2CE::setupFileSearch(); $file_search = I2CE::getFileSearch(); } // just to make sure all of our module paths are loaded. $mod_factory = I2CE_ModuleFactory::instance(); $mod_factory->resetStoredLoadedPaths(); $mod_factory->loadPaths($mod, $path, true, $file_search, $this->storage->config->data); } else { $mods = array_unique(array_merge(array('I2CE'), $enabled_modules)); $file_search = $this->setupFileSearch($mods); } $this->findAvailableConfigs($file_search, true); if (!$this->storage->is_parent("config/data")) { I2CE::raiseError("Data not loaded.", E_ERROR); return array('failure' => "Data not loaded"); } I2CE::raiseError("Done looking for available config files. Available:\n\t" . implode(',', $this->storage->config->data->getKeys())); $conflicts = array(); $available = $this->storage->config->data; if (!is_array($required_modules)) { $required_modules = array($required_modules); } foreach ($removals as $indx => $remove) { if (isset($available->{$remove})) { // I found the thing I thought I might need to remove // someplace else. unset($removals[$indx]); $this->moved[$remove] = true; $updates[] = $remove; } } foreach ($updates as $indx => $update) { // make sure our desired updates are really available if (!isset($available->{$update})) { I2CE::raiseError("Desired update {$update} cannot be found. Instead it is being removed"); I2CE::raiseError(implode(',', $available->getKeys())); $removals[] = $update; unset($updates[$indx]); } } // this now holds everything that needs to be disabled or removed $removals = array_merge($disables, $removals); $tmp_removals = array(); $avails = $available->getKeys(); while (count($removals) > 0) { $r = array_pop($removals); if (!is_scalar($r)) { I2CE::raiseError("Internal misconfiguration on removals", E_USER_ERROR); continue; } if (array_key_exists($r, $tmp_removals) && $tmp_removals[$r]) { continue; } /* * start removing anything that is required by this * guy. work your way up the requirement tree. we * will use the avialable rather than currently * installed information as we want the status at the * _end_ of the process to be correct. we are going to * make sure everything that could be possibly * installed is on the removal list. we will check * against this list as we process the depndencies for * updates and installs to make sure there are no * conflicts */ $t_removals = array($r); while (count($t_removals) > 0) { $remove = array_pop($t_removals); if (!is_string($remove)) { return array('failure' => "bad removals: " . print_r($remove, true)); } if (in_array($remove, $t_removals)) { return array('failure' => "Recursion in dependency " . "list for removal on {$remove}:\n" . implode(',', $t_removals)); } $tmp_removals[$remove] = true; foreach ($avails as $avail) { // should we check versions here? if (isset($available->{$avail}->requirement->{$remove})) { array_push($t_removals, $avail); } } } } $removals = $tmp_removals; //make sure our desired updates are clean of anything set to be removed foreach ($removals as $removal => $do_remove) { if (!is_string($removal) || !$do_remove) { continue; } if (array_key_exists($removal, $updates)) { unset($updates[$removal]); } } //now start working on the dependencies/requirements //updates is an array which holds the things we need to //install or update, but have not processed the requirements for $requirementsDOM = new DOMDocument('1.0', 'UTF-8'); $new_req = array(); //I2CE::raiseError("Making main dependecy tree for:\n\t" . implode(',',$updates)); foreach ($updates as $i => $update) { $optional = !$this->moduleRequires($update, $available->{$update}->version, $required_modules); // if ($optional) { // I2CE::raiseError("Optional $update"); // } else { // I2CE::raiseError("Not optional $update"); // } $t_requirementsDOM = new DOMDocument('1.0', 'UTF-8'); for ($i = 0; $i < $requirementsDOM->childNodes->length; $i++) { $child = $requirementsDOM->childNodes->item($i); $clone = $child->cloneNode(true); $clone = $t_requirementsDOM->importNode($clone, true); $t_requirementsDOM->appendChild($clone); } $new_req = $this->processRequirementsDOM($update, $t_requirementsDOM, $optional); if (is_string($new_req)) { if (!$optional) { return array('failure' => $new_req); } else { //we failed on this update. Try and recover. I2CE::raiseError("Unable to process optional module {$update}, skipping: {$new_req}"); unset($updates[$i]); continue; } } //if (count($new_req) > 0) { // if ($optional) { // I2CE::raiseError("Optional update $update adds the following additional required modules:\n\t" . implode(',',$new_req)); // } else { // I2CE::raiseError("Required update $update adds the following additional required modules:\n\t" . implode(',',$new_req)); // } //} if (!$optional) { foreach ($new_req as $req) { // check to see if this gal is on the removal list if (array_key_exists($req, $removals)) { return array('failure' => "Module {$req} is set to be removed but is also a requirement"); } } } //we are good to to $requirementsDOM = $t_requirementsDOM; } list($requirements, $optionals) = $this->flattenRequirements($requirementsDOM); //the only optional modules here are the ones that are marked to be updated, but not a part of the required modules. //this does not include, in general, all the optional modules that the site wants to enable. these are added below //I2CE::raiseError("Required modules are=" . implode(',',$requirements)); // now check the conflicts with what we desire to update/install and what is already installed. //I2CE::raiseError("Checking for conflicts in " . implode(" ", $requirements) . "\nagainst enabled: " . implode(" " , $enabled_modules)); $ret = $this->checkForConflicts($requirements, $enabled_modules); if (is_string($ret)) { I2CE::raiseError("Found conflict in: {$ret}\nEnabled: " . implode(" ", $enabled_modules) . "\nRequired by update:" . implode(" ", $requirements)); //OK. there is a conflict. Let see if the conflict is against an optional module. this is tedious/slow. //first we build a list of modules that are enabled bu not required. $modules_optional_enabled = array(); foreach ($enabled_modules as $e) { if ($this->moduleRequires($e, $available->{$e}->version, $required_modules)) { continue; } $modules_optional_enabled[] = $e; } if (count($modules_optional_enabled) == 0) { I2CE::raiseError("All currently enabled modules are required"); return array('failure' => $ret); } I2CE::raiseError("The following enabled modules are optional. Will try to disable some of them:\n" . implode(" ", $modules_optional_enabled)); $allowed_optional_enabled = array(); $optional_disables = array(); $non_optional_enabled = array_diff($enabled_modules, $modules_optional_enabled); while (count($modules_optional_enabled) > 0) { $optional_disable = array_pop($modules_optional_enabled); if (is_string($t_ret = $this->checkForConflicts($requirements, array_merge($non_optional_enabled, array($optional_disable))))) { //there is a conflict when trying to enable $optional_disable $optional_disables[] = $optional_disable; I2CE::raiseError("WARNING: The module {$optional_disable} is not allowed"); } else { I2CE::raiseError("The module {$optional_disable} is allowed"); } } I2CE::raiseError('Adding enabled optional modules to removal list: ' . implode(" ", $optional_disables)); $tmp_removals = array(); $avails = $available->getKeys(); while (count($optional_disables) > 0) { $r = array_pop($optional_disables); if (array_key_exists($r, $tmp_removals) && $tmp_removals[$r]) { continue; } /* * start removing anything that is required by this * guy. work your way up the requirement tree. we * will use the avialable rather than currently * installed information as we want the status at the * _end_ of the process to be correct. we are going to * make sure everything that could be possibly * installed is on the removal list. we will check * against this list as we process the depndencies for * updates and installs to make sure there are no * conflicts */ $t_removals = array($r); while (count($t_removals) > 0) { $remove = array_pop($t_removals); if (!is_string($remove)) { return array('failure' => "bad removals: " . print_r($remove, true)); } if (in_array($remove, $t_removals)) { return array('failure' => "Recursion in dependency " . "list for removal on {$remove}:\n" . implode(',', $t_removals)); } $tmp_removals[$remove] = true; foreach ($avails as $avail) { // should we check versions here? if (isset($available->{$avail}->requirement->{$remove})) { array_push($t_removals, $avail); } } } } $optional_disables = $tmp_removals; $xpath = new DOMXPath($requirementsDOM); foreach (array_keys($tmp_removals) as $r) { $this->removeRequirement($r, $requirementsDOM); } I2CE::raiseError('Adding enabled optional modules to removal list (with dependent modules): ' . implode(" ", array_keys($optional_disables))); $removals = array_merge($removals, $optional_disables); $enabled_modules = array_diff($enabled_modules, array_keys($optional_disables)); $requirements = array_diff($requirements, array_keys($optional_disables)); } // now we check to see if there are any modules that are requested to be enabled. $enable_list = array(); foreach (array_diff($requirements, array_keys($removals)) as $req) { $es = array(); $available->setIfIsSet($es, "{$req}/enable", true); foreach ($es as $e) { if (in_array($e, $requirements)) { continue; } $enable_list[$e] = true; } } $enable_list = array_keys($enable_list); if (count($enable_list) > 0) { //I2CE::raiseError("Processing main dependecy for optional modules:". implode(',',$enable_list)); while (count($enable_list) > 0) { $e = array_shift($enable_list); //I2CE::raiseError("Doing $e from enable list"); //I2CE::raiseError("Current optionals on $e=" . implode(" ", $optionals)); $t_requirementsDOM = new DOMDocument('1.0', 'UTF-8'); for ($i = 0; $i < $requirementsDOM->childNodes->length; $i++) { $child = $requirementsDOM->childNodes->item($i); $clone = $child->cloneNode(true); $clone = $t_requirementsDOM->importNode($clone, true); $t_requirementsDOM->appendChild($clone); } //try to see if we can add in $e as an optional module will cause a conflict. $ret = $this->processRequirementsDOM($e, $t_requirementsDOM, true); if (is_string($ret)) { // we don't really care if this fails but lets put // a notice anyways I2CE::raiseError("Internal Configuration Error for {$e} -- Could not enable requested optional module: {$ret}"); continue; } //now check that the new requirements added don't have conflicts with existing enabled modules, any requireed modules. skip the modules set to be removed $conflict = $this->checkForConflicts($requirements, array_merge(array($e), $ret, $enabled_modules)); //$conflict = $this->checkForConflicts(array_merge(array($e),$ret), array_diff(array_unique(array_merge($enabled_modules, $requirements)),array_keys($removals))); if (is_string($conflict)) { I2CE::raiseError("Could not enable {$e} as there was a conflict: {$conflict}"); //skip to the next enable request continue; } // check to see if any of the newly added modules are on the removal list foreach ($ret as $nreq) { if (array_key_exists($nreq, $removals)) { I2CE::raiseError("Could not enable module {$e} as it's " . "requirement {$nreq} is set to be removed"); continue 2; //skip to the next enable request } } //if we made it here, all was good. $requirementsDOM = $t_requirementsDOM; list($requirements, $optionals) = $this->flattenRequirements($t_requirementsDOM); I2CE::raiseError("Current requirements on {$e}=" . implode(" ", $requirements)); I2CE::raiseError("Current optionals on {$e}=" . implode(" ", $optionals)); foreach ($ret as $r) { $es = array(); $available->setIfIsSet($es, "{$r}/enable", true); foreach ($es as $e) { if (in_array($e, $requirements) || in_array($e, $enable_list)) { continue; } $enable_list[] = $e; } } } } // list out all the module requirements. separated by // optional and required $optional = array(); $xpath = new DOMXPath($requirementsDOM); $modules = $xpath->query("//module[@optional='1']"); for ($i = 0; $i < $modules->length; $i++) { $opt_file = false; $opt_mod = $modules->item($i)->getAttribute('name'); if (!$opt_mod) { continue; } if ($available->setIfIsSet($opt_file, "{$opt_mod}/file")) { $optional[$opt_mod] = $opt_file; } //$optional[ ] = $available->$req->file; } $reqs = array(); foreach ($requirements as $req) { $reqs[$req] = $available->{$req}->file; } //for any already //we need the optional module to be enabled in reverse order that they are in the dom. $optional = array_reverse($optional); //get the list of enabled and up to date modules. $ignore_modules = array_diff($enabled_modules, $updates); //I2CE::raiseError("Requirements:" . implode(" ", array_keys($reqs))); //I2CE::raiseError("Optional:" . implode(" ", array_keys($optional))); //I2CE::raiseError("The following modules are being ignored as they are up to date and enabled " . implode(" ", $ignore_modules)); foreach ($ignore_modules as $mod) { if (array_key_exists($mod, $optional)) { unset($optional[$mod]); } if (array_key_exists($mod, $reqs)) { unset($reqs[$mod]); } } //I2CE::raiseError("Needed Up Enable/Update Requirements:" . implode(" ", array_keys($reqs))); //I2CE::raiseError("Need to Enable/Update Optional:" . implode(" ", array_keys($optional))); return array('requirements' => $reqs, 'removals' => $removals, 'optional' => $optional, 'moved' => $this->moved); }