123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- <?php
- // Copyright (C) 2015 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/>
- /**
- * Module specific customizations:
- * The customizations are done in XML, within a module_design section (itop_design/module_designs/module_design)
- * The module reads the cusomtizations by the mean of the ModuleDesign API
- * @package Core
- */
- require_once(APPROOT.'application/utils.inc.php');
- /**
- * Class ModuleDesign
- *
- * Usage from within a module:
- *
- * // Fetch the design
- * $oDesign = new ModuleDesign('tagada');
- *
- * // Read data from the root node
- * $oRoot = $oDesign->documentElement;
- * $oProperties = $oRoot->GetUniqueElement('properties');
- * $prop1 = $oProperties->GetChildText('property1');
- * $prop2 = $oProperties->GetChildText('property2');
- *
- * // Read data by searching the entire DOM
- * foreach ($oDesign->GetNodes('/module_design/bricks/brick') as $oBrickNode)
- * {
- * $sId = $oBrickNode->getAttribute('id');
- * $sType = $oBrickNode->getAttribute('xsi:type');
- * }
- *
- * // Search starting a given node
- * $oBricks = $oDesign->documentElement->GetUniqueElement('bricks');
- * foreach ($oBricks->GetNodes('brick') as $oBrickNode)
- * {
- * ...
- * }
- */
- class ModuleDesign extends DOMDocument
- {
- /**
- * @param string|null $sDesignSourceId Identifier of the section module_design (generally a module name), null to build an empty design
- * @throws Exception
- */
- public function __construct($sDesignSourceId = null)
- {
- parent::__construct('1.0', 'UTF-8');
- $this->Init();
- if (!is_null($sDesignSourceId))
- {
- $this->LoadFromCompiledDesigns($sDesignSourceId);
- }
- }
- /**
- * Overloadable. Called prior to data loading.
- */
- protected function Init()
- {
- $this->registerNodeClass('DOMElement', 'ModuleDesignElement');
- $this->formatOutput = true; // indent (must be loaded with option LIBXML_NOBLANKS)
- $this->preserveWhiteSpace = true; // otherwise the formatOutput option would have no effect
- }
- /**
- * Gets the data where the compiler has left them...
- * @param $sDesignSourceId Identifier of the section module_design (generally a module name)
- * @throws Exception
- */
- protected function LoadFromCompiledDesigns($sDesignSourceId)
- {
- $sDesignDir = APPROOT.'env-'.utils::GetCurrentEnvironment().'/core/module_designs/';
- $sFile = $sDesignDir.$sDesignSourceId.'.xml';
- if (!file_exists($sFile))
- {
- $aFiles = glob($sDesignDir.'/*.xml');
- if (count($aFiles) == 0)
- {
- $sAvailable = 'none!';
- }
- else
- {
- var_dump($aFiles);
- $aAvailable = array();
- foreach ($aFiles as $sFile)
- {
- $aAvailable[] = "'".basename($sFile, '.xml')."'";
- }
- $sAvailable = implode(', ', $aAvailable);
- }
- throw new Exception("Could not load module design '$sDesignSourceId'. Available designs: $sAvailable");
- }
- // Silently keep track of errors
- libxml_use_internal_errors(true);
- libxml_clear_errors();
- $this->load($sFile);
- //$bValidated = $oDocument->schemaValidate(APPROOT.'setup/itop_design.xsd');
- $aErrors = libxml_get_errors();
- if (count($aErrors) > 0)
- {
- $aDisplayErrors = array();
- foreach($aErrors as $oXmlError)
- {
- $aDisplayErrors[] = 'Line '.$oXmlError->line.': '.$oXmlError->message;
- }
- throw new Exception("Invalid XML in '$sFile'. Errors: ".implode(', ', $aDisplayErrors));
- }
- }
- /**
- * Overload of the standard API
- */
- public function load($filename, $options = 0)
- {
- parent::load($filename, LIBXML_NOBLANKS);
- }
- /**
- * Overload of the standard API
- */
- public function save($filename, $options = 0)
- {
- $this->documentElement->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance");
- return parent::save($filename, LIBXML_NOBLANKS);
- }
- /**
- * Create an HTML representation of the DOM, for debugging purposes
- * @param bool|false $bReturnRes Echoes or returns the HTML representation
- * @return mixed void or the HTML representation of the DOM
- */
- public function Dump($bReturnRes = false)
- {
- $sXml = $this->saveXML();
- if ($bReturnRes)
- {
- return $sXml;
- }
- else
- {
- echo "<pre>\n";
- echo htmlentities($sXml);
- echo "</pre>\n";
- }
- }
- /**
- * Quote and escape strings for use within an XPath expression
- * Usage: DesignDocument::GetNodes('class[@id='.DesignDocument::XPathQuote($sId).']');
- * @param $sValue The value to be quoted
- * @return string to be used within an XPath
- */
- public static function XPathQuote($sValue)
- {
- if (strpos($sValue, '"') !== false)
- {
- $aParts = explode('"', $sValue);
- $sRet = 'concat("'.implode('", \'"\', "', $aParts).'")';
- }
- else
- {
- $sRet = '"'.$sValue.'"';
- }
- return $sRet;
- }
- /**
- * Extracts some nodes from the DOM
- * @param string $sXPath A XPath expression
- * @param DesignNode|null $oContextNode The node to start the search from
- * @return DOMNodeList
- */
- public function GetNodes($sXPath, $oContextNode = null)
- {
- $oXPath = new DOMXPath($this);
- if (is_null($oContextNode))
- {
- $oResult = $oXPath->query($sXPath);
- }
- else
- {
- $oResult = $oXPath->query($sXPath, $oContextNode);
- }
- return $oResult;
- }
- /**
- * An alternative to getNodePath, that gives the id of nodes instead of the position within the children
- */
- public static function GetItopNodePath($oNode)
- {
- if ($oNode instanceof DOMDocument) return '';
- $sId = $oNode->getAttribute('id');
- $sNodeDesc = ($sId != '') ? $oNode->nodeName.'['.$sId.']' : $oNode->nodeName;
- return self::GetItopNodePath($oNode->parentNode).'/'.$sNodeDesc;
- }
- }
- /**
- * ModuleDesignElement: helper to read/change the DOM
- * @package ModelFactory
- */
- class ModuleDesignElement extends DOMElement
- {
- /**
- * Extracts some nodes from the DOM
- * @param string $sXPath A XPath expression
- * @return DOMNodeList
- */
- public function GetNodes($sXPath)
- {
- return $this->ownerDocument->GetNodes($sXPath, $this);
- }
- /**
- * Create an HTML representation of the DOM, for debugging purposes
- * @param bool|false $bReturnRes Echoes or returns the HTML representation
- * @return mixed void or the HTML representation of the DOM
- */
- public function Dump($bReturnRes = false)
- {
- $oDoc = new iTopDesignDocument();
- $oClone = $oDoc->importNode($this->cloneNode(true), true);
- $oDoc->appendChild($oClone);
- $sXml = $oDoc->saveXML($oClone);
- if ($bReturnRes)
- {
- return $sXml;
- }
- else
- {
- echo "<pre>\n";
- echo htmlentities($sXml);
- echo "</pre>\n";
- }
- }
- /**
- * Returns the node directly under the given node
- * @param $sTagName
- * @param bool|true $bMustExist
- * @return null
- * @throws DOMFormatException
- */
- public function GetUniqueElement($sTagName, $bMustExist = true)
- {
- $oNode = null;
- foreach($this->childNodes as $oChildNode)
- {
- if ($oChildNode->nodeName == $sTagName)
- {
- $oNode = $oChildNode;
- break;
- }
- }
- if ($bMustExist && is_null($oNode))
- {
- throw new DOMFormatException('Missing unique tag: '.$sTagName);
- }
- return $oNode;
- }
- /**
- * Returns the node directly under the current node, or null if missing
- * @param $sTagName
- * @return null
- * @throws DOMFormatException
- */
- public function GetOptionalElement($sTagName)
- {
- return $this->GetUniqueElement($sTagName, false);
- }
- /**
- * Returns the TEXT of the current node (possibly from several child nodes)
- * @param null $sDefault
- * @return null|string
- */
- public function GetText($sDefault = null)
- {
- $sText = null;
- foreach($this->childNodes as $oChildNode)
- {
- if ($oChildNode instanceof DOMText)
- {
- if (is_null($sText)) $sText = '';
- $sText .= $oChildNode->wholeText;
- }
- }
- if (is_null($sText))
- {
- return $sDefault;
- }
- else
- {
- return $sText;
- }
- }
- /**
- * Get the TEXT value from a child node
- * @param string $sTagName
- * @param string|null $sDefault
- * @return string
- */
- public function GetChildText($sTagName, $sDefault = null)
- {
- $sRet = $sDefault;
- if ($oChild = $this->GetOptionalElement($sTagName))
- {
- $sRet = $oChild->GetText($sDefault);
- }
- return $sRet;
- }
- }
|