Compare commits

...

71 Commits

Author SHA1 Message Date
Stephen Abello
7b44ec23a1 Fix return type in PHPDoc thanks to @jbostoen 2025-07-28 16:14:13 +02:00
BenGrenoble
f00b8d529c N°8286 - Be able to add additional data in iTop session files via custom classes
Change to fit convention.
2025-07-24 10:59:48 +02:00
BenGrenoble
af44ffd0ad N°8286 - Be able to add additional data in iTop session files via custom classes
Experimental warning
2025-07-24 10:28:15 +02:00
Benjamin Dalsass
c63e01e27b N°8552 - Aggregate brick, Search Brick and new look issue 2025-07-23 16:36:01 +02:00
Benjamin Dalsass
1438006861 N°8419 - Improve Setup process by avoiding the update of the attachment table when contact_id is already set 2025-07-16 16:41:50 +02:00
Stephen Abello
700470dd29 N°8219 - Allow to reset user cache 2025-07-11 10:28:24 +02:00
bdalsass
497fc4978a N°8340 - CKEditor is slow with large texts 2025-07-10 10:20:23 +02:00
v-dumas
6826ab509d N°6925 - Add a dedicated dict entry for "View the OQL query" 2025-07-09 17:10:07 +02:00
bdalsass
41194ce9ab N°8365 - Portal: impact analysis and silo, Ticket cannot be created due to CI not visible 2025-07-09 07:38:07 +02:00
Anne-Catherine
0e5bece5b6 N°8347 - Bad display of special characters on the portal CheckToWrite message (#712) 2025-07-08 11:16:11 +02:00
jf-cbd
e987ea72bc Fix broken URI 2025-07-07 16:01:57 +02:00
bdalsass
c8371f1c19 N°8395 - php8.1 compatibility in class extended Exception 2025-07-07 14:03:29 +02:00
bdalsass
740f83f43e N°8395 - php8.1 compatibility in class extended Exception 2025-07-03 13:39:39 +02:00
bdalsass
14791bf6b4 N°8148 - CAS problem when sending a link ending in &
- ApplicationContextTest mocking service
2025-07-01 15:57:45 +02:00
Benjamin Dalsass
062d543b26 N°8148 - CAS problem when sending a link ending in & (#722)
N°8148 - CAS problem when sending a link ending in &
2025-06-30 14:19:33 +02:00
bdalsass
8cece0f0fd N°8395 - php8.1 compatibility in class extended Exception 2025-06-30 10:01:00 +02:00
odain
18871566d2 N°8286 - fix call to CompleteSessionData in case of no proper session content 2025-06-19 11:29:48 +02:00
odain
b565b700a9 Merge branch 'feature/7398-sessiontracker-extensibility' into support/3.2 2025-06-19 10:39:35 +02:00
odain
3846087dc1 N°7398 - regenerate autoload 2025-06-19 10:39:09 +02:00
odain
4f96246cbe N°7398-log level with invalid json 2025-06-19 10:38:01 +02:00
odain
ddd3dcdaf2 N°8286 - Be able to add additional data in iTop session files via custom classes 2025-06-19 10:38:01 +02:00
odain
82ad9d4317 N°7398 - be able to add custom data in session files generated by SessionHandler 2025-06-19 10:38:00 +02:00
Anne-Cath
6dc48ad36d N°8351 - [Export Impact Analysis] Field to display for the CSV export - Add the friendlyname to each list 2025-06-17 10:22:53 +02:00
Björn Rudner
84476a869f 🎨 remove outdated or duplicated elemets (#726) 2025-06-16 16:25:22 +02:00
jf-cbd
684f829581 Merge commit from fork 2025-06-16 15:06:45 +02:00
Björn Rudner
d1e6334629 N°8456 - 🐛 add missing element attributes (#725) 2025-06-13 16:50:56 +02:00
Eric Espie
047ba8c6c7 Better KPI log performances 2025-06-13 15:31:30 +02:00
v-dumas
f609010d7f Fix typo in SynchroLog class name 2025-06-11 17:49:28 +02:00
v-dumas
9330b5ec9a N°8377 - Fix confusing duplicated stimulus label on Change 2025-06-11 17:42:04 +02:00
v-dumas
18b85d4080 N°8377 - Missing rights on change ITIL for SuperUser 2025-06-11 17:41:01 +02:00
v-dumas
7214e5b43a Fix typo in SynchroLog class name 2025-06-11 17:14:15 +02:00
v-dumas
3689a83953 N°2583 - Control rights per class (bulk read on business, bulk write on others) 2025-06-11 17:13:25 +02:00
v-dumas
a715ce1292 Fix typo in SynchroLog class name 2025-06-11 17:11:43 +02:00
v-dumas
17d85fbb3f N°2583 - Advanced mode on CSV import add technical classes and class codes 2025-06-11 16:50:38 +02:00
v-dumas
5e59aff74f N°2583 - Filter Universal Search based on user rights 2025-06-11 16:48:28 +02:00
v-dumas
9e1e81ccc1 N°2583 - Audit, User, Query classes available for universal Search without the "Administrator" profile 2025-06-11 11:17:27 +02:00
v-dumas
c57c8d2af3 N°8406 - Fix FR translations 2025-06-11 10:16:50 +02:00
bdalsass
8a436f9760 N°8251 - Deprecated function when bulk assign
- Deprecated on strlen
2025-06-06 15:26:05 +02:00
bdalsass
2e77713772 N°8399 - Portal: unnecessary screen is displayed during a transition with no field to display 2025-06-06 15:04:21 +02:00
bdalsass
975c554e91 N°8251 - Deprecated function when bulk assign 2025-06-06 09:02:29 +02:00
bdalsass
ac6f642052 N°8232 - CKEditor: Unexpected lines repeatedly added after blocks 2025-06-06 08:54:23 +02:00
bdalsass
cf9ab2a83c N°8436 - CreateBrick fails when used with an abstract class 2025-06-04 16:22:41 +02:00
bdalsass
3ff3dcba54 N°8308 - Broken URL in sending mail 2025-06-02 17:15:51 +02:00
jf-cbd
657fc912bf N°8198 - ModuleInstallation now extends DBObject + better exception message 2025-06-02 16:09:29 +02:00
Anne-Catherine
5ae5221f6f N°6925 - Enable on any search result edition of OQL (#711) 2025-05-28 10:59:50 +02:00
jf-cbd
b15ca2fbc9 N°8260 - Change format of REST logs when they are close to the SQL field size limit 2025-05-27 10:41:05 +02:00
Stephen Abello
cb382eab4e Fix CI following dict behavior change 2025-05-26 16:23:38 +02:00
Stephen Abello
9723cde24c N°7960 - Add a wrapper to vsprintf to handle argument number disparities (#717) 2025-05-26 15:41:25 +02:00
Stephen Abello
7ae49e2cf4 N°8076 - Fix cancel and close buttons in a modal blocking all buttons for the underlying object 2025-05-26 11:25:08 +02:00
v-dumas
cb13a7a5b4 N°2583 - Audit, User, Query classes can be Import with bulk_modify right 2025-05-23 17:37:47 +02:00
bdalsass
9618e47045 N°8201 - [CVE_Request]_Cross-Site-Script Reflected(XSS Reflected at the name="attr_installed" (Low or Medium) 2025-05-23 10:16:22 +02:00
bdalsass
d84506ea9e Merge remote-tracking branch 'origin/support/2.7' into support/3.2
# Conflicts:
#	pages/UI.php
2025-05-23 10:14:50 +02:00
bdalsass
13239c2751 N°8201 - [CVE_Request]_Cross-Site-Script Reflected(XSS Reflected at the name="attr_installed" (Low or Medium) 2025-05-23 10:06:01 +02:00
bdalsass
8b30e36dd1 N°8168 - Stored XSS in portals lnk 2025-05-23 08:52:18 +02:00
bdalsass
80b290ab88 Merge remote-tracking branch 'origin/support/2.7' into support/3.2
# Conflicts:
#	sources/renderer/bootstrap/fieldrenderer/bslinkedsetfieldrenderer.class.inc.php
2025-05-23 08:49:52 +02:00
bdalsass
81b20ee583 N°8168 - Stored XSS in portals lnk 2025-05-23 08:42:56 +02:00
Stephen Abello
a5545b0084 N°8205 - Security hardening 2025-05-20 09:48:00 +02:00
Stephen Abello
d72e861dfe N°8315 - Security hardening 2025-05-19 14:51:12 +02:00
Stephen Abello
92385273ff N°8315 - Security hardening 2025-05-19 14:45:24 +02:00
bdalsass
61c25f85e7 N°8313 - edit dashboard (fix broken test 3.2) 2025-05-19 13:12:02 +02:00
bdalsass
b1cf2ec137 N°8313 - edit dashboard (fix broken test 3.2) 2025-05-16 14:35:39 +02:00
bdalsass
7549ded51d Merge remote-tracking branch 'origin/support/2.7' into support/3.2
# Conflicts:
#	application/dashboard.class.inc.php
#	setup/setuputils.class.inc.php
#	tests/php-unit-tests/unitary-tests/application/utilsTest.php
2025-05-16 14:19:00 +02:00
bdalsass
38683c20b1 N°8313 - edit dashboard (fix broken test) 2025-05-16 14:05:55 +02:00
bdalsass
81791dd253 N°8313 - edit dashboard 2025-05-16 14:05:55 +02:00
bdalsass
e77e0eec9f N°8355 - render_dashboard 2025-05-16 14:05:55 +02:00
jf-cbd
f5ddbbbe0e Rollback typing parameter 2025-05-13 16:59:43 +02:00
jf-cbd
6811a82e1a Merge remote-tracking branch 'origin/support/2.7' into support/3.2
# Conflicts:
#	datamodels/2.x/itop-backup/status.php
#	setup/setuputils.class.inc.php
2025-05-13 16:40:11 +02:00
jf-cbd
960133c0df N°8379 - fix backup issue 2025-05-13 16:27:59 +02:00
jf-cbd
544c4ae888 N°8379 - fix backup issue 2025-05-13 16:05:39 +02:00
jbostoen
9ee18c2f36 Add helper method to create an ObjectResult from a DBObject. (#706)
Author: Jeffrey Bostoen <support@jeffreybostoen.be>
2025-04-25 14:46:33 +02:00
jf-cbd
43a10e6944 Revert "N°8259 - Problem with GetMaxSize on AttributeText"
This reverts commit 29c75f626b.
2025-04-22 09:37:54 +02:00
114 changed files with 878 additions and 343 deletions

View File

@@ -1,5 +1,9 @@
<p align="center"><a href="https://www.combodo.com/itop-193" target="_blank">
<img src="https://www.combodo.com/logos/logo-itop-baseline.svg" width=350>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="/images/logos/logo-itop-baseline-light.svg">
<source media="(prefers-color-scheme: light)" srcset="/images/logos/logo-itop-baseline-dark.svg">
<img src="/images/logos/logo-itop-baseline-light.svg" width="350" alt="Logo iTop with baseline" />
</picture>
</a></p>

View File

@@ -195,16 +195,31 @@ class ApplicationContext
/**
* Returns the context as string with the format name1=value1&name2=value2....
* @return string The context as a string to be appended to an href property
*
*/
public function GetForLink()
public function GetForLink(bool $bWithLeadingAmpersand = false)
{
// If there are no parameters, return an empty string
if(empty($this->aValues)){
return '';
}
// Build the query string with ampersand separated parameters
$aParams = array();
foreach($this->aValues as $sName => $sValue)
{
$aParams[] = "c[$sName]".'='.urlencode($sValue);
}
return implode("&", $aParams);
$sReturnValue = implode('&', $aParams);
// add the leading ampersand if requested
if($bWithLeadingAmpersand){
$sReturnValue = '&' . $sReturnValue;
}
return $sReturnValue;
}
/**
* @since 3.0.0 N°2534 - dashboard: bug with autorefresh that deactivates filtering on organisation
* Returns the params as c[menu]:..., c[org_id]:....
@@ -382,7 +397,7 @@ class ApplicationContext
$sUrl = call_user_func(array($sUrlMakerClass, 'MakeObjectUrl'), $sObjClass, $sObjKey);
if (utils::StrLen($sUrl) > 0) {
if ($bWithNavigationContext) {
return $sUrl."&".$oAppContext->GetForLink();
return $sUrl.$oAppContext->GetForLink(true);
} else {
return $sUrl;
}

View File

@@ -311,7 +311,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
{
$sParams .= $sName.'='.urlencode($value).'&'; // Always add a trailing &
}
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/'.$oObj->GetUIPage().'?'.$sParams.'class='.get_class($oObj).'&id='.$oObj->getKey().'&'.$oAppContext->GetForLink().'&a=1';
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/'.$oObj->GetUIPage().'?'.$sParams.'class='.get_class($oObj).'&id='.$oObj->getKey().$oAppContext->GetForLink(true).'&a=1';
$oPage->add_early_script(<<<JS
if (!sessionStorage.getItem('$sSessionStorageKey'))
{
@@ -1337,7 +1337,7 @@ HTML
}
}
}
$aHeader['friendlyname'] = ['label' => MetaModel::GetName($sClassName)];
foreach ($aList[$sAlias] as $sAttCodeEx => $oAttDef) {
$sColLabel = $bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx) : $sAttCodeEx;
@@ -1358,6 +1358,7 @@ HTML
$aRow = [];
foreach ($aAuthorizedClasses as $sAlias => $sClassName) {
$oObj = $aObjects[$sAlias];
$aRow["friendlyname"] = $oObj->Get('friendlyname');
foreach ($aList[$sAlias] as $sAttCodeEx => $oAttDef) {
if (is_null($oObj)) {
$aRow[$oAttDef->GetCode()] = '';
@@ -3071,7 +3072,7 @@ JS
$oPage->add($oAppContext->GetForForm());
// Hook the cancel button via jQuery so that it can be unhooked easily as well if needed
$sDefaultUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_form&class='.$sClass.'&'.$oAppContext->GetForLink();
$sDefaultUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_form&class='.$sClass.$oAppContext->GetForLink(true);
$sCancelButtonOnClickScript = "let fOnClick{$this->m_iFormId}CancelButton = ";
if(isset($aExtraParams['js_handlers']['cancel_button_on_click'])){
@@ -3079,7 +3080,7 @@ JS
} else {
$sCancelButtonOnClickScript .= "function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl', $sJSToken)};";
}
$sCancelButtonOnClickScript .= "$('#form_{$this->m_iFormId} button.cancel').on('click', fOnClick{$this->m_iFormId}CancelButton);";
$sCancelButtonOnClickScript .= "$('#form_{$this->m_iFormId} button.cancel').on('click.navigation.itop', fOnClick{$this->m_iFormId}CancelButton);";
$oPage->add_ready_script($sCancelButtonOnClickScript);
$iFieldsCount = count($aFieldsMap);

View File

@@ -524,9 +524,7 @@ EOF
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
{
if (!array_key_exists('dashboard_div_id', $aExtraParams)) {
$aExtraParams['dashboard_div_id'] = utils::Sanitize($this->GetId(), '', 'element_identifier');
}
$aExtraParams['dashboard_div_id'] = utils::Sanitize($aExtraParams['dashboard_div_id'] ?? null, $this->GetId(), utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
/** @var \DashboardLayoutMultiCol $oLayout */
$oLayout = new $this->sLayoutClass();
@@ -1052,7 +1050,7 @@ EOF
$sSelectorHtml .= '</div>';
$sFile = addslashes($this->GetDefinitionFile());
$sReloadURL = $this->GetReloadURL();
$sReloadURL = json_encode($this->GetReloadURL());
$bFromDashboardPage = isset($aAjaxParams['from_dashboard_page']) ? isset($aAjaxParams['from_dashboard_page']) : false;
if ($bFromDashboardPage) {
@@ -1141,7 +1139,6 @@ JS
->AddCSSClass('ibo-action-button');
$oToolbar->AddSubBlock($oActionButton);
$aActions = array();
$sFile = addslashes(utils::LocalPath($this->sDefinitionFile));
$sJSExtraParams = json_encode($aExtraParams);
@@ -1166,7 +1163,7 @@ JS
$oToolbar->AddSubBlock($oActionButton)
->AddSubBlock($oActionsMenu);
$sReloadURL = $this->GetReloadURL();
$sReloadURL = json_encode($this->GetReloadURL());
$oPage->add_script(
<<<EOF
function EditDashboard(sId, sDashboardFile, aExtraParams)
@@ -1273,7 +1270,7 @@ EOF
$sTitle = json_encode($this->sTitle);
$sFile = json_encode($this->GetDefinitionFile());
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
$sReloadURL = $this->GetReloadURL();
$sReloadURL = json_encode($this->GetReloadURL());
$sExitConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
$sCancelConfirmationMessage = addslashes(Dict::S('UI:CancelConfirmationMessage'));

View File

@@ -2138,7 +2138,7 @@ class DashletHeaderDynamic extends Dashlet
$oSet = new DBObjectSet($oFilter);
$iCount = $oSet->Count();
$oAppContext = new ApplicationContext();
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($oFilter->serialize());
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search'.$oAppContext->GetForLink(true).'&filter='.rawurlencode($oFilter->serialize());
$oSubTitle->AddHtml('<a class="summary" href="'.$sHyperlink.'">'.Dict::Format(str_replace('_', ':', $sSubtitle), $iCount).'</a>');
return $oPanel;

View File

@@ -1124,7 +1124,7 @@ JS
$oSingleGroupByValueFilter->SetShowObsoleteData($this->m_bShowObsoleteData);
}
$sHyperlink = utils::GetAbsoluteUrlAppRoot()
.'pages/UI.php?operation=search&'.$oAppContext->GetForLink()
.'pages/UI.php?operation=search'.$oAppContext->GetForLink(true)
.'&filter='.rawurlencode($oSingleGroupByValueFilter->serialize());
$aCounts[$sStateValue] = ['link' => $sHyperlink, 'label' => $aCounts[$sStateValue]];
}
@@ -1232,7 +1232,7 @@ JS
$iCount = $this->m_oSet->Count();
$sClassLabel = MetaModel::GetName($sClass);
$sClassIconUrl = MetaModel::GetClassIcon($sClass, false);
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($this->m_oFilter->serialize());
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search'.$oAppContext->GetForLink(true).'&filter='.rawurlencode($this->m_oFilter->serialize());
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
$aRefreshParams = [
@@ -1241,7 +1241,7 @@ JS
];
if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY)) {
$sCreateActionUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class='.$sClass.'&'.$oAppContext->GetForLink();
$sCreateActionUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class='.$sClass.$oAppContext->GetForLink(true);
$sCreateActionLabel = Dict::Format('UI:Button:Create');
$oBlock = DashletFactory::MakeForDashletBadge($sClassIconUrl, $sHyperlink, $iCount, $sClassLabel, $sCreateActionUrl,
$sCreateActionLabel, $aRefreshParams);
@@ -1289,7 +1289,7 @@ JS
$aData = array();
$oAppContext = new ApplicationContext();
$sParams = $oAppContext->GetForLink();
$sParams = $oAppContext->GetForLink(true);
foreach ($aGroupBy as $iRow => $iCount) {
// Build the search for this subset
$oSubsetSearch = $this->m_oFilter->DeepClone();
@@ -1304,7 +1304,7 @@ JS
$aData[] = array(
'group' => $aLabels[$iRow],
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1&$sParams&filter=$sFilter\">$iCount</a>"
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1$sParams&filter=$sFilter\">$iCount</a>"
); // TO DO: add the context information
}
$aAttribs = array(
@@ -1636,7 +1636,7 @@ JS
$sGroupByExpr = isset($aExtraParams['group_by_expr']) ? '&params[group_by_expr]='.$aExtraParams['group_by_expr'] : '';
$sFilter = $this->m_oFilter->serialize(false, $aQueryParams);
$oContext = new ApplicationContext();
$sContextParam = $oContext->GetForLink();
$sContextParam = $oContext->GetForLink(true);
$sAggregationFunction = isset($aExtraParams['aggregation_function']) ? $aExtraParams['aggregation_function'] : '';
$sAggregationAttr = isset($aExtraParams['aggregation_attribute']) ? $aExtraParams['aggregation_attribute'] : '';
$sLimit = isset($aExtraParams['limit']) ? $aExtraParams['limit'] : '';
@@ -1644,7 +1644,7 @@ JS
$sOrderDirection = isset($aExtraParams['order_direction']) ? $aExtraParams['order_direction'] : '';
if (isset($aExtraParams['group_by_label'])) {
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]=$sChartType&params[currentId]=$sChartId{$iChartCounter}&params[order_direction]=$sOrderDirection&params[order_by]=$sOrderBy&params[limit]=$sLimit&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sChartId{$iChartCounter}&filter=".rawurlencode($sFilter).'&'.$sContextParam;
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]=$sChartType&params[currentId]=$sChartId{$iChartCounter}&params[order_direction]=$sOrderDirection&params[order_by]=$sOrderBy&params[limit]=$sLimit&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sChartId{$iChartCounter}&filter=".rawurlencode($sFilter).$sContextParam;
} else {
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[chart_type]=$sChartType&params[currentId]=$sChartId{$iChartCounter}&params[order_direction]=$sOrderDirection&params[order_by]=$sOrderBy&params[limit]=$sLimit&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sChartId{$iChartCounter}&filter=".rawurlencode($sFilter).'&'.$sContextParam;
}
@@ -1681,11 +1681,14 @@ JS
$oBlock = null;
$sJSURLs = '';
$oContext = new ApplicationContext();
$sContextParam = $oContext->GetForLink(true);
if (isset($aExtraParams['group_by'])) {
$this->MakeGroupByQuery($aExtraParams, $oGroupByExp, $sGroupByLabel, $aGroupBy, $sAggregationFunction, $sFctVar, $sAggregationAttr, $sSql);
$aRes = CMDBSource::QueryToArray($sSql);
$oContext = new ApplicationContext();
$sContextParam = $oContext->GetForLink();
$iTotalCount = 0;
$aURLs = array();
@@ -1705,14 +1708,14 @@ JS
$oSubsetSearch = $this->m_oFilter->DeepClone();
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
$oSubsetSearch->AddConditionExpression($oCondition);
$aURLs[] = utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html&filter=".rawurlencode($oSubsetSearch->serialize()).'&'.$sContextParam;
$aURLs[] = utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html&filter=".rawurlencode($oSubsetSearch->serialize()).$sContextParam;
}
$sJSURLs = json_encode($aURLs);
}
if (isset($aExtraParams['group_by_label'])) {
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$aExtraParams[group_by]&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]=$sChartType&params[currentId]=$aExtraParams[currentId]&params[order_direction]=$aExtraParams[order_direction]&params[order_by]=$aExtraParams[order_by]&params[limit]=$aExtraParams[limit]&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).'&'.$sContextParam;
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$aExtraParams[group_by]&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]=$sChartType&params[currentId]=$aExtraParams[currentId]&params[order_direction]=$aExtraParams[order_direction]&params[order_by]=$aExtraParams[order_by]&params[limit]=$aExtraParams[limit]&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).$sContextParam;
} else {
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$aExtraParams[group_by]&params[chart_type]=$sChartType&params[currentId]=$aExtraParams[currentId]&params[order_direction]=$aExtraParams[order_direction]&params[order_by]=$aExtraParams[order_by]&params[limit]=$aExtraParams[limit]&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).'&'.$sContextParam;
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$aExtraParams[group_by]&params[chart_type]=$sChartType&params[currentId]=$aExtraParams[currentId]&params[order_direction]=$aExtraParams[order_direction]&params[order_by]=$aExtraParams[order_by]&params[limit]=$aExtraParams[limit]&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).$sContextParam;
}
switch ($sChartType) {
@@ -1785,7 +1788,7 @@ JS
$oBlock->sCsvFile = strtolower($this->m_oFilter->GetClass()).'.csv';
$oBlock->sDownloadLink = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?expression='.urlencode($this->m_oFilter->ToOQL(true)).'&format=csv&filename='.urlencode($oBlock->sCsvFile);
$oBlock->sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($this->m_oFilter->serialize()).'&format=csv';
$oBlock->sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search'.$oAppContext->GetForLink(true).'&filter='.rawurlencode($this->m_oFilter->serialize()).'&format=csv';
// Pass the parameters via POST, since expression may be very long
$aParamsToPost = array(
'expression' => $this->m_oFilter->ToOQL(true),
@@ -1885,10 +1888,7 @@ class MenuBlock extends DisplayBlock
&& (!isset($aExtraParams['menu']) || $aExtraParams['menu'] === "1" || $aExtraParams['menu'] === true)
) {
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
if (utils::IsNotNullOrEmptyString($sContext)) {
$sContext = '&'.$sContext;
}
$sContext = $oAppContext->GetForLink(true);
$sFilter = $this->GetFilter()->serialize();
@@ -2578,11 +2578,8 @@ class MenuBlock extends DisplayBlock
$sUrl = "{$sRootUrl}pages/{$sUIPage}?{$sUrlParams}";
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
if (utils::IsNotNullOrEmptyString($sContext)) {
$sUrl .= '&'.$sContext;
}
$sContext = $oAppContext->GetForLink(true);
return $sUrl;
return $sUrl . $sContext;
}
}

