diff --git a/application/utils.inc.php b/application/utils.inc.php index 0ce86f468..87e4f7dc0 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -2904,7 +2904,8 @@ HTML; if ($sClassNameFilter !== '' && strpos($sPHPClass, $sClassNameFilter) === false) { $bSkipped = true; } - else { + // For some PHP classes we don't have their file path as they are already in memory, so we never filter on their paths + elseif (utils::IsNotNullOrEmptyString($sPHPFile)) { $sPHPFile = self::LocalPath($sPHPFile); if ($sPHPFile !== false) { $sPHPFile = '/'.$sPHPFile; // for regex diff --git a/core/contexttag.class.inc.php b/core/contexttag.class.inc.php index 9660299c2..bd15600bb 100644 --- a/core/contexttag.class.inc.php +++ b/core/contexttag.class.inc.php @@ -3,7 +3,7 @@ // // This file is part of iTop. // -// iTop is free software; you can redistribute it and/or modify +// iTop is free software; you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. @@ -58,6 +58,16 @@ class ContextTag public const TAG_SETUP = 'Setup'; public const TAG_SYNCHRO = 'Synchro'; public const TAG_REST = 'REST/JSON'; + + /** + * @since 3.1.0 N°6047 + */ + public const TAG_IMPORT = 'Import'; + /** + * @since 3.1.0 N°6047 + */ + public const TAG_EXPORT = 'Export'; + /** * @var string * @since 3.1.0 N°3200 @@ -75,7 +85,7 @@ class ContextTag { static::$aStack[] = $sTag; } - + public static function AddContext($sTag) { static::$aStack[] = $sTag; diff --git a/core/log.class.inc.php b/core/log.class.inc.php index 169bc72d9..73f147441 100644 --- a/core/log.class.inc.php +++ b/core/log.class.inc.php @@ -1230,7 +1230,9 @@ class DeprecatedCallsLog extends LogAPI } /** - * @throws \ConfigException + * @since 3.0.1 3.1.0 N°4725 silently handles ConfigException + * @since 3.0.4 3.1.0 N°4725 remove forgotten throw PHPDoc annotation + * * @link https://www.php.net/debug_backtrace * @uses \debug_backtrace() */ diff --git a/sources/Application/UI/Base/Component/Input/Set/Set.php b/sources/Application/UI/Base/Component/Input/Set/Set.php index d145db15c..804444986 100644 --- a/sources/Application/UI/Base/Component/Input/Set/Set.php +++ b/sources/Application/UI/Base/Component/Input/Set/Set.php @@ -205,6 +205,16 @@ class Set extends AbstractInput return $this; } + /** + * HasAddOptionButtonJsOnClick. + * + * @return bool + */ + public function HasAddOptionButtonJsOnClick(): bool + { + return $this->sAddOptionButtonJsOnClick != null; + } + /** * GetAddOptionButtonJsOnClick. * diff --git a/sources/Renderer/Bootstrap/FieldRenderer/BsLinkedSetFieldRenderer.php b/sources/Renderer/Bootstrap/FieldRenderer/BsLinkedSetFieldRenderer.php index 4d89534fa..408744c69 100644 --- a/sources/Renderer/Bootstrap/FieldRenderer/BsLinkedSetFieldRenderer.php +++ b/sources/Renderer/Bootstrap/FieldRenderer/BsLinkedSetFieldRenderer.php @@ -412,23 +412,23 @@ JS <<GetValidatorsAsJson()}, - 'on_validation_callback': function(){ - const aLinkedSetInputs = $('#{$sFieldWrapperId} input,select,textarea', this.element); + 'on_validation_callback': function(oFormField){ + const aLinkedSetInputs = $('#{$sFieldWrapperId} input,select,textarea', oFormField.element); aLinkedSetInputs.each(function(e){ const oInput = $(this); const aInputValidity = oInput[0].validity; - const oFormField = oInput.closest('.form_field_control'); + const oFormFieldControl = oInput.closest('.form_field_control'); if(aInputValidity.valueMissing){ - oFormField.toggleClass('has-error', true); - $('.help-block', oFormField).html('$aErrorMessagesMandatory'); + oFormFieldControl.toggleClass('has-error', true); + $('.help-block', oFormFieldControl).html('$aErrorMessagesMandatory'); } else if(aInputValidity.patternMismatch){ - oFormField.toggleClass('has-error', true); - $('.help-block', oFormField).html('$aErrorMessagesDefault'); + oFormFieldControl.toggleClass('has-error', true); + $('.help-block', oFormFieldControl).html('$aErrorMessagesDefault'); } else{ - oFormField.toggleClass('has-error', false); - $('.help-block', oFormField).empty(); + oFormFieldControl.toggleClass('has-error', false); + $('.help-block', oFormFieldControl).empty(); } }); }, diff --git a/templates/base/components/input/set/layout.ready.js.twig b/templates/base/components/input/set/layout.ready.js.twig index dd19ed2b1..104c6aced 100644 --- a/templates/base/components/input/set/layout.ready.js.twig +++ b/templates/base/components/input/set/layout.ready.js.twig @@ -19,7 +19,7 @@ let oWidget{{ oUIBlock.GetId() }} = $('#{{ oUIBlock.GetId() }}').selectize({ plugins: { {# PLUGIN update operations #} 'combodo_update_operations' : { - initial: {{ oUIBlock.GetInitialValue()|raw }}, + initial: {{ oUIBlock.GetInitialValue()|raw }} }, {# PLUGIN combodo auto position #} 'combodo_auto_position' : { @@ -42,7 +42,7 @@ let oWidget{{ oUIBlock.GetId() }} = $('#{{ oUIBlock.GetId() }}').selectize({ tooltip_links_will_be_deleted_from_x_objects: '{{ 'UI:Links:Bulk:LinkWillBeDeletedFromXObjects'|dict_s }}', tooltip_links_exist_for_all_objects: '{{ 'UI:Links:Bulk:LinkExistForAllObjects'|dict_s }}', tooltip_links_exist_for_one_object: '{{ 'UI:Links:Bulk:LinkExistForOneObject'|dict_s }}', - tooltip_links_exist_for_x_objects: '{{ 'UI:Links:Bulk:LinkExistForXObjects'|dict_s }}', + tooltip_links_exist_for_x_objects: '{{ 'UI:Links:Bulk:LinkExistForXObjects'|dict_s }}' }, {% endif %} {# PLUGIN remove button #} @@ -200,7 +200,7 @@ let oWidget{{ oUIBlock.GetId() }} = $('#{{ oUIBlock.GetId() }}').selectize({ }, {# plugin combodo_add_button #} - {% if oUIBlock.HasAddOptionButton() %} + {% if oUIBlock.HasAddOptionButton() and oUIBlock.HasAddOptionButtonJsOnClick() %} onAdd: function(){ {{ oUIBlock.GetAddOptionButtonJsOnClick()|raw }} diff --git a/tests/manual-visual-tests/Backoffice/RenderAllUiBlocks.php b/tests/manual-visual-tests/Backoffice/RenderAllUiBlocks.php index 769d1f6db..5d2e4defc 100644 --- a/tests/manual-visual-tests/Backoffice/RenderAllUiBlocks.php +++ b/tests/manual-visual-tests/Backoffice/RenderAllUiBlocks.php @@ -334,7 +334,8 @@ $oPage->AddUiBlock(TitleUIBlockFactory::MakeNeutral('Fieldset/field examples', 2 $oDashletFieldset1 = new FieldSet('Fieldset 1'); $oDashletField1 = FieldUIBlockFactory::MakeStandard('Field A'); $oDashletInput1 = InputUIBlockFactory::MakeStandard('text', 'input1', 'Input 1'); -$oDashletField2 = FieldUIBlockFactory::MakeStandard('Field B'); +$oDashletField2 = FieldUIBlockFactory::MakeStandard('Field B (with a description in a tooltip)') + ->SetDescription('Description for the field B'); $oDashletInput2 = InputUIBlockFactory::MakeStandard('text', 'input2', 'Input 2'); $oDashletField3 = FieldUIBlockFactory::MakeStandard('Field C'); $oDashletInput3 = InputUIBlockFactory::MakeStandard('text', 'input3', 'Input 3'); diff --git a/tests/php-unit-tests/ItopDataTestCase.php b/tests/php-unit-tests/ItopDataTestCase.php index d19c0a5cb..657521ef9 100644 --- a/tests/php-unit-tests/ItopDataTestCase.php +++ b/tests/php-unit-tests/ItopDataTestCase.php @@ -48,6 +48,7 @@ use Server; use TagSetFieldData; use Ticket; use URP_UserProfile; +use User; use UserRequest; use VirtualHost; use VirtualMachine; @@ -482,7 +483,7 @@ class ItopDataTestCase extends ItopTestCase /** @var \ormLinkSet $oSet */ $oSet = $oUser->Get('profile_list'); $oSet->AddItem($oUserProfile); - $oUser = $this->updateObject('UserLocal', $oUser->GetKey(), array( + $oUser = $this->updateObject(User::class, $oUser->GetKey(), array( 'profile_list' => $oSet, )); $this->debug("Updated {$oUser->GetName()} ({$oUser->GetKey()})"); diff --git a/webservices/export-v2.php b/webservices/export-v2.php index 4acdcc570..2bd91c143 100644 --- a/webservices/export-v2.php +++ b/webservices/export-v2.php @@ -548,6 +548,12 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false) // Command Line mode // ///////////////////////////////////////////////////////////////////////////// +/// +/** + * @since 3.1.0 N°6047 + */ +$oCtx = new ContextTag(ContextTag::TAG_EXPORT); + if (utils::IsModeCLI()) { SetupUtils::CheckPhpAndExtensionsForCli(new CLIPage('iTop - Export')); @@ -722,4 +728,4 @@ catch (Exception $e) { $oP->add('Error: '.utils::HtmlEntities($e->getMessage())); IssueLog::Error(utils::HtmlEntities($e->getMessage())."\n".$e->getTraceAsString()); $oP->output(); -} \ No newline at end of file +} diff --git a/webservices/export.php b/webservices/export.php index 59ff077ca..454fb9eeb 100644 --- a/webservices/export.php +++ b/webservices/export.php @@ -48,29 +48,33 @@ catch(Exception $e) exit(EXIT_CODE_FATAL); } -if (utils::IsModeCLI()) +/** + * @since 3.1.0 N°6047 + */ +$oCtx = new ContextTag(ContextTag::TAG_EXPORT); +if (utils::IsModeCLI()) { $oP = new CLIPage("iTop - Export"); SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL); $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data'); - $sAuthPwd = utils::ReadParam('auth_pwd', null, true /* Allow CLI */, 'raw_data'); + $sAuthPwd = utils::ReadParam('auth_pwd', null, true /* Allow CLI */, 'raw_data'); - if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) - { - UserRights::Login($sAuthUser); // Login & set the user's language - } - else - { + if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) + { + UserRights::Login($sAuthUser); // Login & set the user's language + } + else + { $oP->p("Access restricted or wrong credentials ('$sAuthUser')"); - $oP->output(); + $oP->output(); exit(EXIT_CODE_ERROR); } -} -else +} +else { - require_once(APPROOT.'/application/loginwebpage.class.inc.php'); - LoginWebPage::DoLogin(); // Check user rights and prompt if needed + require_once(APPROOT.'/application/loginwebpage.class.inc.php'); + LoginWebPage::DoLogin(); // Check user rights and prompt if needed } ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker'); @@ -111,7 +115,7 @@ if (strlen($sExpression) == 0) if (strlen($sFields) == 0) { $sFields = trim($oQuery->Get('fields')); - } + } } } } @@ -279,13 +283,13 @@ if (!empty($sExpression)) } $oP->add($sOutputData); break; - + case 'spreadsheet': $oP = new WebPage("iTop - Export for spreadsheet"); // Integration within MS-Excel web queries + HTTPS + IIS: // MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS - // Then the fix is to force the reset of header values Pragma and Cache-control + // Then the fix is to force the reset of header values Pragma and Cache-control header("Pragma:", true); header("Cache-control:", true); @@ -298,12 +302,12 @@ if (!empty($sExpression)) $oP = new XMLPage("iTop - Export", true /* passthrough */); cmdbAbstractObject::DisplaySetAsXML($oP, $oSet, array('localize_values' => $bLocalize)); break; - + case 'xlsx': $oP = new AjaxPage(''); $oExporter = new ExcelExporter(); $oExporter->SetObjectList($oFilter); - + // Run the export by chunk of 1000 objects to limit memory usage $oExporter->SetChunkSize(1000); do @@ -311,7 +315,7 @@ if (!empty($sExpression)) $aStatus = $oExporter->Run(); // process one chunk } while( ($aStatus['code'] != 'done') && ($aStatus['code'] != 'error')); - + if ($aStatus['code'] == 'done') { $oP->SetContentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); @@ -324,7 +328,7 @@ if (!empty($sExpression)) $oP->add('Error, xlsx export failed: '.$aStatus['message']); } break; - + default: $oP = new WebPage("iTop - Export"); $oP->add("Unsupported format '$sFormat'. Possible values are: html, csv, spreadsheet or xml."); @@ -336,13 +340,13 @@ if (!empty($sExpression)) $oP = new WebPage("iTop - Export"); $oP->p("Error the query can not be executed."); if ($e instanceof CoreException) - { + { $oP->p($e->GetHtmlDesc()); } else { $oP->p($e->getMessage()); - } + } } } if (!$oP) @@ -356,7 +360,7 @@ if (!$oP) else { $oP = new WebPage("iTop - Export"); - } + } $oP->p("General purpose export page."); $oP->p("Parameters:"); $oP->p(" * expression: an OQL expression (URL encoded if needed)"); diff --git a/webservices/import.php b/webservices/import.php index 3eb79a992..cabf629ec 100644 --- a/webservices/import.php +++ b/webservices/import.php @@ -206,6 +206,10 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter) ///////////////////////////////// // Main program +/** + * @since 3.1.0 N°6047 + */ +$oCtx = new ContextTag(ContextTag::TAG_IMPORT); if (utils::IsModeCLI()) { $oP = new CLIPage("iTop - Bulk import");