mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 02:28:44 +02:00
N°2313 - Markup extensibility: Add meta information and hooks
Admin. console: - Class and id on object form - Class current and target state on object transition form - Object form mode (view/edit/create/stimulus) - Object attributes: Attribute code, attribute definition class and raw value - Navigation menu: Their IDs on group and items - Actions menu: Their IDs on items Portal: - Object attributes: Attribute code, attribute definition class and raw value
This commit is contained in:
@@ -43,8 +43,20 @@ require_once(APPROOT.'sources/application/search/criterionconversionabstract.cla
|
||||
require_once(APPROOT.'sources/application/search/criterionconversion/criteriontooql.class.inc.php');
|
||||
require_once(APPROOT.'sources/application/search/criterionconversion/criteriontosearchform.class.inc.php');
|
||||
|
||||
/**
|
||||
* Class cmdbAbstractObject
|
||||
*/
|
||||
abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
{
|
||||
/** @var string ENUM_OBJECT_MODE_VIEW */
|
||||
const ENUM_OBJECT_MODE_VIEW = 'view';
|
||||
/** @var string ENUM_OBJECT_MODE_EDIT */
|
||||
const ENUM_OBJECT_MODE_EDIT = 'edit';
|
||||
/** @var string ENUM_OBJECT_MODE_CREATE */
|
||||
const ENUM_OBJECT_MODE_CREATE = 'create';
|
||||
/** @var string ENUM_OBJECT_MODE_STIMULUS */
|
||||
const ENUM_OBJECT_MODE_STIMULUS = 'stimulus';
|
||||
|
||||
protected $m_iFormId; // The ID of the form used to edit the object (when in edition mode !)
|
||||
protected static $iGlobalFormId = 1;
|
||||
protected $aFieldsMap;
|
||||
@@ -819,6 +831,9 @@ EOF
|
||||
}
|
||||
foreach($aFields as $sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$sAttDefClass = get_class($oAttDef);
|
||||
|
||||
if ($bEditMode)
|
||||
{
|
||||
$sComments = isset($aFieldsComments[$sAttCode]) ? $aFieldsComments[$sAttCode] : '';
|
||||
@@ -829,7 +844,6 @@ EOF
|
||||
// the caller may override some flags if needed
|
||||
$iFlags = $iFlags | $aExtraFlags[$sAttCode];
|
||||
}
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ((!$oAttDef->IsLinkSet()) && (($iFlags & OPT_ATT_HIDDEN) == 0) && !($oAttDef instanceof AttributeDashboard))
|
||||
{
|
||||
$sInputId = $this->m_iFormId.'_'.$sAttCode;
|
||||
@@ -844,7 +858,6 @@ EOF
|
||||
'value' => $sHTMLValue,
|
||||
'comments' => $sComments,
|
||||
'infos' => $sInfos,
|
||||
'attcode' => $sAttCode,
|
||||
);
|
||||
}
|
||||
else
|
||||
@@ -891,7 +904,6 @@ EOF
|
||||
'value' => $sHTMLValue,
|
||||
'comments' => $sComments,
|
||||
'infos' => $sInfos,
|
||||
'attcode' => $sAttCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -902,29 +914,14 @@ EOF
|
||||
'value' => "<span id=\"field_{$sInputId}\">".$this->GetAsHTML($sAttCode)."</span>",
|
||||
'comments' => $sComments,
|
||||
'infos' => $sInfos,
|
||||
'attcode' => $sAttCode,
|
||||
);
|
||||
$aFieldsMap[$sAttCode] = $sInputId;
|
||||
}
|
||||
|
||||
// Checking how the field should be rendered
|
||||
// Note: For view mode, this is done in cmdbAbstractObject::GetFieldAsHtml()
|
||||
// Note 2: Shouldn't this be a property of the AttDef instead an array that we have to maintain?
|
||||
if (in_array($oAttDef->GetEditClass(),
|
||||
array('Text', 'HTML', 'CaseLog', 'CustomFields', 'OQLExpression')))
|
||||
{
|
||||
$val['layout'] = 'large';
|
||||
}
|
||||
else
|
||||
{
|
||||
$val['layout'] = 'small';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$val = null; // Skip this field
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -934,6 +931,26 @@ EOF
|
||||
|
||||
if ($val != null)
|
||||
{
|
||||
// Add extra data for markup generation
|
||||
// - Attribute code and AttributeDef. class
|
||||
$val['attcode'] = $sAttCode;
|
||||
$val['atttype'] = $sAttDefClass;
|
||||
|
||||
// - How the field should be rendered
|
||||
$val['layout'] = (in_array($oAttDef->GetEditClass(), static::GetAttEditClassesToRenderAsLargeField())) ? 'large' : 'small';
|
||||
|
||||
// - For simple fields, we get the raw (stored) value as well
|
||||
$bExcludeRawValue = false;
|
||||
foreach (static::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as $sAttDefClassToExclude)
|
||||
{
|
||||
if (is_a($sAttDefClass, $sAttDefClassToExclude, true))
|
||||
{
|
||||
$bExcludeRawValue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$val['value_raw'] = ($bExcludeRawValue === false) ? $this->Get($sAttCode) : '';
|
||||
|
||||
// The field is visible, add it to the current column
|
||||
$aDetails[$sTab][$sColIndex][] = $val;
|
||||
$iInputId++;
|
||||
@@ -974,17 +991,21 @@ EOF
|
||||
*/
|
||||
public function DisplayDetails(WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
$sTemplate = Utils::ReadFromFile(MetaModel::GetDisplayTemplate(get_class($this)));
|
||||
$sClass = get_class($this);
|
||||
$iKey = $this->GetKey();
|
||||
$sMode = static::ENUM_OBJECT_MODE_VIEW;
|
||||
|
||||
$sTemplate = Utils::ReadFromFile(MetaModel::GetDisplayTemplate($sClass));
|
||||
if (!empty($sTemplate))
|
||||
{
|
||||
$oTemplate = new DisplayTemplate($sTemplate);
|
||||
// Note: to preserve backward compatibility with home-made templates, the placeholder '$pkey$' has been preserved
|
||||
// but the preferred method is to use '$id$'
|
||||
$oTemplate->Render($oPage, array(
|
||||
'class_name' => MetaModel::GetName(get_class($this)),
|
||||
'class' => get_class($this),
|
||||
'pkey' => $this->GetKey(),
|
||||
'id' => $this->GetKey(),
|
||||
'class_name' => MetaModel::GetName($sClass),
|
||||
'class' => $sClass,
|
||||
'pkey' => $iKey,
|
||||
'id' => $iKey,
|
||||
'name' => $this->GetName(),
|
||||
));
|
||||
}
|
||||
@@ -992,7 +1013,11 @@ EOF
|
||||
{
|
||||
// Object's details
|
||||
// template not found display the object using the *old style*
|
||||
$oPage->add('<div id="search-widget-results-outer">');
|
||||
$oPage->add(<<<HTML
|
||||
<!-- Beginning of object-details -->
|
||||
<div id="search-widget-results-outer" class="object-details" data-object-class="$sClass" data-object-id="$iKey" data-object-mode="$sMode">
|
||||
HTML
|
||||
);
|
||||
$this->DisplayBareHeader($oPage, $bEditMode);
|
||||
/** @var \iTopWebPage $oPage */
|
||||
$oPage->AddTabContainer(OBJECT_PROPERTIES_TAB);
|
||||
@@ -1003,8 +1028,11 @@ EOF
|
||||
//$oPage->SetCurrentTab(Dict::S('UI:HistoryTab'));
|
||||
//$this->DisplayBareHistory($oPage, $bEditMode);
|
||||
$oPage->AddAjaxTab(Dict::S('UI:HistoryTab'),
|
||||
utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=history&class='.get_class($this).'&id='.$this->GetKey());
|
||||
$oPage->add('</div>');
|
||||
utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=history&class='.$sClass.'&id='.$iKey);
|
||||
$oPage->add(<<<HTML
|
||||
</div><!-- End of object-details -->
|
||||
HTML
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2399,15 +2427,31 @@ EOF
|
||||
$oPage->add_dict_entry('UI:ValueMustBeChanged');
|
||||
$oPage->add_dict_entry('UI:ValueInvalidFormat');
|
||||
|
||||
// Note: In 2.8, remove the data-attcode attribute (either because it's has been moved to .field_container in 2.7 or even better because the admin. console has been reworked)
|
||||
return "<div id=\"field_{$iId}\" class=\"field_value_container\"><div class=\"attribute-edit\" data-attcode=\"$sAttCode\">{$sHTMLValue}</div></div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function DisplayModifyForm(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
$sOwnershipToken = null;
|
||||
$iKey = $this->GetKey();
|
||||
$sClass = get_class($this);
|
||||
if ($iKey > 0)
|
||||
$sMode = ($iKey > 0) ? static::ENUM_OBJECT_MODE_EDIT : static::ENUM_OBJECT_MODE_CREATE;
|
||||
|
||||
if ($sMode === static::ENUM_OBJECT_MODE_EDIT)
|
||||
{
|
||||
// The concurrent access lock makes sense only for already existing objects
|
||||
$LockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
|
||||
@@ -2442,13 +2486,22 @@ EOF
|
||||
if (isset($aExtraParams['wizard_container']) && $aExtraParams['wizard_container'])
|
||||
{
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
$sHeaderTitle = Dict::Format('UI:ModificationTitle_Class_Object', $sClassLabel,
|
||||
$this->GetName());
|
||||
|
||||
$oPage->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $this->GetRawName(),
|
||||
$sClassLabel)); // Set title will take care of the encoding
|
||||
$oPage->add("<div class=\"page_header\">\n");
|
||||
$oPage->add("<h1>".$this->GetIcon()." ".Dict::Format('UI:ModificationTitle_Class_Object', $sClassLabel,
|
||||
$this->GetName())."</h1>\n");
|
||||
$oPage->add("</div>\n");
|
||||
$oPage->add("<div class=\"wizContainer\">\n");
|
||||
|
||||
$oPage->add(<<<HTML
|
||||
<!-- Beginning of object-details -->
|
||||
<div class="object-details" data-object-class="$sClass" data-object-id="$iKey" data-object-mode="$sMode">
|
||||
<div class="page_header">
|
||||
<h1>{$this->GetIcon()} $sHeaderTitle</h1>
|
||||
</div>
|
||||
<!-- Beginning of wizContainer -->
|
||||
<div class="wizContainer">
|
||||
HTML
|
||||
);
|
||||
}
|
||||
self::$iGlobalFormId++;
|
||||
$this->aFieldsMap = array();
|
||||
@@ -2475,7 +2528,7 @@ EOF
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($iKey > 0)
|
||||
if ($sMode === static::ENUM_OBJECT_MODE_EDIT)
|
||||
{
|
||||
$sApplyButton = Dict::S('UI:Button:Apply');
|
||||
}
|
||||
@@ -2491,7 +2544,7 @@ EOF
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($iKey > 0)
|
||||
if ($sMode === static::ENUM_OBJECT_MODE_EDIT)
|
||||
{
|
||||
$sOperation = 'apply_modify';
|
||||
}
|
||||
@@ -2500,7 +2553,7 @@ EOF
|
||||
$sOperation = 'apply_new';
|
||||
}
|
||||
}
|
||||
if ($iKey > 0)
|
||||
if ($sMode === static::ENUM_OBJECT_MODE_EDIT)
|
||||
{
|
||||
// The object already exists in the database, it's a modification
|
||||
$sButtons = "<input id=\"{$sPrefix}_id\" type=\"hidden\" name=\"id\" value=\"$iKey\">\n";
|
||||
@@ -2597,7 +2650,7 @@ EOF
|
||||
{
|
||||
$aFieldsMap = array();
|
||||
}
|
||||
if ($iKey > 0)
|
||||
if ($sMode === static::ENUM_OBJECT_MODE_EDIT)
|
||||
{
|
||||
$aFieldsMap['id'] = $sPrefix.'_id';
|
||||
}
|
||||
@@ -2638,7 +2691,12 @@ EOF
|
||||
|
||||
if (isset($aExtraParams['wizard_container']) && $aExtraParams['wizard_container'])
|
||||
{
|
||||
$oPage->add("</div>\n");
|
||||
// Close wizContainer and object-details
|
||||
$oPage->add(<<<HTML
|
||||
</div><!-- End of wizContainer -->
|
||||
</div><!-- End of object-details -->
|
||||
HTML
|
||||
);
|
||||
}
|
||||
|
||||
$iFieldsCount = count($aFieldsMap);
|
||||
@@ -2689,6 +2747,20 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param string $sClass
|
||||
* @param null $oObjectToClone
|
||||
* @param array $aArgs
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public static function DisplayCreationForm(WebPage $oPage, $sClass, $oObjectToClone = null, $aArgs = array(), $aExtraParams = array())
|
||||
{
|
||||
$sClass = ($oObjectToClone == null) ? $sClass : get_class($oObjectToClone);
|
||||
@@ -2773,6 +2845,8 @@ EOF
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
$iKey = $this->GetKey();
|
||||
$sMode = static::ENUM_OBJECT_MODE_STIMULUS;
|
||||
|
||||
$aTransitions = $this->EnumTransitions();
|
||||
$aStimuli = MetaModel::EnumStimuli($sClass);
|
||||
if (!isset($aTransitions[$sStimulus]))
|
||||
@@ -2781,6 +2855,7 @@ EOF
|
||||
throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus,
|
||||
$this->GetName(), $this->GetStateLabel()));
|
||||
}
|
||||
|
||||
// Check for concurrent access lock
|
||||
$LockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
|
||||
$sOwnershipToken = null;
|
||||
@@ -2803,12 +2878,22 @@ EOF
|
||||
}
|
||||
$sActionLabel = $aStimuli[$sStimulus]->GetLabel();
|
||||
$sActionDetails = $aStimuli[$sStimulus]->GetDescription();
|
||||
$oPage->add("<div class=\"page_header\">\n");
|
||||
$oPage->add("<h1>$sActionLabel - <span class=\"hilite\">{$this->GetName()}</span></h1>\n");
|
||||
$oPage->set_title($sActionLabel);
|
||||
$oPage->add("</div>\n");
|
||||
$oPage->add("<h1>$sActionDetails</h1>\n");
|
||||
|
||||
// Get info on current state
|
||||
$sCurrentState = $this->GetState();
|
||||
$sTargetState = $aTransitions[$sStimulus]['target_state'];
|
||||
|
||||
$oPage->set_title($sActionLabel);
|
||||
$oPage->add(<<<HTML
|
||||
<!-- Beginning of object-details -->
|
||||
<div class="object-details" data-object-class="$sClass" data-object-id="$iKey" data-object-mode="$sMode" data-object-current-state="$sCurrentState" data-object-target-state="$sTargetState">
|
||||
<div class="page_header">
|
||||
<h1>$sActionLabel - <span class="hilite">{$this->GetName()}</span></h1>
|
||||
</div>
|
||||
<h1>$sActionDetails</h1>
|
||||
HTML
|
||||
);
|
||||
|
||||
$aExpectedAttributes = $this->GetTransitionAttributes($sStimulus /*, current state*/);
|
||||
if ($aPrefillFormParam != null)
|
||||
{
|
||||
@@ -2923,7 +3008,11 @@ EOF
|
||||
$oPage->add("<button type=\"button\" class=\"action cancel\" onClick=\"BackToDetails('$sClass', ".$this->GetKey().", '', '$sOwnershipToken')\"><span>".Dict::S('UI:Button:Cancel')."</span></button> \n");
|
||||
$oPage->add("<button type=\"submit\" class=\"action\"><span>$sActionLabel</span></button>\n");
|
||||
$oPage->add("</form>\n");
|
||||
$oPage->add("</div>\n");
|
||||
$oPage->add(<<<HTML
|
||||
</div>
|
||||
</div><!-- End of object-details -->
|
||||
HTML
|
||||
);
|
||||
if ($sButtonsPosition != 'top')
|
||||
{
|
||||
// bottom or both: Displays the ticket details AFTER the actions
|
||||
@@ -3081,20 +3170,7 @@ EOF
|
||||
'label' => '<span title="'.MetaModel::GetDescription($sClass,
|
||||
$sAttCode).'">'.MetaModel::GetLabel($sClass, $sAttCode).'</span>',
|
||||
'value' => $sDisplayValue,
|
||||
'attcode' => $sAttCode,
|
||||
);
|
||||
|
||||
// Checking how the field should be rendered
|
||||
// Note: For edit mode, this is done in self::GetBareProperties()
|
||||
// Note 2: Shouldn't this be a AttDef property instead of an array to maintain?
|
||||
if (in_array($oAttDef->GetEditClass(), array('Text', 'HTML', 'CaseLog', 'CustomFields', 'OQLExpression')))
|
||||
{
|
||||
$retVal['layout'] = 'large';
|
||||
}
|
||||
else
|
||||
{
|
||||
$retVal['layout'] = 'small';
|
||||
}
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
@@ -4994,4 +5070,40 @@ EOF
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of AttributeDefinition EditClass that should be rendered as large field in the UI
|
||||
*
|
||||
* @return array
|
||||
* @since 2.7.0
|
||||
*/
|
||||
protected static function GetAttEditClassesToRenderAsLargeField(){
|
||||
return array(
|
||||
'CaseLog',
|
||||
'CustomFields',
|
||||
'HTML',
|
||||
'OQLExpression',
|
||||
'Text',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of AttributeDefinition classes that should be excluded from the markup metadata when priting raw value (typically large values)
|
||||
* This markup is mostly aimed at CSS/JS hooks for extensions and Behat tests
|
||||
*
|
||||
* @return array
|
||||
* @since 2.7.0
|
||||
*/
|
||||
protected static function GetAttDefClassesToExcludeFromMarkupMetadataRawValue(){
|
||||
return array(
|
||||
'AttributeBlob',
|
||||
'AttributeCustomFields',
|
||||
'AttributeDashboard',
|
||||
'AttributeLinkedSet',
|
||||
'AttributeStopWatch',
|
||||
'AttributeSubItem',
|
||||
'AttributeTable',
|
||||
'AttributeText'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user