ComputeAndReport('Data model loaded'); $oKPI = new ExecutionKPI(); require_once(APPROOT.'/application/loginwebpage.class.inc.php'); $operation = utils::ReadParam('operation', '', false, utils::ENUM_SANITIZATION_FILTER_OPERATION); // Only allow export functions to portal users switch ($operation) { case 'export_build_portal': case 'export_cancel': case 'export_download': case 'cke_img_upload': case 'cke_upload_and_browse': case 'cke_browse': $sRequestedPortalId = null; // Allowed for all users break; default: $sRequestedPortalId = 'backoffice'; // Allowed only for console users break; } LoginWebPage::DoLoginEx($sRequestedPortalId, false); $oKPI->ComputeAndReport('User login'); // N°2780 Fix ContextTag for console // Some operations are also used in the portal though switch ($operation) { case 'export_build_portal': case 'export_download': // Do nothing: used in portal (export.js in portal-base) break; default: $oTag = new ContextTag(ContextTag::TAG_CONSOLE); } // First check if we can redirect the route to a dedicated controller // IMPORTANT: Even though iTop router is deprecated, we must ensure old URLs keep working (for a while) as they can be used in received notifications, bookmarks, extensions, ... $sRoute = utils::ReadParam('route', '', false, utils::ENUM_SANITIZATION_FILTER_ROUTE); $oRouter = Router::GetInstance(); if ($oRouter->CanDispatchRoute($sRoute)) { $mResponse = $oRouter->DispatchRoute($sRoute); // If response isn't a WebPage, it is most likely that the output already occured, stop the script. // Note that this is done here and not directly in the Router::DispatchRoute() so custom endpoint can handle null responses their own way. if (false === ($mResponse instanceof WebPage)) { die(); } // Response is a WebPage, let's handle it like legacy operations $oPage = $mResponse; } // Otherwise, use legacy operation else { // Default for most operations, but can be switch to a JsonPage, DownloadPage or else if the operation requires it $oPage = new AjaxPage(""); //--------------------------------------------------------------------------------------------------------- // // Mind that the following backward compatibility pattern is also present in the `pages/UI.php` endpoint // //--------------------------------------------------------------------------------------------------------- // // Kept for backward compatibility with iTop 3.1.x notification URLs, don't remove // Non-dispatchable "routes" (iTop 3.1) are mapped to their old corresponding "operation" (iTop 3.0 and older), in order to be later redirected to the proper new URLs (iTop 3.2 and newer) switch ($sRoute) { case 'object.new': case 'object.modify': $operation = str_ireplace("object.", "", $sRoute); break; } if (utils::IsNotNullOrEmptyString($sRoute)) { DeprecatedCallsLog::Debug("Calling UI.php with a deprecated \"route\" parameter that was migrated to Symfony router, redirecting to fallback \"operation\" parameter. If URI comes from a custom code, migrate it. If URI comes from an old notification, mind that it may not work in future versions.", LogChannels::ROUTER, [ "request_uri" => $_SERVER["REQUEST_URI"], "route" => $sRoute, "fallback_operation" => $operation, ]); } // Kept for backward compatibility with iTop 3.0 and older notification URLs, don't remove // These operations have been migrated to Symfony router, we need to redirect to the proper new URLs switch ($operation) { /** @deprecated 3.1.0 Use the "b_object_new" Symfony route instead */ case 'new': /** @deprecated 3.1.0 Use the "b_object_modify" Symfony route instead */ case 'modify': // Start a Symfony kernel and redirect to the corresponding route / controller $oKernel = new Kernel("prod", false); $oRequest = Request::createFromGlobals(); /** @var string $sRouteName Route name to redirect to */ $sRouteName = ''; /** @var string[] $aPath Parts of the Symfony route path (eg. for "/object/{sCLass}/{sId}", ["sClass" => "UserRequest", "sId" => "123"]) */ $aPath = []; /** @var string[] $aQuery Query parameters of the request -parameters after the "?" of the URI- (eg. for "/object/UserRequest/123?a=1&b=2", ["a" => 1, "b" => 2]) */ $aQuery = $oRequest->query->all(); // Find controller corresponding to the old "operation" switch ($operation) { case 'new': $sRouteName = 'b_object_new'; $aPath['_controller'] = ObjectController::class . "::OperationNew"; $aPath['sClass'] = utils::ReadParam('class', null, false, utils::ENUM_SANITIZATION_FILTER_CLASS); break; case 'modify': $sRouteName = 'b_object_modify'; $aPath['_controller'] = ObjectController::class . "::OperationModify"; $aPath['sClass'] = utils::ReadParam('class', '', false, utils::ENUM_SANITIZATION_FILTER_CLASS); $aPath['sId'] = utils::ReadParam('id', ''); break; } // Internal Symfony redirection // Pros: // - Keeps query parameters // - Keeps posted parameters (essential for submission URLs) // Cons: // - Still displays the old URL // - Uses a not so well-formed Symfony kernel? $oSubRequest = $oRequest->duplicate($aQuery, null, $aPath); $oResponse = $oKernel->handle($oSubRequest, \Symfony\Component\HttpKernel\HttpKernelInterface::SUB_REQUEST); $oResponse->send(); $oKernel->terminate($oSubRequest, $oResponse); die(); } $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); $sEncoding = utils::ReadParam('encoding', 'serialize'); $sClass = utils::ReadParam('class', 'MissingAjaxParam', false, 'class'); $sStyle = utils::ReadParam('style', 'list'); $oAjaxRenderController = new AjaxRenderController(); switch ($operation) { case 'search_and_refresh': $oPage = new JsonPage(); // Feeds dataTables directly $oPage->SetOutputDataOnly(true); $aResult = AjaxRenderController::SearchAndRefresh($sFilter); $oPage->SetData($aResult); break; case 'search': $oPage = new JsonPage(); // Feeds dataTables directly $oPage->SetOutputDataOnly(true); $aResult = AjaxRenderController::Search($sEncoding, $sFilter); $oPage->SetData($aResult); break; case 'refreshDashletCount': $oPage->SetContentType('application/json'); $aResult = AjaxRenderController::RefreshDashletCount($sFilter); $oPage->add(json_encode($aResult)); break; case 'refreshDashletList': $oPage->SetContentType('application/json'); $aResult = AjaxRenderController::RefreshDashletList($sStyle, $sFilter); $oPage->add(json_encode($aResult)); break; case 'refreshDashletSummary': $oPage->SetContentType('text/html'); $sExtraParams = utils::ReadParam('extra_params', '', false, 'raw_data'); $aExtraParams = json_decode($sExtraParams, true); $aQueryParams = []; if (isset($aExtraParams['query_params'])) { $aQueryParams = $aExtraParams['query_params']; } $oFilter = DBObjectSearch::FromOQL($sFilter, $aQueryParams); $oFilter->SetShowObsoleteData(utils::ShowObsoleteData()); $oSet = new CMDBObjectSet($oFilter, [], $aExtraParams); $oBlock = new displayblock($oFilter, 'summary', false, [], $oSet); $oBlock->RenderContent($oPage, $aExtraParams); break; case 'datatable_save_settings': $oPage->SetContentType('text/plain'); $bRet = AjaxRenderController::DatatableSaveSettings(); $oPage->add($bRet ? 'Ok' : 'KO'); break; case 'datatable_reset_settings': $oPage->SetContentType('text/plain'); $bRet = AjaxRenderController::DatatableResetSettings(); $oPage->add($bRet ? 'Ok' : 'KO'); break; // ui.searchformforeignkeys case 'ShowModalSearchForeignKeys': $oPage->SetContentType('text/html'); $iInputId = utils::ReadParam('iInputId', ''); $sTitle = utils::ReadParam('sTitle', '', false, 'raw_data'); $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $oWidget = new UISearchFormForeignKeys($sTargetClass, $iInputId); $oWidget->ShowModalSearchForeignKeys($oPage, $sTitle); break; // ui.searchformforeignkeys case 'GetFullListForeignKeysFromSelection': $oPage->SetContentType('application/json'); $oWidget = new UISearchFormForeignKeys($sClass); $oFullSetFilter = new DBObjectSearch($sClass); $oWidget->GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter); break; // ui.searchformforeignkeys case 'ListResultsSearchForeignKeys': $oPage->SetContentType('text/html'); $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $iInputId = utils::ReadParam('iInputId', ''); $sRemoteClass = utils::ReadParam('sRemoteClass', '', false, 'class'); $oWidget = new UISearchFormForeignKeys($sTargetClass, $iInputId); $oWidget->ListResultsSearchForeignKeys($oPage, $sRemoteClass); break; // ui.linkswidget case 'addObjects': $oPage->SetContentType('text/html'); $sAttCode = utils::ReadParam('sAttCode', ''); $iInputId = utils::ReadParam('iInputId', ''); $sSuffix = utils::ReadParam('sSuffix', ''); $bDuplicates = (utils::ReadParam('bDuplicates', 'false') == 'false') ? false : true; $sJson = utils::ReadParam('json', '', false, 'raw_data'); if (!empty($sJson)) { $oWizardHelper = WizardHelper::FromJSON($sJson); $oObj = $oWizardHelper->GetTargetObject(); } else { // Search form: no current object $oObj = null; } $oWidget = new UILinksWidget($sClass, $sAttCode, $iInputId, $sSuffix, $bDuplicates); $oAppContext = new ApplicationContext(); $aPrefillFormParam = array( 'user' => Session::Get("auth_user"), 'context' => $oAppContext->GetAsHash(), 'att_code' => $sAttCode, 'origin' => 'console', 'source_obj' => $oObj ); $aAlreadyLinked = utils::ReadParam('aAlreadyLinked', array()); /** @var \DBObject $oObj */ $oWidget->GetObjectPickerDialog($oPage, $oObj, $sJson, $aAlreadyLinked, $aPrefillFormParam); break; // ui.linkswidget case 'searchObjectsToAdd': $oPage->SetContentType('text/html'); $sRemoteClass = utils::ReadParam('sRemoteClass', '', false, 'class'); $sAttCode = utils::ReadParam('sAttCode', ''); $iInputId = utils::ReadParam('iInputId', ''); $sSuffix = utils::ReadParam('sSuffix', ''); $bDuplicates = (utils::ReadParam('bDuplicates', 'false') == 'false') ? false : true; $aAlreadyLinked = utils::ReadParam('aAlreadyLinked', array()); $oWidget = new UILinksWidget($sClass, $sAttCode, $iInputId, $sSuffix, $bDuplicates); $oWidget->SearchObjectsToAdd($oPage, $sRemoteClass, $aAlreadyLinked); break; //ui.linksdirectwidget case 'createObject': $oPage->SetContentType('text/html'); $sClass = utils::ReadParam('class', '', false, 'class'); $sRealClass = utils::ReadParam('real_class', '', false, 'class'); $sAttCode = utils::ReadParam('att_code', ''); $iInputId = utils::ReadParam('iInputId', ''); $oPage->SetContentType('text/html'); $sJson = utils::ReadParam('json', '', false, 'raw_data'); if (!empty($sJson)) { $oWizardHelper = WizardHelper::FromJSON($sJson); $oObj = $oWizardHelper->GetTargetObject(); } $oObj = $oWizardHelper->GetTargetObject(); $oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iInputId); $oWidget->GetObjectCreationDlg($oPage, $sRealClass, $oObj); break; // ui.linksdirectwidget case 'getLinksetRow': $oPage = new JsonPage(); $oPage->SetOutputDataOnly(true); $sClass = utils::ReadParam('class', '', false, 'class'); $sRealClass = utils::ReadParam('real_class', '', false, 'class'); $sAttCode = utils::ReadParam('att_code', ''); $iInputId = utils::ReadParam('iInputId', ''); $iTempId = utils::ReadParam('tempId', ''); $aValues = utils::ReadParam('values', array(), false, 'raw_data'); $oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iInputId); $oPage->SetData($oWidget->GetFormRow($oPage, $sRealClass, $aValues, -$iTempId)); break; // ui.linksdirectwidget case 'selectObjectsToAdd': $oPage->SetContentType('text/html'); $sClass = utils::ReadParam('class', '', false, 'class'); $aAlreadyLinked = utils::ReadParam('aAlreadyLinked', array()); $sJson = utils::ReadParam('json', '', false, 'raw_data'); /** @var \DBObject $oObj */ $oObj = null; if ($sJson != '') { $oWizardHelper = WizardHelper::FromJSON($sJson); $oObj = $oWizardHelper->GetTargetObject(); } $sRealClass = utils::ReadParam('real_class', '', false, 'class'); $sAttCode = utils::ReadParam('att_code', ''); $iInputId = utils::ReadParam('iInputId', ''); $iCurrObjectId = utils::ReadParam('iObjId', 0); $oPage->SetContentType('text/html'); $oAppContext = new ApplicationContext(); $aPrefillFormParam = array( 'user' => Session::Get('auth_user'), 'context' => $oAppContext->GetAsHash(), 'att_code' => $sAttCode, 'origin' => 'console', 'source_obj' => $oObj, ); $aPrefillFormParam['dest_class'] = ($oObj === null ? '' : $oObj->Get($sAttCode)->GetClass()); $oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iInputId); $oWidget->GetObjectsSelectionDlg($oPage, $oObj, $aAlreadyLinked, $aPrefillFormParam); break; // ui.linksdirectwidget case 'searchObjectsToAdd2': $oPage->SetContentType('text/html'); $sClass = utils::ReadParam('class', '', false, 'class'); $sRealClass = utils::ReadParam('real_class', '', false, 'class'); $sAttCode = utils::ReadParam('att_code', ''); $iInputId = utils::ReadParam('iInputId', ''); $aAlreadyLinked = utils::ReadParam('aAlreadyLinked', array()); $sJson = utils::ReadParam('json', '', false, 'raw_data'); $oObj = null; if ($sJson != '') { $oWizardHelper = WizardHelper::FromJSON($sJson); $oObj = $oWizardHelper->GetTargetObject(); } $oAppContext = new ApplicationContext(); $aPrefillFormParam = array( 'user' => Session::Get('auth_user'), 'context' => $oAppContext->GetAsHash(), 'att_code' => $sAttCode, 'origin' => 'console', 'source_obj' => $oObj, ); $aPrefillFormParam['dest_class'] = ($oObj === null ? '' : $oObj->Get($sAttCode)->GetClass()); $oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iInputId); $oWidget->SearchObjectsToAdd($oPage, $sRealClass, $aAlreadyLinked, $oObj, $aPrefillFormParam); break; // ui.linksdirectwidget case 'doAddObjects2': $oPage->SetContentType('text/html'); $oPage->SetContentType('text/html'); $sClass = utils::ReadParam('class', '', false, 'class'); $sRealClass = utils::ReadParam('real_class', '', false, 'class'); $sAttCode = utils::ReadParam('att_code', ''); $iInputId = utils::ReadParam('iInputId', ''); $iCurrObjectId = utils::ReadParam('iObjId', 0); $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); if ($sFilter != '') { $oFullSetFilter = DBObjectSearch::unserialize($sFilter); } else { $oLinksetDef = MetaModel::GetAttributeDef($sClass, $sAttCode); $valuesDef = $oLinksetDef->GetValuesDef(); if ($valuesDef === null) { $oFullSetFilter = new DBObjectSearch($oLinksetDef->GetLinkedClass()); } else { if (!$valuesDef instanceof ValueSetObjects) { throw new Exception('Error: only ValueSetObjects are supported for "allowed_values" in AttributeLinkedSet ('.$this->sClass.'/'.$this->sAttCode.').'); } $oFullSetFilter = DBObjectSearch::FromOQL($valuesDef->GetFilterExpression()); } } $oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iInputId); $oFullSetFilter->SetShowObsoleteData(utils::ShowObsoleteData()); $oWidget->DoAddObjects($oPage, $oFullSetFilter); break; //////////////////////////////////////////////////////////// // ui.extkeywidget case 'searchObjectsToSelect': $oPage->SetContentType('text/html'); $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $iInputId = utils::ReadParam('iInputId', ''); $sRemoteClass = utils::ReadParam('sRemoteClass', '', false, 'class'); $sFilter = utils::ReadParam('sFilter', '', false, 'raw_data'); $sJson = utils::ReadParam('json', '', false, 'raw_data'); $sAttCode = utils::ReadParam('sAttCode', ''); $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); if (!empty($sJson)) { $oWizardHelper = WizardHelper::FromJSON($sJson); $oObj = $oWizardHelper->GetTargetObject(); } else { // Search form: no current object $oObj = null; } $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode); $oWidget->SearchObjectsToSelect($oPage, $sFilter, $sRemoteClass, $oObj); break; // ui.extkeywidget: autocomplete case 'ac_extkey': $oPage->SetContentType('text/plain'); $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $iInputId = utils::ReadParam('iInputId', ''); $sFilter = utils::ReadParam('sFilter', '', false, 'raw_data'); $sJson = utils::ReadParam('json', '', false, 'raw_data'); $sContains = utils::ReadParam('q', '', false, 'raw_data'); $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); $sOutputFormat = utils::ReadParam('sOutputFormat', UIExtKeyWidget::ENUM_OUTPUT_FORMAT_CSV, false, 'raw_data'); if ($sContains != '') { if (!empty($sJson)) { $oWizardHelper = WizardHelper::FromJSON($sJson); $oObj = $oWizardHelper->GetTargetObject(); } else { // Search form: no current object $oObj = null; } $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, '', $bSearchMode); $oWidget->AutoComplete($oPage, $sFilter, $oObj, $sContains, $sOutputFormat); } break; // ui.extkeywidget case 'objectSearchForm': $oPage->SetContentType('text/html'); $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $sFilter = utils::ReadParam('sFilter', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA); $iInputId = utils::ReadParam('iInputId', ''); $sTitle = utils::ReadParam('sTitle', '', false, 'raw_data'); $sAttCode = utils::ReadParam('sAttCode', ''); $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode, $sFilter); $sJson = utils::ReadParam('json', '', false, 'raw_data'); if (!empty($sJson)) { $oWizardHelper = WizardHelper::FromJSON($sJson); $oObj = $oWizardHelper->GetTargetObject(); } else { // Search form: no current object $oObj = null; } $oWidget->GetSearchDialog($oPage, $sTitle, $oObj); break; // ui.extkeywidget case 'objectCreationForm': $oPage->SetContentType('text/html'); // Retrieving parameters $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $iInputId = utils::ReadParam('iInputId', ''); $sAttCode = utils::ReadParam('sAttCode', ''); $sJson = utils::ReadParam('json', '', false, 'raw_data'); $bTargetClassSelected = utils::ReadParam('bTargetClassSelected', '', false, 'raw_data'); // Building form, if target class has child classes we ask the user for the desired leaf class, unless we've already done just that $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, false); if(!$bTargetClassSelected && MetaModel::HasChildrenClasses($sTargetClass)){ $oWidget->GetClassSelectionForm($oPage); } else { $aPrefillFormParam = array(); if (!empty($sJson)) { $oWizardHelper = WizardHelper::FromJSON($sJson); $oObj = $oWizardHelper->GetTargetObject(); $oAppContext = new ApplicationContext(); $aPrefillFormParam = array( 'user' => Session::Get('auth_user'), 'context' => $oAppContext->GetAsHash(), 'att_code' => $sAttCode, 'source_obj' => $oObj, 'origin' => 'console' ); } else { // Search form: no current object $oObj = null; } $oWidget->GetObjectCreationForm($oPage, $oObj, $aPrefillFormParam); } break; // ui.extkeywidget case 'doCreateObject': $oPage->SetContentType('application/json'); $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $iInputId = utils::ReadParam('iInputId', ''); $sFormPrefix = utils::ReadParam('sFormPrefix', ''); $sAttCode = utils::ReadParam('sAttCode', ''); $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, false); $aResult = $oWidget->DoCreateObject($oPage); echo json_encode($aResult); break; // ui.extkeywidget case 'getObjectName': $oPage->SetContentType('application/json'); $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $iInputId = utils::ReadParam('iInputId', ''); $iObjectId = utils::ReadParam('iObjectId', ''); $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); $sFormAttCode = utils::ReadParam('sFormAttCode', null); $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, '', $bSearchMode); $sName = $oWidget->GetObjectName($iObjectId, $sFormAttCode); echo json_encode(array('name' => $sName)); break; // ui.extkeywidget case 'displayHierarchy': $oPage->SetContentType('text/html'); $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $sInputId = utils::ReadParam('sInputId', ''); $sFilter = utils::ReadParam('sFilter', '', false, 'raw_data'); $sJson = utils::ReadParam('json', '', false, 'raw_data'); $currValue = utils::ReadParam('value', ''); $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); if (!empty($sJson)) { $oWizardHelper = WizardHelper::FromJSON($sJson); $oObj = $oWizardHelper->GetTargetObject(); } else { // Search form: no current object $oObj = null; } $oWidget = new UIExtKeyWidget($sTargetClass, $sInputId, '', $bSearchMode); $oWidget->DisplayHierarchy($oPage, $sFilter, $currValue, $oObj); break; //////////////////////////////////////////////////// // ui.linkswidget case 'doAddObjects': $oPage->SetContentType('text/html'); AjaxRenderController::DoAddObjects($oPage, $sClass, $sFilter); break; // ui.linkswidget case 'doAddIndirectLinks': $oPage = new JsonPage(); AjaxRenderController::DoAddIndirectLinks($oPage, $sClass, $sFilter); break; //////////////////////////////////////////////////////////// /// WizardHelper : see the corresponding PHP class, and JS class case 'wizard_helper_preview': $oPage->SetContentType('text/html'); $sJson = utils::ReadParam('json_obj', '', false, 'raw_data'); $oWizardHelper = WizardHelper::FromJSON($sJson); $oObj = $oWizardHelper->GetTargetObject(); $oObj->DisplayBareProperties($oPage); break; case 'wizard_helper': $oPage->SetContentType('text/html'); $sJson = utils::ReadParam('json_obj', '', false, 'raw_data'); $oWizardHelper = WizardHelper::FromJSON($sJson); /** @var \DBObject $oObj */ $oObj = $oWizardHelper->GetTargetObject(); $sClass = $oWizardHelper->GetTargetClass(); foreach ($oWizardHelper->GetFieldsForDefaultValue() as $sAttCode) { $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); $defaultValue = $oAttDef->GetDefaultValue($oObj); $oWizardHelper->SetDefaultValue($sAttCode, $defaultValue); $oObj->Set($sAttCode, $defaultValue); } $sFormPrefix = $oWizardHelper->GetFormPrefix(); $aExpectedAttributes = ($oWizardHelper->GetStimulus() === null) ? array() : $oObj->GetTransitionAttributes($oWizardHelper->GetStimulus(), $oWizardHelper->GetInitialState()); foreach ($oWizardHelper->GetFieldsForAllowedValues() as $sAttCode) { $sId = $oWizardHelper->GetIdForField($sAttCode); if ($sId != '') { if (array_key_exists($sAttCode, $aExpectedAttributes)) { $iFlags = $aExpectedAttributes[$sAttCode]; } elseif ($oObj->IsNew()) { $iFlags = $oObj->GetInitialStateAttributeFlags($sAttCode); } else { $iFlags = $oObj->GetAttributeFlags($sAttCode); } if ($iFlags & OPT_ATT_READONLY) { $sHTMLValue = "".$oObj->GetAsHTML($sAttCode); $oWizardHelper->SetAllowedValuesHtml($sAttCode, $sHTMLValue); } else { // It may happen that the field we'd like to update does not // exist in the form. For example, if the field should be hidden/read-only // in the current state of the object $value = $oObj->Get($sAttCode); $displayValue = $oObj->GetEditValue($sAttCode); $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); if (!$oAttDef->IsWritable() || ($oWizardHelper->GetReturnNotEditableFields())) { // Even non-writable fields (like AttributeExternal) can be refreshed $sHTMLValue = "
".$oObj->GetAsHTML($sAttCode)."
"; } else { $sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $value, $displayValue, $sId, '', $iFlags, array('this' => $oObj, 'formPrefix' => $sFormPrefix), false); // Make sure that we immediately validate the field when we reload it $oPage->add_ready_script("$('#$sId').trigger('validate');"); } $oWizardHelper->SetAllowedValuesHtml($sAttCode, $sHTMLValue); } } } $oWizardHelper->AddJsForUpdateFields($oPage); 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', '', false, '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 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 /* associative array */); } // Restore the app context from the ExtraParams $oAppContext = new ApplicationContext(false); // false => don't read the context yet ! $aContext = array(); foreach ($oAppContext->GetNames() as $sName) { $sParamName = 'c['.$sName.']'; if (isset($aExtraParams[$sParamName])) { $aContext[$sName] = $aExtraParams[$sParamName]; } } $_REQUEST['c'] = $aContext; if ($sEncoding == 'oql') { $oFilter = DBSearch::FromOQL($sFilter); } else { try { $oFilter = DBSearch::unserialize($sFilter); } catch (CoreException $e) { $sFilter = utils::HtmlEntities($sFilter); $oPage->p("Invalid query (invalid filter) : $sFilter"); IssueLog::Error("ajax.render operation='ajax', invalid DBSearch filter param : $sFilter"); break; } } $oDisplayBlock = new DisplayBlock($oFilter, $sStyle, false); $aExtraParams['display_limit'] = true; $oDisplayBlock->RenderContent($oPage, $aExtraParams); } else { $oPage->p("Invalid query (empty filter)."); } break; case 'displayCSVHistory': $oPage->SetContentType('text/html'); $bShowAll = (utils::ReadParam('showall', 'false') == 'true'); BulkChange::DisplayImportHistory($oPage, true, $bShowAll); break; case 'details': $oPage->SetContentType('text/html'); $key = utils::ReadParam('id', 0); $oFilter = new DBObjectSearch($sClass); $oFilter->AddCondition('id', $key, '='); $oDisplayBlock = new DisplayBlock($oFilter, 'details', false); $oDisplayBlock->RenderContent($oPage); break; case 'pie_chart': $oPage->SetContentType('application/json'); $sGroupBy = utils::ReadParam('group_by', ''); if ($sFilter != '') { if ($sEncoding == 'oql') { $oFilter = DBSearch::FromOQL($sFilter); } else { $oFilter = DBSearch::unserialize($sFilter); } $oDisplayBlock = new DisplayBlock($oFilter, 'pie_chart_ajax', false); $oDisplayBlock->RenderContent($oPage, array('group_by' => $sGroupBy)); } else { $oPage->add("\n3d pie\n."); } break; case 'chart': $iRefresh = utils::ReadParam('refresh', '-1', false, 'int'); if ($iRefresh != -1) { $oPage->SetContentType('application/json'); $aParams = utils::ReadParam('params', array(), false, 'raw_data'); if ($sFilter != '') { $oFilter = DBObjectSearch::FromOQL($sFilter); $oDisplayBlock = new DisplayBlock($oFilter, 'chart_ajax', false); $oBlock = $oDisplayBlock->GetRenderContent($oPage, $aParams, $aParams['currentId']); $sChartType = isset($aParams['chart_type']) ? $aParams['chart_type'] : 'pie'; switch ($sChartType) { case 'bars': $aResult['type'] = 'bars'; //$aResult['JSNames'] = str_replace('"','\'',$oBlock->sJSNames); $aResult['Json'] = str_replace('"', '\'', $oBlock->sJson); $aResult['JSURLs'] = str_replace('"', '\'', $oBlock->sJSURLs); $aResult['js'] = 'charts['.$iRefresh.'].load({json: '.str_replace('"', '\'', $oBlock->sJson). ',keys: { x: \'label\', value: [\'value\']'. '},onclick: function (d) { var aURLs = $.parseJSON('.str_replace('"', '\'', $oBlock->sJSURLs).'); window.location.href= aURLs[d.index]; }})'; break; case 'pie': $aResult['type'] = 'pie'; $aResult['JSColumns'] = str_replace('"', '\'', $oBlock->sJSColumns); $aResult['JSNames'] = str_replace('"', '\'', $oBlock->sJSNames); //$aResult['JSNames'] = json_decode($oBlock->sJSNames); $aResult['JSURLs'] = str_replace('"', '\'', $oBlock->sJSURLs); $aResult['js'] = 'charts['.$iRefresh.'].load({columns: '.str_replace('"', '\'', $oBlock->sJSColumns). ',names: '.str_replace('"', '\'', $oBlock->sJSNames). ',onclick: function (d) { var aURLs = $.parseJSON('.str_replace('"', '\'', $oBlock->sJSURLs).'); window.location.href= aURLs[d.index]; }})'; break; } } else { $aResult = []; } $oPage->add(json_encode($aResult)); } else { // Workaround for IE8 + IIS + HTTPS // See TRAC #363, fix described here: http://forums.codecharge.com/posts.php?post_id=97771 $oPage->add_header("Cache-Control: cache, must-revalidate"); $oPage->add_header("Pragma: public"); $oPage->add_header("Expires: Fri, 17 Jul 1970 05:00:00 GMT"); $aParams = utils::ReadParam('params', array(), false, 'raw_data'); if ($sFilter != '') { $oFilter = DBSearch::unserialize($sFilter); $oDisplayBlock = new DisplayBlock($oFilter, 'chart_ajax', false); $oDisplayBlock->RenderContent($oPage, $aParams); } else { $oPage->add("\n3d pie\n."); } } break; case 'modal_details': $oPage->SetContentType('text/html'); $key = utils::ReadParam('id', 0); $oFilter = new DBObjectSearch($sClass); $oFilter->AddCondition('id', $key, '='); $oPage->Add("

