1<?php 2// Copyright (C) 2015-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 * Export data specified by an OQL or a query phrasebook entry 21 * 22 * @copyright Copyright (C) 2015-2017 Combodo SARL 23 * @license http://opensource.org/licenses/AGPL-3.0 24 */ 25 26if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__)); 27require_once(__DIR__.'/../approot.inc.php'); 28require_once(APPROOT.'/application/application.inc.php'); 29require_once(APPROOT.'/application/nicewebpage.class.inc.php'); 30require_once(APPROOT.'/application/ajaxwebpage.class.inc.php'); 31require_once(APPROOT.'/application/csvpage.class.inc.php'); 32require_once(APPROOT.'/application/itopwebpage.class.inc.php'); 33require_once(APPROOT.'/application/xmlpage.class.inc.php'); 34require_once(APPROOT.'/application/clipage.class.inc.php'); 35require_once(APPROOT.'/application/excelexporter.class.inc.php'); 36require_once(APPROOT.'/core/bulkexport.class.inc.php'); 37 38require_once(APPROOT.'/application/startup.inc.php'); 39 40 41function ReportErrorAndExit($sErrorMessage) 42{ 43 if (utils::IsModeCLI()) 44 { 45 $oP = new CLIPage("iTop - Export"); 46 $oP->p('ERROR: '.$sErrorMessage); 47 $oP->output(); 48 exit(-1); 49 } 50 else 51 { 52 $oP = new WebPage("iTop - Export"); 53 $oP->p('ERROR: '.$sErrorMessage); 54 $oP->output(); 55 exit(-1); 56 } 57} 58 59function ReportErrorAndUsage($sErrorMessage) 60{ 61 if (utils::IsModeCLI()) 62 { 63 $oP = new CLIPage("iTop - Export"); 64 $oP->p('ERROR: '.$sErrorMessage); 65 Usage($oP); 66 $oP->output(); 67 exit(-1); 68 } 69 else 70 { 71 $oP = new WebPage("iTop - Export"); 72 $oP->p('ERROR: '.$sErrorMessage); 73 Usage($oP); 74 $oP->output(); 75 exit(-1); 76 } 77} 78 79function Usage(Page $oP) 80{ 81 if (Utils::IsModeCLI()) 82 { 83 $oP->p('Usage: php '.basename(__FILE__).' --auth_user=<user> --auth_pwd=<password> --expression=<OQL Query> --query=<phrasebook_id> [--arg_xxx=<query_arguments>] [--no_localize=0|1] [--format=<format>] [--format-options...]'); 84 $oP->p("Parameters:"); 85 $oP->p(" * auth_user: the iTop user account for authentication"); 86 $oP->p(" * auth_pwd: the password of the iTop user account"); 87 } 88 else 89 { 90 $oP->p("Parameters:"); 91 } 92 $oP->p(" * expression: an OQL expression (e.g. SELECT Contact WHERE name LIKE 'm%')"); 93 $oP->p(" * query: (alternative to 'expression') the id of an entry from the query phrasebook"); 94 if (Utils::IsModeCLI()) 95 { 96 $oP->p(" * with_archive: (optional, defaults to 0) if set to 1 then the result set will include archived objects"); 97 } 98 else 99 { 100 $oP->p(" * with_archive: (optional, defaults to the current mode) if set to 1 then the result set will include archived objects"); 101 } 102 $oP->p(" * arg_xxx: (needed if the query has parameters) the value of the parameter 'xxx'"); 103 $aSupportedFormats = BulkExport::FindSupportedFormats(); 104 $oP->p(" * format: (optional, default is html) the desired output format. Can be one of '".implode("', '", array_keys($aSupportedFormats))."'"); 105 foreach($aSupportedFormats as $sFormatCode => $sLabel) 106 { 107 $oExporter = BulkExport::FindExporter($sFormatCode); 108 if ($oExporter !== null) 109 { 110 if (!Utils::IsModeCLI()) 111 { 112 $oP->add('<hr/>'); 113 } 114 $oExporter->DisplayUsage($oP); 115 if (!Utils::IsModeCLI()) 116 { 117 $oP->add('</div>'); 118 } 119 } 120 } 121 //if (!Utils::IsModeCLI()) 122 //{ 123 // $oP->add('</pre>'); 124 //} 125} 126 127function DisplayExpressionForm(WebPage $oP, $sAction, $sExpression = '', $sExceptionMessage = '') 128{ 129 $oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:ScopeDefinition').'</legend>'); 130 $oP->add('<form id="export-form" action="'.$sAction.'" method="post">'); 131 $oP->add('<input type="hidden" name="interactive" value="1">'); 132 $oP->add('<table style="width:100%" class="export_parameters">'); 133 $sExpressionHint = empty($sExceptionMessage) ? '' : '<tr><td colspan="2">'.htmlentities($sExceptionMessage, ENT_QUOTES, 'UTF-8').'</td></tr>'; 134 $oP->add('<tr><td class="column-label"><span style="white-space: nowrap;"><input type="radio" name="query_mode" value="oql" id="radio_oql" checked><label for="radio_oql">'.Dict::S('Core:BulkExportLabelOQLExpression').'</label></span></td>'); 135 $oP->add('<td><textarea style="width:100%" cols="70" rows="8" name="expression" id="textarea_oql" placeholder="'.Dict::S('Core:BulkExportQueryPlaceholder').'">'.htmlentities($sExpression, ENT_QUOTES, 'UTF-8').'</textarea></td></tr>'); 136 $oP->add($sExpressionHint); 137 $oP->add('<tr><td class="column-label"><span style="white-space: nowrap;"><input type="radio" name="query_mode" value="phrasebook" id="radio_phrasebook"><label for="radio_phrasebook">'.Dict::S('Core:BulkExportLabelPhrasebookEntry').'</label></span></td>'); 138 $oP->add('<td><select name="query" id="select_phrasebook">'); 139 $oP->add('<option value="">'.Dict::S('UI:SelectOne').'</option>'); 140 $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL'); 141 $oSearch->UpdateContextFromUser(); 142 $oQueries = new DBObjectSet($oSearch); 143 while ($oQuery = $oQueries->Fetch()) 144 { 145 $oP->add('<option value="'.$oQuery->GetKey().'">'.htmlentities($oQuery->Get('name'), ENT_QUOTES, 'UTF-8').'</option>'); 146 } 147 $oP->add('</select></td></tr>'); 148 $oP->add('<tr><td colspan="2" style="text-align:right"><button type="submit" id="next-btn">'.Dict::S('UI:Button:Next').'</button></td></tr>'); 149 $oP->add('</table>'); 150 $oP->add('</form>'); 151 $oP->add('</fieldset>'); 152 $oP->p('<a target="_blank" href="'.utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php">'.Dict::S('Core:BulkExportCanRunNonInteractive').'</a>'); 153 $oP->p('<a target="_blank" href="'.utils::GetAbsoluteUrlAppRoot().'webservices/export.php">'.Dict::S('Core:BulkExportLegacyExport').'</a>'); 154 $sJSEmptyOQL = json_encode(Dict::S('Core:BulkExportMessageEmptyOQL')); 155 $sJSEmptyQueryId = json_encode(Dict::S('Core:BulkExportMessageEmptyPhrasebookEntry')); 156 157 $oP->add_ready_script( 158<<<EOF 159var colWidth = 0; 160$('td.column-label').each(function() { 161 var jLabel = $(this).find('span'); 162 colWidth = Math.max(colWidth, jLabel.width()); 163}); 164$('td.column-label').each(function() { 165 var jLabel = $(this).width(colWidth); 166}); 167 168$('#textarea_oql').on('change keyup', function() { 169 $('#radio_oql').prop('checked', true); 170}); 171$('#select_phrasebook').on('change', function() { 172 $('#radio_phrasebook').prop('checked', true); 173}); 174$('#export-form').on('submit', function() { 175 if ($('#radio_oql').prop('checked')) 176 { 177 var sOQL = $('#textarea_oql').val(); 178 if (sOQL == '') 179 { 180 alert($sJSEmptyOQL); 181 return false; 182 } 183 } 184 else 185 { 186 var sQueryId = $('#select_phrasebook').val(); 187 if (sQueryId == '') 188 { 189 alert($sJSEmptyQueryId); 190 return false; 191 } 192 } 193 return true; 194}); 195EOF 196 ); 197} 198 199function DisplayForm(WebPage $oP, $sAction = '', $sExpression = '', $sQueryId = '', $sFormat = null) 200{ 201 $oExportSearch = null; 202 $oP->add_script(DateTimeFormat::GetJSSQLToCustomFormat()); 203 $sJSDefaultDateTimeFormat = json_encode((string)AttributeDateTime::GetFormat()); 204 $oP->add_script( 205<<<EOF 206function FormatDatesInPreview(sRadioSelector, sPreviewSelector) 207{ 208 if ($('#'+sRadioSelector+'_date_time_format_default').prop('checked')) 209 { 210 sPHPFormat = $sJSDefaultDateTimeFormat; 211 } 212 else 213 { 214 sPHPFormat = $('#'+sRadioSelector+'_custom_date_time_format').val(); 215 } 216 $('#interactive_fields_'+sPreviewSelector+' .user-formatted-date-time').each(function() { 217 var val = $(this).attr('data-date'); 218 var sDisplay = DateTimeFormatFromPHP(val, sPHPFormat); 219 $(this).html(sDisplay); 220 }); 221 $('#interactive_fields_'+sPreviewSelector+' .user-formatted-date').each(function() { 222 var val = $(this).attr('data-date'); 223 var sDisplay = DateFormatFromPHP(val, sPHPFormat); 224 $(this).html(sDisplay); 225 }); 226} 227EOF 228 ); 229 $oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/tabularfieldsselector.js'); 230 $oP->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.dragtable.js'); 231 $oP->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/dragtable.css'); 232 $oP->add('<form id="export-form" action="'.$sAction.'" method="post" data-state="not-yet-started">'); 233 $bExpressionIsValid = true; 234 $sExpressionError = ''; 235 if (($sExpression === null) && ($sQueryId === null)) 236 { 237 $bExpressionIsValid = false; 238 } 239 else if ($sExpression !== '') 240 { 241 try 242 { 243 $oExportSearch = DBObjectSearch::FromOQL($sExpression); 244 $oExportSearch->UpdateContextFromUser(); 245 } 246 catch(OQLException $e) 247 { 248 $bExpressionIsValid = false; 249 $sExpressionError = $e->getMessage(); 250 } 251 } 252 253 if (!$bExpressionIsValid) 254 { 255 DisplayExpressionForm($oP, $sAction, $sExpression, $sExpressionError); 256 return; 257 } 258 259 if ($sExpression !== '') 260 { 261 $oP->add('<input type="hidden" name="expression" value="'.htmlentities($sExpression, ENT_QUOTES, 'UTF-8').'">'); 262 $oExportSearch = DBObjectSearch::FromOQL($sExpression); 263 $oExportSearch->UpdateContextFromUser(); 264 } 265 else 266 { 267 $oQuery = MetaModel::GetObject('QueryOQL', $sQueryId); 268 $oExportSearch = DBObjectSearch::FromOQL($oQuery->Get('oql')); 269 $oExportSearch->UpdateContextFromUser(); 270 $oP->add('<input type="hidden" name="query" value="'.htmlentities($sQueryId, ENT_QUOTES, 'UTF-8').'">'); 271 } 272 $aFormPartsByFormat = array(); 273 $aAllFormParts = array(); 274 if ($sFormat == null) 275 { 276 // No specific format chosen 277 $sDefaultFormat = utils::ReadParam('format', 'xlsx'); 278 $oP->add('<p>'.Dict::S('Core:BulkExport:ExportFormatPrompt').' <select name="format" id="format_selector">'); 279 $aSupportedFormats = BulkExport::FindSupportedFormats(); 280 asort($aSupportedFormats); 281 foreach($aSupportedFormats as $sFormatCode => $sLabel) 282 { 283 $sSelected = ($sFormatCode == $sDefaultFormat) ? 'selected' : ''; 284 $oP->add('<option value="'.$sFormatCode.'" '.$sSelected.'>'.htmlentities($sLabel, ENT_QUOTES, 'UTF-8').'</option>'); 285 $oExporter = BulkExport::FindExporter($sFormatCode); 286 $oExporter->SetObjectList($oExportSearch); 287 $aParts = $oExporter->EnumFormParts(); 288 foreach($aParts as $sPartId => $void) 289 { 290 $aAllFormParts[$sPartId] = $oExporter; 291 } 292 $aFormPartsByFormat[$sFormatCode] = array_keys($aParts); 293 } 294 $oP->add('</select></p>'); 295 } 296 else 297 { 298 // One specific format was chosen 299 $oP->add('<input type="hidden" name="format" value="'.htmlentities($sFormat, ENT_QUOTES, 'UTF-8').'">'); 300 301 $oExporter = BulkExport::FindExporter($sFormat, $oExportSearch); 302 $aParts = $oExporter->EnumFormParts(); 303 foreach($aParts as $sPartId => $void) 304 { 305 $aAllFormParts[$sPartId] = $oExporter; 306 } 307 $aFormPartsByFormat[$sFormat] = array_keys($aAllFormParts); 308 } 309 foreach($aAllFormParts as $sPartId => $oExport) 310 { 311 $oP->add('<div class="form_part" id="form_part_'.$sPartId.'">'); 312 $oExport->DisplayFormPart($oP, $sPartId); 313 $oP->add('</div>'); 314 } 315 $oP->add('</form>'); 316 $oP->add('<div id="export-feedback" style="display:none;"><p class="export-message" style="text-align:center;">'.Dict::S('ExcelExport:PreparingExport').'</p><div class="export-progress-bar" style="max-width:30em; margin-left:auto;margin-right:auto;"><div class="export-progress-message" style="text-align:center;"></div></div></div>'); 317 $oP->add('<button type="button" id="export-btn">'.Dict::S('UI:Button:Export').'</button>'); 318 $oP->add('<div id="export_text_result" style="display:none;">'); 319 $oP->add('<div>'.Dict::S('Core:BulkExport:ExportResult').'</div>'); 320 $oP->add('<textarea id="export_content" style="width:100%;min-height:15em;"></textarea>'); 321 $oP->add('</div>'); 322 323 $sJSParts = json_encode($aFormPartsByFormat); 324 $oP->add_ready_script( 325<<<EOF 326window.aFormParts = $sJSParts; 327$('#format_selector').on('change init', function() { 328 ExportToggleFormat($(this).val()); 329}).trigger('init'); 330 331$('.export-progress-bar').progressbar({ 332 value: 0, 333 change: function() { 334 $('.export-progress-message').text( $(this).progressbar( "value" ) + "%" ); 335 }, 336 complete: function() { 337 $('.export-progress-message').text( '100 %' ); 338 } 339}); 340 341ExportInitButton('#export-btn'); 342 343EOF 344 ); 345 346} 347 348function InteractiveShell($sExpression, $sQueryId, $sFormat, $sFileName, $sMode) 349{ 350 if ($sMode == 'dialog') 351 { 352 $oP = new ajax_page(''); 353 $oP->add('<div id="interactive_export_dlg">'); 354 $sExportBtnLabel = json_encode(Dict::S('UI:Button:Export')); 355 $sJSTitle = json_encode(htmlentities(utils::ReadParam('dialog_title', '', false, 'raw_data'), ENT_QUOTES, 'UTF-8')); 356 $oP->add_ready_script( 357<<<EOF 358 $('#interactive_export_dlg').dialog({ 359 autoOpen: true, 360 modal: true, 361 width: '80%', 362 height: 'auto', 363 maxHeight: $(window).height() - 50, 364 title: $sJSTitle, 365 close: function() { $('#export-form').attr('data-state', 'cancelled'); $(this).remove(); }, 366 buttons: [ 367 {text: $sExportBtnLabel, id: 'export-dlg-submit', click: function() {} } 368 ] 369 }); 370 371 setTimeout(function() { $('#interactive_export_dlg').dialog('option', { position: { my: "center", at: "center", of: window }}); $('#export-btn').hide(); ExportInitButton('#export-dlg-submit'); }, 100); 372EOF 373 ); 374 } 375 else 376 { 377 $oP = new iTopWebPage('iTop Export'); 378 $oP->SetBreadCrumbEntry('ui-tool-export', Dict::S('Menu:ExportMenu'), Dict::S('Menu:ExportMenu+'), '', utils::GetAbsoluteUrlAppRoot().'images/wrench.png'); 379 } 380 381 if ($sExpression === null) 382 { 383 // No expression supplied, let's check if phrasebook entry is given 384 if ($sQueryId !== null) 385 { 386 $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId)); 387 $oSearch->UpdateContextFromUser(); 388 $oQueries = new DBObjectSet($oSearch); 389 if ($oQueries->Count() > 0) 390 { 391 $oQuery = $oQueries->Fetch(); 392 $sExpression = $oQuery->Get('oql'); 393 } 394 else 395 { 396 ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'"); 397 } 398 } 399 else 400 { 401 if (utils::IsModeCLI()) 402 { 403 Usage($oP); 404 ReportErrorAndExit("No expression or query phrasebook identifier supplied."); 405 } 406 else 407 { 408 // form to enter an OQL query or pick a query phrasebook identifier 409 DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat); 410 $oP->output(); 411 exit; 412 } 413 } 414 } 415 416 417 if ($sFormat !== null) 418 { 419 $oExporter = BulkExport::FindExporter($sFormat); 420 if ($oExporter === null) 421 { 422 $aSupportedFormats = BulkExport::FindSupportedFormats(); 423 ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats))); 424 } 425 else 426 { 427 DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat); 428 } 429 } 430 else 431 { 432 DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat); 433 } 434 if ($sMode == 'dialog') 435 { 436 $oP->add('</div>'); 437 } 438 $oP->output(); 439} 440 441/** 442 * Checks the parameters and returns the appropriate exporter (if any) 443 * @param string $sExpression The OQL query to export or null 444 * @param string $sQueryId The entry in the query phrasebook if $sExpression is null 445 * @param string $sFormat The code of export format: csv, pdf, html, xlsx 446 * @return BulkExport 447 */ 448function CheckParameters($sExpression, $sQueryId, $sFormat) 449{ 450 $oExporter = null; 451 452 if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) 453 { 454 ReportErrorAndExit("The user account is not authorized to access the archives"); 455 } 456 457 if (($sExpression === null) && ($sQueryId === null)) 458 { 459 ReportErrorAndUsage("Missing parameter. The parameter 'expression' or 'query' must be specified."); 460 } 461 462 // Either $sExpression or $sQueryId must be specified 463 if ($sExpression === null) 464 { 465 $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId)); 466 $oSearch->UpdateContextFromUser(); 467 $oQueries = new DBObjectSet($oSearch); 468 if ($oQueries->Count() > 0) 469 { 470 $oQuery = $oQueries->Fetch(); 471 $sExpression = $oQuery->Get('oql'); 472 } 473 else 474 { 475 ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'"); 476 } 477 } 478 if ($sFormat === null) 479 { 480 ReportErrorAndUsage("Missing parameter 'format'."); 481 } 482 483 // Check if the supplied query is valid (and all the parameters are supplied 484 try 485 { 486 $oSearch = DBObjectSearch::FromOQL($sExpression); 487 $oSearch->UpdateContextFromUser(); 488 $aArgs = array(); 489 foreach($oSearch->GetQueryParams() as $sParam => $foo) 490 { 491 $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data'); 492 if (!is_null($value)) 493 { 494 $aArgs[$sParam] = $value; 495 } 496 else 497 { 498 throw new MissingQueryArgument("Missing parameter '--arg_$sParam'"); 499 } 500 } 501 $oSearch->SetInternalParams($aArgs); 502 503 $sFormat = utils::ReadParam('format', 'html', true /* Allow CLI */, 'raw_data'); 504 $oExporter = BulkExport::FindExporter($sFormat, $oSearch); 505 if ($oExporter == null) 506 { 507 $aSupportedFormats = BulkExport::FindSupportedFormats(); 508 ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats))); 509 } 510 } 511 catch(MissingQueryArgument $e) 512 { 513 $oSearch = null; 514 ReportErrorAndUsage("Invalid OQL query: '$sExpression'.\n".$e->getMessage()); 515 } 516 catch(OQLException $e) 517 { 518 $oSearch = null; 519 ReportErrorAndExit("Invalid OQL query: '$sExpression'.\n".$e->getMessage()); 520 } 521 catch(Exception $e) 522 { 523 $oSearch = null; 524 ReportErrorAndExit($e->getMessage()); 525 } 526 527 $oExporter->SetFormat($sFormat); 528 $oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE); 529 $oExporter->SetObjectList($oSearch); 530 $oExporter->ReadParameters(); 531 532 return $oExporter; 533} 534 535function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false) 536{ 537 $oExporter->SetHttpHeaders($oP); 538 $exportResult = $oExporter->GetHeader(); 539 $aStatus = array(); 540 do 541 { 542 $exportResult .= $oExporter->GetNextChunk($aStatus); 543 } 544 while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error')); 545 546 if ($aStatus['code'] == 'error') 547 { 548 $oExporter->Cleanup(); 549 ReportErrorAndExit("Export failed: '{$aStatus['message']}'"); 550 } 551 else 552 { 553 $exportResult .= $oExporter->GetFooter(); 554 $sMimeType = $oExporter->GetMimeType(); 555 if (substr($sMimeType, 0, 5) == 'text/') 556 { 557 $sMimeType .= ';charset='.strtolower($oExporter->GetCharacterSet()); 558 } 559 $oP->SetContentType($sMimeType); 560 $oP->SetContentDisposition('attachment', $oExporter->GetDownloadFileName()); 561 $oP->add($exportResult); 562 $oExporter->Cleanup(); 563 } 564} 565 566 567///////////////////////////////////////////////////////////////////////////// 568// 569// Command Line mode 570// 571///////////////////////////////////////////////////////////////////////////// 572if (utils::IsModeCLI()) 573{ 574 try 575 { 576 // Do this before loging, in order to allow setting user credentials from within the file 577 utils::UseParamFile(); 578 } 579 catch(Exception $e) 580 { 581 echo "Error: ".$e->GetMessage()."<br/>\n"; 582 exit(-2); 583 } 584 585 $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data'); 586 $sAuthPwd = utils::ReadParam('auth_pwd', null, true /* Allow CLI */, 'raw_data'); 587 if ($sAuthUser == null) 588 { 589 ReportErrorAndUsage("Missing parameter '--auth_user'"); 590 } 591 if ($sAuthPwd == null) 592 { 593 ReportErrorAndUsage("Missing parameter '--auth_pwd'"); 594 } 595 596 if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) 597 { 598 UserRights::Login($sAuthUser); // Login & set the user's language 599 } 600 else 601 { 602 ReportErrorAndExit("Access restricted or wrong credentials for user '$sAuthUser'"); 603 } 604 605 $sExpression = utils::ReadParam('expression', null, true /* Allow CLI */, 'raw_data'); 606 $sQueryId = utils::ReadParam('query', null, true /* Allow CLI */, 'raw_data'); 607 $bLocalize = (utils::ReadParam('no_localize', 0) != 1); 608 if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) 609 { 610 ReportErrorAndExit("The user account is not authorized to access the archives"); 611 } 612 613 if (($sExpression == null) && ($sQueryId == null)) 614 { 615 ReportErrorAndUsage("Missing parameter. At least one of '--expression' or '--query' must be specified."); 616 } 617 618 if ($sExpression === null) 619 { 620 $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId)); 621 $oSearch->UpdateContextFromUser(); 622 $oQueries = new DBObjectSet($oSearch); 623 if ($oQueries->Count() > 0) 624 { 625 $oQuery = $oQueries->Fetch(); 626 $sExpression = $oQuery->Get('oql'); 627 } 628 else 629 { 630 ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'"); 631 } 632 } 633 try 634 { 635 $oSearch = DBObjectSearch::FromOQL($sExpression); 636 $oSearch->UpdateContextFromUser(); 637 $aArgs = array(); 638 foreach($oSearch->GetQueryParams() as $sParam => $foo) 639 { 640 $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data'); 641 if (!is_null($value)) 642 { 643 $aArgs[$sParam] = $value; 644 } 645 else 646 { 647 throw new MissingQueryArgument("Missing parameter '--arg_$sParam'"); 648 } 649 } 650 $oSearch->SetInternalParams($aArgs); 651 652 $sFormat = utils::ReadParam('format', 'html', true /* Allow CLI */, 'raw_data'); 653 $oExporter = BulkExport::FindExporter($sFormat); 654 if ($oExporter == null) 655 { 656 $aSupportedFormats = BulkExport::FindSupportedFormats(); 657 ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats))); 658 } 659 660 $oExporter->SetFormat($sFormat); 661 $oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE); 662 $oExporter->SetObjectList($oSearch); 663 $oExporter->ReadParameters(); 664 665 $exportResult = $oExporter->GetHeader(); 666 $aStatus = array(); 667 668 do 669 { 670 $exportResult .= $oExporter->GetNextChunk($aStatus); 671 } 672 while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error')); 673 674 if ($aStatus['code'] == 'error') 675 { 676 ReportErrorAndExit("Export failed: '{$aStatus['message']}'"); 677 } 678 else 679 { 680 $exportResult .= $oExporter->GetFooter(); 681 echo $exportResult; 682 } 683 $oExporter->Cleanup(); 684 685 } 686 catch(MissingQueryArgument $e) 687 { 688 ReportErrorAndUsage("Invalid OQL query: '$sExpression'.\n".$e->getMessage()); 689 } 690 catch(OQLException $e) 691 { 692 ReportErrorAndExit("Invalid OQL query: '$sExpression'.\n".$e->getMessage()); 693 } 694 catch(Exception $e) 695 { 696 ReportErrorAndExit($e->getMessage()); 697 } 698 699 exit; 700} 701 702///////////////////////////////////////////////////////////////////////////// 703// 704// Web Server mode 705// 706///////////////////////////////////////////////////////////////////////////// 707 708try 709{ 710 require_once(APPROOT.'/application/loginwebpage.class.inc.php'); 711 712 // Main parameters 713 $sExpression = utils::ReadParam('expression', null, true /* Allow CLI */, 'raw_data'); 714 $sQueryId = utils::ReadParam('query', null, true /* Allow CLI */, 'raw_data'); 715 $sFormat = utils::ReadParam('format', null, true /* Allow CLI */); 716 $sFileName = utils::ReadParam('filename', '', true, 'string'); 717 $bInteractive = utils::ReadParam('interactive', false); 718 $sMode = utils::ReadParam('mode', ''); 719 720 LoginWebPage::DoLogin(); // Check user rights and prompt if needed 721 722 ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker'); 723 724 if ($bInteractive) 725 { 726 InteractiveShell($sExpression, $sQueryId, $sFormat, $sFileName, $sMode); 727 } 728 else 729 { 730 $oExporter = CheckParameters($sExpression, $sQueryId, $sFormat); 731 $sMimeType = $oExporter->GetMimeType(); 732 if ($sMimeType == 'text/html') 733 { 734 // Note: Using NiceWebPage only for HTML export as it includes JS scripts & files, which makes no sense in other export formats. More over, it breaks Excel spreadsheet import. 735 if($oExporter instanceof HTMLBulkExport) 736 { 737 $oP = new NiceWebPage('iTop export'); 738 $oP->add_ready_script("$('table.listResults').tablesorter({widgets: ['MyZebra']});"); 739 } 740 else 741 { 742 $oP = new WebPage('iTop export'); 743 $oP->add_style("table br { mso-data-placement:same-cell; }"); // Trick for Excel: keep line breaks inside the same cell ! 744 } 745 $oP->add_style("body { overflow: auto; }"); 746 } 747 else 748 { 749 $oP = new ajax_page('iTop export'); 750 $oP->SetContentType($oExporter->GetMimeType()); 751 } 752 DoExport($oP, $oExporter, false); 753 $oP->output(); 754 } 755} 756catch (BulkExportMissingParameterException $e) 757{ 758 $oP = new ajax_page('iTop Export'); 759 $oP->add($e->getMessage()); 760 Usage($oP); 761 $oP->output(); 762} 763catch (Exception $e) 764{ 765 $oP = new WebPage('iTop Export'); 766 $oP->add('Error: '.$e->getMessage()); 767 IssueLog::Error($e->getMessage()."\n".$e->getTraceAsString()); 768 $oP->output(); 769}