Ver Fonte

Split in a separate "tool" class the upgrade of the format of the datamodel from 1.0 to 1.1.
Marked the XML as version 1.1.

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@3362 a333f486-631f-4898-b8df-5754b55c2be0

dflaven há 10 anos atrás
pai
commit
43d2e5470a
2 ficheiros alterados com 177 adições e 58 exclusões
  1. 171 0
      setup/itopdesignformat.class.inc.php
  2. 6 58
      setup/modelfactory.class.inc.php

+ 171 - 0
setup/itopdesignformat.class.inc.php

@@ -0,0 +1,171 @@
+<?php
+// Copyright (C) 2014 Combodo SARL
+//
+//   This file is part of iTop.
+//
+//   iTop is free software; you can redistribute it and/or modify
+//   it under the terms of the GNU Affero General Public License as published by
+//   the Free Software Foundation, either version 3 of the License, or
+//   (at your option) any later version.
+//
+//   iTop 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 Affero General Public License for more details.
+//
+//   You should have received a copy of the GNU Affero General Public License
+//   along with iTop. If not, see <http://www.gnu.org/licenses/>
+
+/**
+ * Utility to upgrade the format of a given XML datamodel to the latest version
+ * The datamodel is supplied as a loaded DOMDocument and modified in-place.
+ * 
+ * Usage:
+ * 
+ * $oDocument = new DOMDocument();
+ * $oDocument->load($sXMLFile);
+ * $aLog = array();
+ * $oFormat = new iTopDesignFormat($oDocument);
+ * if ($oFormat->Upgrade($aLog))
+ * {
+ *     $oDocument->save($sXMLFile);
+ * }
+ * else
+ * {
+ *     echo "Error, failed to upgrade the format, reason(s):\n".implode("\n", $aLog);
+ * }
+ */
+ 
+ define('ITOP_DESIGN_LATEST_VERSION', '1.1');
+ 
+class iTopDesignFormat
+{
+	/**
+	 * The Document to work on
+	 * @var DOMDocument
+	 */
+	protected $oDocument;
+	
+	/**
+	 * Creation from a loaded DOMDocument
+	 * @param DOMDocument $oDocument The document to transform
+	 */
+	public function __construct(DOMDocument $oDocument)
+	{
+		$this->oDocument = $oDocument;
+	}
+	
+	/**
+	 * 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 Array $aLog Array (as a reference) to gather the log results (errors, etc)
+	 * @param string $sTargetVersion The desired version (or the latest possible version if not specified)
+	 */
+	public function Upgrade(&$aLog, $sTargetVersion = ITOP_DESIGN_LATEST_VERSION)
+	{
+		$oXPath = new DOMXPath($this->oDocument);
+		// Retrieve the version number
+		$oNodeList = $oXPath->query('/itop_design');
+		if ($oNodeList->length == 0)
+		{
+			// Hmm, not an iTop Data Model file...
+			$aLog[] = "File format, no root <itop_design> tag found";
+			return false;
+		}
+		else
+		{
+			$sVersion = $oNodeList->item(0)->getAttribute('version');
+			switch($sVersion)
+			{
+				case '': // No version, assume 1.0 !!
+				case '1.0':
+				$bRet = $this->From10To11();
+				if ($bRet)
+				{
+					// Update the version number
+					$oNodeList->item(0)->setAttribute('version', '1.1');
+				}
+				return true;
+				break;
+				
+				case '1.1':
+				return true; // Nothing to do, the document is already at the most recent version
+				break;
+				
+				default:
+				$aLog[] = "Unknown format version: $sVersion";
+				return false; // unknown versions are not supported
+			}
+		}
+	}
+
+	/**
+	 * Upgrade the format from version 1.0 to 1.1
+	 * @return boolean true on success, false otherwise
+	 */
+	protected function From10To11()
+	{
+		// Adjust the XML to transparently add an id (=stimulus) on all life-cycle transitions
+		// which don't already have one
+		$oXPath = new DOMXPath($this->oDocument);
+		$oNodeList = $oXPath->query('/itop_design/classes//class/lifecycle/states/state/transitions/transition/stimulus');
+		foreach ($oNodeList as $oNode)
+		{
+			$oNode->parentNode->SetAttribute('id', $oNode->textContent);
+			$this->DeleteNode($oNode);
+		}
+		
+		// Adjust the XML to transparently add an id (=percent) on all thresholds of stopwatches
+		// which don't already have one
+		$oNodeList = $oXPath->query("/itop_design/classes//class/fields/field[@xsi:type='AttributeStopWatch']/thresholds/threshold/percent");
+		foreach ($oNodeList as $oNode)
+		{
+			$oNode->parentNode->SetAttribute('id', $oNode->textContent);
+			$this->DeleteNode($oNode);
+		}
+		
+		// Adjust the XML to transparently add an id (=action:<type>) on all allowed actions (profiles)
+		// which don't already have one
+		$oNodeList = $oXPath->query('/itop_design/user_rights/profiles/profile/groups/group/actions/action');
+		foreach ($oNodeList as $oNode)
+		{
+			if ($oNode->getAttribute('id') == '')
+			{
+				$oNode->SetAttribute('id', 'action:' . $oNode->getAttribute('xsi:type'));
+				$oNode->removeAttribute('xsi:type');
+			}
+			elseif ($oNode->getAttribute('xsi:type') == 'stimulus')
+			{
+				$oNode->SetAttribute('id', 'stimulus:' . $oNode->getAttribute('id'));
+				$oNode->removeAttribute('xsi:type');
+			}
+		}
+		
+		// Adjust the XML to transparently add an id (=value) on all values of an enum which don't already have one.
+		// This enables altering an enum for just adding/removing one value, intead of redefining the whole list of values.
+		$oNodeList = $oXPath->query("/itop_design/classes//class/fields/field[@xsi:type='AttributeEnum']/values/value");
+		foreach ($oNodeList as $oNode)
+		{
+			if ($oNode->getAttribute('id') == '')
+			{
+				$oNode->SetAttribute('id', $oNode->textContent);
+			}
+		}
+		
+		return true;
+	}
+	
+	/**
+	 * Delete a node from the DOM and make sure to also remove the immediately following line break (DOMText), if any.
+	 * This prevents generating empty lines in the middle of the XML
+	 * @param DOMNode $oNode
+	 */
+	protected function DeleteNode($oNode)
+	{
+		if ( $oNode->nextSibling && ($oNode->nextSibling instanceof DOMText) && ($oNode->nextSibling->isWhitespaceInElementContent()) )
+		{
+			$oNode->parentNode->removeChild($oNode->nextSibling);
+		}
+		$oNode->parentNode->removeChild($oNode);
+	}
+}