View File

@@ -63,14 +63,14 @@ class CoreCannotSaveObjectException extends CoreException
public function getTextMessage()
{
$sTitle = Dict::S('UI:Error:SaveFailed');
$sContent = utils::HtmlEntities($sTitle);
$sContent = $sTitle;
if (count($this->aIssues) == 1) {
$sIssue = reset($this->aIssues);
$sContent .= utils::HtmlEntities($sIssue);
$sContent .= $sIssue;
} else {
foreach ($this->aIssues as $sError) {
$sContent .= " ".utils::HtmlEntities($sError).", ";
$sContent .= " " . $sError . ", ";
}
}

View File

@@ -67,7 +67,7 @@ class CoreException extends Exception
public function getHtmlDesc($sHighlightHtmlBegin = '<b>', $sHighlightHtmlEnd = '</b>')
{
return $this->getMessage();
return utils::EscapeHtml($this->getMessage());
}
/**

View File

@@ -250,7 +250,7 @@ class UIExtKeyWidget
foreach ($aAdditionalField as $sAdditionalField) {
array_push($aArguments, $oObj->Get($sAdditionalField));
}
$aOption['additional_field'] = utils::HtmlEntities(vsprintf($sFormatAdditionalField, $aArguments));
$aOption['additional_field'] = utils::HtmlEntities(utils::VSprintf($sFormatAdditionalField, $aArguments));
}
if (!empty($sObjectImageAttCode)) {
// Try to retrieve image for contact

View File

@@ -521,8 +521,8 @@ class utils
// For URL
case static::ENUM_SANITIZATION_FILTER_URL:
// N°6350 - returns only valid URLs
$retValue = filter_var($value, FILTER_VALIDATE_URL);
$retValue = filter_var($value, FILTER_SANITIZE_URL);
$retValue = filter_var($retValue, FILTER_VALIDATE_URL);
break;
default:
@@ -1516,12 +1516,12 @@ class utils
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
/** @var \DBObjectSet $param */
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$sContext = $oAppContext->GetForLink(true);
$sDataTableId = is_null($sDataTableId) ? '' : $sDataTableId;
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($param->GetFilter()->GetClass());
$sOQL = addslashes($param->GetFilter()->ToOQL(true));
$sFilter = urlencode($param->GetFilter()->serialize());
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter.$sContext;
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
@@ -1555,6 +1555,10 @@ class utils
}
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL', '$sContext')");
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
if (ApplicationMenu::IsMenuIdEnabled('RunQueriesMenu')) {
$oMenuItemPlay = new JSPopupMenuItem('UI:Menu:OpenOQL', Dict::S('UI:Menu:OpenOQL'), "OpenOql('$sOQL')");
$aResult[] = $oMenuItemPlay;
}
break;
@@ -1691,8 +1695,8 @@ class utils
$oAppContext = new ApplicationContext();
$sUrl = $sAppRootUrl
.'pages/UI.php?operation=search&'
.$oAppContext->GetForLink()
.'pages/UI.php?operation=search'
.$oAppContext->GetForLink(true)
.'&filter='.rawurlencode($oDataTableSearchFilter->serialize());
$sUrl .= '&aParams='.rawurlencode($sParams); // Not working... yet, cause not handled by UI.php
@@ -2075,6 +2079,127 @@ SQL;
);
}
/**
* Format a string using vsprintf with safety checks to avoid ValueError
*
* This method fills missing arguments with their original format specifiers,
* then calls vsprintf with the complete array.
*
* @param string $sFormat The format string
* @param array $aArgs The arguments to format
* @param bool $bLogErrors Whether to log errors (defaults to true)
*
* @return string The formatted string
* @since 3.2.2
*/
public static function VSprintf(string $sFormat, array $aArgs, bool $bLogErrors = true): string
{
// Extract all format specifiers
$sPattern = '/%(?:(?:[1-9][0-9]*)\$)?[-+\'0# ]*(?:[0-9]*|\*)?(?:\.(?:[0-9]*|\*))?(?:[hlL])?[diouxXeEfFgGcrs%]/';
preg_match_all($sPattern, $sFormat, $aMatches, PREG_OFFSET_CAPTURE);
// Process matches, keeping track of their positions and excluding escaped percent signs (%%)
$aSpecifierMatches = [];
foreach ($aMatches[0] as $sMatch) {
if ($sMatch[0] !== '%%') {
$aSpecifierMatches[] = $sMatch;
}
}
// Check for positional specifiers and build position map
$bHasPositional = false;
$iMaxPosition = 0;
$aPositions = [];
$aUniquePositions = [];
foreach ($aSpecifierMatches as $index => $match) {
$sSpec = $match[0];
if (preg_match('/^%([1-9][0-9]*)\$/', $sSpec, $posMatch)) {
$bHasPositional = true;
$iPosition = (int)$posMatch[1] - 1; // Convert to 0-based
$aPositions[$index] = $iPosition;
$aUniquePositions[$iPosition] = true;
$iMaxPosition = max($iMaxPosition, $iPosition + 1);
} else {
$aPositions[$index] = $index;
$aUniquePositions[$index] = true;
$iMaxPosition = max($iMaxPosition, $index + 1);
}
}
// Count unique positions, this tells us how many arguments we actually need
$iExpectedCount = count($aUniquePositions);
$iActualCount = count($aArgs);
// If we have enough arguments, just use vsprintf
if ($iActualCount >= $iExpectedCount) {
return vsprintf($sFormat, $aArgs);
}
// else log the error if needed
if ($bLogErrors) {
IssueLog::Warning("Format string requires $iExpectedCount arguments, but only $iActualCount provided. Format: '$sFormat'" );
}
// Create a replacement map
if ($bHasPositional) {
// For positional, we need to handle the exact positions
$aReplacements = array_fill(0, $iMaxPosition, null);
// Fill in the real arguments first
foreach ($aArgs as $index => $sValue) {
if ($index < $iMaxPosition) {
$aReplacements[$index] = $sValue;
}
}
// For null values in the replacement map, use the original specifier
foreach ($aSpecifierMatches as $index => $sMatch) {
$iPosition = $aPositions[$index];
if ($aReplacements[$iPosition] === null) {
// Use the original format specifier when we don't have an argument
$aReplacements[$iPosition] = $sMatch[0];
}
}
// Remove any remaining nulls (for positions that weren't referenced)
$aReplacements = array_filter($aReplacements, static function($val) { return $val !== null; });
} else {
// For non-positional, we need to map each position
$aReplacements = [];
$iUsed = 0;
// Create a map of what values to use for each position
$aPositionValues = [];
for ($i = 0; $i < $iMaxPosition; $i++) {
if (isset($aUniquePositions[$i])) {
if ($iUsed < $iActualCount) {
// We have an actual argument for this position
$aPositionValues[$i] = $aArgs[$iUsed++];
} else {
// Mark this position to use the original specifier
$aPositionValues[$i] = null;
}
}
}
// Build the replacements array preserving the original order
foreach ($aSpecifierMatches as $index => $sMatch) {
$iPosition = $aPositions[$index];
if (isset($aPositionValues[$iPosition])) {
$aReplacements[] = $aPositionValues[$iPosition];
} else {
// Use the original format specifier when we don't have an argument
$aReplacements[] = $sMatch[0];
// Mark this position as used, so if it appears again, it gets the same replacement
$aPositionValues[$iPosition] = $sMatch[0];
}
}
}
// Process the format string with our filled-in arguments
return vsprintf($sFormat, $aReplacements);
}
/**
* Convert a string containing some (valid) HTML markup to plain text
*

View File

@@ -4440,7 +4440,7 @@ class AttributeText extends AttributeString
{
// Is there a way to know the current limitation for mysql?
// See mysql_field_len()
return 16383; // number of characters (that can be 1-4 bytes long), not of bytes
return 65535;
}
public static function RenderWikiHtml($sText, $bWikiOnly = false)
@@ -4995,7 +4995,7 @@ class AttributeCaseLog extends AttributeLongText
}
else
{
if (strlen($proposedValue) > 0)
if (utils::StrLen($proposedValue) > 0)
{
//N°5135 - add impersonation information in caselog
if (UserRights::IsImpersonated()){
@@ -10954,12 +10954,12 @@ abstract class AttributeSet extends AttributeDBFieldVoid
$sDescription = utils::EscapeHtml($this->GetValueDescription($sValue));
$oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sValue'");
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$sContext = $oAppContext->GetForLink(true);
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($oFilter->GetClass());
$sFilter = rawurlencode($oFilter->serialize());
$sLink = '';
if ($bWithLink && $this->bDisplayLink) {
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter.$sContext;
$sLink = ' href="'.$sUrl.'"';
}
@@ -12276,13 +12276,13 @@ class AttributeTagSet extends AttributeSet
$sTagDescription = $oTag->Get('description');
$oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sTagCode'");
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$sContext = $oAppContext->GetForLink(true);
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($oFilter->GetClass());
$sFilter = rawurlencode($oFilter->serialize());
$sLink = '';
if ($bWithLink && $this->bDisplayLink) {
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter.$sContext;
$sLink = ' href="'.$sUrl.'"';
}

View File

@@ -1406,7 +1406,7 @@ class BulkChange
$aDetails = array();
while ($oChange = $oBulkChanges->Fetch())
{
$sDate = '<a href="csvimport.php?step=10&changeid='.$oChange->GetKey().'&'.$oAppContext->GetForLink().'">'.$oChange->Get('date').'</a>';
$sDate = '<a href="csvimport.php?step=10&changeid='.$oChange->GetKey().$oAppContext->GetForLink(true).'">'.$oChange->Get('date').'</a>';
$sUser = $oChange->GetUserName();
if (preg_match('/^(.*)\\(CSV\\)$/i', $oChange->Get('userinfo'), $aMatches))
{
@@ -1488,7 +1488,7 @@ EOF
function OnTruncatedHistoryToggle(bShowAll)
{
$('#csv_history_reload').html('<img src="' + GetAbsoluteUrlAppRoot() + 'images/indicator.gif"/>');
$.get(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?{$sAppContext}', {operation: 'displayCSVHistory', showall: bShowAll}, function(data)
$.get(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?$sAppContext', {operation: 'displayCSVHistory', showall: bShowAll}, function(data)
{
$('#$sAjaxDivId').html(data);
}

View File

@@ -25,7 +25,7 @@ define('EXPORTER_DEFAULT_CHUNK_SIZE', 1000);
class BulkExportException extends Exception
{
protected $sLocalizedMessage;
public function __construct($message, $sLocalizedMessage, $code = null, $previous = null)
public function __construct($message, $sLocalizedMessage, $code = 0, $previous = null)
{
parent::__construct($message, $code, $previous);
$this->sLocalizedMessage = $sLocalizedMessage;

View File

@@ -1233,6 +1233,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'sessions_tracking.session_handler_extension' => [
'type' => 'string',
'description' => 'to store more data in itop session files, set your own iSessionHandlerExtension implementation class in this variable',
'default' => '',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'sessions_tracking.gc_threshold' => [
'type' => 'integer',
'description' => 'fallback in case cron is not active: probability in percent that session files are cleanup during any itop request (100 means always)',

View File

@@ -760,10 +760,10 @@ abstract class DBObject implements iDisplay
*/
public function SetTrim($sAttCode, $sValue)
{
if (!$this->StringFitsInField($sAttCode, $sValue)) {
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
$iMaxSize = $oAttDef->GetMaxSize();
$sLength = mb_strlen($sValue);
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
$iMaxSize = $oAttDef->GetMaxSize();
$sLength = mb_strlen($sValue);
if ($iMaxSize && ($sLength > $iMaxSize)) {
$sMessage = " -truncated ($sLength chars)";
$sValue = mb_substr($sValue, 0, $iMaxSize - mb_strlen($sMessage)).$sMessage;
}
@@ -818,24 +818,6 @@ abstract class DBObject implements iDisplay
$oKPI->ComputeStatsForExtension($this, 'AfterDelete');
}
/**
* @param string $sAttCode
* @param string $sValue
*
* @return bool
* @throws \Exception
*
* @Since 3.2.2
*/
public function StringFitsInField(string $sAttCode, string $sValue): bool
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
$iMaxSize = $oAttDef->GetMaxSize();
$sLength = mb_strlen($sValue);
return !($iMaxSize && ($sLength > $iMaxSize));
}
/**
* Compute (and optionally start) the StopWatches deadlines
*

View File

@@ -206,7 +206,7 @@ class Dict
}
try{
return vsprintf($sLocalizedFormat, $aArguments);
return utils::VSprintf($sLocalizedFormat, $aArguments);
} catch(\Throwable $e){
\IssueLog::Error("Cannot format dict key", null, ["sFormatCode" => $sFormatCode, "sLangCode" => $sLangCode, 'exception_msg' => $e->getMessage() ]);
return $sFormatCode.' - '.implode(', ', $aArguments);

View File

@@ -1470,8 +1470,8 @@ class DisplayableGraph extends SimpleGraph
try {
$this->InitFromGraphviz();
$sExportAsPdfURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_pdf&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
$sContext = $oAppContext->GetForLink();
$sDrillDownURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class=%1$s&id=%2$s&'.$sContext;
$sContext = $oAppContext->GetForLink(true);
$sDrillDownURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class=%1$s&id=%2$s'.$sContext;
$sExportAsDocumentURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_attachment&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
$sLoadFromURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_json&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
$sAttachmentExportTitle = '';

View File

@@ -469,7 +469,8 @@ class ExecutionKPI
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
//$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
$sExtension = '';
$oKPILogData = new KpiLogData(
KpiLogData::TYPE_STATS,
$sOperation,

View File

@@ -7081,7 +7081,7 @@ abstract class MetaModel
* @param array $aParams
* @param bool $bAllowAllData
*
* @return \DBObject
* @return \DBObject|null
* @throws \OQLException
*/
public static function GetObjectFromOQL($sQuery, $aParams = null, $bAllowAllData = false)

