public function __construct($sClass, $sAttCode, $iInputId, $sNameSuffix = '', $bDuplicatesAllowed = false) { $this->m_sClass = $sClass; $this->m_sAttCode = $sAttCode; $this->m_sNameSuffix = $sNameSuffix; $this->m_iInputId = $iInputId; $this->m_bDuplicatesAllowed = $bDuplicatesAllowed; $this->m_aEditableFields = array(); $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode); $this->m_sLinkedClass = $oAttDef->GetLinkedClass(); $this->m_sExtKeyToRemote = $oAttDef->GetExtKeyToRemote(); $this->m_sExtKeyToMe = $oAttDef->GetExtKeyToMe(); $oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote); $this->m_sRemoteClass = $oLinkingAttDef->GetTargetClass(); $sExtKeyToMe = $oAttDef->GetExtKeyToMe(); $sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass); $sDefaultState = MetaModel::GetDefaultState($this->m_sClass); $this->m_aEditableFields = array(); $this->m_aTableConfig = array(); $this->m_aTableConfig['form::checkbox'] = array('label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget" . $this->m_iInputId . ".OnSelectChange();\">", 'description' => Dict::S('UI:SelectAllToggle+')); foreach (MetaModel::FlattenZList(MetaModel::GetZListItems($this->m_sLinkedClass, 'list')) as $sAttCode) { $oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sAttCode); if ($sStateAttCode == $sAttCode) { // State attribute is always hidden from the UI } else { if ($oAttDef->IsWritable() && $sAttCode != $sExtKeyToMe && $sAttCode != $this->m_sExtKeyToRemote && $sAttCode != 'finalclass') { $iFlags = MetaModel::GetAttributeFlags($this->m_sLinkedClass, $sDefaultState, $sAttCode); if (!($iFlags & OPT_ATT_HIDDEN) && !($iFlags & OPT_ATT_READONLY)) { $this->m_aEditableFields[] = $sAttCode; $this->m_aTableConfig[$sAttCode] = array('label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription()); } } } } $this->m_aTableConfig['static::key'] = array('label' => MetaModel::GetName($this->m_sRemoteClass), 'description' => MetaModel::GetClassDescription($this->m_sRemoteClass)); foreach (MetaModel::GetZListItems($this->m_sRemoteClass, 'list') as $sFieldCode) { // TO DO: check the state of the attribute: hidden or visible ? if ($sFieldCode != 'finalclass') { $oAttDef = MetaModel::GetAttributeDef($this->m_sRemoteClass, $sFieldCode); $this->m_aTableConfig['static::' . $sFieldCode] = array('label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription()); } } }
/** * Helper to generate a Graphviz code for displaying the life cycle of a class * @param string $sClass The class to display * @return string The Graph description in Graphviz/Dot syntax */ function GraphvizLifecycle($sClass) { $sDotFileContent = ""; $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); if (empty($sStateAttCode)) { //$oPage->p("no lifecycle for this class"); } else { $aStates = MetaModel::EnumStates($sClass); $aStimuli = MetaModel::EnumStimuli($sClass); $sDotFileContent .= "digraph finite_state_machine {\r\n\tgraph [bgcolor = \"transparent\"];\r\n\trankdir=LR;\r\n\tsize=\"12,12\"\r\n\tnode [ fontname=Verdana style=filled fillcolor=\"#ffffff\" ];\r\n\tedge [ fontname=Verdana ];\r\n"; $aStatesLinks = array(); foreach ($aStates as $sStateCode => $aStateDef) { $aStatesLinks[$sStateCode] = array('in' => 0, 'out' => 0); } foreach ($aStates as $sStateCode => $aStateDef) { $sStateLabel = MetaModel::GetStateLabel($sClass, $sStateCode); $sStateDescription = MetaModel::GetStateDescription($sClass, $sStateCode); foreach (MetaModel::EnumTransitions($sClass, $sStateCode) as $sStimulusCode => $aTransitionDef) { $aStatesLinks[$sStateCode]['out']++; $aStatesLinks[$aTransitionDef['target_state']]['in']++; $sStimulusLabel = $aStimuli[$sStimulusCode]->GetLabel(); $sTargetStateLabel = MetaModel::GetStateLabel($sClass, $aTransitionDef['target_state']); $sDotFileContent .= "\t{$sStateCode} -> {$aTransitionDef['target_state']} [ label=\"" . GraphvizEscape($sStimulusLabel) . "\"];\n"; } } foreach ($aStates as $sStateCode => $aStateDef) { if ($aStatesLinks[$sStateCode]['out'] > 0 || $aStatesLinks[$sStateCode]['in'] > 0) { // Show only reachable states $sStateLabel = str_replace(' ', '\\n', MetaModel::GetStateLabel($sClass, $sStateCode)); if ($aStatesLinks[$sStateCode]['in'] == 0 || $aStatesLinks[$sStateCode]['out'] == 0) { // End or Start state, make it look different $sDotFileContent .= "\t{$sStateCode} [ shape=doublecircle,label=\"" . GraphvizEscape($sStateLabel) . "\"];\n"; } else { $sDotFileContent .= "\t{$sStateCode} [ shape=circle,label=\"" . GraphvizEscape($sStateLabel) . "\"];\n"; } } } $sDotFileContent .= "}\n"; } return $sDotFileContent; }
/** * Constructs the PHP target object from the parameters sent to the web page by the wizard * @param boolean $bReadUploadedFiles True to also ready any uploaded file (for blob/document fields) * @return object */ public function GetTargetObject($bReadUploadedFiles = false) { if (isset($this->m_aData['m_oCurrentValues']['id'])) { $oObj = MetaModel::GetObject($this->m_aData['m_sClass'], $this->m_aData['m_oCurrentValues']['id']); } else { $oObj = MetaModel::NewObject($this->m_aData['m_sClass']); } foreach ($this->m_aData['m_oCurrentValues'] as $sAttCode => $value) { // Because this is stored in a Javascript array, unused indexes // are filled with null values and unused keys (stored as strings) contain $$NULL$$ if ($sAttCode != 'id' && $sAttCode !== false && $value !== null && $value !== '$$NULL$$') { $oAttDef = MetaModel::GetAttributeDef($this->m_aData['m_sClass'], $sAttCode); if ($oAttDef->IsLinkSet() && $value != '') { // special handling for lists // assumes this is handled as an array of objects // thus encoded in json like: [ { name:'link1', 'id': 123}, { name:'link2', 'id': 124}...] $aData = json_decode($value, true); // true means decode as a hash array (not an object) // Check what are the meaningful attributes $aFields = $this->GetLinkedWizardStructure($oAttDef); $sLinkedClass = $oAttDef->GetLinkedClass(); $aLinkedObjectsArray = array(); if (!is_array($aData)) { echo "aData: '{$aData}' (value: '{$value}')\n"; } foreach ($aData as $aLinkedObject) { $oLinkedObj = MetaModel::NewObject($sLinkedClass); foreach ($aFields as $sLinkedAttCode) { if (isset($aLinkedObject[$sLinkedAttCode]) && $aLinkedObject[$sLinkedAttCode] !== null) { $sLinkedAttDef = MetaModel::GetAttributeDef($sLinkedClass, $sLinkedAttCode); if ($sLinkedAttDef->IsExternalKey() && $aLinkedObject[$sLinkedAttCode] != '' && $aLinkedObject[$sLinkedAttCode] > 0) { // For external keys: load the target object so that external fields // get filled too $oTargetObj = MetaModel::GetObject($sLinkedAttDef->GetTargetClass(), $aLinkedObject[$sLinkedAttCode]); $oLinkedObj->Set($sLinkedAttCode, $oTargetObj); } else { $oLinkedObj->Set($sLinkedAttCode, $aLinkedObject[$sLinkedAttCode]); } } } $aLinkedObjectsArray[] = $oLinkedObj; } $oSet = DBObjectSet::FromArray($sLinkedClass, $aLinkedObjectsArray); $oObj->Set($sAttCode, $oSet); } else { if ($oAttDef->GetEditClass() == 'Document') { if ($bReadUploadedFiles) { $oDocument = utils::ReadPostedDocument('attr_' . $sAttCode, 'fcontents'); $oObj->Set($sAttCode, $oDocument); } else { // Create a new empty document, just for displaying the file name $oDocument = new ormDocument(null, '', $value); $oObj->Set($sAttCode, $oDocument); } } else { if ($oAttDef->IsExternalKey() && !empty($value) && $value > 0) { // For external keys: load the target object so that external fields // get filled too $oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value); $oObj->Set($sAttCode, $oTargetObj); } else { $oObj->Set($sAttCode, $value); } } } } } if (isset($this->m_aData['m_sState']) && !empty($this->m_aData['m_sState'])) { $oObj->Set(MetaModel::GetStateAttributeCode($this->m_aData['m_sClass']), $this->m_aData['m_sState']); } $oObj->DoComputeValues(); return $oObj; }
/** * Display a form for modifying several objects at once * The form will be submitted to the current page, with the specified additional values */ public static function DisplayBulkModifyForm($oP, $sClass, $aSelectedObj, $sCustomOperation, $sCancelUrl, $aExcludeAttributes = array(), $aContextData = array()) { if (count($aSelectedObj) > 0) { $iAllowedCount = count($aSelectedObj); $sSelectedObj = implode(',', $aSelectedObj); $sOQL = "SELECT {$sClass} WHERE id IN (" . $sSelectedObj . ")"; $oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL)); // Compute the distribution of the values for each field to determine which of the "scalar" fields are homogenous $aList = MetaModel::ListAttributeDefs($sClass); $aValues = array(); foreach ($aList as $sAttCode => $oAttDef) { if ($oAttDef->IsScalar()) { $aValues[$sAttCode] = array(); } } while ($oObj = $oSet->Fetch()) { foreach ($aList as $sAttCode => $oAttDef) { if ($oAttDef->IsScalar() && $oAttDef->IsWritable()) { $currValue = $oObj->Get($sAttCode); if ($oAttDef instanceof AttributeCaseLog) { $currValue = ' '; // Don't put an empty string, in case the field would be considered as mandatory... } if (is_object($currValue)) { continue; } // Skip non scalar values... if (!array_key_exists($currValue, $aValues[$sAttCode])) { $aValues[$sAttCode][$currValue] = array('count' => 1, 'display' => $oObj->GetAsHTML($sAttCode)); } else { $aValues[$sAttCode][$currValue]['count']++; } } } } // Now create an object that has values for the homogenous values only $oDummyObj = new $sClass(); // @@ What if the class is abstract ? $aComments = array(); function MyComparison($a, $b) { if ($a['count'] == $b['count']) { return 0; } return $a['count'] > $b['count'] ? -1 : 1; } $iFormId = cmdbAbstractObject::GetNextFormId(); // Identifier that prefixes all the form fields $sReadyScript = ''; $aDependsOn = array(); $sFormPrefix = '2_'; foreach ($aList as $sAttCode => $oAttDef) { $aPrerequisites = MetaModel::GetPrerequisiteAttributes($sClass, $sAttCode); // List of attributes that are needed for the current one if (count($aPrerequisites) > 0) { // When 'enabling' a field, all its prerequisites must be enabled too $sFieldList = "['{$sFormPrefix}" . implode("','{$sFormPrefix}", $aPrerequisites) . "']"; $oP->add_ready_script("\$('#enable_{$sFormPrefix}{$sAttCode}').bind('change', function(evt, sFormId) { return PropagateCheckBox( this.checked, {$sFieldList}, true); } );\n"); } $aDependents = MetaModel::GetDependentAttributes($sClass, $sAttCode); // List of attributes that are needed for the current one if (count($aDependents) > 0) { // When 'disabling' a field, all its dependent fields must be disabled too $sFieldList = "['{$sFormPrefix}" . implode("','{$sFormPrefix}", $aDependents) . "']"; $oP->add_ready_script("\$('#enable_{$sFormPrefix}{$sAttCode}').bind('change', function(evt, sFormId) { return PropagateCheckBox( this.checked, {$sFieldList}, false); } );\n"); } if ($oAttDef->IsScalar() && $oAttDef->IsWritable()) { if ($oAttDef->GetEditClass() == 'One Way Password') { $sTip = "Unknown values"; $sReadyScript .= "\$('#multi_values_{$sAttCode}').qtip( { content: '{$sTip}', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );"; $oDummyObj->Set($sAttCode, null); $aComments[$sAttCode] = '<input type="checkbox" id="enable_' . $iFormId . '_' . $sAttCode . '" onClick="ToogleField(this.checked, \'' . $iFormId . '_' . $sAttCode . '\')"/>'; $aComments[$sAttCode] .= '<div class="multi_values" id="multi_values_' . $sAttCode . '"> ? </div>'; $sReadyScript .= 'ToogleField(false, \'' . $iFormId . '_' . $sAttCode . '\');' . "\n"; } else { $iCount = count($aValues[$sAttCode]); if ($iCount == 1) { // Homogenous value reset($aValues[$sAttCode]); $aKeys = array_keys($aValues[$sAttCode]); $currValue = $aKeys[0]; // The only value is the first key //echo "<p>current value for $sAttCode : $currValue</p>"; $oDummyObj->Set($sAttCode, $currValue); $aComments[$sAttCode] = ''; if ($sAttCode != MetaModel::GetStateAttributeCode($sClass)) { $aComments[$sAttCode] .= '<input type="checkbox" checked id="enable_' . $iFormId . '_' . $sAttCode . '" onClick="ToogleField(this.checked, \'' . $iFormId . '_' . $sAttCode . '\')"/>'; } $aComments[$sAttCode] .= '<div class="mono_value">1</div>'; } else { // Non-homogenous value $aMultiValues = $aValues[$sAttCode]; uasort($aMultiValues, 'MyComparison'); $iMaxCount = 5; $sTip = "<p><b>" . Dict::Format('UI:BulkModify_Count_DistinctValues', $iCount) . "</b><ul>"; $index = 0; foreach ($aMultiValues as $sCurrValue => $aVal) { $sDisplayValue = empty($aVal['display']) ? '<i>' . Dict::S('Enum:Undefined') . '</i>' : str_replace(array("\n", "\r"), " ", $aVal['display']); $sTip .= "<li>" . Dict::Format('UI:BulkModify:Value_Exists_N_Times', $sDisplayValue, $aVal['count']) . "</li>"; $index++; if ($iMaxCount == $index) { $sTip .= "<li>" . Dict::Format('UI:BulkModify:N_MoreValues', count($aMultiValues) - $iMaxCount) . "</li>"; break; } } $sTip .= "</ul></p>"; $sTip = addslashes($sTip); $sReadyScript .= "\$('#multi_values_{$sAttCode}').qtip( { content: '{$sTip}', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );"; $oDummyObj->Set($sAttCode, null); $aComments[$sAttCode] = ''; if ($sAttCode != MetaModel::GetStateAttributeCode($sClass)) { $aComments[$sAttCode] .= '<input type="checkbox" id="enable_' . $iFormId . '_' . $sAttCode . '" onClick="ToogleField(this.checked, \'' . $iFormId . '_' . $sAttCode . '\')"/>'; } $aComments[$sAttCode] .= '<div class="multi_values" id="multi_values_' . $sAttCode . '">' . $iCount . '</div>'; } $sReadyScript .= 'ToogleField(' . ($iCount == 1 ? 'true' : 'false') . ', \'' . $iFormId . '_' . $sAttCode . '\');' . "\n"; } } } $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); if ($sStateAttCode != '' && $oDummyObj->GetState() == '') { // Hmmm, it's not gonna work like this ! Set a default value for the "state" // Maybe we should use the "state" that is the most common among the objects... $aMultiValues = $aValues[$sStateAttCode]; uasort($aMultiValues, 'MyComparison'); foreach ($aMultiValues as $sCurrValue => $aVal) { $oDummyObj->Set($sStateAttCode, $sCurrValue); break; } //$oStateAtt = MetaModel::GetAttributeDef($sClass, $sStateAttCode); //$oDummyObj->Set($sStateAttCode, $oStateAtt->GetDefaultValue()); } $oP->add("<div class=\"page_header\">\n"); $oP->add("<h1>" . $oDummyObj->GetIcon() . " " . Dict::Format('UI:Modify_M_ObjectsOf_Class_OutOf_N', $iAllowedCount, $sClass, $iAllowedCount) . "</h1>\n"); $oP->add("</div>\n"); $oP->add("<div class=\"wizContainer\">\n"); $sDisableFields = json_encode($aExcludeAttributes); $aParams = array('fieldsComments' => $aComments, 'noRelations' => true, 'custom_operation' => $sCustomOperation, 'custom_button' => Dict::S('UI:Button:PreviewModifications'), 'selectObj' => $sSelectedObj, 'preview_mode' => true, 'disabled_fields' => $sDisableFields, 'disable_plugins' => true); $aParams = $aParams + $aContextData; // merge keeping associations $oDummyObj->DisplayModifyForm($oP, $aParams); $oP->add("</div>\n"); $oP->add_ready_script($sReadyScript); $oP->add_ready_script(<<<EOF \$('.wizContainer button.cancel').unbind('click'); \$('.wizContainer button.cancel').click( function() { window.location.href = '{$sCancelUrl}'; } ); EOF ); } else { $oP->p("No object selected !, nothing to do"); } }
public function Render(WebPage $oPage, $aParams = array(), $bEditMode = false) { $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this->m_oObj)); $aTemplateFields = array(); preg_match_all('/\\$this->([a-z0-9_]+)\\$/', $this->m_sTemplate, $aMatches); foreach ($aMatches[1] as $sAttCode) { if (MetaModel::IsValidAttCode(get_class($this->m_oObj), $sAttCode)) { $aTemplateFields[] = $sAttCode; } else { $aParams['this->' . $sAttCode] = "<!--Unknown attribute: {$sAttCode}-->"; } } preg_match_all('/\\$this->field\\(([a-z0-9_]+)\\)\\$/', $this->m_sTemplate, $aMatches); foreach ($aMatches[1] as $sAttCode) { if (MetaModel::IsValidAttCode(get_class($this->m_oObj), $sAttCode)) { $aTemplateFields[] = $sAttCode; } else { $aParams['this->field(' . $sAttCode . ')'] = "<!--Unknown attribute: {$sAttCode}-->"; } } $aFieldsComments = isset($aParams['fieldsComments']) ? $aParams['fieldsComments'] : array(); $aFieldsMap = array(); $sClass = get_class($this->m_oObj); // Renders the fields used in the template foreach (MetaModel::ListAttributeDefs(get_class($this->m_oObj)) as $sAttCode => $oAttDef) { $aParams['this->label(' . $sAttCode . ')'] = $oAttDef->GetLabel(); $aParams['this->comments(' . $sAttCode . ')'] = isset($aFieldsComments[$sAttCode]) ? $aFieldsComments[$sAttCode] : ''; $iInputId = '2_' . $sAttCode; // TODO: generate a real/unique prefix... if (in_array($sAttCode, $aTemplateFields)) { if ($this->m_oObj->IsNew()) { $iFlags = $this->m_oObj->GetInitialStateAttributeFlags($sAttCode); } else { $iFlags = $this->m_oObj->GetAttributeFlags($sAttCode); } if ($iFlags & OPT_ATT_MANDATORY && $this->m_oObj->IsNew()) { $iFlags = $iFlags & ~OPT_ATT_READONLY; // Mandatory fields cannot be read-only when creating an object } if (!$oAttDef->IsWritable() || $sStateAttCode == $sAttCode) { $iFlags = $iFlags | OPT_ATT_READONLY; } if ($iFlags & OPT_ATT_HIDDEN) { $aParams['this->label(' . $sAttCode . ')'] = ''; $aParams['this->field(' . $sAttCode . ')'] = ''; $aParams['this->comments(' . $sAttCode . ')'] = ''; $aParams['this->' . $sAttCode] = ''; } else { if ($bEditMode && $iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE)) { // Check if the attribute is not read-only because of a synchro... $aReasons = array(); $sSynchroIcon = ''; if ($iFlags & OPT_ATT_SLAVE) { $iSynchroFlags = $this->m_oObj->GetSynchroReplicaFlags($sAttCode, $aReasons); $sSynchroIcon = " <img id=\"synchro_{$sInputId}\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>"; $sTip = ''; foreach ($aReasons as $aRow) { $sTip .= "<p>Synchronized with {$aRow['name']} - {$aRow['description']}</p>"; } $oPage->add_ready_script("\$('#synchro_{$iInputId}').qtip( { content: '{$sTip}', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );"); } // Attribute is read-only $sHTMLValue = "<span id=\"field_{$iInputId}\">" . $this->m_oObj->GetAsHTML($sAttCode); $sHTMLValue .= '<input type="hidden" id="' . $iInputId . '" name="attr_' . $sAttCode . '" value="' . htmlentities($this->m_oObj->Get($sAttCode), ENT_QUOTES, 'UTF-8') . '"/></span>'; $aFieldsMap[$sAttCode] = $iInputId; $aParams['this->comments(' . $sAttCode . ')'] = $sSynchroIcon; } if ($bEditMode && !($iFlags & OPT_ATT_READONLY)) { $aParams['this->field(' . $sAttCode . ')'] = "<span id=\"field_{$iInputId}\">" . $this->m_oObj->GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $this->m_oObj->Get($sAttCode), $this->m_oObj->GetEditValue($sAttCode), $iInputId, '', $iFlags, array('this' => $this->m_oObj)) . '</span>'; $aFieldsMap[$sAttCode] = $iInputId; } else { $aParams['this->field(' . $sAttCode . ')'] = $this->m_oObj->GetAsHTML($sAttCode); } $aParams['this->' . $sAttCode] = "<table class=\"field\"><tr><td class=\"label\">" . $aParams['this->label(' . $sAttCode . ')'] . ":</td><td>" . $aParams['this->field(' . $sAttCode . ')'] . "</td><td>" . $aParams['this->comments(' . $sAttCode . ')'] . "</td></tr></table>"; } } } // Renders the PlugIns used in the template preg_match_all('/\\$PlugIn:([A-Za-z0-9_]+)->properties\\(\\)\\$/', $this->m_sTemplate, $aMatches); $aPlugInProperties = $aMatches[1]; foreach ($aPlugInProperties as $sPlugInClass) { $oInstance = MetaModel::GetPlugins('iApplicationUIExtension', $sPlugInClass); if ($oInstance != null) { $offset = $oPage->start_capture(); $oInstance->OnDisplayProperties($this->m_oObj, $oPage, $bEditMode); $sContent = $oPage->end_capture($offset); $aParams["PlugIn:{$sPlugInClass}->properties()"] = $sContent; } else { $aParams["PlugIn:{$sPlugInClass}->properties()"] = "Missing PlugIn: {$sPlugInClass}"; } } $offset = $oPage->start_capture(); parent::Render($oPage, $aParams); $sContent = $oPage->end_capture($offset); // Remove empty table rows in case some attributes are hidden... $sContent = preg_replace('/<tr[^>]*>\\s*(<td[^>]*>\\s*<\\/td>)+\\s*<\\/tr>/im', '', $sContent); $oPage->add($sContent); return $aFieldsMap; }
} $oWizardHelper->SetAllowedValuesHtml($sAttCode, $sHTMLValue); } } } $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', 'transaction_id' => $iTransactionId)); break; // DisplayBlock // DisplayBlock case 'ajax': $oPage->SetContentType('text/html'); if ($sFilter != "") { $sExtraParams = stripslashes(utils::ReadParam('extra_params', '', false, 'raw_data')); $aExtraParams = array(); if (!empty($sExtraParams)) { $aExtraParams = json_decode(str_replace("'", '"', $sExtraParams), true); } // Restore the app context from the ExtraParams $oAppContext = new ApplicationContext(false); // false => don't read the context yet !
/** * Renders the "Actions" popup menu for the given set of objects * * Note that the menu links containing (or ending) with a hash (#) will have their fragment * part (whatever is after the hash) dynamically replaced (by javascript) when the menu is * displayed, to correspond to the current hash/fragment in the page. This allows modifying * an object in with the same tab active by default as the tab that was active when selecting * the "Modify..." action. */ public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId) { if ($this->m_sStyle == 'popup') { $this->m_sStyle = 'list'; } $sHtml = ''; $oAppContext = new ApplicationContext(); $sContext = $oAppContext->GetForLink(); if (!empty($sContext)) { $sContext = '&' . $sContext; } $sClass = $this->m_oFilter->GetClass(); $oReflectionClass = new ReflectionClass($sClass); $oSet = new CMDBObjectSet($this->m_oFilter); $sFilter = $this->m_oFilter->serialize(); $sFilterDesc = $this->m_oFilter->ToOql(true); $aActions = array(); $sUIPage = cmdbAbstractObject::ComputeStandardUIPage($sClass); $sRootUrl = utils::GetAbsoluteUrlAppRoot(); // 1:n links, populate the target object as a default value when creating a new linked object if (isset($aExtraParams['target_attr'])) { $aExtraParams['default'][$aExtraParams['target_attr']] = $aExtraParams['object_id']; } $sDefault = ''; if (!empty($aExtraParams['default'])) { foreach ($aExtraParams['default'] as $sKey => $sValue) { $sDefault .= "&default[{$sKey}]={$sValue}"; } } $bIsCreationAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_CREATE) == UR_ALLOWED_YES && $oReflectionClass->IsSubclassOf('cmdbAbstractObject'); switch ($oSet->Count()) { case 0: // No object in the set, the only possible action is "new" if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=new&class={$sClass}{$sContext}{$sDefault}"); } break; case 1: $oObj = $oSet->Fetch(); $id = $oObj->GetKey(); $bLocked = false; if (MetaModel::GetConfig()->Get('concurrent_lock_enabled')) { $aLockInfo = iTopOwnershipLock::IsLocked(get_class($oObj), $id); if ($aLockInfo['locked']) { $bLocked = true; //$this->AddMenuSeparator($aActions); //$aActions['concurrent_lock_unlock'] = array ('label' => Dict::S('UI:Menu:ReleaseConcurrentLock'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=kill_lock&class=$sClass&id=$id{$sContext}"); } } $bRawModifiedAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES && $oReflectionClass->IsSubclassOf('cmdbAbstractObject'); $bIsModifyAllowed = !$bLocked && $bRawModifiedAllowed; $bIsDeleteAllowed = !$bLocked && UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet); // Just one object in the set, possible actions are "new / clone / modify and delete" if (!isset($aExtraParams['link_attr'])) { if ($bIsModifyAllowed) { $aActions['UI:Menu:Modify'] = array('label' => Dict::S('UI:Menu:Modify'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=modify&class={$sClass}&id={$id}{$sContext}#"); } if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=new&class={$sClass}{$sContext}{$sDefault}"); } if ($bIsDeleteAllowed) { $aActions['UI:Menu:Delete'] = array('label' => Dict::S('UI:Menu:Delete'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=delete&class={$sClass}&id={$id}{$sContext}"); } // Transitions / Stimuli if (!$bLocked) { $aTransitions = $oObj->EnumTransitions(); if (count($aTransitions)) { $this->AddMenuSeparator($aActions); $aStimuli = Metamodel::EnumStimuli(get_class($oObj)); foreach ($aTransitions as $sStimulusCode => $aTransitionDef) { $iActionAllowed = get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction' ? UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet) : UR_ALLOWED_NO; switch ($iActionAllowed) { case UR_ALLOWED_YES: $aActions[$sStimulusCode] = array('label' => $aStimuli[$sStimulusCode]->GetLabel(), 'url' => "{$sRootUrl}pages/UI.php?operation=stimulus&stimulus={$sStimulusCode}&class={$sClass}&id={$id}{$sContext}"); break; default: // Do nothing } } } } // Relations... $aRelations = MetaModel::EnumRelationsEx($sClass); if (count($aRelations)) { $this->AddMenuSeparator($aActions); foreach ($aRelations as $sRelationCode => $aRelationInfo) { if (array_key_exists('down', $aRelationInfo)) { $aActions[$sRelationCode . '_down'] = array('label' => $aRelationInfo['down'], 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=swf_navigator&relation={$sRelationCode}&direction=down&class={$sClass}&id={$id}{$sContext}"); } if (array_key_exists('up', $aRelationInfo)) { $aActions[$sRelationCode . '_up'] = array('label' => $aRelationInfo['up'], 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=swf_navigator&relation={$sRelationCode}&direction=up&class={$sClass}&id={$id}{$sContext}"); } } } if ($bLocked && $bRawModifiedAllowed) { // Add a special menu to kill the lock, but only to allowed users who can also modify this object $aAllowedProfiles = MetaModel::GetConfig()->Get('concurrent_lock_override_profiles'); $bCanKill = false; $oUser = UserRights::GetUserObject(); $aUserProfiles = array(); if (!is_null($oUser)) { $oProfileSet = $oUser->Get('profile_list'); while ($oProfile = $oProfileSet->Fetch()) { $aUserProfiles[$oProfile->Get('profile')] = true; } } foreach ($aAllowedProfiles as $sProfile) { if (array_key_exists($sProfile, $aUserProfiles)) { $bCanKill = true; break; } } if ($bCanKill) { $this->AddMenuSeparator($aActions); $aActions['concurrent_lock_unlock'] = array('label' => Dict::S('UI:Menu:KillConcurrentLock'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=kill_lock&class={$sClass}&id={$id}{$sContext}"); } } /* $this->AddMenuSeparator($aActions); // Static menus: Email this page & CSV Export $sUrl = ApplicationContext::MakeObjectUrl($sClass, $id); $aActions['UI:Menu:EMail'] = array ('label' => Dict::S('UI:Menu:EMail'), 'url' => "mailto:?subject=".urlencode($oObj->GetRawName())."&body=".urlencode($sUrl)); $aActions['UI:Menu:CSVExport'] = array ('label' => Dict::S('UI:Menu:CSVExport'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."&format=csv{$sContext}"); // The style tells us whether the menu is displayed on a list of one object, or on the details of the given object if ($this->m_sStyle == 'list') { // Actions specific to the list $sOQL = addslashes($sFilterDesc); $aActions['UI:Menu:AddToDashboard'] = array ('label' => Dict::S('UI:Menu:AddToDashboard'), 'url' => "#", 'onclick' => "return DashletCreationDlg('$sOQL')"); } */ } $this->AddMenuSeparator($aActions); foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) { $oSet->Rewind(); foreach ($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $sUrl) { $aActions[$sLabel] = array('label' => $sLabel, 'url' => $sUrl); } } break; default: // Check rights // New / Modify $bIsModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) && $oReflectionClass->IsSubclassOf('cmdbAbstractObject'); $bIsBulkModifyAllowed = !MetaModel::IsAbstract($sClass) && UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY, $oSet) && $oReflectionClass->IsSubclassOf('cmdbAbstractObject'); $bIsBulkDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_DELETE, $oSet); if (isset($aExtraParams['link_attr'])) { $id = $aExtraParams['object_id']; $sTargetAttr = $aExtraParams['target_attr']; $oAttDef = MetaModel::GetAttributeDef($sClass, $sTargetAttr); $sTargetClass = $oAttDef->GetTargetClass(); $bIsDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet); if ($bIsModifyAllowed) { $aActions['UI:Menu:Add'] = array('label' => Dict::S('UI:Menu:Add'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=modify_links&class={$sClass}&link_attr=" . $aExtraParams['link_attr'] . "&target_class={$sTargetClass}&id={$id}&addObjects=true{$sContext}"); } if ($bIsBulkModifyAllowed) { $aActions['UI:Menu:Manage'] = array('label' => Dict::S('UI:Menu:Manage'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=modify_links&class={$sClass}&link_attr=" . $aExtraParams['link_attr'] . "&target_class={$sTargetClass}&id={$id}{$sContext}"); } //if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All...', 'url' => "#"); } } else { // many objects in the set, possible actions are: new / modify all / delete all if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=new&class={$sClass}{$sContext}{$sDefault}"); } if ($bIsBulkModifyAllowed) { $aActions['UI:Menu:ModifyAll'] = array('label' => Dict::S('UI:Menu:ModifyAll'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=select_for_modify_all&class={$sClass}&filter=" . urlencode($sFilter) . "{$sContext}"); } if ($bIsBulkDeleteAllowed) { $aActions['UI:Menu:BulkDelete'] = array('label' => Dict::S('UI:Menu:BulkDelete'), 'url' => "{$sRootUrl}pages/{$sUIPage}?operation=select_for_deletion&filter=" . urlencode($sFilter) . "{$sContext}"); } // Stimuli $aStates = MetaModel::EnumStates($sClass); // Do not perform time consuming computations if there are too may objects in the list $iLimit = MetaModel::GetConfig()->Get('complex_actions_limit'); if (count($aStates) > 0 && ($iLimit == 0 || $oSet->Count() < $iLimit)) { // Life cycle actions may be available... if all objects are in the same state // // Group by <state> $oGroupByExp = new FieldExpression(MetaModel::GetStateAttributeCode($sClass), $this->m_oFilter->GetClassAlias()); $aGroupBy = array('__state__' => $oGroupByExp); $aQueryParams = array(); if (isset($aExtraParams['query_params'])) { $aQueryParams = $aExtraParams['query_params']; } $sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy); $aRes = CMDBSource::QueryToArray($sSql); if (count($aRes) == 1) { // All objects are in the same state... $sState = $aRes[0]['__state__']; $aTransitions = Metamodel::EnumTransitions($sClass, $sState); if (count($aTransitions)) { $this->AddMenuSeparator($aActions); $aStimuli = Metamodel::EnumStimuli($sClass); foreach ($aTransitions as $sStimulusCode => $aTransitionDef) { $oSet->Rewind(); // As soon as the user rights implementation will browse the object set, // then we might consider using OptimizeColumnLoad() here $iActionAllowed = UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet); $iActionAllowed = get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction' ? $iActionAllowed : UR_ALLOWED_NO; switch ($iActionAllowed) { case UR_ALLOWED_YES: case UR_ALLOWED_DEPENDS: $aActions[$sStimulusCode] = array('label' => $aStimuli[$sStimulusCode]->GetLabel(), 'url' => "{$sRootUrl}pages/UI.php?operation=select_bulk_stimulus&stimulus={$sStimulusCode}&state={$sState}&class={$sClass}&filter=" . urlencode($sFilter) . "{$sContext}"); break; default: // Do nothing } } } } } /* $this->AddMenuSeparator($aActions); $sUrl = utils::GetAbsoluteUrlAppRoot(); $aActions['UI:Menu:EMail'] = array ('label' => Dict::S('UI:Menu:EMail'), 'url' => "mailto:?subject=$sFilterDesc&body=".urlencode("{$sUrl}pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."{$sContext}")); $aActions['UI:Menu:CSVExport'] = array ('label' => Dict::S('UI:Menu:CSVExport'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."&format=csv{$sContext}"); $sOQL = addslashes($sFilterDesc); $aActions['UI:Menu:AddToDashboard'] = array ('label' => Dict::S('UI:Menu:AddToDashboard'), 'url' => "#", 'onclick' => "return DashletCreationDlg('$sOQL')"); */ } } $this->AddMenuSeparator($aActions); foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) { $oSet->Rewind(); foreach ($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $data) { if (is_array($data)) { // New plugins can provide javascript handlers via the 'onclick' property //TODO: enable extension of different menus by checking the 'target' property ?? $aActions[$sLabel] = array('label' => $sLabel, 'url' => isset($data['url']) ? $data['url'] : '#', 'onclick' => isset($data['onclick']) ? $data['onclick'] : ''); } else { // Backward compatibility with old plugins $aActions[$sLabel] = array('label' => $sLabel, 'url' => $data); } } } // New extensions based on iPopupMenuItem interface switch ($this->m_sStyle) { case 'list': $oSet->Rewind(); $param = $oSet; $iMenuId = iPopupMenuExtension::MENU_OBJLIST_ACTIONS; break; case 'details': $oSet->Rewind(); $param = $oSet->Fetch(); $iMenuId = iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS; break; } utils::GetPopupMenuItems($oPage, $iMenuId, $param, $aActions); $aFavoriteActions = array(); $aCallSpec = array($sClass, 'GetShortcutActions'); if (is_callable($aCallSpec)) { $aShortcutActions = call_user_func($aCallSpec, $sClass); foreach ($aActions as $key => $aAction) { if (in_array($key, $aShortcutActions)) { $aFavoriteActions[] = $aAction; unset($aActions[$key]); } } } else { $aShortcutActions = array(); } if (count($aFavoriteActions) > 0) { $sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>" . Dict::S('UI:Menu:OtherActions') . "\n<ul>\n"; } else { $sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>" . Dict::S('UI:Menu:Actions') . "\n<ul>\n"; } $sHtml .= $oPage->RenderPopupMenuItems($aActions, $aFavoriteActions); static $bPopupScript = false; if (!$bPopupScript) { // Output this once per page... $oPage->add_ready_script("\$(\"div.itop_popup>ul\").popupmenu();\n"); $bPopupScript = true; } return $sHtml; }
/** * Compute the order of the fields & pages in the wizard * @param $oPage iTopWebPage The current page (used to display error messages) * @param $sClass string Name of the class * @param $sStateCode string Code of the target state of the object * @return hash Two dimensional array: each element represents the list of fields for a given page */ protected function ComputeWizardStructure() { $aWizardSteps = array('mandatory' => array(), 'optional' => array()); $aFieldsDone = array(); // Store all the fields that are already covered by a previous step of the wizard $aStates = MetaModel::EnumStates($this->m_sClass); $sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass); $aMandatoryAttributes = array(); // Some attributes are always mandatory independently of the state machine (if any) foreach (MetaModel::GetAttributesList($this->m_sClass) as $sAttCode) { $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); if (!$oAttDef->IsExternalField() && !$oAttDef->IsNullAllowed() && $oAttDef->IsWritable() && $sAttCode != $sStateAttCode) { $aMandatoryAttributes[$sAttCode] = OPT_ATT_MANDATORY; } } // Now check the attributes that are mandatory in the specified state if (!empty($this->m_sTargetState) && count($aStates[$this->m_sTargetState]['attribute_list']) > 0) { // Check all the fields that *must* be included in the wizard for this // particular target state $aFields = array(); foreach ($aStates[$this->m_sTargetState]['attribute_list'] as $sAttCode => $iOptions) { if (isset($aMandatoryAttributes[$sAttCode]) && $aMandatoryAttributes[$sAttCode] & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) { $aMandatoryAttributes[$sAttCode] |= $iOptions; } else { $aMandatoryAttributes[$sAttCode] = $iOptions; } } } // Check all the fields that *must* be included in the wizard // i.e. all mandatory, must-change or must-prompt fields that are // not also read-only or hidden. // Some fields may be required (null not allowed) from the database // perspective, but hidden or read-only from the user interface perspective $aFields = array(); foreach ($aMandatoryAttributes as $sAttCode => $iOptions) { if ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT) && !($iOptions & (OPT_ATT_READONLY | OPT_ATT_HIDDEN))) { $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); $aPrerequisites = $oAttDef->GetPrerequisiteAttributes(); $aFields[$sAttCode] = array(); foreach ($aPrerequisites as $sCode) { $aFields[$sAttCode][$sCode] = ''; } } } // Now use the dependencies between the fields to order them // Start from the order of the 'details' $aList = MetaModel::FlattenZlist(MetaModel::GetZListItems($this->m_sClass, 'details')); $index = 0; $aOrder = array(); foreach ($aFields as $sAttCode => $void) { $aOrder[$sAttCode] = 999; // At the end of the list... } foreach ($aList as $sAttCode) { if (array_key_exists($sAttCode, $aFields)) { $aOrder[$sAttCode] = $index; } $index++; } foreach ($aFields as $sAttCode => $aDependencies) { // All fields with no remaining dependencies can be entered at this // step of the wizard if (count($aDependencies) > 0) { $iMaxPos = 0; // Remove this field from the dependencies of the other fields foreach ($aDependencies as $sDependentAttCode => $void) { // position the current field after the ones it depends on $iMaxPos = max($iMaxPos, 1 + $aOrder[$sDependentAttCode]); } } } asort($aOrder); $aCurrentStep = array(); foreach ($aOrder as $sAttCode => $rank) { $aCurrentStep[] = $sAttCode; $aFieldsDone[$sAttCode] = ''; } $aWizardSteps['mandatory'][] = $aCurrentStep; // Now computes the steps to fill the optional fields $aFields = array(); // reset foreach (MetaModel::ListAttributeDefs($this->m_sClass) as $sAttCode => $oAttDef) { $iOptions = isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode]) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0; if ($sStateAttCode != $sAttCode && !$oAttDef->IsExternalField() && ($iOptions & (OPT_ATT_HIDDEN | OPT_ATT_READONLY)) == 0 && !isset($aFieldsDone[$sAttCode])) { // 'State', external fields, read-only and hidden fields // and fields that are already listed in the wizard // are removed from the 'optional' part of the wizard $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); $aPrerequisites = $oAttDef->GetPrerequisiteAttributes(); $aFields[$sAttCode] = array(); foreach ($aPrerequisites as $sCode) { if (!isset($aFieldsDone[$sCode])) { // retain only the dependencies that were not covered // in the 'mandatory' part of the wizard $aFields[$sAttCode][$sCode] = ''; } } } } // Now use the dependencies between the fields to order them while (count($aFields) > 0) { $aCurrentStep = array(); foreach ($aFields as $sAttCode => $aDependencies) { // All fields with no remaining dependencies can be entered at this // step of the wizard if (count($aDependencies) == 0) { $aCurrentStep[] = $sAttCode; $aFieldsDone[$sAttCode] = ''; unset($aFields[$sAttCode]); // Remove this field from the dependencies of the other fields foreach ($aFields as $sUpdatedCode => $aDummy) { // remove the dependency unset($aFields[$sUpdatedCode][$sAttCode]); } } } if (count($aCurrentStep) == 0) { // This step of the wizard would contain NO field ! $this->m_oPage->add(Dict::S('UI:Error:WizardCircularReferenceInDependencies')); print_r($aFields); break; } $aWizardSteps['optional'][] = $aCurrentStep; } return $aWizardSteps; }
function DisplayBareRelations(WebPage $oPage, $bEditMode = false) { parent::DisplayBareRelations($oPage, $bEditMode); $sTicketListAttCode = 'tickets_list'; if (MetaModel::IsValidAttCode(get_class($this), $sTicketListAttCode)) { // Display one list per leaf class (the only way to display the status as of now) $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sTicketListAttCode); $sLnkClass = $oAttDef->GetLinkedClass(); $sExtKeyToMe = $oAttDef->GetExtKeyToMe(); $sExtKeyToRemote = $oAttDef->GetExtKeyToRemote(); $iTotal = 0; $aSearches = array(); foreach (MetaModel::EnumChildClasses('Ticket') as $sSubClass) { if (!MetaModel::HasChildrenClasses($sSubClass)) { $sStateAttCode = MetaModel::GetStateAttributeCode($sSubClass); if ($sStateAttCode != '') { $oSearch = DBSearch::FromOQL("SELECT {$sSubClass} AS t JOIN {$sLnkClass} AS lnk ON lnk.{$sExtKeyToRemote} = t.id WHERE {$sExtKeyToMe} = :myself AND {$sStateAttCode} NOT IN ('rejected', 'resolved', 'closed')", array('myself' => $this->GetKey())); $aSearches[$sSubClass] = $oSearch; $oSet = new DBObjectSet($oSearch); $iTotal += $oSet->Count(); } } } $sCount = $iTotal > 0 ? ' (' . $iTotal . ')' : ''; $oPage->SetCurrentTab(Dict::S('Class:FunctionalCI/Tab:OpenedTickets') . $sCount); foreach ($aSearches as $sSubClass => $oSearch) { $sBlockId = __CLASS__ . '_opened_' . $sSubClass; $oPage->add('<fieldset>'); $oPage->add('<legend>' . MetaModel::GetName($sSubClass) . '</legend>'); $oBlock = new DisplayBlock($oSearch, 'list', false); $oBlock->Display($oPage, $sBlockId, array('menu' => false)); $oPage->add('</fieldset>'); } } }
/** * Designed as an action to be called when a stop watch threshold times out * or from within the framework */ public function ApplyStimulus($sStimulusCode, $bDoNotWrite = false) { $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this)); if (empty($sStateAttCode)) { return false; } MyHelpers::CheckKeyInArray('object lifecycle stimulus', $sStimulusCode, MetaModel::EnumStimuli(get_class($this))); $aStateTransitions = $this->EnumTransitions(); if (!array_key_exists($sStimulusCode, $aStateTransitions)) { // This simulus has no effect in the current state... do nothing return; } $aTransitionDef = $aStateTransitions[$sStimulusCode]; // Change the state before proceeding to the actions, this is necessary because an action might // trigger another stimuli (alternative: push the stimuli into a queue) $sPreviousState = $this->Get($sStateAttCode); $sNewState = $aTransitionDef['target_state']; $this->Set($sStateAttCode, $sNewState); // $aTransitionDef is an // array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD $bSuccess = true; foreach ($aTransitionDef['actions'] as $sActionHandler) { // std PHP spec $aActionCallSpec = array($this, $sActionHandler); if (!is_callable($aActionCallSpec)) { throw new CoreException("Unable to call action: " . get_class($this) . "::{$sActionHandler}"); return; } $bRet = call_user_func($aActionCallSpec, $sStimulusCode); // if one call fails, the whole is considered as failed if (!$bRet) { $bSuccess = false; } } if ($bSuccess) { $sClass = get_class($this); // Stop watches foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { if ($oAttDef instanceof AttributeStopWatch) { $oSW = $this->Get($sAttCode); if (in_array($sNewState, $oAttDef->GetStates())) { $oSW->Start($this, $oAttDef); } else { $oSW->Stop($this, $oAttDef); } $this->Set($sAttCode, $oSW); } } if (!$bDoNotWrite) { $this->DBWrite(); } // Change state triggers... $sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL)); $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class IN ('{$sClassList}') AND t.state='{$sPreviousState}'")); while ($oTrigger = $oSet->Fetch()) { $oTrigger->DoActivate($this->ToArgs('this')); } $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class IN ('{$sClassList}') AND t.state='{$sNewState}'")); while ($oTrigger = $oSet->Fetch()) { $oTrigger->DoActivate($this->ToArgs('this')); } } return $bSuccess; }
$oP->set_title(Dict::Format('UI:StimulusModify_N_ObjectsOf_Class', $sActionLabel, count($aSelectObject), $sClass)); $oP->add('<div class="page_header">'); $oP->add('<h1>' . MetaModel::GetClassIcon($sClass) . ' ' . Dict::Format('UI:StimulusModify_N_ObjectsOf_Class', $sActionLabel, count($aSelectObject), $sClass) . '</h1>'); $oP->add('</div>'); $aExpectedAttributes = $aTargetStateDef['attribute_list']; $aDetails = array(); $iFieldIndex = 0; $aFieldsMap = array(); $aValues = array(); $aObjects = array(); foreach ($aSelectObject as $iId) { $aObjects[] = MetaModel::GetObject($sClass, $iId); } $oSet = DBObjectSet::FromArray($sClass, $aObjects); $oObj = $oSet->ComputeCommonObject($aValues); $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); $oObj->Set($sStateAttCode, $sTargetState); $sReadyScript = ''; foreach ($aExpectedAttributes as $sAttCode => $iExpectCode) { // Prompt for an attribute if // - the attribute must be changed or must be displayed to the user for confirmation // - or the field is mandatory and currently empty if ($iExpectCode & (OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT) || $iExpectCode & OPT_ATT_MANDATORY && $oObj->Get($sAttCode) == '') { $aAttributesDef = MetaModel::ListAttributeDefs($sClass); $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); $aPrerequisites = MetaModel::GetPrequisiteAttributes($sClass, $sAttCode); // List of attributes that are needed for the current one if (count($aPrerequisites) > 0) { // When 'enabling' a field, all its prerequisites must be enabled too $sFieldList = "['" . implode("','", $aPrerequisites) . "']"; $oP->add_ready_script("\$('#enable_{$sAttCode}').bind('change', function(evt, sFormId) { return PropagateCheckBox( this.checked, {$sFieldList}, true); } );\n");
/** * Helper for the lifecycle details of a given class */ function DisplayLifecycle($oPage, $sClass) { $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); if (empty($sStateAttCode)) { $oPage->p(Dict::S('UI:Schema:NoLifeCyle')); } else { $aStates = MetaModel::EnumStates($sClass); $aStimuli = MetaModel::EnumStimuli($sClass); $oPage->add("<img src=\"" . utils::GetAbsoluteUrlAppRoot() . "pages/graphviz.php?class={$sClass}\">\n"); $oPage->add("<h3>" . Dict::S('UI:Schema:LifeCycleTransitions') . "</h3>\n"); $oPage->add("<ul>\n"); foreach ($aStates as $sStateCode => $aStateDef) { $sStateLabel = MetaModel::GetStateLabel($sClass, $sStateCode); $sStateDescription = MetaModel::GetStateDescription($sClass, $sStateCode); $oPage->add("<li title=\"code: {$sStateCode}\">{$sStateLabel} <span style=\"color:grey;\">({$sStateCode}) {$sStateDescription}</span></li>\n"); $oPage->add("<ul>\n"); foreach (MetaModel::EnumTransitions($sClass, $sStateCode) as $sStimulusCode => $aTransitionDef) { $sStimulusLabel = $aStimuli[$sStimulusCode]->GetLabel(); $sTargetStateLabel = MetaModel::GetStateLabel($sClass, $aTransitionDef['target_state']); if (count($aTransitionDef['actions']) > 0) { $sActions = " <em>(" . implode(', ', $aTransitionDef['actions']) . ")</em>"; } else { $sActions = ""; } $oPage->add("<li><span style=\"color:red;font-weight=bold;\">{$sStimulusLabel}</span> => {$sTargetStateLabel} {$sActions}</li>\n"); } $oPage->add("</ul>\n"); } $oPage->add("</ul>\n"); $oPage->add("<h3>" . Dict::S('UI:Schema:LifeCyleAttributeOptions') . "</h3>\n"); $oPage->add("<ul>\n"); foreach ($aStates as $sStateCode => $aStateDef) { $sStateLabel = MetaModel::GetStateLabel($sClass, $sStateCode); $sStateDescription = MetaModel::GetStateDescription($sClass, $sStateCode); $oPage->add("<li title=\"code: {$sStateCode}\">{$sStateLabel} <span style=\"color:grey;\">({$sStateCode}) {$sStateDescription}</span></li>\n"); if (count($aStates[$sStateCode]['attribute_list']) > 0) { $oPage->add("<ul>\n"); foreach ($aStates[$sStateCode]['attribute_list'] as $sAttCode => $iOptions) { $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); $sAttLabel = $oAttDef->GetLabel(); $aOptions = array(); if ($iOptions & OPT_ATT_HIDDEN) { $aOptions[] = Dict::S('UI:Schema:LifeCycleHiddenAttribute'); } if ($iOptions & OPT_ATT_READONLY) { $aOptions[] = Dict::S('UI:Schema:LifeCycleReadOnlyAttribute'); } if ($iOptions & OPT_ATT_MANDATORY) { $aOptions[] = Dict::S('UI:Schema:LifeCycleMandatoryAttribute'); } if ($iOptions & OPT_ATT_MUSTCHANGE) { $aOptions[] = Dict::S('UI:Schema:LifeCycleAttributeMustChange'); } if ($iOptions & OPT_ATT_MUSTPROMPT) { $aOptions[] = Dict::S('UI:Schema:LifeCycleAttributeMustPrompt'); } if (count($aOptions)) { $sOptions = implode(', ', $aOptions); } else { $sOptions = ""; } $oPage->add("<li><span style=\"color:purple;font-weight=bold;\">{$sAttLabel}</span> {$sOptions}</li>\n"); } $oPage->add("</ul>\n"); } else { $oPage->p("<em>" . Dict::S('UI:Schema:LifeCycleEmptyList') . "</em>"); } } $oPage->add("</ul>\n"); } }
function MakeDictionaryTemplate($sModules = '', $sLanguage = 'EN US') { $sRes = ''; Dict::SetDefaultLanguage($sLanguage); $aAvailableLanguages = Dict::GetLanguages(); $sDesc = $aAvailableLanguages[$sLanguage]['description']; $sLocalizedDesc = $aAvailableLanguages[$sLanguage]['localized_description']; $sRes .= "// Dictionary conventions\n"; $sRes .= htmlentities("// Class:<class_name>\n", ENT_QUOTES, 'UTF-8'); $sRes .= htmlentities("// Class:<class_name>+\n", ENT_QUOTES, 'UTF-8'); $sRes .= htmlentities("// Class:<class_name>/Attribute:<attribute_code>\n", ENT_QUOTES, 'UTF-8'); $sRes .= htmlentities("// Class:<class_name>/Attribute:<attribute_code>+\n", ENT_QUOTES, 'UTF-8'); $sRes .= htmlentities("// Class:<class_name>/Attribute:<attribute_code>/Value:<value>\n", ENT_QUOTES, 'UTF-8'); $sRes .= htmlentities("// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+\n", ENT_QUOTES, 'UTF-8'); $sRes .= htmlentities("// Class:<class_name>/Stimulus:<stimulus_code>\n", ENT_QUOTES, 'UTF-8'); $sRes .= htmlentities("// Class:<class_name>/Stimulus:<stimulus_code>+\n", ENT_QUOTES, 'UTF-8'); $sRes .= "\n"; // Note: I did not use EnumCategories(), because a given class maybe found in several categories // Need to invent the "module", to characterize the origins of a class if (strlen($sModules) == 0) { $aModules = array('bizmodel', 'core/cmdb', 'gui', 'application', 'addon/userrights', 'monitoring'); } else { $aModules = explode(', ', $sModules); } $sRes .= "//////////////////////////////////////////////////////////////////////\n"; $sRes .= "// Note: The classes have been grouped by categories: " . implode(', ', $aModules) . "\n"; $sRes .= "//////////////////////////////////////////////////////////////////////\n"; foreach ($aModules as $sCategory) { $sRes .= "//////////////////////////////////////////////////////////////////////\n"; $sRes .= "// Classes in '{$sCategory}'\n"; $sRes .= "//////////////////////////////////////////////////////////////////////\n"; $sRes .= "//\n"; $sRes .= "\n"; foreach (MetaModel::GetClasses($sCategory) as $sClass) { if (!MetaModel::HasTable($sClass)) { continue; } $bNotInDico = false; $bNotImportant = true; $sClassRes = "//\n"; $sClassRes .= "// Class: {$sClass}\n"; $sClassRes .= "//\n"; $sClassRes .= "\n"; $sClassRes .= "Dict::Add('{$sLanguage}', '{$sDesc}', '{$sLocalizedDesc}', array(\n"; $sClassRes .= MakeDictEntry("Class:{$sClass}", MetaModel::GetName_Obsolete($sClass), $sClass, $bNotInDico); $sClassRes .= MakeDictEntry("Class:{$sClass}+", MetaModel::GetClassDescription_Obsolete($sClass), '', $bNotImportant); foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { if ($sAttCode == 'friendlyname') { continue; } // Skip this attribute if not originaly defined in this class if (MetaModel::GetAttributeOrigin($sClass, $sAttCode) != $sClass) { continue; } $sClassRes .= MakeDictEntry("Class:{$sClass}/Attribute:{$sAttCode}", $oAttDef->GetLabel_Obsolete(), $sAttCode, $bNotInDico); $sClassRes .= MakeDictEntry("Class:{$sClass}/Attribute:{$sAttCode}+", $oAttDef->GetDescription_Obsolete(), '', $bNotImportant); if ($oAttDef instanceof AttributeEnum) { if (MetaModel::GetStateAttributeCode($sClass) == $sAttCode) { foreach (MetaModel::EnumStates($sClass) as $sStateCode => $aStateData) { if (array_key_exists('label', $aStateData)) { $sValue = $aStateData['label']; } else { $sValue = MetaModel::GetStateLabel($sClass, $sStateCode); } if (array_key_exists('description', $aStateData)) { $sValuePlus = $aStateData['description']; } else { $sValuePlus = MetaModel::GetStateDescription($sClass, $sStateCode); } $sClassRes .= MakeDictEntry("Class:{$sClass}/Attribute:{$sAttCode}/Value:{$sStateCode}", $sValue, '', $bNotInDico); $sClassRes .= MakeDictEntry("Class:{$sClass}/Attribute:{$sAttCode}/Value:{$sStateCode}+", $sValuePlus, '', $bNotImportant); } } else { foreach ($oAttDef->GetAllowedValues() as $sKey => $value) { $sClassRes .= MakeDictEntry("Class:{$sClass}/Attribute:{$sAttCode}/Value:{$sKey}", $value, '', $bNotInDico); $sClassRes .= MakeDictEntry("Class:{$sClass}/Attribute:{$sAttCode}/Value:{$sKey}+", $value, '', $bNotImportant); } } } } foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) { $sClassRes .= MakeDictEntry("Class:{$sClass}/Stimulus:{$sStimulusCode}", $oStimulus->GetLabel_Obsolete(), '', $bNotInDico); $sClassRes .= MakeDictEntry("Class:{$sClass}/Stimulus:{$sStimulusCode}+", $oStimulus->GetDescription_Obsolete(), '', $bNotImportant); } $sClassRes .= "));\n"; $sClassRes .= "\n"; $sRes .= $sClassRes; } } return $sRes; }
function test_object_lifecycle() { echo "<h4>Test object lifecycle</h4>"; self::DumpVariable(MetaModel::GetStateAttributeCode("cmdbContact")); self::DumpVariable(MetaModel::EnumStates("cmdbContact")); self::DumpVariable(MetaModel::EnumStimuli("cmdbContact")); foreach (MetaModel::EnumStates("cmdbContact") as $sStateCode => $aStateDef) { echo "<p>Transition from <strong>{$sStateCode}</strong></p>\n"; self::DumpVariable(MetaModel::EnumTransitions("cmdbContact", $sStateCode)); } $oObj = MetaModel::GetObject("cmdbContact", 18); echo "Current state: " . $oObj->GetState() . "... let's go to school..."; self::DumpVariable($oObj->EnumTransitions()); $oObj->ApplyStimulus("toschool"); echo "New state: " . $oObj->GetState() . "... let's get older..."; self::DumpVariable($oObj->EnumTransitions()); $oObj->ApplyStimulus("raise"); echo "New state: " . $oObj->GetState() . "... let's try to go further... (should give an error)"; self::DumpVariable($oObj->EnumTransitions()); $oObj->ApplyStimulus("raise"); // should give an error }