+ 6 - 58
setup/modelfactory.class.inc.php

@@ -25,6 +25,7 @@
 
 
 require_once(APPROOT.'setup/moduleinstaller.class.inc.php');
+require_once(APPROOT.'setup/itopdesignformat.class.inc.php');
 
  /**
  * ModelFactoryModule: the representation of a Module (i.e. element that can be selected during the setup)
@@ -200,6 +201,7 @@ class ModelFactory
 		$this->oDOMDocument = new MFDocument();
 		$this->oRoot = $this->oDOMDocument->CreateElement('itop_design');
 		$this->oRoot->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance");
+		$this->oRoot->setAttribute('version', ITOP_DESIGN_LATEST_VERSION);
 		$this->oDOMDocument->AppendChild($this->oRoot);
 		$this->oModules = $this->oDOMDocument->CreateElement('loaded_modules');
 		$this->oRoot->AppendChild($this->oModules);
@@ -432,7 +434,9 @@ class ModelFactory
 					}
 				}
 				
-				self::UpgradeDocument($oDocument);
+				$oFormat = new iTopDesignFormat($oDocument);
+				$aErrorLog = array();
+				if (!$oFormat->Upgrade($aErrorLog)) throw new Exception("Cannot load module $sModuleName, failed to upgrade to datamodel format of: $sXmlFile. Reason(s): ".implode("\n", $aErrorLog));
 				
 				$oDeltaRoot = $oDocument->childNodes->item(0);
 				$this->LoadDelta($oDeltaRoot, $this->oDOMDocument);
@@ -513,63 +517,6 @@ class ModelFactory
 			throw new Exception('Error loading module "'.$oModule->GetName().'": '.$e->getMessage().' - Loaded modules: '.implode(',', $aLoadedModuleNames));
 		}
 	}
-	
-	/**
-	 * Make adjustements to the DOM to migrate it to the latest version
-	 * @param DOMDocument $oDocument
-	 */
-	public static function UpgradeDocument(DOMDocument $oDocument)
-	{
-		// Adjust the XML to transparently add an id (=stimulus) on all life-cycle transitions
-		// which don't already have one
-		$oXPath = new DOMXPath($oDocument);
-		$oNodeList = $oXPath->query('/itop_design/classes//class/lifecycle/states/state/transitions/transition/stimulus');
-		foreach ($oNodeList as $oNode)
-		{
-			if ($oNode->parentNode->getAttribute('id') == '')
-			{
-				$oNode->parentNode->SetAttribute('id', $oNode->textContent);
-				$oNode->parentNode->removeChild($oNode);
-			}
-		}
-
-		// Adjust the XML to transparently add an id (=percent) on all thresholds of stopwatches
-		// which don't already have one
-		$oNodeList = $oXPath->query("/itop_design/classes//class/fields/field[@xsi:type='AttributeStopWatch']/thresholds/threshold/percent");
-		foreach ($oNodeList as $oNode)
-		{
-			$oNode->parentNode->SetAttribute('id', $oNode->textContent);
-			$oNode->parentNode->removeChild($oNode);
-		}
-
-		// Adjust the XML to transparently add an id (=action:<type>) on all allowed actions (profiles)
-		// which don't already have one
-		$oNodeList = $oXPath->query('/itop_design/user_rights/profiles/profile/groups/group/actions/action');
-		foreach ($oNodeList as $oNode)
-		{
-			if ($oNode->getAttribute('id') == '')
-			{
-				$oNode->SetAttribute('id', 'action:' . $oNode->getAttribute('xsi:type'));
-				$oNode->removeAttribute('xsi:type');
-			}
-			elseif ($oNode->getAttribute('xsi:type') == 'stimulus')
-			{
-				$oNode->SetAttribute('id', 'stimulus:' . $oNode->getAttribute('id'));
-				$oNode->removeAttribute('xsi:type');
-			}
-		}
-
-		// Adjust the XML to transparently add an id (=value) on all values of an enum which don't already have one.
-		// This enables altering an enum for just adding/removing one value, intead of redefining the whole list of values.
-		$oNodeList = $oXPath->query("/itop_design/classes//class/fields/field[@xsi:type='AttributeEnum']/values/value");
-		foreach ($oNodeList as $oNode)
-		{
-			if ($oNode->getAttribute('id') == '')
-			{
-				$oNode->SetAttribute('id', $oNode->textContent);
-			}
-		}
-	}
 
 	/**
 	 * Collects the PHP Dict entries into the ModelFactory for transforming the dictionary into an XML structure
@@ -1866,6 +1813,7 @@ class MFDocument extends DOMDocument
 		{
 			$oRootNode = $this->createElement('itop_design'); // make sure that the document is not empty
 			$oRootNode->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance");
+			$oRootNode->setAttribute('version', ITOP_DESIGN_LATEST_VERSION);
 			$this->appendChild($oRootNode);
 		}
 		return parent::saveXML();