View File

@@ -77,7 +77,7 @@ abstract class ModelReflection
return $sFormatCode.' - '.implode(', ', $aArguments);
}
return vsprintf($sLocalizedFormat, $aArguments);
return utils::VSprintf($sLocalizedFormat, $aArguments);
}
/**

View File

@@ -76,6 +76,52 @@ class ObjectResult
$this->fields = array();
}
/**
* Creates an ObjectResult from a DBObject.
*
* @param DBObject $oObj The object.
* @param array|null $aFieldSpec An array of class => attribute codes (Cf. RestUtils::GetFieldList). List of the attributes to be reported.
* @param boolean $bExtendedOutput Output all of the link set attributes ?
* @param integer $iCode An error code (RestResult::OK is no issue has been found)
* @param string $sMessage Description of the error if any, an empty string otherwise
*
* @return ObjectResult
*/
public static function FromDBObject(DBObject $oObj, ?array $aFieldSpec = null, $bExtendedOutput = false, $iCode = 0, $sMessage = '') : ObjectResult {
$oObjRes = new ObjectResult($oObj::class, $oObj->GetKey());
$oObjRes->code = $iCode;
$oObjRes->message = $sMessage;
$aFields = null;
if (!is_null($aFieldSpec))
{
// Enum all classes in the hierarchy, starting with the current one
foreach (MetaModel::EnumParentClasses($oObj::class, ENUM_PARENT_CLASSES_ALL, false) as $sRefClass)
{
if (array_key_exists($sRefClass, $aFieldSpec))
{
$aFields = $aFieldSpec[$sRefClass];
break;
}
}
}
if (is_null($aFields))
{
// No fieldspec given, or not found...
$aFields = array('id', 'friendlyname');
}
foreach ($aFields as $sAttCode)
{
$oObjRes->AddField($oObj, $sAttCode, $bExtendedOutput);
}
return $oObjRes;
}
/**
* Helper to make an output value for a given attribute
*
@@ -204,34 +250,7 @@ class RestResultWithObjects extends RestResult
*/
public function AddObject($iCode, $sMessage, $oObject, $aFieldSpec = null, $bExtendedOutput = false)
{
$sClass = get_class($oObject);
$oObjRes = new ObjectResult($sClass, $oObject->GetKey());
$oObjRes->code = $iCode;
$oObjRes->message = $sMessage;
$aFields = null;
if (!is_null($aFieldSpec))
{
// Enum all classes in the hierarchy, starting with the current one
foreach (MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, false) as $sRefClass)
{
if (array_key_exists($sRefClass, $aFieldSpec))
{
$aFields = $aFieldSpec[$sRefClass];
break;
}
}
}
if (is_null($aFields))
{
// No fieldspec given, or not found...
$aFields = array('id', 'friendlyname');
}
foreach ($aFields as $sAttCode)
{
$oObjRes->AddField($oObject, $sAttCode, $bExtendedOutput);
}
$oObjRes = ObjectResult::FromDBObject($oObject, $aFieldSpec, $bExtendedOutput, $iCode, $sMessage);
$sObjKey = get_class($oObject).'::'.$oObject->GetKey();
$this->objects[$sObjKey] = $oObjRes;

