1<?php 2// Copyright (C) 2010-2017 Combodo SARL 3// 4// This file is part of iTop. 5// 6// iTop is free software; you can redistribute it and/or modify 7// it under the terms of the GNU Affero General Public License as published by 8// the Free Software Foundation, either version 3 of the License, or 9// (at your option) any later version. 10// 11// iTop is distributed in the hope that it will be useful, 12// but WITHOUT ANY WARRANTY; without even the implied warranty of 13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14// GNU Affero General Public License for more details. 15// 16// You should have received a copy of the GNU Affero General Public License 17// along with iTop. If not, see <http://www.gnu.org/licenses/> 18 19 20/** 21 * Handles various ajax requests 22 * 23 * @copyright Copyright (C) 2010-2017 Combodo SARL 24 * @license http://opensource.org/licenses/AGPL-3.0 25 */ 26 27require_once('../approot.inc.php'); 28require_once(APPROOT.'application/application.inc.php'); 29require_once(APPROOT.'application/webpage.class.inc.php'); 30require_once(APPROOT.'application/ajaxwebpage.class.inc.php'); 31require_once(APPROOT.'application/pdfpage.class.inc.php'); 32require_once(APPROOT.'application/wizardhelper.class.inc.php'); 33require_once(APPROOT.'application/ui.linkswidget.class.inc.php'); 34require_once(APPROOT.'application/ui.searchformforeignkeys.class.inc.php'); 35require_once(APPROOT.'application/ui.extkeywidget.class.inc.php'); 36require_once(APPROOT.'application/datatable.class.inc.php'); 37require_once(APPROOT.'application/excelexporter.class.inc.php'); 38 39 40function LogErrorMessage($sMsgPrefix, $aContextInfo) { 41 $sCurrentUserLogin = UserRights::GetUser(); 42 $sContextInfo = urldecode(http_build_query($aContextInfo, '', ', ')); 43 $sErrorMessage = "$sMsgPrefix - User='$sCurrentUserLogin', $sContextInfo"; 44 IssueLog::Error($sErrorMessage); 45} 46 47 48try 49{ 50 require_once(APPROOT.'/application/startup.inc.php'); 51 require_once(APPROOT.'/application/user.preferences.class.inc.php'); 52 53 require_once(APPROOT.'/application/loginwebpage.class.inc.php'); 54 LoginWebPage::DoLoginEx(null /* any portal */, false); 55 56 $oPage = new ajax_page(""); 57 $oPage->no_cache(); 58 59 60 $operation = utils::ReadParam('operation', ''); 61 $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); 62 $sEncoding = utils::ReadParam('encoding', 'serialize'); 63 $sClass = utils::ReadParam('class', 'MissingAjaxParam', false, 'class'); 64 $sStyle = utils::ReadParam('style', 'list'); 65 66 switch ($operation) 67 { 68 case 'datatable': 69 case 'pagination': 70 $oPage->SetContentType('text/html'); 71 $extraParams = utils::ReadParam('extra_param', '', false, 'raw_data'); 72 $aExtraParams = array(); 73 if (is_array($extraParams)) 74 { 75 $aExtraParams = $extraParams; 76 } 77 else 78 { 79 $sExtraParams = stripslashes($extraParams); 80 if (!empty($sExtraParams)) 81 { 82 $val = json_decode(str_replace("'", '"', $sExtraParams), true /* associative array */); 83 if ($val !== null) 84 { 85 $aExtraParams = $val; 86 } 87 } 88 } 89 if ($sEncoding == 'oql') 90 { 91 $oFilter = DBSearch::FromOQL($sFilter); 92 } 93 else 94 { 95 $oFilter = DBSearch::unserialize($sFilter); 96 } 97 $iStart = utils::ReadParam('start', 0); 98 $iEnd = utils::ReadParam('end', 1); 99 $iSortCol = utils::ReadParam('sort_col', 'null'); 100 $sSelectMode = utils::ReadParam('select_mode', ''); 101 if (!empty($sSelectMode) && ($sSelectMode != 'none')) 102 { 103 // The first column is used for the selection (radio / checkbox) and is not sortable 104 $iSortCol--; 105 } 106 $bDisplayKey = utils::ReadParam('display_key', 'true') == 'true'; 107 $aColumns = utils::ReadParam('columns', array(), false, 'raw_data'); 108 $aClassAliases = utils::ReadParam('class_aliases', array()); 109 $iListId = utils::ReadParam('list_id', 0); 110 111 // Filter the list to removed linked set since we are not able to display them here 112 $aOrderBy = array(); 113 $iSortIndex = 0; 114 115 $aColumnsLoad = array(); 116 foreach($aClassAliases as $sAlias => $sClassName) 117 { 118 $aColumnsLoad[$sAlias] = array(); 119 foreach($aColumns[$sAlias] as $sAttCode => $aData) 120 { 121 if ($aData['checked'] == 'true') 122 { 123 $aColumns[$sAlias][$sAttCode]['checked'] = true; 124 if ($sAttCode == '_key_') 125 { 126 if ($iSortCol == $iSortIndex) 127 { 128 if (!MetaModel::HasChildrenClasses($oFilter->GetClass())) 129 { 130 $aNameSpec = MetaModel::GetNameSpec($oFilter->GetClass()); 131 if ($aNameSpec[0] == '%1$s') 132 { 133 // The name is made of a single column, let's sort according to the sort algorithm for this column 134 $aOrderBy[$sAlias.'.'.$aNameSpec[1][0]] = (utils::ReadParam('sort_order', 'asc') == 'asc'); 135 } 136 else 137 { 138 $aOrderBy[$sAlias.'.'.'friendlyname'] = (utils::ReadParam('sort_order', 'asc') == 'asc'); 139 } 140 } 141 else 142 { 143 $aOrderBy[$sAlias.'.'.'friendlyname'] = (utils::ReadParam('sort_order', 'asc') == 'asc'); 144 } 145 } 146 } 147 else 148 { 149 $oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode); 150 if ($oAttDef instanceof AttributeLinkedSet) 151 { 152 // Removed from the display list 153 unset($aColumns[$sAlias][$sAttCode]); 154 } 155 else 156 { 157 $aColumnsLoad[$sAlias][] = $sAttCode; 158 } 159 if ($iSortCol == $iSortIndex) 160 { 161 if ($oAttDef->IsExternalKey()) 162 { 163 $sSortCol = $sAttCode.'_friendlyname'; 164 } 165 else 166 { 167 $sSortCol = $sAttCode; 168 } 169 $aOrderBy[$sAlias.'.'.$sSortCol] = (utils::ReadParam('sort_order', 'asc') == 'asc'); 170 } 171 } 172 $iSortIndex++; 173 } 174 else 175 { 176 $aColumns[$sAlias][$sAttCode]['checked'] = false; 177 } 178 } 179 180 } 181 182 // Load only the requested columns 183 $oSet = new DBObjectSet($oFilter, $aOrderBy, $aExtraParams, null, $iEnd - $iStart, $iStart); 184 $oSet->OptimizeColumnLoad($aColumnsLoad); 185 186 if (isset($aExtraParams['show_obsolete_data'])) 187 { 188 $bShowObsoleteData = $aExtraParams['show_obsolete_data']; 189 } 190 else 191 { 192 $bShowObsoleteData = utils::ShowObsoleteData(); 193 } 194 $oSet->SetShowObsoleteData($bShowObsoleteData); 195 196 $oDataTable = new DataTable($iListId, $oSet, $oSet->GetSelectedClasses()); 197 if ($operation == 'datatable') 198 { 199 // Redraw the whole table 200 $sHtml = $oDataTable->UpdatePager($oPage, $iEnd - $iStart, $iStart); // Set the default page size 201 $sHtml .= $oDataTable->GetHTMLTable($oPage, $aColumns, $sSelectMode, $iEnd - $iStart, $bDisplayKey, $aExtraParams); 202 } 203 else 204 { 205 // redraw just the needed rows 206 $sHtml = $oDataTable->GetAsHTMLTableRows($oPage, $iEnd - $iStart, $aColumns, $sSelectMode, $bDisplayKey, $aExtraParams); 207 } 208 $oPage->add($sHtml); 209 break; 210 211 case 'datatable_save_settings': 212 $oPage->SetContentType('text/plain'); 213 $iPageSize = utils::ReadParam('page_size', 10); 214 $sTableId = utils::ReadParam('table_id', null, false, 'raw_data'); 215 $bSaveAsDefaults = (utils::ReadParam('defaults', 'true') == 'true'); 216 $aClassAliases = utils::ReadParam('class_aliases', array(), false, 'raw_data'); 217 $aColumns = utils::ReadParam('columns', array(), false, 'raw_data'); 218 219 foreach($aColumns as $sAlias => $aList) 220 { 221 foreach($aList as $sAttCode => $aData) 222 { 223 $aColumns[$sAlias][$sAttCode]['checked'] = ($aData['checked'] == 'true'); 224 $aColumns[$sAlias][$sAttCode]['disabled'] = ($aData['disabled'] == 'true'); 225 $aColumns[$sAlias][$sAttCode]['sort'] = ($aData['sort']); 226 } 227 } 228 229 $oSettings = new DataTableSettings($aClassAliases, $sTableId); 230 $oSettings->iDefaultPageSize = $iPageSize; 231 $oSettings->aColumns = $aColumns; 232 233 if ($bSaveAsDefaults) 234 { 235 if ($sTableId != null) 236 { 237 $oCurrSettings = DataTableSettings::GetTableSettings($aClassAliases, $sTableId, true /* bOnlyTable */); 238 if ($oCurrSettings) 239 { 240 $oCurrSettings->ResetToDefault(false); // Reset this table to the defaults 241 } 242 } 243 $bRet = $oSettings->SaveAsDefault(); 244 } 245 else 246 { 247 $bRet = $oSettings->Save(); 248 } 249 $oPage->add($bRet ? 'Ok' : 'KO'); 250 break; 251 252 case 'datatable_reset_settings': 253 $oPage->SetContentType('text/plain'); 254 $sTableId = utils::ReadParam('table_id', null, false, 'raw_data'); 255 $aClassAliases = utils::ReadParam('class_aliases', array(), false, 'raw_data'); 256 $bResetAll = (utils::ReadParam('defaults', 'true') == 'true'); 257 258 $oSettings = new DataTableSettings($aClassAliases, $sTableId); 259 $bRet = $oSettings->ResetToDefault($bResetAll); 260 $oPage->add($bRet ? 'Ok' : 'KO'); 261 break; 262 263 // ui.searchformforeignkeys 264 case 'ShowModalSearchForeignKeys': 265 $oPage->SetContentType('text/html'); 266 $iInputId = utils::ReadParam('iInputId', ''); 267 $sTitle = utils::ReadParam('sTitle', '', false, 'raw_data'); 268 $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); 269 $oWidget = new UISearchFormForeignKeys($sTargetClass, $iInputId); 270 $oWidget->ShowModalSearchForeignKeys($oPage, $sTitle); 271 break; 272 273 // ui.searchformforeignkeys 274 case 'GetFullListForeignKeysFromSelection': 275 $oPage->SetContentType('application/json'); 276 $oWidget = new UISearchFormForeignKeys($sClass); 277 $oFullSetFilter = new DBObjectSearch($sClass); 278 $oWidget->GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter); 279 break; 280 281 // ui.searchformforeignkeys 282 case 'ListResultsSearchForeignKeys': 283 $oPage->SetContentType('text/html'); 284 $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); 285 $iInputId = utils::ReadParam('iInputId', ''); 286 $sRemoteClass = utils::ReadParam('sRemoteClass', '', false, 'class'); 287 $oWidget = new UISearchFormForeignKeys($sTargetClass, $iInputId); 288 $oWidget->ListResultsSearchForeignKeys($oPage, $sRemoteClass); 289 break; 290 291 292 // ui.linkswidget 293 case 'addObjects': 294 $oPage->SetContentType('text/html'); 295 $sAttCode = utils::ReadParam('sAttCode', ''); 296 $iInputId = utils::ReadParam('iInputId', ''); 297 $sSuffix = utils::ReadParam('sSuffix', ''); 298 $bDuplicates = (utils::ReadParam('bDuplicates', 'false') == 'false') ? false : true; 299 $sJson = utils::ReadParam('json', '', false, 'raw_data'); 300 if (!empty($sJson)) 301 { 302 $oWizardHelper = WizardHelper::FromJSON($sJson); 303 $oObj = $oWizardHelper->GetTargetObject(); 304 } 305 else 306 { 307 // Search form: no current object 308 $oObj = null; 309 } 310 $oWidget = new UILinksWidget($sClass, $sAttCode, $iInputId, $sSuffix, $bDuplicates); 311 $oAppContext = new ApplicationContext(); 312 $aPrefillFormParam = array( 'user' => $_SESSION["auth_user"], 313 'context' => $oAppContext->GetAsHash(), 314 'att_code' => $sAttCode, 315 'origin' => 'console', 316 'source_obj' => $oObj 317 ); 318 $aAlreadyLinked = utils::ReadParam('aAlreadyLinked', array()); 319 $oWidget->GetObjectPickerDialog($oPage, $oObj, $sJson, $aAlreadyLinked, $aPrefillFormParam); 320 break; 321 322 // ui.linkswidget 323 case 'searchObjectsToAdd': 324 $oPage->SetContentType('text/html'); 325 $sRemoteClass = utils::ReadParam('sRemoteClass', '', false, 'class'); 326 $sAttCode = utils::ReadParam('sAttCode', ''); 327 $iInputId = utils::ReadParam('iInputId', ''); 328 $sSuffix = utils::ReadParam('sSuffix', ''); 329 $bDuplicates = (utils::ReadParam('bDuplicates', 'false') == 'false') ? false : true; 330 $aAlreadyLinked = utils::ReadParam('aAlreadyLinked', array()); 331 $oWidget = new UILinksWidget($sClass, $sAttCode, $iInputId, $sSuffix, $bDuplicates); 332 $oWidget->SearchObjectsToAdd($oPage, $sRemoteClass, $aAlreadyLinked); 333 break; 334 335 //ui.linksdirectwidget 336 case 'createObject': 337 $oPage->SetContentType('text/html'); 338 $sClass = utils::ReadParam('class', '', false, 'class'); 339 $sRealClass = utils::ReadParam('real_class', '', false, 'class'); 340 $sAttCode = utils::ReadParam('att_code', ''); 341 $iInputId = utils::ReadParam('iInputId', ''); 342 $oPage->SetContentType('text/html'); 343 $sJson = utils::ReadParam('json', '', false, 'raw_data'); 344 if (!empty($sJson)) 345 { 346 $oWizardHelper = WizardHelper::FromJSON($sJson); 347 $oObj = $oWizardHelper->GetTargetObject(); 348 } 349 $oObj = $oWizardHelper->GetTargetObject(); 350 $oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iInputId); 351 $oWidget->GetObjectCreationDlg($oPage, $sRealClass, $oObj); 352 break; 353 354 // ui.linksdirectwidget 355 case 'getLinksetRow': 356 $oPage->SetContentType('text/html'); 357 $sClass = utils::ReadParam('class', '', false, 'class'); 358 $sRealClass = utils::ReadParam('real_class', '', false, 'class'); 359 $sAttCode = utils::ReadParam('att_code', ''); 360 $iInputId = utils::ReadParam('iInputId', ''); 361 $iTempId = utils::ReadParam('tempId', ''); 362 $aValues = utils::ReadParam('values', array(), false, 'raw_data'); 363 $oPage->SetContentType('text/html'); 364 $oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iInputId); 365 $oPage->add($oWidget->GetRow($oPage, $sRealClass, $aValues, -$iTempId)); 366 break; 367 368 // ui.linksdirectwidget 369 case 'selectObjectsToAdd': 370 $oPage->SetContentType('text/html'); 371 $sClass = utils::ReadParam('class', '', false, 'class'); 372 $aAlreadyLinked = utils::ReadParam('aAlreadyLinked', array()); 373 $sJson = utils::ReadParam('json', '', false, 'raw_data'); 374 $oObj = null; 375 if ($sJson != '') 376 { 377 $oWizardHelper = WizardHelper::FromJSON($sJson); 378 $oObj = $oWizardHelper->GetTargetObject(); 379 } 380 $sRealClass = utils::ReadParam('real_class', '', false, 'class'); 381 $sAttCode = utils::ReadParam('att_code', ''); 382 $iInputId = utils::ReadParam('iInputId', ''); 383 $iCurrObjectId = utils::ReadParam('iObjId', 0); 384 $oPage->SetContentType('text/html'); 385 $oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iInputId); 386 $oWidget->GetObjectsSelectionDlg($oPage, $oObj, $aAlreadyLinked); 387 break; 388 389 // ui.linksdirectwidget 390 case 'searchObjectsToAdd2': 391 $oPage->SetContentType('text/html'); 392 $sClass = utils::ReadParam('class', '', false, 'class'); 393 $sRealClass = utils::ReadParam('real_class', '', false, 'class'); 394 $sAttCode = utils::ReadParam('att_code', ''); 395 $iInputId = utils::ReadParam('iInputId', ''); 396 $aAlreadyLinked = utils::ReadParam('aAlreadyLinked', array()); 397 $sJson = utils::ReadParam('json', '', false, 'raw_data'); 398 $oObj = null; 399 if ($sJson != '') 400 { 401 $oWizardHelper = WizardHelper::FromJSON($sJson); 402 $oObj = $oWizardHelper->GetTargetObject(); 403 } 404 $oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iInputId); 405 $oWidget->SearchObjectsToAdd($oPage, $sRealClass, $aAlreadyLinked, $oObj); 406 break; 407 408 // ui.linksdirectwidget 409 case 'doAddObjects2': 410 $oPage->SetContentType('text/html'); 411 $oPage->SetContentType('text/html'); 412 $sClass = utils::ReadParam('class', '', false, 'class'); 413 $sRealClass = utils::ReadParam('real_class', '', false, 'class'); 414 $sAttCode = utils::ReadParam('att_code', ''); 415 $iInputId = utils::ReadParam('iInputId', ''); 416 $iCurrObjectId = utils::ReadParam('iObjId', 0); 417 $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); 418 if ($sFilter != '') 419 { 420 $oFullSetFilter = DBObjectSearch::unserialize($sFilter); 421 } 422 else 423 { 424 $oLinksetDef = MetaModel::GetAttributeDef($sClass, $sAttCode); 425 $valuesDef = $oLinksetDef->GetValuesDef(); 426 if ($valuesDef === null) 427 { 428 $oFullSetFilter = new DBObjectSearch($oLinksetDef->GetLinkedClass()); 429 } 430 else 431 { 432 if (!$valuesDef instanceof ValueSetObjects) 433 { 434 throw new Exception('Error: only ValueSetObjects are supported for "allowed_values" in AttributeLinkedSet ('.$this->sClass.'/'.$this->sAttCode.').'); 435 } 436 $oFullSetFilter = DBObjectSearch::FromOQL($valuesDef->GetFilterExpression()); 437 } 438 } 439 $oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iInputId); 440 $oWidget->DoAddObjects($oPage, $oFullSetFilter); 441 break; 442 443 //////////////////////////////////////////////////////////// 444 445 // ui.extkeywidget 446 case 'searchObjectsToSelect': 447 $oPage->SetContentType('text/html'); 448 $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); 449 $iInputId = utils::ReadParam('iInputId', ''); 450 $sRemoteClass = utils::ReadParam('sRemoteClass', '', false, 'class'); 451 $sFilter = utils::ReadParam('sFilter', '', false, 'raw_data'); 452 $sJson = utils::ReadParam('json', '', false, 'raw_data'); 453 $sAttCode = utils::ReadParam('sAttCode', ''); 454 $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); 455 if (!empty($sJson)) 456 { 457 $oWizardHelper = WizardHelper::FromJSON($sJson); 458 $oObj = $oWizardHelper->GetTargetObject(); 459 } 460 else 461 { 462 // Search form: no current object 463 $oObj = null; 464 } 465 $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode); 466 $oWidget->SearchObjectsToSelect($oPage, $sFilter, $sRemoteClass, $oObj); 467 break; 468 469 // ui.extkeywidget: autocomplete 470 case 'ac_extkey': 471 $oPage->SetContentType('text/plain'); 472 $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); 473 $iInputId = utils::ReadParam('iInputId', ''); 474 $sFilter = utils::ReadParam('sFilter', '', false, 'raw_data'); 475 $sJson = utils::ReadParam('json', '', false, 'raw_data'); 476 $sContains = utils::ReadParam('q', '', false, 'raw_data'); 477 $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); 478 $sOutputFormat = utils::ReadParam('sOutputFormat', UIExtKeyWidget::ENUM_OUTPUT_FORMAT_CSV, false, 'raw_data'); 479 $sAutocompleteOperation = utils::ReadParam('sAutocompleteOperation', null, false, 'raw_data'); 480 if ($sContains != '') 481 { 482 if (!empty($sJson)) 483 { 484 $oWizardHelper = WizardHelper::FromJSON($sJson); 485 $oObj = $oWizardHelper->GetTargetObject(); 486 } 487 else 488 { 489 // Search form: no current object 490 $oObj = null; 491 } 492 $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, '', $bSearchMode); 493 $oWidget->AutoComplete($oPage, $sFilter, $oObj, $sContains, $sOutputFormat, $sAutocompleteOperation); 494 } 495 break; 496 497 // ui.extkeywidget 498 case 'objectSearchForm': 499 $oPage->SetContentType('text/html'); 500 $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); 501 $iInputId = utils::ReadParam('iInputId', ''); 502 $sTitle = utils::ReadParam('sTitle', '', false, 'raw_data'); 503 $sAttCode = utils::ReadParam('sAttCode', ''); 504 $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); 505 $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode); 506 $sJson = utils::ReadParam('json', '', false, 'raw_data'); 507 if (!empty($sJson)) 508 { 509 $oWizardHelper = WizardHelper::FromJSON($sJson); 510 $oObj = $oWizardHelper->GetTargetObject(); 511 } 512 else 513 { 514 // Search form: no current object 515 $oObj = null; 516 } 517 $oWidget->GetSearchDialog($oPage, $sTitle, $oObj); 518 break; 519 520 // ui.extkeywidget 521 case 'objectCreationForm': 522 $oPage->SetContentType('text/html'); 523 // Retrieving parameters 524 $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); 525 $iInputId = utils::ReadParam('iInputId', ''); 526 $sAttCode = utils::ReadParam('sAttCode', ''); 527 $sJson = utils::ReadParam('json', '', false, 'raw_data'); 528 // Building form, if target class is abstract we ask the user for the desired leaf class 529 $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, false); 530 if(MetaModel::IsAbstract($sTargetClass)) 531 { 532 $oWidget->GetClassSelectionForm($oPage); 533 } 534 else 535 { 536 $aPrefillFormParam = array(); 537 if (!empty($sJson)) 538 { 539 $oWizardHelper = WizardHelper::FromJSON($sJson); 540 $oObj = $oWizardHelper->GetTargetObject(); 541 $oAppContext = new ApplicationContext(); 542 $aPrefillFormParam = array( 'user' => $_SESSION["auth_user"], 543 'context' => $oAppContext->GetAsHash(), 544 'att_code' => $sAttCode, 545 'source_obj' => $oObj, 546 'origin' => 'console' 547 ); 548 } 549 else 550 { 551 // Search form: no current object 552 $oObj = null; 553 } 554 $oWidget->GetObjectCreationForm($oPage, $oObj, $aPrefillFormParam); 555 } 556 break; 557 558 // ui.extkeywidget 559 case 'doCreateObject': 560 $oPage->SetContentType('application/json'); 561 $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); 562 $iInputId = utils::ReadParam('iInputId', ''); 563 $sFormPrefix = utils::ReadParam('sFormPrefix', ''); 564 $sAttCode = utils::ReadParam('sAttCode', ''); 565 $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, false); 566 $aResult = $oWidget->DoCreateObject($oPage); 567 echo json_encode($aResult); 568 break; 569 570 // ui.extkeywidget 571 case 'getObjectName': 572 $oPage->SetContentType('application/json'); 573 $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); 574 $iInputId = utils::ReadParam('iInputId', ''); 575 $iObjectId = utils::ReadParam('iObjectId', ''); 576 $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); 577 $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, '', $bSearchMode); 578 $sName = $oWidget->GetObjectName($iObjectId); 579 echo json_encode(array('name' => $sName)); 580 break; 581 582 // ui.extkeywidget 583 case 'displayHierarchy': 584 $oPage->SetContentType('text/html'); 585 $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); 586 $sInputId = utils::ReadParam('sInputId', ''); 587 $sFilter = utils::ReadParam('sFilter', '', false, 'raw_data'); 588 $sJson = utils::ReadParam('json', '', false, 'raw_data'); 589 $currValue = utils::ReadParam('value', ''); 590 $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); 591 if (!empty($sJson)) 592 { 593 $oWizardHelper = WizardHelper::FromJSON($sJson); 594 $oObj = $oWizardHelper->GetTargetObject(); 595 } 596 else 597 { 598 // Search form: no current object 599 $oObj = null; 600 } 601 $oWidget = new UIExtKeyWidget($sTargetClass, $sInputId, '', $bSearchMode); 602 $oWidget->DisplayHierarchy($oPage, $sFilter, $currValue, $oObj); 603 break; 604 605 //////////////////////////////////////////////////// 606 607 // ui.linkswidget 608 case 'doAddObjects': 609 $oPage->SetContentType('text/html'); 610 $sAttCode = utils::ReadParam('sAttCode', ''); 611 $iInputId = utils::ReadParam('iInputId', ''); 612 $sSuffix = utils::ReadParam('sSuffix', ''); 613 $sRemoteClass = utils::ReadParam('sRemoteClass', $sClass, false, 'class'); 614 $bDuplicates = (utils::ReadParam('bDuplicates', 'false') == 'false') ? false : true; 615 $sJson = utils::ReadParam('json', '', false, 'raw_data'); 616 $iMaxAddedId = utils::ReadParam('max_added_id'); 617 $oWizardHelper = WizardHelper::FromJSON($sJson); 618 $oObj = $oWizardHelper->GetTargetObject(); 619 $oWidget = new UILinksWidget($sClass, $sAttCode, $iInputId, $sSuffix, $bDuplicates); 620 if ($sFilter != '') 621 { 622 $oFullSetFilter = DBObjectSearch::unserialize($sFilter); 623 } 624 else 625 { 626 $oFullSetFilter = new DBObjectSearch($sRemoteClass); 627 } 628 $oWidget->DoAddObjects($oPage, $iMaxAddedId, $oFullSetFilter, $oObj); 629 break; 630 631 //////////////////////////////////////////////////////////// 632 633 case 'wizard_helper_preview': 634 $oPage->SetContentType('text/html'); 635 $sJson = utils::ReadParam('json_obj', '', false, 'raw_data'); 636 $oWizardHelper = WizardHelper::FromJSON($sJson); 637 $oObj = $oWizardHelper->GetTargetObject(); 638 $oObj->DisplayBareProperties($oPage); 639 break; 640 641 case 'wizard_helper': 642 $oPage->SetContentType('text/html'); 643 $sJson = utils::ReadParam('json_obj', '', false, 'raw_data'); 644 $oWizardHelper = WizardHelper::FromJSON($sJson); 645 $oObj = $oWizardHelper->GetTargetObject(); 646 $sClass = $oWizardHelper->GetTargetClass(); 647 foreach($oWizardHelper->GetFieldsForDefaultValue() as $sAttCode) 648 { 649 $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); 650 $defaultValue = $oAttDef->GetDefaultValue($oObj); 651 $oWizardHelper->SetDefaultValue($sAttCode, $defaultValue); 652 $oObj->Set($sAttCode, $defaultValue); 653 } 654 $sFormPrefix = $oWizardHelper->GetFormPrefix(); 655 $aExpectedAttributes = ($oWizardHelper->GetStimulus() === null) ? array() : $oObj->GetTransitionAttributes($oWizardHelper->GetStimulus(), $oWizardHelper->GetInitialState()); 656 foreach($oWizardHelper->GetFieldsForAllowedValues() as $sAttCode) 657 { 658 $sId = $oWizardHelper->GetIdForField($sAttCode); 659 if ($sId != '') 660 { 661 if (array_key_exists($sAttCode, $aExpectedAttributes)) 662 { 663 $iFlags = $aExpectedAttributes[$sAttCode]; 664 } 665 elseif ($oObj->IsNew()) 666 { 667 $iFlags = $oObj->GetInitialStateAttributeFlags($sAttCode); 668 } 669 else 670 { 671 $iFlags = $oObj->GetAttributeFlags($sAttCode); 672 } 673 if ($iFlags & OPT_ATT_READONLY) 674 { 675 $sHTMLValue = "<span id=\"field_{$sId}\">".$oObj->GetAsHTML($sAttCode); 676 $oWizardHelper->SetAllowedValuesHtml($sAttCode, $sHTMLValue); 677 } 678 else 679 { 680 // It may happen that the field we'd like to update does not 681 // exist in the form. For example, if the field should be hidden/read-only 682 // in the current state of the object 683 $value = $oObj->Get($sAttCode); 684 $displayValue = $oObj->GetEditValue($sAttCode); 685 $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); 686 if (!$oAttDef->IsWritable()) 687 { 688 // Even non-writable fields (like AttributeExternal) can be refreshed 689 $sHTMLValue = $oObj->GetAsHTML($sAttCode); 690 } 691 else 692 { 693 $iFlags = MetaModel::GetAttributeFlags($sClass, $oObj->GetState(), $sAttCode); 694 $sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $value, $displayValue, $sId, '', $iFlags, array('this' => $oObj, 'formPrefix' => $sFormPrefix), false); 695 // Make sure that we immediately validate the field when we reload it 696 $oPage->add_ready_script("$('#$sId').trigger('validate');"); 697 } 698 $oWizardHelper->SetAllowedValuesHtml($sAttCode, $sHTMLValue); 699 } 700 } 701 } 702 $oPage->add_script("oWizardHelper{$sFormPrefix}.m_oData=".$oWizardHelper->ToJSON().";\noWizardHelper{$sFormPrefix}.UpdateFields();\n"); 703 break; 704 705 case 'obj_creation_form': 706 $oPage->SetContentType('text/html'); 707 $sJson = utils::ReadParam('json_obj', '', false, 'raw_data'); 708 $oWizardHelper = WizardHelper::FromJSON($sJson); 709 $oObj = $oWizardHelper->GetTargetObject(); 710 $sClass = $oWizardHelper->GetTargetClass(); 711 $sTargetState = utils::ReadParam('target_state', ''); 712 $iTransactionId = utils::ReadParam('transaction_id', '', false, 'transaction_id'); 713 $oObj->Set(MetaModel::GetStateAttributeCode($sClass), $sTargetState); 714 cmdbAbstractObject::DisplayCreationForm($oPage, $sClass, $oObj, array(), array('action' => utils::GetAbsoluteUrlAppRoot().'pages/UI.php', 'transaction_id' => $iTransactionId)); 715 break; 716 717 // DisplayBlock 718 case 'ajax': 719 $oPage->SetContentType('text/html'); 720 if ($sFilter != "") 721 { 722 $sExtraParams = stripslashes(utils::ReadParam('extra_params', '', false, 'raw_data')); 723 $aExtraParams = array(); 724 if (!empty($sExtraParams)) 725 { 726 $aExtraParams = json_decode(str_replace("'", '"', $sExtraParams), true /* associative array */); 727 } 728 // Restore the app context from the ExtraParams 729 $oAppContext = new ApplicationContext(false); // false => don't read the context yet ! 730 $aContext = array(); 731 foreach($oAppContext->GetNames() as $sName) 732 { 733 $sParamName = 'c['.$sName.']'; 734 if (isset($aExtraParams[$sParamName])) 735 { 736 $aContext[$sName] = $aExtraParams[$sParamName]; 737 } 738 } 739 $_REQUEST['c'] = $aContext; 740 if ($sEncoding == 'oql') 741 { 742 $oFilter = DBSearch::FromOQL($sFilter); 743 } 744 else 745 { 746 $oFilter = DBSearch::unserialize($sFilter); 747 } 748 $oDisplayBlock = new DisplayBlock($oFilter, $sStyle, false); 749 $aExtraParams['display_limit'] = true; 750 $aExtraParams['truncated'] = true; 751 $oDisplayBlock->RenderContent($oPage, $aExtraParams); 752 } 753 else 754 { 755 $oPage->p("Invalid query (empty filter)."); 756 } 757 break; 758 759 case 'displayCSVHistory': 760 $oPage->SetContentType('text/html'); 761 $bShowAll = (utils::ReadParam('showall', 'false') == 'true'); 762 BulkChange::DisplayImportHistory($oPage, true, $bShowAll); 763 break; 764 765 case 'details': 766 $oPage->SetContentType('text/html'); 767 $key = utils::ReadParam('id', 0); 768 $oFilter = new DBObjectSearch($sClass); 769 $oFilter->AddCondition('id', $key, '='); 770 $oDisplayBlock = new DisplayBlock($oFilter, 'details', false); 771 $oDisplayBlock->RenderContent($oPage); 772 break; 773 774 case 'pie_chart': 775 $oPage->SetContentType('application/json'); 776 $sGroupBy = utils::ReadParam('group_by', ''); 777 if ($sFilter != '') 778 { 779 if ($sEncoding == 'oql') 780 { 781 $oFilter = DBSearch::FromOQL($sFilter); 782 } 783 else 784 { 785 $oFilter = DBSearch::unserialize($sFilter); 786 } 787 $oDisplayBlock = new DisplayBlock($oFilter, 'pie_chart_ajax', false); 788 $oDisplayBlock->RenderContent($oPage, array('group_by' => $sGroupBy)); 789 } 790 else 791 { 792 793 $oPage->add("<chart>\n<chart_type>3d pie</chart_type><!-- empty filter '$sFilter' --></chart>\n."); 794 } 795 break; 796 797 case 'chart': 798 // Workaround for IE8 + IIS + HTTPS 799 // See TRAC #363, fix described here: http://forums.codecharge.com/posts.php?post_id=97771 800 $oPage->add_header("Expires: Fri, 17 Jul 1970 05:00:00 GMT"); 801 $oPage->add_header("Cache-Control: cache, must-revalidate"); 802 $oPage->add_header("Pragma: public"); 803 804 $aParams = utils::ReadParam('params', array(), false, 'raw_data'); 805 if ($sFilter != '') 806 { 807 $oFilter = DBSearch::unserialize($sFilter); 808 $oDisplayBlock = new DisplayBlock($oFilter, 'chart_ajax', false); 809 $oDisplayBlock->RenderContent($oPage, $aParams); 810 } 811 else 812 { 813 814 $oPage->add("<chart>\n<chart_type>3d pie</chart_type><!-- empty filter '$sFilter' --></chart>\n."); 815 } 816 break; 817 818 case 'modal_details': 819 $oPage->SetContentType('text/html'); 820 $key = utils::ReadParam('id', 0); 821 $oFilter = new DBObjectSearch($sClass); 822 $oFilter->AddCondition('id', $key, '='); 823 $oPage->Add("<p style=\"width:100%; margin-top:-5px;padding:3px; background-color:#33f; color:#fff;\">Object Details</p>\n"); 824 $oDisplayBlock = new DisplayBlock($oFilter, 'details', false); 825 $oDisplayBlock->RenderContent($oPage); 826 $oPage->Add("<input type=\"button\" class=\"jqmClose\" value=\" Close \" />\n"); 827 break; 828 829 case 'link': 830 $oPage->SetContentType('text/html'); 831 $sClass = utils::ReadParam('sclass', 'logInfra', false, 'class'); 832 $sAttCode = utils::ReadParam('attCode', 'name'); 833 //$sOrg = utils::ReadParam('org_id', ''); 834 $sName = utils::ReadParam('q', ''); 835 $iMaxCount = utils::ReadParam('max', 30); 836 $iCount = 0; 837 $oFilter = new DBObjectSearch($sClass); 838 $oFilter->AddCondition($sAttCode, $sName, 'Begins with'); 839 //$oFilter->AddCondition('org_id', $sOrg, '='); 840 $oSet = new CMDBObjectSet($oFilter, array($sAttCode => true)); 841 while (($iCount < $iMaxCount) && ($oObj = $oSet->fetch())) 842 { 843 $oPage->add($oObj->GetAsHTML($sAttCode)."|".$oObj->GetKey()."\n"); 844 $iCount++; 845 } 846 break; 847 848 case 'combo_options': 849 $oPage->SetContentType('text/html'); 850 $oFilter = DBSearch::FromOQL($sFilter); 851 $oSet = new CMDBObjectSet($oFilter); 852 while ($oObj = $oSet->fetch()) 853 { 854 $oPage->add('<option title="Here is more information..." value="'.$oObj->GetKey().'">'.$oObj->GetName().'</option>'); 855 } 856 break; 857 858 case 'display_document': 859 $id = utils::ReadParam('id', ''); 860 $sField = utils::ReadParam('field', ''); 861 if (!empty($sClass) && ($sClass != 'InlineImage') && !empty($id) && !empty($sField)) 862 { 863 ormDocument::DownloadDocument($oPage, $sClass, $id, $sField, 'inline'); 864 } 865 break; 866 867 case 'search_form': 868 $oPage->SetContentType('text/html'); 869 $sClass = utils::ReadParam('className', '', false, 'class'); 870 $sRootClass = utils::ReadParam('baseClass', '', false, 'class'); 871 $currentId = utils::ReadParam('currentId', ''); 872 $sTableId = utils::ReadParam('_table_id_', null, false, 'raw_data'); 873 $sAction = utils::ReadParam('action', ''); 874 $sSelectionMode = utils::ReadParam('selection_mode', null,false,'raw_data'); 875 $sResultListOuterSelector = utils::ReadParam('result_list_outer_selector', null,false,'raw_data'); 876 $scssCount = utils::ReadParam('css_count', null,false,'raw_data'); 877 $sTableInnerId = utils::ReadParam('table_inner_id', null,false,'raw_data'); 878 879 $oFilter = new DBObjectSearch($sClass); 880 $oSet = new CMDBObjectSet($oFilter); 881 $sHtml = cmdbAbstractObject::GetSearchForm($oPage, $oSet, array('currentId' => $currentId, 882 'baseClass' => $sRootClass, 883 'action' => $sAction, 884 'table_id' => $sTableId, 885 'selection_mode' => $sSelectionMode, 886 'result_list_outer_selector' => $sResultListOuterSelector, 887 'cssCount' => $scssCount, 888 'table_inner_id' => $sTableInnerId)); 889 $oPage->add($sHtml); 890 break; 891 892 case 'set_pref': 893 $sCode = utils::ReadPostedParam('code', ''); 894 $sValue = utils::ReadPostedParam('value', '', 'raw_data'); 895 appUserPreferences::SetPref($sCode, $sValue); 896 break; 897 898 case 'erase_all_pref': 899 // Can be useful in case a user got some corrupted prefs... 900 appUserPreferences::ClearPreferences(); 901 break; 902 903 case 'on_form_cancel': 904 // Called when a creation/modification form is cancelled by the end-user 905 // Let's take this opportunity to inform the plug-ins so that they can perform some cleanup 906 $iTransactionId = utils::ReadParam('transaction_id', 0, false, 'transaction_id'); 907 $sTempId = utils::GetUploadTempId($iTransactionId); 908 InlineImage::OnFormCancel($sTempId); 909 foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) 910 { 911 $oExtensionInstance->OnFormCancel($sTempId); 912 } 913 $sObjClass = utils::ReadParam('obj_class', '', false, 'class'); 914 $iObjKey = (int)utils::ReadParam('obj_key', 0, false, 'integer'); 915 $sToken = utils::ReadParam('token', 0, false, 'raw_data'); 916 if (($sObjClass != '') && ($iObjKey != 0) && ($sToken != '')) 917 { 918 $bReleaseLock = iTopOwnershipLock::ReleaseLock($sObjClass, $iObjKey, $sToken); 919 } 920 break; 921 922 case 'dashboard': 923 $oPage->SetContentType('text/html'); 924 $id = (int)utils::ReadParam('id', 0); 925 $sAttCode = utils::ReadParam('attcode', ''); 926 /** @var \cmdbAbstractObject $oObj */ 927 $oObj = MetaModel::GetObject($sClass, $id); 928 $oObj->DisplayDashboard($oPage, $sAttCode); 929 break; 930 931 case 'export_dashboard': 932 $sDashboardId = utils::ReadParam('id', '', false, 'raw_data'); 933 $sDashboardFile = utils::ReadParam('file', '', false, 'raw_data'); 934 $oDashboard = RuntimeDashboard::GetDashboard($sDashboardFile, $sDashboardId); 935 if (!is_null($oDashboard)) 936 { 937 $oPage->TrashUnexpectedOutput(); 938 $oPage->SetContentType('text/xml'); 939 $oPage->SetContentDisposition('attachment', 'dashboard_'.$oDashboard->GetTitle().'.xml'); 940 $oPage->add($oDashboard->ToXml()); 941 } 942 break; 943 944 case 'import_dashboard': 945 $sTransactionId = utils::ReadParam('transaction_id', '', false, 'transaction_id'); 946 if (!utils::IsTransactionValid($sTransactionId, true)) 947 { 948 throw new SecurityException('ajax.render.php import_dashboard : invalid transaction_id'); 949 } 950 $sDashboardId = utils::ReadParam('id', '', false, 'raw_data'); 951 $sDashboardFile = utils::ReadParam('file', '', false, 'raw_data'); 952 $oDashboard = RuntimeDashboard::GetDashboard($sDashboardFile, $sDashboardId); 953 $aResult = array('error' => ''); 954 if (!is_null($oDashboard)) 955 { 956 try 957 { 958 $oDoc = utils::ReadPostedDocument('dashboard_upload_file'); 959 $oDashboard->FromXml($oDoc->GetData()); 960 $oDashboard->Save(); 961 } catch (DOMException $e) 962 { 963 $aResult = array('error' => Dict::S('UI:Error:InvalidDashboardFile')); 964 } catch (Exception $e) 965 { 966 $aResult = array('error' => $e->getMessage()); 967 } 968 } 969 else 970 { 971 $aResult['error'] = 'Dashboard id="'.$sDashboardId.'" not found.'; 972 } 973 $oPage->add(json_encode($aResult)); 974 break; 975 976 case 'toggle_dashboard': 977 $oPage->SetContentType('text/html'); 978 $sDashboardId = utils::ReadParam('dashboard_id', '', false, 'raw_data'); 979 980 $bStandardSelected = appUserPreferences::GetPref('display_original_dashboard_'.$sDashboardId, false); 981 appUserPreferences::UnsetPref('display_original_dashboard_'.$sDashboardId); 982 appUserPreferences::SetPref('display_original_dashboard_'.$sDashboardId, !$bStandardSelected); 983 984 $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); 985 $sDashboardFile = utils::ReadParam('file', '', false, 'raw_data'); 986 $sReloadURL = utils::ReadParam('reload_url', '', false, 'raw_data'); 987 $oDashboard = RuntimeDashboard::GetDashboard($sDashboardFile, $sDashboardId); 988 $aResult = array('error' => ''); 989 if (!is_null($oDashboard)) 990 { 991 if (!empty($sReloadURL)) 992 { 993 $oDashboard->SetReloadURL($sReloadURL); 994 } 995 $oDashboard->Render($oPage, false, $aExtraParams); 996 } 997 $oPage->add_ready_script("$('.dashboard_contents table.listResults').tableHover(); $('.dashboard_contents table.listResults').tablesorter( { widgets: ['myZebra', 'truncatedList']} );"); 998 break; 999 1000 case 'reload_dashboard': 1001 $oPage->SetContentType('text/html'); 1002 $sDashboardId = utils::ReadParam('dashboard_id', '', false, 'raw_data'); 1003 $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); 1004 $sDashboardFile = utils::ReadParam('file', '', false, 'raw_data'); 1005 $sReloadURL = utils::ReadParam('reload_url', '', false, 'raw_data'); 1006 $oDashboard = RuntimeDashboard::GetDashboard($sDashboardFile, $sDashboardId); 1007 $aResult = array('error' => ''); 1008 if (!is_null($oDashboard)) 1009 { 1010 if (!empty($sReloadURL)) 1011 { 1012 $oDashboard->SetReloadURL($sReloadURL); 1013 } 1014 $oDashboard->Render($oPage, false, $aExtraParams); 1015 } 1016 $oPage->add_ready_script("$('.dashboard_contents table.listResults').tableHover(); $('.dashboard_contents table.listResults').tablesorter( { widgets: ['myZebra', 'truncatedList']} );"); 1017 break; 1018 1019 case 'save_dashboard': 1020 $sDashboardId = utils::ReadParam('dashboard_id', '', false, 'raw_data'); 1021 $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); 1022 $sReloadURL = utils::ReadParam('reload_url', '', false, 'raw_data'); 1023 $sJSExtraParams = json_encode($aExtraParams); 1024 $aParams = array(); 1025 $aParams['layout_class'] = utils::ReadParam('layout_class', ''); 1026 $aParams['title'] = utils::ReadParam('title', '', false, 'raw_data'); 1027 $aParams['auto_reload'] = utils::ReadParam('auto_reload', false); 1028 $aParams['auto_reload_sec'] = utils::ReadParam('auto_reload_sec', 300); 1029 $aParams['cells'] = utils::ReadParam('cells', array(), false, 'raw_data'); 1030 $oDashboard = new RuntimeDashboard($sDashboardId); 1031 $oDashboard->FromParams($aParams); 1032 $oDashboard->Save(); 1033 $sDashboardFile = addslashes(utils::ReadParam('file', '', false, 'raw_data')); 1034 $sDivId = preg_replace('/[^a-zA-Z0-9_]/', '', $sDashboardId); 1035 // trigger a reload of the current page since the dashboard just changed 1036 $oPage->add_script( 1037<<<EOF 1038 $('.dashboard_contents#$sDivId').block(); 1039 $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', 1040 { operation: 'reload_dashboard', dashboard_id: '$sDashboardId', file: '$sDashboardFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'}, 1041 function(data){ 1042 $('.dashboard_contents#$sDivId').html(data); 1043 $('.dashboard_contents#$sDivId').unblock(); 1044 } 1045 ); 1046EOF 1047 ); 1048 break; 1049 1050 case 'revert_dashboard': 1051 $sDashboardId = utils::ReadParam('dashboard_id', '', false, 'raw_data'); 1052 $sReloadURL = utils::ReadParam('reload_url', '', false, 'raw_data'); 1053 appUserPreferences::UnsetPref('display_original_dashboard_'.$sDashboardId); 1054 $oDashboard = new RuntimeDashboard($sDashboardId); 1055 $oDashboard->Revert(); 1056 $sFile = addslashes($oDashboard->GetDefinitionFile()); 1057 $sDivId = preg_replace('/[^a-zA-Z0-9_]/', '', $sDashboardId); 1058 // trigger a reload of the current page since the dashboard just changed 1059 $oPage->add_script( 1060<<<EOF 1061 $('.dashboard_contents#$sDivId').block(); 1062 $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', 1063 { operation: 'reload_dashboard', dashboard_id: '$sDashboardId', file: '$sFile', reload_url: '$sReloadURL'}, 1064 function(data){ 1065 $('.dashboard_contents#$sDivId').html(data); 1066 $('.dashboard_contents#$sDivId').unblock(); 1067 } 1068 ); 1069EOF 1070 ); 1071 break; 1072 1073 case 'render_dashboard': 1074 $sDashboardId = utils::ReadParam('dashboard_id', '', false, 'raw_data'); 1075 $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); 1076 $aParams = array(); 1077 $aParams['layout_class'] = utils::ReadParam('layout_class', ''); 1078 $aParams['title'] = utils::ReadParam('title', '', false, 'raw_data'); 1079 $aParams['cells'] = utils::ReadParam('cells', array(), false, 'raw_data'); 1080 $aParams['auto_reload'] = utils::ReadParam('auto_reload', false); 1081 $aParams['auto_reload_sec'] = utils::ReadParam('auto_reload_sec', 300); 1082 $sReloadURL = utils::ReadParam('reload_url', '', false, 'raw_data'); 1083 $oDashboard = new RuntimeDashboard($sDashboardId); 1084 $oDashboard->FromParams($aParams); 1085 $oDashboard->SetReloadURL($sReloadURL); 1086 $oDashboard->Render($oPage, true /* bEditMode */, $aExtraParams); 1087 break; 1088 1089 case 'dashboard_editor': 1090 $sId = utils::ReadParam('id', '', false, 'raw_data'); 1091 $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); 1092 $sDashboardFile = utils::ReadParam('file', '', false, 'raw_data'); 1093 $sReloadURL = utils::ReadParam('reload_url', '', false, 'raw_data'); 1094 $oDashboard = RuntimeDashboard::GetDashboard($sDashboardFile, $sId); 1095 if (!is_null($oDashboard)) 1096 { 1097 if (!empty($sReloadURL)) 1098 { 1099 $oDashboard->SetReloadURL($sReloadURL); 1100 } 1101 $oDashboard->RenderEditor($oPage, $aExtraParams); 1102 } 1103 break; 1104 1105 case 'new_dashlet': 1106 require_once(APPROOT.'application/forms.class.inc.php'); 1107 require_once(APPROOT.'application/dashlet.class.inc.php'); 1108 $sDashletClass = utils::ReadParam('dashlet_class', ''); 1109 $sDashletId = utils::ReadParam('dashlet_id', '', false, 'raw_data'); 1110 if (is_subclass_of($sDashletClass, 'Dashlet')) 1111 { 1112 $oDashlet = new $sDashletClass(new ModelReflectionRuntime(), $sDashletId); 1113 $offset = $oPage->start_capture(); 1114 $oDashlet->DoRender($oPage, true /* bEditMode */, false /* bEnclosingDiv */); 1115 $sHtml = addslashes($oPage->end_capture($offset)); 1116 $sHtml = str_replace("\n", '', $sHtml); 1117 $sHtml = str_replace("\r", '', $sHtml); 1118 $oPage->add_script("$('#dashlet_$sDashletId').html('$sHtml');"); // in ajax web page add_script has the same effect as add_ready_script 1119 // but is executed BEFORE all 'ready_scripts' 1120 $oForm = $oDashlet->GetForm(); // Rebuild the form since the values/content changed 1121 $oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property')); 1122 $sHtml = addslashes($oForm->RenderAsPropertySheet($oPage, true /* bReturnHtml */, '.itop-dashboard')); 1123 $sHtml = str_replace("\n", '', $sHtml); 1124 $sHtml = str_replace("\r", '', $sHtml); 1125 $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' 1126 } 1127 break; 1128 1129 case 'update_dashlet_property': 1130 require_once(APPROOT.'application/forms.class.inc.php'); 1131 require_once(APPROOT.'application/dashlet.class.inc.php'); 1132 $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); 1133 $aParams = utils::ReadParam('params', '', false, 'raw_data'); 1134 $sDashletClass = $aParams['attr_dashlet_class']; 1135 $sDashletType = $aParams['attr_dashlet_type']; 1136 $sDashletId = $aParams['attr_dashlet_id']; 1137 $aUpdatedProperties = $aParams['updated']; // Code of the changed properties as an array: 'attr_xxx', 'attr_xxy', etc... 1138 $aPreviousValues = $aParams['previous_values']; // hash array: 'attr_xxx' => 'old_value' 1139 if (is_subclass_of($sDashletClass, 'Dashlet')) 1140 { 1141 /** @var \Dashlet $oDashlet */ 1142 $oDashlet = new $sDashletClass(new ModelReflectionRuntime(), $sDashletId); 1143 $oDashlet->SetDashletType($sDashletType); 1144 $oForm = $oDashlet->GetForm(); 1145 $aValues = $oForm->ReadParams(); // hash array: 'xxx' => 'new_value' 1146 1147 $aCurrentValues = $aValues; 1148 $aUpdatedDecoded = array(); 1149 foreach($aUpdatedProperties as $sProp) 1150 { 1151 $sDecodedProp = str_replace('attr_', '', $sProp); // Remove the attr_ prefix 1152 $aCurrentValues[$sDecodedProp] = (isset($aPreviousValues[$sProp]) ? $aPreviousValues[$sProp] : ''); // Set the previous value 1153 $aUpdatedDecoded[] = $sDecodedProp; 1154 } 1155 1156 $oDashlet->FromParams($aCurrentValues); 1157 $sPrevClass = get_class($oDashlet); 1158 $oDashlet = $oDashlet->Update($aValues, $aUpdatedDecoded); 1159 $sNewClass = get_class($oDashlet); 1160 if ($sNewClass != $sPrevClass) 1161 { 1162 $oPage->add_ready_script("$('#dashlet_$sDashletId').dashlet('option', {dashlet_class: '$sNewClass'});"); 1163 } 1164 if ($oDashlet->IsRedrawNeeded()) 1165 { 1166 $offset = $oPage->start_capture(); 1167 $oDashlet->DoRender($oPage, true /* bEditMode */, false /* bEnclosingDiv */, $aExtraParams); 1168 $sHtml = addslashes($oPage->end_capture($offset)); 1169 $sHtml = str_replace("\n", '', $sHtml); 1170 $sHtml = str_replace("\r", '', $sHtml); 1171 1172 $oPage->add_script("$('#dashlet_$sDashletId').html('$sHtml');"); // in ajax web page add_script has the same effect as add_ready_script 1173 // but is executed BEFORE all 'ready_scripts' 1174 } 1175 if ($oDashlet->IsFormRedrawNeeded()) 1176 { 1177 $oForm = $oDashlet->GetForm(); // Rebuild the form since the values/content changed 1178 $oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property', 'extra_params' => $aExtraParams)); 1179 $sHtml = addslashes($oForm->RenderAsPropertySheet($oPage, true /* bReturnHtml */, '.itop-dashboard')); 1180 $sHtml = str_replace("\n", '', $sHtml); 1181 $sHtml = str_replace("\r", '', $sHtml); 1182 $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' 1183 // but is executed BEFORE all 'ready_scripts' 1184 } 1185 } 1186 break; 1187 1188 case 'dashlet_creation_dlg': 1189 $sOQL = utils::ReadParam('oql', '', false, 'raw_data'); 1190 RuntimeDashboard::GetDashletCreationDlgFromOQL($oPage, $sOQL); 1191 break; 1192 1193 case 'add_dashlet': 1194 $oForm = RuntimeDashboard::GetDashletCreationForm(); 1195 $aValues = $oForm->ReadParams(); 1196 1197 $sDashletClass = $aValues['dashlet_class']; 1198 $sMenuId = $aValues['menu_id']; 1199 1200 if (is_subclass_of($sDashletClass, 'Dashlet')) 1201 { 1202 $oDashlet = new $sDashletClass(new ModelReflectionRuntime(), 0); 1203 $oDashlet->FromParams($aValues); 1204 1205 ApplicationMenu::LoadAdditionalMenus(); 1206 $index = ApplicationMenu::GetMenuIndexById($sMenuId); 1207 $oMenu = ApplicationMenu::GetMenuNode($index); 1208 $oMenu->AddDashlet($oDashlet); 1209 // navigate to the dashboard page 1210 if ($aValues['open_editor']) 1211 { 1212 $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 1213 } 1214 } 1215 break; 1216 1217 case 'shortcut_list_dlg': 1218 $sOQL = utils::ReadParam('oql', '', false, 'raw_data'); 1219 $sTableSettings = utils::ReadParam('table_settings', '', false, 'raw_data'); 1220 ShortcutOQL::GetCreationDlgFromOQL($oPage, $sOQL, $sTableSettings); 1221 break; 1222 1223 case 'shortcut_list_create': 1224 $oForm = ShortcutOQL::GetCreationForm(); 1225 $aValues = $oForm->ReadParams(); 1226 1227 $oAppContext = new ApplicationContext(); 1228 $aContext = $oAppContext->GetAsHash(); 1229 $sContext = serialize($aContext); 1230 1231 $oShortcut = MetaModel::NewObject("ShortcutOQL"); 1232 $oShortcut->Set('user_id', UserRights::GetUserId()); 1233 $oShortcut->Set("context", $sContext); 1234 $oShortcut->Set("name", $aValues['name']); 1235 $oShortcut->Set("oql", $aValues['oql']); 1236 $iAutoReload = (int)$aValues['auto_reload_sec']; 1237 if (($aValues['auto_reload']) && ($iAutoReload > 0)) 1238 { 1239 $oShortcut->Set("auto_reload_sec", max(MetaModel::GetConfig()->Get('min_reload_interval'), $iAutoReload)); 1240 $oShortcut->Set("auto_reload", 'custom'); 1241 } 1242 utils::PushArchiveMode(false); 1243 $iId = $oShortcut->DBInsertNoReload(); 1244 utils::PopArchiveMode(); 1245 1246 $oShortcut->CloneTableSettings($aValues['table_settings']); 1247 1248 // Add the menu node in the right place 1249 // 1250 // Mmmm... already done because the newly created menu is read from the DB 1251 // as soon as we invoke DisplayMenu 1252 1253 // Refresh the menu pane 1254 $aExtraParams = array(); 1255 ApplicationMenu::DisplayMenu($oPage, $aExtraParams); 1256 break; 1257 1258 case 'shortcut_rename_dlg': 1259 $oSearch = new DBObjectSearch('Shortcut'); 1260 $aShortcuts = utils::ReadMultipleSelection($oSearch); 1261 $iShortcut = $aShortcuts[0]; 1262 $oShortcut = MetaModel::GetObject('Shortcut', $iShortcut); 1263 $oShortcut->StartRenameDialog($oPage); 1264 break; 1265 1266 case 'shortcut_rename_go': 1267 $iShortcut = utils::ReadParam('id', 0); 1268 $oShortcut = MetaModel::GetObject('Shortcut', $iShortcut); 1269 1270 $sName = utils::ReadParam('attr_name', '', false, 'raw_data'); 1271 if (strlen($sName) > 0) 1272 { 1273 $oShortcut->Set('name', $sName); 1274 utils::PushArchiveMode(false); 1275 $oShortcut->DBUpdate(); 1276 utils::PopArchiveMode(); 1277 $oPage->add_ready_script('window.location.reload();'); 1278 } 1279 1280 break; 1281 1282 case 'shortcut_delete_go': 1283 $oSearch = new DBObjectSearch('Shortcut'); 1284 $oSearch->AddCondition('user_id', UserRights::GetUserId(), '='); 1285 $aShortcuts = utils::ReadMultipleSelection($oSearch); 1286 foreach($aShortcuts as $iShortcut) 1287 { 1288 $oShortcut = MetaModel::GetObject('Shortcut', $iShortcut); 1289 utils::PushArchiveMode(false); 1290 $oShortcut->DBDelete(); 1291 utils::PopArchiveMode(); 1292 $oPage->add_ready_script('window.location.reload();'); 1293 } 1294 break; 1295 1296 case 'about_box': 1297 $oPage->SetContentType('text/html'); 1298 1299 $sDialogTitle = addslashes(Dict::S('UI:About:Title')); 1300 $oPage->add_ready_script( 1301 <<<EOF 1302$('#about_box').dialog({ 1303 width: 700, 1304 modal: true, 1305 title: '$sDialogTitle', 1306 close: function() { $(this).remove(); } 1307}); 1308$("#collapse_support_details").click(function() { 1309 $("#support_details").slideToggle('normal'); 1310 $("#collapse_support_details").toggleClass('open'); 1311}); 1312$('#support_details').toggle(); 1313EOF 1314 ); 1315 $sVersionString = Dict::Format('UI:iTopVersion:Long', ITOP_APPLICATION, ITOP_VERSION, ITOP_REVISION, ITOP_BUILD_DATE); 1316 $sMySQLVersion = CMDBSource::GetDBVersion(); 1317 $sPHPVersion = phpversion(); 1318 $sOSVersion = PHP_OS; 1319 $sWebServerVersion = $_SERVER["SERVER_SOFTWARE"]; 1320 $sModules = implode(', ', get_loaded_extensions()); 1321 1322 // Get the datamodel directory 1323 $oFilter = DBObjectSearch::FromOQL('SELECT ModuleInstallation WHERE name="datamodel"'); 1324 $oSet = new DBObjectSet($oFilter, array('installed' => false)); // Most recent first 1325 $oLastInstall = $oSet->Fetch(); 1326 $sLastInstallDate = $oLastInstall->Get('installed'); 1327 $sDataModelVersion = $oLastInstall->Get('version'); 1328 $aDataModelInfo = json_decode($oLastInstall->Get('comment'), true); 1329 $sDataModelSourceDir = $aDataModelInfo['source_dir']; 1330 1331 require_once(APPROOT.'setup/runtimeenv.class.inc.php'); 1332 $sCurrEnv = utils::GetCurrentEnvironment(); 1333 $oRuntimeEnv = new RunTimeEnvironment($sCurrEnv); 1334 $aSearchDirs = array(APPROOT.$sDataModelSourceDir); 1335 if (file_exists(APPROOT.'extensions')) 1336 { 1337 $aSearchDirs[] = APPROOT.'extensions'; 1338 } 1339 $sExtraDir = APPROOT.'data/'.$sCurrEnv.'-modules/'; 1340 if (file_exists($sExtraDir)) 1341 { 1342 $aSearchDirs[] = $sExtraDir; 1343 } 1344 $aAvailableModules = $oRuntimeEnv->AnalyzeInstallation(MetaModel::GetConfig(), $aSearchDirs); 1345 1346 require_once(APPROOT.'setup/setuputils.class.inc.php'); 1347 $aLicenses = SetupUtils::GetLicenses($sCurrEnv); 1348 1349 $aItopSettings = array('cron_max_execution_time', 'timezone'); 1350 $aPHPSettings = array('memory_limit', 'max_execution_time', 'upload_max_filesize', 'post_max_size'); 1351 $aMySQLSettings = array('max_allowed_packet', 'key_buffer_size', 'query_cache_size'); 1352 $aMySQLStatuses = array('Key_read_requests', 'Key_reads'); 1353 1354 if (extension_loaded('suhosin')) 1355 { 1356 $aPHPSettings[] = 'suhosin.post.max_vars'; 1357 $aPHPSettings[] = 'suhosin.get.max_value_length'; 1358 } 1359 1360 $aMySQLVars = array(); 1361 foreach(CMDBSource::QueryToArray('SHOW VARIABLES') as $aRow) 1362 { 1363 $aMySQLVars[$aRow['Variable_name']] = $aRow['Value']; 1364 } 1365 1366 $aMySQLStats = array(); 1367 foreach(CMDBSource::QueryToArray('SHOW GLOBAL STATUS') as $aRow) 1368 { 1369 $aMySQLStats[$aRow['Variable_name']] = $aRow['Value']; 1370 } 1371 1372 // Display 1373 // 1374 $oPage->add("<div id=\"about_box\">"); 1375 $oPage->add('<div style="margin-left: 120px;">'); 1376 $oPage->add('<table>'); 1377 $oPage->add('<tr>'); 1378 $oPage->add('<td><a href="http://www.combodo.com" title="www.combodo.com" target="_blank" style="background: none;"><img src="../images/logo-combodo.png?t='.utils::GetCacheBusterTimestamp().'" style="float: right;"/></a></td>'); 1379 $oPage->add('<td style="padding-left: 20px;">'); 1380 $oPage->add($sVersionString.'<br/>'); 1381 $oPage->add(Dict::S('UI:About:DataModel').': '.$sDataModelVersion.'<br/>'); 1382 $oPage->add('MySQL: '.$sMySQLVersion.'<br/>'); 1383 $oPage->add('PHP: '.$sPHPVersion.'<br/>'); 1384 $oPage->add('</td>'); 1385 $oPage->add('</tr>'); 1386 $oPage->add('</table>'); 1387 $oPage->add("</div>"); 1388 1389 $oPage->add("<div>"); 1390 $oPage->add('<fieldset>'); 1391 $oPage->add('<legend>'.Dict::S('UI:About:Licenses').'</legend>'); 1392 $oPage->add('<ul style="margin: 0; font-size: smaller; max-height: 15em; overflow: auto;">'); 1393 $index = 0; 1394 foreach($aLicenses as $oLicense) 1395 { 1396 $oPage->add('<li><b>'.$oLicense->product.'</b>, © '.$oLicense->author.' is licensed under the <b>'.$oLicense->license_type.' license</b>. (<a id="toggle_'.$index.'" class="CollapsibleLabel" style="cursor:pointer;">Details</a>)'); 1397 $oPage->add('<div id="license_'.$index.'" class="license_text" style="display:none;overflow:auto;max-height:10em;font-size:small;border:1px #696969 solid;margin-bottom:1em; margin-top:0.5em;padding:0.5em;">'.$oLicense->text.'</div>'); 1398 $oPage->add_ready_script('$("#toggle_'.$index.'").click( function() { $("#license_'.$index.'").slideToggle("normal"); } );'); 1399 $index++; 1400 } 1401 $oPage->add('</ul>'); 1402 $oPage->add('</fieldset>'); 1403 $oPage->add("</div>"); 1404 1405 $oPage->add('<fieldset>'); 1406 $oPage->add('<legend>'.Dict::S('UI:About:InstallationOptions').'</legend>'); 1407 $oPage->add("<div style=\"max-height: 150px; overflow: auto; font-size: smaller;\">"); 1408 $oPage->add('<ul style="margin: 0;">'); 1409 1410 require_once(APPROOT.'setup/extensionsmap.class.inc.php'); 1411 $oExtensionsMap = new iTopExtensionsMap(); 1412 $oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig()); 1413 $aChoices = $oExtensionsMap->GetChoices(); 1414 foreach($aChoices as $oExtension) 1415 { 1416 switch ($oExtension->sSource) 1417 { 1418 case iTopExtension::SOURCE_REMOTE: 1419 $sSource = ' <span class="extension-source">'.Dict::S('UI:About:RemoteExtensionSource').'</span>'; 1420 break; 1421 1422 case iTopExtension::SOURCE_MANUAL: 1423 $sSource = ' <span class="extension-source">'.Dict::S('UI:About:ManualExtensionSource').'</span>'; 1424 break; 1425 1426 default: 1427 $sSource = ''; 1428 } 1429 $oPage->add('<li title="'.Dict::Format('UI:About:Extension_Version', $oExtension->sInstalledVersion).'">'.$oExtension->sLabel.$sSource.'</li>'); 1430 } 1431 $oPage->add('</ul>'); 1432 $oPage->add("</div>"); 1433 $oPage->add('</fieldset>'); 1434 1435 1436 // MUST NOT be localized, as the information given here will be sent to the support 1437 $oPage->add("<a id=\"collapse_support_details\" class=\"CollapsibleLabel\" href=\"#\">".Dict::S('UI:About:Support')."</a></br>\n"); 1438 $oPage->add("<div id=\"support_details\">"); 1439 $oPage->add('<textarea readonly style="width: 660px; height: 150px; font-size: smaller;">'); 1440 $oPage->add("===== begin =====\n"); 1441 $oPage->add('iTopVersion: '.ITOP_VERSION."\n"); 1442 $oPage->add('iTopBuild: '.ITOP_REVISION."\n"); 1443 $oPage->add('iTopBuildDate: '.ITOP_BUILD_DATE."\n"); 1444 $oPage->add('DataModelVersion: '.$sDataModelVersion."\n"); 1445 $oPage->add('MySQLVersion: '.$sMySQLVersion."\n"); 1446 $oPage->add('PHPVersion: '.$sPHPVersion."\n"); 1447 $oPage->add('OSVersion: '.$sOSVersion."\n"); 1448 $oPage->add('WebServerVersion: '.$sWebServerVersion."\n"); 1449 $oPage->add('PHPModules: '.$sModules."\n"); 1450 foreach($aItopSettings as $siTopVar) 1451 { 1452 $oPage->add('ItopSetting/'.$siTopVar.': '.MetaModel::GetConfig()->Get($siTopVar)."\n"); 1453 } 1454 foreach($aPHPSettings as $sPHPVar) 1455 { 1456 $oPage->add('PHPSetting/'.$sPHPVar.': '.ini_get($sPHPVar)."\n"); 1457 } 1458 foreach($aMySQLSettings as $sMySQLVar) 1459 { 1460 $oPage->add('MySQLSetting/'.$sMySQLVar.': '.$aMySQLVars[$sMySQLVar]."\n"); 1461 } 1462 foreach($aMySQLStatuses as $sMySQLStatus) 1463 { 1464 $oPage->add('MySQLStatus/'.$sMySQLStatus.': '.$aMySQLStats[$sMySQLStatus]."\n"); 1465 } 1466 1467 $oPage->add('InstallDate: '.$sLastInstallDate."\n"); 1468 $oPage->add('InstallPath: '.APPROOT."\n"); 1469 $oPage->add("---- Installation choices ----\n"); 1470 foreach($aChoices as $oExtension) 1471 { 1472 switch ($oExtension->sSource) 1473 { 1474 case iTopExtension::SOURCE_REMOTE: 1475 $sSource = ' ('.Dict::S('UI:About:RemoteExtensionSource').')'; 1476 break; 1477 1478 case iTopExtension::SOURCE_MANUAL: 1479 $sSource = ' ('.Dict::S('UI:About:ManualExtensionSource').')'; 1480 break; 1481 1482 default: 1483 $sSource = ''; 1484 } 1485 $oPage->add('InstalledExtension/'.$oExtension->sCode.'/'.$oExtension->sVersion.$sSource."\n"); 1486 } 1487 $oPage->add("---- Actual modules installed ----\n"); 1488 foreach($aAvailableModules as $sModuleId => $aModuleData) 1489 { 1490 if ($sModuleId == '_Root_') continue; 1491 if ($aModuleData['version_db'] == '') continue; 1492 $oPage->add('InstalledModule/'.$sModuleId.': '.$aModuleData['version_db']."\n"); 1493 } 1494 1495 $oPage->add('===== end ====='); 1496 $oPage->add('</textarea>'); 1497 $oPage->add("</div>"); 1498 1499 $oPage->add("</div>"); 1500 break; 1501 1502 case 'history': 1503 $oPage->SetContentType('text/html'); 1504 $id = (int)utils::ReadParam('id', 0); 1505 $iStart = (int)utils::ReadParam('start', 0); 1506 $iCount = (int)utils::ReadParam('count', MetaModel::GetConfig()->Get('max_history_length')); 1507 $oObj = MetaModel::GetObject($sClass, $id); 1508 $oObj->DisplayBareHistory($oPage, false, $iCount, $iStart); 1509 $oPage->add_ready_script("$('#history table.listResults').tableHover(); $('#history table.listResults').tablesorter( { widgets: ['myZebra', 'truncatedList']} );"); 1510 break; 1511 1512 case 'history_from_filter': 1513 $oPage->SetContentType('text/html'); 1514 $oHistoryFilter = DBSearch::unserialize($sFilter); 1515 $iStart = (int)utils::ReadParam('start', 0); 1516 $iCount = (int)utils::ReadParam('count', MetaModel::GetConfig()->Get('max_history_length')); 1517 $oBlock = new HistoryBlock($oHistoryFilter, 'table', false); 1518 $oBlock->SetLimit($iCount, $iStart); 1519 $oBlock->Display($oPage, 'history'); 1520 $oPage->add_ready_script("$('#history table.listResults').tableHover(); $('#history table.listResults').tablesorter( { widgets: ['myZebra', 'truncatedList']} );"); 1521 break; 1522 1523 case 'full_text_search': 1524 $aFullTextNeedles = utils::ReadParam('needles', array(), false, 'raw_data'); 1525 $sFullText = trim(implode(' ', $aFullTextNeedles)); 1526 $sClassName = utils::ReadParam('class', ''); 1527 $iCount = utils::ReadParam('count', 0); 1528 $iCurrentPos = utils::ReadParam('position', 0); 1529 $iTune = utils::ReadParam('tune', 0); 1530 if (empty($sFullText)) 1531 { 1532 $oPage->p(Dict::S('UI:Search:NoSearch')); 1533 break; 1534 } 1535 1536 // Search in full text mode in all the classes 1537 $aMatches = array(); 1538 1539 // Build the ordered list of classes to search into 1540 // 1541 if (empty($sClassName)) 1542 { 1543 $aSearchClasses = MetaModel::GetClasses('searchable'); 1544 } 1545 else 1546 { 1547 // Search is limited to a given class and its subclasses 1548 $aSearchClasses = MetaModel::EnumChildClasses($sClassName, ENUM_CHILD_CLASSES_ALL); 1549 } 1550 // Skip abstract classes, since we search in all the child classes anyway 1551 foreach($aSearchClasses as $idx => $sClass) 1552 { 1553 if (MetaModel::IsAbstract($sClass)) 1554 { 1555 unset($aSearchClasses[$idx]); 1556 } 1557 } 1558 1559 $sMaxChunkDuration = MetaModel::GetConfig()->Get('full_text_chunk_duration'); 1560 $aAccelerators = MetaModel::GetConfig()->Get('full_text_accelerators'); 1561 1562 foreach(array_reverse($aAccelerators) as $sClass => $aRestriction) 1563 { 1564 $bSkip = false; 1565 $iPos = array_search($sClass, $aSearchClasses); 1566 if ($iPos !== false) 1567 { 1568 unset($aSearchClasses[$iPos]); 1569 } 1570 else 1571 { 1572 $bSkip = true; 1573 } 1574 $bSkip |= array_key_exists('skip', $aRestriction) ? $aRestriction['skip'] : false; 1575 if (!in_array($sClass, $aSearchClasses)) 1576 { 1577 if ($sClass == $sClassName) 1578 { 1579 // Class explicitely requested, do NOT skip it 1580 // beware: there may not be a 'query' defined for a skipped class ! 1581 $bSkip = false; 1582 } 1583 } 1584 if (!$bSkip) 1585 { 1586 // NOT skipped, add the class to the list of classes to search into 1587 if (array_key_exists('query', $aRestriction)) 1588 { 1589 array_unshift($aSearchClasses, $aRestriction['query']); 1590 } 1591 else 1592 { 1593 // No accelerator query 1594 array_unshift($aSearchClasses, $sClassName); 1595 } 1596 } 1597 } 1598 1599 $aSearchClasses = array_values($aSearchClasses); // renumbers the array starting from zero, removing the missing indexes 1600 $fStarted = microtime(true); 1601 $iFoundInThisRound = 0; 1602 for($iPos = $iCurrentPos; $iPos < count($aSearchClasses); $iPos++) 1603 { 1604 if ($iFoundInThisRound && (microtime(true) - $fStarted >= $sMaxChunkDuration)) 1605 { 1606 break; 1607 } 1608 1609 $sClassSpec = $aSearchClasses[$iPos]; 1610 if (substr($sClassSpec, 0, 7) == 'SELECT ') 1611 { 1612 $oFilter = DBObjectSearch::FromOQL($sClassSpec); 1613 $sClassName = $oFilter->GetClass(); 1614 $sNeedleFormat = isset($aAccelerators[$sClassName]['needle']) ? $aAccelerators[$sClassName]['needle'] : '%$needle$%'; 1615 $sNeedle = str_replace('$needle$', $sFullText, $sNeedleFormat); 1616 $aParams = array('needle' => $sNeedle); 1617 } 1618 else 1619 { 1620 $sClassName = $sClassSpec; 1621 $oFilter = new DBObjectSearch($sClassName); 1622 $aParams = array(); 1623 1624 foreach($aFullTextNeedles as $sSearchText) 1625 { 1626 $oFilter->AddCondition_FullText($sSearchText); 1627 } 1628 } 1629 $oFilter->SetShowObsoleteData(utils::ShowObsoleteData()); 1630 // Skip abstract classes 1631 if (MetaModel::IsAbstract($sClassName)) continue; 1632 1633 if ($iTune > 0) 1634 { 1635 $fStartedClass = microtime(true); 1636 } 1637 $oSet = new DBObjectSet($oFilter, array(), $aParams); 1638 if (array_key_exists($sClassName, $aAccelerators) && array_key_exists('attributes', $aAccelerators[$sClassName])) 1639 { 1640 $oSet->OptimizeColumnLoad(array($oFilter->GetClassAlias() => $aAccelerators[$sClassName]['attributes'])); 1641 } 1642 1643 $sFullTextJS = addslashes($sFullText); 1644 $bEnableEnlarge = array_key_exists($sClassName, $aAccelerators) && array_key_exists('query', $aAccelerators[$sClassName]); 1645 if (array_key_exists($sClassName, $aAccelerators) && array_key_exists('enable_enlarge', $aAccelerators[$sClassName])) 1646 { 1647 $bEnableEnlarge &= $aAccelerators[$sClassName]['enable_enlarge']; 1648 } 1649 $sEnlargeTheSearch = 1650 <<<EOF 1651 $('.search-class-$sClassName button').prop('disabled', true); 1652 1653 $('.search-class-$sClassName h2').append(' <img id="indicator" src="../images/indicator.gif">'); 1654 var oParams = {operation: 'full_text_search_enlarge', class: '$sClassName', text: '$sFullTextJS'}; 1655 $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function(data) { 1656 $('.search-class-$sClassName').html(data); 1657 }); 1658EOF; 1659 1660 1661 $sEnlargeButton = ''; 1662 if ($bEnableEnlarge) 1663 { 1664 $sEnlargeButton = " <button onclick=\"".htmlentities($sEnlargeTheSearch, ENT_QUOTES, 'UTF-8')."\">".Dict::S('UI:Search:Enlarge')."</button>"; 1665 } 1666 if ($oSet->Count() > 0) 1667 { 1668 $aLeafs = array(); 1669 while ($oObj = $oSet->Fetch()) 1670 { 1671 if (get_class($oObj) == $sClassName) 1672 { 1673 $aLeafs[] = $oObj->GetKey(); 1674 $iFoundInThisRound++; 1675 } 1676 } 1677 $oLeafsFilter = new DBObjectSearch($sClassName); 1678 if (count($aLeafs) > 0) 1679 { 1680 $iCount += count($aLeafs); 1681 $oPage->add("<div class=\"search-class-result search-class-$sClassName\">\n"); 1682 $oPage->add("<div class=\"page_header\">\n"); 1683 if (array_key_exists($sClassName, $aAccelerators)) 1684 { 1685 $oPage->add("<h2>".MetaModel::GetClassIcon($sClassName)." <span class=\"hilite\">".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aLeafs), Metamodel::GetName($sClassName)).$sEnlargeButton."</h2>\n"); 1686 } 1687 else 1688 { 1689 $oPage->add("<h2>".MetaModel::GetClassIcon($sClassName)." <span class=\"hilite\">".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aLeafs), Metamodel::GetName($sClassName))."</h2>\n"); 1690 } 1691 $oPage->add("</div>\n"); 1692 $oLeafsFilter->AddCondition('id', $aLeafs, 'IN'); 1693 $oBlock = new DisplayBlock($oLeafsFilter, 'list', false); 1694 $sBlockId = 'global_search_'.$sClassName; 1695 $oPage->add('<div id="'.$sBlockId.'">'); 1696 $oBlock->RenderContent($oPage, array('table_id' => $sBlockId, 'currentId' => $sBlockId)); 1697 $oPage->add("</div>\n"); 1698 $oPage->add("</div>\n"); 1699 $oPage->p(' '); // Some space ? 1700 } 1701 } 1702 else 1703 { 1704 if (array_key_exists($sClassName, $aAccelerators)) 1705 { 1706 $oPage->add("<div class=\"search-class-result search-class-$sClassName\">\n"); 1707 $oPage->add("<div class=\"page_header\">\n"); 1708 $oPage->add("<h2>".MetaModel::GetClassIcon($sClassName)." <span class=\"hilite\">".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', 0, Metamodel::GetName($sClassName)).$sEnlargeButton."</h2>\n"); 1709 $oPage->add("</div>\n"); 1710 $oPage->add("</div>\n"); 1711 $oPage->p(' '); // Some space ? 1712 } 1713 } 1714 if ($iTune > 0) 1715 { 1716 $fDurationClass = microtime(true) - $fStartedClass; 1717 $oPage->add_script("oTimeStatistics.$sClassName = $fDurationClass;"); 1718 } 1719 } 1720 if ($iPos < count($aSearchClasses)) 1721 { 1722 $sJSNeedle = json_encode($aFullTextNeedles); 1723 $oPage->add_ready_script( 1724 <<<EOF 1725 var oParams = {operation: 'full_text_search', position: $iPos, needles: $sJSNeedle, count: $iCount, tune: $iTune}; 1726 $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function(data) { 1727 $('#full_text_results').append(data); 1728 }); 1729EOF 1730 ); 1731 } 1732 else 1733 { 1734 // We're done 1735 $oPage->add_ready_script( 1736 <<<EOF 1737$('#full_text_indicator').hide(); 1738$('#full_text_progress,#full_text_progress_placeholder').hide(500); 1739EOF 1740 ); 1741 1742 if ($iTune > 0) 1743 { 1744 $oPage->add_ready_script( 1745 <<<EOF 1746 var sRes = '<h4>Search statistics (tune = 1)</h4><table>'; 1747 sRes += '<thead><tr><th>Class</th><th>Time</th></tr></thead>'; 1748 sRes += '<tbody>'; 1749 var fTotal = 0; 1750 for (var sClass in oTimeStatistics) 1751 { 1752 fTotal = fTotal + oTimeStatistics[sClass]; 1753 fRounded = Math.round(oTimeStatistics[sClass] * 1000) / 1000; 1754 sRes += '<tr><td>' + sClass + '</td><td>' + fRounded + '</td></tr>'; 1755 } 1756 1757 fRoundedTotal = Math.round(fTotal * 1000) / 1000; 1758 sRes += '<tr><td><b>Total</b></td><td><b>' + fRoundedTotal + '</b></td></tr>'; 1759 sRes += '</tbody>'; 1760 sRes += '</table>'; 1761 $('#full_text_results').append(sRes); 1762EOF 1763 ); 1764 } 1765 1766 if ($iCount == 0) 1767 { 1768 $sFullTextSummary = addslashes(Dict::S('UI:Search:NoObjectFound')); 1769 $oPage->add_ready_script("$('#full_text_results').append('<div id=\"no_object_found\">$sFullTextSummary</div>');"); 1770 } 1771 } 1772 break; 1773 1774 case 'full_text_search_enlarge': 1775 $sFullText = trim(utils::ReadParam('text', '', false, 'raw_data')); 1776 $sClass = trim(utils::ReadParam('class', '')); 1777 $iTune = utils::ReadParam('tune', 0); 1778 1779 if (preg_match('/^"(.*)"$/', $sFullText, $aMatches)) 1780 { 1781 // The text is surrounded by double-quotes, remove the quotes and treat it as one single expression 1782 $aFullTextNeedles = array($aMatches[1]); 1783 } 1784 else 1785 { 1786 // Split the text on the blanks and treat this as a search for <word1> AND <word2> AND <word3> 1787 $aFullTextNeedles = explode(' ', $sFullText); 1788 } 1789 1790 $oFilter = new DBObjectSearch($sClass); 1791 foreach($aFullTextNeedles as $sSearchText) 1792 { 1793 $oFilter->AddCondition_FullText($sSearchText); 1794 } 1795 $oFilter->SetShowObsoleteData(utils::ShowObsoleteData()); 1796 $oSet = new DBObjectSet($oFilter); 1797 $oPage->add("<div class=\"page_header\">\n"); 1798 $oPage->add("<h2>".MetaModel::GetClassIcon($sClass)." <span class=\"hilite\">".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', $oSet->Count(), Metamodel::GetName($sClass))."</h2>\n"); 1799 $oPage->add("</div>\n"); 1800 if ($oSet->Count() > 0) 1801 { 1802 $aLeafs = array(); 1803 while ($oObj = $oSet->Fetch()) 1804 { 1805 if (get_class($oObj) == $sClass) 1806 { 1807 $aLeafs[] = $oObj->GetKey(); 1808 } 1809 } 1810 $oLeafsFilter = new DBObjectSearch($sClass); 1811 if (count($aLeafs) > 0) 1812 { 1813 $oLeafsFilter->AddCondition('id', $aLeafs, 'IN'); 1814 $oBlock = new DisplayBlock($oLeafsFilter, 'list', false); 1815 $sBlockId = 'global_search_'.$sClass; 1816 $oPage->add('<div id="'.$sBlockId.'">'); 1817 $oBlock->RenderContent($oPage, array('table_id' => $sBlockId, 'currentId' => $sBlockId)); 1818 $oPage->add('</div>'); 1819 $oPage->P(' '); // Some space ? 1820 // Hide "no object found" 1821 $oPage->add_ready_script('$("#no_object_found").hide();'); 1822 } 1823 } 1824 $oPage->add_ready_script( 1825 <<<EOF 1826$('#full_text_indicator').hide(); 1827$('#full_text_progress,#full_text_progress_placeholder').hide(500); 1828EOF 1829 ); 1830 break; 1831 1832 case 'xlsx_export_dialog': 1833 $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); 1834 $oPage->SetContentType('text/html'); 1835 $oPage->add( 1836 <<<EOF 1837<style> 1838 .ui-progressbar { 1839 position: relative; 1840} 1841.progress-label { 1842 position: absolute; 1843 left: 50%; 1844 top: 1px; 1845 font-size: 11pt; 1846} 1847.download-form button { 1848 display:block; 1849 margin-left: auto; 1850 margin-right: auto; 1851 margin-top: 2em; 1852} 1853.ui-progressbar-value { 1854 background: url(../setup/orange-progress.gif); 1855} 1856.progress-bar { 1857 height: 20px; 1858} 1859.statistics > div { 1860 padding-left: 16px; 1861 cursor: pointer; 1862 font-size: 10pt; 1863 background: url(../images/minus.gif) 0 2px no-repeat; 1864} 1865.statistics > div.closed { 1866 padding-left: 16px; 1867 background: url(../images/plus.gif) 0 2px no-repeat; 1868} 1869 1870.statistics .closed .stats-data { 1871 display: none; 1872} 1873.stats-data td { 1874 padding-right: 5px; 1875} 1876</style> 1877EOF 1878 ); 1879 $oPage->add('<div id="XlsxExportDlg">'); 1880 $oPage->add('<div class="export-options">'); 1881 $oPage->add('<p><input type="checkbox" id="export-advanced-mode"/> <label for="export-advanced-mode">'.Dict::S('UI:CSVImport:AdvancedMode').'</label></p>'); 1882 $oPage->add('<p style="font-size:10pt;margin-left:2em;margin-top:-0.5em;padding-bottom:1em;">'.Dict::S('UI:CSVImport:AdvancedMode+').'</p>'); 1883 $oPage->add('<p><input type="checkbox" id="export-auto-download" checked="checked"/> <label for="export-auto-download">'.Dict::S('ExcelExport:AutoDownload').'</label></p>'); 1884 $oPage->add('</div>'); 1885 $oPage->add('<div class="progress"><p class="status-message">'.Dict::S('ExcelExport:PreparingExport').'</p><div class="progress-bar"><div class="progress-label"></div></div></div>'); 1886 $oPage->add('<div class="statistics"><div class="stats-toggle closed">'.Dict::S('ExcelExport:Statistics').'<div class="stats-data"></div></div></div>'); 1887 $oPage->add('</div>'); 1888 $aLabels = array( 1889 'dialog_title' => Dict::S('ExcelExporter:ExportDialogTitle'), 1890 'cancel_button' => Dict::S('UI:Button:Cancel'), 1891 'export_button' => Dict::S('ExcelExporter:ExportButton'), 1892 'download_button' => Dict::Format('ExcelExporter:DownloadButton', 'export.xlsx'), //TODO: better name for the file (based on the class of the filter??) 1893 ); 1894 $sJSLabels = json_encode($aLabels); 1895 $sFilter = addslashes($sFilter); 1896 $sJSPageUrl = addslashes(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php'); 1897 $oPage->add_ready_script("$('#XlsxExportDlg').xlsxexporter({filter: '$sFilter', labels: $sJSLabels, ajax_page_url: '$sJSPageUrl'});"); 1898 break; 1899 1900 case 'xlsx_start': 1901 $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); 1902 $bAdvanced = (utils::ReadParam('advanced', 'false') == 'true'); 1903 $oSearch = DBObjectSearch::unserialize($sFilter); 1904 1905 $oExcelExporter = new ExcelExporter(); 1906 $oExcelExporter->SetObjectList($oSearch); 1907 //$oExcelExporter->SetChunkSize(10); //Only for testing 1908 $oExcelExporter->SetAdvancedMode($bAdvanced); 1909 $sToken = $oExcelExporter->SaveState(); 1910 $oPage->add(json_encode(array('status' => 'ok', 'token' => $sToken))); 1911 break; 1912 1913 case 'xlsx_run': 1914 $sMemoryLimit = MetaModel::GetConfig()->Get('xlsx_exporter_memory_limit'); 1915 ini_set('memory_limit', $sMemoryLimit); 1916 ini_set('max_execution_time', max(300, ini_get('max_execution_time'))); // At least 5 minutes 1917 1918 $sToken = utils::ReadParam('token', '', false, 'raw_data'); 1919 $oExcelExporter = new ExcelExporter($sToken); 1920 $aStatus = $oExcelExporter->Run(); 1921 $aResults = array('status' => $aStatus['code'], 'percentage' => $aStatus['percentage'], 'message' => $aStatus['message']); 1922 if ($aStatus['code'] == 'done') 1923 { 1924 $aResults['statistics'] = $oExcelExporter->GetStatistics('html'); 1925 } 1926 $oPage->add(json_encode($aResults)); 1927 break; 1928 1929 case 'xlsx_download': 1930 $sToken = utils::ReadParam('token', '', false, 'raw_data'); 1931 $oPage->SetContentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); 1932 $oPage->SetContentDisposition('attachment', 'export.xlsx'); 1933 $sFileContent = ExcelExporter::GetExcelFileFromToken($sToken); 1934 $oPage->add($sFileContent); 1935 ExcelExporter::CleanupFromToken($sToken); 1936 break; 1937 1938 case 'xlsx_abort': 1939 // Stop & cleanup an export... 1940 $sToken = utils::ReadParam('token', '', false, 'raw_data'); 1941 ExcelExporter::CleanupFromToken($sToken); 1942 break; 1943 1944 case 'relation_pdf': 1945 case 'relation_attachment': 1946 require_once(APPROOT.'core/simplegraph.class.inc.php'); 1947 require_once(APPROOT.'core/relationgraph.class.inc.php'); 1948 require_once(APPROOT.'core/displayablegraph.class.inc.php'); 1949 $sRelation = utils::ReadParam('relation', 'impacts'); 1950 $sDirection = utils::ReadParam('direction', 'down'); 1951 1952 $iGroupingThreshold = utils::ReadParam('g', 5, false, 'integer'); 1953 $sPageFormat = utils::ReadParam('p', 'A4'); 1954 $sPageOrientation = utils::ReadParam('o', 'L'); 1955 $sTitle = utils::ReadParam('title', '', false, 'raw_data'); 1956 $sPositions = utils::ReadParam('positions', null, false, 'raw_data'); 1957 $aExcludedClasses = utils::ReadParam('excluded_classes', array(), false, 'raw_data'); 1958 $bIncludeList = (bool)utils::ReadParam('include_list', false); 1959 $sComments = utils::ReadParam('comments', '', false, 'raw_data'); 1960 $aContexts = utils::ReadParam('contexts', array(), false, 'raw_data'); 1961 $sContextKey = utils::ReadParam('context_key', '', false, 'raw_data'); 1962 $aPositions = null; 1963 if ($sPositions != null) 1964 { 1965 $aPositions = json_decode($sPositions, true); 1966 } 1967 1968 // Get the list of source objects 1969 $aSources = utils::ReadParam('sources', array(), false, 'raw_data'); 1970 $aSourceObjects = array(); 1971 foreach($aSources as $sClass => $aIDs) 1972 { 1973 $oSearch = new DBObjectSearch($sClass); 1974 $oSearch->AddCondition('id', $aIDs, 'IN'); 1975 $oSet = new DBObjectSet($oSearch); 1976 while ($oObj = $oSet->Fetch()) 1977 { 1978 $aSourceObjects[] = $oObj; 1979 } 1980 } 1981 $sSourceClass = '*'; 1982 if (count($aSourceObjects) == 1) 1983 { 1984 $sSourceClass = get_class($aSourceObjects[0]); 1985 } 1986 1987 // Get the list of excluded objects 1988 $aExcluded = utils::ReadParam('excluded', array(), false, 'raw_data'); 1989 $aExcludedObjects = array(); 1990 foreach($aExcluded as $sClass => $aIDs) 1991 { 1992 $oSearch = new DBObjectSearch($sClass); 1993 $oSearch->AddCondition('id', $aIDs, 'IN'); 1994 $oSet = new DBObjectSet($oSearch); 1995 while ($oObj = $oSet->Fetch()) 1996 { 1997 $aExcludedObjects[] = $oObj; 1998 } 1999 } 2000 2001 $iMaxRecursionDepth = MetaModel::GetConfig()->Get('relations_max_depth'); 2002 if ($sDirection == 'up') 2003 { 2004 $oRelGraph = MetaModel::GetRelatedObjectsUp($sRelation, $aSourceObjects, $iMaxRecursionDepth, true, $aContexts); 2005 } 2006 else 2007 { 2008 $oRelGraph = MetaModel::GetRelatedObjectsDown($sRelation, $aSourceObjects, $iMaxRecursionDepth, true, $aExcludedObjects, $aContexts); 2009 } 2010 2011 // Remove excluded classes from the graph 2012 if (count($aExcludedClasses) > 0) 2013 { 2014 $oIterator = new RelationTypeIterator($oRelGraph, 'Node'); 2015 foreach($oIterator as $oNode) 2016 { 2017 $oObj = $oNode->GetProperty('object'); 2018 if ($oObj && in_array(get_class($oObj), $aExcludedClasses)) 2019 { 2020 $oRelGraph->FilterNode($oNode); 2021 } 2022 } 2023 } 2024 2025 $oPage = new PDFPage($sTitle, $sPageFormat, $sPageOrientation); 2026 $oPage->SetContentDisposition('attachment', $sTitle.'.pdf'); 2027 2028 $oGraph = DisplayableGraph::FromRelationGraph($oRelGraph, $iGroupingThreshold, ($sDirection == 'down')); 2029 $oGraph->InitFromGraphviz(); 2030 if ($aPositions != null) 2031 { 2032 $oGraph->UpdatePositions($aPositions); 2033 } 2034 2035 $aGroups = array(); 2036 $oIterator = new RelationTypeIterator($oGraph, 'Node'); 2037 foreach($oIterator as $oNode) 2038 { 2039 if ($oNode instanceof DisplayableGroupNode) 2040 { 2041 $aGroups[$oNode->GetProperty('group_index')] = $oNode->GetObjects(); 2042 } 2043 } 2044 // First page is the graph 2045 $oGraph->RenderAsPDF($oPage, $sComments, $sContextKey); 2046 2047 if ($bIncludeList) 2048 { 2049 // Then the lists of objects (one table per finalclass) 2050 $aResults = array(); 2051 $oIterator = new RelationTypeIterator($oRelGraph, 'Node'); 2052 foreach($oIterator as $oNode) 2053 { 2054 $oObj = $oNode->GetProperty('object'); // Some nodes (Redundancy Nodes and Group) do not contain an object 2055 if ($oObj) 2056 { 2057 $sObjClass = get_class($oObj); 2058 if (!array_key_exists($sObjClass, $aResults)) 2059 { 2060 $aResults[$sObjClass] = array(); 2061 } 2062 $aResults[$sObjClass][] = $oObj; 2063 } 2064 } 2065 2066 $oPage->get_tcpdf()->AddPage(); 2067 $oPage->get_tcpdf()->SetFont('dejavusans', '', 10, '', true); // Reset the font size to its default 2068 $oPage->add('<div class="page_header"><h1>'.Dict::S('UI:RelationshipList').'</h1></div>'); 2069 $iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop'); 2070 foreach($aResults as $sListClass => $aObjects) 2071 { 2072 set_time_limit($iLoopTimeLimit * count($aObjects)); 2073 $oSet = CMDBObjectSet::FromArray($sListClass, $aObjects); 2074 $oSet->SetShowObsoleteData(utils::ShowObsoleteData()); 2075 $sHtml = "<div class=\"page_header\">\n"; 2076 $sHtml .= "<table class=\"section\"><tr><td>".MetaModel::GetClassIcon($sListClass, true, 'width: 24px; height: 24px;')." ".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', $oSet->Count(), 2077 Metamodel::GetName($sListClass))."</td></tr></table>\n"; 2078 $sHtml .= "</div>\n"; 2079 $oPage->add($sHtml); 2080 cmdbAbstractObject::DisplaySet($oPage, $oSet, array('table_id' => $sSourceClass.'_'.$sRelation.'_'.$sDirection.'_'.$sListClass)); 2081 $oPage->p(''); // Some space 2082 } 2083 2084 // Then the content of the groups (one table per group) 2085 if (count($aGroups) > 0) 2086 { 2087 $oPage->get_tcpdf()->AddPage(); 2088 $oPage->add('<div class="page_header"><h1>'.Dict::S('UI:RelationGroups').'</h1></div>'); 2089 foreach($aGroups as $idx => $aObjects) 2090 { 2091 set_time_limit($iLoopTimeLimit * count($aObjects)); 2092 $sListClass = get_class(current($aObjects)); 2093 $oSet = CMDBObjectSet::FromArray($sListClass, $aObjects); 2094 $sHtml = "<div class=\"page_header\">\n"; 2095 $sHtml .= "<table class=\"section\"><tr><td>".MetaModel::GetClassIcon($sListClass, true, 'width: 24px; height: 24px;')." ".Dict::Format('UI:RelationGroupNumber_N', (1 + $idx))."</td></tr></table>\n"; 2096 $sHtml .= "</div>\n"; 2097 $oPage->add($sHtml); 2098 cmdbAbstractObject::DisplaySet($oPage, $oSet); 2099 $oPage->p(''); // Some space 2100 } 2101 } 2102 } 2103 if ($operation == 'relation_attachment') 2104 { 2105 $sObjClass = utils::ReadParam('obj_class', '', false, 'class'); 2106 $iObjKey = (int)utils::ReadParam('obj_key', 0, false, 'integer'); 2107 2108 // Save the generated PDF as an attachment 2109 $sPDF = $oPage->get_pdf(); 2110 $oPage = new ajax_page(''); 2111 $oAttachment = new Attachment(); 2112 $oAttachment->Set('item_class', $sObjClass); 2113 $oAttachment->Set('item_id', $iObjKey); 2114 $oDoc = new ormDocument($sPDF, 'application/pdf', $sTitle.'.pdf'); 2115 $oAttachment->Set('contents', $oDoc); 2116 $iAttachmentId = $oAttachment->DBInsert(); 2117 $aRet = array( 2118 'status' => 'ok', 2119 'att_id' => $iAttachmentId, 2120 ); 2121 $oPage->add(json_encode($aRet)); 2122 } 2123 break; 2124 2125 case 'relation_json': 2126 require_once(APPROOT.'core/simplegraph.class.inc.php'); 2127 require_once(APPROOT.'core/relationgraph.class.inc.php'); 2128 require_once(APPROOT.'core/displayablegraph.class.inc.php'); 2129 $sRelation = utils::ReadParam('relation', 'impacts'); 2130 $sDirection = utils::ReadParam('direction', 'down'); 2131 $iGroupingThreshold = utils::ReadParam('g', 5); 2132 $sPositions = utils::ReadParam('positions', null, false, 'raw_data'); 2133 $aExcludedClasses = utils::ReadParam('excluded_classes', array(), false, 'raw_data'); 2134 $aContexts = utils::ReadParam('contexts', array(), false, 'raw_data'); 2135 $sContextKey = utils::ReadParam('context_key', array(), false, 'raw_data'); 2136 $aPositions = null; 2137 if ($sPositions != null) 2138 { 2139 $aPositions = json_decode($sPositions, true); 2140 } 2141 2142 // Get the list of source objects 2143 $aSources = utils::ReadParam('sources', array(), false, 'raw_data'); 2144 $aSourceObjects = array(); 2145 foreach($aSources as $sClass => $aIDs) 2146 { 2147 $oSearch = new DBObjectSearch($sClass); 2148 $oSearch->AddCondition('id', $aIDs, 'IN'); 2149 $oSet = new DBObjectSet($oSearch); 2150 while ($oObj = $oSet->Fetch()) 2151 { 2152 $aSourceObjects[] = $oObj; 2153 } 2154 } 2155 2156 // Get the list of excluded objects 2157 $aExcluded = utils::ReadParam('excluded', array(), false, 'raw_data'); 2158 $aExcludedObjects = array(); 2159 foreach($aExcluded as $sClass => $aIDs) 2160 { 2161 $oSearch = new DBObjectSearch($sClass); 2162 $oSearch->AddCondition('id', $aIDs, 'IN'); 2163 $oSet = new DBObjectSet($oSearch); 2164 while ($oObj = $oSet->Fetch()) 2165 { 2166 $aExcludedObjects[] = $oObj; 2167 } 2168 } 2169 2170 // Compute the graph 2171 $iMaxRecursionDepth = MetaModel::GetConfig()->Get('relations_max_depth'); 2172 if ($sDirection == 'up') 2173 { 2174 $oRelGraph = MetaModel::GetRelatedObjectsUp($sRelation, $aSourceObjects, $iMaxRecursionDepth, true, $aContexts); 2175 } 2176 else 2177 { 2178 $oRelGraph = MetaModel::GetRelatedObjectsDown($sRelation, $aSourceObjects, $iMaxRecursionDepth, true, $aExcludedObjects, $aContexts); 2179 } 2180 2181 // Remove excluded classes from the graph 2182 if (count($aExcludedClasses) > 0) 2183 { 2184 $oIterator = new RelationTypeIterator($oRelGraph, 'Node'); 2185 foreach($oIterator as $oNode) 2186 { 2187 $oObj = $oNode->GetProperty('object'); 2188 if ($oObj && in_array(get_class($oObj), $aExcludedClasses)) 2189 { 2190 $oRelGraph->FilterNode($oNode); 2191 } 2192 } 2193 } 2194 2195 $oGraph = DisplayableGraph::FromRelationGraph($oRelGraph, $iGroupingThreshold, ($sDirection == 'down')); 2196 $oGraph->InitFromGraphviz(); 2197 if ($aPositions != null) 2198 { 2199 $oGraph->UpdatePositions($aPositions); 2200 } 2201 $oPage->add($oGraph->GetAsJSON($sContextKey)); 2202 $oPage->SetContentType('application/json'); 2203 break; 2204 2205 case 'relation_groups': 2206 $aGroups = utils::ReadParam('groups'); 2207 $iBlock = 1; // Zero is not a valid blockid 2208 foreach($aGroups as $idx => $aDefinition) 2209 { 2210 $sListClass = $aDefinition['class']; 2211 $oSearch = new DBObjectSearch($sListClass); 2212 $oSearch->AddCondition('id', $aDefinition['keys'], 'IN'); 2213 $oSearch->SetShowObsoleteData(utils::ShowObsoleteData()); 2214 $oPage->add("<h1>".Dict::Format('UI:RelationGroupNumber_N', (1 + $idx))."</h1>\n"); 2215 $oPage->add("<div id=\"relation_group_$idx\" class=\"page_header\">\n"); 2216 $oPage->add("<h2>".MetaModel::GetClassIcon($sListClass)." <span class=\"hilite\">".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aDefinition['keys']), Metamodel::GetName($sListClass))."</h2>\n"); 2217 $oPage->add("</div>\n"); 2218 $oBlock = new DisplayBlock($oSearch, 'list'); 2219 $oBlock->Display($oPage, 'group_'.$iBlock++); 2220 $oPage->p(' '); // Some space ? 2221 } 2222 break; 2223 2224 case 'relation_lists': 2225 $aLists = utils::ReadParam('lists'); 2226 $iBlock = 1; // Zero is not a valid blockid 2227 foreach($aLists as $sListClass => $aKeys) 2228 { 2229 $oSearch = new DBObjectSearch($sListClass); 2230 $oSearch->AddCondition('id', $aKeys, 'IN'); 2231 $oSearch->SetShowObsoleteData(utils::ShowObsoleteData()); 2232 $oPage->add("<div class=\"page_header\">\n"); 2233 $oPage->add("<h2>".MetaModel::GetClassIcon($sListClass)." <span class=\"hilite\">".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aKeys), Metamodel::GetName($sListClass))."</h2>\n"); 2234 $oPage->add("</div>\n"); 2235 $oBlock = new DisplayBlock($oSearch, 'list'); 2236 $oBlock->Display($oPage, 'list_'.$iBlock++, array('table_id' => 'ImpactAnalysis_'.$sListClass)); 2237 $oPage->p(' '); // Some space ? 2238 } 2239 break; 2240 2241 case 'ticket_impact': 2242 require_once(APPROOT.'core/simplegraph.class.inc.php'); 2243 require_once(APPROOT.'core/relationgraph.class.inc.php'); 2244 require_once(APPROOT.'core/displayablegraph.class.inc.php'); 2245 $sRelation = utils::ReadParam('relation', 'impacts'); 2246 $sDirection = utils::ReadParam('direction', 'down'); 2247 $iGroupingThreshold = utils::ReadParam('g', 5); 2248 $sClass = utils::ReadParam('class', '', false, 'class'); 2249 $sAttCode = utils::ReadParam('attcode', 'functionalcis_list'); 2250 $sImpactAttCode = utils::ReadParam('impact_attcode', 'impact_code'); 2251 $sImpactAttCodeValue = utils::ReadParam('impact_attcode_value', 'manual'); 2252 $iId = (int)utils::ReadParam('id', 0, false, 'integer'); 2253 2254 // Get the list of source objects 2255 $oTicket = MetaModel::GetObject($sClass, $iId); 2256 $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); 2257 $sExtKeyToRemote = $oAttDef->GetExtKeyToRemote(); 2258 $oExtKeyToRemote = MetaModel::GetAttributeDef($oAttDef->GetLinkedClass(), $sExtKeyToRemote); 2259 $sRemoteClass = $oExtKeyToRemote->GetTargetClass(); 2260 $oSet = $oTicket->Get($sAttCode); 2261 $aSourceObjects = array(); 2262 $aExcludedObjects = array(); 2263 while ($oLnk = $oSet->Fetch()) 2264 { 2265 if ($oLnk->Get($sImpactAttCode) == 'manual') 2266 { 2267 $aSourceObjects[] = MetaModel::GetObject($sRemoteClass, $oLnk->Get($sExtKeyToRemote)); 2268 } 2269 if ($oLnk->Get($sImpactAttCode) == 'not_impacted') 2270 { 2271 $aExcludedObjects[] = MetaModel::GetObject($sRemoteClass, $oLnk->Get($sExtKeyToRemote)); 2272 } 2273 } 2274 2275 // Compute the graph 2276 $iMaxRecursionDepth = MetaModel::GetConfig()->Get('relations_max_depth'); 2277 if ($sDirection == 'up') 2278 { 2279 $oRelGraph = MetaModel::GetRelatedObjectsUp($sRelation, $aSourceObjects, $iMaxRecursionDepth); 2280 } 2281 else 2282 { 2283 $oRelGraph = MetaModel::GetRelatedObjectsDown($sRelation, $aSourceObjects, $iMaxRecursionDepth, $aExcludedObjects); 2284 } 2285 2286 $aResults = $oRelGraph->GetObjectsByClass(); 2287 $oGraph = DisplayableGraph::FromRelationGraph($oRelGraph, $iGroupingThreshold, ($sDirection == 'down')); 2288 2289 $sContextKey = 'itop-tickets/relation_context/'.$sClass.'/'.$sRelation.'/'.$sDirection; 2290 $oAppContext = new ApplicationContext(); 2291 $oGraph->Display($oPage, $aResults, $sRelation, $oAppContext, $aExcludedObjects, $sClass, $iId, $sContextKey, array('this' => $oTicket)); 2292 break; 2293 2294 case 'export_build': 2295 register_shutdown_function(function () { 2296 $aErr = error_get_last(); 2297 if (($aErr !== null) && ($aErr['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR))) 2298 { 2299 ob_end_clean(); 2300 echo json_encode(array('code' => 'error', 'percentage' => 100, 'message' => Dict::Format('UI:Error_Details', $aErr['message']))); 2301 } 2302 }); 2303 try 2304 { 2305 $token = utils::ReadParam('token', null); 2306 $sTokenForDisplay = utils::HtmlEntities($token); 2307 $aResult = array( // Fallback error, just in case 2308 'code' => 'error', 2309 'percentage' => 100, 2310 'message' => "Export not found for token: '$sTokenForDisplay'", 2311 ); 2312 $data = ''; 2313 if ($token === null) 2314 { 2315 $sFormat = utils::ReadParam('format', ''); 2316 $sExpression = utils::ReadParam('expression', null, false, 'raw_data'); 2317 $iQueryId = utils::ReadParam('query', null); 2318 if ($sExpression === null) 2319 { 2320 $oQuerySearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $iQueryId)); 2321 $oQuerySearch->UpdateContextFromUser(); 2322 $oQueries = new DBObjectSet($oQuerySearch); 2323 if ($oQueries->Count() > 0) 2324 { 2325 $oQuery = $oQueries->Fetch(); 2326 $sExpression = $oQuery->Get('oql'); 2327 } 2328 else 2329 { 2330 $aResult = array('code' => 'error', 'percentage' => 100, 'message' => "Invalid query phrasebook identifier: '$iQueryId'"); 2331 } 2332 } 2333 if ($sExpression !== null) 2334 { 2335 $oSearch = DBObjectSearch::FromOQL($sExpression); 2336 $oSearch->UpdateContextFromUser(); 2337 $oExporter = BulkExport::FindExporter($sFormat, $oSearch); 2338 $oExporter->SetObjectList($oSearch); 2339 $oExporter->SetFormat($sFormat); 2340 $oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE); 2341 $oExporter->ReadParameters(); 2342 } 2343 2344 // First pass, generate the headers 2345 $data .= $oExporter->GetHeader(); 2346 } 2347 else 2348 { 2349 $oExporter = BulkExport::FindExporterFromToken($token); 2350 } 2351 2352 if ($oExporter) 2353 { 2354 $data .= $oExporter->GetNextChunk($aResult); 2355 if ($aResult['code'] != 'done') 2356 { 2357 $oExporter->AppendToTmpFile($data); 2358 $aResult['token'] = $oExporter->SaveState(); 2359 } 2360 else 2361 { 2362 // Last pass 2363 $data .= $oExporter->GetFooter(); 2364 $oExporter->AppendToTmpFile($data); 2365 $aResult['token'] = $oExporter->SaveState(); 2366 if (substr($oExporter->GetMimeType(), 0, 5) == 'text/') 2367 { 2368 // Result must be encoded in UTF-8 to be passed as part of a JSON structure 2369 $sCharset = $oExporter->GetCharacterSet(); 2370 if (strtoupper($sCharset) != 'UTF-8') 2371 { 2372 $aResult['text_result'] = iconv($sCharset, 'UTF-8', file_get_contents($oExporter->GetTmpFilePath())); 2373 } 2374 else 2375 { 2376 $aResult['text_result'] = file_get_contents($oExporter->GetTmpFilePath()); 2377 } 2378 $aResult['mime_type'] = $oExporter->GetMimeType(); 2379 } 2380 $aResult['message'] = Dict::Format('Core:BulkExport:ClickHereToDownload_FileName', $oExporter->GetDownloadFileName()); 2381 } 2382 } 2383 $oPage->add(json_encode($aResult)); 2384 } catch (BulkExportException $e) 2385 { 2386 $aResult = array('code' => 'error', 'percentage' => 100, 'message' => utils::HtmlEntities($e->GetLocalizedMessage())); 2387 $oPage->add(json_encode($aResult)); 2388 } catch (Exception $e) 2389 { 2390 $aResult = array('code' => 'error', 'percentage' => 100, 'message' => utils::HtmlEntities($e->getMessage())); 2391 $oPage->add(json_encode($aResult)); 2392 } 2393 break; 2394 2395 case 'export_download': 2396 $token = utils::ReadParam('token', null); 2397 if ($token !== null) 2398 { 2399 $oExporter = BulkExport::FindExporterFromToken($token); 2400 if ($oExporter) 2401 { 2402 $sMimeType = $oExporter->GetMimeType(); 2403 if (substr($sMimeType, 0, 5) == 'text/') 2404 { 2405 $sMimeType .= ';charset='.strtolower($oExporter->GetCharacterSet()); 2406 } 2407 $oPage->SetContentType($sMimeType); 2408 $oPage->SetContentDisposition('attachment', $oExporter->GetDownloadFileName()); 2409 $oPage->add(file_get_contents($oExporter->GetTmpFilePath())); 2410 } 2411 } 2412 break; 2413 2414 case 'export_cancel': 2415 $token = utils::ReadParam('token', null); 2416 if ($token !== null) 2417 { 2418 $oExporter = BulkExport::FindExporterFromToken($token); 2419 if ($oExporter) 2420 { 2421 $oExporter->Cleanup(); 2422 } 2423 } 2424 $aResult = array('code' => 'error', 'percentage' => 100, 'message' => Dict::S('Core:BulkExport:ExportCancelledByUser')); 2425 $oPage->add(json_encode($aResult)); 2426 break; 2427 2428 case 'extend_lock': 2429 $sObjClass = utils::ReadParam('obj_class', '', false, 'class'); 2430 $iObjKey = (int)utils::ReadParam('obj_key', 0, false, 'integer'); 2431 $sToken = utils::ReadParam('token', 0, false, 'raw_data'); 2432 $aResult = iTopOwnershipLock::ExtendLock($sObjClass, $iObjKey, $sToken); 2433 if (!$aResult['status']) 2434 { 2435 if ($aResult['operation'] == 'lost') 2436 { 2437 $sName = $aResult['owner']->GetName(); 2438 if ($aResult['owner']->Get('contactid') != 0) 2439 { 2440 $sName .= ' ('.$aResult['owner']->Get('contactid_friendlyname').')'; 2441 } 2442 $aResult['message'] = Dict::Format('UI:CurrentObjectIsLockedBy_User', $sName); 2443 $aResult['popup_message'] = Dict::Format('UI:CurrentObjectIsLockedBy_User_Explanation', $sName); 2444 } 2445 else 2446 { 2447 if ($aResult['operation'] == 'expired') 2448 { 2449 $aResult['message'] = Dict::S('UI:CurrentObjectLockExpired'); 2450 $aResult['popup_message'] = Dict::S('UI:CurrentObjectLockExpired_Explanation'); 2451 } 2452 } 2453 } 2454 $oPage->add(json_encode($aResult)); 2455 break; 2456 2457 case 'watchdog': 2458 $oPage->add('ok'); // Better for debugging... 2459 break; 2460 2461 case 'cke_img_upload': 2462 // Image uploaded via CKEditor 2463 $aResult = array( 2464 'uploaded' => 0, 2465 'fileName' => '', 2466 'url' => '', 2467 'icon' => '', 2468 'msg' => '', 2469 'att_id' => 0, 2470 'preview' => 'false', 2471 ); 2472 2473 $sObjClass = stripslashes(utils::ReadParam('obj_class', '', false, 'class')); 2474 $sTempId = utils::ReadParam('temp_id', '', false, 'transaction_id'); 2475 if (empty($sObjClass)) 2476 { 2477 $aResult['error'] = "Missing argument 'obj_class'"; 2478 } 2479 elseif (empty($sTempId)) 2480 { 2481 $aResult['error'] = "Missing argument 'temp_id'"; 2482 } 2483 else 2484 { 2485 try 2486 { 2487 $oDoc = utils::ReadPostedDocument('upload'); 2488 if (InlineImage::IsImage($oDoc->GetMimeType())) 2489 { 2490 $aDimensions = null; 2491 $oDoc = InlineImage::ResizeImageToFit($oDoc, $aDimensions); 2492 $oAttachment = MetaModel::NewObject('InlineImage'); 2493 $oAttachment->Set('expire', time() + MetaModel::GetConfig()->Get('draft_attachments_lifetime')); 2494 $oAttachment->Set('temp_id', $sTempId); 2495 $oAttachment->Set('item_class', $sObjClass); 2496 $oAttachment->SetDefaultOrgId(); 2497 $oAttachment->Set('contents', $oDoc); 2498 $oAttachment->Set('secret', sprintf('%06x', mt_rand(0, 0xFFFFFF))); // something not easy to guess 2499 $iAttId = $oAttachment->DBInsert(); 2500 2501 $aResult['uploaded'] = 1; 2502 $aResult['msg'] = htmlentities($oDoc->GetFileName(), ENT_QUOTES, 'UTF-8'); 2503 $aResult['fileName'] = $oDoc->GetFileName(); 2504 $aResult['url'] = utils::GetAbsoluteUrlAppRoot().INLINEIMAGE_DOWNLOAD_URL.$iAttId.'&s='.$oAttachment->Get('secret'); 2505 if (is_array($aDimensions)) 2506 { 2507 $aResult['width'] = $aDimensions['width']; 2508 $aResult['height'] = $aDimensions['height']; 2509 } 2510 } 2511 else 2512 { 2513 $aResult['error'] = $oDoc->GetFileName().' is not a valid image format.'; 2514 } 2515 } catch (FileUploadException $e) 2516 { 2517 $aResult['error'] = $e->GetMessage(); 2518 } 2519 } 2520 $oPage->add(json_encode($aResult)); 2521 break; 2522 2523 case 'cke_upload_and_browse': 2524 $sTempId = utils::ReadParam('temp_id', '', false, 'transaction_id'); 2525 $sObjClass = utils::ReadParam('obj_class', '', false, 'class'); 2526 try 2527 { 2528 $oDoc = utils::ReadPostedDocument('upload'); 2529 $sDocMimeType = $oDoc->GetMimeType(); 2530 if (!InlineImage::IsImage($sDocMimeType)) 2531 { 2532 LogErrorMessage('CKE : error when uploading image in ajax.render.php, not an image', 2533 array( 2534 'operation' => 'cke_upload_and_browse', 2535 'class' => $sObjClass, 2536 'ImgMimeType' => $sDocMimeType, 2537 )); 2538 } else { 2539 $aDimensions = null; 2540 $oDoc = InlineImage::ResizeImageToFit($oDoc, $aDimensions); 2541 $oAttachment = MetaModel::NewObject('InlineImage'); 2542 $oAttachment->Set('expire', time() + MetaModel::GetConfig()->Get('draft_attachments_lifetime')); 2543 $oAttachment->Set('temp_id', $sTempId); 2544 $oAttachment->Set('item_class', $sObjClass); 2545 $oAttachment->SetDefaultOrgId(); 2546 $oAttachment->Set('contents', $oDoc); 2547 $oAttachment->Set('secret', sprintf('%06x', mt_rand(0, 0xFFFFFF))); // something not easy to guess 2548 $iAttId = $oAttachment->DBInsert(); 2549 } 2550 2551 } catch (FileUploadException $e) 2552 { 2553 LogErrorMessage('CKE : error when uploading image in ajax.render.php, exception occured', 2554 array( 2555 'operation' => 'cke_upload_and_browse', 2556 'class' => $sObjClass, 2557 'exceptionMsg' => $e, 2558 )); 2559 } 2560 // Fall though !! => browse 2561 2562 case 'cke_browse': 2563 $oPage = new NiceWebPage(Dict::S('UI:BrowseInlineImages')); 2564 $oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/magnific-popup.css'); 2565 $oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.magnific-popup.min.js'); 2566 $sImgUrl = utils::GetAbsoluteUrlAppRoot().INLINEIMAGE_DOWNLOAD_URL; 2567 2568 $sTempId = utils::ReadParam('temp_id', '', false, 'transaction_id'); 2569 $sClass = utils::ReadParam('obj_class', '', false, 'class'); 2570 $iObjectId = utils::ReadParam('obj_key', 0, false, 'integer'); 2571 $sCKEditorFuncNum = utils::ReadParam('CKEditorFuncNum', ''); 2572 2573 $sPostUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?CKEditorFuncNum='.$sCKEditorFuncNum; 2574 2575 $oPage->add_style( 2576 <<<EOF 2577body { 2578 overflow: auto; 2579} 2580EOF 2581 ); 2582 $sMaxUpload = InlineImage::GetMaxUpload(); 2583 $sUploadLegend = Dict::S('UI:UploadInlineImageLegend'); 2584 $sUploadLabel = Dict::S('UI:SelectInlineImageToUpload'); 2585 $sAvailableImagesLegend = Dict::S('UI:AvailableInlineImagesLegend'); 2586 $sInsertBtnLabel = Dict::S('UI:Button:Insert'); 2587 $sNoInlineImage = Dict::S('UI:NoInlineImage'); 2588 $oPage->add( 2589 <<<EOF 2590<div> 2591 <fieldset> 2592 <legend>$sUploadLegend</legend> 2593 <form method="post" id="upload_form" action="$sPostUrl" enctype="multipart/form-data"> 2594 <input type="hidden" name="operation" value="cke_upload_and_browse"> 2595 <input type="hidden" name="temp_id" value="$sTempId"> 2596 <input type="hidden" name="obj_class" value="$sClass"> 2597 <input type="hidden" name="obj_key" value="$iObjectId"> 2598 $sUploadLabel <input id="upload_button" type="file" name="upload"> <span id="upload_status"> $sMaxUpload</span> 2599 </form> 2600 </fieldset> 2601</div> 2602EOF 2603 ); 2604 2605 $oPage->add_script( 2606 <<<EOF 2607 // Helper function to get parameters from the query string. 2608 function getUrlParam( paramName ) { 2609 var reParam = new RegExp( '(?:[\?&]|&)' + paramName + '=([^&]+)', 'i' ); 2610 var match = window.location.search.match( reParam ); 2611 2612 return ( match && match.length > 1 ) ? match[1] : null; 2613 } 2614 // Simulate user action of selecting a file to be returned to CKEditor. 2615 function returnFileUrl(iAttId, sAltText, sSecret) { 2616 2617 var funcNum = getUrlParam( 'CKEditorFuncNum' ); 2618 var fileUrl = '$sImgUrl'+iAttId+'&s='+sSecret; 2619 window.opener.CKEDITOR.tools.callFunction( funcNum, fileUrl, function() { 2620 // Get the reference to a dialog window. 2621 var dialog = this.getDialog(); 2622 // Check if this is the Image Properties dialog window. 2623 if ( dialog.getName() == 'image' ) { 2624 // Get the reference to a text field that stores the "alt" attribute. 2625 var element = dialog.getContentElement( 'info', 'txtAlt' ); 2626 // Assign the new value. 2627 if ( element ) 2628 element.setValue(sAltText); 2629 } 2630 // Return "false" to stop further execution. In such case CKEditor will ignore the second argument ("fileUrl") 2631 // and the "onSelect" function assigned to the button that called the file manager (if defined). 2632 // return false; 2633 } ); 2634 window.close(); 2635 } 2636EOF 2637 ); 2638 $oPage->add_ready_script( 2639 <<<EOF 2640$('#upload_button').on('change', function() { 2641 $('#upload_status').html('<img src="../images/indicator.gif">'); 2642 $('#upload_form').submit(); 2643 $(this).prop('disabled', true); 2644}); 2645$('.img-picker').magnificPopup({type: 'image', closeOnContentClick: true }); 2646EOF 2647 ); 2648 $sOQL = "SELECT InlineImage WHERE ((temp_id = :temp_id) OR (item_class = :obj_class AND item_id = :obj_id))"; 2649 $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array(), array('temp_id' => $sTempId, 'obj_class' => $sClass, 'obj_id' => $iObjectId)); 2650 $oPage->add("<div><fieldset><legend>$sAvailableImagesLegend</legend>"); 2651 2652 if ($oSet->Count() == 0) 2653 { 2654 $oPage->add("<p style=\"text-align:center\">$sNoInlineImage</p>"); 2655 } 2656 else 2657 { 2658 while ($oAttachment = $oSet->Fetch()) 2659 { 2660 $oDoc = $oAttachment->Get('contents'); 2661 if ($oDoc->GetMainMimeType() == 'image') 2662 { 2663 $sDocName = addslashes(htmlentities($oDoc->GetFileName(), ENT_QUOTES, 'UTF-8')); 2664 $iAttId = $oAttachment->GetKey(); 2665 $sSecret = $oAttachment->Get('secret'); 2666 $oPage->add("<div style=\"float:left;margin:1em;text-align:center;\"><img class=\"img-picker\" style=\"max-width:300px;cursor:zoom-in\" href=\"{$sImgUrl}{$iAttId}&s={$sSecret}\" alt=\"$sDocName\" title=\"$sDocName\" src=\"{$sImgUrl}{$iAttId}&s={$sSecret}\"><br/><button onclick=\"returnFileUrl($iAttId, '$sDocName', '$sSecret')\">$sInsertBtnLabel</button></div>"); 2667 } 2668 } 2669 } 2670 $oPage->add("</fieldset></div>"); 2671 break; 2672 2673 case 'custom_fields_update': 2674 $oPage->SetContentType('application/json'); 2675 $sAttCode = utils::ReadParam('attcode', ''); 2676 $aRequestedFields = utils::ReadParam('requested_fields', array()); 2677 $sRequestedFieldsFormPath = utils::ReadParam('form_path', ''); 2678 $sJson = utils::ReadParam('json_obj', '', false, 'raw_data'); 2679 2680 $aResult = array(); 2681 2682 try 2683 { 2684 $oWizardHelper = WizardHelper::FromJSON($sJson); 2685 $oObj = $oWizardHelper->GetTargetObject(); 2686 2687 $oOrmCustomFieldValue = $oObj->Get($sAttCode); 2688 $oForm = $oOrmCustomFieldValue->GetForm(); 2689 $oSubForm = $oForm->FindSubForm($sRequestedFieldsFormPath); 2690 $oRenderer = new \Combodo\iTop\Renderer\Console\ConsoleFormRenderer($oSubForm); 2691 $aRenderRes = $oRenderer->Render($aRequestedFields); 2692 2693 $aResult['form']['updated_fields'] = $aRenderRes; 2694 } catch (Exception $e) 2695 { 2696 $aResult['error'] = $e->getMessage(); 2697 } 2698 $oPage->add(json_encode($aResult)); 2699 break; 2700 2701 default: 2702 $oPage->p("Invalid query."); 2703 } 2704 2705 $oPage->output(); 2706} catch (Exception $e) 2707{ 2708 // note: transform to cope with XSS attacks 2709 echo htmlentities($e->GetMessage(), ENT_QUOTES, 'utf-8'); 2710 IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString()); 2711} 2712