Kaynağa Gözat

- Allow creation of an ticket in a different initial state via the new 'initial_state_path' attribute.
- Support update of CaseLog fields in bulk_modify mode.

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

dflaven 13 yıl önce
ebeveyn
işleme
c21460e179

+ 15 - 1
application/ajaxwebpage.class.inc.php

@@ -193,7 +193,7 @@ EOF
         if (!empty($this->s_deferred_content))
         {
             echo "<script type=\"text/javascript\">\n";
-            echo "\$('body').append('".$this->s_deferred_content."');\n";
+            echo "\$('body').append('".addslashes(str_replace("\n", '', $this->s_deferred_content))."');\n";
             echo "\n</script>\n";
         }
         if (!empty($this->m_sReadyScript))
@@ -229,6 +229,20 @@ EOF
             parent::add($sHtml);
         }
     }
+
+	/**
+	 * Add any text or HTML fragment (identified by an ID) at the end of the body of the page
+	 * This is useful to add hidden content, DIVs or FORMs that should not
+	 * be embedded into each other.	 	 
+	 */
+    public function add_at_the_end($s_html, $sId = '')
+    {
+    	if ($sId != '')
+    	{
+	    	$this->add_script("$('#{$sId}').remove();"); // Remove any previous instance of the same Id
+    	}
+        $this->s_deferred_content .= $s_html;
+    }
 	
 	/**
 	 * Adds a script to be executed when the DOM is ready (typical JQuery use)

+ 94 - 15
application/cmdbabstract.class.inc.php

@@ -214,7 +214,8 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
 		{
 			if ($oAttDef instanceof AttributeCaseLog)
 			{
-				$this->DisplayCaseLog($oPage, $sAttCode, '', '', $bEditMode);
+				$sComment = (isset($aExtraParams['fieldsComments'][$sAttCode])) ? $aExtraParams['fieldsComments'][$sAttCode] : '';
+				$this->DisplayCaseLog($oPage, $sAttCode, $sComment, $sPrefix, $bEditMode);
 			}
 		}
 
@@ -253,7 +254,14 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
 			$oPage->SetCurrentTab($oAttDef->GetLabel().$sCount);
 			if ($bEditMode)
 			{
-				$iFlags = $this->GetAttributeFlags($sAttCode);
+				if ($this->IsNew())
+				{
+					$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
+				}
+				else
+				{
+					$iFlags = $this->GetAttributeFlags($sAttCode);
+				}
 				$sInputId = $this->m_iFormId.'_'.$sAttCode;
 				if (get_class($oAttDef) == 'AttributeLinkedSet')
 				{
@@ -448,7 +456,18 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
 
 						$sComments = isset($aFieldsComments[$sAttCode]) ? $aFieldsComments[$sAttCode] : '&nbsp;';
 						$sInfos = '&nbsp;';
-						$iFlags = $this->GetAttributeFlags($sAttCode);
+						if ($this->IsNew())
+						{
+							$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
+						}
+						else
+						{
+							$iFlags = $this->GetAttributeFlags($sAttCode);
+						}
+						if (($iFlags & OPT_ATT_MANDATORY) && $this->IsNew())
+						{
+							$iFlags = $iFlags & ~OPT_ATT_READONLY; // Mandatory fields cannot be read-only when creating an object
+						}
 						$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
 						if ( (!$oAttDef->IsLinkSet()) && (($iFlags & OPT_ATT_HIDDEN) == 0))
 						{
@@ -1728,8 +1747,9 @@ EOF
 		}
 
 		$aTransitions = $this->EnumTransitions();
-		if (count($aTransitions))
+		if (!isset($aExtraParams['custom_operation']) && count($aTransitions))
 		{
+			// transitions are displayed only for the standard new/modify actions, not for modify_all or any other case...
 			$oSetToCheckRights = DBObjectSet::FromObject($this);
 			$aStimuli = Metamodel::EnumStimuli($sClass);
 			foreach($aTransitions as $sStimulusCode => $aTransitionDef)
@@ -1748,9 +1768,31 @@ EOF
 		}
 				
 		$sButtonsPosition = MetaModel::GetConfig()->Get('buttons_position');
-		$iTransactionId = utils::GetNewTransactionId();
+		$iTransactionId = isset($aExtraParams['transaction_id']) ? $aExtraParams['transaction_id'] : utils::GetNewTransactionId();
 		$oPage->SetTransactionId($iTransactionId);
 		$oPage->add("<form action=\"$sFormAction\" id=\"form_{$this->m_iFormId}\" enctype=\"multipart/form-data\" method=\"post\" onSubmit=\"return OnSubmit('form_{$this->m_iFormId}');\">\n");
+		$sStatesSelection = '';
+		if (!isset($aExtraParams['custom_operation']) && $this->IsNew())
+		{
+			$aInitialStates = MetaModel::EnumInitialStates($sClass);
+			//$aInitialStates = array('new' => 'foo', 'closed' => 'bar');
+			if (count($aInitialStates) > 1)
+			{
+				$sStatesSelection = Dict::Format('UI:Create_Class_InState', MetaModel::GetName($sClass)).'<select name="obj_state" class="state_select_'.$this->m_iFormId.'">';
+				foreach($aInitialStates as $sStateCode => $sStateData)
+				{
+					$sSelected = '';
+					if ($sStateCode == $this->GetState())
+					{
+						$sSelected = ' selected';
+					}
+					$sStatesSelection .= '<option value="'.$sStateCode.'"'.$sSelected.'>'.MetaModel::GetStateLabel($sClass, $sStateCode).'</option>';
+				}
+				$sStatesSelection .= '</select>';
+				$oPage->add_ready_script("$('.state_select_{$this->m_iFormId}').change( function() { oWizardHelper$sPrefix.ReloadObjectCreationForm('form_{$this->m_iFormId}', $(this).val()); } );");
+			}
+		}
+
 		$sConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
 		$oPage->add_ready_script(
 <<<EOF
@@ -1768,6 +1810,7 @@ EOF
 		if ($sButtonsPosition != 'bottom')
 		{
 			// top or both, display the buttons here
+			$oPage->p($sStatesSelection);
 			$oPage->add($sButtons);
 		}
 
@@ -1793,6 +1836,7 @@ EOF
 		if ($sButtonsPosition != 'top')
 		{
 			// bottom or both: display the buttons here
+			$oPage->p($sStatesSelection);
 			$oPage->add($sButtons);
 		}
 
@@ -1829,6 +1873,7 @@ EOF
 		$sClass = ($oObjectToClone == null) ? $sClass : get_class($oObjectToClone);
 		$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
 		$aStates = MetaModel::EnumStates($sClass);
+		$sStatesSelection = '';
 		
 		if ($oObjectToClone == null)
 		{
@@ -1843,6 +1888,7 @@ EOF
 		{
 			$oObj = clone $oObjectToClone;
 		}
+
 		// Pre-fill the object with default values, when there is only on possible choice
 		// AND the field is mandatory (otherwise there is always the possiblity to let it empty)
 		$aArgs['this'] = $oObj;
@@ -1867,7 +1913,7 @@ EOF
 				$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
 
 				// If the field is mandatory, set it to the only possible value
-				$iFlags = $oObj->GetAttributeFlags($sAttCode);
+				$iFlags = $oObj->GetInitialStateAttributeFlags($sAttCode);
 				if ((!$oAttDef->IsNullAllowed()) || ($iFlags & OPT_ATT_MANDATORY))
 				{
 					if ($oAttDef->IsExternalKey())
@@ -1977,7 +2023,14 @@ EOF
 	protected function GetFieldAsHtml($sClass, $sAttCode, $sStateAttCode)
 	{
 		$retVal = null;
-		$iFlags = $this->GetAttributeFlags($sAttCode);
+		if ($this->IsNew())
+		{
+			$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
+		}
+		else
+		{
+			$iFlags = $this->GetAttributeFlags($sAttCode);
+		}
 		$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
 		if ( (!$oAttDef->IsLinkSet()) && (($iFlags & OPT_ATT_HIDDEN) == 0) )
 		{
@@ -2189,8 +2242,15 @@ EOF
 			// WARNING: if you change this also check the functions DisplayModifyForm and DisplayCaseLog
 			foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
 			{
-				$aVoid = array();
-				$iFlags = $this->GetAttributeFlags($sAttCode, $aVoid, $sTargetState);
+				if ($this->IsNew())
+				{
+					$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
+				}
+				else
+				{
+					$aVoid = array();
+					$iFlags = $this->GetAttributeFlags($sAttCode, $aVoid, $sTargetState);
+				}
 				if ($oAttDef instanceof AttributeCaseLog)
 				{
 					if (!($iFlags & (OPT_ATT_HIDDEN|OPT_ATT_SLAVE|OPT_ATT_READONLY)))
@@ -2206,8 +2266,15 @@ EOF
 		{
 			$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
 			
-			$aVoid = array();
-			$iFlags = $this->GetAttributeFlags($sAttCode, $aVoid, $sTargetState);
+			if ($this->IsNew())
+			{
+				$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
+			}
+			else
+			{
+				$aVoid = array();
+				$iFlags = $this->GetAttributeFlags($sAttCode, $aVoid, $sTargetState);
+			}
 			if ($oAttDef->IsWritable())
 			{
 				if ( $iFlags & (OPT_ATT_HIDDEN | OPT_ATT_READONLY))
@@ -2532,7 +2599,14 @@ EOF
 	{
 		$oPage->SetCurrentTab(Dict::S('UI:PropertiesTab'));
 		$sClass = get_class($this);
-		$iFlags = $this->GetAttributeFlags($sAttCode);
+		if ($this->IsNew())
+		{
+			$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
+		}
+		else
+		{
+			$iFlags = $this->GetAttributeFlags($sAttCode);
+		}
 		if ( $iFlags & OPT_ATT_HIDDEN)
 		{
 			// The case log is hidden do nothing
@@ -2544,7 +2618,7 @@ EOF
 			
 			if ((!$bEditMode) || ($iFlags & (OPT_ATT_READONLY|OPT_ATT_SLAVE)))
 			{
-				// Check if the attribute is not read-only becuase of a synchro...
+				// Check if the attribute is not read-only because of a synchro...
 				$aReasons = array();
 				$sSynchroIcon = '';
 				if ($iFlags & OPT_ATT_SLAVE)
@@ -2570,12 +2644,17 @@ EOF
 				$sValue = $this->Get($sAttCode);
 				$sDisplayValue = $this->GetEditValue($sAttCode);
 				$aArgs = array('this' => $this, 'formPrefix' => $sPrefix);
-				$sHTMLValue = "<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>';
+				$sHTMLValue = '';
+				if ($sComment != '')
+				{
+					$sHTMLValue = '<span>'.$sComment.'</span><br/>';
+				}
+				$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, 'comments' => $sComments, 'infos' => $sInfos);
-			$oPage->add('<fieldset><legend>'.$oAttDef->GetLabel().'&nbsp<span>'.$sComment.'</span></legend>');
+			$oPage->add('<fieldset><legend>'.$oAttDef->GetLabel().'</legend>');
 			$oPage->add($sHTMLValue);
 			$oPage->add('</fieldset>');
 		}

+ 1 - 1
application/ui.linkswidget.class.inc.php

@@ -243,7 +243,7 @@ EOF
 		$sHtmlValue .= "&nbsp;&nbsp;&nbsp;<input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sRemoteClass))."\" onClick=\"oWidget{$this->m_iInputId}.AddObjects();\"></span>\n";
 		$sHtmlValue .= "<span style=\"clear:both;\"><p>&nbsp;</p></span>\n";
 		$sHtmlValue .= "</div>\n";
-		$oPage->add_at_the_end($this->GetObjectPickerDialog($oPage)); // To prevent adding forms inside the main form
+		$oPage->add_at_the_end($this->GetObjectPickerDialog($oPage), "dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}"); // To prevent adding forms inside the main form
 		return $sHtmlValue;
 	}
 	         

+ 2 - 2
application/webpage.class.inc.php

@@ -96,11 +96,11 @@ class WebPage
     }
     
 	/**
-	 * Add any text or HTML fragment at the end of the body of the page
+	 * Add any text or HTML fragment (identified by an ID) at the end of the body of the page
 	 * This is useful to add hidden content, DIVs or FORMs that should not
 	 * be embedded into each other.	 	 
 	 */