Object Details

\n"); $oDisplayBlock = new DisplayBlock($oFilter, 'details', false); $oDisplayBlock->RenderContent($oPage); $oPage->Add("\n"); break; case 'link': $oPage->SetContentType('text/html'); $sClass = utils::ReadParam('sclass', 'logInfra', false, 'class'); $sAttCode = utils::ReadParam('attCode', 'name'); //$sOrg = utils::ReadParam('org_id', ''); $sName = utils::ReadParam('q', ''); $iMaxCount = utils::ReadParam('max', 30); $iCount = 0; $oFilter = new DBObjectSearch($sClass); $oFilter->AddCondition($sAttCode, $sName, 'Begins with'); //$oFilter->AddCondition('org_id', $sOrg, '='); $oSet = new CMDBObjectSet($oFilter, array($sAttCode => true)); while (($iCount < $iMaxCount) && ($oObj = $oSet->fetch())) { $oPage->add($oObj->GetAsHTML($sAttCode)."|".$oObj->GetKey()."\n"); $iCount++; } break; case 'combo_options': $oPage->SetContentType('text/html'); $oFilter = DBSearch::FromOQL($sFilter); $oSet = new CMDBObjectSet($oFilter); while ($oObj = $oSet->fetch()) { $oPage->add(''); } break; case 'display_document': $id = utils::ReadParam('id', ''); $sField = utils::ReadParam('field', ''); if (!empty($sClass) && ($sClass != 'InlineImage') && !empty($id) && !empty($sField)) { $oPage = new DownloadPage(''); // X-Frame http header : set in page constructor, but we need to allow frame integration for this specific page // so we're resetting its value ! (see N°3416) $oPage->add_xframe_options(''); $iCacheSec = (int)utils::ReadParam('cache', 0); $oPage->set_cache($iCacheSec); // N°4129 - Prevent XSS attacks & other script executions if (utils::GetConfig()->Get('security.disable_inline_documents_sandbox') === false) { $oPage->add_header('Content-Security-Policy: sandbox;'); } ormDocument::DownloadDocument($oPage, $sClass, $id, $sField, 'inline'); } break; case 'search_form': $oPage->SetContentType('text/html'); $sClass = utils::ReadParam('className', '', false, 'class'); $sRootClass = utils::ReadParam('baseClass', '', false, 'class'); $currentId = utils::ReadParam('currentId', ''); $sTableId = utils::ReadParam('_table_id_', null, false, 'raw_data'); $sAction = utils::ReadParam('action', ''); $sSelectionMode = utils::ReadParam('selection_mode', null, false, 'raw_data'); $sResultListOuterSelector = utils::ReadParam('result_list_outer_selector', null, false, 'raw_data'); $scssCount = utils::ReadParam('css_count', null, false, 'raw_data'); $sTableInnerId = utils::ReadParam('table_inner_id', $sTableId, false, 'raw_data'); $oFilter = new DBObjectSearch($sClass); $oSet = new CMDBObjectSet($oFilter); $sHtml = cmdbAbstractObject::GetSearchForm($oPage, $oSet, array( 'currentId' => $currentId, 'baseClass' => $sRootClass, 'action' => $sAction, 'table_id' => $sTableId, 'selection_mode' => $sSelectionMode, 'result_list_outer_selector' => $sResultListOuterSelector, 'cssCount' => $scssCount, 'table_inner_id' => $sTableInnerId )); $oPage->add($sHtml); break; case 'set_pref': $sCode = utils::ReadPostedParam('code', '', 'raw_data'); $sValue = utils::ReadPostedParam('value', '', 'raw_data'); appUserPreferences::SetPref($sCode, $sValue); break; case 'erase_all_pref': // Can be useful in case a user got some corrupted prefs... appUserPreferences::ClearPreferences(); break; case 'on_form_cancel': // Called when a creation/modification form is cancelled by the end-user // Let's take this opportunity to inform the plug-ins so that they can perform some cleanup $iTransactionId = utils::ReadParam('transaction_id', 0, false, 'transaction_id'); $sTempId = utils::GetUploadTempId($iTransactionId); InlineImage::OnFormCancel($sTempId); /** @var \iApplicationUIExtension $oExtensionInstance */ foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) { $oExtensionInstance->OnFormCancel($sTempId); } $sObjClass = utils::ReadParam('obj_class', '', false, 'class'); $iObjKey = (int)utils::ReadParam('obj_key', 0, false, 'integer'); $sToken = utils::ReadParam('token', 0, false, 'raw_data'); if (($sObjClass != '') && ($iObjKey != 0) && ($sToken != '')) { $bReleaseLock = iTopOwnershipLock::ReleaseLock($sObjClass, $iObjKey, $sToken); } // Invalidate temporary objects TemporaryObjectManager::GetInstance()->CancelAllTemporaryObjects($iTransactionId); IssueLog::Trace('on_form_cancel', $sObjClass, array( '$iObjKey' => $iObjKey, '$sTransactionId' => $iTransactionId, '$sTempId' => $sTempId, '$sToken' => $sToken, '$sUser' => UserRights::GetUser(), 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], )); break; case 'dashboard': $oPage->SetContentType('text/html'); $id = (int)utils::ReadParam('id', 0); $sAttCode = utils::ReadParam('attcode', ''); /** @var \cmdbAbstractObject $oObj */ $oObj = MetaModel::GetObject($sClass, $id); // - Add graphs dependencies WebResourcesHelper::EnableC3JSToWebPage($oPage); $oObj->DisplayDashboard($oPage, $sAttCode); break; case 'export_dashboard': $oPage = new DownloadPage(''); $sDashboardId = utils::ReadParam('id', '', false, 'raw_data'); $sDashboardFileRelative = utils::ReadParam('file', '', false, 'raw_data'); $sDashboardFile = RuntimeDashboard::GetDashboardFileFromRelativePath($sDashboardFileRelative); $oDashboard = RuntimeDashboard::GetDashboard($sDashboardFile, $sDashboardId); if (!is_null($oDashboard)) { $oPage->TrashUnexpectedOutput(); $oPage->SetContentType('text/xml'); $oPage->SetContentDisposition('attachment', 'dashboard_'.$oDashboard->GetTitle().'.xml'); $oPage->add($oDashboard->ToXml()); } break; case 'import_dashboard': $oPage = new JsonPage(); $oPage->SetOutputDataOnly(true); $sTransactionId = utils::ReadParam('transaction_id', '', false, 'transaction_id'); if (!utils::IsTransactionValid($sTransactionId, true)) { throw new SecurityException('ajax.render.php import_dashboard : invalid transaction_id'); } $sDashboardId = utils::ReadParam('id', '', false, 'raw_data'); $sDashboardFileRelative = utils::ReadParam('file', '', false, 'raw_data'); $sDashboardFile = RuntimeDashboard::GetDashboardFileFromRelativePath($sDashboardFileRelative); $oDashboard = RuntimeDashboard::GetDashboard($sDashboardFile, $sDashboardId); $aResult = array('error' => ''); if (!is_null($oDashboard)) { try { $oDoc = utils::ReadPostedDocument('dashboard_upload_file'); $oDashboard->FromXml($oDoc->GetData()); $oDashboard->Save(); } catch (DOMException $e) { $aResult = array('error' => Dict::S('UI:Error:InvalidDashboardFile')); } catch (Exception $e) { $aResult = array('error' => $e->getMessage()); } } else { $aResult['error'] = 'Dashboard id="'.$sDashboardId.'" not found.'; } $oPage->SetData($aResult); break; case 'toggle_dashboard': $oPage->SetContentType('text/html'); $sDashboardId = utils::ReadParam('dashboard_id', '', false, 'raw_data'); $bStandardSelected = appUserPreferences::GetPref('display_original_dashboard_'.$sDashboardId, false); appUserPreferences::UnsetPref('display_original_dashboard_'.$sDashboardId); appUserPreferences::SetPref('display_original_dashboard_'.$sDashboardId, !$bStandardSelected); $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); $sDashboardFile = utils::ReadParam('file', '', false, 'raw_data'); $sReloadURL = utils::ReadParam('reload_url', '', false, utils::ENUM_SANITIZATION_FILTER_URL); $oDashboard = RuntimeDashboard::GetDashboard($sDashboardFile, $sDashboardId); $aResult = array('error' => ''); if (!is_null($oDashboard)) { if (!empty($sReloadURL)) { $oDashboard->SetReloadURL($sReloadURL); } $oDashboard->Render($oPage, false, $aExtraParams); } break; case 'reload_dashboard': $oPage->SetContentType('text/html'); $sDashboardId = utils::ReadParam('dashboard_id', '', false, 'raw_data'); $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); $sDashboardFile = utils::ReadParam('file', '', false, 'raw_data'); $sReloadURL = utils::ReadParam('reload_url', '', false, utils::ENUM_SANITIZATION_FILTER_URL); $oDashboard = RuntimeDashboard::GetDashboard($sDashboardFile, $sDashboardId); $aResult = array('error' => ''); if (!is_null($oDashboard)) { if (!empty($sReloadURL)) { $oDashboard->SetReloadURL($sReloadURL); } $oDashboard->Render($oPage, false, $aExtraParams); } break; case 'save_dashboard': $sDashboardId = utils::ReadParam('dashboard_id', '', false, 'context_param'); $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); $sReloadURL = utils::ReadParam('reload_url', '', false, utils::ENUM_SANITIZATION_FILTER_URL); appUserPreferences::SetPref('display_original_dashboard_'.$sDashboardId, false); $sJSExtraParams = json_encode($aExtraParams); $aParams = array(); $aParams['layout_class'] = utils::ReadParam('layout_class', ''); $aParams['title'] = utils::ReadParam('title', '', false, 'raw_data'); $aParams['auto_reload'] = utils::ReadParam('auto_reload', false); $aParams['auto_reload_sec'] = utils::ReadParam('auto_reload_sec', 300); $aParams['cells'] = utils::ReadParam('cells', array(), false, 'raw_data'); $oDashboard = new RuntimeDashboard($sDashboardId); $oDashboard->FromParams($aParams); $bIsNew = $oDashboard->Save(); $sDashboardFile = addslashes(utils::ReadParam('file', '', false, 'string')); $sDashboardDivId = preg_replace('/[^a-zA-Z0-9_]/', '', $sDashboardId); $sOperation = 'reload_dashboard'; if ($bIsNew) { // Trigger a reload of the current page since the dashboard just changed $oPage->add_script( <<add_script( <<Revert(); $sFile = addslashes($oDashboard->GetDefinitionFile()); $sDivId = utils::Sanitize($sDashboardId, '', 'element_identifier'); // trigger a reload of the current page since the dashboard just changed $oPage->add_script( <<FromParams($aParams); $oDashboard->SetReloadURL($sReloadURL); $oDashboard->Render($oPage, true /* bEditMode */, $aExtraParams); break; case 'dashboard_editor': $sId = utils::ReadParam('id', '', false, 'context_param'); $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); $aExtraParams['dashboard_div_id'] = utils::Sanitize($sId, '', 'element_identifier'); $sDashboardFile = utils::ReadParam('file', '', false, 'string'); $sReloadURL = utils::ReadParam('reload_url', '', false, utils::ENUM_SANITIZATION_FILTER_URL); $oDashboard = RuntimeDashboard::GetDashboardToEdit($sDashboardFile, $sId); if (!is_null($oDashboard)) { if (!empty($sReloadURL)) { $oDashboard->SetReloadURL($sReloadURL); } $oDashboard->RenderEditor($oPage, $aExtraParams); } break; case 'new_dashlet_id': $sDashboardDivId = utils::ReadParam("dashboardid"); $bIsCustomized = true; // Only called at runtime when customizing a dashboard $iRow = utils::ReadParam("iRow", 0, false, utils::ENUM_SANITIZATION_FILTER_INTEGER); $iCol = utils::ReadParam("iCol", 0, false, utils::ENUM_SANITIZATION_FILTER_INTEGER); $sDashletIdOrig = utils::ReadParam("dashletid", '', false, utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER); $sFinalDashletId = Dashboard::GetDashletUniqueId($bIsCustomized, $sDashboardDivId, $iRow, $iCol, $sDashletIdOrig); $oPage = new AjaxPage(''); $oPage->SetOutputDataOnly(true); $oPage->add($sFinalDashletId); break; case 'new_dashlet': require_once(APPROOT.'application/forms.class.inc.php'); require_once(APPROOT.'application/dashlet.class.inc.php'); $sDashletClass = utils::ReadParam('dashlet_class', '', false, utils::ENUM_SANITIZATION_FILTER_PHP_CLASS); $sDashletId = utils::ReadParam('dashlet_id', '', false, utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER); if (is_subclass_of($sDashletClass, 'Dashlet')) { $oDashlet = new $sDashletClass(new ModelReflectionRuntime(), $sDashletId); $offset = $oPage->start_capture(); $oDashlet->DoRender($oPage, true /* bEditMode */, false /* bEnclosingDiv */); $sHtml = addslashes($oPage->end_capture($offset)); $sHtml = str_replace("\n", '', $sHtml); $sHtml = str_replace("\r", '', $sHtml); $oPage->add_script("$('#dashlet_$sDashletId').html('$sHtml');"); // in ajax web page add_script has the same effect as add_ready_script // but is executed BEFORE all 'ready_scripts' $oForm = $oDashlet->GetForm(); // Rebuild the form since the values/content changed $oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property')); $sHtml = addslashes($oForm->RenderAsPropertySheet($oPage, true /* bReturnHtml */, '.itop-dashboard')); $sHtml = str_replace("\n", '', $sHtml); $sHtml = str_replace("\r", '', $sHtml); $oPage->add_script("$('#dashlet_properties_$sDashletId').html('$sHtml')"); // in ajax web page add_script has the same effect as add_ready_script // but is executed BEFORE all 'ready_scripts' } break; case 'update_dashlet_property': require_once(APPROOT.'application/forms.class.inc.php'); require_once(APPROOT.'application/dashlet.class.inc.php'); $aExtraParams = utils::ReadParam('extra_params', array(), false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA); $aParams = utils::ReadParam('params', [], false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA); // raw_data because we need different filter depending on the options $sDashletClass = utils::Sanitize($aParams['attr_dashlet_class'], DashletUnknown::class, utils::ENUM_SANITIZATION_FILTER_PHP_CLASS); // Dashlet PHP class or DashletUnknown if impl isn't present in the installed extensions $sDashletType = utils::Sanitize($aParams['attr_dashlet_type'], '', utils::ENUM_SANITIZATION_FILTER_PHP_CLASS); // original Dashlet PHP class, could be non-existing on the iTop instance (XML definition loading) $sDashletId = utils::Sanitize($aParams['attr_dashlet_id'], '', utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER); $aUpdatedProperties = utils::Sanitize($aParams['updated'], [], utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER); // Code of the changed properties as an array: 'attr_xxx', 'attr_xxy' etc $aPreviousValues = utils::Sanitize($aParams['previous_values'], [], utils::ENUM_SANITIZATION_FILTER_RAW_DATA); // hash array: 'attr_xxx' => 'old_value' - no sanitization as values will be handled in the Dashlet object if (is_subclass_of($sDashletClass, 'Dashlet')) { /** @var \Dashlet $oDashlet */ $oDashlet = new $sDashletClass(new ModelReflectionRuntime(), $sDashletId); $oDashlet->SetDashletType($sDashletType); $oForm = $oDashlet->GetForm(); $aValues = $oForm->ReadParams(); // hash array: 'xxx' => 'new_value' $aCurrentValues = $aValues; $aUpdatedDecoded = array(); foreach ($aUpdatedProperties as $sProp) { $sDecodedProp = str_replace('attr_', '', $sProp); // Remove the attr_ prefix // Set the previous value if (isset($aPreviousValues[$sProp]) && $aPreviousValues[$sProp] != '') { $aCurrentValues[$sDecodedProp] = $aPreviousValues[$sProp]; } else { if (gettype($aCurrentValues[$sDecodedProp]) == "array") { $aCurrentValues[$sDecodedProp] = []; } else { $aCurrentValues[$sDecodedProp] = ''; } } $aUpdatedDecoded[] = $sDecodedProp; } $oDashlet->FromParams($aCurrentValues); $sPrevClass = get_class($oDashlet); $oDashlet = $oDashlet->Update($aValues, $aUpdatedDecoded); $sNewClass = get_class($oDashlet); if ($sNewClass != $sPrevClass) { $oPage->add_ready_script("$('#dashlet_$sDashletId').dashlet('option', {dashlet_class: '$sNewClass'});"); } if ($oDashlet->IsRedrawNeeded()) { $oBlock = $oDashlet->DoRender($oPage, true, false, $aExtraParams); $sHtml = ConsoleBlockRenderer::RenderBlockTemplateInPage($oPage, $oBlock); $sHtml = str_replace("\n", '', $sHtml); $sHtml = str_replace("\r", '', $sHtml); $sHtml = str_replace("'", "\'", $sHtml); $oPage->add_script("$('#dashlet_$sDashletId').html('$sHtml');"); } if ($oDashlet->IsFormRedrawNeeded()) { $oForm = $oDashlet->GetForm(); // Rebuild the form since the values/content changed $oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property', 'extra_params' => $aExtraParams)); $sHtml = addslashes($oForm->RenderAsPropertySheet($oPage, true, '.itop-dashboard')); $sHtml = str_replace("\n", '', $sHtml); $sHtml = str_replace("\r", '', $sHtml); $oPage->add_script("$('#dashlet_properties_$sDashletId').html('$sHtml')"); } } break; case 'dashlet_creation_dlg': $sOQL = utils::ReadParam('oql', '', false, 'raw_data'); RuntimeDashboard::GetDashletCreationDlgFromOQL($oPage, $sOQL); break; case 'add_dashlet': $oForm = RuntimeDashboard::GetDashletCreationForm(); $aValues = $oForm->ReadParams(); $sDashletClass = $aValues['dashlet_class']; $sMenuId = $aValues['menu_id']; if (is_subclass_of($sDashletClass, 'Dashlet')) { $oDashlet = new $sDashletClass(new ModelReflectionRuntime(), 0); $oDashlet->FromParams($aValues); ApplicationMenu::LoadAdditionalMenus(); $index = ApplicationMenu::GetMenuIndexById($sMenuId); $oMenu = ApplicationMenu::GetMenuNode($index); $oMenu->AddDashlet($oDashlet); // navigate to the dashboard page if ($aValues['open_editor']) { $oPage->add_ready_script("window.location.href='".addslashes(utils::GetAbsoluteUrlAppRoot().'pages/UI.php?c[menu]='.urlencode($sMenuId))."&edit=1';"); // reloads the page, doing a GET even if we arrived via a POST } } break; case 'shortcut_list_dlg': $sOQL = utils::ReadParam('oql', '', false, 'raw_data'); $sTableSettings = utils::ReadParam('table_settings', '', false, 'raw_data'); ShortcutOQL::GetCreationDlgFromOQL($oPage, $sOQL, $sTableSettings); break; case 'shortcut_list_create': $oForm = ShortcutOQL::GetCreationForm(); $aValues = $oForm->ReadParams(); $oAppContext = new ApplicationContext(); $aContext = $oAppContext->GetAsHash(); $sContext = serialize($aContext); // Create shortcut /** @var ShortcutOQL $oShortcut */ $oShortcut = MetaModel::NewObject("ShortcutOQL"); $oShortcut->Set('user_id', UserRights::GetUserId()); $oShortcut->Set("context", $sContext); $oShortcut->Set("name", $aValues['name']); $oShortcut->Set("oql", $aValues['oql']); $iAutoReload = (int)$aValues['auto_reload_sec']; if (($aValues['auto_reload']) && ($iAutoReload > 0)) { $oShortcut->Set("auto_reload_sec", max(MetaModel::GetConfig()->Get('min_reload_interval'), $iAutoReload)); $oShortcut->Set("auto_reload", 'custom'); } utils::PushArchiveMode(false); $iId = $oShortcut->DBInsertNoReload(); utils::PopArchiveMode(); $oShortcut->CloneTableSettings($aValues['table_settings']); // Add shortcut to current menu // - Init. app. menu ApplicationMenu::LoadAdditionalMenus(); // - Find newly created shortcut $aNewShortcutNode = null; $sMenuGroupId = 'MyShortcuts'; $sMenuGroupIdx = ApplicationMenu::GetMenuIndexById($sMenuGroupId); if (0 <= $sMenuGroupIdx) { $sNewShortcutId = $sMenuGroupId.'_'.$oShortcut->GetKey(); $aShortcutsNodes = ApplicationMenu::GetSubMenuNodes($sMenuGroupIdx); foreach ($aShortcutsNodes as $aShortcutNode) { if ($sNewShortcutId === $aShortcutNode['sId']) { $aNewShortcutNode = $aShortcutNode; break; } } } // - If shortcut found, insert it in the navigation menu if (!empty($aNewShortcutNode)) { $sHtml = TwigHelper::RenderTemplate( TwigHelper::GetTwigEnvironment(TwigHelper::ENUM_TEMPLATES_BASE_PATH_BACKOFFICE), ['aMenuNode' => $aNewShortcutNode], 'base/layouts/navigation-menu/menu-node' ); $MenuNameEscaped = utils::HtmlEntities($aValues['name']); // Important: Mind the back ticks to avoid line breaks to break the JS $oPage->add_script(<<StartRenameDialog($oPage); break; case 'shortcut_rename_go': $iShortcut = utils::ReadParam('id', 0); $oShortcut = MetaModel::GetObject('Shortcut', $iShortcut); $sName = utils::ReadParam('attr_name', '', false, 'raw_data'); if (strlen($sName) > 0) { $oShortcut->Set('name', $sName); utils::PushArchiveMode(false); $oShortcut->DBUpdate(); utils::PopArchiveMode(); $oPage->add_ready_script('window.location.reload();'); } break; case 'shortcut_delete_go': $oSearch = new DBObjectSearch('Shortcut'); $oSearch->AddCondition('user_id', UserRights::GetUserId(), '='); $aShortcuts = utils::ReadMultipleSelection($oSearch); foreach ($aShortcuts as $iShortcut) { $oShortcut = MetaModel::GetObject('Shortcut', $iShortcut); utils::PushArchiveMode(false); $oShortcut->DBDelete(); utils::PopArchiveMode(); $oPage->add_ready_script('window.location.reload();'); } break; case 'about_box': AjaxRenderController::DisplayAboutBox($oPage); break; case 'full_text_search': $aFullTextNeedles = utils::ReadParam('needles', array(), false, 'raw_data'); $sFullText = trim(implode(' ', $aFullTextNeedles)); $sClassName = utils::ReadParam('classname', ''); $iCount = utils::ReadParam('count', 0); $iCurrentPos = utils::ReadParam('position', 0); $iTune = utils::ReadParam('tune', 0); if (empty($sFullText)) { $oPage->p(Dict::S('UI:Search:NoSearch')); break; } // Search in full text mode in all the classes $aMatches = array(); // Build the ordered list of classes to search into // if (empty($sClassName)) { $aSearchClasses = MetaModel::GetClasses('searchable'); } else { // Search is limited to a given class and its subclasses $aSearchClasses = MetaModel::EnumChildClasses($sClassName, ENUM_CHILD_CLASSES_ALL); } // Skip abstract classes, since we search in all the child classes anyway foreach ($aSearchClasses as $idx => $sClass) { if (MetaModel::IsAbstract($sClass)) { unset($aSearchClasses[$idx]); } } $sMaxChunkDuration = MetaModel::GetConfig()->Get('full_text_chunk_duration'); $aAccelerators = MetaModel::GetConfig()->Get('full_text_accelerators'); foreach (array_reverse($aAccelerators) as $sClass => $aRestriction) { $bSkip = false; $iPos = array_search($sClass, $aSearchClasses); if ($iPos !== false) { unset($aSearchClasses[$iPos]); } else { $bSkip = true; } $bSkip |= array_key_exists('skip', $aRestriction) ? $aRestriction['skip'] : false; if (!in_array($sClass, $aSearchClasses)) { if ($sClass == $sClassName) { // Class explicitely requested, do NOT skip it // beware: there may not be a 'query' defined for a skipped class ! $bSkip = false; } } if (!$bSkip) { // NOT skipped, add the class to the list of classes to search into if (array_key_exists('query', $aRestriction)) { array_unshift($aSearchClasses, $aRestriction['query']); } else { // No accelerator query array_unshift($aSearchClasses, $sClassName); } } } $aSearchClasses = array_values($aSearchClasses); // renumbers the array starting from zero, removing the missing indexes $fStarted = microtime(true); $iFoundInThisRound = 0; for ($iPos = $iCurrentPos; $iPos < count($aSearchClasses); $iPos++) { if ($iFoundInThisRound && (microtime(true) - $fStarted >= $sMaxChunkDuration)) { break; } $sClassSpec = $aSearchClasses[$iPos]; if (substr($sClassSpec, 0, 7) == 'SELECT ') { $oFilter = DBObjectSearch::FromOQL($sClassSpec); $sClassName = $oFilter->GetClass(); $sNeedleFormat = isset($aAccelerators[$sClassName]['needle']) ? $aAccelerators[$sClassName]['needle'] : '%$needle$%'; $sNeedle = str_replace('$needle$', $sFullText, $sNeedleFormat); $aParams = array('needle' => $sNeedle); } else { $sClassName = $sClassSpec; $oFilter = new DBObjectSearch($sClassName); $aParams = array(); foreach ($aFullTextNeedles as $sSearchText) { $oFilter->AddCondition_FullText($sSearchText); } } $oFilter->SetShowObsoleteData(utils::ShowObsoleteData()); // Skip abstract classes if (MetaModel::IsAbstract($sClassName)) { continue; } if ($iTune > 0) { $fStartedClass = microtime(true); } $oSet = new DBObjectSet($oFilter, array(), $aParams); if (array_key_exists($sClassName, $aAccelerators) && array_key_exists('attributes', $aAccelerators[$sClassName])) { $oSet->OptimizeColumnLoad(array($oFilter->GetClassAlias() => $aAccelerators[$sClassName]['attributes'])); } $sFullTextJS = addslashes($sFullText); $bEnableEnlarge = array_key_exists($sClassName, $aAccelerators) && array_key_exists('query', $aAccelerators[$sClassName]); if (array_key_exists($sClassName, $aAccelerators) && array_key_exists('enable_enlarge', $aAccelerators[$sClassName])) { $bEnableEnlarge &= $aAccelerators[$sClassName]['enable_enlarge']; } $sEnlargeTheSearch = <<'); var oParams = {operation: 'full_text_search_enlarge', class: '$sClassName', text: '$sFullTextJS'}; $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function(data) { $('.search-class-$sClassName').html(data); }); EOF; $sEnlargeButton = ''; if ($bEnableEnlarge) { $sEnlargeButton = " "; } if ($oSet->Count() > 0) { $aLeafs = array(); while ($oObj = $oSet->Fetch()) { if (get_class($oObj) == $sClassName) { $aLeafs[] = $oObj->GetKey(); $iFoundInThisRound++; } } $oLeafsFilter = new DBObjectSearch($sClassName); if (count($aLeafs) > 0) { $iCount += count($aLeafs); $oPage->add("
\n"); $oPage->add("
\n"); if (array_key_exists($sClassName, $aAccelerators)) { $oPage->add('

