Browse Source

- Implemented some hints to help users find out what are the mandatory fields for a CSV import (Trac #276). Though not 100% bullet-proof.

- Continue to implement support of hierarchical ZLists in modification forms.

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@904 a333f486-631f-4898-b8df-5754b55c2be0
dflaven 14 years ago
parent
commit
ff09a8c4f0
3 changed files with 139 additions and 61 deletions
  1. 113 58
      application/cmdbabstract.class.inc.php
  2. 20 3
      pages/ajax.csvimport.php
  3. 6 0
      webservices/import.php

+ 113 - 58
application/cmdbabstract.class.inc.php

@@ -299,24 +299,31 @@ abstract class cmdbAbstractObject extends CMDBObject
 			$oPage->add('<table style="vertical-align:top"><tr>');
 			foreach($aCols as $sColIndex => $aFieldsets)
 			{
-				$aDetails[$sTab][$sColIndex] = array();
+				$oPage->add('<td style="vertical-align:top">');
+				//$aDetails[$sTab][$sColIndex] = array();
 				foreach($aFieldsets as $sFieldsetName => $aFields)
 				{
-					//if ($sFieldsetName == '')
-					//{
-						foreach($aFields as $sAttCode)
+					$aDetails[$sTab][$sColIndex] = array();
+					if (!empty($sFieldsetName))
+					{
+						$oPage->add('<fieldset>');
+						$oPage->add('<legend>'.Dict::S($sFieldsetName).'</legend>');
+					}
+					foreach($aFields as $sAttCode)
+					{
+						$val = $this->GetFieldAsHtml($sClass, $sAttCode, $sStateAttCode);
+						if ($val != null)
 						{
-							$val = $this->GetFieldAsHtml($sClass, $sAttCode, $sStateAttCode);
-							if ($val != null)
-							{
-								// The field is visible, add it to the current column
-								$aDetails[$sTab][$sColIndex][] = $val;
-							}				
-						}
-					//}
+							// The field is visible, add it to the current column
+							$aDetails[$sTab][$sColIndex][] = $val;
+						}				
+					}
+					$oPage->Details($aDetails[$sTab][$sColIndex]);
+					if (!empty($sFieldsetName))
+					{
+						$oPage->add('</fieldset>');
+					}
 				}
-				$oPage->add('<td style="vertical-align:top">');
-				$oPage->Details($aDetails[$sTab][$sColIndex]);
 				$oPage->add('</td>');
 			}
 			$oPage->add('</tr></table>');
@@ -743,9 +750,15 @@ EOF
 			$aHeader[] = 'id';
 			foreach($aList[$sClassName] as $sAttCode => $oAttDef)
 			{
+				$sStar = '';
 				if ($oAttDef->IsExternalField())
 				{
 					$sExtKeyLabel = MetaModel::GetLabel($sClassName, $oAttDef->GetKeyAttCode());
+					$oExtKeyAttDef = MetaModel::GetAttributeDef($sClassName, $oAttDef->GetKeyAttCode());
+					if (!$oExtKeyAttDef->IsNullAllowed() && isset($aParams['showMandatoryFields']))
+					{
+						$sStar = '*';
+					}
 					$sRemoteAttLabel = MetaModel::GetLabel($oAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
 					$oTargetAttDef = MetaModel::GetAttributeDef($oAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
 					$sSuffix = '';
@@ -754,11 +767,15 @@ EOF
 						$sSuffix = '->id';
 					}
 					
-					$aHeader[] = $sExtKeyLabel.'->'.$sRemoteAttLabel.$sSuffix;
+					$aHeader[] = $sExtKeyLabel.'->'.$sRemoteAttLabel.$sSuffix.$sStar;
 				}
 				else
 				{
-					$aHeader[] = MetaModel::GetLabel($sClassName, $sAttCode);
+					if (!$oAttDef->IsNullAllowed() && isset($aParams['showMandatoryFields']))
+					{
+						$sStar = '*';
+					}
+					$aHeader[] = MetaModel::GetLabel($sClassName, $sAttCode).$sStar;
 				}
 			}
 		}
@@ -1184,69 +1201,107 @@ EOF
 		$oPage->AddTabContainer(OBJECT_PROPERTIES_TAB);
 		$oPage->SetCurrentTabContainer(OBJECT_PROPERTIES_TAB);
 		$oPage->SetCurrentTab(Dict::S('UI:PropertiesTab'));
-		$aDetailsList = $this->FLattenZList(MetaModel::GetZListItems($sClass, 'details'));
+//		$aDetailsList = $this->FLattenZList(MetaModel::GetZListItems($sClass, 'details'));
 		//$aFullList = MetaModel::ListAttributeDefs($sClass);
 		$aList = array();
 		// Compute the list of properties to display, first the attributes in the 'details' list, then 
 		// all the remaining attributes that are not external fields
-		foreach($aDetailsList as $sAttCode)
-		{
-			$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
-			if (!$oAttDef->IsExternalField())
-			{
-				$aList[] = $sAttCode;
-			}
-		}
+//		foreach($aDetailsList as $sAttCode)
+//		{
+//			$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
+//			if (!$oAttDef->IsExternalField())
+//			{
+//				$aList[] = $sAttCode;
+//			}
+//		}
 