-    public function add_at_the_end($s_html)
+    public function add_at_the_end($s_html, $sId = '')
     {
         $this->s_deferred_content .= $s_html;
     }

+ 17 - 0
core/dbobject.class.php

@@ -687,6 +687,23 @@ abstract class DBObject
 		return $iFlags | $iSynchroFlags; // Combine both sets of flags
 	}
 
+	/**
+	 * Returns the set of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...)
+	 * for the given attribute for the current state of the object considered as an INITIAL state
+	 * @param string $sAttCode The code of the attribute
+	 * @return integer Flags: the binary combination of the flags applicable to this attribute
+	 */	 	  	 	
+	public function GetInitialStateAttributeFlags($sAttCode, &$aReasons = array())
+	{
+		$iFlags = 0;
+		$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
+		if (!empty($sStateAttCode))
+		{
+			$iFlags = MetaModel::GetInitialStateAttributeFlags(get_class($this), $this->Get($sStateAttCode), $sAttCode);
+		}
+		return $iFlags; // No need to care about the synchro flags since we'll be creating a new object anyway
+	}
+
 	// check if the given (or current) value is suitable for the attribute
 	// return true if successfull
 	// return the error desciption otherwise

+ 70 - 0
core/metamodel.class.php

@@ -958,6 +958,36 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
 		}
 	}
 
