Compare commits

..

3 Commits

Author SHA1 Message Date
Denis
6a95d3f4af Preparing next version 2024-09-25 10:54:37 +02:00
Anne-Catherine
330e8f48da N°7801 - Erratic behavior on organization filter (#667) 2024-09-12 14:46:04 +02:00
Eric Espie
2147ae9105 N°7802 - MTP from itophub/designer failing in itop 3.2.0 2024-09-11 17:24:08 +02:00
25 changed files with 348 additions and 862 deletions

View File

@@ -706,7 +706,7 @@ class DisplayBlock
if ($bDoSearch)
{
// Keep the table_id identifying this table if we're performing a search
$sTableId = utils::ReadParam('_table_id_', null, false, utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
$sTableId = utils::ReadParam('_table_id_', null, false, 'raw_data');
if ($sTableId != null)
{
$aExtraParams['table_id'] = $sTableId;

View File

@@ -113,11 +113,6 @@ class utils
* @since 2.7.10 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER = 'element_identifier';
/**
* @var string For XML / HTML node id/class selector
* @since 3.1.2 3.2.1
*/
public const ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR = 'element_selector';
/**
* @var string For variables names
* @since 3.0.0
@@ -502,17 +497,8 @@ class utils
}
break;
// For XML / HTML node identifiers
case static::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER:
$retValue = preg_replace('/[^a-zA-Z0-9_-]/', '', $value);
$retValue = filter_var($retValue, FILTER_VALIDATE_REGEXP,
['options' => ['regexp' => '/^[A-Za-z0-9][A-Za-z0-9_-]*$/']]);
break;
// For XML / HTML node id selector
case static::ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR:
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
['options' => ['regexp' => '/^[#\.][A-Za-z0-9][A-Za-z0-9_-]*$/']]);
break;
case static::ENUM_SANITIZATION_FILTER_VARIABLE_NAME:

View File

@@ -2069,7 +2069,7 @@ class AttributeLinkedSet extends AttributeDefinition
public function GetImportColumns()
{
$aColumns = array();
$aColumns[$this->GetCode()] = 'MEDIUMTEXT'.CMDBSource::GetSqlStringColumnDefinition();
$aColumns[$this->GetCode()] = 'TEXT'.CMDBSource::GetSqlStringColumnDefinition();
return $aColumns;
}

View File

@@ -29,7 +29,7 @@ define('ITOP_APPLICATION_SHORT', 'iTop');
*
* @see ITOP_CORE_VERSION to get iTop core version
*/
define('ITOP_VERSION', '3.2.0-dev');
define('ITOP_VERSION', '3.2.1-dev');
define('ITOP_VERSION_NAME', 'Fullmoon');
define('ITOP_REVISION', 'svn');

View File

@@ -1173,7 +1173,7 @@ class DeprecatedCallsLog extends LogAPI
/**
* This will catch a message for all E_DEPRECATED and E_USER_DEPRECATED errors.
* This handler is set in {@see DeprecatedCallsLog::Enable}
* This handler is set in DeprecatedCallsLog::Enable
*
* @param int $errno
* @param string $errstr
@@ -1193,22 +1193,52 @@ class DeprecatedCallsLog extends LogAPI
return false;
}
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 4);
if (isset($aStack[2]['function']) && ($aStack[2]['function'] == 'ForwardToTriggerError')) {
// Let the notice bubble up
return false;
}
if (false === static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD)) {
// returns true so that nothing is thrown!
// returns true so that nothing is throwned !
return true;
}
$aStack = static::StripCallStack($aStack);
$sMessage = "$errstr, called from ".static::SummarizeCallStack($aStack);
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 4);
$iStackDeprecatedMethodLevel = 2; // level 0 = current method, level 1 = @trigger_error, level 2 = method containing the `trigger_error` call (can be either 'trigger_deprecation' or the faulty method), level 3 = In some cases, method containing the 'trigger_deprecation' call
// In case current level is actually a 'trigger_deprecation' call, try to go one level further to get the real deprecated method
if (array_key_exists($iStackDeprecatedMethodLevel, $aStack) && ($aStack[$iStackDeprecatedMethodLevel]['function'] === 'trigger_deprecation') && array_key_exists($iStackDeprecatedMethodLevel + 1, $aStack)) {
$iStackDeprecatedMethodLevel++;
}
$sDeprecatedObject = $aStack[$iStackDeprecatedMethodLevel]['class'];
$sDeprecatedMethod = $aStack[$iStackDeprecatedMethodLevel]['function'];
if (($sDeprecatedObject === __CLASS__) && ($sDeprecatedMethod === 'Log')) {
// We are generating a trigger_error ourselves, we don't want to trace them !
return false;
}
$sCallerFile = $aStack[$iStackDeprecatedMethodLevel]['file'];
$sCallerLine = $aStack[$iStackDeprecatedMethodLevel]['line'];
$sMessage = "Call to {$sDeprecatedObject}::{$sDeprecatedMethod} in {$sCallerFile}#L{$sCallerLine}";
$iStackCallerMethodLevel = $iStackDeprecatedMethodLevel + 1; // level 3 = caller of the deprecated method
if (array_key_exists($iStackCallerMethodLevel, $aStack)) {
$sCallerObject = $aStack[$iStackCallerMethodLevel]['class'] ?? null;
$sCallerMethod = $aStack[$iStackCallerMethodLevel]['function'] ?? null;
$sMessage .= ' (';
if (!is_null($sCallerObject)) {
$sMessage .= "{$sCallerObject}::{$sCallerMethod}";
} else {
$sCallerMethodFile = $aStack[$iStackCallerMethodLevel]['file'];
$sCallerMethodLine = $aStack[$iStackCallerMethodLevel]['line'];
if (!is_null($sCallerMethod)) {
$sMessage .= "call to {$sCallerMethod}() in {$sCallerMethodFile}#L{$sCallerMethodLine}";
} else {
$sMessage .= "{$sCallerMethodFile}#L{$sCallerMethodLine}";
}
}
$sMessage .= ')';
}
if (!empty($errstr)) {
$sMessage .= ' : '.$errstr;
}
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_LIBMETHOD);
static::ForwardToTriggerError($sMessage);
return true;
}
@@ -1234,8 +1264,6 @@ class DeprecatedCallsLog extends LogAPI
}
/**
* Call this helper at the beginning of a deprecated file (in its global scope)
*
* @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
*
@@ -1270,12 +1298,9 @@ class DeprecatedCallsLog extends LogAPI
}
static::Warning($sMessage, static::ENUM_CHANNEL_FILE);
static::ForwardToTriggerError($sMessage);
}
/**
* Call this helper when calling a deprecated extension method
*
* @param string $sImplementationClass Class implementing the deprecated API
* @param string $sDeprecatedApi Class name of the deprecated API
* @param string $sDeprecatedMethod Method name of the deprecated API
@@ -1302,12 +1327,9 @@ class DeprecatedCallsLog extends LogAPI
}
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_API);
static::ForwardToTriggerError($sMessage);
}
/**
* Call this helper within deprecated methods
*
* @param string|null $sAdditionalMessage
*
* @link https://www.php.net/debug_backtrace
@@ -1325,24 +1347,52 @@ class DeprecatedCallsLog extends LogAPI
}
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
$sMessage = self::GetMessageFromStack($aStack);
if (isset($aStack[1]['class'])) {
$sFunctionDesc = $aStack[1]['class'].$aStack[1]['type'].$aStack[1]['function'];
}
else {
$sFunctionDesc = $aStack[1]['function'];
}
$sMessage = "Function $sFunctionDesc() is deprecated";
if (!is_null($sAdditionalMessage)) {
$sMessage .= ': '.$sAdditionalMessage;
$sMessage .= ' : '.$sAdditionalMessage;
}
$sMessage .= '. Caller: '.self::SummarizeCallStack(array_slice($aStack, 1));
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_METHOD);
static::ForwardToTriggerError($sMessage);
}
/**
* @param array $aDebugBacktrace data from {@see debug_backtrace()}
*
* @return string message to print to the log
*/
private static function GetMessageFromStack(array $aDebugBacktrace): string
{
// level 0 = current method
// level 1 = deprecated method, containing the `NotifyDeprecatedPhpMethod` call
$sMessage = 'Call'.self::GetMessageForCurrentStackLevel($aDebugBacktrace[1], " to ");
// level 2 = caller of the deprecated method
if (array_key_exists(2, $aDebugBacktrace)) {
$sMessage .= ' (from ';
$sMessage .= self::GetMessageForCurrentStackLevel($aDebugBacktrace[2]);
$sMessage .= ')';
}
return $sMessage;
}
private static function GetMessageForCurrentStackLevel(array $aCurrentLevelDebugTrace, ?string $sPrefix = ""): string
{
$sMessage = "";
if (array_key_exists('class', $aCurrentLevelDebugTrace)) {
$sDeprecatedObject = $aCurrentLevelDebugTrace['class'];
$sDeprecatedMethod = $aCurrentLevelDebugTrace['function'] ?? "";
$sMessage = "{$sPrefix}{$sDeprecatedObject}::{$sDeprecatedMethod} in ";
}
if (array_key_exists('file', $aCurrentLevelDebugTrace)) {
$sCallerFile = $aCurrentLevelDebugTrace['file'];
$sCallerLine = $aCurrentLevelDebugTrace['line'] ?? "";
$sMessage .= "{$sCallerFile}#L{$sCallerLine}";
}
return $sMessage;
}
/**
@@ -1372,11 +1422,14 @@ class DeprecatedCallsLog extends LogAPI
}
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_ENDPOINT);
static::ForwardToTriggerError($sMessage);
}
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array()): void
{
if (true === utils::IsDevelopmentEnvironment()) {
trigger_error($sMessage, E_USER_DEPRECATED);
}
try {
parent::Log($sLevel, $sMessage, $sChannel, $aContext);
}
@@ -1384,61 +1437,6 @@ class DeprecatedCallsLog extends LogAPI
// nothing much we can do... and we don't want to crash the caller !
}
}
/**
* Strips some elements from the top of the call stack to skip calls that are not relevant to report the deprecated call
* @param array $aCallStack Call stack as returned by {@see debug_backtrace()}
*/
protected static function StripCallStack($aCallStack): array
{
if (!isset($aCallStack[0]['line'])) {
$aCallStack = array_slice($aCallStack, 1);
}
if (isset($aCallStack[1]['function']) && $aCallStack[1]['function'] === 'trigger_deprecation') {
$aCallStack = array_slice($aCallStack, 1);
}
return $aCallStack;
}
protected static function SummarizeCallStack($aCallStack, $bRecurse = true)
{
if (count($aCallStack) == 0) {
return null;
}
$sFileLine = $aCallStack[0]['file'].'#'.$aCallStack[0]['line'];
$sSummary = $sFileLine;
// If possible and meaningful, add the class and method
if (isset($aCallStack[1]['class'])) {
$sSummary = $aCallStack[1]['class'].$aCallStack[1]['type'].$aCallStack[1]['function']." ($sFileLine)";
}
elseif (isset($aCallStack[1]['function'])) {
if (in_array($aCallStack[1]['function'], ['include', 'require', 'include_once', 'require_once'])) {
// No need to show the generic mechanism of inclusion
$bRecurse = false;
}
else {
$sSummary = $aCallStack[1]['function']." ($sFileLine)";
}
}
if ($bRecurse) {
$sUpperSummary = static::SummarizeCallStack(array_slice($aCallStack, 1), false);
if (!is_null($sUpperSummary)) {
$sSummary .= ', itself called from '.$sUpperSummary;
}
}
return $sSummary;
}
private static function ForwardToTriggerError(string $sMessage): void
{
if (true === utils::IsDevelopmentEnvironment()) {
trigger_error($sMessage, E_USER_DEPRECATED);
}
}
}

View File

@@ -1,108 +1,75 @@
# Description
This is a brief description of the how the backoffice theme is structured using both BEM and SASS 7-1 systems and how to use them.
* [7-1 pattern](#7-1-pattern)
* [File structure](#file-structure)
* [Usage](#usage)
* [BEM methodology](#bem-methodology)
* [Principles](#principles)
* [Examples](#examples)
## Description
This is a brief description of the SASS 7-1 system and how to use it.
- [File structure](#file-structure)
- [Usage](#usage)
# 7-1 pattern
## File structure
SCSS files are structured following the [7-1 pattern](https://sass-guidelin.es/#the-7-1-pattern). \
@rveitch made a great summary with the following, which can also be found [here](https://gist.github.com/rveitch/84cea9650092119527bc).
_Note: Folders with an * are customizations we made to the original 7-1 pattern to best fit our needs_
_Note: Folders with an * are customizations we made to the original 7-1 pattern to best fit our needs_
```
css/backoffice/
|
| utils/
| | variables/ # Sass Variables used in Functions, Mixins, Helpers, ...
| | |- colors/
| | | |- _base.scss
| | | |- _base-palette.scss # Base colors used everywhere
| | | |- _lifecycle-palette.scss # Colors used for lifecycle of an object (e.g. representing states such as new, frozen, done, ...), based on the base colors
| | | |- _semantic-palette.scss # Colors used for semantic meaning (e.g. red for errors, green for success, ...), based on the base colors
| | | ...
| | |
| | |- _depression.scss
| | |- _elevation.scss
| | |- _size.scss # Base sizes used everywhere (spacings, ...)
| | |- _spacing.scss
| | |- _typography.scss # Typography sizes, weights, families, ...
| | ...
| |
| | functions/ # Sass Functions
| | |- _color.scss # Color manipulation functions
| |
| | mixins/ # Sass Mixins
| | helpers/ # Class & placeholders helpers
| | _variables.scss # Sass Variables
| | _functions.scss # Sass Functions
| | _mixins.scss # Sass Mixins
| | _helpers.scss # Class & placeholders helpers
|
| vendors/ # Third-party libs, should be either:
| # - Overload of the lib SCSS variables (BEST way, but possible only if the lib exposes them. e.g. Bulma)
| # - Overload of the lib necessary CSS classes only (not great as it duplicates some rules in the browser, which add weight and computation. e.g. dataTables)
| # - Duplicate the lib CSS completly to insert SCSS variables (not great as it will be outdated when updating the lib itself. e.g. jQuery UI)
| | _bulma-variables-overload.scss # Bulma CSS framework
| | _jquery-ui.scss # jQuery UI
| ... # Etc…
| vendors/
| | _bootstrap.scss # Bootstrap
| | _jquery-ui.scss # jQuery UI
| ... # Etc…
|
| base/
| | _reset.scss # Reset/normalize
| | _typography.scss # Typography fonts imports
| ... # Etc…
| | _reset.scss # Reset/normalize
| | _typography.scss # Typography rules
| ... # Etc…
|
| components/ # Components of the UI, each corresponding to a UI block and being usable as a standalone
| | _button.scss
| | _button-group.scss
| | _global-search.scss
| | _quick-create.scss
| ...
| components/
| | _buttons.scss # Buttons
| | _carousel.scss # Carousel
| | _cover.scss # Cover
| | _dropdown.scss # Dropdown
| ... # Etc…
|
| layout/ # Elements of the UI made of several components, making the layout of the app
| | activity-panel/
| | dashboard/
| | object/ # DM object display (details, summary card, ...)
| | tab-container/
| ...
| layout/
| | _navigation.scss # Navigation
| | _grid.scss # Grid system
| | _header.scss # Header
| | _footer.scss # Footer
| | _sidebar.scss # Sidebar
| | _forms.scss # Forms
| ... # Etc…
|
|- *application/ # Elements that are not usable as a standalone (like componants and layouts are) and very application (the backoffice) specific
|- *application/ # Elements that are not usable as a standalone (like componants and layouts are) and very application (the backoffice) specific
| |- display-block
| |- tabular-fields
| ...
|
|- *datamodel/ # SCSS / CSS3 variables and CSS classes for *PHP* classes of the DM that are part of the core (not in a module) and cannot be styled otherwise
| |- _action.scss
|- *datamodel/ # SCSS / CSS3 variables and CSS classes for PHP classes of the DM that are part of the core (not in a module) and cannot be styled otherwise
| |- _user.scss
| ...
|
| pages/ # SCSS / CSS3 variables and CSS classes for HTML elements specific to backoffice pages
| | _base.scss # Base for all backoffice pages
| | _audit.scss # Audit page
| | _csv-import.scss # CSV Import page
| ... # Etc…
| pages/
| | _home.scss # Home specific styles
| | _contact.scss # Contact specific styles
| ... # Etc…
|
|- *blocks-integrations # Specific rules for the integration of a UI block with another one, those kind of rules should NEVER be in the block partial directly
| |- alert/
| | |- _alert-with-blocks.scss # How an alert should be displayed when after another block
| |- button/
| | |- _button-with-button.scss # How a button should be displayed when after another button
| | |- _button-with-button-group.scss # How a button should be displayed when before/after a button group
| |- panel/
| | |- _panel-with-blocks.scss # How a panel should be displayed when after another block
| | |- _panel-within-main-content.scss # How a panel becomes sticky when in the main content
| | |- _panel-within-modal.scss # How a panel becomes sticky when in a modal
| |- _tab-container-within-panel.scss # Changes the negative margins of the datatable so it overlaps the panel's original padding
|- *blocks-integrations # Specific rules for the integration of a block with another one, those kind of rules should never be in the block partial directly
| |- _panel-with-datatable.scss # Changes the negative margins of the datatable so it overlaps the panel's original padding
| ...
|
| themes/
| | _page-banner.scss # ???
| ... # Etc…
| | _theme.scss # Default theme
| | _admin.scss # Admin theme
| ... # Etc…
|
|
|- _fallback.scss # Fallback file, should only contain rules that make standard HTML tags fallback to the style of a custom CSS class
|- _shame.scss # Shame file, should contain all the ugly hacks (https://sass-guidelin.es/#shame-file)
` main.scss # Main Sass file
`- _shame.scss # Shame file, should contain all the ugly hacks (https://sass-guidelin.es/#shame-file)
` main.scss # Main Sass file
```
## Usage
@@ -117,58 +84,7 @@ To avoid common errors, files should be imported in the final file in the follow
- Components
- Layout
- \*Application
- \*Datamodel
- Pages
- \*Block integrations
- Themes
- Shame file
# BEM methodology
## Principles
[BEM is a methodology](https://getbem.com/) that helps you to create reusable components and code sharing in frontend development. \
The main idea is to use discriminant classes instead of nested basic selectors for 2 main reasons:
* It's easier to understand the purpose of a specific class when seeing it in the HTML markup of the SCSS file
* It's easier to override a specific class when needed as you don't need to use a selector at least as precise/complex as the one you want to override
In our implementation, we start with the code of the UI block, followed by the sub-element, then the property or modifier. Separation is made of `--` instead of `__`.
## Examples
### Classes and CSS properties example
```scss
// SCSS variables:
// - For CSS properties: CSS class, followed by CSS property
$ibo-button--padding-y: 6px !default;
$ibo-button--padding-x: 9px !default;
$ibo-button--border: 0 !default;
$ibo-button--border-radius: $ibo-border-radius-400 !default;
$ibo-button--box-shadow-bottom: 0px 2px 0px !default;
$ibo-button--box-shadow-top: inset 0px 2px 0px !default;
$ibo-button--label--margin-left: $ibo-spacing-200 !default;
// CSS classes:
.ibo-button {
padding: $ibo-button--padding-y $ibo-button--padding-x;
border: $ibo-button--border;
border-radius: $ibo-button--border-radius;
}
.ibo-button--label {
margin-left: $ibo-button--label--margin-left;
}
```
### States example
```scss
// SCSS variables:
// Same rule as before, but with a `--is-` or `--on--` suffix
$ibo-quick-create--input--padding: $ibo-spacing-0 default;
$ibo-quick-create--input--padding-x--is-opened: $ibo-spacing-300 !default;
$ibo-quick-create--input--padding-y--is-opened: $ibo-spacing-300 !default;
$ibo-quick-create--input--width: $ibo-size-0 !default;
$ibo-quick-create--input--width--is-opened: 245px !default;
$ibo-quick-create--input--background-color: $ibo-color-white-100 !default;
$ibo-quick-create--input--background-color--on-hover: $ibo-color-grey-200 !default;
```
- Shame file

View File

@@ -6,5 +6,5 @@
$ibo-depression-100: inset 0 1px 1px 0 rgba(0, 0, 0, 0.15) !default;
:root{
--ibo-depression-100: #{$ibo-depression-100};
--ibo-elevation-100: #{$ibo-depression-100};
}

View File

@@ -64,25 +64,6 @@ class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExt
return $result;
}
/**
* @param cmdbAbstractObject $oObject
*
* @return bool
* @since 3.2.1 N°7534
*/
public static function IsAttachmentAllowedForObject(cmdbAbstractObject $oObject) : bool
{
$aAllowedClasses = MetaModel::GetModuleSetting('itop-attachments', 'allowed_classes', array('Ticket'));
foreach ($aAllowedClasses as $sAllowedClass)
{
if ($oObject instanceof $sAllowedClass)
{
return true;
}
}
return false;
}
/**
* Returns the max. file upload size allowed as a dictionary entry
*

View File

@@ -23,7 +23,7 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Application\WebPage\AjaxPage;
use Combodo\iTop\Application\WebPage\JsonPage;
require_once(APPROOT.'application/utils.inc.php');
require_once(APPROOT.'core/log.class.inc.php');
@@ -36,7 +36,6 @@ require_once(APPROOT.'core/dict.class.inc.php');
require_once(APPROOT.'setup/xmldataloader.class.inc.php');
require_once(__DIR__.'/hubruntimeenvironment.class.inc.php');
/**
* Overload of DBBackup to handle logging
*/
@@ -80,11 +79,11 @@ function DoBackup($sTargetFile)
// Make sure the target directory exists
$sBackupDir = dirname($sTargetFile);
SetupUtils::builddir($sBackupDir);
$oBackup = new DBBackupWithErrorReporting();
$oBackup->SetMySQLBinDir(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''));
$sSourceConfigFile = APPCONF.utils::GetCurrentEnvironment().'/'.ITOP_CONFIG_FILE;
$oMutex = new iTopMutex('backup.'.utils::GetCurrentEnvironment());
$oMutex->Lock();
try
@@ -102,7 +101,7 @@ function DoBackup($sTargetFile)
/**
* Outputs the status of the current ajax execution (as a JSON structure)
*
*
* @param string $sMessage
* @param bool $bSuccess
* @param number $iErrorCode
@@ -111,20 +110,21 @@ function DoBackup($sTargetFile)
*/
function ReportStatus($sMessage, $bSuccess, $iErrorCode = 0, $aMoreFields = array())
{
$oPage = new AjaxPage("");
$oPage->SetContentType('application/json');
// Do not use AjaxPage during setup phases, because it uses InterfaceDiscovery in Twig compilation
$oPage = new JsonPage();
$aResult = array(
'code' => $iErrorCode,
'message' => $sMessage,
'fields' => $aMoreFields
);
$oPage->add(json_encode($aResult));
$oPage->SetData($aResult);
$oPage->SetOutputDataOnly(true);
$oPage->output();
}
/**
* Helper to output the status of a successful execution
*
*
* @param string $sMessage
* @param array $aMoreFields
* Extra fields to pass to the caller, if needed
@@ -136,7 +136,7 @@ function ReportSuccess($sMessage, $aMoreFields = array())
/**
* Helper to output the status of a failed execution
*
*
* @param string $sMessage
* @param number $iErrorCode
* @param array $aMoreFields
@@ -157,21 +157,21 @@ try
SetupUtils::ExitMaintenanceMode(false); // Reset maintenance mode in case of problem
utils::PushArchiveMode(false);
ini_set('max_execution_time', max(3600, ini_get('max_execution_time'))); // Under Windows SQL/backup operations are part of the PHP timeout and require extra time
ini_set('display_errors', 1); // Make sure that fatal errors remain visible from the end-user
// Most of the ajax calls are done without the MetaModel being loaded
// Therefore, the language must be passed as an argument,
// and the dictionnaries be loaded here
$sLanguage = utils::ReadParam('language', '');
if ($sLanguage!='')
{
foreach (glob(APPROOT.'env-'. utils::GetCurrentEnvironment(). '/dictionaries/*.dict.php') as $sFilePath)
foreach (glob(APPROOT.'env-production/dictionaries/*.dict.php') as $sFilePath)
{
require_once ($sFilePath);
}
$aLanguages = Dict::GetLanguages();
if (array_key_exists($sLanguage, $aLanguages))
{
@@ -212,7 +212,7 @@ try
}
}
break;
case 'do_backup':
require_once (APPROOT.'/application/startup.inc.php');
require_once (APPROOT.'/application/loginwebpage.class.inc.php');
@@ -255,9 +255,8 @@ try
ReportError($e->getMessage(), $e->getCode());
}
break;
case 'compile':
SetupLog::Info('Deployment starts...');
$sAuthent = utils::ReadParam('authent', '', false, 'raw_data');
if (!file_exists(APPROOT.'data/hub/compile_authent') || $sAuthent !== file_get_contents(APPROOT.'data/hub/compile_authent'))
@@ -268,13 +267,13 @@ try
$aSelectedExtensionCodes = utils::ReadParam('extension_codes', array());
$aSelectedExtensionDirs = utils::ReadParam('extension_dirs', array());
$oRuntimeEnv = new HubRunTimeEnvironment(utils::GetCurrentEnvironment(), false); // use a temp environment: production-build
$oRuntimeEnv = new HubRunTimeEnvironment('production', false); // use a temp environment: production-build
$oRuntimeEnv->MoveSelectedExtensions(APPROOT.'/data/downloaded-extensions/', $aSelectedExtensionDirs);
$oConfig = new Config(APPCONF.utils::GetCurrentEnvironment().'/'.ITOP_CONFIG_FILE);
$oConfig = new Config(APPCONF.'production/'.ITOP_CONFIG_FILE);
if ($oConfig->Get('demo_mode')) throw new Exception('Sorry the installation of extensions is not allowed in demo mode');
$aSelectModules = $oRuntimeEnv->CompileFrom(utils::GetCurrentEnvironment(), false); // WARNING symlinks does not seem to be compatible with manual Commit
$aSelectModules = $oRuntimeEnv->CompileFrom('production', false); // WARNING symlinks does not seem to be compatible with manual Commit
$oRuntimeEnv->UpdateIncludes($oConfig);
@@ -293,11 +292,11 @@ try
SetupLog::Info('Compilation completed...');
ReportSuccess('Ok'); // No access to Dict::S here
break;
case 'move_to_production':
// Second step: update the schema and the data
// Everything happening below is based on env-production
$oRuntimeEnv = new RunTimeEnvironment(utils::GetCurrentEnvironment(), true);
$oRuntimeEnv = new RunTimeEnvironment('production', true);
try
{
@@ -309,7 +308,7 @@ try
}
unlink(APPROOT.'data/hub/compile_authent');
// Load the "production" config file to clone & update it
$oConfig = new Config(APPCONF.utils::GetCurrentEnvironment().'/'.ITOP_CONFIG_FILE);
$oConfig = new Config(APPCONF.'production/'.ITOP_CONFIG_FILE);
SetupUtils::EnterReadOnlyMode($oConfig);
$oRuntimeEnv->InitDataModel($oConfig, true /* model only */);
@@ -377,7 +376,7 @@ try
unlink(APPROOT.'data/hub/compile_authent');
}
// Note: at this point, the dictionnary is not necessarily loaded
SetupLog::Error(get_class($e).': '.Dict::S('iTopHub:ConfigurationSafelyReverted'), null, [$e->getMessage(), $e->getTraceAsString()]);
SetupLog::Error(get_class($e).': '.Dict::S('iTopHub:ConfigurationSafelyReverted')."\n".$e->getMessage());
SetupLog::Error('Debug trace: '.$e->getTraceAsString());
ReportError($e->getMessage(), $e->getCode());
}
@@ -386,17 +385,17 @@ try
SetupUtils::ExitReadOnlyMode();
}
break;
default:
ReportError("Invalid operation: '$sOperation'", -1);
}
}
catch (Exception $e)
{
SetupLog::Error(get_class($e).': '.Dict::S('iTopHub:ConfigurationSafelyReverted'), null, [$e->getMessage(), $e->getTraceAsString()]);
SetupLog::Error(get_class($e).': '.Dict::S('iTopHub:ConfigurationSafelyReverted')."\n".$e->getMessage());
SetupLog::Error('Debug trace: '.$e->getTraceAsString());
utils::PopArchiveMode();
ReportError($e->getMessage(), $e->getCode());
}

View File

@@ -1,6 +1,6 @@
<?php
class HubRunTimeEnvironment extends RunTimeEnvironment
{
{
/**
* Constructor
* @param string $sEnvironment
@@ -9,7 +9,7 @@ class HubRunTimeEnvironment extends RunTimeEnvironment
public function __construct($sEnvironment = 'production', $bAutoCommit = true)
{
parent::__construct($sEnvironment, $bAutoCommit);
if ($sEnvironment != $this->sTargetEnv)
{
if (is_dir(APPROOT.'/env-'.$this->sTargetEnv))
@@ -23,7 +23,7 @@ class HubRunTimeEnvironment extends RunTimeEnvironment
SetupUtils::copydir(APPROOT.'/data/'.$sEnvironment.'-modules', APPROOT.'/data/'.$this->sTargetEnv.'-modules');
}
}
/**
* Update the includes for the target environment
* @param Config $oConfig
@@ -32,7 +32,7 @@ class HubRunTimeEnvironment extends RunTimeEnvironment
{
$oConfig->UpdateIncludes('env-'.$this->sTargetEnv); // TargetEnv != FinalEnv
}
/**
* Move an extension (path to folder of this extension) to the target environment
* @param string $sExtensionDirectory The folder of the extension
@@ -45,7 +45,7 @@ class HubRunTimeEnvironment extends RunTimeEnvironment
if (!mkdir(APPROOT.'/data/'.$this->sTargetEnv.'-modules')) throw new Exception("ERROR: failed to create directory:'".(APPROOT.'/data/'.$this->sTargetEnv.'-modules')."'");
}
$sDestinationPath = APPROOT.'/data/'.$this->sTargetEnv.'-modules/';
// Make sure that the destination directory of the extension does not already exist
if (is_dir($sDestinationPath.basename($sExtensionDirectory)))
{
@@ -54,7 +54,7 @@ class HubRunTimeEnvironment extends RunTimeEnvironment
}
if (!rename($sExtensionDirectory, $sDestinationPath.basename($sExtensionDirectory))) throw new Exception("ERROR: failed move directory:'$sExtensionDirectory' to '".$sDestinationPath.basename($sExtensionDirectory)."'");
}
/**
* Move the selected extensions located in the given directory in data/<target-env>-modules
* @param string $sDownloadedExtensionsDir The directory to scan

View File

@@ -20,7 +20,7 @@ function DisplayStatus(WebPage $oPage)
if (is_dir($sPath)) {
$aExtraDirs[] = $sPath; // Also read the extra downloaded-modules directory
}
$oExtensionsMap = new iTopExtensionsMap(utils::GetCurrentEnvironment(), true, $aExtraDirs);
$oExtensionsMap = new iTopExtensionsMap('production', true, $aExtraDirs);
$oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig());
foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) {
@@ -156,7 +156,7 @@ function DoInstall(WebPage $oPage)
if (is_dir($sPath)) {
$aExtraDirs[] = $sPath; // Also read the extra downloaded-modules directory
}
$oExtensionsMap = new iTopExtensionsMap(utils::GetCurrentEnvironment(), true, $aExtraDirs);
$oExtensionsMap = new iTopExtensionsMap('production', true, $aExtraDirs);
$oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig());
foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) {

View File

@@ -97,12 +97,6 @@ p_object_attachment_add:
defaults:
_controller: 'Combodo\iTop\Portal\Controller\ObjectController::AttachmentAction'
p_object_attachment_display:
path: '/object/attachment/display/{sAttachmentId}'
defaults:
_controller: 'Combodo\iTop\Portal\Controller\ObjectController::AttachmentAction'
sOperation: 'display'
p_object_attachment_download:
path: '/object/attachment/download/{sAttachmentId}'
defaults:

View File

@@ -31,7 +31,7 @@ $.fn.modal.Constructor.prototype.enforceFocus = function () {
var $parent = $(e.target.parentNode);
if ($modalElement[0] !== e.target && !$modalElement.has(e.target).length &&
!$parent.hasClass('ck-input')) {
$(e.target.activeElement).focus();
e.target.focus()
}
})
};

View File

@@ -1241,19 +1241,6 @@ class ObjectController extends BrickController
break;
case 'display':
// Preparing redirection
// - Route
$aRouteParams = array(
'sObjectClass' => 'Attachment',
'sObjectId' => $this->oRequestManipulatorHelper->ReadParam('sAttachmentId', null),
'sObjectField' => 'contents',
);
$oResponse = $this->ForwardToRoute('p_object_document_display', $aRouteParams, $oRequest->query->all());
break;
default:
throw new HttpException(Response::HTTP_FORBIDDEN, Dict::S('Error:HTTP:400'));
break;

View File

@@ -94,8 +94,6 @@ class ObjectFormManager extends FormManager
*/
private $oFormHandlerHelper;
/** @var array $aPlugins plugins data */
private array $aPlugins = array();
/**
* @param string|array $formManagerData value of the formmanager_data portal parameter, either JSON or object
@@ -504,11 +502,6 @@ class ObjectFormManager extends FormManager
{
$aFieldsExtraData[$sFieldId]['opened'] = true;
}
// Checking if the field is handled by a plugin
if ($oFieldNode->hasAttribute('data-field-plugin'))
{
$aFieldsExtraData[$sFieldId]['plugin'] = $oFieldNode->getAttribute('data-field-plugin');
}
// Checking if field allows to ignore scope (For linked set)
if ($oFieldNode->hasAttribute('data-field-ignore-scopes') && ($oFieldNode->getAttribute('data-field-ignore-scopes') === 'true'))
{
@@ -658,20 +651,6 @@ class ObjectFormManager extends FormManager
// Building the form
foreach ($aFieldsAtts as $sAttCode => $iFieldFlags)
{
// handle plugins fields
if(array_key_exists($sAttCode, $aFieldsExtraData)
&& array_key_exists('plugin', $aFieldsExtraData[$sAttCode])){
$sPluginName = $aFieldsExtraData[$sAttCode]['plugin'];
switch($sPluginName){
case AttachmentPlugIn::class:
$this->AddAttachmentField($oForm, $sAttCode, $aFieldsExtraData);
break;
default:
throw new Exception('Unknown plugin ' . $sPluginName);
}
continue;
}
$oAttDef = MetaModel::GetAttributeDef(get_class($this->oObject), $sAttCode);
/** @var Field $oField */
@@ -1011,12 +990,49 @@ class ObjectFormManager extends FormManager
}
}
// fallback Checking if the instance has attachments
// (in case attachment is not explicitly declared in layout)
if (class_exists('Attachment') && class_exists('AttachmentPlugIn')
&& !$this->IsPluginInitialized(AttachmentPlugIn::class)
&& AttachmentPlugIn::IsAttachmentAllowedForObject($this->oObject)){
$this->AddAttachmentField($oForm, 'attachments_plugin', $aFieldsExtraData);
// Checking if the instance has attachments
if (class_exists('Attachment') && class_exists('AttachmentPlugIn'))
{
// Checking if the object is allowed for attachments
$bClassAllowed = false;
$aAllowedClasses = MetaModel::GetModuleSetting('itop-attachments', 'allowed_classes', array('Ticket'));
foreach ($aAllowedClasses as $sAllowedClass)
{
if ($this->oObject instanceof $sAllowedClass)
{
$bClassAllowed = true;
break;
}
}
// Adding attachment field
if ($bClassAllowed)
{
// set id to a unique key - avoid collisions with another attribute that could exist with the name 'attachments'
$oField = new FileUploadField('attachments_plugin');
$oField->SetLabel(Dict::S('Portal:Attachments'))
->SetUploadEndpoint($this->oFormHandlerHelper->GetUrlGenerator()->generate('p_object_attachment_add'))
->SetDownloadEndpoint($this->oFormHandlerHelper->GetUrlGenerator()->generate('p_object_attachment_download',
array('sAttachmentId' => '-sAttachmentId-')))
->SetTransactionId($oForm->GetTransactionId())
->SetAllowDelete($this->oFormHandlerHelper->getCombodoPortalConf()['properties']['attachments']['allow_delete'])
->SetObject($this->oObject);
// Checking if we can edit attachments in the current state
if (($this->sMode === static::ENUM_MODE_VIEW)
|| AttachmentPlugIn::IsReadonlyState($this->oObject, $this->oObject->GetState(),
AttachmentPlugIn::ENUM_GUI_PORTALS) === true
|| $oForm->GetEditableFieldCount(true) === 0)
{
$oField->SetReadOnly(true);
}
// Adding attachements field in transition only if it is editable
if (!$this->IsTransitionForm() || ($this->IsTransitionForm() && !$oField->GetReadOnly()))
{
$oForm->AddField($oField);
}
}
}
$oForm->Finalize();
@@ -1024,78 +1040,6 @@ class ObjectFormManager extends FormManager
$this->oRenderer->SetForm($this->oForm);
}
/**
* IsPluginInitialized.
*
* @param string $sPluginName
*
* @return bool
*/
private function IsPluginInitialized(string $sPluginName) : bool
{
return array_key_exists($sPluginName, $this->aPlugins);
}
/**
* AddAttachmentField.
*
* @param $oForm
* @param $sId
* @param $aFieldsExtraData
*
* @throws \Exception
*/
private function AddAttachmentField($oForm, $sId, $aFieldsExtraData) : void
{
// only one instance allowed
if($this->IsPluginInitialized(AttachmentPlugIn::class)){
throw new Exception("Unable to process field `$sId`, AttachmentPlugIn has already been initialized with field `" . $this->aPlugins[AttachmentPlugIn::class]['field']->GetId() . '`');
}
// not allowed for object class
if(!AttachmentPlugIn::IsAttachmentAllowedForObject($this->oObject)){
throw new Exception("Unable to process field `$sId`, AttachmentPlugIn is not allowed for class `" . $this->oObject::class . '`');
}
// set id to a unique key - avoid collisions with another attribute that could exist with the name 'attachments'
$oField = new FileUploadField($sId);
$oField->SetLabel(Dict::S('Portal:Attachments'))
->SetUploadEndpoint($this->oFormHandlerHelper->GetUrlGenerator()->generate('p_object_attachment_add'))
->SetDownloadEndpoint($this->oFormHandlerHelper->GetUrlGenerator()->generate('p_object_attachment_download',
array('sAttachmentId' => '-sAttachmentId-')))
->SetDisplayEndpoint($this->oFormHandlerHelper->GetUrlGenerator()->generate('p_object_attachment_display',
array('sAttachmentId' => '-sAttachmentId-')))
->SetTransactionId($oForm->GetTransactionId())
->SetAllowDelete($this->oFormHandlerHelper->getCombodoPortalConf()['properties']['attachments']['allow_delete'])
->SetObject($this->oObject);
// Checking if we can edit attachments in the current state
$oObjectFormManager = $this;
$oField->SetOnFinalizeCallback(function() use ($oObjectFormManager, $oForm, $oField){
if (($oObjectFormManager->sMode === static::ENUM_MODE_VIEW)
|| AttachmentPlugIn::IsReadonlyState($oObjectFormManager->oObject, $oObjectFormManager->oObject->GetState(),
AttachmentPlugIn::ENUM_GUI_PORTALS) === true
|| $oForm->GetEditableFieldCount(true) === 0)
{
$oField->SetReadOnly(true);
}
});
if (array_key_exists($sId, $aFieldsExtraData) && array_key_exists('opened', $aFieldsExtraData[$sId])){
$oField->SetDisplayOpened(true);
}
// Adding attachments field in transition only if it is editable
if (!$this->IsTransitionForm() || !$oField->GetReadOnly()){
$oForm->AddField($oField);
}
// save plugin data
$this->aPlugins[AttachmentPlugIn::class] = [
'field' => $oField,
];
}
/**
* @inheritDoc
*

View File

@@ -782,12 +782,12 @@ try
$sClass = utils::ReadParam('className', '', false, 'class');
$sRootClass = utils::ReadParam('baseClass', '', false, 'class');
$currentId = utils::ReadParam('currentId', '');
$sTableId = utils::ReadParam('_table_id_', null, false, utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
$sTableId = utils::ReadParam('_table_id_', null, false, 'raw_data');
$sAction = utils::ReadParam('action', '');
$sSelectionMode = utils::ReadParam('selection_mode');
$sResultListOuterSelector = utils::ReadParam('result_list_outer_selector', null,false, utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER); // actually an Id not a selector
$scssCount = utils::ReadParam('css_count', null,false,utils::ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR);
$sTableInnerId = utils::ReadParam('table_inner_id', null,false, utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
$sSelectionMode = utils::ReadParam('selection_mode', null, false, 'raw_data');
$sResultListOuterSelector = utils::ReadParam('result_list_outer_selector', null, false, 'raw_data');
$scssCount = utils::ReadParam('css_count', null, false, 'raw_data');
$sTableInnerId = utils::ReadParam('table_inner_id', $sTableId, false, 'raw_data');
$oFilter = new DBObjectSearch($sClass);
$oSet = new CMDBObjectSet($oFilter);

View File

@@ -27,11 +27,7 @@ require_once(APPROOT.'core/metamodel.class.php');
IssueLog::Trace('----- Request: '.utils::GetRequestUri(), LogChannels::WEB_REQUEST);
utils::InitTimeZone();
set_error_handler(function ($errno, $errstr) {
$e = new \Exception("");
\SetupLog::Error("Catching", null, [$e->getMessage(), $e->getTraceAsString()]);
echo $e->getMessage() . '<BR>' .$e->getTraceAsString();
}, E_WARNING);
/**
* @param string $sPagePath full path (if symlink, it will be resolved)

View File

@@ -2259,7 +2259,7 @@ EOF
$this->CompileCommonProperty('is_null_allowed', $oField, $aParameters, $sModuleRelativeDir, false);
$this->CompileCommonProperty('default_value', $oField, $aParameters, $sModuleRelativeDir, '');
$this->CompileCommonProperty('allowed_values', $oField, $aParameters, $sModuleRelativeDir);
$aParameters['class_category'] = $this->GetPropString($oField, 'class_category', '');
$aParameters['class_category'] = $this->GetPropString($oField, 'class_category');
$aParameters['more_values'] = $this->GetPropString($oField, 'more_values', '');
$aParameters['depends_on'] = $sDependencies;
}else {

View File

@@ -383,13 +383,13 @@ class NavigationMenu extends UIBlock implements iKeyboardShortcut
}
$sPageJS = $oPage->GetJS();
$oPage->add_ready_script('$(\'[data-role="ibo-navigation-menu--silo-selection--form"] #org_id\').on(\'extkeychange\', function() { $(\'[data-role="ibo-navigation-menu--silo-selection--form"]\').submit(); } );');
$oPage->add_ready_script('$(\'[data-role="ibo-navigation-menu--silo-selection--form"] #label_org_id\').on(\'click\', function() { if ($(\'[data-role="ibo-navigation-menu--silo-selection--form"] #org_id\').val() == \'\') { $(this).val(\'\'); } } );');
$sPageReadyJS = $oPage->GetReadyJS();
$this->aSiloSelection['js'] =
<<<JS
$sPageJS
$sPageReadyJS
$('[data-role="ibo-navigation-menu--silo-selection--form"] #org_id').on('extkeychange', function() { $('[data-role="ibo-navigation-menu--silo-selection--form"]').submit(); } )
$('[data-role="ibo-navigation-menu--silo-selection--form"] #label_org_id').on('click', function() { if ($('[data-role="ibo-navigation-menu--silo-selection--form"] #org_id').val() == '') { $(this).val(''); } } );
$sAddClearButton
JS;
}

View File

@@ -919,7 +919,7 @@ class WebPage implements Page
// Check if URI is absolute ("://" do allow any protocol), otherwise warn that it's a deprecated behavior
if (false === stripos($sLinkedScriptAbsUrl, "://")) {
IssueLog::Debug("Linked script added to page with a non absolute URL, it may lead to it not being loaded and causing javascript errors. See alternatives WebPage::LinkScriptFromXXX", null, [
IssueLog::Warning("Linked script added to page via deprecated API with a non absolute URL, it may lead to it not being loaded and causing javascript errors.", null, [
"linked_script_url" => $sLinkedScriptAbsUrl,
"request_uri" => $_SERVER['REQUEST_URI'] ?? '' /* CLI */,
]);
@@ -1188,7 +1188,7 @@ JS;
// Check if URI is absolute ("://" do allow any protocol), otherwise warn that it's a deprecated behavior
if (false === stripos($sLinkedStylesheet, "://")) {
IssueLog::Debug("Linked stylesheet added to page with a non absolute URL, it may lead to it not being loaded and causing visual glitches. See alternatives WebPage::LinkStylesheetFromXXX", null, [
IssueLog::Warning("Linked stylesheet added to page via deprecated API with a non absolute URL, it may lead to it not being loaded and causing visual glitches.", null, [
"linked_stylesheet_url" => $sLinkedStylesheet,
"request_uri" => $_SERVER['REQUEST_URI'] ?? '' /* CLI */,
]);

View File

@@ -30,11 +30,6 @@ class FileUploadField extends AbstractSimpleField
{
/** @var bool DEFAULT_ALLOW_DELETE */
const DEFAULT_ALLOW_DELETE = true;
/**
* @var bool DEFAULT_DISPLAY_OPENED
* @since 3.2.1 N°7534
*/
const DEFAULT_DISPLAY_OPENED = false;
/** @var string|null $sTransactionId */
protected $sTransactionId;
@@ -44,18 +39,8 @@ class FileUploadField extends AbstractSimpleField
protected $sUploadEndpoint;
/** @var string|null $sDownloadEndpoint */
protected $sDownloadEndpoint;
/**
* @var string|null $sViewEndpoint
* @since 3.2.1 N°7534
*/
protected ?string $sDisplayEndpoint;
/** @var bool $bAllowDelete */
protected $bAllowDelete;
/**
* @var bool $bDisplayOpened
* @since 3.2.1 N°7534
*/
protected bool $bDisplayOpened;
/**
* @inheritDoc
@@ -66,9 +51,7 @@ class FileUploadField extends AbstractSimpleField
$this->oObject = null;
$this->sUploadEndpoint = null;
$this->sDownloadEndpoint = null;
$this->sDisplayEndpoint = null;
$this->bAllowDelete = static::DEFAULT_ALLOW_DELETE;
$this->bDisplayOpened = static::DEFAULT_DISPLAY_OPENED;
parent::__construct($sId, $onFinalizeCallback);
}
@@ -151,27 +134,6 @@ class FileUploadField extends AbstractSimpleField
return $this;
}
/**
* @return string|null
* @since 3.2.1 N°7534
*/
public function GetDisplayEndpoint(): ?string
{
return $this->sDisplayEndpoint;
}
/**
* @param string $sDisplayEndpoint
*
* @return FileUploadField
* @since 3.2.1 N°7534
*/
public function SetDisplayEndpoint(string $sDisplayEndpoint): FileUploadField
{
$this->sDisplayEndpoint = $sDisplayEndpoint;
return $this;
}
/**
* @return bool
*/
@@ -191,29 +153,4 @@ class FileUploadField extends AbstractSimpleField
return $this;
}
/**
* Sets if the field should be displayed opened on initialization
*
* @param bool $bDisplayOpened
*
* @return FileUploadField
* @since 3.2.1 N°7534
*/
public function SetDisplayOpened(bool $bDisplayOpened) : FileUploadField
{
$this->bDisplayOpened = $bDisplayOpened;
return $this;
}
/**
* Returns if the field should be displayed opened on initialization
*
* @return boolean
* @since 3.2.1 N°7534
*/
public function GetDisplayOpened() : bool
{
return $this->bDisplayOpened;
}
}

View File

@@ -80,17 +80,11 @@ class BsFileUploadFieldRenderer extends BsFieldRenderer
$sFieldWrapperId = 'form_upload_wrapper_' . $this->oField->GetGlobalId();
$sFieldDescriptionForHTMLTag = ($this->oField->HasDescription()) ? 'data-tooltip-content="'.utils::HtmlEntities($this->oField->GetDescription()).'"' : '';
// Preparing collapsed state
if ($this->oField->GetDisplayOpened()) {
$sCollapseTogglerExpanded = 'true';
$sCollapseTogglerIconClass = $sCollapseTogglerIconVisibleClass;
$sCollapseJSInitState = 'true';
} else {
$sCollapseTogglerClass .= ' collapsed';
$sCollapseTogglerExpanded = 'false';
$sCollapseTogglerIconClass = $sCollapseTogglerIconHiddenClass;
$sCollapseJSInitState = 'false';
}
// If collapsed
$sCollapseTogglerClass .= ' collapsed';
$sCollapseTogglerExpanded = 'false';
$sCollapseTogglerIconClass = $sCollapseTogglerIconHiddenClass;
$sCollapseJSInitState = 'false';
// Label
$oOutput->AddHtml('<div class="form_field_label">');
@@ -189,7 +183,6 @@ JS
'{{iAttId}}',
'{{sLineStyle}}',
'{{sDocDownloadUrl}}',
'{{sDocDisplayUrl}}',
true,
'{{sAttachmentThumbUrl}}',
'{{sFileName}}',
@@ -241,7 +234,6 @@ JS
var \$oAttachmentTBody = $(this).closest('.fileupload_field_content').find('.attachments_container table#$sAttachmentTableId>tbody'),
iAttId = data.result.att_id,
sDownloadLink = '{$this->oField->GetDownloadEndpoint()}'.replace(/-sAttachmentId-/, iAttId),
sDisplayLink = '{$this->oField->GetDisplayEndpoint()}'.replace(/-sAttachmentId-/, iAttId),
sAttachmentMeta = '<input id="attachment_'+iAttId+'" type="hidden" name="attachments[]" value="'+iAttId+'"/>';
// hide "no attachment" line if present
@@ -255,7 +247,6 @@ JS
{search: "{{iAttId}}", replace:iAttId },
{search: "{{lineStyle}}", replace:'' },
{search: "{{sDocDownloadUrl}}", replace:sDownloadLink },
{search: "{{sDocDisplayUrl}}", replace:sDisplayLink },
{search: "{{sAttachmentThumbUrl}}", replace:data.result.icon },
{search: "{{sFileName}}", replace: data.result.msg },
{search: "{{sAttachmentMeta}}", replace:sAttachmentMeta },
@@ -411,7 +402,6 @@ HTML
$sFileName = utils::EscapeHtml($oDoc->GetFileName());
$sDocDownloadUrl = str_replace('-sAttachmentId-', $iAttId, $this->oField->GetDownloadEndpoint());
$sDocDisplayUrl = str_replace('-sAttachmentId-', $iAttId, $this->oField->GetDisplayEndpoint());
$sAttachmentThumbUrl = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName);
$bHasPreview = false;
@@ -441,7 +431,6 @@ HTML
$iAttId,
$sLineStyle,
$sDocDownloadUrl,
$sDocDisplayUrl,
$bHasPreview,
$sAttachmentThumbUrl,
$sFileName,
@@ -496,7 +485,6 @@ HTML;
* @param int $iAttId
* @param string $sLineStyle
* @param string $sDocDownloadUrl
* @param string $sDocDisplayUrl
* @param bool $bHasPreview replace string $sIconClass since 3.0.1
* @param string $sAttachmentThumbUrl
* @param string $sFileName
@@ -511,7 +499,7 @@ HTML;
* @since 2.7.0
*/
protected static function GetAttachmentTableRow(
$iAttId, $sLineStyle, $sDocDownloadUrl, $sDocDisplayUrl, $bHasPreview, $sAttachmentThumbUrl, $sFileName, $sAttachmentMeta, $sFileSize,
$iAttId, $sLineStyle, $sDocDownloadUrl, $bHasPreview, $sAttachmentThumbUrl, $sFileName, $sAttachmentMeta, $sFileSize,
$iFileSizeRaw, $iFileDownloadsCount, $sAttachmentDate, $iAttachmentDateRaw, $bIsDeleteAllowed
) {
$sDeleteCell = '';
@@ -523,16 +511,16 @@ HTML;
$sHtml = "<tr id=\"display_attachment_{$iAttId}\" class=\"attachment\" $sLineStyle>";
if($bHasPreview) {
$sHtml .= "<td role=\"icon\"><a href=\"$sDocDisplayUrl\" target=\"_blank\" data-tooltip-content=\"<img class='attachment-tooltip' src='{$sDocDownloadUrl}'>\" data-tooltip-html-enabled=true><img src=\"$sAttachmentThumbUrl\" ></a></td>";
$sHtml .= "<td role=\"icon\"><a href=\"$sDocDownloadUrl\" target=\"_blank\" data-tooltip-content=\"<img class='attachment-tooltip' src='{$sDocDownloadUrl}'>\" data-tooltip-html-enabled=true><img src=\"$sAttachmentThumbUrl\" ></a></td>";
} else {
$sHtml .= "<td role=\"icon\"><a href=\"$sDocDisplayUrl\" target=\"_blank\"><img src=\"$sAttachmentThumbUrl\" ></a></td>";
$sHtml .= "<td role=\"icon\"><a href=\"$sDocDownloadUrl\" target=\"_blank\"><img src=\"$sAttachmentThumbUrl\" ></a></td>";
}
$sHtml .= <<<HTML
<td role="filename"><a href="$sDocDisplayUrl" target="_blank">$sFileName</a>$sAttachmentMeta</td>
<td role="filename"><a href="$sDocDownloadUrl" target="_blank">$sFileName</a>$sAttachmentMeta</td>
<td role="formatted-size" data-order="$iFileSizeRaw">$sFileSize</td>
<td role="upload-date" data-order="$iAttachmentDateRaw">$sAttachmentDate</td>
<td role="downloads-count"><a href="$sDocDownloadUrl" target="_blank"><span class="fas fa-download fa-lg" style="float: right;"></span></a>$iFileDownloadsCount</td>
<td role="downloads-count">$iFileDownloadsCount</td>
$sDeleteCell
</tr>
HTML;

View File

@@ -254,6 +254,7 @@ JS
CombodoCKEditorHandler.GetInstance("#{$this->oField->GetGlobalId()}")
.then((oCKEditor) => {
oCKEditor.model.document.on("change:data", () => {
console.log("desc changed!");
const oFieldElem = $("#{$this->oField->GetGlobalId()}");
oFieldElem.closest(".field_set").trigger("field_change", {
id: oFieldElem.attr("id"),

View File

@@ -13,256 +13,143 @@ use DeprecatedCallsLog;
class DeprecatedCallsLogTest extends ItopTestCase
{
/**
* @dataProvider StripCallStackProvider
* We are testing for a undefined offset error. This was throwing a Notice, but starting with PHP 8.0 it was converted to a Warning ! Also the message was changed :(
*
* @link https://www.php.net/manual/en/migration80.incompatible.php check "A number of notices have been converted into warnings:"
*/
public function testStripCallStack($sInputStack, $sExpectedStack)
private function SetUndefinedOffsetExceptionToExpect(): void
{
$this->assertEquals(
$sExpectedStack,
$this->InvokeNonPublicStaticMethod(DeprecatedCallsLog::class, 'StripCallStack', [$sInputStack]),
'The top item of the call stack should be the first item meaningful to track deprecated calls, not the intermediate layers of the PHP engine'
);
/** @noinspection ConstantCanBeUsedInspection Preferring the function call as it is easier to read and won't cost that much in this PHPUnit context */
if (version_compare(PHP_VERSION, '8.0', '>=')) {
$this->expectWarning();
$sUndefinedOffsetExceptionMessage = 'Undefined array key "tutu"';
} else {
$this->expectNotice();
$sUndefinedOffsetExceptionMessage = 'Undefined index: tutu';
}
$this->expectExceptionMessage($sUndefinedOffsetExceptionMessage);
}
public function StripCallStackProvider()
public function testPhpNoticeWithoutDeprecatedCallsLog(): void
{
return [
/* A deprecated PHP method is invoked from scratch.php, at line 25 (note: the name of the method is not present in the callstack) */
'Should preserve the handler when the notice is fired by PHP itself' => [
'in' => [
[
'file' => 'whateverfolder/scratch.php',
'line' => 25,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
],
'out' => [
[
'file' => 'whateverfolder/scratch.php',
'line' => 25,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
],
],
'Should skip the handler when the notice is fired by a call to trigger_error' => [
'in' => [
[
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'whateverfolder/scratch.php',
'line' => 25,
'function' => 'trigger_error',
],
],
'out' => [
[
'file' => 'whateverfolder/scratch.php',
'line' => 25,
'function' => 'trigger_error',
],
],
],
'Should skip two levels when the notice is fired by trigger_deprecation (Symfony helper)' => [
'in' => [
[
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'symfony/deprecation.php',
'line' => 12,
'function' => 'trigger_error',
],
[
'file' => 'symfony/service.php',
'line' => 25,
'function' => 'trigger_deprecation',
],
],
'out' => [
[
'file' => 'symfony/service.php',
'line' => 25,
'function' => 'trigger_deprecation',
],
],
],
];
$this->SetUndefinedOffsetExceptionToExpect();
$aArray = [];
if ('toto' === $aArray['tutu']) {
//Do nothing, just raising a undefined offset warning
}
}
/**
* @dataProvider SummarizeCallStackProvider
* @runInSeparateProcess Necessary, due to the DeprecatedCallsLog being enabled (no mean to reset)
*
* The error handler set by DeprecatedCallsLog during startup was causing PHPUnit to miss PHP notices like "undefined offset"
*
* The error handler is now disabled when running PHPUnit
*
* @since 3.0.4 N°6274
* @covers DeprecatedCallsLog::DeprecatedNoticesErrorHandler
*/
public function testSummarizeCallStack($sInputStackStripped, $sExpectedSummary)
public function testPhpNoticeWithDeprecatedCallsLog(): void
{
$this->assertEquals(
$sExpectedSummary,
$this->InvokeNonPublicStaticMethod(DeprecatedCallsLog::class, 'SummarizeCallStack', [$sInputStackStripped])
);
$this->RequireOnceItopFile('core/log.class.inc.php');
DeprecatedCallsLog::Enable(); // will set error handler
$this->SetUndefinedOffsetExceptionToExpect();
$aArray = [];
if ('toto' === $aArray['tutu']) {
//Do nothing, just raising a undefined offset warning
}
}
public function SummarizeCallStackProvider()
/**
* @dataProvider GetMessageFromStackProvider
*/
public function testGetMessageFromStack($aDebugBacktrace, $sExpectedMessage): void
{
$sActualMessage = $this->InvokeNonPublicStaticMethod(DeprecatedCallsLog::class, 'GetMessageFromStack', [$aDebugBacktrace]);
$this->assertEquals($sExpectedMessage, $sActualMessage);
}
public function GetMessageFromStackProvider()
{
// All tests are based on a call stack issued from a deprecated PHP function
// Other cases are similar: what counts on the top level item is the file and line number of the deprecated call
return [
'From the main page (deprecated PHP function)' => [
'in:stripped call stack' => [
'Call in a file outside of a function or class' => [
[
[
'file' => 'whateverfolder/scratch.php',
'line' => 25,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
],
'out' => 'whateverfolder/scratch.php#25',
],
'From an iTop method (deprecated PHP function)' => [
'in:stripped call stack' => [
[
'file' => 'whateverfolder/someclass.php',
'line' => 18,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
'file' => 'C:\Dev\wamp64\www\itop-32\sources\Application\WebPage\WebPage.php',
'line' => '866',
'function' => 'NotifyDeprecatedPhpMethod',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'whateverfolder/index.php',
'line' => 25,
'function' => 'SomeMethod',
'class' => 'SomeClass',
'type' => '->',
],
],
'out' => 'SomeClass->SomeMethod (whateverfolder/someclass.php#18), itself called from whateverfolder/index.php#25',
],
'From an iTop static method (deprecated PHP function)' => [
'in:stripped call stack' => [
[
'file' => 'whateverfolder/someclass.php',
'line' => 18,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
'file' => 'C:\Dev\wamp64\www\itop-32\extensions\itop-object-copier\copy.php',
'line' => '130',
'function' => 'add_linked_script',
'class' => 'Combodo\iTop\Application\WebPage\WebPage',
'type' => '->',
],
[
'file' => 'whateverfolder/index.php',
'line' => 25,
'function' => 'SomeMethod',
'class' => 'SomeClass',
'type' => '::',
],
],
'out' => 'SomeClass::SomeMethod (whateverfolder/someclass.php#18), itself called from whateverfolder/index.php#25',
],
'From an iTop function (deprecated PHP function)' => [
'in:stripped call stack' => [
[
'file' => 'whateverfolder/someclass.php',
'line' => 18,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'whateverfolder/index.php',
'line' => 25,
'function' => 'SomeFunction',
],
],
'out' => 'SomeFunction (whateverfolder/someclass.php#18), itself called from whateverfolder/index.php#25',
],
'From a code snippet (deprecated PHP function)' => [
'in:stripped call stack' => [
[
'file' => 'itop-root/env-production/core/main.php',
'line' => 1290,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'itop-root/core/metamodel.class.php',
'line' => 6698,
'function' => 'require_once',
],
[
'file' => 'itop-root/env-production/autoload.php',
'line' => 6,
'function' => 'IncludeModule',
'class' => 'MetaModel',
'type' => '::',
],
[
'file' => 'itop-root/core/metamodel.class.php',
'line' => 6487,
'file' => 'C:\Dev\wamp64\www\itop-32\pages\exec.php',
'line' => '102',
'args' => ['C:\Dev\wamp64\www\itop-32\extensions\itop-object-copier\copy.php'],
'function' => 'require_once',
],
],
'out' => 'itop-root/env-production/core/main.php#1290'
'Call to Combodo\iTop\Application\WebPage\WebPage::add_linked_script in C:\Dev\wamp64\www\itop-32\extensions\itop-object-copier\copy.php#L130 (from C:\Dev\wamp64\www\itop-32\pages\exec.php#L102)',
],
'From a persistent object method (deprecated PHP function)' => [
'in:stripped call stack' => [
[
'file' => 'itop-root/env-production/itop-tickets/model.itop-tickets.php',
'line' => 165,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'itop-root/core/dbobject.class.php',
'line' => 6575,
'function' => 'OnBeforeWriteTicket',
'class' => 'Ticket',
'type' => '->',
],
[
'file' => 'itop-root/application/cmdbabstract.class.inc.php',
'line' => 5933,
'function' => 'FireEvent',
'class' => 'DBObject',
'type' => '->',
],
[
'file' => 'itop-root/core/dbobject.class.php',
'line' => 3643,
'function' => 'FireEventBeforeWrite',
'class' => 'cmdbAbstractObject',
'type' => '->',
],
[
'file' => 'itop-root/application/cmdbabstract.class.inc.php',
'line' => 4593,
'function' => 'DBUpdate',
'class' => 'DBObject',
'type' => '->',
],
[
'file' => 'itop-root/sources/Controller/Base/Layout/ObjectController.php',
'line' => 649,
'function' => 'DBUpdate',
'class' => 'cmdbAbstractObject',
'type' => '->',
],
[
'file' => 'itop-root/pages/UI.php',
'line' => 720,
'function' => 'OperationApplyModify',
'class' => 'Combodo\\iTop\\Controller\\Base\\Layout\\ObjectController',
'type' => '->',
],
'Call in a file function, outside of a class' => [
[
[
'file' => 'C:\\Dev\\wamp64\\www\\itop-32\\sources\\Application\\WebPage\\WebPage.php',
'line' => 866,
'function' => 'NotifyDeprecatedPhpMethod',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'C:\\Dev\\wamp64\\www\\itop-32\\extensions\\itop-object-copier\\copy.php',
'line' => 81,
'function' => 'add_linked_script',
'class' => 'Combodo\\iTop\\Application\\WebPage\\WebPage',
'type' => '->',
],
[
'file' => 'C:\\Dev\\wamp64\\www\\itop-32\\extensions\\itop-object-copier\\copy.php',
'line' => 123,
'function' => 'myFunction',
],
],
'out' => 'Ticket->OnBeforeWriteTicket (itop-root/env-production/itop-tickets/model.itop-tickets.php#165), itself called from DBObject->FireEvent (itop-root/core/dbobject.class.php#6575)'
'Call to Combodo\iTop\Application\WebPage\WebPage::add_linked_script in C:\Dev\wamp64\www\itop-32\extensions\itop-object-copier\copy.php#L81 (from C:\Dev\wamp64\www\itop-32\extensions\itop-object-copier\copy.php#L123)',
],
'Call from a class method' => [
[
[
'file' => 'C:\\Dev\\wamp64\\www\\itop-32\\sources\\Application\\WebPage\\WebPage.php',
'line' => 866,
'function' => 'NotifyDeprecatedPhpMethod',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'C:\\Dev\\wamp64\\www\\itop-32\\extensions\\itop-object-copier\\copy.php',
'line' => 82,
'function' => 'add_linked_script',
'class' => 'Combodo\\iTop\\Application\\WebPage\\WebPage',
'type' => '->',
],
[
'file' => 'C:\\Dev\\wamp64\\www\\itop-32\\extensions\\itop-object-copier\\copy.php',
'line' => 125,
'function' => 'MyMethod',
'class' => 'MyClass',
'type' => '::',
],
],
'Call to Combodo\iTop\Application\WebPage\WebPage::add_linked_script in C:\Dev\wamp64\www\itop-32\extensions\itop-object-copier\copy.php#L82 (from MyClass::MyMethod in C:\Dev\wamp64\www\itop-32\extensions\itop-object-copier\copy.php#L125)',
],
];
}

View File

@@ -1,128 +0,0 @@
<?php
namespace Combodo\iTop\Test\UnitTest\Hub;
use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Application\UI\Base\iUIBlockFactory;
use Combodo\iTop\Service\InterfaceDiscovery\InterfaceDiscovery;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
class HubSetupTest extends ItopDataTestCase {
const USE_TRANSACTION = false;
public function setUp() : void {
parent::setUp();
$this->RequireOnceItopFile("application/utils.inc.php");
$this->RequireOnceItopFile("core/log.class.inc.php");
$this->RequireOnceItopFile("setup/runtimeenv.class.inc.php");
$this->RequireOnceItopFile("setup/backup.class.inc.php");
$this->RequireOnceItopFile("core/mutex.class.inc.php");
$this->RequireOnceItopFile("core/dict.class.inc.php");
$this->RequireOnceItopFile("setup/xmldataloader.class.inc.php");
$this->RequireOnceItopFile("datamodels/2.x/itop-hub-connector/hubruntimeenvironment.class.inc.php");
}
public function testSetupViaHub() {
$sEnvironment = 'test';
$_REQUEST['switch_env']=$sEnvironment;
Session::Set('itop_env', $sEnvironment);
require_once (APPROOT.'/application/startup.inc.php');
require_once (APPROOT.'/application/loginwebpage.class.inc.php');
/*$aSelectedExtensionCodes = ['molkobain-datacenter-view'];
$aSelectedExtensionDirs = $aSelectedExtensionCodes;
ini_set('display_errors', 1);
$oRuntimeEnv = new \HubRunTimeEnvironment($sEnvironment, false); // use a temp environment: production-build
$oRuntimeEnv->MoveSelectedExtensions(APPROOT.'/data/downloaded-extensions/', $aSelectedExtensionDirs);
$oConfig = new \Config(APPCONF."$sEnvironment/".ITOP_CONFIG_FILE);
$oRuntimeEnv->CompileFrom("production", false); // WARNING symlinks does not seem to be compatible with manual Commit
$oRuntimeEnv->UpdateIncludes($oConfig);*/
//$oRuntimeEnv->InitDataModel($oConfig, true /* model only */);
// Safety check: check the inter dependencies, will throw an exception in case of inconsistency
/*$oRuntimeEnv->AnalyzeInstallation($oConfig, $oRuntimeEnv->GetBuildDir(), true);
$oRuntimeEnv->CheckMetaModel(); // Will throw an exception if a problem is detected
// Everything seems Ok so far, commit in env-production!
$oRuntimeEnv->WriteConfigFileSafe($oConfig);
$oRuntimeEnv->Commit();*/
/*$sPath = APPROOT.'data/downloaded-extensions/';
$aExtraDirs = array();
if (is_dir($sPath)) {
$aExtraDirs[] = $sPath; // Also read the extra downloaded-modules directory
}
$oExtensionsMap = new \iTopExtensionsMap($sEnvironment, true, $aExtraDirs);*/
//InterfaceDiscovery::GetInstance()->FindItopClasses(iUIBlockFactory::class);
$sPassword = "abCDEF12345@";
/** @var User oUser */
$this->oUser = $this->CreateContactlessUser('login' . uniqid(),
ItopDataTestCase::$aURP_Profiles['Administrator'],
$sPassword
);
$sLogin = $this->oUser->Get('login');
$aPostFields = [
'auth_user' => $sLogin,
'auth_pwd' => $sPassword,
];
//$oConfig = new \Config();
$sConfigPath = APPCONF . "$sEnvironment/config-itop.php"; //$oConfig->GetLoadedFile();
@chmod($sConfigPath, 0770);
//$oConfig->WriteToFile($sConfigPath);
//@chmod($sConfigPath, 0440);
$sOutput = $this->CallItopUrl("/pages/exec.php?exec_module=itop-hub-connector&exec_page=ajax.php&switch_env=$sEnvironment&exec_env=$sEnvironment&maintenance=1",
[
'auth_user' => $sLogin,
'auth_pwd' => $sPassword,
'operation' => "compile",
'extension_codes[]' => "molkobain-datacenter-view",
'extension_dirs[]' => "molkobain-datacenter-view",
'authent' => '14b5da9d092f84044187421419a0347e7317bc8cd2b486fdda631be06b959269',
]);
/*$sOutput = $this->CallItopUrl("/pages/exec.php?exec_module=itop-hub-connector&exec_page=land.php&switch_env=$sEnvironment&exec_env=$sEnvironment&operation=install",
$aPostFields);*/
//var_dump($sOutput);
$aRes = json_decode($sOutput, true);
$this->assertNotNull($aRes, "output should be a json without any warning:" . PHP_EOL . $sOutput);
}
protected function CallItopUrl($sUri, ?array $aPostFields = null, $bXDebugEnabled = false)
{
$ch = curl_init();
if ($bXDebugEnabled) {
curl_setopt($ch, CURLOPT_COOKIE, 'XDEBUG_SESSION=phpstorm');
}
$sUrl = \MetaModel::GetConfig()->Get('app_root_url')."/$sUri";
var_dump($sUrl);
curl_setopt($ch, CURLOPT_URL, $sUrl);
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
$sOutput = curl_exec($ch);
//echo "$sUrl error code:".curl_error($ch);
curl_close($ch);
return $sOutput;
}
}