From eebc29d2bbcae80ade912206a33ba498538f08d4 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 1 Sep 2020 17:17:01 +0200 Subject: [PATCH] =?UTF-8?q?N=C2=B03111=20-=20Fix=20Portal=20export?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit d3b57c3bda70cc91a9ad2224b92fe6333830106c) --- composer.json | 3 +- core/metamodel.class.php | 2 +- .../portal/public/js/export.js | 4 +- lib/composer/autoload_classmap.php | 1 + lib/composer/autoload_static.php | 1 + pages/ajax.render.php | 120 ++---------------- sources/Controller/AjaxRenderController.php | 116 +++++++++++++++++ 7 files changed, 133 insertions(+), 114 deletions(-) create mode 100644 sources/Controller/AjaxRenderController.php diff --git a/composer.json b/composer.json index 8b9321e99..6a4bf3852 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,8 @@ "core", "application", "sources/application", - "sources/Composer" + "sources/Composer", + "sources/Controller" ], "exclude-from-classmap": [ "core/dbobjectsearch.class.php", diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 6bfc5fc90..7872cc6e7 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -4181,7 +4181,7 @@ abstract class MetaModel { $oSearch = DBObjectSearch::FromOQL("SELECT Contact WHERE id = :id"); $oSet = new DBObjectSet($oSearch, array(), array('id' => UserRights::GetContactId())); - $oSet->OptimizeColumnLoad($aCurrentContact); + $oSet->OptimizeColumnLoad(['Contact' => $aCurrentContact]); $oUser = $oSet->fetch(); foreach ($aCurrentContact as $sField) { diff --git a/datamodels/2.x/itop-portal-base/portal/public/js/export.js b/datamodels/2.x/itop-portal-base/portal/public/js/export.js index 5334ac363..eadc664ad 100644 --- a/datamodels/2.x/itop-portal-base/portal/public/js/export.js +++ b/datamodels/2.x/itop-portal-base/portal/public/js/export.js @@ -18,7 +18,7 @@ function ExportStartExport() { var oParams = {}; - oParams.operation = 'export_build'; + oParams.operation = 'export_build_portal'; oParams.format = sFormat; oParams.token = sToken; oParams.start = 1; @@ -56,7 +56,7 @@ function ExportRun(data) { $('#export-close').show(); } else { - oParams.operation = 'export_build'; + oParams.operation = 'export_build_portal'; } $.post(GetAbsoluteUrlAppRoot() + 'pages/ajax.render.php', oParams, function (data) { diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index 8f8e6b200..ec8c6b1ef 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -148,6 +148,7 @@ return array( 'Combodo\\iTop\\Application\\TwigBase\\Twig\\Extension' => $baseDir . '/sources/application/TwigBase/Twig/Extension.php', 'Combodo\\iTop\\Application\\TwigBase\\Twig\\TwigHelper' => $baseDir . '/sources/application/TwigBase/Twig/TwigHelper.php', 'Combodo\\iTop\\Composer\\iTopComposer' => $baseDir . '/sources/Composer/iTopComposer.php', + 'Combodo\\iTop\\Controller\\AjaxRenderController' => $baseDir . '/sources/Controller/AjaxRenderController.php', 'Combodo\\iTop\\DesignDocument' => $baseDir . '/core/designdocument.class.inc.php', 'Combodo\\iTop\\DesignElement' => $baseDir . '/core/designdocument.class.inc.php', 'Combodo\\iTop\\TwigExtension' => $baseDir . '/application/twigextension.class.inc.php', diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index 818c364ae..456be1c42 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -378,6 +378,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'Combodo\\iTop\\Application\\TwigBase\\Twig\\Extension' => __DIR__ . '/../..' . '/sources/application/TwigBase/Twig/Extension.php', 'Combodo\\iTop\\Application\\TwigBase\\Twig\\TwigHelper' => __DIR__ . '/../..' . '/sources/application/TwigBase/Twig/TwigHelper.php', 'Combodo\\iTop\\Composer\\iTopComposer' => __DIR__ . '/../..' . '/sources/Composer/iTopComposer.php', + 'Combodo\\iTop\\Controller\\AjaxRenderController' => __DIR__ . '/../..' . '/sources/Controller/AjaxRenderController.php', 'Combodo\\iTop\\DesignDocument' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php', 'Combodo\\iTop\\DesignElement' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php', 'Combodo\\iTop\\TwigExtension' => __DIR__ . '/../..' . '/application/twigextension.class.inc.php', diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 795c43d13..c50178a25 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -17,6 +17,7 @@ * You should have received a copy of the GNU Affero General Public License */ +use Combodo\iTop\Controller\AjaxRenderController; use Combodo\iTop\Renderer\Console\ConsoleFormRenderer; require_once('../approot.inc.php'); @@ -51,17 +52,17 @@ try // Only allow export functions to portal users switch ($operation) { - case 'export_build': + case 'export_build_portal': case 'export_cancel': case 'export_download': case 'cke_img_upload': case 'cke_upload_and_browse': case 'cke_browse': - $sRequestedPortalId = null; + $sRequestedPortalId = null; // Allowed for all users break; default: - $sRequestedPortalId = 'backoffice'; + $sRequestedPortalId = 'backoffice'; // Allowed only for console users break; } LoginWebPage::DoLoginEx($sRequestedPortalId, false); @@ -79,9 +80,10 @@ try // some operations are also used in the portal though switch ($operation) { - case 'export_build': + case 'export_build_portal': case 'export_download': // do nothing : used in portal (export.js in portal-base) + break; default: ContextTag::AddContext(ContextTag::TAG_CONSOLE); @@ -2444,113 +2446,11 @@ EOF break; case 'export_build': - register_shutdown_function(function () { - $aErr = error_get_last(); - if (($aErr !== null) && ($aErr['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR))) - { - ob_end_clean(); - echo json_encode(array('code' => 'error', 'percentage' => 100, 'message' => Dict::Format('UI:Error_Details', $aErr['message']))); - } - }); - try - { - $token = utils::ReadParam('token', null); - $sTokenForDisplay = utils::HtmlEntities($token); - $aResult = array( // Fallback error, just in case - 'code' => 'error', - 'percentage' => 100, - 'message' => "Export not found for token: '$sTokenForDisplay'", - ); - $data = ''; - if ($token === null) - { - if (!ContextTag::Check('backoffice')) - { - throw new Exception('Missing token'); - } - $sFormat = utils::ReadParam('format', ''); - $sExpression = utils::ReadParam('expression', null, false, 'raw_data'); - $iQueryId = utils::ReadParam('query', null); - if ($sExpression === null) - { - $oQuerySearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $iQueryId)); - $oQuerySearch->UpdateContextFromUser(); - $oQueries = new DBObjectSet($oQuerySearch); - if ($oQueries->Count() > 0) - { - $oQuery = $oQueries->Fetch(); - $sExpression = $oQuery->Get('oql'); - } - else - { - $aResult = array('code' => 'error', 'percentage' => 100, 'message' => "Invalid query phrasebook identifier: '$iQueryId'"); - } - } - if ($sExpression !== null) - { - $oSearch = DBObjectSearch::FromOQL($sExpression); - $oSearch->UpdateContextFromUser(); - $oExporter = BulkExport::FindExporter($sFormat, $oSearch); - $oExporter->SetObjectList($oSearch); - $oExporter->SetFormat($sFormat); - $oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE); - $oExporter->ReadParameters(); - } + AjaxRenderController::ExportBuild($oPage, false); + break; - // First pass, generate the headers - $data .= $oExporter->GetHeader(); - } - else - { - $oExporter = BulkExport::FindExporterFromToken($token); - if (utils::ReadParam('start', 0, false, 'integer') == 1) - { - // From portal, the first call is using a token - $data .= $oExporter->GetHeader(); - } - } - - if ($oExporter) - { - $data .= $oExporter->GetNextChunk($aResult); - if ($aResult['code'] != 'done') - { - $oExporter->AppendToTmpFile($data); - $aResult['token'] = $oExporter->SaveState(); - } - else - { - // Last pass - $data .= $oExporter->GetFooter(); - $oExporter->AppendToTmpFile($data); - $aResult['token'] = $oExporter->SaveState(); - if (substr($oExporter->GetMimeType(), 0, 5) == 'text/') - { - // Result must be encoded in UTF-8 to be passed as part of a JSON structure - $sCharset = $oExporter->GetCharacterSet(); - if (strtoupper($sCharset) != 'UTF-8') - { - $aResult['text_result'] = iconv($sCharset, 'UTF-8', file_get_contents($oExporter->GetTmpFilePath())); - } - else - { - $aResult['text_result'] = file_get_contents($oExporter->GetTmpFilePath()); - } - $aResult['mime_type'] = $oExporter->GetMimeType(); - } - $aResult['message'] = Dict::Format('Core:BulkExport:ClickHereToDownload_FileName', $oExporter->GetDownloadFileName()); - } - } - $oPage->add(json_encode($aResult)); - } catch (BulkExportException $e) - { - $aResult = array('code' => 'error', 'percentage' => 100, 'message' => utils::HtmlEntities($e->GetLocalizedMessage())); - $oPage->add(json_encode($aResult)); - } catch (Exception $e) - { - $aResult = array('code' => 'error', 'percentage' => 100, 'message' => utils::HtmlEntities($e->getMessage())); - $oPage->add(json_encode($aResult)); - } + case 'export_build_portal': + AjaxRenderController::ExportBuild($oPage, true); break; case 'export_download': diff --git a/sources/Controller/AjaxRenderController.php b/sources/Controller/AjaxRenderController.php new file mode 100644 index 000000000..00a26ec1e --- /dev/null +++ b/sources/Controller/AjaxRenderController.php @@ -0,0 +1,116 @@ + 'error', 'percentage' => 100, 'message' => Dict::Format('UI:Error_Details', $aErr['message']))); + } + }); + + try { + $token = utils::ReadParam('token', null); + $sTokenForDisplay = utils::HtmlEntities($token); + $aResult = array( // Fallback error, just in case + 'code' => 'error', + 'percentage' => 100, + 'message' => "Export not found for token: '$sTokenForDisplay'", + ); + $data = ''; + if ($token === null) { + if ($bTokenOnly) { + throw new Exception('Access not allowed'); + } + $sFormat = utils::ReadParam('format', ''); + $sExpression = utils::ReadParam('expression', null, false, 'raw_data'); + $iQueryId = utils::ReadParam('query', null); + if ($sExpression === null) { + $oQuerySearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $iQueryId)); + $oQuerySearch->UpdateContextFromUser(); + $oQueries = new DBObjectSet($oQuerySearch); + if ($oQueries->Count() > 0) { + $oQuery = $oQueries->Fetch(); + $sExpression = $oQuery->Get('oql'); + } else { + $aResult = array('code' => 'error', 'percentage' => 100, 'message' => "Invalid query phrasebook identifier: '$iQueryId'"); + } + } + if ($sExpression !== null) { + $oSearch = DBObjectSearch::FromOQL($sExpression); + $oSearch->UpdateContextFromUser(); + $oExporter = BulkExport::FindExporter($sFormat, $oSearch); + $oExporter->SetObjectList($oSearch); + $oExporter->SetFormat($sFormat); + $oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE); + $oExporter->ReadParameters(); + } + + // First pass, generate the headers + $data .= $oExporter->GetHeader(); + } else { + $oExporter = BulkExport::FindExporterFromToken($token); + if (utils::ReadParam('start', 0, false, 'integer') == 1) { + // From portal, the first call is using a token + $data .= $oExporter->GetHeader(); + } + } + + if ($oExporter) { + $data .= $oExporter->GetNextChunk($aResult); + if ($aResult['code'] != 'done') { + $oExporter->AppendToTmpFile($data); + $aResult['token'] = $oExporter->SaveState(); + } else { + // Last pass + $data .= $oExporter->GetFooter(); + $oExporter->AppendToTmpFile($data); + $aResult['token'] = $oExporter->SaveState(); + if (substr($oExporter->GetMimeType(), 0, 5) == 'text/') { + // Result must be encoded in UTF-8 to be passed as part of a JSON structure + $sCharset = $oExporter->GetCharacterSet(); + if (strtoupper($sCharset) != 'UTF-8') { + $aResult['text_result'] = iconv($sCharset, 'UTF-8', file_get_contents($oExporter->GetTmpFilePath())); + } else { + $aResult['text_result'] = file_get_contents($oExporter->GetTmpFilePath()); + } + $aResult['mime_type'] = $oExporter->GetMimeType(); + } + $aResult['message'] = Dict::Format('Core:BulkExport:ClickHereToDownload_FileName', $oExporter->GetDownloadFileName()); + } + } + $oPage->add(json_encode($aResult)); + } catch (BulkExportException $e) { + $aResult = array('code' => 'error', 'percentage' => 100, 'message' => utils::HtmlEntities($e->GetLocalizedMessage())); + $oPage->add(json_encode($aResult)); + } catch (Exception $e) { + $aResult = array('code' => 'error', 'percentage' => 100, 'message' => utils::HtmlEntities($e->getMessage())); + $oPage->add(json_encode($aResult)); + } + } +}