+	/*
+	* Enumerate all possible initial states, including the default one
+	*/
+	public static function EnumInitialStates($sClass)
+	{
+		if (array_key_exists($sClass, self::$m_aStates))
+		{
+			$aRet = array();
+			// Add the states for which the flag 'is_initial_state' is set to <true>
+			foreach(self::$m_aStates[$sClass] as $aStateCode => $aProps)
+			{
+				if (isset($aProps['initial_state_path']))
+				{
+					$aRet[$aStateCode] = $aProps['initial_state_path'];
+				}
+			}
+			// Add the default initial state
+			$sMainInitialState = self::GetDefaultState($sClass);
+			if (!isset($aRet[$sMainInitialState]))
+			{
+				$aRet[$sMainInitialState] = array();
+			}
+			return $aRet;
+		}
+		else
+		{
+			return array();
+		}
+	}
+
 	public static function EnumStimuli($sClass)
 	{
 		if (array_key_exists($sClass, self::$m_aStimuli))
@@ -1014,6 +1044,46 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
 		return $iFlags;
 	}
 	
+	/**
+	 * Combines the flags from the all states that compose the initial_state_path
+	 */
+	public static function GetInitialStateAttributeFlags($sClass, $sState, $sAttCode)
+	{
+		$iFlags = self::GetAttributeFlags($sClass, $sState, $sAttCode); // Be default set the same flags as the 'target' state
+		$sStateAttCode = self::GetStateAttributeCode($sClass);
+		if (!empty($sStateAttCode))
+		{
+			$aStates = MetaModel::EnumInitialStates($sClass);
+			if (array_key_exists($sState, $aStates))
+			{
+				$bReadOnly = (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY);
+				$bHidden = (($iFlags & OPT_ATT_HIDDEN) == OPT_ATT_HIDDEN);
+				foreach($aStates[$sState] as $sPrevState)
+				{
+					$iPrevFlags = self::GetAttributeFlags($sClass, $sPrevState, $sAttCode);
+					$bReadOnly = $bReadOnly && (($iPrevFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY); // if it is/was not readonly => then it's not
+					$bHidden = $bHidden && (($iPrevFlags & OPT_ATT_HIDDEN) == OPT_ATT_HIDDEN); // if it is/was not hidden => then it's not
+				}
+				if ($bReadOnly)
+				{
+					$iFlags = $iFlags | OPT_ATT_READONLY;
+				}
+				else
+				{
+					$iFlags = $iFlags & ~OPT_ATT_READONLY;
+				}
+				if ($bHidden)
+				{
+					$iFlags = $iFlags | OPT_ATT_HIDDEN;
+				}
+				else
+				{
+					$iFlags = $iFlags & ~OPT_ATT_HIDDEN;
+				}
+			}
+		}
+		return $iFlags;
+	}
 	//
 	// Allowed values
 	//

+ 1 - 0
dictionaries/dictionary.itop.ui.php

@@ -927,5 +927,6 @@ When associated with a trigger, each action is given an "order" number, specifyi
 	'UI:FavoriteOrganizations+' => 'Check in the list below the organizations that you want to see in the drop-down menu for a quick access. '.
 								   'Note that this is not a security setting, objects from any organization are still visible and can be accessed by selecting "All Organizations" in the drop-down list.',
 	'UI:NavigateAwayConfirmationMessage' => 'Any modification will be discarded.',
+	'UI:Create_Class_InState' => 'Create the %1$s in state: ',
 ));
 ?>

