diff --git a/core/cmdbobject.class.inc.php b/core/cmdbobject.class.inc.php index bc6b325be..5943f12b3 100644 --- a/core/cmdbobject.class.inc.php +++ b/core/cmdbobject.class.inc.php @@ -76,6 +76,7 @@ require_once('cmdbchangeop.class.inc.php'); // Romain: temporary moved into application.inc.php (see explanations there) //require_once('event.class.inc.php'); +require_once('templatestring.class.inc.php'); require_once('csvparser.class.inc.php'); require_once('bulkchange.class.inc.php'); diff --git a/core/dbobject.class.php b/core/dbobject.class.php index ba6ebc5de..00c3336e6 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -367,6 +367,34 @@ abstract class DBObject } public function Get($sAttCode) + { + if (($iPos = strpos($sAttCode, '->')) === false) + { + return $this->GetStrict($sAttCode); + } + else + { + $sExtKeyAttCode = substr($sAttCode, 0, $iPos); + $sRemoteAttCode = substr($sAttCode, $iPos + 2); + if (!MetaModel::IsValidAttCode(get_class($this), $sExtKeyAttCode)) + { + throw new CoreException("Unknown external key '$sExtKeyAttCode' for the class ".get_class($this)); + } + $oKeyAttDef = MetaModel::GetAttributeDef(get_class($this), $sExtKeyAttCode); + $sRemoteClass = $oKeyAttDef->GetTargetClass(); + $oRemoteObj = MetaModel::GetObject($sRemoteClass, $this->GetStrict($sExtKeyAttCode), false); + if (is_null($oRemoteObj)) + { + return ''; + } + else + { + return $oRemoteObj->Get($sRemoteAttCode); + } + } + } + + public function GetStrict($sAttCode) { if (!array_key_exists($sAttCode, MetaModel::ListAttributeDefs(get_class($this)))) { diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 116e5e634..30a1794b1 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -676,10 +676,38 @@ abstract class MetaModel if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass])) return false; return (self::$m_aAttribDefs[$sClass][$sAttCode]->IsExternalKey()); } - final static public function IsValidAttCode($sClass, $sAttCode) + final static public function IsValidAttCode($sClass, $sAttCode, $bExtended = false) { if (!array_key_exists($sClass, self::$m_aAttribDefs)) return false; - return (array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass])); + + if ($bExtended) + { + if (($iPos = strpos($sAttCode, '->')) === false) + { + $bRes = array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]); + } + else + { + $sExtKeyAttCode = substr($sAttCode, 0, $iPos); + $sRemoteAttCode = substr($sAttCode, $iPos + 2); + if (MetaModel::IsValidAttCode($sClass, $sExtKeyAttCode)) + { + $oKeyAttDef = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode); + $sRemoteClass = $oKeyAttDef->GetTargetClass(); + $bRes = MetaModel::IsValidAttCode($sRemoteClass, $sRemoteAttCode, true); + } + else + { + $bRes = false; + } + } + } + else + { + $bRes = array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]); + } + + return $bRes; } final static public function IsAttributeOrigin($sClass, $sAttCode) { diff --git a/core/templatestring.class.inc.php b/core/templatestring.class.inc.php new file mode 100644 index 000000000..5c48f85d7 --- /dev/null +++ b/core/templatestring.class.inc.php @@ -0,0 +1,177 @@ +friendlyname$ is in location $this->location_id->name$ ('$this->location_id->org_id->name$)"); + * echo $oString->Render(array('this' => $oContact)); + + * @author Erwan Taloc + * @author Romain Quetiez + * @author Denis Flaven + * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL + */ + +/** + * Helper class + */ +class TemplateStringPlaceholder +{ + public $sToken; + public $sAttCode; + public $sFunction; + public $sParamName; + public $bIsValid; + + public function __construct($sToken) + { + $this->sToken = $sToken; + $this->sAttcode = ''; + $this->sFunction = ''; + $this->sParamName = ''; + $this->bIsValid = false; // Validity may be false in general, but it can work anyway (thanks to specialization) when rendering + } +} + +/** + * Class TemplateString + */ +class TemplateString +{ + protected $m_sRaw; + protected $m_aPlaceholders; + + public function __construct($sRaw) + { + $this->m_sRaw = $sRaw; + $this->m_aPlaceholders = null; + } + + /** + * Split the string into placholders + * @param Hash $aParamTypes Class of the expected parameters: hash array of '' => '' + * @return void + */ + protected function Analyze($aParamTypes = array()) + { + if (!is_null($this->m_aPlaceholders)) return; + + $this->m_aPlaceholders = array(); + if (preg_match_all('/\\$([a-z0-9_]+(->[a-z0-9_]+)*)\\$/', $this->m_sRaw, $aMatches)) + { + foreach($aMatches[1] as $sPlaceholder) + { + $oPlaceholder = new TemplateStringPlaceholder($sPlaceholder); + $oPlaceholder->bIsValid = false; + foreach ($aParamTypes as $sParamName => $sClass) + { + $sParamPrefix = $sParamName.'->'; + if (substr($sPlaceholder, 0, strlen($sParamPrefix)) == $sParamPrefix) + { + // Todo - detect functions (label...) + $oPlaceholder->sFunction = ''; + + $oPlaceholder->sParamName = $sParamName; + $sAttcode = substr($sPlaceholder, strlen($sParamPrefix)); + $oPlaceholder->sAttcode = $sAttcode; + $oPlaceholder->bIsValid = MetaModel::IsValidAttCode($sClass, $sAttcode, true /* extended */); + } + } + + $this->m_aPlaceholders[] = $oPlaceholder; + } + } + } + + /** + * Return the placeholders (for reporting purposes) + * @return void + */ + public function GetPlaceholders() + { + return $this->m_aPlaceholders; + } + + /** + * Check the format when possible + * @param Hash $aParamTypes Class of the expected parameters: hash array of '' => '' + * @return void + */ + public function IsValid($aParamTypes = array()) + { + $this->Analyze($aParamTypes); + + foreach($this->m_aPlaceholders as $oPlaceholder) + { + if (!$oPlaceholder->bIsValid) + { + if (count($aParamTypes) == 0) + { + return false; + } + if (array_key_exists($oPlaceholder->sParamName, $aParamTypes)) + { + return false; + } + } + } + return true; + } + + /** + * Apply the given parameters to replace the placeholders + * @param Hash $aParamValues Value of the expected parameters: hash array of '' => '' + * @return void + */ + public function Render($aParamValues = array()) + { + $aParamTypes = array(); + foreach($aParamValues as $sParamName => $value) + { + $aParamTypes[$sParamName] = get_class($value); + } + $this->Analyze($aParamTypes); + + $aSearch = array(); + $aReplace = array(); + foreach($this->m_aPlaceholders as $oPlaceholder) + { + if (array_key_exists($oPlaceholder->sParamName, $aParamValues)) + { + $oRef = $aParamValues[$oPlaceholder->sParamName]; + try + { + $value = $oRef->Get($oPlaceholder->sAttcode); + $aSearch[] = '$'.$oPlaceholder->sToken.'$'; + $aReplace[] = $value; + $oPlaceholder->bIsValid = true; + } + catch(Exception $e) + { + $oPlaceholder->bIsValid = false; + } + } + else + { + $oPlaceholder->bIsValid = false; + } + } + return str_replace($aSearch, $aReplace, $this->m_sRaw); + } +} +?> \ No newline at end of file