diff --git a/core/tabularbulkexport.class.inc.php b/core/tabularbulkexport.class.inc.php index 21d61b6b1..05e29d282 100644 --- a/core/tabularbulkexport.class.inc.php +++ b/core/tabularbulkexport.class.inc.php @@ -1,20 +1,10 @@ +/* + * @copyright Copyright (C) 2010-2021 Combodo SARL + * @license http://opensource.org/licenses/AGPL-3.0 + */ + +use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory; /** @@ -47,10 +37,8 @@ abstract class TabularBulkExport extends BulkExport $aSuggestedFields = explode(',', $sSuggestedFields); $sFields = implode(',', $this->SuggestFields($aSuggestedFields)); } - $oP->add(''); - //TODO 3.0 test - return null; + return InputUIBlockFactory::MakeForHidden("fields", $sFields, "tabular_fields"); break; default: diff --git a/js/utils.js b/js/utils.js index b0428385b..ad54692bb 100644 --- a/js/utils.js +++ b/js/utils.js @@ -1,3 +1,8 @@ +/* + * @copyright Copyright (C) 2010-2021 Combodo SARL + * @license http://opensource.org/licenses/AGPL-3.0 + */ + // Some general purpose JS functions for the iTop application /** @@ -12,8 +17,7 @@ function ReloadTruncatedList(divId, sSerializedFilter, sExtraParams) { try { aAjaxRequest = aTruncatedLists[divId]; aAjaxRequest.abort(); - } - catch (e) { + } catch (e) { // Do nothing special, just continue console.log('Uh,uh, exception !'); } @@ -33,8 +37,7 @@ function ReloadTruncatedList(divId, sSerializedFilter, sExtraParams) { if (checkbox) { // There is a checkbox in the first column, don't make it sortable table.tablesorter({headers: {0: {sorter: false}}, widgets: ['myZebra', 'truncatedList']}).tablesorterPager({container: $("#pager")}); // sortable and zebra tables - } - else { + } else { // There is NO checkbox in the first column, all columns are considered sortable table.tablesorter({widgets: ['myZebra', 'truncatedList']}).tablesorterPager({container: $("#pager"), totalRows: 97, filter: sSerializedFilter, extra_params: sExtraParams}); // sortable and zebra tables } @@ -80,8 +83,7 @@ function ReloadBlock(divId, sStyle, sSerializedFilter, sExtraParams) { if (bIsDataTable) { oDataTable.DataTable().clearPipeline(); oDataTable.DataTable().ajax.reload(null, false); - } - else { + } else { $('#'+divId).block(); $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?style='+sStyle, @@ -130,13 +132,10 @@ function UpdateFileName(id, sNewFileName) { $('#'+id).val(sNewFileName); $('#'+id).trigger('validate'); $('#name_'+id).text(sNewFileName); - if(sNewFileName=='') - { + if(sNewFileName=='') { $('#do_remove_'+id).val('1'); $('#remove_attr_' + id).hide(); - } - else - { + } else { $('#do_remove_'+id).val('0'); $('#remove_attr_' + id).show(); } @@ -175,24 +174,19 @@ function ReloadSearchForm(divId, sClassName, sBaseClass, sContext, sTableId, sEx oMap.currentId = divId; oMap._table_id_ = sTableId; oMap.action = sAction; - if(sExtraParams['selection_mode']) - { + if(sExtraParams['selection_mode']) { oMap.selection_mode = sExtraParams['selection_mode']; } - if(sExtraParams['result_list_outer_selector']) - { + if(sExtraParams['result_list_outer_selector']) { oMap.result_list_outer_selector = sExtraParams['result_list_outer_selector']; } - if(sExtraParams['cssCount']) - { + if(sExtraParams['cssCount']) { oMap.css_count = sExtraParams['cssCount']; $(sExtraParams['cssCount']).val(0).trigger('change'); } - if(sExtraParams['table_inner_id']) - { + if(sExtraParams['table_inner_id']) { oMap.table_inner_id = sExtraParams['table_inner_id']; - } - else{ + } else{ oMap.table_inner_id = sTableId; } @@ -216,8 +210,7 @@ function SetUserPreference(sPreferenceCode, sPrefValue, bPersistent) { sPreviousValue = undefined; try { sPreviousValue = oUserPreferences[sPreferenceCode]; - } - catch (err) { + } catch (err) { sPreviousValue = undefined; } oUserPreferences[sPreferenceCode] = sPrefValue; @@ -286,8 +279,7 @@ function ToggleField(value, field_id) { $('#'+field_id).prop('disabled', false); // In case the field is rendered as a div containing several inputs (e.g. RedundancySettings) $('#'+field_id+' :input').prop('disabled', false); - } - else { + } else { $('#'+field_id).prop('disabled', true); // In case the field is rendered as a div containing several inputs (e.g. RedundancySettings) $('#'+field_id+' :input').prop('disabled', true); @@ -303,8 +295,7 @@ function ToggleField(value, field_id) { function BlockField(field_id, bBlocked) { if (bBlocked) { $('#'+field_id).block({message: ' ** disabled ** ', enableValidation : true}); - } - else { + } else { $('#'+field_id).unblock(); } } @@ -319,8 +310,7 @@ function ToggleDurationField(field_id) { for (var i = 0; i < aSubFields.length; i++) { $('#'+field_id+'_'+aSubFields[i]).prop('disabled', true); } - } - else { + } else { for (var i = 0; i < aSubFields.length; i++) { $('#'+field_id+'_'+aSubFields[i]).prop('disabled', false); } @@ -338,7 +328,7 @@ function PropagateCheckBox(bCurrValue, aFieldsList, bCheck) { ToggleField(bCheck, sFieldId); // Cascade propagation - $('#enable_'+sFieldId).trigger('change'); + $('#enable_'+sFieldId).trigger('change'); } } } @@ -350,8 +340,7 @@ function FixTableSorter(table) { if (checkbox) { // There is a checkbox in the first column, don't make it sort-able table.tablesorter({headers: {0: {sorter: false}}, widgets: ['myZebra', 'truncatedList']}); // sort-able and zebra tables - } - else { + } else { // There is NO checkbox in the first column, all columns are considered sort-able table.tablesorter({widgets: ['myZebra', 'truncatedList']}); // sort-able and zebra tables } @@ -435,8 +424,8 @@ function ExportStartExport() { } } }); - $('#export-form').hide(); - $('#export-feedback').show(); + $('#export-form').addClass('ibo-is-hidden'); + $('#export-feedback').removeClass('ibo-is-hidden'); oParams.operation = 'export_build'; oParams.format = $('#export-form :input[name=format]').val(); var sQueryMode = $(':input[name=query_mode]:checked').val(); @@ -446,16 +435,14 @@ function ExportStartExport() { } else { oParams.query = $('#export-form :input[name=query]').val(); } - } - else { + } else { oParams.expression = $('#export-form :input[name=expression]').val(); oParams.query = $('#export-form :input[name=query]').val(); } $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function (data) { if (data == null) { ExportError('Export failed (no data provided), please contact your administrator'); - } - else { + } else { ExportRun(data); } }, 'json') @@ -481,8 +468,7 @@ function ExportRun(data) { var sDataState = $('#export-form').attr('data-state'); if (sDataState == 'cancelled') { oParams.operation = 'export_cancel'; - } - else { + } else { oParams.operation = 'export_build'; } @@ -505,8 +491,7 @@ function ExportRun(data) { $('#export_text_result').show(); //$('#export_text_result .listResults').tableHover(); $('#export_text_result .listResults').tablesorter({widgets: ['myZebra']}); - } - else { + } else { if ($('#export_text_result').closest('ui-dialog').length == 0) { // not inside a dialog box, adjust the height... approximately var jPane = $('#export_text_result').closest('.ui-layout-content'); @@ -552,8 +537,7 @@ function ExportInitButton(sSelector) { } if ($(this).hasClass('ui-button')) { $(this).button('option', 'label', Dict.S('UI:Button:Cancel')); - } - else { + } else { $(this).html(Dict.S('UI:Button:Cancel')); } $('#export-form').attr('data-state', 'running'); @@ -563,8 +547,7 @@ function ExportInitButton(sSelector) { case 'running': if ($(this).hasClass('ui-button')) { $(this).button('disable'); - } - else { + } else { $(this).prop('disabled', true); } $('#export-form').attr('data-state', 'cancelled'); @@ -651,20 +634,17 @@ function IsElementVisibleToTheUser(oDOMElem, bCompletely = false, iThreshold = 0 const oRect = oDOMElem.getBoundingClientRect(), fViewportWidth = window.innerWidth || doc.documentElement.clientWidth, fViewportHeight = window.innerHeight || doc.documentElement.clientHeight, - efp = function (x, y) - { + efp = function (x, y) { return document.elementFromPoint(x, y) }; // Return false if it's not in the viewport if (oRect.right < 0 || oRect.bottom < 0 - || oRect.left > fViewportWidth || oRect.top > fViewportHeight) - { + || oRect.left > fViewportWidth || oRect.top > fViewportHeight) { return false; } - if (bCompletely === true) - { + if (bCompletely === true) { // Return true if ALL of its four corners are visible return ( oDOMElem.contains(efp(oRect.left+iThreshold, oRect.top+iThreshold)) @@ -672,8 +652,7 @@ function IsElementVisibleToTheUser(oDOMElem, bCompletely = false, iThreshold = 0 && oDOMElem.contains(efp(oRect.right-iThreshold, oRect.bottom-iThreshold)) && oDOMElem.contains(efp(oRect.left+iThreshold, oRect.bottom-iThreshold)) ); - } else - { + } else { // Return true if ANY of its four corners are visible return ( oDOMElem.contains(efp(oRect.left, oRect.top)) @@ -698,8 +677,7 @@ else { Dict.S = function (sEntry) { if (sEntry in Dict._entries) { return Dict._entries[sEntry]; - } - else { + } else { return sEntry; } }; @@ -859,8 +837,7 @@ const CombodoJSConsole = { * @returns {boolean} * @internal */ - _Trace: function(sMessage, sLevel = 'log') - { + _Trace: function(sMessage, sLevel = 'log') { // Check if browser has JS console if (!window.console) { return false; @@ -879,8 +856,7 @@ const CombodoJSConsole = { * @param sMessage {string} * @constructor */ - Log: function(sMessage) - { + Log: function(sMessage) { this._Trace(sMessage, 'log'); }, /** @@ -889,8 +865,7 @@ const CombodoJSConsole = { * @param sMessage {string} * @constructor */ - Info: function(sMessage) - { + Info: function(sMessage) { this._Trace(sMessage, 'info'); }, /** @@ -899,8 +874,7 @@ const CombodoJSConsole = { * @param sMessage {string} * @constructor */ - Debug: function(sMessage) - { + Debug: function(sMessage) { this._Trace(sMessage, 'debug'); }, /** @@ -909,8 +883,7 @@ const CombodoJSConsole = { * @param sMessage {string} * @constructor */ - Warn: function(sMessage) - { + Warn: function(sMessage) { this._Trace(sMessage, 'warn'); }, /** @@ -919,8 +892,7 @@ const CombodoJSConsole = { * @param sMessage {string} * @constructor */ - Error: function(sMessage) - { + Error: function(sMessage) { this._Trace(sMessage, 'error'); } } \ No newline at end of file diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index 74d5b8335..f42ba8aed 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -415,6 +415,7 @@ return array( 'DisplayableNode' => $baseDir . '/core/displayablegraph.class.inc.php', 'DisplayableRedundancyNode' => $baseDir . '/core/displayablegraph.class.inc.php', 'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php', + 'DownloadPage' => $baseDir . '/sources/application/WebPage/DownloadPage.php', 'EMail' => $baseDir . '/core/email.class.inc.php', 'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php', 'ErrorPage' => $baseDir . '/sources/application/WebPage/ErrorPage.php', diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index 7f7edc157..a0b56cbd9 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -645,6 +645,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'DisplayableNode' => __DIR__ . '/../..' . '/core/displayablegraph.class.inc.php', 'DisplayableRedundancyNode' => __DIR__ . '/../..' . '/core/displayablegraph.class.inc.php', 'DivisionByZeroError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php', + 'DownloadPage' => __DIR__ . '/../..' . '/sources/application/WebPage/DownloadPage.php', 'EMail' => __DIR__ . '/../..' . '/core/email.class.inc.php', 'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php', 'ErrorPage' => __DIR__ . '/../..' . '/sources/application/WebPage/ErrorPage.php', diff --git a/pages/ajax.render.php b/pages/ajax.render.php index f887986aa..15fd427e0 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -1,20 +1,7 @@ SetContentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); $oPage->SetContentDisposition('attachment', 'export.xlsx'); $sFileContent = ExcelExporter::GetExcelFileFromToken($sToken); - $oPage->add($sFileContent); + $oPage->SetContent($sFileContent); ExcelExporter::CleanupFromToken($sToken); break; @@ -2336,19 +2324,17 @@ EOF case 'export_download': $token = utils::ReadParam('token', null); - if ($token !== null) - { + if ($token !== null) { $oExporter = BulkExport::FindExporterFromToken($token); - if ($oExporter) - { + if ($oExporter) { $sMimeType = $oExporter->GetMimeType(); - if (substr($sMimeType, 0, 5) == 'text/') - { + if (substr($sMimeType, 0, 5) == 'text/') { $sMimeType .= ';charset='.strtolower($oExporter->GetCharacterSet()); } + $oPage = new DownloadPage(); $oPage->SetContentType($sMimeType); $oPage->SetContentDisposition('attachment', $oExporter->GetDownloadFileName()); - $oPage->add(file_get_contents($oExporter->GetTmpFilePath())); + $oPage->SetContent(file_get_contents($oExporter->GetTmpFilePath())); } } break; diff --git a/sources/application/WebPage/DownloadPage.php b/sources/application/WebPage/DownloadPage.php new file mode 100644 index 000000000..799e222a0 --- /dev/null +++ b/sources/application/WebPage/DownloadPage.php @@ -0,0 +1,72 @@ +sContentType = $sContentType; + + return $this; + } + + /** + * Set the content-disposition (mime type) for the page's content + * + * @param $sDisposition string The disposition: 'inline' or 'attachment' + * @param $sFileName string The original name of the file + * + * @return $this + */ + public function SetContentDisposition($sDisposition, $sFileName) + { + $this->sContentDisposition = $sDisposition; + $this->sContentFileName = $sFileName; + + return $this; + } + + /** + * @param string $sContent + * + * @return $this + */ + public function SetContent(string $sContent) + { + $this->sContent = $sContent; + + return $this; + } + + public function output() + { + if (!empty($this->sContentType)) { + header('Content-type: '.$this->sContentType); + } + if (!empty($this->sContentDisposition)) { + header('Content-Disposition: '.$this->sContentDisposition.'; filename="'.$this->sContentFileName.'"'); + } + echo $this->sContent; + } + +}