GetMessages(); if (count($aProvidersMessagesData) > 0) { TwigHelper::RenderIntoPage($oP, APPROOT."/", "templates/application/welcome-popup/layout", [ "aProvidersMessagesData" => $aProvidersMessagesData, "sEndpointAbsURIForAcknowledgeMessage" => Router::GetInstance()->GenerateUrl(WelcomePopupController::ROUTE_NAMESPACE.".acknowledge_message"), ]); } Session::Set("welcome", "ok"); // Try just once per session } } /** * Apply the 'next-action' to the given object or redirect to the page that prompts for additional information if needed * * @param $oP WebPage The page for the output * @param $oObj CMDBObject The object to process * @param $sNextAction string The code of the stimulus for the 'action' (i.e. Transition) to apply * * @throws \ApplicationException * @throws \CoreException * @throws \CoreUnexpectedValue */ function ApplyNextAction(Webpage $oP, CMDBObject $oObj, $sNextAction) { // Here handle the apply stimulus $aTransitions = $oObj->EnumTransitions(); if (!isset($aTransitions[$sNextAction])) { // Invalid stimulus throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sNextAction, $oObj->GetName(), $oObj->GetStateLabel())); } // Get the list of missing mandatory fields for the target state, considering only the changes from the previous form (i.e don't prompt twice) $aExpectedAttributes = $oObj->GetTransitionAttributes($sNextAction); if (count($aExpectedAttributes) == 0) { // If all the mandatory fields are already present, just apply the transition silently... if ($oObj->ApplyStimulus($sNextAction)) { $oObj->DBUpdate(); } else { throw new ApplicationException(Dict::S('UI:FailedToApplyStimuli')); } ReloadAndDisplay($oP, $oObj); } else { // redirect to the 'stimulus' action $oAppContext = new ApplicationContext(); //echo "
Missing Attributes
".print_r($aExpectedAttributes, true)."\n"; $oP->add_header('Location: '.utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=stimulus&class='.get_class($oObj).'&stimulus='.$sNextAction.'&id='.$oObj->getKey().$oAppContext->GetForLink(true)); } } function ReloadAndDisplay($oPage, $oObj, $sMessageId = '', $sMessage = '', $sSeverity = null) { $oAppContext = new ApplicationContext(); if ($sMessageId != '') { cmdbAbstractObject::SetSessionMessage(get_class($oObj), $oObj->GetKey(), $sMessageId, $sMessage, $sSeverity, 0, true /* must not exist */); } $oPage->add_header('Location: '.utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class='.get_class($oObj).'&id='.$oObj->getKey().$oAppContext->GetForLink(true)); } /** * Display the session messages relative to the object identified by its "message key" (class::id) * @param string $sMessageKey * @param WebPage $oPage */ function DisplayMessages(string $sMessageKey, WebPage $oPage) { $oPage->AddSessionMessages($sMessageKey); } /** * Helper to update the breadrumb for the current object * @param DBObject $oObj * @param WebPage $oPage * @throws \CoreException * @throws \DictExceptionMissingString */ function SetObjectBreadCrumbEntry(DBObject $oObj, WebPage $oPage) { $sClass = get_class($oObj); // get the leaf class $sIcon = MetaModel::GetClassIcon($sClass, false); $sIconType = iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_IMAGE; if ($sIcon == '') { $sIcon = 'fas fa-cube'; $sIconType = iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES; } $oPage->SetBreadCrumbEntry("ui-details-$sClass-".$oObj->GetKey(), $oObj->Get('friendlyname'), MetaModel::GetName($sClass).': '.$oObj->Get('friendlyname'), '', $sIcon, $sIconType); } /** * Displays the result of a search request * @param $oP WebPage Web page for the output * @param $oFilter DBSearch The search of objects to display * @param $bSearchForm boolean Whether or not to display the search form at the top the page * @param $sBaseClass string The base class for the search (can be different from the actual class of the results) * @param $sFormat string The format to use for the output: csv or html * @param $bDoSearch bool True to display the search results below the search form * @param $bSearchFormOpen bool True to display the search form fully expanded (only if $bSearchForm of course) * @throws \CoreException * @throws \DictExceptionMissingString */ function DisplaySearchSet($oP, $oFilter, $bSearchForm = true, $sBaseClass = '', $sFormat = '', $bDoSearch = true, $bSearchFormOpen = true, $aParams = []) { //search block $oBlockForm = null; if ($bSearchForm) { $aParams['open'] = $bSearchFormOpen; if (false === isset($aParams['table_id'])) { $aParams['table_id'] = 'result_1'; } if (!empty($sBaseClass)) { $aParams['baseClass'] = $sBaseClass; } $oBlockForm = new DisplayBlock($oFilter, 'search', false /* Asynchronous */, $aParams); if (!$bDoSearch) { $oBlockForm->Display($oP, 0); } } if ($bDoSearch) { if (strtolower($sFormat) == 'csv') { $oBlock = new DisplayBlock($oFilter, 'csv', false); // Adjust the size of the Textarea containing the CSV to fit almost all the remaining space $oP->add_ready_script(" $('#1>textarea').height($('#1').parent().height() - $('#0').outerHeight() - 30).width( $('#1').parent().width() - 20);"); // adjust the size of the block } else { $oBlock = new DisplayBlock($oFilter, 'list', false); // Breadcrumb //$iCount = $oBlock->GetDisplayedCount(); $sPageId = "ui-search-".$oFilter->GetClass(); $sLabel = MetaModel::GetName($oFilter->GetClass()); $oP->SetBreadCrumbEntry($sPageId, $sLabel, '', '', 'fas fa-search', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES); } if ($bSearchForm) { //add search block $sTableId = utils::ReadParam('_table_id_', null, false, 'raw_data'); if ($sTableId == '') { $sTableId = 'result_1'; } $aExtraParams['table_id'] = $sTableId; $aExtraParams['submit_on_load'] = false; $oUIBlockForm = $oBlockForm->GetDisplay($oP, 'search_1', $aExtraParams); //add result block $oUIBlock = $oBlock->GetDisplay($oP, $sTableId); $oUIBlock->AddCSSClasses(['display_block', 'sf_results_area']); $oUIBlock->AddDataAttribute('target', 'search_results'); //$oUIBlockForm->AddSubBlock($oUIBlock); $oP->AddUiBlock($oUIBlockForm); $oUIBlockForm->AddSubBlock($oUIBlock); } else { $oBlock->Display($oP, 1); } } } /** * Displays a form (checkboxes) to select the objects for which to apply a given action * Only the objects for which the action is valid can be checked. By default all valid objects are checked * * @param WebPage $oP WebPage The page for output * @param \DBSearch $oFilter DBSearch The filter that defines the list of objects * @param string $sNextOperation string The next operation (code) to be executed when the form is submitted * @param ActionChecker $oChecker ActionChecker The helper class/instance used to check for which object the action is valid * @param array $aExtraFormParams * @param array $aDisplayParams * * @since 3.0.0 $aDisplayParams parameter * * @throws \ApplicationException * @throws \ArchivedObjectException * @throws \CoreException */ function DisplayMultipleSelectionForm(WebPage $oP, DBSearch $oFilter, string $sNextOperation, ActionChecker $oChecker, array $aExtraFormParams = [], array $aDisplayParams = []) { $oAppContext = new ApplicationContext(); $iBulkActionAllowed = $oChecker->IsAllowed(); $aExtraParams = ['selection_type' => 'multiple', 'selection_mode' => true, 'display_limit' => false, 'menu' => false]; if ($iBulkActionAllowed == UR_ALLOWED_DEPENDS) { $aExtraParams['selection_enabled'] = $oChecker->GetAllowedIDs(); } else { if (UR_ALLOWED_NO) { throw new ApplicationException(Dict::Format('UI:ActionNotAllowed')); } } $oForm = new Form(); $oForm->SetAction('./UI.php'); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', $sNextOperation)); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $oFilter->GetClass())); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('filter', utils::HtmlEntities($oFilter->Serialize()))); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::GetNewTransactionId())); foreach ($aExtraFormParams as $sName => $sValue) { $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden($sName, $sValue)); } $oForm->AddSubBlock($oAppContext->GetForFormBlock()); $oDisplayBlock = new DisplayBlock($oFilter, 'list', false); //by default all the elements are selected $aExtraParams['selectionMode'] = 'negative'; if (array_key_exists('icon', $aDisplayParams) || array_key_exists('title', $aDisplayParams)) { $aExtraParams['surround_with_panel'] = true; if (array_key_exists('icon', $aDisplayParams)) { $aExtraParams['panel_icon'] = $aDisplayParams['icon']; } if (array_key_exists('title', $aDisplayParams)) { $aExtraParams['panel_title'] = $aDisplayParams['title']; } } $oForm->AddSubBlock($oDisplayBlock->GetDisplay($oP, 1, $aExtraParams)); $oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null); $oToolbarButtons->AddCSSClass('ibo-toolbar--button'); $oForm->AddSubBlock($oToolbarButtons); $oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'), 'cancel')->SetOnClickJsCode('window.history.back()')); $oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('UI:Button:Next'), 'next', Dict::S('UI:Button:Next'), true)); $oP->AddUiBlock($oForm); } /** * @param $oP * @param $aResults * @param $sRelation * @param $sDirection * @param $oObj * for operation : 'swf_navigator' * * @throws \DictExceptionMissingString */ function DisplayNavigatorListTab($oP, $aResults, $sRelation, $sDirection, $oObj) { $oP->SetCurrentTab('UI:RelationshipList'); $oImpactedObject = UIContentBlockUIBlockFactory::MakeStandard("impacted_objects", ['ibo-is-visible']); $oP->AddSubBlock($oImpactedObject); $oImpactedObjectList = UIContentBlockUIBlockFactory::MakeStandard("impacted_objects_lists", ['ibo-is-visible']); $oImpactedObject->AddSubBlock($oImpactedObjectList); $oImpactedObjectList->AddSubBlock(UIContentBlockUIBlockFactory::MakeStandard("impacted_objects_lists_placeholder", ['ibo-is-visible'])); } function DisplayNavigatorGroupTab($oP) { $oP->SetCurrentTab('UI:RelationGroups'); $oP->add("
'.Dict::Format('UI:Error:IncorrectOQLQuery_Message', $e->getHtmlDesc()).'
".Dict::Format('UI:BulkModify_Count_DistinctValues', count($aValues[$sAttCode]))."
| '); $oForm->AddHtml($oP->GetDetails($aDetails)); $oForm->AddHtml(' |
'.implode('
', $aErrorsToDisplay)."\n"; } } else { $sStatus = Dict::S('UI:BulkModifyStatusSkipped'); $sError = ''.Dict::S('UI:FailedToApplyStimuli')."
\n"; } } else { $sStatus = Dict::S('UI:BulkModifyStatusSkipped'); $sError = '
'.implode('
', $aErrors)."\n"; } } } catch (Exception $e) { $sError = $e->getMessage(); $sStatus = Dict::S('UI:BulkModifyStatusSkipped'); } $aRows[] = [ 'object' => $oObj->GetHyperlink(), 'status' => $sStatus, 'errors' => $sError, ]; } $oBlock = PanelUIBlockFactory::MakeForClass($sClass, Dict::Format('UI:StimulusModify_N_ObjectsOf_Class', $sActionLabel, count($aObjects), $sClass)); $oBlock->SetIcon(MetaModel::GetClassIcon($sClass, false)); $oDataTable = DataTableUIBlockFactory::MakeForStaticData('', $aHeaders, $aRows); $oBlock->AddSubBlock($oDataTable); $oP->AddUiBlock($oBlock); // Back to the list $sURL = "./UI.php?operation=search&filter=".urlencode($sFilter).$oAppContext->GetForLink(true); $oSubmitButton = ButtonUIBlockFactory::MakeForSecondaryAction(Dict::S('UI:Button:Done'), 'submit', 'submit', true); $oSubmitButton->SetOnClickJsCode("window.location.href='$sURL'"); $oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null); $oToolbarButtons->AddCSSClass('ibo-toolbar--button'); $oToolbarButtons->AddSubBlock($oSubmitButton); $oP->AddSubBlock($oToolbarButtons); } break; case 'stimulus': // Form displayed when applying a stimulus (state change) $oP->DisableBreadCrumb(); $sClass = utils::ReadParam('class', '', false, 'class'); $id = utils::ReadParam('id', ''); $sStimulus = utils::ReadParam('stimulus', ''); if (empty($sClass) || empty($id) || empty($sStimulus)) { // TO DO: check that the class name is valid ! throw new ApplicationException(Dict::Format('UI:Error:3ParametersMissing', 'class', 'id', 'stimulus')); } $aStimuli = MetaModel::EnumStimuli($sClass); if ((get_class($aStimuli[$sStimulus]) !== 'StimulusUserAction') || (UserRights::IsStimulusAllowed($sClass, $sStimulus) === UR_ALLOWED_NO)) { $sUser = UserRights::GetUser(); IssueLog::Error("UI.php '$operation' : Stimulus '$sStimulus' not allowed ! data: user='$sUser', class='$sClass'"); throw new ApplicationException(Dict::S('UI:Error:ActionNotAllowed')); } /** @var \cmdbAbstractObject $oObj */ $oObj = MetaModel::GetObject($sClass, $id, false); if ($oObj != null) { $aPrefillFormParam = [ 'user' => Session::Get('auth_user'), 'context' => $oAppContext->GetAsHash(), 'stimulus' => $sStimulus, 'origin' => 'console', ]; try { $bApplyTransition = $oObj->DisplayStimulusForm($oP, $sStimulus, $aPrefillFormParam); } catch (ApplicationException $e) { $bApplyTransition = false; $sMessage = $e->getMessage(); $sSeverity = 'warning'; ReloadAndDisplay($oP, $oObj, 'stimulus', $sMessage, $sSeverity); } catch (CoreCannotSaveObjectException $e) { $bApplyTransition = false; $aIssues = $e->getIssues(); $sMessage = $e->getHtmlMessage(); $sSeverity = 'warning'; ReloadAndDisplay($oP, $oObj, 'stimulus', $sMessage, $sSeverity); } if ($bApplyTransition) { $sMessage = Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName()); $sSeverity = 'ok'; //transition is ok, whe can display object with transition message ReloadAndDisplay($oP, $oObj, 'apply_stimulus', $sMessage, $sSeverity); } } else { $oP->set_title(Dict::S('UI:ErrorPageTitle')); $oP->P(Dict::S('UI:ObjectDoesNotExist')); } break; /////////////////////////////////////////////////////////////////////////////////////////// case 'apply_stimulus': // Actual state change $oP->DisableBreadCrumb(); $sClass = utils::ReadPostedParam('class', '', 'class'); $id = utils::ReadPostedParam('id', ''); $sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id'); $sStimulus = utils::ReadPostedParam('stimulus', ''); if (empty($sClass) || empty($id) || empty($sStimulus)) { // TO DO: check that the class name is valid ! throw new ApplicationException(Dict::Format('UI:Error:3ParametersMissing', 'class', 'id', 'stimulus')); } /** @var \cmdbAbstractObject $oObj */ $oObj = MetaModel::GetObject($sClass, $id, false); if ($oObj != null) { $aTransitions = $oObj->EnumTransitions(); $aStimuli = MetaModel::EnumStimuli($sClass); $sMessage = ''; $sSeverity = 'ok'; $bDisplayDetails = true; if (!isset($aTransitions[$sStimulus])) { throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $oObj->GetName(), $oObj->GetStateLabel())); } if (!utils::IsTransactionValid($sTransactionId)) { $sUser = UserRights::GetUser(); IssueLog::Error("UI.php '$operation' : invalid transaction_id ! data: user='$sUser', class='$sClass'"); $sMessage = Dict::S('UI:Error:ObjectAlreadyUpdated'); $sSeverity = 'info'; } elseif ((get_class($aStimuli[$sStimulus]) !== 'StimulusUserAction') || (UserRights::IsStimulusAllowed($sClass, $sStimulus) === UR_ALLOWED_NO)) { $sUser = UserRights::GetUser(); IssueLog::Error("UI.php '$operation' : Stimulus '$sStimulus' not allowed ! data: user='$sUser', class='$sClass'"); $sMessage = Dict::S('UI:Error:ActionNotAllowed'); $sSeverity = 'error'; } else { $sActionLabel = $aStimuli[$sStimulus]->GetLabel(); $sActionDetails = $aStimuli[$sStimulus]->GetDescription(); $sTargetState = $aTransitions[$sStimulus]['target_state']; $aExpectedAttributes = $oObj->GetTransitionAttributes($sStimulus /*, current state*/); $aDetails = []; $aErrors = []; foreach ($aExpectedAttributes as $sAttCode => $iExpectCode) { $iFlags = $oObj->GetTransitionFlags($sAttCode, $sStimulus); if (($iExpectCode & (OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) || ($oObj->Get($sAttCode) == '')) { $paramValue = utils::ReadPostedParam("attr_$sAttCode", '', 'raw_data'); if (($iFlags & OPT_ATT_SLAVE) && ($paramValue != $oObj->Get($sAttCode))) { $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); $aErrors[] = Dict::Format('UI:AttemptingToChangeASlaveAttribute_Name', $oAttDef->GetLabel()); unset($aExpectedAttributes[$sAttCode]); } } } $oObj->UpdateObjectFromPostedForm('', array_keys($aExpectedAttributes), $aExpectedAttributes); if (count($aErrors) == 0) { $sIssues = ''; $bApplyStimulus = true; list($bRes, $aIssues) = $oObj->CheckToWrite(); // Check before trying to write the object if ($bRes) { try { $bApplyStimulus = $oObj->ApplyStimulus($sStimulus); // will write the object in the DB } catch (CoreException $e) { // Rollback to the previous state... by reloading the object from the database and applying the modifications again $oObj = MetaModel::GetObject(get_class($oObj), $oObj->GetKey()); $oObj->UpdateObjectFromPostedForm('', array_keys($aExpectedAttributes), $aExpectedAttributes); $sIssues = $e->getMessage(); } } else { $sIssues = implode(' ', $aIssues); } if (!$bApplyStimulus) { $sMessage = Dict::S('UI:FailedToApplyStimuli'); $sSeverity = 'error'; $sOwnershipToken = utils::ReadPostedParam('ownership_token', null, 'raw_data'); if ($sOwnershipToken !== null) { // Release the concurrent lock, if any iTopOwnershipLock::ReleaseLock(get_class($oObj), $oObj->GetKey(), $sOwnershipToken); } } elseif ($sIssues != '') { $sOwnershipToken = utils::ReadPostedParam('ownership_token', null, 'raw_data'); if ($sOwnershipToken !== null) { // Release the concurrent lock, if any, a new lock will be re-acquired by DisplayStimulusForm below iTopOwnershipLock::ReleaseLock(get_class($oObj), $oObj->GetKey(), $sOwnershipToken); } $bDisplayDetails = false; // Found issues, explain and give the user a second chance // try { $oObj->DisplayStimulusForm($oP, $sStimulus); } catch (ApplicationException $e) { $sMessage = $e->getMessage(); $sSeverity = 'info'; } $sIssueDesc = Dict::Format('UI:ObjectCouldNotBeWritten', $sIssues); $oP->add_ready_script("CombodoModal.OpenErrorModal('".addslashes($sIssueDesc)."');"); } else { $sMessage = Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName()); $sSeverity = 'ok'; utils::RemoveTransaction($sTransactionId); $bLockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled'); if ($bLockEnabled) { // Release the concurrent lock, if any $sOwnershipToken = utils::ReadPostedParam('ownership_token', null, 'raw_data'); if ($sOwnershipToken !== null) { // We're done, let's release the lock iTopOwnershipLock::ReleaseLock(get_class($oObj), $oObj->GetKey(), $sOwnershipToken); } } } } else { $sMessage = implode('', $aErrors); $sSeverity = 'error'; } } if ($bDisplayDetails) { ReloadAndDisplay($oP, $oObj, 'apply_stimulus', $sMessage, $sSeverity); } } else { $oP->set_title(Dict::S('UI:ErrorPageTitle')); $oP->P(Dict::S('UI:ObjectDoesNotExist')); } break; /////////////////////////////////////////////////////////////////////////////////////////// case 'view_relations': // Graphical display of the relations "impact" / "depends on" require_once(APPROOT.'core/simplegraph.class.inc.php'); require_once(APPROOT.'core/relationgraph.class.inc.php'); require_once(APPROOT.'core/displayablegraph.class.inc.php'); $sClass = utils::ReadParam('class', '', false, 'class'); $id = utils::ReadParam('id', 0); $sRelation = utils::ReadParam('relation', 'impact'); $sDirection = utils::ReadParam('direction', 'down'); $iGroupingThreshold = utils::ReadParam('g', 5); $bDirDown = ($sDirection === 'down'); $oObj = MetaModel::GetObject($sClass, $id); $iMaxRecursionDepth = MetaModel::GetConfig()->Get('relations_max_depth'); $aSourceObjects = [$oObj]; $oP->set_title(MetaModel::GetRelationDescription($sRelation, $bDirDown).' '.$oObj->GetName()); $sPageId = "ui-relation-graph-".$sClass.'::'.$id; $sLabel = $oObj->GetName().' '.MetaModel::GetRelationLabel($sRelation, $bDirDown); $sDescription = MetaModel::GetRelationDescription($sRelation, $bDirDown).' '.$oObj->GetName(); $oP->SetBreadCrumbEntry($sPageId, $sLabel, $sDescription); if ($sRelation == 'depends on') { $sRelation = 'impacts'; $sDirection = 'up'; } if ($sDirection == 'up') { $oRelGraph = MetaModel::GetRelatedObjectsUp($sRelation, $aSourceObjects, $iMaxRecursionDepth); } else { $oRelGraph = MetaModel::GetRelatedObjectsDown($sRelation, $aSourceObjects, $iMaxRecursionDepth); } $aResults = $oRelGraph->GetObjectsByClass(); $oDisplayGraph = DisplayableGraph::FromRelationGraph($oRelGraph, $iGroupingThreshold, ($sDirection == 'down')); $sTitle = MetaModel::GetRelationDescription($sRelation, $bDirDown).' '.$oObj->GetName(); $sClassIcon = MetaModel::GetClassIcon($sClass, false); $sFirstTab = MetaModel::GetConfig()->Get('impact_analysis_first_tab'); $bLazyLoading = MetaModel::GetConfig()->Get('impact_analysis_lazy_loading'); $sContextKey = "itop-config-mgmt/relation_context/$sClass/$sRelation/$sDirection"; // Check if the current object supports Attachments, similar to AttachmentPlugin::IsTargetObject $sClassForAttachment = null; $iIdForAttachment = null; if (class_exists('Attachment')) { $aAllowedClasses = MetaModel::GetModuleSetting('itop-attachments', 'allowed_classes', ['Ticket']); foreach ($aAllowedClasses as $sAllowedClass) { if ($oObj instanceof $sAllowedClass) { $iIdForAttachment = $id; $sClassForAttachment = $sClass; } } } $oP->AddSubBlock($oDisplayGraph->DisplayFilterBox($oP, $aResults, $bLazyLoading)); $oPanel = PanelUIBlockFactory::MakeForClass($sClass, $sTitle); $oPanel->SetIcon($sClassIcon); $oP->AddSubBlock($oPanel); $oP->AddTabContainer('Navigator', '', $oPanel); $oP->SetCurrentTabContainer('Navigator'); // Display the tabs if ($sFirstTab == 'list') { DisplayNavigatorListTab($oP, $aResults, $sRelation, $sDirection, $oObj); $oP->SetCurrentTab('UI:RelationshipGraph'); $oDisplayGraph->DisplayGraph($oP, $sRelation, $oAppContext, [], $sClassForAttachment, $iIdForAttachment, $sContextKey, ['this' => $oObj], $bLazyLoading); DisplayNavigatorGroupTab($oP); } else { $oP->SetCurrentTab('UI:RelationshipGraph'); $oDisplayGraph->DisplayGraph($oP, $sRelation, $oAppContext, [], $sClassForAttachment, $iIdForAttachment, $sContextKey, ['this' => $oObj], $bLazyLoading); DisplayNavigatorListTab($oP, $aResults, $sRelation, $sDirection, $oObj); DisplayNavigatorGroupTab($oP); } $oP->SetCurrentTab(''); break; /////////////////////////////////////////////////////////////////////////////////////////// case 'kill_lock': $oP->DisableBreadCrumb(); $sClass = utils::ReadParam('class', '', false, '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->DisableBreadCrumb(); $oP->set_title(Dict::S('UI:OperationCancelled')); $oP->add('