'.MetaModel::GetClassIcon($sClassName).Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aLeafs), Metamodel::GetName($sClassName)).$sEnlargeButton."

\n"); } else { $oPage->add('

'.MetaModel::GetClassIcon($sClassName).Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aLeafs), Metamodel::GetName($sClassName))."

\n"); } $oPage->add("
\n"); $oLeafsFilter->AddCondition('id', $aLeafs, 'IN'); $oBlock = new DisplayBlock($oLeafsFilter, 'list', false); $sBlockId = 'global_search_'.$sClassName; $oPage->add('
'); $oBlock->RenderContent($oPage, array('table_id' => $sBlockId, 'currentId' => $sBlockId)); $oPage->add("
\n"); $oPage->add("
\n"); $oPage->p(' '); // Some space ? } } else { if (array_key_exists($sClassName, $aAccelerators)) { $oPage->add("
\n"); $oPage->add("
\n"); $oPage->add('

'.MetaModel::GetClassIcon($sClassName).Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', 0, Metamodel::GetName($sClassName)).$sEnlargeButton."

\n"); $oPage->add("
\n"); $oPage->add("
\n"); $oPage->p(' '); // Some space ? } } if ($iTune > 0) { $fDurationClass = microtime(true) - $fStartedClass; $oPage->add_script("oTimeStatistics.$sClassName = $fDurationClass;"); } } if ($iPos < count($aSearchClasses)) { $sJSNeedle = json_encode($aFullTextNeedles); $oPage->add_ready_script( <<add_ready_script( << 0) { $oPage->add_ready_script( <<' + fRounded + ''; } fRoundedTotal = Math.round(fTotal * 1000) / 1000; sRes += 'Total' + fRoundedTotal + ''; sRes += ''; sRes += ''; $('#full_text_results').append(sRes); EOF ); } if ($iCount == 0) { $sFullTextSummary = addslashes(Dict::S('UI:Search:NoObjectFound')); $oPage->add_ready_script("$('#full_text_results').append('
$sFullTextSummary
');"); } } break; case 'full_text_search_enlarge': $sFullText = trim(utils::ReadParam('text', '', false, 'raw_data')); $sClass = trim(utils::ReadParam('class', '')); $iTune = utils::ReadParam('tune', 0); if (preg_match('/^"(.*)"$/', $sFullText, $aMatches)) { // The text is surrounded by double-quotes, remove the quotes and treat it as one single expression $aFullTextNeedles = array($aMatches[1]); } else { // Split the text on the blanks and treat this as a search for AND AND $aFullTextNeedles = explode(' ', $sFullText); } $oFilter = new DBObjectSearch($sClass); foreach ($aFullTextNeedles as $sSearchText) { $oFilter->AddCondition_FullText($sSearchText); } $oFilter->SetShowObsoleteData(utils::ShowObsoleteData()); $oSet = new DBObjectSet($oFilter); $oPage->add("
\n"); $oPage->add("

".MetaModel::GetClassIcon($sClass)." ".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', $oSet->Count(), Metamodel::GetName($sClass))."

\n"); $oPage->add("
\n"); if ($oSet->Count() > 0) { $aLeafs = array(); while ($oObj = $oSet->Fetch()) { if (get_class($oObj) == $sClass) { $aLeafs[] = $oObj->GetKey(); } } $oLeafsFilter = new DBObjectSearch($sClass); if (count($aLeafs) > 0) { $oLeafsFilter->AddCondition('id', $aLeafs, 'IN'); $oBlock = new DisplayBlock($oLeafsFilter, 'list', false); $sBlockId = 'global_search_'.$sClass; $oPage->add('
'); $oBlock->RenderContent($oPage, array('table_id' => $sBlockId, 'currentId' => $sBlockId)); $oPage->add('
'); $oPage->P(' '); // Some space ? // Hide "no object found" $oPage->add_ready_script('$("#no_object_found").hide();'); } } $oPage->add_ready_script( <<SetContentType('text/html'); $oPage->add( << .ui-progressbar { position: relative; } .progress-label { position: absolute; left: 50%; top: 1px; font-size: 11pt; } .download-form button { display:block; margin-left: auto; margin-right: auto; margin-top: 2em; } .ui-progressbar-value { background: url(../setup/orange-progress.gif); } .progress-bar { height: 20px; } .statistics > div { padding-left: 16px; cursor: pointer; font-size: 10pt; background: url({$sAppRootUrl}images/minus.gif) 0 2px no-repeat; } .statistics > div.closed { padding-left: 16px; background: url({$sAppRootUrl}images/plus.gif) 0 2px no-repeat; } .statistics .closed .stats-data { display: none; } .stats-data td { padding-right: 5px; } EOF ); $oPage->add('
'); $oPage->add('
'); $oPage->add('

 

'); $oPage->add('

'.Dict::S('UI:CSVImport:AdvancedMode+').'

'); $oPage->add('

 

'); $oPage->add('
'); $oPage->add('

'.Dict::S('ExcelExport:PreparingExport').'

'); $oPage->add('
'.Dict::S('ExcelExport:Statistics').'
'); $oPage->add('
'); $aLabels = array( 'dialog_title' => Dict::S('ExcelExporter:ExportDialogTitle'), 'cancel_button' => Dict::S('UI:Button:Cancel'), 'export_button' => Dict::S('ExcelExporter:ExportButton'), 'download_button' => Dict::Format('ExcelExporter:DownloadButton', 'export.xlsx'), //TODO: better name for the file (based on the class of the filter??) ); $sJSLabels = json_encode($aLabels); $sFilter = addslashes($sFilter); $sJSPageUrl = addslashes($sAppRootUrl.'pages/ajax.render.php'); $oPage->add_ready_script("$('#XlsxExportDlg').xlsxexporter({filter: '$sFilter', labels: $sJSLabels, ajax_page_url: '$sJSPageUrl'});"); break; case 'xlsx_start': DeprecatedCallsLog::NotifyDeprecatedPhpEndpoint('Use "export_*" operations instead of "'.$operation.'"'); $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); $bAdvanced = (utils::ReadParam('advanced', 'false') == 'true'); $oSearch = DBObjectSearch::unserialize($sFilter); $oExcelExporter = new ExcelExporter(); $oExcelExporter->SetObjectList($oSearch); //$oExcelExporter->SetChunkSize(10); //Only for testing $oExcelExporter->SetAdvancedMode($bAdvanced); $sToken = $oExcelExporter->SaveState(); $oPage->add(json_encode(array('status' => 'ok', 'token' => $sToken))); break; case 'xlsx_run': DeprecatedCallsLog::NotifyDeprecatedPhpEndpoint('Use "export_*" operations instead of "'.$operation.'"'); $sMemoryLimit = MetaModel::GetConfig()->Get('xlsx_exporter_memory_limit'); if (utils::SetMinMemoryLimit($sMemoryLimit) === false) { IssueLog::Warning("XSLX export : cannot set memory_limit to {$sMemoryLimit}"); } ini_set('max_execution_time', max(300, ini_get('max_execution_time'))); // At least 5 minutes $sToken = utils::ReadParam('token', '', false, 'raw_data'); $oExcelExporter = new ExcelExporter($sToken); $aStatus = $oExcelExporter->Run(); $aResults = array('status' => $aStatus['code'], 'percentage' => $aStatus['percentage'], 'message' => $aStatus['message']); if ($aStatus['code'] == 'done') { $aResults['statistics'] = $oExcelExporter->GetStatistics('html'); } $oPage->add(json_encode($aResults)); break; case 'xlsx_download': DeprecatedCallsLog::NotifyDeprecatedPhpEndpoint('Use "export_*" operations instead of "'.$operation.'"'); $oPage = new DownloadPage(''); $sToken = utils::ReadParam('token', '', false, 'raw_data'); $oPage->SetContentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); $oPage->SetContentDisposition('attachment', 'export.xlsx'); $sFileContent = ExcelExporter::GetExcelFileFromToken($sToken); $oPage->add($sFileContent); ExcelExporter::CleanupFromToken($sToken); break; case 'xlsx_abort': DeprecatedCallsLog::NotifyDeprecatedPhpEndpoint('Use "export_*" operations instead of "'.$operation.'"'); // Stop & cleanup an export... $sToken = utils::ReadParam('token', '', false, 'raw_data'); ExcelExporter::CleanupFromToken($sToken); break; case 'relation_pdf': case 'relation_attachment': require_once(APPROOT.'core/simplegraph.class.inc.php'); require_once(APPROOT.'core/relationgraph.class.inc.php'); require_once(APPROOT.'core/displayablegraph.class.inc.php'); $sRelation = utils::ReadParam('relation', 'impacts'); $sDirection = utils::ReadParam('direction', 'down'); $iGroupingThreshold = utils::ReadParam('g', 5, false, 'integer'); $sPageFormat = utils::ReadParam('p', 'A4'); $sPageOrientation = utils::ReadParam('o', 'L'); $sTitle = utils::ReadParam('title', '', false, 'raw_data'); $sPositions = utils::ReadParam('positions', null, false, 'raw_data'); $aExcludedClasses = utils::ReadParam('excluded_classes', array(), false, 'raw_data'); $bIncludeList = (bool)utils::ReadParam('include_list', false); $sComments = utils::ReadParam('comments', '', false, 'raw_data'); $aContexts = utils::ReadParam('contexts', array(), false, 'raw_data'); $sContextKey = utils::ReadParam('context_key', '', false, 'raw_data'); $aPositions = null; if ($sPositions != null) { $aPositions = json_decode($sPositions, true); } // Get the list of source objects $aSources = utils::ReadParam('sources', array(), false, 'raw_data'); $aSourceObjects = array(); foreach ($aSources as $sClass => $aIDs) { $oSearch = new DBObjectSearch($sClass); $oSearch->AddCondition('id', $aIDs, 'IN'); $oSet = new DBObjectSet($oSearch); while ($oObj = $oSet->Fetch()) { $aSourceObjects[] = $oObj; } } $sSourceClass = '*'; if (count($aSourceObjects) == 1) { $sSourceClass = get_class($aSourceObjects[0]); } // Get the list of excluded objects $aExcluded = utils::ReadParam('excluded', array(), false, 'raw_data'); $aExcludedObjects = array(); foreach ($aExcluded as $sClass => $aIDs) { $oSearch = new DBObjectSearch($sClass); $oSearch->AddCondition('id', $aIDs, 'IN'); $oSet = new DBObjectSet($oSearch); while ($oObj = $oSet->Fetch()) { $aExcludedObjects[] = $oObj; } } $iMaxRecursionDepth = MetaModel::GetConfig()->Get('relations_max_depth'); if ($sDirection == 'up') { $oRelGraph = MetaModel::GetRelatedObjectsUp($sRelation, $aSourceObjects, $iMaxRecursionDepth, true, $aContexts); } else { $oRelGraph = MetaModel::GetRelatedObjectsDown($sRelation, $aSourceObjects, $iMaxRecursionDepth, true, $aExcludedObjects, $aContexts); } // Remove excluded classes from the graph if (count($aExcludedClasses) > 0) { $oIterator = new RelationTypeIterator($oRelGraph, 'Node'); foreach ($oIterator as $oNode) { $oObj = $oNode->GetProperty('object'); if ($oObj && in_array(get_class($oObj), $aExcludedClasses)) { $oRelGraph->FilterNode($oNode); } } } $oPage = new PDFPage($sTitle, $sPageFormat, $sPageOrientation); $oPage->SetContentDisposition('attachment', $sTitle.'.pdf'); $oGraph = DisplayableGraph::FromRelationGraph($oRelGraph, $iGroupingThreshold, ($sDirection == 'down'), true); $oGraph->InitFromGraphviz(); if ($aPositions != null) { $oGraph->UpdatePositions($aPositions); } $aGroups = array(); $oIterator = new RelationTypeIterator($oGraph, 'Node'); foreach ($oIterator as $oNode) { if ($oNode instanceof DisplayableGroupNode) { $aGroups[$oNode->GetProperty('group_index')] = $oNode->GetObjects(); } } // First page is the graph $oGraph->RenderAsPDF($oPage, $sComments, $sContextKey); if ($bIncludeList) { // Then the lists of objects (one table per finalclass) $aResults = array(); $oIterator = new RelationTypeIterator($oRelGraph, 'Node'); foreach ($oIterator as $oNode) { $oObj = $oNode->GetProperty('object'); // Some nodes (Redundancy Nodes and Group) do not contain an object if ($oObj) { $sObjClass = get_class($oObj); if (!array_key_exists($sObjClass, $aResults)) { $aResults[$sObjClass] = array(); } $aResults[$sObjClass][] = $oObj; } } $oPage->get_tcpdf()->AddPage(); $oPage->get_tcpdf()->SetFontSize(10); // Reset the font size to its default $oPage->AddSubBlock(TitleUIBlockFactory::MakeNeutral(Dict::S('UI:RelationshipList'))); $iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop'); foreach ($aResults as $sListClass => $aObjects) { set_time_limit($iLoopTimeLimit * count($aObjects)); $oSet = CMDBObjectSet::FromArray($sListClass, $aObjects); $oSet->SetShowObsoleteData(utils::ShowObsoleteData()); /* cf N°3928 - Polishing: Impact analysis - remove icons in pdf list $sIconUrl = MetaModel::GetClassIcon($sListClass, false); $sIconUrl = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-'.utils::GetCurrentEnvironment().'/', $sIconUrl); $oTitle = new Html(" ".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', $oSet->Count(), Metamodel::GetName($sListClass)));*/ $oTitle = new Html(Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', $oSet->Count(), Metamodel::GetName($sListClass))); $oPage->AddSubBlock(TitleUIBlockFactory::MakeStandard($oTitle, 2)); $oPage->AddSubBlock(cmdbAbstractObject::GetDataTableFromDBObjectSet($oSet, array('table_id' => $sSourceClass.'_'.$sRelation.'_'.$sDirection.'_'.$sListClass))); } // Then the content of the groups (one table per group) if (count($aGroups) > 0) { $oPage->get_tcpdf()->AddPage(); $oPage->AddSubBlock(TitleUIBlockFactory::MakeNeutral(Dict::S('UI:RelationGroups'))); foreach ($aGroups as $idx => $aObjects) { set_time_limit($iLoopTimeLimit * count($aObjects)); $sListClass = get_class(current($aObjects)); $oSet = CMDBObjectSet::FromArray($sListClass, $aObjects); $sIconUrl = MetaModel::GetClassIcon($sListClass, false); $sIconUrl = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-'.utils::GetCurrentEnvironment().'/', $sIconUrl); $oTitle = new Html(" ".Dict::Format('UI:RelationGroupNumber_N', (1 + $idx)), Metamodel::GetName($sListClass)); $oPage->AddSubBlock(TitleUIBlockFactory::MakeStandard($oTitle, 2)); $oPage->AddSubBlock(cmdbAbstractObject::GetDataTableFromDBObjectSet($oSet)); } } } if ($operation == 'relation_attachment') { $sObjClass = utils::ReadParam('obj_class', '', false, 'class'); $iObjKey = (int)utils::ReadParam('obj_key', 0, false, 'integer'); // Save the generated PDF as an attachment $sPDF = $oPage->get_pdf(); $oPage = new JsonPage(); $oPage->SetOutputDataOnly(true); $oAttachment = MetaModel::NewObject('Attachment'); $oAttachment->Set('item_class', $sObjClass); $oAttachment->Set('item_id', $iObjKey); $oDoc = new ormDocument($sPDF, 'application/pdf', $sTitle.'.pdf'); $oAttachment->Set('contents', $oDoc); $iAttachmentId = $oAttachment->DBInsert(); $aRet = array( 'status' => 'ok', 'att_id' => $iAttachmentId, ); $oPage->SetData($aRet); } break; case 'relation_json': require_once(APPROOT.'core/simplegraph.class.inc.php'); require_once(APPROOT.'core/relationgraph.class.inc.php'); require_once(APPROOT.'core/displayablegraph.class.inc.php'); $sRelation = utils::ReadParam('relation', 'impacts'); $sDirection = utils::ReadParam('direction', 'down'); $iGroupingThreshold = utils::ReadParam('g', 5); $sPositions = utils::ReadParam('positions', null, false, 'raw_data'); $aExcludedClasses = utils::ReadParam('excluded_classes', array(), false, 'raw_data'); $aContexts = utils::ReadParam('contexts', array(), false, 'raw_data'); $sContextKey = utils::ReadParam('context_key', array(), false, 'raw_data'); $aPositions = null; if ($sPositions != null) { $aPositions = json_decode($sPositions, true); } // Get the list of source objects $aSources = utils::ReadParam('sources', array(), false, 'raw_data'); $aSourceObjects = array(); foreach ($aSources as $sClass => $aIDs) { $oSearch = new DBObjectSearch($sClass); $oSearch->AddCondition('id', $aIDs, 'IN'); $oSet = new DBObjectSet($oSearch); while ($oObj = $oSet->Fetch()) { $aSourceObjects[] = $oObj; } } // Get the list of excluded objects $aExcluded = utils::ReadParam('excluded', array(), false, 'raw_data'); $aExcludedObjects = array(); foreach ($aExcluded as $sClass => $aIDs) { $oSearch = new DBObjectSearch($sClass); $oSearch->AddCondition('id', $aIDs, 'IN'); $oSet = new DBObjectSet($oSearch); while ($oObj = $oSet->Fetch()) { $aExcludedObjects[] = $oObj; } } // Compute the graph $iMaxRecursionDepth = MetaModel::GetConfig()->Get('relations_max_depth'); if ($sDirection == 'up') { $oRelGraph = MetaModel::GetRelatedObjectsUp($sRelation, $aSourceObjects, $iMaxRecursionDepth, true, $aContexts); } else { $oRelGraph = MetaModel::GetRelatedObjectsDown($sRelation, $aSourceObjects, $iMaxRecursionDepth, true, $aExcludedObjects, $aContexts); } // Remove excluded classes from the graph if (count($aExcludedClasses) > 0) { $oIterator = new RelationTypeIterator($oRelGraph, 'Node'); foreach ($oIterator as $oNode) { $oObj = $oNode->GetProperty('object'); if ($oObj && in_array(get_class($oObj), $aExcludedClasses)) { $oRelGraph->FilterNode($oNode); } } } $oGraph = DisplayableGraph::FromRelationGraph($oRelGraph, $iGroupingThreshold, ($sDirection == 'down')); $oGraph->InitFromGraphviz(); if ($aPositions != null) { $oGraph->UpdatePositions($aPositions); } $oPage->add($oGraph->GetAsJSON($sContextKey)); $oPage->SetContentType('application/json'); break; case 'relation_groups': $aGroups = utils::ReadParam('groups'); $iBlock = 1; // Zero is not a valid blockid foreach ($aGroups as $idx => $aDefinition) { $sListClass = $aDefinition['class']; $oSearch = new DBObjectSearch($sListClass); $oSearch->AddCondition('id', $aDefinition['keys'], 'IN'); $oSearch->SetShowObsoleteData(utils::ShowObsoleteData()); $oPage->AddUiBlock(TitleUIBlockFactory::MakeNeutral(Dict::Format('UI:RelationGroupNumber_N', (1 + $idx)), 1, "relation_group_$idx")); $oBlock = new DisplayBlock($oSearch, 'list'); $oBlock->Display($oPage, 'group_'.$iBlock++, array( 'surround_with_panel' => true, 'panel_class' => $sListClass, 'panel_title' => Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aDefinition['keys']), Metamodel::GetName($sListClass)), 'panel_icon' => MetaModel::GetClassIcon($sListClass, false), )); } break; case 'relation_lists': $aLists = utils::ReadParam('lists'); $iBlock = 1; // Zero is not a valid blockid foreach ($aLists as $sListClass => $aKeys) { $oSearch = new DBObjectSearch($sListClass); $oSearch->AddCondition('id', $aKeys, 'IN'); $oSearch->SetShowObsoleteData(utils::ShowObsoleteData()); $oBlock = new DisplayBlock($oSearch, 'list'); $oBlock->Display($oPage, 'list_'.$iBlock++, array( 'table_id' => 'ImpactAnalysis_'.$sListClass, 'surround_with_panel' => true, 'panel_class' => $sListClass, 'panel_title' => Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aKeys), Metamodel::GetName($sListClass)), 'panel_icon' => MetaModel::GetClassIcon($sListClass, false), )); } break; case 'ticket_impact': require_once(APPROOT.'core/simplegraph.class.inc.php'); require_once(APPROOT.'core/relationgraph.class.inc.php'); require_once(APPROOT.'core/displayablegraph.class.inc.php'); $sRelation = utils::ReadParam('relation', 'impacts'); $sDirection = utils::ReadParam('direction', 'down'); $iGroupingThreshold = utils::ReadParam('g', 5); $sClass = utils::ReadParam('class', '', false, 'class'); $sAttCode = utils::ReadParam('attcode', 'functionalcis_list'); $sImpactAttCode = utils::ReadParam('impact_attcode', 'impact_code'); $sImpactAttCodeValue = utils::ReadParam('impact_attcode_value', 'manual'); $iId = (int)utils::ReadParam('id', 0, false, 'integer'); WebResourcesHelper::EnableSimpleGraphInWebPage($oPage); // Get the list of source objects $oTicket = MetaModel::GetObject($sClass, $iId); $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); $sExtKeyToRemote = $oAttDef->GetExtKeyToRemote(); $oExtKeyToRemote = MetaModel::GetAttributeDef($oAttDef->GetLinkedClass(), $sExtKeyToRemote); $sRemoteClass = $oExtKeyToRemote->GetTargetClass(); $oSet = $oTicket->Get($sAttCode); $aSourceObjects = array(); $aExcludedObjects = array(); while ($oLnk = $oSet->Fetch()) { if ($oLnk->Get($sImpactAttCode) == 'manual') { $aSourceObjects[] = MetaModel::GetObject($sRemoteClass, $oLnk->Get($sExtKeyToRemote)); } if ($oLnk->Get($sImpactAttCode) == 'not_impacted') { $aExcludedObjects[] = MetaModel::GetObject($sRemoteClass, $oLnk->Get($sExtKeyToRemote)); } } // Compute the graph $iMaxRecursionDepth = MetaModel::GetConfig()->Get('relations_max_depth'); if ($sDirection == 'up') { $oRelGraph = MetaModel::GetRelatedObjectsUp($sRelation, $aSourceObjects, $iMaxRecursionDepth); } else { $oRelGraph = MetaModel::GetRelatedObjectsDown($sRelation, $aSourceObjects, $iMaxRecursionDepth, $aExcludedObjects); } $aResults = $oRelGraph->GetObjectsByClass(); $oGraph = DisplayableGraph::FromRelationGraph($oRelGraph, $iGroupingThreshold, ($sDirection == 'down')); $sContextKey = 'itop-tickets/relation_context/'.$sClass.'/'.$sRelation.'/'.$sDirection; $oAppContext = new ApplicationContext(); $oPage->AddSubBlock($oGraph->DisplayFilterBox($oPage, $aResults)); $oGraph->DisplayGraph($oPage, $sRelation, $oAppContext, $aExcludedObjects, $sClass, $iId, $sContextKey, array('this' => $oTicket)); break; case 'export_build': $oPage = new JsonPage(); $oPage->SetOutputDataOnly(true); $oAjaxRenderController->ExportBuild($oPage, false); break; case 'export_build_portal': $oPage = new JsonPage(); $oPage->SetOutputDataOnly(true); $oAjaxRenderController->ExportBuild($oPage, true); break; case 'export_download': $token = utils::ReadParam('token', null); if ($token !== null) { $oExporter = BulkExport::FindExporterFromToken($token); if ($oExporter) { $sMimeType = $oExporter->GetMimeType(); if (substr($sMimeType, 0, 5) == 'text/') { $sMimeType .= ';charset='.strtolower($oExporter->GetCharacterSet()); } $oPage = new DownloadPage(''); $oPage->SetContentType($sMimeType); $oPage->SetContentDisposition('attachment', $oExporter->GetDownloadFileName()); $oPage->add(file_get_contents($oExporter->GetTmpFilePath())); } } break; 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 'check_lock_state': $sObjClass = utils::ReadParam('obj_class', '', false, 'class'); $iObjKey = (int)utils::ReadParam('obj_key', 0, false, 'integer'); $aLockData = iTopOwnershipLock::IsLocked($sObjClass, $iObjKey); $aResult = [ 'locked' => $aLockData['locked'], 'message' => '', ]; // If lock taken by someone else, tell by who if (true === $aLockData['locked']) { // Either the contact friendlyname if the user has a contact, otherwise its login $sOwner = ($aLockData['owner']->Get('contactid') > 0) ? $aLockData['owner']->Get('contactid_friendlyname') : $aLockData['owner']->GetRawName(); $aResult['message'] = Dict::Format('UI:CurrentObjectIsSoftLockedBy_User', $sOwner); } $oPage->SetContentType('application/json'); $oPage->add(json_encode($aResult)); break; // Important: Only from the backoffice AND logged in case 'acquire_lock': $sObjClass = utils::ReadParam('obj_class', '', false, 'class'); $iObjKey = (int)utils::ReadParam('obj_key', 0, false, 'integer'); $aResult = iTopOwnershipLock::AcquireLock($sObjClass, $iObjKey); if (false === $aResult['success']) { $aLockData = iTopOwnershipLock::IsLocked($sObjClass, $iObjKey); // If lock taken by someone else, tell by who if (true === $aLockData['locked']) { // Either the contact friendlyname if the user has a contact, otherwise its login $sOwner = ($aLockData['owner']->Get('contactid') > 0) ? $aLockData['owner']->Get('contactid_friendlyname') : $aLockData['owner']->GetRawName(); $aResult['message'] = Dict::Format('UI:CurrentObjectIsSoftLockedBy_User', $sOwner); } } $oPage->SetContentType('application/json'); $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'); } } } $oPage->SetContentType('application/json'); $oPage->add(json_encode($aResult)); break; case 'release_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'); $bReleased = iTopOwnershipLock::ReleaseLock($sObjClass, $iObjKey, $sToken); $aResult = [ 'success' => $bReleased, ]; $oPage->SetContentType('application/json'); $oPage->add(json_encode($aResult)); break; case 'watchdog': $oPage->add('ok'); // Better for debugging... break; case 'cke_img_upload': $oPage = new JsonPage(); $oPage->SetOutputDataOnly(true); // Image uploaded via CKEditor $aResult = array( 'uploaded' => 0, 'fileName' => '', 'url' => '', 'icon' => '', 'msg' => '', 'att_id' => 0, 'preview' => 'false', ); $sObjClass = stripslashes(utils::ReadParam('obj_class', '', false, 'class')); $sTempId = utils::ReadParam('temp_id', '', false, 'transaction_id'); if (empty($sObjClass)) { $aResult['error'] = "Missing argument 'obj_class'"; } elseif (empty($sTempId)) { $aResult['error'] = "Missing argument 'temp_id'"; } else { try { $oDoc = utils::ReadPostedDocument('upload'); if (InlineImage::IsImage($oDoc->GetMimeType())) { $aDimensions = null; $oDoc = InlineImage::ResizeImageToFit($oDoc, $aDimensions); /** @var InlineImage $oAttachment */ $oAttachment = MetaModel::NewObject('InlineImage'); $oAttachment->Set('expire', time() + MetaModel::GetConfig()->Get('draft_attachments_lifetime')); $oAttachment->Set('temp_id', $sTempId); $oAttachment->Set('item_class', $sObjClass); $oAttachment->SetDefaultOrgId(); $oAttachment->Set('contents', $oDoc); $oAttachment->Set('secret', sprintf('%06x', mt_rand(0, 0xFFFFFF))); // something not easy to guess $iAttId = $oAttachment->DBInsert(); $aResult['uploaded'] = 1; $aResult['msg'] = utils::EscapeHtml($oDoc->GetFileName()); $aResult['fileName'] = $oDoc->GetFileName(); $aResult['url'] = utils::GetAbsoluteUrlAppRoot().INLINEIMAGE_DOWNLOAD_URL.$iAttId.'&s='.$oAttachment->Get('secret'); if (is_array($aDimensions)) { $aResult['width'] = $aDimensions['width']; $aResult['height'] = $aDimensions['height']; } IssueLog::Trace('InlineImage created', LogChannels::INLINE_IMAGE, array( '$operation' => $operation, '$aResult' => $aResult, 'secret' => $oAttachment->Get('secret'), 'temp_id' => $sTempId, 'item_class' => $sObjClass, 'user' => UserRights::GetUser(), 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], )); } else { $aResult['error'] = $oDoc->GetFileName().' is not a valid image format.'; } } catch (FileUploadException $e) { $aResult['error'] = $e->GetMessage(); } } $oPage->SetData($aResult); break; /** @noinspection PhpMissingBreakStatementInspection cke_upload_and_browse and cke_browse are chained */ case 'cke_upload_and_browse': $sTempId = utils::ReadParam('temp_id', '', false, 'transaction_id'); $sObjClass = utils::ReadParam('obj_class', '', false, 'class'); try { $oDoc = utils::ReadPostedDocument('upload'); $sDocMimeType = $oDoc->GetMimeType(); if (!InlineImage::IsImage($sDocMimeType)) { LogErrorMessage('CKE : error when uploading image in ajax.render.php, not an image', array( 'operation' => 'cke_upload_and_browse', 'class' => $sObjClass, 'ImgMimeType' => $sDocMimeType, )); } else { $aDimensions = null; $oDoc = InlineImage::ResizeImageToFit($oDoc, $aDimensions); /** @var InlineImage $oAttachment */ $oAttachment = MetaModel::NewObject('InlineImage'); $oAttachment->Set('expire', time() + MetaModel::GetConfig()->Get('draft_attachments_lifetime')); $oAttachment->Set('temp_id', $sTempId); $oAttachment->Set('item_class', $sObjClass); $oAttachment->SetDefaultOrgId(); $oAttachment->Set('contents', $oDoc); $oAttachment->Set('secret', sprintf('%06x', mt_rand(0, 0xFFFFFF))); // something not easy to guess $iAttId = $oAttachment->DBInsert(); IssueLog::Trace('InlineImage created', LogChannels::INLINE_IMAGE, array( '$operation' => $operation, 'secret' => $oAttachment->Get('secret'), 'temp_id' => $sTempId, 'item_class' => $sObjClass, 'user' => UserRights::GetUser(), 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], )); } } catch (FileUploadException $e) { LogErrorMessage('CKE : error when uploading image in ajax.render.php, exception occured', array( 'operation' => 'cke_upload_and_browse', 'class' => $sObjClass, 'exceptionMsg' => $e, )); } // Fall though !! => browse case 'cke_browse': $oPage = new NiceWebPage(Dict::S('UI:BrowseInlineImages')); $oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/magnific-popup.css'); $oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.magnific-popup.min.js'); $sAppRootUrl = utils::GetAbsoluteUrlAppRoot(); $sImgUrl = $sAppRootUrl.INLINEIMAGE_DOWNLOAD_URL; /** @noinspection SuspiciousAssignmentsInspection cke_upload_and_browse and cke_browse are chained */ $sTempId = utils::ReadParam('temp_id', '', false, 'transaction_id'); $sClass = utils::ReadParam('obj_class', '', false, 'class'); $iObjectId = utils::ReadParam('obj_key', 0, false, 'integer'); $sCKEditorFuncNum = utils::ReadParam('CKEditorFuncNum', ''); if (empty($sTempId)) { throw new SecurityException('Cannot access endpoint with empty temp_id parameter'); } if (false === privUITransaction::IsTransactionValid($sTempId, false)) { throw new SecurityException('Access rejected'); } if (false === MetaModel::IsValidClass($sClass)) { throw new CoreUnexpectedValue('Invalid object'); } if ($iObjectId > 0) { // searching for object in the DB with a count query // using DBSearch so that user rights are applied ! $oSearch = new DBObjectSearch($sClass); $oSearch->AddCondition(MetaModel::DBGetKey($sClass), $iObjectId, '='); $oSet = new CMDBObjectSet($oSearch); if (false === $oSet->CountExceeds(0)) { throw new SecurityException(Dict::S('UI:ObjectDoesNotExist')); } } $sPostUrl = $sAppRootUrl.'pages/ajax.render.php?CKEditorFuncNum='.$sCKEditorFuncNum; $oPage->add_style( <<add( <<
$sUploadLegend
$sUploadLabel $sMaxUpload
EOF ); $oPage->add_script( << 1 ) ? match[1] : null; } // Simulate user action of selecting a file to be returned to CKEditor. function returnFileUrl(iAttId, sAltText, sSecret) { var funcNum = getUrlParam( 'CKEditorFuncNum' ); var fileUrl = '$sImgUrl'+iAttId+'&s='+sSecret; window.opener.CKEDITOR.tools.callFunction( funcNum, fileUrl, function() { // Get the reference to a dialog window. var dialog = this.getDialog(); // Check if this is the Image Properties dialog window. if ( dialog.getName() == 'image' ) { // Get the reference to a text field that stores the "alt" attribute. var element = dialog.getContentElement( 'info', 'txtAlt' ); // Assign the new value. if ( element ) element.setValue(sAltText); } // Return "false" to stop further execution. In such case CKEditor will ignore the second argument ("fileUrl") // and the "onSelect" function assigned to the button that called the file manager (if defined). // return false; } ); window.close(); } EOF ); $oPage->add_ready_script( <<'); $('#upload_form').submit(); $(this).prop('disabled', true); }); $('.img-picker').magnificPopup({type: 'image', closeOnContentClick: true }); EOF ); $sOQL = "SELECT InlineImage WHERE ((temp_id = :temp_id) OR (item_class = :obj_class AND item_id = :obj_id))"; $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array(), array('temp_id' => $sTempId, 'obj_class' => $sClass, 'obj_id' => $iObjectId)); $oPage->add("
$sAvailableImagesLegend"); if ($oSet->Count() == 0) { $oPage->add("

