diff --git a/core/autoload.php b/core/autoload.php index ee5e67b42..c1d674e59 100644 --- a/core/autoload.php +++ b/core/autoload.php @@ -29,6 +29,7 @@ MetaModel::IncludeModule('core/action.class.inc.php'); MetaModel::IncludeModule('core/trigger.class.inc.php'); MetaModel::IncludeModule('core/bulkexport.class.inc.php'); MetaModel::IncludeModule('core/ownershiplock.class.inc.php'); +MetaModel::IncludeModule('core/tagfield.class.inc.php'); MetaModel::IncludeModule('synchro/synchrodatasource.class.inc.php'); MetaModel::IncludeModule('core/backgroundtask.class.inc.php'); MetaModel::IncludeModule('core/inlineimage.class.inc.php'); diff --git a/core/tagfield.class.inc.php b/core/tagfield.class.inc.php new file mode 100644 index 000000000..a8a785fb9 --- /dev/null +++ b/core/tagfield.class.inc.php @@ -0,0 +1,81 @@ + + + +/** + *

Stores data for {@link AttributeTagSet} fields + * + *

We will have an implementation for each class/field to be able to customize rights (generated in \MFCompiler::CompileClass).
+ * Only this abstract class will exists in the DB : the implementations won't had any new field. + * + * @since 2.6 N°931 tag fields + */ +abstract class TagSetFieldData extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + 'category' => 'bizmodel', + 'key_type' => 'autoincrement', + 'name_attcode' => array('tag_label'), + 'state_attcode' => '', + 'reconc_keys' => array('tag_code'), + 'db_table' => 'priv_tagfielddata', + 'db_key_field' => 'id', + 'db_finalclass_field' => 'finalclass', + ); + + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + + MetaModel::Init_AddAttribute(new AttributeString("tag_code", array( + "allowed_values" => null, + "sql" => 'tag_code', + "default_value" => '', + "is_null_allowed" => false, + "depends_on" => array() + ))); + MetaModel::Init_AddAttribute(new AttributeString("tag_label", array( + "allowed_values" => null, + "sql" => 'tag_label', + "default_value" => '', + "is_null_allowed" => false, + "depends_on" => array() + ))); + MetaModel::Init_AddAttribute(new AttributeString("tag_description", array( + "allowed_values" => null, + "sql" => 'tag_description', + "default_value" => '', + "is_null_allowed" => false, + "depends_on" => array() + ))); + MetaModel::Init_AddAttribute(new AttributeBoolean("is_default", array( + "allowed_values" => null, + "sql" => "is_default", + "default_value" => false, + "is_null_allowed" => false, + "depends_on" => array() + ))); + + + MetaModel::Init_SetZListItems('details', array('tag_code', 'tag_label', 'tag_description', 'is_default')); + MetaModel::Init_SetZListItems('standard_search', array('tag_code', 'tag_label', 'tag_description', 'is_default')); + MetaModel::Init_SetZListItems('list', array('tag_code', 'tag_label', 'tag_description', 'is_default')); + } +} \ No newline at end of file diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index 39f147c48..8adb472ee 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -25,7 +25,8 @@ require_once(APPROOT.'core/moduledesign.class.inc.php'); class DOMFormatException extends Exception { /** - * Overrides the Exception default constructor to automatically add informations about the concerned node (path and line number) + * Overrides the Exception default constructor to automatically add informations about the concerned node (path and + * line number) * * @param string $message * @param $code @@ -47,6 +48,7 @@ class DOMFormatException extends Exception */ class MFCompiler { + /** @var \ModelFactory */ protected $oFactory; protected $aRootClasses; @@ -280,13 +282,15 @@ class MFCompiler } else { + /** @var \DOMElement $oClass */ foreach($oClasses as $oClass) { $sClass = $oClass->getAttribute("id"); $aAllClasses[] = $sClass; try { - $sCompiledCode .= $this->CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir, $oP); + $sCompiledCode .= $this->CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, + $sRelativeDir); } catch (DOMFormatException $e) { @@ -932,17 +936,15 @@ EOF } /** - * @param $oClass - * @param $sTempTargetDir - * @param $sFinalTargetDir - * @param $sModuleRelativeDir - * @param $oP + * @param \DOMElement $oClass + * @param string $sTempTargetDir + * @param string $sFinalTargetDir + * @param string $sModuleRelativeDir * * @return string * @throws \DOMFormatException - * @throws \Exception */ - protected function CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sModuleRelativeDir, $oP) + protected function CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sModuleRelativeDir) { $sClass = $oClass->getAttribute('id'); $oProperties = $oClass->GetUniqueElement('properties'); @@ -1089,17 +1091,12 @@ EOF // Finalize class params declaration // - $aClassParamsPHP = array(); - foreach($aClassParams as $sKey => $sPHPValue) - { - $aClassParamsPHP[] = " '$sKey' => $sPHPValue,"; - } - $sClassParams = implode("\n", $aClassParamsPHP); - + $sClassParams = $this->GetAssociativeArrayAsPhpCode($aClassParams); + // Comment on top of the class declaration // $sCodeComment = $oProperties->GetChildText('comment'); - + // Fields // $oFields = $oClass->GetOptionalElement('fields'); @@ -1108,6 +1105,8 @@ EOF $this->CompileFiles($oFields, $sTempTargetDir.'/'.$sModuleRelativeDir, $sFinalTargetDir.'/'.$sModuleRelativeDir, ''); } $sAttributes = ''; + $aTagFieldsInfo = array(); + /** @var \DOMElement $oField */ foreach($this->oFactory->ListFields($oClass) as $oField) { try @@ -1397,7 +1396,12 @@ EOF $aParameters['is_null_allowed'] = $this->GetPropBoolean($oField, 'is_null_allowed', false); $aParameters['depends_on'] = $sDependencies; } - + + if ($sAttType == 'AttributeTagSet') + { + $aTagFieldsInfo[] = $sAttCode; + } + // Optional parameters (more for historical reasons) // Added if present... // @@ -1786,8 +1790,10 @@ EOF // Let's make the whole class declaration // - $sPHP = "\n\n$sCodeComment\n"; + $sClassName = $oClass->getAttribute('id'); + $bIsAbstractClass = ($oProperties->GetChildText('abstract') == 'true'); $oPhpParent = $oClass->GetUniqueElement('php_parent', false); + $aRequiredFiles = array(); if ($oPhpParent) { $sParentClass = $oPhpParent->GetChildText('name', ''); @@ -1798,7 +1804,7 @@ EOF $sIncludeFile = $oPhpParent->GetChildText('file', ''); if ($sIncludeFile != '') { - $sPHP .= "\nrequire_once('$sIncludeFile'); // Implementation of the class $sParentClass\n"; + $aRequiredFiles[] = $sIncludeFile; } //TODO fix this !!! // $sFullPath = $this->sSourceDir.'/'.$sModuleRelativeDir.'/'.$sIncludeFile; @@ -1811,37 +1817,44 @@ EOF { $sParentClass = $oClass->GetChildText('parent', 'DBObject'); } - if ($oProperties->GetChildText('abstract') == 'true') - { - $sPHP .= 'abstract class '.$oClass->getAttribute('id'); - } - else - { - $sPHP .= 'class '.$oClass->getAttribute('id'); - } - $sPHP .= " extends $sParentClass\n"; - $sPHP .= -<<GeneratePhpCodeForClass($sClassName, $sParentClass, $sClassParams, $sInitMethodCalls, + $bIsAbstractClass, $sMethods, $aRequiredFiles, $sCodeComment); + + // N°931 generates TagFieldData classes for AttributeTag fields + if (!empty($aTagFieldsInfo)) + { + $sTagClassParentClass = "TagSetFieldData"; + $aTagClassParams = array + ( + 'category' => 'bizmodel', + 'key_type' => 'autoincrement', + 'name_attcode' => array('tag_label'), + 'state_attcode' => '', + 'reconc_keys' => array('tag_code'), + 'db_table' => '', // no need to have a corresponding table : this class exists only for rights, no additional field + 'db_key_field' => 'id', + 'db_finalclass_field' => 'finalclass', + ); + foreach ($aTagFieldsInfo as $sTagFieldName) + { + $sTagSuffix = $sClassName.'_'.$sTagFieldName; + $sTagClassName = 'TagSetFieldDataFor_'.$sTagSuffix; + $sTagClassParams = var_export($aTagClassParams, true); + $sPHP .= $this->GeneratePhpCodeForClass($sTagClassName, $sTagClassParentClass, $sTagClassParams); + } + } + return $sPHP; - }// function CompileClass() + } /** @@ -1984,7 +1997,7 @@ EOF; $aPHPMenu[] = "\$__comp_menus__['$sMenuId']->SetParameters(array('auto_reload' => $sAutoReload));"; } return $aPHPMenu; - } // function CompileMenu + } /** * Helper to compute the grant, taking any existing grant into account @@ -2355,9 +2368,9 @@ EOF; return $s; } - // Transform the file references into the corresponding filename (and create the file in the relevant directory) - // /** + * Transform the file references into the corresponding filename (and create the file in the relevant directory) + * * @param $oNode * @param $sTempTargetDir * @param $sFinalTargetDir @@ -2627,4 +2640,74 @@ EOF; uasort($this->aSnippets[$sModuleId]['before'], array(get_class($this), 'SortOnRank')); } } + + /** + * We can't use var_export() as we need to output some PHP code, for example `utils::GetAbsoluteUrlModulesRoot()` calls + * + * @param string[string] $aAssocArray + * + * @return string PHP declaration of the array + */ + private function GetAssociativeArrayAsPhpCode($aAssocArray) + { + $aArrayPhp = array(); + foreach ($aAssocArray as $sKey => $sPHPValue) + { + $aArrayPhp[] = " '$sKey' => $sPHPValue,"; + } + $sArrayPhp = implode("\n", $aArrayPhp); + + return 'array('.$sArrayPhp.')'; + } + + /** + * @param string $sClassName + * @param string $sParentClassName + * @param string $sClassParams serialized array. Use ::GetAssociativeArrayAsPhpCode if you need to keep some PHP code calls + * @param string $sInitMethodCalls + * @param bool $bIsAbstractClass + * @param string $sMethods + * + * @param string $aRequiredFiles + * @param string $sCodeComment + * + * @return string php code for the class + */ + private function GeneratePhpCodeForClass( + $sClassName, $sParentClassName, $sClassParams, $sInitMethodCalls = '', $bIsAbstractClass = false, $sMethods = '', + $aRequiredFiles = array(), $sCodeComment = '' + ) { + $sPHP = "\n\n$sCodeComment\n"; + + foreach ($aRequiredFiles as $sIncludeFile) + { + $sPHP .= "\nrequire_once('$sIncludeFile');\n"; + } + + if ($bIsAbstractClass) + { + $sPHP .= 'abstract class '.$sClassName; + } + else + { + $sPHP .= 'class '.$sClassName; + } + $sPHP .= " extends $sParentClassName\n"; + $sPHP .= + <<