Merge remote-tracking branch 'origin/support/3.0' into develop

This commit is contained in:
Eric Espie
2022-03-03 10:23:38 +01:00
54 changed files with 631 additions and 901 deletions

View File

@@ -3542,12 +3542,22 @@ EOF;
/**
* Write a file only if not exists
* Also add some informations in case of a write failleure
* @param $sFilename
* @param $sContent
* Also add some informations when write failure occurs
*
* @param string $sFilename
* @param string $sContent
* @param int $flags
*
* @return bool|int
* @throws \Exception
*
* @uses \unlink()
* @uses \file_put_contents()
*
* @since 3.0.0 The file is removed before writing (commit c5d265f6)
* For now this causes model.*.php files to always be located in env-* dir, even if symlinks are enabled
* See N°4854
* @link https://www.itophub.io/wiki/page?id=3_0_0%3Arelease%3A3_0_whats_new#compiler_always_generate_new_model_php compiler behavior change documentation
*/
protected function WriteFile($sFilename, $sContent, $flags = null)
{

View File

@@ -17,8 +17,6 @@
* You should have received a copy of the GNU Affero General Public License
*/
use Combodo\iTop\DesignDocument;
/**
* Utility to upgrade the format of a given XML datamodel to the latest version
@@ -117,8 +115,6 @@ class iTopDesignFormat
*/
protected $aLog;
protected $bStatus;
protected $bKeepObsoleteNodes;
protected $sKeepVersion;
/**
* Creation from a loaded DOMDocument
@@ -220,20 +216,6 @@ class iTopDesignFormat
return self::GetItopNodePath($oNode->parentNode).'/'.$sNodeDesc;
}
/**
* Compute a real xpath from iTop one
*
* @param \Combodo\iTop\DesignElement $oNode
*
* @return string
*/
public static function GetNodeXPath($oNode)
{
$sITopXPath = DesignDocument::GetItopNodePath($oNode);
return preg_replace(["@\[@", "@]@"], ["[@id=\"", "\"]"], $sITopXPath);
}
/**
* Test the conversion without altering the DOM
*
@@ -251,21 +233,19 @@ class iTopDesignFormat
}
/**
* Make adjustments to the DOM to migrate it to the specified version (default is latest)
* Make adjustements to the DOM to migrate it to the specified version (default is latest)
* For now only the conversion from version 1.0 to 1.1 is supported.
*
* @param string $sTargetVersion The desired version (or the latest possible version if not specified)
* @param \ModelFactory|null $oFactory Full data model (not yet used, aimed at allowing conversion that could not be performed without knowing the
* @param object $oFactory Full data model (not yet used, aimed at allowing conversion that could not be performed without knowing the
* whole data model)
* @param bool $bKeepObsoleteNodes
*
* @return bool True on success, False if errors have been encountered (still the DOM may be altered!)
*/
public function Convert($sTargetVersion = ITOP_DESIGN_LATEST_VERSION, $oFactory = null, $bKeepObsoleteNodes = true)
public function Convert($sTargetVersion = ITOP_DESIGN_LATEST_VERSION, $oFactory = null)
{
$this->aLog = array();
$this->bStatus = true;
$this->bKeepObsoleteNodes = $bKeepObsoleteNodes;
$oXPath = new DOMXPath($this->oDocument);
// Retrieve the version number
@@ -328,7 +308,6 @@ class iTopDesignFormat
$sIntermediate = self::$aVersions[$sFrom]['next'];
$sTransform = self::$aVersions[$sFrom]['go_to_next'];
$this->LogInfo("Upgrading from $sFrom to $sIntermediate ($sTransform)");
$this->sKeepVersion = $sFrom;
}
else
{
@@ -336,16 +315,12 @@ class iTopDesignFormat
$sIntermediate = self::$aVersions[$sFrom]['previous'];
$sTransform = self::$aVersions[$sFrom]['go_to_previous'];
$this->LogInfo("Downgrading from $sFrom to $sIntermediate ($sTransform)");
$this->sKeepVersion = null;
}
// Transform to the intermediate format
$aCallSpec = array($this, $sTransform);
try
{
call_user_func($aCallSpec, $oFactory);
if ($iFrom > $iTo && $this->bKeepObsoleteNodes) {
$this->RestorePreviousNodes($sIntermediate);
}
// Recurse
$this->DoConvert($sIntermediate, $sTo, $oFactory);
@@ -698,8 +673,6 @@ class iTopDesignFormat
*/
protected function From16To15($oFactory)
{
$oXPath = new DOMXPath($this->oDocument);
// Remove AttributeTagSet nodes
//
$sPath = "/itop_design/classes/class/fields/field[@xsi:type='AttributeTagSet']";
@@ -836,15 +809,7 @@ class iTopDesignFormat
$oNode->appendChild($oCodeNode);
}
// N°3516 Remove legacy backoffice theme
// Remove completely light-grey theme
$this->RemoveNodeFromXPath('/itop_design/branding/themes/theme[@id="light-grey"]');
// Update test-red theme
$this->RemoveNodeFromXPath('/itop_design/branding/themes/theme[@id="test-red"]/imports/import[@id="css-variables"]');
$this->RemoveNodeFromXPath('/itop_design/branding/themes/theme[@id="test-red"]/stylesheets/stylesheet[@id="jqueryui"]');
$this->RemoveNodeFromXPath('/itop_design/branding/themes/theme[@id="test-red"]/stylesheets/stylesheet[@id="main"]');
$oNodeList = $oXPath->query('/itop_design/branding/themes/theme[@id="test-red"]/variables/variable[@id="backoffice-environment-banner-background-color"]');
foreach ($oNodeList as $oNode) {
$oNode->setAttribute('id', 'ibo-page-banner--background-color');
@@ -860,21 +825,6 @@ class iTopDesignFormat
$oNode->setAttribute('id', 'ibo-page-banner--text-content');
}
$this->RemoveNodeFromXPath('/itop_design/branding/themes/theme[@id="test-red"]/stylesheets/stylesheet[@id="environment-banner"]');
// Add new stylesheets
$oStyleSheetsNode = $oXPath->query('/itop_design/branding/themes/theme[@id="test-red"]/stylesheets')->item(0);
if ($oStyleSheetsNode) {
$oStyleSheetNode = $oStyleSheetsNode->ownerDocument->createElement("stylesheet");
$oStyleSheetNode->setAttribute('id', 'environment-banner');
$oStyleSheetNode->appendChild(new DOMText('../css/backoffice/themes/page-banner.scss'));
$oStyleSheetsNode->appendChild($oStyleSheetNode);
$oStyleSheetNode = $oStyleSheetsNode->ownerDocument->createElement("stylesheet");
$oStyleSheetNode->setAttribute('id', 'fullmoon');
$oStyleSheetNode->appendChild(new DOMText('../css/backoffice/main.scss'));
$oStyleSheetsNode->appendChild($oStyleSheetNode);
}
// Add new attribute to theme import nodes
$oNodeList = $oXPath->query('/itop_design/branding/themes/theme/imports/import');
foreach ($oNodeList as $oNode) {
@@ -954,24 +904,27 @@ class iTopDesignFormat
// N°3516 Bring back legacy themes
// Update test-red theme
$oNodeList = $oXPath->query('/itop_design/branding/themes/theme[@id="test-red"]/variables/variable[@id="ibo-page-banner--background-color"]');
foreach ($oNodeList as $oNode) {
$oNode->setAttribute('id', 'backoffice-environment-banner-background-color');
if (!$oXPath->query('/itop_design/branding/themes/theme[@id="test-red"]/variables/variable[@id="backoffice-environment-banner-background-color"]')->item(0)) {
$oNodeList = $oXPath->query('/itop_design/branding/themes/theme[@id="test-red"]/variables/variable[@id="ibo-page-banner--background-color"]');
foreach ($oNodeList as $oNode) {
$oNode->setAttribute('id', 'backoffice-environment-banner-background-color');
}
}
$oNodeList = $oXPath->query('/itop_design/branding/themes/theme[@id="test-red"]/variables/variable[@id="ibo-page-banner--text-color"]');
foreach ($oNodeList as $oNode) {
$oNode->setAttribute('id', 'backoffice-environment-banner-text-color');
if (!$oXPath->query('/itop_design/branding/themes/theme[@id="test-red"]/variables/variable[@id="backoffice-environment-banner-text-color"]')->item(0)) {
$oNodeList = $oXPath->query('/itop_design/branding/themes/theme[@id="test-red"]/variables/variable[@id="ibo-page-banner--text-color"]');
foreach ($oNodeList as $oNode) {
$oNode->setAttribute('id', 'backoffice-environment-banner-text-color');
}
}
$oNodeList = $oXPath->query( '/itop_design/branding/themes/theme[@id="test-red"]/variables/variable[@id="ibo-page-banner--text-content"]');
foreach ($oNodeList as $oNode) {
$oNode->setAttribute('id', 'backoffice-environment-banner-text-content');
if (!$oXPath->query('/itop_design/branding/themes/theme[@id="test-red"]/variables/variable[@id="backoffice-environment-banner-text-content"]')->item(0)) {
$oNodeList = $oXPath->query('/itop_design/branding/themes/theme[@id="test-red"]/variables/variable[@id="ibo-page-banner--text-content"]');
foreach ($oNodeList as $oNode) {
$oNode->setAttribute('id', 'backoffice-environment-banner-text-content');
}
}
$this->RemoveNodeFromXPath('/itop_design/branding/themes/theme[@id="test-red"]/stylesheets/stylesheet[@id="environment-banner"]');
$this->RemoveNodeFromXPath('/itop_design/branding/themes/theme[@id="test-red"]/stylesheets/stylesheet[@id="fullmoon"]');
// Add new attribute to theme import nodes
$oNodeList = $oXPath->query('/itop_design/branding/themes/theme/imports/import');
@@ -1012,88 +965,12 @@ class iTopDesignFormat
//nothing
}
/**
* @param string $sNodeMetaVersion
*
* @return void
* @throws \Exception
*/
private function RestorePreviousNodes($sNodeMetaVersion)
{
$oXPath = new DOMXPath($this->oDocument);
$sVersion = str_replace('.', '_', $sNodeMetaVersion);
$oTrashedNodes = $oXPath->query("/itop_design/meta/previous_versions/previous_version_$sVersion/trashed_nodes/trashed_node");
foreach ($oTrashedNodes as $oTrashedNode) {
if ($oTrashedNode->nodeType == XML_ELEMENT_NODE) {
$oXPathNode = $oXPath->query('parent_xpath', $oTrashedNode)->item(0);
$oNodeTreeNode = $oXPath->query('node_tree', $oTrashedNode)->item(0);
if (!is_null($oXPathNode) && !is_null($oNodeTreeNode)) {
$sXPath = $this->GetText($oXPathNode, '');
$oParentNode = $oXPath->query($sXPath)->item(0);
if ($oParentNode) {
$oNode = $oNodeTreeNode->firstChild;
while ($oNode) {
$oNextNode = $oNode->nextSibling;
if ($oNode->nodeType == XML_ELEMENT_NODE) {
// Check for collision
$sId = $oNode->getAttribute('id');
$sNodeXPath = ($sId != '') ? $oNode->nodeName.'[@id="'.$sId.'"]' : $oNode->nodeName;
$sNodeXPath = $sXPath.'/'.$sNodeXPath;
$oTarget = $oXPath->query($sNodeXPath)->item(0);
if ($oTarget) {
// Do not continue migration
throw new Exception("Trying to restore an existing node $sNodeXPath from version $sNodeMetaVersion");
}
// Restore the modification flags
$oModifiedNodeList = $oXPath->query('descendant-or-self::*[@_disabled_delta or @_disabled_rename_from]', $oNode);
foreach ($oModifiedNodeList as $oModifiedNode) {
foreach (['_delta', '_rename_from'] as $sModificationFlag) {
$sCurrentFlag = $oNode->getAttribute('_disabled'.$sModificationFlag);
if (!empty($sCurrentFlag)) {
$oModifiedNode->setAttribute($sModificationFlag, $sCurrentFlag);
$oModifiedNode->removeAttribute('_disabled'.$sModificationFlag);
}
}
}
// Move the node back in place
$oParentNode->appendChild($oNode);
}
$oNode = $oNextNode;
}
}
}
}
}
// Clean up the mess
$this->RemoveNodeFromXPath("/itop_design/meta/previous_versions/previous_version_$sVersion", false);
$this->RemoveEmptyNodeFromXPath("/itop_design/meta/previous_versions");
$this->RemoveEmptyNodeFromXPath("/itop_design/meta");
}
private function RemoveEmptyNodeFromXPath($sXPath, $bStoreThisNodeInMetaVersion = false)
{
$oXPath = new DOMXPath($this->oDocument);
$oNodeToRemove = $oXPath->query($sXPath)->item(0);
if (is_null($oNodeToRemove)) {
return;
}
$oNode = $oNodeToRemove->firstChild;
while ($oNode) {
if ($oNode->nodeType == XML_ELEMENT_NODE) {
return;
}
$oNode = $oNode->nextSibling;
}
$this->RemoveNodeFromXPath($sXPath, $bStoreThisNodeInMetaVersion);
}
/**
* @param string $sPath
* @param bool $bStoreThisNodeInMetaVersion
*
* @return void
*/
private function RemoveNodeFromXPath($sPath, $bStoreThisNodeInMetaVersion = true)
private function RemoveNodeFromXPath($sPath)
{
$oXPath = new DOMXPath($this->oDocument);
@@ -1101,56 +978,9 @@ class iTopDesignFormat
foreach ($oNodeList as $oNode)
{
$this->LogWarning('Node '.self::GetItopNodePath($oNode).' is irrelevant in this version, it will be removed.');
if ($bStoreThisNodeInMetaVersion && $this->bKeepObsoleteNodes && $this->sKeepVersion) {
// Move the node to <Meta> to keep it safe for backward migration
$oItopDesignNode = $this->GetOrCreateNode('/itop_design', 'itop_design', null);
$oMetaNode = $this->GetOrCreateNode('meta', 'meta', $oItopDesignNode);
$oPreviousVersionsNode = $this->GetOrCreateNode('previous_versions', 'previous_versions', $oMetaNode);
$sVersion = str_replace('.', '_', $this->sKeepVersion);
$oPreviousVersionNode = $this->GetOrCreateNode("previous_version_$sVersion", "previous_version_$sVersion", $oPreviousVersionsNode);
$oTrashedNodeList = $this->GetOrCreateNode('trashed_nodes', 'trashed_nodes', $oPreviousVersionNode);
$iNextId = str_replace('.', '', uniqid('', true));
$oTrashedNode = $this->GetOrCreateNode("trashed_node[@id='$iNextId']", 'trashed_node', $oTrashedNodeList);
$oTrashedNode->setAttribute('id', $iNextId);
$oTrashedNode->setAttribute('_delta', 'define');
$oXPathNode = $this->GetOrCreateNode('parent_xpath', 'parent_xpath', $oTrashedNode);
$oParentNode = $oNode->parentNode;
if ($oParentNode instanceof DOMElement) {
$sParentXPath = static::GetNodeXPath($oParentNode);
$oXPathNode->appendChild(new DOMText($sParentXPath));
}
$oNodeTreeNode = $this->GetOrCreateNode('node_tree', 'node_tree', $oTrashedNode);
// Store the modification flags
$oModifiedNodeList = $oXPath->query('descendant-or-self::*[@_delta or @_rename_from]', $oNode);
foreach ($oModifiedNodeList as $oModifiedNode) {
foreach (['_delta', '_rename_from'] as $sModificationFlag) {
$sCurrentFlag = $oNode->getAttribute($sModificationFlag);
if (!empty($sCurrentFlag)) {
$oModifiedNode->setAttribute('_disabled'.$sModificationFlag, $sCurrentFlag);
$oModifiedNode->removeAttribute($sModificationFlag);
}
}
}
$oNodeTreeNode->appendChild($oNode);
} else {
$this->DeleteNode($oNode);
}
}
}
private function GetOrCreateNode($sXPath, $sName, $oRootNode)
{
$oXPath = new DOMXPath($this->oDocument);
$oNode = $oXPath->query($sXPath, $oRootNode)->item(0);
if (is_null($oNode)) {
$oNode = $oRootNode->ownerDocument->createElement($sName);
$oRootNode->appendChild($oNode);
}
return $oNode;
}
/**
* Clean a collection node by removing the _delta="define" on it and moving it to the item nodes.
@@ -1338,31 +1168,4 @@ class iTopDesignFormat
return null;
}
/**
* Returns the TEXT of the current node (possibly from several child nodes)
* @param null $sDefault
* @return null|string
*/
protected function GetText($oNode, $sDefault = null)
{
$sText = null;
foreach($oNode->childNodes as $oChildNode)
{
if ($oChildNode instanceof \DOMText)
{
if (is_null($sText)) $sText = '';
$sText .= $oChildNode->wholeText;
}
}
if (is_null($sText))
{
return $sDefault;
}
else
{
return $sText;
}
}
}