Compare commits

...

56 Commits

Author SHA1 Message Date
Anne-Cath
229f6df925 N°9468 - Make sure max-height is removed if needed, harmonize behavior between extkey and input set 2026-04-09 15:03:16 +02:00
Stephen Abello
7071b3301c N°9468 - Fix double scroll down bars in input set 2026-04-09 10:49:37 +02:00
Stephen Abello
6bd34dc73e N°4460 - Fix date and date time picker in Darkmoon 2026-04-08 14:59:36 +02:00
Stephen Abello
9dc3c56689 N°9448 - Fix external auth variable value (#871) 2026-04-08 14:19:24 +02:00
Stephen Abello
effd35c3e6 N°8758 - Fix mandatory caselog in transition requiring double confirmation (#868) 2026-04-08 14:19:09 +02:00
lenaick.moreira
f1735767c3 💚 Fix CI 2026-04-08 10:19:31 +02:00
lenaick.moreira
00735f0c54 N°9483 - The search suggestion message is not displaying correctly 2026-04-08 09:48:15 +02:00
lenaick.moreira
882390e8d6 N°9043 - Fix automatic search on direct linkset block 2026-04-08 09:48:15 +02:00
Lenaick
5d0da47f21 N°8178 - Respect "high_cardinality_classes" parameter on search operation (#870) 2026-04-08 09:47:46 +02:00
lenaick.moreira
4eadff7f3b ♻️ Refactor BODY_DATA_GUI_TYPE constants to public visibility (N°9101) 2026-04-08 09:38:40 +02:00
Molkobain
f66ce1c956 Fix cURL calls to iTop within Docker dev. environment with PHP version autodetection (#869) 2026-04-07 19:06:30 +02:00
jf-cbd
802f9f3e08 N°8576 - [RequestTemplate] Slowness during the selection of drop-down list field (#872) 2026-04-07 17:06:21 +02:00
lenaick.moreira
5c5d98bb78 Enable the retrieval of all data for event notifications in the activity panel 2026-04-01 16:12:36 +02:00
Stephen Abello
a08a9b43f3 N°8737 - Fix search criteria dropdown vertical overflow (#867)
* N°8737 - Fix search criteria dropdown vertical overflow

* Update css/backoffice/components/_search-form.scss
2026-04-01 14:57:10 +02:00
jf-cbd
abd85ff4db Merge remote-tracking branch 'origin/support/3.2' into support/3.2 2026-04-01 10:37:59 +02:00
jf-cbd
81f328b26e N°8543 - rename security parameter with new convention 2026-04-01 10:37:46 +02:00
lenaick.moreira
9a2c8f10bf Fix CliResetSessionTest by allowing empty POST fields to be defined in the curl options 2026-04-01 10:35:59 +02:00
Lenaick
3cdadf3c6e Add tests for lock acquisition functionality (#865) 2026-03-31 17:01:22 +02:00
jf-cbd
a6295f1b14 Duplicate try-catch to avoid finally statement in case of uncorrrecth auth 2026-03-31 16:39:05 +02:00
Stephen Abello
e467ca83cf N°8532 - Apply filters on all DBSearch classes (#848)
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
2026-03-31 15:41:28 +02:00
Stephen Abello
7791585387 N°9231 - Make OrmDocument apply same safety to attachments and regular documents (#860) 2026-03-30 15:25:52 +02:00
Molkobain
3406ca79de N°9361 - Update PHPDoc 2026-03-30 13:49:30 +02:00
Stephen Abello
91ad01055e N°5228 - Allow themes variable imports to be overloaded by variable entries (#858) 2026-03-27 15:46:15 +01:00
Lenaick
804cdffe42 N°8234 - Fix permission checks to conditionally allow display of unauthorized objects (#859) 2026-03-27 15:09:31 +01:00
v-dumas
5f4affc896 N°9057 - Fix tests broken due to ModuleInstallation given grant_by_profile (2) 2026-03-26 17:56:52 +01:00
v-dumas
042fee2360 N°9057 - Fix tests broken due to ModuleInstallation given grant_by_profile 2026-03-26 17:37:18 +01:00
Vincent Dumas
7f8ec25977 N°9057 - Enable SuperUser to execute collectors (#799) 2026-03-26 12:13:45 +01:00
Lenaick
41f8437c23 N°8234 - Allow display of unauthorized objects in notifications and event queries (#853)
* N°8234 - Allow display of unauthorized objects in notifications and event queries

* Refactor EventNotificationNewsroom class usage in iTopNewsroomController
2026-03-26 11:44:06 +01:00
Anne-Catherine
df8b25d4b4 N°9223 - Portal - AttributeExternalKey or AttributeEnum are not displayed after adding a link. (#802) 2026-03-26 10:34:41 +01:00
Lenaick
511dabe2b0 N°8834 - Updated the PHP version to 8.4 for the CI in the commit 2026-03-25 17:43:39 +01:00
Lenaick
0c517f254c N°9101 - Improve HTML markup for end-to-end tests automation (#856) 2026-03-25 17:20:28 +01:00
Stephen Abello
c56c7a1f9d Fix CI by fixing code style 2026-03-25 10:25:37 +01:00
Stephen Abello
fb2f0f1447 N°9328 - Add scssphp compatibility with PHP 8.4 (#851) 2026-03-25 09:58:05 +01:00
Lenaick
b3223eb9b6 N°8606 - Check user permissions in search operation of ajax.render.php (#836) 2026-03-24 08:52:22 +01:00
Benjamin DALSASS
458a996c29 N°8612 - force authentication for inline image endpoints
- ajax.render dict route needs to be reached without login authentication
2026-03-23 15:50:47 +01:00
Anne-Catherine
c61b21559c N°8692 - Notification - placeholder attributesubitem (#778) 2026-03-23 15:33:50 +01:00
jf-cbd
ed33238750 Merge remote-tracking branch 'origin/support/3.2' into support/3.2 2026-03-20 16:31:44 +01:00
jf-cbd
272678b8cd N°9361 - Indicate to itop admin concerned by 8543 that changes could be required in extension 2026-03-20 16:30:53 +01:00
Benjamin Dalsass
170014e8f0 N°9232 - Information Disclosure (#850) 2026-03-20 14:35:05 +01:00
Molkobain
006f666089 N°8554 - Fix impossible installation of portal new look via iTop Hub (#846) 2026-03-18 19:59:42 +01:00
Stephen Abello
2a16143e53 N°9229 - Modernize search foreign keys code with built in JS tools (#847)
* N°9229 - Modernize search foreign keys code with built in JS tools

* N°9229 - Allow modals to have button id specified

* N°9229 - Remove the modal instead of only destroying it

* N°9229 - Remove dead code

* Update js/searchformforeignkeys.js

* Add robustness to modals button id
2026-03-18 15:23:52 +01:00
jf-cbd
eabbe2f00b N°8543 - best practices 2026-03-18 10:09:25 +01:00
Molkobain
58790bc352 N°8597 - Fix special characters being escaped for BrowseBrick items in "Tree" mode (#845)
* N°8597 - Fix special characters being escaped for BrowseBrick items in "Tree" mode

* N°8597 - Fix forgotten use case for "Tree" mode for intermediate levels
2026-03-17 15:02:20 +01:00
Lenaick
28db230697 N°9233 - Check user access before acquiring lock on object (#844) 2026-03-16 17:07:26 +01:00
jf-cbd
4fe61cbdc7 N°8543 - Add checks on exec.php (#835) 2026-03-16 17:06:37 +01:00
Benjamin Dalsass
e2994b645b N°8612 inline images to base64 (#826) 2026-03-16 08:36:37 +01:00
lenaick.moreira
9fca81cc32 N°9366 - Update PHPUnit to be compatible with PHP 8.4 2026-03-10 10:59:54 +01:00
lenaick.moreira
9792358aea Remove debug OQL filter in HTML comments of the universal search page 2026-03-09 17:06:00 +01:00
lenaick.moreira
7bfa14a874 N°9235 - Sanitize oql_clause query parameter in universal search page 2026-03-09 17:06:00 +01:00
Lenaick
9236449b21 N°9238 - Sanitize data_source_id query parameter in synchro_import script (#831) 2026-03-09 09:02:17 +01:00
Benjamin Dalsass
ab8e7bd15e N°9236 - tag admin fix (#832) 2026-03-09 08:30:39 +01:00
lenaick.moreira
307c308eb0 Fix unit test testPhpMinVersionConsistency 2026-03-05 16:15:18 +01:00
Lenaick
61e5536b50 N°9234 - Sanitize query expression parameter in suggested OQL on run query page (#829) 2026-03-05 16:02:30 +01:00
Lenaick
104dd1970f N°9230 - Sanitize dashboard_id parameter in "revert_dashboard" operation of AJAX render function (#828) 2026-03-05 15:55:28 +01:00
Stephen Abello
929b8b9eca Fix CI by fixing code style 2026-03-05 15:28:27 +01:00
Stephen Abello
3b8e079cf1 N°6977 - Sanitize Excel formulas in exports (#818)
* N°6977 - Sanitize Excel formulas in export in the backoffice
---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2026-03-05 15:07:27 +01:00
139 changed files with 2975 additions and 1098 deletions

View File

@@ -810,6 +810,7 @@ HTML
foreach ($aNotificationClasses as $sNotifClass) {
$aNotifSearches[$sNotifClass] = DBObjectSearch::FromOQL("SELECT $sNotifClass AS Ev JOIN Trigger AS T ON Ev.trigger_id = T.id WHERE T.id IN (:triggers) AND Ev.object_id = :id");
$aNotifSearches[$sNotifClass]->SetInternalParams($aParams);
$aNotifSearches[$sNotifClass]->AllowAllData();
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass], []);
$iNotifsCount += $oNotifSet->Count();
}
@@ -823,6 +824,7 @@ HTML
'menu' => false,
'panel_title' => MetaModel::GetName($sNotifClass),
'panel_icon' => MetaModel::GetClassIcon($sNotifClass, false),
'display_unauthorized_objects' => true,
]);
}
}

View File

@@ -724,6 +724,10 @@ class DisplayBlock
}
}
if (!$this->m_oFilter->IsAllDataAllowed() && ($aExtraParams['display_unauthorized_objects'] ?? false) === true) {
$this->m_oFilter->AllowAllData();
}
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, $aOrderBy, $aQueryParams);
}
@@ -1381,7 +1385,10 @@ JS
// Check the classes that can be read (i.e authorized) by this user...
foreach ($aClasses as $sAlias => $sClassName) {
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $this->m_oSet) != UR_ALLOWED_NO) {
if (
(UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $this->m_oSet) !== UR_ALLOWED_NO)
|| ($aExtraParams['display_unauthorized_objects'] ?? false) === true
) {
$aAuthorizedClasses[$sAlias] = $sClassName;
}
}

View File

@@ -75,13 +75,10 @@ class LoginExternal extends AbstractLoginFSMExtension
}
/**
* @return bool
* @return bool|mixed
*/
private function GetAuthUser()
{
$sExtAuthVar = MetaModel::GetConfig()->GetExternalAuthenticationVariable(); // In which variable is the info passed ?
eval('$sAuthUser = isset('.$sExtAuthVar.') ? '.$sExtAuthVar.' : false;'); // Retrieve the value
/** @var string $sAuthUser */
return $sAuthUser; // Retrieve the value
return MetaModel::GetConfig()->GetExternalAuthenticationVariable();
}
}

View File

@@ -936,11 +936,6 @@ CSS;
public static function CloneThemeParameterAndIncludeVersion($aThemeParameters, $bSetupCompilationTimestamp, $aImportsPaths)
{
$aThemeParametersVariable = [];
if (array_key_exists('variables', $aThemeParameters)) {
if (is_array($aThemeParameters['variables'])) {
$aThemeParametersVariable = array_merge([], $aThemeParameters['variables']);
}
}
if (array_key_exists('variable_imports', $aThemeParameters)) {
if (is_array($aThemeParameters['variable_imports'])) {
@@ -948,6 +943,14 @@ CSS;
}
}
// Variables defined in theme XML have the priority over variables defined in XML imports files
// They're defined after so they overwrite previous parameters
if (array_key_exists('variables', $aThemeParameters)) {
if (is_array($aThemeParameters['variables'])) {
$aThemeParametersVariable = array_merge($aThemeParametersVariable, $aThemeParameters['variables']);
}
}
$aThemeParametersVariable['$version'] = $bSetupCompilationTimestamp;
return $aThemeParametersVariable;
}
@@ -979,7 +982,9 @@ CSS;
}
}
}
array_map(function ($sVariableValue) { return ltrim($sVariableValue); }, $aVariablesResults);
array_map(function ($sVariableValue) {
return ltrim($sVariableValue);
}, $aVariablesResults);
return $aVariablesResults;
}

View File

@@ -228,7 +228,7 @@ JS
<<<HTML
<form id="ObjectsAddForm_{$this->sInputid}">
<div id="SearchResultsToAdd_{$this->sInputid}">
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
<div style="border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
</div>
<input type="hidden" id="count_{$this->sInputid}" value="0"/>
</form>

View File

@@ -27,6 +27,9 @@ require_once(APPROOT.'/application/displayblock.class.inc.php');
class UISearchFormForeignKeys
{
private $m_sRemoteClass;
private $m_iInputId;
public function __construct($sTargetClass, $iInputId = null)
{
$this->m_sRemoteClass = $sTargetClass;
@@ -40,7 +43,7 @@ class UISearchFormForeignKeys
*
* @throws \Exception
*/
public function ShowModalSearchForeignKeys($oPage, $sTitle)
public function ShowModalSearchForeignKeys($oPage)
{
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
@@ -60,52 +63,17 @@ class UISearchFormForeignKeys
]
));
$sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sCancel = Dict::S('UI:Button:Cancel');
$sAdd = Dict::S('UI:Button:Add');
$oPage->add(
<<<HTML
<form id="ObjectsAddForm_{$this->m_iInputId}">
<div id="SearchResultsToAdd_{$this->m_iInputId}" style="vertical-align:top;height:100%;overflow:auto;padding:0;border:0;">
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
<div style="border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
</div>
<input type="hidden" id="count_{$this->m_iInputId}" value="0"/>
</form>
HTML
);
$oPage->add_ready_script(
<<<JS
$('#dlg_{$this->m_iInputId}').dialog({
width: $(window).width()*0.8,
height: $(window).height()*0.8,
autoOpen: false,
modal: true,
resizeStop: oForeignKeysWidget{$this->m_iInputId}.UpdateSizes,
buttons: [
{
text: Dict.S('UI:Button:Cancel'),
class: "cancel ibo-is-alternative ibo-is-neutral",
click: function() {
$('#dlg_{$this->m_iInputId}').dialog('close');
}
},
{
text: Dict.S('UI:Button:Add'),
id: 'btn_ok_{$this->m_iInputId}',
class: "ok ibo-is-regular ibo-is-primary",
click: function() {
oForeignKeysWidget{$this->m_iInputId}.DoAddObjects(this.id);
}
},
],
});
$('#dlg_{$this->m_iInputId}').dialog('option', {title:'$sTitle'});
$('#SearchFormToAdd_{$this->m_iInputId} form').on('submit.uilinksWizard', oForeignKeysWidget{$this->m_iInputId}.SearchObjectsToAdd);
$('#SearchFormToAdd_{$this->m_iInputId}').on('resize', oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);
JS
);
}
public function GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter)
@@ -119,31 +87,4 @@ JS
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
}
}
/**
* Search for objects to be linked to the current object (i.e "remote" objects)
*
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of m_sRemoteClass
*
* @throws \Exception
*/
public function ListResultsSearchForeignKeys(WebPage $oP, $sRemoteClass = '')
{
if ($sRemoteClass != '') {
// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
$oFilter = new DBObjectSearch($sRemoteClass);
} else {
// No remote class specified use the one defined in the linkedset
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
}
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display(
$oP,
"ResultsToAdd_{$this->m_iInputId}",
['menu' => false, 'cssCount' => "#count_{$this->m_iInputId}", 'selection_mode' => true, 'table_id' => "add_{$this->m_iInputId}"]
);
}
}

View File

@@ -122,6 +122,11 @@ class utils
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_VARIABLE_NAME = 'variable_name';
/**
* @var string For module codes (e.g. `itop-portal-base`, `combodo-webhook-integration`, `some-module-code-x.y`, ...)
* @since 3.2.3 3.3.0 N°8554
*/
public const ENUM_SANITIZATION_FILTER_MODULE_CODE = 'module_code';
/**
* @var string
* @since 2.7.10 3.0.0
@@ -393,6 +398,7 @@ class utils
* @since 2.7.10 N°6606 use the utils::ENUM_SANITIZATION_* const
* @since 2.7.10 N°6606 new case for ENUM_SANITIZATION_FILTER_PHP_CLASS
* @since 3.2.1-1 N°8242 Allow value to be an array for every filter
* @since 3.2.3 3.3.0 N°8554 new case for ENUM_SANITIZATION_FILTER_MODULE_CODE
*
* @link https://www.php.net/manual/en/filter.filters.sanitize.php PHP sanitization filters
*/
@@ -480,7 +486,7 @@ class utils
);
break;
// For XML / HTML node id selector
// For XML / HTML node selector
case static::ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR:
$retValue = filter_var(
$value,
@@ -493,6 +499,15 @@ class utils
$retValue = preg_replace('/[^a-zA-Z0-9_]/', '', $value);
break;
case static::ENUM_SANITIZATION_FILTER_MODULE_CODE:
// Module codes allow all alphabets letters, numbers, dash and dot characters
$retValue = filter_var(
$value,
FILTER_VALIDATE_REGEXP,
['options' => ['regexp' => '/^[\p{L}\d.-]+$/u']]
);
break;
// For URL
case static::ENUM_SANITIZATION_FILTER_URL:
$retValue = filter_var($value, FILTER_SANITIZE_URL);
@@ -1440,6 +1455,12 @@ class utils
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
/** @var \DBObjectSet $param */
// Check if the user has the right to read the objects of this list, otherwise do not propose any action (eg. configure this list, export, etc.)
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_READ, $param) !== UR_ALLOWED_YES) {
break;
}
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink(true);
$sDataTableId = is_null($sDataTableId) ? '' : $sDataTableId;
@@ -2081,7 +2102,9 @@ SQL;
}
// Remove any remaining nulls (for positions that weren't referenced)
$aReplacements = array_filter($aReplacements, static function ($val) { return $val !== null; });
$aReplacements = array_filter($aReplacements, static function ($val) {
return $val !== null;
});
} else {
// For non-positional, we need to map each position
$aReplacements = [];

View File

@@ -19,7 +19,7 @@
"pear/archive_tar": "~1.4.14",
"pelago/emogrifier": "^7.2.0",
"psr/log": "^3.0.0",
"scssphp/scssphp": "^1.12.1",
"scssphp/scssphp": "dev-combodo/1.x",
"symfony/console": "~6.4.0",
"symfony/dotenv": "~6.4.0",
"symfony/framework-bundle": "~6.4.0",
@@ -43,6 +43,10 @@
{
"type": "vcs",
"url": "https://github.com/EsupPortail/phpCAS"
},
{
"type": "vcs",
"url": "https://github.com/combodo-itop-libs/scssphp"
}
],
"suggest": {

30
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ceac38f6033afe07b7ab977fa39fe348",
"content-hash": "eebbdc6c10a479b0e62fc18d88496f5c",
"packages": [
{
"name": "apereo/phpcas",
@@ -1588,16 +1588,16 @@
},
{
"name": "scssphp/scssphp",
"version": "v1.13.0",
"version": "dev-combodo/1.x",
"source": {
"type": "git",
"url": "https://github.com/scssphp/scssphp.git",
"reference": "63d1157457e5554edf00b0c1fabab4c1511d2520"
"url": "https://github.com/combodo-itop-libs/scssphp.git",
"reference": "dde81c0a39d02e8e6fc81b70269747734e16d526"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/63d1157457e5554edf00b0c1fabab4c1511d2520",
"reference": "63d1157457e5554edf00b0c1fabab4c1511d2520",
"url": "https://api.github.com/repos/combodo-itop-libs/scssphp/zipball/dde81c0a39d02e8e6fc81b70269747734e16d526",
"reference": "dde81c0a39d02e8e6fc81b70269747734e16d526",
"shasum": ""
},
"require": {
@@ -1626,8 +1626,8 @@
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": false,
"forward-command": false
"forward-command": false,
"bin-links": false
}
},
"autoload": {
@@ -1635,7 +1635,11 @@
"ScssPhp\\ScssPhp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"autoload-dev": {
"psr-4": {
"ScssPhp\\ScssPhp\\Tests\\": "tests/"
}
},
"license": [
"MIT"
],
@@ -1661,10 +1665,9 @@
"stylesheet"
],
"support": {
"issues": "https://github.com/scssphp/scssphp/issues",
"source": "https://github.com/scssphp/scssphp/tree/v1.13.0"
"source": "https://github.com/combodo-itop-libs/scssphp/tree/combodo/1.x"
},
"time": "2024-08-17T21:02:11+00:00"
"time": "2026-03-23T15:26:59+00:00"
},
{
"name": "soundasleep/html2text",
@@ -5097,7 +5100,8 @@
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"apereo/phpcas": 20
"apereo/phpcas": 20,
"scssphp/scssphp": 20
},
"prefer-stable": false,
"prefer-lowest": false,

View File

@@ -234,10 +234,11 @@ abstract class Action extends cmdbAbstractObject
}
$oActionFilter = DBObjectSearch::FromOQL($sActionQueryOql, $aActionQueryParams);
$oActionFilter->AllowAllData();
$oSet = new DBObjectSet($oActionFilter, ['date' => false]);
$sPanelTitle = Dict::Format('Action:last_executions_tab_panel_title', $sActionQueryLimit);
$oExecutionsListBlock = DataTableUIBlockFactory::MakeForResult($oPage, 'action_executions_list', $oSet, ['panel_title' => $sPanelTitle]);
$oExecutionsListBlock = DataTableUIBlockFactory::MakeForResult($oPage, 'action_executions_list', $oSet, ['panel_title' => $sPanelTitle, 'display_unauthorized_objects' => true]);
$oPage->AddUiBlock($oExecutionsListBlock);
}

View File

@@ -4344,7 +4344,9 @@ class AttributeText extends AttributeString
} else {
$sValue = self::RenderWikiHtml($sValue, true /* wiki only */);
return "<div class=\"HTML ibo-is-html-content\" $sStyle>".InlineImage::FixUrls($sValue).'</div>';
$sImageHtml = UserRights::IsLoggedIn() ? InlineImage::FixUrls($sValue) : InlineImage::ReplaceInlineImagesWithBase64Representation($sValue);
return "<div class=\"HTML ibo-is-html-content\" $sStyle>".$sImageHtml.'</div>';
}
}
@@ -8988,7 +8990,10 @@ class AttributeStopWatch extends AttributeDefinition
switch ($sThresholdCode) {
case 'deadline':
if ($value) {
if (is_int($value)) {
if (is_numeric($value)) {
if (!is_int($value)) {
$value = intval($value);
}
$sDate = date(AttributeDateTime::GetInternalFormat(), $value);
$sRet = AttributeDeadline::FormatDeadline($sDate);
} else {

View File

@@ -75,6 +75,7 @@ define('DEFAULT_EXT_AUTH_VARIABLE', '$_SERVER[\'REMOTE_USER\']');
define('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random generated key later (if possible)
define('DEFAULT_ENCRYPTION_LIB', 'Mcrypt'); // We'll define the best encryption available later
define('DEFAULT_HASH_ALGO', PASSWORD_DEFAULT);
/**
* Config
* configuration data (this class cannot not be localized, because it is responsible for loading the dictionaries)
@@ -869,6 +870,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'ext_auth_variable' => [
'type' => 'string',
'description' => 'External authentication expression (allowed: $_SERVER[\'key\'], $_COOKIE[\'key\'], $_REQUEST[\'key\'], getallheaders()[\'Header-Name\'])',
'default' => '$_SERVER[\'REMOTE_USER\']',
'value' => '$_SERVER[\'REMOTE_USER\']',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'login_debug' => [
'type' => 'bool',
'description' => 'Activate the login FSM debug',
@@ -1603,7 +1612,7 @@ class Config
'show_in_conf_sample' => false,
],
'search_manual_submit' => [
'type' => 'array',
'type' => 'bool',
'description' => 'Force manual submit of search all requests',
'default' => false,
'value' => true,
@@ -1730,6 +1739,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'security.disable_joined_classes_filter' => [
'type' => 'bool',
'description' => 'If true, scope filters aren\'t applied to joined classes or union classes not directly listed in the SELECT clause.',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'security.hide_administrators' => [
'type' => 'bool',
'description' => 'If true, non-administrator users will not be able to see the administrator accounts, the Administrator profile and the links between the administrator accounts and their profiles.',
@@ -1738,6 +1755,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'security.disable_exec_forced_login_for_all_enpoints' => [
'type' => 'bool',
'description' => 'If true, when no delegated authentication module is defined, no login will be forced on modules exec endpoints',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'behind_reverse_proxy' => [
'type' => 'bool',
'description' => 'If true, then proxies custom header (X-Forwarded-*) are taken into account. Use only if the webserver is not publicly accessible (reachable only by the reverse proxy)',
@@ -1950,11 +1975,6 @@ class Config
*/
protected $m_sAllowedLoginTypes;
/**
* @var string Name of the PHP variable in which external authentication information is passed by the web server
*/
protected $m_sExtAuthVariable;
/**
* @var string Encryption key used for all attributes of type "encrypted string". Can be set to a random value
* unless you want to import a database from another iTop instance, in which case you must use
@@ -2027,7 +2047,6 @@ class Config
$this->m_bSecureConnectionRequired = DEFAULT_SECURE_CONNECTION_REQUIRED;
$this->m_sDefaultLanguage = 'EN US';
$this->m_sAllowedLoginTypes = DEFAULT_ALLOWED_LOGIN_TYPES;
$this->m_sExtAuthVariable = DEFAULT_EXT_AUTH_VARIABLE;
$this->m_aCharsets = [];
$this->m_bQueryCacheEnabled = DEFAULT_QUERY_CACHE_ENABLED;
$this->m_iPasswordHashAlgo = DEFAULT_HASH_ALGO;
@@ -2181,7 +2200,6 @@ class Config
$this->m_sDefaultLanguage = isset($MySettings['default_language']) ? trim($MySettings['default_language']) : 'EN US';
$this->m_sAllowedLoginTypes = isset($MySettings['allowed_login_types']) ? trim($MySettings['allowed_login_types']) : DEFAULT_ALLOWED_LOGIN_TYPES;
$this->m_sExtAuthVariable = isset($MySettings['ext_auth_variable']) ? trim($MySettings['ext_auth_variable']) : DEFAULT_EXT_AUTH_VARIABLE;
$this->m_sEncryptionKey = isset($MySettings['encryption_key']) ? trim($MySettings['encryption_key']) : $this->m_sEncryptionKey;
$this->m_sEncryptionLibrary = isset($MySettings['encryption_library']) ? trim($MySettings['encryption_library']) : $this->m_sEncryptionLibrary;
$this->m_aCharsets = isset($MySettings['csv_import_charsets']) ? $MySettings['csv_import_charsets'] : [];
@@ -2334,9 +2352,73 @@ class Config
return explode('|', $this->m_sAllowedLoginTypes);
}
/**
* @return bool|mixed
* @since 3.2.3 return the parsed value instead of an unsecured variable name
*/
public function GetExternalAuthenticationVariable()
{
return $this->m_sExtAuthVariable;
$sExpression = $this->Get('ext_auth_variable');
$aParsed = $this->ParseExternalAuthVariableExpression($sExpression);
if ($aParsed === null) {
return false;
}
$sKey = $aParsed['key'];
switch ($aParsed['type']) {
case 'server':
return $_SERVER[$sKey] ?? false;
case 'cookie':
return $_COOKIE[$sKey] ?? false;
case 'request':
return $_REQUEST[$sKey] ?? false;
case 'header':
if (!function_exists('getallheaders')) {
return false;
}
$aHeaders = getallheaders();
if (!is_array($aHeaders)) {
return false;
}
return $aHeaders[$sKey] ?? false;
}
return false;
}
/**
* @param $sExpression
* @return array|null
*/
private function ParseExternalAuthVariableExpression($sExpression)
{
// If it's a configuration parameter it's probably already trimmed, but just in case
$sExpression = trim((string) $sExpression);
if ($sExpression === '') {
return null;
}
// Match $_SERVER/$_COOKIE/$_REQUEST['key'] with optional whitespace and single/double quotes.
if (preg_match('/^\$_(SERVER|COOKIE|REQUEST)\s*\[\s*(["\'])\s*([^"\']+)\2\s*\]\s*$/', $sExpression, $aMatches) === 1) {
$sContext = strtoupper($aMatches[1]);
$sKey = $aMatches[3];
return [
'type' => strtolower($sContext),
'key' => $sKey,
'normalized' => '$_'.$sContext.'[\''.$sKey.'\']',
];
}
// Match getallheaders()['Header-Name'] in a case-insensitive way.
if (preg_match('/^getallheaders\(\)\s*\[\s*(["\'])\s*([^"\']+)\1\s*\]\s*$/i', $sExpression, $aMatches) === 1) {
$sKey = $aMatches[2];
return [
'type' => 'header',
'key' => $sKey,
'normalized' => 'getallheaders()[\''.$sKey.'\']',
];
}
return null;
}
public function GetCSVImportCharsets()
@@ -2432,7 +2514,7 @@ class Config
public function SetExternalAuthenticationVariable($sExtAuthVariable)
{
$this->m_sExtAuthVariable = $sExtAuthVariable;
$this->Set('ext_auth_variable', $sExtAuthVariable);
}
public function SetEncryptionKey($sKey)
@@ -2487,7 +2569,6 @@ class Config
$aSettings['secure_connection_required'] = $this->m_bSecureConnectionRequired;
$aSettings['default_language'] = $this->m_sDefaultLanguage;
$aSettings['allowed_login_types'] = $this->m_sAllowedLoginTypes;
$aSettings['ext_auth_variable'] = $this->m_sExtAuthVariable;
$aSettings['encryption_key'] = $this->m_sEncryptionKey;
$aSettings['encryption_library'] = $this->m_sEncryptionLibrary;
$aSettings['csv_import_charsets'] = $this->m_aCharsets;
@@ -2592,7 +2673,6 @@ class Config
$aOtherValues = [
'default_language' => $this->m_sDefaultLanguage,
'allowed_login_types' => $this->m_sAllowedLoginTypes,
'ext_auth_variable' => $this->m_sExtAuthVariable,
'encryption_key' => $this->m_sEncryptionKey,
'encryption_library' => $this->m_sEncryptionLibrary,
'csv_import_charsets' => $this->m_aCharsets,

View File

@@ -5,6 +5,7 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Application\Helper\ExportHelper;
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSetUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
@@ -13,7 +14,6 @@ use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectUIBlockFactory
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\ColumnUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactory;
use Combodo\iTop\Application\Helper\ExportHelper;
use Combodo\iTop\Application\WebPage\Page;
use Combodo\iTop\Application\WebPage\WebPage;
@@ -55,6 +55,8 @@ class CSVBulkExport extends TabularBulkExport
$this->aStatusInfo['charset'] = strtoupper(utils::ReadParam('charset', 'UTF-8', true, 'raw_data'));
$this->aStatusInfo['formatted_text'] = (bool)utils::ReadParam('formatted_text', 0, true);
$this->aStatusInfo['ignore_excel_sanitization'] = (bool)utils::ReadParam('ignore_excel_sanitization', 0, true, utils::ENUM_SANITIZATION_FILTER_INTEGER);
$sDateFormatRadio = utils::ReadParam('csv_date_format_radio', '');
switch ($sDateFormatRadio) {
case 'default':
@@ -223,6 +225,10 @@ class CSVBulkExport extends TabularBulkExport
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
$oFieldSetDate->AddSubBlock($oRadioCustom);
$oFieldSetSecurity = FieldSetUIBlockFactory::MakeStandard(Dict::S('Core:BulkExport:Security'));
$oMulticolumn->AddColumn(ColumnUIBlockFactory::MakeForBlock($oFieldSetSecurity));
$oFieldSetSecurity->AddSubBlock(ExportHelper::GetInputForSanitizeExcelExport());
$oP->add_ready_script(
<<<EOF
$('#form_part_csv_options').on('preview_updated', function() { FormatDatesInPreview('csv', 'csv'); });
@@ -264,6 +270,13 @@ EOF
default:
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
}
// If the option to ignore Excel sanitization is not set or explicitly set to false, apply sanitization
if (!(array_key_exists('ignore_excel_sanitization', $this->aStatusInfo)) || $this->aStatusInfo['ignore_excel_sanitization'] === false) {
return ExportHelper::SanitizeField($sRet, $this->aStatusInfo['text_qualifier'] ?? '');
}
// The option to ignore Excel sanitization is explicitly set to true: return the raw value without sanitization
return $sRet;
}
@@ -337,6 +350,12 @@ EOF
$sField = $oObj->GetAsCSV($sAttCode, $this->aStatusInfo['separator'], $this->aStatusInfo['text_qualifier'], $this->bLocalizeOutput, !$this->aStatusInfo['formatted_text']);
}
}
// If the option to ignore Excel sanitization is not set or absent, sanitize the field
if (!(array_key_exists('ignore_excel_sanitization', $this->aStatusInfo)) || $this->aStatusInfo['ignore_excel_sanitization'] === false) {
$sField = ExportHelper::SanitizeField($sField, $this->aStatusInfo['text_qualifier']);
}
if ($this->aStatusInfo['charset'] != 'UTF-8') {
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
// and thus to convert field by field and not the whole row or file at once (see ticket N°991)

View File

@@ -409,7 +409,7 @@
</php_parent>
<parent>cmdbAbstractObject</parent>
<properties>
<category>core/cmdb,view_in_gui</category>
<category>core/cmdb,grant_by_profile,silo</category>
<abstract>false</abstract>
<key_type>autoincrement</key_type>
<db_table>priv_event_newsroom</db_table>
@@ -888,7 +888,7 @@
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>Event</parent>
<properties>
<category>core/cmdb,view_in_gui</category>
<category>core/cmdb,grant_by_profile,silo</category>
</properties>
<fields>
<field id="message" xsi:type="AttributeText"/>

View File

@@ -1925,4 +1925,37 @@ class DBObjectSearch extends DBSearch
{
return $this->GetCriteria()->ListParameters();
}
/**
* @inheritDoc
* @return DBObjectSearch
*/
protected function ApplyDataFilters(): DBObjectSearch
{
if ($this->IsAllDataAllowed() || $this->IsDataFiltered()) {
return $this;
}
$oSearch = $this;
$aClassesToFilter = $this->GetSelectedClasses();
// Opt-in for joined classes filtering, otherwise only filter the selected class(es)
if (MetaModel::GetConfig()->Get('security.disable_joined_classes_filter') === false) {
$aClassesToFilter = $this->GetJoinedClasses();
}
// Apply filter (this is similar to the one in DBSearch but the factorization could make it less readable)
foreach ($aClassesToFilter as $sClassAlias => $sClass) {
$oVisibleObjects = UserRights::GetSelectFilter($sClass, $this->GetModifierProperties('UserRightsGetSelectFilter'));
if ($oVisibleObjects === false) {
$oVisibleObjects = DBObjectSearch::FromEmptySet($sClass);
}
if (is_object($oVisibleObjects)) {
$oVisibleObjects->AllowAllData();
$oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects);
$oSearch->SetDataFiltered();
}
}
return $oSearch;
}
}

View File

@@ -1122,21 +1122,7 @@ abstract class DBSearch
*/
protected function GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $aGroupByExpr = null, $aSelectExpr = null)
{
$oSearch = $this;
if (!$this->IsAllDataAllowed() && !$this->IsDataFiltered()) {
foreach ($this->GetSelectedClasses() as $sClassAlias => $sClass) {
$oVisibleObjects = UserRights::GetSelectFilter($sClass, $this->GetModifierProperties('UserRightsGetSelectFilter'));
if ($oVisibleObjects === false) {
// Make sure this is a valid search object, saying NO for all
$oVisibleObjects = DBObjectSearch::FromEmptySet($sClass);
}
if (is_object($oVisibleObjects)) {
$oVisibleObjects->AllowAllData();
$oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects);
$oSearch->SetDataFiltered();
}
}
}
$oSearch = $this->ApplyDataFilters();
if (is_array($aGroupByExpr)) {
foreach ($aGroupByExpr as $sAlias => $oGroupByExp) {
@@ -1608,4 +1594,33 @@ abstract class DBSearch
* @return array{\VariableExpression}
*/
abstract public function GetExpectedArguments(): array;
/**
* Apply data filters to the search, if needed
*
* @return DBSearch
* @throws CoreException
*/
protected function ApplyDataFilters(): DBSearch
{
if ($this->IsAllDataAllowed() || $this->IsDataFiltered()) {
return $this;
}
$oSearch = $this;
$aClassesToFilter = $this->GetSelectedClasses();
foreach ($aClassesToFilter as $sClassAlias => $sClass) {
$oVisibleObjects = UserRights::GetSelectFilter($sClass, $this->GetModifierProperties('UserRightsGetSelectFilter'));
if ($oVisibleObjects === false) {
$oVisibleObjects = DBObjectSearch::FromEmptySet($sClass);
}
if (is_object($oVisibleObjects)) {
$oVisibleObjects->AllowAllData();
$oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects);
$oSearch->SetDataFiltered();
}
}
return $oSearch;
}
}

View File

@@ -673,4 +673,30 @@ class DBUnionSearch extends DBSearch
return $aVariableCriteria;
}
/**
* @inheritDoc
* @return DBUnionSearch
*/
protected function ApplyDataFilters(): DBUnionSearch
{
if ($this->IsAllDataAllowed() || $this->IsDataFiltered()) {
return $this;
}
// Opt-in for joined classes filtering, otherwise fallback on DBSearch filtering
if (MetaModel::GetConfig()->Get('security.disable_joined_classes_filter') === true) {
return parent::ApplyDataFilters();
}
// Apply filters per sub-search
$aFilteredSearches = [];
foreach ($this->GetSearches() as $oSubSearch) {
// Recursively call ApplyDataFilters on sub-searches
$aFilteredSearches[] = $oSubSearch->ApplyDataFilters();
}
$oSearch = new DBUnionSearch($aFilteredSearches);
return $oSearch;
}
}