-		foreach($aList as $sAttCode)
+		$aDetailsList = MetaModel::GetZListItems($sClass, 'details');
+		$aDetailsStruct = self::ProcessZlist($aDetailsList, array('UI:PropertiesTab' => array()), 'UI:PropertiesTab', 'col1', '');
+		$sHtml = '';
+		$aDetails = array();
+		foreach($aDetailsStruct as $sTab => $aCols )
 		{
-			$iFlags = $this->GetAttributeFlags($sAttCode);
-			$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
-			if ( (!$oAttDef->IsLinkSet()) && (($iFlags & OPT_ATT_HIDDEN) == 0))
+			$aDetails[$sTab] = array();
+			ksort($aCols);
+			$oPage->SetCurrentTab(Dict::S($sTab));
+			$oPage->add('<table style="vertical-align:top"><tr>');
+			foreach($aCols as $sColIndex => $aFieldsets)
 			{
-				if ($oAttDef->IsWritable())
+				$oPage->add('<td style="vertical-align:top">');
+				//$aDetails[$sTab][$sColIndex] = array();
+				foreach($aFieldsets as $sFieldsetName => $aFields)
 				{
-					if ($sStateAttCode == $sAttCode)
+					$aDetails[$sTab][$sColIndex] = array();
+					if (!empty($sFieldsetName))
 					{
-						// State attribute is always read-only from the UI
-						$sHTMLValue = $this->GetStateLabel();
-						$aDetails[] = array('label' => $oAttDef->GetLabel(), 'value' => $sHTMLValue);
+						$oPage->add('<fieldset>');
+						$oPage->add('<legend>'.Dict::S($sFieldsetName).'</legend>');
 					}
-					else
+					foreach($aFields as $sAttCode)
 					{
-						$iFlags = $this->GetAttributeFlags($sAttCode);				
-						if ($iFlags & OPT_ATT_HIDDEN)
-						{
-							// Attribute is hidden, do nothing
-						}
-						else
+						$aVal = null;
+						$iFlags = $this->GetAttributeFlags($sAttCode);
+						$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
+						if ( (!$oAttDef->IsLinkSet()) && (($iFlags & OPT_ATT_HIDDEN) == 0))
 						{
-							if ($iFlags & OPT_ATT_READONLY)
+							if ($oAttDef->IsWritable())
 							{
-								// Attribute is read-only
-								$sHTMLValue = $this->GetAsHTML($sAttCode);
+								if ($sStateAttCode == $sAttCode)
+								{
+									// State attribute is always read-only from the UI
+									$sHTMLValue = $this->GetStateLabel();
+									$aVal = array('label' => $oAttDef->GetLabel(), 'value' => $sHTMLValue);
+								}
+								else
+								{
+									$iFlags = $this->GetAttributeFlags($sAttCode);				
+									if ($iFlags & OPT_ATT_HIDDEN)
+									{
+										// Attribute is hidden, do nothing
+									}
+									else
+									{
+										if ($iFlags & OPT_ATT_READONLY)
+										{
+											// Attribute is read-only
+											$sHTMLValue = $this->GetAsHTML($sAttCode);
+										}
+										else
+										{
+											$sValue = $this->Get($sAttCode);
+											$sDisplayValue = $this->GetEditValue($sAttCode);
+											$aArgs = array('this' => $this);
+											$sInputId = $this->m_iFormId.'_'.$sAttCode;
+											$sHTMLValue = "<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>';
+											$aFieldsMap[$sAttCode] = $sInputId;
+											
+										}
+										$aVal = array('label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().'</span>', 'value' => $sHTMLValue);
+									}
+								}
 							}
 							else
 							{
-								$sValue = $this->Get($sAttCode);
-								$sDisplayValue = $this->GetEditValue($sAttCode);
-								$aArgs = array('this' => $this);
-								$sInputId = $this->m_iFormId.'_'.$sAttCode;
-								$sHTMLValue = "<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>';
-								$aFieldsMap[$sAttCode] = $sInputId;
-								
+								$aVal = array('label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().'</span>', 'value' => $this->GetAsHTML($sAttCode));			
 							}
-							$aDetails[] = array('label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().'</span>', 'value' => $sHTMLValue);
 						}
+						if ($aVal != null)
+						{
+							// The field is visible, add it to the current column
+							$aDetails[$sTab][$sColIndex][] = $aVal;
+						}				
+					}
+					$oPage->Details($aDetails[$sTab][$sColIndex]);
+					if (!empty($sFieldsetName))
+					{
+						$oPage->add('</fieldset>');
 					}
 				}
-				else
-				{
-					$aDetails[] = array('label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().'</span>', 'value' => $this->GetAsHTML($sAttCode));			
-				}
+				$oPage->add('</td>');
 			}
+			$oPage->add('</tr></table>');
 		}
-		$oPage->details($aDetails);
+
 		// Now display the relations, one tab per relation
 
 		$this->DisplayBareRelations($oPage, true); // Edit mode
@@ -1413,7 +1468,7 @@ EOF
 			}
 			else
 			{
-				$aResult = array_merge($aResult, $this->FlattenZList($value));
+				$aResult = array_merge($aResult,self::FlattenZList($value));
 			}
 		}
 		return $aResult;

+ 20 - 3
pages/ajax.csvimport.php

@@ -104,6 +104,13 @@ function GetMappingForField($sClassName, $sFieldName, $iFieldIndex, $bAdvancedMo
 	$aChoices = array('' => Dict::S('UI:CSVImport:MappingSelectOne'));
 	$aChoices[':none:'] = Dict::S('UI:CSVImport:MappingNotApplicable');
 	$sFieldCode = ''; // Code of the attribute, if there is a match
+	$aMatches  = array();
+	if (preg_match('/^(.+)\*$/', $sFieldName, $aMatches))
+	{
+		// Remove any trailing "star" character.
+		// A star character at the end can be used to indicate a mandatory field
+		$sFieldName = $aMatches[1];
+	}
 	if ($sFieldName == 'id')
 	{
 		$sFieldCode = 'id';
@@ -114,6 +121,7 @@ function GetMappingForField($sClassName, $sFieldName, $iFieldIndex, $bAdvancedMo
 	}
 	foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef)
 	{
+		$sStar = '';
 		if ($oAttDef->IsExternalKey())
 		{
 			if ( ($sFieldName == $oAttDef->GetLabel()) || ($sFieldName == $sAttCode))
@@ -124,6 +132,11 @@ function GetMappingForField($sClassName, $sFieldName, $iFieldIndex, $bAdvancedMo
 			{
 				$aChoices[$sAttCode] = $oAttDef->GetLabel();
 			}
+			$oExtKeyAttDef = MetaModel::GetAttributeDef($sClassName, $oAttDef->GetKeyAttCode());
+			if (!$oExtKeyAttDef->IsNullAllowed())
+			{
+				$sStar = '*';
+			}
 			// Get fields of the external class that are considered as reconciliation keys
 			$sTargetClass = $oAttDef->GetTargetClass();
 			foreach(MetaModel::ListAttributeDefs($sTargetClass) as $sTargetAttCode => $oTargetAttDef)
@@ -140,7 +153,7 @@ function GetMappingForField($sClassName, $sFieldName, $iFieldIndex, $bAdvancedMo
 					{
 					
 						// When not in advanced mode do not allow to use reconciliation keys (on external keys) if they are themselves external keys !
-						$aChoices[$sAttCode.'->'.$sTargetAttCode] = $oAttDef->GetLabel().'->'.$oTargetAttDef->GetLabel().$sSuffix;
+						$aChoices[$sAttCode.'->'.$sTargetAttCode] = $oAttDef->GetLabel().'->'.$oTargetAttDef->GetLabel().$sSuffix.$sStar;
 						if ((strcasecmp($sFieldName, $aChoices[$sAttCode.'->'.$sTargetAttCode]) == 0) || (strcasecmp($sFieldName, ($sAttCode.'->'.$sTargetAttCode.$sSuffix)) == 0) )
 						{
 							$sFieldCode = $sAttCode.'->'.$sTargetAttCode;
@@ -151,7 +164,11 @@ function GetMappingForField($sClassName, $sFieldName, $iFieldIndex, $bAdvancedMo
 		}
 		else if ( ($oAttDef->IsWritable()) && (!$oAttDef->IsLinkSet()) )
 		{
-			$aChoices[$sAttCode] = $oAttDef->GetLabel();
+			if (!$oAttDef->IsNullAllowed())
+			{
+				$sStar = '*';
+			}
+			$aChoices[$sAttCode] = $oAttDef->GetLabel().$sStar;
 			if ( ($sFieldName == $oAttDef->GetLabel()) || ($sFieldName == $sAttCode))
 			{
 				$sFieldCode = $sAttCode;
@@ -356,7 +373,7 @@ EOF
 	$oSearch = new DBObjectSearch($sClassName);
 	$oSearch->AddCondition('id', 0, '='); // Make sure we create an empty set
 	$oSet = new CMDBObjectSet($oSearch);
-	$sResult = cmdbAbstractObject::GetSetAsCSV($oSet);
+	$sResult = cmdbAbstractObject::GetSetAsCSV($oSet, array('showMandatoryFields' => true));
 	//$aCSV = explode("\n", $sCSV);
 	// If there are more than one line, let's assume that the first line is a comment and skip it.
 	//if (count($aCSV) > 1)

+ 6 - 0
webservices/import.php

@@ -279,6 +279,12 @@ try
 	$aExtKeys = array();
 	foreach($aRawFieldList as $iFieldId => $sFieldName)
 	{
+		$aMatches = array();
+		if (preg_match('/^(.+)\*$/', $sFieldName, $aMatches))
+		{
+			// Ignore any trailing "star" (*) that simply indicates a mandatory field
+			$sFieldName = $aMatches[1];
+		}
 		if (preg_match('/^(.*)->(.*)$/', trim($sFieldName), $aMatches))
 		{
 			// The column has been specified as "extkey->attcode"