+ 1 - 0
dictionaries/fr.dictionary.itop.ui.php

@@ -777,5 +777,6 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
 	'UI:FavoriteOrganizations+' => 'Cochez dans la liste ci-dessous les organisations que vous voulez voir listées dans le menu principal. '.
 								   'Ceci n\'est pas un réglage de sécurité. Les objets de toutes les organisations sont toujours visibles en choisissant "Toutes les Organisations" dans le menu.',
 	'UI:NavigateAwayConfirmationMessage' => 'Toute modification sera perdue.',
+	'UI:Create_Class_InState' => 'Créer l\'objet %1$s dans l\'état: ',
 ));
 ?>

+ 17 - 0
js/wizardhelper.js

@@ -189,4 +189,21 @@ function WizardHelper(sClass, sFormPrefix, sState)
 		}
 		this.AjaxQueryServer();
 	}
+	
+	this.ReloadObjectCreationForm = function(sFormId, sTargetState)
+	{
+		$('#'+sFormId).block();
+		this.UpdateWizard();
+		this.ResetQuery();
+		var sTransactionId = $('input[name=transaction_id]').val();
+		$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
+			{ json_obj: this.ToJSON(), operation: 'obj_creation_form', target_state: sTargetState, transaction_id: sTransactionId },
+			function(data)
+			{
+				$('#'+sFormId).html(data);
+				onDelayedReady();
+				$('#'+sFormId).unblock();
+			}
+		);		
+	}
 }