View File

@@ -26,7 +26,7 @@ class Event extends DBObject implements iDisplay
{
$aParams =
[
"category" => "core/cmdb,view_in_gui",
"category" => "core/cmdb,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -120,7 +120,7 @@ class EventNotification extends Event
{
$aParams =
[
"category" => "core/cmdb,view_in_gui",
"category" => "core/cmdb,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -154,7 +154,7 @@ class EventNotificationEmail extends EventNotification
{
$aParams =
[
"category" => "core/cmdb,view_in_gui",
"category" => "core/cmdb,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -190,7 +190,7 @@ class EventIssue extends Event
{
$aParams =
[
"category" => "core/cmdb,view_in_gui",
"category" => "core/cmdb,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -284,7 +284,7 @@ class EventWebService extends Event
{
$aParams =
[
"category" => "core/cmdb,view_in_gui",
"category" => "core/cmdb,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -319,7 +319,7 @@ class EventRestService extends Event
{
$aParams =
[
"category" => "core/cmdb,view_in_gui",
"category" => "core/cmdb,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -354,7 +354,7 @@ class EventLoginUsage extends Event
{
$aParams =
[
"category" => "core/cmdb,view_in_gui",
"category" => "core/cmdb,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -392,7 +392,7 @@ class EventOnObject extends Event
{
$aParams =
[
"category" => "core/cmdb,view_in_gui",
"category" => "core/cmdb,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",

View File

@@ -5,13 +5,13 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Application\Helper\ExportHelper;
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSetUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\ColumnUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactory;
use Combodo\iTop\Application\Helper\ExportHelper;
use Combodo\iTop\Application\WebPage\Page;
use Combodo\iTop\Application\WebPage\WebPage;
@@ -63,6 +63,8 @@ class ExcelBulkExport extends TabularBulkExport
// Export from the command line (or scripted) => default format is SQL, as in previous versions of iTop, unless specified otherwise
$this->aStatusInfo['date_format'] = utils::ReadParam('date_format', (string)AttributeDateTime::GetSQLFormat(), true, 'raw_data');
}
$this->aStatusInfo['ignore_excel_sanitization'] = (bool)utils::ReadParam('ignore_excel_sanitization', 0, true, utils::ENUM_SANITIZATION_FILTER_INTEGER);
}
public function EnumFormParts()
@@ -121,6 +123,10 @@ class ExcelBulkExport extends TabularBulkExport
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
$oFieldSetDate->AddSubBlock($oRadioCustom);
$oFieldSetSecurity = FieldSetUIBlockFactory::MakeStandard(Dict::S('Core:BulkExport:Security'));
$oMulticolumn->AddColumn(ColumnUIBlockFactory::MakeForBlock($oFieldSetSecurity));
$oFieldSetSecurity->AddSubBlock(ExportHelper::GetInputForSanitizeExcelExport());
$oP->add_ready_script(
<<<EOF
$('#form_part_xlsx_options').on('preview_updated', function() { FormatDatesInPreview('excel', 'xlsx'); });
@@ -216,6 +222,12 @@ EOF
}
}
}
// If the option to ignore Excel sanitization is not set or absent, sanitize the field
if (!(array_key_exists('ignore_excel_sanitization', $this->aStatusInfo)) || $this->aStatusInfo['ignore_excel_sanitization'] === false) {
return ExportHelper::SanitizeField($sRet, '');
}
return $sRet;
}

View File

@@ -296,6 +296,46 @@ class InlineImage extends DBObject
return $sHtml;
}
/**
* Replace <img> tags with a data-img-id attribute by the actual image in base64 representation
* so that the image can be displayed even if the download URL is not accessible (e.g. in unauthenticated approval templates)
*
* @param string $sHtml The HTML fragment to process
*
* @return String The modified HTML
* @since 3.2.3
*/
public static function ReplaceInlineImagesWithBase64Representation(string $sHtml): String
{
return preg_replace_callback(
'/<img\s+[^>]*'.static::DOM_ATTR_ID.'="(\d+)"[^>]*>/i',
function ($matches) {
// Extract inline image ID from the tag
$id = $matches[1];
try {
// Retrieve inline image
$oInline = MetaModel::GetObject(InlineImage::class, $id, true, true);
$oOrmDocument = $oInline->Get('contents');
// Replace src image by the base64 representation
$sInlineImageAsBase64 = base64_encode($oOrmDocument->GetData());
$sDataUri = 'data:'.$oOrmDocument->GetMimeType().';base64,'.$sInlineImageAsBase64;
$sImage = preg_replace('/src=["\'][^"\']+["\']/', 'src="'.$sDataUri.'"', $matches[0]);
// Remove sensitive information (the image ID and secret) from the tag
$sImage = preg_replace('/'.static::DOM_ATTR_ID.'="\d+"\s+'.static::DOM_ATTR_SECRET.'="\w+"/', '', $sImage);
} catch (Exception $e) {
$sImage = '<img src="" alt="'.Dict::S('UI:MissingInlineImage').'">';
}
return $sImage;
},
$sHtml
);
}
/**
* Add an extra attribute data-img-id for images which are based on an actual InlineImage
* so that we can later reconstruct the full "src" URL when needed

View File

@@ -350,15 +350,18 @@ class ormDocument
if (!is_object($oObj)) {
// If access to the document is not granted, check if the access to the host object is allowed
$oObj = MetaModel::GetObject($sClass, $id, false, true);
$bHasHostRights = false;
if ($oObj instanceof Attachment) {
$sItemClass = $oObj->Get('item_class');
$sItemId = $oObj->Get('item_id');
$oHost = MetaModel::GetObject($sItemClass, $sItemId, false, false);
if (!is_object($oHost)) {
$oObj = null;
if (is_object($oHost)) {
$bHasHostRights = true;
}
}
if (!is_object($oObj)) {
// We could neither read the object nor get a host object matching our rights
if ($bHasHostRights !== true) {
throw new Exception("Invalid id ($id) for class '$sClass' - the object does not exist or you are not allowed to view it");
}
}

View File

@@ -4,3 +4,4 @@
*/
@import "bulk-modify";
@import "bulk-export";

View File

@@ -0,0 +1,10 @@
/*
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
#form_part_csv_options:has(#ibo-sanitize-excel-export--input:checked), #form_part_xlsx_options:has(#ibo-sanitize-excel-export--input:checked){
#ibo-sanitize-excel-export--alert {
display: none;
}
}

View File

@@ -21,6 +21,8 @@ $ibo-search-form-panel--more-criteria--color: $ibo-color-blue-grey-800 !default;
$ibo-search-form-panel--more-criteria--background-color: $ibo-color-white-100 !default;
$ibo-search-form-panel--more-criteria--icon--color: $ibo-color-primary-600 !default;
$ibo-search-form-panel--more-criteria--border-color: $ibo-search-form-panel--criteria--border-color !default;
// calc is redundant but avoid SCSS min() from being used instead of CSS min()
$ibo-search-form-panel--criteria--max-height: calc(min(#{$ibo-size-750}, 50vh)) !default;
$ibo-search-form-panel--items--hover--color: $ibo-color-grey-200 !default;
@@ -278,9 +280,10 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
}
.sfc_form_group {
display: block;
margin-top: -1px;
z-index: -1;
display: flex;
flex-direction: column;
margin-top: -1px;
z-index: -1;
}
}
@@ -346,11 +349,15 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
display: none;
max-width: 450px;
width: max-content;
max-height: 520px;
max-height: $ibo-search-form-panel--criteria--max-height;
overflow-x: auto;
overflow-y: hidden;
.sfc_fg_operators {
display: flex;
flex-direction: column;
overflow: auto;
min-height: 0;
font-size: 12px;
.sfc_fg_operator {
@@ -387,6 +394,9 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
}
.sfc_opc_multichoices {
display: flex;
flex-direction: column;
height: 100%;
label > input {
vertical-align: text-top;
margin-left: $ibo-spacing-0;
@@ -398,7 +408,6 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
}
.sfc_opc_mc_items_wrapper {
max-height: 415px; /* Must be less than .sfc_form_group:max-height - .sfc_opc_mc_toggler:height - .sfc_opc_mc_filter:height */
overflow-y: auto;
margin: $ibo-spacing-0 -8px; /* Compensate .sfc_opc_multichoices side padding so the hover style can take the full with */
@@ -560,8 +569,14 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
&.search_form_criteria_enum {
.sfc_form_group {
.sfc_fg_operator_in {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
> label {
display: inline-block;
display: flex;
height: 100%;
min-height: 0;
width: 100%;
line-height: initial;
white-space: nowrap;

View File

@@ -201,8 +201,9 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
}
// N°7982 Default selectize stylesheet override
// N°9468 Dropdown content needs to be a few pixel shorter than the dropdown itself to avoid double scrollbar
.selectize-dropdown-content{
max-height: $ibo-input-select-selectize--dropdown--max-height;
max-height: calc(#{$ibo-input-select-selectize--dropdown--max-height} - 4px);
}
.selectize-dropdown.ui-menu .ui-state-active {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -68,47 +68,47 @@ $ibo-color-information-900: #0f172a !default;
$ibo-color-information-950: #020617 !default;
$ibo-lifecycle-new-state-primary-color: $ibo-color-information-600;
$ibo-lifecycle-new-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-neutral-state-primary-color: $ibo-color-information-600;
$ibo-lifecycle-neutral-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-waiting-state-primary-color: $ibo-color-yellow-700;
$ibo-lifecycle-waiting-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-success-state-primary-color: $ibo-color-blue-700;
$ibo-lifecycle-success-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-failure-state-primary-color: $ibo-color-orange-800;
$ibo-lifecycle-failure-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-frozen-state-primary-color: $ibo-color-information-200;
$ibo-lifecycle-frozen-state-secondary-color: $ibo-color-information-700;
$ibo-lifecycle-new-state-primary-color: $ibo-color-information-600 !default;
$ibo-lifecycle-new-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-neutral-state-primary-color: $ibo-color-information-600 !default;
$ibo-lifecycle-neutral-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-waiting-state-primary-color: $ibo-color-yellow-700 !default;
$ibo-lifecycle-waiting-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-success-state-primary-color: $ibo-color-blue-700 !default;
$ibo-lifecycle-success-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-failure-state-primary-color: $ibo-color-orange-800 !default;
$ibo-lifecycle-failure-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-frozen-state-primary-color: $ibo-color-information-200 !default;
$ibo-lifecycle-frozen-state-secondary-color: $ibo-color-information-700 !default;
$ibo-lifecycle-active-state-primary-color: $ibo-color-blue-700;
$ibo-lifecycle-active-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-inactive-state-primary-color: $ibo-color-yellow-700;
$ibo-lifecycle-inactive-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-active-state-primary-color: $ibo-color-blue-700 !default;
$ibo-lifecycle-active-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-inactive-state-primary-color: $ibo-color-yellow-700 !default;
$ibo-lifecycle-inactive-state-secondary-color: $ibo-color-white-100 !default;
$ibo-caselog-highlight-color-1: $ibo-color-blue-700;
$ibo-caselog-highlight-color-2: $ibo-color-yellow-700;
$ibo-caselog-highlight-color-3: $ibo-color-information-600;
$ibo-caselog-highlight-color-4: $ibo-color-yellow-500;
$ibo-caselog-highlight-color-5: $ibo-color-blue-500;
$ibo-caselog-highlight-color-6: $ibo-color-yellow-300;
$ibo-caselog-highlight-color-7: $ibo-color-blue-300;
$ibo-caselog-highlight-color-1: $ibo-color-blue-700 !default;
$ibo-caselog-highlight-color-2: $ibo-color-yellow-700 !default;
$ibo-caselog-highlight-color-3: $ibo-color-information-600 !default;
$ibo-caselog-highlight-color-4: $ibo-color-yellow-500 !default;
$ibo-caselog-highlight-color-5: $ibo-color-blue-500 !default;
$ibo-caselog-highlight-color-6: $ibo-color-yellow-300 !default;
$ibo-caselog-highlight-color-7: $ibo-color-blue-300 !default;
$ibo-input-wrapper--is-error--border-color: $ibo-color-warning-700;
$ibo-field-validation: $ibo-color-warning-800;
$ibo-input-wrapper--is-error--border-color: $ibo-color-warning-700 !default;
$ibo-field-validation: $ibo-color-warning-800 !default;
$ibo-navigation-menu--visual-hint--background-color: $ibo-color-blue-400;
$ibo-navigation-menu--visual-hint--background-color: $ibo-color-blue-400 !default;
$ibo-wizard-container--background-color: $ibo-color-information-200;
$ibo-wizard-container--border-color: $ibo-color-information-600;
$ibo-wizard-container--background-color: $ibo-color-information-200 !default;
$ibo-wizard-container--border-color: $ibo-color-information-600 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--background-color: $ibo-color-white-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--border: solid 2px $ibo-color-grey-500;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--background-color: $ibo-color-danger-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--border: solid 2px $ibo-color-danger-500;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--background-color: $ibo-color-warning-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--border: solid 2px $ibo-color-warning-500;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--background-color: $ibo-color-success-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--border: solid 2px $ibo-color-success-500;
$ibo-navigation-menu--notifications--item--new-message-indicator--background-color: $ibo-color-white-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--border: solid 2px $ibo-color-grey-500 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--background-color: $ibo-color-danger-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--border: solid 2px $ibo-color-danger-500 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--background-color: $ibo-color-warning-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--border: solid 2px $ibo-color-warning-500 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--background-color: $ibo-color-success-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--border: solid 2px $ibo-color-success-500 !default;
$ibo-notifications--view-all--item--unread--highlight--background-color: $ibo-color-blue-600;
$ibo-notifications--view-all--item--unread--highlight--background-color: $ibo-color-blue-600 !default;

View File

@@ -32,47 +32,47 @@ $ibo-color-information-900: #0f172a !default;
$ibo-color-information-950: #020617 !default;
$ibo-lifecycle-new-state-primary-color: $ibo-color-information-600;
$ibo-lifecycle-new-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-neutral-state-primary-color: $ibo-color-information-600;
$ibo-lifecycle-neutral-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-waiting-state-primary-color: $ibo-color-red-200;
$ibo-lifecycle-waiting-state-secondary-color: $ibo-color-red-800;
$ibo-lifecycle-success-state-primary-color: $ibo-color-blue-700;
$ibo-lifecycle-success-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-failure-state-primary-color: $ibo-color-red-800;
$ibo-lifecycle-failure-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-frozen-state-primary-color: $ibo-color-information-200;
$ibo-lifecycle-frozen-state-secondary-color: $ibo-color-information-700;
$ibo-lifecycle-new-state-primary-color: $ibo-color-information-600 !default;
$ibo-lifecycle-new-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-neutral-state-primary-color: $ibo-color-information-600 !default;
$ibo-lifecycle-neutral-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-waiting-state-primary-color: $ibo-color-red-200 !default;
$ibo-lifecycle-waiting-state-secondary-color: $ibo-color-red-800 !default;
$ibo-lifecycle-success-state-primary-color: $ibo-color-blue-700 !default;
$ibo-lifecycle-success-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-failure-state-primary-color: $ibo-color-red-800 !default;
$ibo-lifecycle-failure-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-frozen-state-primary-color: $ibo-color-information-200 !default;
$ibo-lifecycle-frozen-state-secondary-color: $ibo-color-information-700 !default;
$ibo-lifecycle-active-state-primary-color: $ibo-color-blue-700;
$ibo-lifecycle-active-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-inactive-state-primary-color: $ibo-color-red-700;
$ibo-lifecycle-inactive-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-active-state-primary-color: $ibo-color-blue-700 !default;
$ibo-lifecycle-active-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-inactive-state-primary-color: $ibo-color-red-700 !default;
$ibo-lifecycle-inactive-state-secondary-color: $ibo-color-white-100 !default;
$ibo-caselog-highlight-color-1: $ibo-color-blue-700;
$ibo-caselog-highlight-color-2: $ibo-color-red-700;
$ibo-caselog-highlight-color-3: $ibo-color-information-600;
$ibo-caselog-highlight-color-4: $ibo-color-red-500;
$ibo-caselog-highlight-color-5: $ibo-color-blue-500;
$ibo-caselog-highlight-color-6: $ibo-color-red-300;
$ibo-caselog-highlight-color-7: $ibo-color-blue-300;
$ibo-caselog-highlight-color-1: $ibo-color-blue-700 !default;
$ibo-caselog-highlight-color-2: $ibo-color-red-700 !default;
$ibo-caselog-highlight-color-3: $ibo-color-information-600 !default;
$ibo-caselog-highlight-color-4: $ibo-color-red-500 !default;
$ibo-caselog-highlight-color-5: $ibo-color-blue-500 !default;
$ibo-caselog-highlight-color-6: $ibo-color-red-300 !default;
$ibo-caselog-highlight-color-7: $ibo-color-blue-300 !default;
$ibo-input-wrapper--is-error--border-color: $ibo-color-pink-700;
$ibo-field-validation: $ibo-color-pink-800;
$ibo-input-wrapper--is-error--border-color: $ibo-color-pink-700 !default;
$ibo-field-validation: $ibo-color-pink-800 !default;
$ibo-navigation-menu--visual-hint--background-color: $ibo-color-pink-600;
$ibo-navigation-menu--visual-hint--background-color: $ibo-color-pink-600 !default;
$ibo-wizard-container--background-color: $ibo-color-information-200;
$ibo-wizard-container--border-color: $ibo-color-information-600;
$ibo-wizard-container--background-color: $ibo-color-information-200 !default;
$ibo-wizard-container--border-color: $ibo-color-information-600 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--background-color: $ibo-color-white-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--border: solid 2px $ibo-color-grey-500;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--background-color: $ibo-color-pink-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--border: solid 2px $ibo-color-pink-600;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--background-color: $ibo-color-warning-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--border: solid 2px $ibo-color-warning-400;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--background-color: $ibo-color-success-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--border: solid 2px $ibo-color-success-500;
$ibo-navigation-menu--notifications--item--new-message-indicator--background-color: $ibo-color-white-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--border: solid 2px $ibo-color-grey-500 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--background-color: $ibo-color-pink-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--border: solid 2px $ibo-color-pink-600 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--background-color: $ibo-color-warning-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--border: solid 2px $ibo-color-warning-400 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--background-color: $ibo-color-success-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--border: solid 2px $ibo-color-success-500 !default;
$ibo-notifications--view-all--item--unread--highlight--background-color: $ibo-color-pink-500;
$ibo-notifications--view-all--item--unread--highlight--background-color: $ibo-color-pink-500 !default;

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -8,5 +9,4 @@ namespace Combodo\iTop\DBTools\Exception;
class AuthenticationException extends \Exception
{
}
}

View File

@@ -41,6 +41,11 @@ SetupWebPage::AddModule(
'doc.manual_setup' => '',
'doc.more_information' => '',
// Security
'delegated_authentication_endpoints' => [
'ajax.backup.php',
],
// Default settings
//
'settings' => [

View File

@@ -242,8 +242,8 @@ try {
throw new SecurityException(Dict::S('iTopHub:FailAuthent'));
}
// First step: prepare the datamodel, if it fails, roll-back
$aSelectedExtensionCodes = utils::ReadParam('extension_codes', []);
$aSelectedExtensionDirs = utils::ReadParam('extension_dirs', []);
$aSelectedExtensionCodes = utils::ReadParam('extension_codes', [], false, utils::ENUM_SANITIZATION_FILTER_MODULE_CODE);
$aSelectedExtensionDirs = utils::ReadParam('extension_dirs', [], false, utils::ENUM_SANITIZATION_FILTER_MODULE_CODE);
$oRuntimeEnv = new HubRunTimeEnvironment('production', false); // use a temp environment: production-build
$oRuntimeEnv->MoveSelectedExtensions(APPROOT.'/data/downloaded-extensions/', $aSelectedExtensionDirs);
@@ -279,11 +279,24 @@ try {
$oRuntimeEnv = new RunTimeEnvironment('production', true);
try {
SetupLog::Info('Move to production 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')) {
throw new SecurityException(Dict::S('iTopHub:FailAuthent'));
}
} catch (Exception $e) {
if (file_exists(APPROOT.'data/hub/compile_authent')) {
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')."\n".$e->getMessage());
SetupLog::Error('Debug trace: '.$e->getTraceAsString());
ReportError($e->getMessage(), $e->getCode());
break;
}
try {
SetupLog::Info('Move to production starts...');
unlink(APPROOT.'data/hub/compile_authent');
// Load the "production" config file to clone & update it
$oConfig = new Config(APPCONF.'production/'.ITOP_CONFIG_FILE);

View File

@@ -37,6 +37,10 @@ SetupWebPage::AddModule(
// add your sample data XML files here,
],
'delegated_authentication_endpoints' => [
'ajax.php',
],
// Documentation
//
'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any

View File

@@ -1386,19 +1386,27 @@ class ObjectController extends BrickController
if ($oField instanceof DateTimeField) {
$oField->SetDateTimePickerWidgetParent($sDateTimePickerWidgetParent);
}
$sFieldRendererClass = BsLinkedSetFieldRenderer::GetFieldRendererClass($oField);
// View data
$sValue = $oAttDef->GetAsHTML($oNewLink->Get($sAttCode));
$aObjectData['attributes']['lnk__'.$sAttCode] = [
'object_class' => $sLinkClass,
'object_id' => $oNewLink->GetKey(),
'prefix' => 'lnk__',
'attribute_code' => $sAttCode,
'attribute_type' => get_class($oAttDef),
'value_html' => $sValue,
];
// If the field has a renderer we adjust view data
$sFieldRendererClass = BsLinkedSetFieldRenderer::GetFieldRendererClass($oField);
if ($sFieldRendererClass !== null) {
$oFieldRenderer = new $sFieldRendererClass($oField);
$oFieldOutput = $oFieldRenderer->Render();
$sValue = $oFieldOutput->GetHtml();
$aObjectData['attributes']['lnk__'.$sAttCode]['value_html'] = $oFieldOutput->GetHtml();
$aObjectData['attributes']['lnk__'.$sAttCode]['css_inline'] = $oFieldOutput->GetCss();
$aObjectData['attributes']['lnk__'.$sAttCode]['js_inline'] = $oFieldOutput->GetJs();
}
$aObjectData['attributes']['lnk__'.$sAttCode] = [
'att_code' => $sAttCode,
'value' => $sValue,
'css_inline' => $oFieldOutput->GetCss(),
'js_inline' => $oFieldOutput->GetJs(),
];
}
$aData['items'][] = $aObjectData;

View File

@@ -317,7 +317,7 @@ class BrowseBrickHelper
$aRow[$key] = [
'level_alias' => $key,
'id' => $sCurrentObjectId,
'name' => utils::EscapeHtml($value->Get($sNameAttCode)),
'name' => $value->Get($sNameAttCode),
'class' => $sCurrentObjectClass,
'action_rules_token' => $this->PrepareActionRulesForItems($aItems, $key, $aLevelsProperties),
'metadata' => [
@@ -476,7 +476,7 @@ class BrowseBrickHelper
$aItems[$sCurrentIndex] = [
'level_alias' => $aCurrentRowKeys[0],
'id' => $aCurrentRowValues[0]->GetKey(),
'name' => utils::EscapeHtml($aCurrentRowValues[0]->Get($aLevelsProperties[$aCurrentRowKeys[0]]['name_att'])),
'name' => $aCurrentRowValues[0]->Get($aLevelsProperties[$aCurrentRowKeys[0]]['name_att']),
'class' => get_class($aCurrentRowValues[0]),
'subitems' => [],
'filter_data' => $this->GetFilterData($aLevelsProperties[$aCurrentRowKeys[0]], $aCurrentRowKeys[0], $aCurrentRowValues[0]),

View File

@@ -80,11 +80,11 @@
// N°4662 - Surround tooltip with div to ensure text retrival
if( (data.tooltip !== undefined) && ($('<div>'+data.tooltip+'</div>').text() !== ''))
{
cellElem.html( $('<span></span>').attr('data-tooltip-content', data.tooltip).attr('data-tooltip-html-enabled', true).html(data.name).prop('outerHTML') );
cellElem.html( $('<span></span>').attr('data-tooltip-content', data.tooltip).attr('data-tooltip-html-enabled', true).text(data.name).prop('outerHTML') );
}
else
{
cellElem.html(data.name);
cellElem.text(data.name);
}
// Building actions

View File

@@ -197,7 +197,7 @@
if( (item.name !== undefined) && (item.name !== '') )
{
iItemFlags += 1;
textWrapperElem.append( $('<div></div>').addClass('mosaic-item-name').html(item.name) );
textWrapperElem.append( $('<div></div>').addClass('mosaic-item-name').text(item.name) );
}
// - Adding description
if( (item.description !== undefined) && (item.description !== '') )

View File

@@ -233,7 +233,9 @@
{
case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_DRILLDOWN') }}':
spanElem.addClass('tree-toggle');
nameElem.html('<span class="glyphicon '+sNodeCollapsedClass+'" aria-hidden="true"></span><span class="list-group-item-text">'+nameElem.text()+'</span>');
var iconElem = $('<span></span>').addClass('glyphicon '+sNodeCollapsedClass).attr('aria-hidden', 'true');
var textElem = $('<span></span>').addClass('list-group-item-text').text(nameElem.text());
nameElem.empty().append(iconElem).append(textElem);
break;
case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_VIEW') }}':
url = '{{ app.url_generator.generate('p_object_view', {'sObjectClass': '-objectClass-', 'sObjectId': '-objectId-'})|raw }}'.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);

View File

@@ -11,7 +11,7 @@
</div>
<div id="export-feedback">
<p id="export-excel-warning" class="alert alert-warning" role="alert">{{ 'UI:Bulk:Export:MaliciousInjection:Alert:Message'|dict_format(sWikiUrl)|raw }}</p>
<p id="export-excel-warning" class="alert alert-warning" role="alert">{{ 'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message'|dict_format(sWikiUrl)|raw }}</p>
<p class="export-message" style="text-align:center;">{{ 'ExcelExport:PreparingExport'|dict_s }}</p>
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: 0%"

View File

@@ -186,6 +186,7 @@
<group id="AdminSysReadOnly" _delta="define">
<classes>
<class id="ItopFenceLogin"/>
<class id="ModuleInstallation"/>
</classes>
</group>
<group id="AdminSys" _delta="define">
@@ -195,6 +196,11 @@
<class id="RessourceHybridAuthMenu"/>
</classes>
</group>
<group id="Event" _delta="define">
<classes>
<class id="Event"/>
</classes>
</group>
</groups>
<profiles>
<profile id="117" _delta="define">
@@ -290,6 +296,16 @@
<action id="stimulus:ev_close">allow</action>
</actions>
</group>
<group id="Event">
<actions>
<action id="action:read">allow</action>
<action id="action:bulk read">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="action:delete">allow</action>
<action id="action:bulk delete">allow</action>
</actions>
</group>
</groups>
</profile>
<profile id="3" _delta="define">

View File

@@ -1394,6 +1394,7 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [
'UI:SelectInlineImageToUpload' => 'Vyberte obrázek',
'UI:AvailableInlineImagesLegend' => 'Dostupné obrázky',
'UI:NoInlineImage' => 'Na serveru není dostupný žádný obrázek. Nahrajte nějaký pomocí tlačítka výše.',
'UI:MissingInlineImage' => 'Chybějící obrázek',
'UI:ToggleFullScreen' => 'Přepnout zobrazení',
'UI:Button:ResetImage' => 'Obnovit původní obrázek',
'UI:Button:RemoveImage' => 'Odebrat obrázek',

View File

@@ -1397,6 +1397,7 @@ Ved tilknytningen til en trigger, bliver hver handling tildelt et "rækkefølge"
'UI:SelectInlineImageToUpload' => 'Select the image to upload~~',
'UI:AvailableInlineImagesLegend' => 'Available images~~',
'UI:NoInlineImage' => 'There is no image available on the server. Use the "Browse" button above to select an image from your computer and upload it to the server.~~',
'UI:MissingInlineImage' => 'Manglende billede',
'UI:ToggleFullScreen' => 'Toggle Maximize / Minimize~~',
'UI:Button:ResetImage' => 'Recover the previous image~~',
'UI:Button:RemoveImage' => 'Remove the image~~',

View File

@@ -1394,6 +1394,7 @@ Wenn Aktionen mit Trigger verknüpft sind, bekommt jede Aktion eine Auftragsnumm
'UI:SelectInlineImageToUpload' => 'Wähle das Bild für den Upload aus',
'UI:AvailableInlineImagesLegend' => 'Verfügbare Bilder',
'UI:NoInlineImage' => 'Es sind keine Bilder auf dem Server verfügbar. Nutze den "Durchsuchen" Button oben, um ein Bild vom Computer hochzuladen.',
'UI:MissingInlineImage' => 'Bild fehlt',
'UI:ToggleFullScreen' => 'Maximieren / Minimieren',
'UI:Button:ResetImage' => 'Vorheriges Bild wiederherstellen',
'UI:Button:RemoveImage' => 'Bild löschen',

View File

@@ -1471,6 +1471,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:SelectInlineImageToUpload' => 'Select the image to upload',
'UI:AvailableInlineImagesLegend' => 'Available images',
'UI:NoInlineImage' => 'There is no image available on the server. Use the "Browse" button above to select an image from your computer and upload it to the server.',
'UI:MissingInlineImage' => 'Missing image',
'UI:ToggleFullScreen' => 'Toggle Maximize / Minimize',
'UI:Button:ResetImage' => 'Recover the previous image',

View File

@@ -1471,6 +1471,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:SelectInlineImageToUpload' => 'Select the image to upload',
'UI:AvailableInlineImagesLegend' => 'Available images',
'UI:NoInlineImage' => 'There is no image available on the server. Use the "Browse" button above to select an image from your computer and upload it to the server.',
'UI:MissingInlineImage' => 'Missing image',
'UI:ToggleFullScreen' => 'Toggle Maximise / Minimise',
'UI:Button:ResetImage' => 'Recover the previous image',

View File

@@ -1397,6 +1397,7 @@ Cuando se asocien con un disparador, cada acción recibe un número de "orden",
'UI:SelectInlineImageToUpload' => 'Seleccione la imágen a subir',
'UI:AvailableInlineImagesLegend' => 'Imágenes disponibles',
'UI:NoInlineImage' => 'No hay imágenes disponibles en el servidor. Use el botón "Seleccionar archivo" para seleccionar una imágen de su equipo local y subirla al servidor.',
'UI:MissingInlineImage' => 'Imagen faltante',
'UI:ToggleFullScreen' => 'Cambiar Maximizar / Minimizar',
'UI:Button:ResetImage' => 'Recuperar imágen previa',
'UI:Button:RemoveImage' => 'Remover imágen',

View File

@@ -1398,6 +1398,7 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:SelectInlineImageToUpload' => 'Sélectionnez l\'image à ajouter',
'UI:AvailableInlineImagesLegend' => 'Images disponibles',
'UI:NoInlineImage' => 'Il n\'y a aucune image de disponible sur le serveur. Utilisez le bouton "Parcourir" (ci-dessus) pour sélectionner une image sur votre ordinateur et la télécharger sur le serveur.',
'UI:MissingInlineImage' => 'Image introuvable',
'UI:ToggleFullScreen' => 'Agrandir / Minimiser',
'UI:Button:ResetImage' => 'Récupérer l\'image initiale',
'UI:Button:RemoveImage' => 'Supprimer l\'image',

View File

@@ -1400,6 +1400,7 @@ A művelet eseményindítóhoz rendelésekor kap egy sorszámot , amely meghatá
'UI:SelectInlineImageToUpload' => 'Válasszon egy képet',
'UI:AvailableInlineImagesLegend' => 'Elérhető képek',
'UI:NoInlineImage' => 'A szerveren nincs elérhető kép. Használja a fenti "Tallózás" gombot egy kép kiválasztásához a számítógépéről, és töltse fel a szerverre.',
'UI:MissingInlineImage' => 'Hiányzó kép',
'UI:ToggleFullScreen' => 'Maximalizálás / Minimalizálás',
'UI:Button:ResetImage' => 'Az előző kép visszaállítása',
'UI:Button:RemoveImage' => 'Kép eltávolítása',

View File

@@ -1399,6 +1399,7 @@ Quando è associata a un trigger, a ogni azione è assegnato un numero "ordine",
'UI:SelectInlineImageToUpload' => 'Seleziona l\'immagine da caricare',
'UI:AvailableInlineImagesLegend' => 'Immagini disponibili',
'UI:NoInlineImage' => 'Non ci sono immagini disponibili sul server. Utilizza il pulsante "Sfoglia" sopra per selezionare un\'immagine dal tuo computer e caricarla sul server.',
'UI:MissingInlineImage' => 'Immagine mancante',
'UI:ToggleFullScreen' => 'Attiva/Disattiva a schermo intero',
'UI:Button:ResetImage' => 'Ripristina l\'immagine precedente',
'UI:Button:RemoveImage' => 'Rimuovi l\'immagine',

View File

@@ -1401,6 +1401,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', [
'UI:SelectInlineImageToUpload' => 'Select the image to upload~~',
'UI:AvailableInlineImagesLegend' => 'Available images~~',
'UI:NoInlineImage' => 'There is no image available on the server. Use the "Browse" button above to select an image from your computer and upload it to the server.~~',
'UI:MissingInlineImage' => 'Missing image~~',
'UI:ToggleFullScreen' => 'Toggle Maximize / Minimize~~',
'UI:Button:ResetImage' => 'Recover the previous image~~',
'UI:Button:RemoveImage' => 'Remove the image~~',

View File

@@ -1400,6 +1400,7 @@ Bij die koppeling wordt aan elke actie een volgorde-nummer gegeven. Dit bepaalt
'UI:SelectInlineImageToUpload' => 'Selecteer een afbeelding om te uploaden',
'UI:AvailableInlineImagesLegend' => 'Beschikbare afbeeldingen',
'UI:NoInlineImage' => 'Er is geen afbeelding beschikbaar op de server. Gebruik de "Afbeeldingen doorbladeren..." knop hierboven om een afbeelding te kiezen op je toestel.',
'UI:MissingInlineImage' => 'Ontbrekende afbeelding',
'UI:ToggleFullScreen' => 'Minimaliseren / Maximaliseren',
'UI:Button:ResetImage' => 'Vorige afbeelding herstellen',
'UI:Button:RemoveImage' => 'Afbeelding verwijderen',

View File

@@ -1408,6 +1408,7 @@ W przypadku powiązania z wyzwalaczem, każde działanie otrzymuje numer "porzą
'UI:SelectInlineImageToUpload' => 'Wybierz obraz do przesłania',
'UI:AvailableInlineImagesLegend' => 'Dostępne obrazy',
'UI:NoInlineImage' => 'Na serwerze nie ma obrazu. Użyj przycisku "Przeglądaj" powyżej, aby wybrać obraz ze swojego komputera i przesłać go na serwer.',
'UI:MissingInlineImage' => 'Brakujący obraz',
'UI:ToggleFullScreen' => 'Przełącz Maksymalizuj / Minimalizuj',
'UI:Button:ResetImage' => 'Odzyskaj poprzedni obraz',
'UI:Button:RemoveImage' => 'Usuń obraz',

View File

@@ -1393,6 +1393,7 @@ Quando associada a um gatilho, cada ação recebe um número de "ordem", especif
'UI:SelectInlineImageToUpload' => 'Selecione a imagem para enviar',
'UI:AvailableInlineImagesLegend' => 'Imagens disponíveis',
'UI:NoInlineImage' => 'Não há imagem disponível no servidor. Use o botão "Escolher arquivo" acima para selecionar uma imagem do seu computador e fazer o upload para o servidor',
'UI:MissingInlineImage' => 'Imagem ausente',
'UI:ToggleFullScreen' => 'Alternancia Maximizar / Minimizar',
'UI:Button:ResetImage' => 'Recupere a imagem anterior',
'UI:Button:RemoveImage' => 'Remover a imagem',

View File

@@ -1397,6 +1397,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', [
'UI:SelectInlineImageToUpload' => 'Выберите изображение для загрузки',
'UI:AvailableInlineImagesLegend' => 'Доступные изображения',
'UI:NoInlineImage' => 'На сервере нет доступных изображений. С помощью кнопки "Обзор..." выше выберите изображение на вашем компьютере, чтобы загрузить его на сервер.',
'UI:MissingInlineImage' => 'Отсутствует изображение',
'UI:ToggleFullScreen' => 'Развернуть / Свернуть',
'UI:Button:ResetImage' => 'Восстановить предыдущее изображение',
'UI:Button:RemoveImage' => 'Удалить изображение',

View File

@@ -1398,6 +1398,7 @@ Keď sú priradené spúštačom, každej akcii je dané číslo "príkazu", šp
'UI:SelectInlineImageToUpload' => 'Select the image to upload~~',
'UI:AvailableInlineImagesLegend' => 'Available images~~',
'UI:NoInlineImage' => 'There is no image available on the server. Use the "Browse" button above to select an image from your computer and upload it to the server.~~',
'UI:MissingInlineImage' => 'Missing image~~',
'UI:ToggleFullScreen' => 'Toggle Maximize / Minimize~~',
'UI:Button:ResetImage' => 'Recover the previous image~~',
'UI:Button:RemoveImage' => 'Remove the image~~',

View File

@@ -1401,6 +1401,7 @@ Tetikleme gerçekleştiriğinde işlemler tanımlanan sıra numarası ile gerçe
'UI:SelectInlineImageToUpload' => 'Select the image to upload~~',
'UI:AvailableInlineImagesLegend' => 'Available images~~',
'UI:NoInlineImage' => 'There is no image available on the server. Use the "Browse" button above to select an image from your computer and upload it to the server.~~',
'UI:MissingInlineImage' => 'Missing image~~',
'UI:ToggleFullScreen' => 'Toggle Maximize / Minimize~~',
'UI:Button:ResetImage' => 'Recover the previous image~~',
'UI:Button:RemoveImage' => 'Remove the image~~',

View File

@@ -13,5 +13,9 @@
Dict::Add('CS CZ', 'Czech', 'Čeština', [
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
'Core:BulkExport:Security' => 'Security~~',
]);

View File

@@ -13,5 +13,9 @@
Dict::Add('DA DA', 'Danish', 'Dansk', [
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
'Core:BulkExport:Security' => 'Security~~',
]);

View File

@@ -13,5 +13,9 @@
Dict::Add('DE DE', 'German', 'Deutsch', [
'UI:Bulk:modify:IncompatibleAttribute' => 'Dieses Attribut kann in einer Massenänderung nicht bearbeitet werden.',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel-Sicherheitswarnung',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Das Öffnen einer Datei mit nicht vertrauenswürdigen Daten in Microsoft Excel kann zu einer Formel-Injektion führen. Stellen Sie sicher, dass Ihre Excel-Einstellungen so konfiguriert sind, dass Dateien sicher verarbeitet werden. <a href="%1$s">Erfahren Sie mehr in unserer Dokumentation.</a>',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Das Öffnen einer Datei mit nicht vertrauenswürdigen Daten in Microsoft Excel kann zu einer Formel-Injektion führen. Stellen Sie sicher, dass Ihre Excel-Einstellungen so konfiguriert sind, dass Dateien sicher verarbeitet werden. <a href="%1$s" target="_blank">Erfahren Sie mehr in unserer Dokumentation.</a>',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
'Core:BulkExport:Security' => 'Security~~',
]);

View File

@@ -23,5 +23,9 @@ Dict::Add('EN US', 'English', 'English', [
// Bulk modify
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s" target="_blank">Learn more in our documentation.</a>',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.',
'Core:BulkExport:Security' => 'Security',
]);

View File

@@ -10,5 +10,9 @@ Dict::Add('EN GB', 'British English', 'British English', [
// Bulk modify
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s" target="_blank">Learn more in our documentation.</a>',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.',
'Core:BulkExport:Security' => 'Security',
]);

View File

@@ -11,5 +11,9 @@
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
'UI:Bulk:modify:IncompatibleAttribute' => 'Este atributo no se puede editar en contexto masivo',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Advertencia de seguridad de Excel',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Abrir un archivo con datos que no son de confianza en Microsoft Excel puede provocar la inyección de fórmulas. Asegúrese de que la configuración de Excel esté configurada para manejar archivos de forma segura. <a href="%1$s">Obtenga más información en nuestra documentación.</a>',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Abrir un archivo con datos que no son de confianza en Microsoft Excel puede provocar la inyección de fórmulas. Asegúrese de que la configuración de Excel esté configurada para manejar archivos de forma segura. <a href="%1$s" target="_blank">Obtenga más información en nuestra documentación.</a>',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
'Core:BulkExport:Security' => 'Security~~',
]);

View File

@@ -13,5 +13,9 @@
Dict::Add('FR FR', 'French', 'Français', [
'UI:Bulk:modify:IncompatibleAttribute' => 'Cet attribut ne peut être édité dans une modification en masse',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Avertissement sur la sécurité d\'Excel',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'L\'ouverture d\'un fichier contenant des données non fiables dans Microsoft Excel peut entraîner l\'injection de formules. Assurez-vous que vos paramètres Excel sont configurés pour traiter les fichiers en toute sécurité. <a href="%1$s">Pour en savoir plus, consultez notre documentation.</a>',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'L\'ouverture d\'un fichier contenant des données non fiables dans Microsoft Excel peut entraîner l\'injection de formules. Assurez-vous que vos paramètres Excel sont configurés pour traiter les fichiers en toute sécurité. <a href="%1$s" target="_blank">Pour en savoir plus, consultez notre documentation.</a>',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Certaines valeurs ont été échappées pour prévenir de potentielles failles de sécurités dans Microsoft Excel. <a href="%1$s" target="_blank">Pour en savoir plus, consultez notre documentation</a>',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitiser les valeurs potentiellement dangereuses',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'Lorsqu\'elle est activée, les valeurs potentiellement dangereuses seront sanitizées lors de l\'exportation. Cela empêchera Microsoft Excel de les interpréter comme des formules. Notez que cela peut altérer les données originales en les préfixant avec une simple quote (\') pour s\'assurer qu\'elles soient traitées comme du texte.',
'Core:BulkExport:Security' => 'Sécurité',
]);

View File

@@ -13,5 +13,9 @@
Dict::Add('HU HU', 'Hungarian', 'Magyar', [
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
'Core:BulkExport:Security' => 'Security~~',
]);

View File

@@ -13,5 +13,9 @@
Dict::Add('IT IT', 'Italian', 'Italiano', [
'UI:Bulk:modify:IncompatibleAttribute' => 'Questo attributo non può essere modificato nel contesto di modifica bulk',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Avviso di sicurezza di Excel',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'L\'apertura di un file con dati non fidati in Microsoft Excel potrebbe comportare l\'iniezione di formule. Assicurati che le impostazioni di Excel siano configurate per gestire i file in modo sicuro. <a href="%1$s">Ulteriori informazioni nella nostra documentazione.</a>',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'L\'apertura di un file con dati non fidati in Microsoft Excel potrebbe comportare l\'iniezione di formule. Assicurati che le impostazioni di Excel siano configurate per gestire i file in modo sicuro. <a href="%1$s" target="_blank">Ulteriori informazioni nella nostra documentazione.</a>',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
'Core:BulkExport:Security' => 'Security~~',
]);

View File

@@ -13,5 +13,9 @@
Dict::Add('JA JP', 'Japanese', '日本語', [
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
'Core:BulkExport:Security' => 'Security~~',
]);

View File

@@ -13,5 +13,9 @@
Dict::Add('NL NL', 'Dutch', 'Nederlands', [
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
'Core:BulkExport:Security' => 'Security~~',
]);

View File

@@ -13,5 +13,9 @@
Dict::Add('PL PL', 'Polish', 'Polski', [
'UI:Bulk:modify:IncompatibleAttribute' => 'Tego atrybutu nie można edytować zbiorczo',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Ostrzeżenie dotyczące bezpieczeństwa programu Excel',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Otwarcie pliku z niezaufanymi danymi w programie Microsoft Excel może spowodować wstrzyknięcie formuły. Upewnij się, że ustawienia programu Excel są skonfigurowane tak, aby bezpiecznie obsługiwać pliki. <a href="%1$s">Dowiedz się więcej w naszej dokumentacji.</a>',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Otwarcie pliku z niezaufanymi danymi w programie Microsoft Excel może spowodować wstrzyknięcie formuły. Upewnij się, że ustawienia programu Excel są skonfigurowane tak, aby bezpiecznie obsługiwać pliki. <a href="%1$s" target="_blank">Dowiedz się więcej w naszej dokumentacji.</a>',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
'Core:BulkExport:Security' => 'Security~~',
]);

View File

@@ -13,5 +13,9 @@
Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
'Core:BulkExport:Security' => 'Security~~',
]);

View File

@@ -13,5 +13,9 @@
Dict::Add('RU RU', 'Russian', 'Русский', [
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
'Core:BulkExport:Security' => 'Security~~',
]);

View File

@@ -13,5 +13,9 @@
Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
'Core:BulkExport:Security' => 'Security~~',
]);

View File

@@ -13,5 +13,9 @@
Dict::Add('TR TR', 'Turkish', 'Türkçe', [
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
'Core:BulkExport:Security' => 'Security~~',
]);

View File

@@ -22,5 +22,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
// Bulk modify
'UI:Bulk:modify:IncompatibleAttribute' => '此属性无法在批量操作中编辑',
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel 安全警告',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => '在 Microsoft Excel 中打开不信任的文件可能导致公式注入. 请确保 Excel 设置能够安全的处理该文件. <a href="%1$s">进入我们的文档了解更多.</a>',
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => '在 Microsoft Excel 中打开不信任的文件可能导致公式注入. 请确保 Excel 设置能够安全的处理该文件. <a href="%1$s" target="_blank">进入我们的文档了解更多.</a>',
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
'Core:BulkExport:Security' => 'Security~~',
]);

View File

@@ -1398,6 +1398,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
'UI:SelectInlineImageToUpload' => '选择要上传的图片',
'UI:AvailableInlineImagesLegend' => '可用的图片',
'UI:NoInlineImage' => '服务器上没有图片. 使用上面的 "浏览" 按钮, 从您的电脑上选择并上传到服务器.',
'UI:MissingInlineImage' => '缺少图片',
'UI:ToggleFullScreen' => '切换最大化/最小化',
'UI:Button:ResetImage' => '恢复之前的图片',
'UI:Button:RemoveImage' => '移除图片',

View File

@@ -171,7 +171,7 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
// To avoid dropdown to be cut by the container's overflow hidden rule
dropdownParent: 'body',
onDropdownOpen: function (oDropdownElem) {
me.UpdateDropdownPosition(this.$control, oDropdownElem);
me.UpdateDropdownPosition(this.$control, oDropdownElem, this.$dropdown_content);
},
});
let $selectize = $select[0].selectize; // This stores the selectize object to a variable (with name 'selectize')
@@ -314,13 +314,14 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
};
/**
* Update the dropdown's position so it always fits in the screen
*
* @param {object} oControlElem jQuery object representing the "control" input (= where the user types) of the external key
* @param {object} oDropdownElem jQuery object representing the results dropdown
* @return {void}
*/
this.UpdateDropdownPosition = function (oControlElem, oDropdownElem) {
* Update the dropdown's position so it always fits in the screen
*
* @param {object} oControlElem jQuery object representing the "control" input (= where the user types) of the external key
* @param {object} oDropdownElem jQuery object representing the results dropdown
* @param {object|undefined} oDropdownContentElem
* @return {void}
*/
this.UpdateDropdownPosition = function (oControlElem, oDropdownElem, oDropdownContentElem) {
// First fix width to ensure it's not too long
const fControlWidth = oControlElem.outerWidth();
oDropdownElem.css('width', fControlWidth);
@@ -328,6 +329,13 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
// Then, fix height / position to ensure it's within the viewport
const fWindowHeight = window.innerHeight;
// Clear previously set rule so the comparison is done with dropdown real height
oDropdownElem.css('max-height', '');
if(oDropdownContentElem) {
oDropdownContentElem.css('max-height', '');
}
const fControlTopY = oControlElem.offset().top;
const fControlHeight = oControlElem.outerHeight();
@@ -338,14 +346,38 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
if (fDropdownBottomY > fWindowHeight) {
// Set dropdown max-height to 1/3 of the screen, this way we are sure the dropdown will fit in either the top / bottom half of the screen
oDropdownElem.css('max-height', '30vh');
oDropdownElem.css({
maxHeight: '30vh',
});
fDropdownHeight = oDropdownElem.outerHeight();
// Position dropdown above input if not enough space on the bottom part of the screen
// N°9468 Dropdown content needs to be a few pixel shorter than the dropdown itself to avoid double scrollbar
if(oDropdownContentElem) {
oDropdownContentElem.css('max-height', 'calc(30vh - 4px)');
}
/* Position dropdown above input if not enough space on the bottom part of the screen
Doesn't seem to work with selectize as an internal plugin "auto_position" refreshes the top position after
this method is called, input set use a custom plugin to avoid fix this issue "plugin_combodo_auto_position"
This would need to take the potential 4px difference into account if this is fixed.
*/
if ((fDropdownTopY / fWindowHeight) > 0.6) {
oDropdownElem.css('top', fDropdownTopY - fDropdownHeight - fControlHeight);
}
oDropdownElem.css({
top: fDropdownTopY - fDropdownHeight - fControlHeight,
borderTop: oDropdownElem.css('border-bottom')
});
}
else {
oDropdownElem.css({
borderTop: 'none'
})
}
}
else {
oDropdownElem.css({
borderTop: 'none'
})
}
};
this.ManageScroll = function () {
if ($('#label_'+me.id).scrollParent()[0].tagName != 'HTML') {

View File

@@ -189,11 +189,14 @@ $(function()
this.buildData.script_code = '';
this.buildData.style_code = '';
for (var i in oData.updated_fields)
for (let i in oData.updated_fields)
{
var oUpdatedField = oData.updated_fields[i];
this.options.fields_list[oUpdatedField.id] = oUpdatedField;
this._prepareField(oUpdatedField.id);
const oUpdatedField = oData.updated_fields[i];
const oPreviousField = this.options.fields_list[oUpdatedField.id];
if (!oPreviousField || JSON.stringify(oPreviousField) !== JSON.stringify(oUpdatedField)) {
this.options.fields_list[oUpdatedField.id] = oUpdatedField;
this._prepareField(oUpdatedField.id);
}
}
// Adding code to the dom

View File

@@ -301,88 +301,112 @@ function ValidateField(sFieldId, sPattern, bMandatory, sFormId, nullValue, origi
return true; // Do not stop propagation ??
}
function ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue)
function EvaluateCKEditorValidation(oOptions)
{
let oField = $('#'+sFieldId);
let oField = $('#'+oOptions.sFieldId);
if (oField.length === 0) {
return false;
}
let oCKEditor = CombodoCKEditorHandler.GetInstanceSynchronous('#'+sFieldId);
let oCKEditor = CombodoCKEditorHandler.GetInstanceSynchronous('#'+oOptions.sFieldId);
let bValid = true;
let sExplain = '';
let sTextContent;
let sTextOriginalContents;
var bValid;
var sExplain = '';
if (oField.prop('disabled')) {
bValid = true; // disabled fields are not checked
} else {
// If the CKEditor is not yet loaded, we need to wait for it to be ready
// but as we need this function to be synchronous, we need to call it again when the CKEditor is ready
if (oCKEditor === undefined){
CombodoCKEditorHandler.GetInstance('#'+sFieldId).then((oCKEditor) => {
ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue);
CombodoCKEditorHandler.GetInstance('#'+oOptions.sFieldId).then((oCKEditor) => {
oOptions.onRetry();
});
return;
return false;
}
let sTextContent;
let sFormattedContent = oCKEditor.getData();
// Get the contents without the tags
// Check if we have a formatted content that is HTML, otherwise we just have plain text, and we can use it directly
let sFormattedContent = oCKEditor.getData();
sTextContent = $(sFormattedContent).length > 0 ? $(sFormattedContent).text() : sFormattedContent;
if (sTextContent === '') {
if (sTextContent === '')
{
// No plain text, maybe there is just an image
let oImg = $(sFormattedContent).find("img");
if (oImg.length !== 0) {
let oImg = $(sFormattedContent).find('img');
if (oImg.length !== 0)
{
sTextContent = 'image';
}
}
// Get the original value without the tags
let oFormattedOriginalContents = (originalValue !== undefined) ? $('<div></div>').html(originalValue) : undefined;
let sTextOriginalContents = (oFormattedOriginalContents !== undefined) ? oFormattedOriginalContents.text() : undefined;
let oFormattedOriginalContents = (oOptions.sOriginalValue !== undefined) ? $('<div></div>').html(oOptions.sOriginalValue) : undefined;
sTextOriginalContents = (oFormattedOriginalContents !== undefined) ? oFormattedOriginalContents.text() : undefined;
if (bMandatory && (sTextContent === nullValue)) {
bValid = false;
sExplain = Dict.S('UI:ValueMustBeSet');
} else if ((sTextOriginalContents !== undefined) && (sTextContent === sTextOriginalContents)) {
bValid = false;
if (sTextOriginalContents === nullValue) {
sExplain = Dict.S('UI:ValueMustBeSet');
} else {
// Note: value change check is not working well yet as the HTML to Text conversion is not exactly the same when done from the PHP value or the CKEditor value.
sExplain = Dict.S('UI:ValueMustBeChanged');
}
} else {
bValid = true;
if (oOptions.validate !== undefined) {
let oValidation = oOptions.validate(sTextContent, sTextOriginalContents);
bValid = oValidation.bValid;
sExplain = oValidation.sExplain;
}
// Put and event to check the field when the content changes, remove the event right after as we'll call this same function again, and we don't want to call the event more than once (especially not ^2 times on each call)
// Put an event to check the field when the content changes, remove the event right after as we'll call this same function again, and we don't want to call the event more than once (especially not ^2 times on each call)
oCKEditor.model.document.once('change:data', (event) => {
ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue);
oOptions.onChange();
});
}
ReportFieldValidationStatus(sFieldId, sFormId, bValid, sExplain);
ReportFieldValidationStatus(oOptions.sFieldId, oOptions.sFormId, bValid, sExplain);
return bValid;
}
function ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue)
{
return EvaluateCKEditorValidation({
sFieldId: sFieldId,
sFormId: sFormId,
sOriginalValue: originalValue,
onRetry: function() {
ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue);
},
onChange: function() {
ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue);
},
validate: function(sTextContent, sTextOriginalContents) {
var bValid;
var sExplain = '';
if (bMandatory && (sTextContent === nullValue)) {
bValid = false;
sExplain = Dict.S('UI:ValueMustBeSet');
} else if ((sTextOriginalContents !== undefined) && (sTextContent === sTextOriginalContents)) {
bValid = false;
if (sTextOriginalContents === nullValue) {
sExplain = Dict.S('UI:ValueMustBeSet');
} else {
// Note: value change check is not working well yet as the HTML to Text conversion is not exactly the same when done from the PHP value or the CKEditor value.
sExplain = Dict.S('UI:ValueMustBeChanged');
}
} else {
bValid = true;
}
return {bValid: bValid, sExplain: sExplain};
}
});
}
function ResetPwd(id)
{
// Reset the values of the password fields
$('#'+id).val('*****');
$('#'+id+'_confirm').val('*****');
// And reset the flag, to tell it that the password remains unchanged
$('#'+id+'_changed').val(0);
// Visual feedback, None when it's Ok
$('#v_'+id).html('');
// Reset the values of the password fields
$('#'+id).val('*****');
$('#'+id+'_confirm').val('*****');
// And reset the flag, to tell it that the password remains unchanged
$('#'+id+'_changed').val(0);
// Visual feedback, None when it's Ok
$('#v_'+id).html('');
}
// Called whenever the content of a one way encrypted password changes
function PasswordFieldChanged(id)
{
// Set the flag, to tell that the password changed
$('#'+id+'_changed').val(1);
// Set the flag, to tell that the password changed
$('#'+id+'_changed').val(1);
}
// Special validation function for one way encrypted password fields
@@ -415,37 +439,48 @@ function ValidatePasswordField(id, sFormId)
// to determine if the field is empty or not
function ValidateCaseLogField(sFieldId, bMandatory, sFormId, nullValue, originalValue)
{
var bValid = true;
var sExplain = '';
var sTextContent;
if ($('#'+sFieldId).prop('disabled'))
{
bValid = true; // disabled fields are not checked
}
else
{
// Get the contents (with tags)
// Note: For CaseLog we can't retrieve the formatted contents from CKEditor (unlike in ValidateCKEditorField() method) because of the place holder.
sTextContent = $('#' + sFieldId).val();
var count = $('#'+sFieldId+'_count').val();
return EvaluateCKEditorValidation({
sFieldId: sFieldId,
sFormId: sFormId,
sOriginalValue: originalValue,
onRetry: function() {
ValidateCaseLogField(sFieldId, bMandatory, sFormId, nullValue, originalValue);
},
onChange: function() {
ValidateCaseLogField(sFieldId, bMandatory, sFormId, nullValue, originalValue);
},
validate: function(sTextContent, sTextOriginalContents) {
var bValid;
var sExplain = '';
// CaseLog is special: history count matters when deciding if the field is empty
var count = $('#'+sFieldId+'_count').val();
if (bMandatory && (count == 0) && (sTextContent == nullValue))
{
// No previous entry and no content typed
bValid = false;
sExplain = Dict.S('UI:ValueMustBeSet');
if (bMandatory && (count == 0) && (sTextContent === nullValue))
{
// No previous entry and no content typed
bValid = false;
sExplain = Dict.S('UI:ValueMustBeSet');
}
else if ((sTextOriginalContents !== undefined) && (sTextContent === sTextOriginalContents))
{
bValid = false;
if (sTextOriginalContents === nullValue)
{
sExplain = Dict.S('UI:ValueMustBeSet');
}
else
{
// Note: value change check is not working well yet as the HTML to Text conversion is not exactly the same when done from the PHP value or the CKEditor value.
sExplain = Dict.S('UI:ValueMustBeChanged');
}
}
else
{
bValid = true;
}
return {bValid: bValid, sExplain: sExplain};
}
else if ((originalValue != undefined) && (sTextContent == originalValue))
{
bValid = false;
sExplain = Dict.S('UI:ValueMustBeChanged');
}
}
ReportFieldValidationStatus(sFieldId, sFormId, bValid, '' /* sExplain */);
// We need to check periodically as CKEditor doesn't trigger our events. More details in UIHTMLEditorWidget::Display() @ line 92
setTimeout(function(){ValidateCaseLogField(sFieldId, bMandatory, sFormId, nullValue, originalValue);}, 500);
});
}
// Validate the inputs depending on the current setting

View File

@@ -332,6 +332,12 @@ CombodoModal._ConvertButtonDefinition = function (aButtonsDefinitions) {
class: typeof(element.classes) !== 'undefined' ? element.classes.join(' ') : '',
click: element.callback_on_click
}
// id is optional, and we don't want to set it if not defined
if (typeof element.id !== 'undefined' && element.id !== null) {
aButton.id = element.id;
}
aConverted.push(aButton);
}
);

View File

@@ -35,116 +35,63 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, oSearchWidgetElmt, sF
this.sAttCode = sAttCode;
this.oSearchWidgetElmt = oSearchWidgetElmt;
this.emptyHtml = ''; // content to be displayed when the search results are empty (when opening the dialog)
this.emptyOnClose = true; // Workaround for the JQuery dialog being very slow when opening and closing if the content contains many INPUT tags
this.ajax_request = null;
// this.bSelectMode = bSelectMode; // true if the edited field is a SELECT, false if it's an autocomplete
// this.bSearchMode = bSearchMode; // true if selecting a value in the context of a search form
var me = this;
this.Init = function()
{
// make sure that the form is clean
$('#linkedset_'+this.id+' .selection').each( function() { this.checked = false; });
$('#'+this.id+'_btnRemove').prop('disabled', false);
$('<div id="dlg_'+me.id+'"></div>').appendTo(document.body);
// me.trace(dialog);
//TODO : check and remove all unneded code bellow this line!!
$('#'+this.id+'_linksToRemove').val('');
$('#linkedset_'+me.id).on('remove', function() {
// prevent having the dlg div twice
$('#dlg_'+me.id).remove();
});
$('#'+this.iInputId).closest('form').on('submit', function() {
return me.OnFormSubmit();
});
};
this.StopPendingRequest = function()
{
if (me.ajax_request)
{
me.ajax_request.abort();
me.ajax_request = null;
}
};
this.ShowModalSearchForeignKeys = function()
{
// // Query the server to get the form to search for target objects
// if (me.bSelectMode)
// {
// $('#fstatus_'+me.id).html('<img src="../images/indicator.gif" />');
// }
// else
// {
// $('#label_'+me.id).addClass('dlg_loading');
// }
$('#label_'+me.id).addClass('dlg_loading');
var theMap = {
sAttCode: me.sAttCode,
iInputId: me.id,
sTitle: me.sTitle,
sTargetClass: me.sTargetClass,
// bSearchMode: me.bSearchMode,
operation: 'ShowModalSearchForeignKeys'
};
const oModalParams = {
content: {
endpoint: AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'),
data: {
sAttCode: me.sAttCode,
iInputId: me.id,
sTargetClass: me.sTargetClass,
operation: 'ShowModalSearchForeignKeys'
},
},
title: me.sTitle,
id: 'dlg_'+me.id,
size: 'lg',
buttons: [
{
text: Dict.S('UI:Button:Cancel'),
callback_on_click: function() {
$(this).dialog("close");
},
classes: ['cancel', 'ibo-is-alternative', 'ibo-is-neutral'],
},
{
text: Dict.S('UI:Button:Add'),
id: "btn_ok_"+me.id,
classes: ['ok', 'ibo-is-regular', 'ibo-is-primary'],
callback_on_click: function() {
me.DoAddObjects();
}
}
],
callback_on_content_loaded: function(oModalContentElement){
// Update initial buttons state
me.UpdateButtons();
},
extra_options: {
callback_on_modal_close: function () {
$(this).remove(); // destroy then remove dialog object
}
}
}
const oModal = CombodoModal.OpenModal(oModalParams);
// Make sure that we cancel any pending request before issuing another
// since responses may arrive in arbitrary order
me.StopPendingRequest();
// Run the query and get the result back directly in HTML
me.ajax_request = $.post( AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), theMap,
function(data)
{
// $('#dlg_'+me.id).html(data);
$('#dlg_'+me.id).empty().append($(data)); // $(data).filter(':not(script)'));
$('#dlg_'+me.id).dialog('open');
me.UpdateSizes();
me.UpdateButtons();
me.ajax_request = null;
me.ListResultsSearchForeignKeys();
},
'html'
);
};
this.UpdateSizes = function()
{
var dlg = $('#dlg_'+me.id);
// Adjust the dialog's size to fit into the screen
if (dlg.width() > ($(window).width()-40))
{
dlg.width($(window).width()-40);
}
if (dlg.height() > ($(window).height()-70))
{
dlg.height($(window).height()-70);
}
var searchForm = dlg.find('div.display_block:first'); // Top search form, enclosing display_block
var results = $('#SearchResultsToAdd_'+me.id);
var oPadding = {};
var aKeys = ['top', 'right', 'bottom', 'left'];
for(k in aKeys)
{
oPadding[aKeys[k]] = 0;
if (dlg.css('padding-'+aKeys[k]))
{
oPadding[aKeys[k]] = parseInt(dlg.css('padding-'+aKeys[k]).replace('px', ''));
}
}
//var width = dlg.innerWidth() - oPadding['right'] - oPadding['left'] - 22; // 5 (margin-left) + 5 (padding-left) + 5 (padding-right) + 5 (margin-right) + 2 for rounding !
var height = dlg.innerHeight()-oPadding['top']-oPadding['bottom']-22;
var form_height = searchForm.outerHeight();
results.height(height - form_height - 40); // Leave some space for the buttons
// Bind events
oModal.on('change', '#count_'+me.id, function(){
me.UpdateButtons();
});
};
this.UpdateButtons = function()
@@ -160,63 +107,6 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, oSearchWidgetElmt, sF
}
};
/**
* @return {boolean}
*/
this.ListResultsSearchForeignKeys = function ()
{
var theMap = {
sTargetClass: me.sTargetClass,
iInputId: me.id,
sFilter: me.sfilter,
// bSearchMode: me.bSearchMode
};
// Gather the parameters from the search form
$('#fs_'+me.id+' :input').each( function() {
if (this.name !== '')
{
var val = $(this).val(); // supports multiselect as well
if (val !== null)
{
theMap[this.name] = val;
}
}
});
theMap['sRemoteClass'] = theMap['class']; // swap 'class' (defined in the form) and 'remoteClass'
theMap.operation = 'ListResultsSearchForeignKeys'; // Override what is defined in the form itself
theMap.sAttCode = me.sAttCode;
var sSearchAreaId = '#SearchResultsToAdd_'+me.id;
//$(sSearchAreaId).html('<div style="text-align:center;width:100%;height:24px;vertical-align:middle;"><img src="../images/indicator.gif" /></div>');
$(sSearchAreaId).block();
me.UpdateButtons();
// Make sure that we cancel any pending request before issuing another
// since responses may arrive in arbitrary order
me.StopPendingRequest();
// Run the query and display the results
me.ajax_request = $.post(AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), theMap,
function(data)
{
$(sSearchAreaId).html(data);
$('#fr_'+me.id+' input:radio').on('click', function() { me.UpdateButtons(); });
me.UpdateButtons();
me.ajax_request = null;
$('#count_'+me.id).on('change', function(){
me.UpdateButtons();
});
me.UpdateSizes();
},
'html'
);
return false; // Don't submit the form, stay in the current page !
};
/**
* @return {boolean}
*/
@@ -286,56 +176,4 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, oSearchWidgetElmt, sF
return false;
};
// Workaround for a ui.jquery limitation: if the content of
// the dialog contains many INPUTs, closing and opening the
// dialog is very slow. So empty it each time.
this.OnClose = function()
{
me.StopPendingRequest();
// called by the dialog, so in the context 'this' points to the jQueryObject
if (me.emptyOnClose)
{
$('#SearchResultsToAdd_'+me.id).html(me.emptyHtml);
}
$('#label_'+me.id).removeClass('dlg_loading');
$('#label_'+me.id).focus();
me.ajax_request = null;
};
this.DoSelectObjectClass = function()
{
// Retrieving selected value
var oSelectedClass = $('#ac_create_'+me.id+' select');
if(oSelectedClass.length !== 1) return;
// Setting new target class
me.sTargetClass = oSelectedClass.val();
// Opening real creation form
$('#ac_create_'+me.id).dialog('close');
me.CreateObject();
};
this.Update = function()
{
if ($('#'+me.id).prop('disabled'))
{
$('#v_'+me.id).html('');
$('#label_'+me.id).prop('disabled', true);
$('#label_'+me.id).css({'background': 'transparent'});
$('#mini_add_'+me.id).hide();
$('#mini_tree_'+me.id).hide();
$('#mini_search_'+me.id).hide();
}
else
{
$('#label_'+me.id).prop('disabled', false);
$('#label_'+me.id).css({'background': '#fff url(../images/ac-background.gif) no-repeat right'});
$('#mini_add_'+me.id).show();
$('#mini_tree_'+me.id).show();
$('#mini_search_'+me.id).show();
}
};
}

View File

@@ -22,7 +22,7 @@ Selectize.define("combodo_auto_position", function (aOptions) {
// Plugin options
aOptions = $.extend({
maxDropDownHeight: 200,
maxDropDownHeight: '200px',
},
aOptions
);
@@ -33,28 +33,47 @@ Selectize.define("combodo_auto_position", function (aOptions) {
// Override position dropdown function
oSelf.positionDropdown = (function () {
return function () {
let iRefHeight = oSelf.$dropdown.outerHeight() < aOptions.maxDropDownHeight ?
oSelf.$dropdown.outerHeight() : aOptions.maxDropDownHeight;
// Clear previously set rules so the comparison is done with dropdown real height
oSelf.$dropdown.css({
'max-height': '',
});
if(oSelf.$control.offset().top + oSelf.$control.outerHeight() + iRefHeight > window.innerHeight){
oSelf.$dropdown_content.css({
'max-height': '',
});
oSelf.$dropdown.css({
top: oSelf.$control.offset().top - iRefHeight,
left: oSelf.$control.offset().left,
let iDropdownHeight = oSelf.$dropdown.outerHeight();
if(oSelf.$control.offset().top + oSelf.$control.outerHeight() + iDropdownHeight > window.innerHeight){
// Apply max-height as we are overflowing, that'll allow us to calculate where we should place ourselves later
oSelf.$dropdown.css({
maxHeight: `${aOptions.maxDropDownHeight}`,
})
iDropdownHeight = oSelf.$dropdown.outerHeight();
oSelf.$dropdown.css({
top: oSelf.$control.offset().top - iDropdownHeight + 4, // Content will be shorter, so our real height too
left: oSelf.$control.offset().left,
width: oSelf.$wrapper.outerWidth(),
'max-height': `${aOptions.maxDropDownHeight}px`,
'overflow-y': 'auto',
'border-top': '1px solid #d0d0d0',
overflowY: 'auto',
borderTop : oSelf.$dropdown.css('border-bottom')
});
// N°9468 Dropdown content needs to be a few pixel shorter than the dropdown itself to avoid double scrollbar
oSelf.$dropdown_content.css({
'max-height': `calc(${aOptions.maxDropDownHeight} - 4px)`
});
}
else{
oSelf.$dropdown.css({
top: oSelf.$control.offset().top + oSelf.$control.outerHeight(),
left: oSelf.$control.offset().left,
width: oSelf.$wrapper.outerWidth(),
'max-height': `${aOptions.maxDropDownHeight}px`,
'overflow-y': 'auto'
});
overflowY: 'auto',
borderTop: 'none'
});
}
};
}());

View File

@@ -487,6 +487,12 @@ function ExportStartExport() {
oParams.expression = $('#export-form :input[name=expression]').val();
oParams.query = $('#export-form :input[name=query]').val();
}
// Read the "sanitize_excel_export" checkbox if it exists, and set the corresponding "ignore_excel_sanitization" parameter
if($(':input[name=sanitize_excel_export]').length > 0) {
oParams.ignore_excel_sanitization = $(':input[name=sanitize_excel_export]').is(':checked') ? 0 : 1;
}
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function (data) {
if (data == null) {
ExportError('Export failed (no data provided), please contact your administrator');

View File

@@ -193,6 +193,7 @@ return array(
'Combodo\\iTop\\Application\\Helper\\CKEditorHelper' => $baseDir . '/sources/Application/Helper/CKEditorHelper.php',
'Combodo\\iTop\\Application\\Helper\\ExportHelper' => $baseDir . '/sources/Application/Helper/ExportHelper.php',
'Combodo\\iTop\\Application\\Helper\\FormHelper' => $baseDir . '/sources/Application/Helper/FormHelper.php',
'Combodo\\iTop\\Application\\Helper\\SearchHelper' => $baseDir . '/sources/Application/Helper/SearchHelper.php',
'Combodo\\iTop\\Application\\Helper\\Session' => $baseDir . '/sources/Application/Helper/Session.php',
'Combodo\\iTop\\Application\\Helper\\WebResourcesHelper' => $baseDir . '/sources/Application/Helper/WebResourcesHelper.php',
'Combodo\\iTop\\Application\\Newsroom\\iTopNewsroomProvider' => $baseDir . '/sources/Application/Newsroom/iTopNewsroomProvider.php',

View File

@@ -548,6 +548,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Application\\Helper\\CKEditorHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/CKEditorHelper.php',
'Combodo\\iTop\\Application\\Helper\\ExportHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/ExportHelper.php',
'Combodo\\iTop\\Application\\Helper\\FormHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/FormHelper.php',
'Combodo\\iTop\\Application\\Helper\\SearchHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/SearchHelper.php',
'Combodo\\iTop\\Application\\Helper\\Session' => __DIR__ . '/../..' . '/sources/Application/Helper/Session.php',
'Combodo\\iTop\\Application\\Helper\\WebResourcesHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/WebResourcesHelper.php',
'Combodo\\iTop\\Application\\Newsroom\\iTopNewsroomProvider' => __DIR__ . '/../..' . '/sources/Application/Newsroom/iTopNewsroomProvider.php',

View File

@@ -1654,17 +1654,17 @@
},
{
"name": "scssphp/scssphp",
"version": "v1.13.0",
"version_normalized": "1.13.0.0",
"version": "dev-combodo/1.x",
"version_normalized": "dev-combodo/1.x",
"source": {
"type": "git",
"url": "https://github.com/scssphp/scssphp.git",
"reference": "63d1157457e5554edf00b0c1fabab4c1511d2520"
"url": "https://github.com/combodo-itop-libs/scssphp.git",
"reference": "dde81c0a39d02e8e6fc81b70269747734e16d526"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/63d1157457e5554edf00b0c1fabab4c1511d2520",
"reference": "63d1157457e5554edf00b0c1fabab4c1511d2520",
"url": "https://api.github.com/repos/combodo-itop-libs/scssphp/zipball/dde81c0a39d02e8e6fc81b70269747734e16d526",
"reference": "dde81c0a39d02e8e6fc81b70269747734e16d526",
"shasum": ""
},
"require": {
@@ -1687,15 +1687,15 @@
"ext-iconv": "Can be used as fallback when ext-mbstring is not available",
"ext-mbstring": "For best performance, mbstring should be installed as it is faster than ext-iconv"
},
"time": "2024-08-17T21:02:11+00:00",
"time": "2026-03-23T15:26:59+00:00",
"bin": [
"bin/pscss"
],
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": false,
"forward-command": false
"forward-command": false,
"bin-links": false
}
},
"installation-source": "dist",
@@ -1704,7 +1704,11 @@
"ScssPhp\\ScssPhp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"autoload-dev": {
"psr-4": {
"ScssPhp\\ScssPhp\\Tests\\": "tests/"
}
},
"license": [
"MIT"
],
@@ -1730,8 +1734,7 @@
"stylesheet"
],
"support": {
"issues": "https://github.com/scssphp/scssphp/issues",
"source": "https://github.com/scssphp/scssphp/tree/v1.13.0"
"source": "https://github.com/combodo-itop-libs/scssphp/tree/combodo/1.x"
},
"install-path": "../scssphp/scssphp"
},

View File

@@ -292,9 +292,9 @@
'dev_requirement' => false,
),
'scssphp/scssphp' => array(
'pretty_version' => 'v1.13.0',
'version' => '1.13.0.0',
'reference' => '63d1157457e5554edf00b0c1fabab4c1511d2520',
'pretty_version' => 'dev-combodo/1.x',
'version' => 'dev-combodo/1.x',
'reference' => 'dde81c0a39d02e8e6fc81b70269747734e16d526',
'type' => 'library',
'install_path' => __DIR__ . '/../scssphp/scssphp',
'aliases' => array(),

View File

@@ -5052,7 +5052,7 @@ EOL;
*
* @return array
*/
protected function multiplyMedia(Environment $env = null, $childQueries = null)
protected function multiplyMedia(?Environment $env = null, $childQueries = null)
{
if (
! isset($env) ||
@@ -5144,7 +5144,7 @@ EOL;
*
* @return \ScssPhp\ScssPhp\Compiler\Environment
*/
protected function pushEnv(Block $block = null)
protected function pushEnv(?Block $block = null)
{
$env = new Environment();
$env->parent = $this->env;
@@ -5208,7 +5208,7 @@ EOL;
*
* @return void
*/
protected function set($name, $value, $shadow = false, Environment $env = null, $valueUnreduced = null)
protected function set($name, $value, $shadow = false, ?Environment $env = null, $valueUnreduced = null)
{
$name = $this->normalizeName($name);
@@ -5314,7 +5314,7 @@ EOL;
*
* @return mixed|null
*/
public function get($name, $shouldThrow = true, Environment $env = null, $unreduced = false)
public function get($name, $shouldThrow = true, ?Environment $env = null, $unreduced = false)
{
$normalizedName = $this->normalizeName($name);
$specialContentKey = static::$namespaces['special'] . 'content';
@@ -5379,7 +5379,7 @@ EOL;
*
* @return bool
*/
protected function has($name, Environment $env = null)
protected function has($name, ?Environment $env = null)
{
return ! \is_null($this->get($name, false, $env));
}

View File

@@ -272,7 +272,7 @@ abstract class Formatter
*
* @return string
*/
public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null)
public function format(OutputBlock $block, ?SourceMapGenerator $sourceMapGenerator = null)
{
$this->sourceMapGenerator = null;

View File

@@ -578,7 +578,7 @@ class Number extends Node implements \ArrayAccess, \JsonSerializable
*
* @return string
*/
public function output(Compiler $compiler = null)
public function output(?Compiler $compiler = null)
{
$dimension = round($this->dimension, self::PRECISION);

View File

@@ -140,7 +140,7 @@ class Parser
* @param bool $cssOnly
* @param LoggerInterface|null $logger
*/
public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8', Cache $cache = null, $cssOnly = false, LoggerInterface $logger = null)
public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8', ?Cache $cache = null, $cssOnly = false, ?LoggerInterface $logger = null)
{
$this->sourceName = $sourceName ?: '(stdin)';
$this->sourceIndex = $sourceIndex;

View File

@@ -59,7 +59,7 @@ final class Warn
*
* @internal
*/
public static function setCallback(callable $callback = null)
public static function setCallback(?callable $callback = null)
{
$previousCallback = self::$callback;
self::$callback = $callback;

View File

@@ -5,6 +5,7 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Application\Helper\SearchHelper;
use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
@@ -126,72 +127,6 @@ function SetObjectBreadCrumbEntry(DBObject $oObj, WebPage $oPage)
$oPage->SetBreadCrumbEntry("ui-details-$sClass-".$oObj->GetKey(), $oObj->Get('friendlyname'), MetaModel::GetName($sClass).': '.$oObj->Get('friendlyname'), '', $sIcon, $sIconType);
}
/**
* Displays the result of a search request
* @param $oP WebPage Web page for the output
* @param $oFilter DBSearch The search of objects to display
* @param $bSearchForm boolean Whether or not to display the search form at the top the page
* @param $sBaseClass string The base class for the search (can be different from the actual class of the results)
* @param $sFormat string The format to use for the output: csv or html
* @param $bDoSearch bool True to display the search results below the search form
* @param $bSearchFormOpen bool True to display the search form fully expanded (only if $bSearchForm of course)
* @throws \CoreException
* @throws \DictExceptionMissingString
*/
function DisplaySearchSet($oP, $oFilter, $bSearchForm = true, $sBaseClass = '', $sFormat = '', $bDoSearch = true, $bSearchFormOpen = true, $aParams = [])
{
//search block
$oBlockForm = null;
if ($bSearchForm) {
$aParams['open'] = $bSearchFormOpen;
if (false === isset($aParams['table_id'])) {
$aParams['table_id'] = 'result_1';
}
if (!empty($sBaseClass)) {
$aParams['baseClass'] = $sBaseClass;
}
$oBlockForm = new DisplayBlock($oFilter, 'search', false /* Asynchronous */, $aParams);
if (!$bDoSearch) {
$oBlockForm->Display($oP, 0);
}
}
if ($bDoSearch) {
if (strtolower($sFormat) == 'csv') {
$oBlock = new DisplayBlock($oFilter, 'csv', false);
// Adjust the size of the Textarea containing the CSV to fit almost all the remaining space
$oP->add_ready_script(" $('#1>textarea').height($('#1').parent().height() - $('#0').outerHeight() - 30).width( $('#1').parent().width() - 20);"); // adjust the size of the block
} else {
$oBlock = new DisplayBlock($oFilter, 'list', false);
// Breadcrumb
//$iCount = $oBlock->GetDisplayedCount();
$sPageId = "ui-search-".$oFilter->GetClass();
$sLabel = MetaModel::GetName($oFilter->GetClass());
$oP->SetBreadCrumbEntry($sPageId, $sLabel, '', '', 'fas fa-search', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
}
if ($bSearchForm) {
//add search block
$sTableId = utils::ReadParam('_table_id_', null, false, 'raw_data');
if ($sTableId == '') {
$sTableId = 'result_1';
}
$aExtraParams['table_id'] = $sTableId;
$aExtraParams['submit_on_load'] = false;
$oUIBlockForm = $oBlockForm->GetDisplay($oP, 'search_1', $aExtraParams);
//add result block
$oUIBlock = $oBlock->GetDisplay($oP, $sTableId);
$oUIBlock->AddCSSClasses(['display_block', 'sf_results_area']);
$oUIBlock->AddDataAttribute('target', 'search_results');
//$oUIBlockForm->AddSubBlock($oUIBlock);
$oP->AddUiBlock($oUIBlockForm);
$oUIBlockForm->AddSubBlock($oUIBlock);
} else {
$oBlock->Display($oP, 1);
}
}
}
/**
* Displays a form (checkboxes) to select the objects for which to apply a given action
* Only the objects for which the action is valid can be checked. By default all valid objects are checked
@@ -460,7 +395,7 @@ try {
$sOQL = "SELECT $sOQLClass $sOQLClause";
try {
$oFilter = DBObjectSearch::FromOQL($sOQL);
DisplaySearchSet($oP, $oFilter, $bSearchForm, $sBaseClass, $sFormat);
SearchHelper::DisplaySearchSet($oP, $oFilter, $bSearchForm, $sBaseClass, $sFormat);
} catch (CoreException $e) {
$oFilter = new DBObjectSearch($sOQLClass);
$oSet = new DBObjectSet($oFilter);
@@ -487,7 +422,7 @@ try {
}
$oP->set_title(Dict::S('UI:SearchResultsPageTitle'));
$oFilter = new DBObjectSearch($sClass);
DisplaySearchSet($oP, $oFilter, $bSearchForm, '' /* sBaseClass */, $sFormat, $bDoSearch, true /* Search Form Expanded */);
SearchHelper::DisplaySearchSet($oP, $oFilter, $bSearchForm, '' /* sBaseClass */, $sFormat, $bDoSearch, true /* Search Form Expanded */);
break;
///////////////////////////////////////////////////////////////////////////////////////////
@@ -509,7 +444,7 @@ try {
// $sParams = utils::ReadParam('aParams', '{}', false, \utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
// $aParams = json_decode($sParams, true);
DisplaySearchSet($oP, $oFilter, $bSearchForm, '' /* sBaseClass */, $sFormat); //, true, true, $aParams
SearchHelper::DisplaySearchSet($oP, $oFilter, $bSearchForm, '' /* sBaseClass */, $sFormat); //, true, true, $aParams
break;
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -107,10 +107,6 @@ if ($oFilter != null) {
$sPageId = "ui-search-".$oFilter->GetClass();
$sLabel = MetaModel::GetName($oFilter->GetClass());
$oP->SetBreadCrumbEntry($sPageId, $sLabel, '', '', 'fas fa-search', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
// Menu node
$sFilter = $oFilter->ToOQL();
$oP->add("\n<!-- $sFilter -->\n");
}
$oP->add("</div>\n");
$oP->output();

View File

@@ -34,6 +34,7 @@ try {
require_once(APPROOT.'/application/startup.inc.php');
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
IssueLog::Trace('----- Request: '.utils::GetRequestUri(), LogChannels::WEB_REQUEST);
$oPage = new DownloadPage("");
@@ -43,7 +44,7 @@ try {
switch ($operation) {
case 'download_document':
LoginWebPage::DoLoginEx('backoffice', false);
LoginWebPage::DoLoginEx();
$id = utils::ReadParam('id', '');
$sField = utils::ReadParam('field', '');
if ($sClass == 'Attachment') {
@@ -63,8 +64,7 @@ try {
break;
case 'download_inlineimage':
// No login is required because the "secret" protects us
// Benefit: the inline image can be inserted into any HTML (templating = $this->html(public_log)$)
LoginWebPage::DoLoginEx();
$id = utils::ReadParam('id', '');
$sSecret = utils::ReadParam('s', '');
$iCacheSec = 31556926; // One year ahead: an inline image cannot change

View File

@@ -173,10 +173,9 @@ try {
case 'ShowModalSearchForeignKeys':
$oPage->SetContentType('text/html');
$iInputId = utils::ReadParam('iInputId', '');
$sTitle = utils::ReadParam('sTitle', '', false, 'raw_data');
$sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class');
$oWidget = new UISearchFormForeignKeys($sTargetClass, $iInputId);
$oWidget->ShowModalSearchForeignKeys($oPage, $sTitle);
$oWidget->ShowModalSearchForeignKeys($oPage);
break;
// ui.searchformforeignkeys
@@ -187,16 +186,6 @@ try {
$oWidget->GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter);
break;
// ui.searchformforeignkeys
case 'ListResultsSearchForeignKeys':
$oPage->SetContentType('text/html');
$sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class');
$iInputId = utils::ReadParam('iInputId', '');
$sRemoteClass = utils::ReadParam('sRemoteClass', '', false, 'class');
$oWidget = new UISearchFormForeignKeys($sTargetClass, $iInputId);
$oWidget->ListResultsSearchForeignKeys($oPage, $sRemoteClass);
break;
// ui.linkswidget
case 'addObjects':
$oPage->SetContentType('text/html');
@@ -998,7 +987,7 @@ JS
break;
case 'revert_dashboard':
$sDashboardId = utils::ReadParam('dashboard_id', '', false, 'raw_data');
$sDashboardId = utils::ReadParam('dashboard_id', '', false, utils::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM);
$sReloadURL = utils::ReadParam('reload_url', '', false, utils::ENUM_SANITIZATION_FILTER_URL);
appUserPreferences::UnsetPref('display_original_dashboard_'.$sDashboardId);
$oDashboard = new RuntimeDashboard($sDashboardId);
@@ -2047,6 +2036,17 @@ EOF
$sObjClass = utils::ReadParam('obj_class', '', false, 'class');
$iObjKey = (int)utils::ReadParam('obj_key', 0, false, 'integer');
// Check user has access to the object before trying to acquire the lock
$oSearch = new DBObjectSearch($sObjClass);
$oSearch->AddCondition(MetaModel::DBGetKey($sObjClass), $iObjKey, '=');
$oSet = new CMDBObjectSet($oSearch);
if (
false === $oSet->CountExceeds(0) ||
UserRights::IsActionAllowed($sObjClass, UR_ACTION_MODIFY, $oSet) !== UR_ALLOWED_YES
) {
throw new SecurityException(Dict::S('UI:ObjectDoesNotExist'));
}
$aResult = iTopOwnershipLock::AcquireLock($sObjClass, $iObjKey);
if (false === $aResult['success']) {
$aLockData = iTopOwnershipLock::IsLocked($sObjClass, $iObjKey);
@@ -2502,8 +2502,7 @@ EOF
$oKPI->ComputeAndReport('Data fetch and format');
$oPage->output();
} catch (Exception $e) {
// note: transform to cope with XSS attacks
echo utils::EscapeHtml($e->GetMessage());
echo utils::EscapeHtml(Dict::S('UI:PageTitle:FatalError'));
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
}

View File

@@ -87,7 +87,7 @@ if (is_link($sPageEnvFullPath)) {
}
$sTargetPage = CheckPageExists($sPageEnvFullPath, $aPossibleBasePaths);
if ($sTargetPage === false) {
if ($sTargetPage === false || $sModule === 'core' || $sModule === 'dictionaries') {
// Do not recall the page parameters (security takes precedence)
echo "Wrong module, page name or environment...";
exit;
@@ -97,4 +97,41 @@ if ($sTargetPage === false) {
//
// GO!
//
// check module white list
// check conf param
// force login if needed
$aModuleDelegatedAuthenticationEndpointsList = GetModuleDelegatedAuthenticationEndpoints($sModule);
// If module doesn't have the delegated authentication endpoints list defined, we rely on the conf. param. to decide if we force login or not.
if (is_null($aModuleDelegatedAuthenticationEndpointsList)) {
$bForceLoginWhenNoDelegatedAuthenticationEndpoints = !utils::GetConfig()->Get('security.disable_exec_forced_login_for_all_enpoints');
if ($bForceLoginWhenNoDelegatedAuthenticationEndpoints) {
require_once(APPROOT.'/application/startup.inc.php');
LoginWebPage::DoLoginEx();
}
}
// If module defined a delegated authentication endpoints but not for the current page, we consider that the page is not allowed to be executed without login
if (is_array($aModuleDelegatedAuthenticationEndpointsList) && !in_array($sPage, $aModuleDelegatedAuthenticationEndpointsList)) {
require_once(APPROOT.'/application/startup.inc.php');
LoginWebPage::DoLoginEx();
}
// If user is not logged in, log a warning in the log file as the page is executed without login, which is not recommended for security reason
if (is_null($aModuleDelegatedAuthenticationEndpointsList) && !UserRights::IsLoggedIn()) {
require_once(APPROOT.'/application/startup.inc.php');
IssueLog::Debug("The '$sPage' page is executed without logging in. This call will be blocked in the future and will likely cause unwanted behaviour in the '$sModule' module. Please define a delegated authentication endpoint for the module, as described at https://www.itophub.io/wiki/page?id=latest:customization:new_extension#security.");
}
require_once($sTargetPage);
function GetModuleDelegatedAuthenticationEndpoints(string $sModuleName): ?array
{
$sModuleFile = utils::GetAbsoluteModulePath($sModuleName).'/module.'.$sModuleName.'.php';
if (!file_exists($sModuleFile)) {
echo 'Wrong module, page name or environment...';
exit;
}
require_once APPROOT.'setup/extensionsmap.class.inc.php';
$oExtensionMap = new iTopExtensionsMap();
$aModuleParam = $oExtensionMap->GetModuleInfo($sModuleFile)[2];
return $aModuleParam['delegated_authentication_endpoints'] ?? null;
}

View File

@@ -306,7 +306,7 @@ JS
$sBefore = substr($sExpression, 0, $e->GetColumn());
$sAfter = substr($sExpression, $e->GetColumn() + strlen($sWrongWord));
$sFixedExpression = $sBefore.$sSuggestedWord.$sAfter;
$sFixedExpressionHtml = $sBefore.'<span class="ibo-run-query--highlight">'.$sSuggestedWord.'</span>'.$sAfter;
$sFixedExpressionHtml = $sBefore.'<span class="ibo-run-query--highlight">'.$sSuggestedWord.'</span>'.utils::EscapeHtml($sAfter);
$sSyntaxErrorText .= "<p>Suggesting: $sFixedExpressionHtml</p>";
$oSyntaxErrorPanel->AddSubBlock(new Html($sSyntaxErrorText));

View File

@@ -107,6 +107,7 @@ try {
// Menu node
$sFilter = $oFilter->ToOQL();
$sFilter = utils::EscapeHtml($sFilter);
$oP->add("\n<!-- $sFilter -->\n");
} else {
$oP->add("<p>");

View File

@@ -390,7 +390,7 @@ class iTopExtensionsMap
* @param string $sModuleFile
* @return array
*/
protected function GetModuleInfo($sModuleFile)
public function GetModuleInfo($sModuleFile)
{
static $iDummyClassIndex = 0;

View File

@@ -31,7 +31,7 @@ class ModuleInstallation extends DBObject
{
$aParams =
[
"category" => "core,view_in_gui",
"category" => "core,view_in_gui,grant_by_profile",
"key_type" => "autoincrement",
'name_attcode' => ['name', 'version'],
"state_attcode" => "",

View File

@@ -36,6 +36,8 @@ class SetupPage extends NiceWebPage
{
public const DEFAULT_PAGE_TEMPLATE_REL_PATH = 'pages/backoffice/setuppage/layout';
public const BODY_DATA_GUI_TYPE = 'setup';
public function __construct($sTitle)
{
parent::__construct($sTitle);

Some files were not shown because too many files have changed in this diff Show More