$sNoInlineImage

"); } else { while ($oAttachment = $oSet->Fetch()) { $oDoc = $oAttachment->Get('contents'); if ($oDoc->GetMainMimeType() == 'image') { $sDocName = addslashes(utils::EscapeHtml($oDoc->GetFileName())); $iAttId = $oAttachment->GetKey(); $sSecret = $oAttachment->Get('secret'); $oPage->add("
\"$sDocName\"
"); } } } $oPage->add("
"); break; // TODO 3.0.0: Move this to new ajax render controller? case 'cke_mentions': $oPage->SetContentType('application/json'); $sMarker = utils::ReadParam('marker', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA); $sNeedle = utils::ReadParam('needle', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA); $sHostClass = utils::ReadParam('host_class', '', false, utils::ENUM_SANITIZATION_FILTER_CLASS); $iHostId = (int)utils::ReadParam('host_id', 0, false, utils::ENUM_SANITIZATION_FILTER_INTEGER); // Check parameters if ($sMarker === '') { throw new Exception('Invalid parameters, marker must be specified.'); } $aMentionsAllowedClasses = MetaModel::GetConfig()->Get('mentions.allowed_classes'); if (isset($aMentionsAllowedClasses[$sMarker]) === false) { throw new Exception('Invalid marker "'.$sMarker.'"'); } $aMatches = array(); if ($sNeedle !== '') { // Retrieve mentioned class from marker $sMentionedClass = $aMentionsAllowedClasses[$sMarker]; if (MetaModel::IsValidClass($sMentionedClass) === false) { throw new Exception('Invalid class "'.$sMentionedClass.'" for marker "'.$sMarker.'"'); } // Base search used when no trigger configured $oSearch = DBSearch::FromOQL("SELECT $sMentionedClass"); $aSearchParams = ['needle' => "%$sNeedle%"]; // Retrieve restricting scopes from triggers if any if ((strlen($sHostClass) > 0) && ($iHostId > 0)) { $oHostObj = MetaModel::GetObject($sHostClass, $iHostId); $aSearchParams['this'] = $oHostObj; $aTriggerMentionedSearches = []; $aTriggerSetParams = array('class_list' => MetaModel::EnumParentClasses($sHostClass, ENUM_PARENT_CLASSES_ALL)); $oTriggerSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectMention AS t WHERE t.target_class IN (:class_list)"), array(), $aTriggerSetParams); /** @var \TriggerOnObjectMention $oTrigger */ while ($oTrigger = $oTriggerSet->Fetch()) { $sTriggerMentionedOQL = $oTrigger->Get('mentioned_filter'); // No filter on mentioned objects, don't restrict the scope at all, it can be any object of $sMentionedClass if (strlen($sTriggerMentionedOQL) === 0) { $aTriggerMentionedSearches = [$oSearch]; break; } $oTriggerMentionedSearch = DBSearch::FromOQL($sTriggerMentionedOQL); $sTriggerMentionedClass = $oTriggerMentionedSearch->GetClass(); // Filter is not about the mentioned class, don't mind it if (is_a($sMentionedClass, $sTriggerMentionedClass, true) === false) { continue; } $aTriggerMentionedSearches[] = $oTriggerMentionedSearch; } if (count($aTriggerMentionedSearches) > 0) { $oSearch = new DBUnionSearch($aTriggerMentionedSearches); } } $sSearchMainClassName = $oSearch->GetClass(); $sSearchMainClassAlias = $oSearch->GetClassAlias(); $sObjectImageAttCode = MetaModel::GetImageAttributeCode($sSearchMainClassName); // Add condition to filter on the friendlyname $oSearch->AddConditionExpression( new BinaryExpression(new FieldExpression('friendlyname', $sSearchMainClassAlias), 'LIKE', new VariableExpression('needle')) ); $oSet = new DBObjectSet($oSearch, [], $aSearchParams); // Optimize fields to load $aObjectAttCodesToLoad = []; if (MetaModel::IsValidAttCode($sSearchMainClassName, $sObjectImageAttCode)) { $aObjectAttCodesToLoad[] = $sObjectImageAttCode; } $oSet->OptimizeColumnLoad([$oSearch->GetClassAlias() => $aObjectAttCodesToLoad]); $oSet->SetLimit(MetaModel::GetConfig()->Get('max_autocomplete_results')); // Note: We have to this manually because of a bug in DBSearch not checking the user prefs. by default. $oSet->SetShowObsoleteData(utils::ShowObsoleteData()); while ($oObject = $oSet->Fetch()) { // Note $oObject finalclass might be different than $sMentionedClass $sObjectClass = get_class($oObject); $iObjectId = $oObject->GetKey(); $aMatch = [ 'class' => $sObjectClass, 'id' => $iObjectId, 'friendlyname' => $oObject->Get('friendlyname'), ]; // Try to retrieve image for contact if (!empty($sObjectImageAttCode)) { /** @var \ormDocument $oImage */ $oImage = $oObject->Get($sObjectImageAttCode); if (!$oImage->IsEmpty()) { $aMatch['picture_style'] = "background-image: url('".$oImage->GetDisplayURL($sObjectClass, $iObjectId, $sObjectImageAttCode)."')"; $aMatch['initials'] = ''; } else { // If no image found, fallback on initials $aMatch['picture_style'] = ''; $aMatch['initials'] = utils::FormatInitialsForMedallion(utils::ToAcronym($oObject->Get('friendlyname'))); } } $aMatches[] = $aMatch; } } $oPage->add(json_encode($aMatches)); break; case 'custom_fields_update': $oPage = new JsonPage(); $sAttCode = utils::ReadParam('attcode', ''); $aRequestedFields = utils::ReadParam('requested_fields', array()); $sRequestedFieldsFormPath = utils::ReadParam('form_path', ''); $sJson = utils::ReadParam('json_obj', '', false, 'raw_data'); $aResult = array(); try { $oWizardHelper = WizardHelper::FromJSON($sJson); $oObj = $oWizardHelper->GetTargetObject(); /** @var \ormCustomFieldsValue $oOrmCustomFieldValue */ $oOrmCustomFieldValue = $oObj->Get($sAttCode); $oForm = $oOrmCustomFieldValue->GetForm(); $oSubForm = $oForm->FindSubForm($sRequestedFieldsFormPath); $oRenderer = new ConsoleFormRenderer($oSubForm); $aRenderRes = $oRenderer->Render($aRequestedFields); $aResult['form']['updated_fields'] = $aRenderRes; } catch (Exception $e) { $aResult['error'] = $e->getMessage(); } $oPage->SetData($aResult); $oPage->SetOutputDataOnly(true); break; //-------------------------------- // Preferences //-------------------------------- /** @internal */ case 'preferences.set_user_picture': $oPage = new JsonPage(); try { $oController = new PreferencesController(); $aResult = $oController->SetUserPicture(); $aResult['success'] = true; } catch (Exception $oException) { $aResult = [ 'success' => false, 'error_message' => $oException->getMessage(), ]; } $oPage->SetData($aResult); break; //-------------------------------- // Activity panel //-------------------------------- /** @internal */ case 'activity_panel.save_state': $oPage = new JsonPage(); try { $oController = new ActivityPanelController(); $oController->SaveState(); $aResult = [ 'success' => true, ]; } catch (Exception $oException) { $aResult = [ 'success' => false, 'error_message' => $oException->getMessage(), ]; } $oPage->SetData($aResult); break; /** @internal */ case 'activity_panel.add_caselog_entries': $oPage = new JsonPage(); try { $oController = new ActivityPanelController(); $aResult = $oController->AddCaseLogsEntries(); } catch (Exception $oException) { $aResult = [ 'success' => false, 'error_message' => $oException->getMessage(), ]; } $oPage->SetData($aResult); break; /** @internal */ case 'activity_panel.load_more_entries': $oPage = new JsonPage(); try { $oController = new ActivityPanelController(); $aResult = $oController->LoadMoreEntries(); } catch (Exception $oException) { $aResult = [ 'success' => false, 'error_message' => $oException->getMessage(), ]; } $oPage->SetData($aResult); break; //-------------------------------- // Navigation menu //-------------------------------- case 'get_menus_count': $oPage = new JsonPage(); $oPage->SetOutputDataOnly(true); $oAjaxRenderController->GetMenusCount($oPage); break; //-------------------------------- // Object //-------------------------------- /** @internal */ case 'object.modify': $oController = new ObjectController(); $oPage = $oController->OperationModify(); break; default: $oPage->p("Invalid query."); } } $oKPI->ComputeAndReport('Data fetch and format'); $oPage->output(); } catch (Exception $e) { // note: transform to cope with XSS attacks echo utils::EscapeHtml($e->GetMessage()); IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString()); }