/** * Main page of iTop * * @copyright Copyright (C) 2010-2016 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ /** * Displays a popup welcome message, once per session at maximum * until the user unchecks the "Display welcome at startup" * @param WebPage $oP The current web page for the display * @return void */ function DisplayWelcomePopup(WebPage $oP) { if (!isset($_SESSION['welcome'])) { // Check, only once per session, if the popup should be displayed... // If the user did not already ask for hiding it forever $bPopup = appUserPreferences::GetPref('welcome_popup', true); if ($bPopup) { $sTemplate = @file_get_contents('../application/templates/welcome_popup.html'); if ($sTemplate !== false) { $oTemplate = new DisplayTemplate($sTemplate); $oP->add("
\n"); $oP->add("
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()); } } 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()); } /** * Displays the details of an object * @param $oP WebPage Page for the output * @param $sClass string The name of the class of the object * @param $oObj DBObject The object to display * @param $id mixed Identifier of the object (name or ID) */ function DisplayDetails($oP, $sClass, $oObj, $id) { $sClassLabel = MetaModel::GetName($sClass); $oSearch = new DBObjectSearch($sClass); $oBlock = new DisplayBlock($oSearch, 'search', false); $oBlock->Display($oP, 0); // The object could be listed, check if it is actually allowed to view it $oSet = CMDBObjectSet::FromObject($oObj); if (UserRights::IsActionAllowed($sClass, UR_ACTION_READ, $oSet) == UR_ALLOWED_NO) { throw new SecurityException('User not allowed to view this object', array('class' => $sClass, 'id' => $id)); } $oP->set_title(Dict::Format('UI:DetailsPageTitle', $oObj->GetRawName(), $sClassLabel)); // Set title will take care of the encoding $oObj->DisplayDetails($oP); } /** * 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 */ function DisplaySearchSet($oP, $oFilter, $bSearchForm = true, $sBaseClass = '', $sFormat = '', $bDoSearch = true) { if ($bSearchForm) { $aParams = array('open' => true); if (!empty($sBaseClass)) { $aParams['baseClass'] = $sBaseClass; } $oBlock = new DisplayBlock($oFilter, 'search', false /* Asynchronous */, $aParams); $oBlock->Display($oP, 0); } if ($bDoSearch) { if (strtolower($sFormat) == 'csv') { $oBlock = new DisplayBlock($oFilter, 'csv', false); $oBlock->Display($oP, 1); // 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); $oBlock->Display($oP, 1); // Breadcrumb //$iCount = $oBlock->GetDisplayedCount(); $sPageId = "ui-search-".$oFilter->GetClass(); $sLabel = MetaModel::GetName($oFilter->GetClass()); $oP->SetBreadCrumbEntry($sPageId, $sLabel, '', '', '../images/breadcrumb-search.png'); } } } /** * 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 $oP WebPage The page for output * @param $oFilter DBSearch The filter that defines the list of objects * @param $sNextOperation string The next operation (code) to be executed when the form is submitted * @param $oChecker ActionChecker The helper class/instance used to check for which object the action is valid * @return none */ function DisplayMultipleSelectionForm($oP, $oFilter, $sNextOperation, $oChecker, $aExtraFormParams = array()) { $oAppContext = new ApplicationContext(); $iBulkActionAllowed = $oChecker->IsAllowed(); $sClass = $oFilter->GetClass(); $aExtraParams = array('selection_type' => 'multiple', 'selection_mode' => true, 'display_limit' => false, 'menu' => false); if ($iBulkActionAllowed == UR_ALLOWED_DEPENDS) { $aAllowed = array(); $aExtraParams['selection_enabled'] = $oChecker->GetAllowedIDs(); } else if(UR_ALLOWED_NO) { throw new ApplicationException(Dict::Format('UI:ActionNotAllowed')); } $oBlock = new DisplayBlock($oFilter, 'list', false); $oP->add("\n"); $oP->add_ready_script("$('#1 table.listResults').trigger('check_all');"); } function DisplayNavigatorListTab($oP, $aResults, $sRelation, $sDirection, $oObj) { $oP->SetCurrentTab(Dict::S('UI:RelationshipList')); $oP->add("
".Dict::Format('UI:BulkModify_Count_DistinctValues', count($aValues[$sAttCode]))."
'.implode('
',$aErrors)."\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[] = array( 'object' => $oObj->GetHyperlink(), 'status' => $sStatus, 'errors' => $sError, ); } $oP->Table($aHeaders, $aRows); // Back to the list $sURL = "./UI.php?operation=search&filter=".urlencode($sFilter)."&".$oAppContext->GetForLink(); $oP->add(''); } 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')); } $oObj = MetaModel::GetObject($sClass, $id, false); if ($oObj != null) { $oObj->DisplayStimulusForm($oP, $sStimulus); } 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', ''); $id = utils::ReadPostedParam('id', ''); $sTransactionId = utils::ReadPostedParam('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')); } $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)) { $sMessage = Dict::S('UI:Error:ObjectAlreadyUpdated'); $sSeverity = 'info'; } else { $sActionLabel = $aStimuli[$sStimulus]->GetLabel(); $sActionDetails = $aStimuli[$sStimulus]->GetDescription(); $aTransition = $aTransitions[$sStimulus]; $sTargetState = $aTransition['target_state']; $aTargetStates = MetaModel::EnumStates($sClass); $aTargetState = $aTargetStates[$sTargetState]; $aExpectedAttributes = $aTargetState['attribute_list']; $aDetails = array(); $aErrors = array(); foreach($aExpectedAttributes as $sAttCode => $iExpectCode) { $iFlags = $oObj->GetAttributeFlags($sAttCode); 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), $sTargetState); 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), $sTargetState); $sIssues = $e->getMessage(); } } else { $sIssues = implode(' ', $aIssues); } if (!$bApplyStimulus) { $sMessage = Dict::S('UI:FailedToApplyStimuli'); $sSeverity = 'error'; } else if ($sIssues != '') { $sOwnershipToken = utils::ReadPostedParam('ownership_token', null, false, '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 // $oObj->DisplayStimulusForm($oP, $sStimulus); $sIssueDesc = Dict::Format('UI:ObjectCouldNotBeWritten',$sIssues); $oP->add_ready_script("alert('".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, false, '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 'swf_navigator': // 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); $oObj = MetaModel::GetObject($sClass, $id); $iMaxRecursionDepth = MetaModel::GetConfig()->Get('relations_max_depth', 20); $aSourceObjects = array($oObj); $oP->set_title(MetaModel::GetRelationDescription($sRelation).' '.$oObj->GetName()); $sPageId = "ui-relation-graph-".$sClass.'::'.$id; $sLabel = $oObj->GetName().' '.MetaModel::GetRelationLabel($sRelation); $sDescription = MetaModel::GetRelationDescription($sRelation).' '.$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')); $oP->AddTabContainer('Navigator'); $oP->SetCurrentTabContainer('Navigator'); $sFirstTab = MetaModel::GetConfig()->Get('impact_analysis_first_tab'); $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', array('Ticket')); foreach($aAllowedClasses as $sAllowedClass) { if ($oObj instanceof $sAllowedClass) { $iIdForAttachment = $id; $sClassForAttachment = $sClass; } } } $oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/tabularfieldsselector.js'); $oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.dragtable.js'); $oP->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/dragtable.css'); // Display the tabs if ($sFirstTab == 'list') { DisplayNavigatorListTab($oP, $aResults, $sRelation, $sDirection, $oObj); $oP->SetCurrentTab(Dict::S('UI:RelationshipGraph')); $oDisplayGraph->Display($oP, $aResults, $sRelation, $oAppContext, array(), $sClassForAttachment, $iIdForAttachment, $sContextKey, array('this' => $oObj)); DisplayNavigatorGroupTab($oP); } else { $oP->SetCurrentTab(Dict::S('UI:RelationshipGraph')); $oDisplayGraph->Display($oP, $aResults, $sRelation, $oAppContext, array(), $sClassForAttachment, $iIdForAttachment, $sContextKey, array('this' => $oObj)); DisplayNavigatorListTab($oP, $aResults, $sRelation, $sDirection, $oObj); DisplayNavigatorGroupTab($oP); } $oP->SetCurrentTab(''); break; /////////////////////////////////////////////////////////////////////////////////////////// case 'kill_lock': $oP->DisableBreadCrumb(); $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->DisableBreadCrumb(); $oP->set_title(Dict::S('UI:OperationCancelled')); $oP->add('