mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-27 06:04:12 +01:00
Compare commits
255 Commits
saas/manag
...
saas/3.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e8c963c4d | ||
|
|
dcb40a832f | ||
|
|
998586a14c | ||
|
|
c61565101e | ||
|
|
d1ece12c44 | ||
|
|
fc2ba866f2 | ||
|
|
6a0695f347 | ||
|
|
8854f159a2 | ||
|
|
02a293c464 | ||
|
|
84f36eeebb | ||
|
|
ff9827ad17 | ||
|
|
bc5db3cd7d | ||
|
|
220be64f6e | ||
|
|
9a8e9a0b01 | ||
|
|
f6a7e6b4e1 | ||
|
|
2829fb291f | ||
|
|
6110abfc7f | ||
|
|
6046f44f56 | ||
|
|
6c6131ce03 | ||
|
|
178c922407 | ||
|
|
343e87a8d4 | ||
|
|
d7e5d6fb7f | ||
|
|
993606f102 | ||
|
|
44c189223e | ||
|
|
e76728b2bf | ||
|
|
f22921d8f8 | ||
|
|
3e258f32cc | ||
|
|
3c51d6fb98 | ||
|
|
7cfe1389aa | ||
|
|
7292a8540b | ||
|
|
f65c690462 | ||
|
|
ecf8bc42fa | ||
|
|
faba812fc1 | ||
|
|
add433d702 | ||
|
|
9c99cb35e5 | ||
|
|
9d392ad167 | ||
|
|
ea8509db1f | ||
|
|
df25ce76b6 | ||
|
|
e946fc65fc | ||
|
|
d203e075a8 | ||
|
|
dbe2f66539 | ||
|
|
235c63147f | ||
|
|
5485b0d9b0 | ||
|
|
cc56413ee7 | ||
|
|
b3820560f1 | ||
|
|
144ca490ed | ||
|
|
70d271fcb1 | ||
|
|
7443fdd525 | ||
|
|
ff351d6b4b | ||
|
|
6f6b385550 | ||
|
|
1d525e92e8 | ||
|
|
f25e9045c9 | ||
|
|
0d8ff7bbac | ||
|
|
0b5a9aa5da | ||
|
|
03568f2fa5 | ||
|
|
965659cdb4 | ||
|
|
d07747d82d | ||
|
|
15900720c8 | ||
|
|
6d3f7f4976 | ||
|
|
6dc88c372b | ||
|
|
48eb022824 | ||
|
|
03c9ffc033 | ||
|
|
61a9a4ac65 | ||
|
|
38962e68ee | ||
|
|
483dbb4a5d | ||
|
|
1f4dcc4f9e | ||
|
|
e86309669e | ||
|
|
6d6f55acf7 | ||
|
|
6ebcd44bb1 | ||
|
|
f8fb51fea0 | ||
|
|
bf768311c2 | ||
|
|
d797436786 | ||
|
|
b508c0d983 | ||
|
|
351893bbdd | ||
|
|
59e4bb028f | ||
|
|
6d895371ec | ||
|
|
ab91631e68 | ||
|
|
3366bae0ab | ||
|
|
03b484c349 | ||
|
|
ebab4f8f96 | ||
|
|
ac792e7a4c | ||
|
|
70081ecf33 | ||
|
|
575ba1cd7b | ||
|
|
d130959692 | ||
|
|
a8c689c6c0 | ||
|
|
1990ccb5d8 | ||
|
|
e107be56e4 | ||
|
|
0f8e87e001 | ||
|
|
d92d2b5e9e | ||
|
|
ebd0136773 | ||
|
|
f6653e1594 | ||
|
|
65bb76b9e3 | ||
|
|
f238593966 | ||
|
|
d951d3b872 | ||
|
|
ccceb870e3 | ||
|
|
ed6df77cbb | ||
|
|
1ad28312ec | ||
|
|
f002aa04cd | ||
|
|
b86d70623e | ||
|
|
fe3467309d | ||
|
|
851ab9c356 | ||
|
|
aef3c2e609 | ||
|
|
5212e15cc4 | ||
|
|
f04fc546b5 | ||
|
|
204a6d8e51 | ||
|
|
caf3076b12 | ||
|
|
7670c8723a | ||
|
|
c4c400d852 | ||
|
|
6cc4cc4fb6 | ||
|
|
d7495af207 | ||
|
|
4c26a7a682 | ||
|
|
13ad98b9b3 | ||
|
|
4be54fdd65 | ||
|
|
6d13397ba1 | ||
|
|
48e7e0309a | ||
|
|
bcb110d99e | ||
|
|
446a13ad72 | ||
|
|
87ecbe9d37 | ||
|
|
5bceee3bdb | ||
|
|
1378afbfa2 | ||
|
|
2ce9b2afaf | ||
|
|
d64a91d4ce | ||
|
|
c0c8a13864 | ||
|
|
5ffa41bc16 | ||
|
|
d2eef06276 | ||
|
|
77b14c516e | ||
|
|
880a824f2f | ||
|
|
f7f1b5f399 | ||
|
|
18efbfa803 | ||
|
|
7aa478d6ff | ||
|
|
97700dbf15 | ||
|
|
c25c69d746 | ||
|
|
734a788340 | ||
|
|
eb1eb15791 | ||
|
|
a84077782d | ||
|
|
26048150d3 | ||
|
|
e5b6e2eb8c | ||
|
|
87b6ea4def | ||
|
|
72873a3343 | ||
|
|
5ef25ccb77 | ||
|
|
1682a85cc0 | ||
|
|
cd9beec313 | ||
|
|
8295eaed90 | ||
|
|
86a7cefa68 | ||
|
|
829b648dd2 | ||
|
|
5475b9fbbe | ||
|
|
6f8e7c7002 | ||
|
|
67ca554261 | ||
|
|
f89953f39e | ||
|
|
772368ef8a | ||
|
|
2e049aa244 | ||
|
|
a57b6471c9 | ||
|
|
bc7c1b4744 | ||
|
|
12c78697f4 | ||
|
|
046e857768 | ||
|
|
4d8246c4d8 | ||
|
|
5c61d725e1 | ||
|
|
0c7195f1a3 | ||
|
|
00b070b3cf | ||
|
|
2c4cad4dac | ||
|
|
9c37d5c23e | ||
|
|
89145593ef | ||
|
|
b2e80d37dd | ||
|
|
6432678de9 | ||
|
|
952194b385 | ||
|
|
bfb452dd69 | ||
|
|
64baeba1c7 | ||
|
|
71ed784c60 | ||
|
|
da45651121 | ||
|
|
d388ce9a06 | ||
|
|
47e71d8838 | ||
|
|
2b5973ec67 | ||
|
|
e58918f53e | ||
|
|
125715af3f | ||
|
|
ea8e7c5131 | ||
|
|
06e5e0b102 | ||
|
|
5247f5b3ea | ||
|
|
78396d8e4a | ||
|
|
f1ee22cbed | ||
|
|
39305468f8 | ||
|
|
32fd75bc4b | ||
|
|
efadf2cc79 | ||
|
|
40d63a2fa4 | ||
|
|
baa6dedbcf | ||
|
|
556b9ad89a | ||
|
|
9afc22bd8f | ||
|
|
ef0b0f88c9 | ||
|
|
a010239efb | ||
|
|
264a8cd70a | ||
|
|
aa1834170b | ||
|
|
f94d67ab35 | ||
|
|
3048c8c41f | ||
|
|
246e4a9f50 | ||
|
|
58f4d3b53c | ||
|
|
427fc6f9f9 | ||
|
|
07eadb3ea7 | ||
|
|
97f4818076 | ||
|
|
ad46d47e21 | ||
|
|
cd3f7d7ead | ||
|
|
c6b203fc4e | ||
|
|
b8d04e40e4 | ||
|
|
d8c7888eac | ||
|
|
9d6f4569ef | ||
|
|
1e41e805a2 | ||
|
|
bd1e4389f7 | ||
|
|
b059fb72a2 | ||
|
|
1b3b2e8a69 | ||
|
|
6b448e29f5 | ||
|
|
7cb6af0a2b | ||
|
|
ddc9952ec1 | ||
|
|
a1a9ffe192 | ||
|
|
7728082c00 | ||
|
|
970183ef45 | ||
|
|
6c2db1e687 | ||
|
|
32d74fbc8e | ||
|
|
477f2f51e9 | ||
|
|
94ea8e60e8 | ||
|
|
b9a00b15f5 | ||
|
|
e87f5af465 | ||
|
|
97d717b016 | ||
|
|
251fd3c67b | ||
|
|
7b0a569c64 | ||
|
|
7176bc8686 | ||
|
|
9c0b906ded | ||
|
|
0533916dad | ||
|
|
60b08586c2 | ||
|
|
28df2942e4 | ||
|
|
cd48d2ad37 | ||
|
|
9db2205241 | ||
|
|
045985cd5b | ||
|
|
809b371520 | ||
|
|
9bbc7342b8 | ||
|
|
973c435138 | ||
|
|
1c3dfd6491 | ||
|
|
9400b697eb | ||
|
|
49748a0374 | ||
|
|
163276a6c2 | ||
|
|
9b0c2f7324 | ||
|
|
e1807f598f | ||
|
|
02e63fff64 | ||
|
|
0864f05d9f | ||
|
|
1bbcd9656a | ||
|
|
34d8e52c22 | ||
|
|
ac7309e48c | ||
|
|
c8fade6013 | ||
|
|
ad052dd861 | ||
|
|
a5ea868609 | ||
|
|
0b03b3ef4d | ||
|
|
174cace20a | ||
|
|
e3f5dbfc80 | ||
|
|
40e24c25a2 | ||
|
|
c6e4466c53 | ||
|
|
6638eb4adc | ||
|
|
eb40968e34 | ||
|
|
0001e8ffc4 |
Binary file not shown.
|
After Width: | Height: | Size: 3.0 MiB |
@@ -62,6 +62,12 @@ 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).
|
||||
|
||||
@@ -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:
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -2246,3 +2246,70 @@ interface iModuleExtension
|
||||
*/
|
||||
public function __construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to provide messages to be displayed in the "Welcome Popup"
|
||||
*
|
||||
* @api
|
||||
* @private
|
||||
* @since 3.1.0
|
||||
*/
|
||||
interface iWelcomePopup
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* KPI logging extensibility point
|
||||
*
|
||||
* KPI Logger extension
|
||||
*/
|
||||
interface iKPILoggerExtension
|
||||
{
|
||||
/**
|
||||
* Init the statistics collected
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function InitStats();
|
||||
|
||||
/**
|
||||
* Add a new KPI to the stats
|
||||
*
|
||||
* @param \Combodo\iTop\Core\Kpi\KpiLogData $oKpiLogData
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function LogOperation($oKpiLogData);
|
||||
}
|
||||
|
||||
@@ -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())));
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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" => "",
|
||||
|
||||
@@ -1166,7 +1166,7 @@ HTML
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param \CMDBObjectSet $oSet
|
||||
* @param array $aExtraParams
|
||||
* @param array $aExtraParams See possible values in {@see DataTableUIBlockFactory::RenderDataTable()}
|
||||
*
|
||||
* @throws \ApplicationException
|
||||
* @throws \CoreException
|
||||
@@ -4547,7 +4547,9 @@ 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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4562,13 +4564,16 @@ HTML;
|
||||
|
||||
protected function DBCloneTracked_Internal($newKey = null)
|
||||
{
|
||||
$oNewObj = parent::DBCloneTracked_Internal($newKey);
|
||||
/** @var cmdbAbstractObject $oNewObj */
|
||||
$oNewObj = MetaModel::GetObject(get_class($this), 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;
|
||||
@@ -4605,7 +4610,9 @@ 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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4649,7 +4656,9 @@ HTML;
|
||||
/** @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);
|
||||
@@ -4668,7 +4677,10 @@ HTML;
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
if ($oExtensionInstance->OnIsModified($this)) {
|
||||
$oKPI = new ExecutionKPI();
|
||||
$bIsModified = $oExtensionInstance->OnIsModified($this);
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnIsModified');
|
||||
if ($bIsModified) {
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified() -> true");
|
||||
return true;
|
||||
} else {
|
||||
@@ -4724,7 +4736,9 @@ 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);
|
||||
@@ -4772,7 +4786,9 @@ 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);
|
||||
|
||||
@@ -918,6 +918,11 @@ 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(), '=');
|
||||
@@ -929,7 +934,7 @@ class RuntimeDashboard extends Dashboard
|
||||
$sDashboardDefinition = $oUserDashboard->Get('contents');
|
||||
$bCustomized = true;
|
||||
} else {
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFile);
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
|
||||
}
|
||||
|
||||
|
||||
@@ -937,7 +942,7 @@ class RuntimeDashboard extends Dashboard
|
||||
$oDashboard = new RuntimeDashboard($sDashBoardId);
|
||||
$oDashboard->FromXml($sDashboardDefinition);
|
||||
$oDashboard->SetCustomFlag($bCustomized);
|
||||
$oDashboard->SetDefinitionFile($sDashboardFile);
|
||||
$oDashboard->SetDefinitionFile($sDashboardFileSanitized);
|
||||
} else {
|
||||
$oDashboard = null;
|
||||
}
|
||||
@@ -1136,7 +1141,7 @@ JS
|
||||
$oToolbar->AddSubBlock($oActionButton);
|
||||
|
||||
$aActions = array();
|
||||
$sFile = addslashes($this->sDefinitionFile);
|
||||
$sFile = addslashes(utils::LocalPath($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)");
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -1246,6 +1246,10 @@ 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;
|
||||
}
|
||||
|
||||
@@ -248,6 +248,7 @@ 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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
/**
|
||||
@@ -103,7 +104,7 @@ class ApplicationMenu
|
||||
{
|
||||
self::$sFavoriteSiloQuery = $sOQL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the query used to limit the list of displayed organizations in the drop-down menu
|
||||
* @return string The OQL query returning a list of Organization objects
|
||||
@@ -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::Debug('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,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -536,7 +548,7 @@ EOF
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the currently active menu (if any, otherwise the first menu is the default)
|
||||
* @return string The Id of the currently active menu
|
||||
@@ -544,7 +556,7 @@ EOF
|
||||
public static function GetActiveNodeId()
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sMenuId = $oAppContext->GetCurrentValue('menu', null);
|
||||
$sMenuId = $oAppContext->GetCurrentValue('menu', null);
|
||||
if ($sMenuId === null)
|
||||
{
|
||||
$sMenuId = self::GetDefaultMenuId();
|
||||
@@ -654,7 +666,7 @@ abstract class MenuNode
|
||||
|
||||
/**
|
||||
* Stimulus to check: if the user can 'apply' this stimulus, then she/he can see this menu
|
||||
*/
|
||||
*/
|
||||
protected $m_aEnableStimuli;
|
||||
|
||||
/**
|
||||
@@ -814,7 +826,7 @@ abstract class MenuNode
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a limiting display condition for the same menu node. The conditions will be combined with a AND
|
||||
* @param $oMenuNode MenuNode Another definition of the same menu node, with potentially different access restriction
|
||||
@@ -987,7 +999,7 @@ class TemplateMenuNode extends MenuNode
|
||||
* @var string
|
||||
*/
|
||||
protected $sTemplateFile;
|
||||
|
||||
|
||||
/**
|
||||
* Create a menu item based on a custom template and inserts it into the application's main menu
|
||||
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
|
||||
@@ -1058,7 +1070,7 @@ class OQLMenuNode extends MenuNode
|
||||
* @var bool|null
|
||||
*/
|
||||
protected $bSearchFormOpen;
|
||||
|
||||
|
||||
/**
|
||||
* Extra parameters to be passed to the display block to fine tune its appearence
|
||||
*/
|
||||
@@ -1091,7 +1103,7 @@ class OQLMenuNode extends MenuNode
|
||||
// Enhancement: we could set as the "enable" condition that the user has enough rights to "read" the objects
|
||||
// of the class specified by the OQL...
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set some extra parameters to be passed to the display block to fine tune its appearence
|
||||
* @param array $aParams paramCode => value. See DisplayBlock::GetDisplay for the meaning of the parameters
|
||||
@@ -1111,7 +1123,7 @@ class OQLMenuNode extends MenuNode
|
||||
*/
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
ContextTag::AddContext(ContextTag::TAG_OBJECT_SEARCH);
|
||||
$oTag = new ContextTag(ContextTag::TAG_OBJECT_SEARCH);
|
||||
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
|
||||
OQLMenuNode::RenderOQLSearch
|
||||
(
|
||||
@@ -1120,7 +1132,7 @@ class OQLMenuNode extends MenuNode
|
||||
'Menu_'.$this->GetMenuId(),
|
||||
$this->bSearch, // Search pane
|
||||
$this->bSearchFormOpen, // Search open
|
||||
$oPage,
|
||||
$oPage,
|
||||
array_merge($this->m_aParams, $aExtraParams),
|
||||
true
|
||||
);
|
||||
@@ -1354,10 +1366,10 @@ class NewObjectMenuNode extends MenuNode
|
||||
{
|
||||
// Enable this menu, only if the current user has enough rights to create such an object, or an object of
|
||||
// any child class
|
||||
|
||||
|
||||
$aSubClasses = MetaModel::EnumChildClasses($this->sClass, ENUM_CHILD_CLASSES_ALL); // Including the specified class itself
|
||||
$bActionIsAllowed = false;
|
||||
|
||||
|
||||
foreach($aSubClasses as $sCandidateClass)
|
||||
{
|
||||
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
|
||||
@@ -1366,7 +1378,7 @@ class NewObjectMenuNode extends MenuNode
|
||||
break; // Enough for now
|
||||
}
|
||||
}
|
||||
return $bActionIsAllowed;
|
||||
return $bActionIsAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1508,7 +1520,7 @@ class DashboardMenuNode extends MenuNode
|
||||
throw new Exception("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1549,7 +1561,7 @@ class ShortcutContainerMenuNode extends MenuNode
|
||||
$sName = $this->GetMenuId().'_'.$oShortcut->GetKey();
|
||||
new ShortcutMenuNode($sName, $oShortcut, $this->GetIndex(), $fRank++);
|
||||
}
|
||||
|
||||
|
||||
// Complete the tree
|
||||
//
|
||||
parent::PopulateChildMenus();
|
||||
|
||||
@@ -296,7 +296,7 @@ class QueryOQL extends Query
|
||||
}
|
||||
catch
|
||||
(OQLException $e) {
|
||||
$oAlert = AlertUIBlockFactory::MakeForFailure(Dict::Format('UI:RunQuery:Error'), $e->getHtmlDesc())
|
||||
$oAlert = AlertUIBlockFactory::MakeForFailure(Dict::S('UI:RunQuery:Error'), $e->getHtmlDesc())
|
||||
->SetIsClosable(false)
|
||||
->SetIsCollapsible(false);
|
||||
$oAlert->AddCSSClass('mb-5');
|
||||
|
||||
@@ -99,4 +99,10 @@ else
|
||||
Session::Set('itop_env', ITOP_DEFAULT_ENV);
|
||||
}
|
||||
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
||||
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
|
||||
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', []);
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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_BULK_MODIFY) && $bAllowTargetCreation);
|
||||
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_MODIFY) && $bAllowTargetCreation);
|
||||
$bExtensions = true;
|
||||
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
|
||||
|
||||
@@ -178,17 +178,18 @@ class UILinksWidget
|
||||
|
||||
$oDisplayBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
$oBlock->AddSubBlock($oDisplayBlock->GetDisplay($oPage, "SearchFormToAdd_{$sLinkedSetId}",
|
||||
array(
|
||||
'menu' => false,
|
||||
[
|
||||
'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,
|
||||
)));
|
||||
'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,
|
||||
]));
|
||||
|
||||
$oBlock->AddForm();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
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;
|
||||
@@ -1396,13 +1397,23 @@ 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 APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/';
|
||||
return static::GetDataPath().'cache-'.MetaModel::GetEnvironment().'/';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2265,24 +2276,7 @@ SQL;
|
||||
*/
|
||||
public static function GetCurrentModuleName($iCallDepth = 0)
|
||||
{
|
||||
$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;
|
||||
return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2304,24 +2298,7 @@ SQL;
|
||||
*/
|
||||
public static function 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;
|
||||
return ModuleService::GetInstance()->GetCurrentModuleDir($iCallDepth);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2336,12 +2313,7 @@ SQL;
|
||||
*/
|
||||
public static function GetCurrentModuleUrl()
|
||||
{
|
||||
$sDir = static::GetCurrentModuleDir(1);
|
||||
if ( $sDir !== '')
|
||||
{
|
||||
return static::GetAbsoluteUrlModulesRoot().'/'.$sDir;
|
||||
}
|
||||
return '';
|
||||
return ModuleService::GetInstance()->GetCurrentModuleUrl(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2351,8 +2323,7 @@ SQL;
|
||||
*/
|
||||
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
|
||||
{
|
||||
$sModuleName = static::GetCurrentModuleName(1);
|
||||
return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultvalue);
|
||||
return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2361,12 +2332,7 @@ SQL;
|
||||
*/
|
||||
public static function GetCompiledModuleVersion($sModuleName)
|
||||
{
|
||||
$aModulesInfo = GetModulesInfo();
|
||||
if (array_key_exists($sModuleName, $aModulesInfo))
|
||||
{
|
||||
return $aModulesInfo[$sModuleName]['version'];
|
||||
}
|
||||
return null;
|
||||
return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2691,24 +2657,26 @@ SQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local path relative to the iTop installation of an existing file
|
||||
* Returns the local path relative to the iTop installation (APPROOT or the given base path)
|
||||
* 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
|
||||
* @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
|
||||
*/
|
||||
final public static function LocalPath($sAbsolutePath)
|
||||
final public static function LocalPath($sAbsolutePath, string $sBasePath = APPROOT)
|
||||
{
|
||||
$sRootPath = realpath(APPROOT);
|
||||
$sRootPath = realpath($sBasePath);
|
||||
$sFullPath = realpath($sAbsolutePath);
|
||||
if (($sFullPath === false) || !self::StartsWith($sFullPath, $sRootPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$sLocalPath = substr($sFullPath, strlen($sRootPath.DIRECTORY_SEPARATOR));
|
||||
$sLocalPath = str_replace(DIRECTORY_SEPARATOR, '/', $sLocalPath);
|
||||
return $sLocalPath;
|
||||
|
||||
return str_replace(DIRECTORY_SEPARATOR, '/', $sLocalPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2900,7 +2868,7 @@ HTML;
|
||||
|
||||
// Add already loaded classes
|
||||
$aCurrentClasses = array_fill_keys(get_declared_classes(), '');
|
||||
$aClassMap = array_merge($aClassMap, $aCurrentClasses);
|
||||
$aClassMap = array_merge($aCurrentClasses, $aClassMap);
|
||||
|
||||
foreach ($aClassMap as $sPHPClass => $sPHPFile) {
|
||||
$bSkipped = false;
|
||||
@@ -2925,11 +2893,12 @@ HTML;
|
||||
$bSkipped = true; // file not found
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!$bSkipped){
|
||||
try {
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable()) {
|
||||
if ($oRefClass->implementsInterface($sInterface) &&
|
||||
!$oRefClass->isInterface() && !$oRefClass->isAbstract() && !$oRefClass->isTrait()) {
|
||||
$aMatchingClasses[] = $sPHPClass;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
|
||||
@@ -45,6 +45,7 @@ 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';
|
||||
|
||||
@@ -59,9 +59,16 @@ 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
|
||||
*/
|
||||
public static function SetDbConnectionMockForQuery(?mysqli $oMysqli): void
|
||||
public static function SetDbConnectionMockForQuery(?mysqli $oMysqli = null): void
|
||||
{
|
||||
static::$oDbCnxMockableForQuery = $oMysqli;
|
||||
if (is_null($oMysqli)) {
|
||||
// Reset to standard connection
|
||||
static::$oDbCnxMockableForQuery = static::$oDbCnxStandard;
|
||||
}
|
||||
else {
|
||||
static::$oDbCnxMockableForQuery = $oMysqli;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,6 +419,7 @@ class MyHelpers
|
||||
//}
|
||||
return $sOutput;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -523,5 +524,3 @@ class Str
|
||||
return (strtolower($sString) == $sString);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -650,6 +650,9 @@ 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'];
|
||||
|
||||
@@ -431,6 +431,7 @@ class CMDBSource
|
||||
{
|
||||
self::$m_sDBName = '';
|
||||
}
|
||||
self::_TablesInfoCacheReset(); // reset the table info cache!
|
||||
}
|
||||
|
||||
public static function CreateTable($sQuery)
|
||||
@@ -607,8 +608,9 @@ class CMDBSource
|
||||
{
|
||||
self::LogDeadLock($e, true);
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
}
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||
} finally {
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||
}
|
||||
if ($oResult === false) {
|
||||
$aContext = array('query' => $sSql);
|
||||
|
||||
@@ -626,18 +628,24 @@ 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)
|
||||
private static function LogDeadLock(Exception $e, $bForQuery = false, $bCheckMysqliErrno = true)
|
||||
{
|
||||
// checks MySQL error code
|
||||
$iMySqlErrorNo = DbConnectionWrapper::GetDbConnection($bForQuery)->errno;
|
||||
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK))) {
|
||||
return;
|
||||
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";
|
||||
}
|
||||
|
||||
// Get error info
|
||||
@@ -664,7 +672,10 @@ class CMDBSource
|
||||
);
|
||||
DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext);
|
||||
|
||||
IssueLog::Error($sMessage, LogChannels::DEADLOCK, [$e->getMessage()]);
|
||||
IssueLog::Error($sMessage, LogChannels::DEADLOCK, [
|
||||
'exception.class' => get_class($e),
|
||||
'exception.message' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,7 +29,7 @@ define('ITOP_APPLICATION_SHORT', 'iTop');
|
||||
*
|
||||
* @see ITOP_CORE_VERSION to get iTop core version
|
||||
*/
|
||||
define('ITOP_VERSION', '3.1.0-dev');
|
||||
define('ITOP_VERSION', '3.1.1-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' => array(
|
||||
'email_transport_smtp.allow_self_signed' => [
|
||||
'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' => array(
|
||||
],
|
||||
'email_transport_smtp.verify_peer' => [
|
||||
'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,6 +1069,14 @@ 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.',
|
||||
@@ -1185,6 +1193,30 @@ 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).',
|
||||
@@ -1345,6 +1377,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',
|
||||
@@ -1627,6 +1667,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)',
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is only here for compatibility issues. Will be removed in iTop 3.1.0 (N°3664)
|
||||
* This file is only here for compatibility reasons.
|
||||
* It will be removed in future iTop versions (N°6533)
|
||||
*
|
||||
* @deprecated 3.0.0 N°3663 Exception classes were moved to `/application/exceptions`, use autoloader instead of require !
|
||||
*/
|
||||
require_once '../approot.inc.php';
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('Classes were moved to /application/exceptions');
|
||||
|
||||
require_once __DIR__ . '/../approot.inc.php';
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('Classes were moved to /application/exceptions and can be used directly with the autoloader');
|
||||
|
||||
@@ -188,8 +188,8 @@ final class ItopCounter
|
||||
|
||||
if (!$hDBLink)
|
||||
{
|
||||
throw new Exception("Could not connect to the DB server (host=$sDBHost, user=$sDBUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
|
||||
}
|
||||
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
|
||||
}
|
||||
|
||||
return $hDBLink;
|
||||
}
|
||||
|
||||
@@ -219,6 +219,19 @@
|
||||
<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>
|
||||
|
||||
@@ -59,7 +59,7 @@ require_once('mutex.class.inc.php');
|
||||
|
||||
|
||||
/**
|
||||
* A persistent object, as defined by the metamodel
|
||||
* A persistent object, as defined by the metamodel
|
||||
*
|
||||
* @package iTopORM
|
||||
* @api
|
||||
@@ -307,9 +307,9 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* Whether the object is already persisted in DB or not.
|
||||
*
|
||||
*
|
||||
* @api
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function IsNew()
|
||||
@@ -319,9 +319,9 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* Returns an Id for memory objects
|
||||
*
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
*
|
||||
* @param string $sClass
|
||||
*
|
||||
* @return int
|
||||
@@ -358,7 +358,7 @@ abstract class DBObject implements iDisplay
|
||||
$sRet .= "<b title=\"$sRootClass\">$sClass</b>::$iPKey ($sFriendlyname)<br/>\n";
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Alias of DBObject::Reload()
|
||||
*
|
||||
@@ -381,7 +381,7 @@ abstract class DBObject implements iDisplay
|
||||
*
|
||||
* @internal
|
||||
* @see m_bFullyLoaded
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
* @throws CoreException
|
||||
*/
|
||||
@@ -504,7 +504,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
$aAttList = $aAttToLoad[$sClassAlias];
|
||||
}
|
||||
|
||||
|
||||
foreach($aAttList as $sAttCode=>$oAttDef)
|
||||
{
|
||||
// Skip links (could not be loaded by the mean of this query)
|
||||
@@ -564,7 +564,7 @@ abstract class DBObject implements iDisplay
|
||||
$bFullyLoaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Load extended data
|
||||
if ($aExtendedDataSpec != null)
|
||||
{
|
||||
@@ -588,7 +588,7 @@ abstract class DBObject implements iDisplay
|
||||
*
|
||||
* @internal
|
||||
* @see Set()
|
||||
*
|
||||
*
|
||||
* @param string $sAttCode
|
||||
* @param mixed $value
|
||||
*/
|
||||
@@ -793,11 +793,11 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* Get the label of an attribute.
|
||||
*
|
||||
*
|
||||
* Shortcut to the field's AttributeDefinition->GetLabel()
|
||||
*
|
||||
* @api
|
||||
*
|
||||
*
|
||||
* @param string $sAttCode
|
||||
*
|
||||
* @return string
|
||||
@@ -870,7 +870,7 @@ abstract class DBObject implements iDisplay
|
||||
*
|
||||
* @internal
|
||||
* @see Get
|
||||
*
|
||||
*
|
||||
* @param string $sAttCode
|
||||
*
|
||||
* @return int|mixed|null
|
||||
@@ -975,7 +975,7 @@ abstract class DBObject implements iDisplay
|
||||
* Returns the default value of the $sAttCode.
|
||||
*
|
||||
* Returns the default value of the given attribute.
|
||||
*
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @param string $sAttCode
|
||||
@@ -996,12 +996,12 @@ abstract class DBObject implements iDisplay
|
||||
* @internal
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
*/
|
||||
public function GetExtendedData()
|
||||
{
|
||||
return $this->m_aExtendedData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the HighlightCode
|
||||
*
|
||||
@@ -1023,7 +1023,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
$fCurrentRank = $aHighlightScale[$this->m_sHighlightCode]['rank'];
|
||||
}
|
||||
|
||||
|
||||
if (array_key_exists($sCode, $aHighlightScale))
|
||||
{
|
||||
$fRank = $aHighlightScale[$sCode]['rank'];
|
||||
@@ -1033,13 +1033,13 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the current HighlightCode
|
||||
*
|
||||
*
|
||||
* @internal
|
||||
* @used-by DBObject::ComputeHighlightCode()
|
||||
*
|
||||
*
|
||||
* @return string|null The Hightlight code (null if none set, meaning rank = 0)
|
||||
*/
|
||||
protected function GetHighlightCode()
|
||||
@@ -1088,7 +1088,7 @@ abstract class DBObject implements iDisplay
|
||||
* corresponding to the external key and getting the value from it
|
||||
*
|
||||
* UNUSED ?
|
||||
*
|
||||
*
|
||||
* @internal
|
||||
* @todo: check if this is dead code.
|
||||
*
|
||||
@@ -1152,12 +1152,14 @@ abstract class DBObject implements iDisplay
|
||||
return; //skip!
|
||||
}
|
||||
$this->FireEventComputeValues();
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->ComputeValues();
|
||||
$oKPI->ComputeStatsForExtension($this, 'ComputeValues');
|
||||
}
|
||||
|
||||
/**
|
||||
* @api
|
||||
*
|
||||
*
|
||||
* @param string $sAttCode
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
@@ -1203,11 +1205,11 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* Get the value as it must be in the edit areas (forms)
|
||||
*
|
||||
*
|
||||
* Makes a raw text representation of the value.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
*
|
||||
* @param string $sAttCode
|
||||
*
|
||||
* @return int|mixed|string
|
||||
@@ -1237,7 +1239,7 @@ abstract class DBObject implements iDisplay
|
||||
else
|
||||
{
|
||||
$sEditValue = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1253,14 +1255,14 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* Get $sAttCode formatted as XML
|
||||
*
|
||||
*
|
||||
* The returned value is a text that is suitable for insertion into an XML node.
|
||||
* Depending on the type of attribute, the returned text is either:
|
||||
* * A literal, with XML entities already escaped,
|
||||
* * XML
|
||||
*
|
||||
* @api
|
||||
*
|
||||
*
|
||||
* @param string $sAttCode
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
@@ -1298,10 +1300,10 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @see GetAsHTML()
|
||||
* @see GetOriginal()
|
||||
*
|
||||
*
|
||||
* @param string $sAttCode
|
||||
* @param bool $bLocalize
|
||||
*
|
||||
@@ -1476,7 +1478,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
*
|
||||
* @param string $sClass
|
||||
*
|
||||
* @return mixed
|
||||
@@ -1531,7 +1533,7 @@ abstract class DBObject implements iDisplay
|
||||
* Get the id
|
||||
*
|
||||
* @api
|
||||
*
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function GetKey()
|
||||
@@ -1542,7 +1544,7 @@ abstract class DBObject implements iDisplay
|
||||
/**
|
||||
* Primary key Setter
|
||||
* Usable only for not yet persisted DBObjects
|
||||
*
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @param int $iNewKey the desired identifier
|
||||
@@ -1555,7 +1557,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
throw new CoreException("An object id must be an integer value ($iNewKey)");
|
||||
}
|
||||
|
||||
|
||||
if ($this->m_bIsInDB && !empty($this->m_iKey) && ($this->m_iKey != $iNewKey))
|
||||
{
|
||||
throw new CoreException("Changing the key ({$this->m_iKey} to $iNewKey) on an object (class {".get_class($this).") wich already exists in the Database");
|
||||
@@ -1565,7 +1567,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* Get the icon representing this object
|
||||
*
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param boolean $bImgTag If true the result is a full IMG tag (or an empty string if no icon is defined)
|
||||
@@ -1655,7 +1657,7 @@ abstract class DBObject implements iDisplay
|
||||
*
|
||||
* Returns the label as defined in the dictionary for the language of the current user
|
||||
*
|
||||
* @api
|
||||
* @api
|
||||
*
|
||||
* @return string (empty for default name scheme)
|
||||
*/
|
||||
@@ -1722,7 +1724,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* Helper to get the state
|
||||
*
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @return mixed|string '' if no state attribute, object representing its value otherwise
|
||||
@@ -1744,9 +1746,9 @@ abstract class DBObject implements iDisplay
|
||||
/**
|
||||
* Get the label (raw text) of the current state
|
||||
* helper for MetaModel::GetStateLabel()
|
||||
*
|
||||
*
|
||||
* @api
|
||||
*
|
||||
*
|
||||
* @return mixed|string
|
||||
*
|
||||
* @throws ArchivedObjectException
|
||||
@@ -1793,7 +1795,7 @@ abstract class DBObject implements iDisplay
|
||||
* Define attributes read-only from the end-user perspective
|
||||
*
|
||||
* @return array|null List of attcodes
|
||||
*/
|
||||
*/
|
||||
public static function GetReadOnlyAttributes()
|
||||
{
|
||||
return null;
|
||||
@@ -1802,14 +1804,14 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* Get predefined objects
|
||||
*
|
||||
*
|
||||
* The predefined objects will be synchronized with the DB at each install/upgrade
|
||||
* As soon as a class has predefined objects, then nobody can create nor delete objects
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return array An array of id => array of attcode => php value(so-called "real value": integer, string, ormDocument, DBObjectSet, etc.)
|
||||
*/
|
||||
*/
|
||||
public static function GetPredefinedObjects()
|
||||
{
|
||||
return null;
|
||||
@@ -1938,7 +1940,7 @@ abstract class DBObject implements iDisplay
|
||||
* Note: Attributes (and flags) from the target state and the transition are combined.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
*
|
||||
* @param string $sStimulus
|
||||
* @param string $sOriginState Default is current state
|
||||
*
|
||||
@@ -2145,7 +2147,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*
|
||||
@@ -2488,7 +2490,6 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
$this->m_aCheckIssues = array();
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
if ($bDoComputeValues) {
|
||||
$this->DoComputeValues();
|
||||
}
|
||||
@@ -2498,8 +2499,9 @@ abstract class DBObject implements iDisplay
|
||||
$this->FireEventCheckToWrite();
|
||||
$this->SetReadWrite();
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->DoCheckToWrite();
|
||||
$oKPI->ComputeStats('CheckToWrite', get_class($this));
|
||||
$oKPI->ComputeStatsForExtension($this, 'DoCheckToWrite');
|
||||
if (count($this->m_aCheckIssues) == 0)
|
||||
{
|
||||
$this->m_bCheckStatus = true;
|
||||
@@ -2517,7 +2519,7 @@ abstract class DBObject implements iDisplay
|
||||
*
|
||||
* an array of displayable error is added in {@see DBObject::$m_aDeleteIssues}
|
||||
*
|
||||
* @internal
|
||||
* @internal
|
||||
*
|
||||
* @param \DeletionPlan $oDeletionPlan
|
||||
*
|
||||
@@ -2634,7 +2636,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
// The value is a scalar, the comparison must be 100% strict
|
||||
if($this->m_aOrigValues[$sAtt] !== $proposedValue)
|
||||
{
|
||||
{
|
||||
//echo "$sAtt:<pre>\n";
|
||||
//var_dump($this->m_aOrigValues[$sAtt]);
|
||||
//var_dump($proposedValue);
|
||||
@@ -2756,7 +2758,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* Used only by insert, Meant to be overloaded
|
||||
*
|
||||
*
|
||||
* @overwritable-hook You can extend this method in order to provide your own logic.
|
||||
*/
|
||||
protected function OnObjectKeyReady()
|
||||
@@ -2864,7 +2866,7 @@ abstract class DBObject implements iDisplay
|
||||
// fields in first array, values in the second
|
||||
$aFieldsToWrite = array();
|
||||
$aValuesToWrite = array();
|
||||
|
||||
|
||||
if (!empty($this->m_iKey) && ($this->m_iKey >= 0))
|
||||
{
|
||||
// Add it to the list of fields to write
|
||||
@@ -2873,7 +2875,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
$aHierarchicalKeys = array();
|
||||
|
||||
|
||||
foreach(MetaModel::ListAttributeDefs($sTableClass) as $sAttCode=>$oAttDef) {
|
||||
// Skip this attribute if not defined in this table
|
||||
if ((!MetaModel::IsAttributeOrigin($sTableClass, $sAttCode) && !$oAttDef->CopyOnAllTables())
|
||||
@@ -2883,7 +2885,7 @@ abstract class DBObject implements iDisplay
|
||||
$aAttColumns = $oAttDef->GetSQLValues($this->m_aCurrValues[$sAttCode]);
|
||||
foreach($aAttColumns as $sColumn => $sValue)
|
||||
{
|
||||
$aFieldsToWrite[] = "`$sColumn`";
|
||||
$aFieldsToWrite[] = "`$sColumn`";
|
||||
$aValuesToWrite[] = CMDBSource::Quote($sValue);
|
||||
}
|
||||
if ($oAttDef->IsHierarchicalKey())
|
||||
@@ -2907,7 +2909,7 @@ abstract class DBObject implements iDisplay
|
||||
self::$m_aBulkInsertCols[$sClass][$sTable] = implode(', ', $aFieldsToWrite);
|
||||
}
|
||||
self::$m_aBulkInsertItems[$sClass][$sTable][] = '('.implode (', ', $aValuesToWrite).')';
|
||||
|
||||
|
||||
$iNewKey = 999999; // TODO - compute next id....
|
||||
}
|
||||
else
|
||||
@@ -2992,7 +2994,7 @@ abstract class DBObject implements iDisplay
|
||||
// fields in first array, values in the second
|
||||
$aFieldsToWrite = array();
|
||||
$aValuesToWrite = array();
|
||||
|
||||
|
||||
if (!empty($this->m_iKey) && ($this->m_iKey >= 0))
|
||||
{
|
||||
// Add it to the list of fields to write
|
||||
@@ -3027,7 +3029,7 @@ abstract class DBObject implements iDisplay
|
||||
$aAttColumns = $oAttDef->GetSQLValues($value);
|
||||
foreach($aAttColumns as $sColumn => $sValue)
|
||||
{
|
||||
$aFieldsToWrite[] = "`$sColumn`";
|
||||
$aFieldsToWrite[] = "`$sColumn`";
|
||||
$aValuesToWrite[] = CMDBSource::Quote($sValue);
|
||||
}
|
||||
if ($oAttDef->IsHierarchicalKey())
|
||||
@@ -3122,7 +3124,9 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
// Ensure the update of the values (we are accessing the data directly)
|
||||
$this->DoComputeValues();
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->OnInsert();
|
||||
$oKPI->ComputeStatsForExtension($this, 'OnInsert');
|
||||
|
||||
$this->FireEventBeforeWrite();
|
||||
|
||||
@@ -3165,6 +3169,10 @@ abstract class DBObject implements iDisplay
|
||||
// First query built upon on the root class, because the ID must be created first
|
||||
$this->m_iKey = $this->DBInsertSingleTable($sRootClass);
|
||||
|
||||
//since N°5324: issue with test and db links events
|
||||
$this->SetReadOnly('No modification allowed during transaction');
|
||||
MetaModel::StartReentranceProtection($this);
|
||||
|
||||
// Then do the leaf class, if different from the root class
|
||||
if ($sClass != $sRootClass) {
|
||||
$this->DBInsertSingleTable($sClass);
|
||||
@@ -3178,7 +3186,9 @@ abstract class DBObject implements iDisplay
|
||||
$this->DBInsertSingleTable($sParentClass);
|
||||
}
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->OnObjectKeyReady();
|
||||
$oKPI->ComputeStatsForExtension($this, 'OnObjectKeyReady');
|
||||
$this->UpdateCurrentObjectInCrudStack();
|
||||
|
||||
$this->DBWriteLinks();
|
||||
@@ -3214,6 +3224,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
|
||||
$this->SetReadWrite();
|
||||
$this->m_bIsInDB = true;
|
||||
$this->m_bDirty = false;
|
||||
foreach ($this->m_aCurrValues as $sAttCode => $value) {
|
||||
@@ -3224,7 +3235,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
// Prevent DBUpdate at this point (reentrance protection)
|
||||
MetaModel::StartReentranceProtection($this);
|
||||
//MetaModel::StartReentranceProtection($this);
|
||||
|
||||
try {
|
||||
$this->PostInsertActions();
|
||||
@@ -3255,7 +3266,9 @@ abstract class DBObject implements iDisplay
|
||||
public function PostInsertActions(): void
|
||||
{
|
||||
$this->FireEventAfterWrite([], true);
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->AfterInsert();
|
||||
$oKPI->ComputeStatsForExtension($this, 'AfterInsert');
|
||||
|
||||
// Activate any existing trigger
|
||||
$sClass = get_class($this);
|
||||
@@ -3301,7 +3314,7 @@ abstract class DBObject implements iDisplay
|
||||
$this->RecordObjCreation();
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function is automatically called after cloning an object with the "clone" PHP language construct
|
||||
* The purpose of this method is to reset the appropriate attributes of the object in
|
||||
@@ -3353,7 +3366,9 @@ abstract class DBObject implements iDisplay
|
||||
try {
|
||||
$this->DoComputeValues();
|
||||
$this->ComputeStopWatchesDeadline(false);
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->OnUpdate();
|
||||
$oKPI->ComputeStatsForExtension($this, 'OnUpdate');
|
||||
|
||||
$this->FireEventBeforeWrite();
|
||||
|
||||
@@ -3390,6 +3405,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
|
||||
$this->SetReadOnly('No modification allowed during transaction');
|
||||
$iTransactionRetry = 1;
|
||||
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
|
||||
if ($bIsTransactionEnabled) {
|
||||
@@ -3504,6 +3520,8 @@ abstract class DBObject implements iDisplay
|
||||
// following lines are resetting changes (so after this {@see DBObject::ListChanges()} won't return changes anymore)
|
||||
// new values are already in the object (call {@see DBObject::Get()} to get them)
|
||||
// call {@see DBObject::ListPreviousValuesForUpdatedAttributes()} to get changed fields and previous values
|
||||
|
||||
$this->SetReadWrite();
|
||||
$this->m_bDirty = false;
|
||||
$this->m_aTouchedAtt = array();
|
||||
$this->m_aModifiedAtt = array();
|
||||
@@ -3567,7 +3585,9 @@ abstract class DBObject implements iDisplay
|
||||
public function PostUpdateActions(array $aChanges): void
|
||||
{
|
||||
$this->FireEventAfterWrite($aChanges, false);
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->AfterUpdate();
|
||||
$oKPI->ComputeStatsForExtension($this, 'AfterUpdate');
|
||||
|
||||
// - TriggerOnObjectUpdate
|
||||
$aParams = array('class_list' => MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL));
|
||||
@@ -3794,7 +3814,9 @@ abstract class DBObject implements iDisplay
|
||||
return;
|
||||
}
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->OnDelete();
|
||||
$oKPI->ComputeStatsForExtension($this, 'OnDelete');
|
||||
|
||||
// Activate any existing trigger
|
||||
$sClass = get_class($this);
|
||||
@@ -3902,7 +3924,9 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
$this->FireEventAfterDelete();
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->AfterDelete();
|
||||
$oKPI->ComputeStatsForExtension($this, 'AfterDelete');
|
||||
|
||||
|
||||
$this->m_bIsInDB = false;
|
||||
@@ -3917,7 +3941,7 @@ abstract class DBObject implements iDisplay
|
||||
* First, checks if the object can be deleted regarding database integrity.
|
||||
* If the answer is yes, it performs any required cleanup (delete other objects or reset external keys) in addition to the object
|
||||
* deletion.
|
||||
*
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param \DeletionPlan $oDeletionPlan Do not use: aims at dealing with recursion
|
||||
@@ -4340,7 +4364,7 @@ abstract class DBObject implements iDisplay
|
||||
*
|
||||
* @api
|
||||
*
|
||||
*/
|
||||
*/
|
||||
public function Reset($sAttCode)
|
||||
{
|
||||
$this->Set($sAttCode, $this->GetDefaultValue($sAttCode));
|
||||
@@ -4352,7 +4376,7 @@ abstract class DBObject implements iDisplay
|
||||
* Suitable for use as a lifecycle action
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
*/
|
||||
public function Copy($sDestAttCode, $sSourceAttCode)
|
||||
{
|
||||
$oTypeValueToCopy = MetaModel::GetAttributeDef(get_class($this), $sSourceAttCode);
|
||||
@@ -4682,7 +4706,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
throw new CoreException("Unknown attribute '$sExtKeyAttCode' for the class ".get_class($this));
|
||||
}
|
||||
|
||||
|
||||
$oKeyAttDef = MetaModel::GetAttributeDef(get_class($this), $sExtKeyAttCode);
|
||||
if (!$oKeyAttDef instanceof AttributeExternalKey)
|
||||
{
|
||||
@@ -4700,14 +4724,14 @@ abstract class DBObject implements iDisplay
|
||||
$ret = $oRemoteObj->GetForTemplate($sRemoteAttCode);
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
switch($sPlaceholderAttCode)
|
||||
{
|
||||
case 'id':
|
||||
$ret = $this->GetKey();
|
||||
break;
|
||||
|
||||
|
||||
case 'name()':
|
||||
$ret = $this->GetName();
|
||||
break;
|
||||
@@ -4894,7 +4918,7 @@ abstract class DBObject implements iDisplay
|
||||
if ($oOwner)
|
||||
{
|
||||
$sLinkSetOwnerClass = get_class($oOwner);
|
||||
|
||||
|
||||
$oMyChangeOp = MetaModel::NewObject($sChangeOpClass);
|
||||
$oMyChangeOp->Set("objclass", $sLinkSetOwnerClass);
|
||||
$oMyChangeOp->Set("objkey", $iLinkSetOwnerId);
|
||||
@@ -4921,7 +4945,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
/** @var \AttributeLinkedSet $oLinkSet */
|
||||
if (($oLinkSet->GetTrackingLevel() & LINKSET_TRACKING_LIST) == 0) continue;
|
||||
|
||||
|
||||
$iLinkSetOwnerId = $this->Get($sExtKeyAttCode);
|
||||
$oMyChangeOp = $this->PrepareChangeOpLinkSet($iLinkSetOwnerId, $oLinkSet, 'CMDBChangeOpSetAttributeLinksAddRemove');
|
||||
if ($oMyChangeOp)
|
||||
@@ -4991,7 +5015,7 @@ abstract class DBObject implements iDisplay
|
||||
// Keep track of link changes
|
||||
//
|
||||
if (($oLinkSet->GetTrackingLevel() & LINKSET_TRACKING_DETAILS) == 0) continue;
|
||||
|
||||
|
||||
$iLinkSetOwnerId = $this->Get($sExtKeyAttCode);
|
||||
$oMyChangeOp = $this->PrepareChangeOpLinkSet($iLinkSetOwnerId, $oLinkSet, 'CMDBChangeOpSetAttributeLinksTune');
|
||||
if ($oMyChangeOp)
|
||||
@@ -5140,7 +5164,7 @@ abstract class DBObject implements iDisplay
|
||||
$this->FireEventCheckToDelete($oDeletionPlan);
|
||||
$this->DoCheckToDelete($oDeletionPlan);
|
||||
$oDeletionPlan->SetDeletionIssues($this, $this->m_aDeleteIssues, $this->m_bSecurityIssue);
|
||||
|
||||
|
||||
$aDependentObjects = $this->GetReferencingObjects(true /* allow all data */);
|
||||
|
||||
// Getting and setting time limit are not symmetric:
|
||||
@@ -5322,7 +5346,7 @@ abstract class DBObject implements iDisplay
|
||||
$aSynchroClasses[] = $sTarget;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach($aSynchroClasses as $sClass)
|
||||
{
|
||||
if ($this instanceof $sClass)
|
||||
|
||||
@@ -767,7 +767,10 @@ 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
|
||||
@@ -847,8 +850,11 @@ 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);
|
||||
@@ -859,6 +865,42 @@ 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
|
||||
*
|
||||
@@ -875,8 +917,11 @@ 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);
|
||||
@@ -887,7 +932,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
$iCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = $this->m_iNumTotalDBRows;
|
||||
@@ -912,8 +957,11 @@ 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);
|
||||
@@ -924,7 +972,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
$iCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = $this->m_iNumTotalDBRows;
|
||||
|
||||
@@ -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,10 +56,11 @@ 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)
|
||||
public static function SetUserLanguage($sLanguageCode = null)
|
||||
{
|
||||
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
|
||||
if (!is_null($sLanguageCode) && !array_key_exists($sLanguageCode, self::$m_aLanguages))
|
||||
{
|
||||
throw new DictExceptionUnknownLanguage($sLanguageCode);
|
||||
}
|
||||
@@ -115,33 +116,50 @@ 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 dictionnaries. default labels displayed");
|
||||
IssueLog::Warning("Cannot find $sLangCode in all registered dictionaries.");
|
||||
// It may happen, when something happens before the dictionaries get loaded
|
||||
return $sStringCode;
|
||||
return [ 'label' => $sStringCode, 'lang' => $sLangCode ];
|
||||
}
|
||||
$aCurrentDictionary = self::$m_aData[$sLangCode];
|
||||
if (is_array($aCurrentDictionary) && array_key_exists($sStringCode, $aCurrentDictionary))
|
||||
{
|
||||
return $aCurrentDictionary[$sStringCode];
|
||||
return [ 'label' => $aCurrentDictionary[$sStringCode], 'lang' => $sLangCode ];
|
||||
}
|
||||
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 $aDefaultDictionary[$sStringCode];
|
||||
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => self::$m_sDefaultLanguage ];
|
||||
}
|
||||
// Attempt to find the string in english
|
||||
//
|
||||
@@ -150,17 +168,17 @@ class Dict
|
||||
$aDefaultDictionary = self::$m_aData['EN US'];
|
||||
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => 'EN US' ];
|
||||
}
|
||||
}
|
||||
// Could not find the string...
|
||||
//
|
||||
if (is_null($sDefault))
|
||||
{
|
||||
return $sStringCode;
|
||||
return [ 'label' => $sStringCode, 'lang' => null ];
|
||||
}
|
||||
|
||||
return $sDefault;
|
||||
return [ 'label' => $sDefault, 'lang' => null ];
|
||||
}
|
||||
|
||||
|
||||
@@ -176,19 +194,25 @@ class Dict
|
||||
*/
|
||||
public static function Format($sFormatCode /*, ... arguments ... */)
|
||||
{
|
||||
$sLocalizedFormat = self::S($sFormatCode);
|
||||
['label' => $sLocalizedFormat, 'lang' => $sLangCode] = self::GetLabelAndLangCode($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);
|
||||
}
|
||||
|
||||
return vsprintf($sLocalizedFormat, $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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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'
|
||||
@@ -198,7 +222,7 @@ class Dict
|
||||
{
|
||||
self::$m_aData[$sLanguageCode] = $aEntries;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the list of available languages
|
||||
* @param hash $aLanguagesList
|
||||
@@ -259,7 +283,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))
|
||||
{
|
||||
@@ -269,7 +293,7 @@ class Dict
|
||||
}
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable caching (cached using APC)
|
||||
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
|
||||
@@ -312,14 +336,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]))
|
||||
@@ -327,7 +351,7 @@ class Dict
|
||||
$aMissing[$sStringCode] = $sValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue)
|
||||
{
|
||||
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef]))
|
||||
@@ -350,7 +374,7 @@ class Dict
|
||||
}
|
||||
return array($aMissing, $aUnexpected, $aNotTranslated, $aOK);
|
||||
}
|
||||
|
||||
|
||||
public static function Dump()
|
||||
{
|
||||
MyHelpers::var_dump_html(self::$m_aData);
|
||||
@@ -373,7 +397,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
|
||||
@@ -386,7 +410,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)
|
||||
{
|
||||
@@ -395,7 +419,7 @@ class Dict
|
||||
$aEntries[$sCode] = $sEntry;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Now put (overwrite) the entries for the user language
|
||||
foreach(self::$m_aData[self::GetUserLanguage()] as $sCode => $sEntry)
|
||||
{
|
||||
|
||||
@@ -101,7 +101,7 @@ EOF;
|
||||
EOF;
|
||||
|
||||
SetupUtils::builddir(dirname($sFilePath));
|
||||
file_put_contents($sFilePath, $content);
|
||||
file_put_contents($sFilePath, $content, LOCK_EX);
|
||||
}
|
||||
}
|
||||
Dict::SetUserLanguage($sUserLang);
|
||||
|
||||
@@ -1,27 +1,14 @@
|
||||
<?php
|
||||
// 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/>
|
||||
/**
|
||||
* @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;
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -30,6 +17,8 @@ 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
|
||||
@@ -86,14 +75,39 @@ 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 = implode(', ', $aFeatures);
|
||||
$sFeatures = 'Measures: '.implode(', ', $aFeatures);
|
||||
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
|
||||
return "KPI logging is active for $sFor. Measures: $sFeatures";
|
||||
$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";
|
||||
}
|
||||
|
||||
static public function ReportStats()
|
||||
@@ -101,7 +115,28 @@ 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)
|
||||
@@ -114,9 +149,9 @@ class ExecutionKPI
|
||||
|
||||
$sHtml = "<hr/>";
|
||||
$sHtml .= "<div style=\"background-color: grey; padding: 10px;\">";
|
||||
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")</h3>";
|
||||
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - $sRequest</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\">";
|
||||
@@ -257,7 +292,7 @@ class ExecutionKPI
|
||||
$sTotalInter = round($fTotalInter, 3);
|
||||
$sMinInter = round($fMinInter, 3);
|
||||
$sMaxInter = round($fMaxInter, 3);
|
||||
if (($fTotalInter >= $fSlowQueries))
|
||||
if (($fTotalInter >= self::$m_fSlowQueries))
|
||||
{
|
||||
if ($bDisplayHeader)
|
||||
{
|
||||
@@ -285,37 +320,19 @@ 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
|
||||
//
|
||||
@@ -323,9 +340,15 @@ class ExecutionKPI
|
||||
{
|
||||
global $fItopStarted;
|
||||
|
||||
if (!self::IsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aNewEntry = null;
|
||||
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$fStarted = $this->m_fStarted;
|
||||
$fStopped = $this->m_fStarted;
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$aNewEntry = array(
|
||||
'op' => $sOperationDesc,
|
||||
@@ -336,6 +359,9 @@ 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();
|
||||
@@ -345,40 +371,103 @@ class ExecutionKPI
|
||||
}
|
||||
$aNewEntry['mem_begin'] = $this->m_iInitialMemory;
|
||||
$aNewEntry['mem_end'] = $iCurrentMemory;
|
||||
if (function_exists('memory_get_peak_usage'))
|
||||
{
|
||||
$aNewEntry['mem_peak'] = memory_get_peak_usage();
|
||||
}
|
||||
$iPeakMemory = self::memory_get_peak_usage();
|
||||
$aNewEntry['mem_peak'] = $iPeakMemory;
|
||||
// Reset for the next operation (if the object is recycled)
|
||||
$this->m_iInitialMemory = $iCurrentMemory;
|
||||
}
|
||||
|
||||
if (!is_null($aNewEntry))
|
||||
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)
|
||||
{
|
||||
self::$m_aExecData[] = $aNewEntry;
|
||||
}
|
||||
$this->ResetCounters();
|
||||
}
|
||||
|
||||
public function ComputeStatsForExtension($object, $sMethod)
|
||||
{
|
||||
if (!self::IsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sSignature = ModuleService::GetInstance()->GetModuleMethodSignature($object, $sMethod);
|
||||
if (utils::StartsWith($sSignature, '[')) {
|
||||
$this->ComputeStats('Extension', $sSignature);
|
||||
}
|
||||
}
|
||||
|
||||
public function ComputeStats($sOperation, $sArguments)
|
||||
{
|
||||
if (!self::IsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fDuration = 0;
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$fDuration = $fStopped - $this->m_fStarted;
|
||||
$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);
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function ResetCounters()
|
||||
@@ -408,35 +497,7 @@ class ExecutionKPI
|
||||
|
||||
static protected function memory_get_usage()
|
||||
{
|
||||
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;
|
||||
}
|
||||
return memory_get_usage(true);
|
||||
}
|
||||
|
||||
static public function memory_get_peak_usage($bRealUsage = false)
|
||||
|
||||
@@ -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,6 +576,11 @@ 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
|
||||
@@ -1138,7 +1143,7 @@ class DeprecatedCallsLog extends LogAPI
|
||||
parent::Enable($sTargetFile);
|
||||
|
||||
if (
|
||||
(false === defined(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME))
|
||||
(false === defined('ITOP_PHPUNIT_RUNNING_CONSTANT_NAME'))
|
||||
&& static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD)
|
||||
) {
|
||||
set_error_handler([static::class, 'DeprecatedNoticesErrorHandler'], E_DEPRECATED | E_USER_DEPRECATED);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -3522,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
|
||||
@@ -3564,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
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3764,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);
|
||||
}
|
||||
|
||||
@@ -4219,40 +4219,77 @@ abstract class MetaModel
|
||||
}
|
||||
else
|
||||
{
|
||||
$aCurrentUser = array();
|
||||
$aCurrentContact = array();
|
||||
$aCurrentUser = [];
|
||||
$aCurrentContact = [];
|
||||
foreach ($aExpectedArgs as $expression)
|
||||
{
|
||||
$aName = explode('->', $expression->GetName());
|
||||
if ($aName[0] == 'current_contact_id') {
|
||||
$aPlaceholders['current_contact_id'] = UserRights::GetContactId();
|
||||
}
|
||||
if ($aName[0] == 'current_user') {
|
||||
} else if ($aName[0] == 'current_user') {
|
||||
array_push($aCurrentUser, $aName[1]);
|
||||
}
|
||||
if ($aName[0] == 'current_contact') {
|
||||
} else if ($aName[0] == 'current_contact') {
|
||||
array_push($aCurrentContact, $aName[1]);
|
||||
}
|
||||
}
|
||||
if (count($aCurrentUser) > 0) {
|
||||
$oUser = UserRights::GetUserObject();
|
||||
$aPlaceholders['current_user->object()'] = $oUser;
|
||||
foreach ($aCurrentUser as $sField) {
|
||||
$aPlaceholders['current_user->'.$sField] = $oUser->Get($sField);
|
||||
}
|
||||
static::FillObjectPlaceholders($aPlaceholders, 'current_user', UserRights::GetUserObject(), $aCurrentUser);
|
||||
}
|
||||
if (count($aCurrentContact) > 0) {
|
||||
$oPerson = UserRights::GetContactObject();
|
||||
$aPlaceholders['current_contact->object()'] = $oPerson;
|
||||
foreach ($aCurrentContact as $sField) {
|
||||
$aPlaceholders['current_contact->'.$sField] = $oPerson->Get($sField);
|
||||
}
|
||||
static::FillObjectPlaceholders($aPlaceholders, 'current_contact', UserRights::GetContactObject(), $aCurrentContact);
|
||||
}
|
||||
}
|
||||
|
||||
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";
|
||||
if (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
|
||||
*
|
||||
@@ -6298,6 +6335,13 @@ 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 {
|
||||
@@ -6376,7 +6420,9 @@ 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::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'));
|
||||
|
||||
self::$m_bSkipCheckToWrite = self::$m_oConfig->Get('skip_check_to_write');
|
||||
self::$m_bSkipCheckExtKeys = self::$m_oConfig->Get('skip_check_ext_keys');
|
||||
@@ -6470,7 +6516,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)
|
||||
@@ -6495,6 +6541,7 @@ abstract class MetaModel
|
||||
|
||||
CMDBSource::InitFromConfig(self::$m_oConfig);
|
||||
// Later when timezone implementation is correctly done: CMDBSource::SetTimezone($sDBTimezone);
|
||||
ExecutionKPI::InitStats();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6526,6 +6573,19 @@ 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
|
||||
*/
|
||||
@@ -6807,25 +6867,21 @@ 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;
|
||||
@@ -7567,6 +7623,20 @@ abstract class MetaModel
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°5324: to ease reentrance checks when using events on links (to avoid reentering if main link object ongoing operation)
|
||||
*/
|
||||
public static function GetReentranceObjectByChildClass(string $sParentClass, $sKey)
|
||||
{
|
||||
foreach (self::EnumChildClasses($sParentClass, ENUM_CHILD_CLASSES_ALL, false) as $sChildClass){
|
||||
if (self::GetReentranceObject($sChildClass, $sKey)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBObject $oObject
|
||||
*
|
||||
@@ -7608,14 +7678,12 @@ abstract class MetaModel
|
||||
// Build the list of available extensions
|
||||
//
|
||||
$aInterfaces = [
|
||||
'iApplicationUIExtension',
|
||||
'iPreferencesExtension',
|
||||
'iApplicationObjectExtension',
|
||||
'iLoginFSMExtension',
|
||||
'iLoginUIExtension',
|
||||
'iLogoutExtension',
|
||||
'iQueryModifier',
|
||||
'iOnClassInitialization',
|
||||
'iLoginUIExtension',
|
||||
'iPreferencesExtension',
|
||||
'iApplicationUIExtension',
|
||||
'iApplicationObjectExtension',
|
||||
'iPopupMenuExtension',
|
||||
'iPageUIExtension',
|
||||
'iPageUIBlockExtension',
|
||||
@@ -7629,9 +7697,12 @@ abstract class MetaModel
|
||||
'iBackofficeDictEntriesExtension',
|
||||
'iBackofficeDictEntriesPrefixesExtension',
|
||||
'iPortalUIExtension',
|
||||
'iQueryModifier',
|
||||
'iOnClassInitialization',
|
||||
'iModuleExtension',
|
||||
'iKPILoggerExtension',
|
||||
'ModuleHandlerApiInterface',
|
||||
'iNewsroomProvider',
|
||||
'iModuleExtension',
|
||||
];
|
||||
foreach ($aInterfaces as $sInterface) {
|
||||
self::$m_aExtensionClassNames[$sInterface] = array();
|
||||
|
||||
@@ -257,7 +257,7 @@ class iTopMutex
|
||||
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
|
||||
|
||||
if (!$this->hDBLink) {
|
||||
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
|
||||
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
|
||||
}
|
||||
|
||||
// Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection,
|
||||
|
||||
@@ -121,7 +121,9 @@ abstract class Trigger extends cmdbAbstractObject
|
||||
$oAction = MetaModel::GetObject('Action', $iActionId);
|
||||
if ($oAction->IsActive())
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oAction->DoExecute($this, $aContextArgs);
|
||||
$oKPI->ComputeStatsForExtension($oAction, 'DoExecute');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -761,14 +761,25 @@ class UserRights
|
||||
protected static $m_aCacheContactPictureAbsUrl = [];
|
||||
/** @var UserRightsAddOnAPI $m_oAddOn */
|
||||
protected static $m_oAddOn;
|
||||
protected static $m_oUser;
|
||||
protected static $m_oRealUser;
|
||||
protected static $m_oUser = null;
|
||||
protected static $m_oRealUser = null;
|
||||
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
|
||||
*
|
||||
@@ -787,8 +798,7 @@ class UserRights
|
||||
}
|
||||
self::$m_oAddOn = new $sModuleName;
|
||||
self::$m_oAddOn->Init();
|
||||
self::$m_oUser = null;
|
||||
self::$m_oRealUser = null;
|
||||
self::ResetCurrentUserData();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -855,6 +865,8 @@ class UserRights
|
||||
*/
|
||||
public static function Login($sLogin, $sAuthentication = 'any')
|
||||
{
|
||||
static::Logoff();
|
||||
|
||||
$oUser = self::FindUser($sLogin, $sAuthentication);
|
||||
if (is_null($oUser))
|
||||
{
|
||||
@@ -872,6 +884,17 @@ class UserRights
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
||||
@@ -20,6 +20,8 @@ $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};
|
||||
@@ -74,18 +76,27 @@ $ibo-dashlet-badge--action-icon--margin-right: $ibo-spacing-300 !default;
|
||||
@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--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;
|
||||
}
|
||||
|
||||
2
css/backoffice/pages/_csv-import.scss
vendored
2
css/backoffice/pages/_csv-import.scss
vendored
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -344,6 +344,7 @@ 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();
|
||||
|
||||
@@ -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.
|
||||
@@ -34,7 +34,7 @@ class DBRestore extends DBBackup
|
||||
|
||||
protected function LogInfo($sMsg)
|
||||
{
|
||||
//IssueLog::Info('non juste info: '.$sMsg);
|
||||
IssueLog::Info('non juste info: '.$sMsg);
|
||||
}
|
||||
|
||||
protected function LogError($sMsg)
|
||||
@@ -91,7 +91,7 @@ class DBRestore extends DBBackup
|
||||
{
|
||||
$this->LogError("mysql said: $sLine");
|
||||
}
|
||||
if (count($aOutput) == 1)
|
||||
if (count($aOutput) == 1)
|
||||
{
|
||||
$sMoreInfo = trim($aOutput[0]);
|
||||
}
|
||||
@@ -187,7 +187,7 @@ class DBRestore extends DBBackup
|
||||
@chmod($sConfigFile, 0770); // Allow overwriting the file
|
||||
rename($sDataDir.'/config-itop.php', $sConfigFile);
|
||||
@chmod($sConfigFile, 0440); // Read-only
|
||||
|
||||
|
||||
$aExtraFiles = $this->ListExtraFiles($sDataDir);
|
||||
foreach($aExtraFiles as $sSourceFilePath => $sDestinationFilePath) {
|
||||
SetupUtils::builddir(dirname($sDestinationFilePath));
|
||||
@@ -235,13 +235,16 @@ class DBRestore extends DBBackup
|
||||
if (in_array($oFileInfo->getFilename(), $aStandardFiles)) {
|
||||
continue;
|
||||
}
|
||||
if (strncmp($oFileInfo->getPathname(), $sDataDir.'/production-modules', strlen($sDataDir.'/production-modules')) == 0) {
|
||||
// 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) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$aExtraFiles[$oFileInfo->getPathname()] = APPROOT.substr($oFileInfo->getPathname(), strlen($sDataDir));
|
||||
}
|
||||
|
||||
|
||||
return $aExtraFiles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
|
||||
// Errors
|
||||
'iTopUpdate:Error:MissingFunction' => 'Lehetetlen elindítani a frissítést, hiányzó funkció',
|
||||
'iTopUpdate:Error:MissingFile' => 'Hiányzó fájl: %1$',
|
||||
'iTopUpdate:Error:MissingFile' => 'Hiányzó fájl: %1$s',
|
||||
'iTopUpdate:Error:CorruptedFile' => 'A %1$s fájl sérült',
|
||||
'iTopUpdate:Error:BadFileFormat' => 'A frissítési fájl nem zip fájl',
|
||||
'iTopUpdate:Error:BadFileContent' => 'A frissítési fájl nem alkalmazás archívum',
|
||||
|
||||
@@ -58,7 +58,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:FAQ/Attribute:category_id+' => '',
|
||||
'Class:FAQ/Attribute:category_name' => '类别名称',
|
||||
'Class:FAQ/Attribute:category_name+' => '',
|
||||
'Class:FAQ/Attribute:error_code' => '错误代码',
|
||||
'Class:FAQ/Attribute:error_code' => '错误编码',
|
||||
'Class:FAQ/Attribute:error_code+' => '',
|
||||
'Class:FAQ/Attribute:key_words' => '关键字',
|
||||
'Class:FAQ/Attribute:key_words+' => '',
|
||||
|
||||
@@ -66,11 +66,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:Incident/Attribute:status+' => '',
|
||||
'Class:Incident/Attribute:status/Value:new' => '新建',
|
||||
'Class:Incident/Attribute:status/Value:new+' => '',
|
||||
'Class:Incident/Attribute:status/Value:escalated_tto' => '已升级响应时间',
|
||||
'Class:Incident/Attribute:status/Value:escalated_tto' => '已升级TTO',
|
||||
'Class:Incident/Attribute:status/Value:escalated_tto+' => '',
|
||||
'Class:Incident/Attribute:status/Value:assigned' => '已分配',
|
||||
'Class:Incident/Attribute:status/Value:assigned+' => '',
|
||||
'Class:Incident/Attribute:status/Value:escalated_ttr' => '已升级解决时间',
|
||||
'Class:Incident/Attribute:status/Value:escalated_ttr' => '已升级TTR',
|
||||
'Class:Incident/Attribute:status/Value:escalated_ttr+' => '',
|
||||
'Class:Incident/Attribute:status/Value:waiting_for_approval' => '等待批准',
|
||||
'Class:Incident/Attribute:status/Value:waiting_for_approval+' => '',
|
||||
@@ -90,8 +90,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:Incident/Attribute:impact/Value:3+' => '',
|
||||
'Class:Incident/Attribute:priority' => '优先级',
|
||||
'Class:Incident/Attribute:priority+' => '',
|
||||
'Class:Incident/Attribute:priority/Value:1' => '非常高',
|
||||
'Class:Incident/Attribute:priority/Value:1+' => '非常高',
|
||||
'Class:Incident/Attribute:priority/Value:1' => '紧急',
|
||||
'Class:Incident/Attribute:priority/Value:1+' => '紧急',
|
||||
'Class:Incident/Attribute:priority/Value:2' => '高',
|
||||
'Class:Incident/Attribute:priority/Value:2+' => '高',
|
||||
'Class:Incident/Attribute:priority/Value:3' => '中',
|
||||
@@ -100,8 +100,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:Incident/Attribute:priority/Value:4+' => '低',
|
||||
'Class:Incident/Attribute:urgency' => '紧急度',
|
||||
'Class:Incident/Attribute:urgency+' => '',
|
||||
'Class:Incident/Attribute:urgency/Value:1' => '非常高',
|
||||
'Class:Incident/Attribute:urgency/Value:1+' => '非常高',
|
||||
'Class:Incident/Attribute:urgency/Value:1' => '紧急',
|
||||
'Class:Incident/Attribute:urgency/Value:1+' => '紧急',
|
||||
'Class:Incident/Attribute:urgency/Value:2' => '高',
|
||||
'Class:Incident/Attribute:urgency/Value:2+' => '高',
|
||||
'Class:Incident/Attribute:urgency/Value:3' => '中',
|
||||
@@ -136,7 +136,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:Incident/Attribute:escalation_flag/Value:no+' => '否',
|
||||
'Class:Incident/Attribute:escalation_flag/Value:yes' => '是',
|
||||
'Class:Incident/Attribute:escalation_flag/Value:yes+' => '是',
|
||||
'Class:Incident/Attribute:escalation_reason' => '热门',
|
||||
'Class:Incident/Attribute:escalation_reason' => '升级原因',
|
||||
'Class:Incident/Attribute:escalation_reason+' => '',
|
||||
'Class:Incident/Attribute:assignment_date' => '分配日期',
|
||||
'Class:Incident/Attribute:assignment_date+' => '',
|
||||
@@ -146,21 +146,21 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:Incident/Attribute:last_pending_date+' => '',
|
||||
'Class:Incident/Attribute:cumulatedpending' => '累计待定',
|
||||
'Class:Incident/Attribute:cumulatedpending+' => '',
|
||||
'Class:Incident/Attribute:tto' => '响应时间',
|
||||
'Class:Incident/Attribute:tto+' => '',
|
||||
'Class:Incident/Attribute:ttr' => '解决时间',
|
||||
'Class:Incident/Attribute:ttr+' => '',
|
||||
'Class:Incident/Attribute:tto_escalation_deadline' => '响应时间截止',
|
||||
'Class:Incident/Attribute:tto' => 'TTO',
|
||||
'Class:Incident/Attribute:tto+' => '响应时间',
|
||||
'Class:Incident/Attribute:ttr' => 'TTR',
|
||||
'Class:Incident/Attribute:ttr+' => '解决时限',
|
||||
'Class:Incident/Attribute:tto_escalation_deadline' => 'TTO截止日期',
|
||||
'Class:Incident/Attribute:tto_escalation_deadline+' => '',
|
||||
'Class:Incident/Attribute:sla_tto_passed' => '超过SLA响应时间',
|
||||
'Class:Incident/Attribute:sla_tto_passed' => 'SLA TTO 合格',
|
||||
'Class:Incident/Attribute:sla_tto_passed+' => '',
|
||||
'Class:Incident/Attribute:sla_tto_over' => 'SLA响应时间结束',
|
||||
'Class:Incident/Attribute:sla_tto_over' => 'SLA TTO 超时',
|
||||
'Class:Incident/Attribute:sla_tto_over+' => '',
|
||||
'Class:Incident/Attribute:ttr_escalation_deadline' => '解决时间截止',
|
||||
'Class:Incident/Attribute:ttr_escalation_deadline' => 'TTR截止日期',
|
||||
'Class:Incident/Attribute:ttr_escalation_deadline+' => '',
|
||||
'Class:Incident/Attribute:sla_ttr_passed' => '超过SLA解决时间',
|
||||
'Class:Incident/Attribute:sla_ttr_passed' => 'SLA TTR 合格',
|
||||
'Class:Incident/Attribute:sla_ttr_passed+' => '',
|
||||
'Class:Incident/Attribute:sla_ttr_over' => 'SLA解决时间结束',
|
||||
'Class:Incident/Attribute:sla_ttr_over' => 'SLA TTR 超时',
|
||||
'Class:Incident/Attribute:sla_ttr_over+' => '',
|
||||
'Class:Incident/Attribute:time_spent' => '耗时',
|
||||
'Class:Incident/Attribute:time_spent+' => '',
|
||||
@@ -182,7 +182,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:Incident/Attribute:resolution_code/Value:training+' => '培训',
|
||||
'Class:Incident/Attribute:solution' => '解决方案',
|
||||
'Class:Incident/Attribute:solution+' => '',
|
||||
'Class:Incident/Attribute:pending_reason' => '待定的原因',
|
||||
'Class:Incident/Attribute:pending_reason' => '待定原因',
|
||||
'Class:Incident/Attribute:pending_reason+' => '',
|
||||
'Class:Incident/Attribute:parent_incident_id' => '父级事件',
|
||||
'Class:Incident/Attribute:parent_incident_id+' => '',
|
||||
|
||||
@@ -66,7 +66,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:KnownError/Attribute:workaround+' => '',
|
||||
'Class:KnownError/Attribute:solution' => '解决方案',
|
||||
'Class:KnownError/Attribute:solution+' => '',
|
||||
'Class:KnownError/Attribute:error_code' => '错误代码',
|
||||
'Class:KnownError/Attribute:error_code' => '错误编码',
|
||||
'Class:KnownError/Attribute:error_code+' => '',
|
||||
'Class:KnownError/Attribute:domain' => '类型',
|
||||
'Class:KnownError/Attribute:domain+' => '',
|
||||
|
||||
@@ -230,6 +230,7 @@ HTML
|
||||
$this->Set('refresh_token', $oAccessToken->getRefreshToken());
|
||||
}
|
||||
$this->Set('status', 'active');
|
||||
$this->AllowWrite();
|
||||
$this->DBUpdate();
|
||||
}
|
||||
]]></code>
|
||||
|
||||
@@ -11,6 +11,7 @@ use Combodo\iTop\Application\TwigBase\Controller\Controller;
|
||||
use Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderFactory;
|
||||
use Dict;
|
||||
use IssueLog;
|
||||
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
use MetaModel;
|
||||
use utils;
|
||||
use WebPage;
|
||||
@@ -65,13 +66,15 @@ class AjaxOauthClientController extends Controller
|
||||
}
|
||||
if (isset($aQuery['code'])) {
|
||||
$sCode = $aQuery['code'];
|
||||
$oAccessToken = OAuthClientProviderFactory::GetAccessTokenFromCode($oOAuthClient, $sCode);
|
||||
|
||||
$oOAuthClient->SetAccessToken($oAccessToken);
|
||||
|
||||
|
||||
|
||||
$aResult['status'] = 'success';
|
||||
try {
|
||||
$oAccessToken = OAuthClientProviderFactory::GetAccessTokenFromCode($oOAuthClient, $sCode);
|
||||
$oOAuthClient->SetAccessToken($oAccessToken);
|
||||
$aResult['status'] = 'success';
|
||||
}
|
||||
catch (IdentityProviderException $e) {
|
||||
$aResult['status'] = 'error';
|
||||
$aResult['error_description'] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$aResult['status'] = 'error';
|
||||
|
||||
@@ -103,8 +103,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:Problem/Attribute:impact/Value:3+' => '',
|
||||
'Class:Problem/Attribute:urgency' => '紧急度',
|
||||
'Class:Problem/Attribute:urgency+' => '',
|
||||
'Class:Problem/Attribute:urgency/Value:1' => '非常高',
|
||||
'Class:Problem/Attribute:urgency/Value:1+' => '非常高',
|
||||
'Class:Problem/Attribute:urgency/Value:1' => '紧急',
|
||||
'Class:Problem/Attribute:urgency/Value:1+' => '紧急',
|
||||
'Class:Problem/Attribute:urgency/Value:2' => '高',
|
||||
'Class:Problem/Attribute:urgency/Value:2+' => '高',
|
||||
'Class:Problem/Attribute:urgency/Value:3' => '中',
|
||||
@@ -113,8 +113,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:Problem/Attribute:urgency/Value:4+' => '低',
|
||||
'Class:Problem/Attribute:priority' => '优先级',
|
||||
'Class:Problem/Attribute:priority+' => '',
|
||||
'Class:Problem/Attribute:priority/Value:1' => '非常高',
|
||||
'Class:Problem/Attribute:priority/Value:1+' => '非常高',
|
||||
'Class:Problem/Attribute:priority/Value:1' => '紧急',
|
||||
'Class:Problem/Attribute:priority/Value:1+' => '紧急',
|
||||
'Class:Problem/Attribute:priority/Value:2' => '高',
|
||||
'Class:Problem/Attribute:priority/Value:2+' => '高',
|
||||
'Class:Problem/Attribute:priority/Value:3' => '中',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<itop_design version="3.1">
|
||||
<classes/>
|
||||
<user_rights>
|
||||
<groups>
|
||||
@@ -125,6 +125,7 @@
|
||||
<group id="Audit" _delta="define">
|
||||
<classes>
|
||||
<!-- This class list is also present in AdminTools group -->
|
||||
<class id="AuditDomain"/>
|
||||
<class id="AuditCategory"/>
|
||||
<class id="AuditRule"/>
|
||||
<class id="ResourceRunQueriesMenu"/>
|
||||
@@ -166,6 +167,7 @@
|
||||
<class id="URP_UserProfile"/>
|
||||
<class id="URP_Profiles"/>
|
||||
<!-- Audit group -->
|
||||
<class id="AuditDomain"/>
|
||||
<class id="AuditCategory"/>
|
||||
<class id="AuditRule"/>
|
||||
<!-- Query group -->
|
||||
@@ -544,5 +546,6 @@
|
||||
<groups/>
|
||||
</profile>
|
||||
</profiles>
|
||||
<dictionaries/>
|
||||
</user_rights>
|
||||
</itop_design>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) Combodo SARL 2022
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
Dict::Add('EN US', 'English', 'English', array(
|
||||
'Class:User/NonStandaloneProfileWarning' => 'Profile %1$s cannot be standalone. You should add other profiles to user %2$s otherwise you may encounter access issue with this user.',
|
||||
'Class:User/NonStandaloneProfileWarning-ReparationMessage' => 'Profile %1$s cannot be standalone. User %2$s has been completed with another profile: %3$s.',
|
||||
));
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @copyright Copyright (C) Combodo SARL 2022
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'Class:User/NonStandaloneProfileWarning' => 'Le profil %1$s ne peut être seul. Sans le rajout d\'autres profiles, l\'utilisateur %2$s peut rencontrer des problèmes dans iTop.',
|
||||
'Class:User/NonStandaloneProfileWarning-ReparationMessage' => 'Le profil %1$s ne peut être seul. Le user %2$s a été complété par le profil %3$s.',
|
||||
));
|
||||
|
||||
@@ -36,6 +36,7 @@ SetupWebPage::AddModule(
|
||||
// Components
|
||||
//
|
||||
'datamodel' => array(
|
||||
'src/UserProfilesEventListener.php'
|
||||
),
|
||||
'webservice' => array(
|
||||
//'webservices.itop-profiles-itil.php',
|
||||
|
||||
@@ -0,0 +1,394 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\ItilProfiles;
|
||||
|
||||
use Combodo\iTop\Service\Events\EventData;
|
||||
use Combodo\iTop\Service\Events\EventService;
|
||||
use Combodo\iTop\Service\Events\iEventServiceSetup;
|
||||
use Exception;
|
||||
use IssueLog;
|
||||
use LogChannels;
|
||||
|
||||
define('POWER_USER_PORTAL_PROFILE_NAME', 'Portal power user');
|
||||
|
||||
/**
|
||||
* Class UserProfilesEventListener
|
||||
*
|
||||
* @package Combodo\iTop\Core\EventListener
|
||||
* @since 3.1 N°5324 - Avoid to have users with non-standalone power portal profile only
|
||||
*
|
||||
*/
|
||||
class UserProfilesEventListener implements iEventServiceSetup
|
||||
{
|
||||
const USERPROFILE_REPAIR_ITOP_PARAM_NAME = 'security.single_profile_completion';
|
||||
private $bIsRepairmentEnabled = false;
|
||||
|
||||
//map: non standalone profile name => repairing profile id
|
||||
private $aNonStandaloneProfilesMap = [];
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function RegisterEventsAndListeners()
|
||||
{
|
||||
$this->Init();
|
||||
|
||||
if (false === $this->bIsRepairmentEnabled){
|
||||
IssueLog::Debug('UserProfilesEventListener bIsRepairmentEnabled disabled', LogChannels::DM_CRUD);
|
||||
return;
|
||||
}
|
||||
|
||||
$aEventSource = [\User::class, \UserExternal::class, \UserInternal::class];
|
||||
EventService::RegisterListener(
|
||||
EVENT_DB_BEFORE_WRITE,
|
||||
[$this, 'OnUserEdition'],
|
||||
$aEventSource
|
||||
);
|
||||
|
||||
EventService::RegisterListener(
|
||||
EVENT_DB_BEFORE_WRITE,
|
||||
[ $this, 'OnUserProfileEdition' ],
|
||||
[ \URP_UserProfile::class ],
|
||||
[],
|
||||
null
|
||||
);
|
||||
|
||||
EventService::RegisterListener(
|
||||
EVENT_DB_CHECK_TO_DELETE,
|
||||
[ $this, 'OnUserProfileLinkDeletion' ],
|
||||
[ \URP_UserProfile::class ],
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
public function IsRepairmentEnabled() : bool
|
||||
{
|
||||
return $this->bIsRepairmentEnabled;
|
||||
}
|
||||
|
||||
|
||||
public function OnUserEdition(EventData $oEventData): void {
|
||||
/** @var \User $oObject */
|
||||
$oUser = $oEventData->Get('object');
|
||||
|
||||
try {
|
||||
$this->ValidateThenRepairOrWarn($oUser);
|
||||
} catch (Exception $e) {
|
||||
IssueLog::Error('Exception occurred on RepairProfiles', LogChannels::DM_CRUD, [
|
||||
'user_class' => get_class($oUser),
|
||||
'user_id' => $oUser->GetKey(),
|
||||
'exception_message' => $e->getMessage(),
|
||||
'exception_stacktrace' => $e->getTraceAsString(),
|
||||
]);
|
||||
if ($e instanceof \CoreCannotSaveObjectException){
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function OnUserProfileEdition(EventData $oEventData): void {
|
||||
$oURP_UserProfile = $oEventData->Get('object');
|
||||
|
||||
try {
|
||||
$iUserId = $oURP_UserProfile->Get('userid');
|
||||
$oUser = \MetaModel::GetReentranceObjectByChildClass(\User::class, $iUserId);
|
||||
if (false !== $oUser){
|
||||
IssueLog::Debug('OnUserProfileEdition user already being edited', LogChannels::DM_CRUD);
|
||||
//user edition: handled by other event
|
||||
return;
|
||||
}
|
||||
|
||||
$oUser = \MetaModel::GetObject(\User::class, $iUserId);
|
||||
$aChanges = $oURP_UserProfile->ListChanges();
|
||||
if (array_key_exists('userid', $aChanges)) {
|
||||
IssueLog::Debug('OnUserProfileEdition userid changed', LogChannels::DM_CRUD);
|
||||
$iUserId = $oURP_UserProfile->GetOriginal('userid');
|
||||
$oPreviousUser = \MetaModel::GetObject(\User::class, $iUserId);
|
||||
|
||||
$oProfileLinkSet = $oPreviousUser->Get('profile_list');
|
||||
$oProfileLinkSet->Rewind();
|
||||
$iCount = 0;
|
||||
$sSingleProfileName = null;
|
||||
while ($oCurrentURP_UserProfile = $oProfileLinkSet->Fetch()) {
|
||||
$sNewUserId = $oCurrentURP_UserProfile->Get('userid');
|
||||
$sOriginalUserId = $oCurrentURP_UserProfile->GetOriginal('userid');
|
||||
if ($sNewUserId !== $sOriginalUserId) {
|
||||
$sRemovedProfileId = $oCurrentURP_UserProfile->GetOriginal('profileid');
|
||||
IssueLog::Debug('OnUserProfileEdition profile moved does not count', LogChannels::DM_CRUD, [
|
||||
'URP_UserProfile' => $oURP_UserProfile->GetKey(),
|
||||
'sRemovedProfileId' => $sRemovedProfileId,
|
||||
'sNewUserId' => $sNewUserId,
|
||||
'sOriginalUserId' => $sOriginalUserId,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$iCount++;
|
||||
if ($iCount > 1){
|
||||
IssueLog::Debug('OnUserProfileEdition more than one user', LogChannels::DM_CRUD);
|
||||
//more than one profile: no repairment needed
|
||||
return;
|
||||
}
|
||||
$sSingleProfileName = $oCurrentURP_UserProfile->Get('profile');
|
||||
}
|
||||
$this->RepairProfileChangesOrWarn($oPreviousUser, $sSingleProfileName, $oURP_UserProfile, $sRemovedProfileId);
|
||||
} else if (array_key_exists('profileid', $aChanges)){
|
||||
IssueLog::Debug('OnUserProfileEdition profileid changed', LogChannels::DM_CRUD);
|
||||
$oCurrentUserProfileSet = $oUser->Get('profile_list');
|
||||
if ($oCurrentUserProfileSet->Count() === 1){
|
||||
$oProfile = $oCurrentUserProfileSet->Fetch();
|
||||
|
||||
$this->RepairProfileChangesOrWarn($oUser, $oProfile->Get('profile'), $oURP_UserProfile, $oProfile->GetOriginal("profileid"));
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
IssueLog::Error('OnUserProfileEdition Exception', LogChannels::DM_CRUD, [
|
||||
'user_id' => $iUserId,
|
||||
'lnk_id' => $oURP_UserProfile->GetKey(),
|
||||
'exception_message' => $e->getMessage(),
|
||||
'exception_stacktrace' => $e->getTraceAsString(),
|
||||
]);
|
||||
if ($e instanceof \CoreCannotSaveObjectException){
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function OnUserProfileLinkDeletion(EventData $oEventData): void {
|
||||
$oURP_UserProfile = $oEventData->Get('object');
|
||||
|
||||
try {
|
||||
$iUserId = $oURP_UserProfile->Get('userid');
|
||||
$oUser = \MetaModel::GetReentranceObjectByChildClass(\User::class, $iUserId);
|
||||
if (false !== $oUser){
|
||||
IssueLog::Debug('OnUserProfileLinkDeletion user being deleted already', LogChannels::DM_CRUD);
|
||||
//user edition: handled by other event
|
||||
return;
|
||||
}
|
||||
|
||||
$oUser = \MetaModel::GetObject(\User::class, $iUserId);
|
||||
|
||||
/** @var \DeletionPlan $oDeletionPlan */
|
||||
$oDeletionPlan = $oEventData->Get('deletion_plan');
|
||||
$aDeletedURP_UserProfiles = [];
|
||||
if (! is_null($oDeletionPlan)){
|
||||
$aListDeletes = $oDeletionPlan->ListDeletes();
|
||||
if (array_key_exists(\URP_UserProfile::class, $aListDeletes)) {
|
||||
foreach ($aListDeletes[\URP_UserProfile::class] as $iId => $aDeletes) {
|
||||
$aDeletedURP_UserProfiles []= $iId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oProfileLinkSet = $oUser->Get('profile_list');
|
||||
$oProfileLinkSet->Rewind();
|
||||
$sSingleProfileName = null;
|
||||
$iCount = 0;
|
||||
while ($oCurrentURP_UserProfile = $oProfileLinkSet->Fetch()) {
|
||||
if (in_array($oCurrentURP_UserProfile->GetKey(), $aDeletedURP_UserProfiles)) {
|
||||
continue;
|
||||
}
|
||||
$iCount++;
|
||||
if ($iCount > 1){
|
||||
IssueLog::Debug('OnUserProfileLinkDeletion more than one profile', LogChannels::DM_CRUD);
|
||||
//more than one profile: no repairment needed
|
||||
return;
|
||||
}
|
||||
$sSingleProfileName = $oCurrentURP_UserProfile->Get('profile');
|
||||
}
|
||||
|
||||
$this->RepairProfileChangesOrWarn($oUser, $sSingleProfileName, $oURP_UserProfile, $oURP_UserProfile->Get('profileid'), true);
|
||||
} catch (Exception $e) {
|
||||
IssueLog::Error('OnUserProfileLinkDeletion Exception', LogChannels::DM_CRUD, [
|
||||
'user_id' => $iUserId,
|
||||
'profile_id' => $oURP_UserProfile->Get('profileid'),
|
||||
'exception_message' => $e->getMessage(),
|
||||
'exception_stacktrace' => $e->getTraceAsString(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $aPortalDispatcherData: passed only for testing purpose
|
||||
*
|
||||
* @return void
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function Init($aPortalDispatcherData=null) : void {
|
||||
if (is_null($aPortalDispatcherData)){
|
||||
$aPortalDispatcherData = \PortalDispatcherData::GetData();
|
||||
}
|
||||
|
||||
$aNonStandaloneProfiles = \utils::GetConfig()->Get(self::USERPROFILE_REPAIR_ITOP_PARAM_NAME, null);
|
||||
|
||||
//When there are several customized portals on an itop, choosing a specific profile means choosing which portal user will access
|
||||
//In that case, itop administrator has to specify it via itop configuration. we dont use default profiles repairment otherwise
|
||||
if (is_null($aNonStandaloneProfiles)){
|
||||
if (count($aPortalDispatcherData) > 2){
|
||||
IssueLog::Debug('Init repairment disabled as there are more than 2 portals (extended customer should decide on their own)', LogChannels::DM_CRUD);
|
||||
$this->bIsRepairmentEnabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
$aPortalNames = array_keys($aPortalDispatcherData);
|
||||
sort($aPortalNames);
|
||||
if ($aPortalNames !== ['backoffice', 'itop-portal']){
|
||||
IssueLog::Debug('Init repairment disabled there is a custom portal', LogChannels::DM_CRUD, [$aPortalNames]);
|
||||
$this->bIsRepairmentEnabled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($aNonStandaloneProfiles)){
|
||||
//default configuration in the case there are no customized portals
|
||||
$aNonStandaloneProfiles = [ POWER_USER_PORTAL_PROFILE_NAME => PORTAL_PROFILE_NAME ];
|
||||
}
|
||||
|
||||
if (! is_array($aNonStandaloneProfiles)){
|
||||
\IssueLog::Error(sprintf("%s is badly configured. it should be an array.", self::USERPROFILE_REPAIR_ITOP_PARAM_NAME),LogChannels::DM_CRUD, [self::USERPROFILE_REPAIR_ITOP_PARAM_NAME => $aNonStandaloneProfiles]);
|
||||
$this->bIsRepairmentEnabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($aNonStandaloneProfiles)){
|
||||
//Feature specifically disabled in itop configuration
|
||||
IssueLog::Debug('Init repairment disabled by conf on purpose', LogChannels::DM_CRUD);
|
||||
$this->bIsRepairmentEnabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$this->FetchRepairingProfileIds($aNonStandaloneProfiles);
|
||||
}
|
||||
|
||||
public function FetchRepairingProfileIds(array $aNonStandaloneProfiles) : void {
|
||||
$aProfiles = [];
|
||||
try {
|
||||
$aProfilesToSearch = array_unique(array_values($aNonStandaloneProfiles));
|
||||
if(($iIndex = array_search(null, $aProfilesToSearch)) !== false) {
|
||||
unset($aProfilesToSearch[$iIndex]);
|
||||
}
|
||||
|
||||
if (1 === count($aProfilesToSearch)){
|
||||
$sInCondition = sprintf('"%s"', array_pop($aProfilesToSearch));
|
||||
} else {
|
||||
$sInCondition = sprintf('"%s"', implode('","', $aProfilesToSearch));
|
||||
}
|
||||
|
||||
$sOql = "SELECT URP_Profiles WHERE name IN ($sInCondition)";
|
||||
$oSearch = \DBSearch::FromOQL($sOql);
|
||||
$oSearch->AllowAllData();
|
||||
$oSet = new \DBObjectSet($oSearch);
|
||||
while(($oProfile = $oSet->Fetch()) != null) {
|
||||
$sProfileName = $oProfile->Get('name');
|
||||
$aProfiles[$sProfileName] = $oProfile->GetKey();
|
||||
}
|
||||
|
||||
$this->aNonStandaloneProfilesMap = [];
|
||||
foreach ($aNonStandaloneProfiles as $sNonStandaloneProfileName => $sRepairProfileName) {
|
||||
if (is_null($sRepairProfileName)) {
|
||||
$this->aNonStandaloneProfilesMap[$sNonStandaloneProfileName] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! array_key_exists($sRepairProfileName, $aProfiles)) {
|
||||
throw new \Exception(sprintf("%s is badly configured. profile $sRepairProfileName does not exist.", self::USERPROFILE_REPAIR_ITOP_PARAM_NAME));
|
||||
}
|
||||
|
||||
$this->aNonStandaloneProfilesMap[$sNonStandaloneProfileName] = [ 'name' => $sRepairProfileName, 'id' => $aProfiles[$sRepairProfileName]];
|
||||
}
|
||||
|
||||
$this->bIsRepairmentEnabled = true;
|
||||
} catch (\Exception $e) {
|
||||
IssueLog::Error('Exception when searching user portal profile', LogChannels::DM_CRUD, [
|
||||
'exception_message' => $e->getMessage(),
|
||||
'exception_stacktrace' => $e->getTraceAsString(),
|
||||
'aProfiles' => $aProfiles,
|
||||
'aNonStandaloneProfiles' => $aNonStandaloneProfiles,
|
||||
]);
|
||||
$this->bIsRepairmentEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function ValidateThenRepairOrWarn(\User $oUser) : void
|
||||
{
|
||||
$oCurrentUserProfileSet = $oUser->Get('profile_list');
|
||||
if ($oCurrentUserProfileSet->Count() === 1){
|
||||
IssueLog::Debug('ValidateThenRepairOrWarn one profile found', LogChannels::DM_CRUD);
|
||||
$oProfile = $oCurrentUserProfileSet->Fetch();
|
||||
|
||||
$this->RepairUserChangesOrWarn($oUser, $oProfile->Get('profile'));
|
||||
}
|
||||
}
|
||||
|
||||
public function RepairUserChangesOrWarn(\User $oUser, string $sSingleProfileName) : void {
|
||||
IssueLog::Debug('RepairUserChangesOrWarn', LogChannels::DM_CRUD,
|
||||
[
|
||||
'aNonStandaloneProfilesMap' => $this->aNonStandaloneProfilesMap,
|
||||
'sSingleProfileName' => $sSingleProfileName
|
||||
]
|
||||
);
|
||||
|
||||
if (array_key_exists($sSingleProfileName, $this->aNonStandaloneProfilesMap)) {
|
||||
$aRepairingProfileInfo = $this->aNonStandaloneProfilesMap[$sSingleProfileName];
|
||||
if (is_null($aRepairingProfileInfo)){
|
||||
$sMessage = \Dict::Format("Class:User/NonStandaloneProfileWarning", $sSingleProfileName, $oUser->Get('friendlyname'));
|
||||
throw new \CoreCannotSaveObjectException(array('issues' => [$sMessage], 'class' => get_class($oUser), 'id' => $oUser->GetKey()));
|
||||
} else {
|
||||
//Completing profiles profiles by adding repairing one : by default portal user to a power portal user
|
||||
$oUserProfile = new \URP_UserProfile();
|
||||
$oUserProfile->Set('profileid', $aRepairingProfileInfo['id']);
|
||||
$oCurrentUserProfileSet = $oUser->Get('profile_list');
|
||||
$oCurrentUserProfileSet->AddItem($oUserProfile);
|
||||
$oUser->Set('profile_list', $oCurrentUserProfileSet);
|
||||
$sMessage = \Dict::Format("Class:User/NonStandaloneProfileWarning-ReparationMessage", $sSingleProfileName, $oUser->Get('friendlyname'), $aRepairingProfileInfo['name']);
|
||||
$oUser::SetSessionMessage(get_class($oUser), $oUser->GetKey(), 1, $sMessage, 'WARNING', 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function RepairProfileChangesOrWarn(\User $oUser, ?string $sSingleProfileName, \URP_UserProfile $oURP_UserProfile, string $sRemovedProfileId, $bIsRemoval=false) : void {
|
||||
IssueLog::Debug('RepairUserChangesOrWarn', LogChannels::DM_CRUD,
|
||||
[
|
||||
'aNonStandaloneProfilesMap' => $this->aNonStandaloneProfilesMap,
|
||||
'sSingleProfileName' => $sSingleProfileName
|
||||
]
|
||||
);
|
||||
|
||||
if (is_null($sSingleProfileName)){
|
||||
return;
|
||||
}
|
||||
|
||||
if (array_key_exists($sSingleProfileName, $this->aNonStandaloneProfilesMap)) {
|
||||
$aRepairingProfileInfo = $this->aNonStandaloneProfilesMap[$sSingleProfileName];
|
||||
if (is_null($aRepairingProfileInfo)
|
||||
|| ($aRepairingProfileInfo['id'] === $sRemovedProfileId) //cannot repair by readding same remove profile as it will raise uniqueness rule
|
||||
){
|
||||
$sMessage = \Dict::Format("Class:User/NonStandaloneProfileWarning", $sSingleProfileName, $oUser->Get('friendlyname'));
|
||||
if ($bIsRemoval){
|
||||
$oURP_UserProfile->AddDeleteIssue($sMessage);
|
||||
} else {
|
||||
throw new \CoreCannotSaveObjectException(array('issues' => [$sMessage], 'class' => get_class($oURP_UserProfile), 'id' => $oURP_UserProfile->GetKey()));
|
||||
}
|
||||
} else {
|
||||
//Completing profiles profiles by adding repairing one : by default portal user to a power portal user
|
||||
$oUserProfile = new \URP_UserProfile();
|
||||
$oUserProfile->Set('profileid', $aRepairingProfileInfo['id']);
|
||||
$oCurrentUserProfileSet = $oUser->Get('profile_list');
|
||||
$oCurrentUserProfileSet->AddItem($oUserProfile);
|
||||
$oUser->Set('profile_list', $oCurrentUserProfileSet);
|
||||
$oUser->DBWrite();
|
||||
|
||||
$sMessage = \Dict::Format("Class:User/NonStandaloneProfileWarning-ReparationMessage", $sSingleProfileName, $oUser->Get('friendlyname'), $aRepairingProfileInfo['name']);
|
||||
$oURP_UserProfile::SetSessionMessage(get_class($oURP_UserProfile), $oURP_UserProfile->GetKey(), 1, $sMessage, 'WARNING', 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,11 +58,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:UserRequest/Attribute:status+' => '',
|
||||
'Class:UserRequest/Attribute:status/Value:new' => '新建',
|
||||
'Class:UserRequest/Attribute:status/Value:new+' => '',
|
||||
'Class:UserRequest/Attribute:status/Value:escalated_tto' => '已升级响应时间',
|
||||
'Class:UserRequest/Attribute:status/Value:escalated_tto' => '已升级TTO',
|
||||
'Class:UserRequest/Attribute:status/Value:escalated_tto+' => '',
|
||||
'Class:UserRequest/Attribute:status/Value:assigned' => '已分配',
|
||||
'Class:UserRequest/Attribute:status/Value:assigned+' => '',
|
||||
'Class:UserRequest/Attribute:status/Value:escalated_ttr' => '已升级解决时间',
|
||||
'Class:UserRequest/Attribute:status/Value:escalated_ttr' => '已升级TTR',
|
||||
'Class:UserRequest/Attribute:status/Value:escalated_ttr+' => '',
|
||||
'Class:UserRequest/Attribute:status/Value:waiting_for_approval' => '等待批准',
|
||||
'Class:UserRequest/Attribute:status/Value:waiting_for_approval+' => '',
|
||||
@@ -90,8 +90,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:UserRequest/Attribute:impact/Value:3+' => '',
|
||||
'Class:UserRequest/Attribute:priority' => '优先级',
|
||||
'Class:UserRequest/Attribute:priority+' => '',
|
||||
'Class:UserRequest/Attribute:priority/Value:1' => '非常高',
|
||||
'Class:UserRequest/Attribute:priority/Value:1+' => '非常高',
|
||||
'Class:UserRequest/Attribute:priority/Value:1' => '紧急',
|
||||
'Class:UserRequest/Attribute:priority/Value:1+' => '紧急',
|
||||
'Class:UserRequest/Attribute:priority/Value:2' => '高',
|
||||
'Class:UserRequest/Attribute:priority/Value:2+' => '高',
|
||||
'Class:UserRequest/Attribute:priority/Value:3' => '中',
|
||||
@@ -100,8 +100,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:UserRequest/Attribute:priority/Value:4+' => '低',
|
||||
'Class:UserRequest/Attribute:urgency' => '紧急度',
|
||||
'Class:UserRequest/Attribute:urgency+' => '',
|
||||
'Class:UserRequest/Attribute:urgency/Value:1' => '非常高',
|
||||
'Class:UserRequest/Attribute:urgency/Value:1+' => '非常高',
|
||||
'Class:UserRequest/Attribute:urgency/Value:1' => '紧急',
|
||||
'Class:UserRequest/Attribute:urgency/Value:1+' => '紧急',
|
||||
'Class:UserRequest/Attribute:urgency/Value:2' => '高',
|
||||
'Class:UserRequest/Attribute:urgency/Value:2+' => '高',
|
||||
'Class:UserRequest/Attribute:urgency/Value:3' => '中',
|
||||
@@ -134,7 +134,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:UserRequest/Attribute:servicesubcategory_id+' => '',
|
||||
'Class:UserRequest/Attribute:servicesubcategory_name' => '子服务名称',
|
||||
'Class:UserRequest/Attribute:servicesubcategory_name+' => '',
|
||||
'Class:UserRequest/Attribute:escalation_flag' => '是否升级',
|
||||
'Class:UserRequest/Attribute:escalation_flag' => '升级标签',
|
||||
'Class:UserRequest/Attribute:escalation_flag+' => '',
|
||||
'Class:UserRequest/Attribute:escalation_flag/Value:no' => '否',
|
||||
'Class:UserRequest/Attribute:escalation_flag/Value:no+' => '否',
|
||||
@@ -150,30 +150,30 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:UserRequest/Attribute:last_pending_date+' => '',
|
||||
'Class:UserRequest/Attribute:cumulatedpending' => '累计待定',
|
||||
'Class:UserRequest/Attribute:cumulatedpending+' => '',
|
||||
'Class:UserRequest/Attribute:tto' => '响应时间',
|
||||
'Class:UserRequest/Attribute:tto' => 'TTO',
|
||||
'Class:UserRequest/Attribute:tto+' => '',
|
||||
'Class:UserRequest/Attribute:ttr' => '解决时间',
|
||||
'Class:UserRequest/Attribute:ttr' => 'TTR',
|
||||
'Class:UserRequest/Attribute:ttr+' => '',
|
||||
'Class:UserRequest/Attribute:tto_escalation_deadline' => '响应时间截止',
|
||||
'Class:UserRequest/Attribute:tto_escalation_deadline' => 'TTO截止日期',
|
||||
'Class:UserRequest/Attribute:tto_escalation_deadline+' => '',
|
||||
'Class:UserRequest/Attribute:sla_tto_passed' => '超过SLA响应时间',
|
||||
'Class:UserRequest/Attribute:sla_tto_passed' => 'SLA TTO 合格',
|
||||
'Class:UserRequest/Attribute:sla_tto_passed+' => '',
|
||||
'Class:UserRequest/Attribute:sla_tto_over' => 'SLA响应时间超过',
|
||||
'Class:UserRequest/Attribute:sla_tto_over' => 'SLA TTO 超时',
|
||||
'Class:UserRequest/Attribute:sla_tto_over+' => '',
|
||||
'Class:UserRequest/Attribute:ttr_escalation_deadline' => '解决时间截止',
|
||||
'Class:UserRequest/Attribute:ttr_escalation_deadline' => 'TTR截止日期',
|
||||
'Class:UserRequest/Attribute:ttr_escalation_deadline+' => '',
|
||||
'Class:UserRequest/Attribute:sla_ttr_passed' => '超过SLA解决时间',
|
||||
'Class:UserRequest/Attribute:sla_ttr_passed' => 'SLA TTR 合格',
|
||||
'Class:UserRequest/Attribute:sla_ttr_passed+' => '',
|
||||
'Class:UserRequest/Attribute:sla_ttr_over' => 'SLA解决时间超过',
|
||||
'Class:UserRequest/Attribute:sla_ttr_over' => 'SLA TTR 超时',
|
||||
'Class:UserRequest/Attribute:sla_ttr_over+' => '',
|
||||
'Class:UserRequest/Attribute:time_spent' => '耗时',
|
||||
'Class:UserRequest/Attribute:time_spent+' => '',
|
||||
'Class:UserRequest/Attribute:resolution_code' => '解决代码',
|
||||
'Class:UserRequest/Attribute:resolution_code' => '解决编码',
|
||||
'Class:UserRequest/Attribute:resolution_code+' => '',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:assistance' => '帮助',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:assistance+' => '帮助',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:bug fixed' => '缺陷修复',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:bug fixed+' => '缺陷修复',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:bug fixed' => 'bug修复',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:bug fixed+' => 'bug修复',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:hardware repair' => '硬件维修',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:hardware repair+' => '硬件维修',
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:other' => '其它',
|
||||
|
||||
@@ -62,11 +62,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:UserRequest/Attribute:status+' => '',
|
||||
'Class:UserRequest/Attribute:status/Value:new' => '新建',
|
||||
'Class:UserRequest/Attribute:status/Value:new+' => '',
|
||||
'Class:UserRequest/Attribute:status/Value:escalated_tto' => '已升级响应时间',
|
||||
'Class:UserRequest/Attribute:status/Value:escalated_tto' => '已升级TTO',
|
||||
'Class:UserRequest/Attribute:status/Value:escalated_tto+' => '',
|
||||
'Class:UserRequest/Attribute:status/Value:assigned' => '已分配',
|
||||
'Class:UserRequest/Attribute:status/Value:assigned+' => '',
|
||||
'Class:UserRequest/Attribute:status/Value:escalated_ttr' => '已升级解决时间',
|
||||
'Class:UserRequest/Attribute:status/Value:escalated_ttr' => '已升级TTR',
|
||||
'Class:UserRequest/Attribute:status/Value:escalated_ttr+' => '',
|
||||
'Class:UserRequest/Attribute:status/Value:waiting_for_approval' => '等待批准',
|
||||
'Class:UserRequest/Attribute:status/Value:waiting_for_approval+' => '',
|
||||
@@ -156,21 +156,21 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:UserRequest/Attribute:last_pending_date+' => '',
|
||||
'Class:UserRequest/Attribute:cumulatedpending' => '累计待定',
|
||||
'Class:UserRequest/Attribute:cumulatedpending+' => '',
|
||||
'Class:UserRequest/Attribute:tto' => '响应时间',
|
||||
'Class:UserRequest/Attribute:tto+' => '',
|
||||
'Class:UserRequest/Attribute:ttr' => '解决时间',
|
||||
'Class:UserRequest/Attribute:ttr+' => '',
|
||||
'Class:UserRequest/Attribute:tto_escalation_deadline' => '响应时间期限',
|
||||
'Class:UserRequest/Attribute:tto' => 'TTO',
|
||||
'Class:UserRequest/Attribute:tto+' => '响应时间',
|
||||
'Class:UserRequest/Attribute:ttr' => 'TTR',
|
||||
'Class:UserRequest/Attribute:ttr+' => '解决时限',
|
||||
'Class:UserRequest/Attribute:tto_escalation_deadline' => 'TTO截止日期',
|
||||
'Class:UserRequest/Attribute:tto_escalation_deadline+' => '',
|
||||
'Class:UserRequest/Attribute:sla_tto_passed' => '超过SLA响应时间',
|
||||
'Class:UserRequest/Attribute:sla_tto_passed' => 'SLA TTO 合格',
|
||||
'Class:UserRequest/Attribute:sla_tto_passed+' => '',
|
||||
'Class:UserRequest/Attribute:sla_tto_over' => 'SLA响应时间结束',
|
||||
'Class:UserRequest/Attribute:sla_tto_over' => 'SLA TTO 超时',
|
||||
'Class:UserRequest/Attribute:sla_tto_over+' => '',
|
||||
'Class:UserRequest/Attribute:ttr_escalation_deadline' => '解决时间期限',
|
||||
'Class:UserRequest/Attribute:ttr_escalation_deadline' => 'TTR截止日期',
|
||||
'Class:UserRequest/Attribute:ttr_escalation_deadline+' => '',
|
||||
'Class:UserRequest/Attribute:sla_ttr_passed' => '超过SLA解决时间',
|
||||
'Class:UserRequest/Attribute:sla_ttr_passed' => 'SLA TTR 合格',
|
||||
'Class:UserRequest/Attribute:sla_ttr_passed+' => '',
|
||||
'Class:UserRequest/Attribute:sla_ttr_over' => 'SLA解决时间结束',
|
||||
'Class:UserRequest/Attribute:sla_ttr_over' => 'SLA TTR 超时',
|
||||
'Class:UserRequest/Attribute:sla_ttr_over+' => '',
|
||||
'Class:UserRequest/Attribute:time_spent' => '耗时',
|
||||
'Class:UserRequest/Attribute:time_spent+' => '',
|
||||
@@ -192,7 +192,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:UserRequest/Attribute:resolution_code/Value:training+' => '培训',
|
||||
'Class:UserRequest/Attribute:solution' => '解决方案',
|
||||
'Class:UserRequest/Attribute:solution+' => '',
|
||||
'Class:UserRequest/Attribute:pending_reason' => '待定的原因',
|
||||
'Class:UserRequest/Attribute:pending_reason' => '待定原因',
|
||||
'Class:UserRequest/Attribute:pending_reason+' => '',
|
||||
'Class:UserRequest/Attribute:parent_request_id' => '父级需求',
|
||||
'Class:UserRequest/Attribute:parent_request_id+' => '',
|
||||
|
||||
@@ -99,9 +99,9 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'Class:Contract/Attribute:organization_name' => 'Nom client',
|
||||
'Class:Contract/Attribute:organization_name+' => 'Nom commun',
|
||||
'Class:Contract/Attribute:contacts_list' => 'Contacts',
|
||||
'Class:Contract/Attribute:contacts_list+' => 'Tous les contacts for ce contrat client',
|
||||
'Class:Contract/Attribute:contacts_list+' => 'Tous les contacts pour ce contrat client',
|
||||
'Class:Contract/Attribute:documents_list' => 'Documents',
|
||||
'Class:Contract/Attribute:documents_list+' => 'Tous les documents for ce contrat client',
|
||||
'Class:Contract/Attribute:documents_list+' => 'Tous les documents pour ce contrat client',
|
||||
'Class:Contract/Attribute:description' => 'Description',
|
||||
'Class:Contract/Attribute:description+' => '',
|
||||
'Class:Contract/Attribute:start_date' => 'Date de début',
|
||||
|
||||
@@ -389,8 +389,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:SLT/Attribute:name+' => '',
|
||||
'Class:SLT/Attribute:priority' => '优先级',
|
||||
'Class:SLT/Attribute:priority+' => '',
|
||||
'Class:SLT/Attribute:priority/Value:1' => '非常高',
|
||||
'Class:SLT/Attribute:priority/Value:1+' => '非常高',
|
||||
'Class:SLT/Attribute:priority/Value:1' => '紧急',
|
||||
'Class:SLT/Attribute:priority/Value:1+' => '紧急',
|
||||
'Class:SLT/Attribute:priority/Value:2' => '高',
|
||||
'Class:SLT/Attribute:priority/Value:2+' => '高',
|
||||
'Class:SLT/Attribute:priority/Value:3' => '中',
|
||||
@@ -403,15 +403,15 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:SLT/Attribute:request_type/Value:incident+' => '事件',
|
||||
'Class:SLT/Attribute:request_type/Value:service_request' => '服务需求',
|
||||
'Class:SLT/Attribute:request_type/Value:service_request+' => '服务需求',
|
||||
'Class:SLT/Attribute:metric' => '指标',
|
||||
'Class:SLT/Attribute:metric' => '衡量指标',
|
||||
'Class:SLT/Attribute:metric+' => '',
|
||||
'Class:SLT/Attribute:metric/Value:tto' => '响应时间',
|
||||
'Class:SLT/Attribute:metric/Value:tto' => 'TTO',
|
||||
'Class:SLT/Attribute:metric/Value:tto+' => '响应时间',
|
||||
'Class:SLT/Attribute:metric/Value:ttr' => '解决时间',
|
||||
'Class:SLT/Attribute:metric/Value:ttr+' => '解决时间',
|
||||
'Class:SLT/Attribute:metric/Value:ttr' => 'TTR',
|
||||
'Class:SLT/Attribute:metric/Value:ttr+' => '解决时限',
|
||||
'Class:SLT/Attribute:value' => '值',
|
||||
'Class:SLT/Attribute:value+' => '',
|
||||
'Class:SLT/Attribute:unit' => '单位',
|
||||
'Class:SLT/Attribute:unit' => '度量单位',
|
||||
'Class:SLT/Attribute:unit+' => '',
|
||||
'Class:SLT/Attribute:unit/Value:hours' => '小时',
|
||||
'Class:SLT/Attribute:unit/Value:hours+' => '小时',
|
||||
|
||||
@@ -157,6 +157,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:ProviderContract/Attribute:contracttype_id+' => '',
|
||||
'Class:ProviderContract/Attribute:contracttype_name' => 'Vertragstyp-Name',
|
||||
'Class:ProviderContract/Attribute:contracttype_name+' => '',
|
||||
'Class:ProviderContract/Attribute:services_list' => 'Services',
|
||||
'Class:ProviderContract/Attribute:services_list+' => 'Alle für diesen Vertrag erworbenen Services',
|
||||
));
|
||||
|
||||
//
|
||||
|
||||
@@ -169,6 +169,8 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Class:ProviderContract/Attribute:contracttype_id+' => '',
|
||||
'Class:ProviderContract/Attribute:contracttype_name' => 'Contract type name',
|
||||
'Class:ProviderContract/Attribute:contracttype_name+' => '',
|
||||
'Class:ProviderContract/Attribute:services_list' => 'Services',
|
||||
'Class:ProviderContract/Attribute:services_list+' => 'All the services purchased with this contract',
|
||||
));
|
||||
|
||||
//
|
||||
|
||||
@@ -157,6 +157,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'Class:ProviderContract/Attribute:contracttype_id+' => '',
|
||||
'Class:ProviderContract/Attribute:contracttype_name' => 'Nom Type de contrat',
|
||||
'Class:ProviderContract/Attribute:contracttype_name+' => '',
|
||||
'Class:ProviderContract/Attribute:services_list' => 'Services',
|
||||
'Class:ProviderContract/Attribute:services_list+' => 'Tous les services achetés par ce contrat',
|
||||
));
|
||||
|
||||
//
|
||||
|
||||
@@ -362,8 +362,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:SLT/Attribute:name+' => '',
|
||||
'Class:SLT/Attribute:priority' => '优先级',
|
||||
'Class:SLT/Attribute:priority+' => '',
|
||||
'Class:SLT/Attribute:priority/Value:1' => '非常高',
|
||||
'Class:SLT/Attribute:priority/Value:1+' => '非常高',
|
||||
'Class:SLT/Attribute:priority/Value:1' => '紧急',
|
||||
'Class:SLT/Attribute:priority/Value:1+' => '紧急',
|
||||
'Class:SLT/Attribute:priority/Value:2' => '高',
|
||||
'Class:SLT/Attribute:priority/Value:2+' => '高',
|
||||
'Class:SLT/Attribute:priority/Value:3' => '中',
|
||||
@@ -376,15 +376,15 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:SLT/Attribute:request_type/Value:incident+' => '事件',
|
||||
'Class:SLT/Attribute:request_type/Value:service_request' => '服务需求',
|
||||
'Class:SLT/Attribute:request_type/Value:service_request+' => '服务需求',
|
||||
'Class:SLT/Attribute:metric' => '指标',
|
||||
'Class:SLT/Attribute:metric' => '衡量指标',
|
||||
'Class:SLT/Attribute:metric+' => '',
|
||||
'Class:SLT/Attribute:metric/Value:tto' => '响应时间',
|
||||
'Class:SLT/Attribute:metric/Value:tto' => 'TTO',
|
||||
'Class:SLT/Attribute:metric/Value:tto+' => '响应时间',
|
||||
'Class:SLT/Attribute:metric/Value:ttr' => '解决时间',
|
||||
'Class:SLT/Attribute:metric/Value:ttr+' => '解决时间',
|
||||
'Class:SLT/Attribute:metric/Value:ttr' => 'TTR',
|
||||
'Class:SLT/Attribute:metric/Value:ttr+' => '解决时限',
|
||||
'Class:SLT/Attribute:value' => '值',
|
||||
'Class:SLT/Attribute:value+' => '',
|
||||
'Class:SLT/Attribute:unit' => '单位',
|
||||
'Class:SLT/Attribute:unit' => '度量单位',
|
||||
'Class:SLT/Attribute:unit+' => '',
|
||||
'Class:SLT/Attribute:unit/Value:hours' => '小时',
|
||||
'Class:SLT/Attribute:unit/Value:hours+' => '小时',
|
||||
|
||||
@@ -251,7 +251,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:DocumentNote' => '文档笔记',
|
||||
'Class:DocumentNote+' => '',
|
||||
'Class:DocumentNote/Attribute:text' => '文本',
|
||||
'Class:DocumentNote/Attribute:text' => '正文',
|
||||
'Class:DocumentNote/Attribute:text+' => '',
|
||||
));
|
||||
|
||||
|
||||
@@ -240,9 +240,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:cmdbAbstractObject/Method:ApplyStimulus+' => 'Apply the specified stimulus to the current object',
|
||||
'Class:cmdbAbstractObject/Method:ApplyStimulus/Param:1' => 'Stimulus code',
|
||||
'Class:cmdbAbstractObject/Method:ApplyStimulus/Param:1+' => 'A valid stimulus code for the current class',
|
||||
'Class:ResponseTicketTTO/Interface:iMetricComputer' => '响应时间',
|
||||
'Class:ResponseTicketTTO/Interface:iMetricComputer' => 'TTO',
|
||||
'Class:ResponseTicketTTO/Interface:iMetricComputer+' => 'SLT 的响应时间',
|
||||
'Class:ResponseTicketTTR/Interface:iMetricComputer' => '解决时间',
|
||||
'Class:ResponseTicketTTR/Interface:iMetricComputer+' => 'SLT 的解决时间',
|
||||
'Class:ResponseTicketTTR/Interface:iMetricComputer' => 'TTR',
|
||||
'Class:ResponseTicketTTR/Interface:iMetricComputer+' => 'SLT 的解决时限',
|
||||
));
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Core:AttributeTagSet' => 'List of tags',
|
||||
'Core:AttributeTagSet+' => '',
|
||||
'Core:AttributeSet:placeholder' => 'click to add',
|
||||
'Core:Placeholder:CannotBeResolved' => '(%1$s : cannot be resolved)',
|
||||
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromClass' => '%1$s (%2$s)',
|
||||
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromOneChildClass' => '%1$s (%2$s from %3$s)',
|
||||
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromSeveralChildClasses' => '%1$s (%2$s from child classes)',
|
||||
|
||||
@@ -452,6 +452,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
We hope you’ll enjoy this version as much as we enjoyed imagining and creating it.</div>
|
||||
|
||||
<div>Customize your '.ITOP_APPLICATION.' preferences for a personalized experience.</div>',
|
||||
'UI:WelcomePopup:Button:Acknowledge' => 'Ok, discard this message',
|
||||
'UI:WelcomeMenu:AllOpenRequests' => 'Open requests: %1$d',
|
||||
'UI:WelcomeMenu:MyCalls' => 'My requests',
|
||||
'UI:WelcomeMenu:OpenIncidents' => 'Open incidents: %1$d',
|
||||
@@ -837,7 +838,7 @@ We hope you’ll enjoy this version as much as we enjoyed imagining and creating
|
||||
'UI:RunQuery:DevelopedOQLCount' => 'Developed OQL for count',
|
||||
'UI:RunQuery:ResultSQLCount' => 'Resulting SQL for count',
|
||||
'UI:RunQuery:ResultSQL' => 'Resulting SQL',
|
||||
'UI:RunQuery:Error' => 'An error occured while running the query',
|
||||
'UI:RunQuery:Error' => 'An error occured while running the query: %1$s',
|
||||
'UI:Query:UrlForExcel' => 'URL to use for MS-Excel web queries',
|
||||
'UI:Query:UrlV1' => 'The list of fields has been left unspecified. The page <em>export-V2.php</em> cannot be invoked without this information. Therefore, the URL suggested here below points to the legacy page: <em>export.php</em>. This legacy version of the export has the following limitation: the list of exported fields may vary depending on the output format and the data model of '.ITOP_APPLICATION_SHORT.'. <br/>Should you want to guarantee that the list of exported columns will remain stable on the long run, then you must specify a value for the attribute "Fields" and use the page <em>export-V2.php</em>.',
|
||||
'UI:Schema:Title' => ITOP_APPLICATION_SHORT.' objects schema',
|
||||
|
||||
@@ -444,6 +444,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
Esperamos distrute de esta versión tanto como nosotros la imaginamos y creamos.</div>
|
||||
|
||||
<div>Configure las preferencias de '.ITOP_APPLICATION.' para una experiencia personalizada.</div>',
|
||||
'UI:WelcomePopup:Button:Acknowledge' => 'Ok, descartar este mensaje',
|
||||
'UI:WelcomeMenu:AllOpenRequests' => 'Requerimientos Abiertos: %1$d',
|
||||
'UI:WelcomeMenu:MyCalls' => 'Mis Requerimientos',
|
||||
'UI:WelcomeMenu:OpenIncidents' => 'Incidentes Abiertos: %1$d',
|
||||
|
||||
@@ -39,6 +39,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'Core:AttributeTagSet' => 'Liste d\'étiquettes',
|
||||
'Core:AttributeTagSet+' => '',
|
||||
'Core:AttributeSet:placeholder' => 'cliquer pour ajouter',
|
||||
'Core:Placeholder:CannotBeResolved' => '(%1$s : non remplacé)',
|
||||
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromClass' => '%1$s (%2$s)',
|
||||
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromOneChildClass' => '%1$s (%2$s de la classe %3$s)',
|
||||
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromSeveralChildClasses' => '%1$s (%2$s d\'une sous-classe)',
|
||||
|
||||
@@ -433,6 +433,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
Nous espérons que vous aimerez cette version autant que nous avons eu du plaisir à l\'imaginer et à la créer.</div>
|
||||
|
||||
<div>Configurez vos préférences '.ITOP_APPLICATION.' pour une expérience personnalisée.</div>',
|
||||
'UI:WelcomePopup:Button:Acknowledge' => 'Ok, supprimer ce message',
|
||||
'UI:WelcomeMenu:AllOpenRequests' => 'Requêtes en cours: %1$d',
|
||||
'UI:WelcomeMenu:MyCalls' => 'Mes appels support',
|
||||
'UI:WelcomeMenu:OpenIncidents' => 'Incidents en cours: %1$d',
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
'Core:DeletedObjectLabel' => '%1s (törölve)',
|
||||
'Core:DeletedObjectLabel' => '%1$s (törölve)',
|
||||
'Core:DeletedObjectTip' => 'A %1$s objektum törölve (%2$s)',
|
||||
'Core:UnknownObjectLabel' => 'Objektum nem található (osztály: %1$s, id: %2$d)',
|
||||
'Core:UnknownObjectTip' => 'Az objektumot nem sikerült megtalálni. Lehet, hogy már törölték egy ideje, és a naplót azóta törölték.',
|
||||
|
||||
@@ -509,7 +509,7 @@ Reméljük, hogy ezt a verziót ugyanúgy kedvelni fogja, mint ahogy mi élvezt
|
||||
'UI:Error:2ParametersMissing' => 'Hiba: a következő paramétereket meg kell adni ennél a műveletnél: %1$s és %2$s.',
|
||||
'UI:Error:3ParametersMissing' => 'Hiba: a következő paramétereket meg kell adni ennél a műveletnél: %1$s, %2$s és %3$s.',
|
||||
'UI:Error:4ParametersMissing' => 'Hiba: a következő paramétereket meg kell adni ennél a műveletnél: %1$s, %2$s, %3$s és %4$s.',
|
||||
'UI:Error:IncorrectOQLQuery_Message' => 'Hiba: nem megfelelő OQL lekérdezés: %1$',
|
||||
'UI:Error:IncorrectOQLQuery_Message' => 'Hiba: nem megfelelő OQL lekérdezés: %1$s',
|
||||
'UI:Error:AnErrorOccuredWhileRunningTheQuery_Message' => 'Hiba történt a lekérdezés futtatása közben: %1$s',
|
||||
'UI:Error:ObjectAlreadyUpdated' => 'Hiba: az objketum már korábban módosításra került.',
|
||||
'UI:Error:ObjectCannotBeUpdated' => 'Hiba: az objektum nem frissíthető.',
|
||||
@@ -715,7 +715,7 @@ Reméljük, hogy ezt a verziót ugyanúgy kedvelni fogja, mint ahogy mi élvezt
|
||||
'UI:CSVReport-Value-Issue-Null' => 'A nulla nem engedélyezett',
|
||||
'UI:CSVReport-Value-Issue-NotFound' => 'Az objektum nincs meg',
|
||||
'UI:CSVReport-Value-Issue-FoundMany' => '%1$d egyezés található',
|
||||
'UI:CSVReport-Value-Issue-Readonly' => 'A \'%1$\'s attribútum csak olvasható (jelenlegi érték: %2$s, várható érték: %3$s)',
|
||||
'UI:CSVReport-Value-Issue-Readonly' => 'A \'%1$s attribútum csak olvasható (jelenlegi érték: %2$s, várható érték: %3$s)',
|
||||
'UI:CSVReport-Value-Issue-Format' => 'A bevitel feldolgozása sikertelen: %1$s',
|
||||
'UI:CSVReport-Value-Issue-NoMatch' => 'A \'%1$s\' attribútum nem várt értéket kapott: nincs egyezés, ellenőrizze a beírást',
|
||||
'UI:CSVReport-Value-Issue-AllowedValues' => 'Allowed \'%1$s\' value(s): %2$s~~',
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* @licence http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
Dict::Add('JA JP', 'Japanese', '日本語', array(
|
||||
'Core:DeletedObjectLabel' => '%1s (削除されました)',
|
||||
'Core:DeletedObjectLabel' => '%1$s (削除されました)',
|
||||
'Core:DeletedObjectTip' => 'オブジェクトは削除されました %1$s (%2$s)',
|
||||
'Core:UnknownObjectLabel' => 'オブジェクトは見つかりません (クラス: %1$s, id: %2$d)',
|
||||
'Core:UnknownObjectTip' => 'オブジェクトは見つかりません。しばらく前に削除され、その後ログが削除されたかもしれません。',
|
||||
|
||||
@@ -915,7 +915,7 @@ We hope you’ll enjoy this version as much as we enjoyed imagining and creating
|
||||
'UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class' => '%2$sクラスの%1$dオブジェクトの削除',
|
||||
'UI:Delete:CannotDeleteBecause' => '削除できません: %1$s',
|
||||
'UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible' => '自動的に削除されるべきですが、出来ません。: %1$s',
|
||||
'UI:Delete:MustBeDeletedManuallyButNotPossible' => '手動で削除されるべきですが、出来ません。: %1$',
|
||||
'UI:Delete:MustBeDeletedManuallyButNotPossible' => '手動で削除されるべきですが、出来ません。: %1$s',
|
||||
'UI:Delete:WillBeDeletedAutomatically' => '自動的に削除されます。',
|
||||
'UI:Delete:MustBeDeletedManually' => '手動で削除されるべきです。',
|
||||
'UI:Delete:CannotUpdateBecause_Issue' => '自動的に更新されるべきですが、しかし: %1$s',
|
||||
@@ -1159,8 +1159,7 @@ We hope you’ll enjoy this version as much as we enjoyed imagining and creating
|
||||
'Enum:Undefined' => '未定義',
|
||||
'UI:DurationForm_Days_Hours_Minutes_Seconds' => '%1$s 日 %2$s 時 %3$s 分 %4$s 秒',
|
||||
'UI:ModifyAllPageTitle' => '全てを修正',
|
||||
'UI:Modify_ObjectsOf_Class' => 'Modifying objects of class %1$s~~',
|
||||
'UI:Modify_N_ObjectsOf_Class' => 'クラス%2$Sの%1$dオブジェクトを修正',
|
||||
'UI:Modify_N_ObjectsOf_Class' => 'クラス%2$sの%1$dオブジェクトを修正',
|
||||
'UI:Modify_M_ObjectsOf_Class_OutOf_N' => 'クラス%2$sの%3$d中%1$dを修正',
|
||||
'UI:Menu:ModifyAll' => '修正...',
|
||||
'UI:Menu:ModifyAll_Class' => 'Modify %1$s objects...~~',
|
||||
@@ -1180,7 +1179,7 @@ We hope you’ll enjoy this version as much as we enjoyed imagining and creating
|
||||
'UI:BulkModify_Count_DistinctValues' => '%1$d 個の個別の値:',
|
||||
'UI:BulkModify:Value_Exists_N_Times' => '%1$s, %2$d 回存在',
|
||||
'UI:BulkModify:N_MoreValues' => '%1$d 個以上の値...',
|
||||
'UI:AttemptingToSetAReadOnlyAttribute_Name' => '読み込み専用フィールド %1$にセットしょうとしています。',
|
||||
'UI:AttemptingToSetAReadOnlyAttribute_Name' => '読み込み専用フィールド %1$sにセットしょうとしています。',
|
||||
'UI:FailedToApplyStimuli' => 'アクションは失敗しました。',
|
||||
'UI:StimulusModify_N_ObjectsOf_Class' => '%1$s: クラス%3$sの%2$dオブジェクトを修正',
|
||||
'UI:CaseLogTypeYourTextHere' => 'テキストを入力ください:',
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*/
|
||||
Dict::Add('RU RU', 'Russian', 'Русский', array(
|
||||
'Core:DeletedObjectLabel' => '%1ы (удален)',
|
||||
'Core:DeletedObjectLabel' => '%1$sы (удален)',
|
||||
'Core:DeletedObjectTip' => 'Объект был удален %1$s (%2$s)',
|
||||
'Core:UnknownObjectLabel' => 'Объект не найден (class: %1$s, id: %2$d)',
|
||||
'Core:UnknownObjectTip' => 'Объект не удается найти. Возможно, он был удален некоторое время назад, и журнал с тех пор был очищен.',
|
||||
|
||||
@@ -497,7 +497,7 @@ We hope you’ll enjoy this version as much as we enjoyed imagining and creating
|
||||
'UI:Error:MandatoryTemplateParameter_group_by' => 'Parameter group_by je povinný. Skontrolujte definíciu šablóny zobrazenia.',
|
||||
'UI:Error:InvalidGroupByFields' => 'Neplatný zoznam polí pre skupinu podľa: "%1$s".',
|
||||
'UI:Error:UnsupportedStyleOfBlock' => 'Chyba: nepodporovaný štýl bloku: "%1$s".',
|
||||
'UI:Error:IncorrectLinkDefinition_LinkedClass_Class' => 'Nesprávna definícia spojenia : trieda objektov na manažovanie : %l$s nebol nájdený ako externý kľúč v triede %2$s',
|
||||
'UI:Error:IncorrectLinkDefinition_LinkedClass_Class' => 'Nesprávna definícia spojenia : trieda objektov na manažovanie : %1$s nebol nájdený ako externý kľúč v triede %2$s',
|
||||
'UI:Error:Object_Class_Id_NotFound' => 'Objekt: %1$s:%2$d nebol nájdený.',
|
||||
'UI:Error:WizardCircularReferenceInDependencies' => 'Chyba: Cyklický odkaz v závislostiach medzi poliami, skontrolujte dátový model.',
|
||||
'UI:Error:UploadedFileTooBig' => 'Nahraný súbor je príliš veľký. (Max povolená veľkosť je %1$s). Ak chcete zmeniť tento limit, obráťte sa na správcu ITOP . (Skontrolujte, PHP konfiguráciu pre upload_max_filesize a post_max_size na serveri).',
|
||||
@@ -1301,7 +1301,7 @@ Keď sú priradené spúštačom, každej akcii je dané číslo "príkazu", šp
|
||||
'UI:DashletGroupBy:Prop-GroupBy:DayOfMonth' => 'Deň v mesiaci pre %1$s',
|
||||
'UI:DashletGroupBy:Prop-GroupBy:Select-Hour' => '%1$s (hodina)',
|
||||
'UI:DashletGroupBy:Prop-GroupBy:Select-Month' => '%1$s (mesiac)',
|
||||
'UI:DashletGroupBy:Prop-GroupBy:Select-DayOfWeek' => '%1$ (deň v týžni)',
|
||||
'UI:DashletGroupBy:Prop-GroupBy:Select-DayOfWeek' => '%1$s (deň v týžni)',
|
||||
'UI:DashletGroupBy:Prop-GroupBy:Select-DayOfMonth' => '%1$s (deň v mesiaci)',
|
||||
'UI:DashletGroupBy:MissingGroupBy' => 'Prosím zvoľte pole na ktorom objekty budú zoskupené spolu',
|
||||
'UI:DashletGroupByPie:Label' => 'Koláčový graf',
|
||||
|
||||
@@ -278,7 +278,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
|
||||
'Change:AttName_SetTo_NewValue_PreviousValue_OldValue' => '%1$s\'nin değeri %2$s olarak atandı (önceki değer: %3$s)',
|
||||
'Change:AttName_SetTo' => '%1$s\'nin değeri %2$s olarak atandı',
|
||||
'Change:Text_AppendedTo_AttName' => '%2$s\'ye %1$s eklendi',
|
||||
'Change:AttName_Changed_PreviousValue_OldValue' => '%1$\'nin değeri deiştirildi, önceki değer: %2$s',
|
||||
'Change:AttName_Changed_PreviousValue_OldValue' => '%1$s nin değeri deiştirildi, önceki değer: %2$s',
|
||||
'Change:AttName_Changed' => '%1$s değiştirildi',
|
||||
'Change:AttName_EntryAdded' => '%1$s değiştirilmiş, yeni giriş eklendi.',
|
||||
'Change:State_Changed_NewValue_OldValue' => 'Changed from %2$s to %1$s~~',
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
// Display DataTable
|
||||
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'UI:Datatables:Language:Processing' => '请稍候...',
|
||||
'UI:Datatables:Language:LengthMenu' => '_MENU_ 每页',
|
||||
'UI:Datatables:Language:LengthMenu' => '每页 _MENU_ 项',
|
||||
'UI:Datatables:Language:ZeroRecords' => '未找到相关结果',
|
||||
'UI:Datatables:Language:Info' => '_TOTAL_ 项',
|
||||
'UI:Datatables:Language:Info' => '共 _TOTAL_ 项',
|
||||
'UI:Datatables:Language:InfoEmpty' => '未找到相关信息',
|
||||
'UI:Datatables:Language:EmptyTable' => '表格中暂无数据',
|
||||
'UI:Datatables:Language:Error' => '运行查询时出错',
|
||||
|
||||
@@ -27,8 +27,8 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'UI:Layout:NavigationMenu:MenuFilter:Input:Hint' => 'As correspondências em todos os grupos de menus serão exibidas',
|
||||
'UI:Layout:NavigationMenu:MenuFilter:Placeholder:Hint' => 'Nenhum resultado para este filtro de menu',
|
||||
'UI:Layout:NavigationMenu:UserInfo:WelcomeMessage:Text' => 'Olá %1$s!',
|
||||
'UI:Layout:NavigationMenu:UserInfo:Picture:AltText' => 'Imagem do contato %1$',
|
||||
'UI:Layout:NavigationMenu:UserInfo:Picture:AltText' => 'Imagem do contato %1$s',
|
||||
'UI:Layout:NavigationMenu:UserMenu:Toggler:Label' => 'Abrir menu do usuário',
|
||||
'UI:Layout:NavigationMenu:KeyboardShortcut:FocusFilter' => 'Filtrar entradas de menu',
|
||||
|
||||
));
|
||||
));
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
// Navigation menu
|
||||
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'UI:Layout:NavigationMenu:CompanyLogo:AltText' => '公司logo',
|
||||
|
||||
@@ -20,44 +20,60 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Core:DeletedObjectLabel' => '%1s (已删除)',
|
||||
'Core:DeletedObjectTip' => '对象已被删除于 %1$s (%2$s)',
|
||||
|
||||
'Core:UnknownObjectLabel' => '对象找不到 (class: %1$s, id: %2$d)',
|
||||
'Core:UnknownObjectTip' => 'The object could not be found. It may have been deleted some time ago and the log has been purged since.~~',
|
||||
|
||||
'Core:UniquenessDefaultError' => 'Uniqueness rule \'%1$s\' in error~~',
|
||||
'Core:CheckConsistencyError' => 'Consistency rules not followed: %1$s~~',
|
||||
'Core:CheckValueError' => 'Unexpected value for attribute \'%1$s\' (%2$s) : %3$s~~',
|
||||
|
||||
'Core:AttributeLinkedSet' => '对象数组',
|
||||
'Core:AttributeLinkedSet+' => 'Any kind of objects of the same class or subclass~~',
|
||||
|
||||
'Core:AttributeLinkedSetDuplicatesFound' => 'Duplicates in the \'%1$s\' field : %2$s~~',
|
||||
|
||||
'Core:AttributeDashboard' => '仪表盘',
|
||||
'Core:AttributeDashboard+' => '',
|
||||
|
||||
'Core:AttributePhoneNumber' => '电话号码',
|
||||
'Core:AttributePhoneNumber+' => '',
|
||||
|
||||
'Core:AttributeObsolescenceDate' => '报废日期',
|
||||
'Core:AttributeObsolescenceDate+' => '',
|
||||
|
||||
'Core:AttributeTagSet' => '清单',
|
||||
'Core:AttributeTagSet+' => '',
|
||||
'Core:AttributeSet:placeholder' => '请点击这里添加',
|
||||
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromClass' => '%1$s (%2$s)~~',
|
||||
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromOneChildClass' => '%1$s (%2$s from %3$s)~~',
|
||||
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromSeveralChildClasses' => '%1$s (%2$s from child classes)~~',
|
||||
|
||||
'Core:AttributeCaseLog' => '日志',
|
||||
'Core:AttributeCaseLog+' => '',
|
||||
|
||||
'Core:AttributeMetaEnum' => 'Computed enum~~',
|
||||
'Core:AttributeMetaEnum+' => '~~',
|
||||
|
||||
'Core:AttributeLinkedSetIndirect' => '对象数组(N-N)',
|
||||
'Core:AttributeLinkedSetIndirect+' => 'Any kind of objects [subclass] of the same class~~',
|
||||
|
||||
'Core:AttributeInteger' => '整数',
|
||||
'Core:AttributeInteger+' => '整数值(可以为负)',
|
||||
|
||||
'Core:AttributeDecimal' => '小数',
|
||||
'Core:AttributeDecimal+' => '小数(可以为负)',
|
||||
|
||||
'Core:AttributeBoolean' => '布尔',
|
||||
'Core:AttributeBoolean+' => '布尔',
|
||||
'Core:AttributeBoolean+' => '',
|
||||
'Core:AttributeBoolean/Value:null' => '',
|
||||
'Core:AttributeBoolean/Value:yes' => '是',
|
||||
'Core:AttributeBoolean/Value:no' => '否',
|
||||
|
||||
'Core:AttributeArchiveFlag' => '是否归档',
|
||||
'Core:AttributeArchiveFlag/Value:yes' => '是',
|
||||
'Core:AttributeArchiveFlag/Value:yes+' => '此对象仅在归档模式可见',
|
||||
@@ -66,6 +82,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Core:AttributeArchiveFlag/Label+' => '',
|
||||
'Core:AttributeArchiveDate/Label' => '归档日期',
|
||||
'Core:AttributeArchiveDate/Label+' => '',
|
||||
|
||||
'Core:AttributeObsolescenceFlag' => '是否废弃',
|
||||
'Core:AttributeObsolescenceFlag/Value:yes' => '是',
|
||||
'Core:AttributeObsolescenceFlag/Value:yes+' => 'This object is excluded from the impact analysis, and hidden from search results~~',
|
||||
@@ -74,38 +91,54 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Core:AttributeObsolescenceFlag/Label+' => 'Computed dynamically on other attributes~~',
|
||||
'Core:AttributeObsolescenceDate/Label' => '废弃时间',
|
||||
'Core:AttributeObsolescenceDate/Label+' => 'Approximative date at which the object has been considered obsolete~~',
|
||||
|
||||
'Core:AttributeString' => '字符串',
|
||||
'Core:AttributeString+' => '字符串',
|
||||
|
||||
'Core:AttributeClass' => '类',
|
||||
'Core:AttributeClass+' => '类别',
|
||||
'Core:AttributeClass+' => '',
|
||||
|
||||
'Core:AttributeApplicationLanguage' => '用户语言',
|
||||
'Core:AttributeApplicationLanguage+' => '语言和国家地区(EN US)',
|
||||
|
||||
'Core:AttributeFinalClass' => '类 (auto)',
|
||||
'Core:AttributeFinalClass+' => 'Real class of the object (automatically created by the core)',
|
||||
|
||||
'Core:AttributePassword' => '密码',
|
||||
'Core:AttributePassword+' => '外部设备的密码',
|
||||
|
||||
'Core:AttributeEncryptedString' => '加密字符串',
|
||||
'Core:AttributeEncryptedString+' => 'String encrypted with a local key~~',
|
||||
'Core:AttributeEncryptUnknownLibrary' => '未知的加密库 (%1$s)',
|
||||
'Core:AttributeEncryptFailedToDecrypt' => '** 解密错误 **',
|
||||
|
||||
'Core:AttributeText' => '文本',
|
||||
'Core:AttributeText+' => '多行字符串',
|
||||
|
||||
'Core:AttributeHTML' => 'HTML',
|
||||
'Core:AttributeHTML+' => 'HTML字符串',
|
||||
|
||||
'Core:AttributeEmailAddress' => '邮箱地址',
|
||||
'Core:AttributeEmailAddress+' => '邮箱地址',
|
||||
|
||||
'Core:AttributeIPAddress' => 'IP地址',
|
||||
'Core:AttributeIPAddress+' => 'IP地址',
|
||||
|
||||
'Core:AttributeOQL' => 'OQL',
|
||||
'Core:AttributeOQL+' => 'Object Query Langage expression~~',
|
||||
|
||||
'Core:AttributeEnum' => 'Enum~~',
|
||||
'Core:AttributeEnum+' => 'List of predefined alphanumeric strings~~',
|
||||
|
||||
'Core:AttributeTemplateString' => '字符模板',
|
||||
'Core:AttributeTemplateString+' => '包含占位符的字符串',
|
||||
|
||||
'Core:AttributeTemplateText' => '文字模板',
|
||||
'Core:AttributeTemplateText+' => '包含占位符的文本',
|
||||
|
||||
'Core:AttributeTemplateHTML' => 'HTML模板',
|
||||
'Core:AttributeTemplateHTML+' => 'HTML containing placeholders~~',
|
||||
|
||||
'Core:AttributeDateTime' => '日期/时间',
|
||||
'Core:AttributeDateTime+' => 'Date and time (年-月-日 时:分:秒)',
|
||||
'Core:AttributeDateTime?SmartSearch' => '
|
||||
@@ -123,6 +156,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
<p>
|
||||
如果不写具体时间,则默认00:00:00
|
||||
</p>',
|
||||
|
||||
'Core:AttributeDate' => '日期',
|
||||
'Core:AttributeDate+' => '日期 (年-月-日)',
|
||||
'Core:AttributeDate?SmartSearch' => '
|
||||
@@ -137,30 +171,43 @@ Operators:<br/>
|
||||
<b><</b><em>日期</em><br/>
|
||||
<b>[</b><em>日期</em>,<em>日期</em><b>]</b>
|
||||
</p>',
|
||||
|
||||
'Core:AttributeDeadline' => '截止日期',
|
||||
'Core:AttributeDeadline+' => '日期, 显示与当前的相对时间',
|
||||
|
||||
'Core:AttributeExternalKey' => '外键',
|
||||
'Core:AttributeExternalKey+' => 'External (or foreign) key~~',
|
||||
|
||||
'Core:AttributeHierarchicalKey' => 'Hierarchical Key~~',
|
||||
'Core:AttributeHierarchicalKey+' => 'External (or foreign) key to the parent~~',
|
||||
|
||||
'Core:AttributeExternalField' => '外部字段',
|
||||
'Core:AttributeExternalField+' => 'Field mapped to an external key~~',
|
||||
|
||||
'Core:AttributeURL' => 'URL',
|
||||
'Core:AttributeURL+' => 'Absolute or relative URL as a text string~~',
|
||||
|
||||
'Core:AttributeBlob' => 'Blob',
|
||||
'Core:AttributeBlob+' => '任何二进制内容(文档)',
|
||||
|
||||
'Core:AttributeOneWayPassword' => '单向密码',
|
||||
'Core:AttributeOneWayPassword+' => '单向加密(或哈希) 的密码',
|
||||
|
||||
'Core:AttributeTable' => '表',
|
||||
'Core:AttributeTable+' => '带索引的二维数组',
|
||||
|
||||
'Core:AttributePropertySet' => '属性',
|
||||
'Core:AttributePropertySet+' => 'List of untyped properties (name and value)~~',
|
||||
|
||||
'Core:AttributeFriendlyName' => '通用名称',
|
||||
'Core:AttributeFriendlyName+' => 'Attribute created automatically ; the friendly name is computed after several attributes~~',
|
||||
|
||||
'Core:FriendlyName-Label' => '全称',
|
||||
'Core:FriendlyName-Description' => '全称',
|
||||
|
||||
'Core:AttributeTag' => '标签',
|
||||
'Core:AttributeTag+' => '标签',
|
||||
|
||||
'Core:Context=REST/JSON' => 'REST',
|
||||
'Core:Context=Synchro' => '同步',
|
||||
'Core:Context=Setup' => '安装向导',
|
||||
@@ -201,10 +248,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
//
|
||||
|
||||
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:CMDBChangeOp' => '变更操作',
|
||||
'Class:CMDBChangeOp+' => '变更操作跟踪',
|
||||
'Class:CMDBChangeOp' => '变更操作跟踪',
|
||||
'Class:CMDBChangeOp+' => '某人在某时某刻对某个对象的变更操作',
|
||||
'Class:CMDBChangeOp/Attribute:change' => '变更',
|
||||
'Class:CMDBChangeOp/Attribute:change+' => '变更',
|
||||
'Class:CMDBChangeOp/Attribute:change+' => '',
|
||||
'Class:CMDBChangeOp/Attribute:date' => '日期',
|
||||
'Class:CMDBChangeOp/Attribute:date+' => '变更的日期和时间',
|
||||
'Class:CMDBChangeOp/Attribute:userinfo' => '用户',
|
||||
@@ -325,9 +372,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:EventNotification' => '通知事件',
|
||||
'Class:EventNotification+' => 'Trace of a notification that has been sent~~',
|
||||
'Class:EventNotification/Attribute:trigger_id' => '触发器',
|
||||
'Class:EventNotification/Attribute:trigger_id+' => '用户账户',
|
||||
'Class:EventNotification/Attribute:trigger_id+' => '用户账号',
|
||||
'Class:EventNotification/Attribute:action_id' => '用户',
|
||||
'Class:EventNotification/Attribute:action_id+' => '用户账户',
|
||||
'Class:EventNotification/Attribute:action_id+' => '用户账号',
|
||||
'Class:EventNotification/Attribute:object_id' => '对象id',
|
||||
'Class:EventNotification/Attribute:object_id+' => 'object id (class defined by the trigger ?)~~',
|
||||
));
|
||||
@@ -408,8 +455,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:EventRestService/Attribute:version+' => '参数 \'版本\'',
|
||||
'Class:EventRestService/Attribute:json_input' => '输入',
|
||||
'Class:EventRestService/Attribute:json_input+' => 'Argument \'json_data\'~~',
|
||||
'Class:EventRestService/Attribute:code' => '代码',
|
||||
'Class:EventRestService/Attribute:code+' => '返回代码',
|
||||
'Class:EventRestService/Attribute:code' => '编码',
|
||||
'Class:EventRestService/Attribute:code+' => '返回编码',
|
||||
'Class:EventRestService/Attribute:json_output' => '响应',
|
||||
'Class:EventRestService/Attribute:json_output+' => 'HTTP 响应 (json)',
|
||||
'Class:EventRestService/Attribute:provider' => '提供者',
|
||||
@@ -660,7 +707,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:TriggerOnAttributeBlobDownload' => 'Trigger (on object\'s document download)~~',
|
||||
'Class:TriggerOnAttributeBlobDownload+' => 'Trigger on object\'s document field download of [a child class of] the given class~~',
|
||||
'Class:TriggerOnAttributeBlobDownload/Attribute:target_attcodes' => 'Target fields~~',
|
||||
'Class:TriggerOnAttributeBlobDownload/Attribute:target_attcodes' => '目标字段',
|
||||
'Class:TriggerOnAttributeBlobDownload/Attribute:target_attcodes+' => '',
|
||||
));
|
||||
|
||||
@@ -671,7 +718,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:TriggerOnThresholdReached' => '触发器 (基于阈值)',
|
||||
'Class:TriggerOnThresholdReached+' => '当达到某个阈值时触发',
|
||||
'Class:TriggerOnThresholdReached/Attribute:stop_watch_code' => '秒表',
|
||||
'Class:TriggerOnThresholdReached/Attribute:stop_watch_code' => '计时',
|
||||
'Class:TriggerOnThresholdReached/Attribute:stop_watch_code+' => '',
|
||||
'Class:TriggerOnThresholdReached/Attribute:threshold_index' => '阈值',
|
||||
'Class:TriggerOnThresholdReached/Attribute:threshold_index+' => '',
|
||||
@@ -768,6 +815,7 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of
|
||||
'Class:SynchroDataSource/Attribute:user_delete_policy/Value:administrators' => 'Administrators only',
|
||||
'Class:SynchroDataSource/Attribute:user_delete_policy/Value:everybody' => 'Everybody allowed to delete such objects',
|
||||
'Class:SynchroDataSource/Attribute:user_delete_policy/Value:nobody' => 'Nobody',
|
||||
|
||||
'SynchroDataSource:Description' => '描述',
|
||||
'SynchroDataSource:Reconciliation' => 'Search & reconciliation~~',
|
||||
'SynchroDataSource:Deletion' => 'Deletion rules~~',
|
||||
@@ -1005,8 +1053,9 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of
|
||||
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:TagSetFieldData' => '%2$s for class %1$s~~',
|
||||
'Class:TagSetFieldData+' => '~~',
|
||||
'Class:TagSetFieldData/Attribute:code' => '代码',
|
||||
'Class:TagSetFieldData/Attribute:code+' => '内部代码. 必须至少包含3个数字或字母',
|
||||
|
||||
'Class:TagSetFieldData/Attribute:code' => '编码',
|
||||
'Class:TagSetFieldData/Attribute:code+' => '内部编码. 必须至少包含3个数字或字母',
|
||||
'Class:TagSetFieldData/Attribute:label' => '标签',
|
||||
'Class:TagSetFieldData/Attribute:label+' => '显示的标签',
|
||||
'Class:TagSetFieldData/Attribute:description' => '描述',
|
||||
@@ -1014,6 +1063,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:TagSetFieldData/Attribute:finalclass' => 'Tag class~~~~',
|
||||
'Class:TagSetFieldData/Attribute:obj_class' => 'Object class~~~~',
|
||||
'Class:TagSetFieldData/Attribute:obj_attcode' => 'Field code~~~~',
|
||||
|
||||
'Core:TagSetFieldData:ErrorDeleteUsedTag' => '已使用的标签无法删除',
|
||||
'Core:TagSetFieldData:ErrorDuplicateTagCodeOrLabel' => 'Tags codes or labels must be unique~~',
|
||||
'Core:TagSetFieldData:ErrorTagCodeSyntax' => 'Tags code must contain between 3 and %1$d alphanumeric characters, starting with a letter.~~',
|
||||
@@ -1139,6 +1189,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:ResourceSystemMenu' => 'Resource System Menu~~',
|
||||
'Class:ResourceSystemMenu+' => '',
|
||||
));
|
||||
|
||||
|
||||
// Additional language entries not present in English dict
|
||||
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'INTERNAL:JQuery-DatePicker:LangCode' => 'zh-CN'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB |
@@ -332,11 +332,14 @@ $(function()
|
||||
oParams.dashlet_class = sDashletClass;
|
||||
oParams.dashlet_id = sDashletId;
|
||||
oParams.dashlet_type = options.dashlet_type;
|
||||
oParams.ajax_promise_id = 'ajax_promise_' + sDashletId;
|
||||
var me = this;
|
||||
$.post(this.options.render_to, oParams, function (data) {
|
||||
me.ajax_div.html(data);
|
||||
me.add_dashlet_finalize(options, sDashletId, sDashletClass);
|
||||
me.mark_as_modified();
|
||||
window[oParams.ajax_promise_id].then(function(){
|
||||
me.add_dashlet_finalize(options, sDashletId, sDashletClass);
|
||||
me.mark_as_modified();
|
||||
});
|
||||
});
|
||||
},
|
||||
on_dashlet_moved: function (oDashlet, oReceiver, bRefresh) {
|
||||
|
||||
@@ -117,6 +117,7 @@ $(function()
|
||||
locked_by_someone_else: 'locked_by_someone_else',
|
||||
},
|
||||
},
|
||||
release_lock_promise_resolve: null, // N°4494 - Resolve callback of the Promise used for the action following the log entry send, which must be done only once the lock is released
|
||||
|
||||
// the constructor
|
||||
_create: function () {
|
||||
@@ -500,7 +501,7 @@ $(function()
|
||||
{
|
||||
// Hide all filters' options only if click wasn't on one of them
|
||||
if(($(oEvent.target).closest(this.js_selectors.activity_filter_options_toggler).length === 0)
|
||||
&& $(oEvent.target).closest(this.js_selectors.activity_filter_options).length === 0) {
|
||||
&& $(oEvent.target).closest(this.js_selectors.activity_filter_options).length === 0) {
|
||||
this._HideAllFiltersOptions();
|
||||
}
|
||||
},
|
||||
@@ -947,9 +948,9 @@ $(function()
|
||||
|
||||
// Send request to server
|
||||
$.post(
|
||||
GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
|
||||
oParams,
|
||||
'json'
|
||||
GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
|
||||
oParams,
|
||||
'json'
|
||||
)
|
||||
.fail(function (oXHR, sStatus, sErrorThrown) {
|
||||
CombodoModal.OpenErrorModal(sErrorThrown);
|
||||
@@ -972,12 +973,24 @@ $(function()
|
||||
|
||||
// For now, we don't hide the forms as the user may want to add something else
|
||||
me.element.find(me.js_selectors.caselog_entry_form).trigger('clear_entry.caselog_entry_form.itop');
|
||||
|
||||
// Redirect to stimulus
|
||||
// - Convert undefined, null and empty string to null
|
||||
sStimulusCode = ((sStimulusCode ?? '') === '') ? null : sStimulusCode;
|
||||
if (null !== sStimulusCode) {
|
||||
window.location.href = GetAbsoluteUrlAppRoot()+'pages/UI.php?operation=stimulus&class='+me._GetHostObjectClass()+'&id='+me._GetHostObjectID()+'&stimulus='+sStimulusCode;
|
||||
if (me.options.lock_enabled) {
|
||||
// Use a Promise to ensure that we redirect to the stimulus page ONLY when the lock is released, otherwise we might lock ourselves
|
||||
const oPromise = new Promise(function(resolve) {
|
||||
// Store the resolve callback so we can call it later from outside
|
||||
me.release_lock_promise_resolve = resolve;
|
||||
});
|
||||
oPromise.then(function () {
|
||||
window.location.href = GetAbsoluteUrlAppRoot()+'pages/UI.php?operation=stimulus&class='+me._GetHostObjectClass()+'&id='+me._GetHostObjectID()+'&stimulus='+sStimulusCode;
|
||||
// Resolve callback is reinitialized in case the redirection fails for any reason and we might need to retry
|
||||
me.release_lock_promise_resolve = null;
|
||||
});
|
||||
} else {
|
||||
window.location.href = GetAbsoluteUrlAppRoot()+'pages/UI.php?operation=stimulus&class='+me._GetHostObjectClass()+'&id='+me._GetHostObjectID()+'&stimulus='+sStimulusCode;
|
||||
}
|
||||
}
|
||||
})
|
||||
.always(function () {
|
||||
@@ -995,7 +1008,7 @@ $(function()
|
||||
_IncreaseTabTogglerMessagesCounter: function(sCaseLogAttCode){
|
||||
let oTabTogglerCounter = this._GetTabTogglerFromCaseLogAttCode(sCaseLogAttCode).find('[data-role="ibo-activity-panel--tab-title-messages-count"]');
|
||||
let iNewCounterValue = parseInt(oTabTogglerCounter.attr('data-messages-count')) + 1;
|
||||
|
||||
|
||||
oTabTogglerCounter.attr('data-messages-count', iNewCounterValue).text(iNewCounterValue);
|
||||
},
|
||||
/**
|
||||
@@ -1143,11 +1156,10 @@ $(function()
|
||||
else {
|
||||
oParams.operation = 'check_lock_state';
|
||||
}
|
||||
|
||||
$.post(
|
||||
this.options.lock_endpoint,
|
||||
oParams,
|
||||
'json'
|
||||
this.options.lock_endpoint,
|
||||
oParams,
|
||||
'json'
|
||||
)
|
||||
.fail(function (oXHR, sStatus, sErrorThrown) {
|
||||
// In case of HTTP request failure (not lock request), put the details in the JS console
|
||||
@@ -1196,6 +1208,9 @@ $(function()
|
||||
// Tried to release our lock
|
||||
else if ('release_lock' === oParams.operation) {
|
||||
sNewLockStatus = me.enums.lock_status.unknown;
|
||||
if (me.release_lock_promise_resolve !== null) {
|
||||
me.release_lock_promise_resolve();
|
||||
}
|
||||
}
|
||||
|
||||
// Just checked if object was locked
|
||||
@@ -1430,9 +1445,9 @@ $(function()
|
||||
limit_results_length: bLimitResultsLength,
|
||||
};
|
||||
$.post(
|
||||
this.options.load_more_entries_endpoint,
|
||||
oParams,
|
||||
'json'
|
||||
this.options.load_more_entries_endpoint,
|
||||
oParams,
|
||||
'json'
|
||||
)
|
||||
.fail(function (oXHR, sStatus, sErroThrown) {
|
||||
CombodoModal.OpenErrorModal(sErrorThrown);
|
||||
|
||||
@@ -153,16 +153,15 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizH
|
||||
"dataType": "html"
|
||||
})
|
||||
.done(function (data) {
|
||||
/* N°6152 - Hide during data loading and before open */
|
||||
$('#dlg_'+me.id).hide();
|
||||
$('#dlg_'+me.id).html(data);
|
||||
window[sPromiseId].then(function () {
|
||||
$('#dlg_'+me.id).dialog('open');
|
||||
me.UpdateSizes(null, null);
|
||||
if (me.bDoSearch)
|
||||
{
|
||||
if (me.bDoSearch) {
|
||||
me.SearchObjectsToAdd();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$('#count_'+me.id).change(function () {
|
||||
let c = this.value;
|
||||
me.UpdateButtons(c);
|
||||
|
||||
@@ -14,6 +14,7 @@ return array(
|
||||
'AbstractPortalUIExtension' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'AbstractPreferencesExtension' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'AbstractWeeklyScheduledProcess' => $baseDir . '/core/backgroundprocess.inc.php',
|
||||
'AbstractWelcomePopup' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'Action' => $baseDir . '/core/action.class.inc.php',
|
||||
'ActionChecker' => $baseDir . '/core/userrights.class.inc.php',
|
||||
'ActionEmail' => $baseDir . '/core/action.class.inc.php',
|
||||
@@ -363,6 +364,8 @@ return array(
|
||||
'Combodo\\iTop\\Application\\UI\\Links\\Set\\LinkSetUIBlockFactory' => $baseDir . '/sources/Application/UI/Links/Set/LinksSetUIBlockFactory.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Preferences\\BlockShortcuts\\BlockShortcuts' => $baseDir . '/sources/Application/UI/Preferences/BlockShortcuts/BlockShortcuts.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Printable\\BlockPrintHeader\\BlockPrintHeader' => $baseDir . '/sources/Application/UI/Printable/BlockPrintHeader/BlockPrintHeader.php',
|
||||
'Combodo\\iTop\\Application\\WelcomePopup\\DefaultWelcomePopup' => $baseDir . '/sources/Application/WelcomePopup/DefaultWelcomePopup.php',
|
||||
'Combodo\\iTop\\Application\\WelcomePopup\\WelcomePopupService' => $baseDir . '/sources/Application/WelcomePopup/WelcomePopupService.php',
|
||||
'Combodo\\iTop\\Composer\\iTopComposer' => $baseDir . '/sources/Composer/iTopComposer.php',
|
||||
'Combodo\\iTop\\Controller\\AbstractController' => $baseDir . '/sources/Controller/AbstractController.php',
|
||||
'Combodo\\iTop\\Controller\\AjaxRenderController' => $baseDir . '/sources/Controller/AjaxRenderController.php',
|
||||
@@ -372,6 +375,7 @@ return array(
|
||||
'Combodo\\iTop\\Controller\\OAuth\\OAuthLandingController' => $baseDir . '/sources/Controller/OAuth/OAuthLandingController.php',
|
||||
'Combodo\\iTop\\Controller\\PreferencesController' => $baseDir . '/sources/Controller/PreferencesController.php',
|
||||
'Combodo\\iTop\\Controller\\TemporaryObjects\\TemporaryObjectController' => $baseDir . '/sources/Controller/TemporaryObjects/TemporaryObjectController.php',
|
||||
'Combodo\\iTop\\Controller\\WelcomePopupController' => $baseDir . '/sources/Controller/WelcomePopupController.php',
|
||||
'Combodo\\iTop\\Controller\\iController' => $baseDir . '/sources/Controller/iController.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\IOAuthClientProvider' => $baseDir . '/sources/Core/Authentication/Client/OAuth/IOAuthClientProvider.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderAbstract' => $baseDir . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderAbstract.php',
|
||||
@@ -383,6 +387,7 @@ return array(
|
||||
'Combodo\\iTop\\Core\\Email\\EmailFactory' => $baseDir . '/sources/Core/Email/EmailFactory.php',
|
||||
'Combodo\\iTop\\Core\\Email\\iEMail' => $baseDir . '/sources/Core/Email/iEMail.php',
|
||||
'Combodo\\iTop\\Core\\EventListener\\AttributeBlobEventListener' => $baseDir . '/sources/Core/EventListener/AttributeBlobEventListener.php',
|
||||
'Combodo\\iTop\\Core\\Kpi\\KpiLogData' => $baseDir . '/sources/Core/Kpi/KpiLogData.php',
|
||||
'Combodo\\iTop\\Core\\MetaModel\\FriendlyNameType' => $baseDir . '/sources/Core/MetaModel/FriendlyNameType.php',
|
||||
'Combodo\\iTop\\Core\\MetaModel\\HierarchicalKey' => $baseDir . '/sources/Core/MetaModel/HierarchicalKey.php',
|
||||
'Combodo\\iTop\\DesignDocument' => $baseDir . '/core/designdocument.class.inc.php',
|
||||
@@ -461,6 +466,7 @@ return array(
|
||||
'Combodo\\iTop\\Service\\Links\\LinkSetModel' => $baseDir . '/sources/Service/Links/LinkSetModel.php',
|
||||
'Combodo\\iTop\\Service\\Links\\LinkSetRepository' => $baseDir . '/sources/Service/Links/LinkSetRepository.php',
|
||||
'Combodo\\iTop\\Service\\Links\\LinksBulkDataPostProcessor' => $baseDir . '/sources/Service/Links/LinksBulkDataPostProcessor.php',
|
||||
'Combodo\\iTop\\Service\\Module\\ModuleService' => $baseDir . '/sources/Service/Module/ModuleService.php',
|
||||
'Combodo\\iTop\\Service\\Router\\Exception\\RouteNotFoundException' => $baseDir . '/sources/Service/Router/Exception/RouteNotFoundException.php',
|
||||
'Combodo\\iTop\\Service\\Router\\Exception\\RouterException' => $baseDir . '/sources/Service/Router/Exception/RouterException.php',
|
||||
'Combodo\\iTop\\Service\\Router\\Router' => $baseDir . '/sources/Service/Router/Router.php',
|
||||
@@ -470,6 +476,8 @@ return array(
|
||||
'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectManager' => $baseDir . '/sources/Service/TemporaryObjects/TemporaryObjectManager.php',
|
||||
'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectRepository' => $baseDir . '/sources/Service/TemporaryObjects/TemporaryObjectRepository.php',
|
||||
'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectsEvents' => $baseDir . '/sources/Service/TemporaryObjects/TemporaryObjectsEvents.php',
|
||||
'Combodo\\iTop\\SessionTracker\\SessionGC' => $baseDir . '/sources/SessionTracker/SessionGC.php',
|
||||
'Combodo\\iTop\\SessionTracker\\SessionHandler' => $baseDir . '/sources/SessionTracker/SessionHandler.php',
|
||||
'CompileCSSService' => $baseDir . '/application/compilecssservice.class.inc.php',
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
'Config' => $baseDir . '/core/config.class.inc.php',
|
||||
@@ -2952,6 +2960,7 @@ return array(
|
||||
'iDBObjectURLMaker' => $baseDir . '/application/applicationcontext.class.inc.php',
|
||||
'iDisplay' => $baseDir . '/core/dbobject.class.php',
|
||||
'iFieldRendererMappingsExtension' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'iKPILoggerExtension' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'iKeyboardShortcut' => $baseDir . '/sources/Application/UI/Hook/iKeyboardShortcut.php',
|
||||
'iLogFileNameBuilder' => $baseDir . '/core/log.class.inc.php',
|
||||
'iLoginExtension' => $baseDir . '/application/applicationextension.inc.php',
|
||||
@@ -2982,6 +2991,7 @@ return array(
|
||||
'iTopWebPage' => $baseDir . '/sources/Application/WebPage/iTopWebPage.php',
|
||||
'iTopWizardWebPage' => $baseDir . '/sources/Application/WebPage/iTopWizardWebPage.php',
|
||||
'iTopXmlException' => $baseDir . '/application/exceptions/iTopXmlException.php',
|
||||
'iWelcomePopup' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'iWorkingTimeComputer' => $baseDir . '/core/computing.inc.php',
|
||||
'lnkAuditCategoryToAuditDomain' => $baseDir . '/application/audit.domain.class.inc.php',
|
||||
'lnkTriggerAction' => $baseDir . '/core/trigger.class.inc.php',
|
||||
|
||||
@@ -378,6 +378,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'AbstractPortalUIExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'AbstractPreferencesExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'AbstractWeeklyScheduledProcess' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.php',
|
||||
'AbstractWelcomePopup' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'Action' => __DIR__ . '/../..' . '/core/action.class.inc.php',
|
||||
'ActionChecker' => __DIR__ . '/../..' . '/core/userrights.class.inc.php',
|
||||
'ActionEmail' => __DIR__ . '/../..' . '/core/action.class.inc.php',
|
||||
@@ -727,6 +728,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Application\\UI\\Links\\Set\\LinkSetUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Links/Set/LinksSetUIBlockFactory.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Preferences\\BlockShortcuts\\BlockShortcuts' => __DIR__ . '/../..' . '/sources/Application/UI/Preferences/BlockShortcuts/BlockShortcuts.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Printable\\BlockPrintHeader\\BlockPrintHeader' => __DIR__ . '/../..' . '/sources/Application/UI/Printable/BlockPrintHeader/BlockPrintHeader.php',
|
||||
'Combodo\\iTop\\Application\\WelcomePopup\\DefaultWelcomePopup' => __DIR__ . '/../..' . '/sources/Application/WelcomePopup/DefaultWelcomePopup.php',
|
||||
'Combodo\\iTop\\Application\\WelcomePopup\\WelcomePopupService' => __DIR__ . '/../..' . '/sources/Application/WelcomePopup/WelcomePopupService.php',
|
||||
'Combodo\\iTop\\Composer\\iTopComposer' => __DIR__ . '/../..' . '/sources/Composer/iTopComposer.php',
|
||||
'Combodo\\iTop\\Controller\\AbstractController' => __DIR__ . '/../..' . '/sources/Controller/AbstractController.php',
|
||||
'Combodo\\iTop\\Controller\\AjaxRenderController' => __DIR__ . '/../..' . '/sources/Controller/AjaxRenderController.php',
|
||||
@@ -736,6 +739,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Controller\\OAuth\\OAuthLandingController' => __DIR__ . '/../..' . '/sources/Controller/OAuth/OAuthLandingController.php',
|
||||
'Combodo\\iTop\\Controller\\PreferencesController' => __DIR__ . '/../..' . '/sources/Controller/PreferencesController.php',
|
||||
'Combodo\\iTop\\Controller\\TemporaryObjects\\TemporaryObjectController' => __DIR__ . '/../..' . '/sources/Controller/TemporaryObjects/TemporaryObjectController.php',
|
||||
'Combodo\\iTop\\Controller\\WelcomePopupController' => __DIR__ . '/../..' . '/sources/Controller/WelcomePopupController.php',
|
||||
'Combodo\\iTop\\Controller\\iController' => __DIR__ . '/../..' . '/sources/Controller/iController.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\IOAuthClientProvider' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/IOAuthClientProvider.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderAbstract' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderAbstract.php',
|
||||
@@ -747,6 +751,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Core\\Email\\EmailFactory' => __DIR__ . '/../..' . '/sources/Core/Email/EmailFactory.php',
|
||||
'Combodo\\iTop\\Core\\Email\\iEMail' => __DIR__ . '/../..' . '/sources/Core/Email/iEMail.php',
|
||||
'Combodo\\iTop\\Core\\EventListener\\AttributeBlobEventListener' => __DIR__ . '/../..' . '/sources/Core/EventListener/AttributeBlobEventListener.php',
|
||||
'Combodo\\iTop\\Core\\Kpi\\KpiLogData' => __DIR__ . '/../..' . '/sources/Core/Kpi/KpiLogData.php',
|
||||
'Combodo\\iTop\\Core\\MetaModel\\FriendlyNameType' => __DIR__ . '/../..' . '/sources/Core/MetaModel/FriendlyNameType.php',
|
||||
'Combodo\\iTop\\Core\\MetaModel\\HierarchicalKey' => __DIR__ . '/../..' . '/sources/Core/MetaModel/HierarchicalKey.php',
|
||||
'Combodo\\iTop\\DesignDocument' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
|
||||
@@ -825,6 +830,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Service\\Links\\LinkSetModel' => __DIR__ . '/../..' . '/sources/Service/Links/LinkSetModel.php',
|
||||
'Combodo\\iTop\\Service\\Links\\LinkSetRepository' => __DIR__ . '/../..' . '/sources/Service/Links/LinkSetRepository.php',
|
||||
'Combodo\\iTop\\Service\\Links\\LinksBulkDataPostProcessor' => __DIR__ . '/../..' . '/sources/Service/Links/LinksBulkDataPostProcessor.php',
|
||||
'Combodo\\iTop\\Service\\Module\\ModuleService' => __DIR__ . '/../..' . '/sources/Service/Module/ModuleService.php',
|
||||
'Combodo\\iTop\\Service\\Router\\Exception\\RouteNotFoundException' => __DIR__ . '/../..' . '/sources/Service/Router/Exception/RouteNotFoundException.php',
|
||||
'Combodo\\iTop\\Service\\Router\\Exception\\RouterException' => __DIR__ . '/../..' . '/sources/Service/Router/Exception/RouterException.php',
|
||||
'Combodo\\iTop\\Service\\Router\\Router' => __DIR__ . '/../..' . '/sources/Service/Router/Router.php',
|
||||
@@ -834,6 +840,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectManager' => __DIR__ . '/../..' . '/sources/Service/TemporaryObjects/TemporaryObjectManager.php',
|
||||
'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectRepository' => __DIR__ . '/../..' . '/sources/Service/TemporaryObjects/TemporaryObjectRepository.php',
|
||||
'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectsEvents' => __DIR__ . '/../..' . '/sources/Service/TemporaryObjects/TemporaryObjectsEvents.php',
|
||||
'Combodo\\iTop\\SessionTracker\\SessionGC' => __DIR__ . '/../..' . '/sources/SessionTracker/SessionGC.php',
|
||||
'Combodo\\iTop\\SessionTracker\\SessionHandler' => __DIR__ . '/../..' . '/sources/SessionTracker/SessionHandler.php',
|
||||
'CompileCSSService' => __DIR__ . '/../..' . '/application/compilecssservice.class.inc.php',
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
'Config' => __DIR__ . '/../..' . '/core/config.class.inc.php',
|
||||
@@ -3316,6 +3324,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'iDBObjectURLMaker' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php',
|
||||
'iDisplay' => __DIR__ . '/../..' . '/core/dbobject.class.php',
|
||||
'iFieldRendererMappingsExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'iKPILoggerExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'iKeyboardShortcut' => __DIR__ . '/../..' . '/sources/Application/UI/Hook/iKeyboardShortcut.php',
|
||||
'iLogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php',
|
||||
'iLoginExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
@@ -3346,6 +3355,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'iTopWebPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/iTopWebPage.php',
|
||||
'iTopWizardWebPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/iTopWizardWebPage.php',
|
||||
'iTopXmlException' => __DIR__ . '/../..' . '/application/exceptions/iTopXmlException.php',
|
||||
'iWelcomePopup' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'iWorkingTimeComputer' => __DIR__ . '/../..' . '/core/computing.inc.php',
|
||||
'lnkAuditCategoryToAuditDomain' => __DIR__ . '/../..' . '/application/audit.domain.class.inc.php',
|
||||
'lnkTriggerAction' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'reference' => 'dbf3393c9729a20f0bf389d343507238d61fef56',
|
||||
'reference' => '204a6d8e51619cd669c6d9d7722edaa36d1c3394',
|
||||
'name' => 'combodo/itop',
|
||||
'dev' => true,
|
||||
),
|
||||
@@ -25,7 +25,7 @@
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'reference' => 'dbf3393c9729a20f0bf389d343507238d61fef56',
|
||||
'reference' => '204a6d8e51619cd669c6d9d7722edaa36d1c3394',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'combodo/tcpdf' => array(
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<security>
|
||||
<requestFiltering>
|
||||
<fileExtensions applyToWebDAV="false" allowUnlisted="false"></fileExtensions>
|
||||
</requestFiltering>
|
||||
<authorization>
|
||||
<deny users="*" /> <!-- Denies all users -->
|
||||
</authorization>
|
||||
</security>
|
||||
</system.webServer>
|
||||
<system.web>
|
||||
<authorization>
|
||||
<deny users="*" /> <!-- Denies all users -->
|
||||
</authorization>
|
||||
</system.web>
|
||||
</configuration>
|
||||
28
pages/UI.php
28
pages/UI.php
@@ -20,6 +20,7 @@ use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
|
||||
use Combodo\iTop\Controller\Base\Layout\ObjectController;
|
||||
use Combodo\iTop\Service\Router\Router;
|
||||
use Combodo\iTop\Application\WelcomePopup\WelcomePopupService;
|
||||
|
||||
/**
|
||||
* Displays a popup welcome message, once per session at maximum
|
||||
@@ -29,19 +30,18 @@ use Combodo\iTop\Service\Router\Router;
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function DisplayWelcomePopup(WebPage $oP)
|
||||
function DisplayWelcomePopup(WebPage $oP): void
|
||||
{
|
||||
if (!Session::IsSet('welcome'))
|
||||
{
|
||||
// Check, only once per session, if the popup should be displayed...
|
||||
// If the user did not already ask for hiding it forever
|
||||
$bPopup = appUserPreferences::GetPref('welcome_popup', true);
|
||||
if ($bPopup)
|
||||
$oWelcomePopupService = new WelcomePopupService();
|
||||
$aMessages = $oWelcomePopupService->GetMessages();
|
||||
if (count($aMessages) > 0)
|
||||
{
|
||||
TwigHelper::RenderIntoPage($oP, APPROOT.'/', 'templates/pages/backoffice/welcome_popup/welcome_popup');
|
||||
Session::Set('welcome', 'ok');
|
||||
TwigHelper::RenderIntoPage($oP, APPROOT.'/', 'templates/pages/backoffice/welcome_popup/welcome_popup', ['messages' => $aMessages]);
|
||||
}
|
||||
}
|
||||
Session::Set('welcome', 'ok'); // Try just once per session
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +66,7 @@ function ApplyNextAction(Webpage $oP, CMDBObject $oObj, $sNextAction)
|
||||
}
|
||||
// Get the list of missing mandatory fields for the target state, considering only the changes from the previous form (i.e don't prompt twice)
|
||||
$aExpectedAttributes = $oObj->GetTransitionAttributes($sNextAction);
|
||||
|
||||
|
||||
if (count($aExpectedAttributes) == 0)
|
||||
{
|
||||
// If all the mandatory fields are already present, just apply the transition silently...
|
||||
@@ -85,7 +85,7 @@ function ApplyNextAction(Webpage $oP, CMDBObject $oObj, $sNextAction)
|
||||
// redirect to the 'stimulus' action
|
||||
$oAppContext = new ApplicationContext();
|
||||
//echo "<p>Missing Attributes <pre>".print_r($aExpectedAttributes, true)."</pre></p>\n";
|
||||
|
||||
|
||||
$oP->add_header('Location: '.utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=stimulus&class='.get_class($oObj).'&stimulus='.$sNextAction.'&id='.$oObj->getKey().'&'.$oAppContext->GetForLink());
|
||||
}
|
||||
}
|
||||
@@ -243,7 +243,7 @@ function DisplayMultipleSelectionForm(WebPage $oP, DBSearch $oFilter, string $sN
|
||||
$aExtraParams['surround_with_panel'] = true;
|
||||
if(array_key_exists('icon', $aDisplayParams)){
|
||||
$aExtraParams['panel_icon'] = $aDisplayParams['icon'];
|
||||
}
|
||||
}
|
||||
if(array_key_exists('title', $aDisplayParams)){
|
||||
$aExtraParams['panel_title'] = $aDisplayParams['title'];
|
||||
}
|
||||
@@ -292,7 +292,7 @@ function DisplayNavigatorGroupTab($oP)
|
||||
}
|
||||
|
||||
/***********************************************************************************
|
||||
*
|
||||
*
|
||||
* Main user interface page starts here
|
||||
*
|
||||
***********************************************************************************/
|
||||
@@ -700,7 +700,7 @@ try
|
||||
break;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/** @deprecated 3.1.0 Use the "object.new" route instead */
|
||||
// Kept for backward compatibility
|
||||
case 'new': // Form to create a new object
|
||||
@@ -1638,4 +1638,4 @@ class UI
|
||||
);
|
||||
cmdbAbstractObject::DoBulkModify($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $bPreview, $sCancelUrl, $aContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ if ($oFilter != null)
|
||||
$aExtraParams['action'] = utils::GetAbsoluteUrlAppRoot().'pages/UniversalSearch.php';
|
||||
$aExtraParams['table_id'] = '1';
|
||||
$aExtraParams['search_header_force_dropdown'] = $sSearchHeaderForceDropdown;
|
||||
//$aExtraParams['class'] = $sClassName;
|
||||
$aExtraParams['submit_on_load'] = false;
|
||||
$oBlock->Display($oP, 0, $aExtraParams);
|
||||
|
||||
// Search results
|
||||
|
||||
@@ -17,6 +17,7 @@ use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
|
||||
use Combodo\iTop\Renderer\Console\ConsoleFormRenderer;
|
||||
use Combodo\iTop\Service\Router\Router;
|
||||
use Combodo\iTop\Service\TemporaryObjects\TemporaryObjectManager;
|
||||
use Combodo\iTop\Controller\WelcomePopupController;
|
||||
|
||||
require_once('../approot.inc.php');
|
||||
|
||||
@@ -68,7 +69,7 @@ try
|
||||
break;
|
||||
|
||||
default:
|
||||
ContextTag::AddContext(ContextTag::TAG_CONSOLE);
|
||||
$oTag = new ContextTag(ContextTag::TAG_CONSOLE);
|
||||
}
|
||||
|
||||
// First check if we can redirect the route to a dedicated controller
|
||||
@@ -201,10 +202,10 @@ try
|
||||
$oWidget = new UILinksWidget($sClass, $sAttCode, $iInputId, $sSuffix, $bDuplicates);
|
||||
$oAppContext = new ApplicationContext();
|
||||
$aPrefillFormParam = array(
|
||||
'user' => Session::Get("auth_user"),
|
||||
'context' => $oAppContext->GetAsHash(),
|
||||
'att_code' => $sAttCode,
|
||||
'origin' => 'console',
|
||||
'user' => Session::Get("auth_user"),
|
||||
'context' => $oAppContext->GetAsHash(),
|
||||
'att_code' => $sAttCode,
|
||||
'origin' => 'console',
|
||||
'source_obj' => $oObj
|
||||
);
|
||||
$aAlreadyLinked = utils::ReadParam('aAlreadyLinked', array());
|
||||
@@ -276,10 +277,10 @@ try
|
||||
$oPage->SetContentType('text/html');
|
||||
$oAppContext = new ApplicationContext();
|
||||
$aPrefillFormParam = array(
|
||||
'user' => Session::Get('auth_user'),
|
||||
'context' => $oAppContext->GetAsHash(),
|
||||
'att_code' => $sAttCode,
|
||||
'origin' => 'console',
|
||||
'user' => Session::Get('auth_user'),
|
||||
'context' => $oAppContext->GetAsHash(),
|
||||
'att_code' => $sAttCode,
|
||||
'origin' => 'console',
|
||||
'source_obj' => $oObj,
|
||||
);
|
||||
$aPrefillFormParam['dest_class'] = ($oObj === null ? '' : $oObj->Get($sAttCode)->GetClass());
|
||||
@@ -303,10 +304,10 @@ try
|
||||
}
|
||||
$oAppContext = new ApplicationContext();
|
||||
$aPrefillFormParam = array(
|
||||
'user' => Session::Get('auth_user'),
|
||||
'context' => $oAppContext->GetAsHash(),
|
||||
'att_code' => $sAttCode,
|
||||
'origin' => 'console',
|
||||
'user' => Session::Get('auth_user'),
|
||||
'context' => $oAppContext->GetAsHash(),
|
||||
'att_code' => $sAttCode,
|
||||
'origin' => 'console',
|
||||
'source_obj' => $oObj,
|
||||
);
|
||||
$aPrefillFormParam['dest_class'] = ($oObj === null ? '' : $oObj->Get($sAttCode)->GetClass());
|
||||
@@ -418,10 +419,10 @@ try
|
||||
$iInputId = utils::ReadParam('iInputId', '');
|
||||
$sAttCode = utils::ReadParam('sAttCode', '');
|
||||
$sJson = utils::ReadParam('json', '', false, 'raw_data');
|
||||
$bTargetClassSelected = utils::ReadParam('bTargetClassSelected', '', false, 'raw_data');
|
||||
// Building form, if target class has child classes we ask the user for the desired leaf class, unless we've already done just that
|
||||
$bTargetClassSelected = utils::ReadParam('bTargetClassSelected', '', false, 'raw_data');
|
||||
// Building form, if target class has child classes we ask the user for the desired leaf class, unless we've already done just that
|
||||
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, false);
|
||||
if(!$bTargetClassSelected && MetaModel::HasChildrenClasses($sTargetClass)){
|
||||
if (!$bTargetClassSelected && MetaModel::HasChildrenClasses($sTargetClass)) {
|
||||
$oWidget->GetClassSelectionForm($oPage);
|
||||
} else {
|
||||
$aPrefillFormParam = array();
|
||||
@@ -430,11 +431,11 @@ try
|
||||
$oObj = $oWizardHelper->GetTargetObject();
|
||||
$oAppContext = new ApplicationContext();
|
||||
$aPrefillFormParam = array(
|
||||
'user' => Session::Get('auth_user'),
|
||||
'context' => $oAppContext->GetAsHash(),
|
||||
'att_code' => $sAttCode,
|
||||
'user' => Session::Get('auth_user'),
|
||||
'context' => $oAppContext->GetAsHash(),
|
||||
'att_code' => $sAttCode,
|
||||
'source_obj' => $oObj,
|
||||
'origin' => 'console'
|
||||
'origin' => 'console'
|
||||
);
|
||||
} else {
|
||||
// Search form: no current object
|
||||
@@ -527,7 +528,8 @@ try
|
||||
$oObj->Set($sAttCode, $defaultValue);
|
||||
}
|
||||
$sFormPrefix = $oWizardHelper->GetFormPrefix();
|
||||
$aExpectedAttributes = ($oWizardHelper->GetStimulus() === null) ? array() : $oObj->GetTransitionAttributes($oWizardHelper->GetStimulus(), $oWizardHelper->GetInitialState());
|
||||
$aExpectedAttributes = ($oWizardHelper->GetStimulus() === null) ? array() : $oObj->GetTransitionAttributes($oWizardHelper->GetStimulus(),
|
||||
$oWizardHelper->GetInitialState());
|
||||
foreach ($oWizardHelper->GetFieldsForAllowedValues() as $sAttCode) {
|
||||
$sId = $oWizardHelper->GetIdForField($sAttCode);
|
||||
if ($sId != '') {
|
||||
@@ -573,7 +575,8 @@ try
|
||||
$sTargetState = utils::ReadParam('target_state', '');
|
||||
$iTransactionId = utils::ReadParam('transaction_id', '', false, 'transaction_id');
|
||||
$oObj->Set(MetaModel::GetStateAttributeCode($sClass), $sTargetState);
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $sClass, $oObj, array(), array('action' => utils::GetAbsoluteUrlAppRoot().'pages/UI.php', 'transaction_id' => $iTransactionId));
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $sClass, $oObj, array(),
|
||||
array('action' => utils::GetAbsoluteUrlAppRoot().'pages/UI.php', 'transaction_id' => $iTransactionId));
|
||||
break;
|
||||
|
||||
// DisplayBlock
|
||||
@@ -600,8 +603,7 @@ try
|
||||
} else {
|
||||
try {
|
||||
$oFilter = DBSearch::unserialize($sFilter);
|
||||
}
|
||||
catch (CoreException $e) {
|
||||
} catch (CoreException $e) {
|
||||
$sFilter = utils::HtmlEntities($sFilter);
|
||||
$oPage->p("Invalid query (invalid filter) : <code>$sFilter</code>");
|
||||
IssueLog::Error("ajax.render operation='ajax', invalid DBSearch filter param : $sFilter");
|
||||
@@ -666,7 +668,8 @@ try
|
||||
$aResult['JSURLs'] = str_replace('"', '\'', $oBlock->sJSURLs);
|
||||
$aResult['js'] = 'charts['.$iRefresh.'].load({json: '.str_replace('"', '\'', $oBlock->sJson).
|
||||
',keys: { x: \'label\', value: [\'value\']'.
|
||||
'},onclick: function (d) { var aURLs = $.parseJSON('.str_replace('"', '\'', $oBlock->sJSURLs).'); window.location.href= aURLs[d.index]; }})';
|
||||
'},onclick: function (d) { var aURLs = $.parseJSON('.str_replace('"', '\'',
|
||||
$oBlock->sJSURLs).'); window.location.href= aURLs[d.index]; }})';
|
||||
break;
|
||||
|
||||
case 'pie':
|
||||
@@ -677,7 +680,8 @@ try
|
||||
$aResult['JSURLs'] = str_replace('"', '\'', $oBlock->sJSURLs);
|
||||
$aResult['js'] = 'charts['.$iRefresh.'].load({columns: '.str_replace('"', '\'', $oBlock->sJSColumns).
|
||||
',names: '.str_replace('"', '\'', $oBlock->sJSNames).
|
||||
',onclick: function (d) { var aURLs = $.parseJSON('.str_replace('"', '\'', $oBlock->sJSURLs).'); window.location.href= aURLs[d.index]; }})';
|
||||
',onclick: function (d) { var aURLs = $.parseJSON('.str_replace('"', '\'',
|
||||
$oBlock->sJSURLs).'); window.location.href= aURLs[d.index]; }})';
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -777,14 +781,14 @@ try
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$sHtml = cmdbAbstractObject::GetSearchForm($oPage, $oSet, array(
|
||||
'currentId' => $currentId,
|
||||
'baseClass' => $sRootClass,
|
||||
'action' => $sAction,
|
||||
'table_id' => $sTableId,
|
||||
'selection_mode' => $sSelectionMode,
|
||||
'currentId' => $currentId,
|
||||
'baseClass' => $sRootClass,
|
||||
'action' => $sAction,
|
||||
'table_id' => $sTableId,
|
||||
'selection_mode' => $sSelectionMode,
|
||||
'result_list_outer_selector' => $sResultListOuterSelector,
|
||||
'cssCount' => $scssCount,
|
||||
'table_inner_id' => $sTableInnerId
|
||||
'cssCount' => $scssCount,
|
||||
'table_inner_id' => $sTableInnerId
|
||||
));
|
||||
$oPage->add($sHtml);
|
||||
break;
|
||||
@@ -821,13 +825,13 @@ try
|
||||
TemporaryObjectManager::GetInstance()->CancelAllTemporaryObjects($iTransactionId);
|
||||
|
||||
IssueLog::Trace('on_form_cancel', $sObjClass, array(
|
||||
'$iObjKey' => $iObjKey,
|
||||
'$iObjKey' => $iObjKey,
|
||||
'$sTransactionId' => $iTransactionId,
|
||||
'$sTempId' => $sTempId,
|
||||
'$sToken' => $sToken,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
'$sTempId' => $sTempId,
|
||||
'$sToken' => $sToken,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
));
|
||||
|
||||
break;
|
||||
@@ -879,11 +883,9 @@ try
|
||||
$oDoc = utils::ReadPostedDocument('dashboard_upload_file');
|
||||
$oDashboard->FromXml($oDoc->GetData());
|
||||
$oDashboard->Save();
|
||||
}
|
||||
catch (DOMException $e) {
|
||||
} catch (DOMException $e) {
|
||||
$aResult = array('error' => Dict::S('UI:Error:InvalidDashboardFile'));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
$aResult = array('error' => $e->getMessage());
|
||||
}
|
||||
} else {
|
||||
@@ -1054,7 +1056,8 @@ EOF
|
||||
$oPage->add_script("$('#dashlet_$sDashletId').html('$sHtml');"); // in ajax web page add_script has the same effect as add_ready_script
|
||||
// but is executed BEFORE all 'ready_scripts'
|
||||
$oForm = $oDashlet->GetForm(); // Rebuild the form since the values/content changed
|
||||
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property'));
|
||||
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php',
|
||||
array('operation' => 'update_dashlet_property'));
|
||||
$sHtml = addslashes($oForm->RenderAsPropertySheet($oPage, true /* bReturnHtml */, '.itop-dashboard'));
|
||||
$sHtml = str_replace("\n", '', $sHtml);
|
||||
$sHtml = str_replace("\r", '', $sHtml);
|
||||
@@ -1113,7 +1116,8 @@ EOF
|
||||
}
|
||||
if ($oDashlet->IsFormRedrawNeeded()) {
|
||||
$oForm = $oDashlet->GetForm(); // Rebuild the form since the values/content changed
|
||||
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property', 'extra_params' => $aExtraParams));
|
||||
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php',
|
||||
array('operation' => 'update_dashlet_property', 'extra_params' => $aExtraParams));
|
||||
$sHtml = addslashes($oForm->RenderAsPropertySheet($oPage, true, '.itop-dashboard'));
|
||||
$sHtml = str_replace("\n", '', $sHtml);
|
||||
$sHtml = str_replace("\r", '', $sHtml);
|
||||
@@ -1359,7 +1363,8 @@ JS
|
||||
}
|
||||
|
||||
$sFullTextJS = addslashes($sFullText);
|
||||
$bEnableEnlarge = array_key_exists($sClassName, $aAccelerators) && array_key_exists('query', $aAccelerators[$sClassName]);
|
||||
$bEnableEnlarge = array_key_exists($sClassName, $aAccelerators) && array_key_exists('query',
|
||||
$aAccelerators[$sClassName]);
|
||||
if (array_key_exists($sClassName, $aAccelerators) && array_key_exists('enable_enlarge', $aAccelerators[$sClassName])) {
|
||||
$bEnableEnlarge &= $aAccelerators[$sClassName]['enable_enlarge'];
|
||||
}
|
||||
@@ -1393,9 +1398,11 @@ EOF;
|
||||
$oPage->add("<div class=\"search-class-result search-class-$sClassName\">\n");
|
||||
$oPage->add("<div class=\"page_header\">\n");
|
||||
if (array_key_exists($sClassName, $aAccelerators)) {
|
||||
$oPage->add('<h2 class="ibo-global-search--result--title">'.MetaModel::GetClassIcon($sClassName).Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aLeafs), Metamodel::GetName($sClassName)).$sEnlargeButton."</h2>\n");
|
||||
$oPage->add('<h2 class="ibo-global-search--result--title">'.MetaModel::GetClassIcon($sClassName).Dict::Format('UI:Search:Count_ObjectsOf_Class_Found',
|
||||
count($aLeafs), Metamodel::GetName($sClassName)).$sEnlargeButton."</h2>\n");
|
||||
} else {
|
||||
$oPage->add('<h2 class="ibo-global-search--result--title">'.MetaModel::GetClassIcon($sClassName).Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aLeafs), Metamodel::GetName($sClassName))."</h2>\n");
|
||||
$oPage->add('<h2 class="ibo-global-search--result--title">'.MetaModel::GetClassIcon($sClassName).Dict::Format('UI:Search:Count_ObjectsOf_Class_Found',
|
||||
count($aLeafs), Metamodel::GetName($sClassName))."</h2>\n");
|
||||
}
|
||||
$oPage->add("</div>\n");
|
||||
$oLeafsFilter->AddCondition('id', $aLeafs, 'IN');
|
||||
@@ -1411,7 +1418,8 @@ EOF;
|
||||
if (array_key_exists($sClassName, $aAccelerators)) {
|
||||
$oPage->add("<div class=\"search-class-result search-class-$sClassName\">\n");
|
||||
$oPage->add("<div class=\"page_header\">\n");
|
||||
$oPage->add('<h2 class="ibo-global-search--result--title">'.MetaModel::GetClassIcon($sClassName).Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', 0, Metamodel::GetName($sClassName)).$sEnlargeButton."</h2>\n");
|
||||
$oPage->add('<h2 class="ibo-global-search--result--title">'.MetaModel::GetClassIcon($sClassName).Dict::Format('UI:Search:Count_ObjectsOf_Class_Found',
|
||||
0, Metamodel::GetName($sClassName)).$sEnlargeButton."</h2>\n");
|
||||
$oPage->add("</div>\n");
|
||||
$oPage->add("</div>\n");
|
||||
$oPage->p(' '); // Some space ?
|
||||
@@ -1491,7 +1499,8 @@ EOF
|
||||
$oFilter->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
$oPage->add("<div class=\"page_header\">\n");
|
||||
$oPage->add("<h2>".MetaModel::GetClassIcon($sClass)." <span class=\"hilite\">".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', $oSet->Count(), Metamodel::GetName($sClass))."</h2>\n");
|
||||
$oPage->add("<h2>".MetaModel::GetClassIcon($sClass)." <span class=\"hilite\">".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found',
|
||||
$oSet->Count(), Metamodel::GetName($sClass))."</h2>\n");
|
||||
$oPage->add("</div>\n");
|
||||
if ($oSet->Count() > 0) {
|
||||
$aLeafs = array();
|
||||
@@ -1579,10 +1588,11 @@ EOF
|
||||
$oPage->add('<div class="statistics"><div class="stats-toggle closed">'.Dict::S('ExcelExport:Statistics').'<div class="stats-data"></div></div></div>');
|
||||
$oPage->add('</div>');
|
||||
$aLabels = array(
|
||||
'dialog_title' => Dict::S('ExcelExporter:ExportDialogTitle'),
|
||||
'cancel_button' => Dict::S('UI:Button:Cancel'),
|
||||
'export_button' => Dict::S('ExcelExporter:ExportButton'),
|
||||
'download_button' => Dict::Format('ExcelExporter:DownloadButton', 'export.xlsx'), //TODO: better name for the file (based on the class of the filter??)
|
||||
'dialog_title' => Dict::S('ExcelExporter:ExportDialogTitle'),
|
||||
'cancel_button' => Dict::S('UI:Button:Cancel'),
|
||||
'export_button' => Dict::S('ExcelExporter:ExportButton'),
|
||||
'download_button' => Dict::Format('ExcelExporter:DownloadButton', 'export.xlsx'),
|
||||
//TODO: better name for the file (based on the class of the filter??)
|
||||
);
|
||||
$sJSLabels = json_encode($aLabels);
|
||||
$sFilter = addslashes($sFilter);
|
||||
@@ -1694,7 +1704,8 @@ EOF
|
||||
if ($sDirection == 'up') {
|
||||
$oRelGraph = MetaModel::GetRelatedObjectsUp($sRelation, $aSourceObjects, $iMaxRecursionDepth, true, $aContexts);
|
||||
} else {
|
||||
$oRelGraph = MetaModel::GetRelatedObjectsDown($sRelation, $aSourceObjects, $iMaxRecursionDepth, true, $aExcludedObjects, $aContexts);
|
||||
$oRelGraph = MetaModel::GetRelatedObjectsDown($sRelation, $aSourceObjects, $iMaxRecursionDepth, true, $aExcludedObjects,
|
||||
$aContexts);
|
||||
}
|
||||
|
||||
// Remove excluded classes from the graph
|
||||
@@ -1754,9 +1765,11 @@ EOF
|
||||
$sIconUrl = MetaModel::GetClassIcon($sListClass, false);
|
||||
$sIconUrl = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-'.utils::GetCurrentEnvironment().'/', $sIconUrl);
|
||||
$oTitle = new Html("<img src=\"$sIconUrl\" style=\"vertical-align:middle;width: 24px; height: 24px;\"/> ".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', $oSet->Count(), Metamodel::GetName($sListClass)));*/
|
||||
$oTitle = new Html(Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', $oSet->Count(), Metamodel::GetName($sListClass)));
|
||||
$oTitle = new Html(Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', $oSet->Count(),
|
||||
Metamodel::GetName($sListClass)));
|
||||
$oPage->AddSubBlock(TitleUIBlockFactory::MakeStandard($oTitle, 2));
|
||||
$oPage->AddSubBlock(cmdbAbstractObject::GetDataTableFromDBObjectSet($oSet, array('table_id' => $sSourceClass.'_'.$sRelation.'_'.$sDirection.'_'.$sListClass)));
|
||||
$oPage->AddSubBlock(cmdbAbstractObject::GetDataTableFromDBObjectSet($oSet,
|
||||
array('table_id' => $sSourceClass.'_'.$sRelation.'_'.$sDirection.'_'.$sListClass)));
|
||||
}
|
||||
|
||||
// Then the content of the groups (one table per group)
|
||||
@@ -1768,8 +1781,10 @@ EOF
|
||||
$sListClass = get_class(current($aObjects));
|
||||
$oSet = CMDBObjectSet::FromArray($sListClass, $aObjects);
|
||||
$sIconUrl = MetaModel::GetClassIcon($sListClass, false);
|
||||
$sIconUrl = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-'.utils::GetCurrentEnvironment().'/', $sIconUrl);
|
||||
$oTitle = new Html("<img src=\"$sIconUrl\" style=\"vertical-align:middle;width: 24px; height: 24px;\"/> ".Dict::Format('UI:RelationGroupNumber_N', (1 + $idx)), Metamodel::GetName($sListClass));
|
||||
$sIconUrl = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-'.utils::GetCurrentEnvironment().'/',
|
||||
$sIconUrl);
|
||||
$oTitle = new Html("<img src=\"$sIconUrl\" style=\"vertical-align:middle;width: 24px; height: 24px;\"/> ".Dict::Format('UI:RelationGroupNumber_N',
|
||||
(1 + $idx)), Metamodel::GetName($sListClass));
|
||||
$oPage->AddSubBlock(TitleUIBlockFactory::MakeStandard($oTitle, 2));
|
||||
$oPage->AddSubBlock(cmdbAbstractObject::GetDataTableFromDBObjectSet($oSet));
|
||||
|
||||
@@ -1843,7 +1858,8 @@ EOF
|
||||
if ($sDirection == 'up') {
|
||||
$oRelGraph = MetaModel::GetRelatedObjectsUp($sRelation, $aSourceObjects, $iMaxRecursionDepth, true, $aContexts);
|
||||
} else {
|
||||
$oRelGraph = MetaModel::GetRelatedObjectsDown($sRelation, $aSourceObjects, $iMaxRecursionDepth, true, $aExcludedObjects, $aContexts);
|
||||
$oRelGraph = MetaModel::GetRelatedObjectsDown($sRelation, $aSourceObjects, $iMaxRecursionDepth, true, $aExcludedObjects,
|
||||
$aContexts);
|
||||
}
|
||||
|
||||
// Remove excluded classes from the graph
|
||||
@@ -1874,13 +1890,15 @@ EOF
|
||||
$oSearch = new DBObjectSearch($sListClass);
|
||||
$oSearch->AddCondition('id', $aDefinition['keys'], 'IN');
|
||||
$oSearch->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
$oPage->AddUiBlock(TitleUIBlockFactory::MakeNeutral(Dict::Format('UI:RelationGroupNumber_N', (1 + $idx)), 1, "relation_group_$idx"));
|
||||
$oPage->AddUiBlock(TitleUIBlockFactory::MakeNeutral(Dict::Format('UI:RelationGroupNumber_N', (1 + $idx)), 1,
|
||||
"relation_group_$idx"));
|
||||
$oBlock = new DisplayBlock($oSearch, 'list');
|
||||
$oBlock->Display($oPage, 'group_'.$iBlock++, array(
|
||||
'surround_with_panel' => true,
|
||||
'panel_class' => $sListClass,
|
||||
'panel_title' => Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aDefinition['keys']), Metamodel::GetName($sListClass)),
|
||||
'panel_icon' => MetaModel::GetClassIcon($sListClass, false),
|
||||
'panel_class' => $sListClass,
|
||||
'panel_title' => Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aDefinition['keys']),
|
||||
Metamodel::GetName($sListClass)),
|
||||
'panel_icon' => MetaModel::GetClassIcon($sListClass, false),
|
||||
));
|
||||
}
|
||||
break;
|
||||
@@ -1894,11 +1912,12 @@ EOF
|
||||
$oSearch->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
$oBlock = new DisplayBlock($oSearch, 'list');
|
||||
$oBlock->Display($oPage, 'list_'.$iBlock++, array(
|
||||
'table_id' => 'ImpactAnalysis_'.$sListClass,
|
||||
'table_id' => 'ImpactAnalysis_'.$sListClass,
|
||||
'surround_with_panel' => true,
|
||||
'panel_class' => $sListClass,
|
||||
'panel_title' => Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aKeys), Metamodel::GetName($sListClass)),
|
||||
'panel_icon' => MetaModel::GetClassIcon($sListClass, false),
|
||||
'panel_class' => $sListClass,
|
||||
'panel_title' => Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', count($aKeys),
|
||||
Metamodel::GetName($sListClass)),
|
||||
'panel_icon' => MetaModel::GetClassIcon($sListClass, false),
|
||||
));
|
||||
}
|
||||
break;
|
||||
@@ -1950,7 +1969,8 @@ EOF
|
||||
|
||||
$sContextKey = 'itop-tickets/relation_context/'.$sClass.'/'.$sRelation.'/'.$sDirection;
|
||||
$oAppContext = new ApplicationContext();
|
||||
$oGraph->Display($oPage, $aResults, $sRelation, $oAppContext, $aExcludedObjects, $sClass, $iId, $sContextKey, array('this' => $oTicket));
|
||||
$oGraph->Display($oPage, $aResults, $sRelation, $oAppContext, $aExcludedObjects, $sClass, $iId, $sContextKey,
|
||||
array('this' => $oTicket));
|
||||
break;
|
||||
|
||||
case 'export_build':
|
||||
@@ -2000,7 +2020,7 @@ EOF
|
||||
$aLockData = iTopOwnershipLock::IsLocked($sObjClass, $iObjKey);
|
||||
|
||||
$aResult = [
|
||||
'locked' => $aLockData['locked'],
|
||||
'locked' => $aLockData['locked'],
|
||||
'message' => '',
|
||||
];
|
||||
|
||||
@@ -2087,11 +2107,11 @@ EOF
|
||||
$aResult = array(
|
||||
'uploaded' => 0,
|
||||
'fileName' => '',
|
||||
'url' => '',
|
||||
'icon' => '',
|
||||
'msg' => '',
|
||||
'att_id' => 0,
|
||||
'preview' => 'false',
|
||||
'url' => '',
|
||||
'icon' => '',
|
||||
'msg' => '',
|
||||
'att_id' => 0,
|
||||
'preview' => 'false',
|
||||
);
|
||||
|
||||
$sObjClass = stripslashes(utils::ReadParam('obj_class', '', false, 'class'));
|
||||
@@ -2126,20 +2146,19 @@ EOF
|
||||
}
|
||||
|
||||
IssueLog::Trace('InlineImage created', LogChannels::INLINE_IMAGE, array(
|
||||
'$operation' => $operation,
|
||||
'$aResult' => $aResult,
|
||||
'secret' => $oAttachment->Get('secret'),
|
||||
'temp_id' => $sTempId,
|
||||
'item_class' => $sObjClass,
|
||||
'user' => UserRights::GetUser(),
|
||||
'$operation' => $operation,
|
||||
'$aResult' => $aResult,
|
||||
'secret' => $oAttachment->Get('secret'),
|
||||
'temp_id' => $sTempId,
|
||||
'item_class' => $sObjClass,
|
||||
'user' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
));
|
||||
} else {
|
||||
$aResult['error'] = $oDoc->GetFileName().' is not a valid image format.';
|
||||
}
|
||||
}
|
||||
catch (FileUploadException $e) {
|
||||
} catch (FileUploadException $e) {
|
||||
$aResult['error'] = $e->GetMessage();
|
||||
}
|
||||
}
|
||||
@@ -2156,8 +2175,8 @@ EOF
|
||||
if (!InlineImage::IsImage($sDocMimeType)) {
|
||||
LogErrorMessage('CKE : error when uploading image in ajax.render.php, not an image',
|
||||
array(
|
||||
'operation' => 'cke_upload_and_browse',
|
||||
'class' => $sObjClass,
|
||||
'operation' => 'cke_upload_and_browse',
|
||||
'class' => $sObjClass,
|
||||
'ImgMimeType' => $sDocMimeType,
|
||||
));
|
||||
} else {
|
||||
@@ -2174,22 +2193,21 @@ EOF
|
||||
$iAttId = $oAttachment->DBInsert();
|
||||
|
||||
IssueLog::Trace('InlineImage created', LogChannels::INLINE_IMAGE, array(
|
||||
'$operation' => $operation,
|
||||
'secret' => $oAttachment->Get('secret'),
|
||||
'temp_id' => $sTempId,
|
||||
'item_class' => $sObjClass,
|
||||
'user' => UserRights::GetUser(),
|
||||
'$operation' => $operation,
|
||||
'secret' => $oAttachment->Get('secret'),
|
||||
'temp_id' => $sTempId,
|
||||
'item_class' => $sObjClass,
|
||||
'user' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
catch (FileUploadException $e) {
|
||||
} catch (FileUploadException $e) {
|
||||
LogErrorMessage('CKE : error when uploading image in ajax.render.php, exception occured',
|
||||
array(
|
||||
'operation' => 'cke_upload_and_browse',
|
||||
'class' => $sObjClass,
|
||||
'operation' => 'cke_upload_and_browse',
|
||||
'class' => $sObjClass,
|
||||
'exceptionMsg' => $e,
|
||||
));
|
||||
}
|
||||
@@ -2303,7 +2321,8 @@ $('.img-picker').magnificPopup({type: 'image', closeOnContentClick: true });
|
||||
EOF
|
||||
);
|
||||
$sOQL = "SELECT InlineImage WHERE ((temp_id = :temp_id) OR (item_class = :obj_class AND item_id = :obj_id))";
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array(), array('temp_id' => $sTempId, 'obj_class' => $sClass, 'obj_id' => $iObjectId));
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array(),
|
||||
array('temp_id' => $sTempId, 'obj_class' => $sClass, 'obj_id' => $iObjectId));
|
||||
$oPage->add("<div><fieldset><legend>$sAvailableImagesLegend</legend>");
|
||||
|
||||
if ($oSet->Count() == 0) {
|
||||
@@ -2360,7 +2379,8 @@ EOF
|
||||
$aTriggerMentionedSearches = [];
|
||||
|
||||
$aTriggerSetParams = array('class_list' => MetaModel::EnumParentClasses($sHostClass, ENUM_PARENT_CLASSES_ALL));
|
||||
$oTriggerSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectMention AS t WHERE t.target_class IN (:class_list)"), array(), $aTriggerSetParams);
|
||||
$oTriggerSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectMention AS t WHERE t.target_class IN (:class_list)"),
|
||||
array(), $aTriggerSetParams);
|
||||
/** @var \TriggerOnObjectMention $oTrigger */
|
||||
while ($oTrigger = $oTriggerSet->Fetch()) {
|
||||
$sTriggerMentionedOQL = $oTrigger->Get('mentioned_filter');
|
||||
@@ -2394,7 +2414,8 @@ EOF
|
||||
|
||||
// Add condition to filter on the friendlyname
|
||||
$oSearch->AddConditionExpression(
|
||||
new BinaryExpression(new FieldExpression('friendlyname', $sSearchMainClassAlias), 'LIKE', new VariableExpression('needle'))
|
||||
new BinaryExpression(new FieldExpression('friendlyname', $sSearchMainClassAlias), 'LIKE',
|
||||
new VariableExpression('needle'))
|
||||
);
|
||||
|
||||
$oSet = new DBObjectSet($oSearch, [], $aSearchParams);
|
||||
@@ -2413,8 +2434,8 @@ EOF
|
||||
$sObjectClass = get_class($oObject);
|
||||
$iObjectId = $oObject->GetKey();
|
||||
$aMatch = [
|
||||
'class' => $sObjectClass,
|
||||
'id' => $iObjectId,
|
||||
'class' => $sObjectClass,
|
||||
'id' => $iObjectId,
|
||||
'friendlyname' => $oObject->Get('friendlyname'),
|
||||
];
|
||||
|
||||
@@ -2423,7 +2444,8 @@ EOF
|
||||
/** @var \ormDocument $oImage */
|
||||
$oImage = $oObject->Get($sObjectImageAttCode);
|
||||
if (!$oImage->IsEmpty()) {
|
||||
$aMatch['picture_style'] = "background-image: url('".$oImage->GetDisplayURL($sObjectClass, $iObjectId, $sObjectImageAttCode)."')";
|
||||
$aMatch['picture_style'] = "background-image: url('".$oImage->GetDisplayURL($sObjectClass, $iObjectId,
|
||||
$sObjectImageAttCode)."')";
|
||||
$aMatch['initials'] = '';
|
||||
} else {
|
||||
// If no image found, fallback on initials
|
||||
@@ -2460,8 +2482,7 @@ EOF
|
||||
$aRenderRes = $oRenderer->Render($aRequestedFields);
|
||||
|
||||
$aResult['form']['updated_fields'] = $aRenderRes;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
$aResult['error'] = $e->getMessage();
|
||||
}
|
||||
$oPage->SetData($aResult);
|
||||
@@ -2478,10 +2499,9 @@ EOF
|
||||
$oController = new PreferencesController();
|
||||
$aResult = $oController->SetUserPicture();
|
||||
$aResult['success'] = true;
|
||||
}
|
||||
catch (Exception $oException) {
|
||||
} catch (Exception $oException) {
|
||||
$aResult = [
|
||||
'success' => false,
|
||||
'success' => false,
|
||||
'error_message' => $oException->getMessage(),
|
||||
];
|
||||
}
|
||||
@@ -2500,10 +2520,9 @@ EOF
|
||||
$aResult = [
|
||||
'success' => true,
|
||||
];
|
||||
}
|
||||
catch (Exception $oException) {
|
||||
} catch (Exception $oException) {
|
||||
$aResult = [
|
||||
'success' => false,
|
||||
'success' => false,
|
||||
'error_message' => $oException->getMessage(),
|
||||
];
|
||||
}
|
||||
@@ -2516,10 +2535,9 @@ EOF
|
||||
try {
|
||||
$oController = new ActivityPanelController();
|
||||
$aResult = $oController->AddCaseLogsEntries();
|
||||
}
|
||||
catch (Exception $oException) {
|
||||
} catch (Exception $oException) {
|
||||
$aResult = [
|
||||
'success' => false,
|
||||
'success' => false,
|
||||
'error_message' => $oException->getMessage(),
|
||||
];
|
||||
}
|
||||
@@ -2532,10 +2550,9 @@ EOF
|
||||
try {
|
||||
$oController = new ActivityPanelController();
|
||||
$aResult = $oController->LoadMoreEntries();
|
||||
}
|
||||
catch (Exception $oException) {
|
||||
} catch (Exception $oException) {
|
||||
$aResult = [
|
||||
'success' => false,
|
||||
'success' => false,
|
||||
'error_message' => $oException->getMessage(),
|
||||
];
|
||||
}
|
||||
@@ -2551,6 +2568,24 @@ EOF
|
||||
$oAjaxRenderController->GetMenusCount($oPage);
|
||||
break;
|
||||
|
||||
//--------------------------------
|
||||
// WelcomePopupMenu
|
||||
//--------------------------------
|
||||
case 'welcome_popup_acknowledge_message':
|
||||
$oPage = new JsonPage();
|
||||
try {
|
||||
$oController = new WelcomePopupController();
|
||||
$oController->AcknowledgeMessage();
|
||||
$aResult = ['success' => true];
|
||||
} catch (Exception $oException) {
|
||||
$aResult = [
|
||||
'success' => false,
|
||||
'error_message' => $oException->getMessage(),
|
||||
];
|
||||
}
|
||||
$oPage->SetData($aResult);
|
||||
break;
|
||||
|
||||
//--------------------------------
|
||||
// Object
|
||||
//--------------------------------
|
||||
|
||||
@@ -141,55 +141,58 @@ JS
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
$oFavoriteOrganizationsBlock = new Panel(Dict::S('UI:FavoriteOrganizations'), array(), 'grey', 'ibo-favorite-organizations');
|
||||
$oFavoriteOrganizationsBlock->SetSubTitle(Dict::S('UI:FavoriteOrganizations+'));
|
||||
$oFavoriteOrganizationsBlock->AddCSSClass('ibo-datatable-panel');
|
||||
$oFavoriteOrganizationsForm = new Form();
|
||||
$oFavoriteOrganizationsBlock->AddSubBlock($oFavoriteOrganizationsForm);
|
||||
// Favorite organizations: the organizations listed in the drop-down menu
|
||||
$sOQL = ApplicationMenu::GetFavoriteSiloQuery();
|
||||
$oFilter = DBObjectSearch::FromOQL($sOQL);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$bIsSiloSelectionEnabled = MetaModel::GetConfig()->Get('navigation_menu.show_organization_filter');
|
||||
if ($bIsSiloSelectionEnabled)
|
||||
{
|
||||
$oFavoriteOrganizationsBlock = new Panel(Dict::S('UI:FavoriteOrganizations'), array(), 'grey', 'ibo-favorite-organizations');
|
||||
$oFavoriteOrganizationsBlock->SetSubTitle(Dict::S('UI:FavoriteOrganizations+'));
|
||||
$oFavoriteOrganizationsBlock->AddCSSClass('ibo-datatable-panel');
|
||||
$oFavoriteOrganizationsForm = new Form();
|
||||
$oFavoriteOrganizationsBlock->AddSubBlock($oFavoriteOrganizationsForm);
|
||||
// Favorite organizations: the organizations listed in the drop-down menu
|
||||
$sOQL = ApplicationMenu::GetFavoriteSiloQuery();
|
||||
$oFilter = DBObjectSearch::FromOQL($sOQL);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
|
||||
$aFavoriteOrgs = appUserPreferences::GetPref('favorite_orgs', null);
|
||||
$aFavoriteOrgs = appUserPreferences::GetPref('favorite_orgs', null);
|
||||
|
||||
$sIdFavoriteOrganizations = 1;
|
||||
$oFavoriteOrganizationsForm->AddSubBlock($oBlock->GetDisplay($oP, $sIdFavoriteOrganizations, [
|
||||
'menu' => false,
|
||||
'selection_mode' => true,
|
||||
'selection_type' => 'multiple',
|
||||
'table_id' => 'user_prefs',
|
||||
'surround_with_panel' => false,
|
||||
'selected_rows' => $aFavoriteOrgs,
|
||||
]));
|
||||
$oFavoriteOrganizationsForm->AddSubBlock($oAppContext->GetForFormBlock());
|
||||
$sIdFavoriteOrganizations = 1;
|
||||
$oFavoriteOrganizationsForm->AddSubBlock($oBlock->GetDisplay($oP, $sIdFavoriteOrganizations, [
|
||||
'menu' => false,
|
||||
'selection_mode' => true,
|
||||
'selection_type' => 'multiple',
|
||||
'table_id' => 'user_prefs',
|
||||
'surround_with_panel' => false,
|
||||
'selected_rows' => $aFavoriteOrgs,
|
||||
]));
|
||||
$oFavoriteOrganizationsForm->AddSubBlock($oAppContext->GetForFormBlock());
|
||||
|
||||
// Button toolbar
|
||||
$oFavoriteOrganizationsToolBar = ToolbarUIBlockFactory::MakeForButton(null, ['ibo-is-fullwidth']);
|
||||
$oFavoriteOrganizationsForm->AddSubBlock($oFavoriteOrganizationsToolBar);
|
||||
// Button toolbar
|
||||
$oFavoriteOrganizationsToolBar = ToolbarUIBlockFactory::MakeForButton(null, ['ibo-is-fullwidth']);
|
||||
$oFavoriteOrganizationsForm->AddSubBlock($oFavoriteOrganizationsToolBar);
|
||||
|
||||
// - Cancel button
|
||||
$oFavoriteOrganizationsCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'));
|
||||
$oFavoriteOrganizationsToolBar->AddSubBlock($oFavoriteOrganizationsCancelButton);
|
||||
$oFavoriteOrganizationsCancelButton->SetOnClickJsCode("window.location.href = '$sURL'");
|
||||
// - Submit button
|
||||
$oFavoriteOrganizationsSubmitButton = ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('UI:Button:Apply'), 'operation', 'apply', true);
|
||||
$oFavoriteOrganizationsToolBar->AddSubBlock($oFavoriteOrganizationsSubmitButton);
|
||||
// - Cancel button
|
||||
$oFavoriteOrganizationsCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'));
|
||||
$oFavoriteOrganizationsToolBar->AddSubBlock($oFavoriteOrganizationsCancelButton);
|
||||
$oFavoriteOrganizationsCancelButton->SetOnClickJsCode("window.location.href = '$sURL'");
|
||||
// - Submit button
|
||||
$oFavoriteOrganizationsSubmitButton = ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('UI:Button:Apply'), 'operation', 'apply', true);
|
||||
$oFavoriteOrganizationsToolBar->AddSubBlock($oFavoriteOrganizationsSubmitButton);
|
||||
|
||||
// TODO 3.0 have this code work again, currently it prevents the display of favorite organizations and shortcuts.
|
||||
// if ($aFavoriteOrgs == null) {
|
||||
// // All checked
|
||||
// $oP->add_ready_script(
|
||||
// <<<JS
|
||||
// $('#$sIdFavoriteOrganizations.checkAll').prop('checked', true);
|
||||
// checkAllDataTable('datatable_$sIdFavoriteOrganizations',true,'$sIdFavoriteOrganizations');
|
||||
//JS
|
||||
// );
|
||||
//
|
||||
// }
|
||||
|
||||
$oContentLayout->AddMainBlock($oFavoriteOrganizationsBlock);
|
||||
// TODO 3.0 have this code work again, currently it prevents the display of favorite organizations and shortcuts.
|
||||
// if ($aFavoriteOrgs == null) {
|
||||
// // All checked
|
||||
// $oP->add_ready_script(
|
||||
// <<<JS
|
||||
// $('#$sIdFavoriteOrganizations.checkAll').prop('checked', true);
|
||||
// checkAllDataTable('datatable_$sIdFavoriteOrganizations',true,'$sIdFavoriteOrganizations');
|
||||
//JS
|
||||
// );
|
||||
//
|
||||
// }
|
||||
|
||||
$oContentLayout->AddMainBlock($oFavoriteOrganizationsBlock);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Shortcuts
|
||||
|
||||
@@ -109,6 +109,7 @@ try
|
||||
$aExtraParams['action'] = utils::GetAbsoluteUrlAppRoot().'pages/tagadmin.php';
|
||||
$aExtraParams['table_id'] = '1';
|
||||
$aExtraParams['search_header_force_dropdown'] = $sSearchHeaderForceDropdown;
|
||||
$aExtraParams['submit_on_load'] = false;
|
||||
$oBlock->Display($oP, 0, $aExtraParams);
|
||||
|
||||
// Search results
|
||||
|
||||
@@ -149,7 +149,7 @@ class DBBackup
|
||||
* @param string|null $sNameSpec Name and path, eventually containing itop placeholders + time formatting following the strftime() format {@link https://www.php.net/manual/fr/function.strftime.php}
|
||||
* @param \DateTime|null $oDateTime Date time to use for the name
|
||||
*
|
||||
* @return string Name of the backup file WITHOUT the file extension (eg. `.tar.gz`)
|
||||
* @return ?string Name of the backup file WITHOUT the file extension (eg. `.tar.gz`)
|
||||
* @since 3.1.0 N°5279 Add $oDateTime parameter
|
||||
*/
|
||||
public function MakeName(?string $sNameSpec = null, ?DateTime $oDateTime = null)
|
||||
@@ -363,6 +363,7 @@ class DBBackup
|
||||
|
||||
$sPortOption = self::GetMysqliCliSingleOption('port', $this->iDBPort);
|
||||
$sTlsOptions = self::GetMysqlCliTlsOptions($this->oConfig);
|
||||
$sProtocolOption = self::GetMysqlCliTransportOption($this->sDBHost);
|
||||
|
||||
$sMysqlVersion = CMDBSource::GetDBVersion();
|
||||
$bIsMysqlSupportUtf8mb4 = (version_compare($sMysqlVersion, self::MYSQL_VERSION_WITH_UTF8MB4_IN_PROGRAMS) === -1);
|
||||
@@ -383,8 +384,8 @@ EOF;
|
||||
|
||||
// Note: opt implicitely sets lock-tables... which cancels the benefit of single-transaction!
|
||||
// skip-lock-tables compensates and allows for writes during a backup
|
||||
$sCommand = "$sMySQLDump --defaults-extra-file=\"$sMySQLDumpCnfFile\" --opt --skip-lock-tables --default-character-set=".$sMysqldumpCharset." --add-drop-database --single-transaction --host=$sHost $sPortOption --user=$sUser $sTlsOptions --result-file=$sTmpFileName $sDBName $sTables 2>&1";
|
||||
$sCommandDisplay = "$sMySQLDump --defaults-extra-file=\"$sMySQLDumpCnfFile\" --opt --skip-lock-tables --default-character-set=".$sMysqldumpCharset." --add-drop-database --single-transaction --host=$sHost $sPortOption --user=xxxxx $sTlsOptions --result-file=$sTmpFileName $sDBName $sTables";
|
||||
$sCommand = "$sMySQLDump --defaults-extra-file=\"$sMySQLDumpCnfFile\" --opt --skip-lock-tables --default-character-set=".$sMysqldumpCharset." --add-drop-database --single-transaction --host=$sHost $sPortOption $sProtocolOption --user=$sUser $sTlsOptions --result-file=$sTmpFileName $sDBName $sTables 2>&1";
|
||||
$sCommandDisplay = "$sMySQLDump --defaults-extra-file=\"$sMySQLDumpCnfFile\" --opt --skip-lock-tables --default-character-set=".$sMysqldumpCharset." --add-drop-database --single-transaction --host=$sHost $sPortOption $sProtocolOption --user=xxxxx $sTlsOptions --result-file=$sTmpFileName $sDBName $sTables";
|
||||
|
||||
// Now run the command for real
|
||||
$this->LogInfo("backup: generate data file with command: $sCommandDisplay");
|
||||
@@ -472,8 +473,8 @@ EOF;
|
||||
if ($oMysqli->connect_errno)
|
||||
{
|
||||
$sHost = is_null($this->iDBPort) ? $this->sDBHost : $this->sDBHost.' on port '.$this->iDBPort;
|
||||
throw new BackupException("Cannot connect to the MySQL server '$sHost' (".$oMysqli->connect_errno.") ".$oMysqli->connect_error);
|
||||
}
|
||||
throw new MySQLException('Could not connect to the DB server '.$oMysqli->connect_errno.' (mysql errno: '.$oMysqli->connect_error, array('host' => $sHost, 'user' => $sUser));
|
||||
}
|
||||
if (!$oMysqli->select_db($this->sDBName))
|
||||
{
|
||||
throw new BackupException("The database '$this->sDBName' does not seem to exist");
|
||||
@@ -581,6 +582,28 @@ EOF;
|
||||
return ' --'.$sCliArgName.'='.self::EscapeShellArg($sData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define if we should force a transport option
|
||||
*
|
||||
* @param string $sHost
|
||||
*
|
||||
* @return string .
|
||||
|
||||
* @since 2.7.9 3.0.4 3.1.1 N°6123
|
||||
*/
|
||||
public static function GetMysqlCliTransportOption(string $sHost)
|
||||
{
|
||||
$sTransportOptions = '';
|
||||
|
||||
/** N°6123 As we're using a --port option, if we use localhost as host,
|
||||
* MariaDB > 10.6 will implicitly change its protocol from socket to tcp and throw a warning **/
|
||||
if($sHost === 'localhost'){
|
||||
$sTransportOptions = '--protocol=tcp';
|
||||
}
|
||||
|
||||
return $sTransportOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the command to launch mysqldump (without its params)
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user