/** * Updates the object form POSTED arguments, and writes it into the DB (applies a stimuli if requested) * @param DBObject $oObj The object to update * $param array $aAttList If set, this will limit the list of updated attributes * @return void */ public function DoUpdateObjectFromPostedForm(DBObject $oObj, $aAttList = null) { $sTransactionId = utils::ReadPostedParam('transaction_id', ''); if (!utils::IsTransactionValid($sTransactionId)) { throw new TransactionException(); } $sClass = get_class($oObj); $sStimulus = trim(utils::ReadPostedParam('apply_stimulus', '')); $sTargetState = ''; if (!empty($sStimulus)) { // Compute the target state $aTransitions = $oObj->EnumTransitions(); if (!isset($aTransitions[$sStimulus])) { throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $oObj->GetName(), $oObj->GetStateLabel())); } $sTargetState = $aTransitions[$sStimulus]['target_state']; } $oObj->UpdateObjectFromPostedForm('', $aAttList, $sTargetState); // Optional: apply a stimulus // if (!empty($sStimulus)) { if (!$oObj->ApplyStimulus($sStimulus)) { throw new Exception("Cannot apply stimulus '{$sStimulus}' to {$oObj->GetName()}"); } } if ($oObj->IsModified()) { // Record the change // $oObj->DBUpdate(); // Trigger ? // $aClasses = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL); $sClassList = implode(", ", CMDBSource::Quote($aClasses)); $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnPortalUpdate AS t WHERE t.target_class IN ({$sClassList})")); while ($oTrigger = $oSet->Fetch()) { $oTrigger->DoActivate($oObj->ToArgs('this')); } $this->p("<h1>" . Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName()) . "</h1>\n"); } $bLockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled'); if ($bLockEnabled) { // Release the concurrent lock, if any $sOwnershipToken = utils::ReadPostedParam('ownership_token', null, false, 'raw_data'); if ($sOwnershipToken !== null) { // We're done, let's release the lock iTopOwnershipLock::ReleaseLock(get_class($oObj), $oObj->GetKey(), $sOwnershipToken); } } }
public function DisplayStimulusForm(WebPage $oPage, $sStimulus) { $sClass = get_class($this); $iKey = $this->GetKey(); $aTransitions = $this->EnumTransitions(); $aStimuli = MetaModel::EnumStimuli($sClass); if (!isset($aTransitions[$sStimulus])) { // Invalid stimulus 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; if ($LockEnabled) { $sOwnershipToken = utils::ReadPostedParam('ownership_token', null, false, 'raw_data'); $aLockInfo = iTopOwnershipLock::AcquireLock($sClass, $iKey); if ($aLockInfo['success']) { $sOwnershipToken = $aLockInfo['token']; $sOwnershipDate = $aLockInfo['acquired']; } else { $oOwner = $aLockInfo['lock']->GetOwner(); // If the object is locked by the current user, it's worth trying again, since // the lock may be released by 'onunload' which is called AFTER loading the current page. //$bTryAgain = $oOwner->GetKey() == UserRights::GetUserId(); self::ReloadAndDisplay($oPage, $this, array('operation' => 'stimulus', 'stimulus' => $sStimulus)); return; } } $sActionLabel = $aStimuli[$sStimulus]->GetLabel(); $sActionDetails = $aStimuli[$sStimulus]->GetDescription(); $aTransition = $aTransitions[$sStimulus]; $sTargetState = $aTransition['target_state']; $aTargetStates = MetaModel::EnumStates($sClass); $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"); $aTargetState = $aTargetStates[$sTargetState]; $aExpectedAttributes = $aTargetState['attribute_list']; $oPage->add("<h1>{$sActionDetails}</h1>\n"); $sButtonsPosition = MetaModel::GetConfig()->Get('buttons_position'); if ($sButtonsPosition == 'bottom') { // bottom: Displays the ticket details BEFORE the actions $oPage->add('<div class="ui-widget-content">'); $this->DisplayBareProperties($oPage); $oPage->add('</div>'); } $oPage->add("<div class=\"wizContainer\">\n"); $oPage->add("<form id=\"apply_stimulus\" method=\"post\" onSubmit=\"return OnSubmit('apply_stimulus');\">\n"); $aDetails = array(); $iFieldIndex = 0; $aFieldsMap = array(); // The list of candidate fields is made of the ordered list of "details" attributes + other attributes $aAttributes = array(); foreach ($this->FlattenZList(MetaModel::GetZListItems($sClass, 'details')) as $sAttCode) { $aAttributes[$sAttCode] = true; } foreach (MetaModel::GetAttributesList($sClass) as $sAttCode) { if (!array_key_exists($sAttCode, $aAttributes)) { $aAttributes[$sAttCode] = true; } } // Order the fields based on their dependencies, set the fields for which there is only one possible value // and perform this in the order of dependencies to avoid dead-ends $aDeps = array(); foreach ($aAttributes as $sAttCode => $trash) { $aDeps[$sAttCode] = MetaModel::GetPrerequisiteAttributes($sClass, $sAttCode); } $aList = $this->OrderDependentFields($aDeps); foreach ($aList as $sAttCode) { // Consider only the "expected" fields for the target state if (array_key_exists($sAttCode, $aExpectedAttributes)) { $iExpectCode = $aExpectedAttributes[$sAttCode]; // 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 && $this->Get($sAttCode) == '') { $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); $aArgs = array('this' => $this); // If the field is mandatory, set it to the only possible value if (!$oAttDef->IsNullAllowed() || $iExpectCode & OPT_ATT_MANDATORY) { if ($oAttDef->IsExternalKey()) { $oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs); if ($oAllowedValues->Count() == 1) { $oRemoteObj = $oAllowedValues->Fetch(); $this->Set($sAttCode, $oRemoteObj->GetKey()); } } else { $aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, $aArgs); if (count($aAllowedValues) == 1) { $aValues = array_keys($aAllowedValues); $this->Set($sAttCode, $aValues[0]); } } } $sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $this->Get($sAttCode), $this->GetEditValue($sAttCode), 'att_' . $iFieldIndex, '', $iExpectCode, $aArgs); $aDetails[] = array('label' => '<span>' . $oAttDef->GetLabel() . '</span>', 'value' => "<span id=\"field_att_{$iFieldIndex}\">{$sHTMLValue}</span>"); $aFieldsMap[$sAttCode] = 'att_' . $iFieldIndex; $iFieldIndex++; } } } $oPage->add('<table><tr><td>'); $oPage->details($aDetails); $oPage->add('</td></tr></table>'); $oPage->add("<input type=\"hidden\" name=\"id\" value=\"" . $this->GetKey() . "\" id=\"id\">\n"); $aFieldsMap['id'] = 'id'; $oPage->add("<input type=\"hidden\" name=\"class\" value=\"{$sClass}\">\n"); $oPage->add("<input type=\"hidden\" name=\"operation\" value=\"apply_stimulus\">\n"); $oPage->add("<input type=\"hidden\" name=\"stimulus\" value=\"{$sStimulus}\">\n"); $iTransactionId = utils::GetNewTransactionId(); $oPage->add("<input type=\"hidden\" name=\"transaction_id\" value=\"" . $iTransactionId . "\">\n"); if ($sOwnershipToken !== null) { $oPage->add("<input type=\"hidden\" name=\"ownership_token\" value=\"" . htmlentities($sOwnershipToken, ENT_QUOTES, 'UTF-8') . "\">\n"); } $oAppContext = new ApplicationContext(); $oPage->add($oAppContext->GetForForm()); $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"); if ($sButtonsPosition != 'top') { // bottom or both: Displays the ticket details AFTER the actions $oPage->add('<div class="ui-widget-content">'); $this->DisplayBareProperties($oPage); $oPage->add('</div>'); } $iFieldsCount = count($aFieldsMap); $sJsonFieldsMap = json_encode($aFieldsMap); $oPage->add_script(<<<EOF \t\t// Initializes the object once at the beginning of the page... \t\tvar oWizardHelper = new WizardHelper('{$sClass}', '', '{$sTargetState}'); \t\toWizardHelper.SetFieldsMap({$sJsonFieldsMap}); \t\toWizardHelper.SetFieldsCount({$iFieldsCount}); EOF ); $sJSToken = json_encode($sOwnershipToken); $oPage->add_ready_script(<<<EOF \t\t// Starts the validation when the page is ready \t\tCheckFields('apply_stimulus', false); \t\t\$(window).unload(function() { return OnUnload('{$iTransactionId}', '{$sClass}', {$iKey}, {$sJSToken}) } ); EOF ); if ($sOwnershipToken !== null) { $this->GetOwnershipJSHandler($oPage, $sOwnershipToken); } }
/** * 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; }
case 'export_cancel': $token = utils::ReadParam('token', null); if ($token !== null) { $oExporter = BulkExport::FindExporterFromToken($token); if ($oExporter) { $oExporter->Cleanup(); } } $aResult = array('code' => 'error', 'percentage' => 100, 'message' => Dict::S('Core:BulkExport:ExportCancelledByUser')); $oPage->add(json_encode($aResult)); break; case 'extend_lock': $sObjClass = utils::ReadParam('obj_class', '', false, 'class'); $iObjKey = (int) utils::ReadParam('obj_key', 0, false, 'integer'); $sToken = utils::ReadParam('token', 0, false, 'raw_data'); $aResult = iTopOwnershipLock::ExtendLock($sObjClass, $iObjKey, $sToken); if (!$aResult['status']) { if ($aResult['operation'] == 'lost') { $sName = $aResult['owner']->GetName(); if ($aResult['owner']->Get('contactid') != 0) { $sName .= ' (' . $aResult['owner']->Get('contactid_friendlyname') . ')'; } $aResult['message'] = Dict::Format('UI:CurrentObjectIsLockedBy_User', $sName); $aResult['popup_message'] = Dict::Format('UI:CurrentObjectIsLockedBy_User_Explanation', $sName); } else { if ($aResult['operation'] == 'expired') { $aResult['message'] = Dict::S('UI:CurrentObjectLockExpired'); $aResult['popup_message'] = Dict::S('UI:CurrentObjectLockExpired_Explanation'); } } }
$oDisplayGraph->Display($oP, $aResults, $sRelation, $oAppContext, array(), $sClassForAttachment, $iIdForAttachment, $sContextKey, array('this' => $oObj)); DisplayNavigatorGroupTab($oP, $aGroups, $sRelation, $oObj); } else { $oP->SetCurrentTab(Dict::S('UI:RelationshipGraph')); $oDisplayGraph->Display($oP, $aResults, $sRelation, $oAppContext, array(), $sClassForAttachment, $iIdForAttachment, $sContextKey, array('this' => $oObj)); DisplayNavigatorListTab($oP, $aResults, $sRelation, $oObj); DisplayNavigatorGroupTab($oP, $aGroups, $sRelation, $oObj); } $oP->SetCurrentTab(''); break; /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// case 'kill_lock': $sClass = utils::ReadParam('class', ''); $id = utils::ReadParam('id', ''); iTopOwnershipLock::KillLock($sClass, $id); $oObj = MetaModel::GetObject($sClass, $id); ReloadAndDisplay($oP, $oObj, 'concurrent_lock_killed', Dict::S('UI:ConcurrentLockKilled'), 'info'); break; /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// case 'cancel': // An action was cancelled $oP->set_title(Dict::S('UI:OperationCancelled')); $oP->add('<h1>' . Dict::S('UI:OperationCancelled') . '</h1>'); break; /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// default: // Menu node rendering (templates) ApplicationMenu::LoadAdditionalMenus();
/** * Checks if an exclusive lock exists on the specified DBObject. * @param string $sObjClass The class of the object for which to acquire the lock * @param integer $iObjKey The identifier of the object for which to acquire the lock * @return multitype:boolean iTopOwnershipLock Ambigous <boolean, string, DBObjectSet> */ public static function IsLocked($sObjClass, $iObjKey) { $bLocked = false; $oMutex = new iTopMutex('lock_' . $sObjClass . '::' . $iObjKey); $oMutex->Lock(); $oOwnershipLock = new iTopOwnershipLock($sObjClass, $iObjKey); if ($oOwnershipLock->IsOwned()) { $bLocked = true; } $oMutex->Unlock(); return array('locked' => $bLocked, 'owner' => $oOwnershipLock->GetOwner()); }