+ 10 - 0
pages/UI.php

@@ -835,6 +835,10 @@ try
 					if ($oAttDef->IsScalar() && $oAttDef->IsWritable())
 					{
 						$currValue = $oObj->Get($sAttCode);
+						if ($oAttDef instanceof AttributeCaseLog)
+						{
+							$currValue = '';
+						}
 						if (is_object($currValue)) continue; // Skip non scalar values...
 						if(!array_key_exists($currValue, $aValues[$sAttCode]))
 						{
@@ -1365,6 +1369,12 @@ EOF
 		else
 		{
 			$oObj = MetaModel::NewObject($sClass);
+			$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
+			if (!empty($sStateAttCode))
+			{
+				$sTargetState = utils::ReadPostedParam('obj_state', '');
+				$oObj->Set($sStateAttCode, $sTargetState);
+			}
 			$oObj->UpdateObjectFromPostedForm();
 		}
 		if (isset($oObj) && is_object($oObj))

+ 21 - 2
pages/ajax.render.php

@@ -335,7 +335,14 @@ try
 			$sId = $oWizardHelper->GetIdForField($sAttCode);
 			if ($sId != '')
 			{
-				$iFlags = $oObj->GetAttributeFlags($sAttCode);
+				if ($oObj->IsNew())
+				{
+					$iFlags = $oObj->GetInitialStateAttributeFlags($sAttCode);
+				}
+				else
+				{
+					$iFlags = $oObj->GetAttributeFlags($sAttCode);
+				}
 				if ($iFlags & OPT_ATT_READONLY)
 				{
 					$sHTMLValue = "<span id=\"field_{$sId}\">".$oObj->GetAsHTML($sAttCode);
@@ -360,7 +367,19 @@ try
 		}
 		$oPage->add_script("oWizardHelper{$sFormPrefix}.m_oData=".$oWizardHelper->ToJSON().";\noWizardHelper{$sFormPrefix}.UpdateFields();\n");
 		break;
-			
+		
+		case 'obj_creation_form':
+		$oPage->SetContentType('text/html');
+		$sJson = utils::ReadParam('json_obj', '', false, 'raw_data');
+		$oWizardHelper = WizardHelper::FromJSON($sJson);
+		$oObj = $oWizardHelper->GetTargetObject(); 
+		$sClass = $oWizardHelper->GetTargetClass();
+		$sTargetState = utils::ReadParam('target_state', '');
+		$iTransactionId = utils::ReadParam('transaction_id', '');
+		$oObj->Set(MetaModel::GetStateAttributeCode($sClass), $sTargetState);
+		cmdbAbstractObject::DisplayCreationForm($oPage, $sClass, $oObj, array(), array('action' => utils::GetAbsoluteUrlAppRoot().'pages/UI.php', 'transcation_id' => $iTransactionId)); 
+		break;
+		
 		// DisplayBlock
 		case 'ajax':
 		$oPage->SetContentType('text/html');