View File

@@ -1962,6 +1962,15 @@ class UserRights
return self::$m_aCacheUsers[$sAuthentication][$sLogin];
}
/**
* Reset the cache of users
* @return void
*/
public static function ResetCacheUsers()
{
self::$m_aCacheUsers = [ 'internal' => [], 'external' => [] ];
}
/**
* @param string$sClass
* @param array $aAllowedOrgs
@@ -2120,6 +2129,8 @@ class StimulusChecker extends ActionChecker
{
var $sState = null;
public mixed $iState = null;
public function __construct(DBSearch $oFilter, $sState, $iStimulusCode)
{
parent::__construct($oFilter, $iStimulusCode);

View File

@@ -435,7 +435,7 @@ class ValueSetObjects extends ValueSetDefinition
foreach ($aAdditionalField as $sAdditionalField) {
array_push($aArguments, $oObject->Get($sAdditionalField));
}
$aData['additional_field'] = vsprintf($sFormatAdditionalField, $aArguments);
$aData['additional_field'] = utils::VSprintf($sFormatAdditionalField, $aArguments);
} else {
$aData['additional_field'] = '';
}

View File

@@ -21,12 +21,10 @@
<db_key_field>id</db_key_field>
<db_final_class_field/>
<naming>
<format>%1$s</format>
<attributes>
<attribute id="login"/>
</attributes>
</naming>
<display_template/>
<style>
<icon/>
</style>

View File

@@ -201,7 +201,7 @@ SQL;
}
SetupLog::Info("Initializing attachment/item_org_id - $iUpdated records have been adjusted");
if (MetaModel::GetAttributeDef('Attachment', 'contact_id') instanceof AttributeExternalKey) {
if (MetaModel::GetAttributeDef('Attachment', 'contact_id') instanceof AttributeExternalKey && version_compare($sPreviousVersion, '3.2.0', '<')) {
SetupLog::Info("Upgrading itop-attachment from '$sPreviousVersion' to '$sCurrentVersion'. Starting with 3.2.0, contact_id will be added into the DB...");
$sUserTableName = MetaModel::DBGetTable('User');
$sUserFieldContactId = MetaModel::GetAttributeDef('User', 'contactid')->Get('sql');

View File

@@ -53,14 +53,7 @@ class DBRestore extends DBBackup
$sUser = self::EscapeShellArg($this->sDBUser);
$sPwd = self::EscapeShellArg($this->sDBPwd);
$sDBName = self::EscapeShellArg($this->sDBName);
if (empty($this->sMySQLBinDir))
{
$sMySQLExe = 'mysql';
}
else
{
$sMySQLExe = '"'.$this->sMySQLBinDir.'/mysql"';
}
$sMySQLExe = DBBackup::MakeSafeMySQLCommand($this->sMySQLBinDir, 'mysql');
if (is_null($this->iDBPort))
{
$sPortOption = '';

View File

@@ -95,12 +95,7 @@ try {
//
$sMySQLBinDir = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', '');
$sMySQLBinDir = utils::ReadParam('mysql_bindir', $sMySQLBinDir, true);
if (empty($sMySQLBinDir)) {
$sMySQLDump = 'mysqldump';
} else {
//echo 'Info - Found mysql_bindir: '.$sMySQLBinDir;
$sMySQLDump = '"'.$sMySQLBinDir.'/mysqldump"';
}
$sMySQLDump = DBBackup::MakeSafeMySQLCommand($sMySQLBinDir, 'mysqldump');
$sCommand = "$sMySQLDump -V 2>&1";
$aOutput = array();

View File

@@ -46,12 +46,12 @@
<field id="status" xsi:type="AttributeEnum">
<sort_type>rank</sort_type>
<values>
<value id="approved">approved
<value id="approved">
<code>approved</code>
<rank>60</rank>
<style><main_color>$ibo-lifecycle-success-state-primary-color</main_color><complementary_color>$ibo-lifecycle-success-state-secondary-color</complementary_color><decoration_classes>fas fa-user-check</decoration_classes></style>
</value>
<value id="assigned">assigned
<value id="assigned">
<code>assigned</code>
<rank>40</rank>
<style><main_color>$ibo-lifecycle-neutral-state-primary-color</main_color><complementary_color>$ibo-lifecycle-neutral-state-secondary-color</complementary_color><decoration_classes/></style>

View File

@@ -151,7 +151,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:Change/Stimulus:ev_approve+' => '',
'Class:Change/Stimulus:ev_replan' => 'Replan',
'Class:Change/Stimulus:ev_replan+' => '',
'Class:Change/Stimulus:ev_notapprove' => 'Reject',
'Class:Change/Stimulus:ev_notapprove' => 'Reject approval',
'Class:Change/Stimulus:ev_notapprove+' => '',
'Class:Change/Stimulus:ev_implement' => 'Implement',
'Class:Change/Stimulus:ev_implement+' => '',

View File

@@ -151,7 +151,7 @@ Dict::Add('EN GB', 'British English', 'British English', array(
'Class:Change/Stimulus:ev_approve+' => '',
'Class:Change/Stimulus:ev_replan' => 'Replan',
'Class:Change/Stimulus:ev_replan+' => '',
'Class:Change/Stimulus:ev_notapprove' => 'Reject',
'Class:Change/Stimulus:ev_notapprove' => 'Reject approval',
'Class:Change/Stimulus:ev_notapprove+' => '',
'Class:Change/Stimulus:ev_implement' => 'Implement',
'Class:Change/Stimulus:ev_implement+' => '',

View File

@@ -139,7 +139,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
'Class:Change/Stimulus:ev_approve+' => '~~',
'Class:Change/Stimulus:ev_replan' => 'Replan~~',
'Class:Change/Stimulus:ev_replan+' => '~~',
'Class:Change/Stimulus:ev_notapprove' => 'Reject~~',
'Class:Change/Stimulus:ev_notapprove' => 'Reject approval~~',
'Class:Change/Stimulus:ev_notapprove+' => '~~',
'Class:Change/Stimulus:ev_implement' => 'Implement~~',
'Class:Change/Stimulus:ev_implement+' => '~~',

View File

@@ -380,7 +380,6 @@
</attribute>
<attribute id="private_log">
<read_only/>
<read_only/>
</attribute>
<attribute id="caller_id">
<read_only/>

View File

@@ -4351,7 +4351,6 @@
</item>
<item id="document_id">
<rank>20</rank>
<rank>20</rank>
</item>
</items>
</list>
@@ -5736,8 +5735,7 @@
<duplicates/>
</field>
</fields>
<methods>
</methods>
<methods/>
<presentation>
<details>
<items>
@@ -7230,7 +7228,12 @@
</class>
<class id="DocumentFile" _delta="must_exist">
<presentation>
<details><items><item id="cis_list" _delta="define"><rank>70</rank></item></items>&gt;
<details>
<items>
<item id="cis_list" _delta="define">
<rank>70</rank>
</item>
</items>
</details>
</presentation>
</class>

View File

@@ -323,8 +323,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:NetworkDevice/Attribute:networkdevicetype_id+' => '',
'Class:NetworkDevice/Attribute:networkdevicetype_name' => 'Nom Type',
'Class:NetworkDevice/Attribute:networkdevicetype_name+' => '',
'Class:NetworkDevice/Attribute:connectablecis_list' => 'Matériel connectés',
'Class:NetworkDevice/Attribute:connectablecis_list+' => 'Tous les matériels connectés à cet appareil réseau',
'Class:NetworkDevice/Attribute:connectablecis_list' => 'Matériel connecté',
'Class:NetworkDevice/Attribute:connectablecis_list+' => 'Tous les équipements connectés à cet appareil réseau',
'Class:NetworkDevice/Attribute:iosversion_id' => 'Version IOS',
'Class:NetworkDevice/Attribute:iosversion_id+' => '',
'Class:NetworkDevice/Attribute:iosversion_name' => 'Nom Version IOS',
@@ -1597,7 +1597,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:lnkConnectableCIToNetworkDevice/Attribute:network_port+' => '',
'Class:lnkConnectableCIToNetworkDevice/Attribute:device_port' => 'Port matériel',
'Class:lnkConnectableCIToNetworkDevice/Attribute:device_port+' => '',
'Class:lnkConnectableCIToNetworkDevice/Attribute:connection_type' => 'Type de connection',
'Class:lnkConnectableCIToNetworkDevice/Attribute:connection_type' => 'Type de connexion',
'Class:lnkConnectableCIToNetworkDevice/Attribute:connection_type+' => '',
'Class:lnkConnectableCIToNetworkDevice/Attribute:connection_type/Value:downlink' => 'lien descendant',
'Class:lnkConnectableCIToNetworkDevice/Attribute:connection_type/Value:downlink+' => 'lien descendant',

View File

@@ -797,7 +797,6 @@
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="powerstart_name" xsi:type="AttributeExternalField">
<extkey_attcode>powerstart_id</extkey_attcode>

View File

@@ -247,8 +247,10 @@
<db_key_field>link_id</db_key_field>
<db_final_class_field/>
<naming>
<attributes id="error_name"/>
<attributes id="functionalci_name"/>
<attributes>
<attribute id="error_name"/>
<attribute id="functionalci_name"/>
</attributes>
</naming>
<style>
<icon/>

View File

@@ -631,7 +631,7 @@ abstract class AbstractBrick implements TemplatesProviderInterface
// Checking mandatory elements
if (!$oMDElement->hasAttribute('id'))
{
throw new DOMFormatException('Brick node must have both id and xsi:type attributes defined', null, null, $oMDElement);
throw new DOMFormatException('Brick node must have both id and xsi:type attributes defined', 0, null, $oMDElement);
}
$this->SetId($oMDElement->getAttribute('id'));

View File

@@ -93,7 +93,7 @@ class AggregatePageBrick extends PortalBrick
{
if (!$oAggregatePageBrickNode->hasAttribute('id'))
{
throw new DOMFormatException('AggregatePageBrick : must have an id attribute', null,
throw new DOMFormatException('AggregatePageBrick : must have an id attribute', 0,
null, $oAggregatePageBrickNode);
}
$sBrickName = $oAggregatePageBrickNode->getAttribute('id');

View File

@@ -375,7 +375,7 @@ class BrowseBrick extends PortalBrick
{
if (!$oModeNode->hasAttribute('id'))
{
throw new DOMFormatException('BrowseBrick: Browse mode must have a unique ID attribute', null,
throw new DOMFormatException('BrowseBrick: Browse mode must have a unique ID attribute', 0,
null, $oModeNode);
}
@@ -416,7 +416,7 @@ class BrowseBrick extends PortalBrick
// Checking that the brick has at least a browse mode
if (count($this->GetAvailablesBrowseModes()) === 0)
{
throw new DOMFormatException('BrowseBrick : Must have at least one browse mode', null, null, $oMDElement);
throw new DOMFormatException('BrowseBrick : Must have at least one browse mode', 0, null, $oMDElement);
}
// Checking that default browse mode in among the available
if (!in_array($this->sDefaultBrowseMode, array_keys($this->aAvailablesBrowseModes)))
@@ -427,7 +427,7 @@ class BrowseBrick extends PortalBrick
// Checking that the brick has at least a level
if (count($this->GetLevels()) === 0)
{
throw new DOMFormatException('BrowseBrick : Must have at least one level', null, null, $oMDElement);
throw new DOMFormatException('BrowseBrick : Must have at least one level', 0, null, $oMDElement);
}
return $this;

View File

@@ -21,6 +21,8 @@
namespace Combodo\iTop\Portal\Brick;
use Combodo\iTop\DesignElement;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplateDefinitionDto;
use Combodo\iTop\Portal\Service\TemplatesProvider\TemplatesRegister;
use DOMFormatException;
/**
@@ -46,6 +48,17 @@ class CreateBrick extends PortalBrick
/** @var array $aRules */
protected $aRules;
const DEFAULT_PAGE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/create/modal.html.twig';
/** @inheritdoc */
public static function RegisterTemplates(TemplatesRegister $oTemplatesRegister): void
{
parent::RegisterTemplates($oTemplatesRegister);
$oTemplatesRegister->RegisterTemplates(self::class,
TemplateDefinitionDto::Create('page', static::TEMPLATES_BASE_PATH . 'create/modal.html.twig')
);
}
/**
* Constructor
*/

