Compare commits

..

70 Commits

Author SHA1 Message Date
odain
ebab4f8f96 fix merge of N°5324 power portal branch 2023-08-18 10:53:03 +02:00
odain
ac792e7a4c Merge branch 'feature/5324-powerportaluser-repairprofiles' into saas/3.1.0 2023-08-18 10:45:24 +02:00
odain
204a6d8e51 fix merge: broken render.php in case of exception - broken impact analysis tab for ex. 2023-08-10 12:06:34 +02:00
odain
7670c8723a fix merge 3.1 by moving test that remained in /test 2023-08-09 16:13:43 +02:00
odain
4c26a7a682 N°6640 - Broken unattended setup with XML backup configuration 2023-08-08 15:41:12 +02:00
odain
bcb110d99e N°6640 - Broken unattended setup with XML backup configuration 2023-08-08 15:00:14 +02:00
odain
446a13ad72 N°6640 - Broken unattended setup with XML backup configuration 2023-08-08 14:38:25 +02:00
odain
87ecbe9d37 N°6167 - Change welcome pop up and reactivate it on every instance - fix merge missing dependency 2023-08-08 11:54:31 +02:00
odain
5bceee3bdb Fix merge: 5619/5620/6293 dev lost because due to folder sources/Application renaming 2023-08-08 11:36:27 +02:00
odain
1378afbfa2 Merge branch 'saas/3.0' into saas/3.1.0 2023-08-08 11:35:27 +02:00
odain
427fc6f9f9 5324-Guillaume s feedback in PR 2023-06-28 21:31:11 +02:00
odain
07eadb3ea7 N°5324 -rename and move conf parameter to security.single-profile-completion + display warningmessage 2023-06-28 14:41:20 +02:00
odain
97f4818076 5324- Guillaume PR feedbacks 2023-06-26 15:01:57 +02:00
odain
ad46d47e21 N°5324 - repairing or warning profiles conf 2023-06-21 23:44:39 +02:00
odain
cd3f7d7ead N°5324 - disable repairment with backoffice and a customized portal 2023-06-21 21:42:40 +02:00
odain
c6b203fc4e N°5324 - make EVENT_DB_LINKS_CHANGED event work with URP_UserProfile by adding is-link property 2023-06-14 15:14:55 +02:00
odain
b8d04e40e4 N°5324 - repair profiles by default or if configured for customer with multiple profiles + move dedicated test in UserProfilesEventListenerTest.php 2023-06-14 15:13:29 +02:00
odain
d8c7888eac N°5324 - [ERGO] Avoid to have users with non-standalone power portal profile only 2023-06-13 15:39:34 +02:00
denis.flaven@combodo.com
9d6f4569ef Adapt Welcome Popup API for the 3.0.x branch 2023-05-30 08:00:32 +02:00
denis.flaven@combodo.com
1e41e805a2 API for Welcome Popup 2023-05-25 18:04:40 +02:00
odain
bd1e4389f7 N°6293 - [ERGO] Symplify avatar menu - first prototype to sort user menus after setup on page loading 2023-05-22 16:44:57 +02:00
odain
b059fb72a2 Merge branch 'support/3.0' into saas/3.0 2023-05-22 14:08:02 +02:00
odain
1b3b2e8a69 N°6171 - Password Expiration: can expire mode has no effect on user who have never changed their password 2023-05-05 11:44:26 +02:00
jf-cbd
6b448e29f5 PR fix 2023-04-19 10:46:36 +02:00
jf-cbd
7cb6af0a2b fix for 6179 (with description instead of tooltip)
PR fix
2023-04-19 10:46:36 +02:00
jf-cbd
ddc9952ec1 N°6179 - Tooltip attribute in field component (in Twig) 2023-04-13 15:21:11 +02:00
denis.flaven@combodo.com
a1a9ffe192 Merge remote-tracking branch 'origin/feature/6133-add-extra-files-to-backup-and-restore' into saas/3.0 2023-04-06 10:51:29 +02:00
denis.flaven@combodo.com
7728082c00 Merge remote-tracking branch 'origin/feature/6133-add-extra-files-to-backup-and-restore' into saas/3.0 2023-04-04 15:55:09 +02:00
denis.flaven@combodo.com
970183ef45 Merge remote-tracking branch 'origin/feature/6132-disabling-tabs-dynamically' into saas/3.0 2023-04-03 14:02:19 +02:00
denis.flaven@combodo.com
6c2db1e687 Fixed tab activation afeter re-enabling. 2023-04-03 13:45:32 +02:00
denis.flaven@combodo.com
32d74fbc8e Merge branch 'feature/6132-disabling-tabs-dynamically' into saas/3.0 2023-03-30 17:08:02 +02:00
Molkobain
477f2f51e9 Update code to match conventions 2023-03-30 16:50:08 +02:00
denis.flaven@combodo.com
94ea8e60e8 Typo! 2023-03-30 14:29:29 +02:00
denis.flaven@combodo.com
b9a00b15f5 Disable tabs by ID instead of index
Disabled tabs are visible (with a 'not-allowed' cursor)  instead of being hidden from the extra tabs menu.
2023-03-30 14:16:26 +02:00
Denis
e87f5af465 Apply suggestions from code review
JS cleanup after review

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-03-30 13:25:55 +02:00
denis.flaven@combodo.com
97d717b016 Merge from support/3.0.2 2023-03-29 16:56:42 +02:00
denis.flaven@combodo.com
251fd3c67b N°6132 - disable tabs dynamically 2023-03-28 15:07:43 +02:00
odain
7b0a569c64 N°4762 - menu compilation: fix ci (merge issue) 2023-03-07 14:07:17 +01:00
odain
7176bc8686 N°4762 - menu compilation: fix broken menus scenario via AVA6 delta XML
N°4762-enhance test
2023-03-07 09:49:21 +01:00
Denis
9c0b906ded N°5922 - Fix plus button semantic on ext. key widget (#448)
* N°5922 - Enhance plus button on extkeywidget

* Properly reset the target class when closing the dialog

* Make icon buttons as actual clickable links for BeHat

* Apply suggestions from code review

Review by Guillaume. Thanks!

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-03-03 14:12:09 +01:00
odain
0533916dad 4762-menu compilation rework after brainstorming 2023-03-02 08:49:41 +01:00
odain
60b08586c2 N°4762-fix fresh install setup crash 2023-02-24 10:11:17 +01:00
odain
28df2942e4 N°6022 - Make synchro scripts work by http via token authentication with SYNCHRO scopes 2023-02-24 09:11:43 +01:00
odain
cd48d2ad37 N°4762 - Designer customization of menus moved in itop-structure crashs in iTop 3.0 2023-02-24 09:01:10 +01:00
odain
9db2205241 N°5891 - fix and enable tests 2023-02-16 16:59:43 +01:00
odain
045985cd5b 5891-renable badly named tests 2023-02-16 15:56:49 +01:00
odain
809b371520 N°5753 - add config parameter allow_rest_services_via_tokens to bypass rest secure profile option 2022-12-21 15:23:49 +01:00
odain
9bbc7342b8 enhance test framework: let AddProfileToUser work on any User not only UserLocal 2022-12-21 15:22:52 +01:00
odain
973c435138 Revert "N°5753 - exposer l'API Rest dans le SaaS - ugly way of passing API scope to rest.php during login"
This reverts commit 49748a0374.
2022-12-21 14:39:03 +01:00
denis.flaven@combodo.com
1c3dfd6491 Merge branch 5620 - also hide favorites orgs from preferences 2022-12-19 15:57:44 +01:00
denis.flaven@combodo.com
9400b697eb N°5620 Also hide the favorite orgs in preferences 2022-12-19 15:55:52 +01:00
odain
49748a0374 N°5753 - exposer l'API Rest dans le SaaS - ugly way of passing API scope to rest.php during login 2022-12-19 14:22:45 +01:00
odain
163276a6c2 N°5620-merge fix 2022-11-23 09:24:21 +01:00
odain
9b0c2f7324 Merge branch 'feature/5620-hide-org-filter-menu' into saas/3.0 2022-11-22 13:47:33 +01:00
odain
e1807f598f N°5620-fix ci 2022-11-22 13:46:33 +01:00
Molkobain
02e63fff64 Add PHPDoc 2022-11-22 13:28:02 +01:00
odain
0864f05d9f N°5620 - conf param renaming + backward compatibility test 2022-11-22 08:25:43 +01:00
denis.flaven@combodo.com
1bbcd9656a N°5619 - fixed crash when no provider at all! 2022-11-22 08:25:43 +01:00
odain
34d8e52c22 N°5620 - remove debug log 2022-11-22 08:25:27 +01:00
odain
ac7309e48c N°5620 - Hide the organization filter with a conf parameter 2022-11-22 08:25:27 +01:00
odain
c8fade6013 5620-simplify test to avoid regression in other test sections linked to MetaModel use 2022-11-22 07:42:03 +01:00
odain
ad052dd861 N°5620-renaming IsOrgMenuFilterAllowed<-IsSiloSelectionEnabled and made public 2022-11-22 07:31:12 +01:00
odain
a5ea868609 fix test 2022-11-21 10:41:03 +01:00
odain
0b03b3ef4d N°5620 - conf param renaming + backward compatibility test 2022-11-21 09:56:56 +01:00
odain
174cace20a N°5620 - remove debug log 2022-11-15 09:55:12 +01:00
odain
e3f5dbfc80 N°5620 - Hide the organization filter with a conf parameter 2022-11-15 09:49:36 +01:00
denis.flaven@combodo.com
40e24c25a2 N°5619 - hide newsroom menu when no provider 2022-11-15 09:17:27 +01:00
odain
c6e4466c53 ci: fix ItopDataTestCase CreateUser contactid unset 2022-10-26 14:03:11 +02:00
odain
6638eb4adc ci: adapt impersonate test to any friendlyname output 2022-10-26 09:50:56 +02:00
odain
eb40968e34 ci: add CreateContactlessUser method in test framework 2022-10-25 09:26:57 +02:00
429 changed files with 6238 additions and 9966 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

View File

@@ -62,12 +62,6 @@ gitGraph
commit id: "2022-12-28" tag: "2.7.8"
checkout support/3.0
commit id: "2023-04-12" tag: "3.0.3"
checkout develop
commit id: "2023-06-19" tag: "3.1.0-beta" type: REVERSE
commit id: "2023-07-26" tag: "3.1.0-1" type: HIGHLIGHT
branch support/3.1 order: 840
checkout support/3.1
commit id: "2023-08-09" tag: "3.1.0-2"
```
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).

6
.gitignore vendored
View File

@@ -37,9 +37,7 @@ tests/*/vendor/*
# iTop extensions
/extensions/**
!/extensions/.htaccess
!/extensions/readme.txt
!/extensions/web.config
# all logs but listing prevention
/log/**
@@ -47,10 +45,8 @@ tests/*/vendor/*
!/log/index.php
!/log/web.config
# PHPUnit: Cache file, local XML working copies
# PHPUnit cache file
/tests/php-unit-tests/.phpunit.result.cache
/tests/php-unit-tests/phpunit.xml
/tests/php-unit-tests/postbuild_integration.xml
# Jetbrains

View File

@@ -161,4 +161,4 @@ We have one sticker per contribution type. You might get multiple stickers with
Here is the design of each stickers for year 2022:
![iTop stickers 2023](.doc/contributing-guide/2023.contributing-stickers-side-by-side.png)
![iTop stickers 2022](.doc/contributing-guide/2022.contributing-stickers-side-by-side.png)

View File

@@ -228,7 +228,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
"db_table" => "priv_urp_userprofile",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true, /** @since 3.1.0 N°6482 */
"is_link" => true, /** @since 3.1.0 N°6482 N°5324 */
'uniqueness_rules' => array(
'no_duplicate' => array(
'attributes' => array(
@@ -446,12 +446,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
UR_ACTION_BULK_DELETE => 'bd',
);
/**
* @var array $aUsersProfilesList Cache of users' profiles. Hash array of user ID => [profile ID => profile friendlyname, profile ID => profile friendlyname, ...]
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6887
*/
private $aUsersProfilesList = [];
// Installation: create the very first user
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
{
@@ -508,7 +502,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
}
protected $m_aUserOrgs = array(); // userid -> array of orgid
protected $m_aAdministrators = null; // [user id]
// Built on demand, could be optimized if necessary (doing a query for each attribute that needs to be read)
protected $m_aObjectActionGrants = array();
@@ -565,7 +558,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Cache
$this->m_aObjectActionGrants = array();
$this->m_aAdministrators = null;
}
public function LoadCache()
@@ -708,10 +700,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
*/
private function GetAdministrators()
{
if ($this->m_aAdministrators === null)
static $aAdministrators = null;
if ($aAdministrators === null)
{
// Find all administrators
$this->m_aAdministrators = array();
$aAdministrators = array();
$oAdministratorsFilter = new DBObjectSearch('User');
$oLnkFilter = new DBObjectSearch('URP_UserProfile');
$oExpression = new FieldExpression('profileid', 'URP_UserProfile');
@@ -724,10 +718,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oSet->OptimizeColumnLoad(array('User' => array('login')));
while($oUser = $oSet->Fetch())
{
$this->m_aAdministrators[] = $oUser->GetKey();
$aAdministrators[] = $oUser->GetKey();
}
}
return $this->m_aAdministrators;
return $aAdministrators;
}
/**
@@ -764,12 +758,8 @@ class UserRightsProfile extends UserRightsAddOnAPI
$sAction = self::$m_aActionCodes[$iActionCode];
$bStatus = null;
// Cache user's profiles
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
}
// Call the API of UserRights because it caches the list for us
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
{
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
if (!is_null($bGrant))
@@ -895,16 +885,11 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Note: this code is VERY close to the code of IsActionAllowed()
$iUser = $oUser->GetKey();
// Cache user's profiles
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
}
// Note: The object set is ignored because it was interesting to optimize for huge data sets
// and acceptable to consider only the root class of the object set
$bStatus = null;
// Call the API of UserRights because it caches the list for us
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
{
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
if (!is_null($bGrant))
@@ -933,9 +918,8 @@ class UserRightsProfile extends UserRightsAddOnAPI
}
/**
* @param string $sClass
* @return string|null Find out which attribute is corresponding the dimension 'owner org'
* returns null if no such attribute has been found (no filtering should occur)
* Find out which attribute is corresponding the the dimension 'owner org'
* returns null if no such attribute has been found (no filtering should occur)
*/
public static function GetOwnerOrganizationAttCode($sClass)
{

View File

@@ -23,7 +23,7 @@ define('PORTAL_PROFILE_NAME', 'Portal user');
class UserRightsBaseClassGUI extends cmdbAbstractObject
{
// Whenever something changes, reload the privileges
protected function AfterInsert()
{
UserRights::FlushPrivileges();
@@ -43,7 +43,7 @@ class UserRightsBaseClassGUI extends cmdbAbstractObject
class UserRightsBaseClass extends DBObject
{
// Whenever something changes, reload the privileges
protected function AfterInsert()
{
UserRights::FlushPrivileges();
@@ -100,7 +100,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
$this->m_bCheckReservedNames = false;
}
protected static $m_aActions = array(
UR_ACTION_READ => 'Read',
UR_ACTION_MODIFY => 'Modify',
@@ -113,7 +113,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
protected static $m_aCacheActionGrants = null;
protected static $m_aCacheStimulusGrants = null;
protected static $m_aCacheProfiles = null;
public static function DoCreateProfile($sName, $sDescription, $bReservedName = false)
{
if (is_null(self::$m_aCacheProfiles))
@@ -125,7 +125,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
{
self::$m_aCacheProfiles[$oProfile->Get('name')] = $oProfile->GetKey();
}
}
}
$sCacheKey = $sName;
if (isset(self::$m_aCacheProfiles[$sCacheKey]))
@@ -137,17 +137,17 @@ class URP_Profiles extends UserRightsBaseClassGUI
$oNewObj->Set('description', $sDescription);
if ($bReservedName)
{
$oNewObj->DisableCheckOnReservedNames();
$oNewObj->DisableCheckOnReservedNames();
}
$iId = $oNewObj->DBInsertNoReload();
self::$m_aCacheProfiles[$sCacheKey] = $iId;
self::$m_aCacheProfiles[$sCacheKey] = $iId;
return $iId;
}
public static function DoCreateActionGrant($iProfile, $iAction, $sClass, $bPermission = true)
{
$sAction = self::$m_aActions[$iAction];
if (is_null(self::$m_aCacheActionGrants))
{
self::$m_aCacheActionGrants = array();
@@ -157,7 +157,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
{
self::$m_aCacheActionGrants[$oGrant->Get('profileid').'-'.$oGrant->Get('action').'-'.$oGrant->Get('class')] = $oGrant->GetKey();
}
}
}
$sCacheKey = "$iProfile-$sAction-$sClass";
if (isset(self::$m_aCacheActionGrants[$sCacheKey]))
@@ -171,10 +171,10 @@ class URP_Profiles extends UserRightsBaseClassGUI
$oNewObj->Set('class', $sClass);
$oNewObj->Set('action', $sAction);
$iId = $oNewObj->DBInsertNoReload();
self::$m_aCacheActionGrants[$sCacheKey] = $iId;
self::$m_aCacheActionGrants[$sCacheKey] = $iId;
return $iId;
}
public static function DoCreateStimulusGrant($iProfile, $sStimulusCode, $sClass)
{
if (is_null(self::$m_aCacheStimulusGrants))
@@ -186,7 +186,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
{
self::$m_aCacheStimulusGrants[$oGrant->Get('profileid').'-'.$oGrant->Get('stimulus').'-'.$oGrant->Get('class')] = $oGrant->GetKey();
}
}
}
$sCacheKey = "$iProfile-$sStimulusCode-$sClass";
if (isset(self::$m_aCacheStimulusGrants[$sCacheKey]))
@@ -199,13 +199,13 @@ class URP_Profiles extends UserRightsBaseClassGUI
$oNewObj->Set('class', $sClass);
$oNewObj->Set('stimulus', $sStimulusCode);
$iId = $oNewObj->DBInsertNoReload();
self::$m_aCacheStimulusGrants[$sCacheKey] = $iId;
self::$m_aCacheStimulusGrants[$sCacheKey] = $iId;
return $iId;
}
/*
* Create the built-in Administrator profile with its reserved name
*/
*/
public static function DoCreateAdminProfile()
{
self::DoCreateProfile(ADMIN_PROFILE_NAME, 'Has the rights on everything (bypassing any control)', true /* reserved name */);
@@ -213,7 +213,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
/*
* Overload the standard behavior to preserve reserved names
*/
*/
public function DoCheckToWrite()
{
parent::DoCheckToWrite();
@@ -255,7 +255,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
}
}
function DoShowGrantSumary($oPage)
{
if ($this->GetRawName() == "Administrator")
@@ -267,7 +267,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
// Note: for sure, we assume that the instance is derived from UserRightsProfile
$oUserRights = UserRights::GetModuleInstance();
$aDisplayData = array();
foreach (MetaModel::GetClasses('bizmodel') as $sClass)
{
@@ -284,7 +284,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
}
}
$sStimuli = implode(', ', $aStimuli);
$aDisplayData[] = array(
'class' => MetaModel::GetName($sClass),
'read' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Read'),
@@ -296,7 +296,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
'stimuli' => $sStimuli,
);
}
$aDisplayConfig = array();
$aDisplayConfig['class'] = array('label' => Dict::S('UI:UserManagement:Class'), 'description' => Dict::S('UI:UserManagement:Class+'));
$aDisplayConfig['read'] = array('label' => Dict::S('UI:UserManagement:Action:Read'), 'description' => Dict::S('UI:UserManagement:Action:Read+'));
@@ -334,7 +334,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
"db_table" => "priv_urp_userprofile",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true, /** @since 3.1.0 N°6482 */
"is_link" => true, /** @since 3.1.0 N°6482 N°5324 */
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
@@ -580,10 +580,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
/**
* Read and cache organizations allowed to the given user
*
* @param User $oUser
* @param string $sClass (not used here but can be used in overloads)
* @param $oUser
* @param $sClass (not used here but can be used in overloads)
*
* @return array keys of the User allowed org
* @return array
* @throws \CoreException
* @throws \Exception
*/
@@ -611,7 +611,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oSearch->AllowAllData();
$oCondition = new BinaryExpression(new FieldExpression('userid'), '=', new VariableExpression('userid'));
$oSearch->AddConditionExpression($oCondition);
$oUserOrgSet = new DBObjectSet($oSearch, array(), array('userid' => $iUser));
while ($oUserOrg = $oUserOrgSet->Fetch())
{
@@ -633,7 +633,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oSearch->AllowAllData();
$oCondition = new BinaryExpression(new FieldExpression('userid'), '=', new VariableExpression('userid'));
$oSearch->AddConditionExpression($oCondition);
$this->m_aUserProfiles[$iUser] = array();
$oUserProfileSet = new DBObjectSet($oSearch, array(), array('userid' => $iUser));
while ($oUserProfile = $oUserProfileSet->Fetch())
@@ -648,7 +648,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
public function ResetCache()
{
// Loaded by Load cache
$this->m_aProfiles = null;
$this->m_aProfiles = null;
$this->m_aUserProfiles = array();
$this->m_aUserOrgs = array();
@@ -658,7 +658,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Loaded on demand (time consuming as compared to the others)
$this->m_aClassActionGrants = null;
$this->m_aClassStimulusGrants = null;
$this->m_aObjectActionGrants = array();
}
@@ -694,10 +694,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
}
$oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_Profiles"));
$this->m_aProfiles = array();
$this->m_aProfiles = array();
while ($oProfile = $oProfileSet->Fetch())
{
$this->m_aProfiles[$oProfile->GetKey()] = $oProfile;
$this->m_aProfiles[$oProfile->GetKey()] = $oProfile;
}
$this->m_aClassStimulusGrants = array();
@@ -871,7 +871,7 @@ exit;
$this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode] = $aRes;
return $aRes;
}
public function IsActionAllowed($oUser, $sClass, $iActionCode, $oInstanceSet = null)
{
$this->LoadCache();
@@ -1009,8 +1009,8 @@ exit;
/**
* Find out which attribute is corresponding the the dimension 'owner org'
* returns null if no such attribute has been found (no filtering should occur)
*/
* returns null if no such attribute has been found (no filtering should occur)
*/
public static function GetOwnerOrganizationAttCode($sClass)
{
$sAttCode = null;

View File

@@ -22,9 +22,9 @@ define('ADMIN_PROFILE_ID', 1);
class UserRightsBaseClass extends cmdbAbstractObject
{
// Whenever something changes, reload the privileges
// Whenever something changes, reload the privileges
protected function AfterInsert()
{
UserRights::FlushPrivileges();
@@ -78,7 +78,7 @@ class URP_Profiles extends UserRightsBaseClass
function GetGrantAsHtml($oUserRights, $sClass, $sAction)
{
$oGrant = $oUserRights->GetClassActionGrant($this->GetKey(), $sClass, $sAction);
if (is_object($oGrant) && ($oGrant->Get('permission') == 'yes'))
if (is_object($oGrant) && ($oGrant->Get('permission') == 'yes'))
{
return '<span style="background-color: #ddffdd;">'.Dict::S('UI:UserManagement:ActionAllowed:Yes').'</span>';
}
@@ -87,7 +87,7 @@ class URP_Profiles extends UserRightsBaseClass
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
}
}
function DoShowGrantSumary($oPage)
{
if ($this->GetRawName() == "Administrator")
@@ -99,7 +99,7 @@ class URP_Profiles extends UserRightsBaseClass
// Note: for sure, we assume that the instance is derived from UserRightsProjection
$oUserRights = UserRights::GetModuleInstance();
$aDisplayData = array();
foreach (MetaModel::GetClasses('bizmodel') as $sClass)
{
@@ -116,7 +116,7 @@ class URP_Profiles extends UserRightsBaseClass
}
}
$sStimuli = implode(', ', $aStimuli);
$aDisplayData[] = array(
'class' => MetaModel::GetName($sClass),
'read' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Read'),
@@ -128,7 +128,7 @@ class URP_Profiles extends UserRightsBaseClass
'stimuli' => $sStimuli,
);
}
$aDisplayConfig = array();
$aDisplayConfig['class'] = array('label' => Dict::S('UI:UserManagement:Class'), 'description' => Dict::S('UI:UserManagement:Class+'));
$aDisplayConfig['read'] = array('label' => Dict::S('UI:UserManagement:Action:Read'), 'description' => Dict::S('UI:UserManagement:Action:Read+'));
@@ -277,7 +277,7 @@ class URP_UserProfile extends UserRightsBaseClass
"db_table" => "priv_urp_userprofile",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true, /** @since 3.1.0 N°6482 */
"is_link" => true, /** @since 3.1.0 N°6482 N°5324 */
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
@@ -356,7 +356,7 @@ class URP_ProfileProjection extends UserRightsBaseClass
{
$aRes = array($oUser->Get($sColumn));
}
}
elseif (($sExpr == '<any>') || ($sExpr == ''))
{
@@ -427,14 +427,14 @@ class URP_ClassProjection extends UserRightsBaseClass
{
$aRes = array($oObject->Get($sColumn));
}
}
elseif (($sExpr == '<any>') || ($sExpr == ''))
{
$aRes = null;
}
elseif (strtolower(substr($sExpr, 0, 6)) == 'select')
{
{
$sColumn = $this->Get('attribute');
// SELECT...
$oValueSetDef = new ValueSetObjects($sExpr, $sColumn, array(), true /*allow all data*/);
@@ -585,14 +585,14 @@ class UserRightsProjection extends UserRightsAddOnAPI
$oContact->Set('org_id', $iOrgId);
$oContact->Set('email', 'my.email@foo.org');
$iContactId = $oContact->DBInsertNoReload();
$oUser = new UserLocal();
$oUser->Set('login', $sAdminUser);
$oUser->Set('password', $sAdminPwd);
$oUser->Set('contactid', $iContactId);
$oUser->Set('language', $sLanguage); // Language was chosen during the installation
$iUserId = $oUser->DBInsertNoReload();
// Add this user to the very specific 'admin' profile
$oUserProfile = new URP_UserProfile();
$oUserProfile->Set('userid', $iUserId);
@@ -643,24 +643,24 @@ class UserRightsProjection extends UserRightsAddOnAPI
// Could be loaded in a shared memory (?)
$oDimensionSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_Dimensions"));
$this->m_aDimensions = array();
$this->m_aDimensions = array();
while ($oDimension = $oDimensionSet->Fetch())
{
$this->m_aDimensions[$oDimension->GetKey()] = $oDimension;
$this->m_aDimensions[$oDimension->GetKey()] = $oDimension;
}
$oClassProjSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_ClassProjection"));
$this->m_aClassProjs = array();
$this->m_aClassProjs = array();
while ($oClassProj = $oClassProjSet->Fetch())
{
$this->m_aClassProjs[$oClassProj->Get('class')][$oClassProj->Get('dimensionid')] = $oClassProj;
$this->m_aClassProjs[$oClassProj->Get('class')][$oClassProj->Get('dimensionid')] = $oClassProj;
}
$oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_Profiles"));
$this->m_aProfiles = array();
$this->m_aProfiles = array();
while ($oProfile = $oProfileSet->Fetch())
{
$this->m_aProfiles[$oProfile->GetKey()] = $oProfile;
$this->m_aProfiles[$oProfile->GetKey()] = $oProfile;
}
$oUserProfileSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_UserProfile"));
@@ -676,10 +676,10 @@ class UserRightsProjection extends UserRightsAddOnAPI
}
$oProProSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_ProfileProjection"));
$this->m_aProPros = array();
$this->m_aProPros = array();
while ($oProPro = $oProProSet->Fetch())
{
$this->m_aProPros[$oProPro->Get('profileid')][$oProPro->Get('dimensionid')] = $oProPro;
$this->m_aProPros[$oProPro->Get('profileid')][$oProPro->Get('dimensionid')] = $oProPro;
}
/*
@@ -707,7 +707,7 @@ exit;
// Authorize any for this dimension, then no additional criteria is required
continue;
}
// 1 - Get class projection info
//
$oExpression = null;
@@ -731,13 +731,13 @@ exit;
}
elseif (strtolower(substr($sExpr, 0, 6)) == 'select')
{
throw new CoreException('Sorry, projections by the mean of OQL are not supported currently, please specify an attribute instead', array('class' => $sClass, 'expression' => $sExpr));
throw new CoreException('Sorry, projections by the mean of OQL are not supported currently, please specify an attribute instead', array('class' => $sClass, 'expression' => $sExpr));
}
else
{
// Constant value(s)
// unsupported
throw new CoreException('Sorry, constant projections are not supported currently, please specify an attribute instead', array('class' => $sClass, 'expression' => $sExpr));
throw new CoreException('Sorry, constant projections are not supported currently, please specify an attribute instead', array('class' => $sClass, 'expression' => $sExpr));
// $aRes = explode(';', trim($sExpr));
}
@@ -866,7 +866,7 @@ exit;
$this->m_aObjectActionGrants[$oUser->GetKey()][$sClass][$iObjectRef][$iActionCode] = $aRes;
return $aRes;
}
public function IsActionAllowed($oUser, $sClass, $iActionCode, $oInstanceSet = null)
{
if (is_null($oInstanceSet))
@@ -934,7 +934,7 @@ exit;
}
else
{
$iInstancePermission = UR_ALLOWED_NO;
$iInstancePermission = UR_ALLOWED_NO;
}
if (isset($iGlobalPermission))
@@ -1140,7 +1140,7 @@ exit;
}
protected $m_aMatchingProfiles = array(); // cache of the matching profiles for a given user/object
protected function GetMatchingProfiles($oUser, $sClass, /*DBObject*/ $oObject = null)
{
$iUser = $oUser->GetKey();
@@ -1186,7 +1186,7 @@ exit;
@$aProfileRes[$iProfile] += 1;
}
}
$aRes = array();
$iDimCount = count($this->m_aDimensions);
foreach ($aProfileRes as $iProfile => $iMatches)
@@ -1200,7 +1200,7 @@ exit;
// store into the cache
$this->m_aMatchingProfiles[$iUser][$sClass][$iObjectRef] = $aRes;
return $aRes;
return $aRes;
}
public function FlushPrivileges()

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/AjaxPage.php, now loadable using autoloader
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/AjaxPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -335,6 +335,7 @@ abstract class AbstractPreferencesExtension implements iPreferencesExtension
* A recommended pattern is to cache data by the mean of static members.
*
* @api
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method
* @package UIExtensibilityAPI
*/
interface iApplicationUIExtension
@@ -486,6 +487,7 @@ interface iApplicationUIExtension
* @api
* @package UIExtensibilityAPI
* @since 2.7.0
* @deprecated
*/
abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
{
@@ -558,7 +560,6 @@ abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
* or through the GUI.
*
* @api
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method. More details on each method PHPDoc.
* @package ORMExtensibilityAPI
*/
interface iApplicationObjectExtension
@@ -573,7 +574,6 @@ interface iApplicationObjectExtension
* Otherwise, the answer is definitively "yes, the object has changed".
*
* @api
* @deprecated 3.1.0 N°4756 No alternative available, this API was unstable and is abandoned
* @param \cmdbAbstractObject $oObject The target object
*
* @return boolean True if something has changed for the target object
@@ -587,7 +587,6 @@ interface iApplicationObjectExtension
* Anyhow, this API can be called in other contexts such as the CSV import tool.
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_CHECK_TO_WRITE event instead
* @param \cmdbAbstractObject $oObject The target object
*
* @return string[] A list of errors message. An error message is made of one line and it can be displayed to the end-user.
@@ -602,7 +601,6 @@ interface iApplicationObjectExtension
* Please not that it is not possible to cascade deletion by this mean: only stopper issues can be handled.
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_CHECK_TO_DELETE event instead
* @param \cmdbAbstractObject $oObject The target object
*
* @return string[] A list of errors message. An error message is made of one line and it can be displayed to the end-user.
@@ -619,7 +617,6 @@ interface iApplicationObjectExtension
* * {@see DBObject::Get()} : for a given attribute the new value that was persisted
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_WRITE event instead
* @param \cmdbAbstractObject $oObject The target object
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
* once for all the changes made within the current page
@@ -636,7 +633,6 @@ interface iApplicationObjectExtension
* The method is called right <b>after</b> the object has been written to the database.
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_WRITE event instead
* @param \cmdbAbstractObject $oObject The target object
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
* once for all the changes made within the current page
@@ -651,7 +647,6 @@ interface iApplicationObjectExtension
* The method is called right <b>before</b> the object will be deleted from the database.
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_DELETE event instead
* @param \cmdbAbstractObject $oObject The target object
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
* once for all the changes made within the current page
@@ -665,7 +660,6 @@ interface iApplicationObjectExtension
* Extend this class instead of iApplicationObjectExtension if you don't need to overload all methods
*
* @api
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method
* @package ORMExtensibilityAPI
* @since 2.7.0
*/
@@ -2015,8 +2009,6 @@ class RestUtils
*
* @return DBObject The object found
* @throws Exception If the input structure is not valid or it could not find exactly one object
*
* @see DBObject::CheckChangedExtKeysValues() generic method to check that we can access the linked object isn't used in that use case because values can be literal, OQL, friendlyname
*/
public static function FindObjectFromKey($sClass, $key, $bAllowNullValue = false)
{
@@ -2256,25 +2248,43 @@ interface iModuleExtension
}
/**
* KPI logging extensibility point
* Interface to provide messages to be displayed in the "Welcome Popup"
*
* KPI Logger extension
* @api
* @private
* @since 3.1.0
*/
interface iKPILoggerExtension
interface iWelcomePopup
{
/**
* Init the statistics collected
*
* @return void
*/
public function InitStats();
// Importance for ordering messages
// Just two levels since less important messages have nothing to do in the welcome popup
const IMPORTANCE_CRITICAL = 0;
const IMPORTANCE_HIGH = 1;
/**
* @return [['importance' => IMPORTANCE_CRITICAL|IMPORTANCE_HIGH, 'id' => '...', 'title' => '', 'html' => '', 'twig' => '']]
*/
public function GetMessages();
/**
* The message specified by the given Id has been acknowledged by the current user
* @param string $sMessageId
*/
public function AcknowledgeMessage(string $sMessageId): void;
}
/**
* Add a new KPI to the stats
*
* @param \Combodo\iTop\Core\Kpi\KpiLogData $oKpiLogData
*
* @return mixed
*/
public function LogOperation($oKpiLogData);
}
/**
* Inherit from this class to provide messages to be displayed in the "Welcome Popup"
*
* @api
* @since 3.1.0
*/
abstract class AbstractWelcomePopup implements iWelcomePopup
{
public function GetMessages()
{
return [];
}
public function AcknowledgeMessage(string $sMessageId): void
{
return;
}
}

View File

@@ -34,15 +34,15 @@ class AuditCategory extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_auditcategory",
"db_key_field" => "id",
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_auditcategory",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-folder.svg'),
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-folder.svg'),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));

View File

@@ -35,7 +35,7 @@ class AuditDomain extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application,grant_by_profile",
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"complementary_name_attcode" => array('description'),

View File

@@ -35,7 +35,7 @@ class AuditRule extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application,grant_by_profile",
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/CaptureWebPage.php, now loadable using autoloader
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/CaptureWebPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/CLIPage.php, now loadable using autoloader
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/CLIPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -744,13 +744,7 @@ HTML
$oPage->SetCurrentTab($sTabCode, $oAttDef->GetLabel().$sCount, $sTabDescription);
$aArgs = array('this' => $this);
$sEditWhen = $oAttDef->GetEditWhen();
// Calculate if edit_when allows to edit based on current $bEditMode
$bIsEditableBasedOnEditWhen = ($sEditWhen === LINKSET_EDITWHEN_ALWAYS) ||
($bEditMode ? $sEditWhen === LINKSET_EDITWHEN_ON_HOST_EDITION : $sEditWhen === LINKSET_EDITWHEN_ON_HOST_DISPLAY);
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE)) || !$bIsEditableBasedOnEditWhen;
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE));
if ($bEditMode && (!$bReadOnly)) {
$sInputId = $this->m_iFormId.'_'.$sAttCode;
$sDisplayValue = ''; // not used
@@ -760,9 +754,9 @@ HTML
$oPage->add($sHTMLValue);
} else {
if ($oAttDef->IsIndirect()) {
$oBlockLinkSetViewTable = new BlockIndirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef, $bReadOnly);
$oBlockLinkSetViewTable = new BlockIndirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef);
} else {
$oBlockLinkSetViewTable = new BlockDirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef, $bReadOnly);
$oBlockLinkSetViewTable = new BlockDirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef);
}
$oPage->AddUiBlock($oBlockLinkSetViewTable);
}
@@ -1172,7 +1166,7 @@ HTML
/**
* @param \WebPage $oPage
* @param \CMDBObjectSet $oSet
* @param array $aExtraParams See possible values in {@see DataTableUIBlockFactory::RenderDataTable()}
* @param array $aExtraParams
*
* @throws \ApplicationException
* @throws \CoreException
@@ -4544,7 +4538,7 @@ HTML;
return $res;
}
protected function PostInsertActions(): void
public function PostInsertActions(): void
{
parent::PostInsertActions();
@@ -4553,9 +4547,7 @@ HTML;
foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) {
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBInsert()");
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
}
}
@@ -4568,21 +4560,15 @@ HTML;
InlineImage::FinalizeInlineImages($this);
}
/**
* @deprecated 3.1.1 3.2.0 N°6966 We will have only one DBClone method in the future
*/
protected function DBCloneTracked_Internal($newKey = null)
{
/** @var cmdbAbstractObject $oNewObj */
$oNewObj = MetaModel::GetObject(get_class($this), parent::DBCloneTracked_Internal($newKey));
$oNewObj = parent::DBCloneTracked_Internal($newKey);
// Invoke extensions after insertion (the object must exist, have an id, etc.)
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
}
return $oNewObj;
@@ -4610,7 +4596,7 @@ HTML;
return $res;
}
protected function PostUpdateActions(array $aChanges): void
public function PostUpdateActions(array $aChanges): void
{
parent::PostUpdateActions($aChanges);
@@ -4619,9 +4605,7 @@ HTML;
foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) {
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBUpdate()");
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBUpdate');
}
}
@@ -4659,18 +4643,13 @@ HTML;
return $oDeletionPlan;
}
/**
* @deprecated 3.1.1 3.2.0 N°6967 We will have only one DBDelete method in the future
*/
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
{
// Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
}
return parent::DBDeleteTracked_Internal($oDeletionPlan);
@@ -4689,10 +4668,7 @@ HTML;
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$sExtensionClass = get_class($oExtensionInstance);
$oKPI = new ExecutionKPI();
$bIsModified = $oExtensionInstance->OnIsModified($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnIsModified');
if ($bIsModified) {
if ($oExtensionInstance->OnIsModified($this)) {
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified() -> true");
return true;
} else {
@@ -4748,9 +4724,7 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oKPI = new ExecutionKPI();
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToWrite');
if (is_array($aNewIssues) && (count($aNewIssues) > 0)) // Some extensions return null instead of an empty array
{
$this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues);
@@ -4798,9 +4772,7 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oKPI = new ExecutionKPI();
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToDelete');
if (is_array($aNewIssues) && count($aNewIssues) > 0)
{
$this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues);
@@ -5354,7 +5326,7 @@ EOF
$aErrors = $oObj->UpdateObjectFromPostedForm('');
$bResult = (count($aErrors) == 0);
if ($bResult) {
[$bResult, $aErrors] = $oObj->CheckToWrite();
list($bResult, $aErrors) = $oObj->CheckToWrite();
}
if ($bPreview) {
$sStatus = $bResult ? Dict::S('UI:BulkModifyStatusOk') : Dict::S('UI:BulkModifyStatusError');
@@ -5371,11 +5343,6 @@ EOF
'errors' => '<p>'.($bResult ? '' : implode('</p><p>', $aErrorsToDisplay)).'</p>',
);
if ($bResult && (!$bPreview)) {
// doing the check will load multiple times same objects :/
// but it shouldn't cost too much on execution time
// user can mitigate by selecting less extkeys/lnk to set and/or less objects to update 🤷‍♂️
$oObj->CheckChangedExtKeysValues();
$oObj->DBUpdate();
}
}
@@ -5958,56 +5925,42 @@ JS
}
/**
* Possibility for linked classes to be notified of current class modification
*
* If the passed object is an instance of a link class, then will register each remote object for modification using {@see static::RegisterObjectAwaitingEventDbLinksChanged()}
* If an external key was modified, register also the previous object that was linked previously.
*
* @uses static::RegisterObjectAwaitingEventDbLinksChanged()
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \Exception
*
* @throws ArchivedObjectException
* @throws CoreException
* @throws Exception
*
* @since 3.1.0 N°5906 method creation
* @since 3.1.1 3.2.0 N°6228 now just notify attributes having `with_php_computation`
* @since 3.1.0 N°5906
*/
final protected function NotifyAttachedObjectsOnLinkClassModification(): void
{
$sClass = get_class($this);
if (false === MetaModel::IsLinkClass($sClass)) {
return;
}
// previous values in case of link change
$aPreviousValues = $this->ListPreviousValuesForUpdatedAttributes();
$sClass = get_class($this);
$aClassExtKeyAttCodes = MetaModel::GetAttributesList($sClass, [AttributeExternalKey::class]);
foreach ($aClassExtKeyAttCodes as $sExternalKeyAttCode) {
/** @var AttributeExternalKey $oAttDef */
$oAttDef = MetaModel::GetAttributeDef($sClass, $sExternalKeyAttCode);
if (false === $this->DoesTargetObjectHavePhpComputation($oAttDef)) {
continue;
$aLnkClassExternalKeys = MetaModel::GetAttributesList($sClass, [AttributeExternalKey::class]);
foreach ($aLnkClassExternalKeys as $sExternalKeyAttCode) {
/** @var \AttributeExternalKey $oExternalKeyAttDef */
$oExternalKeyAttDef = MetaModel::GetAttributeDef($sClass, $sExternalKeyAttCode);
$sRemoteClassName = $oExternalKeyAttDef->GetTargetClass();
$sRemoteObjectId = $this->Get($sExternalKeyAttCode);
if ($sRemoteObjectId > 0) {
self::RegisterObjectAwaitingEventDbLinksChanged($sRemoteClassName, $sRemoteObjectId);
}
$sTargetObjectId = $this->Get($sExternalKeyAttCode);
if ($sTargetObjectId > 0) {
self::RegisterObjectAwaitingEventDbLinksChanged($oAttDef->GetTargetClass(), $sTargetObjectId);
}
$sPreviousTargetObjectId = $aPreviousValues[$sExternalKeyAttCode] ?? 0;
if ($sPreviousTargetObjectId > 0) {
self::RegisterObjectAwaitingEventDbLinksChanged($oAttDef->GetTargetClass(), $sPreviousTargetObjectId);
$sPreviousRemoteObjectId = $aPreviousValues[$sExternalKeyAttCode] ?? 0;
if ($sPreviousRemoteObjectId > 0) {
self::RegisterObjectAwaitingEventDbLinksChanged($sRemoteClassName, $sPreviousRemoteObjectId);
}
}
}
private function DoesTargetObjectHavePhpComputation(AttributeExternalKey $oAttDef): bool
{
/** @var AttributeLinkedSet $oAttDefMirrorLink */
$oAttDefMirrorLink = $oAttDef->GetMirrorLinkAttribute();
if (is_null($oAttDefMirrorLink) || false === $oAttDefMirrorLink->HasPHPComputation()){
return false;
}
return true;
}
/**
* Register one object for later EVENT_DB_LINKS_CHANGED event.
*

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/CSVPage.php, now loadable using autoloader
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/CSVPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -918,11 +918,6 @@ class RuntimeDashboard extends Dashboard
{
$bCustomized = false;
$sDashboardFileSanitized = utils::RealPath(APPROOT.$sDashboardFile, APPROOT);
if (false === $sDashboardFileSanitized) {
throw new SecurityException('Invalid dashboard file !');
}
// Search for an eventual user defined dashboard
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
@@ -934,7 +929,7 @@ class RuntimeDashboard extends Dashboard
$sDashboardDefinition = $oUserDashboard->Get('contents');
$bCustomized = true;
} else {
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
$sDashboardDefinition = @file_get_contents($sDashboardFile);
}
@@ -942,7 +937,7 @@ class RuntimeDashboard extends Dashboard
$oDashboard = new RuntimeDashboard($sDashBoardId);
$oDashboard->FromXml($sDashboardDefinition);
$oDashboard->SetCustomFlag($bCustomized);
$oDashboard->SetDefinitionFile($sDashboardFileSanitized);
$oDashboard->SetDefinitionFile($sDashboardFile);
} else {
$oDashboard = null;
}
@@ -1141,7 +1136,7 @@ JS
$oToolbar->AddSubBlock($oActionButton);
$aActions = array();
$sFile = addslashes(utils::LocalPath($this->sDefinitionFile));
$sFile = addslashes($this->sDefinitionFile);
$sJSExtraParams = json_encode($aExtraParams);
if ($this->HasCustomDashboard()) {
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:EditCustom'), "return EditDashboard('{$this->sId}', '$sFile', $sJSExtraParams)");
@@ -1264,12 +1259,12 @@ EOF
$sOkButtonLabel = Dict::S('UI:Button:Save');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$sId = utils::HtmlEntities($this->sId);
$sLayoutClass = utils::HtmlEntities($this->sLayoutClass);
$sId = addslashes($this->sId);
$sLayoutClass = addslashes($this->sLayoutClass);
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
$sAutoReloadSec = (string) $this->iAutoReloadSec;
$sTitle = utils::HtmlEntities($this->sTitle);
$sFile = utils::HtmlEntities($this->GetDefinitionFile());
$sTitle = addslashes($this->sTitle);
$sFile = addslashes($this->GetDefinitionFile());
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
$sReloadURL = $this->GetReloadURL();

View File

@@ -40,6 +40,36 @@
<presentation/>
<methods/>
</class>
<class id="WelcomePopupAcknowledge" _delta="define">
<parent>DBObject</parent>
<properties>
<comment>/* Acknowledge welcome popup messages */</comment>
<abstract>false</abstract>
<category></category>
<key_type>autoincrement</key_type>
<db_table>priv_welcome_popup_acknowledge</db_table>
</properties>
<fields>
<field id="message_uuid" xsi:type="AttributeString">
<sql>message_uuid</sql>
<default_value/>
<is_null_allowed>false</is_null_allowed>
</field>
<field id="user_id" xsi:type="AttributeExternalKey">
<sql>user_id</sql>
<target_class>User</target_class>
<is_null_allowed>false</is_null_allowed>
<on_target_delete>DEL_SILENT</on_target_delete>
</field>
<field id="acknowledge_date" xsi:type="AttributeDateTime">
<sql>acknowledge_date</sql>
<default_value/>
<is_null_allowed>false</is_null_allowed>
</field>
</fields>
<presentation/>
<methods/>
</class>
</classes>
<portals>
<portal id="backoffice" _delta="define">

View File

@@ -1246,10 +1246,6 @@ JS
} else {
$oBlock = DashletFactory::MakeForDashletBadge($sClassIconUrl, $sHyperlink, $iCount, $sClassLabel, null, null, $aRefreshParams);
}
$sClassDescription = MetaModel::GetClassDescription($sClass);
if (utils::IsNotNullOrEmptyString($sClassDescription)) {
$oBlock->SetClassDescription($sClassDescription);
}
return $oBlock;
}
@@ -2042,7 +2038,7 @@ class MenuBlock extends DisplayBlock
}
//----------------------------------------------------
// Any style but NOT \DisplayBlock::ENUM_STYLE_LIST_IN_OBJECT (linksets) actions
// Any style but NOT "listInObject" (linksets) actions
//----------------------------------------------------
if ($this->m_sStyle !== static::ENUM_STYLE_LIST_IN_OBJECT) {
switch ($iSetCount) {

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/ErrorPage.php, now loadable using autoloader
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/ErrorPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -1,36 +0,0 @@
<?php
/**
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6458 object creation
*/
class InvalidExternalKeyValueException extends CoreUnexpectedValue
{
private const ENUM_PARAMS_OBJECT = 'current_object';
private const ENUM_PARAMS_ATTCODE = 'attcode';
private const ENUM_PARAMS_ATTVALUE = 'attvalue';
private const ENUM_PARAMS_USER = 'current_user';
public function __construct($oObject, $sAttCode, $aContextData = null, $oPrevious = null)
{
$aContextData[self::ENUM_PARAMS_OBJECT] = get_class($oObject) . '::' . $oObject->GetKey();
$aContextData[self::ENUM_PARAMS_ATTCODE] = $sAttCode;
$aContextData[self::ENUM_PARAMS_ATTVALUE] = $oObject->Get($sAttCode);
$oCurrentUser = UserRights::GetUserObject();
if (false === is_null($oCurrentUser)) {
$aContextData[self::ENUM_PARAMS_USER] = get_class($oCurrentUser) . '::' . $oCurrentUser->GetKey();
}
parent::__construct('Attribute pointing to an object that is either non existing or not readable by the current user', $aContextData, '', $oPrevious);
}
public function GetAttCode(): string
{
return $this->getContextData()[self::ENUM_PARAMS_ATTCODE];
}
public function GetAttValue(): string
{
return $this->getContextData()[self::ENUM_PARAMS_ATTVALUE];
}
}

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/iTopWebPage.php, now loadable using autoloader
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/iTopWebPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/iTopWizardWebPage.php, now loadable using autoloader
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/iTopWizardWebPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -248,7 +248,6 @@ class LoginWebPage extends NiceWebPage
$oEmail = new Email();
$oEmail->SetRecipientTO($sTo);
$sFrom = MetaModel::GetConfig()->Get('forgot_password_from');
$sFrom = utils::IsNullOrEmptyString($sFrom) ? MetaModel::GetConfig()->Get('email_default_sender_address') : $sFrom;
$oEmail->SetRecipientFrom($sFrom);
$oEmail->SetSubject(Dict::S('UI:ResetPwd-EmailSubject', $oUser->Get('login')));
$sResetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=reset_pwd&auth_user='.urlencode($oUser->Get('login')).'&token='.urlencode($sToken);

View File

@@ -9,6 +9,7 @@ use Combodo\iTop\Application\Helper\WebResourcesHelper;
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/application/template.class.inc.php');
require_once(APPROOT."/application/user.dashboard.class.inc.php");
require_once(APPROOT."/setup/parentmenunodecompiler.class.inc.php");
/**
@@ -273,12 +274,23 @@ class ApplicationMenu
continue;
}
$aSubMenuNodes = static::GetSubMenuNodes($sMenuGroupIdx, $aExtraParams);
if (! ParentMenuNodeCompiler::$bUseLegacyMenuCompilation && !($oMenuNode instanceof ShortcutMenuNode)){
if (is_array($aSubMenuNodes) && 0 === sizeof($aSubMenuNodes)){
IssueLog::Error('Empty menu node not displayed', LogChannels::CONSOLE, [
'menu_node_class' => get_class($oMenuNode),
'menu_node_label' => $oMenuNode->GetLabel(),
]);
continue;
}
}
$aMenuGroups[] = [
'sId' => $oMenuNode->GetMenuID(),
'sIconCssClasses' => $oMenuNode->GetDecorationClasses(),
'sInitials' => $oMenuNode->GetInitials(),
'sTitle' => $oMenuNode->GetTitle(),
'aSubMenuNodes' => static::GetSubMenuNodes($sMenuGroupIdx, $aExtraParams),
'aSubMenuNodes' => $aSubMenuNodes,
];
}
@@ -1111,7 +1123,7 @@ class OQLMenuNode extends MenuNode
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
$oTag = new ContextTag(ContextTag::TAG_OBJECT_SEARCH);
ContextTag::AddContext(ContextTag::TAG_OBJECT_SEARCH);
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
OQLMenuNode::RenderOQLSearch
(

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/NiceWebPage.php, now loadable using autoloader
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/NiceWebPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/PDFPage.php, now loadable using autoloader
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/PDFPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -296,7 +296,7 @@ class QueryOQL extends Query
}
catch
(OQLException $e) {
$oAlert = AlertUIBlockFactory::MakeForFailure(Dict::S('UI:RunQuery:Error'), $e->getHtmlDesc())
$oAlert = AlertUIBlockFactory::MakeForFailure(Dict::Format('UI:RunQuery:Error'), $e->getHtmlDesc())
->SetIsClosable(false)
->SetIsCollapsible(false);
$oAlert->AddCSSClass('mb-5');

View File

@@ -99,10 +99,4 @@ else
Session::Set('itop_env', ITOP_DEFAULT_ENV);
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
try {
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
}
catch (MySQLException $e) {
IssueLog::Debug($e->getMessage());
throw new MySQLException('Could not connect to the DB server', []);
}
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);

View File

@@ -1,29 +0,0 @@
<div style="width:100%;background: #fff url(../images/welcome.jpg) top left no-repeat;">
<style>
.welcome_popup_cell {
vertical-align:top;
width:50%;
border:0px solid #000;
-moz-border-radius:10px;
padding:5px;
text-align:left;
}
tr td.welcome_popup_cell, tr td.welcome_popup_cell ul {
font-size:10pt;
}
</style>
<p></p>
<p></p>
<p style="text-align:left; font-size:32px;padding-left:400px;padding-top:40px;margin-bottom:30px;margin-top:0;color:#FFFFFF;"><itopstring>UI:WelcomeMenu:Title</itopstring></p>
<p></p>
<table border="0" style="padding:10px;border-spacing: 10px;width:100%">
<tr>
<td class="welcome_popup_cell">
<itopstring>UI:WelcomeMenu:LeftBlock</itopstring>
</td>
<td class="welcome_popup_cell">
<itopstring>UI:WelcomeMenu:RightBlock</itopstring>
</td>
</tr>
</table>
</div>

View File

@@ -163,7 +163,7 @@ class UIExtKeyWidget
$oPage->add_linked_script('../js/extkeywidget.js');
$oPage->add_linked_script('../js/forms-json-utils.js');
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_MODIFY) && $bAllowTargetCreation);
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
$bExtensions = true;
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
@@ -975,10 +975,6 @@ HTML
// Remove blob edition from creation form @see N°5863 to allow blob edition in modal context
FormHelper::DisableAttributeBlobInputs($this->sTargetClass, $aFormExtraParams);
if(FormHelper::HasMandatoryAttributeBlobInputs($oNewObj)){
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
}
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), $aFormExtraParams);
$oPage->add(<<<HTML
</div>

View File

@@ -143,10 +143,6 @@ JS
// Remove blob edition from creation form @see N°5863 to allow blob edition in modal context
FormHelper::DisableAttributeBlobInputs($sRealClass, $aFormExtraParams);
if(FormHelper::HasMandatoryAttributeBlobInputs($oObj)){
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
}
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), $aFormExtraParams);
}

View File

@@ -178,18 +178,17 @@ class UILinksWidget
$oDisplayBlock = new DisplayBlock($oFilter, 'search', false);
$oBlock->AddSubBlock($oDisplayBlock->GetDisplay($oPage, "SearchFormToAdd_{$sLinkedSetId}",
[
'menu' => false,
array(
'menu' => false,
'result_list_outer_selector' => "SearchResultsToAdd_{$sLinkedSetId}",
'table_id' => "add_{$sLinkedSetId}",
'table_inner_id' => "ResultsToAdd_{$sLinkedSetId}",
'selection_mode' => true,
'json' => $sJson,
'cssCount' => '#count_'.$this->m_sAttCode.$this->m_sNameSuffix,
'query_params' => $oFilter->GetInternalParams(),
'hidden_criteria' => $sAlreadyLinkedExpression,
'submit_on_load' => false,
]));
'table_id' => "add_{$sLinkedSetId}",
'table_inner_id' => "ResultsToAdd_{$sLinkedSetId}",
'selection_mode' => true,
'json' => $sJson,
'cssCount' => '#count_'.$this->m_sAttCode.$this->m_sNameSuffix,
'query_params' => $oFilter->GetInternalParams(),
'hidden_criteria' => $sAlreadyLinkedExpression,
)));
$oBlock->AddForm();
}

View File

@@ -20,7 +20,6 @@
use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use Combodo\iTop\Service\Module\ModuleService;
use ScssPhp\ScssPhp\Compiler;
use ScssPhp\ScssPhp\OutputStyle;
use ScssPhp\ScssPhp\ValueConverter;
@@ -52,31 +51,22 @@ class utils
{
/**
* @var string
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_INTEGER = 'integer';
/**
* Datamodel class
* @var string
* @since 2.7.10 3.0.0
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6606 update PHPDoc
* @uses MetaModel::IsValidClass()
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_CLASS = 'class';
/**
* @var string
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6606
* @uses class_exists()
*/
public const ENUM_SANITIZATION_FILTER_PHP_CLASS = 'php_class';
/**
* @var string
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_STRING = 'string';
/**
* @var string
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_CONTEXT_PARAM = 'context_param';
/**
@@ -91,22 +81,22 @@ class utils
public const ENUM_SANITIZATION_FILTER_OPERATION = 'operation';
/**
* @var string
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_PARAMETER = 'parameter';
/**
* @var string
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_FIELD_NAME = 'field_name';
/**
* @var string
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_TRANSACTION_ID = 'transaction_id';
/**
* @var string For XML / HTML node identifiers
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER = 'element_identifier';
/**
@@ -116,13 +106,12 @@ class utils
public const ENUM_SANITIZATION_FILTER_VARIABLE_NAME = 'variable_name';
/**
* @var string
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_RAW_DATA = 'raw_data';
/**
* @var string
* @since 3.0.2 3.1.0 N°4899
* @since 2.7.10 N°6606
* @since 3.0.2, 3.1.0 N°4899
*/
public const ENUM_SANITIZATION_FILTER_URL = 'url';
@@ -165,8 +154,6 @@ class utils
private static $iNextId = 0;
private static $m_sAppRootUrl = null;
protected static function LoadParamFile($sParamFile)
{
if (!file_exists($sParamFile)) {
@@ -408,10 +395,6 @@ class utils
* @since 2.7.0 new 'element_identifier' filter
* @since 3.0.0 new utils::ENUM_SANITIZATION_* const
* @since 2.7.7, 3.0.2, 3.1.0 N°4899 - new 'url' filter
* @since 2.7.10 N°6606 use the utils::ENUM_SANITIZATION_* const
* @since 2.7.10 N°6606 new case for ENUM_SANITIZATION_FILTER_PHP_CLASS
*
* @link https://www.php.net/manual/en/filter.filters.sanitize.php PHP sanitization filters
*/
protected static function Sanitize_Internal($value, $sSanitizationFilter)
{
@@ -432,13 +415,6 @@ class utils
$retValue = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
break;
case static::ENUM_SANITIZATION_FILTER_PHP_CLASS:
$retValue = $value;
if (!class_exists($value)) {
$retValue = false;
}
break;
case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM:
case static::ENUM_SANITIZATION_FILTER_ROUTE:
case static::ENUM_SANITIZATION_FILTER_OPERATION:
@@ -504,7 +480,6 @@ class utils
// For URL
case static::ENUM_SANITIZATION_FILTER_URL:
// N°6350 - returns only valid URLs
$retValue = filter_var($value, FILTER_VALIDATE_URL);
break;
@@ -1048,7 +1023,7 @@ class utils
*/
public static function GetAbsoluteUrlAppRoot($bForceTrustProxy = false)
{
$sUrl = static::$m_sAppRootUrl;
static $sUrl = null;
if ($sUrl === null || $bForceTrustProxy)
{
$sUrl = self::GetConfig()->Get('app_root_url');
@@ -1069,9 +1044,8 @@ class utils
}
$sUrl = str_replace(SERVER_NAME_PLACEHOLDER, $sServerName, $sUrl);
}
static::$m_sAppRootUrl = $sUrl;
}
return static::$m_sAppRootUrl;
return $sUrl;
}
/**
@@ -1422,23 +1396,13 @@ class utils
return APPROOT . 'env-' . MetaModel::GetEnvironment() . '/';
}
/**
* @return string A path to the folder into which data can be written
* @internal
* @since N°6097 2.7.10 3.0.4 3.1.1
*/
public static function GetDataPath(): string
{
return APPROOT.'data/';
}
/**
* @return string A path to a folder into which any module can store cache data
* The corresponding folder is created or cleaned upon code compilation
*/
public static function GetCachePath()
{
return static::GetDataPath().'cache-'.MetaModel::GetEnvironment().'/';
return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/';
}
/**
@@ -2301,7 +2265,24 @@ SQL;
*/
public static function GetCurrentModuleName($iCallDepth = 0)
{
return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth + 1);
$sCurrentModuleName = '';
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
foreach(GetModulesInfo() as $sModuleName => $aInfo)
{
if ($aInfo['root_dir'] !== '')
{
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
{
$sCurrentModuleName = $sModuleName;
break;
}
}
}
return $sCurrentModuleName;
}
/**
@@ -2323,7 +2304,24 @@ SQL;
*/
public static function GetCurrentModuleDir($iCallDepth)
{
return ModuleService::GetInstance()->GetCurrentModuleDir($iCallDepth);
$sCurrentModuleDir = '';
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
foreach(GetModulesInfo() as $sModuleName => $aInfo)
{
if ($aInfo['root_dir'] !== '')
{
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
{
$sCurrentModuleDir = basename($sRootDir);
break;
}
}
}
return $sCurrentModuleDir;
}
/**
@@ -2338,7 +2336,12 @@ SQL;
*/
public static function GetCurrentModuleUrl()
{
return ModuleService::GetInstance()->GetCurrentModuleUrl(1);
$sDir = static::GetCurrentModuleDir(1);
if ( $sDir !== '')
{
return static::GetAbsoluteUrlModulesRoot().'/'.$sDir;
}
return '';
}
/**
@@ -2348,7 +2351,8 @@ SQL;
*/
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
{
return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
$sModuleName = static::GetCurrentModuleName(1);
return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultvalue);
}
/**
@@ -2357,7 +2361,12 @@ SQL;
*/
public static function GetCompiledModuleVersion($sModuleName)
{
return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
$aModulesInfo = GetModulesInfo();
if (array_key_exists($sModuleName, $aModulesInfo))
{
return $aModulesInfo[$sModuleName]['version'];
}
return null;
}
/**
@@ -2499,17 +2508,15 @@ SQL;
$aHeaders = static::ParseHeaders($http_response_header);
$sMimeType = array_key_exists('Content-Type', $aHeaders) ? strtolower($aHeaders['Content-Type']) : 'application/x-octet-stream';
// Compute the file extension from the MIME Type
foreach ($aKnownExtensions as $sExtValue => $sMime) {
if ($sMime === $sMimeType) {
foreach($aKnownExtensions as $sExtValue => $sMime)
{
if ($sMime === $sMimeType)
{
$sExtension = '.'.$sExtValue;
break;
}
}
}
$sPathName = pathinfo($sPath, PATHINFO_FILENAME);
if (utils::IsNotNullOrEmptyString($sPathName)) {
$sFileName = $sPathName;
}
$sFileName .= $sExtension;
}
$oUploadedDoc = new ormDocument($sData, $sMimeType, $sFileName);
@@ -2684,26 +2691,24 @@ SQL;
}
/**
* Returns the local path relative to the iTop installation (APPROOT or the given base path)
* Returns the local path relative to the iTop installation of an existing file
* Dir separator is changed to '/' for consistency among the different OS
*
* @param string $sAbsolutePath absolute path
* @param string $sBasePath Base path for the resulting local path (default APPROOT)
*
* @return false|string The generated local path or false if absolute path is not under the base path
* @since 3.1.1 Added base path defaulted to previous version APPROOT
* @return false|string
*/
final public static function LocalPath($sAbsolutePath, string $sBasePath = APPROOT)
final public static function LocalPath($sAbsolutePath)
{
$sRootPath = realpath($sBasePath);
$sRootPath = realpath(APPROOT);
$sFullPath = realpath($sAbsolutePath);
if (($sFullPath === false) || !self::StartsWith($sFullPath, $sRootPath))
{
return false;
}
$sLocalPath = substr($sFullPath, strlen($sRootPath.DIRECTORY_SEPARATOR));
return str_replace(DIRECTORY_SEPARATOR, '/', $sLocalPath);
$sLocalPath = str_replace(DIRECTORY_SEPARATOR, '/', $sLocalPath);
return $sLocalPath;
}
/**
@@ -2895,7 +2900,7 @@ HTML;
// Add already loaded classes
$aCurrentClasses = array_fill_keys(get_declared_classes(), '');
$aClassMap = array_merge($aCurrentClasses, $aClassMap);
$aClassMap = array_merge($aClassMap, $aCurrentClasses);
foreach ($aClassMap as $sPHPClass => $sPHPFile) {
$bSkipped = false;
@@ -2920,12 +2925,11 @@ HTML;
$bSkipped = true; // file not found
}
}
if(!$bSkipped){
try {
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->implementsInterface($sInterface) &&
!$oRefClass->isInterface() && !$oRefClass->isAbstract() && !$oRefClass->isTrait()) {
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable()) {
$aMatchingClasses[] = $sPHPClass;
}
} catch (Exception $e) {
@@ -3031,7 +3035,6 @@ HTML;
*
* @return bool if string null or empty
* @since 3.0.2 N°5302
* @since 2.7.10 N°6458 add method in the 2.7 branch
*/
public static function IsNullOrEmptyString(?string $sString): bool
{
@@ -3047,7 +3050,6 @@ HTML;
*
* @return bool if string is not null and not empty
* @since 3.0.2 N°5302
* @since 2.7.10 N°6458 add method in the 2.7 branch
*/
public static function IsNotNullOrEmptyString(?string $sString): bool
{

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/WebPage.php, now loadable using autoloader
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/WebPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -351,7 +351,6 @@ class WizardHelper
/**
* @return string JS code to be executed for fields update
* @since 3.0.0 N°3198
* @deprecated 3.0.3-2 3.0.4 3.1.1 3.2.0 Use {@see \WizardHelper::AddJsForUpdateFields()} instead
*/
public function GetJsForUpdateFields()
{
@@ -364,32 +363,6 @@ class WizardHelper
JS;
}
/**
* Add necessary JS snippets (to the page) to be executed for fields update
*
* @param \WebPage $oPage
* @return void
* @since 3.0.3-2 3.0.4 3.1.1 3.2.0 N°6766
*/
public function AddJsForUpdateFields(WebPage $oPage)
{
$sWizardHelperJsVar = (!is_null($this->m_aData['m_sWizHelperJsVarName'])) ? utils::Sanitize($this->m_aData['m_sWizHelperJsVarName'], '', utils::ENUM_SANITIZATION_FILTER_PARAMETER) : 'oWizardHelper'.$this->GetFormPrefix();
$sWizardHelperJson = $this->ToJSON();
$oPage->add_script(<<<JS
{$sWizardHelperJsVar}.m_oData = {$sWizardHelperJson};
{$sWizardHelperJsVar}.UpdateFields();
JS
);
$oPage->add_ready_script(<<<JS
if ({$sWizardHelperJsVar}.m_oDependenciesUpdatedPromiseResolve !== null){
{$sWizardHelperJsVar}.m_oDependenciesUpdatedPromiseResolve();
}
JS
);
}
/*
* Function with an old pattern of code
* @deprecated 3.1.0
@@ -398,9 +371,11 @@ JS
{
$aSet = json_decode($sJsonSet, true); // true means hash array instead of object
$oSet = CMDBObjectSet::FromScratch($sLinkClass);
foreach ($aSet as $aLinkObj) {
foreach ($aSet as $aLinkObj)
{
$oLink = MetaModel::NewObject($sLinkClass);
foreach ($aLinkObj as $sAttCode => $value) {
foreach ($aLinkObj as $sAttCode => $value)
{
$oAttDef = MetaModel::GetAttributeDef($sLinkClass, $sAttCode);
if (($oAttDef->IsExternalKey()) && ($value != '') && ($value > 0))
{

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/XMLPage.php, now loadable using autoloader
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/XMLPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -23,13 +23,11 @@ define('ITOP_DESIGN_LATEST_VERSION', '3.1');
* @used-by utils::GetItopVersionWikiSyntax()
* @used-by iTopModulesPhpVersionIntegrationTest
*/
define('ITOP_CORE_VERSION', '3.1.1');
define('ITOP_CORE_VERSION', '3.1.0');
/**
* @var string
* @since 3.0.4 3.1.0 3.2.0 N°6274 Allow to test if PHPUnit is currently running. Starting with PHPUnit 9.5 we'll be able to replace it with $GLOBALS['phpunit_version']
* @since 3.0.4 3.1.1 3.2.0 N°6976 Fix constant name (DeprecatedCallsLog error handler was never set)
* @since 3.0.4 N°6274 Allow to test if PHPUnit is currently running. Starting with PHPUnit 9.5 we'll be able to replace it with $GLOBALS['phpunit_version']
*/
const ITOP_PHPUNIT_RUNNING_CONSTANT_NAME = 'ITOP_PHPUNIT_RUNNING';
define('ITOP_PHPUNIT_RUNNING_CONSTANT_NAME', 'ITOP_PHPUNIT_RUNNING');
require_once APPROOT.'bootstrap.inc.php';

View File

@@ -45,7 +45,6 @@ define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
define('READONLY_MODE_FILE', APPROOT.'data/.readonly');
$fItopStarted = microtime(true);
$iItopInitialMemory = memory_get_usage(true);
if (!isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false) {
require_once APPROOT.'/lib/autoload.php';

22
composer.lock generated
View File

@@ -4476,16 +4476,16 @@
},
{
"name": "symfony/twig-bridge",
"version": "v5.4.31",
"version": "v5.4.11",
"source": {
"type": "git",
"url": "https://github.com/symfony/twig-bridge.git",
"reference": "fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942"
"reference": "63b8a50d48c9fe3d04e77307d4f1771dd848baa8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942",
"reference": "fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942",
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/63b8a50d48c9fe3d04e77307d4f1771dd848baa8",
"reference": "63b8a50d48c9fe3d04e77307d4f1771dd848baa8",
"shasum": ""
},
"require": {
@@ -4498,22 +4498,22 @@
"phpdocumentor/reflection-docblock": "<3.2.2",
"phpdocumentor/type-resolver": "<1.4.0",
"symfony/console": "<5.3",
"symfony/form": "<5.4.21|>=6,<6.2.7",
"symfony/form": "<5.3",
"symfony/http-foundation": "<5.3",
"symfony/http-kernel": "<4.4",
"symfony/translation": "<5.2",
"symfony/workflow": "<5.2"
},
"require-dev": {
"doctrine/annotations": "^1.12|^2",
"egulias/email-validator": "^2.1.10|^3|^4",
"doctrine/annotations": "^1.12",
"egulias/email-validator": "^2.1.10|^3",
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"symfony/asset": "^4.4|^5.0|^6.0",
"symfony/console": "^5.3|^6.0",
"symfony/dependency-injection": "^4.4|^5.0|^6.0",
"symfony/expression-language": "^4.4|^5.0|^6.0",
"symfony/finder": "^4.4|^5.0|^6.0",
"symfony/form": "^5.4.21|^6.2.7",
"symfony/form": "^5.3|^6.0",
"symfony/http-foundation": "^5.3|^6.0",
"symfony/http-kernel": "^4.4|^5.0|^6.0",
"symfony/intl": "^4.4|^5.0|^6.0",
@@ -4577,7 +4577,7 @@
"description": "Provides integration for Twig with various Symfony components",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/twig-bridge/tree/v5.4.31"
"source": "https://github.com/symfony/twig-bridge/tree/v5.4.11"
},
"funding": [
{
@@ -4593,7 +4593,7 @@
"type": "tidelift"
}
],
"time": "2023-11-09T21:19:08+00:00"
"time": "2022-07-20T13:00:38+00:00"
},
{
"name": "symfony/twig-bundle",
@@ -5276,5 +5276,5 @@
"platform-overrides": {
"php": "7.4.0"
},
"plugin-api-version": "2.6.0"
"plugin-api-version": "2.3.0"
}

View File

@@ -59,17 +59,9 @@ class DbConnectionWrapper
* Use this to register a mock that will handle {@see mysqli::query()}
*
* @param \mysqli|null $oMysqli
* @since 3.0.4 3.1.1 3.2.0 Param $oMysqli becomes nullable
* @since 3.1.0-4 N°6848 backport of restoring cnx on null parameter value
*/
public static function SetDbConnectionMockForQuery(?mysqli $oMysqli = null): void
public static function SetDbConnectionMockForQuery(?mysqli $oMysqli): void
{
if (is_null($oMysqli)) {
// Reset to standard connection
static::$oDbCnxMockableForQuery = static::$oDbCnxStandard;
}
else {
static::$oDbCnxMockableForQuery = $oMysqli;
}
static::$oDbCnxMockableForQuery = $oMysqli;
}
}

View File

@@ -419,7 +419,6 @@ class MyHelpers
//}
return $sOutput;
}
}
/**
@@ -524,3 +523,5 @@ class Str
return (strtolower($sString) == $sString);
}
}
?>

View File

@@ -650,9 +650,6 @@ class ActionEmail extends ActionNotification
$aMessageContent['subject'] = 'TEST['.$aMessageContent['subject'].']';
$aMessageContent['body'] = $sTestBody;
$aMessageContent['to'] = $this->Get('test_recipient');
// N°6677 Ensure emails in test are never sent to cc'd and bcc'd addresses
$aMessageContent['cc'] = '';
$aMessageContent['bcc'] = '';
}
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
$aMessageContent['in_reply_to'] = $aMessageContent['references'];

View File

@@ -91,12 +91,6 @@ define('LINKSET_EDITMODE_ACTIONS', 2); // Show the usual 'Actions' popup menu
define('LINKSET_EDITMODE_INPLACE', 3); // The "linked" objects can be created/modified/deleted in place
define('LINKSET_EDITMODE_ADDREMOVE', 4); // The "linked" objects can be added/removed in place
define('LINKSET_EDITWHEN_NEVER', 0); // The linkset cannot be edited at all from inside this object
define('LINKSET_EDITWHEN_ON_HOST_EDITION', 1); // The only possible action is to open a new window to create a new object
define('LINKSET_EDITWHEN_ON_HOST_DISPLAY', 2); // Show the usual 'Actions' popup menu
define('LINKSET_EDITWHEN_ALWAYS', 3); // Show the usual 'Actions' popup menu
define('LINKSET_DISPLAY_STYLE_PROPERTY', 'property');
define('LINKSET_DISPLAY_STYLE_TAB', 'tab');
@@ -797,7 +791,7 @@ abstract class AttributeDefinition
public function HasAValue($proposedValue): bool
{
// Default implementation, we don't really know what type $proposedValue will be
return !(is_null($proposedValue));
return is_null($proposedValue);
}
/**
@@ -1709,15 +1703,6 @@ class AttributeLinkedSet extends AttributeDefinition
public function GetEditMode()
{
return $this->GetOptional('edit_mode', LINKSET_EDITMODE_ACTIONS);
}
/**
* @return int see LINKSET_EDITWHEN_* constants
* @since 3.1.1 3.2.0 N°6385
*/
public function GetEditWhen(): int
{
return $this->GetOptional('edit_when', LINKSET_EDITWHEN_ALWAYS);
}
/**
@@ -1739,20 +1724,11 @@ class AttributeLinkedSet extends AttributeDefinition
* @return bool true if Attribute has constraints
* @since 3.1.0 N°6228
*/
public function HasPHPConstraint(): bool
public function GetHasConstraint()
{
return $this->GetOptional('with_php_constraint', false);
}
/**
* @return bool true if Attribute has computation (DB_LINKS_CHANGED event propagation, `with_php_computation` attribute xml property), false otherwise
* @since 3.1.1 3.2.0 N°6228
*/
public function HasPHPComputation(): bool
{
return $this->GetOptional('with_php_computation', false);
}
public function GetLinkedClass()
{
return $this->Get('linked_class');
@@ -8608,7 +8584,7 @@ class AttributeBlob extends AttributeDefinition
public function RecordAttChange(DBObject $oObject, $original, $value): void
{
// N°6502 Don't record history if only the download count has changed
if ((null !== $original) && (null !== $value) && $original->EqualsExceptDownloadsCount($value)) {
if ($original->EqualsExceptDownloadsCount($value)) {
return;
}

View File

@@ -149,9 +149,7 @@ abstract class BulkExport
$this->oSearch = null;
$this->iChunkSize = 0;
$this->sFormatCode = null;
$this->aStatusInfo = [
'show_obsolete_data' => utils::ShowObsoleteData(),
];
$this->aStatusInfo = array();
$this->oBulkExportResult = null;
$this->sTmpFile = '';
$this->bLocalizeOutput = false;
@@ -205,17 +203,15 @@ abstract class BulkExport
if ($oInfo && ($oInfo->Get('user_id') == UserRights::GetUserId()))
{
$sFormatCode = $oInfo->Get('format');
$aStatusInfo = json_decode($oInfo->Get('status_info'),true);
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
$oSearch->SetShowObsoleteData($aStatusInfo['show_obsolete_data']);
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
if ($oBulkExporter)
{
$oBulkExporter->SetFormat($sFormatCode);
$oBulkExporter->SetObjectList($oSearch);
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
$oBulkExporter->SetStatusInfo($aStatusInfo);
$oBulkExporter->SetStatusInfo(json_decode($oInfo->Get('status_info'), true));
$oBulkExporter->SetLocalizeOutput($oInfo->Get('localize_output'));
@@ -293,7 +289,6 @@ abstract class BulkExport
*/
public function SetObjectList(DBSearch $oSearch)
{
$oSearch->SetShowObsoleteData($this->aStatusInfo['show_obsolete_data']);
$this->oSearch = $oSearch;
}

View File

@@ -438,8 +438,7 @@ abstract class CMDBObject extends DBObject
}
/**
* @deprecated 3.1.0 N°5232 N°6966 simply use {@see DBObject::DBClone()} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before.
* @deprecated 3.1.0 N°5232 not used
*/
public function DBCloneTracked(CMDBChange $oChange, $newKey = null)
{
@@ -447,9 +446,6 @@ abstract class CMDBObject extends DBObject
$this->DBCloneTracked_Internal($newKey);
}
/**
* @deprecated 3.1.1 3.2.0 N°6966 We will have only one DBClone method in the future
*/
protected function DBCloneTracked_Internal($newKey = null)
{
$newKey = parent::DBClone($newKey);
@@ -474,13 +470,23 @@ abstract class CMDBObject extends DBObject
public function DBDelete(&$oDeletionPlan = null)
{
$this->LogCRUDEnter(__METHOD__);
$oDeletionPlan = parent::DBDelete($oDeletionPlan);
$oDeletionPlan = $this->DBDeleteTracked_Internal($oDeletionPlan);
$this->LogCRUDExit(__METHOD__);
return $oDeletionPlan;
}
/**
* @deprecated 3.1.1 3.2.0 N°6967 We will have only one DBDelete method in the future
* @param null $oDeletionPlan
*
* @return \DeletionPlan|null
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DeleteException
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
{

View File

@@ -3,7 +3,7 @@
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -380,7 +380,7 @@ class CMDBSource
public static function GetDBVendor()
{
$sDBVendor = static::ENUM_DB_VENDOR_MYSQL;
$sVersionComment = static::GetServerVariable('version') . ' - ' . static::GetServerVariable('version_comment');
if(preg_match('/mariadb/i', $sVersionComment) === 1)
{
@@ -390,7 +390,7 @@ class CMDBSource
{
$sDBVendor = static::ENUM_DB_VENDOR_PERCONA;
}
return $sDBVendor;
}
@@ -431,7 +431,6 @@ class CMDBSource
{
self::$m_sDBName = '';
}
self::_TablesInfoCacheReset(); // reset the table info cache!
}
public static function CreateTable($sQuery)
@@ -608,9 +607,8 @@ class CMDBSource
{
self::LogDeadLock($e, true);
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
} finally {
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
}
}
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
if ($oResult === false) {
$aContext = array('query' => $sSql);
@@ -628,24 +626,18 @@ class CMDBSource
}
/**
* @param Exception $e
* @param \Exception $e
* @param bool $bForQuery to get the proper DB connection
* @param bool $bCheckMysqliErrno if false won't try to check for mysqli::errno value
*
* @since 2.7.1
* @since 3.0.0 N°4325 add new optional parameter to use the correct DB connection
* @since 3.0.4 3.1.1 3.2.0 N°6643 new bCheckMysqliErrno parameter as a workaround for mysqli::errno cannot be mocked
*/
private static function LogDeadLock(Exception $e, $bForQuery = false, $bCheckMysqliErrno = true)
private static function LogDeadLock(Exception $e, $bForQuery = false)
{
// checks MySQL error code
if ($bCheckMysqliErrno) {
$iMySqlErrorNo = DbConnectionWrapper::GetDbConnection($bForQuery)->errno;
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK))) {
return;
}
} else {
$iMySqlErrorNo = "N/A";
$iMySqlErrorNo = DbConnectionWrapper::GetDbConnection($bForQuery)->errno;
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK))) {
return;
}
// Get error info
@@ -672,10 +664,7 @@ class CMDBSource
);
DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext);
IssueLog::Error($sMessage, LogChannels::DEADLOCK, [
'exception.class' => get_class($e),
'exception.message' => $e->getMessage(),
]);
IssueLog::Error($sMessage, LogChannels::DEADLOCK, $e->getMessage());
}
/**
@@ -934,7 +923,7 @@ class CMDBSource
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
}
while ($aRow = $oResult->fetch_array($iMode))
{
$aData[] = $aRow;
@@ -1088,7 +1077,7 @@ class CMDBSource
if (!array_key_exists($iKey, $aTableInfo["Fields"])) return false;
$aFieldData = $aTableInfo["Fields"][$iKey];
if (!array_key_exists("Key", $aFieldData)) return false;
return ($aFieldData["Key"] == "PRI");
return ($aFieldData["Key"] == "PRI");
}
public static function IsAutoIncrement($sTable, $sField)
@@ -1099,7 +1088,7 @@ class CMDBSource
$aFieldData = $aTableInfo["Fields"][$sField];
if (!array_key_exists("Extra", $aFieldData)) return false;
//MyHelpers::debug_breakpoint($aFieldData);
return (strstr($aFieldData["Extra"], "auto_increment"));
return (strstr($aFieldData["Extra"], "auto_increment"));
}
public static function IsField($sTable, $sField)
@@ -1366,13 +1355,13 @@ class CMDBSource
public static function GetTableFieldsList($sTable)
{
assert(!empty($sTable));
$aTableInfo = self::GetTableInfo($sTable);
if (empty($aTableInfo)) return array(); // #@# or an error ?
return array_keys($aTableInfo["Fields"]);
}
// Cache the information about existing tables, and their fields
private static $m_aTablesInfo = array();
private static function _TablesInfoCacheReset($sTableName = null)
@@ -1505,7 +1494,7 @@ class CMDBSource
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
}
$aRows = array();
while ($aRow = $oResult->fetch_array(MYSQLI_ASSOC))
{
@@ -1514,7 +1503,7 @@ class CMDBSource
$oResult->free();
return $aRows;
}
/**
* Returns the value of the specified server variable
* @param string $sVarName Name of the server variable
@@ -1530,7 +1519,7 @@ class CMDBSource
/**
* Returns the privileges of the current user
* @return string privileges in a raw format
*/
*/
public static function GetRawPrivileges()
{
try
@@ -1556,8 +1545,8 @@ class CMDBSource
/**
* Determine the slave status of the server
* @return bool true if the server is slave
*/
* @return bool true if the server is slave
*/
public static function IsSlaveServer()
{
try

View File

@@ -29,7 +29,7 @@ define('ITOP_APPLICATION_SHORT', 'iTop');
*
* @see ITOP_CORE_VERSION to get iTop core version
*/
define('ITOP_VERSION', '3.1.1-dev');
define('ITOP_VERSION', '3.1.0-dev');
define('ITOP_VERSION_NAME', 'Fullmoon');
define('ITOP_REVISION', 'svn');
@@ -656,22 +656,22 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'email_transport_smtp.allow_self_signed' => [
'email_transport_smtp.allow_self_signed' => array(
'type' => 'bool',
'description' => 'Allow self signed peer certificates',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'email_transport_smtp.verify_peer' => [
),
'email_transport_smtp.verify_peer' => array(
'type' => 'bool',
'description' => 'Verify peer certificate',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
),
'email_css' => [
'type' => 'string',
'description' => 'CSS that will override the standard stylesheet used for the notifications',
@@ -1069,14 +1069,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'log_kpi_generate_legacy_report' => [
'type' => 'bool',
'description' => 'Generate the legacy KPI report (kpi.html)',
'default' => true,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'max_linkset_output' => [
'type' => 'integer',
'description' => 'Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.',
@@ -1193,30 +1185,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'sessions_tracking.enabled' => [
'type' => 'bool',
'description' => 'Whether or not the whole mechanism to track active sessions is enabled. See PHP session.gc_maxlifetime setting to configure session expiration.',
'default' => false,
'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)',
'default' => 1,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'sessions_tracking.gc_duration_in_seconds' => [
'type' => 'integer',
'description' => 'fallback in case cron is not active: when a cleanup is triggered cleanup duration will not exceed this duration (in seconds).',
'default' => 1,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'transaction_storage' => [
'type' => 'string',
'description' => 'The type of mechanism to use for storing the unique identifiers for transactions (Session|File).',
@@ -1377,6 +1345,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'navigation_menu.sorted_popup_user_menu_items' => [
'type' => 'array',
'description' => 'Sort user menu items after setup on page load',
'default' => [],
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'quick_create.enabled' => [
'type' => 'bool',
'description' => 'Whether or not the quick create is enabled',
@@ -1659,6 +1635,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'security.single_profile_completion' => [
'type' => 'array',
'description' => 'Non standalone profiles can be completed by other profiles via this configuration. default configuration is equivalent to [\'Portal power user\' => \'Portal user\'] configuration. unless you have specific portal customization.',
'default' => null,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'behind_reverse_proxy' => [
'type' => 'bool',
'description' => 'If true, then proxies custom header (X-Forwarded-*) are taken into account. Use only if the webserver is not publicly accessible (reachable only by the reverse proxy)',

View File

@@ -1,11 +1,8 @@
<?php
/**
* This file is only here for compatibility reasons.
* It will be removed in future iTop versions (N°6533)
* This file is only here for compatibility issues. Will be removed in iTop 3.1.0 (N°3664)
*
* @deprecated 3.0.0 N°3663 Exception classes were moved to `/application/exceptions`, use autoloader instead of require !
*/
require_once __DIR__ . '/../approot.inc.php';
DeprecatedCallsLog::NotifyDeprecatedFile('Classes were moved to /application/exceptions and can be used directly with the autoloader');
require_once '../approot.inc.php';
DeprecatedCallsLog::NotifyDeprecatedFile('Classes were moved to /application/exceptions');

View File

@@ -188,8 +188,8 @@ final class ItopCounter
if (!$hDBLink)
{
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
}
throw new Exception("Could not connect to the DB server (host=$sDBHost, user=$sDBUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
}
return $hDBLink;
}

View File

@@ -12,7 +12,6 @@ use Combodo\iTop\Application\UI\Base\Component\Input\SelectUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\ColumnUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactory;
use Combodo\iTop\Application\Helper\ExportHelper;
/**
* Bulk export: CSV export
@@ -115,7 +114,6 @@ class CSVBulkExport extends TabularBulkExport
case 'csv_options':
$oPanel = PanelUIBlockFactory::MakeNeutral(Dict::S('Core:BulkExport:CSVOptions'));
$oPanel->AddSubBlock(ExportHelper::GetAlertForExcelMaliciousInjection());
$oMulticolumn = MultiColumnUIBlockFactory::MakeStandard();
$oPanel->AddSubBlock($oMulticolumn);

View File

@@ -219,19 +219,6 @@
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
</fields>
</class>
<class id="AuditDomain" _delta="define">
<parent>cmdbAbstractObject</parent>
<properties>
<category>application, grant_by_profile</category>
</properties>
<fields>
<field id="name" xsi:type="AttributeString"/>
<field id="description" xsi:type="AttributeString"/>
<field id="icon" xsi:type="AttributeImage"/>
<field id="categories_list" xsi:type="AttributeLinkedSet"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
</fields>
</class>
<class id="Query" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent>
@@ -502,12 +489,6 @@
<type>boolean</type>
<default>false</default>
</property>
<property id="with_php_computation">
<php_param>with_php_computation</php_param>
<mandatory>false</mandatory>
<type>boolean</type>
<default>false</default>
</property>
<property id="create_temporary_object">
<php_param>create_temporary_object</php_param>
<mandatory>false</mandatory>

File diff suppressed because it is too large Load Diff

View File

@@ -767,10 +767,7 @@ class DBObjectSet implements iDBObjectSetIterator
try
{
$oKPI = new ExecutionKPI();
$this->m_oSQLResult = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, $this->GetRealSortOrder(), $this->m_iLimitCount, $this->m_iLimitStart, false);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
} catch (MySQLException $e)
{
// 1116 = ER_TOO_MANY_TABLES
@@ -850,11 +847,8 @@ class DBObjectSet implements iDBObjectSetIterator
{
if (is_null($this->m_iNumTotalDBRows))
{
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
$resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), 0, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if (!$resQuery) return 0;
$aRow = CMDBSource::FetchArray($resQuery);
@@ -865,42 +859,6 @@ class DBObjectSet implements iDBObjectSetIterator
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ??
}
/**
* @param \DBSearch $oFilter
* @param array $aOrder
* @param int $iLimitCount
* @param int $iLimitStart
* @param bool $bCount
*
* @return string
*/
private function GetPseudoOQL($oFilter, $aOrder, $iLimitCount, $iLimitStart, $bCount)
{
$sOQL = '';
if ($bCount) {
$sOQL .= 'COUNT ';
}
$sOQL .= $oFilter->ToOQL();
if ($iLimitCount > 0) {
$sOQL .= ' LIMIT ';
if ($iLimitStart > 0) {
$sOQL .= "$iLimitStart, ";
}
$sOQL .= "$iLimitCount";
}
if (count($aOrder) > 0) {
$sOQL .= ' ORDER BY ';
$aOrderBy = [];
foreach ($aOrder as $sAttCode => $bAsc) {
$aOrderBy[] = $sAttCode.' '.($bAsc ? 'ASC' : 'DESC');
}
$sOQL .= implode(', ', $aOrderBy);
}
return $sOQL;
}
/**
* Check if the count exceeds a given limit
*
@@ -917,11 +875,8 @@ class DBObjectSet implements iDBObjectSetIterator
{
if (is_null($this->m_iNumTotalDBRows))
{
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
$resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if ($resQuery)
{
$aRow = CMDBSource::FetchArray($resQuery);
@@ -932,7 +887,7 @@ class DBObjectSet implements iDBObjectSetIterator
{
$iCount = 0;
}
}
}
else
{
$iCount = $this->m_iNumTotalDBRows;
@@ -957,11 +912,8 @@ class DBObjectSet implements iDBObjectSetIterator
{
if (is_null($this->m_iNumTotalDBRows))
{
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
$resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if ($resQuery)
{
$aRow = CMDBSource::FetchArray($resQuery);
@@ -972,7 +924,7 @@ class DBObjectSet implements iDBObjectSetIterator
{
$iCount = 0;
}
}
}
else
{
$iCount = $this->m_iNumTotalDBRows;

View File

@@ -3,7 +3,7 @@
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -56,11 +56,10 @@ class Dict
* @param $sLanguageCode
*
* @throws \DictExceptionUnknownLanguage
* @since 3.0.4 3.1.1 3.2.0 Param $sLanguageCode becomes nullable
*/
public static function SetUserLanguage($sLanguageCode = null)
public static function SetUserLanguage($sLanguageCode)
{
if (!is_null($sLanguageCode) && !array_key_exists($sLanguageCode, self::$m_aLanguages))
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
{
throw new DictExceptionUnknownLanguage($sLanguageCode);
}
@@ -116,50 +115,33 @@ class Dict
* @return string
*/
public static function S($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
{
$aInfo = self::GetLabelAndLangCode($sStringCode, $sDefault, $bUserLanguageOnly);
return $aInfo['label'];
}
/**
* Returns a localised string from the dictonary with its associated lang code
*
* @param string $sStringCode The code identifying the dictionary entry
* @param string $sDefault Default value if there is no match in the dictionary
* @param bool $bUserLanguageOnly True to allow the use of the default language as a fallback, false otherwise
*
* @return array{
* lang: string, label: string
* } with localized label string and used lang code
*/
private static function GetLabelAndLangCode($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
{
// Attempt to find the string in the user language
//
$sLangCode = self::GetUserLanguage();
self::InitLangIfNeeded($sLangCode);
if (! array_key_exists($sLangCode, self::$m_aData))
if (!array_key_exists($sLangCode, self::$m_aData))
{
IssueLog::Warning("Cannot find $sLangCode in all registered dictionaries.");
IssueLog::Warning("Cannot find $sLangCode in dictionnaries. default labels displayed");
// It may happen, when something happens before the dictionaries get loaded
return [ 'label' => $sStringCode, 'lang' => $sLangCode ];
return $sStringCode;
}
$aCurrentDictionary = self::$m_aData[$sLangCode];
if (is_array($aCurrentDictionary) && array_key_exists($sStringCode, $aCurrentDictionary))
{
return [ 'label' => $aCurrentDictionary[$sStringCode], 'lang' => $sLangCode ];
return $aCurrentDictionary[$sStringCode];
}
if (!$bUserLanguageOnly)
{
// Attempt to find the string in the default language
//
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
{
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => self::$m_sDefaultLanguage ];
return $aDefaultDictionary[$sStringCode];
}
// Attempt to find the string in english
//
@@ -168,17 +150,17 @@ class Dict
$aDefaultDictionary = self::$m_aData['EN US'];
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
{
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => 'EN US' ];
return $aDefaultDictionary[$sStringCode];
}
}
// Could not find the string...
//
if (is_null($sDefault))
{
return [ 'label' => $sStringCode, 'lang' => null ];
return $sStringCode;
}
return [ 'label' => $sDefault, 'lang' => null ];
return $sDefault;
}
@@ -194,25 +176,19 @@ class Dict
*/
public static function Format($sFormatCode /*, ... arguments ... */)
{
['label' => $sLocalizedFormat, 'lang' => $sLangCode] = self::GetLabelAndLangCode($sFormatCode);
$sLocalizedFormat = self::S($sFormatCode);
$aArguments = func_get_args();
array_shift($aArguments);
if ($sLocalizedFormat == $sFormatCode)
{
// Make sure the information will be displayed (ex: an error occuring before the dictionary gets loaded)
return $sFormatCode.' - '.implode(', ', $aArguments);
}
try{
return 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);
}
return vsprintf($sLocalizedFormat, $aArguments);
}
/**
* Initialize a the entries for a given language (replaces the former Add() method)
* @param string $sLanguageCode Code identifying the language i.e. 'FR-FR', 'EN-US'
@@ -222,7 +198,7 @@ class Dict
{
self::$m_aData[$sLanguageCode] = $aEntries;
}
/**
* Set the list of available languages
* @param hash $aLanguagesList
@@ -283,7 +259,7 @@ class Dict
{
$sDictFile = APPROOT.'env-'.utils::GetCurrentEnvironment().'/dictionaries/'.str_replace(' ', '-', strtolower($sLangCode)).'.dict.php';
require_once($sDictFile);
if (self::GetApcService()->function_exists('apc_store')
&& (self::$m_sApplicationPrefix !== null))
{
@@ -293,7 +269,7 @@ class Dict
}
return $bResult;
}
/**
* Enable caching (cached using APC)
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
@@ -336,14 +312,14 @@ class Dict
}
}
}
public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US')
{
$aMissing = array(); // Strings missing for the target language
$aUnexpected = array(); // Strings defined for the target language, but not found in the reference dictionary
$aNotTranslated = array(); // Strings having the same value in both dictionaries
$aOK = array(); // Strings having different values in both dictionaries
foreach (self::$m_aData[$sLanguageRef] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageCode]))
@@ -351,7 +327,7 @@ class Dict
$aMissing[$sStringCode] = $sValue;
}
}
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef]))
@@ -374,7 +350,7 @@ class Dict
}
return array($aMissing, $aUnexpected, $aNotTranslated, $aOK);
}
public static function Dump()
{
MyHelpers::var_dump_html(self::$m_aData);
@@ -397,7 +373,7 @@ class Dict
// No need to actually load the strings since it's only used to know the list of languages
// at setup time !!
}
/**
* Export all the dictionary entries - of the given language - whose code matches the given prefix
* missing entries in the current language will be replaced by entries in the default language
@@ -410,7 +386,7 @@ class Dict
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
$aEntries = array();
$iLength = strlen($sStartingWith);
// First prefill the array with entries from the default language
foreach(self::$m_aData[self::$m_sDefaultLanguage] as $sCode => $sEntry)
{
@@ -419,7 +395,7 @@ class Dict
$aEntries[$sCode] = $sEntry;
}
}
// Now put (overwrite) the entries for the user language
foreach(self::$m_aData[self::GetUserLanguage()] as $sCode => $sEntry)
{

View File

@@ -16,11 +16,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Application\Helper\WebResourcesHelper;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\Component\MedallionIcon\MedallionIcon;
use Combodo\iTop\Application\UI\Base\Component\Panel\Panel;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
use Combodo\iTop\Renderer\BlockRenderer;
/**
@@ -1415,8 +1412,6 @@ class DisplayableGraph extends SimpleGraph
/**
* Display the graph inside the given page, with the "filter" drawer above it
*
* @deprecated 3.1.1 3.2.0 N°3767 Use \DisplayableGraph::DisplayFilterBox() and \DisplayableGraph::DisplayGraph() instead
*
* @param WebPage $oP
* @param array $aResults
* @param string $sRelation
@@ -1430,35 +1425,10 @@ class DisplayableGraph extends SimpleGraph
*
* @throws \CoreException
* @throws \DictExceptionMissingString
*
*/
function Display(WebPage $oP, $aResults, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams = array(), bool $bLazyLoading = false)
{
$oP->AddSubBlock($this->DisplayFilterBox($oP, $aResults, $bLazyLoading));
$this->DisplayGraph($oP, $sRelation, $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams, $bLazyLoading);
}
/**
* Display only the graph inside the given page, with the parameters of filter box draw with DisplayFilterBox
*
* @param WebPage $oP
* @param string $sRelation
* @param ApplicationContext $oAppContext
* @param array $aExcludedObjects
* @param string $sObjClass
* @param int $iObjKey
* @param string $sContextKey
* @param array $aContextParams
* @param bool $bLazyLoading
*
* @throws \CoreException
* @throws \DictExceptionMissingString
*
* @since 3.1.1 3.2.0 N°3767
*/
function DisplayGraph(WebPage $oP, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams = array(), bool $bLazyLoading = false): void
{
list($aExcludedByClass, $aAdditionalContexts) = $this->GetFilteringData($sContextKey, $aContextParams, $aExcludedObjects);
list($aExcludedByClass, $aAdditionalContexts) = $this->DisplayFiltering($sContextKey, $aContextParams, $aExcludedObjects, $oP, $aResults, $bLazyLoading);
$iGroupingThreshold = utils::ReadParam('g', 5);
@@ -1543,10 +1513,12 @@ class DisplayableGraph extends SimpleGraph
$oP->add_ready_script(" $('#$sId').simple_graph(".json_encode($aParams).");");
} else {
$oP->add_script("function Load(){var aExcluded = []; $('input[name^=excluded]').each( function() {if (!$(this).prop('checked')) { aExcluded.push($(this).val()); }} ); var params= $.extend(".json_encode($aParams).", {excluded_classes: aExcluded}); $('#$sId').simple_graph(params);}");
$oP->add_ready_script("$('#graph').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');$('#impacted_objects_lists').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');$('#impacted_groups').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');");
$oP->add_ready_script("$('#impacted_objects_lists').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');$('#impacted_groups').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');");
}
}
catch (Exception $e) {
catch(Exception $e)
{
$oP->add('<div>'.$e->getMessage().'</div>');
}
$oP->add_script(
@@ -1591,41 +1563,23 @@ EOF
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
*
* @deprecated 3.1.1 3.2.0 N°3767 Use \DisplayableGraph::DisplayFilterBox() and \DisplayableGraph::GetFilteringData() instead
*/
public function DisplayFiltering(string $sContextKey, array $aContextParams, array $aExcludedObjects, WebPage $oP, array $aResults, bool $bLazyLoading = false): array
{
$oP->Add($this->DisplayFilterBox($oP, $aResults, $bLazyLoading));
return $this->GetFilteringData($sContextKey, $aContextParams, $aExcludedObjects);
}
/**
* @param \WebPage $oP
* @param array $aResults
* @param bool $bLazyLoading
*
* @return UIContentBlock
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \ReflectionException
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
*
* @since 3.1.1 3.2.0 N°3767
*/
public function DisplayFilterBox(WebPage $oP, array $aResults, bool $bLazyLoading = false): UIContentBlock
{
$aContextDefs = static::GetContextDefinitions($sContextKey, true, $aContextParams);
$aExcludedByClass = array();
foreach ($aExcludedObjects as $oObj) {
if (!array_key_exists(get_class($oObj), $aExcludedByClass)) {
$aExcludedByClass[get_class($oObj)] = array();
}
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
}
$sSftShort = Dict::S('UI:ElementsDisplayed');
$oBlock = UIContentBlockUIBlockFactory::MakeStandard(null, ['not-printable']);
$oP->add("<div class=\"not-printable\">\n");
$oUiSearchBlock = new Panel($sSftShort, [], Panel::ENUM_COLOR_SCHEME_CYAN, 'dh_flash');
$oUiSearchBlock->SetCSSClasses(["ibo-search-form-panel", "display_block"])
->SetIsCollapsible(true);
$oUiHtmlBlock = new Html(
$oUiSearchBlock->SetCSSClasses(["ibo-search-form-panel", "display_block"]);
$oUiSearchBlock->SetIsCollapsible(true);
$oUiHtmlBlock = new Combodo\iTop\Application\UI\Base\Component\Html\Html(
<<<EOF
<div id="ds_flash" class="search_box ibo-display-graph--search-box">
@@ -1672,23 +1626,11 @@ EOF
$oUiHtmlBlock->AddHtml("<button type=\"button\" id=\"ReloadMovieBtn\" class=\"ibo-button ibo-is-neutral ibo-is-regular\" onClick=\"$sOnCLick\">".Dict::S('UI:Button:Refresh')."</button></div></form>");
}
$oUiHtmlBlock->AddHtml("</div>\n");
$oUiHtmlBlock->AddHtml("</div>\n"); // class="not-printable"
$oUiSearchBlock->AddSubBlock($oUiHtmlBlock);
$oBlock->AddSubBlock($oUiSearchBlock);
$oP->AddUiBlock($oUiSearchBlock);
return $oBlock;
}
public function GetFilteringData(string $sContextKey, array $aContextParams, array $aExcludedObjects): array
{
$aContextDefs = static::GetContextDefinitions($sContextKey, true, $aContextParams);
$aExcludedByClass = array();
foreach ($aExcludedObjects as $oObj) {
if (!array_key_exists(get_class($oObj), $aExcludedByClass)) {
$aExcludedByClass[get_class($oObj)] = array();
}
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
}
$aAdditionalContexts = array();
foreach ($aContextDefs as $sKey => $aDefinition) {
$aAdditionalContexts[] = array('key' => $sKey, 'label' => Dict::S($aDefinition['dict']), 'oql' => $aDefinition['oql'], 'default' => (array_key_exists('default', $aDefinition) && ($aDefinition['default'] == 'yes')));

View File

@@ -10,7 +10,6 @@ use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\ColumnUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactory;
use Combodo\iTop\Application\Helper\ExportHelper;
require_once(APPROOT.'application/xlsxwriter.class.php');
@@ -83,7 +82,6 @@ class ExcelBulkExport extends TabularBulkExport
case 'xlsx_options':
$oPanel = PanelUIBlockFactory::MakeNeutral(Dict::S('Core:BulkExport:XLSXOptions'));
$oPanel->AddSubBlock(ExportHelper::GetAlertForExcelMaliciousInjection());
$oMulticolumn = MultiColumnUIBlockFactory::MakeStandard();
$oPanel->AddSubBlock($oMulticolumn);

View File

@@ -1,15 +1,27 @@
<?php
/**
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Core\Kpi\KpiLogData;
use Combodo\iTop\Service\Module\ModuleService;
// Copyright (C) 2010-2023 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Measures operations duration, memory usage, etc. (and some other KPIs)
*
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ExecutionKPI
@@ -18,8 +30,6 @@ class ExecutionKPI
static protected $m_bEnabled_Memory = false;
static protected $m_bBlameCaller = false;
static protected $m_sAllowedUser = '*';
static protected $m_bGenerateLegacyReport = true;
static protected $m_fSlowQueries = 0;
static protected $m_aStats = []; // Recurrent operations
static protected $m_aExecData = []; // One shot operations
@@ -76,39 +86,14 @@ class ExecutionKPI
return false;
}
static public function SetGenerateLegacyReport($bReportExtensionsOnly)
{
self::$m_bGenerateLegacyReport = $bReportExtensionsOnly;
}
static public function SetSlowQueries($fSlowQueries)
{
self::$m_fSlowQueries = $fSlowQueries;
}
static public function GetDescription()
{
$aFeatures = array();
if (self::$m_bEnabled_Duration) $aFeatures[] = 'Duration';
if (self::$m_bEnabled_Memory) $aFeatures[] = 'Memory usage';
$sFeatures = 'Measures: '.implode(', ', $aFeatures);
$sFeatures = implode(', ', $aFeatures);
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
$sSlowQueries = '';
if (self::$m_fSlowQueries > 0) {
$sSlowQueries = ". Slow Queries: ".self::$m_fSlowQueries."s";
}
$aExtensions = [];
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$aExtensions[] = ModuleService::GetInstance()->GetModuleNameFromObject($oExtensionInstance);
}
$sExtensions = '';
if (count($aExtensions) > 0) {
$sExtensions = '. KPI Extensions: ['.implode(', ', $aExtensions).']';
}
return "KPI logging is active for $sFor. $sFeatures$sSlowQueries$sExtensions";
return "KPI logging is active for $sFor. Measures: $sFeatures";
}
static public function ReportStats()
@@ -116,28 +101,7 @@ class ExecutionKPI
if (!self::IsEnabled()) return;
global $fItopStarted;
global $iItopInitialMemory;
$sExecId = microtime(); // id to differentiate the hrefs!
$sRequest = $_SERVER['REQUEST_URI'].' ('.$_SERVER['REQUEST_METHOD'].')';
if (isset($_POST['operation'])) {
$sRequest .= ' operation: '.$_POST['operation'];
}
$fStop = MyHelpers::getmicrotime();
if (($fStop - $fItopStarted) > self::$m_fSlowQueries) {
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
$iCurrentMemory = self::memory_get_usage();
$iPeakMemory = self::memory_get_peak_usage();
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$oKPILogData = new KpiLogData(KpiLogData::TYPE_REQUEST, 'Page', $sRequest, $fItopStarted, $fStop, '', $iItopInitialMemory, $iCurrentMemory, $iPeakMemory);
$oExtensionInstance->LogOperation($oKPILogData);
}
}
if (!self::$m_bGenerateLegacyReport) {
return;
}
$aBeginTimes = array();
foreach (self::$m_aExecData as $aOpStats)
@@ -150,9 +114,9 @@ class ExecutionKPI
$sHtml = "<hr/>";
$sHtml .= "<div style=\"background-color: grey; padding: 10px;\">";
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - $sRequest</h3>";
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")</h3>";
$oStarted = DateTime::createFromFormat('U.u', $fItopStarted);
$sHtml .= '<p>'.$oStarted->format('Y-m-d H:i:s.u').'</p>';
$sHtml .= "<p>".$oStarted->format('Y-m-d H:i:s.u')."</p>";
$sHtml .= "<p>log_kpi_user_id: ".UserRights::GetUserId()."</p>";
$sHtml .= "<div>";
$sHtml .= "<table border=\"1\" style=\"$sTableStyle\">";
@@ -293,7 +257,7 @@ class ExecutionKPI
$sTotalInter = round($fTotalInter, 3);
$sMinInter = round($fMinInter, 3);
$sMaxInter = round($fMaxInter, 3);
if (($fTotalInter >= self::$m_fSlowQueries))
if (($fTotalInter >= $fSlowQueries))
{
if ($bDisplayHeader)
{
@@ -321,19 +285,37 @@ class ExecutionKPI
self::Report($sHtml);
}
public static function InitStats()
{
// Invoke extensions to initialize the KPI statistics
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$oExtensionInstance->InitStats();
}
}
public function __construct()
{
$this->ResetCounters();
}
self::Push($this);
}
/**
* Stack executions to remove children duration from stats
*
* @param \ExecutionKPI $oExecutionKPI
*/
private static function Push(ExecutionKPI $oExecutionKPI)
{
self::$m_aExecutionStack[] = $oExecutionKPI;
}
/**
* Pop current child and count its duration in its parent
*
* @param float|int $fChildDuration
*/
private static function Pop(float $fChildDuration = 0)
{
array_pop(self::$m_aExecutionStack);
// Update the parent's children duration
$oPrevExecutionKPI = end(self::$m_aExecutionStack);
if ($oPrevExecutionKPI) {
$oPrevExecutionKPI->m_fChildrenDuration += $fChildDuration;
}
}
// Get the duration since startup, and reset the counter for the next measure
//
@@ -341,15 +323,9 @@ class ExecutionKPI
{
global $fItopStarted;
if (!self::IsEnabled()) {
return;
}
$aNewEntry = null;
$fStarted = $this->m_fStarted;
$fStopped = $this->m_fStarted;
if (self::$m_bEnabled_Duration) {
if (self::$m_bEnabled_Duration) {
$fStopped = MyHelpers::getmicrotime();
$aNewEntry = array(
'op' => $sOperationDesc,
@@ -360,9 +336,6 @@ class ExecutionKPI
$this->m_fStarted = $fStopped;
}
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
$iCurrentMemory = 0;
$iPeakMemory = 0;
if (self::$m_bEnabled_Memory)
{
$iCurrentMemory = self::memory_get_usage();
@@ -372,118 +345,40 @@ class ExecutionKPI
}
$aNewEntry['mem_begin'] = $this->m_iInitialMemory;
$aNewEntry['mem_end'] = $iCurrentMemory;
$iPeakMemory = self::memory_get_peak_usage();
$aNewEntry['mem_peak'] = $iPeakMemory;
if (function_exists('memory_get_peak_usage'))
{
$aNewEntry['mem_peak'] = memory_get_peak_usage();
}
// Reset for the next operation (if the object is recycled)
$this->m_iInitialMemory = $iCurrentMemory;
}
if (self::$m_bEnabled_Duration || self::$m_bEnabled_Memory) {
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance)
{
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
$oKPILogData = new KpiLogData(
KpiLogData::TYPE_REPORT,
'Step',
$sOperationDesc,
$fStarted,
$fStopped,
$sExtension,
$iInitialMemory,
$iCurrentMemory,
$iPeakMemory);
$oExtensionInstance->LogOperation($oKPILogData);
}
}
if (!is_null($aNewEntry) && self::$m_bGenerateLegacyReport)
if (!is_null($aNewEntry))
{
self::$m_aExecData[] = $aNewEntry;
}
$this->ResetCounters();
}
/**
* Compute statistics for a call to an extension
* Note: not working in dev mode (with links to env-production)
*
* @param object|string $object object called
* @param string $sMethod method called on the object
* @param string $sMessage additional message
*
* @return bool true if an extension was found for this object::method
* @throws \ReflectionException
*/
public function ComputeStatsForExtension($object, string $sMethod, string $sMessage = ''): bool
{
if (!self::IsEnabled()) {
return true;
}
$sSignature = ModuleService::GetInstance()->GetModuleMethodSignature($object, $sMethod);
if (utils::StartsWith($sSignature, '[')) {
$this->ComputeStats('Extension', "$sSignature $sMessage");
return true;
}
return false;
}
public function ComputeStats($sOperation, $sArguments)
{
if (!self::IsEnabled()) {
return;
}
$fDuration = 0;
if (self::$m_bEnabled_Duration) {
$fStopped = MyHelpers::getmicrotime();
$fDuration = $fStopped - $this->m_fStarted;
$aCallstack = [];
if (self::$m_bGenerateLegacyReport) {
if (self::$m_bBlameCaller) {
$aCallstack = MyHelpers::get_callstack(1);
self::$m_aStats[$sOperation][$sArguments][] = [
'time' => $fDuration,
'callers' => $aCallstack,
];
} else {
self::$m_aStats[$sOperation][$sArguments][] = [
'time' => $fDuration
];
}
}
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
$iCurrentMemory = 0;
$iPeakMemory = 0;
if (self::$m_bEnabled_Memory)
{
$iCurrentMemory = self::memory_get_usage();
$iPeakMemory = self::memory_get_peak_usage();
}
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
$oKPILogData = new KpiLogData(
KpiLogData::TYPE_STATS,
$sOperation,
$sArguments,
$this->m_fStarted,
$fStopped,
$sExtension,
$iInitialMemory,
$iCurrentMemory,
$iPeakMemory,
$aCallstack);
$oExtensionInstance->LogOperation($oKPILogData);
}
}
$fSelfDuration = $fDuration - $this->m_fChildrenDuration;
if (self::$m_bBlameCaller) {
self::$m_aStats[$sOperation][$sArguments][] = array(
'time' => $fSelfDuration,
'callers' => MyHelpers::get_callstack(1),
);
} else {
self::$m_aStats[$sOperation][$sArguments][] = array(
'time' => $fSelfDuration,
);
}
}
self::Pop($fDuration);
}
protected function ResetCounters()
@@ -513,7 +408,35 @@ class ExecutionKPI
static protected function memory_get_usage()
{
return memory_get_usage(true);
if (function_exists('memory_get_usage'))
{
return memory_get_usage(true);
}
// Copied from the PHP manual
//
//If its Windows
//Tested on Win XP Pro SP2. Should work on Win 2003 Server too
//Doesn't work for 2000
//If you need it to work for 2000 look at http://us2.php.net/manual/en/function.memory-get-usage.php#54642
if (substr(PHP_OS,0,3) == 'WIN')
{
$output = array();
exec('tasklist /FI "PID eq ' . getmypid() . '" /FO LIST', $output);
return preg_replace( '/[\D]/', '', $output[5] ) * 1024;
}
else
{
//We now assume the OS is UNIX
//Tested on Mac OS X 10.4.6 and Linux Red Hat Enterprise 4
//This should work on most UNIX systems
$pid = getmypid();
exec("ps -eo%mem,rss,pid | grep $pid", $output);
$output = explode(" ", $output[0]);
//rss is given in 1024 byte units
return $output[1] * 1024;
}
}
static public function memory_get_peak_usage($bRealUsage = false)

View File

@@ -3,7 +3,7 @@
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -576,11 +576,6 @@ class LogChannels
public const DATATABLE = 'Datatable';
public const DEADLOCK = 'DeadLock';
/**
* @var string Everything related to PHP sessions tracking
* @since 3.1.1 3.2.0 N°6901
*/
public const SESSIONTRACKER = 'SessionTracker';
/**
* @var string Everything related to the datamodel CRUD
@@ -1143,13 +1138,9 @@ class DeprecatedCallsLog extends LogAPI
parent::Enable($sTargetFile);
if (
(
(false === defined(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME))
|| (defined(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME) && (constant(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME) !== true))
)
(false === defined(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME))
&& static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD)
) {
IssueLog::Trace('Setting '.static::class.' error handler to catch DEPRECATED', static::ENUM_CHANNEL_PHP_LIBMETHOD);
set_error_handler([static::class, 'DeprecatedNoticesErrorHandler'], E_DEPRECATED | E_USER_DEPRECATED);
}
}
@@ -1680,8 +1671,6 @@ class ExceptionLog extends LogAPI
*/
private static function GetLastEventIssue()
{
$oRet = self::$oLastEventIssue;
self::$oLastEventIssue = null;
return $oRet;
return self::$oLastEventIssue;
}
}

View File

@@ -1241,7 +1241,7 @@ abstract class MetaModel
}
$sTable = self::DBGetTable($sClass);
// Could be completed later with all the classes that are using a given table
// Could be completed later with all the classes that are using a given table
if (!array_key_exists($sTable, $aTables)) {
$aTables[$sTable] = array();
}
@@ -1445,10 +1445,8 @@ abstract class MetaModel
*
* @return AttributeDefinition[]
* @throws \CoreException
*
* @see GetAttributesList for attcode list
*/
final public static function ListAttributeDefs($sClass)
final static public function ListAttributeDefs($sClass)
{
self::_check_subclass($sClass);
return self::$m_aAttribDefs[$sClass];
@@ -1461,10 +1459,8 @@ abstract class MetaModel
* @param string[] $aDesiredAttTypes Array of AttributeDefinition classes to filter the list on
* @param string|null $sListCode If provided, attributes will be limited to those in this zlist
*
* @return string[] list of attcodes
* @return array
* @throws \CoreException
*
* @see ListAttributeDefs to get AttributeDefinition array instead
*/
final public static function GetAttributesList(string $sClass, array $aDesiredAttTypes = [], ?string $sListCode = null)
{
@@ -3526,7 +3522,7 @@ abstract class MetaModel
}
// Set the "host class" as soon as possible, since HierarchicalKeys use it for their 'target class' as well
// and this needs to be know early (for Init_IsKnowClass 19 lines below)
// and this needs to be know early (for Init_IsKnowClass 19 lines below)
$oAtt->SetHostClass($sTargetClass);
// Some attributes could refer to a class
@@ -3568,7 +3564,7 @@ abstract class MetaModel
self::$m_aAttribDefs[$sTargetClass][$oAtt->GetCode()] = $oAtt;
self::$m_aAttribOrigins[$sTargetClass][$oAtt->GetCode()] = $sTargetClass;
// Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used
// Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used
}
/**
@@ -3768,7 +3764,7 @@ abstract class MetaModel
self::$m_aStimuli[$sTargetClass][$oStimulus->GetCode()] = $oStimulus;
// I wanted to simplify the syntax of the declaration of objects in the biz model
// Therefore, the reference to the host class is set there
// Therefore, the reference to the host class is set there
$oStimulus->SetHostClass($sTargetClass);
}
@@ -4223,78 +4219,40 @@ abstract class MetaModel
}
else
{
$aCurrentUser = [];
$aCurrentContact = [];
$aCurrentUser = array();
$aCurrentContact = array();
foreach ($aExpectedArgs as $expression)
{
$aName = explode('->', $expression->GetName());
if ($aName[0] == 'current_contact_id') {
$aPlaceholders['current_contact_id'] = UserRights::GetContactId();
} else if ($aName[0] == 'current_user') {
}
if ($aName[0] == 'current_user') {
array_push($aCurrentUser, $aName[1]);
} else if ($aName[0] == 'current_contact') {
}
if ($aName[0] == 'current_contact') {
array_push($aCurrentContact, $aName[1]);
}
}
if (count($aCurrentUser) > 0) {
static::FillObjectPlaceholders($aPlaceholders, 'current_user', UserRights::GetUserObject(), $aCurrentUser);
$oUser = UserRights::GetUserObject();
$aPlaceholders['current_user->object()'] = $oUser;
foreach ($aCurrentUser as $sField) {
$aPlaceholders['current_user->'.$sField] = $oUser->Get($sField);
}
}
if (count($aCurrentContact) > 0) {
static::FillObjectPlaceholders($aPlaceholders, 'current_contact', UserRights::GetContactObject(), $aCurrentContact);
$oPerson = UserRights::GetContactObject();
$aPlaceholders['current_contact->object()'] = $oPerson;
foreach ($aCurrentContact as $sField) {
$aPlaceholders['current_contact->'.$sField] = $oPerson->Get($sField);
}
}
}
return $aPlaceholders;
}
/**
* @since 3.1.1 N°6824
* @param array $aPlaceholders
* @param string $sPlaceHolderPrefix
* @param ?\DBObject $oObject
* @param array $aCurrentUser
*
* @return void
*
*/
private static function FillObjectPlaceholders(array &$aPlaceholders, string $sPlaceHolderPrefix, ?\DBObject $oObject, array $aCurrentUser) : void {
$sPlaceHolderKey = $sPlaceHolderPrefix."->object()";
if (is_null($oObject)){
$aContext = [
"current_user_id" => UserRights::GetUserId(),
"null object type" => $sPlaceHolderPrefix,
"fields" => $aCurrentUser,
];
IssueLog::Warning("Unresolved placeholders due to null object in current context", null,
$aContext);
$aPlaceholders[$sPlaceHolderKey] = Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
foreach ($aCurrentUser as $sField) {
$sPlaceHolderKey = $sPlaceHolderPrefix . "->$sField";
$aPlaceholders[$sPlaceHolderKey] = Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
}
} else {
$aPlaceholders[$sPlaceHolderKey] = $oObject;
foreach ($aCurrentUser as $sField) {
$sPlaceHolderKey = $sPlaceHolderPrefix . "->$sField";
// Mind that the "id" is not viewed as a valid att. code by \MetaModel::IsValidAttCode() so we have to test it manually
if ($sField !== "id" && false === MetaModel::IsValidAttCode(get_class($oObject), $sField)){
$aContext = [
"current_user_id" => UserRights::GetUserId(),
"obj_class" => get_class($oObject),
"placeholder" => $sPlaceHolderKey,
"invalid_field" => $sField,
];
IssueLog::Warning("Unresolved placeholder due to invalid attribute", null,
$aContext);
$aPlaceholders[$sPlaceHolderKey] = Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
continue;
}
$aPlaceholders[$sPlaceHolderKey] = $oObject->Get($sField);
}
}
}
/**
* @param \DBSearch $oFilter
*
@@ -5156,7 +5114,7 @@ abstract class MetaModel
*/
protected static function DBCreateTables($aCallback = null)
{
[$aErrors, $aSugFix, $aCondensedQueries] = self::DBCheckFormat();
list($aErrors, $aSugFix, $aCondensedQueries) = self::DBCheckFormat();
//$sSQL = implode('; ', $aCondensedQueries); Does not work - multiple queries not allowed
foreach($aCondensedQueries as $sQuery)
@@ -5178,7 +5136,7 @@ abstract class MetaModel
*/
protected static function DBCreateViews()
{
[$aErrors, $aSugFix] = self::DBCheckViews();
list($aErrors, $aSugFix) = self::DBCheckViews();
foreach($aSugFix as $sClass => $aTarget)
{
@@ -6340,13 +6298,6 @@ abstract class MetaModel
*/
public static function Startup($config, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false, $sEnvironment = 'production')
{
// Startup on a new environment is not supported
static $bStarted = false;
if ($bStarted) {
return;
}
$bStarted = true;
self::$m_sEnvironment = $sEnvironment;
try {
@@ -6425,9 +6376,7 @@ abstract class MetaModel
ExecutionKPI::EnableDuration(self::$m_oConfig->Get('log_kpi_duration'));
ExecutionKPI::EnableMemory(self::$m_oConfig->Get('log_kpi_memory'));
ExecutionKPI::SetAllowedUser(self::$m_oConfig->Get('log_kpi_user_id'));
ExecutionKPI::SetGenerateLegacyReport(self::$m_oConfig->Get('log_kpi_generate_legacy_report'));
ExecutionKPI::SetSlowQueries(self::$m_oConfig->Get('log_kpi_slow_queries'));
ExecutionKPI::SetAllowedUser(self::$m_oConfig->Get('log_kpi_user_id'));
self::$m_bSkipCheckToWrite = self::$m_oConfig->Get('skip_check_to_write');
self::$m_bSkipCheckExtKeys = self::$m_oConfig->Get('skip_check_ext_keys');
@@ -6521,7 +6470,7 @@ abstract class MetaModel
$aCache['m_aExtensionClassNames'] = self::$m_aExtensionClassNames;
$aCache['m_Category2Class'] = self::$m_Category2Class;
$aCache['m_aRootClasses'] = self::$m_aRootClasses; // array of "classname" => "rootclass"
$aCache['m_aParentClasses'] = self::$m_aParentClasses; // array of ("classname" => array of "parentclass")
$aCache['m_aParentClasses'] = self::$m_aParentClasses; // array of ("classname" => array of "parentclass")
$aCache['m_aChildClasses'] = self::$m_aChildClasses; // array of ("classname" => array of "childclass")
$aCache['m_aClassParams'] = self::$m_aClassParams; // array of ("classname" => array of class information)
$aCache['m_aAttribDefs'] = self::$m_aAttribDefs; // array of ("classname" => array of attributes)
@@ -6546,7 +6495,6 @@ abstract class MetaModel
CMDBSource::InitFromConfig(self::$m_oConfig);
// Later when timezone implementation is correctly done: CMDBSource::SetTimezone($sDBTimezone);
ExecutionKPI::InitStats();
}
/**
@@ -6578,19 +6526,6 @@ abstract class MetaModel
return $value;
}
/**
* @internal Used for resetting the configuration during automated tests
* @param \Config $oConfiguration
*
* @return void
* @since 3.0.4 3.1.1 3.2.0
*/
public static function SetConfig(Config $oConfiguration)
{
self::$m_oConfig = $oConfiguration;
}
/**
* @return Config
*/
@@ -6779,13 +6714,7 @@ abstract class MetaModel
if ($bMustBeFound && empty($aRow))
{
$sNotFoundErrorMessage = "No result for the single row query";
IssueLog::Info($sNotFoundErrorMessage, LogChannels::CMDB_SOURCE, [
'class' => $sClass,
'key' => $iKey,
'sql_query' => $sSQL,
]);
throw new CoreException($sNotFoundErrorMessage);
throw new CoreException("No result for the single row query: '$sSQL'");
}
return $aRow;
@@ -6878,21 +6807,25 @@ abstract class MetaModel
* $bMustBeFound=false)
* @throws CoreException if no result found and $bMustBeFound=true
* @throws ArchivedObjectException if archive mode disabled and result is archived and $bMustBeFound=true
* @throws \Exception
*
*/
public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
{
$oObject = self::GetObjectWithArchive($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties);
if (empty($oObject)) {
if (empty($oObject))
{
return null;
}
if (!utils::IsArchiveMode() && $oObject->IsArchived()) {
if (!utils::IsArchiveMode() && $oObject->IsArchived())
{
if ($bMustBeFound) {
throw new ArchivedObjectException("The object $sClass::$iKey is archived");
} else {
return null;
}
return null;
}
return $oObject;
@@ -6927,22 +6860,6 @@ abstract class MetaModel
return $iCount === 1;
}
public static function GetFinalClassName(string $sClass, int $iKey): string
{
if (MetaModel::IsStandaloneClass($sClass)) {
return $sClass;
}
$sRootClass = MetaModel::GetRootClass($sClass);
$sTable = MetaModel::DBGetTable($sRootClass);
$sKeyCol = MetaModel::DBGetKey($sRootClass);
$sEscapedKey = CMDBSource::Quote($iKey);
$sFinalClassField = Metamodel::DBGetClassField($sRootClass);
$sQuery = "SELECT `{$sFinalClassField}` FROM `{$sTable}` WHERE `{$sKeyCol}` = {$sEscapedKey}";
return CMDBSource::QueryToScalar($sQuery);
}
/**
* Search for the specified class and id. If the object is archived it will be returned anyway (this is for pre-2.4
* module compatibility, see N.1108)
@@ -7691,12 +7608,14 @@ abstract class MetaModel
// Build the list of available extensions
//
$aInterfaces = [
'iLoginFSMExtension',
'iLogoutExtension',
'iLoginUIExtension',
'iPreferencesExtension',
'iApplicationUIExtension',
'iPreferencesExtension',
'iApplicationObjectExtension',
'iLoginFSMExtension',
'iLoginUIExtension',
'iLogoutExtension',
'iQueryModifier',
'iOnClassInitialization',
'iPopupMenuExtension',
'iPageUIExtension',
'iPageUIBlockExtension',
@@ -7710,12 +7629,9 @@ abstract class MetaModel
'iBackofficeDictEntriesExtension',
'iBackofficeDictEntriesPrefixesExtension',
'iPortalUIExtension',
'iQueryModifier',
'iOnClassInitialization',
'iModuleExtension',
'iKPILoggerExtension',
'ModuleHandlerApiInterface',
'iNewsroomProvider',
'iModuleExtension',
];
foreach ($aInterfaces as $sInterface) {
self::$m_aExtensionClassNames[$sInterface] = array();

View File

@@ -22,9 +22,7 @@
* A class to serialize the execution of some code sections
* Emulates the API of PECL Mutex class
* Relies on MySQL locks because the API sem_get is not always present in the
* installed PHP.
*
* @link https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html MySQL locking functions documentation
* installed PHP.
*
* @copyright Copyright (C) 2013-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
@@ -259,7 +257,7 @@ class iTopMutex
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
if (!$this->hDBLink) {
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
}
// Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection,

View File

@@ -121,9 +121,7 @@ abstract class Trigger extends cmdbAbstractObject
$oAction = MetaModel::GetObject('Action', $iActionId);
if ($oAction->IsActive())
{
$oKPI = new ExecutionKPI();
$oAction->DoExecute($this, $aContextArgs);
$oKPI->ComputeStatsForExtension($oAction, 'DoExecute');
}
}
}
@@ -256,38 +254,6 @@ abstract class TriggerOnObject extends Trigger
}
}
/**
* Activate trigger based on attribute list given instead of changed attributes
*
* @param array $aContextArgs
* @param array|null $aAttributes if null default to changed attributes
*
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @since 3.1.1 3.2.0 N°6228
*/
public function DoActivateForSpecificAttributes(array $aContextArgs, ?array $aAttributes)
{
if (isset($aContextArgs['this->object()']))
{
/** @var \DBObject $oObject */
$oObject = $aContextArgs['this->object()'];
if (is_null($aAttributes)) {
$aChanges = $oObject->ListPreviousValuesForUpdatedAttributes();
} else {
$aChanges = array_fill_keys($aAttributes, true);
}
if (false === $this->IsTargetObject($oObject->GetKey(), $aChanges)) {
return;
}
}
parent::DoActivate($aContextArgs);
}
/**
* @param $iObjectId
* @param array $aChanges

View File

@@ -761,25 +761,14 @@ class UserRights
protected static $m_aCacheContactPictureAbsUrl = [];
/** @var UserRightsAddOnAPI $m_oAddOn */
protected static $m_oAddOn;
protected static $m_oUser = null;
protected static $m_oRealUser = null;
protected static $m_oUser;
protected static $m_oRealUser;
protected static $m_sSelfRegisterAddOn = null;
protected static $m_aAdmins = array();
protected static $m_aPortalUsers = array();
/** @var array array('sName' => $sName, 'bSuccess' => $bSuccess); */
private static $m_sLastLoginStatus = null;
/**
* @return void
* @since 3.0.4 3.1.1 3.2.0
*/
protected static function ResetCurrentUserData()
{
self::$m_oUser = null;
self::$m_oRealUser = null;
self::$m_sLastLoginStatus = null;
}
/**
* @param string $sModuleName
*
@@ -798,7 +787,8 @@ class UserRights
}
self::$m_oAddOn = new $sModuleName;
self::$m_oAddOn->Init();
self::ResetCurrentUserData();
self::$m_oUser = null;
self::$m_oRealUser = null;
}
/**
@@ -856,8 +846,6 @@ class UserRights
}
/**
* Set the current user (as part of the login process)
*
* @param string $sLogin Login of the concerned user
* @param string $sAuthentication
*
@@ -884,19 +872,6 @@ class UserRights
return true;
}
/**
* Reset current user and cleanup associated SESSION data
*
* @return void
* @since 3.0.4 3.1.1 3.2.0
*/
public static function Logoff()
{
self::ResetCurrentUserData();
Dict::SetUserLanguage(null);
self::_ResetSessionCache();
}
/**
* @param string $sLogin Login of the user to check the credentials for
* @param string $sPassword
@@ -1121,7 +1096,9 @@ class UserRights
}
/**
* @return string connected {@see User} login field value, otherwise empty string
* Return the current user login or an empty string if nobody connected.
*
* @return string
*/
public static function GetUser()
{
@@ -1569,9 +1546,9 @@ class UserRights
/**
* @param string $sClass
* @param int $iActionCode see UR_ACTION_* constants
* @param DBObjectSet $oInstanceSet
* @param User $oUser
* @param int $iActionCode
* @param \DBObjectSet $oInstanceSet
* @param \User $oUser
*
* @return int (UR_ALLOWED_YES|UR_ALLOWED_NO|UR_ALLOWED_DEPENDS)
* @throws \CoreException

View File

@@ -20,8 +20,6 @@ $ibo-dashlet-badge--icon--size: 48px !default;
$ibo-dashlet-badge--action-icon--margin-right: $ibo-spacing-300 !default;
$ibo-dashlet-badge--body--tooltip-title--margin-bottom: $ibo-spacing-500 !default;
/* CSS variables (can be changed directly from the browser) */
:root {
--ibo-dashlet-badge--min-width: #{$ibo-dashlet-badge--min-width};
@@ -76,27 +74,18 @@ $ibo-dashlet-badge--body--tooltip-title--margin-bottom: $ibo-spacing-500 !defaul
@extend %ibo-hyperlink-inherited-colors;
}
}
.ibo-dashlet-badge--action-list-count {
margin-right: $ibo-dashlet-badge--action-list-count--margin-right;
@extend %ibo-font-ral-bol-450;
.ibo-dashlet-badge--action-list-count{
margin-right: $ibo-dashlet-badge--action-list-count--margin-right;
@extend %ibo-font-ral-bol-450;
}
.ibo-dashlet-badge--action-list-label {
display: inline-block;
@extend %ibo-text-truncated-with-ellipsis;
.ibo-dashlet-badge--action-list-label{
display: inline-block;
@extend %ibo-text-truncated-with-ellipsis;
}
.ibo-dashlet-badge--action-create {
@extend %ibo-baseline-centered-content;
@extend %ibo-font-size-150;
.ibo-dashlet-badge--action-create{
@extend %ibo-baseline-centered-content;
@extend %ibo-font-size-150;
}
.ibo-dashlet-badge--action-create-icon {
margin-right: $ibo-dashlet-badge--action-icon--margin-right;
}
.ibo-dashlet-badge--body--tooltip-title {
@extend %ibo-font-weight-600;
margin-bottom: $ibo-dashlet-badge--body--tooltip-title--margin-bottom;
.ibo-dashlet-badge--action-create-icon{
margin-right: $ibo-dashlet-badge--action-icon--margin-right;
}

View File

@@ -51,4 +51,4 @@ tr.ibo-csv-import--row-added td {
font-size: $ibo-csv-import--download-file--font-size;
color: $ibo-csv-import--download-file--color;
margin: $ibo-csv-import--download-file--margin;
}
}

View File

@@ -17,7 +17,9 @@ $ibo-welcome-popup--text--options--bottom: 10px !default;
#welcome_popup{
display: flex;
}
.ibo-welcome-popup--columns{
display: flex;
}
.ibo-welcome-popup--image{
display: flex;
@@ -44,7 +46,39 @@ $ibo-welcome-popup--text--options--bottom: 10px !default;
}
}
}
.ibo-welcome-popup--text--options{
position: absolute;
bottom: $ibo-welcome-popup--text--options--bottom;
.ibo-welcome-popup--dialog {
width: 60rem;
}
.ibo-welcome-popup--content {
width: 100%;
.ibo-welcome-popup--message {
width: 100%;
min-height: 12rem;
}
.ibo-welcome-popup--button {
width: 100%;
text-align: center;
padding-top: 1rem;
position: absolute;
bottom: 4.5rem;
}
}
.ibo-welcome-popup--indicators {
width: 100%;
display: block;
text-align: center;
padding-top: 1.5rem;
padding-bottom: 0;
height: 3rem;
.ibo-welcome-popup--indicator {
width: 1rem;
height: 1rem;
border-radius: 0.5rem;
background-color: $ibo-color-secondary-600;
display: inline-block;
cursor: pointer;
}
.ibo-welcome-popup--active {
background-color: $ibo-color-information-600 !important;
}
}

View File

@@ -13,9 +13,6 @@ $body-overflow-x: hidden !default;
$body-overflow-y: auto !default;
/*N°5786 - Avoid strong text to always be grey (default Bulma color for this var.) so strong text can keep its color. This is mostly for text within the .ibo-is-html-content. */
$text-strong: inherit !default;
/**
* customize Bulma content variables
* See https://bulma.io/documentation/elements/content/

View File

@@ -5,7 +5,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-cas/3.1.1',
'authent-cas/3.1.0',
array(
// Identification
//

View File

@@ -27,7 +27,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-external/3.1.1',
'authent-external/3.1.0',
array(
// Identification
//

View File

@@ -355,26 +355,4 @@
</presentation>
</class>
</classes>
<meta>
<classes>
<class id="UserInternal" _delta="define_if_not_exists">
<fields>
<field id="contactid" xsi:type="AttributeExternalKey">
<target_class>Contact</target_class>
</field>
<field id="first_name" xsi:type="AttributeExternalField"/>
<field id="last_name" xsi:type="AttributeExternalField"/>
<field id="status" xsi:type="AttributeEnum"/>
<field id="org_id" xsi:type="AttributeExternalField"/>
<field id="email" xsi:type="AttributeExternalField"/>
<field id="login" xsi:type="AttributeString"/>
<field id="language" xsi:type="AttributeApplicationLanguage"/>
<field id="status" xsi:type="AttributeEnum"/>
<field id="allowed_org_list" xsi:type="AttributeLinkedSetIndirect"/>
<field id="profile_list" xsi:type="AttributeLinkedSetIndirect"/>
<field id="log" xsi:type="AttributeCaseLog"/>
</fields>
</class>
</classes>
</meta>
</itop_design>

View File

@@ -9,7 +9,7 @@ if (function_exists('ldap_connect'))
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-ldap/3.1.1',
'authent-ldap/3.1.0',
array(
// Identification
//

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-local/3.1.1',
'authent-local/3.1.0',
array(
// Identification
//

View File

@@ -5,7 +5,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'combodo-backoffice-darkmoon-theme/3.1.1',
'combodo-backoffice-darkmoon-theme/3.1.0',
array(
// Identification
//

File diff suppressed because one or more lines are too long

View File

@@ -24,7 +24,7 @@
/** @noinspection PhpUnhandledExceptionInspection */
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'combodo-db-tools/3.1.1',
'combodo-db-tools/3.1.0',
array(
// Identification
//

View File

@@ -344,7 +344,6 @@ class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExt
while ($oAttachment = $oSet->Fetch())
{
$oTempAttachment = clone $oAttachment;
$oTempAttachment->Set('expire', time() + utils::GetConfig()->Get('draft_attachments_lifetime'));
$oTempAttachment->Set('item_id', null);
$oTempAttachment->Set('temp_id', $sTempId);
$oTempAttachment->DBInsert();

View File

@@ -19,7 +19,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-attachments/3.1.1',
'itop-attachments/3.1.0',
array(
// Identification
//

View File

@@ -235,16 +235,13 @@ class DBRestore extends DBBackup
if (in_array($oFileInfo->getFilename(), $aStandardFiles)) {
continue;
}
// Normalize filenames to cope with Windows backslashes
$sPath = str_replace('\\', '/', $oFileInfo->getPathname());
$sRefPath = str_replace('\\', '/', $sDataDir.'/production-modules');
if (strncmp($sPath, $sRefPath, strlen($sRefPath)) == 0) {
if (strncmp($oFileInfo->getPathname(), $sDataDir.'/production-modules', strlen($sDataDir.'/production-modules')) == 0) {
continue;
}
$aExtraFiles[$oFileInfo->getPathname()] = APPROOT.substr($oFileInfo->getPathname(), strlen($sDataDir));
}
return $aExtraFiles;
}
}

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-backup/3.1.1',
'itop-backup/3.1.0',
array(
// Identification
//

View File

@@ -5,7 +5,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-cmdb-services/3.1.1',
'itop-bridge-cmdb-services/3.1.0',
array(
// Identification
//

View File

@@ -5,7 +5,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-cmdb-ticket/3.1.1',
'itop-bridge-cmdb-ticket/3.1.0',
array(
// Identification
//

View File

@@ -5,7 +5,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-datacenter-mgmt-services/3.1.1',
'itop-bridge-datacenter-mgmt-services/3.1.0',
array(
// Identification
//

View File

@@ -5,7 +5,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-endusers-devices-services/3.1.1',
'itop-bridge-endusers-devices-services/3.1.0',
array(
// Identification
//

View File

@@ -5,7 +5,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-storage-mgmt-services/3.1.1',
'itop-bridge-storage-mgmt-services/3.1.0',
array(
// Identification
//

View File

@@ -5,7 +5,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-virtualization-mgmt-services/3.1.1',
'itop-bridge-virtualization-mgmt-services/3.1.0',
array(
// Identification
//

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-virtualization-storage/3.1.1',
'itop-bridge-virtualization-storage/3.1.0',
array(
// Identification
//

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-change-mgmt-itil/3.1.1',
'itop-change-mgmt-itil/3.1.0',
array(
// Identification
//

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-change-mgmt/3.1.1',
'itop-change-mgmt/3.1.0',
array(
// Identification
//

View File

@@ -6354,7 +6354,17 @@
<attribute id="connectableci_id"/>
</attributes>
</reconciliation>
<uniqueness_rules/>
<uniqueness_rules>
<rule id="no_duplicate">
<attributes>
<attribute id="networkdevice_id"/>
<attribute id="connectableci_id"/>
</attributes>
<filter><![CDATA[]]></filter>
<disabled>false</disabled>
<is_blocking>true</is_blocking>
</rule>
</uniqueness_rules>
</properties>
<fields>
<field id="networkdevice_id" xsi:type="AttributeExternalKey">

View File

@@ -28,18 +28,17 @@
//////////////////////////////////////////////////////////////////////
//
Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'Relation:impacts/Description' => 'Prvky ovlivněné objektem',
'Relation:impacts/DownStream' => 'Dopad na',
'Relation:impacts/DownStream+' => 'Elements impacted by~~',
'Relation:impacts/UpStream' => 'Závislost na',
'Relation:impacts/UpStream+' => 'Elements impacting~~',
'Relation:impacts/Description' => 'Prvky ovlivněné objektem',
'Relation:impacts/DownStream' => 'Dopad na',
'Relation:impacts/DownStream+' => 'Elements impacted by~~',
'Relation:impacts/UpStream' => 'Závislost na',
'Relation:impacts/UpStream+' => 'Elements impacting~~',
// Legacy entries
'Relation:depends on/Description' => 'Prvky ovlivňující objekt',
'Relation:depends on/DownStream' => 'Závislost na',
'Relation:depends on/UpStream' => 'Dopad na',
'Relation:impacts/LoadData' => 'Load data~~',
'Relation:impacts/NoFilteredData' => 'please select objects and load data~~',
'Relation:impacts/FilteredData' => 'Filtered data~~',
'Relation:depends on/DownStream' => 'Závislost na',
'Relation:depends on/UpStream' => 'Dopad na',
'Relation:impacts/LoadData' => 'Load data~~',
'Relation:impacts/NoFilteredData' => 'please select objects in Graphical view tag~~',
));

View File

@@ -21,18 +21,17 @@
* @licence http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('DA DA', 'Danish', 'Dansk', array(
'Relation:impacts/Description' => 'Elementer berørt af ...',
'Relation:impacts/DownStream' => 'Påvrikning ...',
'Relation:impacts/DownStream+' => 'Elements impacted by~~',
'Relation:impacts/UpStream' => 'Afhænger af ...',
'Relation:impacts/UpStream+' => 'Elements impacting~~',
'Relation:impacts/Description' => 'Elementer berørt af ...',
'Relation:impacts/DownStream' => 'Påvrikning ...',
'Relation:impacts/DownStream+' => 'Elements impacted by~~',
'Relation:impacts/UpStream' => 'Afhænger af ...',
'Relation:impacts/UpStream+' => 'Elements impacting~~',
// Legacy entries
'Relation:depends on/Description' => 'Elementer, som afhænger af dette element',
'Relation:depends on/DownStream' => 'Afhænger af ...',
'Relation:depends on/UpStream' => 'Påvirker ...',
'Relation:impacts/LoadData' => 'Load data~~',
'Relation:impacts/NoFilteredData' => 'please select objects and load data~~',
'Relation:impacts/FilteredData' => 'Filtered data~~',
'Relation:depends on/DownStream' => 'Afhænger af ...',
'Relation:depends on/UpStream' => 'Påvirker ...',
'Relation:impacts/LoadData' => 'Load data~~',
'Relation:impacts/NoFilteredData' => 'please select objects in Graphical view tag~~',
));

View File

@@ -23,18 +23,18 @@
*
*/
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Relation:impacts/Description' => 'Elemente betroffen von',
'Relation:impacts/DownStream' => 'Auswirkung ...',
'Relation:impacts/DownStream+' => 'Elemente betroffen von',
'Relation:impacts/UpStream' => 'Hängt ab von ...',
'Relation:impacts/UpStream+' => 'Betroffene Elemente',
'Relation:impacts/Description' => 'Elemente betroffen von',
'Relation:impacts/DownStream' => 'Auswirkung ...',
'Relation:impacts/DownStream+' => 'Elemente betroffen von',
'Relation:impacts/UpStream' => 'Hängt ab von ...',
'Relation:impacts/UpStream+' => 'Betroffene Elemente',
// Legacy entries
'Relation:depends on/Description' => 'Elemente, von denen dieses Element abhängt.',
'Relation:depends on/DownStream' => 'Hängt ab von ...',
'Relation:depends on/UpStream' => 'Wirkt auf ...',
'Relation:impacts/LoadData' => 'Daten laden',
'Relation:impacts/FilteredData' => 'Daten sind über den Tab "Grafische Ansicht" gefiltert',
'Relation:impacts/NoFilteredData' => 'Bitte wählen Sie Objekte ~~',
'Relation:depends on/DownStream' => 'Hängt ab von ...',
'Relation:depends on/UpStream' => 'Wirkt auf ...',
'Relation:impacts/LoadData' => 'Daten laden',
'Relation:impacts/FilteredData' => 'Daten sind über den Tab "Grafische Ansicht" gefiltert',
'Relation:impacts/NoFilteredData' => 'Bitte wählen Sie Objekte im Tab "Grafische Ansicht"',
));

View File

@@ -27,18 +27,17 @@
//
Dict::Add('EN US', 'English', 'English', array(
'Relation:impacts/Description' => 'Elements impacted by',
'Relation:impacts/DownStream' => 'Impacts...',
'Relation:impacts/DownStream+' => 'Elements impacted by',
'Relation:impacts/UpStream' => 'Depends on......',
'Relation:impacts/UpStream+' => 'Elements impacting',
'Relation:impacts/Description' => 'Elements impacted by',
'Relation:impacts/DownStream' => 'Impacts...',
'Relation:impacts/DownStream+' => 'Elements impacted by',
'Relation:impacts/UpStream' => 'Depends on......',
'Relation:impacts/UpStream+' => 'Elements impacting',
// Legacy entries
'Relation:depends on/Description' => 'Elements impacting',
'Relation:depends on/DownStream' => 'Depends on...',
'Relation:depends on/UpStream' => 'Impacts...',
'Relation:depends on/DownStream' => 'Depends on...',
'Relation:depends on/UpStream' => 'Impacts...',
'Relation:impacts/LoadData' => 'Load data',
'Relation:impacts/NoFilteredData' => 'please select objects and load data',
'Relation:impacts/FilteredData' => 'Filtered data',
'Relation:impacts/NoFilteredData' => 'please select objects in Graphical view tag',
));

View File

@@ -27,18 +27,17 @@
//////////////////////////////////////////////////////////////////////
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Relation:impacts/Description' => 'Elementos Impactados por',
'Relation:impacts/DownStream' => 'Impacto...',
'Relation:impacts/DownStream+' => 'Elementos Impactados por',
'Relation:impacts/UpStream' => 'Depende de...',
'Relation:impacts/UpStream+' => 'Elementos de los cuales depende',
'Relation:impacts/Description' => 'Elementos Impactados por',
'Relation:impacts/DownStream' => 'Impacto...',
'Relation:impacts/DownStream+' => 'Elementos Impactados por',
'Relation:impacts/UpStream' => 'Depende de...',
'Relation:impacts/UpStream+' => 'Elementos de los cuales depende',
// Legacy entries
'Relation:depends on/Description' => 'Elementos de los cuales depende',
'Relation:depends on/DownStream' => 'Depende de...',
'Relation:depends on/UpStream' => 'Impactos...',
'Relation:impacts/LoadData' => 'Load data~~',
'Relation:impacts/NoFilteredData' => 'please select objects and load data~~',
'Relation:impacts/FilteredData' => 'Filtered data~~',
'Relation:depends on/DownStream' => 'Depende de...',
'Relation:depends on/UpStream' => 'Impactos...',
'Relation:impacts/LoadData' => 'Load data~~',
'Relation:impacts/NoFilteredData' => 'please select objects in Graphical view tag~~',
));

View File

@@ -23,18 +23,17 @@
// Class: Organization
//
Dict::Add('FR FR', 'French', 'Français', array(
'Relation:impacts/Description' => 'Eléments impactés par',
'Relation:impacts/DownStream' => 'Impacte...',
'Relation:impacts/DownStream+' => 'Eléments impactés par',
'Relation:impacts/UpStream' => 'Dépend de...',
'Relation:impacts/UpStream+' => 'Eléments dont dépend',
'Relation:impacts/Description' => 'Eléments impactés par',
'Relation:impacts/DownStream' => 'Impacte...',
'Relation:impacts/DownStream+' => 'Eléments impactés par',
'Relation:impacts/UpStream' => 'Dépend de...',
'Relation:impacts/UpStream+' => 'Eléments dont dépend',
// Legacy entries
'Relation:depends on/Description' => 'Eléments dont dépend',
'Relation:depends on/DownStream' => 'Dépend de...',
'Relation:depends on/UpStream' => 'Impacte...',
'Relation:impacts/LoadData' => 'Charger les données',
'Relation:impacts/NoFilteredData' => 'Veuillez sélectionner des objets et lancer le chargement des données',
'Relation:impacts/FilteredData' => 'Données filtrées',
'Relation:depends on/DownStream' => 'Dépend de...',
'Relation:depends on/UpStream' => 'Impacte...',
'Relation:impacts/LoadData' => 'Charger les données',
'Relation:impacts/NoFilteredData' => 'Veuillez sélectionner des objets dans l\'onglet Graph',
));

View File

@@ -20,18 +20,17 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'Relation:impacts/Description' => 'Konfigurációs elem működését befolyásolják',
'Relation:impacts/DownStream' => 'Hatás',
'Relation:impacts/DownStream+' => 'Konfigurációs elem működését befolyásolják',
'Relation:impacts/UpStream' => 'Függőségek',
'Relation:impacts/UpStream+' => 'Konfigurációs elemtől függnek',
'Relation:impacts/Description' => 'Konfigurációs elem működését befolyásolják',
'Relation:impacts/DownStream' => 'Hatás',
'Relation:impacts/DownStream+' => 'Konfigurációs elem működését befolyásolják',
'Relation:impacts/UpStream' => 'Függőségek',
'Relation:impacts/UpStream+' => 'Konfigurációs elemtől függnek',
// Legacy entries
'Relation:depends on/Description' => 'Konfigurációs elemtől függnek',
'Relation:depends on/DownStream' => 'Függőségek',
'Relation:depends on/UpStream' => 'Hatások',
'Relation:impacts/LoadData' => 'Adat betöltés',
'Relation:impacts/NoFilteredData' => 'please select objects and load data~~',
'Relation:impacts/FilteredData' => 'Filtered data~~',
'Relation:depends on/DownStream' => 'Függőségek',
'Relation:depends on/UpStream' => 'Hatások',
'Relation:impacts/LoadData' => 'Adat betöltés',
'Relation:impacts/NoFilteredData' => 'kérjük, válassza ki az objektumokat a grafikus nézetben',
));

View File

@@ -20,18 +20,17 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Relation:impacts/Description' => 'Elementi impattati da...',
'Relation:impacts/DownStream' => 'Impatto...',
'Relation:impacts/DownStream+' => 'Elementi impattati da...',
'Relation:impacts/UpStream' => 'Dipende da...',
'Relation:impacts/UpStream+' => 'Elementi di questo elemento dipende da',
'Relation:impacts/Description' => 'Elementi impattati da...',
'Relation:impacts/DownStream' => 'Impatto...',
'Relation:impacts/DownStream+' => 'Elementi impattati da...',
'Relation:impacts/UpStream' => 'Dipende da...',
'Relation:impacts/UpStream+' => 'Elementi di questo elemento dipende da',
// Legacy entries
'Relation:depends on/Description' => 'Elementi di questo elemento dipende da',
'Relation:depends on/DownStream' => 'Dipende da...',
'Relation:depends on/UpStream' => 'Impatto...',
'Relation:impacts/LoadData' => 'Load data~~',
'Relation:impacts/NoFilteredData' => 'please select objects and load data~~',
'Relation:impacts/FilteredData' => 'Filtered data~~',
'Relation:depends on/DownStream' => 'Dipende da...',
'Relation:depends on/UpStream' => 'Impatto...',
'Relation:impacts/LoadData' => 'Load data~~',
'Relation:impacts/NoFilteredData' => 'please select objects in Graphical view tag~~',
));

View File

@@ -20,18 +20,17 @@
* @licence http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('JA JP', 'Japanese', '日本語', array(
'Relation:impacts/Description' => 'インパクトを受ける要素',
'Relation:impacts/DownStream' => 'インパクト...',
'Relation:impacts/DownStream+' => 'インパクトを受ける要素',
'Relation:impacts/UpStream' => '依存...',
'Relation:impacts/UpStream+' => 'この要素が依存している要素',
'Relation:impacts/Description' => 'インパクトを受ける要素',
'Relation:impacts/DownStream' => 'インパクト...',
'Relation:impacts/DownStream+' => 'インパクトを受ける要素',
'Relation:impacts/UpStream' => '依存...',
'Relation:impacts/UpStream+' => 'この要素が依存している要素',
// Legacy entries
'Relation:depends on/Description' => 'この要素が依存している要素',
'Relation:depends on/DownStream' => '依存...',
'Relation:depends on/UpStream' => 'インパクト...',
'Relation:impacts/LoadData' => 'Load data~~',
'Relation:impacts/NoFilteredData' => 'please select objects and load data~~',
'Relation:impacts/FilteredData' => 'Filtered data~~',
'Relation:depends on/DownStream' => '依存...',
'Relation:depends on/UpStream' => 'インパクト...',
'Relation:impacts/LoadData' => 'Load data~~',
'Relation:impacts/NoFilteredData' => 'please select objects in Graphical view tag~~',
));

View File

@@ -32,18 +32,17 @@
//////////////////////////////////////////////////////////////////////
//
Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'Relation:impacts/Description' => 'Elementen met impact van',
'Relation:impacts/DownStream' => 'Impact op...',
'Relation:impacts/DownStream+' => 'Elementen geïmpacteerd door',
'Relation:impacts/UpStream' => 'Is afhankelijk van...',
'Relation:impacts/UpStream+' => 'Elementen met impact op',
'Relation:impacts/Description' => 'Elementen met impact van',
'Relation:impacts/DownStream' => 'Impact op...',
'Relation:impacts/DownStream+' => 'Elementen geïmpacteerd door',
'Relation:impacts/UpStream' => 'Is afhankelijk van...',
'Relation:impacts/UpStream+' => 'Elementen met impact op',
// Legacy entries
'Relation:depends on/Description' => 'Elementen afhankelijk van',
'Relation:depends on/DownStream' => 'Is afhankelijk van...',
'Relation:depends on/UpStream' => 'Impact op...',
'Relation:impacts/LoadData' => 'Load data~~',
'Relation:impacts/NoFilteredData' => 'please select objects and load data~~',
'Relation:impacts/FilteredData' => 'Filtered data~~',
'Relation:depends on/DownStream' => 'Is afhankelijk van...',
'Relation:depends on/UpStream' => 'Impact op...',
'Relation:impacts/LoadData' => 'Load data~~',
'Relation:impacts/NoFilteredData' => 'please select objects in Graphical view tag~~',
));

View File

@@ -25,18 +25,17 @@
//////////////////////////////////////////////////////////////////////
//
Dict::Add('PL PL', 'Polish', 'Polski', array(
'Relation:impacts/Description' => 'Elementy, na które ma wpływ',
'Relation:impacts/DownStream' => 'Wpływa na...',
'Relation:impacts/DownStream+' => 'Elementy, na które ma wpływ',
'Relation:impacts/UpStream' => 'Zależy od......',
'Relation:impacts/UpStream+' => 'Elementy wpływające',
'Relation:impacts/Description' => 'Elementy, na które ma wpływ',
'Relation:impacts/DownStream' => 'Wpływa na...',
'Relation:impacts/DownStream+' => 'Elementy, na które ma wpływ',
'Relation:impacts/UpStream' => 'Zależy od......',
'Relation:impacts/UpStream+' => 'Elementy wpływające',
// Legacy entries
'Relation:depends on/Description' => 'Elementy wpływające',
'Relation:depends on/DownStream' => 'Zależy od...',
'Relation:depends on/UpStream' => 'Wpływa na...',
'Relation:impacts/LoadData' => 'Załaduj dane',
'Relation:impacts/NoFilteredData' => 'please select objects and load data~~',
'Relation:impacts/FilteredData' => 'Filtered data~~',
'Relation:depends on/DownStream' => 'Zależy od...',
'Relation:depends on/UpStream' => 'Wpływa na...',
'Relation:impacts/LoadData' => 'Załaduj dane',
'Relation:impacts/NoFilteredData' => 'proszę wybrać obiekty w widoku graficznym',
));

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