View File

@@ -252,7 +252,7 @@ class FilterBrick extends PortalBrick
// Checking that the brick has at least a target brick id
if (($this->GetTargetBrickId() === null) || ($this->GetTargetBrickId() === ''))
{
throw new DOMFormatException('FilterBrick : Must have a target brick id', null, null, $oMDElement);
throw new DOMFormatException('FilterBrick : Must have a target brick id', 0, null, $oMDElement);
}
return $this;

View File

@@ -893,7 +893,7 @@ class ManageBrick extends PortalBrick
if (!$oModeNode->hasAttribute('id'))
{
throw new DOMFormatException('ManageBrick: Display mode must have a unique ID attribute',
null, null, $oModeNode);
0, null, $oModeNode);
}
$sModeId = $oModeNode->getAttribute('id');
@@ -930,7 +930,7 @@ class ManageBrick extends PortalBrick
{
if (!$oFieldNode->hasAttribute('id'))
{
throw new DOMFormatException('ManageBrick : Field must have a unique ID attribute', null,
throw new DOMFormatException('ManageBrick : Field must have a unique ID attribute', 0,
null, $oFieldNode);
}
$this->AddField($oFieldNode->getAttribute('id'));
@@ -950,7 +950,7 @@ class ManageBrick extends PortalBrick
if (!$oFieldNode->hasAttribute('id'))
{
throw new DOMFormatException('ManageBrick : Field must have a unique ID attribute',
null,
0,
null, $oFieldNode);
}
$this->AddExportField($oFieldNode->getAttribute('id'));
@@ -1012,7 +1012,7 @@ class ManageBrick extends PortalBrick
if (!$oGroupNode->hasAttribute('id'))
{
throw new DOMFormatException('ManageBrick : Group must have a unique ID attribute',
null, null, $oGroupNode);
0, null, $oGroupNode);
}
$sGroupId = $oGroupNode->getAttribute('id');
@@ -1038,12 +1038,12 @@ class ManageBrick extends PortalBrick
if (!isset($aGroup['title']) || $aGroup['title'] === '')
{
throw new DOMFormatException('ManageBrick : Group must have a title tag and it must not be empty',
null, null, $oGroupNode);
0, null, $oGroupNode);
}
if (!isset($aGroup['condition']) || $aGroup['condition'] === '')
{
throw new DOMFormatException('ManageBrick : Group must have a condition tag and it must not be empty',
null, null, $oGroupNode);
0, null, $oGroupNode);
}
$aGroups[] = $aGroup;
}

View File

@@ -211,7 +211,7 @@ class UserProfileBrick extends PortalBrick
$this->aForm['fields'][$sFieldId] = $aField;
} else {
throw new DOMFormatException('Field tag must have an id attribute', null, null, $oFieldNode);
throw new DOMFormatException('Field tag must have an id attribute', 0, null, $oFieldNode);
}
}
}

View File

@@ -53,13 +53,13 @@ class Forms extends AbstractConfiguration
$sFormId = $oFormNode->getAttribute('id');
if ($oFormNode->getAttribute('id') === '')
{
throw new DOMFormatException('form tag must have an id attribute', null, null, $oFormNode);
throw new DOMFormatException('form tag must have an id attribute', 0, null, $oFormNode);
}
// Parsing form object class
if ($oFormNode->GetUniqueElement('class')->GetText() === null)
{
throw new DOMFormatException('Class tag must be defined', null, null, $oFormNode);
throw new DOMFormatException('Class tag must be defined', 0, null, $oFormNode);
}
// Parsing class
@@ -149,7 +149,7 @@ class Forms extends AbstractConfiguration
$sModeId = $oModeNode->getAttribute('id');
if ($sModeId === '')
{
throw new DOMFormatException('mode tag must have an id attribute', null, null,
throw new DOMFormatException('mode tag must have an id attribute', 0, null,
$oFormNode);
}
$aModes[] = $sModeId;
@@ -225,7 +225,7 @@ class Forms extends AbstractConfiguration
}
else
{
throw new DOMFormatException('Field tag must have an id attribute', null, null,
throw new DOMFormatException('Field tag must have an id attribute', 0, null,
$oFormNode);
}
}

View File

@@ -50,7 +50,7 @@ class Lists extends AbstractConfiguration
$sClassId = $oClassNode->getAttribute('id');
if ($sClassId === null)
{
throw new DOMFormatException('Class tag must have an id attribute', null, null, $oClassNode);
throw new DOMFormatException('Class tag must have an id attribute', 0, null, $oClassNode);
}
// - Each lists

View File

@@ -359,7 +359,8 @@ class ObjectFormManager extends FormManager
foreach ($this->aFieldsAtts as $sAttCode => $iFieldFlags)
{
// handle plugins fields
if(array_key_exists($sAttCode, $this->aExtraData)
if($this->sMode !== 'apply_stimulus'
&& array_key_exists($sAttCode, $this->aExtraData)
&& array_key_exists('plugin', $this->aExtraData[$sAttCode])){
$sPluginName = $this->aExtraData[$sAttCode]['plugin'];
switch($sPluginName){
@@ -713,7 +714,8 @@ class ObjectFormManager extends FormManager
// fallback Checking if the instance has attachments
// (in case attachment is not explicitly declared in layout)
if (class_exists('Attachment') && class_exists('AttachmentPlugIn')
if ($this->sMode !== 'apply_stimulus'
&& class_exists('Attachment') && class_exists('AttachmentPlugIn')
&& !$this->IsPluginInitialized(AttachmentPlugIn::class)
&& AttachmentPlugIn::IsAttachmentAllowedForObject($this->oObject)){
$this->AddAttachmentField($this->oForm, 'attachments_plugin', $this->aExtraData);

View File

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

View File

@@ -490,7 +490,7 @@ EOF;
{
throw new DOMFormatException(
'Scope tag in class must have a not empty oql_view tag',
null,
0,
null,
$oScopeNode
);

View File

@@ -10,7 +10,7 @@
{% if aTilesRendering[brick.GetId] is defined %}
{{ aTilesRendering[brick.GetId]|raw }}
{% else %}
{% include '' ~ brick.GetTileTemplatePath with {brick: brick} %}
{% include '' ~ brick.GetTileTemplatePath with {brick: brick} only %}
{% endif %}
{% endfor %}
</section>

View File

@@ -0,0 +1,44 @@
{# itop-portal-base/portal/templates/bricks/create/layout.html.twig #}
{# Create brick base layout #}
{% extends 'itop-portal-base/portal/templates/modal/layout.html.twig' %}
{% block pModalTitle %}
{{ sPageTitle|dict_s }}
{% endblock %}
{% block pModalBody %}
<p>{{ 'Brick:Portal:Create:ChooseType'|dict_s }}</p>
<ul id="{{ sLeafClassesListId }}">
{% for aLeafClass in aLeafClasses %}
<li><a href="#" data-target-class="{{ aLeafClass.id }}">{{ aLeafClass.name }}</a></li>
{% endfor %}
</ul>
<script type="text/javascript">
$(document).ready(function(){
$('#{{ sLeafClassesListId }} a').off('click').on('click', function(oEvent){
oEvent.preventDefault();
// Preparing target class url
var sUrl = '{{ app['url_generator'].generate('p_object_create', {sObjectClass : '-sObjectClass-'})|raw }}';
sUrl = sUrl.replace(/-sObjectClass-/, $(this).attr('data-target-class') );
sUrl = CombodoGlobalToolbox.AddParameterToUrl(sUrl, 'ar_token', '{{ ar_token }}');
// Creating a new modal
CombodoModal.OpenModal({
base_modal: {
usage: 'replace',
selector: $(this).closest('.modal'),
},
content: {
endpoint: sUrl,
},
});
});
});
</script>
{% endblock %}
{% block pModalFooter %}
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'Portal:Button:Cancel'|dict_s }}</button>
{% endblock %}

View File

@@ -156,8 +156,17 @@
return row.attributes[attribute_code].sort_value;
},
filter: function (attribute_code, type, row) {
return $.text($.parseHTML(row.attributes[attribute_code]['value_html']));
},
// Check if the attribute and value_html exist
if (!row.attributes[attribute_code] || !row.attributes[attribute_code]['value_html']) {
return '';
}
// Create a temporary div outside the DOM to filter out XSS
const tempDiv = document.createElement('div');
tempDiv.textContent = row.attributes[attribute_code]['value_html'];
return tempDiv.textContent;
},
},
});
}

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
<itop_design version="3.2">
<classes/>
<user_rights>
<groups>
@@ -199,7 +199,7 @@
<profiles>
<profile id="117" _delta="define">
<name>SuperUser</name>
<description>This profil allows all actions which are not Administrator restricted.</description>
<description>This profile allows all actions which are not Administrator restricted.</description>
<groups>
<group id="AdminTools">
<actions>
@@ -268,6 +268,11 @@
<action id="stimulus:ev_plan">allow</action>
<action id="stimulus:ev_reject">allow</action>
<action id="stimulus:ev_reopen">allow</action>
<action id="stimulus:ev_validate">allow</action>
<action id="stimulus:ev_notapprove">allow</action>
<action id="stimulus:ev_replan">allow</action>
<action id="stimulus:ev_implement">allow</action>
<action id="stimulus:ev_monitor">allow</action>
</actions>
</group>
<group id="Problem">

View File

@@ -256,6 +256,10 @@
<code><![CDATA[
public function OnBeforeWriteTicket(Combodo\iTop\Service\Events\EventData $oEventData)
{
// do not update impacted items from portal
if(ContextTag::Check(ContextTag::TAG_PORTAL)){
return;
}
$aChanges = $this->ListChanges();
if ($this->IsNew() || array_key_exists('functionalcis_list', $aChanges) || array_key_exists('contacts_list', $aChanges)) {
$this->UpdateImpactedItems();

View File

@@ -522,6 +522,7 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'UI:Menu:Manage' => 'Spravovat...',
'UI:Menu:EMail' => 'Email',
'UI:Menu:CSVExport' => 'CSV export',
'UI:Menu:OpenOQL' => 'View the OQL query~~',
'UI:Menu:Modify' => 'Upravit...',
'UI:Menu:Delete' => 'Odstranit...',
'UI:Menu:BulkDelete' => 'Odstranit...',

View File

@@ -937,7 +937,7 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
'Class:SynchroAttLinkSet' => 'Synchro Attribut (Linksæt)',
'Class:SynchroAttLinkSet/Attribute:row_separator' => 'Række separator',
'Class:SynchroAttLinkSet/Attribute:attribute_separator' => 'Attribut separator',
'Class:SynchroLog' => 'Synchr Log',
'Class:SynchroLog' => 'Synchro Log',
'Class:SynchroLog/Attribute:sync_source_id' => 'Synchro Data Kilde',
'Class:SynchroLog/Attribute:start_date' => 'Start Dato',
'Class:SynchroLog/Attribute:end_date' => 'Slut Dato',

View File

@@ -522,6 +522,7 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
'UI:Menu:Manage' => 'Administrer...',
'UI:Menu:EMail' => 'eMail',
'UI:Menu:CSVExport' => 'CSV Eksport...',
'UI:Menu:OpenOQL' => 'View the OQL query~~',
'UI:Menu:Modify' => 'Modificer...',
'UI:Menu:Delete' => 'Slet...',
'UI:Menu:BulkDelete' => 'Slet...',

View File

@@ -521,6 +521,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'UI:Menu:Manage' => 'Verwalten...',
'UI:Menu:EMail' => 'E-Mail',
'UI:Menu:CSVExport' => 'CSV-Export...',
'UI:Menu:OpenOQL' => 'View the OQL query~~',
'UI:Menu:Modify' => 'Modifizieren...',
'UI:Menu:Delete' => 'Löschen...',
'UI:Menu:BulkDelete' => 'Löschen...',

View File

@@ -1014,7 +1014,7 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of
'Class:SynchroAttLinkSet' => 'Synchro Attribute (Linkset)',
'Class:SynchroAttLinkSet/Attribute:row_separator' => 'Rows separator',
'Class:SynchroAttLinkSet/Attribute:attribute_separator' => 'Attributes separator',
'Class:SynchroLog' => 'Synchr Log',
'Class:SynchroLog' => 'Synchro Log',
'Class:SynchroLog/Attribute:sync_source_id' => 'Synchro Data Source',
'Class:SynchroLog/Attribute:start_date' => 'Start Date',
'Class:SynchroLog/Attribute:end_date' => 'End Date',

View File

@@ -543,6 +543,7 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:Menu:Manage' => 'Manage...',
'UI:Menu:EMail' => 'eMail',
'UI:Menu:CSVExport' => 'CSV Export...',
'UI:Menu:OpenOQL' => 'View the OQL query',
'UI:Menu:Modify' => 'Modify...',
'UI:Menu:Delete' => 'Delete...',
'UI:Menu:BulkDelete' => 'Delete...',

View File

@@ -1001,7 +1001,7 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of
'Class:SynchroAttLinkSet' => 'Synchro Attribute (Linkset)',
'Class:SynchroAttLinkSet/Attribute:row_separator' => 'Rows separator',
'Class:SynchroAttLinkSet/Attribute:attribute_separator' => 'Attributes separator',
'Class:SynchroLog' => 'Synchr Log',
'Class:SynchroLog' => 'Synchro Log',
'Class:SynchroLog/Attribute:sync_source_id' => 'Synchro Data Source',
'Class:SynchroLog/Attribute:start_date' => 'Start Date',
'Class:SynchroLog/Attribute:end_date' => 'End Date',

View File

@@ -543,6 +543,7 @@ Dict::Add('EN GB', 'British English', 'British English', array(
'UI:Menu:Manage' => 'Manage...',
'UI:Menu:EMail' => 'eMail',
'UI:Menu:CSVExport' => 'CSV Export...',
'UI:Menu:OpenOQL' => 'View the OQL query',
'UI:Menu:Modify' => 'Modify...',
'UI:Menu:Delete' => 'Delete...',
'UI:Menu:BulkDelete' => 'Delete...',

View File

@@ -520,6 +520,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'UI:Menu:Manage' => 'Administrar',
'UI:Menu:EMail' => 'Enviar por Correo Electrónico',
'UI:Menu:CSVExport' => 'Exportar a CSV...',
'UI:Menu:OpenOQL' => 'View the OQL query~~',
'UI:Menu:Modify' => 'Modificar',
'UI:Menu:Delete' => 'Borrar',
'UI:Menu:BulkDelete' => 'Borrar',

View File

@@ -535,6 +535,7 @@ Nous espérons que vous aimerez cette version autant que nous avons eu du plaisi
'UI:Menu:Manage' => 'Gérer...',
'UI:Menu:EMail' => 'Envoyer par eMail',
'UI:Menu:CSVExport' => 'Exporter en CSV...',
'UI:Menu:OpenOQL' => 'Voir la requête OQL',
'UI:Menu:Modify' => 'Modifier...',
'UI:Menu:Delete' => 'Supprimer...',
'UI:Menu:BulkDelete' => 'Supprimer...',

View File

@@ -524,6 +524,7 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'UI:Menu:Manage' => 'Kezelés...',
'UI:Menu:EMail' => 'Email',
'UI:Menu:CSVExport' => 'CSV exportálás...',
'UI:Menu:OpenOQL' => 'View the OQL query~~',
'UI:Menu:Modify' => 'Módosítás...',
'UI:Menu:Delete' => 'Törlés...',
'UI:Menu:BulkDelete' => 'Törlés...',

View File

@@ -524,6 +524,7 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
'UI:Menu:Manage' => 'Gestisci...',
'UI:Menu:EMail' => 'eMail',
'UI:Menu:CSVExport' => 'Esporta CSV...',
'UI:Menu:OpenOQL' => 'View the OQL query~~',
'UI:Menu:Modify' => 'Modifica...',
'UI:Menu:Delete' => 'Cancella...',
'UI:Menu:BulkDelete' => 'Cancella...',

View File

@@ -525,6 +525,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
'UI:Menu:Manage' => '管理...',
'UI:Menu:EMail' => 'Eメール',
'UI:Menu:CSVExport' => 'CSVエクスポート...',
'UI:Menu:OpenOQL' => 'View the OQL query~~',
'UI:Menu:Modify' => '修正...',
'UI:Menu:Delete' => '削除...',
'UI:Menu:BulkDelete' => '削除...',

View File

@@ -521,6 +521,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'UI:Menu:Manage' => 'Beheer...',
'UI:Menu:EMail' => 'E-mail',
'UI:Menu:CSVExport' => 'CSV Export...',
'UI:Menu:OpenOQL' => 'View the OQL query~~',
'UI:Menu:Modify' => 'Bewerk...',
'UI:Menu:Delete' => 'Verwijder...',
'UI:Menu:BulkDelete' => 'Verwijder...',

View File

@@ -536,6 +536,7 @@ Dict::Add('PL PL', 'Polish', 'Polski', array(
'UI:Menu:Manage' => 'Zarządzaj...',
'UI:Menu:EMail' => 'e-mail',
'UI:Menu:CSVExport' => 'Eksport CSV...',
'UI:Menu:OpenOQL' => 'View the OQL query~~',
'UI:Menu:Modify' => 'Zmień...',
'UI:Menu:Delete' => 'Usuń...',
'UI:Menu:BulkDelete' => 'Usuń...',

View File

@@ -519,6 +519,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'UI:Menu:Manage' => 'Gerenciar...',
'UI:Menu:EMail' => 'Enviar via e-mail',
'UI:Menu:CSVExport' => 'Exportar para CSV...',
'UI:Menu:OpenOQL' => 'View the OQL query~~',
'UI:Menu:Modify' => 'Editar...',
'UI:Menu:Delete' => 'Excluir...',
'UI:Menu:BulkDelete' => 'Exclução em massa...',

View File

@@ -937,7 +937,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:SynchroAttLinkSet' => 'Synchro Attribute (Linkset)~~',
'Class:SynchroAttLinkSet/Attribute:row_separator' => 'Разделитель строк',
'Class:SynchroAttLinkSet/Attribute:attribute_separator' => 'Разделитель атрибутов',
'Class:SynchroLog' => 'Synchr Log~~',
'Class:SynchroLog' => 'Synchro Log~~',
'Class:SynchroLog/Attribute:sync_source_id' => 'Синх.исходные данные',
'Class:SynchroLog/Attribute:start_date' => 'Стартовать в',
'Class:SynchroLog/Attribute:end_date' => 'Закончить в',

View File

@@ -522,6 +522,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'UI:Menu:Manage' => 'Управление...',
'UI:Menu:EMail' => 'Отправить ссылку по email',
'UI:Menu:CSVExport' => 'Экспорт в CSV...',
'UI:Menu:OpenOQL' => 'View the OQL query~~',
'UI:Menu:Modify' => 'Редактировать...',
'UI:Menu:Delete' => 'Удалить...',
'UI:Menu:BulkDelete' => 'Удалить...',

View File

@@ -528,6 +528,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
'UI:Menu:Manage' => 'Manažovať...',
'UI:Menu:EMail' => 'eMail',
'UI:Menu:CSVExport' => 'CSV Export',
'UI:Menu:OpenOQL' => 'View the OQL query~~',
'UI:Menu:Modify' => 'Upraviť...',
'UI:Menu:Delete' => 'Vymazať...',
'UI:Menu:BulkDelete' => 'Vymazať...',

View File

@@ -937,7 +937,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
'Class:SynchroAttLinkSet' => 'Synchro niteliği (LinkSet)',
'Class:SynchroAttLinkSet/Attribute:row_separator' => 'Satır Ayırıcı',
'Class:SynchroAttLinkSet/Attribute:attribute_separator' => 'Nitelik Ayırıcı',
'Class:SynchroLog' => 'Synchr log',
'Class:SynchroLog' => 'Synchro Log',
'Class:SynchroLog/Attribute:sync_source_id' => 'Synchro Veri Kaynağı',
'Class:SynchroLog/Attribute:start_date' => 'Başlangıç tarihi',
'Class:SynchroLog/Attribute:end_date' => 'Bitiş Tarihi',

View File

@@ -525,6 +525,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
'UI:Menu:Manage' => 'Yönet...',
'UI:Menu:EMail' => 'e-posta',
'UI:Menu:CSVExport' => 'CSV olarak dışarı ver...',
'UI:Menu:OpenOQL' => 'View the OQL query~~',
'UI:Menu:Modify' => 'Düzenle...',
'UI:Menu:Delete' => 'Sil...',
'UI:Menu:BulkDelete' => 'Sil...',

View File

@@ -525,6 +525,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'UI:Menu:Manage' => '管理...',
'UI:Menu:EMail' => '邮件',
'UI:Menu:CSVExport' => 'CSV导出...',
'UI:Menu:OpenOQL' => 'View the OQL query~~',
'UI:Menu:Modify' => '修改...',
'UI:Menu:Delete' => '删除...',
'UI:Menu:BulkDelete' => '删除...',

View File

@@ -717,8 +717,11 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
window[sPromiseId].then(function () {
$('#ac_create_'+me.id).dialog('open');
$('#ac_create_'+me.id).dialog("option", "close", me.OnCloseCreateObject);
// Modify the action of the cancel button
$('#ac_create_'+me.id+' button.cancel').off('click').on('click', me.CloseCreateObject);
// Modify the action of the cancel button and the close button
$('#ac_create_'+me.id+' button.cancel').off('click.navigation.itop').on('click.navigation.itop', me.CloseCreateObject);
$('.ui-dialog-titlebar:has(+ #ac_create_'+me.id+') button.ui-dialog-titlebar-close').off('click').on('click', function() {
$('#ac_create_'+me.id+' button.cancel').trigger('click');
});
me.ajax_request = null;
me.sTargetClass = sLocalTargetClass;
// Adjust the dialog's size to fit into the screen

View File

@@ -369,6 +369,24 @@ function DashletCreationDlg(sOQL, sContext) {
return false;
}
function OpenOql(sOQL) {
sBaseUrl = GetAbsoluteUrlAppRoot() + 'pages/run_query.php';
var form = document.createElement("form");
form.setAttribute("method", "post");
form.setAttribute("action", sBaseUrl);
form.setAttribute("target", '_blank');
form.setAttribute("id", 'run_query_form');
var input = document.createElement('input');
input.type = 'hidden';
input.name = 'expression';
input.value = sOQL;
form.appendChild(input);
document.body.appendChild(form);
// form.submit() is blocked by the browser
$('#run_query_form').submit();
document.body.removeChild(form);
}
function ShortcutListDlg(sOQL, sDataTableId, sContext) {
var sDataTableName = 'datatable_'+sDataTableId;
var oTableSettings = {

View File

@@ -525,6 +525,7 @@ return array(
'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectsEvents' => $baseDir . '/sources/Service/TemporaryObjects/TemporaryObjectsEvents.php',
'Combodo\\iTop\\SessionTracker\\SessionGC' => $baseDir . '/sources/SessionTracker/SessionGC.php',
'Combodo\\iTop\\SessionTracker\\SessionHandler' => $baseDir . '/sources/SessionTracker/SessionHandler.php',
'Combodo\\iTop\\SessionTracker\\iSessionHandlerExtension' => $baseDir . '/sources/SessionTracker/iSessionHandlerExtension.php',
'CompileCSSService' => $baseDir . '/application/compilecssservice.class.inc.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'Config' => $baseDir . '/core/config.class.inc.php',
@@ -3230,5 +3231,5 @@ return array(
'privUITransactionFile' => $baseDir . '/application/transaction.class.inc.php',
'privUITransactionSession' => $baseDir . '/application/transaction.class.inc.php',
'utils' => $baseDir . '/application/utils.inc.php',
'<EFBFBD>' => $vendorDir . '/symfony/cache/Traits/ValueWrapper.php',
'©' => $vendorDir . '/symfony/cache/Traits/ValueWrapper.php',
);

View File

@@ -915,6 +915,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectsEvents' => __DIR__ . '/../..' . '/sources/Service/TemporaryObjects/TemporaryObjectsEvents.php',
'Combodo\\iTop\\SessionTracker\\SessionGC' => __DIR__ . '/../..' . '/sources/SessionTracker/SessionGC.php',
'Combodo\\iTop\\SessionTracker\\SessionHandler' => __DIR__ . '/../..' . '/sources/SessionTracker/SessionHandler.php',
'Combodo\\iTop\\SessionTracker\\iSessionHandlerExtension' => __DIR__ . '/../..' . '/sources/SessionTracker/iSessionHandlerExtension.php',
'CompileCSSService' => __DIR__ . '/../..' . '/application/compilecssservice.class.inc.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'Config' => __DIR__ . '/../..' . '/core/config.class.inc.php',
@@ -3620,7 +3621,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'privUITransactionFile' => __DIR__ . '/../..' . '/application/transaction.class.inc.php',
'privUITransactionSession' => __DIR__ . '/../..' . '/application/transaction.class.inc.php',
'utils' => __DIR__ . '/../..' . '/application/utils.inc.php',
'<EFBFBD>' => __DIR__ . '/..' . '/symfony/cache/Traits/ValueWrapper.php',
'©' => __DIR__ . '/..' . '/symfony/cache/Traits/ValueWrapper.php',
);
public static function getInitializer(ClassLoader $loader)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,4 @@
import { Plugin, type Editor } from 'ckeditor5/src/core.js';
import InsertCarriageReturnAfterBlock from "../insert-carriage-return-after-block/insert-carriage-return-after-block.plugin";
/**
* DetectChanges Plugin.
*
@@ -9,5 +8,4 @@ export default class DetectChanges extends Plugin {
constructor(editor: Editor);
init(): void;
static get pluginName(): string;
static get requires(): (typeof InsertCarriageReturnAfterBlock)[];
}

View File

@@ -1,5 +1,6 @@
import { Plugin } from '@ckeditor/ckeditor5-core';
export default class UpdateInputOnChange extends Plugin {
static get pluginName(): string;
private debounceTimeout;
init(): void;
}

View File

@@ -90,7 +90,7 @@ function ApplyNextAction(Webpage $oP, CMDBObject $oObj, $sNextAction)
$oAppContext = new ApplicationContext();
//echo "<p>Missing Attributes <pre>".print_r($aExpectedAttributes, true)."</pre></p>\n";
$oP->add_header('Location: '.utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=stimulus&class='.get_class($oObj).'&stimulus='.$sNextAction.'&id='.$oObj->getKey().'&'.$oAppContext->GetForLink());
$oP->add_header('Location: '.utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=stimulus&class='.get_class($oObj).'&stimulus='.$sNextAction.'&id='.$oObj->getKey().$oAppContext->GetForLink(true));
}
}
@@ -101,7 +101,7 @@ function ReloadAndDisplay($oPage, $oObj, $sMessageId = '', $sMessage = '', $sSev
{
cmdbAbstractObject::SetSessionMessage(get_class($oObj), $oObj->GetKey(), $sMessageId, $sMessage, $sSeverity, 0, true /* must not exist */);
}
$oPage->add_header('Location: '.utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class='.get_class($oObj).'&id='.$oObj->getKey().'&'.$oAppContext->GetForLink());
$oPage->add_header('Location: '.utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class='.get_class($oObj).'&id='.$oObj->getKey().$oAppContext->GetForLink(true));
}
/**
@@ -993,7 +993,7 @@ try
$oForm->AddHtml($oP->GetDetails($aDetails));
$oForm->AddHtml('</td></tr></table>');
$sURL = "./UI.php?operation=search&filter=".urlencode($sFilter)."&".$oAppContext->GetForLink();
$sURL = "./UI.php?operation=search&filter=".urlencode($sFilter).$oAppContext->GetForLink(true);
$oCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'), 'cancel', 'cancel');
$oCancelButton->SetOnClickJsCode("window.location.href='$sURL'");
$oForm->AddSubBlock($oCancelButton);
@@ -1165,7 +1165,7 @@ try
$oP->AddUiBlock($oBlock);
// Back to the list
$sURL = "./UI.php?operation=search&filter=".urlencode($sFilter)."&".$oAppContext->GetForLink();
$sURL = "./UI.php?operation=search&filter=".urlencode($sFilter).$oAppContext->GetForLink(true);
$oSubmitButton = ButtonUIBlockFactory::MakeForSecondaryAction(Dict::S('UI:Button:Done'), 'submit', 'submit', true);
$oSubmitButton->SetOnClickJsCode("window.location.href='$sURL'");
$oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null);
@@ -1520,7 +1520,7 @@ catch (Exception $e) {
$oErrorPage->add("<h1>".Dict::S('UI:FatalErrorMessage')."</h1>\n");
}
$sErrorDetails = ($e instanceof CoreException) ? $e->getHtmlDesc() : $e->getMessage();
$oErrorPage->error(Dict::Format('UI:Error_Details', $sErrorDetails));
$oErrorPage->error(Dict::Format('UI:Error_Details', utils::EscapeHtml($sErrorDetails)));
$oErrorPage->output();
$sErrorStackTrace = ($e instanceof CoreException) ? $e->getFullStackTraceAsString() : $e->getTraceAsString();
@@ -1606,7 +1606,7 @@ class UI
// Add user filter
$oFullSetFilter->UpdateContextFromUser();
$aSelectedObj = utils::ReadMultipleSelection($oFullSetFilter);
$sCancelUrl = "./UI.php?operation=search&filter=".urlencode($sFilter)."&".$oAppContext->GetForLink();
$sCancelUrl = "./UI.php?operation=search&filter=".urlencode($sFilter).$oAppContext->GetForLink(true);
$aContext = array('filter' => utils::EscapeHtml($sFilter));
cmdbAbstractObject::DisplayBulkModifyForm($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $sCancelUrl, array(), $aContext);
}
@@ -1640,7 +1640,7 @@ class UI
throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObj'));
}
$aSelectedObj = explode(',', $sSelectedObj);
$sCancelUrl = "./UI.php?operation=search&filter=".urlencode($sFilter)."&".$oAppContext->GetForLink();
$sCancelUrl = "./UI.php?operation=search&filter=".urlencode($sFilter).$oAppContext->GetForLink(true);
$aContext = array(
'filter' => utils::EscapeHtml($sFilter),
'selectObj' => $sSelectedObj,

View File

@@ -55,9 +55,11 @@ $oP->SetBreadCrumbEntry('ui-tool-universalsearch', Dict::S('Menu:UniversalSearch
//$sSearchHeaderForceDropdown
$sSearchHeaderForceDropdown = '<select id="select_class" name="baseClass" onChange="this.form.submit();">';
$aClassLabels = array();
foreach(MetaModel::GetClasses('bizmodel') as $sCurrentClass)
{
$aClassLabels[$sCurrentClass] = MetaModel::GetName($sCurrentClass);
foreach (MetaModel::GetClasses('bizmodel, grant_by_profile') as $sCurrentClass) {
if ((MetaModel::HasCategory($sCurrentClass, 'grant_by_profile') && UserRights::IsActionAllowed($sCurrentClass, UR_ACTION_BULK_MODIFY))
|| (MetaModel::HasCategory($sCurrentClass, 'bizmodel') && UserRights::IsActionAllowed($sCurrentClass, UR_ACTION_BULK_READ))) {
$aClassLabels[$sCurrentClass] = MetaModel::GetName($sCurrentClass);
}
}
asort($aClassLabels);
foreach($aClassLabels as $sCurrentClass => $sLabel)

View File

@@ -436,7 +436,7 @@ try
foreach ($aErrors as $aErrorRow) {
$aObjectsWithErrors[$aErrorRow['id']] = true;
}
$aRow['nb_errors'] = ($iErrorsCount == 0) ? '0' : "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">$iErrorsCount</a> <a href=\"?operation=csv&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\"><img src=\"" . utils::GetAbsoluteUrlAppRoot() . "images/icons/icons8-export-csv.svg\" class=\"ibo-audit--audit-line--csv-download\"></a>";
$aRow['nb_errors'] = ($iErrorsCount == 0) ? '0' : "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey().$oAppContext->GetForLink(true)."\">$iErrorsCount</a> <a href=\"?operation=csv&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey().$oAppContext->GetForLink(true)."\"><img src=\"" . utils::GetAbsoluteUrlAppRoot() . "images/icons/icons8-export-csv.svg\" class=\"ibo-audit--audit-line--csv-download\"></a>";
$aRow['percent_ok'] = sprintf('%.2f', 100.0 * (($iCount - $iErrorsCount) / $iCount));
$aRow['class'] = $oAuditCategory->GetReportColor($iCount, $iErrorsCount);
}

View File

@@ -29,9 +29,8 @@ use Combodo\iTop\Application\WebPage\AjaxPage;
use Combodo\iTop\Application\WebPage\ErrorPage;
use Combodo\iTop\Application\WebPage\iTopWebPage;
use Combodo\iTop\Application\WebPage\WebPage;
use Combodo\iTop\Service\Import\CSVImportPageProcessor;
use Combodo\iTop\Core\CMDBChange\CMDBChangeOrigin;
use Combodo\iTop\Renderer\BlockRenderer;
use Combodo\iTop\Service\Import\CSVImportPageProcessor;
try {
require_once('../approot.inc.php');
@@ -83,21 +82,24 @@ try {
*
* @return \Combodo\iTop\Application\UI\Base\Component\Input\Select\
*/
function GetClassesSelectUIBlock(string $sName, $sDefaultValue, int $iActionCode): Select
function GetClassesSelectUIBlock(string $sName, $sDefaultValue, int $iActionCode, bool $bAdvanced = false): Select
{
$oSelectBlock = SelectUIBlockFactory::MakeForSelect($sName, 'select_'.$sName);
$oOption = SelectOptionUIBlockFactory::MakeForSelectOption("", Dict::S('UI:CSVImport:ClassesSelectOne'), false);
$oSelectBlock->AddSubBlock($oOption);
$aValidClasses = array();
$aClassCategories = array('bizmodel', 'addon/authentication');
if ($bAdvanced) {
$aClassCategories[] = 'grant_by_profile';
}
if (UserRights::IsAdministrator()) {
$aClassCategories = array('bizmodel', 'application', 'addon/authentication');
$aClassCategories[] = 'application';
}
foreach ($aClassCategories as $sClassCategory) {
foreach (MetaModel::GetClasses($sClassCategory) as $sClassName) {
if ((is_null($iActionCode) || UserRights::IsActionAllowed($sClassName, $iActionCode)) &&
(!MetaModel::IsAbstract($sClassName))) {
$sDisplayName = MetaModel::GetName($sClassName);
$sDisplayName = ($bAdvanced) ? MetaModel::GetName($sClassName)." ($sClassName)" : MetaModel::GetName($sClassName);
$aValidClasses[$sDisplayName] = SelectOptionUIBlockFactory::MakeForSelectOption($sClassName, $sDisplayName, ($sClassName == $sDefaultValue));
}
}
@@ -333,7 +335,7 @@ try {
$oClassesSelect->AddSubBlock($oDefaultSelect);
$aSynchroUpdate = utils::ReadParam('synchro_update', array());
} else {
$oClassesSelect = GetClassesSelectUIBlock('class_name', $sClassName, UR_ACTION_BULK_MODIFY);
$oClassesSelect = GetClassesSelectUIBlock('class_name', $sClassName, UR_ACTION_BULK_MODIFY, (bool)$bAdvanced);
}
$oPanel = TitleUIBlockFactory::MakeForPage(Dict::S('UI:Title:CSVImportStep3'));
$oPage->AddSubBlock($oPanel);
@@ -398,7 +400,7 @@ try {
$oPage->add_ready_script(
<<<EOF
$('#select_class_name').on('change', function(ev) { DoMapping(); } );
$('#advanced').on('click', function(ev) { DoMapping(); } );
$('#advanced').on('click', function(ev) { DoReload(); } );
EOF
);
if ($sClassName != '')
@@ -416,6 +418,13 @@ EOF
var aDefaultKeys = new Array();
var aReadOnlyKeys = new Array();
function DoReload()
{
$('input[name=step]').val(3);
$('#wizForm').removeAttr('onsubmit'); // No need to perform validation checks when going back
$('#wizForm').submit();
}
function CSVGoBack()
{
$('input[name=step]').val(2);

View File

@@ -1174,11 +1174,7 @@ EOF
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
if (!empty($sContext))
{
$sContext = '&'.$sContext;
}
$sContext = $oAppContext->GetForLink(true);
$operation = utils::ReadParam('operation', '');
$oLayout = new PageContentWithSideContent();

View File

@@ -106,6 +106,8 @@ class DBBackup
/** @var string */
protected $sDBName;
/** @var string */
protected $sMySQLBinDir = '';
/** @var string */
protected $sDBSubName;
/**
@@ -133,7 +135,6 @@ class DBBackup
$this->sDBSubName = $oConfig->get('db_subname');
}
protected $sMySQLBinDir = '';
/**
* Create a normalized backup name, depending on the current date/time and Database
@@ -362,8 +363,9 @@ class DBBackup
}
$this->LogInfo("Starting backup of $this->sDBHost/$this->sDBName(suffix:'$this->sDBSubName')");
$sMySQLBinDir = utils::ReadParam('mysql_bindir', $this->sMySQLBinDir, true);
$sMySQLDump = $this->GetMysqldumpCommand();
$sMySQLDump = $this->MakeSafeMySQLCommand($sMySQLBinDir, 'mysqldump');
// Store the results in a temporary file
$sTmpFileName = self::EscapeShellArg($sBackupFileName);
@@ -624,20 +626,22 @@ EOF;
/**
* @return string the command to launch mysqldump (without its params)
* @throws \BackupException
*/
private function GetMysqldumpCommand()
public static function MakeSafeMySQLCommand($sMySQLBinDir, string $sCmd)
{
$sMySQLBinDir = utils::ReadParam('mysql_bindir', $this->sMySQLBinDir, true);
if (empty($sMySQLBinDir))
{
$sMysqldumpCommand = 'mysqldump';
if (empty($sMySQLBinDir)) {
$sMySQLCommand = $sCmd;
}
else
{
$sMysqldumpCommand = '"'.$sMySQLBinDir.'/mysqldump"';
else {
$sMySQLBinDir = escapeshellcmd($sMySQLBinDir);
$sMySQLCommand = '"'.$sMySQLBinDir.'/$sCmd"';
if (!file_exists($sMySQLCommand)) {
throw new BackupException("$sCmd not found in $sMySQLBinDir");
}
}
return $sMysqldumpCommand;
return $sMySQLCommand;
}
}

View File

@@ -39,7 +39,7 @@ class DOMFormatException extends Exception
* @param $previous
* @param DesignElement|null $node DOMNode causing the DOMFormatException
*/
public function __construct($message, $code = null, $previous = null, DesignElement $node = null)
public function __construct($message, $code = 0, $previous = null, DesignElement $node = null)
{
if($node !== null)
{
@@ -1826,7 +1826,7 @@ EOF;
// Search field in parent class
$oField = $this->GetFieldInParentClasses($oClass, $sStateAttCode);
if ($oField == null) {
throw new DOMFormatException("Non existing attribute '$sStateAttCode'", null, null, $oStateAttribute);
throw new DOMFormatException("Non existing attribute '$sStateAttCode'", 0, null, $oStateAttribute);
}
}
$oCodeNodes = $this->oFactory->GetNodes('values/value/code', $oField);

View File

@@ -70,7 +70,7 @@ class MFException extends Exception
*
* @inheritDoc
*/
public function __construct($message = null, $code = null, $iSourceLineNumber = 0, $sXPath = '', $sExtraInfo = '', $previous = null)
public function __construct($message = null, $code = 0, $iSourceLineNumber = 0, $sXPath = '', $sExtraInfo = '', $previous = null)
{
parent::__construct($message, $code, $previous);
$this->iSourceLineNumber = $iSourceLineNumber;
@@ -2040,7 +2040,7 @@ class MFElement extends Combodo\iTop\DesignElement
{
// Houston!
$sXPath = DesignDocument::GetItopNodePath($this);
throw new DOMFormatException("id '$key' already used in $sXPath", null, null, $oItem);
throw new DOMFormatException("id '$key' already used in $sXPath", 0, null, $oItem);
}
$res[$key] = $oItem->GetNodeAsArrayOfItems();
}

View File

@@ -40,15 +40,17 @@ class MissingDependencyException extends CoreException
<ul>
HTML;
foreach ($this->aModulesInfo as $sModuleId => $aModuleErrors) {
$sModuleLabel = $aModuleErrors['module']['label'];
$sModuleLabel = utils::EscapeHtml($aModuleErrors['module']['label']);
$sModuleId = utils::EscapeHtml($sModuleId);
$aModuleMissingDependencies = $aModuleErrors['dependencies'];
$sErrorMessage .= <<<HTML
<li><strong>{$sModuleLabel}</strong> ({$sModuleId}):
<li><strong>$sModuleLabel</strong> ($sModuleId):
<ul>
HTML;
foreach ($aModuleMissingDependencies as $sMissingModule) {
$sErrorMessage .= "<li>{$sMissingModule}</li>";
$sMissingModule = utils::EscapeHtml($sMissingModule);
$sErrorMessage .= "<li>$sMissingModule</li>";
}
$sErrorMessage .= <<<HTML
</ul>

View File

@@ -25,7 +25,7 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ModuleInstallation extends cmdbAbstractObject
class ModuleInstallation extends DBObject
{
public static function Init()
{

View File

@@ -552,14 +552,15 @@ class SetupUtils
if (empty($sMySQLBinDir) && null != MetaModel::GetConfig()) {
$sMySQLBinDir = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', '');
}
if (empty($sMySQLBinDir)) {
$sMySQLDump = 'mysqldump';
}
else {
$aResult[] = new CheckResult(CheckResult::TRACE, 'Info - Found mysql_bindir: '.$sMySQLBinDir);
$sMySQLDump = '"'.$sMySQLBinDir.'/mysqldump"';
try {
$sMySQLDump = DBBackup::MakeSafeMySQLCommand($sMySQLBinDir, 'mysqldump');
} catch (Exception $e) {
$aResult[] = new CheckResult(CheckResult::ERROR, $e->getMessage());
return $aResult;
}
if (!empty($sMySQLBinDir)) {
$aResult[] = new CheckResult(CheckResult::TRACE, 'Info - Found mysql_bindir: '.$sMySQLBinDir);
}
$sCommand = "$sMySQLDump -V 2>&1";
$aOutput = array();

View File

@@ -99,7 +99,7 @@ class DataTable extends UIContentBlock
{
$oAppContext = new ApplicationContext();
if(strpos ($sAjaxUrl,'?')) {
$this->sAjaxUrl = $sAjaxUrl."&".$oAppContext->GetForLink();
$this->sAjaxUrl = $sAjaxUrl.$oAppContext->GetForLink(true);
} else {
$this->sAjaxUrl = $sAjaxUrl."?".$oAppContext->GetForLink();
}

View File

@@ -97,7 +97,7 @@ class BlockLinkSetDisplayAsProperty extends UIContentBlock
$this->oTwigEnv = TwigHelper::GetTwigEnvironment(TwigHelper::ENUM_TEMPLATES_BASE_PATH_BACKOFFICE);
$oAppContext = new ApplicationContext();
$this->sAppContext = $oAppContext->GetForLink();
$this->sAppContext = $oAppContext->GetForLink(true);
$this->sUIPage = cmdbAbstractObject::ComputeStandardUIPage($this->sTargetClass);
}
@@ -160,7 +160,7 @@ class BlockLinkSetDisplayAsProperty extends UIContentBlock
{
return ' href="'
.utils::GetAbsoluteUrlAppRoot()
."pages/$this->sUIPage?operation=details&class=$this->sTargetClass&id=$id&$this->sAppContext"
."pages/$this->sUIPage?operation=details&class=$this->sTargetClass&id=$id$this->sAppContext"
.'" target="_self"';
}
}

View File

@@ -640,7 +640,7 @@ class AjaxRenderController
$aResult = array();
$oAppContext = new ApplicationContext();
$sParams = $oAppContext->GetForLink();
$sParams = $oAppContext->GetForLink(true);
foreach ($aGroupBy as $iRow => $iCount) {
// Build the search for this subset
$oSubsetSearch = $oFilter->DeepClone();
@@ -655,7 +655,7 @@ class AjaxRenderController
$aResult[] = array(
'group' => $aLabels[$iRow],
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1&$sParams&filter=$sFilter\">$iCount</a>",
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1$sParams&filter=$sFilter\">$iCount</a>",
); // TO DO: add the context information
}

View File

@@ -82,7 +82,9 @@ class ObjectController extends AbstractController
{
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'class'));
}
if (!is_subclass_of($sClass, cmdbAbstractObject::class)) {
throw new SecurityException('The class "'.$sClass.'" is not a subclass of cmdbAbstractObject so it can\'t be created by the user');
}
// If the specified class has subclasses, ask the user an instance of which class to create
$aSubClasses = MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL); // Including the specified class itself
$aPossibleClasses = array();

View File

@@ -435,7 +435,7 @@ class EMailLaminas extends Email
// Add body content to as a new part
$oNewPart = new Part($sBody);
$oNewPart->encoding = Mime::ENCODING_8BIT;
$oNewPart->encoding = Mime::ENCODING_BASE64;
$oNewPart->type = $sMimeType;
$oNewPart->charset = 'UTF-8';
$oBody->addPart($oNewPart);
@@ -463,7 +463,7 @@ class EMailLaminas extends Email
}
$this->m_aData['parts'][] = array('text' => $sText, 'mimeType' => $sMimeType);
$oNewPart = new Part($sText);
$oNewPart->encoding = Mime::ENCODING_8BIT;
$oNewPart->encoding = Mime::ENCODING_BASE64;
$oNewPart->type = $sMimeType;
// setBody called only to refresh Content-Type to multipart/mixed

View File

@@ -893,7 +893,7 @@ JS
} else if ($oAttDef->IsExternalKey()) {
/** @var \AttributeExternalKey $oAttDef */
$aAttProperties['value_html'] = $oItem->Get($sAttCode.'_friendlyname');
$aAttProperties['value_html'] = utils::EscapeHtml($oItem->Get($sAttCode.'_friendlyname'));
// Checking if user can access object's external key
$sObjectUrl = ApplicationContext::MakeObjectUrl($oAttDef->GetTargetClass(), $oItem->Get($sAttCode));

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