Compare commits

...

479 Commits

Author SHA1 Message Date
Anne-Cath
8fc5dadad9 WIP 2025-09-08 16:13:17 +02:00
Anne-Cath
036a5d623b WIP 2025-09-08 16:10:52 +02:00
Anne-Cath
e8e8828f66 WIP 2025-09-08 16:03:52 +02:00
Anne-Cath
bbdef6b730 WIP 2025-09-08 15:56:15 +02:00
Anne-Cath
dd2f6e7413 WIP 2025-09-08 15:21:34 +02:00
Anne-Cath
ab4993debe WIP 2025-09-08 15:10:56 +02:00
Anne-Cath
455ce42a2f composer 2025-09-04 11:30:54 +02:00
Anne-Cath
f4ebbfe8ea move file 2025-09-04 11:25:44 +02:00
Anne-Cath
8d9069728a Rename file 2025-09-04 11:22:41 +02:00
Anne-Cath
80c699c56a move file 2025-09-04 11:22:30 +02:00
Anne-Cath
e923c0fef3 move file 2025-09-04 11:21:24 +02:00
Anne-Cath
70fd8ed128 Rename file 2025-09-04 11:20:14 +02:00
anne-catherine
196ceba1bb Merge branch 'split-file_counter.class.inc.php' into feature/2535-split_file 2025-09-04 09:12:58 +00:00
Potherca-Bot
7d82057e66 Merge branch 'split-file_counter.class.inc.php_KeyValueStore.php' into split-file_counter.class.inc.php 2025-09-04 09:10:58 +00:00
Potherca-Bot
fc9abc95cb Merge branch 'split-file_counter.class.inc.php_ItopCounter.php' into split-file_counter.class.inc.php 2025-09-04 09:10:57 +00:00
Potherca-Bot
ddff020a1d Changes content in separated file 'KeyValueStore.php'. 2025-09-04 09:10:57 +00:00
Potherca-Bot
b803db38b9 Changes content in separated file 'ItopCounter.php'. 2025-09-04 09:10:57 +00:00
Potherca-Bot
010edf1407 Adds separate file for 'KeyValueStore.php'. 2025-09-04 09:10:57 +00:00
Potherca-Bot
0006491304 Adds separate file for 'ItopCounter.php'. 2025-09-04 09:10:57 +00:00
anne-catherine
e0674fb797 end merge 2025-09-04 07:53:12 +00:00
Anne-Cath
f4fbbf3a4f Rename folder 2025-09-04 09:44:43 +02:00
Anne-Cath
392957d849 Move folder 2025-09-04 09:40:39 +02:00
Anne-Cath
71604e65d2 Rename folder 2025-09-04 09:38:12 +02:00
Anne-Cath
648692409b Move folder 2025-09-04 09:35:39 +02:00
Anne-Cath
950f283e9b Rename file 2025-09-04 09:29:46 +02:00
Anne-Cath
10e2679887 WIP 2025-09-04 09:28:59 +02:00
anne-catherine
8e789d51ce end merge 2025-09-03 09:18:40 +00:00
Anne-Cath
7ed43c3e50 N°6932 - Split files 2025-09-03 11:16:06 +02:00
Potherca-Bot
90fc39c409 Merge branch 'split-file_relationgraph.class.inc.php_RelationRedundancyNode.php' into split-file_relationgraph.class.inc.php 2025-09-03 09:12:57 +00:00
Potherca-Bot
88a0678149 Merge branch 'split-file_relationgraph.class.inc.php_RelationObjectNode.php' into split-file_relationgraph.class.inc.php 2025-09-03 09:12:57 +00:00
Potherca-Bot
1f7d9c0d0f Merge branch 'split-file_relationgraph.class.inc.php_RelationEdge.php' into split-file_relationgraph.class.inc.php 2025-09-03 09:12:57 +00:00
Potherca-Bot
e4c025a7f6 Changes content in separated file 'RelationRedundancyNode.php'. 2025-09-03 09:12:56 +00:00
Potherca-Bot
f9d1475147 Changes content in separated file 'RelationObjectNode.php'. 2025-09-03 09:12:56 +00:00
Potherca-Bot
be173eaf1b Adds separate file for 'RelationRedundancyNode.php'. 2025-09-03 09:12:56 +00:00
Potherca-Bot
b7e69c65a8 Adds separate file for 'RelationObjectNode.php'. 2025-09-03 09:12:56 +00:00
Potherca-Bot
980cecccad Changes content in separated file 'RelationEdge.php'. 2025-09-03 09:12:55 +00:00
Potherca-Bot
e2115e61df Adds separate file for 'RelationEdge.php'. 2025-09-03 09:12:55 +00:00
anne-catherine
e013c28d67 Merge branch 'split-file_bulkexport.class.inc.php' into feature/2535-split_file 2025-09-03 09:02:36 +00:00
Potherca-Bot
f7d37b7ee1 Merge branch 'split-file_bulkexport.class.inc.php_BulkExportResult.php' into split-file_bulkexport.class.inc.php 2025-09-03 09:01:49 +00:00
Potherca-Bot
a2b1b77798 Merge branch 'split-file_bulkexport.class.inc.php_BulkExportResultGC.php' into split-file_bulkexport.class.inc.php 2025-09-03 09:01:49 +00:00
Potherca-Bot
aa048b65f3 Merge branch 'split-file_bulkexport.class.inc.php_BulkExport.php' into split-file_bulkexport.class.inc.php 2025-09-03 09:01:49 +00:00
Potherca-Bot
c1e915188a Changes content in separated file 'BulkExportResult.php'. 2025-09-03 09:01:48 +00:00
Potherca-Bot
4b374c7e9e Merge branch 'split-file_bulkexport.class.inc.php_BulkExportMissingParameterException.php' into split-file_bulkexport.class.inc.php 2025-09-03 09:01:48 +00:00
Potherca-Bot
554fec71f0 Merge branch 'split-file_bulkexport.class.inc.php_BulkExportException.php' into split-file_bulkexport.class.inc.php 2025-09-03 09:01:48 +00:00
Potherca-Bot
c4385c1627 Changes content in separated file 'BulkExportResultGC.php'. 2025-09-03 09:01:47 +00:00
Potherca-Bot
2cf62dc72e Changes content in separated file 'BulkExport.php'. 2025-09-03 09:01:47 +00:00
Potherca-Bot
ecf4658d2c Adds separate file for 'BulkExportResult.php'. 2025-09-03 09:01:47 +00:00
Potherca-Bot
26826197ce Adds separate file for 'BulkExportResultGC.php'. 2025-09-03 09:01:47 +00:00
Potherca-Bot
f52bfddc58 Adds separate file for 'BulkExport.php'. 2025-09-03 09:01:47 +00:00
Potherca-Bot
540ff50ce1 Changes content in separated file 'BulkExportMissingParameterException.php'. 2025-09-03 09:01:46 +00:00
Potherca-Bot
fa41afccc8 Changes content in separated file 'BulkExportException.php'. 2025-09-03 09:01:46 +00:00
Potherca-Bot
31db7201c6 Adds separate file for 'BulkExportMissingParameterException.php'. 2025-09-03 09:01:46 +00:00
Potherca-Bot
4375e4085c Adds separate file for 'BulkExportException.php'. 2025-09-03 09:01:45 +00:00
anne-catherine
fbfbc78e2d Merge branch 'split-file_cmdbchangeop.class.inc.php' into feature/2535-split_file 2025-09-02 22:31:02 +00:00
Potherca-Bot
6746136db3 Merge branch 'split-file_cmdbchangeop.class.inc.php_iCMDBChangeOp.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:27 +00:00
Potherca-Bot
ccb60a2d41 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttributeURL.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:27 +00:00
Potherca-Bot
b799a38ec4 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttributeText.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:27 +00:00
Potherca-Bot
ff3da21f36 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttributeTagSet.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:27 +00:00
Potherca-Bot
2ee4595952 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttributeScalar.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:26 +00:00
Potherca-Bot
40a1ca98e1 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttribute.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:26 +00:00
Potherca-Bot
ab28f59a19 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttributeOneWayPassword.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:26 +00:00
Potherca-Bot
4a6804d084 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttributeLongText.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:26 +00:00
Potherca-Bot
1e4cf8931b Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttributeLinksTune.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:26 +00:00
Potherca-Bot
3c2a4c880b Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttributeLinks.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:26 +00:00
Potherca-Bot
28ddfe763c Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttributeLinksAddRemove.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:25 +00:00
Potherca-Bot
ad814df467 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttributeHTML.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:25 +00:00
Potherca-Bot
bcc42a09e3 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttributeEncrypted.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:25 +00:00
Potherca-Bot
e655e80157 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttributeCustomFields.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:25 +00:00
Potherca-Bot
1098e68bd0 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttributeCaseLog.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:25 +00:00
Potherca-Bot
b48e3e8bc5 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpSetAttributeBlob.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:25 +00:00
Potherca-Bot
346b105054 Changes content in separated file 'iCMDBChangeOp.php'. 2025-09-02 22:28:24 +00:00
Potherca-Bot
14095baf67 Changes content in separated file 'CMDBChangeOpSetAttributeURL.php'. 2025-09-02 22:28:24 +00:00
Potherca-Bot
199e02aa43 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpPlugin.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:24 +00:00
Potherca-Bot
30ff03bce0 Adds separate file for 'iCMDBChangeOp.php'. 2025-09-02 22:28:24 +00:00
Potherca-Bot
a4d1218707 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOp.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:24 +00:00
Potherca-Bot
b0bf38f0a4 Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpDelete.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:24 +00:00
Potherca-Bot
ec72dc9cff Merge branch 'split-file_cmdbchangeop.class.inc.php_CMDBChangeOpCreate.php' into split-file_cmdbchangeop.class.inc.php 2025-09-02 22:28:24 +00:00
Potherca-Bot
ea9c458c9f Changes content in separated file 'CMDBChangeOpSetAttributeText.php'. 2025-09-02 22:28:23 +00:00
Potherca-Bot
500ae157b3 Changes content in separated file 'CMDBChangeOpSetAttributeTagSet.php'. 2025-09-02 22:28:23 +00:00
Potherca-Bot
5de3a5331b Changes content in separated file 'CMDBChangeOpSetAttributeScalar.php'. 2025-09-02 22:28:23 +00:00
Potherca-Bot
fddf54b863 Adds separate file for 'CMDBChangeOpSetAttributeURL.php'. 2025-09-02 22:28:23 +00:00
Potherca-Bot
9fba36ae29 Adds separate file for 'CMDBChangeOpSetAttributeText.php'. 2025-09-02 22:28:23 +00:00
Potherca-Bot
d2ca1d59a6 Adds separate file for 'CMDBChangeOpSetAttributeTagSet.php'. 2025-09-02 22:28:23 +00:00
Potherca-Bot
714079df9f Changes content in separated file 'CMDBChangeOpSetAttribute.php'. 2025-09-02 22:28:22 +00:00
Potherca-Bot
b18733ac31 Changes content in separated file 'CMDBChangeOpSetAttributeOneWayPassword.php'. 2025-09-02 22:28:22 +00:00
Potherca-Bot
32ce6ac336 Changes content in separated file 'CMDBChangeOpSetAttributeLongText.php'. 2025-09-02 22:28:22 +00:00
Potherca-Bot
36de69b3d0 Adds separate file for 'CMDBChangeOpSetAttributeScalar.php'. 2025-09-02 22:28:22 +00:00
Potherca-Bot
17b05efbba Adds separate file for 'CMDBChangeOpSetAttribute.php'. 2025-09-02 22:28:22 +00:00
Potherca-Bot
079cceb48b Adds separate file for 'CMDBChangeOpSetAttributeOneWayPassword.php'. 2025-09-02 22:28:22 +00:00
Potherca-Bot
0c5e032d9a Adds separate file for 'CMDBChangeOpSetAttributeLongText.php'. 2025-09-02 22:28:22 +00:00
Potherca-Bot
407b41155c Changes content in separated file 'CMDBChangeOpSetAttributeLinksTune.php'. 2025-09-02 22:28:21 +00:00
Potherca-Bot
c2479757f4 Changes content in separated file 'CMDBChangeOpSetAttributeLinks.php'. 2025-09-02 22:28:21 +00:00
Potherca-Bot
2d27794f85 Changes content in separated file 'CMDBChangeOpSetAttributeLinksAddRemove.php'. 2025-09-02 22:28:21 +00:00
Potherca-Bot
47f0302967 Adds separate file for 'CMDBChangeOpSetAttributeLinksTune.php'. 2025-09-02 22:28:21 +00:00
Potherca-Bot
d9f49a7958 Adds separate file for 'CMDBChangeOpSetAttributeLinks.php'. 2025-09-02 22:28:21 +00:00
Potherca-Bot
8da798be3b Adds separate file for 'CMDBChangeOpSetAttributeLinksAddRemove.php'. 2025-09-02 22:28:21 +00:00
Potherca-Bot
e49b3c7366 Changes content in separated file 'CMDBChangeOpSetAttributeHTML.php'. 2025-09-02 22:28:20 +00:00
Potherca-Bot
d54abbe401 Changes content in separated file 'CMDBChangeOpSetAttributeEncrypted.php'. 2025-09-02 22:28:20 +00:00
Potherca-Bot
23a84b7864 Changes content in separated file 'CMDBChangeOpSetAttributeCustomFields.php'. 2025-09-02 22:28:20 +00:00
Potherca-Bot
5836b2a403 Changes content in separated file 'CMDBChangeOpSetAttributeCaseLog.php'. 2025-09-02 22:28:20 +00:00
Potherca-Bot
50feffe40f Adds separate file for 'CMDBChangeOpSetAttributeHTML.php'. 2025-09-02 22:28:20 +00:00
Potherca-Bot
c9682227b8 Adds separate file for 'CMDBChangeOpSetAttributeEncrypted.php'. 2025-09-02 22:28:20 +00:00
Potherca-Bot
b7f0915e2f Adds separate file for 'CMDBChangeOpSetAttributeCustomFields.php'. 2025-09-02 22:28:20 +00:00
Potherca-Bot
de93208fde Changes content in separated file 'CMDBChangeOpSetAttributeBlob.php'. 2025-09-02 22:28:19 +00:00
Potherca-Bot
e07e435f65 Changes content in separated file 'CMDBChangeOpPlugin.php'. 2025-09-02 22:28:19 +00:00
Potherca-Bot
d8876eef24 Changes content in separated file 'CMDBChangeOp.php'. 2025-09-02 22:28:19 +00:00
Potherca-Bot
e05b519215 Adds separate file for 'CMDBChangeOpSetAttributeCaseLog.php'. 2025-09-02 22:28:19 +00:00
Potherca-Bot
67e288fbc8 Adds separate file for 'CMDBChangeOpSetAttributeBlob.php'. 2025-09-02 22:28:19 +00:00
Potherca-Bot
621b71f672 Adds separate file for 'CMDBChangeOpPlugin.php'. 2025-09-02 22:28:19 +00:00
Potherca-Bot
4adefa5cd8 Adds separate file for 'CMDBChangeOp.php'. 2025-09-02 22:28:19 +00:00
Potherca-Bot
4993240c19 Changes content in separated file 'CMDBChangeOpDelete.php'. 2025-09-02 22:28:18 +00:00
Potherca-Bot
c21169d6a7 Changes content in separated file 'CMDBChangeOpCreate.php'. 2025-09-02 22:28:18 +00:00
Potherca-Bot
af78129b7d Adds separate file for 'CMDBChangeOpDelete.php'. 2025-09-02 22:28:18 +00:00
Potherca-Bot
3175734b4f Adds separate file for 'CMDBChangeOpCreate.php'. 2025-09-02 22:28:18 +00:00
anne-catherine
a27a4d9f64 Merge branch 'split-file_simplecrypt.class.inc.php' into feature/2535-split_file 2025-09-02 21:02:57 +00:00
Potherca-Bot
132876fc57 Merge branch 'split-file_simplecrypt.class.inc.php_SimpleCryptSodiumEngine.php' into split-file_simplecrypt.class.inc.php 2025-09-02 18:15:35 +00:00
Potherca-Bot
a454e9cb84 Merge branch 'split-file_simplecrypt.class.inc.php_SimpleCryptSimpleEngine.php' into split-file_simplecrypt.class.inc.php 2025-09-02 18:15:35 +00:00
Potherca-Bot
7e6389eac2 Merge branch 'split-file_simplecrypt.class.inc.php_SimpleCrypt.php' into split-file_simplecrypt.class.inc.php 2025-09-02 18:15:34 +00:00
Potherca-Bot
4b86b1680b Merge branch 'split-file_simplecrypt.class.inc.php_SimpleCryptOpenSSLMcryptCompatibilityEngine.php' into split-file_simplecrypt.class.inc.php 2025-09-02 18:15:34 +00:00
Potherca-Bot
1d6d58384c Merge branch 'split-file_simplecrypt.class.inc.php_SimpleCryptOpenSSLEngine.php' into split-file_simplecrypt.class.inc.php 2025-09-02 18:15:34 +00:00
Potherca-Bot
f1f2a74394 Merge branch 'split-file_simplecrypt.class.inc.php_SimpleCryptMcryptEngine.php' into split-file_simplecrypt.class.inc.php 2025-09-02 18:15:34 +00:00
Potherca-Bot
bf575889cc Merge branch 'split-file_simplecrypt.class.inc.php_CryptEngine.php' into split-file_simplecrypt.class.inc.php 2025-09-02 18:15:34 +00:00
Potherca-Bot
43a9fee52b Changes content in separated file 'SimpleCryptSodiumEngine.php'. 2025-09-02 18:15:33 +00:00
Potherca-Bot
fd1a457ac3 Changes content in separated file 'SimpleCryptSimpleEngine.php'. 2025-09-02 18:15:33 +00:00
Potherca-Bot
afd8c487c4 Changes content in separated file 'SimpleCrypt.php'. 2025-09-02 18:15:33 +00:00
Potherca-Bot
41b92e2d3d Changes content in separated file 'SimpleCryptOpenSSLMcryptCompatibilityEngine.php'. 2025-09-02 18:15:33 +00:00
Potherca-Bot
b12d185376 Adds separate file for 'SimpleCryptSodiumEngine.php'. 2025-09-02 18:15:33 +00:00
Potherca-Bot
eaac1c293e Adds separate file for 'SimpleCryptSimpleEngine.php'. 2025-09-02 18:15:33 +00:00
Potherca-Bot
f7a97bd590 Adds separate file for 'SimpleCrypt.php'. 2025-09-02 18:15:33 +00:00
Potherca-Bot
29136c269a Changes content in separated file 'SimpleCryptOpenSSLEngine.php'. 2025-09-02 18:15:32 +00:00
Potherca-Bot
1db8aa811e Changes content in separated file 'SimpleCryptMcryptEngine.php'. 2025-09-02 18:15:32 +00:00
Potherca-Bot
d44d538faf Changes content in separated file 'CryptEngine.php'. 2025-09-02 18:15:32 +00:00
Potherca-Bot
8f5cb404c4 Adds separate file for 'SimpleCryptOpenSSLMcryptCompatibilityEngine.php'. 2025-09-02 18:15:32 +00:00
Potherca-Bot
caef7a244e Adds separate file for 'SimpleCryptOpenSSLEngine.php'. 2025-09-02 18:15:32 +00:00
Potherca-Bot
bd514f7a9a Adds separate file for 'SimpleCryptMcryptEngine.php'. 2025-09-02 18:15:32 +00:00
Potherca-Bot
212b98724b Adds separate file for 'CryptEngine.php'. 2025-09-02 18:15:31 +00:00
anne-catherine
afcbc8ce40 Merge branch 'split-file_trigger.class.inc.php' into feature/2535-split_file 2025-09-02 18:02:45 +00:00
Potherca-Bot
df68d2a8da Merge branch 'split-file_trigger.class.inc.php_Trigger.php' into split-file_trigger.class.inc.php 2025-09-02 18:00:48 +00:00
Potherca-Bot
6870d4f4db Merge branch 'split-file_trigger.class.inc.php_TriggerOnThresholdReached.php' into split-file_trigger.class.inc.php 2025-09-02 18:00:48 +00:00
Potherca-Bot
7553ee2510 Merge branch 'split-file_trigger.class.inc.php_TriggerOnStateLeave.php' into split-file_trigger.class.inc.php 2025-09-02 18:00:48 +00:00
Potherca-Bot
1a999b1a1c Merge branch 'split-file_trigger.class.inc.php_TriggerOnStateEnter.php' into split-file_trigger.class.inc.php 2025-09-02 18:00:48 +00:00
Potherca-Bot
182ae5d3aa Merge branch 'split-file_trigger.class.inc.php_TriggerOnStateChange.php' into split-file_trigger.class.inc.php 2025-09-02 18:00:48 +00:00
Potherca-Bot
4edaacc2f5 Merge branch 'split-file_trigger.class.inc.php_TriggerOnPortalUpdate.php' into split-file_trigger.class.inc.php 2025-09-02 18:00:48 +00:00
Potherca-Bot
c98a11623e Merge branch 'split-file_trigger.class.inc.php_TriggerOnObjectUpdate.php' into split-file_trigger.class.inc.php 2025-09-02 18:00:47 +00:00
Potherca-Bot
9feb15f5f6 Merge branch 'split-file_trigger.class.inc.php_TriggerOnObject.php' into split-file_trigger.class.inc.php 2025-09-02 18:00:47 +00:00
Potherca-Bot
a7941c932b Merge branch 'split-file_trigger.class.inc.php_TriggerOnObjectMention.php' into split-file_trigger.class.inc.php 2025-09-02 18:00:47 +00:00
Potherca-Bot
4cfc2a1b26 Merge branch 'split-file_trigger.class.inc.php_TriggerOnObjectDelete.php' into split-file_trigger.class.inc.php 2025-09-02 18:00:47 +00:00
Potherca-Bot
7db88f256f Merge branch 'split-file_trigger.class.inc.php_TriggerOnObjectCreate.php' into split-file_trigger.class.inc.php 2025-09-02 18:00:47 +00:00
Potherca-Bot
0066b24e8f Merge branch 'split-file_trigger.class.inc.php_TriggerOnAttributeBlobDownload.php' into split-file_trigger.class.inc.php 2025-09-02 18:00:47 +00:00
Potherca-Bot
1b96f5b071 Changes content in separated file 'Trigger.php'. 2025-09-02 18:00:46 +00:00
Potherca-Bot
4b2ae38173 Changes content in separated file 'TriggerOnThresholdReached.php'. 2025-09-02 18:00:46 +00:00
Potherca-Bot
a13e2b0b00 Changes content in separated file 'TriggerOnStateLeave.php'. 2025-09-02 18:00:46 +00:00
Potherca-Bot
8f09b8149b Merge branch 'split-file_trigger.class.inc.php_lnkTriggerAction.php' into split-file_trigger.class.inc.php 2025-09-02 18:00:46 +00:00
Potherca-Bot
ff69719b8e Adds separate file for 'Trigger.php'. 2025-09-02 18:00:46 +00:00
Potherca-Bot
9c35d9edc2 Adds separate file for 'TriggerOnThresholdReached.php'. 2025-09-02 18:00:46 +00:00
Potherca-Bot
f9f75f9117 Changes content in separated file 'TriggerOnStateEnter.php'. 2025-09-02 18:00:45 +00:00
Potherca-Bot
48e3d7f67f Changes content in separated file 'TriggerOnStateChange.php'. 2025-09-02 18:00:45 +00:00
Potherca-Bot
0246951cf8 Changes content in separated file 'TriggerOnPortalUpdate.php'. 2025-09-02 18:00:45 +00:00
Potherca-Bot
fbd76e6969 Adds separate file for 'TriggerOnStateLeave.php'. 2025-09-02 18:00:45 +00:00
Potherca-Bot
559fe0be01 Adds separate file for 'TriggerOnStateEnter.php'. 2025-09-02 18:00:45 +00:00
Potherca-Bot
e6a8238924 Adds separate file for 'TriggerOnStateChange.php'. 2025-09-02 18:00:45 +00:00
Potherca-Bot
496f548aea Adds separate file for 'TriggerOnPortalUpdate.php'. 2025-09-02 18:00:45 +00:00
Potherca-Bot
4a2e37fa84 Changes content in separated file 'TriggerOnObjectUpdate.php'. 2025-09-02 18:00:44 +00:00
Potherca-Bot
315f275a6d Changes content in separated file 'TriggerOnObject.php'. 2025-09-02 18:00:44 +00:00
Potherca-Bot
d6118d5a96 Changes content in separated file 'TriggerOnObjectMention.php'. 2025-09-02 18:00:44 +00:00
Potherca-Bot
572793b7b8 Adds separate file for 'TriggerOnObjectUpdate.php'. 2025-09-02 18:00:44 +00:00
Potherca-Bot
391ec877c8 Adds separate file for 'TriggerOnObject.php'. 2025-09-02 18:00:44 +00:00
Potherca-Bot
4151aa5ac1 Adds separate file for 'TriggerOnObjectMention.php'. 2025-09-02 18:00:44 +00:00
Potherca-Bot
c36939cf85 Changes content in separated file 'TriggerOnObjectDelete.php'. 2025-09-02 18:00:43 +00:00
Potherca-Bot
3b0897fd9c Changes content in separated file 'TriggerOnObjectCreate.php'. 2025-09-02 18:00:43 +00:00
Potherca-Bot
a11c87c6c2 Changes content in separated file 'TriggerOnAttributeBlobDownload.php'. 2025-09-02 18:00:43 +00:00
Potherca-Bot
0a2492228b Adds separate file for 'TriggerOnObjectDelete.php'. 2025-09-02 18:00:43 +00:00
Potherca-Bot
36a1dbd34d Adds separate file for 'TriggerOnObjectCreate.php'. 2025-09-02 18:00:43 +00:00
Potherca-Bot
fc988d1eb0 Adds separate file for 'TriggerOnAttributeBlobDownload.php'. 2025-09-02 18:00:43 +00:00
Potherca-Bot
8d0f685fad Changes content in separated file 'lnkTriggerAction.php'. 2025-09-02 18:00:42 +00:00
Potherca-Bot
9974d59f15 Adds separate file for 'lnkTriggerAction.php'. 2025-09-02 18:00:42 +00:00
Anne-Cath
47aee14362 Move to sources Core BulkChange 2025-09-02 18:49:32 +02:00
anne-catherine
a31e3450bf Merge branch 'split-file_bulkchange.class.inc.php' into feature/2535-split_file 2025-09-02 16:46:15 +00:00
Potherca-Bot
c931e3f18a Merge branch 'split-file_bulkchange.class.inc.php_RowStatus.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:23 +00:00
Potherca-Bot
185e03d8e8 Merge branch 'split-file_bulkchange.class.inc.php_RowStatus_NoChange.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:23 +00:00
Potherca-Bot
e8f0542f11 Merge branch 'split-file_bulkchange.class.inc.php_RowStatus_NewObj.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:22 +00:00
Potherca-Bot
69fa0f71d0 Merge branch 'split-file_bulkchange.class.inc.php_RowStatus_Modify.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:22 +00:00
Potherca-Bot
03e8e77596 Merge branch 'split-file_bulkchange.class.inc.php_RowStatus_Issue.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:22 +00:00
Potherca-Bot
c67bf66bd9 Merge branch 'split-file_bulkchange.class.inc.php_RowStatus_Error.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:21 +00:00
Potherca-Bot
3c23b04608 Merge branch 'split-file_bulkchange.class.inc.php_RowStatus_Disappeared.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:21 +00:00
Potherca-Bot
0891272aa3 Merge branch 'split-file_bulkchange.class.inc.php_ReportValue.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:21 +00:00
Potherca-Bot
c6c3f7b421 Merge branch 'split-file_bulkchange.class.inc.php_CellStatus_Void.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:21 +00:00
Potherca-Bot
5f3aa0d083 Merge branch 'split-file_bulkchange.class.inc.php_CellStatus_SearchIssue.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:20 +00:00
Potherca-Bot
046ce13073 Merge branch 'split-file_bulkchange.class.inc.php_CellStatus_NullIssue.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:20 +00:00
Potherca-Bot
a64ab695ce Merge branch 'split-file_bulkchange.class.inc.php_CellStatus_Modify.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:20 +00:00
Potherca-Bot
020e58b112 Merge branch 'split-file_bulkchange.class.inc.php_CellStatus_Issue.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:20 +00:00
Potherca-Bot
c372707124 Merge branch 'split-file_bulkchange.class.inc.php_CellStatus_Ambiguous.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:20 +00:00
Potherca-Bot
6a462d25e0 Changes content in separated file 'RowStatus.php'. 2025-09-02 16:42:19 +00:00
Potherca-Bot
288ffd1830 Merge branch 'split-file_bulkchange.class.inc.php_CellChangeSpec.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:19 +00:00
Potherca-Bot
02c3ca4724 Merge branch 'split-file_bulkchange.class.inc.php_BulkChange.php' into split-file_bulkchange.class.inc.php 2025-09-02 16:42:19 +00:00
Potherca-Bot
5b5ab86862 Changes content in separated file 'RowStatus_NoChange.php'. 2025-09-02 16:42:18 +00:00
Potherca-Bot
62122b0ee6 Changes content in separated file 'RowStatus_NewObj.php'. 2025-09-02 16:42:18 +00:00
Potherca-Bot
7aaa4a2b0d Adds separate file for 'RowStatus.php'. 2025-09-02 16:42:18 +00:00
Potherca-Bot
786651b35c Adds separate file for 'RowStatus_NoChange.php'. 2025-09-02 16:42:18 +00:00
Potherca-Bot
30b793eed8 Changes content in separated file 'RowStatus_Modify.php'. 2025-09-02 16:42:17 +00:00
Potherca-Bot
3f3d9b3d2e Changes content in separated file 'RowStatus_Issue.php'. 2025-09-02 16:42:17 +00:00
Potherca-Bot
dc3c6ea6ce Adds separate file for 'RowStatus_NewObj.php'. 2025-09-02 16:42:17 +00:00
Potherca-Bot
a73321dd02 Adds separate file for 'RowStatus_Modify.php'. 2025-09-02 16:42:17 +00:00
Potherca-Bot
3edcdc452e Changes content in separated file 'RowStatus_Error.php'. 2025-09-02 16:42:16 +00:00
Potherca-Bot
0c93971e13 Changes content in separated file 'RowStatus_Disappeared.php'. 2025-09-02 16:42:16 +00:00
Potherca-Bot
5a59b1870d Adds separate file for 'RowStatus_Issue.php'. 2025-09-02 16:42:16 +00:00
Potherca-Bot
19bb7e5043 Adds separate file for 'RowStatus_Error.php'. 2025-09-02 16:42:16 +00:00
Potherca-Bot
8e08be2258 Adds separate file for 'RowStatus_Disappeared.php'. 2025-09-02 16:42:16 +00:00
Potherca-Bot
22834d32a3 Changes content in separated file 'ReportValue.php'. 2025-09-02 16:42:15 +00:00
Potherca-Bot
23407cd095 Changes content in separated file 'CellStatus_Void.php'. 2025-09-02 16:42:15 +00:00
Potherca-Bot
8403273c0b Adds separate file for 'ReportValue.php'. 2025-09-02 16:42:15 +00:00
Potherca-Bot
d5dc3c0d74 Adds separate file for 'CellStatus_Void.php'. 2025-09-02 16:42:15 +00:00
Potherca-Bot
fbbdcfa7b9 Changes content in separated file 'CellStatus_SearchIssue.php'. 2025-09-02 16:42:14 +00:00
Potherca-Bot
7a528e276f Changes content in separated file 'CellStatus_NullIssue.php'. 2025-09-02 16:42:14 +00:00
Potherca-Bot
ed55bfa6aa Adds separate file for 'CellStatus_SearchIssue.php'. 2025-09-02 16:42:14 +00:00
Potherca-Bot
7f62a78529 Adds separate file for 'CellStatus_NullIssue.php'. 2025-09-02 16:42:14 +00:00
Potherca-Bot
cf51bddbc9 Changes content in separated file 'CellStatus_Modify.php'. 2025-09-02 16:42:13 +00:00
Potherca-Bot
adf3902c37 Changes content in separated file 'CellStatus_Issue.php'. 2025-09-02 16:42:13 +00:00
Potherca-Bot
6f5bed6b98 Changes content in separated file 'CellStatus_Ambiguous.php'. 2025-09-02 16:42:13 +00:00
Potherca-Bot
7079cab1de Adds separate file for 'CellStatus_Modify.php'. 2025-09-02 16:42:13 +00:00
Potherca-Bot
19188ac2fc Adds separate file for 'CellStatus_Issue.php'. 2025-09-02 16:42:13 +00:00
Potherca-Bot
d543e311dd Changes content in separated file 'CellChangeSpec.php'. 2025-09-02 16:42:12 +00:00
Potherca-Bot
19ea531961 Changes content in separated file 'BulkChange.php'. 2025-09-02 16:42:12 +00:00
Potherca-Bot
9bee4bdb5c Adds separate file for 'CellStatus_Ambiguous.php'. 2025-09-02 16:42:12 +00:00
Potherca-Bot
2b4e2939fe Adds separate file for 'CellChangeSpec.php'. 2025-09-02 16:42:12 +00:00
Potherca-Bot
2c8eac0e3b Adds separate file for 'BulkChange.php'. 2025-09-02 16:42:12 +00:00
Anne-Cath
2dd38e81c5 rollback file autoload.php 2025-08-28 14:49:54 +02:00
Anne-Cath
c052c40f9d update autoloader 2025-08-28 11:17:43 +02:00
anne-catherine
89fff9075d end merge 2025-08-28 09:08:22 +00:00
anne-catherine
97b4c80e0b update attributedef.class.inc.php 2025-08-28 09:07:44 +00:00
Potherca-Bot
905388825c Merge branch 'split-file_attributedef.class.inc.php_MissingColumnException.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:42 +00:00
Potherca-Bot
d02dec7a09 Merge branch 'split-file_attributedef.class.inc.php_iAttributeNoGroupBy.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:41 +00:00
Potherca-Bot
438ecfebdf Merge branch 'split-file_attributedef.class.inc.php_AttributeURL.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:41 +00:00
Potherca-Bot
8aa7e52fa2 Merge branch 'split-file_attributedef.class.inc.php_AttributeText.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:41 +00:00
Potherca-Bot
27642d5872 Merge branch 'split-file_attributedef.class.inc.php_AttributeTemplateText.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:41 +00:00
Potherca-Bot
4754009d4f Merge branch 'split-file_attributedef.class.inc.php_AttributeTemplateString.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:40 +00:00
Potherca-Bot
967911c538 Merge branch 'split-file_attributedef.class.inc.php_AttributeTemplateHTML.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:40 +00:00
Potherca-Bot
b3f069fec0 Merge branch 'split-file_attributedef.class.inc.php_AttributeTagSet.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:40 +00:00
Potherca-Bot
59db7535c3 Merge branch 'split-file_attributedef.class.inc.php_AttributeTable.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:39 +00:00
Potherca-Bot
f1657ac695 Merge branch 'split-file_attributedef.class.inc.php_AttributeSubItem.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:39 +00:00
Potherca-Bot
d307f4861f Merge branch 'split-file_attributedef.class.inc.php_AttributeString.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:39 +00:00
Potherca-Bot
6341e7e263 Merge branch 'split-file_attributedef.class.inc.php_AttributeStopWatch.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:38 +00:00
Potherca-Bot
0efec39071 Merge branch 'split-file_attributedef.class.inc.php_AttributeSet.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:38 +00:00
Potherca-Bot
e1ebeac986 Merge branch 'split-file_attributedef.class.inc.php_AttributeRedundancySettings.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:38 +00:00
Potherca-Bot
68c5d7d33b Merge branch 'split-file_attributedef.class.inc.php_AttributeQueryAttCodeSet.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:37 +00:00
Potherca-Bot
2cefaba0d8 Merge branch 'split-file_attributedef.class.inc.php_AttributePropertySet.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:37 +00:00
Potherca-Bot
a57de6f090 Merge branch 'split-file_attributedef.class.inc.php_AttributePhoneNumber.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:37 +00:00
Potherca-Bot
6e63254a6b Merge branch 'split-file_attributedef.class.inc.php_AttributePercentage.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:36 +00:00
Potherca-Bot
2ae2407c24 Merge branch 'split-file_attributedef.class.inc.php_AttributePassword.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:36 +00:00
Potherca-Bot
d970f0e1e4 Merge branch 'split-file_attributedef.class.inc.php_AttributeOQL.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:36 +00:00
Potherca-Bot
b3ce941e5a Merge branch 'split-file_attributedef.class.inc.php_AttributeOneWayPassword.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:36 +00:00
Potherca-Bot
8976ad3635 Merge branch 'split-file_attributedef.class.inc.php_AttributeObsolescenceFlag.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:35 +00:00
Potherca-Bot
03d12d2485 Merge branch 'split-file_attributedef.class.inc.php_AttributeObsolescenceDate.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:35 +00:00
Potherca-Bot
582da19aaa Merge branch 'split-file_attributedef.class.inc.php_AttributeObjectKey.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:35 +00:00
Potherca-Bot
62aa33f0f3 Merge branch 'split-file_attributedef.class.inc.php_AttributeMetaEnum.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:34 +00:00
Potherca-Bot
8bb75006ba Merge branch 'split-file_attributedef.class.inc.php_AttributeLongText.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:34 +00:00
Potherca-Bot
b1881f1847 Merge branch 'split-file_attributedef.class.inc.php_AttributeLinkedSet.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:34 +00:00
Potherca-Bot
03759dffbb Merge branch 'split-file_attributedef.class.inc.php_AttributeLinkedSetIndirect.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:33 +00:00
Potherca-Bot
ada0126f9e Merge branch 'split-file_attributedef.class.inc.php_AttributeIPAddress.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:33 +00:00
Potherca-Bot
d897ddeed3 Merge branch 'split-file_attributedef.class.inc.php_AttributeInteger.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:33 +00:00
Potherca-Bot
eb14ef3c30 Merge branch 'split-file_attributedef.class.inc.php_AttributeImage.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:32 +00:00
Potherca-Bot
e12f6c92b2 Merge branch 'split-file_attributedef.class.inc.php_AttributeHTML.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:32 +00:00
Potherca-Bot
1dbc62ddf1 Merge branch 'split-file_attributedef.class.inc.php_AttributeHierarchicalKey.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:32 +00:00
Potherca-Bot
e6ac5f48b7 Merge branch 'split-file_attributedef.class.inc.php_AttributeFriendlyName.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:32 +00:00
Potherca-Bot
d32b6cc0b9 Merge branch 'split-file_attributedef.class.inc.php_AttributeFinalClass.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:31 +00:00
Potherca-Bot
63b70aa99b Merge branch 'split-file_attributedef.class.inc.php_AttributeExternalKey.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:31 +00:00
Potherca-Bot
71e006054d Merge branch 'split-file_attributedef.class.inc.php_AttributeExternalField.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:31 +00:00
Potherca-Bot
65c23c58c3 Merge branch 'split-file_attributedef.class.inc.php_AttributeEnumSet.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:30 +00:00
Potherca-Bot
8605d8667d Merge branch 'split-file_attributedef.class.inc.php_AttributeEnum.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:30 +00:00
Potherca-Bot
084b10a9a6 Merge branch 'split-file_attributedef.class.inc.php_AttributeEncryptedString.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:30 +00:00
Potherca-Bot
5ee18637bb Merge branch 'split-file_attributedef.class.inc.php_AttributeEmailAddress.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:30 +00:00
Potherca-Bot
bdbdd812e9 Merge branch 'split-file_attributedef.class.inc.php_AttributeDuration.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:29 +00:00
Potherca-Bot
2d43b8946d Merge branch 'split-file_attributedef.class.inc.php_AttributeDefinition.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:29 +00:00
Potherca-Bot
e5100ddeed Merge branch 'split-file_attributedef.class.inc.php_AttributeDecimal.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:29 +00:00
Potherca-Bot
ea9588761d Merge branch 'split-file_attributedef.class.inc.php_AttributeDeadline.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:28 +00:00
Potherca-Bot
16b34ab4e7 Merge branch 'split-file_attributedef.class.inc.php_AttributeDBFieldVoid.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:28 +00:00
Potherca-Bot
7bb8fbc793 Merge branch 'split-file_attributedef.class.inc.php_AttributeDBField.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:28 +00:00
Potherca-Bot
0816d3c84a Merge branch 'split-file_attributedef.class.inc.php_AttributeDateTime.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:28 +00:00
Potherca-Bot
66c5034ff2 Merge branch 'split-file_attributedef.class.inc.php_AttributeDate.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:27 +00:00
Potherca-Bot
03c097f5a1 Merge branch 'split-file_attributedef.class.inc.php_AttributeDashboard.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:27 +00:00
Potherca-Bot
87ca02dba3 Merge branch 'split-file_attributedef.class.inc.php_AttributeCustomFields.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:27 +00:00
Potherca-Bot
f4ba1a15fe Merge branch 'split-file_attributedef.class.inc.php_AttributeClassState.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:26 +00:00
Potherca-Bot
1977fe8cb0 Merge branch 'split-file_attributedef.class.inc.php_AttributeClass.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:26 +00:00
Potherca-Bot
91d0209253 Merge branch 'split-file_attributedef.class.inc.php_AttributeClassAttCodeSet.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:26 +00:00
Potherca-Bot
3bee8de25e Merge branch 'split-file_attributedef.class.inc.php_AttributeCaseLog.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:25 +00:00
Potherca-Bot
52db2a58ea Merge branch 'split-file_attributedef.class.inc.php_AttributeBoolean.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:25 +00:00
Potherca-Bot
16f0927d92 Merge branch 'split-file_attributedef.class.inc.php_AttributeBlob.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:25 +00:00
Potherca-Bot
bd64479687 Merge branch 'split-file_attributedef.class.inc.php_AttributeArchiveFlag.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:24 +00:00
Potherca-Bot
8436970132 Merge branch 'split-file_attributedef.class.inc.php_AttributeArchiveDate.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:24 +00:00
Potherca-Bot
08f35f451f Merge branch 'split-file_attributedef.class.inc.php_AttributeApplicationLanguage.php' into split-file_attributedef.class.inc.php 2025-08-28 09:06:24 +00:00
Potherca-Bot
dd441afa22 Changes content in separated file 'MissingColumnException.php'. 2025-08-28 09:06:23 +00:00
Potherca-Bot
5d8184a673 Changes content in separated file 'iAttributeNoGroupBy.php'. 2025-08-28 09:06:23 +00:00
Potherca-Bot
db91ed0c1d Adds separate file for 'MissingColumnException.php'. 2025-08-28 09:06:23 +00:00
Potherca-Bot
e890d62ab1 Changes content in separated file 'AttributeURL.php'. 2025-08-28 09:06:22 +00:00
Potherca-Bot
6d2b457185 Adds separate file for 'iAttributeNoGroupBy.php'. 2025-08-28 09:06:22 +00:00
Potherca-Bot
4328fc78dc Adds separate file for 'AttributeURL.php'. 2025-08-28 09:06:22 +00:00
Potherca-Bot
37914e2b7f Changes content in separated file 'AttributeText.php'. 2025-08-28 09:06:21 +00:00
Potherca-Bot
0551102364 Changes content in separated file 'AttributeTemplateText.php'. 2025-08-28 09:06:21 +00:00
Potherca-Bot
1ca369041d Adds separate file for 'AttributeText.php'. 2025-08-28 09:06:21 +00:00
Potherca-Bot
2a6a4d3460 Adds separate file for 'AttributeTemplateText.php'. 2025-08-28 09:06:21 +00:00
Potherca-Bot
9edc15f50d Changes content in separated file 'AttributeTemplateString.php'. 2025-08-28 09:06:20 +00:00
Potherca-Bot
6acb89a2fc Changes content in separated file 'AttributeTemplateHTML.php'. 2025-08-28 09:06:20 +00:00
Potherca-Bot
19803c004d Adds separate file for 'AttributeTemplateString.php'. 2025-08-28 09:06:20 +00:00
Potherca-Bot
20de262e25 Changes content in separated file 'AttributeTagSet.php'. 2025-08-28 09:06:19 +00:00
Potherca-Bot
c870fc5922 Adds separate file for 'AttributeTemplateHTML.php'. 2025-08-28 09:06:19 +00:00
Potherca-Bot
09c4a4361a Adds separate file for 'AttributeTagSet.php'. 2025-08-28 09:06:19 +00:00
Potherca-Bot
3544aa4d50 Changes content in separated file 'AttributeTable.php'. 2025-08-28 09:06:18 +00:00
Potherca-Bot
d080898b1d Changes content in separated file 'AttributeSubItem.php'. 2025-08-28 09:06:18 +00:00
Potherca-Bot
78282a80ef Adds separate file for 'AttributeTable.php'. 2025-08-28 09:06:18 +00:00
Potherca-Bot
3c8a198738 Adds separate file for 'AttributeSubItem.php'. 2025-08-28 09:06:18 +00:00
Potherca-Bot
4205988f79 Changes content in separated file 'AttributeString.php'. 2025-08-28 09:06:17 +00:00
Potherca-Bot
517ef9f3cf Changes content in separated file 'AttributeStopWatch.php'. 2025-08-28 09:06:17 +00:00
Potherca-Bot
f6d94ec2b5 Adds separate file for 'AttributeString.php'. 2025-08-28 09:06:17 +00:00
Potherca-Bot
0d9210777c Adds separate file for 'AttributeStopWatch.php'. 2025-08-28 09:06:17 +00:00
Potherca-Bot
bd86e02872 Changes content in separated file 'AttributeSet.php'. 2025-08-28 09:06:16 +00:00
Potherca-Bot
095fed9d4c Changes content in separated file 'AttributeRedundancySettings.php'. 2025-08-28 09:06:16 +00:00
Potherca-Bot
734424aa8a Adds separate file for 'AttributeSet.php'. 2025-08-28 09:06:16 +00:00
Potherca-Bot
bf467f664c Changes content in separated file 'AttributeQueryAttCodeSet.php'. 2025-08-28 09:06:15 +00:00
Potherca-Bot
51d960a229 Adds separate file for 'AttributeRedundancySettings.php'. 2025-08-28 09:06:15 +00:00
Potherca-Bot
63d7a25982 Adds separate file for 'AttributeQueryAttCodeSet.php'. 2025-08-28 09:06:15 +00:00
Potherca-Bot
614cf7b3c8 Changes content in separated file 'AttributePropertySet.php'. 2025-08-28 09:06:14 +00:00
Potherca-Bot
ea20d30ad1 Changes content in separated file 'AttributePhoneNumber.php'. 2025-08-28 09:06:14 +00:00
Potherca-Bot
3fc16deed2 Adds separate file for 'AttributePropertySet.php'. 2025-08-28 09:06:14 +00:00
Potherca-Bot
78e51c1209 Adds separate file for 'AttributePhoneNumber.php'. 2025-08-28 09:06:14 +00:00
Potherca-Bot
b96ff56dfd Changes content in separated file 'AttributePercentage.php'. 2025-08-28 09:06:13 +00:00
Potherca-Bot
bdeed2a14c Changes content in separated file 'AttributePassword.php'. 2025-08-28 09:06:13 +00:00
Potherca-Bot
a63813f284 Adds separate file for 'AttributePercentage.php'. 2025-08-28 09:06:13 +00:00
Potherca-Bot
0ec54bccd2 Adds separate file for 'AttributePassword.php'. 2025-08-28 09:06:13 +00:00
Potherca-Bot
aa83d1160c Changes content in separated file 'AttributeOQL.php'. 2025-08-28 09:06:12 +00:00
Potherca-Bot
dab7fb6c53 Changes content in separated file 'AttributeOneWayPassword.php'. 2025-08-28 09:06:12 +00:00
Potherca-Bot
bdd188f9e8 Adds separate file for 'AttributeOQL.php'. 2025-08-28 09:06:12 +00:00
Potherca-Bot
6d1abd17a2 Adds separate file for 'AttributeOneWayPassword.php'. 2025-08-28 09:06:12 +00:00
Potherca-Bot
149fdbe9e4 Changes content in separated file 'AttributeObsolescenceFlag.php'. 2025-08-28 09:06:11 +00:00
Potherca-Bot
23ba1b41e3 Changes content in separated file 'AttributeObsolescenceDate.php'. 2025-08-28 09:06:11 +00:00
Potherca-Bot
c958f45433 Adds separate file for 'AttributeObsolescenceFlag.php'. 2025-08-28 09:06:11 +00:00
Potherca-Bot
506780ceed Changes content in separated file 'AttributeObjectKey.php'. 2025-08-28 09:06:10 +00:00
Potherca-Bot
bf2d70f952 Changes content in separated file 'AttributeMetaEnum.php'. 2025-08-28 09:06:10 +00:00
Potherca-Bot
73f2311ca4 Adds separate file for 'AttributeObsolescenceDate.php'. 2025-08-28 09:06:10 +00:00
Potherca-Bot
6c4e0c3718 Adds separate file for 'AttributeObjectKey.php'. 2025-08-28 09:06:10 +00:00
Potherca-Bot
28c4ebc7ea Changes content in separated file 'AttributeLongText.php'. 2025-08-28 09:06:09 +00:00
Potherca-Bot
d692e1562d Adds separate file for 'AttributeMetaEnum.php'. 2025-08-28 09:06:09 +00:00
Potherca-Bot
4284ed955b Adds separate file for 'AttributeLongText.php'. 2025-08-28 09:06:09 +00:00
Potherca-Bot
854290dd2d Changes content in separated file 'AttributeLinkedSet.php'. 2025-08-28 09:06:08 +00:00
Potherca-Bot
1cf995b0c1 Changes content in separated file 'AttributeLinkedSetIndirect.php'. 2025-08-28 09:06:08 +00:00
Potherca-Bot
284aa0a20f Adds separate file for 'AttributeLinkedSet.php'. 2025-08-28 09:06:08 +00:00
Potherca-Bot
a5887e20be Changes content in separated file 'AttributeIPAddress.php'. 2025-08-28 09:06:07 +00:00
Potherca-Bot
f0acbdf478 Changes content in separated file 'AttributeInteger.php'. 2025-08-28 09:06:07 +00:00
Potherca-Bot
8a2770cf13 Adds separate file for 'AttributeLinkedSetIndirect.php'. 2025-08-28 09:06:07 +00:00
Potherca-Bot
27c1a23ecf Adds separate file for 'AttributeIPAddress.php'. 2025-08-28 09:06:07 +00:00
Potherca-Bot
67089a6ad2 Changes content in separated file 'AttributeImage.php'. 2025-08-28 09:06:06 +00:00
Potherca-Bot
a711e39976 Adds separate file for 'AttributeInteger.php'. 2025-08-28 09:06:06 +00:00
Potherca-Bot
c1535f07fb Adds separate file for 'AttributeImage.php'. 2025-08-28 09:06:06 +00:00
Potherca-Bot
97ddb2e214 Changes content in separated file 'AttributeHTML.php'. 2025-08-28 09:06:05 +00:00
Potherca-Bot
a2adfe31d8 Changes content in separated file 'AttributeHierarchicalKey.php'. 2025-08-28 09:06:05 +00:00
Potherca-Bot
b3c4edc332 Changes content in separated file 'AttributeFriendlyName.php'. 2025-08-28 09:06:05 +00:00
Potherca-Bot
3b680a6f94 Adds separate file for 'AttributeHTML.php'. 2025-08-28 09:06:05 +00:00
Potherca-Bot
9e92a88cba Adds separate file for 'AttributeHierarchicalKey.php'. 2025-08-28 09:06:05 +00:00
Potherca-Bot
38dbae2bb6 Changes content in separated file 'AttributeFinalClass.php'. 2025-08-28 09:06:04 +00:00
Potherca-Bot
b830398ca3 Adds separate file for 'AttributeFriendlyName.php'. 2025-08-28 09:06:04 +00:00
Potherca-Bot
1e7d1c48c3 Adds separate file for 'AttributeFinalClass.php'. 2025-08-28 09:06:04 +00:00
Potherca-Bot
5bfe501bf4 Changes content in separated file 'AttributeExternalKey.php'. 2025-08-28 09:06:03 +00:00
Potherca-Bot
e55d12fae6 Changes content in separated file 'AttributeExternalField.php'. 2025-08-28 09:06:03 +00:00
Potherca-Bot
0d8857f989 Adds separate file for 'AttributeExternalKey.php'. 2025-08-28 09:06:03 +00:00
Potherca-Bot
81688e9c86 Adds separate file for 'AttributeExternalField.php'. 2025-08-28 09:06:03 +00:00
Potherca-Bot
51ceadd882 Changes content in separated file 'AttributeEnumSet.php'. 2025-08-28 09:06:02 +00:00
Potherca-Bot
71d2ed0402 Changes content in separated file 'AttributeEnum.php'. 2025-08-28 09:06:02 +00:00
Potherca-Bot
a09521ca6b Adds separate file for 'AttributeEnumSet.php'. 2025-08-28 09:06:02 +00:00
Potherca-Bot
17b2bb058d Changes content in separated file 'AttributeEncryptedString.php'. 2025-08-28 09:06:01 +00:00
Potherca-Bot
9433fbbfd1 Changes content in separated file 'AttributeEmailAddress.php'. 2025-08-28 09:06:01 +00:00
Potherca-Bot
ae5b9aac8a Adds separate file for 'AttributeEnum.php'. 2025-08-28 09:06:01 +00:00
Potherca-Bot
7088047a5e Adds separate file for 'AttributeEncryptedString.php'. 2025-08-28 09:06:01 +00:00
Potherca-Bot
b5d8740ab0 Changes content in separated file 'AttributeDuration.php'. 2025-08-28 09:06:00 +00:00
Potherca-Bot
e0e5b094a4 Adds separate file for 'AttributeEmailAddress.php'. 2025-08-28 09:06:00 +00:00
Potherca-Bot
9dc21cc91f Adds separate file for 'AttributeDuration.php'. 2025-08-28 09:06:00 +00:00
Potherca-Bot
99c16135c3 Changes content in separated file 'AttributeDefinition.php'. 2025-08-28 09:05:59 +00:00
Potherca-Bot
849f74147c Changes content in separated file 'AttributeDecimal.php'. 2025-08-28 09:05:59 +00:00
Potherca-Bot
3543115730 Changes content in separated file 'AttributeDeadline.php'. 2025-08-28 09:05:59 +00:00
Potherca-Bot
1f5c1df50c Adds separate file for 'AttributeDefinition.php'. 2025-08-28 09:05:59 +00:00
Potherca-Bot
d917a4cad3 Adds separate file for 'AttributeDecimal.php'. 2025-08-28 09:05:59 +00:00
Potherca-Bot
6bc96fb6eb Changes content in separated file 'AttributeDBFieldVoid.php'. 2025-08-28 09:05:58 +00:00
Potherca-Bot
22dbb0984d Adds separate file for 'AttributeDeadline.php'. 2025-08-28 09:05:58 +00:00
Potherca-Bot
5c5a184388 Adds separate file for 'AttributeDBFieldVoid.php'. 2025-08-28 09:05:58 +00:00
Potherca-Bot
3ee0b3a5e1 Changes content in separated file 'AttributeDBField.php'. 2025-08-28 09:05:57 +00:00
Potherca-Bot
16907d414e Changes content in separated file 'AttributeDateTime.php'. 2025-08-28 09:05:57 +00:00
Potherca-Bot
e928054e9c Adds separate file for 'AttributeDBField.php'. 2025-08-28 09:05:57 +00:00
Potherca-Bot
b5eb93dbd1 Adds separate file for 'AttributeDateTime.php'. 2025-08-28 09:05:57 +00:00
Potherca-Bot
31a4c38638 Changes content in separated file 'AttributeDate.php'. 2025-08-28 09:05:56 +00:00
Potherca-Bot
d6c73c8142 Changes content in separated file 'AttributeDashboard.php'. 2025-08-28 09:05:56 +00:00
Potherca-Bot
2ed9dc6f31 Adds separate file for 'AttributeDate.php'. 2025-08-28 09:05:56 +00:00
Potherca-Bot
fa51ef1adb Changes content in separated file 'AttributeCustomFields.php'. 2025-08-28 09:05:55 +00:00
Potherca-Bot
f2b2d422a6 Changes content in separated file 'AttributeClassState.php'. 2025-08-28 09:05:55 +00:00
Potherca-Bot
c58161b9b4 Adds separate file for 'AttributeDashboard.php'. 2025-08-28 09:05:55 +00:00
Potherca-Bot
0a32ddb8e2 Adds separate file for 'AttributeCustomFields.php'. 2025-08-28 09:05:55 +00:00
Potherca-Bot
161a05132b Changes content in separated file 'AttributeClass.php'. 2025-08-28 09:05:54 +00:00
Potherca-Bot
3967a82719 Changes content in separated file 'AttributeClassAttCodeSet.php'. 2025-08-28 09:05:54 +00:00
Potherca-Bot
ce9425f273 Adds separate file for 'AttributeClassState.php'. 2025-08-28 09:05:54 +00:00
Potherca-Bot
4af8f54f26 Adds separate file for 'AttributeClass.php'. 2025-08-28 09:05:54 +00:00
Potherca-Bot
bb904d7ec3 Changes content in separated file 'AttributeCaseLog.php'. 2025-08-28 09:05:53 +00:00
Potherca-Bot
426da64c46 Adds separate file for 'AttributeClassAttCodeSet.php'. 2025-08-28 09:05:53 +00:00
Potherca-Bot
6b4bfb0238 Adds separate file for 'AttributeCaseLog.php'. 2025-08-28 09:05:53 +00:00
Potherca-Bot
8420ef38ac Changes content in separated file 'AttributeBoolean.php'. 2025-08-28 09:05:52 +00:00
Potherca-Bot
db8f657c8e Changes content in separated file 'AttributeBlob.php'. 2025-08-28 09:05:52 +00:00
Potherca-Bot
a3d881715e Adds separate file for 'AttributeBoolean.php'. 2025-08-28 09:05:52 +00:00
Potherca-Bot
db9e932fc1 Adds separate file for 'AttributeBlob.php'. 2025-08-28 09:05:52 +00:00
Potherca-Bot
0cd9b08bcb Changes content in separated file 'AttributeArchiveFlag.php'. 2025-08-28 09:05:51 +00:00
Potherca-Bot
4b7d589c9f Changes content in separated file 'AttributeArchiveDate.php'. 2025-08-28 09:05:51 +00:00
Potherca-Bot
2554477987 Adds separate file for 'AttributeArchiveFlag.php'. 2025-08-28 09:05:51 +00:00
Potherca-Bot
3d0dc422dc Adds separate file for 'AttributeArchiveDate.php'. 2025-08-28 09:05:51 +00:00
Potherca-Bot
b469a16a05 Changes content in separated file 'AttributeApplicationLanguage.php'. 2025-08-28 09:05:50 +00:00
Potherca-Bot
995d6654fd Adds separate file for 'AttributeApplicationLanguage.php'. 2025-08-28 09:05:50 +00:00
anne-catherine
57892f58d8 end merge 2025-08-28 09:04:27 +00:00
anne-catherine
5c4def502c update asynctask.class.inc.php 2025-08-28 09:03:41 +00:00
Potherca-Bot
0d61f5ac6b Merge branch 'split-file_asynctask.class.inc.php_ExecAsyncTask.php' into split-file_asynctask.class.inc.php 2025-08-28 09:02:25 +00:00
Potherca-Bot
86cfe10e96 Merge branch 'split-file_asynctask.class.inc.php_AsyncTask.php' into split-file_asynctask.class.inc.php 2025-08-28 09:02:24 +00:00
Potherca-Bot
8042364a3b Merge branch 'split-file_asynctask.class.inc.php_AsyncSendNewsroom.php' into split-file_asynctask.class.inc.php 2025-08-28 09:02:24 +00:00
Potherca-Bot
9b29c45f2b Merge branch 'split-file_asynctask.class.inc.php_AsyncSendEmail.php' into split-file_asynctask.class.inc.php 2025-08-28 09:02:24 +00:00
Potherca-Bot
d10d5a6062 Changes content in separated file 'ExecAsyncTask.php'. 2025-08-28 09:02:23 +00:00
Potherca-Bot
cc38a03d17 Changes content in separated file 'AsyncTask.php'. 2025-08-28 09:02:23 +00:00
Potherca-Bot
e640443ea5 Adds separate file for 'ExecAsyncTask.php'. 2025-08-28 09:02:23 +00:00
Potherca-Bot
7f7cf50143 Adds separate file for 'AsyncTask.php'. 2025-08-28 09:02:23 +00:00
Potherca-Bot
3c639bdce3 Changes content in separated file 'AsyncSendNewsroom.php'. 2025-08-28 09:02:22 +00:00
Potherca-Bot
77448a7fc9 Changes content in separated file 'AsyncSendEmail.php'. 2025-08-28 09:02:22 +00:00
Potherca-Bot
41c2a9e97a Adds separate file for 'AsyncSendNewsroom.php'. 2025-08-28 09:02:22 +00:00
Potherca-Bot
080c72eec7 Adds separate file for 'AsyncSendEmail.php'. 2025-08-28 09:02:22 +00:00
anne-catherine
c8de4a4682 end merge 2025-08-28 08:59:06 +00:00
anne-catherine
c4d465574c update action.class.inc.php 2025-08-28 08:53:41 +00:00
Potherca-Bot
d776589fed Merge branch 'split-file_action.class.inc.php_Action.php' into split-file_action.class.inc.php 2025-08-28 08:48:41 +00:00
Potherca-Bot
22895462df Merge branch 'split-file_action.class.inc.php_ActionNotification.php' into split-file_action.class.inc.php 2025-08-28 08:48:40 +00:00
Potherca-Bot
34f4c4102d Changes content in separated file 'Action.php'. 2025-08-28 08:48:40 +00:00
Potherca-Bot
8c79499ace Merge branch 'split-file_action.class.inc.php_ActionEmail.php' into split-file_action.class.inc.php 2025-08-28 08:48:40 +00:00
Potherca-Bot
e7c62c9ef2 Changes content in separated file 'ActionNotification.php'. 2025-08-28 08:48:40 +00:00
Potherca-Bot
96154bd02d Adds separate file for 'Action.php'. 2025-08-28 08:48:40 +00:00
Potherca-Bot
6ecf784bc5 Changes content in separated file 'ActionEmail.php'. 2025-08-28 08:48:39 +00:00
Potherca-Bot
8748a1e51d Adds separate file for 'ActionNotification.php'. 2025-08-28 08:48:39 +00:00
Potherca-Bot
ffad7784aa Adds separate file for 'ActionEmail.php'. 2025-08-28 08:48:39 +00:00
anne-catherine
d4dc6c1860 Merge branch 'split-file_valuesetdef.class.inc.php' into feature/2535-split_file 2025-08-28 08:35:10 +00:00
anne-catherine
a1f92a1b4c update original file 2025-08-28 08:34:03 +00:00
Potherca-Bot
33a61a6daa Merge branch 'split-file_valuesetdef.class.inc.php_ValueSetRange.php' into split-file_valuesetdef.class.inc.php 2025-08-27 21:11:43 +00:00
Potherca-Bot
0ddd73bb36 Merge branch 'split-file_valuesetdef.class.inc.php_ValueSetObjects.php' into split-file_valuesetdef.class.inc.php 2025-08-27 21:11:43 +00:00
Potherca-Bot
a0a61837e7 Merge branch 'split-file_valuesetdef.class.inc.php_ValueSetEnum.php' into split-file_valuesetdef.class.inc.php 2025-08-27 21:11:43 +00:00
Potherca-Bot
b8d6990f17 Merge branch 'split-file_valuesetdef.class.inc.php_ValueSetEnumPadded.php' into split-file_valuesetdef.class.inc.php 2025-08-27 21:11:43 +00:00
Potherca-Bot
4b47eae92c Changes content in separated file 'ValueSetRange.php'. 2025-08-27 21:11:42 +00:00
Potherca-Bot
7dff1bb26f Changes content in separated file 'ValueSetObjects.php'. 2025-08-27 21:11:42 +00:00
Potherca-Bot
7e0e511b4b Changes content in separated file 'ValueSetEnum.php'. 2025-08-27 21:11:42 +00:00
Potherca-Bot
1e5cb01b15 Merge branch 'split-file_valuesetdef.class.inc.php_ValueSetEnumClasses.php' into split-file_valuesetdef.class.inc.php 2025-08-27 21:11:42 +00:00
Potherca-Bot
159496afd7 Adds separate file for 'ValueSetRange.php'. 2025-08-27 21:11:42 +00:00
Potherca-Bot
9178144675 Adds separate file for 'ValueSetObjects.php'. 2025-08-27 21:11:42 +00:00
Potherca-Bot
5264edb7ea Merge branch 'split-file_valuesetdef.class.inc.php_ValueSetDefinition.php' into split-file_valuesetdef.class.inc.php 2025-08-27 21:11:42 +00:00
Potherca-Bot
bcf05493d7 Changes content in separated file 'ValueSetEnumPadded.php'. 2025-08-27 21:11:41 +00:00
Potherca-Bot
599d34921f Adds separate file for 'ValueSetEnum.php'. 2025-08-27 21:11:41 +00:00
Potherca-Bot
f5646d6794 Changes content in separated file 'ValueSetEnumClasses.php'. 2025-08-27 21:11:41 +00:00
Potherca-Bot
842dce7c9c Changes content in separated file 'ValueSetDefinition.php'. 2025-08-27 21:11:41 +00:00
Potherca-Bot
cf15f087d4 Adds separate file for 'ValueSetEnumPadded.php'. 2025-08-27 21:11:41 +00:00
Potherca-Bot
376ec1763f Adds separate file for 'ValueSetEnumClasses.php'. 2025-08-27 21:11:41 +00:00
Potherca-Bot
45d29cf3b4 Adds separate file for 'ValueSetDefinition.php'. 2025-08-27 21:11:41 +00:00
Potherca-Bot
68d16625ce Merge branch 'split-file_event.class.inc.php_EventWebService.php' into split-file_event.class.inc.php 2025-08-27 20:39:13 +00:00
Potherca-Bot
412cef4893 Merge branch 'split-file_event.class.inc.php_EventRestService.php' into split-file_event.class.inc.php 2025-08-27 20:39:13 +00:00
Potherca-Bot
2f6980ab2b Merge branch 'split-file_event.class.inc.php_Event.php' into split-file_event.class.inc.php 2025-08-27 20:39:13 +00:00
Potherca-Bot
cab3705ed1 Changes content in separated file 'EventWebService.php'. 2025-08-27 20:39:12 +00:00
Potherca-Bot
69d33d03c8 Merge branch 'split-file_event.class.inc.php_EventOnObject.php' into split-file_event.class.inc.php 2025-08-27 20:39:12 +00:00
Potherca-Bot
9a7a4b6056 Merge branch 'split-file_event.class.inc.php_EventNotification.php' into split-file_event.class.inc.php 2025-08-27 20:39:12 +00:00
Potherca-Bot
b3b0c2966b Merge branch 'split-file_event.class.inc.php_EventNotificationEmail.php' into split-file_event.class.inc.php 2025-08-27 20:39:12 +00:00
Potherca-Bot
92b4e4bb94 Merge branch 'split-file_event.class.inc.php_EventLoginUsage.php' into split-file_event.class.inc.php 2025-08-27 20:39:12 +00:00
Potherca-Bot
3e28ec3cd4 Merge branch 'split-file_event.class.inc.php_EventIssue.php' into split-file_event.class.inc.php 2025-08-27 20:39:12 +00:00
Potherca-Bot
a4fae555c7 Changes content in separated file 'EventRestService.php'. 2025-08-27 20:39:11 +00:00
Potherca-Bot
dbc6b955b4 Changes content in separated file 'Event.php'. 2025-08-27 20:39:11 +00:00
Potherca-Bot
786be188e4 Adds separate file for 'EventWebService.php'. 2025-08-27 20:39:11 +00:00
Potherca-Bot
7821a9552d Changes content in separated file 'EventOnObject.php'. 2025-08-27 20:39:11 +00:00
Potherca-Bot
2377bd8eac Adds separate file for 'EventRestService.php'. 2025-08-27 20:39:11 +00:00
Potherca-Bot
3be8fa7a80 Adds separate file for 'Event.php'. 2025-08-27 20:39:11 +00:00
Potherca-Bot
12bdead661 Adds separate file for 'EventOnObject.php'. 2025-08-27 20:39:11 +00:00
Potherca-Bot
ad8d49a6af Changes content in separated file 'EventNotification.php'. 2025-08-27 20:39:10 +00:00
Potherca-Bot
0612903c7b Changes content in separated file 'EventNotificationEmail.php'. 2025-08-27 20:39:10 +00:00
Potherca-Bot
3c68bd0c37 Changes content in separated file 'EventLoginUsage.php'. 2025-08-27 20:39:10 +00:00
Potherca-Bot
7a178e9d9f Adds separate file for 'EventNotification.php'. 2025-08-27 20:39:10 +00:00
Potherca-Bot
1c643aa36c Adds separate file for 'EventNotificationEmail.php'. 2025-08-27 20:39:10 +00:00
Potherca-Bot
40c76eb36a Adds separate file for 'EventLoginUsage.php'. 2025-08-27 20:39:10 +00:00
Potherca-Bot
f5190a9dcd Changes content in separated file 'EventIssue.php'. 2025-08-27 20:39:09 +00:00
Potherca-Bot
254c4f5065 Adds separate file for 'EventIssue.php'. 2025-08-27 20:39:09 +00:00
190 changed files with 20720 additions and 21952 deletions

View File

@@ -1,938 +0,0 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// 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/>
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
use Combodo\iTop\Application\WebPage\WebPage;
use Combodo\iTop\Service\Notification\NotificationsRepository;
use Combodo\iTop\Service\Notification\NotificationsService;
use Combodo\iTop\Service\Router\Router;
/**
* Persistent classes (internal): user defined actions
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'/core/asynctask.class.inc.php');
require_once(APPROOT.'/core/email.class.inc.php');
/**
* A user defined action, to customize the application
*
* @package iTopORM
*/
abstract class Action extends cmdbAbstractObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "name",
"complementary_name_attcode" => ['finalclass', 'description'],
"state_attcode" => "status",
"reconc_keys" => ['name'],
"db_table" => "priv_action",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
"style" => new ormStyle("ibo-dm-class--Action", "ibo-dm-class-alt--Action", "var(--ibo-dm-class--Action--main-color)", "var(--ibo-dm-class--Action--complementary-color)", null, '../images/icons/icons8-in-transit.svg'),
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values" => null, "sql" => "name", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array(
"allowed_values" => new ValueSetEnum(array('test' => 'Being tested', 'enabled' => 'In production', 'disabled' => 'Inactive')),
"styled_values" => [
'test' => new ormStyle('ibo-dm-enum--Action-status-test', 'ibo-dm-enum-alt--Action-status-test', 'var(--ibo-dm-enum--Action-status-test--main-color)', 'var(--ibo-dm-enum--Action-status-test--complementary-color)', null, null),
'enabled' => new ormStyle('ibo-dm-enum--Action-status-enabled', 'ibo-dm-enum-alt--Action-status-enabled', 'var(--ibo-dm-enum--Action-status-enabled--main-color)', 'var(--ibo-dm-enum--Action-status-enabled--complementary-color)', 'fas fa-check', null),
'disabled' => new ormStyle('ibo-dm-enum--Action-status-disabled', 'ibo-dm-enum-alt--Action-status-disabled', 'var(--ibo-dm-enum--Action-status-disabled--main-color)', 'var(--ibo-dm-enum--Action-status-disabled--complementary-color)', null, null),
],
"display_style" => 'list',
"sql" => "status",
"default_value" => "test",
"is_null_allowed" => false,
"depends_on" => array(),
)));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("trigger_list",
array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "action_id", "ext_key_to_remote" => "trigger_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array(), "display_style" => 'property')));
MetaModel::Init_AddAttribute(new AttributeEnum("asynchronous", array("allowed_values" => new ValueSetEnum(['use_global_setting' => 'Use global settings','yes' => 'Yes' ,'no' => 'No']), "sql" => "asynchronous", "default_value" => 'use_global_setting', "is_null_allowed" => false, "depends_on" => array())));
// Display lists
// - Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list'));
// - Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status'));
// Search criteria
// - Default criteria of the search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status'));
}
/**
* Encapsulate the execution of the action and handle failure & logging
*
* @param \Trigger $oTrigger
* @param array $aContextArgs
*
* @return mixed
*/
abstract public function DoExecute($oTrigger, $aContextArgs);
/**
* @return bool
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function IsActive()
{
switch($this->Get('status'))
{
case 'enabled':
case 'test':
return true;
default:
return false;
}
}
/**
* Return true if the current action status is set on "test"
*
* @return bool
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function IsBeingTested()
{
switch($this->Get('status'))
{
case 'test':
return true;
default:
return false;
}
}
/**
* @inheritDoc
* @since 3.0.0
*/
public function AfterInsert()
{
parent::AfterInsert();
$this->DoCheckIfHasTrigger();
}
/**
* @inheritDoc
* @since 3.0.0
*/
public function AfterUpdate()
{
parent::AfterUpdate();
$this->DoCheckIfHasTrigger();
}
/**
* Check if the Action has at least 1 trigger linked. Otherwise, it adds a warning.
* @return void
* @since 3.0.0
*/
protected function DoCheckIfHasTrigger()
{
$oTriggersSet = $this->Get('trigger_list');
if ($oTriggersSet->Count() === 0) {
$this->m_aCheckWarnings[] = Dict::S('Action:WarningNoTriggerLinked');
}
}
/**
* @since 3.2.0 N°5472 method creation
*/
public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
{
parent::DisplayBareRelations($oPage, false);
if ($oPage instanceof iTopWebPage && !$this->IsNew()) {
$this->GenerateLastExecutionsTab($oPage, $bEditMode);
}
}
/**
* @since 3.2.0 N°5472 method creation
*/
protected function GenerateLastExecutionsTab(iTopWebPage $oPage, $bEditMode)
{
$oRouter = Router::GetInstance();
$sActionLastExecutionsPageUrl = $oRouter->GenerateUrl('notifications.action.last_executions_tab', ['action_id' => $this->GetKey()]);
$oPage->AddAjaxTab('action_errors', $sActionLastExecutionsPageUrl, false, Dict::S('Action:last_executions_tab'));
}
/**
* @param \Combodo\iTop\Application\WebPage\WebPage $oPage
*
* @throws \ApplicationException
* @throws \ArchivedObjectException
* @throws \ConfigException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DictExceptionMissingString
* @throws \InvalidConfigParamException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @throws \ReflectionException
* @since 3.2.0 N°5472 method creation
*/
public function GetLastExecutionsTabContent(WebPage $oPage): void
{
$oConfig = utils::GetConfig();
$sLastExecutionDaysConfigParamName = 'notifications.last_executions_days';
$iLastExecutionDays = $oConfig->Get($sLastExecutionDaysConfigParamName);
if ($iLastExecutionDays < 0) {
throw new InvalidConfigParamException("Invalid value for {$sLastExecutionDaysConfigParamName} config parameter. Param desc: " . $oConfig->GetDescription($sLastExecutionDaysConfigParamName));
}
$sActionQueryOql = 'SELECT EventNotification WHERE action_id = :action_id';
$aActionQueryParams = ['action_id' => $this->GetKey()];
if ($iLastExecutionDays > 0) {
$sActionQueryOql .= ' AND date > DATE_SUB(NOW(), INTERVAL :days DAY)';
$aActionQueryParams['days'] = $iLastExecutionDays;
$sActionQueryLimit = Dict::Format('Action:last_executions_tab_limit_days', $iLastExecutionDays);
} else {
$sActionQueryLimit = Dict::S('Action:last_executions_tab_limit_none');
}
$oActionFilter = DBObjectSearch::FromOQL($sActionQueryOql, $aActionQueryParams);
$oSet = new DBObjectSet($oActionFilter, ['date' => false]);
$sPanelTitle = Dict::Format('Action:last_executions_tab_panel_title', $sActionQueryLimit);
$oExecutionsListBlock = DataTableUIBlockFactory::MakeForResult($oPage, 'action_executions_list', $oSet, ['panel_title' => $sPanelTitle]);
$oPage->AddUiBlock($oExecutionsListBlock);
}
/**
* Will be overloaded by the children classes to return the value of their global asynchronous setting (eg. `email_asynchronous` for `\ActionEmail`, `prefer_asynchronous` for `\ActionWebhook`, ...)
*
* @return bool true if the global setting for this kind of action if to be executed asynchronously, false otherwise.
* @since 3.2.0
*/
public static function GetAsynchronousGlobalSetting(): bool
{
return false;
}
/**
* @return bool true if that action instance should be executed asynchronously, otherwise false
* @throws \ArchivedObjectException
* @throws \CoreException
* @since 3.2.0
*/
public function IsAsynchronous(): bool
{
$sAsynchronous = $this->Get('asynchronous');
if ($sAsynchronous === 'use_global_setting') {
return static::GetAsynchronousGlobalSetting();
}
return $sAsynchronous === 'yes';
}
}
/**
* A notification
*
* @package iTopORM
*/
abstract class ActionNotification extends Action
{
/**
* @inheritDoc
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "name",
"complementary_name_attcode" => ['finalclass', 'description'],
"state_attcode" => "",
"reconc_keys" => ['name'],
"db_table" => "priv_action_notification",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
// - Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list'));
// - Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('finalclass', 'description', 'status'));
MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", array("sql"=>"language", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Search criteria
// - Criteria of the std search form
// MetaModel::Init_SetZListItems('standard_search', array('name'));
// - Default criteria of the search form
// MetaModel::Init_SetZListItems('default_search', array('name'));
}
/**
* @param $sLanguage
* @param $sLanguageCode
*
* @return array [$sPreviousLanguage, $aPreviousPluginProperties]
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \DictExceptionUnknownLanguage
* @since 3.2.0
*/
public function SetNotificationLanguage($sLanguage = null, $sLanguageCode = null){
$sPreviousLanguage = Dict::GetUserLanguage();
$aPreviousPluginProperties = ApplicationContext::GetPluginProperties('QueryLocalizerPlugin');
$sLanguage = $sLanguage ?? $this->Get('language');
$sLanguageCode = $sLanguageCode ?? $sLanguage;
if (!utils::IsNullOrEmptyString($sLanguage)) {
// If a language is specified for this action, force this language
// when rendering all placeholders inside this message
Dict::SetUserLanguage($sLanguage);
AttributeDateTime::LoadFormatFromConfig();
ApplicationContext::SetPluginProperty('QueryLocalizerPlugin', 'language_code', $sLanguageCode);
}
return [$sPreviousLanguage, $aPreviousPluginProperties];
}
}
/**
* An email notification
*
* @package iTopORM
*/
class ActionEmail extends ActionNotification
{
/**
* @var string
* @since 3.0.1
*/
const ENUM_HEADER_NAME_MESSAGE_ID = 'Message-ID';
/**
* @var string
* @since 3.0.1
*/
const ENUM_HEADER_NAME_REFERENCES = 'References';
/**
* @var string
* @since 3.1.0
*/
const TEMPLATE_BODY_CONTENT = '$content$';
/**
* Wraps the 'body' of the message for previewing inside an IFRAME -- i.e. without any of the iTop stylesheets being applied
* @var string
* @since 3.1.0
*/
const CONTENT_HIGHLIGHT = '<div style="border:2px dashed #6800ff;position:relative;padding:2px;margin-top:14px;"><div style="background-color:#6800ff;color:#fff;font-family:Courier New, sans-serif;font-size:14px;line-height:16px;padding:3px;display:block;position:absolute;top:-22px;right:0;">$content$</div>%s</div>';
/**
* Wraps a placeholder of the email's body for previewing inside an IFRAME -- i.e. without any of the iTop stylesheets being applied
* @var string
*/
const FIELD_HIGHLIGHT = '<span style="background-color:#6800ff;color:#fff;font-size:smaller;font-family:Courier New, sans-serif;padding:2px;">\\$$1\\$</span>';
/**
* @inheritDoc
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_action_email",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-mailing.svg'),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeEmailAddress("test_recipient", array("allowed_values" => null, "sql" => "test_recipient", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("from", array("allowed_values" => null, "sql" => "from", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("from_label", array("allowed_values" => null, "sql" => "from_label", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("reply_to", array("allowed_values" => null, "sql" => "reply_to", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("reply_to_label", array("allowed_values" => null, "sql" => "reply_to_label", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeOQL("to", array("allowed_values" => null, "sql" => "to", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeOQL("cc", array("allowed_values" => null, "sql" => "cc", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeOQL("bcc", array("allowed_values" => null, "sql" => "bcc", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeTemplateString("subject", array("allowed_values" => null, "sql" => "subject", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeTemplateHTML("body", array("allowed_values" => null, "sql" => "body", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("importance", array("allowed_values" => new ValueSetEnum('low,normal,high'), "sql" => "importance", "default_value" => 'normal', "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeBlob("html_template", array("is_null_allowed"=>true, "depends_on"=>array(), "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeEnum("ignore_notify", array("allowed_values" => new ValueSetEnum('yes,no'), "sql" => "ignore_notify", "default_value" => 'yes', "is_null_allowed" => false, "depends_on" => array())));
// Display lists
// - Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array(
'col:col1' => array(
'fieldset:ActionEmail:main' => array(
0 => 'name',
1 => 'description',
2 => 'status',
3 => 'language',
4 => 'html_template',
5 => 'subject',
6 => 'body',
// 5 => 'importance', not handled when sending the mail, better hide it then
),
'fieldset:ActionEmail:trigger' => array(
0 => 'trigger_list',
1 => 'asynchronous'
),
),
'col:col2' => array(
'fieldset:ActionEmail:recipients' => array(
0 => 'from',
1 => 'from_label',
2 => 'reply_to',
3 => 'reply_to_label',
4 => 'test_recipient',
5 => 'ignore_notify',
6 => 'to',
7 => 'cc',
8 => 'bcc',
),
),
));
// - Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('status', 'to', 'subject', 'language'));
// Search criteria
// - Standard criteria of the search
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'status', 'subject', 'language'));
// - Default criteria for the search
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status', 'subject', 'language'));
}
// count the recipients found
protected $m_iRecipients;
// Errors management : not that simple because we need that function to be
// executed in the background, while making sure that any issue would be reported clearly
protected $m_aMailErrors; //array of strings explaining the issue
/**
* Return the list of emails as a string, or a detailed error description
*
* @param string $sRecipAttCode
* @param array $aArgs
*
* @return string
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
protected function FindRecipients($sRecipAttCode, $aArgs)
{
$oTrigger = $aArgs['trigger->object()'] ?? null;
$sOQL = $this->Get($sRecipAttCode);
if (utils::IsNullOrEmptyString($sOQL)) return '';
try
{
$oSearch = DBObjectSearch::FromOQL($sOQL);
if ($this->Get('ignore_notify') === 'no') {
// In theory, it is possible to notify *any* kind of object,
// as long as there is an email attribute in the class
// So let's not assume that the selected class is a Person
$sFirstSelectedClass = $oSearch->GetClass();
if (MetaModel::IsValidAttCode($sFirstSelectedClass, 'notify')) {
$oSearch->AddCondition('notify', 'yes');
}
}
$oSearch->AllowAllData();
}
catch (OQLException $e)
{
$this->m_aMailErrors[] = "query syntax error for recipient '$sRecipAttCode'";
return $e->getMessage();
}
$sClass = $oSearch->GetClass();
// Determine the email attribute (the first one will be our choice)
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
if ($oAttDef instanceof AttributeEmailAddress)
{
$sEmailAttCode = $sAttCode;
// we've got one, exit the loop
break;
}
}
if (!isset($sEmailAttCode))
{
$this->m_aMailErrors[] = "wrong target for recipient '$sRecipAttCode'";
return "The objects of the class '$sClass' do not have any email attribute";
}
if($oTrigger !== null && in_array('Contact', MetaModel::EnumParentClasses($sClass, ENUM_CHILD_CLASSES_ALL), true)) {
$aArgs['trigger_id'] = $oTrigger->GetKey();
$aArgs['action_id'] = $this->GetKey();
$sSubscribedContactsOQL = NotificationsRepository::GetInstance()->GetSearchOQLContactUnsubscribedByTriggerAndAction();
$sSubscribedContactsOQL->ApplyParameters($aArgs);
$sAlias = $oSearch->GetClassAlias();
$oSearch->AddConditionExpression(Expression::FromOQL("`$sAlias`.id NOT IN ($sSubscribedContactsOQL)"));
}
$oSet = new DBObjectSet($oSearch, array() /* order */, $aArgs);
$aRecipients = array();
while ($oObj = $oSet->Fetch())
{
$sAddress = trim($oObj->Get($sEmailAttCode));
if (utils::IsNotNullOrEmptyString($sAddress))
{
$aRecipients[] = $sAddress;
$this->m_iRecipients++;
}
if ($oTrigger !== null && in_array('Contact', MetaModel::EnumParentClasses($sClass, ENUM_CHILD_CLASSES_ALL), true)) {
NotificationsService::GetInstance()->RegisterSubscription($oTrigger, $this, $oObj);
}
}
return implode(', ', $aRecipients);
}
/**
* @inheritDoc
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
*/
public function DoExecute($oTrigger, $aContextArgs)
{
if (MetaModel::IsLogEnabledNotification())
{
$oLog = new EventNotificationEmail();
if ($this->IsBeingTested())
{
$oLog->Set('message', 'TEST - Notification sent ('.$this->Get('test_recipient').')');
}
else
{
$oLog->Set('message', 'Notification pending');
}
$oLog->Set('userinfo', UserRights::GetUser());
$oLog->Set('trigger_id', $oTrigger->GetKey());
$oLog->Set('action_id', $this->GetKey());
$oLog->Set('object_id', $aContextArgs['this->object()']->GetKey());
$oLog->Set('object_class', get_class($aContextArgs['this->object()']));
// Must be inserted now so that it gets a valid id that will make the link
// between an eventual asynchronous task (queued) and the log
$oLog->DBInsertNoReload();
}
else
{
$oLog = null;
}
try
{
$sRes = $this->_DoExecute($oTrigger, $aContextArgs, $oLog);
if ($this->IsBeingTested())
{
$sPrefix = 'TEST ('.$this->Get('test_recipient').') - ';
}
else
{
$sPrefix = '';
}
if ($oLog)
{
$oLog->Set('message', $sPrefix . $sRes);
$oLog->DBUpdate();
}
}
catch (Exception $e)
{
if ($oLog)
{
$oLog->Set('message', 'Error: '.$e->getMessage());
try
{
$oLog->DBUpdate();
}
catch (Exception $eSecondTryUpdate)
{
IssueLog::Error("Failed to process email ".$oLog->GetKey()." - reason: ".$e->getMessage()."\nTrace:\n".$e->getTraceAsString());
$oLog->Set('message', 'Error: more details in the log for email "'.$oLog->GetKey().'"');
$oLog->DBUpdate();
}
}
}
}
/**
* @param \Trigger $oTrigger
* @param array $aContextArgs
* @param \EventNotification $oLog
*
* @return string
* @throws \CoreException
* @throws \Exception
*/
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
{
$sStyles = file_get_contents(APPROOT . utils::GetCSSFromSASS("css/email.scss"));
$sStyles .= MetaModel::GetConfig()->Get('email_css');
$oEmail = new EMail();
$aEmailContent = $this->PrepareMessageContent($aContextArgs, $oLog);
$oEmail->SetSubject($aEmailContent['subject']);
$oEmail->SetBody($aEmailContent['body'], 'text/html', $sStyles);
$oEmail->SetRecipientTO($aEmailContent['to']);
$oEmail->SetRecipientCC($aEmailContent['cc']);
$oEmail->SetRecipientBCC($aEmailContent['bcc']);
$oEmail->SetRecipientFrom($aEmailContent['from'], $aEmailContent['from_label']);
$oEmail->SetRecipientReplyTo($aEmailContent['reply_to'], $aEmailContent['reply_to_label']);
$oEmail->SetReferences($aEmailContent['references']);
$oEmail->SetMessageId($aEmailContent['message_id']);
$oEmail->SetInReplyTo($aEmailContent['in_reply_to']);
foreach($aEmailContent['attachments'] as $aAttachment) {
$oEmail->AddAttachment($aAttachment['data'], $aAttachment['filename'], $aAttachment['mime_type']);
}
if (empty($this->m_aMailErrors))
{
if ($this->m_iRecipients == 0)
{
return 'No recipient';
}
else
{
$aErrors = [];
$iRes = $oEmail->Send($aErrors, $this->IsAsynchronous() ? Email::ENUM_SEND_FORCE_ASYNCHRONOUS : Email::ENUM_SEND_FORCE_SYNCHRONOUS, $oLog);
switch ($iRes)
{
case EMAIL_SEND_OK:
return "Sent";
case EMAIL_SEND_PENDING:
return "Pending";
case EMAIL_SEND_ERROR:
return "Errors: ".implode(', ', $aErrors);
}
}
} else {
if (is_array($this->m_aMailErrors) && count($this->m_aMailErrors) > 0) {
$sError = implode(', ', $this->m_aMailErrors);
} else {
$sError = 'Unknown reason';
}
return 'Notification was not sent: '.$sError;
}
}
/**
* @param array $aContextArgs
* @param \EventNotification $oLog
*
* @return array
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
* @throws \DictExceptionMissingString
* @throws \DictExceptionUnknownLanguage
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @since 3.1.0 N°918
*/
protected function PrepareMessageContent($aContextArgs, &$oLog): array
{
$aMessageContent = [
'to' => '',
'cc' => '',
'bcc' => '',
'from' => '',
'from_label' => '',
'reply_to' => '',
'reply_to_label' => '',
'subject' => '',
'body' => '',
'references' => '',
'message_id' => '',
'in_reply_to' => '',
'attachments' => [],
];
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();
[$sPreviousLanguage, $aPreviousPluginProperties] = $this->SetNotificationLanguage();
try
{
$this->m_iRecipients = 0;
$this->m_aMailErrors = array();
// Determine recipients
//
$aMessageContent['to'] = $this->FindRecipients('to', $aContextArgs);
$aMessageContent['cc'] = $this->FindRecipients('cc', $aContextArgs);
$aMessageContent['bcc'] = $this->FindRecipients('bcc', $aContextArgs);
$aMessageContent['from'] = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
$aMessageContent['from_label'] = MetaModel::ApplyParams($this->Get('from_label'), $aContextArgs);
$aMessageContent['reply_to'] = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
$aMessageContent['reply_to_label'] = MetaModel::ApplyParams($this->Get('reply_to_label'), $aContextArgs);
$aMessageContent['subject'] = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
$sBody = $this->BuildMessageBody(false);
$aMessageContent['body'] = MetaModel::ApplyParams($sBody, $aContextArgs);
$oObj = $aContextArgs['this->object()'];
$aMessageContent['message_id'] = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_MESSAGE_ID);
$aMessageContent['references'] = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_REFERENCES);
}
catch (Exception $e) {
/** @noinspection PhpUnhandledExceptionInspection */
throw $e;
}
finally {
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
$this->SetNotificationLanguage($sPreviousLanguage, $aPreviousPluginProperties['language_code'] ?? null);
}
if (!is_null($oLog)) {
// Note: we have to secure this because those values are calculated
// inside the try statement, and we would like to keep track of as
// many data as we could while some variables may still be undefined
if (isset($aMessageContent['to'])) {
$oLog->Set('to', $aMessageContent['to']);
}
if (isset($aMessageContent['cc'])) {
$oLog->Set('cc', $aMessageContent['cc']);
}
if (isset($aMessageContent['bcc'])) {
$oLog->Set('bcc', $aMessageContent['bcc']);
}
if (isset($aMessageContent['from'])) {
$oLog->Set('from', $aMessageContent['from']);
}
if (isset($aMessageContent['subject'])) {
$oLog->Set('subject', $aMessageContent['subject']);
}
if (isset($aMessageContent['body'])) {
$oLog->Set('body', HTMLSanitizer::Sanitize($aMessageContent['body']));
}
}
if ($this->IsBeingTested()) {
$sTestBody = $aMessageContent['body'];
$sTestBody .= "<div style=\"border: dashed;\">\n";
$sTestBody .= "<h1>Testing email notification ".$this->GetHyperlink()."</h1>\n";
$sTestBody .= "<p>The email should be sent with the following properties\n";
$sTestBody .= "<ul>\n";
$sTestBody .= "<li>TO: {$aMessageContent['to']}</li>\n";
$sTestBody .= "<li>CC: {$aMessageContent['cc']}</li>\n";
$sTestBody .= "<li>BCC: {$aMessageContent['bcc']}</li>\n";
$sTestBody .= empty($aMessageContent['from_label']) ? "<li>From: {$aMessageContent['from']}</li>\n" : "<li>From: {$aMessageContent['from_label']} &lt;{$aMessageContent['from']}&gt;</li>\n";
$sTestBody .= empty($aMessageContent['reply_to_label']) ? "<li>Reply-To: {$aMessageContent['reply_to']}</li>\n" : "<li>Reply-To: {$aMessageContent['reply_to_label']} &lt;{$aMessageContent['reply_to']}&gt;</li>\n";
$sTestBody .= "<li>References: {$aMessageContent['references']}</li>\n";
$sTestBody .= "</ul>\n";
$sTestBody .= "</p>\n";
$sTestBody .= "</div>\n";
$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'];
if (isset($aContextArgs['attachments']))
{
$aAttachmentReport = array();
/** @var \ormDocument $oDocument */
foreach($aContextArgs['attachments'] as $oDocument)
{
$aMessageContent['attachments'][] = ['data' => $oDocument->GetData(), 'filename' => $oDocument->GetFileName(), 'mime_type' => $oDocument->GetMimeType()];
$aAttachmentReport[] = array($oDocument->GetFileName(), $oDocument->GetMimeType(), strlen($oDocument->GetData() ?? ''));
}
$oLog->Set('attachments', $aAttachmentReport);
}
return $aMessageContent;
}
/**
* @param \DBObject $oObject
* @param string $sHeaderName {@see \ActionEmail::ENUM_HEADER_NAME_REFERENCES}, {@see \ActionEmail::ENUM_HEADER_NAME_MESSAGE_ID}
*
* @return string The formatted identifier for $sHeaderName based on $oObject
* @throws \Exception
* @since 3.1.0 N°4849
*/
protected function GenerateIdentifierForHeaders(DBObject $oObject, string $sHeaderName): string
{
$sObjClass = get_class($oObject);
$sObjId = $oObject->GetKey();
$sAppName = utils::Sanitize(ITOP_APPLICATION_SHORT, '', utils::ENUM_SANITIZATION_FILTER_VARIABLE_NAME);
switch ($sHeaderName) {
case static::ENUM_HEADER_NAME_MESSAGE_ID:
case static::ENUM_HEADER_NAME_REFERENCES:
// Prefix
$sPrefix = sprintf('%s_%s_%d', $sAppName, $sObjClass, $sObjId);
if ($sHeaderName === static::ENUM_HEADER_NAME_MESSAGE_ID) {
$sPrefix .= sprintf('_%F', microtime(true /* get as float*/));
}
// Suffix
$sSuffix = sprintf('@%s.openitop.org', MetaModel::GetEnvironmentId());
// Identifier
$sIdentifier = $sPrefix.$sSuffix;
if ($sHeaderName === static::ENUM_HEADER_NAME_REFERENCES) {
$sIdentifier = "<$sIdentifier>";
}
return $sIdentifier;
}
// Requested header name invalid
$sErrorMessage = sprintf('%s: Could not generate identifier for header "%s", only %s are supported', static::class, $sHeaderName, implode(' / ', [static::ENUM_HEADER_NAME_MESSAGE_ID, static::ENUM_HEADER_NAME_REFERENCES]));
IssueLog::Error($sErrorMessage, LogChannels::NOTIFICATIONS, [
'Object' => $sObjClass.'::'.$sObjId.' ('.$oObject->GetRawName().')',
'Action' => get_class($this).'::'.$this->GetKey().' ('.$this->GetRawName().')',
]);
throw new Exception($sErrorMessage);
}
/**
* Compose the body of the message from the 'body' attribute and the HTML template (if any)
* @since 3.1.0 N°4849
* @param bool $bHighlightPlaceholders If true add some extra HTML around placeholders to highlight them
* @return string
*/
protected function BuildMessageBody(bool $bHighlightPlaceholders = false): string
{
// Wrap content with a specific class in order to apply styles of HTML fields through the emogrifier (see `css/email.scss`)
$sBody = <<<HTML
<div class="email-is-html-content">
{$this->Get('body')}
</div>
HTML;
/** @var ormDocument $oHtmlTemplate */
$oHtmlTemplate = $this->Get('html_template');
if ($oHtmlTemplate && !$oHtmlTemplate->IsEmpty()) {
$sHtmlTemplate = $oHtmlTemplate->GetData();
if (false !== mb_strpos($sHtmlTemplate, static::TEMPLATE_BODY_CONTENT)) {
if ($bHighlightPlaceholders) {
// Highlight the $content$ block
$sBody = sprintf(static::CONTENT_HIGHLIGHT, $sBody);
}
$sBody = str_replace(static::TEMPLATE_BODY_CONTENT, $sBody, $oHtmlTemplate->GetData()); // str_replace is ok as long as both strings are well-formed UTF-8
} else {
$sBody = $oHtmlTemplate->GetData();
}
}
if($bHighlightPlaceholders) {
// Highlight all placeholders
$sBody = preg_replace('/\\$([^$]+)\\$/', static::FIELD_HIGHLIGHT, $sBody);
}
return $sBody;
}
/**
* @since 3.1.0 N°4849
* @inheritDoc
* @see cmdbAbstractObject::DisplayBareRelations()
*/
public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
{
parent::DisplayBareRelations($oPage, false);
if (!$bEditMode) {
$oPage->SetCurrentTab('action_email__preview', Dict::S('ActionEmail:preview_tab'), Dict::S('ActionEmail:preview_tab+'));
$sBody = $this->BuildMessageBody(true);
TwigHelper::RenderIntoPage($oPage, APPROOT.'/', 'templates/datamodel/ActionEmail/email-notification-preview', ['iframe_content' => $sBody]);
}
}
/**
* @since 3.1.0
* @inheritDoc
* @see cmdbAbstractObject::DoCheckToWrite()
*/
public function DoCheckToWrite()
{
parent::DoCheckToWrite();
$oHtmlTemplate = $this->Get('html_template');
if ($oHtmlTemplate && !$oHtmlTemplate->IsEmpty()) {
if (false === mb_strpos($oHtmlTemplate->GetData(), static::TEMPLATE_BODY_CONTENT)) {
$this->m_aCheckWarnings[] = Dict::Format('ActionEmail:content_placeholder_missing', static::TEMPLATE_BODY_CONTENT, Dict::S('Class:ActionEmail/Attribute:body'));
}
}
}
/**
* @inheritDoc
* @since 3.2.0
*/
public static function GetAsynchronousGlobalSetting(): bool
{
return utils::GetConfig()->Get('email_asynchronous');
}
}

View File

@@ -1,544 +0,0 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// 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/>
use Combodo\iTop\Service\Notification\Event\EventNotificationNewsroomService;
/**
* Persistent classes (internal): user defined actions
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ExecAsyncTask implements iBackgroundProcess
{
public function GetPeriodicity()
{
return 2; // seconds
}
public function Process($iTimeLimit)
{
$sNow = date(AttributeDateTime::GetSQLFormat());
// Criteria: planned, and expected to occur... ASAP or in the past
$sOQL = "SELECT AsyncTask WHERE (status = 'planned') AND (ISNULL(planned) OR (planned < '$sNow'))";
$iProcessed = 0;
while (time() < $iTimeLimit)
{
// Next one ?
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array(), null, 1 /* limit count */);
$oTask = $oSet->Fetch();
if (is_null($oTask))
{
// Nothing to be done
break;
}
$iProcessed++;
if ($oTask->Process())
{
$oTask->DBDelete();
}
}
return "processed $iProcessed tasks";
}
}
/**
* A
*
* @package iTopORM
*/
abstract class AsyncTask extends DBObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => array('created'),
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_async_task",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
);
MetaModel::Init_Params($aParams);
// Null is allowed to ease the migration from iTop 2.0.2 and earlier, when the status did not exist, and because the default value is not taken into account in the SQL definition
// The value is set from null to planned in the setup program
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('planned,running,idle,error'), "sql"=>"status", "default_value"=>"planned", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("started", array("allowed_values"=>null, "sql"=>"started", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("planned", array("allowed_values"=>null, "sql"=>"planned", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("event_id", array("targetclass"=>"Event", "jointype"=> "", "allowed_values"=>null, "sql"=>"event_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("remaining_retries", array("allowed_values"=>null, "sql"=>"remaining_retries", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("last_error_code", array("allowed_values"=>null, "sql"=>"last_error_code", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("last_error", array("allowed_values"=>null, "sql"=>"last_error", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("last_attempt", array("allowed_values"=>null, "sql"=>"last_attempt", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
}
/**
* Every is fine
*/
const OK = 0;
/**
* The task no longer exists
*/
const DELETED = 1;
/**
* The task is already being executed
*/
const ALREADY_RUNNING = 2;
/**
* The current process requests the ownership on the task.
* In case the task can be accessed concurrently, this function can be overloaded to add a critical section.
* The function must not block the caller if another process is already owning the task
*
* @return integer A code among OK/DELETED/ALREADY_RUNNING.
*/
public function MarkAsRunning()
{
try
{
if ($this->Get('status') == 'running')
{
return self::ALREADY_RUNNING;
}
else
{
$this->Set('status', 'running');
$this->Set('started', time());
$this->DBUpdate();
return self::OK;
}
}
catch(Exception $e)
{
// Corrupted task !! (for example: "Failed to reload object")
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$e->getMessage().' - fatal error, deleting the task.');
if ($this->Get('event_id') != 0)
{
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', 'Failed, corrupted data: '.$e->getMessage());
$oEventLog->DBUpdate();
}
$this->DBDelete();
return self::DELETED;
}
}
public function GetRetryDelay($iErrorCode = null)
{
$iRetryDelay = 600;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
$iRetryDelay = $aConfig['retry_delay'] ?? $iRetryDelay;
}
return $iRetryDelay;
}
public function GetMaxRetries($iErrorCode = null)
{
$iMaxRetries = 0;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
$iMaxRetries = $aConfig['max_retries'] ?? $iMaxRetries;
}
return $iMaxRetries;
}
public function IsRetryDelayExponential()
{
$bExponential = false;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
$bExponential = (bool) ($aConfig['exponential_delay'] ?? $bExponential);
}
return $bExponential;
}
public static function CheckRetryConfig(Config $oConfig, $sAsyncTaskClass)
{
$aMessages = [];
$aRetries = $oConfig->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists($sAsyncTaskClass, $aRetries))
{
$aValidKeys = array("retry_delay", "max_retries", "exponential_delay");
$aConfig = $aRetries[$sAsyncTaskClass];
if (!is_array($aConfig))
{
$aMessages[] = Dict::Format('Class:AsyncTask:InvalidConfig_Class_Keys', $sAsyncTaskClass, implode(', ', $aValidKeys));
}
else
{
foreach($aConfig as $key => $value)
{
if (!in_array($key, $aValidKeys))
{
$aMessages[] = Dict::Format('Class:AsyncTask:InvalidConfig_Class_InvalidKey_Keys', $sAsyncTaskClass, $key, implode(', ', $aValidKeys));
}
}
}
}
return $aMessages;
}
/**
* Compute the delay to wait for the "next retry", based on the given parameters
* @param bool $bIsExponential
* @param int $iRetryDelay
* @param int $iMaxRetries
* @param int $iRemainingRetries
* @return int
*/
public static function GetNextRetryDelay($bIsExponential, $iRetryDelay, $iMaxRetries, $iRemainingRetries)
{
if ($bIsExponential)
{
$iExponent = $iMaxRetries - $iRemainingRetries;
if ($iExponent < 0) $iExponent = 0; // Safety net in case on configuration change in the middle of retries
return $iRetryDelay * (2 ** $iExponent);
}
else
{
return $iRetryDelay;
}
}
/**
* Override to notify people that a task cannot be performed
*/
protected function OnDefinitiveFailure()
{
}
protected function OnInsert()
{
$this->Set('created', time());
}
/**
* @return boolean True if the task record can be deleted
*/
public function Process()
{
// By default: consider that the task is not completed
$bRet = false;
// Attempt to take the ownership
$iStatus = $this->MarkAsRunning();
if ($iStatus == self::OK)
{
try
{
$sStatus = $this->DoProcess();
if ($this->Get('event_id') != 0)
{
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', $sStatus);
$oEventLog->DBUpdate();
}
$bRet = true;
} catch (Exception $e)
{
$this->HandleError($e->getMessage(), $e->getCode());
}
}
else
{
// Already done or being handled by another process... skip...
$bRet = false;
}
return $bRet;
}
/**
* Overridable to extend the behavior in case of error (logging)
*/
protected function HandleError($sErrorMessage, $iErrorCode)
{
if ($this->Get('last_attempt') == '')
{
// First attempt
$this->Set('remaining_retries', $this->GetMaxRetries($iErrorCode));
}
$this->SetTrim('last_error', $sErrorMessage);
$this->Set('last_error_code', $iErrorCode); // Note: can be ZERO !!!
$this->Set('last_attempt', time());
$iRemaining = $this->Get('remaining_retries');
if ($iRemaining > 0)
{
$iRetryDelay = $this->GetRetryDelay($iErrorCode);
$iNextRetryDelay = static::GetNextRetryDelay($this->IsRetryDelayExponential(), $iRetryDelay, $this->GetMaxRetries($iErrorCode), $iRemaining);
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage.' - remaining retries: '.$iRemaining.' - next retry in '.$iNextRetryDelay.'s');
if ($this->Get('event_id') != 0)
{
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', "$sErrorMessage\nFailed to process async task. Remaining retries: $iRemaining. Next retry in {$iNextRetryDelay}s");
try
{
$oEventLog->DBUpdate();
}
catch (Exception $e)
{
$oEventLog->Set('message', "Failed to process async task. Remaining retries: $iRemaining. Next retry in {$iNextRetryDelay}s, more details in the log");
$oEventLog->DBUpdate();
}
}
$this->Set('remaining_retries', $iRemaining - 1);
$this->Set('status', 'planned');
$this->Set('started', null);
$this->Set('planned', time() + $iNextRetryDelay);
}
else
{
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage);
if ($this->Get('event_id') != 0)
{
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', "$sErrorMessage\nFailed to process async task.");
try
{
$oEventLog->DBUpdate();
}
catch (Exception $e)
{
$oEventLog->Set('message', 'Failed to process async task, more details in the log');
$oEventLog->DBUpdate();
}
}
$this->Set('status', 'error');
$this->Set('started', null);
$this->Set('planned', null);
$this->OnDefinitiveFailure();
}
$this->DBUpdate();
}
/**
* Throws an exception (message and code)
*
* @return string
*/
abstract public function DoProcess();
/**
* Describes the error codes that DoProcess can return by the mean of exceptions
*/
static public function EnumErrorCodes()
{
return array();
}
}
/**
* An email notification
*
* @package iTopORM
*/
class AsyncSendEmail extends AsyncTask
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "created",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_async_send_email",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeInteger("version", array("allowed_values"=>null, "sql"=>"version", "default_value"=>Email::ORIGINAL_FORMAT, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("to", array("allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLongText("message", array("allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists
// MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'test_recipient', 'from', 'reply_to', 'to', 'cc', 'bcc', 'subject', 'body', 'importance', 'trigger_list')); // Attributes to be displayed for the complete details
// MetaModel::Init_SetZListItems('list', array('name', 'status', 'to', 'subject')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
static public function AddToQueue(EMail $oEMail, $oLog)
{
$oNew = MetaModel::NewObject(__class__);
if ($oLog)
{
$oNew->Set('event_id', $oLog->GetKey());
}
$oNew->Set('to', $oEMail->GetRecipientTO(true /* string */));
$oNew->Set('subject', $oEMail->GetSubject());
$oNew->Set('version', 2);
$sMessage = $oEMail->SerializeV2();
$oNew->Set('message', $sMessage);
$oNew->DBInsert();
}
/**
* @inheritDoc
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function DoProcess()
{
$sMessage = $this->Get('message');
$iVersion = (int) $this->Get('version');
switch($iVersion)
{
case Email::FORMAT_V2:
$oEMail = Email::UnSerializeV2($sMessage);
break;
case Email::ORIGINAL_FORMAT:
$oEMail = unserialize($sMessage);
break;
default:
return 'Unknown version of the serialization format: '.$iVersion;
}
$iRes = $oEMail->Send($aIssues, true /* force synchro !!!!! */);
switch ($iRes)
{
case EMAIL_SEND_OK:
return "Sent";
case EMAIL_SEND_PENDING:
return "Bug - the email should be sent in synchronous mode";
case EMAIL_SEND_ERROR:
if (is_array($aIssues)) {
$sMessage = "Sending eMail failed: ".implode(', ', $aIssues);
} else {
$sMessage = "Sending eMail failed.";
}
throw new Exception($sMessage);
}
return '';
}
}
/**
* An async notification to be sent to iTop users through the newsroom
* @since 3.2.0
*/
class AsyncSendNewsroom extends AsyncTask {
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "created",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_async_send_newsroom",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeText("recipients", array("allowed_values"=>null, "sql"=>"recipients", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass"=>"Action", "allowed_values"=>null, "sql"=>"action_id", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass"=>"Trigger", "allowed_values"=>null, "sql"=>"trigger_id", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("title", array("allowed_values"=>null, "sql"=>"title", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("message", array("allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("object_id", array("allowed_values"=>null, "sql"=>"object_id", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("object_class", array("allowed_values"=>null, "sql"=>"object_class", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("url", array("allowed_values"=>null, "sql"=>"url", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>'NOW()', "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
}
/**
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
* @throws \MySQLException
* @throws \OQLException
*/
public static function AddToQueue(int $iActionId, int $iTriggerId, array $aRecipients, string $sMessage, string $sTitle, string $sUrl, int $iObjectId, ?string $sObjectClass): void
{
$oNew = new static();
$oNew->Set('action_id', $iActionId);
$oNew->Set('trigger_id', $iTriggerId);
$oNew->Set('recipients', json_encode($aRecipients));
$oNew->Set('message', $sMessage);
$oNew->Set('title', $sTitle);
$oNew->Set('url', $sUrl);
$oNew->Set('object_id', $iObjectId);
$oNew->Set('object_class', $sObjectClass);
$oNew->SetCurrentDate('date');
$oNew->DBInsert();
}
/**
* @inheritDoc
*/
public function DoProcess()
{
$oAction = MetaModel::GetObject('Action', $this->Get('action_id'));
$iTriggerId = $this->Get('trigger_id');
$aRecipients = json_decode($this->Get('recipients'));
$sMessage = $this->Get('message');
$sTitle = $this->Get('title');
$sUrl = $this->Get('url');
$iObjectId = $this->Get('object_id');
$sObjectClass = $this->Get('object_class');
$sDate = $this->Get('date');
foreach ($aRecipients as $iRecipientId)
{
$oEvent = EventNotificationNewsroomService::MakeEventFromAction($oAction, $iRecipientId, $iTriggerId, $sMessage, $sTitle, $sUrl, $iObjectId, $sObjectClass, $sDate);
$oEvent->DBInsertNoReload();
}
return "Sent";
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -26,17 +26,54 @@ MetaModel::IncludeModule('application/query.class.inc.php');
MetaModel::IncludeModule('setup/moduleinstallation.class.inc.php');
MetaModel::IncludeModule('core/event.class.inc.php');
MetaModel::IncludeModule('core/action.class.inc.php');
MetaModel::IncludeModule('core/trigger.class.inc.php');
MetaModel::IncludeModule('core/bulkexport.class.inc.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/Event/Event.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/Event/EventNotification.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/Event/EventNotificationEmail.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/Event/EventIssue.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/Event/EventWebService.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/Event/EventRestService.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/Event/EventLoginUsage.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/Event/EventOnObject.php');
MetaModel::IncludeModule(APPROOT.'/sources/Core/DataModel/AsyncTask/ExecAsyncTask.php');
MetaModel::IncludeModule(APPROOT.'/sources/Core/DataModel/AsyncTask/AsyncTask.php');
MetaModel::IncludeModule(APPROOT.'/sources/Core/DataModel/AsyncTask/AsyncSendEmail.php');
MetaModel::IncludeModule(APPROOT.'/sources/Core/DataModel/AsyncTask/ExecAsyncTask.php');
MetaModel::IncludeModule(APPROOT.'/sources/Core/DataModel/AsyncTask/AsyncSendNewsroom.php');
MetaModel::IncludeModule(APPROOT.'/core/email.class.inc.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/Action.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/ActionNotification.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/ActionEmail.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/lnkTriggerAction.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/Trigger.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/TriggerOnAttributeBlobDownload.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/TriggerOnObject.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/TriggerOnObjectCreate.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/TriggerOnObjectDelete.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/TriggerOnObjectMention.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/TriggerOnObjectUpdate.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/TriggerOnPortalUpdate.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/TriggerOnStateChange.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/TriggerOnStateEnter.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/TriggerOnStateLeave.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/DataModel/TriggerAndAction/TriggerOnThresholdReached.php');
//MetaModel::IncludeModule('core/bulkexport.class.inc.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/BulkExport/BulkExport.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/BulkExport/BulkExportException.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/BulkExport/BulkExportMissingParameterException.php');
MetaModel::IncludeModule(APPROOT.'/sources/Application/BulkExport/BulkExportResultGC.php');
MetaModel::IncludeModule('core/ownershiplock.class.inc.php');
MetaModel::IncludeModule('core/tagsetfield.class.inc.php');
MetaModel::IncludeModule(APPROOT.'/sources/Core/DataModel/TagSetFieldData.php');
MetaModel::IncludeModule('synchro/synchrodatasource.class.inc.php');
MetaModel::IncludeModule('core/backgroundtask.class.inc.php');
MetaModel::IncludeModule(APPROOT.'/sources/Core/DataModel/BackgroundTask.php');
MetaModel::IncludeModule('core/inlineimage.class.inc.php');
MetaModel::IncludeModule('core/counter.class.inc.php');
MetaModel::IncludeModule('core/TemporaryObjectDescriptor.php');
//MetaModel::IncludeModule('core/counter.class.inc.php');
MetaModel::IncludeModule(APPROOT.'/sources/Core/DataModel/KeyValueStore.php');
MetaModel::IncludeModule(APPROOT.'/sources/Core/DataModel/TemporaryObjectDescriptor.php');
MetaModel::IncludeModule('webservices/webservices.basic.php');

File diff suppressed because it is too large Load Diff

View File

@@ -1,514 +0,0 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
* 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
*/
use Combodo\iTop\Application\WebPage\Page;
use Combodo\iTop\Application\WebPage\WebPage;
define('EXPORTER_DEFAULT_CHUNK_SIZE', 1000);
class BulkExportException extends Exception
{
protected $sLocalizedMessage;
public function __construct($message, $sLocalizedMessage, $code = 0, $previous = null)
{
parent::__construct($message, $code, $previous);
$this->sLocalizedMessage = $sLocalizedMessage;
}
public function GetLocalizedMessage()
{
return $this->sLocalizedMessage;
}
}
class BulkExportMissingParameterException extends BulkExportException
{
public function __construct($sFieldCode)
{
parent::__construct('Missing parameter: '.$sFieldCode, Dict::Format('Core:BulkExport:MissingParameter_Param', $sFieldCode));
}
}
/**
* Class BulkExport
*
* @copyright Copyright (C) 2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
class BulkExportResult extends DBObject
{
public static function Init()
{
$aParams = array
(
"category" => 'core/cmdb',
"key_type" => 'autoincrement',
"name_attcode" => array('created'),
"state_attcode" => '',
"reconc_keys" => array(),
"db_table" => 'priv_bulk_export_result',
"db_key_field" => 'id',
"db_finalclass_field" => '',
"display_template" => '',
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("user_id", array("allowed_values"=>null, "sql"=>"user_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("chunk_size", array("allowed_values"=>null, "sql"=>"chunk_size", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("format", array("allowed_values"=>null, "sql"=>"format", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("temp_file_path", array("allowed_values"=>null, "sql"=>"temp_file_path", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLongText("search", array("allowed_values"=>null, "sql"=>"search", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLongText("status_info", array("allowed_values"=>null, "sql"=>"status_info", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeBoolean("localize_output", array("allowed_values"=>null, "sql"=>"localize_output", "default_value"=>true, "is_null_allowed"=>true, "depends_on"=>array())));
}
/**
* @throws CoreUnexpectedValue
* @throws Exception
*/
public function ComputeValues()
{
$this->Set('user_id', UserRights::GetUserId());
}
}
/**
* Garbage collector for cleaning "old" export results from the database and the disk.
* This background process runs once per day and deletes the results of all exports which
* are older than one day.
*/
class BulkExportResultGC implements iBackgroundProcess
{
public function GetPeriodicity()
{
return 24*3600; // seconds
}
public function Process($iTimeLimit)
{
$sDateLimit = date(AttributeDateTime::GetSQLFormat(), time() - 24*3600); // Every BulkExportResult older than one day will be deleted
$sOQL = "SELECT BulkExportResult WHERE created < '$sDateLimit'";
$iProcessed = 0;
while (time() < $iTimeLimit)
{
// Next one ?
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array(), null, 1 /* limit count */);
$oSet->OptimizeColumnLoad(array('BulkExportResult' => array('temp_file_path')));
$oResult = $oSet->Fetch();
if (is_null($oResult))
{
// Nothing to be done
break;
}
$iProcessed++;
@unlink($oResult->Get('temp_file_path'));
utils::PushArchiveMode(false);
$oResult->DBDelete();
utils::PopArchiveMode();
}
return "Cleaned $iProcessed old export results(s).";
}
}
/**
* Class BulkExport
*
* @copyright Copyright (C) 2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class BulkExport
{
protected $oSearch;
protected $iChunkSize;
protected $sFormatCode;
protected $aStatusInfo;
protected $oBulkExportResult;
protected $sTmpFile;
protected $bLocalizeOutput;
public function __construct()
{
$this->oSearch = null;
$this->iChunkSize = 0;
$this->sFormatCode = null;
$this->aStatusInfo = [
'show_obsolete_data' => utils::ShowObsoleteData(),
];
$this->oBulkExportResult = null;
$this->sTmpFile = '';
$this->bLocalizeOutput = false;
}
/**
* Find the first class capable of exporting the data in the given format
*
* @param string $sFormatCode The lowercase format (e.g. html, csv, spreadsheet, xlsx, xml, json, pdf...)
* @param DBSearch $oSearch The search/filter defining the set of objects to export or null when listing the supported formats
*
* @return BulkExport|null
* @throws ReflectionException
*/
static public function FindExporter($sFormatCode, $oSearch = null)
{
foreach(get_declared_classes() as $sPHPClass)
{
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->isSubclassOf('BulkExport') && !$oRefClass->isAbstract())
{
/** @var BulkExport $oBulkExporter */
$oBulkExporter = new $sPHPClass();
if ($oBulkExporter->IsFormatSupported($sFormatCode, $oSearch))
{
if ($oSearch)
{
$oBulkExporter->SetObjectList($oSearch);
}
return $oBulkExporter;
}
}
}
return null;
}
/**
* Find the exporter corresponding to the given persistent token
*
* @param int $iPersistentToken The identifier of the BulkExportResult object storing the information
*
* @return BulkExport|null
* @throws ArchivedObjectException
* @throws CoreException
* @throws ReflectionException
*/
static public function FindExporterFromToken($iPersistentToken = null)
{
$oBulkExporter = null;
$oInfo = MetaModel::GetObject('BulkExportResult', $iPersistentToken, false);
if ($oInfo && ($oInfo->Get('user_id') == UserRights::GetUserId()))
{
$sFormatCode = $oInfo->Get('format');
$aStatusInfo = json_decode($oInfo->Get('status_info'),true);
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
$oSearch->SetShowObsoleteData($aStatusInfo['show_obsolete_data']);
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
if ($oBulkExporter)
{
$oBulkExporter->SetFormat($sFormatCode);
$oBulkExporter->SetObjectList($oSearch);
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
$oBulkExporter->SetStatusInfo($aStatusInfo);
$oBulkExporter->SetLocalizeOutput($oInfo->Get('localize_output'));
$oBulkExporter->sTmpFile = $oInfo->Get('temp_file_path');
$oBulkExporter->oBulkExportResult = $oInfo;
}
}
return $oBulkExporter;
}
/**
* @param $data
* @throws Exception
*/
public function AppendToTmpFile($data)
{
if ($this->sTmpFile == '')
{
$this->sTmpFile = $this->MakeTmpFile($this->GetFileExtension());
}
$hFile = fopen($this->sTmpFile, 'ab');
if ($hFile !== false)
{
fwrite($hFile, $data);
fclose($hFile);
}
}
public function GetTmpFilePath()
{
return $this->sTmpFile;
}
/**
* Lists all possible export formats. The output is a hash array in the form: 'format_code' => 'localized format label'
* @return array :string
*/
static public function FindSupportedFormats()
{
$aSupportedFormats = array();
foreach(get_declared_classes() as $sPHPClass)
{
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->isSubClassOf('BulkExport') && !$oRefClass->isAbstract())
{
$oBulkExporter = new $sPHPClass;
$aFormats = $oBulkExporter->GetSupportedFormats();
$aSupportedFormats = array_merge($aSupportedFormats, $aFormats);
}
}
return $aSupportedFormats;
}
/**
* (non-PHPdoc)
* @see iBulkExport::SetChunkSize()
*/
public function SetChunkSize($iChunkSize)
{
$this->iChunkSize = $iChunkSize;
}
/**
* @param $bLocalizeOutput
*/
public function SetLocalizeOutput($bLocalizeOutput)
{
$this->bLocalizeOutput = $bLocalizeOutput;
}
/**
* (non-PHPdoc)
* @see iBulkExport::SetObjectList()
*/
public function SetObjectList(DBSearch $oSearch)
{
$oSearch->SetShowObsoleteData($this->aStatusInfo['show_obsolete_data']);
$this->oSearch = $oSearch;
}
public function SetFormat($sFormatCode)
{
$this->sFormatCode = $sFormatCode;
}
/**
* (non-PHPdoc)
* @see iBulkExport::IsFormatSupported()
*/
public function IsFormatSupported($sFormatCode, $oSearch = null)
{
return array_key_exists($sFormatCode, $this->GetSupportedFormats());
}
/**
* (non-PHPdoc)
* @see iBulkExport::GetSupportedFormats()
*/
public function GetSupportedFormats()
{
return array(); // return array('csv' => Dict::S('UI:ExportFormatCSV'));
}
public function SetHttpHeaders(WebPage $oPage)
{
}
/**
* @return string
*/
public function GetHeader()
{
return '';
}
abstract public function GetNextChunk(&$aStatus);
/**
* @return string
*/
public function GetFooter()
{
return '';
}
public function SaveState()
{
if ($this->oBulkExportResult === null)
{
$this->oBulkExportResult = new BulkExportResult();
$this->oBulkExportResult->Set('format', $this->sFormatCode);
$this->oBulkExportResult->Set('search', $this->oSearch->serialize());
$this->oBulkExportResult->Set('chunk_size', $this->iChunkSize);
$this->oBulkExportResult->Set('localize_output', $this->bLocalizeOutput);
}
$this->oBulkExportResult->Set('status_info', json_encode($this->GetStatusInfo()));
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
utils::PushArchiveMode(false);
$ret = $this->oBulkExportResult->DBWrite();
utils::PopArchiveMode();
return $ret;
}
public function Cleanup()
{
if (($this->oBulkExportResult && (!$this->oBulkExportResult->IsNew())))
{
$sFilename = $this->oBulkExportResult->Get('temp_file_path');
if ($sFilename != '')
{
@unlink($sFilename);
}
utils::PushArchiveMode(false);
$this->oBulkExportResult->DBDelete();
utils::PopArchiveMode();
}
}
public function EnumFormParts()
{
return array();
}
/**
* @deprecated 3.0.0 use GetFormPart instead
*/
public function DisplayFormPart(WebPage $oP, $sPartId)
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use GetFormPart instead');
$oP->AddSubBlock($this->GetFormPart($oP, $sPartId));
}
/**
* @param WebPage $oP
* @param $sPartId
*
* @return UIContentBlock
*/
public function GetFormPart(WebPage $oP, $sPartId)
{
}
public function DisplayUsage(Page $oP)
{
}
public function ReadParameters()
{
$this->bLocalizeOutput = !((bool)utils::ReadParam('no_localize', 0, true, 'integer'));
}
public function GetResultAsHtml()
{
}
public function GetRawResult()
{
}
/**
* @return string
*/
public function GetMimeType()
{
return '';
}
/**
* @return string
*/
public function GetFileExtension()
{
return '';
}
public function GetCharacterSet()
{
return 'UTF-8';
}
public function GetStatistics()
{
}
public function SetFields($sFields)
{
}
public function GetDownloadFileName()
{
return Dict::Format('Core:BulkExportOf_Class', MetaModel::GetName($this->oSearch->GetClass())).'.'.$this->GetFileExtension();
}
public function SetStatusInfo($aStatusInfo)
{
$this->aStatusInfo = $aStatusInfo;
}
public function GetStatusInfo()
{
return $this->aStatusInfo;
}
/**
* @param $sExtension
* @return string
* @throws Exception
*/
protected function MakeTmpFile($sExtension)
{
if(!is_dir(utils::GetDataPath()."bulk_export"))
{
@mkdir(utils::GetDataPath()."bulk_export", 0777, true /* recursive */);
clearstatcache();
}
if (!is_writable(utils::GetDataPath()."bulk_export"))
{
throw new Exception('Data directory "'.utils::GetDataPath().'bulk_export" could not be written.');
}
$iNum = rand();
do
{
$iNum++;
$sToken = sprintf("%08x", $iNum);
$sFileName = utils::GetDataPath()."bulk_export/$sToken.".$sExtension;
$hFile = @fopen($sFileName, 'x');
}
while($hFile === false);
fclose($hFile);
return $sFileName;
}
}
// The built-in exports
require_once(APPROOT.'core/tabularbulkexport.class.inc.php');
require_once(APPROOT.'core/htmlbulkexport.class.inc.php');
if (extension_loaded('gd'))
{
// PDF export - via TCPDF - requires GD
require_once(APPROOT.'core/pdfbulkexport.class.inc.php');
}
require_once(APPROOT.'core/csvbulkexport.class.inc.php');
require_once(APPROOT.'core/excelbulkexport.class.inc.php');
require_once(APPROOT.'core/spreadsheetbulkexport.class.inc.php');
require_once(APPROOT.'core/xmlbulkexport.class.inc.php');

File diff suppressed because it is too large Load Diff

View File

@@ -40,9 +40,15 @@ require_once('dict.class.inc.php');
require_once('attributedef.class.inc.php');
require_once('stimulus.class.inc.php');
require_once('valuesetdef.class.inc.php');
require_once('MyHelpers.class.inc.php');
require_once(APPROOT.'/sources/Core/ValueSetDefinition/ValueSetDefinition.php');
require_once(APPROOT.'/sources/Core/ValueSetDefinition/ValueSetObjects.php');
require_once(APPROOT.'/sources/Core/ValueSetDefinition/ValueSetEnum.php');
require_once(APPROOT.'/sources/Core/ValueSetDefinition/ValueSetEnumPadded.php');
require_once(APPROOT.'/sources/Core/ValueSetDefinition/ValueSetRange.php');
require_once(APPROOT.'/sources/Core/ValueSetDefinition/ValueSetEnumClasses.php');
require_once('oql/expression.class.inc.php');
require_once('oql/oqlquery.class.inc.php');
require_once('oql/oqlexception.class.inc.php');
@@ -59,12 +65,41 @@ require_once('dbobject.class.php');
require_once('dbobjectset.class.php');
require_once('backgroundprocess.inc.php');
require_once('asynctask.class.inc.php');
require_once('dbproperty.class.inc.php');
//require_once('asynctask.class.inc.php');
require_once(APPROOT.'/sources/Core/DataModel/AsyncTask/ExecAsyncTask.php');
require_once(APPROOT.'/sources/Core/DataModel/AsyncTask/AsyncTask.php');
require_once(APPROOT.'/sources/Core/DataModel/AsyncTask/AsyncSendEmail.php');
require_once(APPROOT.'/sources/Core/DataModel/AsyncTask/ExecAsyncTask.php');
require_once(APPROOT.'/sources/Core/DataModel/AsyncTask/AsyncSendNewsroom.php');
require_once(APPROOT.'/sources/Core/DataModel/DBProperty.php');
// db change tracking data model
require_once('cmdbchange.class.inc.php');
require_once('cmdbchangeop.class.inc.php');
//require_once('cmdbchangeop.class.inc.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/iCMDBChangeOp.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOp.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpCreate.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpDelete.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpPlugin.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttribute.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeBlob.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeCaseLog.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeCustomFields.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeEncrypted.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeHTML.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeLinks.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeLinksAddRemove.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeLinksTune.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeLongText.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeOneWayPassword.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeScalar.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeTagSet.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeText.php');
require_once(APPROOT.'/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeURL.php');
// customization data model
// Romain: temporary moved into application.inc.php (see explanations there)
@@ -77,7 +112,7 @@ require_once('cmdbchangeop.class.inc.php');
require_once('templatestring.class.inc.php');
require_once('csvparser.class.inc.php');
require_once('bulkchange.class.inc.php');
//require_once('bulkchange.class.inc.php');
/**
* A persistent object, which changes are accurately recorded

View File

@@ -49,7 +49,13 @@ define('ACCESS_READONLY', 0);
*/
require_once('attributedef.class.inc.php'); // For the defines
require_once('simplecrypt.class.inc.php');
require_once(APPROOT.'/sources/Application/SimpleCrypt/CryptEngine.php');
require_once(APPROOT.'/sources/Application/SimpleCrypt/SimpleCrypt.php');
require_once(APPROOT.'/sources/Application/SimpleCrypt/SimpleCryptMcryptEngine.php');
require_once(APPROOT.'/sources/Application/SimpleCrypt/SimpleCryptOpenSSLEngine.php');
require_once(APPROOT.'/sources/Application/SimpleCrypt/SimpleCryptOpenSSLMcryptCompatibilityEngine.php');
require_once(APPROOT.'/sources/Application/SimpleCrypt/SimpleCryptSimpleEngine.php');
require_once(APPROOT.'/sources/Application/SimpleCrypt/SimpleCryptSodiumEngine.php');
// was utf8 but it only supports BMP chars (https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html)
// so we switched to utf8mb4 in iTop 2.5, adding dependency to MySQL 5.5.3

View File

@@ -1,251 +0,0 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
* 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
*/
/**
* Class ItopCounter
*
*/
final class ItopCounter
{
/**
* Key based counter.
* The counter is protected against concurrency script.
*
* @param $sCounterName
* @param null|callable $oNewObjectValueProvider optional callable that must return an integer. Used when no key is found
*
* @return int the counter starting at
* * `0` when no $oNewObjectValueProvider is given (or null)
* * `$oNewObjectValueProvider() + 1` otherwise
*
* @throws \CoreException
* @throws \MySQLException
* @throws \Exception
*/
public static function Inc($sCounterName, $oNewObjectValueProvider = null)
{
$sSelfClassName = self::class;
$sMutexKeyName = "{$sSelfClassName}-{$sCounterName}";
$oiTopMutex = new iTopMutex($sMutexKeyName);
$oiTopMutex->Lock();
$bIsInsideTransaction = CMDBSource::IsInsideTransaction();
if ($bIsInsideTransaction)
{
// # Transaction isolation hack:
// When inside a transaction, we need to open a new connection for the counter.
// So it is visible immediately to the connections outside of the transaction.
// Either way, the lock is not long enought, and there would be duplicate ref.
//
// SELECT ... FOR UPDATE would have also worked but with the cost of extra long lock (until the commit),
// we did not wanted this! As opening a short connection is less prone to starving than a long running one.
// Plus it would trigger way more deadlocks!
$hDBLink = self::InitMySQLSession();
}
else
{
$hDBLink = CMDBSource::GetMysqli();
}
try
{
$oFilter = DBObjectSearch::FromOQL('SELECT KeyValueStore WHERE key_name=:key_name AND namespace=:namespace', array(
'key_name' => $sCounterName,
'namespace' => $sSelfClassName,
));
$oAttDef = MetaModel::GetAttributeDef(KeyValueStore::class, 'value');
$aAttToLoad = array(KeyValueStore::class => array('value' => $oAttDef));
$sSql = $oFilter->MakeSelectQuery(array(), array(), $aAttToLoad);
$hResult = mysqli_query($hDBLink, $sSql);
$aCounter = mysqli_fetch_array($hResult, MYSQLI_NUM);
mysqli_free_result($hResult);
//Rebuild the filter, as the MakeSelectQuery polluted the orignal and it cannot be reused
$oFilter = DBObjectSearch::FromOQL('SELECT KeyValueStore WHERE key_name=:key_name AND namespace=:namespace', array(
'key_name' => $sCounterName,
'namespace' => $sSelfClassName,
));
if (is_null($aCounter))
{
if (null != $oNewObjectValueProvider)
{
$iComputedValue = $oNewObjectValueProvider();
}
else
{
$iComputedValue = 0;
}
$iCurrentValue = $iComputedValue + 1;
$aQueryParams = array(
'key_name' => $sCounterName,
'value' => "$iCurrentValue",
'namespace' => $sSelfClassName,
);
$sSql = $oFilter->MakeInsertQuery($aQueryParams);
}
else
{
$iCurrentValue = (int) $aCounter[1];
$iCurrentValue++;
$aQueryParams = array(
'value' => "$iCurrentValue",
);
$sSql = $oFilter->MakeUpdateQuery($aQueryParams);
}
$hResult = mysqli_query($hDBLink, $sSql);
}
catch(Exception $e)
{
IssueLog::Error($e->getMessage());
throw $e;
}
finally
{
if ($bIsInsideTransaction)
{
mysqli_close($hDBLink);
}
$oiTopMutex->Unlock();
}
return $iCurrentValue;
}
/**
* handle a counter for the root class of given $sLeafClass.
* If no counter exist initialize it with the `max(id) + 1`
*
*
*
* @param $sLeafClass
*
* @return int
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreOqlMultipleResultsForbiddenException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
* @throws \OQLException
*/
public static function IncClass($sLeafClass)
{
$sRootClass = MetaModel::GetRootClass($sLeafClass);
$oNewObjectCallback = function() use ($sRootClass)
{
$sRootTable = MetaModel::DBGetTable($sRootClass);
$sIdField = MetaModel::DBGetKey($sRootClass);
return CMDBSource::QueryToScalar("SELECT max(`$sIdField`) FROM `$sRootTable`");
};
return self::Inc($sRootClass, $oNewObjectCallback);
}
/**
* @return \mysqli
* @throws \ConfigException
* @throws \CoreException
* @throws \MySQLException
*/
private static function InitMySQLSession()
{
$oConfig = utils::GetConfig();
$sDBHost = $oConfig->Get('db_host');
$sDBUser = $oConfig->Get('db_user');
$sDBPwd = $oConfig->Get('db_pwd');
$sDBName = $oConfig->Get('db_name');
$bDBTlsEnabled = $oConfig->Get('db_tls.enabled');
$sDBTlsCA = $oConfig->Get('db_tls.ca');
$hDBLink = CMDBSource::GetMysqliInstance($sDBHost, $sDBUser, $sDBPwd, $sDBName, $bDBTlsEnabled, $sDBTlsCA, false);
if (!$hDBLink)
{
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
}
return $hDBLink;
}
}
/**
* Persistent classes for a CMDB
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
class KeyValueStore extends DBObject
{
public static function Init()
{
$aParams = array(
'category' => '',
'key_type' => 'autoincrement',
'name_attcode' => array('key_name'),
'state_attcode' => '',
'reconc_keys' => array(''),
'db_table' => 'key_value_store',
'db_key_field' => 'id',
'db_finalclass_field' => '',
'indexes' => array (
array (
0 => 'key_name',
1 => 'namespace',
),
),);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("namespace", array("allowed_values"=>null, "sql"=>'namespace', "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array(), "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeString("key_name", array("allowed_values"=>null, "sql"=>'key_name', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array(), "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeString("value", array("allowed_values"=>null, "sql"=>'value', "default_value"=>'0', "is_null_allowed"=>false, "depends_on"=>array(), "always_load_in_tables"=>false)));
MetaModel::Init_SetZListItems('details', array (
0 => 'key_name',
1 => 'value',
2 => 'namespace',
));
MetaModel::Init_SetZListItems('standard_search', array (
0 => 'key_name',
1 => 'value',
2 => 'namespace',
));
MetaModel::Init_SetZListItems('list', array (
0 => 'key_name',
1 => 'value',
2 => 'namespace',
));
;
}
}

View File

@@ -1,426 +0,0 @@
<?php
use Combodo\iTop\Application\WebPage\WebPage;
/**
* Copyright (C) 2013-2024 Combodo SAS
*
* 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
*/
class Event extends DBObject implements iDisplay
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeText("message", array("allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'finalclass', 'message')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
/**
* Maps the given context parameter name to the appropriate filter/search code for this class
* @param string $sContextParam Name of the context parameter, i.e. 'org_id'
* @return string Filter code, i.e. 'customer_id'
*/
public static function MapContextParam($sContextParam)
{
if ($sContextParam == 'menu')
{
return null;
}
else
{
return $sContextParam;
}
}
/**
* This function returns a 'hilight' CSS class, used to hilight a given row in a table
* There are currently (i.e defined in the CSS) 4 possible values HILIGHT_CLASS_CRITICAL,
* HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE
* To Be overridden by derived classes
* @param void
* @return String The desired higlight class for the object/row
*/
public function GetHilightClass()
{
// Possible return values are:
// HILIGHT_CLASS_CRITICAL, HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE
return HILIGHT_CLASS_NONE; // Not hilighted by default
}
public static function GetUIPage()
{
return 'UI.php';
}
function DisplayDetails(WebPage $oPage, $bEditMode = false)
{
// Object's details
//$this->DisplayBareHeader($oPage, $bEditMode);
$oPage->AddTabContainer(OBJECT_PROPERTIES_TAB);
$oPage->SetCurrentTabContainer(OBJECT_PROPERTIES_TAB);
$oPage->SetCurrentTab('UI:PropertiesTab');
$this->DisplayBareProperties($oPage, $bEditMode);
}
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
if ($bEditMode) return array(); // Not editable
$aDetails = array();
$sClass = get_class($this);
$aZList = MetaModel::FlattenZlist(MetaModel::GetZListItems($sClass, 'details'));
foreach ($aZList as $sAttCode) {
$sDisplayValue = $this->GetAsHTML($sAttCode);
$aDetails[] = array('label' => '<span title="'.MetaModel::GetDescription($sClass, $sAttCode).'">'.MetaModel::GetLabel($sClass, $sAttCode).'</span>', 'value' => $sDisplayValue);
}
$oPage->Details($aDetails);
return array();
}
}
class EventNotification extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_notification",
"db_key_field" => "id",
"db_finalclass_field" => "",
"order_by_default" => array('date' => false),
'indexes' => array(
array( 'object_class', 'object_id'),
)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass"=>"Trigger", "jointype"=> "", "allowed_values"=>null, "sql"=>"trigger_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass" => "Action", "jointype" => "", "allowed_values" => null, "sql" => "action_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeInteger("object_id", array("allowed_values" => null, "sql" => "object_id", "default_value" => 0, "is_null_allowed" => false, "depends_on" => array())));
//@since 3.2.0
MetaModel::Init_AddAttribute(new AttributeClass("object_class", array("class_category"=>"", "more_values"=>"", "sql"=>"object_class", "default_value"=>null, "is_null_allowed"=>true /*to avoid setting AbstractResource as default in database*/, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo', 'trigger_id', 'action_id', 'object_class', 'object_id')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'message')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
class EventNotificationEmail extends EventNotification
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_email",
"db_key_field" => "id",
"db_finalclass_field" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeText("to", array("allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("cc", array("allowed_values"=>null, "sql"=>"cc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("bcc", array("allowed_values"=>null, "sql"=>"bcc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("from", array("allowed_values"=>null, "sql"=>"from", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeHTML("body", array("allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeTable("attachments", array("allowed_values"=>null, "sql"=>"attachments", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_class', 'object_id', 'to', 'cc', 'bcc', 'from', 'subject', 'body', 'attachments')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'message', 'to', 'subject', 'attachments')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
class EventIssue extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_issue",
"db_key_field" => "id",
"db_finalclass_field" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("issue", array("allowed_values"=>null, "sql"=>"issue", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("impact", array("allowed_values"=>null, "sql"=>"impact", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("page", array("allowed_values"=>null, "sql"=>"page", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributePropertySet("arguments_post", array("allowed_values"=>null, "sql"=>"arguments_post", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributePropertySet("arguments_get", array("allowed_values"=>null, "sql"=>"arguments_get", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeTable("callstack", array("allowed_values"=>null, "sql"=>"callstack", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributePropertySet("data", array("allowed_values"=>null, "sql"=>"data", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo', 'issue', 'impact', 'page', 'arguments_post', 'arguments_get', 'callstack', 'data')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'issue', 'impact')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
protected function OnInsert()
{
// Init page information: name, arguments
//
$this->Set('page', @$GLOBALS['_SERVER']['SCRIPT_NAME']);
if (strlen($this->Get('userinfo')) == 0) {
$this->Set('userinfo', UserRights::GetUserId());
}
if (array_key_exists('_GET', $GLOBALS) && is_array($GLOBALS['_GET']))
{
$this->Set('arguments_get', $this->SanitizeRequestParams($GLOBALS['_GET']));
}
else
{
$this->Set('arguments_get', array());
}
if (array_key_exists('_POST', $GLOBALS) && is_array($GLOBALS['_POST']))
{
$this->Set('arguments_post', $this->SanitizeRequestParams($GLOBALS['_POST']));
} else {
$this->Set('arguments_post', array());
}
$sLength = mb_strlen($this->Get('issue'));
if ($sLength > 255) {
$this->Set('issue', mb_substr($this->Get('issue'), 0, 210)." -truncated ($sLength chars)");
}
$sLength = mb_strlen($this->Get('impact'));
if ($sLength > 255) {
$this->Set('impact', mb_substr($this->Get('impact'), 0, 210)." -truncated ($sLength chars)");
}
$sLength = mb_strlen($this->Get('page'));
if ($sLength > 255) {
$this->Set('page', mb_substr($this->Get('page'), 0, 210)." -truncated ($sLength chars)");
}
}
protected function SanitizeRequestParams(array $aParams): array
{
$aSanitizedParams = [];
foreach ($aParams as $sKey => $sValue) {
if (is_string($sValue)) {
if (stristr($sKey, 'pwd') !== false || stristr($sKey, 'passwd') !== false || stristr($sKey, 'password') !== false) {
$aSanitizedParams[$sKey] = '****';
} elseif (mb_strlen($sValue) < 256) {
$aSanitizedParams[$sKey] = $sValue;
} else {
$aSanitizedParams[$sKey] = '!long string: '.mb_strlen($sValue).' chars';
}
} else {
// Not a string (avoid warnings in case the value cannot be easily cast into a string)
$aSanitizedParams[$sKey] = @(string)$sValue;
}
}
return $aSanitizedParams;
}
}
class EventWebService extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_webservice",
"db_key_field" => "id",
"db_finalclass_field" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("verb", array("allowed_values"=>null, "sql"=>"verb", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
//MetaModel::Init_AddAttribute(new AttributeStructure("arguments", array("allowed_values"=>null, "sql"=>"data", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeBoolean("result", array("allowed_values"=>null, "sql"=>"result", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("log_info", array("allowed_values"=>null, "sql"=>"log_info", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("log_warning", array("allowed_values"=>null, "sql"=>"log_warning", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("log_error", array("allowed_values"=>null, "sql"=>"log_error", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("data", array("allowed_values"=>null, "sql"=>"data", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'verb', 'result', 'log_info', 'log_warning', 'log_error', 'data')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'verb', 'result')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
class EventRestService extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_restservice",
"db_key_field" => "id",
"db_finalclass_field" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("operation", array("allowed_values"=>null, "sql"=>"operation", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("version", array("allowed_values"=>null, "sql"=>"version", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("json_input", array("allowed_values"=>null, "sql"=>"json_input", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("code", array("allowed_values"=>null, "sql"=>"code", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("json_output", array("allowed_values"=>null, "sql"=>"json_output", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("provider", array("allowed_values"=>null, "sql"=>"provider", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'operation', 'version', 'json_input', 'message', 'code', 'json_output', 'provider')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'operation', 'message')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
class EventLoginUsage extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_loginusage",
"db_key_field" => "id",
"db_finalclass_field" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
$aZList = array('date', 'user_id');
if (MetaModel::IsValidAttCode('Contact', 'name'))
{
MetaModel::Init_AddAttribute(new AttributeExternalField("contact_name", array("allowed_values"=>null, "extkey_attcode"=>"user_id", "target_attcode"=>"contactid", "is_null_allowed"=>true, "depends_on"=>array())));
$aZList[] = 'contact_name';
}
if (MetaModel::IsValidAttCode('Contact', 'email'))
{
MetaModel::Init_AddAttribute(new AttributeExternalField("contact_email", array("allowed_values"=>null, "extkey_attcode"=>"user_id", "target_attcode"=>"email", "is_null_allowed"=>true, "depends_on"=>array())));
$aZList[] = 'contact_email';
}
// Display lists
MetaModel::Init_SetZListItems('details', array_merge($aZList, array('userinfo', 'message'))); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array_merge($aZList, array('userinfo'))); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', $aZList); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
class EventOnObject extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_onobject",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("obj_class", array("allowed_values"=>null, "sql"=>"obj_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("obj_key", array("allowed_values"=>null, "sql"=>"obj_key", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'obj_class', 'obj_key', 'message')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'obj_class', 'obj_key', 'message')); // Attributes to be displayed for a list
}
}

View File

@@ -26,7 +26,14 @@ require_once APPROOT.'core/modulehandler.class.inc.php';
require_once APPROOT.'core/querymodifier.class.inc.php';
require_once APPROOT.'core/metamodelmodifier.inc.php';
require_once APPROOT.'core/computing.inc.php';
require_once APPROOT.'core/relationgraph.class.inc.php';
//require_once APPROOT.'core/relationgraph.class.inc.php';
require_once(APPROOT.'/sources/Core/RelationGraph/RelationEdge.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationGraph.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationObjectNode.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationRedundancyNode.php');
require_once APPROOT.'core/apc-compat.php';
require_once APPROOT.'core/expressioncache.class.inc.php';

View File

@@ -1,425 +0,0 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// 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/>
/**
* SimpleCrypt Class - crypto helpers
* Simple encryption of strings, uses mcrypt or degrades to a pure PHP
* implementation when mcrypt is not present.
* Based on Miguel Ros' work found at:
* http://rossoft.wordpress.com/2006/05/22/simple-encryption-class/
*
* Usage:
* $oSimpleCrypt = new SimpleCrypt();
* $encrypted = $oSimpleCrypt->encrypt('a_key','the_text');
* $sClearText = $oSimpleCrypt->decrypt('a_key',$encrypted);
*
* The result is $plain equals to 'the_text'
*
* You can use a different engine if you don't have Mcrypt:
* $oSimpleCrypt = new SimpleCrypt('Simple');
*
* A string encrypted with one engine can't be decrypted with
* a different one even if the key is the same.
*
* @author Miguel Ros <rossoft@gmail.com>
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
class SimpleCrypt
{
/**
* @var \SimpleCrypt
* @since 3.1.0 N°5388
*/
protected $oEngine;
public static function GetNewDefaultParams()
{
if(function_exists('sodium_crypto_secretbox_open') && function_exists('random_bytes')){
$sEngineName = 'Sodium';
}
else if (function_exists('openssl_decrypt'))
{
$sEngineName = 'OpenSSL';
}
else if(function_exists('mcrypt_module_open')){
$sEngineName = 'Mcrypt';
}
else
{
$sEngineName = 'Simple';
}
$sEngineName = 'SimpleCrypt' . $sEngineName . 'Engine';
return $sEngineName::GetNewDefaultParams();
}
/**
* Constructor
* @param string $sEngineName Engine for encryption. Values: Simple, Mcrypt, Sodium or OpenSSL
* @throws Exception This library is unkown
*/
function __construct($sEngineName = 'Mcrypt')
{
switch($sEngineName){
case 'Sodium':
if(!function_exists('sodium_crypto_secretbox_open')){
$sEngineName = 'Simple';
}
break;
case 'Mcrypt':
if(!function_exists('mcrypt_module_open')){
if (function_exists('openssl_decrypt'))
{
$sEngineName = 'OpenSSLMcryptCompatibility';
}
else
{
$sEngineName = 'Simple';
}
}
break;
case 'OpenSSL':
case 'OpenSSLMcryptCompatibility':
if(!function_exists('openssl_decrypt')){
$sEngineName = 'Simple';
}
break;
case 'Simple':
break;
default:
throw new Exception(Dict::Format("Core:AttributeEncryptUnknownLibrary", $sEngineName));
}
$sEngineName = 'SimpleCrypt' . $sEngineName . 'Engine';
$this->oEngine = new $sEngineName;
}
/**
* Encrypts the string with the given key
* @param string $key
* @param string $sString Plaintext string
* @return string Ciphered string
*/
function Encrypt($key, $sString)
{
return $this->oEngine->Encrypt($key,$sString);
}
/**
* Decrypts the string by the given key
* @param string $key
* @param string $string Ciphered string
* @return string Plaintext string
*/
function Decrypt($key, $string)
{
return $this->oEngine->Decrypt($key,$string);
}
/**
* Returns a random "salt" value, to be used when "hashing" a password
* using a one-way encryption algorithm, to prevent an attack using a "rainbow table"
* Tryes to use the best available random number generator
* @return string The generated random "salt"
*/
static function GetNewSalt()
{
// Copied from http://www.php.net/manual/en/function.mt-rand.php#83655
// get 128 pseudorandom bits in a string of 16 bytes
$sRandomBits = null;
// Unix/Linux platform?
$fp = @fopen('/dev/urandom','rb');
if ($fp !== FALSE)
{
//echo "Random bits pulled from /dev/urandom<br/>\n";
$sRandomBits .= @fread($fp,16);
@fclose($fp);
}
else
{
// MS-Windows platform?
if (@class_exists('COM'))
{
// http://msdn.microsoft.com/en-us/library/aa388176(VS.85).aspx
try
{
$CAPI_Util = new COM('CAPICOM.Utilities.1');
$sBase64RandomBits = ''.$CAPI_Util->GetRandom(16,0);
// if we ask for binary data PHP munges it, so we
// request base64 return value. We squeeze out the
// redundancy and useless ==CRLF by hashing...
if ($sBase64RandomBits)
{
//echo "Random bits got from CAPICOM.Utilities.1<br/>\n";
$sRandomBits = md5($sBase64RandomBits, TRUE);
}
}
catch (Exception $ex)
{
// echo 'Exception: ' . $ex->getMessage();
}
}
}
if ($sRandomBits == null)
{
// No "strong" random generator available, use PHP's built-in mechanism
//echo "Random bits generated from mt_rand<br/>\n";
mt_srand(crc32(microtime()));
$sRandomBits = '';
for($i = 0; $i < 4; $i++)
{
$sRandomBits .= sprintf('%04x', mt_rand(0, 65535));
}
}
return $sRandomBits;
}
}
/**
* Interface for encryption engines
*/
interface CryptEngine
{
public static function GetNewDefaultParams();
function Encrypt($key, $sString);
function Decrypt($key, $encrypted_data);
}
/**
* Simple Engine doesn't need any PHP extension.
* Every encryption of the same string with the same key
* will return the same encrypted string
*/
class SimpleCryptSimpleEngine implements CryptEngine
{
public static function GetNewDefaultParams()
{
return array( 'lib' => 'Simple', 'key' => null);
}
public function Encrypt($key, $sString)
{
$result = '';
for($i=1; $i<=strlen($sString); $i++)
{
$char = substr($sString, $i-1, 1);
$keychar = substr($key, ($i % strlen($key))-1, 1);
$char = chr(ord($char)+ord($keychar));
$result.=$char;
}
return $result;
}
public function Decrypt($key, $encrypted_data)
{
$result = '';
for($i=1; $i<=strlen($encrypted_data); $i++)
{
$char = substr($encrypted_data, $i-1, 1);
$keychar = substr($key, ($i % strlen($key))-1, 1);
$char = chr(ord($char)-ord($keychar));
$result.=$char;
}
return $result;
}
}
/**
* McryptEngine requires Mcrypt extension
* Every encryption of the same string with the same key
* will return a different encrypted string.
*/
class SimpleCryptMcryptEngine implements CryptEngine
{
var $alg = MCRYPT_BLOWFISH;
var $td = null;
public static function GetNewDefaultParams()
{
return array('lib' => 'Mcrypt', 'key' => null);
}
public function __construct()
{
$this->td = mcrypt_module_open($this->alg,'','cbc','');
}
public function Encrypt($key, $sString)
{
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($this->td), MCRYPT_DEV_URANDOM); // MCRYPT_DEV_URANDOM is now useable since itop requires php >= 5.6
if (false === $iv) {
throw new Exception('IV generation failed');
}
mcrypt_generic_init($this->td, $key, $iv);
if (empty($sString))
{
$sString = str_repeat("\0", 8);
}
$encrypted_data = mcrypt_generic($this->td, $sString);
mcrypt_generic_deinit($this->td);
return $iv.$encrypted_data;
}
public function Decrypt($key, $encrypted_data)
{
$iv = substr($encrypted_data, 0, mcrypt_enc_get_iv_size($this->td));
$string = substr($encrypted_data, mcrypt_enc_get_iv_size($this->td));
$r = mcrypt_generic_init($this->td, $key, $iv);
if (($r < 0) || ($r === false))
{
$decrypted_data = Dict::S("Core:AttributeEncryptFailedToDecrypt");
}
else
{
$decrypted_data = rtrim(mdecrypt_generic($this->td, $string), "\0");
mcrypt_generic_deinit($this->td);
}
return $decrypted_data;
}
public function __destruct()
{
mcrypt_module_close($this->td);
}
}
/**
* SodiumEngine requires Sodium extension
* Every encryption of the same string with the same key
* will return a different encrypted string.
* The key has to be SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes long.
*/
class SimpleCryptSodiumEngine implements CryptEngine
{
public static function GetNewDefaultParams()
{
return array('lib' => 'Sodium', 'key' => bin2hex(sodium_crypto_secretbox_keygen()));
}
public function Encrypt($key, $sString)
{
$key = hex2bin($key);
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$encrypted_string = sodium_crypto_secretbox($sString, $nonce, $key);
sodium_memzero($sString);
sodium_memzero($key);
return base64_encode($nonce.$encrypted_string);
}
public function Decrypt($key, $encrypted_data)
{
$key = hex2bin($key);
$encrypted_data = base64_decode($encrypted_data);
$nonce = mb_substr($encrypted_data, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$encrypted_data = mb_substr($encrypted_data, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
$plaintext = sodium_crypto_secretbox_open($encrypted_data, $nonce, $key);
if ($plaintext === false)
{
$plaintext = Dict::S("Core:AttributeEncryptFailedToDecrypt");
}
sodium_memzero($encrypted_data);
sodium_memzero($key);
return $plaintext;
}
}
class SimpleCryptOpenSSLEngine implements CryptEngine
{
public static function GetNewDefaultParams()
{
return array('lib' => 'OpenSSL', 'key' => bin2hex(openssl_random_pseudo_bytes(32)));
}
public function Encrypt($key, $sString)
{
$key = hex2bin($key);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("AES-256-CBC"));
$encrypted_string = openssl_encrypt($sString, "AES-256-CBC", $key, 0 , $iv);
return $iv.$encrypted_string;
}
public function Decrypt($key, $encrypted_data)
{
$key = hex2bin($key);
$iv = mb_substr($encrypted_data, 0, openssl_cipher_iv_length("AES-256-CBC"), '8bit');
$encrypted_data = mb_substr($encrypted_data, openssl_cipher_iv_length("AES-256-CBC"), null, '8bit');
$plaintext = openssl_decrypt($encrypted_data,"AES-256-CBC", $key, 0 , $iv);
if ($plaintext === false)
{
$plaintext = Dict::S("Core:AttributeEncryptFailedToDecrypt");
}
return trim($plaintext);
}
}
class SimpleCryptOpenSSLMcryptCompatibilityEngine implements CryptEngine
{
public static function GetNewDefaultParams()
{
return array('lib' => 'OpenSSLMcryptCompatibility', 'key' => null);
}
//fix for php < 7.1.8 (keys are Zero padded instead of cycle padded)
static private function MakeOpenSSLBlowfishKey($key)
{
if("$key" === '')
{
return $key;
}
$len = (16+2)*4;
while(strlen($key) < $len)
{
$key .= $key;
}
$key = substr($key, 0, $len);
return $key;
}
public function Encrypt($key, $sString)
{
$key = SimpleCryptOpenSSLMcryptCompatibilityEngine::MakeOpenSSLBlowfishKey($key);
$blockSize = 8;
$len = strlen($sString);
$paddingLen = intval (($len + $blockSize -1) / $blockSize) * $blockSize - $len;
$padding = str_repeat("\0", $paddingLen);
$sData = $sString . $padding;
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("BF-CBC"));
$encrypted_string = openssl_encrypt($sData, "BF-CBC", $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
return $iv.$encrypted_string;
}
public function Decrypt($key, $encrypted_data)
{
$key = SimpleCryptOpenSSLMcryptCompatibilityEngine::MakeOpenSSLBlowfishKey($key);
$iv = mb_substr($encrypted_data, 0, openssl_cipher_iv_length("BF-CBC"), '8bit');
$encrypted_data = mb_substr($encrypted_data, openssl_cipher_iv_length("BF-CBC"), null, '8bit');
$plaintext = openssl_decrypt($encrypted_data,"BF-CBC", $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
if ($plaintext === false)
{
$plaintext = Dict::S("Core:AttributeEncryptFailedToDecrypt");
}
return trim($plaintext);
}
}

View File

@@ -1,894 +0,0 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
* 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
*/
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
/**
* A user defined trigger, to customize the application
* A trigger will activate an action
*
* @package iTopORM
*/
abstract class Trigger extends cmdbAbstractObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-conflict.svg'),
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("action_list",
array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "trigger_id", "ext_key_to_remote" => "action_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array())));
$aTags = ContextTag::GetTags();
MetaModel::Init_AddAttribute(new AttributeEnumSet("context", array("allowed_values" => null, "possible_values" => new ValueSetEnumPadded($aTags, true), "sql" => "context", "depends_on" => array(), "is_null_allowed" => true, "max_items" => 12)));
// "complement" is a computed field, fed by Trigger sub-classes, in general in ComputeValues method, for eg. the TriggerOnObject fed it with target_class info
MetaModel::Init_AddAttribute(new AttributeString("complement", array("allowed_values" => null, "sql" => "complement", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("subscription_policy", array("allowed_values" => new ValueSetEnum(Combodo\iTop\Core\Trigger\Enum\SubscriptionPolicy::cases()), "sql" => "subscription_policy", "default_value" => \Combodo\iTop\Core\Trigger\Enum\SubscriptionPolicy::AllowNoChannel->value, "is_null_allowed" => false, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('finalclass', 'description', 'context', 'subscription_policy', 'action_list', 'complement')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'complement')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
/**
* Check if the trigger can be used in the current context
*
* @return bool true if context OK
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function IsContextValid()
{
// Check the context
$oContext = $this->Get('context');
$bChecked = false;
$bValid = false;
foreach ($oContext->GetValues() as $sValue)
{
$bChecked = true;
if (ContextTag::Check($sValue))
{
$bValid = true;
break;
}
}
if ($bChecked && !$bValid)
{
// Trigger does not match the current context
return false;
}
return true;
}
/**
* @param $aContextArgs
*
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function DoActivate($aContextArgs)
{
// Check the context
if (!$this->IsContextValid())
{
// Trigger does not match the current context
$sClass = get_class($this);
$sName = $this->Get('friendlyname');
IssueLog::Debug("Context NOT valid for : {$sClass} '$sName'");
return;
}
$aContextArgs['trigger->object()'] = $this;
// Find the related actions
$oLinkedActions = $this->Get('action_list');
// Order actions as expected
$aActionListOrdered = [];
while ($oLink = $oLinkedActions->Fetch()) {
$aActionListOrdered[(int) $oLink->Get('order')][] = $oLink;
}
ksort($aActionListOrdered);
// Execute actions
foreach ($aActionListOrdered as $aActionSubList) {
foreach ($aActionSubList as $oLink) /** @var \DBObject $oLink */ {
/** @var \DBObject $oLink */
$iActionId = $oLink->Get('action_id');
/** @var \Action $oAction */
$oAction = MetaModel::GetObject('Action', $iActionId);
if ($oAction->IsActive()) {
$oKPI = new ExecutionKPI();
$aContextArgs['action->object()'] = $oAction;
$oAction->DoExecute($this, $aContextArgs);
$oKPI->ComputeStatsForExtension($oAction, 'DoExecute');
}
}
}
}
/**
* Check whether the given object is in the scope of this trigger
* and can potentially be the subject of notifications
*
* @param DBObject $oObject The object to check
*
* @return bool
*/
public function IsInScope(DBObject $oObject)
{
// By default the answer is no
// Overload this function in your own derived class for a different behavior
return false;
}
}
/**
* Class TriggerOnObject
*/
abstract class TriggerOnObject extends Trigger
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onobject",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeClass("target_class",
array("class_category" => "bizmodel", "more_values" => "User,UserExternal,UserInternal,UserLDAP,UserLocal", "sql" => "target_class", "default_value" => null, "is_null_allowed" => false, "depends_on" => array(), "class_exclusion_list" => "Attachment")));
MetaModel::Init_AddAttribute(new AttributeOQL("filter", array("allowed_values" => null, "sql" => "filter", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'description')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('default_search', array('description', 'target_class')); // Default criteria of the search banner
// MetaModel::Init_SetZListItems('standard_search', array('name', 'target_class', 'description')); // Criteria of the search form
}
/**
* @throws \CoreException
*/
public function DoCheckToWrite()
{
parent::DoCheckToWrite();
$sFilter = trim($this->Get('filter') ?? '');
if (strlen($sFilter) > 0)
{
try
{
$oSearch = DBObjectSearch::FromOQL($sFilter);
if (!MetaModel::IsParentClass($this->Get('target_class'), $oSearch->GetClass()))
{
$this->m_aCheckIssues[] = Dict::Format('TriggerOnObject:WrongFilterClass', $this->Get('target_class'));
}
} catch (OqlException $e)
{
$this->m_aCheckIssues[] = Dict::Format('TriggerOnObject:WrongFilterQuery', $e->getMessage());
}
}
}
/**
* @throws \CoreException
*/
public function ComputeValues()
{
parent::ComputeValues();
// Complementary name of a Trigger is manually built
// - the Trigger finalclass code not translated
// - an hardcoded text in english
// - the target class code not translated for TriggerOnObject subclasses
$this->Set('complement', 'class restriction: '.$this->Get('target_class'));
}
/**
* Check whether the given object is in the scope of this trigger
* and can potentially be the subject of notifications
*
* @param DBObject $oObject The object to check
*
* @return bool
* @throws \CoreException
*/
public function IsInScope(DBObject $oObject)
{
$sRootClass = $this->Get('target_class');
return ($oObject instanceof $sRootClass);
}
/**
* @param $aContextArgs
*
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function DoActivate($aContextArgs)
{
$bGo = true;
if (isset($aContextArgs['this->object()']))
{
/** @var \DBObject $oObject */
$oObject = $aContextArgs['this->object()'];
$bGo = $this->IsTargetObject($oObject->GetKey(), $oObject->ListPreviousValuesForUpdatedAttributes());
}
if ($bGo)
{
parent::DoActivate($aContextArgs);
}
}
/**
* if the target class is Attachment, then the trigger is read-only
* @param $sAttCode
* @param $aReasons
* @param $sTargetState
* @return int
* @throws ArchivedObjectException
* @throws CoreException
*/
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState='')
{
// Force the computed field to be read-only, preventing it to be written
if ($this->Get('target_class') == 'Attachment' ) {
return OPT_ATT_READONLY;
}
return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
}
public function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
{
$aHeaderBlocks = parent::DisplayBareHeader($oPage, $bEditMode);
if ($this->Get('target_class') == 'Attachment' ) {
$oPage->AddUiBlock(AlertUIBlockFactory::MakeForWarning('', Dict::S('Class:TriggerOnObject:TriggerClassAttachment/ReadOnlyMessage')));
$oPage->add_ready_script("$('#UIMenuModify').hide();");
}
return $aHeaderBlocks;
}
/**
* Activate trigger based on attribute list given instead of changed attributes
*
* @param array $aContextArgs
* @param array|null $aAttributes if null default to changed attributes
*
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @since 3.1.1 3.2.0 N°6228
*/
public function DoActivateForSpecificAttributes(array $aContextArgs, ?array $aAttributes)
{
if (isset($aContextArgs['this->object()']))
{
/** @var \DBObject $oObject */
$oObject = $aContextArgs['this->object()'];
if (is_null($aAttributes)) {
$aChanges = $oObject->ListPreviousValuesForUpdatedAttributes();
} else {
$aChanges = array_fill_keys($aAttributes, true);
}
if (false === $this->IsTargetObject($oObject->GetKey(), $aChanges)) {
return;
}
}
parent::DoActivate($aContextArgs);
}
/**
* @param $iObjectId
* @param array $aChanges
*
* @return bool True if the object of ID $iObjectId is within the scope of the OQL defined by the "filter" attribute
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
public function IsTargetObject($iObjectId, $aChanges = array())
{
$sFilter = trim($this->Get('filter') ?? '');
if (strlen($sFilter) > 0) {
$oSearch = DBObjectSearch::FromOQL($sFilter);
$oSearch->AddCondition('id', $iObjectId, '=');
$oSearch->AllowAllData();
$oSet = new DBObjectSet($oSearch);
$bRet = ($oSet->Count() > 0);
} else {
$bRet = true;
}
return $bRet;
}
/**
* @param Exception $oException
* @param \DBObject $oObject
*
* @return void
*
* @uses \IssueLog::Error()
*
* @since 2.7.9 3.0.3 3.1.0 N°5893
*/
public function LogException($oException, $oObject)
{
$sObjectKey = $oObject->GetKey(); // if object wasn't persisted yet, then we'll have a negative value
$aContext = [
'exception.class' => get_class($oException),
'exception.message' => $oException->getMessage(),
'trigger.class' => get_class($this),
'trigger.id' => $this->GetKey(),
'trigger.friendlyname' => $this->GetRawName(),
'object.class' => get_class($oObject),
'object.id' => $sObjectKey,
'object.friendlyname' => $oObject->GetRawName(),
'current_user' => UserRights::GetUser(),
'exception.stack' => $oException->getTraceAsString(),
];
IssueLog::Error('A trigger did throw an exception', null, $aContext);
}
}
/**
* To trigger notifications when a ticket is updated from the portal
*/
class TriggerOnPortalUpdate extends TriggerOnObject
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onportalupdate",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'description')); // Attributes to be displayed for a list
// Search criteria
}
}
/**
* Class TriggerOnStateChange
*/
abstract class TriggerOnStateChange extends TriggerOnObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onstatechange",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeClassState("state", array("class_field" => 'target_class', "allowed_values" => null, "sql" => "state", "default_value" => null, "is_null_allowed" => false, "depends_on" => array('target_class'))));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'state', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'state')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class', 'state')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
/**
* Class TriggerOnStateEnter
*/
class TriggerOnStateEnter extends TriggerOnStateChange
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onstateenter",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'state', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('target_class', 'state')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class', 'state')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
/**
* Class TriggerOnStateLeave
*/
class TriggerOnStateLeave extends TriggerOnStateChange
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onstateleave",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'state', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('target_class', 'state')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class', 'state')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('')); // Criteria of the advanced search form
}
}
/**
* Class TriggerOnObjectCreate
*/
class TriggerOnObjectCreate extends TriggerOnObject
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onobjcreate",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
/**
* Class TriggerOnObjectCreate
*/
class TriggerOnObjectDelete extends TriggerOnObject
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onobjdelete",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
/**
* Class TriggerOnObjectCreate
*/
class TriggerOnObjectUpdate extends TriggerOnObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onobjupdate",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeClassAttCodeSet('target_attcodes', array("allowed_values" => null, "class_field" => "target_class", "sql" => "target_attcodes", "default_value" => null, "is_null_allowed" => true, "max_items" => 20, "min_items" => 0, "attribute_definition_exclusion_list" => "AttributeDashboard,AttributeExternalField,AttributeFinalClass,AttributeFriendlyName,AttributeObsolescenceDate,AttributeObsolescenceFlag,AttributeSubItem", "attribute_definition_list" => null, "depends_on" => array('target_class'))));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'target_attcodes', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
}
public function IsTargetObject($iObjectId, $aChanges = array())
{
if (!parent::IsTargetObject($iObjectId, $aChanges))
{
return false;
}
// Check the attribute
$oAttCodeSet = $this->Get('target_attcodes');
$aAttCodes = $oAttCodeSet->GetValues();
if (empty($aAttCodes))
{
return true;
}
foreach($aAttCodes as $sAttCode)
{
if (array_key_exists($sAttCode, $aChanges))
{
return true;
}
}
return false;
}
public function ComputeValues()
{
parent::ComputeValues();
// Remove unwanted attribute codes
$aChanges = $this->ListChanges();
if (isset($aChanges['target_attcodes']))
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), 'target_attcodes');
$aArgs = array('this' => $this);
$aAllowedValues = $oAttDef->GetAllowedValues($aArgs);
/** @var \ormSet $oValue */
$oValue = $this->Get('target_attcodes');
$aValues = $oValue->GetValues();
$bChanged = false;
foreach($aValues as $key => $sValue)
{
if (!isset($aAllowedValues[$sValue]))
{
unset($aValues[$key]);
$bChanged = true;
}
}
if ($bChanged)
{
$oValue->SetValues($aValues);
$this->Set('target_attcodes', $oValue);
}
}
}
}
/**
* Class TriggerOnObjectMention
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @since 3.0.0
*/
class TriggerOnObjectMention extends TriggerOnObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onobjmention",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeOQL("mentioned_filter", array("allowed_values" => null, "sql" => "mentioned_filter", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'mentioned_filter', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
}
/**
* @param \DBObject $oObject
*
* @return bool True if $oObject is within the scope of the OQL defined by the "mentioned_filter" attribute OR if no mentioned_filter defined. Otherwise, returns false.
*
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
public function IsMentionedObjectInScope(DBObject $oObject)
{
$sFilter = trim($this->Get('mentioned_filter'));
if (strlen($sFilter) > 0)
{
$oSearch = DBObjectSearch::FromOQL($sFilter);
$sSearchClass = $oSearch->GetClass();
// If filter not on current object class (or descendants), consider it as not in scope
if (is_a($oObject, $sSearchClass, true) === false) {
return false;
}
$oSearch->AddCondition('id', $oObject->GetKey(), '=');
if (MetaModel::IsAbstract($oSearch->GetClass())) {
$oSearch->AddCondition('finalclass', get_class($oObject), '=');
}
$aParams = $oObject->ToArgs('this');
$oSet = new DBObjectSet($oSearch, [], $aParams);
$bRet = $oSet->CountExceeds(0);
}
else
{
$bRet = true;
}
return $bRet;
}
}
/**
* Class TriggerOnAttributeBlobDownload
*
* @since 3.1.0
*/
class TriggerOnAttributeBlobDownload extends TriggerOnObject
{
/**
* @inheritDoc
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onattblobdownload",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
}
}
/**
* Class lnkTriggerAction
*/
class lnkTriggerAction extends cmdbAbstractObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array('action_id', 'trigger_id'),
"db_table" => "priv_link_action_trigger",
"db_key_field" => "link_id",
"db_finalclass_field" => "",
"is_link" => true,
'uniqueness_rules' => array(
'no_duplicate' => array(
'attributes' => array(
0 => 'action_id',
1 => 'trigger_id',
),
'filter' => '',
'disabled' => false,
'is_blocking' => true,
),
),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass" => "Action", "jointype" => '', "allowed_values" => null, "sql" => "action_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("action_name", array("allowed_values" => null, "extkey_attcode" => 'action_id', "target_attcode" => "name")));
MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass" => "Trigger", "jointype" => '', "allowed_values" => null, "sql" => "trigger_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("trigger_name", array("allowed_values" => null, "extkey_attcode" => 'trigger_id', "target_attcode" => "description")));
MetaModel::Init_AddAttribute(new AttributeInteger("order", array("allowed_values" => null, "sql" => "order", "default_value" => 0, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('action_id', 'trigger_id', 'order')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('action_id', 'trigger_id', 'order')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('action_id', 'trigger_id', 'order')); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', array('action_id', 'trigger_id', 'order')); // Criteria of the advanced search form
}
}
/**
* Class TriggerOnThresholdReached
*/
class TriggerOnThresholdReached extends TriggerOnObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_threshold",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeClassAttCodeSet('stop_watch_code', array("allowed_values" => null, "class_field" => "target_class", "sql" => "stop_watch_code", "default_value" => null, "is_null_allowed" => false, "max_items" => 1, "min_items" => 1, "attribute_definition_exclusion_list" => null, "attribute_definition_list" => "AttributeStopWatch", "include_child_classes_attributes" => true, "depends_on" => array('target_class'))));
MetaModel::Init_AddAttribute(new AttributeString("threshold_index", array("allowed_values" => null, "sql" => "threshold_index", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'stop_watch_code', 'threshold_index', 'filter', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('target_class', 'threshold_index', 'threshold_index')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}

View File

@@ -1,663 +0,0 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// 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/>
/**
* Value set definitions (from a fixed list or from a query, etc.)
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
require_once('MyHelpers.class.inc.php');
/**
* ValueSetDefinition
* value sets API and implementations
*
* @package iTopORM
*/
abstract class ValueSetDefinition
{
protected $m_bIsLoaded = false;
protected $m_aValues = array();
// Displayable description that could be computed out of the std usage context
public function GetValuesDescription()
{
$aValues = $this->GetValues(array(), '');
$aDisplayedValues = array();
foreach($aValues as $key => $value)
{
$aDisplayedValues[] = "$key => $value";
}
$sAllowedValues = implode(', ', $aDisplayedValues);
return $sAllowedValues;
}
/**
* @param array $aArgs
* @param string $sContains
* @param string $sOperation for the values {@see static::LoadValues()}
*
* @return array hash array of keys => values
*/
public function GetValues($aArgs, $sContains = '', $sOperation = 'contains')
{
if (!$this->m_bIsLoaded)
{
$this->LoadValues($aArgs);
$this->m_bIsLoaded = true;
}
if (strlen($sContains) == 0)
{
// No filtering
$aRet = $this->m_aValues;
}
else
{
// Filter on results containing the needle <sContain>
$aRet = array();
foreach ($this->m_aValues as $sKey=>$sValue)
{
if (stripos($sValue, $sContains) !== false)
{
$aRet[$sKey] = $sValue;
}
}
}
$this->SortValues($aRet);
return $aRet;
}
/**
* @param array $aValues Values to sort in the form keys => values
*
* @return void
* @since 3.1.0 N°1646 Create method
*/
public function SortValues(array &$aValues): void
{
// Sort alphabetically on values
natcasesort($aValues);
}
abstract protected function LoadValues($aArgs);
}
/**
* Set of existing values for an attribute, given a search filter
*
* @package iTopORM
*/
class ValueSetObjects extends ValueSetDefinition
{
protected $m_sContains;
protected $m_sOperation;
protected $m_sFilterExpr; // in OQL
protected $m_sValueAttCode;
protected $m_aOrderBy;
protected $m_oExtraCondition;
private $m_bAllowAllData;
private $m_aModifierProperties;
private $m_bSort;
private $m_iLimit;
/**
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
*/
public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array(), $bAllowAllData = false, $aModifierProperties = array())
{
$this->m_sContains = '';
$this->m_sOperation = '';
$this->m_sFilterExpr = $sFilterExp;
$this->m_sValueAttCode = $sValueAttCode;
$this->m_aOrderBy = $aOrderBy;
$this->m_bAllowAllData = $bAllowAllData;
$this->m_aModifierProperties = $aModifierProperties;
$this->m_oExtraCondition = null;
$this->m_bSort = true;
$this->m_iLimit = 0;
}
public function SetModifierProperty($sPluginClass, $sProperty, $value)
{
$this->m_aModifierProperties[$sPluginClass][$sProperty] = $value;
$this->m_bIsLoaded = false;
}
/**
* @deprecated use SetCondition instead
*
* @param \DBSearch $oFilter
*/
public function AddCondition(DBSearch $oFilter)
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use SetCondition instead');
$this->SetCondition($oFilter);
}
public function SetCondition(DBSearch $oFilter)
{
$this->m_oExtraCondition = $oFilter;
$this->m_bIsLoaded = false;
}
public function SetOrderBy(array $aOrderBy)
{
$this->m_aOrderBy = $aOrderBy;
}
public function ToObjectSet($aArgs = array(), $sContains = '', $iAdditionalValue = null)
{
if ($this->m_bAllowAllData)
{
$oFilter = DBObjectSearch::FromOQL_AllData($this->m_sFilterExpr);
}
else
{
$oFilter = DBObjectSearch::FromOQL($this->m_sFilterExpr);
}
if (!is_null($this->m_oExtraCondition))
{
$oFilter = $oFilter->Intersect($this->m_oExtraCondition);
}
foreach($this->m_aModifierProperties as $sPluginClass => $aProperties)
{
foreach ($aProperties as $sProperty => $value)
{
$oFilter->SetModifierProperty($sPluginClass, $sProperty, $value);
}
}
if ($iAdditionalValue > 0)
{
$oSearchAdditionalValue = new DBObjectSearch($oFilter->GetClass());
$oSearchAdditionalValue->AddConditionExpression( new BinaryExpression(
new FieldExpression('id', $oSearchAdditionalValue->GetClassAlias()),
'=',
new VariableExpression('current_extkey_id'))
);
$oSearchAdditionalValue->AllowAllData();
$oSearchAdditionalValue->SetArchiveMode(true);
$oSearchAdditionalValue->SetInternalParams( array('current_extkey_id' => $iAdditionalValue) );
$oFilter = new DBUnionSearch(array($oFilter, $oSearchAdditionalValue));
}
return new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs);
}
/**
* @inheritDoc
* @throws CoreException
* @throws OQLException
*/
public function GetValues($aArgs, $sContains = '', $sOperation = 'contains')
{
if (!$this->m_bIsLoaded || ($sContains != $this->m_sContains) || ($sOperation != $this->m_sOperation))
{
$this->LoadValues($aArgs, $sContains, $sOperation);
$this->m_bIsLoaded = true;
}
// The results are already filtered and sorted (on friendly name)
$aRet = $this->m_aValues;
return $aRet;
}
/**
* @param $aArgs
* @param string $sContains
* @param string $sOperation 'contains' or 'equals_start_with'
*
* @return bool
* @throws \CoreException
* @throws \OQLException
*/
protected function LoadValues($aArgs, $sContains = '', $sOperation = 'contains')
{
$this->m_sContains = $sContains;
$this->m_sOperation = $sOperation;
$this->m_aValues = array();
$oFilter = $this->GetFilter($sOperation, $sContains);
$oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs, null, $this->m_iLimit, 0, $this->m_bSort);
if (empty($this->m_sValueAttCode)) {
$aAttToLoad = array($oFilter->GetClassAlias() => array('friendlyname'));
} else {
$aAttToLoad = array($oFilter->GetClassAlias() => array($this->m_sValueAttCode));
}
$oObjects->OptimizeColumnLoad($aAttToLoad);
while ($oObject = $oObjects->Fetch()) {
if (empty($this->m_sValueAttCode)) {
$this->m_aValues[$oObject->GetKey()] = $oObject->GetName();
} else {
$this->m_aValues[$oObject->GetKey()] = $oObject->Get($this->m_sValueAttCode);
}
}
return true;
}
/**
* Get filter for functions LoadValues and LoadValuesForAutocomplete
*
* @param $sOperation
* @param $sContains
*
* @return \DBObjectSearch|\DBSearch|\DBUnionSearch|false|mixed
* @throws \CoreException
* @throws \OQLException
* @since 3.0.3 3.1.0
*/
protected function GetFilter($sOperation, $sContains)
{
$this->m_sContains = $sContains;
$this->m_sOperation = $sOperation;
if ($this->m_bAllowAllData) {
$oFilter = DBObjectSearch::FromOQL_AllData($this->m_sFilterExpr);
} else {
$oFilter = DBObjectSearch::FromOQL($this->m_sFilterExpr);
$oFilter->SetShowObsoleteData(utils::ShowObsoleteData());
}
if (!$oFilter) {
return false;
}
if (!is_null($this->m_oExtraCondition)) {
$oFilter = $oFilter->Intersect($this->m_oExtraCondition);
}
foreach ($this->m_aModifierProperties as $sPluginClass => $aProperties) {
foreach ($aProperties as $sProperty => $value) {
$oFilter->SetModifierProperty($sPluginClass, $sProperty, $value);
}
}
$sClass = $oFilter->GetClass();
switch ($this->m_sOperation) {
case 'equals':
case 'start_with':
if ($this->m_sOperation === 'start_with') {
$this->m_sContains .= '%';
$sOperator = 'LIKE';
} else {
$sOperator = '=';
}
$aAttributes = MetaModel::GetFriendlyNameAttributeCodeList($sClass);
if (count($aAttributes) > 0) {
$sClassAlias = $oFilter->GetClassAlias();
$aFilters = array();
$oValueExpr = new ScalarExpression($this->m_sContains);
foreach ($aAttributes as $sAttribute) {
$oNewFilter = $oFilter->DeepClone();
$oNameExpr = new FieldExpression($sAttribute, $sClassAlias);
$oCondition = new BinaryExpression($oNameExpr, $sOperator, $oValueExpr);
$oNewFilter->AddConditionExpression($oCondition);
$aFilters[] = $oNewFilter;
}
// Unions are much faster than OR conditions
$oFilter = new DBUnionSearch($aFilters);
} else {
$oValueExpr = new ScalarExpression($this->m_sContains);
$oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias());
$oNewCondition = new BinaryExpression($oNameExpr, $sOperator, $oValueExpr);
$oFilter->AddConditionExpression($oNewCondition);
}
break;
default:
$oValueExpr = new ScalarExpression('%'.$this->m_sContains.'%');
$oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias());
$oNewCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
$oFilter->AddConditionExpression($oNewCondition);
break;
}
return $oFilter;
}
public function GetValuesDescription()
{
return 'Filter: '.$this->m_sFilterExpr;
}
public function GetFilterExpression()
{
return $this->m_sFilterExpr;
}
/**
* @param $iLimit
*/
public function SetLimit($iLimit)
{
$this->m_iLimit = $iLimit;
}
/**
* @param $bSort
*/
public function SetSort($bSort)
{
$this->m_bSort = $bSort;
}
public function GetValuesForAutocomplete($aArgs, $sContains = '', $sOperation = 'contains')
{
if (!$this->m_bIsLoaded || ($sContains != $this->m_sContains) || ($sOperation != $this->m_sOperation))
{
$this->LoadValuesForAutocomplete($aArgs, $sContains, $sOperation);
$this->m_bIsLoaded = true;
}
// The results are already filtered and sorted (on friendly name)
$aRet = $this->m_aValues;
return $aRet;
}
/**
* @param $aArgs
* @param string $sContains
* @param string $sOperation 'contains' or 'equals_start_with'
*
* @return bool
* @throws \CoreException
* @throws \OQLException
*/
protected function LoadValuesForAutocomplete($aArgs, $sContains = '', $sOperation = 'contains')
{
$this->m_aValues = array();
$oFilter = $this->GetFilter($sOperation, $sContains);
$sClass = $oFilter->GetClass();
$sClassAlias = $oFilter->GetClassAlias();
$oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs, null, $this->m_iLimit, 0, $this->m_bSort);
if (empty($this->m_sValueAttCode)) {
$aAttToLoad = ['friendlyname'];
} else {
$aAttToLoad = [$this->m_sValueAttCode];
}
$sImageAttr = MetaModel::GetImageAttributeCode($sClass);
if (!empty($sImageAttr)) {
$aAttToLoad [] = $sImageAttr;
}
$aComplementAttributeSpec = MetaModel::GetNameSpec($sClass, FriendlyNameType::COMPLEMENTARY);
$sFormatAdditionalField = $aComplementAttributeSpec[0];
$aAdditionalField = $aComplementAttributeSpec[1];
if (count($aAdditionalField) > 0) {
if (is_array($aAdditionalField)) {
$aAttToLoad = array_merge($aAttToLoad, $aAdditionalField);
} else {
$aAttToLoad [] = $aAdditionalField;
}
}
$oObjects->OptimizeColumnLoad([$sClassAlias => $aAttToLoad]);
while ($oObject = $oObjects->Fetch()) {
$aData = [];
if (empty($this->m_sValueAttCode)) {
$aData['label'] = $oObject->GetName();
} else {
$aData['label'] = $oObject->Get($this->m_sValueAttCode);
}
if ($oObject->IsObsolete()) {
$aData['obsolescence_flag'] = '1';
} else {
$aData['obsolescence_flag'] = '0';
}
if (count($aAdditionalField) > 0) {
$aArguments = [];
foreach ($aAdditionalField as $sAdditionalField) {
array_push($aArguments, $oObject->Get($sAdditionalField));
}
$aData['additional_field'] = utils::VSprintf($sFormatAdditionalField, $aArguments);
} else {
$aData['additional_field'] = '';
}
if (!empty($sImageAttr)) {
/** @var \ormDocument $oImage */
$oImage = $oObject->Get($sImageAttr);
if (!$oImage->IsEmpty()) {
$aData['picture_url'] = $oImage->GetDisplayURL($sClass, $oObject->GetKey(), $sImageAttr);
$aData['initials'] = '';
} else {
$aData['initials'] = utils::ToAcronym($aData['label']);
}
}
$this->m_aValues[$oObject->GetKey()] = $aData;
}
return true;
}
}
/**
* Fixed set values (could be hardcoded in the business model)
*
* @package iTopORM
*/
class ValueSetEnum extends ValueSetDefinition
{
protected $m_values;
/**
* @var bool $bSortByValues If true, values will be sorted at runtime (on their values, not their keys), otherwise it is sorted at compile time in a predefined order.
* {@see \MFCompiler::CompileAttributeEnumValues()} for complete reasons.
* @since 3.1.0 N°1646
*/
protected bool $bSortByValues;
/**
* @param array|string $Values
* @param bool $bLocalizedSort
*
* @since 3.1.0 N°1646 Add $bLocalizedSort parameter
* @since 3.2.0 N°7157 $Values can be an array of backed-enum cases
*/
public function __construct($Values, bool $bSortByValues = false)
{
$this->m_values = $Values;
$this->bSortByValues = $bSortByValues;
}
/**
* @see \ValueSetEnum::$bSortByValues
* @return bool
* @since 3.1.0 N°1646
*/
public function IsSortedByValues(): bool
{
return $this->bSortByValues;
}
// Helper to export the data model
public function GetValueList()
{
$this->LoadValues(null);
return $this->m_aValues;
}
/**
* @inheritDoc
* @since 3.1.0 N°1646 Overload method
*/
public function SortValues(array &$aValues): void
{
// Force sort by values only if necessary
if ($this->bSortByValues) {
natcasesort($aValues);
return;
}
// Don't sort values as we rely on the order defined during compilation
return;
}
/**
* @param array|string $aArgs
*
* @return true
*/
protected function LoadValues($aArgs)
{
$aValues = [];
if (is_array($this->m_values))
{
foreach ($this->m_values as $key => $value) {
// Handle backed-enum case
if (is_object($value) && enum_exists(get_class($value))) {
$aValues[$value->value] = $value->value;
continue;
}
$aValues[$key] = $value;
}
}
elseif (is_string($this->m_values) && strlen($this->m_values) > 0)
{
foreach (explode(",", $this->m_values) as $sVal)
{
$sVal = trim($sVal);
$sKey = $sVal;
$aValues[$sKey] = $sVal;
}
}
else
{
$aValues = [];
}
$this->m_aValues = $aValues;
return true;
}
}
class ValueSetEnumPadded extends ValueSetEnum
{
/**
* @inheritDoc
* @since 3.1.0 N°6448 Add $bSortByValues parameter
*/
public function __construct($Values, bool $bSortByValues = false)
{
parent::__construct($Values, $bSortByValues);
if (is_string($Values))
{
$this->LoadValues(null);
}
else
{
$this->m_aValues = $Values;
}
$aPaddedValues = array();
foreach ($this->m_aValues as $sKey => $sVal)
{
// Pad keys to the min. length required by the \AttributeSet
$sKey = str_pad($sKey, 3, '_', STR_PAD_LEFT);
$aPaddedValues[$sKey] = $sVal;
}
$this->m_values = $aPaddedValues;
}
}
/**
* Fixed set values, defined as a range: 0..59 (with an optional increment)
*
* @package iTopORM
*/
class ValueSetRange extends ValueSetDefinition
{
protected $m_iStart;
protected $m_iEnd;
public function __construct($iStart, $iEnd, $iStep = 1)
{
$this->m_iStart = $iStart;
$this->m_iEnd = $iEnd;
$this->m_iStep = $iStep;
}
protected function LoadValues($aArgs)
{
$iValue = $this->m_iStart;
for($iValue = $this->m_iStart; $iValue <= $this->m_iEnd; $iValue += $this->m_iStep)
{
$this->m_aValues[$iValue] = $iValue;
}
return true;
}
}
/**
* Data model classes
*
* @package iTopORM
*/
class ValueSetEnumClasses extends ValueSetEnum
{
protected $m_sCategories;
public function __construct($sCategories = '', $sAdditionalValues = '')
{
$this->m_sCategories = $sCategories;
parent::__construct($sAdditionalValues, true /* Classes are always sorted alphabetically */);
}
protected function LoadValues($aArgs)
{
// Call the parent to parse the additional values...
parent::LoadValues($aArgs);
// Translate the labels of the additional values
foreach($this->m_aValues as $sClass => $void)
{
if (MetaModel::IsValidClass($sClass))
{
$this->m_aValues[$sClass] = MetaModel::GetName($sClass);
}
else
{
unset($this->m_aValues[$sClass]);
}
}
// Then, add the classes from the category definition
foreach (MetaModel::GetClasses($this->m_sCategories) as $sClass)
{
if (MetaModel::IsValidClass($sClass))
{
$this->m_aValues[$sClass] = MetaModel::GetName($sClass);
}
else
{
unset($this->m_aValues[$sClass]);
}
}
return true;
}
}

View File

@@ -6,99 +6,99 @@ $vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'AbstractApplicationObjectExtension' => $baseDir . '/application/applicationextension.inc.php',
'AbstractApplicationUIExtension' => $baseDir . '/application/applicationextension.inc.php',
'AbstractLoginFSMExtension' => $baseDir . '/application/applicationextension.inc.php',
'AbstractPageUIBlockExtension' => $baseDir . '/application/applicationextension.inc.php',
'AbstractPortalUIExtension' => $baseDir . '/application/applicationextension.inc.php',
'AbstractPreferencesExtension' => $baseDir . '/application/applicationextension.inc.php',
'AbstractApplicationObjectExtension' => $baseDir . '/application/applicationextension/backoffice/AbstractApplicationObjectExtension.php',
'AbstractApplicationUIExtension' => $baseDir . '/application/applicationextension/backoffice/AbstractApplicationUIExtension.php',
'AbstractLoginFSMExtension' => $baseDir . '/application/applicationextension/login/AbstractLoginFSMExtension.php',
'AbstractPageUIBlockExtension' => $baseDir . '/application/applicationextension/backoffice/AbstractPageUIBlockExtension.php',
'AbstractPortalUIExtension' => $baseDir . '/application/applicationextension/portal/AbstractPortalUIExtension.php',
'AbstractPreferencesExtension' => $baseDir . '/application/applicationextension/backoffice/AbstractPreferencesExtension.php',
'AbstractWeeklyScheduledProcess' => $baseDir . '/core/backgroundprocess.inc.php',
'AbstractWelcomePopupExtension' => $baseDir . '/application/applicationextension.inc.php',
'Action' => $baseDir . '/core/action.class.inc.php',
'AbstractWelcomePopupExtension' => $baseDir . '/application/applicationextension/backoffice/AbstractWelcomePopupExtension.php',
'Action' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/Action.php',
'ActionChecker' => $baseDir . '/core/userrights.class.inc.php',
'ActionEmail' => $baseDir . '/core/action.class.inc.php',
'ActionNotification' => $baseDir . '/core/action.class.inc.php',
'ActionEmail' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/ActionEmail.php',
'ActionNotification' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/ActionNotification.php',
'ApcService' => $baseDir . '/core/apc-service.class.inc.php',
'ApplicationContext' => $baseDir . '/application/applicationcontext.class.inc.php',
'ApplicationException' => $baseDir . '/application/exceptions/ApplicationException.php',
'ApplicationMenu' => $baseDir . '/application/menunode.class.inc.php',
'ApplicationPopupMenuItem' => $baseDir . '/application/applicationextension.inc.php',
'ApplicationPopupMenuItem' => $baseDir . '/application/applicationextension/backoffice/ApplicationPopupMenuItem.php',
'Archive_Tar' => $vendorDir . '/pear/archive_tar/Archive/Tar.php',
'ArchivedObjectException' => $baseDir . '/application/exceptions/ArchivedObjectException.php',
'AsyncSendEmail' => $baseDir . '/core/asynctask.class.inc.php',
'AsyncSendNewsroom' => $baseDir . '/core/asynctask.class.inc.php',
'AsyncTask' => $baseDir . '/core/asynctask.class.inc.php',
'AttributeApplicationLanguage' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeArchiveDate' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeArchiveFlag' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeBlob' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeBoolean' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeCaseLog' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeClass' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeClassAttCodeSet' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeClassState' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeCustomFields' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeDBField' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeDBFieldVoid' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeDashboard' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeDate' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeDateTime' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeDeadline' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeDecimal' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeDefinition' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeDuration' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeEmailAddress' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeEncryptedString' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeEnum' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeEnumSet' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeExternalField' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeExternalKey' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeFinalClass' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeFriendlyName' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeHTML' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeHierarchicalKey' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeIPAddress' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeImage' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeInteger' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeLinkedSet' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeLinkedSetIndirect' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeLongText' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeMetaEnum' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeOQL' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeObjectKey' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeObsolescenceDate' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeObsolescenceFlag' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeOneWayPassword' => $baseDir . '/core/attributedef.class.inc.php',
'AttributePassword' => $baseDir . '/core/attributedef.class.inc.php',
'AttributePercentage' => $baseDir . '/core/attributedef.class.inc.php',
'AttributePhoneNumber' => $baseDir . '/core/attributedef.class.inc.php',
'AttributePropertySet' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeQueryAttCodeSet' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeRedundancySettings' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeSet' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeStopWatch' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeString' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeSubItem' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeTable' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeTagSet' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeTemplateHTML' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeTemplateString' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeTemplateText' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeText' => $baseDir . '/core/attributedef.class.inc.php',
'AttributeURL' => $baseDir . '/core/attributedef.class.inc.php',
'AsyncSendEmail' => $baseDir . '/sources/Core/DataModel/AsyncTask/AsyncSendEmail.php',
'AsyncSendNewsroom' => $baseDir . '/sources/Core/DataModel/AsyncTask/AsyncSendNewsroom.php',
'AsyncTask' => $baseDir . '/sources/Core/DataModel/AsyncTask/AsyncTask.php',
'AttributeApplicationLanguage' => $baseDir . '/sources/Core/AttributeDefinition/AttributeApplicationLanguage.php',
'AttributeArchiveDate' => $baseDir . '/sources/Core/AttributeDefinition/AttributeArchiveDate.php',
'AttributeArchiveFlag' => $baseDir . '/sources/Core/AttributeDefinition/AttributeArchiveFlag.php',
'AttributeBlob' => $baseDir . '/sources/Core/AttributeDefinition/AttributeBlob.php',
'AttributeBoolean' => $baseDir . '/sources/Core/AttributeDefinition/AttributeBoolean.php',
'AttributeCaseLog' => $baseDir . '/sources/Core/AttributeDefinition/AttributeCaseLog.php',
'AttributeClass' => $baseDir . '/sources/Core/AttributeDefinition/AttributeClass.php',
'AttributeClassAttCodeSet' => $baseDir . '/sources/Core/AttributeDefinition/AttributeClassAttCodeSet.php',
'AttributeClassState' => $baseDir . '/sources/Core/AttributeDefinition/AttributeClassState.php',
'AttributeCustomFields' => $baseDir . '/sources/Core/AttributeDefinition/AttributeCustomFields.php',
'AttributeDBField' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDBField.php',
'AttributeDBFieldVoid' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDBFieldVoid.php',
'AttributeDashboard' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDashboard.php',
'AttributeDate' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDate.php',
'AttributeDateTime' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDateTime.php',
'AttributeDeadline' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDeadline.php',
'AttributeDecimal' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDecimal.php',
'AttributeDefinition' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDefinition.php',
'AttributeDuration' => $baseDir . '/sources/Core/AttributeDefinition/AttributeDuration.php',
'AttributeEmailAddress' => $baseDir . '/sources/Core/AttributeDefinition/AttributeEmailAddress.php',
'AttributeEncryptedString' => $baseDir . '/sources/Core/AttributeDefinition/AttributeEncryptedString.php',
'AttributeEnum' => $baseDir . '/sources/Core/AttributeDefinition/AttributeEnum.php',
'AttributeEnumSet' => $baseDir . '/sources/Core/AttributeDefinition/AttributeEnumSet.php',
'AttributeExternalField' => $baseDir . '/sources/Core/AttributeDefinition/AttributeExternalField.php',
'AttributeExternalKey' => $baseDir . '/sources/Core/AttributeDefinition/AttributeExternalKey.php',
'AttributeFinalClass' => $baseDir . '/sources/Core/AttributeDefinition/AttributeFinalClass.php',
'AttributeFriendlyName' => $baseDir . '/sources/Core/AttributeDefinition/AttributeFriendlyName.php',
'AttributeHTML' => $baseDir . '/sources/Core/AttributeDefinition/AttributeHTML.php',
'AttributeHierarchicalKey' => $baseDir . '/sources/Core/AttributeDefinition/AttributeHierarchicalKey.php',
'AttributeIPAddress' => $baseDir . '/sources/Core/AttributeDefinition/AttributeIPAddress.php',
'AttributeImage' => $baseDir . '/sources/Core/AttributeDefinition/AttributeImage.php',
'AttributeInteger' => $baseDir . '/sources/Core/AttributeDefinition/AttributeInteger.php',
'AttributeLinkedSet' => $baseDir . '/sources/Core/AttributeDefinition/AttributeLinkedSet.php',
'AttributeLinkedSetIndirect' => $baseDir . '/sources/Core/AttributeDefinition/AttributeLinkedSetIndirect.php',
'AttributeLongText' => $baseDir . '/sources/Core/AttributeDefinition/AttributeLongText.php',
'AttributeMetaEnum' => $baseDir . '/sources/Core/AttributeDefinition/AttributeMetaEnum.php',
'AttributeOQL' => $baseDir . '/sources/Core/AttributeDefinition/AttributeOQL.php',
'AttributeObjectKey' => $baseDir . '/sources/Core/AttributeDefinition/AttributeObjectKey.php',
'AttributeObsolescenceDate' => $baseDir . '/sources/Core/AttributeDefinition/AttributeObsolescenceDate.php',
'AttributeObsolescenceFlag' => $baseDir . '/sources/Core/AttributeDefinition/AttributeObsolescenceFlag.php',
'AttributeOneWayPassword' => $baseDir . '/sources/Core/AttributeDefinition/AttributeOneWayPassword.php',
'AttributePassword' => $baseDir . '/sources/Core/AttributeDefinition/AttributePassword.php',
'AttributePercentage' => $baseDir . '/sources/Core/AttributeDefinition/AttributePercentage.php',
'AttributePhoneNumber' => $baseDir . '/sources/Core/AttributeDefinition/AttributePhoneNumber.php',
'AttributePropertySet' => $baseDir . '/sources/Core/AttributeDefinition/AttributePropertySet.php',
'AttributeQueryAttCodeSet' => $baseDir . '/sources/Core/AttributeDefinition/AttributeQueryAttCodeSet.php',
'AttributeRedundancySettings' => $baseDir . '/sources/Core/AttributeDefinition/AttributeRedundancySettings.php',
'AttributeSet' => $baseDir . '/sources/Core/AttributeDefinition/AttributeSet.php',
'AttributeStopWatch' => $baseDir . '/sources/Core/AttributeDefinition/AttributeStopWatch.php',
'AttributeString' => $baseDir . '/sources/Core/AttributeDefinition/AttributeString.php',
'AttributeSubItem' => $baseDir . '/sources/Core/AttributeDefinition/AttributeSubItem.php',
'AttributeTable' => $baseDir . '/sources/Core/AttributeDefinition/AttributeTable.php',
'AttributeTagSet' => $baseDir . '/sources/Core/AttributeDefinition/AttributeTagSet.php',
'AttributeTemplateHTML' => $baseDir . '/sources/Core/AttributeDefinition/AttributeTemplateHTML.php',
'AttributeTemplateString' => $baseDir . '/sources/Core/AttributeDefinition/AttributeTemplateString.php',
'AttributeTemplateText' => $baseDir . '/sources/Core/AttributeDefinition/AttributeTemplateText.php',
'AttributeText' => $baseDir . '/sources/Core/AttributeDefinition/AttributeText.php',
'AttributeURL' => $baseDir . '/sources/Core/AttributeDefinition/AttributeURL.php',
'AuditCategory' => $baseDir . '/application/audit.category.class.inc.php',
'AuditDomain' => $baseDir . '/application/audit.domain.class.inc.php',
'AuditRule' => $baseDir . '/application/audit.rule.class.inc.php',
'BackgroundTask' => $baseDir . '/core/backgroundtask.class.inc.php',
'BackgroundTask' => $baseDir . '/sources/Core/DataModel/BackgroundTask.php',
'BinaryExpression' => $baseDir . '/core/oql/expression.class.inc.php',
'BinaryOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php',
'BulkChange' => $baseDir . '/core/bulkchange.class.inc.php',
'BulkChange' => $baseDir . '/sources/Core/BulkChange/BulkChange.php',
'BulkChangeException' => $baseDir . '/application/exceptions/BulkChangeException.php',
'BulkExport' => $baseDir . '/core/bulkexport.class.inc.php',
'BulkExportException' => $baseDir . '/core/bulkexport.class.inc.php',
'BulkExportMissingParameterException' => $baseDir . '/core/bulkexport.class.inc.php',
'BulkExportResult' => $baseDir . '/core/bulkexport.class.inc.php',
'BulkExportResultGC' => $baseDir . '/core/bulkexport.class.inc.php',
'BulkExport' => $baseDir . '/sources/Application/BulkExport/BulkExport.php',
'BulkExportException' => $baseDir . '/sources/Application/BulkExport/BulkExportException.php',
'BulkExportMissingParameterException' => $baseDir . '/sources/Application/BulkExport/BulkExportMissingParameterException.php',
'BulkExportResult' => $baseDir . '/sources/Core/DataModel/BulkExportResult.php',
'BulkExportResultGC' => $baseDir . '/sources/Application/BulkExport/BulkExportResultGC.php',
'CAS_AuthenticationException' => $vendorDir . '/apereo/phpcas/source/CAS/AuthenticationException.php',
'CAS_Client' => $vendorDir . '/apereo/phpcas/source/CAS/Client.php',
'CAS_CookieJar' => $vendorDir . '/apereo/phpcas/source/CAS/CookieJar.php',
@@ -151,25 +151,25 @@ return array(
'CAS_Session_PhpSession' => $vendorDir . '/apereo/phpcas/source/CAS/Session/PhpSession.php',
'CAS_TypeMismatchException' => $vendorDir . '/apereo/phpcas/source/CAS/TypeMismatchException.php',
'CMDBChange' => $baseDir . '/core/cmdbchange.class.inc.php',
'CMDBChangeOp' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpCreate' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpDelete' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpPlugin' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttribute' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeBlob' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeCaseLog' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeCustomFields' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeEncrypted' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeHTML' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeLinks' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeLinksAddRemove' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeLinksTune' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeLongText' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeOneWayPassword' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeScalar' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeTagSet' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeText' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeURL' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOp' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOp.php',
'CMDBChangeOpCreate' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpCreate.php',
'CMDBChangeOpDelete' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpDelete.php',
'CMDBChangeOpPlugin' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpPlugin.php',
'CMDBChangeOpSetAttribute' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttribute.php',
'CMDBChangeOpSetAttributeBlob' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeBlob.php',
'CMDBChangeOpSetAttributeCaseLog' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeCaseLog.php',
'CMDBChangeOpSetAttributeCustomFields' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeCustomFields.php',
'CMDBChangeOpSetAttributeEncrypted' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeEncrypted.php',
'CMDBChangeOpSetAttributeHTML' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeHTML.php',
'CMDBChangeOpSetAttributeLinks' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeLinks.php',
'CMDBChangeOpSetAttributeLinksAddRemove' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeLinksAddRemove.php',
'CMDBChangeOpSetAttributeLinksTune' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeLinksTune.php',
'CMDBChangeOpSetAttributeLongText' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeLongText.php',
'CMDBChangeOpSetAttributeOneWayPassword' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeOneWayPassword.php',
'CMDBChangeOpSetAttributeScalar' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeScalar.php',
'CMDBChangeOpSetAttributeTagSet' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeTagSet.php',
'CMDBChangeOpSetAttributeText' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeText.php',
'CMDBChangeOpSetAttributeURL' => $baseDir . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeURL.php',
'CMDBObject' => $baseDir . '/core/cmdbobject.class.inc.php',
'CMDBObjectSet' => $baseDir . '/core/cmdbobject.class.inc.php',
'CMDBSource' => $baseDir . '/core/cmdbsource.class.inc.php',
@@ -177,16 +177,16 @@ return array(
'CSVParser' => $baseDir . '/core/csvparser.class.inc.php',
'CSVParserException' => $baseDir . '/application/exceptions/CSVParserException.php',
'CURLStringFile' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php',
'CellChangeSpec' => $baseDir . '/core/bulkchange.class.inc.php',
'CellStatus_Ambiguous' => $baseDir . '/core/bulkchange.class.inc.php',
'CellStatus_Issue' => $baseDir . '/core/bulkchange.class.inc.php',
'CellStatus_Modify' => $baseDir . '/core/bulkchange.class.inc.php',
'CellStatus_NullIssue' => $baseDir . '/core/bulkchange.class.inc.php',
'CellStatus_SearchIssue' => $baseDir . '/core/bulkchange.class.inc.php',
'CellStatus_Void' => $baseDir . '/core/bulkchange.class.inc.php',
'CellChangeSpec' => $baseDir . '/sources/Core/BulkChange/CellChangeSpec.php',
'CellStatus_Ambiguous' => $baseDir . '/sources/Core/BulkChange/CellStatus_Ambiguous.php',
'CellStatus_Issue' => $baseDir . '/sources/Core/BulkChange/CellStatus_Issue.php',
'CellStatus_Modify' => $baseDir . '/sources/Core/BulkChange/CellStatus_Modify.php',
'CellStatus_NullIssue' => $baseDir . '/sources/Core/BulkChange/CellStatus_NullIssue.php',
'CellStatus_SearchIssue' => $baseDir . '/sources/Core/BulkChange/CellStatus_SearchIssue.php',
'CellStatus_Void' => $baseDir . '/sources/Core/BulkChange/CellStatus_Void.php',
'CharConcatExpression' => $baseDir . '/core/oql/expression.class.inc.php',
'CharConcatWSExpression' => $baseDir . '/core/oql/expression.class.inc.php',
'CheckStopWatchThresholds' => $baseDir . '/core/ormstopwatch.class.inc.php',
'CheckStopWatchThresholds' => $baseDir . '/sources/Core/Orm/ormStopWatch.php',
'CheckableExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php',
'Combodo\\iTop\\Application\\Branding' => $baseDir . '/sources/Application/Branding.php',
'Combodo\\iTop\\Application\\EventRegister\\ApplicationEvents' => $baseDir . '/sources/Application/EventRegister/ApplicationEvents.php',
@@ -539,13 +539,13 @@ return array(
'CoreTemplateException' => $baseDir . '/application/exceptions/CoreTemplateException.php',
'CoreUnexpectedValue' => $baseDir . '/application/exceptions/CoreUnexpectedValue.php',
'CoreWarning' => $baseDir . '/application/exceptions/CoreWarning.php',
'CryptEngine' => $baseDir . '/core/simplecrypt.class.inc.php',
'CryptEngine' => $baseDir . '/sources/Application/SimpleCrypt/CryptEngine.php',
'CustomFieldsHandler' => $baseDir . '/core/customfieldshandler.class.inc.php',
'DBObject' => $baseDir . '/core/dbobject.class.php',
'DBObjectSearch' => $baseDir . '/core/dbobjectsearch.class.php',
'DBObjectSet' => $baseDir . '/core/dbobjectset.class.php',
'DBObjectSetComparator' => $baseDir . '/core/dbobjectset.class.php',
'DBProperty' => $baseDir . '/core/dbproperty.class.inc.php',
'DBProperty' => $baseDir . '/sources/Core/DataModel/DBProperty.php',
'DBSearch' => $baseDir . '/core/dbsearch.class.php',
'DBSearchHelper' => $baseDir . '/application/DBSearchHelper.php',
'DBUnionSearch' => $baseDir . '/core/dbunionsearch.class.php',
@@ -616,18 +616,18 @@ return array(
'DisplayableNode' => $baseDir . '/core/displayablegraph.class.inc.php',
'DisplayableRedundancyNode' => $baseDir . '/core/displayablegraph.class.inc.php',
'EMail' => $baseDir . '/core/email.class.inc.php',
'Event' => $baseDir . '/core/event.class.inc.php',
'EventIssue' => $baseDir . '/core/event.class.inc.php',
'EventLoginUsage' => $baseDir . '/core/event.class.inc.php',
'EventNotification' => $baseDir . '/core/event.class.inc.php',
'EventNotificationEmail' => $baseDir . '/core/event.class.inc.php',
'EventOnObject' => $baseDir . '/core/event.class.inc.php',
'EventRestService' => $baseDir . '/core/event.class.inc.php',
'EventWebService' => $baseDir . '/core/event.class.inc.php',
'ExcelBulkExport' => $baseDir . '/core/excelbulkexport.class.inc.php',
'Event' => $baseDir . '/sources/Application/DataModel/Event/Event.php',
'EventIssue' => $baseDir . '/sources/Application/DataModel/Event/EventIssue.php',
'EventLoginUsage' => $baseDir . '/sources/Application/DataModel/Event/EventLoginUsage.php',
'EventNotification' => $baseDir . '/sources/Application/DataModel/Event/EventNotification.php',
'EventNotificationEmail' => $baseDir . '/sources/Application/DataModel/Event/EventNotificationEmail.php',
'EventOnObject' => $baseDir . '/sources/Application/DataModel/Event/EventOnObject.php',
'EventRestService' => $baseDir . '/sources/Application/DataModel/Event/EventRestService.php',
'EventWebService' => $baseDir . '/sources/Application/DataModel/Event/EventWebService.php',
'ExcelBulkExport' => $baseDir . '/sources/Application/BulkExport/ExcelBulkExport.php',
'ExcelExporter' => $baseDir . '/application/excelexporter.class.inc.php',
'ExceptionLog' => $baseDir . '/core/log.class.inc.php',
'ExecAsyncTask' => $baseDir . '/core/asynctask.class.inc.php',
'ExecAsyncTask' => $baseDir . '/sources/Core/DataModel/AsyncTask/ExecAsyncTask.php',
'ExecutionKPI' => $baseDir . '/core/kpi.class.inc.php',
'Expression' => $baseDir . '/core/oql/expression.class.inc.php',
'ExpressionCache' => $baseDir . '/core/expressioncache.class.inc.php',
@@ -738,7 +738,7 @@ return array(
'GuzzleHttp\\RetryMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/RetryMiddleware.php',
'GuzzleHttp\\TransferStats' => $vendorDir . '/guzzlehttp/guzzle/src/TransferStats.php',
'GuzzleHttp\\Utils' => $vendorDir . '/guzzlehttp/guzzle/src/Utils.php',
'HTMLBulkExport' => $baseDir . '/core/htmlbulkexport.class.inc.php',
'HTMLBulkExport' => $baseDir . '/sources/Application/BulkExport/HTMLBulkExport.php',
'HTMLDOMSanitizer' => $baseDir . '/core/htmlsanitizer.class.inc.php',
'HTMLNullSanitizer' => $baseDir . '/core/htmlsanitizer.class.inc.php',
'HTMLSanitizer' => $baseDir . '/core/htmlsanitizer.class.inc.php',
@@ -753,10 +753,10 @@ return array(
'InvalidExternalKeyValueException' => $baseDir . '/application/exceptions/InvalidExternalKeyValueException.php',
'InvalidPasswordAttributeOneWayPassword' => $baseDir . '/application/exceptions/InvalidPasswordAttributeOneWayPassword.php',
'IssueLog' => $baseDir . '/core/log.class.inc.php',
'ItopCounter' => $baseDir . '/core/counter.class.inc.php',
'JSButtonItem' => $baseDir . '/application/applicationextension.inc.php',
'JSPopupMenuItem' => $baseDir . '/application/applicationextension.inc.php',
'KeyValueStore' => $baseDir . '/core/counter.class.inc.php',
'ItopCounter' => $baseDir . '/sources/Core/ItopCounter.php',
'JSButtonItem' => $baseDir . '/application/applicationextension/backoffice/JSButtonItem.php',
'JSPopupMenuItem' => $baseDir . '/application/applicationextension/backoffice/JSPopupMenuItem.php',
'KeyValueStore' => $baseDir . '/sources/Core/DataModel/KeyValueStore.php',
'Laminas\\Loader\\AutoloaderFactory' => $vendorDir . '/laminas/laminas-loader/src/AutoloaderFactory.php',
'Laminas\\Loader\\ClassMapAutoloader' => $vendorDir . '/laminas/laminas-loader/src/ClassMapAutoloader.php',
'Laminas\\Loader\\Exception\\BadMethodCallException' => $vendorDir . '/laminas/laminas-loader/src/Exception/BadMethodCallException.php',
@@ -1117,7 +1117,7 @@ return array(
'MenuGroup' => $baseDir . '/application/menunode.class.inc.php',
'MenuNode' => $baseDir . '/application/menunode.class.inc.php',
'MetaModel' => $baseDir . '/core/metamodel.class.php',
'MissingColumnException' => $baseDir . '/core/attributedef.class.inc.php',
'MissingColumnException' => $baseDir . '/sources/Core/AttributeDefinition/MissingColumnException.php',
'MissingQueryArgument' => $baseDir . '/core/oql/expression.class.inc.php',
'ModelReflection' => $baseDir . '/core/modelreflection.class.inc.php',
'ModelReflectionRuntime' => $baseDir . '/core/modelreflection.class.inc.php',
@@ -1170,7 +1170,7 @@ return array(
'OqlUnionQuery' => $baseDir . '/core/oql/oqlquery.class.inc.php',
'Override' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/Override.php',
'PDF417' => $vendorDir . '/tecnickcom/tcpdf/include/barcodes/pdf417.php',
'PDFBulkExport' => $baseDir . '/core/pdfbulkexport.class.inc.php',
'PDFBulkExport' => $baseDir . '/sources/Application/BulkExport/PDFBulkExport.php',
'PEAR' => $vendorDir . '/pear/pear-core-minimal/src/PEAR.php',
'PEAR_ErrorStack' => $vendorDir . '/pear/pear-core-minimal/src/PEAR/ErrorStack.php',
'PEAR_Exception' => $vendorDir . '/pear/pear_exception/PEAR/Exception.php',
@@ -1501,26 +1501,26 @@ return array(
'QueryOQL' => $baseDir . '/application/query.class.inc.php',
'QueryReflection' => $baseDir . '/core/modelreflection.class.inc.php',
'QueryReflectionRuntime' => $baseDir . '/core/modelreflection.class.inc.php',
'RelationEdge' => $baseDir . '/core/relationgraph.class.inc.php',
'RelationGraph' => $baseDir . '/core/relationgraph.class.inc.php',
'RelationObjectNode' => $baseDir . '/core/relationgraph.class.inc.php',
'RelationRedundancyNode' => $baseDir . '/core/relationgraph.class.inc.php',
'RelationEdge' => $baseDir . '/sources/Core/RelationGraph/RelationEdge.php',
'RelationGraph' => $baseDir . '/sources/Core/RelationGraph/RelationGraph.php',
'RelationObjectNode' => $baseDir . '/sources/Core/RelationGraph/RelationObjectNode.php',
'RelationRedundancyNode' => $baseDir . '/sources/Core/RelationGraph/RelationRedundancyNode.php',
'RelationTypeIterator' => $baseDir . '/core/simplegraph.class.inc.php',
'ReportValue' => $baseDir . '/core/bulkchange.class.inc.php',
'ReportValue' => $baseDir . '/sources/Core/BulkChange/ReportValue.php',
'RestDelete' => $baseDir . '/core/restservices.class.inc.php',
'RestResult' => $baseDir . '/application/applicationextension.inc.php',
'RestResult' => $baseDir . '/application/applicationextension/rest/RestResult.php',
'RestResultWithObjects' => $baseDir . '/core/restservices.class.inc.php',
'RestResultWithRelations' => $baseDir . '/core/restservices.class.inc.php',
'RestUtils' => $baseDir . '/application/applicationextension.inc.php',
'RestUtils' => $baseDir . '/application/applicationextension/rest/RestUtils.php',
'ReturnTypeWillChange' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php',
'RotatingLogFileNameBuilder' => $baseDir . '/core/log.class.inc.php',
'RowStatus' => $baseDir . '/core/bulkchange.class.inc.php',
'RowStatus_Disappeared' => $baseDir . '/core/bulkchange.class.inc.php',
'RowStatus_Error' => $baseDir . '/core/bulkchange.class.inc.php',
'RowStatus_Issue' => $baseDir . '/core/bulkchange.class.inc.php',
'RowStatus_Modify' => $baseDir . '/core/bulkchange.class.inc.php',
'RowStatus_NewObj' => $baseDir . '/core/bulkchange.class.inc.php',
'RowStatus_NoChange' => $baseDir . '/core/bulkchange.class.inc.php',
'RowStatus' => $baseDir . '/sources/Core/BulkChange/RowStatus.php',
'RowStatus_Disappeared' => $baseDir . '/sources/Core/BulkChange/RowStatus_Disappeared.php',
'RowStatus_Error' => $baseDir . '/sources/Core/BulkChange/RowStatus_Error.php',
'RowStatus_Issue' => $baseDir . '/sources/Core/BulkChange/RowStatus_Issue.php',
'RowStatus_Modify' => $baseDir . '/sources/Core/BulkChange/RowStatus_Modify.php',
'RowStatus_NewObj' => $baseDir . '/sources/Core/BulkChange/RowStatus_NewObj.php',
'RowStatus_NoChange' => $baseDir . '/sources/Core/BulkChange/RowStatus_NoChange.php',
'RunTimeIconSelectionField' => $baseDir . '/application/forms.class.inc.php',
'RuntimeDashboard' => $baseDir . '/application/dashboard.class.inc.php',
'SQLExpression' => $baseDir . '/core/oql/expression.class.inc.php',
@@ -1624,23 +1624,23 @@ return array(
'ScssPhp\\ScssPhp\\Warn' => $vendorDir . '/scssphp/scssphp/src/Warn.php',
'SearchMenuNode' => $baseDir . '/application/menunode.class.inc.php',
'SecurityException' => $baseDir . '/application/exceptions/SecurityException.php',
'SeparatorPopupMenuItem' => $baseDir . '/application/applicationextension.inc.php',
'SeparatorPopupMenuItem' => $baseDir . '/application/applicationextension/backoffice/SeparatorPopupMenuItem.php',
'SetupLog' => $baseDir . '/core/log.class.inc.php',
'Shortcut' => $baseDir . '/application/shortcut.class.inc.php',
'ShortcutContainerMenuNode' => $baseDir . '/application/menunode.class.inc.php',
'ShortcutMenuNode' => $baseDir . '/application/menunode.class.inc.php',
'ShortcutOQL' => $baseDir . '/application/shortcut.class.inc.php',
'SimpleCrypt' => $baseDir . '/core/simplecrypt.class.inc.php',
'SimpleCryptMcryptEngine' => $baseDir . '/core/simplecrypt.class.inc.php',
'SimpleCryptOpenSSLEngine' => $baseDir . '/core/simplecrypt.class.inc.php',
'SimpleCryptOpenSSLMcryptCompatibilityEngine' => $baseDir . '/core/simplecrypt.class.inc.php',
'SimpleCryptSimpleEngine' => $baseDir . '/core/simplecrypt.class.inc.php',
'SimpleCryptSodiumEngine' => $baseDir . '/core/simplecrypt.class.inc.php',
'SimpleCrypt' => $baseDir . '/sources/Application/SimpleCrypt/SimpleCrypt.php',
'SimpleCryptMcryptEngine' => $baseDir . '/sources/Application/SimpleCrypt/SimpleCryptMcryptEngine.php',
'SimpleCryptOpenSSLEngine' => $baseDir . '/sources/Application/SimpleCrypt/SimpleCryptOpenSSLEngine.php',
'SimpleCryptOpenSSLMcryptCompatibilityEngine' => $baseDir . '/sources/Application/SimpleCrypt/SimpleCryptOpenSSLMcryptCompatibilityEngine.php',
'SimpleCryptSimpleEngine' => $baseDir . '/sources/Application/SimpleCrypt/SimpleCryptSimpleEngine.php',
'SimpleCryptSodiumEngine' => $baseDir . '/sources/Application/SimpleCrypt/SimpleCryptSodiumEngine.php',
'SimpleGraph' => $baseDir . '/core/simplegraph.class.inc.php',
'SimpleGraphException' => $baseDir . '/core/simplegraph.class.inc.php',
'Soundasleep\\Html2Text' => $vendorDir . '/soundasleep/html2text/src/Html2Text.php',
'Soundasleep\\Html2TextException' => $vendorDir . '/soundasleep/html2text/src/Html2TextException.php',
'SpreadsheetBulkExport' => $baseDir . '/core/spreadsheetbulkexport.class.inc.php',
'SpreadsheetBulkExport' => $baseDir . '/sources/Application/BulkExport/SpreadsheetBulkExport.php',
'StimulusChecker' => $baseDir . '/core/userrights.class.inc.php',
'StimulusInternal' => $baseDir . '/core/stimulus.class.inc.php',
'StimulusUserAction' => $baseDir . '/core/stimulus.class.inc.php',
@@ -2916,12 +2916,12 @@ return array(
'TCPDF_IMPORT' => $vendorDir . '/tecnickcom/tcpdf/tcpdf_import.php',
'TCPDF_PARSER' => $vendorDir . '/tecnickcom/tcpdf/tcpdf_parser.php',
'TCPDF_STATIC' => $vendorDir . '/tecnickcom/tcpdf/include/tcpdf_static.php',
'TabularBulkExport' => $baseDir . '/core/tabularbulkexport.class.inc.php',
'TagSetFieldData' => $baseDir . '/core/tagsetfield.class.inc.php',
'TabularBulkExport' => $baseDir . '/sources/Application/BulkExport/TabularBulkExport.php',
'TagSetFieldData' => $baseDir . '/sources/Core/DataModel/TagSetFieldData.php',
'TemplateMenuNode' => $baseDir . '/application/menunode.class.inc.php',
'TemplateString' => $baseDir . '/core/templatestring.class.inc.php',
'TemplateStringPlaceholder' => $baseDir . '/core/templatestring.class.inc.php',
'TemporaryObjectDescriptor' => $baseDir . '/core/TemporaryObjectDescriptor.php',
'TemporaryObjectDescriptor' => $baseDir . '/sources/Core/DataModel/TemporaryObjectDescriptor.php',
'TheNetworg\\OAuth2\\Client\\Grant\\JwtBearer' => $vendorDir . '/thenetworg/oauth2-azure/src/Grant/JwtBearer.php',
'TheNetworg\\OAuth2\\Client\\Provider\\Azure' => $vendorDir . '/thenetworg/oauth2-azure/src/Provider/Azure.php',
'TheNetworg\\OAuth2\\Client\\Provider\\AzureResourceOwner' => $vendorDir . '/thenetworg/oauth2-azure/src/Provider/AzureResourceOwner.php',
@@ -2929,18 +2929,18 @@ return array(
'ThemeHandler' => $baseDir . '/application/themehandler.class.inc.php',
'ThemeHandlerService' => $baseDir . '/application/themehandlerservice.class.inc.php',
'ToolsLog' => $baseDir . '/core/log.class.inc.php',
'Trigger' => $baseDir . '/core/trigger.class.inc.php',
'TriggerOnAttributeBlobDownload' => $baseDir . '/core/trigger.class.inc.php',
'TriggerOnObject' => $baseDir . '/core/trigger.class.inc.php',
'TriggerOnObjectCreate' => $baseDir . '/core/trigger.class.inc.php',
'TriggerOnObjectDelete' => $baseDir . '/core/trigger.class.inc.php',
'TriggerOnObjectMention' => $baseDir . '/core/trigger.class.inc.php',
'TriggerOnObjectUpdate' => $baseDir . '/core/trigger.class.inc.php',
'TriggerOnPortalUpdate' => $baseDir . '/core/trigger.class.inc.php',
'TriggerOnStateChange' => $baseDir . '/core/trigger.class.inc.php',
'TriggerOnStateEnter' => $baseDir . '/core/trigger.class.inc.php',
'TriggerOnStateLeave' => $baseDir . '/core/trigger.class.inc.php',
'TriggerOnThresholdReached' => $baseDir . '/core/trigger.class.inc.php',
'Trigger' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/Trigger.php',
'TriggerOnAttributeBlobDownload' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/TriggerOnAttributeBlobDownload.php',
'TriggerOnObject' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/TriggerOnObject.php',
'TriggerOnObjectCreate' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/TriggerOnObjectCreate.php',
'TriggerOnObjectDelete' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/TriggerOnObjectDelete.php',
'TriggerOnObjectMention' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/TriggerOnObjectMention.php',
'TriggerOnObjectUpdate' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/TriggerOnObjectUpdate.php',
'TriggerOnPortalUpdate' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/TriggerOnPortalUpdate.php',
'TriggerOnStateChange' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/TriggerOnStateChange.php',
'TriggerOnStateEnter' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/TriggerOnStateEnter.php',
'TriggerOnStateLeave' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/TriggerOnStateLeave.php',
'TriggerOnThresholdReached' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/TriggerOnThresholdReached.php',
'TrueExpression' => $baseDir . '/core/oql/expression.class.inc.php',
'Twig\\AbstractTwigCallable' => $vendorDir . '/twig/twig/src/AbstractTwigCallable.php',
'Twig\\Attribute\\FirstClassTwigCallableReady' => $vendorDir . '/twig/twig/src/Attribute/FirstClassTwigCallableReady.php',
@@ -3156,8 +3156,8 @@ return array(
'UIPasswordWidget' => $baseDir . '/application/ui.passwordwidget.class.inc.php',
'UISearchFormForeignKeys' => $baseDir . '/application/ui.searchformforeignkeys.class.inc.php',
'UIWizard' => $baseDir . '/application/uiwizard.class.inc.php',
'URLButtonItem' => $baseDir . '/application/applicationextension.inc.php',
'URLPopupMenuItem' => $baseDir . '/application/applicationextension.inc.php',
'URLButtonItem' => $baseDir . '/application/applicationextension/backoffice/URLButtonItem.php',
'URLPopupMenuItem' => $baseDir . '/application/applicationextension/backoffice/URLPopupMenuItem.php',
'UnaryExpression' => $baseDir . '/core/oql/expression.class.inc.php',
'UnknownClassOqlException' => $baseDir . '/core/oql/oqlinterpreter.class.inc.php',
'User' => $baseDir . '/core/userrights.class.inc.php',
@@ -3166,12 +3166,12 @@ return array(
'UserRightException' => $baseDir . '/application/exceptions/UserRightException.php',
'UserRights' => $baseDir . '/core/userrights.class.inc.php',
'UserRightsAddOnAPI' => $baseDir . '/core/userrights.class.inc.php',
'ValueSetDefinition' => $baseDir . '/core/valuesetdef.class.inc.php',
'ValueSetEnum' => $baseDir . '/core/valuesetdef.class.inc.php',
'ValueSetEnumClasses' => $baseDir . '/core/valuesetdef.class.inc.php',
'ValueSetEnumPadded' => $baseDir . '/core/valuesetdef.class.inc.php',
'ValueSetObjects' => $baseDir . '/core/valuesetdef.class.inc.php',
'ValueSetRange' => $baseDir . '/core/valuesetdef.class.inc.php',
'ValueSetDefinition' => $baseDir . '/sources/Core/ValueSetDefinition/ValueSetDefinition.php',
'ValueSetEnum' => $baseDir . '/sources/Core/ValueSetDefinition/ValueSetEnum.php',
'ValueSetEnumClasses' => $baseDir . '/sources/Core/ValueSetDefinition/ValueSetEnumClasses.php',
'ValueSetEnumPadded' => $baseDir . '/sources/Core/ValueSetDefinition/ValueSetEnumPadded.php',
'ValueSetObjects' => $baseDir . '/sources/Core/ValueSetDefinition/ValueSetObjects.php',
'ValueSetRange' => $baseDir . '/sources/Core/ValueSetDefinition/ValueSetRange.php',
'VariableExpression' => $baseDir . '/core/oql/expression.class.inc.php',
'VariableOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php',
'WebPageMenuNode' => $baseDir . '/application/menunode.class.inc.php',
@@ -3181,48 +3181,48 @@ return array(
'WeeklyRotatingLogFileNameBuilder' => $baseDir . '/core/log.class.inc.php',
'WizardHelper' => $baseDir . '/application/wizardhelper.class.inc.php',
'XLSXWriter' => $baseDir . '/application/xlsxwriter.class.php',
'XMLBulkExport' => $baseDir . '/core/xmlbulkexport.class.inc.php',
'XMLBulkExport' => $baseDir . '/sources/Application/BulkExport/XMLBulkExport.php',
'appUserPreferences' => $baseDir . '/application/user.preferences.class.inc.php',
'cmdbAbstractObject' => $baseDir . '/application/cmdbabstract.class.inc.php',
'cmdbDataGenerator' => $baseDir . '/core/data.generator.class.inc.php',
'iApplicationObjectExtension' => $baseDir . '/application/applicationextension.inc.php',
'iApplicationUIExtension' => $baseDir . '/application/applicationextension.inc.php',
'iAttributeNoGroupBy' => $baseDir . '/core/attributedef.class.inc.php',
'iApplicationObjectExtension' => $baseDir . '/application/applicationextension/backoffice/iApplicationObjectExtension.php',
'iApplicationUIExtension' => $baseDir . '/application/applicationextension/backoffice/iApplicationUIExtension.php',
'iAttributeNoGroupBy' => $baseDir . '/sources/Core/AttributeDefinition/iAttributeNoGroupBy.php',
'iBackgroundProcess' => $baseDir . '/core/backgroundprocess.inc.php',
'iBackofficeDictEntriesExtension' => $baseDir . '/application/applicationextension.inc.php',
'iBackofficeDictEntriesPrefixesExtension' => $baseDir . '/application/applicationextension.inc.php',
'iBackofficeEarlyScriptExtension' => $baseDir . '/application/applicationextension.inc.php',
'iBackofficeInitScriptExtension' => $baseDir . '/application/applicationextension.inc.php',
'iBackofficeLinkedScriptsExtension' => $baseDir . '/application/applicationextension.inc.php',
'iBackofficeLinkedStylesheetsExtension' => $baseDir . '/application/applicationextension.inc.php',
'iBackofficeReadyScriptExtension' => $baseDir . '/application/applicationextension.inc.php',
'iBackofficeSassExtension' => $baseDir . '/application/applicationextension.inc.php',
'iBackofficeScriptExtension' => $baseDir . '/application/applicationextension.inc.php',
'iBackofficeStyleExtension' => $baseDir . '/application/applicationextension.inc.php',
'iBackupExtraFilesExtension' => $baseDir . '/application/applicationextension.inc.php',
'iCMDBChangeOp' => $baseDir . '/core/cmdbchangeop.class.inc.php',
'iBackofficeDictEntriesExtension' => $baseDir . '/application/applicationextension/backoffice/iBackofficeDictEntriesExtension.php',
'iBackofficeDictEntriesPrefixesExtension' => $baseDir . '/application/applicationextension/backoffice/iBackofficeDictEntriesPrefixesExtension.php',
'iBackofficeEarlyScriptExtension' => $baseDir . '/application/applicationextension/backoffice/iBackofficeEarlyScriptExtension.php',
'iBackofficeInitScriptExtension' => $baseDir . '/application/applicationextension/backoffice/iBackofficeInitScriptExtension.php',
'iBackofficeLinkedScriptsExtension' => $baseDir . '/application/applicationextension/backoffice/iBackofficeLinkedScriptsExtension.php',
'iBackofficeLinkedStylesheetsExtension' => $baseDir . '/application/applicationextension/backoffice/iBackofficeLinkedStylesheetsExtension.php',
'iBackofficeReadyScriptExtension' => $baseDir . '/application/applicationextension/backoffice/iBackofficeReadyScriptExtension.php',
'iBackofficeSassExtension' => $baseDir . '/application/applicationextension/backoffice/iBackofficeSassExtension.php',
'iBackofficeScriptExtension' => $baseDir . '/application/applicationextension/backoffice/iBackofficeScriptExtension.php',
'iBackofficeStyleExtension' => $baseDir . '/application/applicationextension/backoffice/iBackofficeStyleExtension.php',
'iBackupExtraFilesExtension' => $baseDir . '/application/applicationextension/iBackupExtraFilesExtension.php',
'iCMDBChangeOp' => $baseDir . '/sources/Core/DataModel/CMDBChange/iCMDBChangeOp.php',
'iDBObjectSetIterator' => $baseDir . '/core/dbobjectiterator.php',
'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',
'iFieldRendererMappingsExtension' => $baseDir . '/application/applicationextension/backoffice/iFieldRendererMappingsExtension.php',
'iKPILoggerExtension' => $baseDir . '/application/applicationextension/iKPILoggerExtension.php',
'iLogFileNameBuilder' => $baseDir . '/core/log.class.inc.php',
'iLoginExtension' => $baseDir . '/application/applicationextension.inc.php',
'iLoginFSMExtension' => $baseDir . '/application/applicationextension.inc.php',
'iLoginUIExtension' => $baseDir . '/application/applicationextension.inc.php',
'iLogoutExtension' => $baseDir . '/application/applicationextension.inc.php',
'iLoginExtension' => $baseDir . '/application/applicationextension/login/iLoginExtension.php',
'iLoginFSMExtension' => $baseDir . '/application/applicationextension/login/iLoginFSMExtension.php',
'iLoginUIExtension' => $baseDir . '/application/applicationextension/login/iLoginUIExtension.php',
'iLogoutExtension' => $baseDir . '/application/applicationextension/login/iLogoutExtension.php',
'iMetricComputer' => $baseDir . '/core/computing.inc.php',
'iModuleExtension' => $baseDir . '/application/applicationextension.inc.php',
'iModuleExtension' => $baseDir . '/application/applicationextension/iModuleExtension.php',
'iNewsroomProvider' => $baseDir . '/application/newsroomprovider.class.inc.php',
'iOnClassInitialization' => $baseDir . '/core/metamodelmodifier.inc.php',
'iPageUIBlockExtension' => $baseDir . '/application/applicationextension.inc.php',
'iPopupMenuExtension' => $baseDir . '/application/applicationextension.inc.php',
'iPortalUIExtension' => $baseDir . '/application/applicationextension.inc.php',
'iPreferencesExtension' => $baseDir . '/application/applicationextension.inc.php',
'iPageUIBlockExtension' => $baseDir . '/application/applicationextension/backoffice/iPageUIBlockExtension.php',
'iPopupMenuExtension' => $baseDir . '/application/applicationextension/backoffice/iPopupMenuExtension.php',
'iPortalUIExtension' => $baseDir . '/application/applicationextension/portal/iPortalUIExtension.php',
'iPreferencesExtension' => $baseDir . '/application/applicationextension/backoffice/iPreferencesExtension.php',
'iProcess' => $baseDir . '/core/backgroundprocess.inc.php',
'iQueryModifier' => $baseDir . '/core/querymodifier.class.inc.php',
'iRestInputSanitizer' => $baseDir . '/application/applicationextension.inc.php',
'iRestServiceProvider' => $baseDir . '/application/applicationextension.inc.php',
'iRestInputSanitizer' => $baseDir . '/application/applicationextension/rest/iRestInputSanitizer.php',
'iRestServiceProvider' => $baseDir . '/application/applicationextension/rest/iRestServiceProvider.php',
'iScheduledProcess' => $baseDir . '/core/backgroundprocess.inc.php',
'iSelfRegister' => $baseDir . '/core/userrights.class.inc.php',
'iTopConfigParser' => $baseDir . '/core/iTopConfigParser.php',
@@ -3231,19 +3231,19 @@ return array(
'iTopOwnershipToken' => $baseDir . '/core/ownershiplock.class.inc.php',
'iTopStandardURLMaker' => $baseDir . '/application/applicationcontext.class.inc.php',
'iTopXmlException' => $baseDir . '/application/exceptions/iTopXmlException.php',
'iWelcomePopupExtension' => $baseDir . '/application/applicationextension.inc.php',
'iWelcomePopupExtension' => $baseDir . '/application/applicationextension/backoffice/iWelcomePopupExtension.php',
'iWorkingTimeComputer' => $baseDir . '/core/computing.inc.php',
'lnkAuditCategoryToAuditDomain' => $baseDir . '/application/audit.domain.class.inc.php',
'lnkTriggerAction' => $baseDir . '/core/trigger.class.inc.php',
'ormCaseLog' => $baseDir . '/core/ormcaselog.class.inc.php',
'ormCustomFieldsValue' => $baseDir . '/core/ormcustomfieldsvalue.class.inc.php',
'ormDocument' => $baseDir . '/core/ormdocument.class.inc.php',
'ormLinkSet' => $baseDir . '/core/ormlinkset.class.inc.php',
'ormPassword' => $baseDir . '/core/ormpassword.class.inc.php',
'ormSet' => $baseDir . '/core/ormset.class.inc.php',
'ormStopWatch' => $baseDir . '/core/ormstopwatch.class.inc.php',
'ormStyle' => $baseDir . '/core/ormStyle.class.inc.php',
'ormTagSet' => $baseDir . '/core/ormtagset.class.inc.php',
'lnkTriggerAction' => $baseDir . '/sources/Application/DataModel/TriggerAndAction/lnkTriggerAction.php',
'ormCaseLog' => $baseDir . '/sources/Core/Orm/ormCaseLog.php',
'ormCustomFieldsValue' => $baseDir . '/sources/Core/Orm/ormCustomFieldsValue.php',
'ormDocument' => $baseDir . '/sources/Core/Orm/ormDocument.php',
'ormLinkSet' => $baseDir . '/sources/Core/Orm/ormLinkSet.php',
'ormPassword' => $baseDir . '/sources/Core/Orm/ormPassword.php',
'ormSet' => $baseDir . '/sources/Core/Orm/ormSet.php',
'ormStopWatch' => $baseDir . '/sources/Core/Orm/ormStopWatch.php',
'ormStyle' => $baseDir . '/sources/Core/Orm/ormStyle.php',
'ormTagSet' => $baseDir . '/sources/Core/Orm/ormTagSet.php',
'phpCAS' => $vendorDir . '/apereo/phpcas/source/CAS.php',
'privUITransaction' => $baseDir . '/application/transaction.class.inc.php',
'privUITransactionFile' => $baseDir . '/application/transaction.class.inc.php',

View File

@@ -384,99 +384,99 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
);
public static $classMap = array (
'AbstractApplicationObjectExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'AbstractApplicationUIExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'AbstractLoginFSMExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'AbstractPageUIBlockExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'AbstractPortalUIExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'AbstractPreferencesExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'AbstractApplicationObjectExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/AbstractApplicationObjectExtension.php',
'AbstractApplicationUIExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/AbstractApplicationUIExtension.php',
'AbstractLoginFSMExtension' => __DIR__ . '/../..' . '/application/applicationextension/login/AbstractLoginFSMExtension.php',
'AbstractPageUIBlockExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/AbstractPageUIBlockExtension.php',
'AbstractPortalUIExtension' => __DIR__ . '/../..' . '/application/applicationextension/portal/AbstractPortalUIExtension.php',
'AbstractPreferencesExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/AbstractPreferencesExtension.php',
'AbstractWeeklyScheduledProcess' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.php',
'AbstractWelcomePopupExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'Action' => __DIR__ . '/../..' . '/core/action.class.inc.php',
'AbstractWelcomePopupExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/AbstractWelcomePopupExtension.php',
'Action' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/Action.php',
'ActionChecker' => __DIR__ . '/../..' . '/core/userrights.class.inc.php',
'ActionEmail' => __DIR__ . '/../..' . '/core/action.class.inc.php',
'ActionNotification' => __DIR__ . '/../..' . '/core/action.class.inc.php',
'ActionEmail' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/ActionEmail.php',
'ActionNotification' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/ActionNotification.php',
'ApcService' => __DIR__ . '/../..' . '/core/apc-service.class.inc.php',
'ApplicationContext' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php',
'ApplicationException' => __DIR__ . '/../..' . '/application/exceptions/ApplicationException.php',
'ApplicationMenu' => __DIR__ . '/../..' . '/application/menunode.class.inc.php',
'ApplicationPopupMenuItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'ApplicationPopupMenuItem' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/ApplicationPopupMenuItem.php',
'Archive_Tar' => __DIR__ . '/..' . '/pear/archive_tar/Archive/Tar.php',
'ArchivedObjectException' => __DIR__ . '/../..' . '/application/exceptions/ArchivedObjectException.php',
'AsyncSendEmail' => __DIR__ . '/../..' . '/core/asynctask.class.inc.php',
'AsyncSendNewsroom' => __DIR__ . '/../..' . '/core/asynctask.class.inc.php',
'AsyncTask' => __DIR__ . '/../..' . '/core/asynctask.class.inc.php',
'AttributeApplicationLanguage' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeArchiveDate' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeArchiveFlag' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeBlob' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeBoolean' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeCaseLog' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeClass' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeClassAttCodeSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeClassState' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeCustomFields' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeDBField' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeDBFieldVoid' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeDashboard' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeDate' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeDateTime' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeDeadline' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeDecimal' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeDefinition' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeDuration' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeEmailAddress' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeEncryptedString' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeEnum' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeEnumSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeExternalField' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeExternalKey' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeFinalClass' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeFriendlyName' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeHTML' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeHierarchicalKey' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeIPAddress' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeImage' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeInteger' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeLinkedSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeLinkedSetIndirect' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeLongText' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeMetaEnum' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeOQL' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeObjectKey' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeObsolescenceDate' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeObsolescenceFlag' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeOneWayPassword' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributePassword' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributePercentage' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributePhoneNumber' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributePropertySet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeQueryAttCodeSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeRedundancySettings' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeStopWatch' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeString' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeSubItem' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeTable' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeTagSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeTemplateHTML' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeTemplateString' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeTemplateText' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeText' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AttributeURL' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'AsyncSendEmail' => __DIR__ . '/../..' . '/sources/Core/DataModel/AsyncTask/AsyncSendEmail.php',
'AsyncSendNewsroom' => __DIR__ . '/../..' . '/sources/Core/DataModel/AsyncTask/AsyncSendNewsroom.php',
'AsyncTask' => __DIR__ . '/../..' . '/sources/Core/DataModel/AsyncTask/AsyncTask.php',
'AttributeApplicationLanguage' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeApplicationLanguage.php',
'AttributeArchiveDate' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeArchiveDate.php',
'AttributeArchiveFlag' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeArchiveFlag.php',
'AttributeBlob' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeBlob.php',
'AttributeBoolean' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeBoolean.php',
'AttributeCaseLog' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeCaseLog.php',
'AttributeClass' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeClass.php',
'AttributeClassAttCodeSet' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeClassAttCodeSet.php',
'AttributeClassState' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeClassState.php',
'AttributeCustomFields' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeCustomFields.php',
'AttributeDBField' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDBField.php',
'AttributeDBFieldVoid' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDBFieldVoid.php',
'AttributeDashboard' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDashboard.php',
'AttributeDate' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDate.php',
'AttributeDateTime' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDateTime.php',
'AttributeDeadline' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDeadline.php',
'AttributeDecimal' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDecimal.php',
'AttributeDefinition' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDefinition.php',
'AttributeDuration' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeDuration.php',
'AttributeEmailAddress' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeEmailAddress.php',
'AttributeEncryptedString' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeEncryptedString.php',
'AttributeEnum' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeEnum.php',
'AttributeEnumSet' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeEnumSet.php',
'AttributeExternalField' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeExternalField.php',
'AttributeExternalKey' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeExternalKey.php',
'AttributeFinalClass' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeFinalClass.php',
'AttributeFriendlyName' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeFriendlyName.php',
'AttributeHTML' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeHTML.php',
'AttributeHierarchicalKey' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeHierarchicalKey.php',
'AttributeIPAddress' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeIPAddress.php',
'AttributeImage' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeImage.php',
'AttributeInteger' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeInteger.php',
'AttributeLinkedSet' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeLinkedSet.php',
'AttributeLinkedSetIndirect' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeLinkedSetIndirect.php',
'AttributeLongText' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeLongText.php',
'AttributeMetaEnum' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeMetaEnum.php',
'AttributeOQL' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeOQL.php',
'AttributeObjectKey' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeObjectKey.php',
'AttributeObsolescenceDate' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeObsolescenceDate.php',
'AttributeObsolescenceFlag' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeObsolescenceFlag.php',
'AttributeOneWayPassword' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeOneWayPassword.php',
'AttributePassword' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributePassword.php',
'AttributePercentage' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributePercentage.php',
'AttributePhoneNumber' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributePhoneNumber.php',
'AttributePropertySet' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributePropertySet.php',
'AttributeQueryAttCodeSet' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeQueryAttCodeSet.php',
'AttributeRedundancySettings' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeRedundancySettings.php',
'AttributeSet' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeSet.php',
'AttributeStopWatch' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeStopWatch.php',
'AttributeString' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeString.php',
'AttributeSubItem' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeSubItem.php',
'AttributeTable' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeTable.php',
'AttributeTagSet' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeTagSet.php',
'AttributeTemplateHTML' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeTemplateHTML.php',
'AttributeTemplateString' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeTemplateString.php',
'AttributeTemplateText' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeTemplateText.php',
'AttributeText' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeText.php',
'AttributeURL' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/AttributeURL.php',
'AuditCategory' => __DIR__ . '/../..' . '/application/audit.category.class.inc.php',
'AuditDomain' => __DIR__ . '/../..' . '/application/audit.domain.class.inc.php',
'AuditRule' => __DIR__ . '/../..' . '/application/audit.rule.class.inc.php',
'BackgroundTask' => __DIR__ . '/../..' . '/core/backgroundtask.class.inc.php',
'BackgroundTask' => __DIR__ . '/../..' . '/sources/Core/DataModel/BackgroundTask.php',
'BinaryExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php',
'BinaryOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php',
'BulkChange' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'BulkChange' => __DIR__ . '/../..' . '/sources/Core/BulkChange/BulkChange.php',
'BulkChangeException' => __DIR__ . '/../..' . '/application/exceptions/BulkChangeException.php',
'BulkExport' => __DIR__ . '/../..' . '/core/bulkexport.class.inc.php',
'BulkExportException' => __DIR__ . '/../..' . '/core/bulkexport.class.inc.php',
'BulkExportMissingParameterException' => __DIR__ . '/../..' . '/core/bulkexport.class.inc.php',
'BulkExportResult' => __DIR__ . '/../..' . '/core/bulkexport.class.inc.php',
'BulkExportResultGC' => __DIR__ . '/../..' . '/core/bulkexport.class.inc.php',
'BulkExport' => __DIR__ . '/../..' . '/sources/Application/BulkExport/BulkExport.php',
'BulkExportException' => __DIR__ . '/../..' . '/sources/Application/BulkExport/BulkExportException.php',
'BulkExportMissingParameterException' => __DIR__ . '/../..' . '/sources/Application/BulkExport/BulkExportMissingParameterException.php',
'BulkExportResult' => __DIR__ . '/../..' . '/sources/Core/DataModel/BulkExportResult.php',
'BulkExportResultGC' => __DIR__ . '/../..' . '/sources/Application/BulkExport/BulkExportResultGC.php',
'CAS_AuthenticationException' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS/AuthenticationException.php',
'CAS_Client' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS/Client.php',
'CAS_CookieJar' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS/CookieJar.php',
@@ -529,25 +529,25 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'CAS_Session_PhpSession' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS/Session/PhpSession.php',
'CAS_TypeMismatchException' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS/TypeMismatchException.php',
'CMDBChange' => __DIR__ . '/../..' . '/core/cmdbchange.class.inc.php',
'CMDBChangeOp' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpCreate' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpDelete' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpPlugin' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttribute' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeBlob' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeCaseLog' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeCustomFields' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeEncrypted' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeHTML' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeLinks' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeLinksAddRemove' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeLinksTune' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeLongText' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeOneWayPassword' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeScalar' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeTagSet' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeText' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOpSetAttributeURL' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'CMDBChangeOp' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOp.php',
'CMDBChangeOpCreate' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpCreate.php',
'CMDBChangeOpDelete' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpDelete.php',
'CMDBChangeOpPlugin' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpPlugin.php',
'CMDBChangeOpSetAttribute' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttribute.php',
'CMDBChangeOpSetAttributeBlob' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeBlob.php',
'CMDBChangeOpSetAttributeCaseLog' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeCaseLog.php',
'CMDBChangeOpSetAttributeCustomFields' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeCustomFields.php',
'CMDBChangeOpSetAttributeEncrypted' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeEncrypted.php',
'CMDBChangeOpSetAttributeHTML' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeHTML.php',
'CMDBChangeOpSetAttributeLinks' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeLinks.php',
'CMDBChangeOpSetAttributeLinksAddRemove' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeLinksAddRemove.php',
'CMDBChangeOpSetAttributeLinksTune' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeLinksTune.php',
'CMDBChangeOpSetAttributeLongText' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeLongText.php',
'CMDBChangeOpSetAttributeOneWayPassword' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeOneWayPassword.php',
'CMDBChangeOpSetAttributeScalar' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeScalar.php',
'CMDBChangeOpSetAttributeTagSet' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeTagSet.php',
'CMDBChangeOpSetAttributeText' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeText.php',
'CMDBChangeOpSetAttributeURL' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/CMDBChangeOpSetAttributeURL.php',
'CMDBObject' => __DIR__ . '/../..' . '/core/cmdbobject.class.inc.php',
'CMDBObjectSet' => __DIR__ . '/../..' . '/core/cmdbobject.class.inc.php',
'CMDBSource' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php',
@@ -555,16 +555,16 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'CSVParser' => __DIR__ . '/../..' . '/core/csvparser.class.inc.php',
'CSVParserException' => __DIR__ . '/../..' . '/application/exceptions/CSVParserException.php',
'CURLStringFile' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php',
'CellChangeSpec' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'CellStatus_Ambiguous' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'CellStatus_Issue' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'CellStatus_Modify' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'CellStatus_NullIssue' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'CellStatus_SearchIssue' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'CellStatus_Void' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'CellChangeSpec' => __DIR__ . '/../..' . '/sources/Core/BulkChange/CellChangeSpec.php',
'CellStatus_Ambiguous' => __DIR__ . '/../..' . '/sources/Core/BulkChange/CellStatus_Ambiguous.php',
'CellStatus_Issue' => __DIR__ . '/../..' . '/sources/Core/BulkChange/CellStatus_Issue.php',
'CellStatus_Modify' => __DIR__ . '/../..' . '/sources/Core/BulkChange/CellStatus_Modify.php',
'CellStatus_NullIssue' => __DIR__ . '/../..' . '/sources/Core/BulkChange/CellStatus_NullIssue.php',
'CellStatus_SearchIssue' => __DIR__ . '/../..' . '/sources/Core/BulkChange/CellStatus_SearchIssue.php',
'CellStatus_Void' => __DIR__ . '/../..' . '/sources/Core/BulkChange/CellStatus_Void.php',
'CharConcatExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php',
'CharConcatWSExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php',
'CheckStopWatchThresholds' => __DIR__ . '/../..' . '/core/ormstopwatch.class.inc.php',
'CheckStopWatchThresholds' => __DIR__ . '/../..' . '/sources/Core/Orm/ormStopWatch.php',
'CheckableExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php',
'Combodo\\iTop\\Application\\Branding' => __DIR__ . '/../..' . '/sources/Application/Branding.php',
'Combodo\\iTop\\Application\\EventRegister\\ApplicationEvents' => __DIR__ . '/../..' . '/sources/Application/EventRegister/ApplicationEvents.php',
@@ -917,13 +917,13 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'CoreTemplateException' => __DIR__ . '/../..' . '/application/exceptions/CoreTemplateException.php',
'CoreUnexpectedValue' => __DIR__ . '/../..' . '/application/exceptions/CoreUnexpectedValue.php',
'CoreWarning' => __DIR__ . '/../..' . '/application/exceptions/CoreWarning.php',
'CryptEngine' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php',
'CryptEngine' => __DIR__ . '/../..' . '/sources/Application/SimpleCrypt/CryptEngine.php',
'CustomFieldsHandler' => __DIR__ . '/../..' . '/core/customfieldshandler.class.inc.php',
'DBObject' => __DIR__ . '/../..' . '/core/dbobject.class.php',
'DBObjectSearch' => __DIR__ . '/../..' . '/core/dbobjectsearch.class.php',
'DBObjectSet' => __DIR__ . '/../..' . '/core/dbobjectset.class.php',
'DBObjectSetComparator' => __DIR__ . '/../..' . '/core/dbobjectset.class.php',
'DBProperty' => __DIR__ . '/../..' . '/core/dbproperty.class.inc.php',
'DBProperty' => __DIR__ . '/../..' . '/sources/Core/DataModel/DBProperty.php',
'DBSearch' => __DIR__ . '/../..' . '/core/dbsearch.class.php',
'DBSearchHelper' => __DIR__ . '/../..' . '/application/DBSearchHelper.php',
'DBUnionSearch' => __DIR__ . '/../..' . '/core/dbunionsearch.class.php',
@@ -994,18 +994,18 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'DisplayableNode' => __DIR__ . '/../..' . '/core/displayablegraph.class.inc.php',
'DisplayableRedundancyNode' => __DIR__ . '/../..' . '/core/displayablegraph.class.inc.php',
'EMail' => __DIR__ . '/../..' . '/core/email.class.inc.php',
'Event' => __DIR__ . '/../..' . '/core/event.class.inc.php',
'EventIssue' => __DIR__ . '/../..' . '/core/event.class.inc.php',
'EventLoginUsage' => __DIR__ . '/../..' . '/core/event.class.inc.php',
'EventNotification' => __DIR__ . '/../..' . '/core/event.class.inc.php',
'EventNotificationEmail' => __DIR__ . '/../..' . '/core/event.class.inc.php',
'EventOnObject' => __DIR__ . '/../..' . '/core/event.class.inc.php',
'EventRestService' => __DIR__ . '/../..' . '/core/event.class.inc.php',
'EventWebService' => __DIR__ . '/../..' . '/core/event.class.inc.php',
'ExcelBulkExport' => __DIR__ . '/../..' . '/core/excelbulkexport.class.inc.php',
'Event' => __DIR__ . '/../..' . '/sources/Application/DataModel/Event/Event.php',
'EventIssue' => __DIR__ . '/../..' . '/sources/Application/DataModel/Event/EventIssue.php',
'EventLoginUsage' => __DIR__ . '/../..' . '/sources/Application/DataModel/Event/EventLoginUsage.php',
'EventNotification' => __DIR__ . '/../..' . '/sources/Application/DataModel/Event/EventNotification.php',
'EventNotificationEmail' => __DIR__ . '/../..' . '/sources/Application/DataModel/Event/EventNotificationEmail.php',
'EventOnObject' => __DIR__ . '/../..' . '/sources/Application/DataModel/Event/EventOnObject.php',
'EventRestService' => __DIR__ . '/../..' . '/sources/Application/DataModel/Event/EventRestService.php',
'EventWebService' => __DIR__ . '/../..' . '/sources/Application/DataModel/Event/EventWebService.php',
'ExcelBulkExport' => __DIR__ . '/../..' . '/sources/Application/BulkExport/ExcelBulkExport.php',
'ExcelExporter' => __DIR__ . '/../..' . '/application/excelexporter.class.inc.php',
'ExceptionLog' => __DIR__ . '/../..' . '/core/log.class.inc.php',
'ExecAsyncTask' => __DIR__ . '/../..' . '/core/asynctask.class.inc.php',
'ExecAsyncTask' => __DIR__ . '/../..' . '/sources/Core/DataModel/AsyncTask/ExecAsyncTask.php',
'ExecutionKPI' => __DIR__ . '/../..' . '/core/kpi.class.inc.php',
'Expression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php',
'ExpressionCache' => __DIR__ . '/../..' . '/core/expressioncache.class.inc.php',
@@ -1116,7 +1116,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'GuzzleHttp\\RetryMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RetryMiddleware.php',
'GuzzleHttp\\TransferStats' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/TransferStats.php',
'GuzzleHttp\\Utils' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Utils.php',
'HTMLBulkExport' => __DIR__ . '/../..' . '/core/htmlbulkexport.class.inc.php',
'HTMLBulkExport' => __DIR__ . '/../..' . '/sources/Application/BulkExport/HTMLBulkExport.php',
'HTMLDOMSanitizer' => __DIR__ . '/../..' . '/core/htmlsanitizer.class.inc.php',
'HTMLNullSanitizer' => __DIR__ . '/../..' . '/core/htmlsanitizer.class.inc.php',
'HTMLSanitizer' => __DIR__ . '/../..' . '/core/htmlsanitizer.class.inc.php',
@@ -1131,10 +1131,10 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'InvalidExternalKeyValueException' => __DIR__ . '/../..' . '/application/exceptions/InvalidExternalKeyValueException.php',
'InvalidPasswordAttributeOneWayPassword' => __DIR__ . '/../..' . '/application/exceptions/InvalidPasswordAttributeOneWayPassword.php',
'IssueLog' => __DIR__ . '/../..' . '/core/log.class.inc.php',
'ItopCounter' => __DIR__ . '/../..' . '/core/counter.class.inc.php',
'JSButtonItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'JSPopupMenuItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'KeyValueStore' => __DIR__ . '/../..' . '/core/counter.class.inc.php',
'ItopCounter' => __DIR__ . '/../..' . '/sources/Core/ItopCounter.php',
'JSButtonItem' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/JSButtonItem.php',
'JSPopupMenuItem' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/JSPopupMenuItem.php',
'KeyValueStore' => __DIR__ . '/../..' . '/sources/Core/DataModel/KeyValueStore.php',
'Laminas\\Loader\\AutoloaderFactory' => __DIR__ . '/..' . '/laminas/laminas-loader/src/AutoloaderFactory.php',
'Laminas\\Loader\\ClassMapAutoloader' => __DIR__ . '/..' . '/laminas/laminas-loader/src/ClassMapAutoloader.php',
'Laminas\\Loader\\Exception\\BadMethodCallException' => __DIR__ . '/..' . '/laminas/laminas-loader/src/Exception/BadMethodCallException.php',
@@ -1495,7 +1495,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'MenuGroup' => __DIR__ . '/../..' . '/application/menunode.class.inc.php',
'MenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php',
'MetaModel' => __DIR__ . '/../..' . '/core/metamodel.class.php',
'MissingColumnException' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'MissingColumnException' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/MissingColumnException.php',
'MissingQueryArgument' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php',
'ModelReflection' => __DIR__ . '/../..' . '/core/modelreflection.class.inc.php',
'ModelReflectionRuntime' => __DIR__ . '/../..' . '/core/modelreflection.class.inc.php',
@@ -1548,7 +1548,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'OqlUnionQuery' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php',
'Override' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/Override.php',
'PDF417' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/barcodes/pdf417.php',
'PDFBulkExport' => __DIR__ . '/../..' . '/core/pdfbulkexport.class.inc.php',
'PDFBulkExport' => __DIR__ . '/../..' . '/sources/Application/BulkExport/PDFBulkExport.php',
'PEAR' => __DIR__ . '/..' . '/pear/pear-core-minimal/src/PEAR.php',
'PEAR_ErrorStack' => __DIR__ . '/..' . '/pear/pear-core-minimal/src/PEAR/ErrorStack.php',
'PEAR_Exception' => __DIR__ . '/..' . '/pear/pear_exception/PEAR/Exception.php',
@@ -1879,26 +1879,26 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'QueryOQL' => __DIR__ . '/../..' . '/application/query.class.inc.php',
'QueryReflection' => __DIR__ . '/../..' . '/core/modelreflection.class.inc.php',
'QueryReflectionRuntime' => __DIR__ . '/../..' . '/core/modelreflection.class.inc.php',
'RelationEdge' => __DIR__ . '/../..' . '/core/relationgraph.class.inc.php',
'RelationGraph' => __DIR__ . '/../..' . '/core/relationgraph.class.inc.php',
'RelationObjectNode' => __DIR__ . '/../..' . '/core/relationgraph.class.inc.php',
'RelationRedundancyNode' => __DIR__ . '/../..' . '/core/relationgraph.class.inc.php',
'RelationEdge' => __DIR__ . '/../..' . '/sources/Core/RelationGraph/RelationEdge.php',
'RelationGraph' => __DIR__ . '/../..' . '/sources/Core/RelationGraph/RelationGraph.php',
'RelationObjectNode' => __DIR__ . '/../..' . '/sources/Core/RelationGraph/RelationObjectNode.php',
'RelationRedundancyNode' => __DIR__ . '/../..' . '/sources/Core/RelationGraph/RelationRedundancyNode.php',
'RelationTypeIterator' => __DIR__ . '/../..' . '/core/simplegraph.class.inc.php',
'ReportValue' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'ReportValue' => __DIR__ . '/../..' . '/sources/Core/BulkChange/ReportValue.php',
'RestDelete' => __DIR__ . '/../..' . '/core/restservices.class.inc.php',
'RestResult' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'RestResult' => __DIR__ . '/../..' . '/application/applicationextension/rest/RestResult.php',
'RestResultWithObjects' => __DIR__ . '/../..' . '/core/restservices.class.inc.php',
'RestResultWithRelations' => __DIR__ . '/../..' . '/core/restservices.class.inc.php',
'RestUtils' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'RestUtils' => __DIR__ . '/../..' . '/application/applicationextension/rest/RestUtils.php',
'ReturnTypeWillChange' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php',
'RotatingLogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php',
'RowStatus' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'RowStatus_Disappeared' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'RowStatus_Error' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'RowStatus_Issue' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'RowStatus_Modify' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'RowStatus_NewObj' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'RowStatus_NoChange' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php',
'RowStatus' => __DIR__ . '/../..' . '/sources/Core/BulkChange/RowStatus.php',
'RowStatus_Disappeared' => __DIR__ . '/../..' . '/sources/Core/BulkChange/RowStatus_Disappeared.php',
'RowStatus_Error' => __DIR__ . '/../..' . '/sources/Core/BulkChange/RowStatus_Error.php',
'RowStatus_Issue' => __DIR__ . '/../..' . '/sources/Core/BulkChange/RowStatus_Issue.php',
'RowStatus_Modify' => __DIR__ . '/../..' . '/sources/Core/BulkChange/RowStatus_Modify.php',
'RowStatus_NewObj' => __DIR__ . '/../..' . '/sources/Core/BulkChange/RowStatus_NewObj.php',
'RowStatus_NoChange' => __DIR__ . '/../..' . '/sources/Core/BulkChange/RowStatus_NoChange.php',
'RunTimeIconSelectionField' => __DIR__ . '/../..' . '/application/forms.class.inc.php',
'RuntimeDashboard' => __DIR__ . '/../..' . '/application/dashboard.class.inc.php',
'SQLExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php',
@@ -2002,23 +2002,23 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'ScssPhp\\ScssPhp\\Warn' => __DIR__ . '/..' . '/scssphp/scssphp/src/Warn.php',
'SearchMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php',
'SecurityException' => __DIR__ . '/../..' . '/application/exceptions/SecurityException.php',
'SeparatorPopupMenuItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'SeparatorPopupMenuItem' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/SeparatorPopupMenuItem.php',
'SetupLog' => __DIR__ . '/../..' . '/core/log.class.inc.php',
'Shortcut' => __DIR__ . '/../..' . '/application/shortcut.class.inc.php',
'ShortcutContainerMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php',
'ShortcutMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php',
'ShortcutOQL' => __DIR__ . '/../..' . '/application/shortcut.class.inc.php',
'SimpleCrypt' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php',
'SimpleCryptMcryptEngine' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php',
'SimpleCryptOpenSSLEngine' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php',
'SimpleCryptOpenSSLMcryptCompatibilityEngine' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php',
'SimpleCryptSimpleEngine' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php',
'SimpleCryptSodiumEngine' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php',
'SimpleCrypt' => __DIR__ . '/../..' . '/sources/Application/SimpleCrypt/SimpleCrypt.php',
'SimpleCryptMcryptEngine' => __DIR__ . '/../..' . '/sources/Application/SimpleCrypt/SimpleCryptMcryptEngine.php',
'SimpleCryptOpenSSLEngine' => __DIR__ . '/../..' . '/sources/Application/SimpleCrypt/SimpleCryptOpenSSLEngine.php',
'SimpleCryptOpenSSLMcryptCompatibilityEngine' => __DIR__ . '/../..' . '/sources/Application/SimpleCrypt/SimpleCryptOpenSSLMcryptCompatibilityEngine.php',
'SimpleCryptSimpleEngine' => __DIR__ . '/../..' . '/sources/Application/SimpleCrypt/SimpleCryptSimpleEngine.php',
'SimpleCryptSodiumEngine' => __DIR__ . '/../..' . '/sources/Application/SimpleCrypt/SimpleCryptSodiumEngine.php',
'SimpleGraph' => __DIR__ . '/../..' . '/core/simplegraph.class.inc.php',
'SimpleGraphException' => __DIR__ . '/../..' . '/core/simplegraph.class.inc.php',
'Soundasleep\\Html2Text' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2Text.php',
'Soundasleep\\Html2TextException' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2TextException.php',
'SpreadsheetBulkExport' => __DIR__ . '/../..' . '/core/spreadsheetbulkexport.class.inc.php',
'SpreadsheetBulkExport' => __DIR__ . '/../..' . '/sources/Application/BulkExport/SpreadsheetBulkExport.php',
'StimulusChecker' => __DIR__ . '/../..' . '/core/userrights.class.inc.php',
'StimulusInternal' => __DIR__ . '/../..' . '/core/stimulus.class.inc.php',
'StimulusUserAction' => __DIR__ . '/../..' . '/core/stimulus.class.inc.php',
@@ -3294,12 +3294,12 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'TCPDF_IMPORT' => __DIR__ . '/..' . '/tecnickcom/tcpdf/tcpdf_import.php',
'TCPDF_PARSER' => __DIR__ . '/..' . '/tecnickcom/tcpdf/tcpdf_parser.php',
'TCPDF_STATIC' => __DIR__ . '/..' . '/tecnickcom/tcpdf/include/tcpdf_static.php',
'TabularBulkExport' => __DIR__ . '/../..' . '/core/tabularbulkexport.class.inc.php',
'TagSetFieldData' => __DIR__ . '/../..' . '/core/tagsetfield.class.inc.php',
'TabularBulkExport' => __DIR__ . '/../..' . '/sources/Application/BulkExport/TabularBulkExport.php',
'TagSetFieldData' => __DIR__ . '/../..' . '/sources/Core/DataModel/TagSetFieldData.php',
'TemplateMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php',
'TemplateString' => __DIR__ . '/../..' . '/core/templatestring.class.inc.php',
'TemplateStringPlaceholder' => __DIR__ . '/../..' . '/core/templatestring.class.inc.php',
'TemporaryObjectDescriptor' => __DIR__ . '/../..' . '/core/TemporaryObjectDescriptor.php',
'TemporaryObjectDescriptor' => __DIR__ . '/../..' . '/sources/Core/DataModel/TemporaryObjectDescriptor.php',
'TheNetworg\\OAuth2\\Client\\Grant\\JwtBearer' => __DIR__ . '/..' . '/thenetworg/oauth2-azure/src/Grant/JwtBearer.php',
'TheNetworg\\OAuth2\\Client\\Provider\\Azure' => __DIR__ . '/..' . '/thenetworg/oauth2-azure/src/Provider/Azure.php',
'TheNetworg\\OAuth2\\Client\\Provider\\AzureResourceOwner' => __DIR__ . '/..' . '/thenetworg/oauth2-azure/src/Provider/AzureResourceOwner.php',
@@ -3307,18 +3307,18 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'ThemeHandler' => __DIR__ . '/../..' . '/application/themehandler.class.inc.php',
'ThemeHandlerService' => __DIR__ . '/../..' . '/application/themehandlerservice.class.inc.php',
'ToolsLog' => __DIR__ . '/../..' . '/core/log.class.inc.php',
'Trigger' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
'TriggerOnAttributeBlobDownload' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
'TriggerOnObject' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
'TriggerOnObjectCreate' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
'TriggerOnObjectDelete' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
'TriggerOnObjectMention' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
'TriggerOnObjectUpdate' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
'TriggerOnPortalUpdate' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
'TriggerOnStateChange' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
'TriggerOnStateEnter' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
'TriggerOnStateLeave' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
'TriggerOnThresholdReached' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
'Trigger' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/Trigger.php',
'TriggerOnAttributeBlobDownload' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/TriggerOnAttributeBlobDownload.php',
'TriggerOnObject' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/TriggerOnObject.php',
'TriggerOnObjectCreate' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/TriggerOnObjectCreate.php',
'TriggerOnObjectDelete' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/TriggerOnObjectDelete.php',
'TriggerOnObjectMention' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/TriggerOnObjectMention.php',
'TriggerOnObjectUpdate' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/TriggerOnObjectUpdate.php',
'TriggerOnPortalUpdate' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/TriggerOnPortalUpdate.php',
'TriggerOnStateChange' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/TriggerOnStateChange.php',
'TriggerOnStateEnter' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/TriggerOnStateEnter.php',
'TriggerOnStateLeave' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/TriggerOnStateLeave.php',
'TriggerOnThresholdReached' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/TriggerOnThresholdReached.php',
'TrueExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php',
'Twig\\AbstractTwigCallable' => __DIR__ . '/..' . '/twig/twig/src/AbstractTwigCallable.php',
'Twig\\Attribute\\FirstClassTwigCallableReady' => __DIR__ . '/..' . '/twig/twig/src/Attribute/FirstClassTwigCallableReady.php',
@@ -3534,8 +3534,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'UIPasswordWidget' => __DIR__ . '/../..' . '/application/ui.passwordwidget.class.inc.php',
'UISearchFormForeignKeys' => __DIR__ . '/../..' . '/application/ui.searchformforeignkeys.class.inc.php',
'UIWizard' => __DIR__ . '/../..' . '/application/uiwizard.class.inc.php',
'URLButtonItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'URLPopupMenuItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'URLButtonItem' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/URLButtonItem.php',
'URLPopupMenuItem' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/URLPopupMenuItem.php',
'UnaryExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php',
'UnknownClassOqlException' => __DIR__ . '/../..' . '/core/oql/oqlinterpreter.class.inc.php',
'User' => __DIR__ . '/../..' . '/core/userrights.class.inc.php',
@@ -3544,12 +3544,12 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'UserRightException' => __DIR__ . '/../..' . '/application/exceptions/UserRightException.php',
'UserRights' => __DIR__ . '/../..' . '/core/userrights.class.inc.php',
'UserRightsAddOnAPI' => __DIR__ . '/../..' . '/core/userrights.class.inc.php',
'ValueSetDefinition' => __DIR__ . '/../..' . '/core/valuesetdef.class.inc.php',
'ValueSetEnum' => __DIR__ . '/../..' . '/core/valuesetdef.class.inc.php',
'ValueSetEnumClasses' => __DIR__ . '/../..' . '/core/valuesetdef.class.inc.php',
'ValueSetEnumPadded' => __DIR__ . '/../..' . '/core/valuesetdef.class.inc.php',
'ValueSetObjects' => __DIR__ . '/../..' . '/core/valuesetdef.class.inc.php',
'ValueSetRange' => __DIR__ . '/../..' . '/core/valuesetdef.class.inc.php',
'ValueSetDefinition' => __DIR__ . '/../..' . '/sources/Core/ValueSetDefinition/ValueSetDefinition.php',
'ValueSetEnum' => __DIR__ . '/../..' . '/sources/Core/ValueSetDefinition/ValueSetEnum.php',
'ValueSetEnumClasses' => __DIR__ . '/../..' . '/sources/Core/ValueSetDefinition/ValueSetEnumClasses.php',
'ValueSetEnumPadded' => __DIR__ . '/../..' . '/sources/Core/ValueSetDefinition/ValueSetEnumPadded.php',
'ValueSetObjects' => __DIR__ . '/../..' . '/sources/Core/ValueSetDefinition/ValueSetObjects.php',
'ValueSetRange' => __DIR__ . '/../..' . '/sources/Core/ValueSetDefinition/ValueSetRange.php',
'VariableExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php',
'VariableOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php',
'WebPageMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php',
@@ -3559,48 +3559,48 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'WeeklyRotatingLogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php',
'WizardHelper' => __DIR__ . '/../..' . '/application/wizardhelper.class.inc.php',
'XLSXWriter' => __DIR__ . '/../..' . '/application/xlsxwriter.class.php',
'XMLBulkExport' => __DIR__ . '/../..' . '/core/xmlbulkexport.class.inc.php',
'XMLBulkExport' => __DIR__ . '/../..' . '/sources/Application/BulkExport/XMLBulkExport.php',
'appUserPreferences' => __DIR__ . '/../..' . '/application/user.preferences.class.inc.php',
'cmdbAbstractObject' => __DIR__ . '/../..' . '/application/cmdbabstract.class.inc.php',
'cmdbDataGenerator' => __DIR__ . '/../..' . '/core/data.generator.class.inc.php',
'iApplicationObjectExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iApplicationUIExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iAttributeNoGroupBy' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php',
'iApplicationObjectExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iApplicationObjectExtension.php',
'iApplicationUIExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iApplicationUIExtension.php',
'iAttributeNoGroupBy' => __DIR__ . '/../..' . '/sources/Core/AttributeDefinition/iAttributeNoGroupBy.php',
'iBackgroundProcess' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.php',
'iBackofficeDictEntriesExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iBackofficeDictEntriesPrefixesExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iBackofficeEarlyScriptExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iBackofficeInitScriptExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iBackofficeLinkedScriptsExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iBackofficeLinkedStylesheetsExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iBackofficeReadyScriptExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iBackofficeSassExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iBackofficeScriptExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iBackofficeStyleExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iBackupExtraFilesExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iCMDBChangeOp' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php',
'iBackofficeDictEntriesExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iBackofficeDictEntriesExtension.php',
'iBackofficeDictEntriesPrefixesExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iBackofficeDictEntriesPrefixesExtension.php',
'iBackofficeEarlyScriptExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iBackofficeEarlyScriptExtension.php',
'iBackofficeInitScriptExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iBackofficeInitScriptExtension.php',
'iBackofficeLinkedScriptsExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iBackofficeLinkedScriptsExtension.php',
'iBackofficeLinkedStylesheetsExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iBackofficeLinkedStylesheetsExtension.php',
'iBackofficeReadyScriptExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iBackofficeReadyScriptExtension.php',
'iBackofficeSassExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iBackofficeSassExtension.php',
'iBackofficeScriptExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iBackofficeScriptExtension.php',
'iBackofficeStyleExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iBackofficeStyleExtension.php',
'iBackupExtraFilesExtension' => __DIR__ . '/../..' . '/application/applicationextension/iBackupExtraFilesExtension.php',
'iCMDBChangeOp' => __DIR__ . '/../..' . '/sources/Core/DataModel/CMDBChange/iCMDBChangeOp.php',
'iDBObjectSetIterator' => __DIR__ . '/../..' . '/core/dbobjectiterator.php',
'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',
'iFieldRendererMappingsExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iFieldRendererMappingsExtension.php',
'iKPILoggerExtension' => __DIR__ . '/../..' . '/application/applicationextension/iKPILoggerExtension.php',
'iLogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php',
'iLoginExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iLoginFSMExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iLoginUIExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iLogoutExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iLoginExtension' => __DIR__ . '/../..' . '/application/applicationextension/login/iLoginExtension.php',
'iLoginFSMExtension' => __DIR__ . '/../..' . '/application/applicationextension/login/iLoginFSMExtension.php',
'iLoginUIExtension' => __DIR__ . '/../..' . '/application/applicationextension/login/iLoginUIExtension.php',
'iLogoutExtension' => __DIR__ . '/../..' . '/application/applicationextension/login/iLogoutExtension.php',
'iMetricComputer' => __DIR__ . '/../..' . '/core/computing.inc.php',
'iModuleExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iModuleExtension' => __DIR__ . '/../..' . '/application/applicationextension/iModuleExtension.php',
'iNewsroomProvider' => __DIR__ . '/../..' . '/application/newsroomprovider.class.inc.php',
'iOnClassInitialization' => __DIR__ . '/../..' . '/core/metamodelmodifier.inc.php',
'iPageUIBlockExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iPopupMenuExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iPortalUIExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iPreferencesExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iPageUIBlockExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iPageUIBlockExtension.php',
'iPopupMenuExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iPopupMenuExtension.php',
'iPortalUIExtension' => __DIR__ . '/../..' . '/application/applicationextension/portal/iPortalUIExtension.php',
'iPreferencesExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iPreferencesExtension.php',
'iProcess' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.php',
'iQueryModifier' => __DIR__ . '/../..' . '/core/querymodifier.class.inc.php',
'iRestInputSanitizer' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iRestServiceProvider' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iRestInputSanitizer' => __DIR__ . '/../..' . '/application/applicationextension/rest/iRestInputSanitizer.php',
'iRestServiceProvider' => __DIR__ . '/../..' . '/application/applicationextension/rest/iRestServiceProvider.php',
'iScheduledProcess' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.php',
'iSelfRegister' => __DIR__ . '/../..' . '/core/userrights.class.inc.php',
'iTopConfigParser' => __DIR__ . '/../..' . '/core/iTopConfigParser.php',
@@ -3609,19 +3609,19 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'iTopOwnershipToken' => __DIR__ . '/../..' . '/core/ownershiplock.class.inc.php',
'iTopStandardURLMaker' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php',
'iTopXmlException' => __DIR__ . '/../..' . '/application/exceptions/iTopXmlException.php',
'iWelcomePopupExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iWelcomePopupExtension' => __DIR__ . '/../..' . '/application/applicationextension/backoffice/iWelcomePopupExtension.php',
'iWorkingTimeComputer' => __DIR__ . '/../..' . '/core/computing.inc.php',
'lnkAuditCategoryToAuditDomain' => __DIR__ . '/../..' . '/application/audit.domain.class.inc.php',
'lnkTriggerAction' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
'ormCaseLog' => __DIR__ . '/../..' . '/core/ormcaselog.class.inc.php',
'ormCustomFieldsValue' => __DIR__ . '/../..' . '/core/ormcustomfieldsvalue.class.inc.php',
'ormDocument' => __DIR__ . '/../..' . '/core/ormdocument.class.inc.php',
'ormLinkSet' => __DIR__ . '/../..' . '/core/ormlinkset.class.inc.php',
'ormPassword' => __DIR__ . '/../..' . '/core/ormpassword.class.inc.php',
'ormSet' => __DIR__ . '/../..' . '/core/ormset.class.inc.php',
'ormStopWatch' => __DIR__ . '/../..' . '/core/ormstopwatch.class.inc.php',
'ormStyle' => __DIR__ . '/../..' . '/core/ormStyle.class.inc.php',
'ormTagSet' => __DIR__ . '/../..' . '/core/ormtagset.class.inc.php',
'lnkTriggerAction' => __DIR__ . '/../..' . '/sources/Application/DataModel/TriggerAndAction/lnkTriggerAction.php',
'ormCaseLog' => __DIR__ . '/../..' . '/sources/Core/Orm/ormCaseLog.php',
'ormCustomFieldsValue' => __DIR__ . '/../..' . '/sources/Core/Orm/ormCustomFieldsValue.php',
'ormDocument' => __DIR__ . '/../..' . '/sources/Core/Orm/ormDocument.php',
'ormLinkSet' => __DIR__ . '/../..' . '/sources/Core/Orm/ormLinkSet.php',
'ormPassword' => __DIR__ . '/../..' . '/sources/Core/Orm/ormPassword.php',
'ormSet' => __DIR__ . '/../..' . '/sources/Core/Orm/ormSet.php',
'ormStopWatch' => __DIR__ . '/../..' . '/sources/Core/Orm/ormStopWatch.php',
'ormStyle' => __DIR__ . '/../..' . '/sources/Core/Orm/ormStyle.php',
'ormTagSet' => __DIR__ . '/../..' . '/sources/Core/Orm/ormTagSet.php',
'phpCAS' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS.php',
'privUITransaction' => __DIR__ . '/../..' . '/application/transaction.class.inc.php',
'privUITransactionFile' => __DIR__ . '/../..' . '/application/transaction.class.inc.php',

View File

@@ -36,7 +36,8 @@ if ($issues) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
throw new \RuntimeException(
'Composer detected issues in your platform: ' . implode(' ', $issues)
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}

View File

@@ -1397,7 +1397,10 @@ try
case 'swf_navigator': /** @deprecated SWF was removed in iTop */
case 'view_relations': // Graphical display of the relations "impact" / "depends on"
require_once(APPROOT.'core/simplegraph.class.inc.php');
require_once(APPROOT.'core/relationgraph.class.inc.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationEdge.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationGraph.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationObjectNode.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationRedundancyNode.php');
require_once(APPROOT.'core/displayablegraph.class.inc.php');
$sClass = utils::ReadParam('class', '', false, 'class');
$id = utils::ReadParam('id', 0);

View File

@@ -1538,7 +1538,10 @@ EOF
case 'relation_pdf':
case 'relation_attachment':
require_once(APPROOT.'core/simplegraph.class.inc.php');
require_once(APPROOT.'core/relationgraph.class.inc.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationEdge.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationGraph.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationObjectNode.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationRedundancyNode.php');
require_once(APPROOT.'core/displayablegraph.class.inc.php');
$sRelation = utils::ReadParam('relation', 'impacts');
$sDirection = utils::ReadParam('direction', 'down');
@@ -1696,7 +1699,11 @@ EOF
case 'relation_json':
require_once(APPROOT.'core/simplegraph.class.inc.php');
require_once(APPROOT.'core/relationgraph.class.inc.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationEdge.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationGraph.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationObjectNode.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationRedundancyNode.php');
require_once(APPROOT.'core/displayablegraph.class.inc.php');
$sRelation = utils::ReadParam('relation', 'impacts');
$sDirection = utils::ReadParam('direction', 'down');
@@ -1801,7 +1808,10 @@ EOF
case 'ticket_impact':
require_once(APPROOT.'core/simplegraph.class.inc.php');
require_once(APPROOT.'core/relationgraph.class.inc.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationEdge.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationGraph.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationObjectNode.php');
require_once(APPROOT.'/sources/Core/RelationGraph/RelationRedundancyNode.php');
require_once(APPROOT.'core/displayablegraph.class.inc.php');
$sRelation = utils::ReadParam('relation', 'impacts');

View File

@@ -0,0 +1,371 @@
<?php
use Combodo\iTop\Application\WebPage\Page;
use Combodo\iTop\Application\WebPage\WebPage;
/**
* Class BulkExport
*
* @copyright Copyright (C) 2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class BulkExport
{
protected $oSearch;
protected $iChunkSize;
protected $sFormatCode;
protected $aStatusInfo;
protected $oBulkExportResult;
protected $sTmpFile;
protected $bLocalizeOutput;
public function __construct()
{
$this->oSearch = null;
$this->iChunkSize = 0;
$this->sFormatCode = null;
$this->aStatusInfo = [
'show_obsolete_data' => utils::ShowObsoleteData(),
];
$this->oBulkExportResult = null;
$this->sTmpFile = '';
$this->bLocalizeOutput = false;
}
/**
* Find the first class capable of exporting the data in the given format
*
* @param string $sFormatCode The lowercase format (e.g. html, csv, spreadsheet, xlsx, xml, json, pdf...)
* @param DBSearch $oSearch The search/filter defining the set of objects to export or null when listing the supported formats
*
* @return BulkExport|null
* @throws ReflectionException
*/
static public function FindExporter($sFormatCode, $oSearch = null)
{
foreach (get_declared_classes() as $sPHPClass) {
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->isSubclassOf('BulkExport') && !$oRefClass->isAbstract()) {
/** @var BulkExport $oBulkExporter */
$oBulkExporter = new $sPHPClass();
if ($oBulkExporter->IsFormatSupported($sFormatCode, $oSearch)) {
if ($oSearch) {
$oBulkExporter->SetObjectList($oSearch);
}
return $oBulkExporter;
}
}
}
return null;
}
/**
* Find the exporter corresponding to the given persistent token
*
* @param int $iPersistentToken The identifier of the BulkExportResult object storing the information
*
* @return BulkExport|null
* @throws ArchivedObjectException
* @throws CoreException
* @throws ReflectionException
*/
static public function FindExporterFromToken($iPersistentToken = null)
{
$oBulkExporter = null;
$oInfo = MetaModel::GetObject('BulkExportResult', $iPersistentToken, false);
if ($oInfo && ($oInfo->Get('user_id') == UserRights::GetUserId())) {
$sFormatCode = $oInfo->Get('format');
$aStatusInfo = json_decode($oInfo->Get('status_info'), true);
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
$oSearch->SetShowObsoleteData($aStatusInfo['show_obsolete_data']);
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
if ($oBulkExporter) {
$oBulkExporter->SetFormat($sFormatCode);
$oBulkExporter->SetObjectList($oSearch);
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
$oBulkExporter->SetStatusInfo($aStatusInfo);
$oBulkExporter->SetLocalizeOutput($oInfo->Get('localize_output'));
$oBulkExporter->sTmpFile = $oInfo->Get('temp_file_path');
$oBulkExporter->oBulkExportResult = $oInfo;
}
}
return $oBulkExporter;
}
/**
* @param $data
*
* @throws Exception
*/
public function AppendToTmpFile($data)
{
if ($this->sTmpFile == '') {
$this->sTmpFile = $this->MakeTmpFile($this->GetFileExtension());
}
$hFile = fopen($this->sTmpFile, 'ab');
if ($hFile !== false) {
fwrite($hFile, $data);
fclose($hFile);
}
}
public function GetTmpFilePath()
{
return $this->sTmpFile;
}
/**
* Lists all possible export formats. The output is a hash array in the form: 'format_code' => 'localized format label'
*
* @return array :string
*/
static public function FindSupportedFormats()
{
$aSupportedFormats = array();
foreach (get_declared_classes() as $sPHPClass) {
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->isSubClassOf('BulkExport') && !$oRefClass->isAbstract()) {
$oBulkExporter = new $sPHPClass;
$aFormats = $oBulkExporter->GetSupportedFormats();
$aSupportedFormats = array_merge($aSupportedFormats, $aFormats);
}
}
return $aSupportedFormats;
}
/**
* (non-PHPdoc)
*
* @see iBulkExport::SetChunkSize()
*/
public function SetChunkSize($iChunkSize)
{
$this->iChunkSize = $iChunkSize;
}
/**
* @param $bLocalizeOutput
*/
public function SetLocalizeOutput($bLocalizeOutput)
{
$this->bLocalizeOutput = $bLocalizeOutput;
}
/**
* (non-PHPdoc)
*
* @see iBulkExport::SetObjectList()
*/
public function SetObjectList(DBSearch $oSearch)
{
$oSearch->SetShowObsoleteData($this->aStatusInfo['show_obsolete_data']);
$this->oSearch = $oSearch;
}
public function SetFormat($sFormatCode)
{
$this->sFormatCode = $sFormatCode;
}
/**
* (non-PHPdoc)
*
* @see iBulkExport::IsFormatSupported()
*/
public function IsFormatSupported($sFormatCode, $oSearch = null)
{
return array_key_exists($sFormatCode, $this->GetSupportedFormats());
}
/**
* (non-PHPdoc)
*
* @see iBulkExport::GetSupportedFormats()
*/
public function GetSupportedFormats()
{
return array(); // return array('csv' => Dict::S('UI:ExportFormatCSV'));
}
public function SetHttpHeaders(WebPage $oPage)
{
}
/**
* @return string
*/
public function GetHeader()
{
return '';
}
abstract public function GetNextChunk(&$aStatus);
/**
* @return string
*/
public function GetFooter()
{
return '';
}
public function SaveState()
{
if ($this->oBulkExportResult === null) {
$this->oBulkExportResult = new BulkExportResult();
$this->oBulkExportResult->Set('format', $this->sFormatCode);
$this->oBulkExportResult->Set('search', $this->oSearch->serialize());
$this->oBulkExportResult->Set('chunk_size', $this->iChunkSize);
$this->oBulkExportResult->Set('localize_output', $this->bLocalizeOutput);
}
$this->oBulkExportResult->Set('status_info', json_encode($this->GetStatusInfo()));
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
utils::PushArchiveMode(false);
$ret = $this->oBulkExportResult->DBWrite();
utils::PopArchiveMode();
return $ret;
}
public function Cleanup()
{
if (($this->oBulkExportResult && (!$this->oBulkExportResult->IsNew()))) {
$sFilename = $this->oBulkExportResult->Get('temp_file_path');
if ($sFilename != '') {
@unlink($sFilename);
}
utils::PushArchiveMode(false);
$this->oBulkExportResult->DBDelete();
utils::PopArchiveMode();
}
}
public function EnumFormParts()
{
return array();
}
/**
* @deprecated 3.0.0 use GetFormPart instead
*/
public function DisplayFormPart(WebPage $oP, $sPartId)
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use GetFormPart instead');
$oP->AddSubBlock($this->GetFormPart($oP, $sPartId));
}
/**
* @param \Combodo\iTop\Application\WebPage\WebPage $oP
* @param $sPartId
*
* @return UIContentBlock
*/
public function GetFormPart(WebPage $oP, $sPartId)
{
}
public function DisplayUsage(Page $oP)
{
}
public function ReadParameters()
{
$this->bLocalizeOutput = !((bool)utils::ReadParam('no_localize', 0, true, 'integer'));
}
public function GetResultAsHtml()
{
}
public function GetRawResult()
{
}
/**
* @return string
*/
public function GetMimeType()
{
return '';
}
/**
* @return string
*/
public function GetFileExtension()
{
return '';
}
public function GetCharacterSet()
{
return 'UTF-8';
}
public function GetStatistics()
{
}
public function SetFields($sFields)
{
}
public function GetDownloadFileName()
{
return Dict::Format('Core:BulkExportOf_Class', MetaModel::GetName($this->oSearch->GetClass())).'.'.$this->GetFileExtension();
}
public function SetStatusInfo($aStatusInfo)
{
$this->aStatusInfo = $aStatusInfo;
}
public function GetStatusInfo()
{
return $this->aStatusInfo;
}
/**
* @param $sExtension
*
* @return string
* @throws Exception
*/
protected function MakeTmpFile($sExtension)
{
if (!is_dir(utils::GetDataPath()."bulk_export")) {
@mkdir(utils::GetDataPath()."bulk_export", 0777, true /* recursive */);
clearstatcache();
}
if (!is_writable(utils::GetDataPath()."bulk_export")) {
throw new Exception('Data directory "'.utils::GetDataPath().'bulk_export" could not be written.');
}
$iNum = rand();
do {
$iNum++;
$sToken = sprintf("%08x", $iNum);
$sFileName = utils::GetDataPath()."bulk_export/$sToken.".$sExtension;
$hFile = @fopen($sFileName, 'x');
} while ($hFile === false);
fclose($hFile);
return $sFileName;
}
}

View File

@@ -0,0 +1,17 @@
<?php
class BulkExportException extends Exception
{
protected $sLocalizedMessage;
public function __construct($message, $sLocalizedMessage, $code = 0, $previous = null)
{
parent::__construct($message, $code, $previous);
$this->sLocalizedMessage = $sLocalizedMessage;
}
public function GetLocalizedMessage()
{
return $this->sLocalizedMessage;
}
}

View File

@@ -0,0 +1,10 @@
<?php
class BulkExportMissingParameterException extends BulkExportException
{
public function __construct($sFieldCode)
{
parent::__construct('Missing parameter: ' . $sFieldCode, Dict::Format('Core:BulkExport:MissingParameter_Param', $sFieldCode));
}
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* Garbage collector for cleaning "old" export results from the database and the disk.
* This background process runs once per day and deletes the results of all exports which
* are older than one day.
*/
class BulkExportResultGC implements iBackgroundProcess
{
public function GetPeriodicity()
{
return 24 * 3600; // seconds
}
public function Process($iTimeLimit)
{
$sDateLimit = date(AttributeDateTime::GetSQLFormat(), time() - 24 * 3600); // Every BulkExportResult older than one day will be deleted
$sOQL = "SELECT BulkExportResult WHERE created < '$sDateLimit'";
$iProcessed = 0;
while (time() < $iTimeLimit) {
// Next one ?
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array(), null, 1 /* limit count */);
$oSet->OptimizeColumnLoad(array('BulkExportResult' => array('temp_file_path')));
$oResult = $oSet->Fetch();
if (is_null($oResult)) {
// Nothing to be done
break;
}
$iProcessed++;
@unlink($oResult->Get('temp_file_path'));
utils::PushArchiveMode(false);
$oResult->DBDelete();
utils::PopArchiveMode();
}
return "Cleaned $iProcessed old export results(s).";
}
}

View File

@@ -0,0 +1,110 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
* 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
*/
class Event extends DBObject implements iDisplay
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeText("message", array("allowed_values" => null, "sql" => "message", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values" => null, "sql" => "date", "default_value" => "NOW()", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values" => null, "sql" => "userinfo", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'finalclass', 'message')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
/**
* Maps the given context parameter name to the appropriate filter/search code for this class
* @param string $sContextParam Name of the context parameter, i.e. 'org_id'
* @return string Filter code, i.e. 'customer_id'
*/
public static function MapContextParam($sContextParam)
{
if ($sContextParam == 'menu') {
return null;
} else {
return $sContextParam;
}
}
/**
* This function returns a 'hilight' CSS class, used to hilight a given row in a table
* There are currently (i.e defined in the CSS) 4 possible values HILIGHT_CLASS_CRITICAL,
* HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE
* To Be overridden by derived classes
* @param void
* @return String The desired higlight class for the object/row
*/
public function GetHilightClass()
{
// Possible return values are:
// HILIGHT_CLASS_CRITICAL, HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE
return HILIGHT_CLASS_NONE; // Not hilighted by default
}
public static function GetUIPage()
{
return 'UI.php';
}
function DisplayDetails(\Combodo\iTop\Application\WebPage\WebPage $oPage, $bEditMode = false)
{
// Object's details
//$this->DisplayBareHeader($oPage, $bEditMode);
$oPage->AddTabContainer(OBJECT_PROPERTIES_TAB);
$oPage->SetCurrentTabContainer(OBJECT_PROPERTIES_TAB);
$oPage->SetCurrentTab('UI:PropertiesTab');
$this->DisplayBareProperties($oPage, $bEditMode);
}
function DisplayBareProperties(\Combodo\iTop\Application\WebPage\WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
if ($bEditMode) return array(); // Not editable
$aDetails = array();
$sClass = get_class($this);
$aZList = MetaModel::FlattenZlist(MetaModel::GetZListItems($sClass, 'details'));
foreach ($aZList as $sAttCode) {
$sDisplayValue = $this->GetAsHTML($sAttCode);
$aDetails[] = array('label' => '<span title="' . MetaModel::GetDescription($sClass, $sAttCode) . '">' . MetaModel::GetLabel($sClass, $sAttCode) . '</span>', 'value' => $sDisplayValue);
}
$oPage->Details($aDetails);
return array();
}
}

View File

@@ -0,0 +1,96 @@
<?php
class EventIssue extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_issue",
"db_key_field" => "id",
"db_finalclass_field" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("issue", array("allowed_values" => null, "sql" => "issue", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("impact", array("allowed_values" => null, "sql" => "impact", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("page", array("allowed_values" => null, "sql" => "page", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributePropertySet("arguments_post", array("allowed_values" => null, "sql" => "arguments_post", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributePropertySet("arguments_get", array("allowed_values" => null, "sql" => "arguments_get", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeTable("callstack", array("allowed_values" => null, "sql" => "callstack", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributePropertySet("data", array("allowed_values" => null, "sql" => "data", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo', 'issue', 'impact', 'page', 'arguments_post', 'arguments_get', 'callstack', 'data')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'issue', 'impact')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
protected function OnInsert()
{
// Init page information: name, arguments
//
$this->Set('page', @$GLOBALS['_SERVER']['SCRIPT_NAME']);
if (strlen($this->Get('userinfo')) == 0) {
$this->Set('userinfo', UserRights::GetUserId());
}
if (array_key_exists('_GET', $GLOBALS) && is_array($GLOBALS['_GET'])) {
$this->Set('arguments_get', $this->SanitizeRequestParams($GLOBALS['_GET']));
} else {
$this->Set('arguments_get', array());
}
if (array_key_exists('_POST', $GLOBALS) && is_array($GLOBALS['_POST'])) {
$this->Set('arguments_post', $this->SanitizeRequestParams($GLOBALS['_POST']));
} else {
$this->Set('arguments_post', array());
}
$sLength = mb_strlen($this->Get('issue'));
if ($sLength > 255) {
$this->Set('issue', mb_substr($this->Get('issue'), 0, 210) . " -truncated ($sLength chars)");
}
$sLength = mb_strlen($this->Get('impact'));
if ($sLength > 255) {
$this->Set('impact', mb_substr($this->Get('impact'), 0, 210) . " -truncated ($sLength chars)");
}
$sLength = mb_strlen($this->Get('page'));
if ($sLength > 255) {
$this->Set('page', mb_substr($this->Get('page'), 0, 210) . " -truncated ($sLength chars)");
}
}
protected function SanitizeRequestParams(array $aParams): array
{
$aSanitizedParams = [];
foreach ($aParams as $sKey => $sValue) {
if (is_string($sValue)) {
if (stristr($sKey, 'pwd') !== false || stristr($sKey, 'passwd') !== false || stristr($sKey, 'password') !== false) {
$aSanitizedParams[$sKey] = '****';
} elseif (mb_strlen($sValue) < 256) {
$aSanitizedParams[$sKey] = $sValue;
} else {
$aSanitizedParams[$sKey] = '!long string: ' . mb_strlen($sValue) . ' chars';
}
} else {
// Not a string (avoid warnings in case the value cannot be easily cast into a string)
$aSanitizedParams[$sKey] = @(string)$sValue;
}
}
return $aSanitizedParams;
}
}

View File

@@ -0,0 +1,39 @@
<?php
class EventLoginUsage extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_loginusage",
"db_key_field" => "id",
"db_finalclass_field" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass" => "User", "jointype" => "", "allowed_values" => null, "sql" => "user_id", "is_null_allowed" => false, "on_target_delete" => DEL_SILENT, "depends_on" => array())));
$aZList = array('date', 'user_id');
if (MetaModel::IsValidAttCode('Contact', 'name')) {
MetaModel::Init_AddAttribute(new AttributeExternalField("contact_name", array("allowed_values" => null, "extkey_attcode" => "user_id", "target_attcode" => "contactid", "is_null_allowed" => true, "depends_on" => array())));
$aZList[] = 'contact_name';
}
if (MetaModel::IsValidAttCode('Contact', 'email')) {
MetaModel::Init_AddAttribute(new AttributeExternalField("contact_email", array("allowed_values" => null, "extkey_attcode" => "user_id", "target_attcode" => "email", "is_null_allowed" => true, "depends_on" => array())));
$aZList[] = 'contact_email';
}
// Display lists
MetaModel::Init_SetZListItems('details', array_merge($aZList, array('userinfo', 'message'))); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array_merge($aZList, array('userinfo'))); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', $aZList); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}

View File

@@ -0,0 +1,37 @@
<?php
class EventNotification extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_notification",
"db_key_field" => "id",
"db_finalclass_field" => "",
"order_by_default" => array('date' => false),
'indexes' => array(
array('object_class', 'object_id'),
)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass" => "Trigger", "jointype" => "", "allowed_values" => null, "sql" => "trigger_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass" => "Action", "jointype" => "", "allowed_values" => null, "sql" => "action_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeInteger("object_id", array("allowed_values" => null, "sql" => "object_id", "default_value" => 0, "is_null_allowed" => false, "depends_on" => array())));
//@since 3.2.0
MetaModel::Init_AddAttribute(new AttributeClass("object_class", array("class_category" => "", "more_values" => "", "sql" => "object_class", "default_value" => null, "is_null_allowed" => true /*to avoid setting AbstractResource as default in database*/, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo', 'trigger_id', 'action_id', 'object_class', 'object_id')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'message')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}

View File

@@ -0,0 +1,37 @@
<?php
class EventNotificationEmail extends EventNotification
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_email",
"db_key_field" => "id",
"db_finalclass_field" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeText("to", array("allowed_values" => null, "sql" => "to", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeText("cc", array("allowed_values" => null, "sql" => "cc", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeText("bcc", array("allowed_values" => null, "sql" => "bcc", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeText("from", array("allowed_values" => null, "sql" => "from", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeText("subject", array("allowed_values" => null, "sql" => "subject", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeHTML("body", array("allowed_values" => null, "sql" => "body", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeTable("attachments", array("allowed_values" => null, "sql" => "attachments", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_class', 'object_id', 'to', 'cc', 'bcc', 'from', 'subject', 'body', 'attachments')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'message', 'to', 'subject', 'attachments')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}

View File

@@ -0,0 +1,29 @@
<?php
class EventOnObject extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_onobject",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("obj_class", array("allowed_values" => null, "sql" => "obj_class", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeInteger("obj_key", array("allowed_values" => null, "sql" => "obj_key", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'obj_class', 'obj_key', 'message')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'obj_class', 'obj_key', 'message')); // Attributes to be displayed for a list
}
}

View File

@@ -0,0 +1,36 @@
<?php
class EventRestService extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_restservice",
"db_key_field" => "id",
"db_finalclass_field" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("operation", array("allowed_values" => null, "sql" => "operation", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("version", array("allowed_values" => null, "sql" => "version", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeText("json_input", array("allowed_values" => null, "sql" => "json_input", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeInteger("code", array("allowed_values" => null, "sql" => "code", "default_value" => 0, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeText("json_output", array("allowed_values" => null, "sql" => "json_output", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("provider", array("allowed_values" => null, "sql" => "provider", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'operation', 'version', 'json_input', 'message', 'code', 'json_output', 'provider')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'operation', 'message')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}

View File

@@ -0,0 +1,36 @@
<?php
class EventWebService extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_webservice",
"db_key_field" => "id",
"db_finalclass_field" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("verb", array("allowed_values" => null, "sql" => "verb", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
//MetaModel::Init_AddAttribute(new AttributeStructure("arguments", array("allowed_values"=>null, "sql"=>"data", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeBoolean("result", array("allowed_values" => null, "sql" => "result", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeText("log_info", array("allowed_values" => null, "sql" => "log_info", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeText("log_warning", array("allowed_values" => null, "sql" => "log_warning", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeText("log_error", array("allowed_values" => null, "sql" => "log_error", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeText("data", array("allowed_values" => null, "sql" => "data", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'verb', 'result', 'log_info', 'log_warning', 'log_error', 'data')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'verb', 'result')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}

View File

@@ -0,0 +1,234 @@
<?php
/**
* A user defined action, to customize the application
*
* @package iTopORM
*/
abstract class Action extends cmdbAbstractObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "name",
"complementary_name_attcode" => ['finalclass', 'description'],
"state_attcode" => "status",
"reconc_keys" => ['name'],
"db_table" => "priv_action",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
"style" => new ormStyle("ibo-dm-class--Action", "ibo-dm-class-alt--Action", "var(--ibo-dm-class--Action--main-color)", "var(--ibo-dm-class--Action--complementary-color)", null, '../images/icons/icons8-in-transit.svg'),
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values" => null, "sql" => "name", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array(
"allowed_values" => new ValueSetEnum(array('test' => 'Being tested', 'enabled' => 'In production', 'disabled' => 'Inactive')),
"styled_values" => [
'test' => new ormStyle('ibo-dm-enum--Action-status-test', 'ibo-dm-enum-alt--Action-status-test', 'var(--ibo-dm-enum--Action-status-test--main-color)', 'var(--ibo-dm-enum--Action-status-test--complementary-color)', null, null),
'enabled' => new ormStyle('ibo-dm-enum--Action-status-enabled', 'ibo-dm-enum-alt--Action-status-enabled', 'var(--ibo-dm-enum--Action-status-enabled--main-color)', 'var(--ibo-dm-enum--Action-status-enabled--complementary-color)', 'fas fa-check', null),
'disabled' => new ormStyle('ibo-dm-enum--Action-status-disabled', 'ibo-dm-enum-alt--Action-status-disabled', 'var(--ibo-dm-enum--Action-status-disabled--main-color)', 'var(--ibo-dm-enum--Action-status-disabled--complementary-color)', null, null),
],
"display_style" => 'list',
"sql" => "status",
"default_value" => "test",
"is_null_allowed" => false,
"depends_on" => array(),
)));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("trigger_list",
array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "action_id", "ext_key_to_remote" => "trigger_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array(), "display_style" => 'property')));
MetaModel::Init_AddAttribute(new AttributeEnum("asynchronous", array("allowed_values" => new ValueSetEnum(['use_global_setting' => 'Use global settings', 'yes' => 'Yes', 'no' => 'No']), "sql" => "asynchronous", "default_value" => 'use_global_setting', "is_null_allowed" => false, "depends_on" => array())));
// Display lists
// - Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list'));
// - Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status'));
// Search criteria
// - Default criteria of the search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status'));
}
/**
* Encapsulate the execution of the action and handle failure & logging
*
* @param \Trigger $oTrigger
* @param array $aContextArgs
*
* @return mixed
*/
abstract public function DoExecute($oTrigger, $aContextArgs);
/**
* @return bool
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function IsActive()
{
switch ($this->Get('status')) {
case 'enabled':
case 'test':
return true;
default:
return false;
}
}
/**
* Return true if the current action status is set on "test"
*
* @return bool
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function IsBeingTested()
{
switch ($this->Get('status')) {
case 'test':
return true;
default:
return false;
}
}
/**
* @inheritDoc
* @since 3.0.0
*/
public function AfterInsert()
{
parent::AfterInsert();
$this->DoCheckIfHasTrigger();
}
/**
* @inheritDoc
* @since 3.0.0
*/
public function AfterUpdate()
{
parent::AfterUpdate();
$this->DoCheckIfHasTrigger();
}
/**
* Check if the Action has at least 1 trigger linked. Otherwise, it adds a warning.
* @return void
* @since 3.0.0
*/
protected function DoCheckIfHasTrigger()
{
$oTriggersSet = $this->Get('trigger_list');
if ($oTriggersSet->Count() === 0) {
$this->m_aCheckWarnings[] = Dict::S('Action:WarningNoTriggerLinked');
}
}
/**
* @since 3.2.0 N°5472 method creation
*/
public function DisplayBareRelations(\Combodo\iTop\Application\WebPage\WebPage $oPage, $bEditMode = false)
{
parent::DisplayBareRelations($oPage, false);
if ($oPage instanceof iTopWebPage && !$this->IsNew()) {
$this->GenerateLastExecutionsTab($oPage, $bEditMode);
}
}
/**
* @since 3.2.0 N°5472 method creation
*/
protected function GenerateLastExecutionsTab(iTopWebPage $oPage, $bEditMode)
{
$oRouter = \Combodo\iTop\Service\Router\Router::GetInstance();
$sActionLastExecutionsPageUrl = $oRouter->GenerateUrl('notifications.action.last_executions_tab', ['action_id' => $this->GetKey()]);
$oPage->AddAjaxTab('action_errors', $sActionLastExecutionsPageUrl, false, Dict::S('Action:last_executions_tab'));
}
/**
* @param \Combodo\iTop\Application\WebPage\WebPage $oPage
*
* @throws \ApplicationException
* @throws \ArchivedObjectException
* @throws \ConfigException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DictExceptionMissingString
* @throws \InvalidConfigParamException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @throws \ReflectionException
* @since 3.2.0 N°5472 method creation
*/
public function GetLastExecutionsTabContent(\Combodo\iTop\Application\WebPage\WebPage $oPage): void
{
$oConfig = utils::GetConfig();
$sLastExecutionDaysConfigParamName = 'notifications.last_executions_days';
$iLastExecutionDays = $oConfig->Get($sLastExecutionDaysConfigParamName);
if ($iLastExecutionDays < 0) {
throw new InvalidConfigParamException("Invalid value for {$sLastExecutionDaysConfigParamName} config parameter. Param desc: " . $oConfig->GetDescription($sLastExecutionDaysConfigParamName));
}
$sActionQueryOql = 'SELECT EventNotification WHERE action_id = :action_id';
$aActionQueryParams = ['action_id' => $this->GetKey()];
if ($iLastExecutionDays > 0) {
$sActionQueryOql .= ' AND date > DATE_SUB(NOW(), INTERVAL :days DAY)';
$aActionQueryParams['days'] = $iLastExecutionDays;
$sActionQueryLimit = Dict::Format('Action:last_executions_tab_limit_days', $iLastExecutionDays);
} else {
$sActionQueryLimit = Dict::S('Action:last_executions_tab_limit_none');
}
$oActionFilter = DBObjectSearch::FromOQL($sActionQueryOql, $aActionQueryParams);
$oSet = new DBObjectSet($oActionFilter, ['date' => false]);
$sPanelTitle = Dict::Format('Action:last_executions_tab_panel_title', $sActionQueryLimit);
$oExecutionsListBlock = \Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory::MakeForResult($oPage, 'action_executions_list', $oSet, ['panel_title' => $sPanelTitle]);
$oPage->AddUiBlock($oExecutionsListBlock);
}
/**
* Will be overloaded by the children classes to return the value of their global asynchronous setting (eg. `email_asynchronous` for `\ActionEmail`, `prefer_asynchronous` for `\ActionWebhook`, ...)
*
* @return bool true if the global setting for this kind of action if to be executed asynchronously, false otherwise.
* @since 3.2.0
*/
public static function GetAsynchronousGlobalSetting(): bool
{
return false;
}
/**
* @return bool true if that action instance should be executed asynchronously, otherwise false
* @throws \ArchivedObjectException
* @throws \CoreException
* @since 3.2.0
*/
public function IsAsynchronous(): bool
{
$sAsynchronous = $this->Get('asynchronous');
if ($sAsynchronous === 'use_global_setting') {
return static::GetAsynchronousGlobalSetting();
}
return $sAsynchronous === 'yes';
}
}

View File

@@ -0,0 +1,567 @@
<?php
/**
* An email notification
*
* @package iTopORM
*/
class ActionEmail extends ActionNotification
{
/**
* @var string
* @since 3.0.1
*/
const ENUM_HEADER_NAME_MESSAGE_ID = 'Message-ID';
/**
* @var string
* @since 3.0.1
*/
const ENUM_HEADER_NAME_REFERENCES = 'References';
/**
* @var string
* @since 3.1.0
*/
const TEMPLATE_BODY_CONTENT = '$content$';
/**
* Wraps the 'body' of the message for previewing inside an IFRAME -- i.e. without any of the iTop stylesheets being applied
* @var string
* @since 3.1.0
*/
const CONTENT_HIGHLIGHT = '<div style="border:2px dashed #6800ff;position:relative;padding:2px;margin-top:14px;"><div style="background-color:#6800ff;color:#fff;font-family:Courier New, sans-serif;font-size:14px;line-height:16px;padding:3px;display:block;position:absolute;top:-22px;right:0;">$content$</div>%s</div>';
/**
* Wraps a placeholder of the email's body for previewing inside an IFRAME -- i.e. without any of the iTop stylesheets being applied
* @var string
*/
const FIELD_HIGHLIGHT = '<span style="background-color:#6800ff;color:#fff;font-size:smaller;font-family:Courier New, sans-serif;padding:2px;">\\$$1\\$</span>';
/**
* @inheritDoc
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_action_email",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-mailing.svg'),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeEmailAddress("test_recipient", array("allowed_values" => null, "sql" => "test_recipient", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("from", array("allowed_values" => null, "sql" => "from", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("from_label", array("allowed_values" => null, "sql" => "from_label", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("reply_to", array("allowed_values" => null, "sql" => "reply_to", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("reply_to_label", array("allowed_values" => null, "sql" => "reply_to_label", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeOQL("to", array("allowed_values" => null, "sql" => "to", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeOQL("cc", array("allowed_values" => null, "sql" => "cc", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeOQL("bcc", array("allowed_values" => null, "sql" => "bcc", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeTemplateString("subject", array("allowed_values" => null, "sql" => "subject", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeTemplateHTML("body", array("allowed_values" => null, "sql" => "body", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("importance", array("allowed_values" => new ValueSetEnum('low,normal,high'), "sql" => "importance", "default_value" => 'normal', "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeBlob("html_template", array("is_null_allowed" => true, "depends_on" => array(), "always_load_in_tables" => false)));
MetaModel::Init_AddAttribute(new AttributeEnum("ignore_notify", array("allowed_values" => new ValueSetEnum('yes,no'), "sql" => "ignore_notify", "default_value" => 'yes', "is_null_allowed" => false, "depends_on" => array())));
// Display lists
// - Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array(
'col:col1' => array(
'fieldset:ActionEmail:main' => array(
0 => 'name',
1 => 'description',
2 => 'status',
3 => 'language',
4 => 'html_template',
5 => 'subject',
6 => 'body',
// 5 => 'importance', not handled when sending the mail, better hide it then
),
'fieldset:ActionEmail:trigger' => array(
0 => 'trigger_list',
1 => 'asynchronous'
),
),
'col:col2' => array(
'fieldset:ActionEmail:recipients' => array(
0 => 'from',
1 => 'from_label',
2 => 'reply_to',
3 => 'reply_to_label',
4 => 'test_recipient',
5 => 'ignore_notify',
6 => 'to',
7 => 'cc',
8 => 'bcc',
),
),
));
// - Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('status', 'to', 'subject', 'language'));
// Search criteria
// - Standard criteria of the search
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'status', 'subject', 'language'));
// - Default criteria for the search
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status', 'subject', 'language'));
}
// count the recipients found
protected $m_iRecipients;
// Errors management : not that simple because we need that function to be
// executed in the background, while making sure that any issue would be reported clearly
protected $m_aMailErrors; //array of strings explaining the issue
/**
* Return the list of emails as a string, or a detailed error description
*
* @param string $sRecipAttCode
* @param array $aArgs
*
* @return string
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
protected function FindRecipients($sRecipAttCode, $aArgs)
{
$oTrigger = $aArgs['trigger->object()'] ?? null;
$sOQL = $this->Get($sRecipAttCode);
if (utils::IsNullOrEmptyString($sOQL)) return '';
try {
$oSearch = DBObjectSearch::FromOQL($sOQL);
if ($this->Get('ignore_notify') === 'no') {
// In theory, it is possible to notify *any* kind of object,
// as long as there is an email attribute in the class
// So let's not assume that the selected class is a Person
$sFirstSelectedClass = $oSearch->GetClass();
if (MetaModel::IsValidAttCode($sFirstSelectedClass, 'notify')) {
$oSearch->AddCondition('notify', 'yes');
}
}
$oSearch->AllowAllData();
} catch (OQLException $e) {
$this->m_aMailErrors[] = "query syntax error for recipient '$sRecipAttCode'";
return $e->getMessage();
}
$sClass = $oSearch->GetClass();
// Determine the email attribute (the first one will be our choice)
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
if ($oAttDef instanceof AttributeEmailAddress) {
$sEmailAttCode = $sAttCode;
// we've got one, exit the loop
break;
}
}
if (!isset($sEmailAttCode)) {
$this->m_aMailErrors[] = "wrong target for recipient '$sRecipAttCode'";
return "The objects of the class '$sClass' do not have any email attribute";
}
if ($oTrigger !== null && in_array('Contact', MetaModel::EnumParentClasses($sClass, ENUM_CHILD_CLASSES_ALL), true)) {
$aArgs['trigger_id'] = $oTrigger->GetKey();
$aArgs['action_id'] = $this->GetKey();
$sSubscribedContactsOQL = \Combodo\iTop\Service\Notification\NotificationsRepository::GetInstance()->GetSearchOQLContactUnsubscribedByTriggerAndAction();
$sSubscribedContactsOQL->ApplyParameters($aArgs);
$sAlias = $oSearch->GetClassAlias();
$oSearch->AddConditionExpression(Expression::FromOQL("`$sAlias`.id NOT IN ($sSubscribedContactsOQL)"));
}
$oSet = new DBObjectSet($oSearch, array() /* order */, $aArgs);
$aRecipients = array();
while ($oObj = $oSet->Fetch()) {
$sAddress = trim($oObj->Get($sEmailAttCode));
if (utils::IsNotNullOrEmptyString($sAddress)) {
$aRecipients[] = $sAddress;
$this->m_iRecipients++;
}
if ($oTrigger !== null && in_array('Contact', MetaModel::EnumParentClasses($sClass, ENUM_CHILD_CLASSES_ALL), true)) {
\Combodo\iTop\Service\Notification\NotificationsService::GetInstance()->RegisterSubscription($oTrigger, $this, $oObj);
}
}
return implode(', ', $aRecipients);
}
/**
* @inheritDoc
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
*/
public function DoExecute($oTrigger, $aContextArgs)
{
if (MetaModel::IsLogEnabledNotification()) {
$oLog = new EventNotificationEmail();
if ($this->IsBeingTested()) {
$oLog->Set('message', 'TEST - Notification sent (' . $this->Get('test_recipient') . ')');
} else {
$oLog->Set('message', 'Notification pending');
}
$oLog->Set('userinfo', UserRights::GetUser());
$oLog->Set('trigger_id', $oTrigger->GetKey());
$oLog->Set('action_id', $this->GetKey());
$oLog->Set('object_id', $aContextArgs['this->object()']->GetKey());
$oLog->Set('object_class', get_class($aContextArgs['this->object()']));
// Must be inserted now so that it gets a valid id that will make the link
// between an eventual asynchronous task (queued) and the log
$oLog->DBInsertNoReload();
} else {
$oLog = null;
}
try {
$sRes = $this->_DoExecute($oTrigger, $aContextArgs, $oLog);
if ($this->IsBeingTested()) {
$sPrefix = 'TEST (' . $this->Get('test_recipient') . ') - ';
} else {
$sPrefix = '';
}
if ($oLog) {
$oLog->Set('message', $sPrefix . $sRes);
$oLog->DBUpdate();
}
} catch (Exception $e) {
if ($oLog) {
$oLog->Set('message', 'Error: ' . $e->getMessage());
try {
$oLog->DBUpdate();
} catch (Exception $eSecondTryUpdate) {
IssueLog::Error("Failed to process email " . $oLog->GetKey() . " - reason: " . $e->getMessage() . "\nTrace:\n" . $e->getTraceAsString());
$oLog->Set('message', 'Error: more details in the log for email "' . $oLog->GetKey() . '"');
$oLog->DBUpdate();
}
}
}
}
/**
* @param \Trigger $oTrigger
* @param array $aContextArgs
* @param \EventNotification $oLog
*
* @return string
* @throws \CoreException
* @throws \Exception
*/
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
{
$sStyles = file_get_contents(APPROOT . utils::GetCSSFromSASS("css/email.scss"));
$sStyles .= MetaModel::GetConfig()->Get('email_css');
$oEmail = new EMail();
$aEmailContent = $this->PrepareMessageContent($aContextArgs, $oLog);
$oEmail->SetSubject($aEmailContent['subject']);
$oEmail->SetBody($aEmailContent['body'], 'text/html', $sStyles);
$oEmail->SetRecipientTO($aEmailContent['to']);
$oEmail->SetRecipientCC($aEmailContent['cc']);
$oEmail->SetRecipientBCC($aEmailContent['bcc']);
$oEmail->SetRecipientFrom($aEmailContent['from'], $aEmailContent['from_label']);
$oEmail->SetRecipientReplyTo($aEmailContent['reply_to'], $aEmailContent['reply_to_label']);
$oEmail->SetReferences($aEmailContent['references']);
$oEmail->SetMessageId($aEmailContent['message_id']);
$oEmail->SetInReplyTo($aEmailContent['in_reply_to']);
foreach ($aEmailContent['attachments'] as $aAttachment) {
$oEmail->AddAttachment($aAttachment['data'], $aAttachment['filename'], $aAttachment['mime_type']);
}
if (empty($this->m_aMailErrors)) {
if ($this->m_iRecipients == 0) {
return 'No recipient';
} else {
$aErrors = [];
$iRes = $oEmail->Send($aErrors, $this->IsAsynchronous() ? Email::ENUM_SEND_FORCE_ASYNCHRONOUS : Email::ENUM_SEND_FORCE_SYNCHRONOUS, $oLog);
switch ($iRes) {
case EMAIL_SEND_OK:
return "Sent";
case EMAIL_SEND_PENDING:
return "Pending";
case EMAIL_SEND_ERROR:
return "Errors: " . implode(', ', $aErrors);
}
}
} else {
if (is_array($this->m_aMailErrors) && count($this->m_aMailErrors) > 0) {
$sError = implode(', ', $this->m_aMailErrors);
} else {
$sError = 'Unknown reason';
}
return 'Notification was not sent: ' . $sError;
}
}
/**
* @param array $aContextArgs
* @param \EventNotification $oLog
*
* @return array
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
* @throws \DictExceptionMissingString
* @throws \DictExceptionUnknownLanguage
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @since 3.1.0 N°918
*/
protected function PrepareMessageContent($aContextArgs, &$oLog): array
{
$aMessageContent = [
'to' => '',
'cc' => '',
'bcc' => '',
'from' => '',
'from_label' => '',
'reply_to' => '',
'reply_to_label' => '',
'subject' => '',
'body' => '',
'references' => '',
'message_id' => '',
'in_reply_to' => '',
'attachments' => [],
];
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();
[$sPreviousLanguage, $aPreviousPluginProperties] = $this->SetNotificationLanguage();
try {
$this->m_iRecipients = 0;
$this->m_aMailErrors = array();
// Determine recipients
//
$aMessageContent['to'] = $this->FindRecipients('to', $aContextArgs);
$aMessageContent['cc'] = $this->FindRecipients('cc', $aContextArgs);
$aMessageContent['bcc'] = $this->FindRecipients('bcc', $aContextArgs);
$aMessageContent['from'] = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
$aMessageContent['from_label'] = MetaModel::ApplyParams($this->Get('from_label'), $aContextArgs);
$aMessageContent['reply_to'] = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
$aMessageContent['reply_to_label'] = MetaModel::ApplyParams($this->Get('reply_to_label'), $aContextArgs);
$aMessageContent['subject'] = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
$sBody = $this->BuildMessageBody(false);
$aMessageContent['body'] = MetaModel::ApplyParams($sBody, $aContextArgs);
$oObj = $aContextArgs['this->object()'];
$aMessageContent['message_id'] = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_MESSAGE_ID);
$aMessageContent['references'] = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_REFERENCES);
} catch (Exception $e) {
/** @noinspection PhpUnhandledExceptionInspection */
throw $e;
} finally {
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
$this->SetNotificationLanguage($sPreviousLanguage, $aPreviousPluginProperties['language_code'] ?? null);
}
if (!is_null($oLog)) {
// Note: we have to secure this because those values are calculated
// inside the try statement, and we would like to keep track of as
// many data as we could while some variables may still be undefined
if (isset($aMessageContent['to'])) {
$oLog->Set('to', $aMessageContent['to']);
}
if (isset($aMessageContent['cc'])) {
$oLog->Set('cc', $aMessageContent['cc']);
}
if (isset($aMessageContent['bcc'])) {
$oLog->Set('bcc', $aMessageContent['bcc']);
}
if (isset($aMessageContent['from'])) {
$oLog->Set('from', $aMessageContent['from']);
}
if (isset($aMessageContent['subject'])) {
$oLog->Set('subject', $aMessageContent['subject']);
}
if (isset($aMessageContent['body'])) {
$oLog->Set('body', HTMLSanitizer::Sanitize($aMessageContent['body']));
}
}
if ($this->IsBeingTested()) {
$sTestBody = $aMessageContent['body'];
$sTestBody .= "<div style=\"border: dashed;\">\n";
$sTestBody .= "<h1>Testing email notification " . $this->GetHyperlink() . "</h1>\n";
$sTestBody .= "<p>The email should be sent with the following properties\n";
$sTestBody .= "<ul>\n";
$sTestBody .= "<li>TO: {$aMessageContent['to']}</li>\n";
$sTestBody .= "<li>CC: {$aMessageContent['cc']}</li>\n";
$sTestBody .= "<li>BCC: {$aMessageContent['bcc']}</li>\n";
$sTestBody .= empty($aMessageContent['from_label']) ? "<li>From: {$aMessageContent['from']}</li>\n" : "<li>From: {$aMessageContent['from_label']} &lt;{$aMessageContent['from']}&gt;</li>\n";
$sTestBody .= empty($aMessageContent['reply_to_label']) ? "<li>Reply-To: {$aMessageContent['reply_to']}</li>\n" : "<li>Reply-To: {$aMessageContent['reply_to_label']} &lt;{$aMessageContent['reply_to']}&gt;</li>\n";
$sTestBody .= "<li>References: {$aMessageContent['references']}</li>\n";
$sTestBody .= "</ul>\n";
$sTestBody .= "</p>\n";
$sTestBody .= "</div>\n";
$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'];
if (isset($aContextArgs['attachments'])) {
$aAttachmentReport = array();
/** @var \ormDocument $oDocument */
foreach ($aContextArgs['attachments'] as $oDocument) {
$aMessageContent['attachments'][] = ['data' => $oDocument->GetData(), 'filename' => $oDocument->GetFileName(), 'mime_type' => $oDocument->GetMimeType()];
$aAttachmentReport[] = array($oDocument->GetFileName(), $oDocument->GetMimeType(), strlen($oDocument->GetData() ?? ''));
}
$oLog->Set('attachments', $aAttachmentReport);
}
return $aMessageContent;
}
/**
* @param \DBObject $oObject
* @param string $sHeaderName {@see \ActionEmail::ENUM_HEADER_NAME_REFERENCES}, {@see \ActionEmail::ENUM_HEADER_NAME_MESSAGE_ID}
*
* @return string The formatted identifier for $sHeaderName based on $oObject
* @throws \Exception
* @since 3.1.0 N°4849
*/
protected function GenerateIdentifierForHeaders(DBObject $oObject, string $sHeaderName): string
{
$sObjClass = get_class($oObject);
$sObjId = $oObject->GetKey();
$sAppName = utils::Sanitize(ITOP_APPLICATION_SHORT, '', utils::ENUM_SANITIZATION_FILTER_VARIABLE_NAME);
switch ($sHeaderName) {
case static::ENUM_HEADER_NAME_MESSAGE_ID:
case static::ENUM_HEADER_NAME_REFERENCES:
// Prefix
$sPrefix = sprintf('%s_%s_%d', $sAppName, $sObjClass, $sObjId);
if ($sHeaderName === static::ENUM_HEADER_NAME_MESSAGE_ID) {
$sPrefix .= sprintf('_%F', microtime(true /* get as float*/));
}
// Suffix
$sSuffix = sprintf('@%s.openitop.org', MetaModel::GetEnvironmentId());
// Identifier
$sIdentifier = $sPrefix . $sSuffix;
if ($sHeaderName === static::ENUM_HEADER_NAME_REFERENCES) {
$sIdentifier = "<$sIdentifier>";
}
return $sIdentifier;
}
// Requested header name invalid
$sErrorMessage = sprintf('%s: Could not generate identifier for header "%s", only %s are supported', static::class, $sHeaderName, implode(' / ', [static::ENUM_HEADER_NAME_MESSAGE_ID, static::ENUM_HEADER_NAME_REFERENCES]));
IssueLog::Error($sErrorMessage, LogChannels::NOTIFICATIONS, [
'Object' => $sObjClass . '::' . $sObjId . ' (' . $oObject->GetRawName() . ')',
'Action' => get_class($this) . '::' . $this->GetKey() . ' (' . $this->GetRawName() . ')',
]);
throw new Exception($sErrorMessage);
}
/**
* Compose the body of the message from the 'body' attribute and the HTML template (if any)
* @param bool $bHighlightPlaceholders If true add some extra HTML around placeholders to highlight them
* @return string
* @since 3.1.0 N°4849
*/
protected function BuildMessageBody(bool $bHighlightPlaceholders = false): string
{
// Wrap content with a specific class in order to apply styles of HTML fields through the emogrifier (see `css/email.scss`)
$sBody = <<<HTML
<div class="email-is-html-content">
{$this->Get('body')}
</div>
HTML;
/** @var ormDocument $oHtmlTemplate */
$oHtmlTemplate = $this->Get('html_template');
if ($oHtmlTemplate && !$oHtmlTemplate->IsEmpty()) {
$sHtmlTemplate = $oHtmlTemplate->GetData();
if (false !== mb_strpos($sHtmlTemplate, static::TEMPLATE_BODY_CONTENT)) {
if ($bHighlightPlaceholders) {
// Highlight the $content$ block
$sBody = sprintf(static::CONTENT_HIGHLIGHT, $sBody);
}
$sBody = str_replace(static::TEMPLATE_BODY_CONTENT, $sBody, $oHtmlTemplate->GetData()); // str_replace is ok as long as both strings are well-formed UTF-8
} else {
$sBody = $oHtmlTemplate->GetData();
}
}
if ($bHighlightPlaceholders) {
// Highlight all placeholders
$sBody = preg_replace('/\\$([^$]+)\\$/', static::FIELD_HIGHLIGHT, $sBody);
}
return $sBody;
}
/**
* @since 3.1.0 N°4849
* @inheritDoc
* @see cmdbAbstractObject::DisplayBareRelations()
*/
public function DisplayBareRelations(\Combodo\iTop\Application\WebPage\WebPage $oPage, $bEditMode = false)
{
parent::DisplayBareRelations($oPage, false);
if (!$bEditMode) {
$oPage->SetCurrentTab('action_email__preview', Dict::S('ActionEmail:preview_tab'), Dict::S('ActionEmail:preview_tab+'));
$sBody = $this->BuildMessageBody(true);
\Combodo\iTop\Application\TwigBase\Twig\TwigHelper::RenderIntoPage($oPage, APPROOT . '/', 'templates/datamodel/ActionEmail/email-notification-preview', ['iframe_content' => $sBody]);
}
}
/**
* @since 3.1.0
* @inheritDoc
* @see cmdbAbstractObject::DoCheckToWrite()
*/
public function DoCheckToWrite()
{
parent::DoCheckToWrite();
$oHtmlTemplate = $this->Get('html_template');
if ($oHtmlTemplate && !$oHtmlTemplate->IsEmpty()) {
if (false === mb_strpos($oHtmlTemplate->GetData(), static::TEMPLATE_BODY_CONTENT)) {
$this->m_aCheckWarnings[] = Dict::Format('ActionEmail:content_placeholder_missing', static::TEMPLATE_BODY_CONTENT, Dict::S('Class:ActionEmail/Attribute:body'));
}
}
}
/**
* @inheritDoc
* @since 3.2.0
*/
public static function GetAsynchronousGlobalSetting(): bool
{
return utils::GetConfig()->Get('email_asynchronous');
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* A notification
*
* @package iTopORM
*/
abstract class ActionNotification extends Action
{
/**
* @inheritDoc
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "name",
"complementary_name_attcode" => ['finalclass', 'description'],
"state_attcode" => "",
"reconc_keys" => ['name'],
"db_table" => "priv_action_notification",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
// - Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list'));
// - Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('finalclass', 'description', 'status'));
MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", array("sql" => "language", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Search criteria
// - Criteria of the std search form
// MetaModel::Init_SetZListItems('standard_search', array('name'));
// - Default criteria of the search form
// MetaModel::Init_SetZListItems('default_search', array('name'));
}
/**
* @param $sLanguage
* @param $sLanguageCode
*
* @return array [$sPreviousLanguage, $aPreviousPluginProperties]
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \DictExceptionUnknownLanguage
* @since 3.2.0
*/
public function SetNotificationLanguage($sLanguage = null, $sLanguageCode = null)
{
$sPreviousLanguage = Dict::GetUserLanguage();
$aPreviousPluginProperties = ApplicationContext::GetPluginProperties('QueryLocalizerPlugin');
$sLanguage = $sLanguage ?? $this->Get('language');
$sLanguageCode = $sLanguageCode ?? $sLanguage;
if (!utils::IsNullOrEmptyString($sLanguage)) {
// If a language is specified for this action, force this language
// when rendering all placeholders inside this message
Dict::SetUserLanguage($sLanguage);
AttributeDateTime::LoadFormatFromConfig();
ApplicationContext::SetPluginProperty('QueryLocalizerPlugin', 'language_code', $sLanguageCode);
}
return [$sPreviousLanguage, $aPreviousPluginProperties];
}
}

View File

@@ -0,0 +1,137 @@
<?php
/**
* A user defined trigger, to customize the application
* A trigger will activate an action
*
* @package iTopORM
*/
abstract class Trigger extends cmdbAbstractObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-conflict.svg'),
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("action_list",
array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "trigger_id", "ext_key_to_remote" => "action_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array())));
$aTags = ContextTag::GetTags();
MetaModel::Init_AddAttribute(new AttributeEnumSet("context", array("allowed_values" => null, "possible_values" => new ValueSetEnumPadded($aTags, true), "sql" => "context", "depends_on" => array(), "is_null_allowed" => true, "max_items" => 12)));
// "complement" is a computed field, fed by Trigger sub-classes, in general in ComputeValues method, for eg. the TriggerOnObject fed it with target_class info
MetaModel::Init_AddAttribute(new AttributeString("complement", array("allowed_values" => null, "sql" => "complement", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("subscription_policy", array("allowed_values" => new ValueSetEnum(Combodo\iTop\Core\Trigger\Enum\SubscriptionPolicy::cases()), "sql" => "subscription_policy", "default_value" => \Combodo\iTop\Core\Trigger\Enum\SubscriptionPolicy::AllowNoChannel->value, "is_null_allowed" => false, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('finalclass', 'description', 'context', 'subscription_policy', 'action_list', 'complement')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'complement')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
/**
* Check if the trigger can be used in the current context
*
* @return bool true if context OK
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function IsContextValid()
{
// Check the context
$oContext = $this->Get('context');
$bChecked = false;
$bValid = false;
foreach ($oContext->GetValues() as $sValue) {
$bChecked = true;
if (ContextTag::Check($sValue)) {
$bValid = true;
break;
}
}
if ($bChecked && !$bValid) {
// Trigger does not match the current context
return false;
}
return true;
}
/**
* @param $aContextArgs
*
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function DoActivate($aContextArgs)
{
// Check the context
if (!$this->IsContextValid()) {
// Trigger does not match the current context
$sClass = get_class($this);
$sName = $this->Get('friendlyname');
IssueLog::Debug("Context NOT valid for : {$sClass} '$sName'");
return;
}
$aContextArgs['trigger->object()'] = $this;
// Find the related actions
$oLinkedActions = $this->Get('action_list');
// Order actions as expected
$aActionListOrdered = [];
while ($oLink = $oLinkedActions->Fetch()) {
$aActionListOrdered[(int)$oLink->Get('order')][] = $oLink;
}
ksort($aActionListOrdered);
// Execute actions
foreach ($aActionListOrdered as $aActionSubList) {
foreach ($aActionSubList as $oLink) /** @var \DBObject $oLink */ {
/** @var \DBObject $oLink */
$iActionId = $oLink->Get('action_id');
/** @var \Action $oAction */
$oAction = MetaModel::GetObject('Action', $iActionId);
if ($oAction->IsActive()) {
$oKPI = new ExecutionKPI();
$aContextArgs['action->object()'] = $oAction;
$oAction->DoExecute($this, $aContextArgs);
$oKPI->ComputeStatsForExtension($oAction, 'DoExecute');
}
}
}
}
/**
* Check whether the given object is in the scope of this trigger
* and can potentially be the subject of notifications
*
* @param DBObject $oObject The object to check
*
* @return bool
*/
public function IsInScope(DBObject $oObject)
{
// By default the answer is no
// Overload this function in your own derived class for a different behavior
return false;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Class TriggerOnAttributeBlobDownload
*
* @since 3.1.0
*/
class TriggerOnAttributeBlobDownload extends TriggerOnObject
{
/**
* @inheritDoc
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onattblobdownload",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
}
}

View File

@@ -0,0 +1,229 @@
<?php
/**
* Class TriggerOnObject
*/
abstract class TriggerOnObject extends Trigger
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onobject",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeClass("target_class",
array("class_category" => "bizmodel", "more_values" => "User,UserExternal,UserInternal,UserLDAP,UserLocal", "sql" => "target_class", "default_value" => null, "is_null_allowed" => false, "depends_on" => array(), "class_exclusion_list" => "Attachment")));
MetaModel::Init_AddAttribute(new AttributeOQL("filter", array("allowed_values" => null, "sql" => "filter", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'description')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('default_search', array('description', 'target_class')); // Default criteria of the search banner
// MetaModel::Init_SetZListItems('standard_search', array('name', 'target_class', 'description')); // Criteria of the search form
}
/**
* @throws \CoreException
*/
public function DoCheckToWrite()
{
parent::DoCheckToWrite();
$sFilter = trim($this->Get('filter') ?? '');
if (strlen($sFilter) > 0) {
try {
$oSearch = DBObjectSearch::FromOQL($sFilter);
if (!MetaModel::IsParentClass($this->Get('target_class'), $oSearch->GetClass())) {
$this->m_aCheckIssues[] = Dict::Format('TriggerOnObject:WrongFilterClass', $this->Get('target_class'));
}
} catch (OqlException $e) {
$this->m_aCheckIssues[] = Dict::Format('TriggerOnObject:WrongFilterQuery', $e->getMessage());
}
}
}
/**
* @throws \CoreException
*/
public function ComputeValues()
{
parent::ComputeValues();
// Complementary name of a Trigger is manually built
// - the Trigger finalclass code not translated
// - an hardcoded text in english
// - the target class code not translated for TriggerOnObject subclasses
$this->Set('complement', 'class restriction: ' . $this->Get('target_class'));
}
/**
* Check whether the given object is in the scope of this trigger
* and can potentially be the subject of notifications
*
* @param DBObject $oObject The object to check
*
* @return bool
* @throws \CoreException
*/
public function IsInScope(DBObject $oObject)
{
$sRootClass = $this->Get('target_class');
return ($oObject instanceof $sRootClass);
}
/**
* @param $aContextArgs
*
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function DoActivate($aContextArgs)
{
$bGo = true;
if (isset($aContextArgs['this->object()'])) {
/** @var \DBObject $oObject */
$oObject = $aContextArgs['this->object()'];
$bGo = $this->IsTargetObject($oObject->GetKey(), $oObject->ListPreviousValuesForUpdatedAttributes());
}
if ($bGo) {
parent::DoActivate($aContextArgs);
}
}
/**
* if the target class is Attachment, then the trigger is read-only
* @param $sAttCode
* @param $aReasons
* @param $sTargetState
* @return int
* @throws ArchivedObjectException
* @throws CoreException
*/
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
{
// Force the computed field to be read-only, preventing it to be written
if ($this->Get('target_class') == 'Attachment') {
return OPT_ATT_READONLY;
}
return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
}
public function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
{
$aHeaderBlocks = parent::DisplayBareHeader($oPage, $bEditMode);
if ($this->Get('target_class') == 'Attachment') {
$oPage->AddUiBlock(\Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory::MakeForWarning('', Dict::S('Class:TriggerOnObject:TriggerClassAttachment/ReadOnlyMessage')));
$oPage->add_ready_script("$('#UIMenuModify').hide();");
}
return $aHeaderBlocks;
}
/**
* Activate trigger based on attribute list given instead of changed attributes
*
* @param array $aContextArgs
* @param array|null $aAttributes if null default to changed attributes
*
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @since 3.1.1 3.2.0 N°6228
*/
public function DoActivateForSpecificAttributes(array $aContextArgs, ?array $aAttributes)
{
if (isset($aContextArgs['this->object()'])) {
/** @var \DBObject $oObject */
$oObject = $aContextArgs['this->object()'];
if (is_null($aAttributes)) {
$aChanges = $oObject->ListPreviousValuesForUpdatedAttributes();
} else {
$aChanges = array_fill_keys($aAttributes, true);
}
if (false === $this->IsTargetObject($oObject->GetKey(), $aChanges)) {
return;
}
}
parent::DoActivate($aContextArgs);
}
/**
* @param $iObjectId
* @param array $aChanges
*
* @return bool True if the object of ID $iObjectId is within the scope of the OQL defined by the "filter" attribute
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
public function IsTargetObject($iObjectId, $aChanges = array())
{
$sFilter = trim($this->Get('filter') ?? '');
if (strlen($sFilter) > 0) {
$oSearch = DBObjectSearch::FromOQL($sFilter);
$oSearch->AddCondition('id', $iObjectId, '=');
$oSearch->AllowAllData();
$oSet = new DBObjectSet($oSearch);
$bRet = ($oSet->Count() > 0);
} else {
$bRet = true;
}
return $bRet;
}
/**
* @param Exception $oException
* @param \DBObject $oObject
*
* @return void
*
* @uses \IssueLog::Error()
*
* @since 2.7.9 3.0.3 3.1.0 N°5893
*/
public function LogException($oException, $oObject)
{
$sObjectKey = $oObject->GetKey(); // if object wasn't persisted yet, then we'll have a negative value
$aContext = [
'exception.class' => get_class($oException),
'exception.message' => $oException->getMessage(),
'trigger.class' => get_class($this),
'trigger.id' => $this->GetKey(),
'trigger.friendlyname' => $this->GetRawName(),
'object.class' => get_class($oObject),
'object.id' => $sObjectKey,
'object.friendlyname' => $oObject->GetRawName(),
'current_user' => UserRights::GetUser(),
'exception.stack' => $oException->getTraceAsString(),
];
IssueLog::Error('A trigger did throw an exception', null, $aContext);
}
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* Class TriggerOnObjectCreate
*/
class TriggerOnObjectCreate extends TriggerOnObject
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onobjcreate",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* Class TriggerOnObjectCreate
*/
class TriggerOnObjectDelete extends TriggerOnObject
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onobjdelete",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* Class TriggerOnObjectMention
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @since 3.0.0
*/
class TriggerOnObjectMention extends TriggerOnObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onobjmention",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeOQL("mentioned_filter", array("allowed_values" => null, "sql" => "mentioned_filter", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'mentioned_filter', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
}
/**
* @param \DBObject $oObject
*
* @return bool True if $oObject is within the scope of the OQL defined by the "mentioned_filter" attribute OR if no mentioned_filter defined. Otherwise, returns false.
*
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
public function IsMentionedObjectInScope(DBObject $oObject)
{
$sFilter = trim($this->Get('mentioned_filter'));
if (strlen($sFilter) > 0) {
$oSearch = DBObjectSearch::FromOQL($sFilter);
$sSearchClass = $oSearch->GetClass();
// If filter not on current object class (or descendants), consider it as not in scope
if (is_a($oObject, $sSearchClass, true) === false) {
return false;
}
$oSearch->AddCondition('id', $oObject->GetKey(), '=');
if (MetaModel::IsAbstract($oSearch->GetClass())) {
$oSearch->AddCondition('finalclass', get_class($oObject), '=');
}
$aParams = $oObject->ToArgs('this');
$oSet = new DBObjectSet($oSearch, [], $aParams);
$bRet = $oSet->CountExceeds(0);
} else {
$bRet = true;
}
return $bRet;
}
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* Class TriggerOnObjectCreate
*/
class TriggerOnObjectUpdate extends TriggerOnObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onobjupdate",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeClassAttCodeSet('target_attcodes', array("allowed_values" => null, "class_field" => "target_class", "sql" => "target_attcodes", "default_value" => null, "is_null_allowed" => true, "max_items" => 20, "min_items" => 0, "attribute_definition_exclusion_list" => "AttributeDashboard,AttributeExternalField,AttributeFinalClass,AttributeFriendlyName,AttributeObsolescenceDate,AttributeObsolescenceFlag,AttributeSubItem", "attribute_definition_list" => null, "depends_on" => array('target_class'))));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'target_attcodes', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
}
public function IsTargetObject($iObjectId, $aChanges = array())
{
if (!parent::IsTargetObject($iObjectId, $aChanges)) {
return false;
}
// Check the attribute
$oAttCodeSet = $this->Get('target_attcodes');
$aAttCodes = $oAttCodeSet->GetValues();
if (empty($aAttCodes)) {
return true;
}
foreach ($aAttCodes as $sAttCode) {
if (array_key_exists($sAttCode, $aChanges)) {
return true;
}
}
return false;
}
public function ComputeValues()
{
parent::ComputeValues();
// Remove unwanted attribute codes
$aChanges = $this->ListChanges();
if (isset($aChanges['target_attcodes'])) {
$oAttDef = MetaModel::GetAttributeDef(get_class($this), 'target_attcodes');
$aArgs = array('this' => $this);
$aAllowedValues = $oAttDef->GetAllowedValues($aArgs);
/** @var \ormSet $oValue */
$oValue = $this->Get('target_attcodes');
$aValues = $oValue->GetValues();
$bChanged = false;
foreach ($aValues as $key => $sValue) {
if (!isset($aAllowedValues[$sValue])) {
unset($aValues[$key]);
$bChanged = true;
}
}
if ($bChanged) {
$oValue->SetValues($aValues);
$this->Set('target_attcodes', $oValue);
}
}
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* To trigger notifications when a ticket is updated from the portal
*/
class TriggerOnPortalUpdate extends TriggerOnObject
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onportalupdate",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'description')); // Attributes to be displayed for a list
// Search criteria
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* Class TriggerOnStateChange
*/
abstract class TriggerOnStateChange extends TriggerOnObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onstatechange",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeClassState("state", array("class_field" => 'target_class', "allowed_values" => null, "sql" => "state", "default_value" => null, "is_null_allowed" => false, "depends_on" => array('target_class'))));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'state', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'state')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class', 'state')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* Class TriggerOnStateEnter
*/
class TriggerOnStateEnter extends TriggerOnStateChange
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onstateenter",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'state', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('target_class', 'state')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class', 'state')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* Class TriggerOnStateLeave
*/
class TriggerOnStateLeave extends TriggerOnStateChange
{
/**
* @throws \CoreException
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onstateleave",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'state', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('target_class', 'state')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class', 'state')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('')); // Criteria of the advanced search form
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Class TriggerOnThresholdReached
*/
class TriggerOnThresholdReached extends TriggerOnObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_threshold",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeClassAttCodeSet('stop_watch_code', array(
"allowed_values" => null,
"class_field" => "target_class",
"sql" => "stop_watch_code",
"default_value" => null,
"is_null_allowed" => false,
"max_items" => 1,
"min_items" => 1,
"attribute_definition_exclusion_list" => null,
"attribute_definition_list" => "AttributeStopWatch",
"include_child_classes_attributes" => true,
"depends_on" => array('target_class'),
)));
MetaModel::Init_AddAttribute(new AttributeString("threshold_index", array("allowed_values" => null, "sql" => "threshold_index", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'stop_watch_code', 'threshold_index', 'filter', 'subscription_policy', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('target_class', 'threshold_index', 'threshold_index')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Class lnkTriggerAction
*/
class lnkTriggerAction extends cmdbAbstractObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array('action_id', 'trigger_id'),
"db_table" => "priv_link_action_trigger",
"db_key_field" => "link_id",
"db_finalclass_field" => "",
"is_link" => true,
'uniqueness_rules' => array(
'no_duplicate' => array(
'attributes' => array(
0 => 'action_id',
1 => 'trigger_id',
),
'filter' => '',
'disabled' => false,
'is_blocking' => true,
),
),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass" => "Action", "jointype" => '', "allowed_values" => null, "sql" => "action_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("action_name", array("allowed_values" => null, "extkey_attcode" => 'action_id', "target_attcode" => "name")));
MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass" => "Trigger", "jointype" => '', "allowed_values" => null, "sql" => "trigger_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("trigger_name", array("allowed_values" => null, "extkey_attcode" => 'trigger_id', "target_attcode" => "description")));
MetaModel::Init_AddAttribute(new AttributeInteger("order", array("allowed_values" => null, "sql" => "order", "default_value" => 0, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('action_id', 'trigger_id', 'order')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('action_id', 'trigger_id', 'order')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('action_id', 'trigger_id', 'order')); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', array('action_id', 'trigger_id', 'order')); // Criteria of the advanced search form
}
}

View File

@@ -0,0 +1,13 @@
<?php
/**
* Interface for encryption engines
*/
interface CryptEngine
{
public static function GetNewDefaultParams();
function Encrypt($key, $sString);
function Decrypt($key, $encrypted_data);
}

View File

@@ -0,0 +1,163 @@
<?php
/**
* SimpleCrypt Class - crypto helpers
* Simple encryption of strings, uses mcrypt or degrades to a pure PHP
* implementation when mcrypt is not present.
* Based on Miguel Ros' work found at:
* http://rossoft.wordpress.com/2006/05/22/simple-encryption-class/
*
* Usage:
* $oSimpleCrypt = new SimpleCrypt();
* $encrypted = $oSimpleCrypt->encrypt('a_key','the_text');
* $sClearText = $oSimpleCrypt->decrypt('a_key',$encrypted);
*
* The result is $plain equals to 'the_text'
*
* You can use a different engine if you don't have Mcrypt:
* $oSimpleCrypt = new SimpleCrypt('Simple');
*
* A string encrypted with one engine can't be decrypted with
* a different one even if the key is the same.
*
* @author Miguel Ros <rossoft@gmail.com>
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
class SimpleCrypt
{
/**
* @var \SimpleCrypt
* @since 3.1.0 N°5388
*/
protected $oEngine;
public static function GetNewDefaultParams()
{
if (function_exists('sodium_crypto_secretbox_open') && function_exists('random_bytes')) {
$sEngineName = 'Sodium';
} else if (function_exists('openssl_decrypt')) {
$sEngineName = 'OpenSSL';
} else if (function_exists('mcrypt_module_open')) {
$sEngineName = 'Mcrypt';
} else {
$sEngineName = 'Simple';
}
$sEngineName = 'SimpleCrypt' . $sEngineName . 'Engine';
return $sEngineName::GetNewDefaultParams();
}
/**
* Constructor
* @param string $sEngineName Engine for encryption. Values: Simple, Mcrypt, Sodium or OpenSSL
* @throws Exception This library is unkown
*/
function __construct($sEngineName = 'Mcrypt')
{
switch ($sEngineName) {
case 'Sodium':
if (!function_exists('sodium_crypto_secretbox_open')) {
$sEngineName = 'Simple';
}
break;
case 'Mcrypt':
if (!function_exists('mcrypt_module_open')) {
if (function_exists('openssl_decrypt')) {
$sEngineName = 'OpenSSLMcryptCompatibility';
} else {
$sEngineName = 'Simple';
}
}
break;
case 'OpenSSL':
case 'OpenSSLMcryptCompatibility':
if (!function_exists('openssl_decrypt')) {
$sEngineName = 'Simple';
}
break;
case 'Simple':
break;
default:
throw new Exception(Dict::Format("Core:AttributeEncryptUnknownLibrary", $sEngineName));
}
$sEngineName = 'SimpleCrypt' . $sEngineName . 'Engine';
$this->oEngine = new $sEngineName;
}
/**
* Encrypts the string with the given key
* @param string $key
* @param string $sString Plaintext string
* @return string Ciphered string
*/
function Encrypt($key, $sString)
{
return $this->oEngine->Encrypt($key, $sString);
}
/**
* Decrypts the string by the given key
* @param string $key
* @param string $string Ciphered string
* @return string Plaintext string
*/
function Decrypt($key, $string)
{
return $this->oEngine->Decrypt($key, $string);
}
/**
* Returns a random "salt" value, to be used when "hashing" a password
* using a one-way encryption algorithm, to prevent an attack using a "rainbow table"
* Tryes to use the best available random number generator
* @return string The generated random "salt"
*/
static function GetNewSalt()
{
// Copied from http://www.php.net/manual/en/function.mt-rand.php#83655
// get 128 pseudorandom bits in a string of 16 bytes
$sRandomBits = null;
// Unix/Linux platform?
$fp = @fopen('/dev/urandom', 'rb');
if ($fp !== FALSE) {
//echo "Random bits pulled from /dev/urandom<br/>\n";
$sRandomBits .= @fread($fp, 16);
@fclose($fp);
} else {
// MS-Windows platform?
if (@class_exists('COM')) {
// http://msdn.microsoft.com/en-us/library/aa388176(VS.85).aspx
try {
$CAPI_Util = new COM('CAPICOM.Utilities.1');
$sBase64RandomBits = '' . $CAPI_Util->GetRandom(16, 0);
// if we ask for binary data PHP munges it, so we
// request base64 return value. We squeeze out the
// redundancy and useless ==CRLF by hashing...
if ($sBase64RandomBits) {
//echo "Random bits got from CAPICOM.Utilities.1<br/>\n";
$sRandomBits = md5($sBase64RandomBits, TRUE);
}
} catch (Exception $ex) {
// echo 'Exception: ' . $ex->getMessage();
}
}
}
if ($sRandomBits == null) {
// No "strong" random generator available, use PHP's built-in mechanism
//echo "Random bits generated from mt_rand<br/>\n";
mt_srand(crc32(microtime()));
$sRandomBits = '';
for ($i = 0; $i < 4; $i++) {
$sRandomBits .= sprintf('%04x', mt_rand(0, 65535));
}
}
return $sRandomBits;
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* McryptEngine requires Mcrypt extension
* Every encryption of the same string with the same key
* will return a different encrypted string.
*/
class SimpleCryptMcryptEngine implements CryptEngine
{
var $alg = MCRYPT_BLOWFISH;
var $td = null;
public static function GetNewDefaultParams()
{
return array('lib' => 'Mcrypt', 'key' => null);
}
public function __construct()
{
$this->td = mcrypt_module_open($this->alg, '', 'cbc', '');
}
public function Encrypt($key, $sString)
{
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($this->td), MCRYPT_DEV_URANDOM); // MCRYPT_DEV_URANDOM is now useable since itop requires php >= 5.6
if (false === $iv) {
throw new Exception('IV generation failed');
}
mcrypt_generic_init($this->td, $key, $iv);
if (empty($sString)) {
$sString = str_repeat("\0", 8);
}
$encrypted_data = mcrypt_generic($this->td, $sString);
mcrypt_generic_deinit($this->td);
return $iv . $encrypted_data;
}
public function Decrypt($key, $encrypted_data)
{
$iv = substr($encrypted_data, 0, mcrypt_enc_get_iv_size($this->td));
$string = substr($encrypted_data, mcrypt_enc_get_iv_size($this->td));
$r = mcrypt_generic_init($this->td, $key, $iv);
if (($r < 0) || ($r === false)) {
$decrypted_data = Dict::S("Core:AttributeEncryptFailedToDecrypt");
} else {
$decrypted_data = rtrim(mdecrypt_generic($this->td, $string), "\0");
mcrypt_generic_deinit($this->td);
}
return $decrypted_data;
}
public function __destruct()
{
mcrypt_module_close($this->td);
}
}

View File

@@ -0,0 +1,30 @@
<?php
class SimpleCryptOpenSSLEngine implements CryptEngine
{
public static function GetNewDefaultParams()
{
return array('lib' => 'OpenSSL', 'key' => bin2hex(openssl_random_pseudo_bytes(32)));
}
public function Encrypt($key, $sString)
{
$key = hex2bin($key);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("AES-256-CBC"));
$encrypted_string = openssl_encrypt($sString, "AES-256-CBC", $key, 0, $iv);
return $iv . $encrypted_string;
}
public function Decrypt($key, $encrypted_data)
{
$key = hex2bin($key);
$iv = mb_substr($encrypted_data, 0, openssl_cipher_iv_length("AES-256-CBC"), '8bit');
$encrypted_data = mb_substr($encrypted_data, openssl_cipher_iv_length("AES-256-CBC"), null, '8bit');
$plaintext = openssl_decrypt($encrypted_data, "AES-256-CBC", $key, 0, $iv);
if ($plaintext === false) {
$plaintext = Dict::S("Core:AttributeEncryptFailedToDecrypt");
}
return trim($plaintext);
}
}

View File

@@ -0,0 +1,52 @@
<?php
class SimpleCryptOpenSSLMcryptCompatibilityEngine implements CryptEngine
{
public static function GetNewDefaultParams()
{
return array('lib' => 'OpenSSLMcryptCompatibility', 'key' => null);
}
//fix for php < 7.1.8 (keys are Zero padded instead of cycle padded)
static private function MakeOpenSSLBlowfishKey($key)
{
if ("$key" === '') {
return $key;
}
$len = (16 + 2) * 4;
while (strlen($key) < $len) {
$key .= $key;
}
$key = substr($key, 0, $len);
return $key;
}
public function Encrypt($key, $sString)
{
$key = SimpleCryptOpenSSLMcryptCompatibilityEngine::MakeOpenSSLBlowfishKey($key);
$blockSize = 8;
$len = strlen($sString);
$paddingLen = intval(($len + $blockSize - 1) / $blockSize) * $blockSize - $len;
$padding = str_repeat("\0", $paddingLen);
$sData = $sString.$padding;
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("BF-CBC"));
$encrypted_string = openssl_encrypt($sData, "BF-CBC", $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
return $iv.$encrypted_string;
}
public function Decrypt($key, $encrypted_data)
{
$key = SimpleCryptOpenSSLMcryptCompatibilityEngine::MakeOpenSSLBlowfishKey($key);
$iv = mb_substr($encrypted_data, 0, openssl_cipher_iv_length("BF-CBC"), '8bit');
$encrypted_data = mb_substr($encrypted_data, openssl_cipher_iv_length("BF-CBC"), null, '8bit');
$plaintext = openssl_decrypt($encrypted_data, "BF-CBC", $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
if ($plaintext === false) {
$plaintext = Dict::S("Core:AttributeEncryptFailedToDecrypt");
}
return trim($plaintext);
}
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* Simple Engine doesn't need any PHP extension.
* Every encryption of the same string with the same key
* will return the same encrypted string
*/
class SimpleCryptSimpleEngine implements CryptEngine
{
public static function GetNewDefaultParams()
{
return array('lib' => 'Simple', 'key' => null);
}
public function Encrypt($key, $sString)
{
$result = '';
for ($i = 1; $i <= strlen($sString); $i++) {
$char = substr($sString, $i - 1, 1);
$keychar = substr($key, ($i % strlen($key)) - 1, 1);
$char = chr(ord($char) + ord($keychar));
$result .= $char;
}
return $result;
}
public function Decrypt($key, $encrypted_data)
{
$result = '';
for ($i = 1; $i <= strlen($encrypted_data); $i++) {
$char = substr($encrypted_data, $i - 1, 1);
$keychar = substr($key, ($i % strlen($key)) - 1, 1);
$char = chr(ord($char) - ord($keychar));
$result .= $char;
}
return $result;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* SodiumEngine requires Sodium extension
* Every encryption of the same string with the same key
* will return a different encrypted string.
* The key has to be SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes long.
*/
class SimpleCryptSodiumEngine implements CryptEngine
{
public static function GetNewDefaultParams()
{
return array('lib' => 'Sodium', 'key' => bin2hex(sodium_crypto_secretbox_keygen()));
}
public function Encrypt($key, $sString)
{
$key = hex2bin($key);
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$encrypted_string = sodium_crypto_secretbox($sString, $nonce, $key);
sodium_memzero($sString);
sodium_memzero($key);
return base64_encode($nonce . $encrypted_string);
}
public function Decrypt($key, $encrypted_data)
{
$key = hex2bin($key);
$encrypted_data = base64_decode($encrypted_data);
$nonce = mb_substr($encrypted_data, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$encrypted_data = mb_substr($encrypted_data, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
$plaintext = sodium_crypto_secretbox_open($encrypted_data, $nonce, $key);
if ($plaintext === false) {
$plaintext = Dict::S("Core:AttributeEncryptFailedToDecrypt");
}
sodium_memzero($encrypted_data);
sodium_memzero($key);
return $plaintext;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* An attibute that matches one of the language codes availables in the dictionnary
*
* @package iTopORM
*/
class AttributeApplicationLanguage extends AttributeString
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
public static function ListExpectedParams()
{
return parent::ListExpectedParams();
}
public function __construct($sCode, $aParams)
{
$this->m_sCode = $sCode;
$aAvailableLanguages = Dict::GetLanguages();
$aLanguageCodes = array();
foreach ($aAvailableLanguages as $sLangCode => $aInfo) {
$aLanguageCodes[$sLangCode] = $aInfo['description'] . ' (' . $aInfo['localized_description'] . ')';
}
// N°6462 This should be sorted directly in \Dict during the compilation but we can't for 2 reasons:
// - Additional languages can be added on the fly even though it is not recommended
// - Formatting is done at run time (just above)
natcasesort($aLanguageCodes);
$aParams["allowed_values"] = new ValueSetEnum($aLanguageCodes);
parent::__construct($sCode, $aParams);
}
public function RequiresIndex()
{
return true;
}
public function GetBasicFilterLooseOperator()
{
return '=';
}
}

View File

@@ -0,0 +1,35 @@
<?php
class AttributeArchiveDate extends AttributeDate
{
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public function GetLabel($sDefault = null)
{
$sDefault = Dict::S('Core:AttributeArchiveDate/Label', $sDefault);
return parent::GetLabel($sDefault);
}
public function GetDescription($sDefault = null)
{
$sDefault = Dict::S('Core:AttributeArchiveDate/Label+', $sDefault);
return parent::GetDescription($sDefault);
}
}

View File

@@ -0,0 +1,49 @@
<?php
class AttributeArchiveFlag extends AttributeBoolean
{
public function __construct($sCode)
{
parent::__construct($sCode, array(
"allowed_values" => null,
"sql" => $sCode,
"default_value" => false,
"is_null_allowed" => false,
"depends_on" => array()
));
}
public function RequiresIndex()
{
return true;
}
public function CopyOnAllTables()
{
return true;
}
public function IsWritable()
{
return false;
}
public function IsMagic()
{
return true;
}
public function GetLabel($sDefault = null)
{
$sDefault = Dict::S('Core:AttributeArchiveFlag/Label', $sDefault);
return parent::GetLabel($sDefault);
}
public function GetDescription($sDefault = null)
{
$sDefault = Dict::S('Core:AttributeArchiveFlag/Label+', $sDefault);
return parent::GetDescription($sDefault);
}
}

View File

@@ -0,0 +1,367 @@
<?php
/**
* A blob is an ormDocument, it is stored as several columns in the database
*
* @package iTopORM
*/
class AttributeBlob extends AttributeDefinition
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("depends_on"));
}
public function GetEditClass()
{
return "Document";
}
public static function IsBasedOnDBColumns()
{
return true;
}
public static function IsScalar()
{
return true;
}
public function IsWritable()
{
return true;
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
return new ormDocument('', '', '');
}
public function IsNullAllowed(DBObject $oHostObject = null)
{
return $this->GetOptional("is_null_allowed", false);
}
public function GetEditValue($sValue, $oHostObj = null)
{
return '';
}
/**
* {@inheritDoc}
*
* @param string $proposedValue Can be an URL (including an URL to iTop itself), or a local path (CSV import)
*
* @see AttributeDefinition::MakeRealValue()
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
if ($proposedValue === null) {
return null;
}
if (is_object($proposedValue)) {
$proposedValue = clone $proposedValue;
} else {
try {
// Read the file from iTop, an URL (or the local file system - for admins only)
$proposedValue = Utils::FileGetContentsAndMIMEType($proposedValue);
} catch (Exception $e) {
IssueLog::Warning(get_class($this) . "::MakeRealValue - " . $e->getMessage());
// Not a real document !! store is as text !!! (This was the default behavior before)
$proposedValue = new ormDocument($e->getMessage() . " \n" . $proposedValue, 'text/plain');
}
}
return $proposedValue;
}
public function GetSQLExpressions($sPrefix = '')
{
if ($sPrefix == '') {
$sPrefix = $this->GetCode();
}
$aColumns = array();
// Note: to optimize things, the existence of the attribute is determined by the existence of one column with an empty suffix
$aColumns[''] = $sPrefix . '_mimetype';
$aColumns['_data'] = $sPrefix . '_data';
$aColumns['_filename'] = $sPrefix . '_filename';
$aColumns['_downloads_count'] = $sPrefix . '_downloads_count';
return $aColumns;
}
public function FromSQLToValue($aCols, $sPrefix = '')
{
if (!array_key_exists($sPrefix, $aCols)) {
$sAvailable = implode(', ', array_keys($aCols));
throw new MissingColumnException("Missing column '$sPrefix' from {$sAvailable}");
}
$sMimeType = isset($aCols[$sPrefix]) ? $aCols[$sPrefix] : '';
if (!array_key_exists($sPrefix . '_data', $aCols)) {
$sAvailable = implode(', ', array_keys($aCols));
throw new MissingColumnException("Missing column '" . $sPrefix . "_data' from {$sAvailable}");
}
$data = isset($aCols[$sPrefix . '_data']) ? $aCols[$sPrefix . '_data'] : null;
if (!array_key_exists($sPrefix . '_filename', $aCols)) {
$sAvailable = implode(', ', array_keys($aCols));
throw new MissingColumnException("Missing column '" . $sPrefix . "_filename' from {$sAvailable}");
}
$sFileName = isset($aCols[$sPrefix . '_filename']) ? $aCols[$sPrefix . '_filename'] : '';
if (!array_key_exists($sPrefix . '_downloads_count', $aCols)) {
$sAvailable = implode(', ', array_keys($aCols));
throw new MissingColumnException("Missing column '" . $sPrefix . "_downloads_count' from {$sAvailable}");
}
$iDownloadsCount = isset($aCols[$sPrefix . '_downloads_count']) ? $aCols[$sPrefix . '_downloads_count'] : ormDocument::DEFAULT_DOWNLOADS_COUNT;
$value = new ormDocument($data, $sMimeType, $sFileName, $iDownloadsCount);
return $value;
}
public function GetSQLValues($value)
{
// #@# Optimization: do not load blobs anytime
// As per mySQL doc, selecting blob columns will prevent mySQL from
// using memory in case a temporary table has to be created
// (temporary tables created on disk)
// We will have to remove the blobs from the list of attributes when doing the select
// then the use of Get() should finalize the load
if ($value instanceof ormDocument) {
$aValues = array();
if (!$value->IsEmpty()) {
$aValues[$this->GetCode() . '_data'] = $value->GetData();
} else {
$aValues[$this->GetCode() . '_data'] = '';
}
$aValues[$this->GetCode() . '_mimetype'] = $value->GetMimeType();
$aValues[$this->GetCode() . '_filename'] = $value->GetFileName();
$aValues[$this->GetCode() . '_downloads_count'] = $value->GetDownloadsCount();
} else {
$aValues = array();
$aValues[$this->GetCode() . '_data'] = '';
$aValues[$this->GetCode() . '_mimetype'] = '';
$aValues[$this->GetCode() . '_filename'] = '';
$aValues[$this->GetCode() . '_downloads_count'] = ormDocument::DEFAULT_DOWNLOADS_COUNT;
}
return $aValues;
}
public function GetSQLColumns($bFullSpec = false)
{
$aColumns = array();
$aColumns[$this->GetCode() . '_data'] = 'LONGBLOB'; // 2^32 (4 Gb)
$aColumns[$this->GetCode() . '_mimetype'] = 'VARCHAR(255)' . CMDBSource::GetSqlStringColumnDefinition();
$aColumns[$this->GetCode() . '_filename'] = 'VARCHAR(255)' . CMDBSource::GetSqlStringColumnDefinition();
$aColumns[$this->GetCode() . '_downloads_count'] = 'INT(11) UNSIGNED';
return $aColumns;
}
public function GetBasicFilterOperators()
{
return array();
}
public function GetBasicFilterLooseOperator()
{
return '=';
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
return 'true';
}
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
{
if (is_object($value)) {
return $value->GetAsHTML();
}
return '';
}
/**
* @param string $sValue
* @param string $sSeparator
* @param string $sTextQualifier
* @param \DBObject $oHostObject
* @param bool $bLocalize
* @param bool $bConvertToPlainText
*
* @return string
*/
public function GetAsCSV(
$sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
$bConvertToPlainText = false
)
{
$sAttCode = $this->GetCode();
if ($sValue instanceof ormDocument && !$sValue->IsEmpty()) {
return $sValue->GetDownloadURL(get_class($oHostObject), $oHostObject->GetKey(), $sAttCode);
}
return ''; // Not exportable in CSV !
}
/**
* @param $value
* @param \DBObject $oHostObject
* @param bool $bLocalize
*
* @return mixed|string
*/
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
{
$sRet = '';
if (is_object($value)) {
/** @var \ormDocument $value */
if (!$value->IsEmpty()) {
$sRet = '<mimetype>' . $value->GetMimeType() . '</mimetype>';
$sRet .= '<filename>' . $value->GetFileName() . '</filename>';
$sRet .= '<data>' . base64_encode($value->GetData()) . '</data>';
$sRet .= '<downloads_count>' . $value->GetDownloadsCount() . '</downloads_count>';
}
}
return $sRet;
}
public function GetForJSON($value)
{
if ($value instanceof ormDocument) {
$aValues = array();
$aValues['data'] = base64_encode($value->GetData());
$aValues['mimetype'] = $value->GetMimeType();
$aValues['filename'] = $value->GetFileName();
$aValues['downloads_count'] = $value->GetDownloadsCount();
} else {
$aValues = null;
}
return $aValues;
}
public function FromJSONToValue($json)
{
if (isset($json->data)) {
$data = base64_decode($json->data);
$value = new ormDocument($data, $json->mimetype, $json->filename, $json->downloads_count);
} else {
$value = null;
}
return $value;
}
public function Fingerprint($value)
{
$sFingerprint = '';
if ($value instanceof ormDocument) {
$sFingerprint = $value->GetSignature();
}
return $sFingerprint;
}
public static function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\BlobField';
}
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
/** @var $oFormField \Combodo\iTop\Form\Field\BlobField */
if ($oFormField === null) {
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
// Note: As of today we want this field to always be read-only
$oFormField->SetReadOnly(true);
// Calling parent before so current value is set, then proceed
parent::MakeFormField($oObject, $oFormField);
// Setting current value correctly as the default method returns an empty string when there is no file yet.
/** @var \ormDocument $value */
$value = $oObject->Get($this->GetCode());
if (!is_object($value)) {
$oFormField->SetCurrentValue(new ormDocument());
}
// Generating urls
if (is_object($value) && !$value->IsEmpty()) {
$oFormField->SetDownloadUrl($value->GetDownloadURL(get_class($oObject), $oObject->GetKey(), $this->GetCode()));
$oFormField->SetDisplayUrl($value->GetDisplayURL(get_class($oObject), $oObject->GetKey(), $this->GetCode()));
}
return $oFormField;
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
if (false === ($proposedValue instanceof ormDocument)) {
return parent::HasAValue($proposedValue);
}
// Empty file (no content, just a filename) are supported since PR {@link https://github.com/Combodo/combodo-email-synchro/pull/17}, so we check for both empty content and empty filename to determine that a document has no value
return utils::IsNotNullOrEmptyString($proposedValue->GetData()) && utils::IsNotNullOrEmptyString($proposedValue->GetFileName());
}
/**
* @inheritDoc
* @param \ormDocument $original
* @param \ormDocument $value
* @since N°6502
*/
public function RecordAttChange(DBObject $oObject, $original, $value): void
{
// N°6502 Don't record history if only the download count has changed
if ((null !== $original) && (null !== $value) && $original->EqualsExceptDownloadsCount($value)) {
return;
}
parent::RecordAttChange($oObject, $original, $value);
}
protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void
{
if (is_null($original)) {
$original = new ormDocument();
}
$oMyChangeOp->Set("prevdata", $original);
}
protected function GetChangeRecordClassName(): string
{
return CMDBChangeOpSetAttributeBlob::class;
}
}

View File

@@ -0,0 +1,242 @@
<?php
/**
* Map a boolean column to an attribute
*
* @package iTopORM
*/
class AttributeBoolean extends AttributeInteger
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public static function ListExpectedParams()
{
return parent::ListExpectedParams();
//return array_merge(parent::ListExpectedParams(), array());
}
public function GetEditClass()
{
return "Integer";
}
protected function GetSQLCol($bFullSpec = false)
{
return "TINYINT(1)" . ($bFullSpec ? $this->GetSQLColSpec() : '');
}
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue)) {
return null;
}
if ($proposedValue === '') {
return null;
}
if ((int)$proposedValue) {
return true;
}
return false;
}
public function ScalarToSQL($value)
{
if ($value) {
return 1;
}
return 0;
}
public function GetValueLabel($bValue)
{
if (is_null($bValue)) {
$sLabel = Dict::S('Core:' . get_class($this) . '/Value:null');
} else {
$sValue = $bValue ? 'yes' : 'no';
$sDefault = Dict::S('Core:' . get_class($this) . '/Value:' . $sValue);
$sLabel = $this->SearchLabel('/Attribute:' . $this->m_sCode . '/Value:' . $sValue, $sDefault, true /*user lang*/);
}
return $sLabel;
}
public function GetValueDescription($bValue)
{
if (is_null($bValue)) {
$sDescription = Dict::S('Core:' . get_class($this) . '/Value:null+');
} else {
$sValue = $bValue ? 'yes' : 'no';
$sDefault = Dict::S('Core:' . get_class($this) . '/Value:' . $sValue . '+');
$sDescription = $this->SearchLabel('/Attribute:' . $this->m_sCode . '/Value:' . $sValue . '+', $sDefault,
true /*user lang*/);
}
return $sDescription;
}
public function GetAsHTML($bValue, $oHostObject = null, $bLocalize = true)
{
if (is_null($bValue)) {
$sRes = '';
} elseif ($bLocalize) {
$sLabel = $this->GetValueLabel($bValue);
$sDescription = $this->GetValueDescription($bValue);
// later, we could imagine a detailed description in the title
$sRes = "<span title=\"$sDescription\">" . parent::GetAsHtml($sLabel) . "</span>";
} else {
$sRes = $bValue ? 'yes' : 'no';
}
return $sRes;
}
public function GetAsXML($bValue, $oHostObject = null, $bLocalize = true)
{
if (is_null($bValue)) {
$sFinalValue = '';
} elseif ($bLocalize) {
$sFinalValue = $this->GetValueLabel($bValue);
} else {
$sFinalValue = $bValue ? 'yes' : 'no';
}
$sRes = parent::GetAsXML($sFinalValue, $oHostObject, $bLocalize);
return $sRes;
}
public function GetAsCSV(
$bValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
$bConvertToPlainText = false
)
{
if (is_null($bValue)) {
$sFinalValue = '';
} elseif ($bLocalize) {
$sFinalValue = $this->GetValueLabel($bValue);
} else {
$sFinalValue = $bValue ? 'yes' : 'no';
}
$sRes = parent::GetAsCSV($sFinalValue, $sSeparator, $sTextQualifier, $oHostObject, $bLocalize);
return $sRes;
}
public static function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\SelectField';
}
/**
* @param \DBObject $oObject
* @param \Combodo\iTop\Form\Field\SelectField $oFormField
*
* @return \Combodo\iTop\Form\Field\SelectField
* @throws \CoreException
*/
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
if ($oFormField === null) {
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
$oFormField->SetChoices(array('yes' => $this->GetValueLabel(true), 'no' => $this->GetValueLabel(false)));
parent::MakeFormField($oObject, $oFormField);
return $oFormField;
}
public function GetEditValue($value, $oHostObj = null)
{
if (is_null($value)) {
return '';
} else {
return $this->GetValueLabel($value);
}
}
public function GetForJSON($value)
{
return (bool)$value;
}
public function MakeValueFromString(
$sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null,
$sAttributeQualifier = null
)
{
$sInput = mb_strtolower(trim($sProposedValue));
if ($bLocalizedValue) {
switch ($sInput) {
case '1': // backward compatibility
case $this->GetValueLabel(true):
$value = true;
break;
case '0': // backward compatibility
case 'no':
case $this->GetValueLabel(false):
$value = false;
break;
default:
$value = null;
}
} else {
switch ($sInput) {
case '1': // backward compatibility
case 'yes':
$value = true;
break;
case '0': // backward compatibility
case 'no':
$value = false;
break;
default:
$value = null;
}
}
return $value;
}
public function RecordAttChange(DBObject $oObject, $original, $value): void
{
parent::RecordAttChange($oObject, $original ? 1 : 0, $value ? 1 : 0);
}
protected function GetChangeRecordClassName(): string
{
return CMDBChangeOpSetAttributeScalar::class;
}
public function GetAllowedValues($aArgs = array(), $sContains = ''): array
{
return [
0 => $this->GetValueLabel(false),
1 => $this->GetValueLabel(true)
];
}
public function GetDisplayStyle()
{
return $this->GetOptional('display_style', 'select');
}
}

View File

@@ -0,0 +1,394 @@
<?php
/**
* An attibute that stores a case log (i.e journal)
*
* @package iTopORM
*/
class AttributeCaseLog extends AttributeLongText
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public function GetNullValue()
{
return '';
}
public function IsNull($proposedValue)
{
if (!($proposedValue instanceof ormCaseLog)) {
return ($proposedValue == '');
}
return ($proposedValue->GetText() == '');
}
/**
* @inheritDoc
* @param \ormCaseLog $proposedValue
*/
public function HasAValue($proposedValue): bool
{
// Protection against wrong value type
if (false === ($proposedValue instanceof ormCaseLog)) {
return parent::HasAValue($proposedValue);
}
// We test if there is at least 1 entry in the log, not if the user is adding one
return $proposedValue->GetEntryCount() > 0;
}
public function ScalarToSQL($value)
{
if (!is_string($value) && !is_null($value)) {
throw new CoreWarning('Expected the attribute value to be a string', array(
'found_type' => gettype($value),
'value' => $value,
'class' => $this->GetCode(),
'attribute' => $this->GetHostClass()
));
}
return $value;
}
public function GetEditClass()
{
return "CaseLog";
}
public function GetEditValue($sValue, $oHostObj = null)
{
if (!($sValue instanceof ormCaseLog)) {
return '';
}
return $sValue->GetModifiedEntry();
}
/**
* For fields containing a potential markup, return the value without this markup
*
* @param mixed $value
* @param \DBObject $oHostObj
*
* @return string
*/
public function GetAsPlainText($value, $oHostObj = null)
{
if ($value instanceof ormCaseLog) {
/** ormCaseLog $value */
return $value->GetAsPlainText();
} else {
return (string)$value;
}
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
return new ormCaseLog();
}
public function Equals($val1, $val2)
{
return ($val1->GetText() == $val2->GetText());
}
/**
* Facilitate things: allow the user to Set the value from a string
*
* @param $proposedValue
* @param \DBObject $oHostObj
*
* @return mixed|null|\ormCaseLog|string
* @throws \Exception
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
if ($proposedValue instanceof ormCaseLog) {
// Passthrough
$ret = clone $proposedValue;
} else {
// Append the new value if an instance of the object is supplied
//
$oPreviousLog = null;
if ($oHostObj != null) {
$oPreviousLog = $oHostObj->Get($this->GetCode());
if (!is_object($oPreviousLog)) {
$oPreviousLog = $oHostObj->GetOriginal($this->GetCode());;
}
}
if (is_object($oPreviousLog)) {
$oCaseLog = clone($oPreviousLog);
} else {
$oCaseLog = new ormCaseLog();
}
if ($proposedValue instanceof stdClass) {
$oCaseLog->AddLogEntryFromJSON($proposedValue);
} else {
if (utils::StrLen($proposedValue) > 0) {
//N°5135 - add impersonation information in caselog
if (UserRights::IsImpersonated()) {
$sOnBehalfOf = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUserFriendlyName(), UserRights::GetUserFriendlyName());
$oCaseLog->AddLogEntry($proposedValue, $sOnBehalfOf, UserRights::GetConnectedUserId());
} else {
$oCaseLog->AddLogEntry($proposedValue);
}
}
}
$ret = $oCaseLog;
}
return $ret;
}
public function GetSQLExpressions($sPrefix = '')
{
if ($sPrefix == '') {
$sPrefix = $this->Get('sql');
}
$aColumns = array();
// Note: to optimize things, the existence of the attribute is determined by the existence of one column with an empty suffix
$aColumns[''] = $sPrefix;
$aColumns['_index'] = $sPrefix . '_index';
return $aColumns;
}
/**
* @param array $aCols
* @param string $sPrefix
*
* @return \ormCaseLog
* @throws \MissingColumnException
*/
public function FromSQLToValue($aCols, $sPrefix = '')
{
if (!array_key_exists($sPrefix, $aCols)) {
$sAvailable = implode(', ', array_keys($aCols));
throw new MissingColumnException("Missing column '$sPrefix' from {$sAvailable}");
}
$sLog = $aCols[$sPrefix];
if (isset($aCols[$sPrefix . '_index'])) {
$sIndex = $aCols[$sPrefix . '_index'];
} else {
// For backward compatibility, allow the current state to be: 1 log, no index
$sIndex = '';
}
if (strlen($sIndex) > 0) {
$aIndex = unserialize($sIndex);
$value = new ormCaseLog($sLog, $aIndex);
} else {
$value = new ormCaseLog($sLog);
}
return $value;
}
public function GetSQLValues($value)
{
if (!($value instanceof ormCaseLog)) {
$value = new ormCaseLog('');
}
$aValues = array();
$aValues[$this->GetCode()] = $value->GetText();
$aValues[$this->GetCode() . '_index'] = serialize($value->GetIndex());
return $aValues;
}
public function GetSQLColumns($bFullSpec = false)
{
$aColumns = array();
$aColumns[$this->GetCode()] = 'LONGTEXT' // 2^32 (4 Gb)
. CMDBSource::GetSqlStringColumnDefinition();
$aColumns[$this->GetCode() . '_index'] = 'BLOB';
return $aColumns;
}
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
{
if ($value instanceof ormCaseLog) {
$sContent = $value->GetAsHTML(null, false, array(__class__, 'RenderWikiHtml'));
} else {
$sContent = '';
}
$aStyles = array();
if ($this->GetWidth() != '') {
$aStyles[] = 'width:' . $this->GetWidth();
}
if ($this->GetHeight() != '') {
$aStyles[] = 'height:' . $this->GetHeight();
}
$sStyle = '';
if (count($aStyles) > 0) {
$sStyle = 'style="' . implode(';', $aStyles) . '"';
}
return "<div class=\"caselog\" $sStyle>" . $sContent . '</div>';
}
public function GetAsCSV(
$value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
$bConvertToPlainText = false
)
{
if ($value instanceof ormCaseLog) {
return parent::GetAsCSV($value->GetText($bConvertToPlainText), $sSeparator, $sTextQualifier, $oHostObject,
$bLocalize, $bConvertToPlainText);
} else {
return '';
}
}
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
{
if ($value instanceof ormCaseLog) {
return parent::GetAsXML($value->GetText(), $oHostObject, $bLocalize);
} else {
return '';
}
}
/**
* List the available verbs for 'GetForTemplate'
*/
public function EnumTemplateVerbs()
{
return array(
'' => 'Plain text representation of all the log entries',
'head' => 'Plain text representation of the latest entry',
'head_html' => 'HTML representation of the latest entry',
'html' => 'HTML representation of all the log entries',
);
}
/**
* Get various representations of the value, for insertion into a template (e.g. in Notifications)
*
* @param $value mixed The current value of the field
* @param $sVerb string The verb specifying the representation of the value
* @param $oHostObject DBObject The object
* @param $bLocalize bool Whether or not to localize the value
*
* @return mixed
* @throws \Exception
*/
public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
{
switch ($sVerb) {
case '':
return $value->GetText(true);
case 'head':
return $value->GetLatestEntry('text');
case 'head_html':
return $value->GetLatestEntry('html');
case 'html':
return $value->GetAsEmailHtml();
default:
throw new Exception("Unknown verb '$sVerb' for attribute " . $this->GetCode() . ' in class ' . get_class($oHostObject));
}
}
public function GetForJSON($value)
{
return $value->GetForJSON();
}
public function FromJSONToValue($json)
{
if (is_string($json)) {
// Will be correctly handled in MakeRealValue
$ret = $json;
} else {
if (isset($json->add_item)) {
// Will be correctly handled in MakeRealValue
$ret = $json->add_item;
if (!isset($ret->message)) {
throw new Exception("Missing mandatory entry: 'message'");
}
} else {
$ret = ormCaseLog::FromJSON($json);
}
}
return $ret;
}
public function Fingerprint($value)
{
$sFingerprint = '';
if ($value instanceof ormCaseLog) {
$sFingerprint = $value->GetText();
}
return $sFingerprint;
}
/**
* The actual formatting of the text: either text (=plain text) or html (= text with HTML markup)
*
* @return string
*/
public function GetFormat()
{
return $this->GetOptional('format', 'html'); // default format for case logs is now HTML
}
public static function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\CaseLogField';
}
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
// First we call the parent so the field is build
$oFormField = parent::MakeFormField($oObject, $oFormField);
// Then only we set the value
$oFormField->SetCurrentValue($this->GetEditValue($oObject->Get($this->GetCode())));
// And we set the entries
$oFormField->SetEntries($oObject->Get($this->GetCode())->GetAsArray());
return $oFormField;
}
protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void
{
/** @var \ormCaseLog $value */
$oMyChangeOp->Set("lastentry", $value->GetLatestEntryIndex());
}
protected function GetChangeRecordClassName(): string
{
return CMDBChangeOpSetAttributeCaseLog::class;
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* An attribute that matches an object class
*
* @package iTopORM
*/
class AttributeClass extends AttributeString
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_ENUM;
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array('class_category', 'more_values'));
}
public function __construct($sCode, $aParams)
{
$this->m_sCode = $sCode;
$aParams["allowed_values"] = new ValueSetEnumClasses($aParams['class_category'], $aParams['more_values']);
parent::__construct($sCode, $aParams);
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
$sDefault = parent::GetDefaultValue($oHostObject);
if (!$this->IsNullAllowed() && $this->IsNull($sDefault)) {
// For this kind of attribute specifying null as default value
// is authorized even if null is not allowed
// Pick the first one...
$aClasses = $this->GetAllowedValues();
$sDefault = key($aClasses);
}
return $sDefault;
}
/**
* @param array $aArgs
* @param string $sContains
*
* @return array|null
* @throws \CoreException
*/
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
$oValSetDef = $this->GetValuesDef();
if (!$oValSetDef) {
return null;
}
$aListClass = $oValSetDef->GetValues($aArgs, $sContains);
/* @since 3.3.0 remove elements in class_exclusion_list */
$sClassExclusionList = $this->GetOptional('class_exclusion_list', null);
if (!empty($sClassExclusionList)) {
foreach (explode(',', $sClassExclusionList) as $sClassName) {
unset($aListClass[trim($sClassName)]);
}
}
return $aListClass;
}
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if (empty($sValue)) {
return '';
}
return MetaModel::GetName($sValue);
}
public function RequiresIndex()
{
return true;
}
public function GetBasicFilterLooseOperator()
{
return '=';
}
}

View File

@@ -0,0 +1,214 @@
<?php
class AttributeClassAttCodeSet extends AttributeSet
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
const DEFAULT_PARAM_INCLUDE_CHILD_CLASSES_ATTRIBUTES = false;
public function __construct($sCode, array $aParams)
{
parent::__construct($sCode, $aParams);
$this->aCSSClasses[] = 'attribute-class-attcode-set';
}
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array('class_field', 'attribute_definition_list', 'attribute_definition_exclusion_list'));
}
public function GetMaxSize()
{
return max(255, 15 * $this->GetMaxItems());
}
/**
* @param array $aArgs
* @param string $sContains
*
* @return array|null
* @throws \CoreException
*/
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
if (!isset($aArgs['this'])) {
return null;
}
$oHostObj = $aArgs['this'];
$sTargetClass = $this->Get('class_field');
$sRootClass = $oHostObj->Get($sTargetClass);
$bIncludeChildClasses = $this->GetOptional('include_child_classes_attributes', static::DEFAULT_PARAM_INCLUDE_CHILD_CLASSES_ATTRIBUTES);
$aExcludeDefs = array();
$sAttDefExclusionList = $this->Get('attribute_definition_exclusion_list');
if (!empty($sAttDefExclusionList)) {
foreach (explode(',', $sAttDefExclusionList) as $sAttDefName) {
$sAttDefName = trim($sAttDefName);
$aExcludeDefs[$sAttDefName] = $sAttDefName;
}
}
$aAllowedDefs = array();
$sAttDefList = $this->Get('attribute_definition_list');
if (!empty($sAttDefList)) {
foreach (explode(',', $sAttDefList) as $sAttDefName) {
$sAttDefName = trim($sAttDefName);
$aAllowedDefs[$sAttDefName] = $sAttDefName;
}
}
$aAllAttributes = array();
if (!empty($sRootClass)) {
$aClasses = array($sRootClass);
if ($bIncludeChildClasses === true) {
$aClasses = $aClasses + MetaModel::EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_EXCLUDETOP);
}
foreach ($aClasses as $sClass) {
foreach (MetaModel::GetAttributesList($sClass) as $sAttCode) {
// Add attribute only if not already there (can be in leaf classes but not the root)
if (!array_key_exists($sAttCode, $aAllAttributes)) {
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sAttDefClass = get_class($oAttDef);
// Skip excluded attdefs
if (isset($aExcludeDefs[$sAttDefClass])) {
continue;
}
// Skip not allowed attdefs only if list specified
if (!empty($aAllowedDefs) && !isset($aAllowedDefs[$sAttDefClass])) {
continue;
}
$aAllAttributes[$sAttCode] = array(
'classes' => array($sClass),
);
} else {
$aAllAttributes[$sAttCode]['classes'][] = $sClass;
}
}
}
}
$aAllowedAttributes = array();
foreach ($aAllAttributes as $sAttCode => $aAttData) {
$iAttClassesCount = count($aAttData['classes']);
$sAttFirstClass = $aAttData['classes'][0];
$sAttLabel = MetaModel::GetLabel($sAttFirstClass, $sAttCode);
if ($sAttFirstClass === $sRootClass) {
$sLabel = Dict::Format('Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromClass', $sAttCode, $sAttLabel);
} elseif ($iAttClassesCount === 1) {
$sLabel = Dict::Format('Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromOneChildClass', $sAttCode, $sAttLabel, MetaModel::GetName($sAttFirstClass));
} else {
$sLabel = Dict::Format('Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromSeveralChildClasses', $sAttCode, $sAttLabel);
}
$aAllowedAttributes[$sAttCode] = $sLabel;
}
// N°6460 Always sort on the labels, not on the datamodel definition order
natcasesort($aAllowedAttributes);
return $aAllowedAttributes;
}
/**
* force an allowed value (type conversion and possibly forces a value as mySQL would do upon writing!
*
* @param $proposedValue
* @param \DBObject $oHostObj
*
* @param bool $bIgnoreErrors
*
* @return mixed
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \Exception
*/
public function MakeRealValue($proposedValue, $oHostObj, $bIgnoreErrors = false)
{
$oSet = new ormSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
$aArgs = array();
if (!empty($oHostObj)) {
$aArgs['this'] = $oHostObj;
}
$aAllowedAttributes = $this->GetAllowedValues($aArgs);
$aInvalidAttCodes = array();
if (is_string($proposedValue) && !empty($proposedValue)) {
$aJsonFromWidget = json_decode($proposedValue, true);
if (is_null($aJsonFromWidget)) {
$proposedValue = trim($proposedValue);
$aProposedValues = $this->FromStringToArray($proposedValue);
$aValues = array();
foreach ($aProposedValues as $sValue) {
$sAttCode = trim($sValue);
if (empty($aAllowedAttributes) || isset($aAllowedAttributes[$sAttCode])) {
$aValues[$sAttCode] = $sAttCode;
} else {
$aInvalidAttCodes[] = $sAttCode;
}
}
$oSet->SetValues($aValues);
}
} elseif ($proposedValue instanceof ormSet) {
$oSet = $proposedValue;
}
if (!empty($aInvalidAttCodes) && !$bIgnoreErrors) {
$sTargetClass = $this->Get('class_field');
$sClass = $oHostObj->Get($sTargetClass);
throw new CoreUnexpectedValue("The attribute(s) " . implode(', ', $aInvalidAttCodes) . " are invalid for class {$sClass}");
}
return $oSet;
}
/**
* @param $value
* @param \DBObject $oHostObject
* @param bool $bLocalize
*
* @return string|null
*
* @throws \Exception
*/
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
{
if ($value instanceof ormSet) {
$value = $value->GetValues();
}
if (is_array($value)) {
if (!empty($oHostObject) && $bLocalize) {
$sTargetClass = $this->Get('class_field');
$sClass = $oHostObject->Get($sTargetClass);
$aLocalizedValues = array();
foreach ($value as $sAttCode) {
try {
$sAttClass = $sClass;
// Look for the first class (current or children) that have this attcode
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sChildClass) {
if (MetaModel::IsValidAttCode($sChildClass, $sAttCode)) {
$sAttClass = $sChildClass;
break;
}
}
$sLabelForHtmlAttribute = utils::HtmlEntities(MetaModel::GetLabel($sAttClass, $sAttCode) . " ($sAttCode)");
$aLocalizedValues[] = '<span class="attribute-set-item" data-code="' . $sAttCode . '" data-label="' . $sLabelForHtmlAttribute . '" data-description="" data-tooltip-content="' . $sLabelForHtmlAttribute . '">' . $sAttCode . '</span>';
} catch (Exception $e) {
// Ignore bad values
}
}
$value = $aLocalizedValues;
}
$value = implode('', $value);
}
return '<span class="' . implode(' ', $this->aCSSClasses) . '">' . $value . '</span>';
}
public function IsNull($proposedValue)
{
return (empty($proposedValue));
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* An attribute that matches a class state
*
* @package iTopORM
*/
class AttributeClassState extends AttributeString
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array('class_field'));
}
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
if (isset($aArgs['this'])) {
$oHostObj = $aArgs['this'];
$sTargetClass = $this->Get('class_field');
$sClass = $oHostObj->Get($sTargetClass);
$aAllowedStates = array();
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sChildClass) {
$aValues = MetaModel::EnumStates($sChildClass);
foreach (array_keys($aValues) as $sState) {
$aAllowedStates[$sState] = $sState . ' (' . MetaModel::GetStateLabel($sChildClass, $sState) . ')';
}
}
return $aAllowedStates;
}
return null;
}
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if (empty($sValue)) {
return '';
}
if (!empty($oHostObject)) {
$sTargetClass = $this->Get('class_field');
$sClass = $oHostObject->Get($sTargetClass);
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sChildClass) {
$aValues = MetaModel::EnumStates($sChildClass);
if (in_array($sValue, $aValues)) {
$sLabelForHtmlAttribute = utils::EscapeHtml($sValue . ' (' . MetaModel::GetStateLabel($sChildClass, $sValue) . ')');
$sHTML = '<span class="attribute-set-item" data-code="' . $sValue . '" data-label="' . $sLabelForHtmlAttribute . '" data-description="" data-tooltip-content="' . $sLabelForHtmlAttribute . '">' . $sValue . '</span>';
return $sHTML;
}
}
}
return $sValue;
}
}

View File

@@ -0,0 +1,449 @@
<?php
/**
* Custom fields managed by an external implementation
*
* @package iTopORM
*/
class AttributeCustomFields extends AttributeDefinition
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("handler_class"));
}
public function GetEditClass()
{
return "CustomFields";
}
public function IsWritable()
{
return true;
}
public static function LoadFromClassTables()
{
return false;
} // See ReadValue...
public function GetDefaultValue(DBObject $oHostObject = null)
{
return new ormCustomFieldsValue($oHostObject, $this->GetCode());
}
public function GetBasicFilterOperators()
{
return array();
}
public function GetBasicFilterLooseOperator()
{
return '';
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
return '';
}
/**
* @param DBObject $oHostObject
* @param array|null $aValues
*
* @return CustomFieldsHandler
*/
public function GetHandler($aValues = null)
{
$sHandlerClass = $this->Get('handler_class');
/** @var \TemplateFieldsHandler $oHandler */
$oHandler = new $sHandlerClass($this->GetCode());
if (!is_null($aValues)) {
$oHandler->SetCurrentValues($aValues);
}
return $oHandler;
}
public function GetPrerequisiteAttributes($sClass = null)
{
$sHandlerClass = $this->Get('handler_class');
return $sHandlerClass::GetPrerequisiteAttributes($sClass);
}
public function GetEditValue($sValue, $oHostObj = null)
{
return $this->GetForTemplate($sValue, '', $oHostObj, true);
}
/**
* Makes the string representation out of the values given by the form defined in GetDisplayForm
*/
public function ReadValueFromPostedForm($oHostObject, $sFormPrefix)
{
$aRawData = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->GetCode()}", '{}', 'raw_data'), true);
if ($aRawData != null) {
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aRawData);
} else {
return null;
}
}
public function MakeRealValue($proposedValue, $oHostObject)
{
if (is_object($proposedValue) && ($proposedValue instanceof ormCustomFieldsValue)) {
if (false === $oHostObject->IsNew()) {
// In that case we need additional keys : see \TemplateFieldsHandler::DoBuildForm
$aRequestTemplateValues = $proposedValue->GetValues();
if (false === array_key_exists('current_template_id', $aRequestTemplateValues)) {
$aRequestTemplateValues['current_template_id'] = $aRequestTemplateValues['template_id'];
$aRequestTemplateValues['current_template_data'] = $aRequestTemplateValues['template_data'];
$proposedValue = new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aRequestTemplateValues);
}
}
if (is_null($proposedValue->GetHostObject())) {
// the object might not be set : for example in \AttributeCustomFields::FromJSONToValue we don't have the object available :(
$proposedValue->SetHostObject($oHostObject);
}
return $proposedValue;
}
if (is_string($proposedValue)) {
$aValues = json_decode($proposedValue, true);
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aValues);
}
if (is_array($proposedValue)) {
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $proposedValue);
}
if (is_null($proposedValue)) {
return new ormCustomFieldsValue($oHostObject, $this->GetCode());
}
throw new Exception('Unexpected type for the value of a custom fields attribute: ' . gettype($proposedValue));
}
public static function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\SubFormField';
}
/**
* Override to build the relevant form field
*
* When called first, $oFormField is null and will be created (eg. Make). Then when the ::parent is called and the
* $oFormField is passed, MakeFormField behaves more like a Prepare.
*/
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
if ($oFormField === null) {
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
$oFormField->SetForm($this->GetForm($oObject));
}
parent::MakeFormField($oObject, $oFormField);
return $oFormField;
}
/**
* @param DBObject $oHostObject
* @param null $sFormPrefix
*
* @return Combodo\iTop\Form\Form
* @throws \Exception
*/
public function GetForm(DBObject $oHostObject, $sFormPrefix = null)
{
try {
$oValue = $oHostObject->Get($this->GetCode());
$oHandler = $this->GetHandler($oValue->GetValues());
$sFormId = utils::IsNullOrEmptyString($sFormPrefix) ? 'cf_' . $this->GetCode() : $sFormPrefix . '_cf_' . $this->GetCode();
$oHandler->BuildForm($oHostObject, $sFormId);
$oForm = $oHandler->GetForm();
} catch (Exception $e) {
$oForm = new \Combodo\iTop\Form\Form('');
$oField = new \Combodo\iTop\Form\Field\LabelField('');
$oField->SetLabel('Custom field error: ' . $e->getMessage());
$oForm->AddField($oField);
$oForm->Finalize();
}
return $oForm;
}
/**
* Read the data from where it has been stored. This verb must be implemented as soon as LoadFromClassTables returns false
* and LoadInObject returns true
*
* @param DBObject $oHostObject
*
* @return mixed|null
* @since 3.1.0
*/
public function ReadExternalValues(DBObject $oHostObject)
{
try {
$oHandler = $this->GetHandler();
$aValues = $oHandler->ReadValues($oHostObject);
$oRet = new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aValues);
} catch (Exception $e) {
$oRet = new ormCustomFieldsValue($oHostObject, $this->GetCode());
}
return $oRet;
}
/**
* @inheritDoc
*
* @since 3.1.0 N°6043 Move code contained in \AttributeCustomFields::WriteValue to this generic method
*/
public function WriteExternalValues(DBObject $oHostObject): void
{
$oValue = $oHostObject->Get($this->GetCode());
if (!($oValue instanceof ormCustomFieldsValue)) {
$oHandler = $this->GetHandler();
$aValues = array();
} else {
// Pass the values through the form to make sure that they are correct
$oHandler = $this->GetHandler($oValue->GetValues());
$oHandler->BuildForm($oHostObject, '');
$oForm = $oHandler->GetForm();
$aValues = $oForm->GetCurrentValues();
}
$oHandler->WriteValues($oHostObject, $aValues);
}
/**
* The part of the current attribute in the object's signature, for the supplied value
*
* @param ormCustomFieldsValue $value The value of this attribute for the object
*
* @return string The "signature" for this field/attribute
*/
public function Fingerprint($value)
{
$oHandler = $this->GetHandler($value->GetValues());
return $oHandler->GetValueFingerprint();
}
/**
* Check the validity of the data
*
* @param DBObject $oHostObject
* @param $value
*
* @return bool|string true or error message
*/
public function CheckValue(DBObject $oHostObject, $value)
{
try {
$oHandler = $this->GetHandler($value->GetValues());
$oHandler->BuildForm($oHostObject, '');
$ret = $oHandler->Validate($oHostObject);
} catch (Exception $e) {
$ret = $e->getMessage();
}
return $ret;
}
/**
* Cleanup data upon object deletion (object id still available here)
*
* @param DBObject $oHostObject
*
* @throws \CoreException
* @since 3.1.0
*/
public function DeleteExternalValues(DBObject $oHostObject): void
{
$oValue = $oHostObject->Get($this->GetCode());
$oHandler = $this->GetHandler($oValue->GetValues());
$oHandler->DeleteValues($oHostObject);
}
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
{
try {
/** @var \ormCustomFieldsValue $value */
$sRet = $value->GetAsHTML($bLocalize);
} catch (Exception $e) {
$sRet = 'Custom field error: ' . utils::EscapeHtml($e->getMessage());
}
return $sRet;
}
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
{
try {
$sRet = $value->GetAsXML($bLocalize);
} catch (Exception $e) {
$sRet = Str::pure2xml('Custom field error: ' . $e->getMessage());
}
return $sRet;
}
/**
* @param \ormCustomFieldsValue $value
* @param string $sSeparator
* @param string $sTextQualifier
* @param \DBObject $oHostObject
* @param bool $bLocalize
* @param bool $bConvertToPlainText
*
* @return string
* @noinspection PhpParameterNameChangedDuringInheritanceInspection
*/
public function GetAsCSV(
$value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
$bConvertToPlainText = false
)
{
try {
$sRet = $value->GetAsCSV($sSeparator, $sTextQualifier, $bLocalize, $bConvertToPlainText);
} catch (Exception $e) {
$sFrom = array("\r\n", $sTextQualifier);
$sTo = array("\n", $sTextQualifier . $sTextQualifier);
$sEscaped = str_replace($sFrom, $sTo, 'Custom field error: ' . $e->getMessage());
$sRet = $sTextQualifier . $sEscaped . $sTextQualifier;
}
return $sRet;
}
/**
* List the available verbs for 'GetForTemplate'
*/
public function EnumTemplateVerbs()
{
$sHandlerClass = $this->Get('handler_class');
return $sHandlerClass::EnumTemplateVerbs();
}
/**
* Get various representations of the value, for insertion into a template (e.g. in Notifications)
*
* @param $value mixed The current value of the field
* @param $sVerb string The verb specifying the representation of the value
* @param $oHostObject DBObject The object
* @param $bLocalize bool Whether or not to localize the value
*
* @return string
*/
public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
{
try {
$sRet = $value->GetForTemplate($sVerb, $bLocalize);
} catch (Exception $e) {
$sRet = 'Custom field error: ' . $e->getMessage();
}
return $sRet;
}
public function MakeValueFromString(
$sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null,
$sAttributeQualifier = null
)
{
return null;
}
/**
* @inheritDoc
*
* @param \ormCustomFieldsValue $value
*
* @return string|array
*
* @since 3.1.0 N°1150 now returns the value (was always returning null before)
*/
public function GetForJSON($value)
{
try {
$sRet = $value->GetForJSON();
} catch (Exception $e) {
$sRet = 'Custom field error: ' . $e->getMessage();
}
return $sRet;
}
/**
* @inheritDoc
*
* @return ?\ormCustomFieldsValue with empty host object as we don't have it here (most consumers don't have an object in their context, for example in \RestUtils::GetObjectSetFromKey)
* The host object will be set in {@see MakeRealValue}
* All the necessary checks will be done in {@see CheckValue}
*/
public function FromJSONToValue($json)
{
return ormCustomFieldsValue::FromJSONToValue($json, $this);
}
public function Equals($val1, $val2)
{
try {
$bEquals = $val1->Equals($val2);
} catch (Exception $e) {
$bEquals = false;
}
return $bEquals;
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
// Protection against wrong value type
if (false === ($proposedValue instanceof ormCustomFieldsValue)) {
return parent::HasAValue($proposedValue);
}
return count($proposedValue->GetValues()) > 0;
}
protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void
{
$oMyChangeOp->Set("prevdata", json_encode($original->GetValues()));
}
protected function GetChangeRecordClassName(): string
{
return CMDBChangeOpSetAttributeCustomFields::class;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* Base class for all kind of DB attributes, with the exception of external keys
*
* @package iTopORM
*/
class AttributeDBField extends AttributeDBFieldVoid
{
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("default_value", "is_null_allowed"));
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
return $this->MakeRealValue($this->Get("default_value"), $oHostObject);
}
public function IsNullAllowed()
{
return $this->Get("is_null_allowed");
}
}

View File

@@ -0,0 +1,146 @@
<?php
/**
* Abstract class implementing default filters for a DB column
*
* @package iTopORM
*/
class AttributeDBFieldVoid extends AttributeDefinition
{
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("allowed_values", "depends_on", "sql"));
}
// To be overriden, used in GetSQLColumns
protected function GetSQLCol($bFullSpec = false)
{
return 'VARCHAR(255)'
. CMDBSource::GetSqlStringColumnDefinition()
. ($bFullSpec ? $this->GetSQLColSpec() : '');
}
protected function GetSQLColSpec()
{
$default = $this->ScalarToSQL($this->GetDefaultValue());
if (is_null($default)) {
$sRet = '';
} else {
if (is_numeric($default)) {
// Though it is a string in PHP, it will be considered as a numeric value in MySQL
// Then it must not be quoted here, to preserve the compatibility with the value returned by CMDBSource::GetFieldSpec
$sRet = " DEFAULT $default";
} else {
$sRet = " DEFAULT " . CMDBSource::Quote($default);
}
}
return $sRet;
}
public function GetEditClass()
{
return "String";
}
public function GetValuesDef()
{
return $this->Get("allowed_values");
}
public function GetPrerequisiteAttributes($sClass = null)
{
return $this->Get("depends_on");
}
public static function IsBasedOnDBColumns()
{
return true;
}
public static function IsScalar()
{
return true;
}
public function IsWritable()
{
return !$this->IsMagic();
}
public function GetSQLExpr()
{
return $this->Get("sql");
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
return $this->MakeRealValue("", $oHostObject);
}
public function IsNullAllowed()
{
return false;
}
//
protected function ScalarToSQL($value)
{
return $value;
} // format value as a valuable SQL literal (quoted outside)
public function GetSQLExpressions($sPrefix = '')
{
$aColumns = array();
// Note: to optimize things, the existence of the attribute is determined by the existence of one column with an empty suffix
$aColumns[''] = $this->Get("sql");
return $aColumns;
}
public function FromSQLToValue($aCols, $sPrefix = '')
{
$value = $this->MakeRealValue($aCols[$sPrefix . ''], null);
return $value;
}
public function GetSQLValues($value)
{
$aValues = array();
$aValues[$this->Get("sql")] = $this->ScalarToSQL($value);
return $aValues;
}
public function GetSQLColumns($bFullSpec = false)
{
$aColumns = array();
$aColumns[$this->Get("sql")] = $this->GetSQLCol($bFullSpec);
return $aColumns;
}
public function GetBasicFilterOperators()
{
return array("=" => "equals", "!=" => "differs from");
}
public function GetBasicFilterLooseOperator()
{
return "=";
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
$sQValue = CMDBSource::Quote($value);
switch ($sOpCode) {
case '!=':
return $this->GetSQLExpr() . " != $sQValue";
break;
case '=':
default:
return $this->GetSQLExpr() . " = $sQValue";
}
}
}

View File

@@ -0,0 +1,98 @@
<?php
class AttributeDashboard extends AttributeDefinition
{
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(),
array("definition_file", "is_user_editable"));
}
public function GetDashboard()
{
$sAttCode = $this->GetCode();
$sClass = MetaModel::GetAttributeOrigin($this->GetHostClass(), $sAttCode);
$sFilePath = APPROOT . 'env-' . utils::GetCurrentEnvironment() . '/' . $this->Get('definition_file');
return RuntimeDashboard::GetDashboard($sFilePath, $sClass . '__' . $sAttCode);
}
public function IsUserEditable()
{
return $this->Get('is_user_editable');
}
public function IsWritable()
{
return false;
}
public function GetEditClass()
{
return "";
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
return null;
}
public function GetBasicFilterOperators()
{
return array();
}
public function GetBasicFilterLooseOperator()
{
return '=';
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
return '';
}
/**
* @inheritdoc
*/
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
return null;
}
// if this verb returns false, then GetValue must be implemented
public static function LoadInObject()
{
return false;
}
public function GetValue($oHostObject)
{
return '';
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
// Always return false for now, we don't consider a custom version of a dashboard
return false;
}
}

View File

@@ -0,0 +1,105 @@
<?php
/**
* Map a date+time column to an attribute
*
* @package iTopORM
*/
class AttributeDate extends AttributeDateTime
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_DATE;
public static $oDateFormat = null;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public static function GetFormat()
{
if (self::$oDateFormat == null) {
AttributeDateTime::LoadFormatFromConfig();
}
return self::$oDateFormat;
}
public static function SetFormat(DateTimeFormat $oDateFormat)
{
self::$oDateFormat = $oDateFormat;
}
/**
* Returns the format string used for the date & time stored in memory
*
* @return string
*/
public static function GetInternalFormat()
{
return 'Y-m-d';
}
/**
* Returns the format string used for the date & time written to MySQL
*
* @return string
*/
public static function GetSQLFormat()
{
return 'Y-m-d';
}
public static function ListExpectedParams()
{
return parent::ListExpectedParams();
//return array_merge(parent::ListExpectedParams(), array());
}
public function GetEditClass()
{
return "Date";
}
protected function GetSQLCol($bFullSpec = false)
{
return "DATE";
}
public function GetImportColumns()
{
// Allow an empty string to be a valid value (synonym for "reset")
$aColumns = array();
$aColumns[$this->GetCode()] = 'VARCHAR(10)' . CMDBSource::GetSqlStringColumnDefinition();
return $aColumns;
}
/**
* Override to specify Field class
*
* When called first, $oFormField is null and will be created (eg. Make). Then when the ::parent is called and the
* $oFormField is passed, MakeFormField behave more like a Prepare.
*/
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
$oFormField = parent::MakeFormField($oObject, $oFormField);
$oFormField->SetDateOnly(true);
return $oFormField;
}
}

View File

@@ -0,0 +1,488 @@
<?php
/**
* Map a date+time column to an attribute
*
* @package iTopORM
*/
class AttributeDateTime extends AttributeDBField
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_DATE_TIME;
public static $oFormat = null;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
/**
*
* @return DateTimeFormat
*/
public static function GetFormat()
{
if (self::$oFormat == null) {
static::LoadFormatFromConfig();
}
return self::$oFormat;
}
/**
* Load the 3 settings: date format, time format and data_time format from the configuration
*/
public static function LoadFormatFromConfig()
{
$aFormats = MetaModel::GetConfig()->Get('date_and_time_format');
$sLang = Dict::GetUserLanguage();
$sDateFormat = isset($aFormats[$sLang]['date']) ? $aFormats[$sLang]['date'] : (isset($aFormats['default']['date']) ? $aFormats['default']['date'] : 'Y-m-d');
$sTimeFormat = isset($aFormats[$sLang]['time']) ? $aFormats[$sLang]['time'] : (isset($aFormats['default']['time']) ? $aFormats['default']['time'] : 'H:i:s');
$sDateAndTimeFormat = isset($aFormats[$sLang]['date_time']) ? $aFormats[$sLang]['date_time'] : (isset($aFormats['default']['date_time']) ? $aFormats['default']['date_time'] : '$date $time');
$sFullFormat = str_replace(array('$date', '$time'), array($sDateFormat, $sTimeFormat), $sDateAndTimeFormat);
self::SetFormat(new DateTimeFormat($sFullFormat));
AttributeDate::SetFormat(new DateTimeFormat($sDateFormat));
}
/**
* Returns the format string used for the date & time stored in memory
*
* @return string
*/
public static function GetInternalFormat()
{
return 'Y-m-d H:i:s';
}
/**
* Returns the format string used for the date & time written to MySQL
*
* @return string
*/
public static function GetSQLFormat()
{
return 'Y-m-d H:i:s';
}
public static function SetFormat(DateTimeFormat $oDateTimeFormat)
{
self::$oFormat = $oDateTimeFormat;
}
public static function GetSQLTimeFormat()
{
return 'H:i:s';
}
/**
* Parses a search string coming from user input
*
* @param string $sSearchString
*
* @return string
*/
public function ParseSearchString($sSearchString)
{
try {
$oDateTime = $this->GetFormat()->Parse($sSearchString);
$sSearchString = $oDateTime->format($this->GetInternalFormat());
} catch (Exception $e) {
$sFormatString = '!' . (string)AttributeDate::GetFormat(); // BEWARE: ! is needed to set non-parsed fields to zero !!!
$oDateTime = DateTime::createFromFormat($sFormatString, $sSearchString);
if ($oDateTime !== false) {
$sSearchString = $oDateTime->format($this->GetInternalFormat());
}
}
return $sSearchString;
}
public static function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\DateTimeField';
}
/**
* Override to specify Field class
*
* When called first, $oFormField is null and will be created (eg. Make). Then when the ::parent is called and the
* $oFormField is passed, MakeFormField behave more like a Prepare.
*/
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
if ($oFormField === null) {
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
$oFormField->SetPHPDateTimeFormat((string)$this->GetFormat());
$oFormField->SetJSDateTimeFormat($this->GetFormat()->ToMomentJS());
$oFormField = parent::MakeFormField($oObject, $oFormField);
// After call to the parent as it sets the current value
$oValue = $oObject->Get($this->GetCode());
if ($oValue === $this->GetNullValue()) {
$oValue = $this->GetDefaultValue($oObject);
}
$oFormField->SetCurrentValue($this->GetFormat()->Format($oValue));
return $oFormField;
}
/**
* @inheritdoc
*/
public function EnumTemplateVerbs()
{
return array(
'' => 'Formatted representation',
'raw' => 'Not formatted representation',
);
}
/**
* @inheritdoc
*/
public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
{
switch ($sVerb) {
case '':
case 'text':
return static::GetFormat()->format($value);
break;
case 'html':
// Note: Not passing formatted value as the method will format it.
return $this->GetAsHTML($value);
break;
case 'raw':
return $value;
break;
default:
return parent::GetForTemplate($value, $sVerb, $oHostObject, $bLocalize);
break;
}
}
public static function ListExpectedParams()
{
return parent::ListExpectedParams();
//return array_merge(parent::ListExpectedParams(), array());
}
public function GetEditClass()
{
return "DateTime";
}
public function GetEditValue($sValue, $oHostObj = null)
{
return (string)static::GetFormat()->format($sValue);
}
public function GetValueLabel($sValue, $oHostObj = null)
{
return (string)static::GetFormat()->format($sValue);
}
protected function GetSQLCol($bFullSpec = false)
{
return "DATETIME";
}
public function GetImportColumns()
{
// Allow an empty string to be a valid value (synonym for "reset")
$aColumns = array();
$aColumns[$this->GetCode()] = 'VARCHAR(19)' . CMDBSource::GetSqlStringColumnDefinition();
return $aColumns;
}
public static function GetAsUnixSeconds($value)
{
$oDeadlineDateTime = new DateTime($value);
$iUnixSeconds = $oDeadlineDateTime->format('U');
return $iUnixSeconds;
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
$sDefaultValue = $this->Get('default_value');
if (utils::IsNotNullOrEmptyString($sDefaultValue)) {
try {
$sDefaultDate = Expression::FromOQL($sDefaultValue)->Evaluate([]);
} catch (Exception $e) {
try {
$sDefaultDate = Expression::FromOQL('"' . $sDefaultValue . '"')->Evaluate([]);
} catch (Exception $e) {
IssueLog::Error("Invalid default value '$sDefaultValue' for field '{$this->GetCode()}' on class '{$this->GetHostClass()}', defaulting to null");
return $this->GetNullValue();
}
}
try {
$oDate = new DateTimeImmutable($sDefaultDate);
} catch (Exception $e) {
IssueLog::Error("Invalid default value '$sDefaultValue' for field '{$this->GetCode()}' on class '{$this->GetHostClass()}', defaulting to null");
return $this->GetNullValue();
}
return $oDate->format($this->GetInternalFormat());
}
return $this->GetNullValue();
}
public function GetValidationPattern()
{
return static::GetFormat()->ToRegExpr();
}
public function GetBasicFilterOperators()
{
return array(
"=" => "equals",
"!=" => "differs from",
"<" => "before",
"<=" => "before",
">" => "after (strictly)",
">=" => "after",
"SameDay" => "same day (strip time)",
"SameMonth" => "same year/month",
"SameYear" => "same year",
"Today" => "today",
">|" => "after today + N days",
"<|" => "before today + N days",
"=|" => "equals today + N days",
);
}
public function GetBasicFilterLooseOperator()
{
// Unless we implement a "same xxx, depending on given precision" !
return "=";
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
$sQValue = CMDBSource::Quote($value);
switch ($sOpCode) {
case '=':
case '!=':
case '<':
case '<=':
case '>':
case '>=':
return $this->GetSQLExpr() . " $sOpCode $sQValue";
case 'SameDay':
return "DATE(" . $this->GetSQLExpr() . ") = DATE($sQValue)";
case 'SameMonth':
return "DATE_FORMAT(" . $this->GetSQLExpr() . ", '%Y-%m') = DATE_FORMAT($sQValue, '%Y-%m')";
case 'SameYear':
return "MONTH(" . $this->GetSQLExpr() . ") = MONTH($sQValue)";
case 'Today':
return "DATE(" . $this->GetSQLExpr() . ") = CURRENT_DATE()";
case '>|':
return "DATE(" . $this->GetSQLExpr() . ") > DATE_ADD(CURRENT_DATE(), INTERVAL $sQValue DAY)";
case '<|':
return "DATE(" . $this->GetSQLExpr() . ") < DATE_ADD(CURRENT_DATE(), INTERVAL $sQValue DAY)";
case '=|':
return "DATE(" . $this->GetSQLExpr() . ") = DATE_ADD(CURRENT_DATE(), INTERVAL $sQValue DAY)";
default:
return $this->GetSQLExpr() . " = $sQValue";
}
}
/**
* @inheritDoc
*
* @param int|DateTime|string $proposedValue possible values :
* - timestamp ({@see DateTime::getTimestamp())
* - {@see \DateTime} PHP object
* - string, following the {@see GetInternalFormat} format.
*
* @throws \CoreUnexpectedValue if invalid value type or the string passed cannot be converted
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue)) {
return null;
}
if (is_numeric($proposedValue)) {
return date(static::GetInternalFormat(), $proposedValue);
}
if (is_object($proposedValue) && ($proposedValue instanceof DateTime)) {
return $proposedValue->format(static::GetInternalFormat());
}
if (is_string($proposedValue)) {
if (($proposedValue === '') && $this->IsNullAllowed()) {
return null;
}
try {
$oFormat = new DateTimeFormat(static::GetInternalFormat());
$oFormat->Parse($proposedValue);
} catch (Exception $e) {
throw new CoreUnexpectedValue('Wrong format for date attribute ' . $this->GetCode() . ', expecting "' . $this->GetInternalFormat() . '" and got "' . $proposedValue . '"');
}
return $proposedValue;
}
throw new CoreUnexpectedValue('Wrong format for date attribute ' . $this->GetCode());
}
public function ScalarToSQL($value)
{
if (empty($value)) {
return null;
}
return $value;
}
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
{
return Str::pure2html(static::GetFormat()->format($value));
}
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
{
return Str::pure2xml($value);
}
public function GetAsCSV(
$sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
$bConvertToPlainText = false
)
{
if (empty($sValue) || ($sValue === '0000-00-00 00:00:00') || ($sValue === '0000-00-00')) {
return '';
} else {
if ((string)static::GetFormat() !== static::GetInternalFormat()) {
// Format conversion
$oDate = new DateTime($sValue);
if ($oDate !== false) {
$sValue = static::GetFormat()->format($oDate);
}
}
}
$sFrom = array("\r\n", $sTextQualifier);
$sTo = array("\n", $sTextQualifier . $sTextQualifier);
$sEscaped = str_replace($sFrom, $sTo, (string)$sValue);
return $sTextQualifier . $sEscaped . $sTextQualifier;
}
/**
* Parses a string to find some smart search patterns and build the corresponding search/OQL condition
* Each derived class is reponsible for defining and processing their own smart patterns, the base class
* does nothing special, and just calls the default (loose) operator
*
* @param string $sSearchText The search string to analyze for smart patterns
* @param FieldExpression $oField The FieldExpression representing the atttribute code in this OQL query
* @param array $aParams Values of the query parameters
* @param bool $bParseSearchString
*
* @return Expression The search condition to be added (AND) to the current search
* @throws \CoreException
*/
public function GetSmartConditionExpression(
$sSearchText, FieldExpression $oField, &$aParams, $bParseSearchString = false
)
{
// Possible smart patterns
$aPatterns = array(
'between' => array('pattern' => '/^\[(.*),(.*)\]$/', 'operator' => 'n/a'),
'greater than or equal' => array('pattern' => '/^>=(.*)$/', 'operator' => '>='),
'greater than' => array('pattern' => '/^>(.*)$/', 'operator' => '>'),
'less than or equal' => array('pattern' => '/^<=(.*)$/', 'operator' => '<='),
'less than' => array('pattern' => '/^<(.*)$/', 'operator' => '<'),
);
$sPatternFound = '';
$aMatches = array();
foreach ($aPatterns as $sPatName => $sPattern) {
if (preg_match($sPattern['pattern'], $sSearchText, $aMatches)) {
$sPatternFound = $sPatName;
break;
}
}
switch ($sPatternFound) {
case 'between':
$sParamName1 = $oField->GetParent() . '_' . $oField->GetName() . '_1';
$oRightExpr = new VariableExpression($sParamName1);
if ($bParseSearchString) {
$aParams[$sParamName1] = $this->ParseSearchString($aMatches[1]);
} else {
$aParams[$sParamName1] = $aMatches[1];
}
$oCondition1 = new BinaryExpression($oField, '>=', $oRightExpr);
$sParamName2 = $oField->GetParent() . '_' . $oField->GetName() . '_2';
$oRightExpr = new VariableExpression($sParamName2);
if ($bParseSearchString) {
$aParams[$sParamName2] = $this->ParseSearchString($aMatches[2]);
} else {
$aParams[$sParamName2] = $aMatches[2];
}
$oCondition2 = new BinaryExpression($oField, '<=', $oRightExpr);
$oNewCondition = new BinaryExpression($oCondition1, 'AND', $oCondition2);
break;
case 'greater than':
case 'greater than or equal':
case 'less than':
case 'less than or equal':
$sSQLOperator = $aPatterns[$sPatternFound]['operator'];
$sParamName = $oField->GetParent() . '_' . $oField->GetName();
$oRightExpr = new VariableExpression($sParamName);
if ($bParseSearchString) {
$aParams[$sParamName] = $this->ParseSearchString($aMatches[1]);
} else {
$aParams[$sParamName] = $aMatches[1];
}
$oNewCondition = new BinaryExpression($oField, $sSQLOperator, $oRightExpr);
break;
default:
$oNewCondition = parent::GetSmartConditionExpression($sSearchText, $oField, $aParams);
}
return $oNewCondition;
}
public function GetHelpOnSmartSearch()
{
$sDict = parent::GetHelpOnSmartSearch();
$oFormat = static::GetFormat();
$sExample = $oFormat->Format(new DateTime('2015-07-19 18:40:00'));
return vsprintf($sDict, array($oFormat->ToPlaceholder(), $sExample));
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* A dead line stored as a date & time
* The only difference with the DateTime attribute is the display:
* relative to the current time
*/
class AttributeDeadline extends AttributeDateTime
{
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
{
$sResult = self::FormatDeadline($value);
return $sResult;
}
public static function FormatDeadline($value)
{
$sResult = '';
if ($value !== null) {
$iValue = AttributeDateTime::GetAsUnixSeconds($value);
$sDate = AttributeDateTime::GetFormat()->Format($value);
$difference = $iValue - time();
if ($difference >= 0) {
$sDifference = self::FormatDuration($difference);
} else {
$sDifference = Dict::Format('UI:DeadlineMissedBy_duration', self::FormatDuration(-$difference));
}
$sFormat = MetaModel::GetConfig()->Get('deadline_format');
$sResult = str_replace(array('$date$', '$difference$'), array($sDate, $sDifference), $sFormat);
}
return $sResult;
}
static function FormatDuration($duration)
{
$days = floor($duration / 86400);
$hours = floor(($duration - (86400 * $days)) / 3600);
$minutes = floor(($duration - (86400 * $days + 3600 * $hours)) / 60);
if ($duration < 60) {
// Less than 1 min
$sResult = Dict::S('UI:Deadline_LessThan1Min');
} else {
if ($duration < 3600) {
// less than 1 hour, display it in minutes
$sResult = Dict::Format('UI:Deadline_Minutes', $minutes);
} else {
if ($duration < 86400) {
// Less that 1 day, display it in hours/minutes
$sResult = Dict::Format('UI:Deadline_Hours_Minutes', $hours, $minutes);
} else {
// Less that 1 day, display it in hours/minutes
$sResult = Dict::Format('UI:Deadline_Days_Hours_Minutes', $days, $hours, $minutes);
}
}
}
return $sResult;
}
}

View File

@@ -0,0 +1,157 @@
<?php
/**
* Map a decimal value column (suitable for financial computations) to an attribute
* internally in PHP such numbers are represented as string. Should you want to perform
* a calculation on them, it is recommended to use the BC Math functions in order to
* retain the precision
*
* @package iTopORM
*/
class AttributeDecimal extends AttributeDBField
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_NUMERIC;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array('digits', 'decimals' /* including precision */));
}
public function GetEditClass()
{
return "String";
}
protected function GetSQLCol($bFullSpec = false)
{
return "DECIMAL(" . $this->Get('digits') . "," . $this->Get('decimals') . ")" . ($bFullSpec ? $this->GetSQLColSpec() : '');
}
public function GetValidationPattern()
{
$iNbDigits = $this->Get('digits');
$iPrecision = $this->Get('decimals');
$iNbIntegerDigits = $iNbDigits - $iPrecision;
return "^[\-\+]?\d{1,$iNbIntegerDigits}(\.\d{0,$iPrecision})?$";
}
/**
* @inheritDoc
* @since 3.2.0
*/
public function CheckFormat($value)
{
$sRegExp = $this->GetValidationPattern();
return preg_match("/$sRegExp/", $value);
}
public function GetBasicFilterOperators()
{
return array(
"!=" => "differs from",
"=" => "equals",
">" => "greater (strict) than",
">=" => "greater than",
"<" => "less (strict) than",
"<=" => "less than",
"in" => "in"
);
}
public function GetBasicFilterLooseOperator()
{
// Unless we implement an "equals approximately..." or "same order of magnitude"
return "=";
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
$sQValue = CMDBSource::Quote($value);
switch ($sOpCode) {
case '!=':
return $this->GetSQLExpr() . " != $sQValue";
break;
case '>':
return $this->GetSQLExpr() . " > $sQValue";
break;
case '>=':
return $this->GetSQLExpr() . " >= $sQValue";
break;
case '<':
return $this->GetSQLExpr() . " < $sQValue";
break;
case '<=':
return $this->GetSQLExpr() . " <= $sQValue";
break;
case 'in':
if (!is_array($value)) {
throw new CoreException("Expected an array for argument value (sOpCode='$sOpCode')");
}
return $this->GetSQLExpr() . " IN ('" . implode("', '", $value) . "')";
break;
case '=':
default:
return $this->GetSQLExpr() . " = \"$value\"";
}
}
public function GetNullValue()
{
return null;
}
public function IsNull($proposedValue)
{
return is_null($proposedValue);
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
return utils::IsNotNullOrEmptyString($proposedValue);
}
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue)) {
return null;
}
if ($proposedValue === '') {
return null;
}
return $this->ScalarToSQL($proposedValue);
}
public function ScalarToSQL($value)
{
assert(is_null($value) || preg_match('/' . $this->GetValidationPattern() . '/', $value));
if (!is_null($value) && ($value !== '')) {
$value = sprintf("%1." . $this->Get('decimals') . "F", $value);
}
return $value; // null or string
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,130 @@
<?php
/**
* Store a duration as a number of seconds
*
* @package iTopORM
*/
class AttributeDuration extends AttributeInteger
{
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public function GetEditClass()
{
return "Duration";
}
protected function GetSQLCol($bFullSpec = false)
{
return "INT(11) UNSIGNED";
}
public function GetNullValue()
{
return '0';
}
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue)) {
return null;
}
if (!is_numeric($proposedValue)) {
return null;
}
if (((int)$proposedValue) < 0) {
return null;
}
return (int)$proposedValue;
}
public function ScalarToSQL($value)
{
if (is_null($value)) {
return null;
}
return $value;
}
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
{
return Str::pure2html(self::FormatDuration($value));
}
public static function FormatDuration($duration)
{
$aDuration = self::SplitDuration($duration);
if ($duration < 60) {
// Less than 1 min
$sResult = Dict::Format('Core:Duration_Seconds', $aDuration['seconds']);
} else {
if ($duration < 3600) {
// less than 1 hour, display it in minutes/seconds
$sResult = Dict::Format('Core:Duration_Minutes_Seconds', $aDuration['minutes'], $aDuration['seconds']);
} else {
if ($duration < 86400) {
// Less than 1 day, display it in hours/minutes/seconds
$sResult = Dict::Format('Core:Duration_Hours_Minutes_Seconds', $aDuration['hours'],
$aDuration['minutes'], $aDuration['seconds']);
} else {
// more than 1 day, display it in days/hours/minutes/seconds
$sResult = Dict::Format('Core:Duration_Days_Hours_Minutes_Seconds', $aDuration['days'],
$aDuration['hours'], $aDuration['minutes'], $aDuration['seconds']);
}
}
}
return $sResult;
}
static function SplitDuration($duration)
{
$duration = (int)$duration;
$days = floor($duration / 86400);
$hours = floor(($duration - (86400 * $days)) / 3600);
$minutes = floor(($duration - (86400 * $days + 3600 * $hours)) / 60);
$seconds = ($duration % 60); // modulo
return array('days' => $days, 'hours' => $hours, 'minutes' => $minutes, 'seconds' => $seconds);
}
public static function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\DurationField';
}
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
if ($oFormField === null) {
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
parent::MakeFormField($oObject, $oFormField);
// Note : As of today, this attribute is -by nature- only supported in readonly mode, not edition
$sAttCode = $this->GetCode();
$oFormField->SetCurrentValue($oObject->Get($sAttCode));
$oFormField->SetReadOnly(true);
return $oFormField;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* Specialization of a string: email
*
* @package iTopORM
*/
class AttributeEmailAddress extends AttributeString
{
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public function GetValidationPattern()
{
return $this->GetOptional('validation_pattern', '^' . utils::GetConfig()->Get('email_validation_pattern') . '$');
}
public static function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\EmailField';
}
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if (empty($sValue)) {
return '';
}
$sUrlDecorationClass = utils::GetConfig()->Get('email_decoration_class');
return '<a class="mailto" href="mailto:' . $sValue . '"><span class="text_decoration ' . $sUrlDecorationClass . '"></span>' . parent::GetAsHTML($sValue) . '</a>';
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* Map a text column (size < 255) to an attribute that is encrypted in the database
* The encryption is based on a key set per iTop instance. Thus if you export your
* database (in SQL) to someone else without providing the key at the same time
* the encrypted fields will remain encrypted
*
* @package iTopORM
*/
class AttributeEncryptedString extends AttributeString implements iAttributeNoGroupBy
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
protected function GetSQLCol($bFullSpec = false)
{
return "TINYBLOB";
}
public function GetMaxSize()
{
return 255;
}
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue)) {
return null;
}
return (string)$proposedValue;
}
/**
* Decrypt the value when reading from the database
*
* @param array $aCols
* @param string $sPrefix
*
* @return string
* @throws \Exception
*/
public function FromSQLToValue($aCols, $sPrefix = '')
{
$oSimpleCrypt = new SimpleCrypt(MetaModel::GetConfig()->GetEncryptionLibrary());
$sValue = $oSimpleCrypt->Decrypt(MetaModel::GetConfig()->GetEncryptionKey(), $aCols[$sPrefix]);
return $sValue;
}
/**
* Encrypt the value before storing it in the database
*
* @param $value
*
* @return array
* @throws \Exception
*/
public function GetSQLValues($value)
{
$oSimpleCrypt = new SimpleCrypt(MetaModel::GetConfig()->GetEncryptionLibrary());
$encryptedValue = $oSimpleCrypt->Encrypt(MetaModel::GetConfig()->GetEncryptionKey(), $value);
$aValues = array();
$aValues[$this->Get("sql")] = $encryptedValue;
return $aValues;
}
protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void
{
if (is_null($original)) {
$original = '';
}
$oMyChangeOp->Set("prevstring", $original);
}
protected function GetChangeRecordClassName(): string
{
return CMDBChangeOpSetAttributeEncrypted::class;
}
}

View File

@@ -0,0 +1,399 @@
<?php
/**
* Map a enum column to an attribute
*
* @package iTopORM
*/
class AttributeEnum extends AttributeString
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_ENUM;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public static function ListExpectedParams()
{
return parent::ListExpectedParams();
//return array_merge(parent::ListExpectedParams(), array('styled_values'));
}
public function GetEditClass()
{
return "String";
}
/**
* @param string|null $sValue
*
* @return \ormStyle|null
*/
public function GetStyle(?string $sValue): ?ormStyle
{
if ($this->IsParam('styled_values')) {
$aStyles = $this->Get('styled_values');
if (array_key_exists($sValue, $aStyles)) {
return $aStyles[$sValue];
}
}
if ($this->IsParam('default_style')) {
return $this->Get('default_style');
}
return null;
}
protected function GetSQLCol($bFullSpec = false)
{
// Get the definition of the column, including the actual values present in the table
return $this->GetSQLColHelper($bFullSpec, true);
}
/**
* A more versatile version of GetSQLCol
* @param bool $bFullSpec
* @param bool $bIncludeActualValues
* @param string $sSQLTableName The table where to look for the actual values (may be useful for data synchro tables)
* @return string
* @since 3.0.0
*/
protected function GetSQLColHelper($bFullSpec = false, $bIncludeActualValues = false, $sSQLTableName = null)
{
$oValDef = $this->GetValuesDef();
if ($oValDef) {
$aValues = CMDBSource::Quote(array_keys($oValDef->GetValues(array(), "")), true);
} else {
$aValues = array();
}
// Preserve the values already present in the database to ease migrations
if ($bIncludeActualValues) {
if ($sSQLTableName == null) {
// No SQL table given, use the one of the attribute
$sHostClass = $this->GetHostClass();
$sSQLTableName = MetaModel::DBGetTable($sHostClass, $this->GetCode());
}
$aValues = array_unique(array_merge($aValues, $this->GetActualValuesInDB($sSQLTableName)));
}
if (count($aValues) > 0) {
// The syntax used here do matters
// In particular, I had to remove unnecessary spaces to
// make sure that this string will match the field type returned by the DB
// (used to perform a comparison between the current DB format and the data model)
return "ENUM(" . implode(",", $aValues) . ")"
. CMDBSource::GetSqlStringColumnDefinition()
. ($bFullSpec ? $this->GetSQLColSpec() : '');
} else {
return "VARCHAR(255)"
. CMDBSource::GetSqlStringColumnDefinition()
. ($bFullSpec ? " DEFAULT ''" : ""); // ENUM() is not an allowed syntax!
}
}
/**
* @since 3.0.0
* {@inheritDoc}
* @see AttributeDefinition::GetImportColumns()
*/
public function GetImportColumns()
{
// Note: this is used by the Data Synchro to build the "data" table
// Right now the function is not passed the "target" SQL table, but if we improve this in the future
// we may call $this->GetSQLColHelper(true, true, $sDBTable); to take into account the actual 'enum' values
// in this table
return array($this->GetCode() => $this->GetSQLColHelper(false, false));
}
/**
* Get the list of the actual 'enum' values present in the database
* @return string[]
* @since 3.0.0
*/
protected function GetActualValuesInDB(string $sDBTable)
{
$aValues = array();
try {
$sSQL = "SELECT DISTINCT `" . $this->GetSQLExpr() . "` AS value FROM `$sDBTable`;";
$aValuesInDB = CMDBSource::QueryToArray($sSQL);
foreach ($aValuesInDB as $aRow) {
if ($aRow['value'] !== null) {
$aValues[] = $aRow['value'];
}
}
} catch (MySQLException $e) {
// Never mind, maybe the table does not exist yet (new installation from scratch)
// It seems more efficient to try and ignore errors than to test if the table & column really exists
}
return CMDBSource::Quote($aValues);
}
protected function GetSQLColSpec()
{
$default = $this->ScalarToSQL($this->GetDefaultValue());
if (is_null($default)) {
$sRet = '';
} else {
// ENUMs values are strings so the default value must be a string as well,
// otherwise MySQL interprets the number as the zero-based index of the value in the list (i.e. the nth value in the list)
$sRet = " DEFAULT " . CMDBSource::Quote($default);
}
return $sRet;
}
public function ScalarToSQL($value)
{
// Note: for strings, the null value is an empty string and it is recorded as such in the DB
// but that wasn't working for enums, because '' is NOT one of the allowed values
// that's why a null value must be forced to a real null
$value = parent::ScalarToSQL($value);
if ($this->IsNull($value)) {
return null;
} else {
return $value;
}
}
public function RequiresIndex()
{
return false;
}
public function GetBasicFilterOperators()
{
return parent::GetBasicFilterOperators();
}
public function GetBasicFilterLooseOperator()
{
return '=';
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
return parent::GetBasicFilterSQLExpr($sOpCode, $value);
}
public function GetValueLabel($sValue)
{
if (is_null($sValue)) {
// Unless a specific label is defined for the null value of this enum, use a generic "undefined" label
$sLabel = Dict::S('Class:' . $this->GetHostClass() . '/Attribute:' . $this->GetCode() . '/Value:' . $sValue,
Dict::S('Enum:Undefined'));
} else {
$sLabel = $this->SearchLabel('/Attribute:' . $this->m_sCode . '/Value:' . $sValue, null, true /*user lang*/);
if (is_null($sLabel)) {
$sDefault = str_replace('_', ' ', $sValue);
// Browse the hierarchy again, accepting default (english) translations
$sLabel = $this->SearchLabel('/Attribute:' . $this->m_sCode . '/Value:' . $sValue, $sDefault, false);
}
}
return $sLabel;
}
public function GetValueDescription($sValue)
{
if (is_null($sValue)) {
// Unless a specific label is defined for the null value of this enum, use a generic "undefined" label
$sDescription = Dict::S('Class:' . $this->GetHostClass() . '/Attribute:' . $this->GetCode() . '/Value:' . $sValue . '+',
Dict::S('Enum:Undefined'));
} else {
$sDescription = Dict::S('Class:' . $this->GetHostClass() . '/Attribute:' . $this->GetCode() . '/Value:' . $sValue . '+',
'', true /* user language only */);
if (strlen($sDescription) == 0) {
$sParentClass = MetaModel::GetParentClass($this->m_sHostClass);
if ($sParentClass) {
if (MetaModel::IsValidAttCode($sParentClass, $this->m_sCode)) {
$oAttDef = MetaModel::GetAttributeDef($sParentClass, $this->m_sCode);
$sDescription = $oAttDef->GetValueDescription($sValue);
}
}
}
}
return $sDescription;
}
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if ($bLocalize) {
$sLabel = $this->GetValueLabel($sValue);
// $sDescription = $this->GetValueDescription($sValue);
$oStyle = $this->GetStyle($sValue);
// later, we could imagine a detailed description in the title
// $sRes = "<span title=\"$sDescription\">".parent::GetAsHtml($sLabel)."</span>";
$oBadge = \Combodo\iTop\Application\UI\Base\Component\FieldBadge\FieldBadgeUIBlockFactory::MakeForField($sLabel, $oStyle);
$oRenderer = new \Combodo\iTop\Renderer\BlockRenderer($oBadge);
$sRes = $oRenderer->RenderHtml();
} else {
$sRes = parent::GetAsHtml($sValue, $oHostObject, $bLocalize);
}
return $sRes;
}
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
{
if (is_null($value)) {
$sFinalValue = '';
} elseif ($bLocalize) {
$sFinalValue = $this->GetValueLabel($value);
} else {
$sFinalValue = $value;
}
$sRes = parent::GetAsXML($sFinalValue, $oHostObject, $bLocalize);
return $sRes;
}
public function GetAsCSV(
$sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
$bConvertToPlainText = false
)
{
if (is_null($sValue)) {
$sFinalValue = '';
} elseif ($bLocalize) {
$sFinalValue = $this->GetValueLabel($sValue);
} else {
$sFinalValue = $sValue;
}
$sRes = parent::GetAsCSV($sFinalValue, $sSeparator, $sTextQualifier, $oHostObject, $bLocalize);
return $sRes;
}
public static function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\SelectField';
}
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
if ($oFormField === null) {
// Later : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
$oFormField->SetChoices($this->GetAllowedValues($oObject->ToArgsForQuery()));
parent::MakeFormField($oObject, $oFormField);
return $oFormField;
}
public function GetEditValue($sValue, $oHostObj = null)
{
if (is_null($sValue)) {
return '';
} else {
return $this->GetValueLabel($sValue);
}
}
public function GetForJSON($value)
{
return $value;
}
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
$aRawValues = parent::GetAllowedValues($aArgs, $sContains);
if (is_null($aRawValues)) {
return null;
}
$aLocalizedValues = array();
foreach ($aRawValues as $sKey => $sValue) {
$aLocalizedValues[$sKey] = $this->GetValueLabel($sKey);
}
// Sort by label only if necessary
// See N°1646 and {@see \MFCompiler::CompileAttributeEnumValues()} for complete information as for why sort on labels is done at runtime while other sorting are done at compile time
/** @var \ValueSetEnum $oValueSetDef */
$oValueSetDef = $this->GetValuesDef();
if ($oValueSetDef->IsSortedByValues()) {
asort($aLocalizedValues);
}
return $aLocalizedValues;
}
public function GetMaxSize()
{
return null;
}
/**
* An enum can be localized
*/
public function MakeValueFromString(
$sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null,
$sAttributeQualifier = null
)
{
if ($bLocalizedValue) {
// Lookup for the value matching the input
//
$sFoundValue = null;
$aRawValues = parent::GetAllowedValues();
if (!is_null($aRawValues)) {
foreach ($aRawValues as $sKey => $sValue) {
$sRefValue = $this->GetValueLabel($sKey);
if ($sProposedValue == $sRefValue) {
$sFoundValue = $sKey;
break;
}
}
}
if (is_null($sFoundValue)) {
return null;
}
return $this->MakeRealValue($sFoundValue, null);
} else {
return parent::MakeValueFromString($sProposedValue, $bLocalizedValue, $sSepItem, $sSepAttribute, $sSepValue,
$sAttributeQualifier);
}
}
/**
* Processes the input value to align it with the values supported
* by this type of attribute. In this case: turns empty strings into nulls
*
* @param mixed $proposedValue The value to be set for the attribute
*
* @return mixed The actual value that will be set
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
if ($proposedValue == '') {
return null;
}
return parent::MakeRealValue($proposedValue, $oHostObj);
}
public function GetOrderByHint()
{
$aValues = $this->GetAllowedValues();
return Dict::Format('UI:OrderByHint_Values', implode(', ', $aValues));
}
}

View File

@@ -0,0 +1,230 @@
<?php
/**
* @since 2.7.0 N°985
*/
class AttributeEnumSet extends AttributeSet
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_TAG_SET;
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array('possible_values', 'is_null_allowed', 'max_items'));
}
public function GetMaxSize()
{
$aRawValues = $this->GetRawPossibleValues();
$iMaxItems = $this->GetMaxItems();
$aLengths = array();
foreach (array_keys($aRawValues) as $sKey) {
$aLengths[] = strlen($sKey);
}
rsort($aLengths, SORT_NUMERIC);
$iMaxSize = 2;
for ($i = 0; $i < min($iMaxItems, count($aLengths)); $i++) {
$iMaxSize += $aLengths[$i] + 1;
}
return max(255, $iMaxSize);
}
private function GetRawPossibleValues($aArgs = array(), $sContains = '')
{
/** @var ValueSetEnumPadded $oValSetDef */
$oValSetDef = $this->Get('possible_values');
if (!$oValSetDef) {
return array();
}
return $oValSetDef->GetValues($aArgs, $sContains);
}
public function GetPossibleValues($aArgs = array(), $sContains = '')
{
$aRawValues = $this->GetRawPossibleValues($aArgs, $sContains);
$aLocalizedValues = array();
foreach ($aRawValues as $sKey => $sValue) {
$aLocalizedValues[$sKey] = $this->GetValueLabel($sKey);
}
return $aLocalizedValues;
}
public function GetValueLabel($sValue)
{
if ($sValue instanceof ormSet) {
$sValue = implode(', ', $sValue->GetValues());
}
$aValues = $this->GetRawPossibleValues();
if (is_array($aValues) && is_string($sValue) && isset($aValues[$sValue])) {
$sValue = $aValues[$sValue];
}
if (is_null($sValue)) {
// Unless a specific label is defined for the null value of this enum, use a generic "undefined" label
$sLabel = Dict::S('Class:' . $this->GetHostClass() . '/Attribute:' . $this->GetCode() . '/Value:' . $sValue,
Dict::S('Enum:Undefined'));
} else {
$sLabel = $this->SearchLabel('/Attribute:' . $this->m_sCode . '/Value:' . $sValue, null, true /*user lang*/);
if (is_null($sLabel)) {
// Browse the hierarchy again, accepting default (english) translations
$sLabel = $this->SearchLabel('/Attribute:' . $this->m_sCode . '/Value:' . $sValue, null, false);
if (is_null($sLabel)) {
$sDefault = trim(str_replace('_', ' ', $sValue));
// Browse the hierarchy again, accepting default (english) translations
$sLabel = $this->SearchLabel('/Attribute:' . $this->m_sCode . '/Value:' . $sDefault, $sDefault, false);
}
}
}
return $sLabel;
}
public function GetValueDescription($sValue)
{
if (is_null($sValue)) {
// Unless a specific label is defined for the null value of this enum, use a generic "undefined" label
$sDescription = Dict::S('Class:' . $this->GetHostClass() . '/Attribute:' . $this->GetCode() . '/Value:' . $sValue . '+',
Dict::S('Enum:Undefined'));
} else {
$sDescription = Dict::S('Class:' . $this->GetHostClass() . '/Attribute:' . $this->GetCode() . '/Value:' . $sValue . '+',
'', true /* user language only */);
if (strlen($sDescription) == 0) {
$sParentClass = MetaModel::GetParentClass($this->m_sHostClass);
if ($sParentClass) {
if (MetaModel::IsValidAttCode($sParentClass, $this->m_sCode)) {
$oAttDef = MetaModel::GetAttributeDef($sParentClass, $this->m_sCode);
$sDescription = $oAttDef->GetValueDescription($sValue);
}
}
}
}
return $sDescription;
}
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
{
if ($bLocalize) {
if ($value instanceof ormSet) {
$sRes = $this->GenerateViewHtmlForValues($value->GetValues());
} else {
$sLabel = $this->GetValueLabel($value);
$sDescription = $this->GetValueDescription($value);
$sRes = "<span title=\"$sDescription\">" . parent::GetAsHtml($sLabel) . "</span>";
}
} else {
$sRes = parent::GetAsHtml($value, $oHostObject, $bLocalize);
}
return $sRes;
}
/**
* @param ormSet $value
* @param string $sSeparator
* @param string $sTextQualifier
* @param \DBObject $oHostObject
* @param bool $bLocalize
* @param bool $bConvertToPlainText
*
* @return mixed|string
* @throws \Exception
*/
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
{
$sSepItem = MetaModel::GetConfig()->Get('tag_set_item_separator');
if (is_object($value) && ($value instanceof ormSet)) {
$aValues = $value->GetValues();
if ($bLocalize) {
$aLocalizedValues = array();
foreach ($aValues as $sValue) {
$aLocalizedValues[] = $this->GetValueLabel($sValue);
}
$aValues = $aLocalizedValues;
}
$sRes = implode($sSepItem, $aValues);
} else {
$sRes = '';
}
return "{$sTextQualifier}{$sRes}{$sTextQualifier}";
}
/**
* Get the value from a given string (plain text, CSV import)
*
* @param string $sProposedValue
* @param bool $bLocalizedValue
* @param string $sSepItem
* @param string $sSepAttribute
* @param string $sSepValue
* @param string $sAttributeQualifier
*
* @return mixed null if no match could be found
* @throws \Exception
*/
public function MakeValueFromString($sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null, $sAttributeQualifier = null)
{
if ($bLocalizedValue) {
// Lookup for the values matching the input
//
$aValues = $this->FromStringToArray($sProposedValue);
$aFoundValues = array();
$aRawValues = $this->GetPossibleValues();
foreach ($aValues as $sValue) {
$bFound = false;
foreach ($aRawValues as $sCode => $sRawValue) {
if ($sValue == $sRawValue) {
$aFoundValues[] = $sCode;
$bFound = true;
break;
}
}
if (!$bFound) {
// Not found, break the import
return null;
}
}
return $this->MakeRealValue(implode(',', $aFoundValues), null);
} else {
return $this->MakeRealValue($sProposedValue, null, false);
}
}
/**
* @param string $proposedValue Search string used for MATCHES
*
* @param string $sDefaultSepItem word separator to extract items
*
* @return array of EnumSet codes
* @throws \Exception
*/
public function FromStringToArray($proposedValue, $sDefaultSepItem = ',')
{
$aValues = array();
if (!empty($proposedValue)) {
$sSepItem = MetaModel::GetConfig()->Get('tag_set_item_separator');
// convert also other separators
if ($sSepItem !== $sDefaultSepItem) {
$proposedValue = str_replace($sDefaultSepItem, $sSepItem, $proposedValue);
}
foreach (explode($sSepItem, $proposedValue) as $sCode) {
$sValue = trim($sCode);
if (strlen($sValue) > 2) {
$sLabel = $this->GetValueLabel($sValue);
$aValues[$sLabel] = $sValue;
}
}
}
return $aValues;
}
public function Equals($val1, $val2)
{
return $val1->Equals($val2);
}
}

View File

@@ -0,0 +1,511 @@
<?php
/**
* An attribute which corresponds to an external key (direct or indirect)
*
* @package iTopORM
*/
class AttributeExternalField extends AttributeDefinition
{
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
/**
* Return the search widget type corresponding to this attribute
*
* @return string
* @throws \CoreException
*/
public function GetSearchType()
{
// Not necessary the external key is already present
if ($this->IsFriendlyName()) {
return self::SEARCH_WIDGET_TYPE_RAW;
}
try {
$oRemoteAtt = $this->GetFinalAttDef();
switch (true) {
case ($oRemoteAtt instanceof AttributeString):
return self::SEARCH_WIDGET_TYPE_EXTERNAL_FIELD;
case ($oRemoteAtt instanceof AttributeExternalKey):
return self::SEARCH_WIDGET_TYPE_EXTERNAL_KEY;
}
} catch (CoreException $e) {
}
return self::SEARCH_WIDGET_TYPE_RAW;
}
function IsSearchable()
{
if ($this->IsFriendlyName()) {
return true;
}
return parent::IsSearchable();
}
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("extkey_attcode", "target_attcode"));
}
public function GetEditClass()
{
return "ExtField";
}
/**
* @return \AttributeDefinition
* @throws \CoreException
*/
public function GetFinalAttDef()
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetFinalAttDef();
}
protected function GetSQLCol($bFullSpec = false)
{
// throw new CoreException("external attribute: does it make any sense to request its type ?");
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetSQLCol($bFullSpec);
}
public function GetSQLExpressions($sPrefix = '')
{
if ($sPrefix == '') {
return array('' => $this->GetCode()); // Warning: Use GetCode() since AttributeExternalField does not have any 'sql' property
} else {
return $sPrefix;
}
}
/**
* @param string $sDefault
*
* @return string dict entry if defined, otherwise :
* <ul>
* <li>if field is a friendlyname then display the label of the ExternalKey
* <li>the class hierarchy -> field name
*
* <p>For example, having this :
*
* <pre>
* +---------------------+ +--------------------+ +--------------+
* | Class A | | Class B | | Class C |
* +---------------------+ +--------------------+ +--------------+
* | foo <ExternalField>-------->c_id_friendly_name--------->friendlyname |
* +---------------------+ +--------------------+ +--------------+
* </pre>
*
* <p>The ExternalField foo points to a magical field that is brought by c_id ExternalKey in class B.
*
* <p>In the normal case the foo label would be : B -> C -> friendlyname<br>
* But as foo is a friendlyname its label will be the same as the one on A.b_id field
* This can be overrided with dict key Class:ClassA/Attribute:foo
*
* @throws \CoreException
* @throws \Exception
*/
public function GetLabel($sDefault = null)
{
$sLabelDefaultValue = '';
$sLabel = parent::GetLabel($sLabelDefaultValue);
if ($sLabelDefaultValue !== $sLabel) {
return $sLabel;
}
if ($this->IsFriendlyName() && ($this->Get("target_attcode") === "friendlyname")) {
// This will be used even if we are pointing to a friendlyname in a distance > 1
// For example we can link to a magic friendlyname (like org_id_friendlyname)
// If a specific label is needed, use a Dict key !
// See N°2174
$sKeyAttCode = $this->Get("extkey_attcode");
$oExtKeyAttDef = MetaModel::GetAttributeDef($this->GetHostClass(), $sKeyAttCode);
$sLabel = $oExtKeyAttDef->GetLabel($this->m_sCode);
return $sLabel;
}
$oRemoteAtt = $this->GetExtAttDef();
$sLabel = $oRemoteAtt->GetLabel($this->m_sCode);
$oKeyAtt = $this->GetKeyAttDef();
$sKeyLabel = $oKeyAtt->GetLabel($this->GetKeyAttCode());
$sLabel = "{$sKeyLabel}->{$sLabel}";
return $sLabel;
}
public function GetLabelForSearchField()
{
$sLabel = parent::GetLabel('');
if (strlen($sLabel) == 0) {
$sKeyAttCode = $this->Get("extkey_attcode");
$oExtKeyAttDef = MetaModel::GetAttributeDef($this->GetHostClass(), $sKeyAttCode);
$sLabel = $oExtKeyAttDef->GetLabel($this->m_sCode);
$oRemoteAtt = $this->GetExtAttDef();
$sLabel .= '->' . $oRemoteAtt->GetLabel($this->m_sCode);
}
return $sLabel;
}
public function GetDescription($sDefault = null)
{
$sLabel = parent::GetDescription('');
if (strlen($sLabel) == 0) {
$oRemoteAtt = $this->GetExtAttDef();
$sLabel = $oRemoteAtt->GetDescription('');
}
return $sLabel;
}
public function GetHelpOnEdition($sDefault = null)
{
$sLabel = parent::GetHelpOnEdition('');
if (strlen($sLabel) == 0) {
$oRemoteAtt = $this->GetExtAttDef();
$sLabel = $oRemoteAtt->GetHelpOnEdition('');
}
return $sLabel;
}
public function IsExternalKey($iType = EXTKEY_RELATIVE)
{
switch ($iType) {
case EXTKEY_ABSOLUTE:
// see further
$oRemoteAtt = $this->GetExtAttDef();
return $oRemoteAtt->IsExternalKey($iType);
case EXTKEY_RELATIVE:
return false;
default:
throw new CoreException("Unexpected value for argument iType: '$iType'");
}
}
/**
* @return bool
* @throws \CoreException
*/
public function IsFriendlyName()
{
$oRemoteAtt = $this->GetExtAttDef();
if ($oRemoteAtt instanceof AttributeExternalField) {
$bRet = $oRemoteAtt->IsFriendlyName();
} elseif ($oRemoteAtt instanceof AttributeFriendlyName) {
$bRet = true;
} else {
$bRet = false;
}
return $bRet;
}
public function GetTargetClass($iType = EXTKEY_RELATIVE)
{
return $this->GetKeyAttDef($iType)->GetTargetClass();
}
public static function IsExternalField()
{
return true;
}
public function GetKeyAttCode()
{
return $this->Get("extkey_attcode");
}
public function GetExtAttCode()
{
return $this->Get("target_attcode");
}
/**
* @param int $iType
*
* @return \AttributeExternalKey
* @throws \CoreException
* @throws \Exception
*/
public function GetKeyAttDef($iType = EXTKEY_RELATIVE)
{
switch ($iType) {
case EXTKEY_ABSOLUTE:
// see further
/** @var \AttributeExternalKey $oRemoteAtt */
$oRemoteAtt = $this->GetExtAttDef();
if ($oRemoteAtt->IsExternalField()) {
return $oRemoteAtt->GetKeyAttDef(EXTKEY_ABSOLUTE);
} else {
if ($oRemoteAtt->IsExternalKey()) {
return $oRemoteAtt;
}
}
return $this->GetKeyAttDef(EXTKEY_RELATIVE); // which corresponds to the code hereafter !
case EXTKEY_RELATIVE:
/** @var \AttributeExternalKey $oAttDef */
$oAttDef = MetaModel::GetAttributeDef($this->GetHostClass(), $this->Get("extkey_attcode"));
return $oAttDef;
default:
throw new CoreException("Unexpected value for argument iType: '$iType'");
}
}
public function GetPrerequisiteAttributes($sClass = null)
{
return array($this->Get("extkey_attcode"));
}
/**
* @return \AttributeExternalField
* @throws \CoreException
* @throws \Exception
*/
public function GetExtAttDef()
{
$oKeyAttDef = $this->GetKeyAttDef();
/** @var \AttributeExternalField $oExtAttDef */
$oExtAttDef = MetaModel::GetAttributeDef($oKeyAttDef->GetTargetClass(), $this->Get("target_attcode"));
if (!is_object($oExtAttDef)) {
throw new CoreException("Invalid external field " . $this->GetCode() . " in class " . $this->GetHostClass() . ". The class " . $oKeyAttDef->GetTargetClass() . " has no attribute " . $this->Get("target_attcode"));
}
return $oExtAttDef;
}
/**
* @return mixed
* @throws \CoreException
*/
public function GetSQLExpr()
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetSQLExpr();
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetDefaultValue();
}
public function IsNullAllowed()
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->IsNullAllowed();
}
public static function IsScalar()
{
return true;
}
public function GetBasicFilterOperators()
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetBasicFilterOperators();
}
public function GetBasicFilterLooseOperator()
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetBasicFilterLooseOperator();
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetBasicFilterSQLExpr($sOpCode, $value);
}
public function GetNullValue()
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetNullValue();
}
public function IsNull($proposedValue)
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->IsNull($proposedValue);
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->HasAValue($proposedValue);
}
public function MakeRealValue($proposedValue, $oHostObj)
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->MakeRealValue($proposedValue, $oHostObj);
}
/**
* @inheritDoc
* @since 3.1.0 N°6271 Delegate to remote attribute to ensure cascading computed values
*/
public function GetSQLValues($value)
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetSQLValues($value);
}
public function ScalarToSQL($value)
{
// This one could be used in case of filtering only
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->ScalarToSQL($value);
}
// Do not overload GetSQLExpression here because this is handled in the joins
//public function GetSQLExpressions($sPrefix = '') {return array();}
// Here, we get the data...
public function FromSQLToValue($aCols, $sPrefix = '')
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->FromSQLToValue($aCols, $sPrefix);
}
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetAsHTML($value, null, $bLocalize);
}
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetAsXML($value, null, $bLocalize);
}
public function GetAsCSV(
$value, $sSeparator = ',', $sTestQualifier = '"', $oHostObject = null, $bLocalize = true,
$bConvertToPlainText = false
)
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetAsCSV($value, $sSeparator, $sTestQualifier, null, $bLocalize, $bConvertToPlainText);
}
public static function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\LabelField';
}
/**
* @param \DBObject $oObject
* @param \Combodo\iTop\Form\Field\Field $oFormField
*
* @return null
* @throws \CoreException
*/
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
// Retrieving AttDef from the remote attribute
$oRemoteAttDef = $this->GetExtAttDef();
if ($oFormField === null) {
// ExternalField's FormField are actually based on the FormField from the target attribute.
// Except for the AttributeExternalKey because we have no OQL and stuff
if ($oRemoteAttDef instanceof AttributeExternalKey) {
$sFormFieldClass = static::GetFormFieldClass();
} else {
$sFormFieldClass = $oRemoteAttDef::GetFormFieldClass();
}
/** @var \Combodo\iTop\Form\Field\Field $oFormField */
$oFormField = new $sFormFieldClass($this->GetCode());
switch ($sFormFieldClass) {
case '\Combodo\iTop\Form\Field\SelectField':
$oFormField->SetChoices($oRemoteAttDef->GetAllowedValues($oObject->ToArgsForQuery()));
break;
default:
break;
}
}
parent::MakeFormField($oObject, $oFormField);
if ($oFormField instanceof \Combodo\iTop\Form\Field\TextAreaField) {
if (method_exists($oRemoteAttDef, 'GetFormat')) {
/** @var \Combodo\iTop\Form\Field\TextAreaField $oFormField */
$oFormField->SetFormat($oRemoteAttDef->GetFormat());
}
}
// Manually setting for remote ExternalKey, otherwise, the id would be displayed.
if ($oRemoteAttDef instanceof AttributeExternalKey) {
$oFormField->SetCurrentValue($oObject->Get($this->GetCode() . '_friendlyname'));
}
// Readonly field because we can't update external fields
$oFormField->SetReadOnly(true);
return $oFormField;
}
public function IsPartOfFingerprint()
{
return false;
}
public function GetFormat()
{
$oRemoteAttDef = $this->GetExtAttDef();
if (method_exists($oRemoteAttDef, 'GetFormat')) {
/** @var \Combodo\iTop\Form\Field\TextAreaField $oFormField */
return $oRemoteAttDef->GetFormat();
}
return 'text';
}
}

View File

@@ -0,0 +1,335 @@
<?php
/**
* Map a foreign key to an attribute
* AttributeExternalKey and AttributeExternalField may be an external key
* the difference is that AttributeExternalKey corresponds to a column into the defined table
* where an AttributeExternalField corresponds to a column into another table (class)
*
* @package iTopORM
*/
class AttributeExternalKey extends AttributeDBFieldVoid
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_EXTERNAL_KEY;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
/**
* Return the search widget type corresponding to this attribute
*
* @return string
*/
public function GetSearchType()
{
try {
$oRemoteAtt = $this->GetFinalAttDef();
$sTargetClass = $oRemoteAtt->GetTargetClass();
if (MetaModel::IsHierarchicalClass($sTargetClass)) {
return self::SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY;
}
return self::SEARCH_WIDGET_TYPE_EXTERNAL_KEY;
} catch (CoreException $e) {
}
return self::SEARCH_WIDGET_TYPE_RAW;
}
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("targetclass", "is_null_allowed", "on_target_delete"));
}
public function GetEditClass()
{
return "ExtKey";
}
protected function GetSQLCol($bFullSpec = false)
{
return "INT(11)" . ($bFullSpec ? " DEFAULT 0" : "");
}
public function RequiresIndex()
{
return true;
}
public function IsExternalKey($iType = EXTKEY_RELATIVE)
{
return true;
}
public function GetTargetClass($iType = EXTKEY_RELATIVE)
{
return $this->Get("targetclass");
}
public function GetKeyAttDef($iType = EXTKEY_RELATIVE)
{
return $this;
}
public function GetKeyAttCode()
{
return $this->GetCode();
}
public function GetDisplayStyle()
{
return $this->GetOptional('display_style', 'select');
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
return 0;
}
public function IsNullAllowed()
{
if (MetaModel::GetConfig()->Get('disable_mandatory_ext_keys')) {
return true;
}
return $this->Get("is_null_allowed");
}
public function GetBasicFilterOperators()
{
return parent::GetBasicFilterOperators();
}
public function GetBasicFilterLooseOperator()
{
return parent::GetBasicFilterLooseOperator();
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
return parent::GetBasicFilterSQLExpr($sOpCode, $value);
}
// overloaded here so that an ext key always have the answer to
// "what are your possible values?"
public function GetValuesDef()
{
$oValSetDef = $this->Get("allowed_values");
if (!$oValSetDef) {
// Let's propose every existing value
$oValSetDef = new ValueSetObjects('SELECT ' . $this->GetTargetClass());
}
return $oValSetDef;
}
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
//throw new Exception("GetAllowedValues on ext key has been deprecated");
try {
return parent::GetAllowedValues($aArgs, $sContains);
} catch (Exception $e) {
// Some required arguments could not be found, enlarge to any existing value
$oValSetDef = new ValueSetObjects('SELECT ' . $this->GetTargetClass());
return $oValSetDef->GetValues($aArgs, $sContains);
}
}
public function GetAllowedValuesForSelect($aArgs = array(), $sContains = '')
{
//$this->GetValuesDef();
$oValSetDef = new ValueSetObjects('SELECT ' . $this->GetTargetClass());
return $oValSetDef->GetValuesForAutocomplete($aArgs, $sContains);
}
public function GetAllowedValuesAsObjectSet($aArgs = array(), $sContains = '', $iAdditionalValue = null)
{
$oValSetDef = $this->GetValuesDef();
$oSet = $oValSetDef->ToObjectSet($aArgs, $sContains, $iAdditionalValue);
return $oSet;
}
public function GetAllowedValuesAsFilter($aArgs = array(), $sContains = '', $iAdditionalValue = null)
{
return DBObjectSearch::FromOQL($this->GetValuesDef()->GetFilterExpression());
}
public function GetDeletionPropagationOption()
{
return $this->Get("on_target_delete");
}
public function GetNullValue()
{
return 0;
}
public function IsNull($proposedValue)
{
return ($proposedValue == 0);
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
return ((int)$proposedValue) !== 0;
}
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue)) {
return 0;
}
if ($proposedValue === '') {
return 0;
}
if (MetaModel::IsValidObject($proposedValue)) {
return $proposedValue->GetKey();
}
return (int)$proposedValue;
}
/** @inheritdoc @since 3.1 */
public function WriteExternalValues(DBObject $oHostObject): void
{
$sTargetKey = $oHostObject->Get($this->GetCode());
$oFilter = DBSearch::FromOQL('SELECT `' . TemporaryObjectDescriptor::class . '` WHERE item_class=:class AND item_id=:id');
$oSet = new DBObjectSet($oFilter, [], ['class' => $this->GetTargetClass(), 'id' => $sTargetKey]);
while ($oTemporaryObjectDescriptor = $oSet->Fetch()) {
$oTemporaryObjectDescriptor->Set('host_class', get_class($oHostObject));
$oTemporaryObjectDescriptor->Set('host_id', $oHostObject->GetKey());
$oTemporaryObjectDescriptor->Set('host_att_code', $this->GetCode());
$oTemporaryObjectDescriptor->DBUpdate();
}
}
public function GetMaximumComboLength()
{
return $this->GetOptional('max_combo_length', MetaModel::GetConfig()->Get('max_combo_length'));
}
public function GetMinAutoCompleteChars()
{
return $this->GetOptional('min_autocomplete_chars', MetaModel::GetConfig()->Get('min_autocomplete_chars'));
}
/**
* @return int
* @since 3.0.0
*/
public function GetMaxAutoCompleteResults(): int
{
return MetaModel::GetConfig()->Get('max_autocomplete_results');
}
public function AllowTargetCreation()
{
return $this->GetOptional('allow_target_creation', MetaModel::GetConfig()->Get('allow_target_creation'));
}
/**
* Find the corresponding "link" attribute on the target class, if any
*
* @return null | AttributeDefinition
* @throws \CoreException
*/
public function GetMirrorLinkAttribute()
{
$oRet = null;
$sRemoteClass = $this->GetTargetClass();
foreach (MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef) {
if (!$oRemoteAttDef->IsLinkSet()) {
continue;
}
if (!is_subclass_of($this->GetHostClass(),
$oRemoteAttDef->GetLinkedClass()) && $oRemoteAttDef->GetLinkedClass() != $this->GetHostClass()) {
continue;
}
if ($oRemoteAttDef->GetExtKeyToMe() != $this->GetCode()) {
continue;
}
$oRet = $oRemoteAttDef;
break;
}
return $oRet;
}
public static function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\SelectObjectField';
}
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
/** @var \Combodo\iTop\Form\Field\Field $oFormField */
if ($oFormField === null) {
// Later : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
// Setting params
$oFormField->SetMaximumComboLength($this->GetMaximumComboLength());
$oFormField->SetMinAutoCompleteChars($this->GetMinAutoCompleteChars());
$oFormField->SetMaxAutoCompleteResults($this->GetMaxAutoCompleteResults());
$oFormField->SetHierarchical(MetaModel::IsHierarchicalClass($this->GetTargetClass()));
// Setting choices regarding the field dependencies
$aFieldDependencies = $this->GetPrerequisiteAttributes();
if (!empty($aFieldDependencies)) {
$oTmpAttDef = $this;
$oTmpField = $oFormField;
$oFormField->SetOnFinalizeCallback(function () use ($oTmpField, $oTmpAttDef, $oObject) {
/** @var $oTmpField \Combodo\iTop\Form\Field\Field */
/** @var $oTmpAttDef \AttributeDefinition */
/** @var $oObject \DBObject */
// We set search object only if it has not already been set (overrided)
if ($oTmpField->GetSearch() === null) {
$oSearch = DBSearch::FromOQL($oTmpAttDef->GetValuesDef()->GetFilterExpression());
$oSearch->SetInternalParams(array('this' => $oObject));
$oTmpField->SetSearch($oSearch);
}
});
} else {
$oSearch = DBSearch::FromOQL($this->GetValuesDef()->GetFilterExpression());
$oSearch->SetInternalParams(array('this' => $oObject));
$oFormField->SetSearch($oSearch);
}
parent::MakeFormField($oObject, $oFormField);
return $oFormField;
}
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if (!is_null($oHostObject)) {
return $oHostObject->GetAsHTML($this->GetCode(), $oHostObject);
}
return DBObject::MakeHyperLink($this->GetTargetClass(), $sValue);
}
}

View File

@@ -0,0 +1,197 @@
<?php
/**
* The attribute dedicated to the finalclass automatic attribute
*
* @package iTopORM
*/
class AttributeFinalClass extends AttributeString
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
public $m_sValue;
public function __construct($sCode, $aParams)
{
$this->m_sCode = $sCode;
$aParams["allowed_values"] = null;
parent::__construct($sCode, $aParams);
$this->m_sValue = $this->Get("default_value");
}
public function IsWritable()
{
return false;
}
public function IsMagic()
{
return true;
}
public function RequiresIndex()
{
return true;
}
public function SetFixedValue($sValue)
{
$this->m_sValue = $sValue;
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
return $this->m_sValue;
}
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if (empty($sValue)) {
return '';
}
if ($bLocalize) {
return MetaModel::GetName($sValue);
} else {
return $sValue;
}
}
/**
* An enum can be localized
*
* @param string $sProposedValue
* @param bool $bLocalizedValue
* @param string $sSepItem
* @param string $sSepAttribute
* @param string $sSepValue
* @param string $sAttributeQualifier
*
* @return mixed|null|string
* @throws \CoreException
* @throws \OQLException
*/
public function MakeValueFromString(
$sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null,
$sAttributeQualifier = null
)
{
if ($bLocalizedValue) {
// Lookup for the value matching the input
//
$sFoundValue = null;
$aRawValues = self::GetAllowedValues();
if (!is_null($aRawValues)) {
foreach ($aRawValues as $sKey => $sValue) {
if ($sProposedValue == $sValue) {
$sFoundValue = $sKey;
break;
}
}
}
if (is_null($sFoundValue)) {
return null;
}
return $this->MakeRealValue($sFoundValue, null);
} else {
return parent::MakeValueFromString($sProposedValue, $bLocalizedValue, $sSepItem, $sSepAttribute, $sSepValue,
$sAttributeQualifier);
}
}
// Because this is sometimes used to get a localized/string version of an attribute...
public function GetEditValue($sValue, $oHostObj = null)
{
if (empty($sValue)) {
return '';
}
return MetaModel::GetName($sValue);
}
public function GetForJSON($value)
{
// JSON values are NOT localized
return $value;
}
/**
* @param $value
* @param string $sSeparator
* @param string $sTextQualifier
* @param \DBObject $oHostObject
* @param bool $bLocalize
* @param bool $bConvertToPlainText
*
* @return string
* @throws \CoreException
* @throws \DictExceptionMissingString
*/
public function GetAsCSV(
$value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
$bConvertToPlainText = false
)
{
if ($bLocalize && $value != '') {
$sRawValue = MetaModel::GetName($value);
} else {
$sRawValue = $value;
}
return parent::GetAsCSV($sRawValue, $sSeparator, $sTextQualifier, null, false, $bConvertToPlainText);
}
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
{
if (empty($value)) {
return '';
}
if ($bLocalize) {
$sRawValue = MetaModel::GetName($value);
} else {
$sRawValue = $value;
}
return Str::pure2xml($sRawValue);
}
public function GetBasicFilterLooseOperator()
{
return '=';
}
public function GetValueLabel($sValue)
{
if (empty($sValue)) {
return '';
}
return MetaModel::GetName($sValue);
}
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
$aRawValues = MetaModel::EnumChildClasses($this->GetHostClass(), ENUM_CHILD_CLASSES_ALL);
$aLocalizedValues = array();
foreach ($aRawValues as $sClass) {
$aLocalizedValues[$sClass] = MetaModel::GetName($sClass);
}
return $aLocalizedValues;
}
/**
* @return bool
* @since 2.7.0 N°2272 OQL perf finalclass in all intermediary tables
*/
public function CopyOnAllTables()
{
$sClass = self::GetHostClass();
if (MetaModel::IsLeafClass($sClass)) {
// Leaf class, no finalclass
return false;
}
return true;
}
}

View File

@@ -0,0 +1,199 @@
<?php
/**
* The attribute dedicated to the friendly name automatic attribute (not written)
*
* @package iTopORM
*/
class AttributeFriendlyName extends AttributeDefinition
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
public $m_sValue;
public function __construct($sCode)
{
$this->m_sCode = $sCode;
$aParams = array();
$aParams["default_value"] = '';
parent::__construct($sCode, $aParams);
$this->m_sValue = $this->Get("default_value");
}
public function GetEditClass()
{
return "";
}
public function GetValuesDef()
{
return null;
}
public function GetPrerequisiteAttributes($sClass = null)
{
// Code duplicated with AttributeObsolescenceFlag
$aAttributes = $this->GetOptional("depends_on", array());
$oExpression = $this->GetOQLExpression();
foreach ($oExpression->ListRequiredFields() as $sAttCode) {
if (!in_array($sAttCode, $aAttributes)) {
$aAttributes[] = $sAttCode;
}
}
return $aAttributes;
}
public static function IsScalar()
{
return true;
}
public function IsNullAllowed()
{
return false;
}
public function GetSQLExpressions($sPrefix = '')
{
if ($sPrefix == '') {
$sPrefix = $this->GetCode(); // Warning AttributeComputedFieldVoid does not have any sql property
}
return array('' => $sPrefix);
}
public static function IsBasedOnOQLExpression()
{
return true;
}
public function GetOQLExpression()
{
return MetaModel::GetNameExpression($this->GetHostClass());
}
public function GetLabel($sDefault = null)
{
$sLabel = parent::GetLabel('');
if (strlen($sLabel) == 0) {
$sLabel = Dict::S('Core:FriendlyName-Label');
}
return $sLabel;
}
public function GetDescription($sDefault = null)
{
$sLabel = parent::GetDescription('');
if (strlen($sLabel) == 0) {
$sLabel = Dict::S('Core:FriendlyName-Description');
}
return $sLabel;
}
public function FromSQLToValue($aCols, $sPrefix = '')
{
$sValue = $aCols[$sPrefix];
return $sValue;
}
public function IsWritable()
{
return false;
}
public function IsMagic()
{
return true;
}
public static function IsBasedOnDBColumns()
{
return false;
}
public function SetFixedValue($sValue)
{
$this->m_sValue = $sValue;
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
return $this->m_sValue;
}
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
return Str::pure2html((string)$sValue);
}
public function GetAsCSV(
$sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
$bConvertToPlainText = false
)
{
$sFrom = array("\r\n", $sTextQualifier);
$sTo = array("\n", $sTextQualifier . $sTextQualifier);
$sEscaped = str_replace($sFrom, $sTo, (string)$sValue);
return $sTextQualifier . $sEscaped . $sTextQualifier;
}
static function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\StringField';
}
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
if ($oFormField === null) {
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
$oFormField->SetReadOnly(true);
parent::MakeFormField($oObject, $oFormField);
return $oFormField;
}
// Do not display friendly names in the history of change
public function DescribeChangeAsHTML($sOldValue, $sNewValue, $sLabel = null)
{
return '';
}
public function GetBasicFilterOperators()
{
return array("=" => "equals", "!=" => "differs from");
}
public function GetBasicFilterLooseOperator()
{
return "Contains";
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
$sQValue = CMDBSource::Quote($value);
switch ($sOpCode) {
case '=':
case '!=':
return $this->GetSQLExpr() . " $sOpCode $sQValue";
case 'Contains':
return $this->GetSQLExpr() . " LIKE " . CMDBSource::Quote("%$value%");
case 'NotLike':
return $this->GetSQLExpr() . " NOT LIKE $sQValue";
case 'Like':
default:
return $this->GetSQLExpr() . " LIKE $sQValue";
}
}
public function IsPartOfFingerprint()
{
return false;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Map a text column (size > ?), containing HTML code, to an attribute
*
* @package iTopORM
*/
class AttributeHTML extends AttributeLongText
{
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public function GetSQLColumns($bFullSpec = false)
{
$aColumns = array();
$aColumns[$this->Get('sql')] = $this->GetSQLCol();
if ($this->GetOptional('format', null) != null) {
// Add the extra column only if the property 'format' is specified for the attribute
$aColumns[$this->Get('sql') . '_format'] = "ENUM('text','html')";
if ($bFullSpec) {
$aColumns[$this->Get('sql') . '_format'] .= " DEFAULT 'html'"; // default 'html' is for migrating old records
}
}
return $aColumns;
}
/**
* The actual formatting of the text: either text (=plain text) or html (= text with HTML markup)
*
* @return string
*/
public function GetFormat()
{
return $this->GetOptional('format', 'html'); // Defaults to HTML
}
}

View File

@@ -0,0 +1,186 @@
<?php
/**
* Special kind of External Key to manage a hierarchy of objects
*/
class AttributeHierarchicalKey extends AttributeExternalKey
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY;
protected $m_sTargetClass;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public static function ListExpectedParams()
{
$aParams = parent::ListExpectedParams();
$idx = array_search('targetclass', $aParams);
unset($aParams[$idx]);
$idx = array_search('jointype', $aParams);
unset($aParams[$idx]);
return $aParams; // Later: mettre les bons parametres ici !!
}
public function GetEditClass()
{
return "ExtKey";
}
public function RequiresIndex()
{
return true;
}
/*
* The target class is the class for which the attribute has been defined first
*/
public function SetHostClass($sHostClass)
{
if (!isset($this->m_sTargetClass)) {
$this->m_sTargetClass = $sHostClass;
}
parent::SetHostClass($sHostClass);
}
public static function IsHierarchicalKey()
{
return true;
}
public function GetTargetClass($iType = EXTKEY_RELATIVE)
{
return $this->m_sTargetClass;
}
public function GetKeyAttDef($iType = EXTKEY_RELATIVE)
{
return $this;
}
public function GetKeyAttCode()
{
return $this->GetCode();
}
public function GetBasicFilterOperators()
{
return parent::GetBasicFilterOperators();
}
public function GetBasicFilterLooseOperator()
{
return parent::GetBasicFilterLooseOperator();
}
public function GetSQLColumns($bFullSpec = false)
{
$aColumns = array();
$aColumns[$this->GetCode()] = 'INT(11)' . ($bFullSpec ? ' DEFAULT 0' : '');
$aColumns[$this->GetSQLLeft()] = 'INT(11)' . ($bFullSpec ? ' DEFAULT 0' : '');
$aColumns[$this->GetSQLRight()] = 'INT(11)' . ($bFullSpec ? ' DEFAULT 0' : '');
return $aColumns;
}
public function GetSQLRight()
{
return $this->GetCode() . '_right';
}
public function GetSQLLeft()
{
return $this->GetCode() . '_left';
}
public function GetSQLValues($value)
{
if (!is_array($value)) {
$aValues[$this->GetCode()] = $value;
} else {
$aValues = array();
$aValues[$this->GetCode()] = $value[$this->GetCode()];
$aValues[$this->GetSQLRight()] = $value[$this->GetSQLRight()];
$aValues[$this->GetSQLLeft()] = $value[$this->GetSQLLeft()];
}
return $aValues;
}
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
$oFilter = $this->GetHierachicalFilter($aArgs, $sContains);
if ($oFilter) {
$oValSetDef = $this->GetValuesDef();
$oValSetDef->SetCondition($oFilter);
return $oValSetDef->GetValues($aArgs, $sContains);
} else {
return parent::GetAllowedValues($aArgs, $sContains);
}
}
public function GetAllowedValuesAsObjectSet($aArgs = array(), $sContains = '', $iAdditionalValue = null)
{
$oValSetDef = $this->GetValuesDef();
$oFilter = $this->GetHierachicalFilter($aArgs, $sContains, $iAdditionalValue);
if ($oFilter) {
$oValSetDef->SetCondition($oFilter);
}
$oSet = $oValSetDef->ToObjectSet($aArgs, $sContains, $iAdditionalValue);
return $oSet;
}
public function GetAllowedValuesAsFilter($aArgs = array(), $sContains = '', $iAdditionalValue = null)
{
$oFilter = $this->GetHierachicalFilter($aArgs, $sContains, $iAdditionalValue);
if ($oFilter) {
return $oFilter;
}
return parent::GetAllowedValuesAsFilter($aArgs, $sContains, $iAdditionalValue);
}
private function GetHierachicalFilter($aArgs = array(), $sContains = '', $iAdditionalValue = null)
{
if (array_key_exists('this', $aArgs)) {
// Hierarchical keys have one more constraint: the "parent value" cannot be
// "under" themselves
$iRootId = $aArgs['this']->GetKey();
if ($iRootId > 0) // ignore objects that do no exist in the database...
{
$sClass = $this->m_sTargetClass;
return DBObjectSearch::FromOQL("SELECT $sClass AS node JOIN $sClass AS root ON node." . $this->GetCode() . " NOT BELOW root.id WHERE root.id = $iRootId");
}
}
return false;
}
/**
* Find the corresponding "link" attribute on the target class, if any
*
* @return null | AttributeDefinition
*/
public function GetMirrorLinkAttribute()
{
return null;
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* Specialization of a string: IP address
*
* @package iTopORM
*/
class AttributeIPAddress extends AttributeString
{
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public function GetValidationPattern()
{
$sNum = '(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])';
return "^($sNum\\.$sNum\\.$sNum\\.$sNum)$";
}
public function GetOrderBySQLExpressions($sClassAlias)
{
// Note: This is the responsibility of this function to place backticks around column aliases
return array('INET_ATON(`' . $sClassAlias . $this->GetCode() . '`)');
}
}

View File

@@ -0,0 +1,194 @@
<?php
/**
* An image is a specific type of document, it is stored as several columns in the database
*
* @package iTopORM
*/
class AttributeImage extends AttributeBlob
{
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public function Get($sParamName)
{
$oParamValue = parent::Get($sParamName);
if ($sParamName === 'default_image') {
/** @noinspection NestedPositiveIfStatementsInspection */
if (!empty($oParamValue)) {
return utils::GetAbsoluteUrlModulesRoot() . $oParamValue;
}
}
return $oParamValue;
}
public function GetEditClass()
{
return "Image";
}
/**
* {@inheritDoc}
* @see AttributeBlob::MakeRealValue()
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
$oDoc = parent::MakeRealValue($proposedValue, $oHostObj);
if (($oDoc instanceof ormDocument)
&& (false === $oDoc->IsEmpty())
&& ($oDoc->GetMimeType() === 'image/svg+xml')) {
$sCleanSvg = HTMLSanitizer::Sanitize($oDoc->GetData(), 'svg_sanitizer');
$oDoc = new ormDocument($sCleanSvg, $oDoc->GetMimeType(), $oDoc->GetFileName());
}
// The validation of the MIME Type is done by CheckFormat below
return $oDoc;
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
return new ormDocument('', '', '');
}
/**
* Check that the supplied ormDocument actually contains an image
* {@inheritDoc}
*
* @see AttributeDefinition::CheckFormat()
*/
public function CheckFormat($value)
{
if ($value instanceof ormDocument && !$value->IsEmpty()) {
return ($value->GetMainMimeType() == 'image');
}
return true;
}
/**
* @param \ormDocument $value
* @param \DBObject $oHostObject
* @param bool $bLocalize
*
* @return string
*
* @see edit_image.js for JS generated markup in form edition
*/
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
{
$sRet = '';
$bIsCustomImage = false;
$iMaxWidth = $this->Get('display_max_width');
$sMaxWidthPx = $iMaxWidth . 'px';
$iMaxHeight = $this->Get('display_max_height');
$sMaxHeightPx = $iMaxHeight . 'px';
$sDefaultImageUrl = $this->Get('default_image');
if ($sDefaultImageUrl !== null) {
$sRet = $this->GetHtmlForImageUrl($sDefaultImageUrl, $sMaxWidthPx, $sMaxHeightPx);
}
$sCustomImageUrl = $this->GetAttributeImageFileUrl($value, $oHostObject);
if ($sCustomImageUrl !== null) {
$bIsCustomImage = true;
$sRet = $this->GetHtmlForImageUrl($sCustomImageUrl, $sMaxWidthPx, $sMaxHeightPx);
}
$sCssClasses = 'ibo-input-image--image-view attribute-image';
$sCssClasses .= ' ' . (($bIsCustomImage) ? 'attribute-image-custom' : 'attribute-image-default');
// Important: If you change this, mind updating edit_image.js as well
return '<div class="' . $sCssClasses . '" style="max-width: min(' . $sMaxWidthPx . ',100%); max-height: min(' . $sMaxHeightPx . ',100%); aspect-ratio: ' . $iMaxWidth . ' / ' . $iMaxHeight . '">' . $sRet . '</div>';
}
/**
* @param string $sUrl
* @param int $iMaxWidthPx
* @param int $iMaxHeightPx
*
* @return string
*
* @since 2.6.0 new private method
* @since 2.7.0 change visibility to protected
*/
protected function GetHtmlForImageUrl($sUrl, $iMaxWidthPx, $iMaxHeightPx)
{
return '<img src="' . $sUrl . '" style="max-width: min(' . $iMaxWidthPx . ',100%); max-height: min(' . $iMaxHeightPx . ',100%)">';
}
/**
* @param \ormDocument $value
* @param \DBObject $oHostObject
*
* @return null|string
*
* @since 2.6.0 new private method
* @since 2.7.0 change visibility to protected
*/
protected function GetAttributeImageFileUrl($value, $oHostObject)
{
if (!is_object($value)) {
return null;
}
if ($value->IsEmpty()) {
return null;
}
$bExistingImageModified = ($oHostObject->IsModified() && (array_key_exists($this->GetCode(), $oHostObject->ListChanges())));
if ($oHostObject->IsNew() || ($bExistingImageModified)) {
// If the object is modified (or not yet stored in the database) we must serve the content of the image directly inline
// otherwise (if we just give an URL) the browser will be given the wrong content... and may cache it
return 'data:' . $value->GetMimeType() . ';base64,' . base64_encode($value->GetData());
}
return $value->GetDisplayURL(get_class($oHostObject), $oHostObject->GetKey(), $this->GetCode());
}
public static function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\ImageField';
}
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
if ($oFormField === null) {
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
parent::MakeFormField($oObject, $oFormField);
// Generating urls
$value = $oObject->Get($this->GetCode());
if (is_object($value) && !$value->IsEmpty()) {
$oFormField->SetDownloadUrl($value->GetDownloadURL(get_class($oObject), $oObject->GetKey(), $this->GetCode()));
$oFormField->SetDisplayUrl($value->GetDisplayURL(get_class($oObject), $oObject->GetKey(), $this->GetCode()));
} else {
$oDefaultImage = $this->Get('default_image');
if (is_object($oDefaultImage) && !$oDefaultImage->IsEmpty()) {
$oFormField->SetDownloadUrl($oDefaultImage);
$oFormField->SetDisplayUrl($oDefaultImage);
}
}
return $oFormField;
}
}

View File

@@ -0,0 +1,138 @@
<?php
/**
* Map an integer column to an attribute
*
* @package iTopORM
*/
class AttributeInteger extends AttributeDBField
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_NUMERIC;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public static function ListExpectedParams()
{
return parent::ListExpectedParams();
//return array_merge(parent::ListExpectedParams(), array());
}
public function GetEditClass()
{
return "String";
}
protected function GetSQLCol($bFullSpec = false)
{
return "INT(11)" . ($bFullSpec ? $this->GetSQLColSpec() : '');
}
public function GetValidationPattern()
{
return "^[0-9]+$";
}
public function GetBasicFilterOperators()
{
return array(
"!=" => "differs from",
"=" => "equals",
">" => "greater (strict) than",
">=" => "greater than",
"<" => "less (strict) than",
"<=" => "less than",
"in" => "in"
);
}
public function GetBasicFilterLooseOperator()
{
// Unless we implement an "equals approximately..." or "same order of magnitude"
return "=";
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
$sQValue = CMDBSource::Quote($value);
switch ($sOpCode) {
case '!=':
return $this->GetSQLExpr() . " != $sQValue";
break;
case '>':
return $this->GetSQLExpr() . " > $sQValue";
break;
case '>=':
return $this->GetSQLExpr() . " >= $sQValue";
break;
case '<':
return $this->GetSQLExpr() . " < $sQValue";
break;
case '<=':
return $this->GetSQLExpr() . " <= $sQValue";
break;
case 'in':
if (!is_array($value)) {
throw new CoreException("Expected an array for argument value (sOpCode='$sOpCode')");
}
return $this->GetSQLExpr() . " IN ('" . implode("', '", $value) . "')";
break;
case '=':
default:
return $this->GetSQLExpr() . " = \"$value\"";
}
}
public function GetNullValue()
{
return null;
}
public function IsNull($proposedValue)
{
return is_null($proposedValue);
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
return utils::IsNotNullOrEmptyString($proposedValue);
}
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue)) {
return null;
}
if ($proposedValue === '') {
return null;
} // 0 is transformed into '' !
return (int)$proposedValue;
}
public function ScalarToSQL($value)
{
assert(is_numeric($value) || is_null($value));
return $value; // supposed to be an int
}
}

View File

@@ -0,0 +1,910 @@
<?php
/**
* Set of objects directly linked to an object, and being part of its definition
*
* @package iTopORM
*/
class AttributeLinkedSet extends AttributeDefinition
{
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
$this->aCSSClasses[] = 'attribute-set';
}
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(),
array("allowed_values", "depends_on", "linked_class", "ext_key_to_me", "count_min", "count_max"));
}
public function GetEditClass()
{
return "LinkedSet";
}
/** @inheritDoc */
public static function IsBulkModifyCompatible(): bool
{
return false;
}
public function IsWritable()
{
return true;
}
public static function IsLinkSet()
{
return true;
}
public function IsIndirect()
{
return false;
}
public function GetValuesDef()
{
$oValSetDef = $this->Get("allowed_values");
if (!$oValSetDef) {
// Let's propose every existing value
$oValSetDef = new ValueSetObjects('SELECT ' . \Combodo\iTop\Service\Links\LinkSetModel::GetTargetClass($this));
}
return $oValSetDef;
}
public function GetEditValue($value, $oHostObj = null)
{
/** @var ormLinkSet $value * */
if ($value->Count() === 0) {
return '';
}
/** Return linked objects key as string **/
$aValues = $value->GetValues();
return implode(' ', $aValues);
}
public function GetPrerequisiteAttributes($sClass = null)
{
return $this->Get("depends_on");
}
/**
* @param \DBObject|null $oHostObject
*
* @return \ormLinkSet
*
* @throws \Exception
* @throws \CoreException
* @throws \CoreWarning
*/
public function GetDefaultValue(DBObject $oHostObject = null)
{
if ($oHostObject === null) {
return null;
}
$sLinkClass = $this->GetLinkedClass();
$sExtKeyToMe = $this->GetExtKeyToMe();
// The class to target is not the current class, because if this is a derived class,
// it may differ from the target class, then things start to become confusing
/** @var \AttributeExternalKey $oRemoteExtKeyAtt */
$oRemoteExtKeyAtt = MetaModel::GetAttributeDef($sLinkClass, $sExtKeyToMe);
$sMyClass = $oRemoteExtKeyAtt->GetTargetClass();
$oMyselfSearch = new DBObjectSearch($sMyClass);
if ($oHostObject !== null) {
$oMyselfSearch->AddCondition('id', $oHostObject->GetKey(), '=');
}
$oLinkSearch = new DBObjectSearch($sLinkClass);
$oLinkSearch->AddCondition_PointingTo($oMyselfSearch, $sExtKeyToMe);
if ($this->IsIndirect()) {
// Join the remote class so that the archive flag will be taken into account
/** @var \AttributeLinkedSetIndirect $this */
$sExtKeyToRemote = $this->GetExtKeyToRemote();
/** @var \AttributeExternalKey $oExtKeyToRemote */
$oExtKeyToRemote = MetaModel::GetAttributeDef($sLinkClass, $sExtKeyToRemote);
$sRemoteClass = $oExtKeyToRemote->GetTargetClass();
if (MetaModel::IsArchivable($sRemoteClass)) {
$oRemoteSearch = new DBObjectSearch($sRemoteClass);
/** @var \AttributeLinkedSetIndirect $this */
$oLinkSearch->AddCondition_PointingTo($oRemoteSearch, $this->GetExtKeyToRemote());
}
}
$oLinks = new DBObjectSet($oLinkSearch);
$oLinkSet = new ormLinkSet($this->GetHostClass(), $this->GetCode(), $oLinks);
return $oLinkSet;
}
public function GetTrackingLevel()
{
return $this->GetOptional('tracking_level', MetaModel::GetConfig()->Get('tracking_level_linked_set_default'));
}
/**
* @return string see LINKSET_EDITMODE_* constants
*/
public function GetEditMode()
{
return $this->GetOptional('edit_mode', LINKSET_EDITMODE_ACTIONS);
}
/**
* @return int see LINKSET_EDITWHEN_* constants
* @since 3.1.1 3.2.0 N°6385
*/
public function GetEditWhen(): int
{
return $this->GetOptional('edit_when', LINKSET_EDITWHEN_ALWAYS);
}
/**
* @return string see LINKSET_DISPLAY_STYLE_* constants
* @since 3.1.0 N°3190
*/
public function GetDisplayStyle()
{
$sDisplayStyle = $this->GetOptional('display_style', LINKSET_DISPLAY_STYLE_TAB);
if ($sDisplayStyle === '') {
$sDisplayStyle = LINKSET_DISPLAY_STYLE_TAB;
}
return $sDisplayStyle;
}
/**
* Indicates if the current Attribute has constraints (php constraints or datamodel constraints)
* @return bool true if Attribute has constraints
* @since 3.1.0 N°6228
*/
public function HasPHPConstraint(): bool
{
return $this->GetOptional('with_php_constraint', false);
}
/**
* @return bool true if Attribute has computation (DB_LINKS_CHANGED event propagation, `with_php_computation` attribute xml property), false otherwise
* @since 3.1.1 3.2.0 N°6228
*/
public function HasPHPComputation(): bool
{
return $this->GetOptional('with_php_computation', false);
}
public function GetLinkedClass()
{
return $this->Get('linked_class');
}
public function GetExtKeyToMe()
{
return $this->Get('ext_key_to_me');
}
public function GetBasicFilterOperators()
{
return array();
}
public function GetBasicFilterLooseOperator()
{
return '';
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
return '';
}
/** @inheritDoc * */
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if ($this->GetDisplayStyle() === LINKSET_DISPLAY_STYLE_TAB) {
return $this->GetAsHTMLForTab($sValue, $oHostObject, $bLocalize);
} else {
return $this->GetAsHTMLForProperty($sValue, $oHostObject, $bLocalize);
}
}
public function GetAsHTMLForTab($sValue, $oHostObject = null, $bLocalize = true)
{
if (is_object($sValue) && ($sValue instanceof ormLinkSet)) {
$sValue->Rewind();
$aItems = array();
while ($oObj = $sValue->Fetch()) {
// Show only relevant information (hide the external key to the current object)
$aAttributes = array();
foreach (MetaModel::ListAttributeDefs($this->GetLinkedClass()) as $sAttCode => $oAttDef) {
if ($sAttCode == $this->GetExtKeyToMe()) {
continue;
}
if ($oAttDef->IsExternalField()) {
continue;
}
$sAttValue = $oObj->GetAsHTML($sAttCode);
if (strlen($sAttValue) > 0) {
$aAttributes[] = $sAttValue;
}
}
$sAttributes = implode(', ', $aAttributes);
$aItems[] = $sAttributes;
}
return implode('<br/>', $aItems);
}
return null;
}
public function GetAsHTMLForProperty($sValue, $oHostObject = null, $bLocalize = true): string
{
try {
/** @var ormLinkSet $sValue */
if (is_null($sValue) || $sValue->Count() === 0) {
return '';
}
$oLinkSetBlock = new \Combodo\iTop\Application\UI\Links\Set\BlockLinkSetDisplayAsProperty($this->GetCode(), $this, $sValue);
return \Combodo\iTop\Renderer\Console\ConsoleBlockRenderer::RenderBlockTemplates($oLinkSetBlock);
} catch (Exception $e) {
$sMessage = "Error while displaying attribute {$this->GetCode()}";
IssueLog::Error($sMessage, IssueLog::CHANNEL_DEFAULT, [
'host_object_class' => $this->GetHostClass(),
'host_object_key' => $oHostObject->GetKey(),
'attribute' => $this->GetCode(),
]);
return $sMessage;
}
}
/**
* @param string $sValue
* @param \DBObject $oHostObject
* @param bool $bLocalize
*
* @return string
*
* @throws \CoreException
*/
public function GetAsXML($sValue, $oHostObject = null, $bLocalize = true)
{
if (is_object($sValue) && ($sValue instanceof ormLinkSet)) {
$sValue->Rewind();
$sRes = "<Set>\n";
while ($oObj = $sValue->Fetch()) {
$sObjClass = get_class($oObj);
$sRes .= "<$sObjClass id=\"" . $oObj->GetKey() . "\">\n";
// Show only relevant information (hide the external key to the current object)
foreach (MetaModel::ListAttributeDefs($sObjClass) as $sAttCode => $oAttDef) {
if ($sAttCode == 'finalclass') {
if ($sObjClass == $this->GetLinkedClass()) {
// Simplify the output if the exact class could be determined implicitely
continue;
}
}
if ($sAttCode == $this->GetExtKeyToMe()) {
continue;
}
if ($oAttDef->IsExternalField()) {
/** @var \AttributeExternalField $oAttDef */
if ($oAttDef->GetKeyAttCode() == $this->GetExtKeyToMe()) {
continue;
}
/** @var AttributeExternalField $oAttDef */
if ($oAttDef->IsFriendlyName()) {
continue;
}
}
if ($oAttDef instanceof AttributeFriendlyName) {
continue;
}
if (!$oAttDef->IsScalar()) {
continue;
}
$sAttValue = $oObj->GetAsXML($sAttCode, $bLocalize);
$sRes .= "<$sAttCode>$sAttValue</$sAttCode>\n";
}
$sRes .= "</$sObjClass>\n";
}
$sRes .= "</Set>\n";
} else {
$sRes = '';
}
return $sRes;
}
/**
* @param $sValue
* @param string $sSeparator
* @param string $sTextQualifier
* @param \DBObject $oHostObject
* @param bool $bLocalize
* @param bool $bConvertToPlainText
*
* @return mixed|string
* @throws \CoreException
*/
public function GetAsCSV(
$sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true,
$bConvertToPlainText = false
)
{
$sSepItem = MetaModel::GetConfig()->Get('link_set_item_separator');
$sSepAttribute = MetaModel::GetConfig()->Get('link_set_attribute_separator');
$sSepValue = MetaModel::GetConfig()->Get('link_set_value_separator');
$sAttributeQualifier = MetaModel::GetConfig()->Get('link_set_attribute_qualifier');
if (is_object($sValue) && ($sValue instanceof ormLinkSet)) {
$sValue->Rewind();
$aItems = array();
while ($oObj = $sValue->Fetch()) {
$sObjClass = get_class($oObj);
// Show only relevant information (hide the external key to the current object)
$aAttributes = array();
foreach (MetaModel::ListAttributeDefs($sObjClass) as $sAttCode => $oAttDef) {
if ($sAttCode == 'finalclass') {
if ($sObjClass == $this->GetLinkedClass()) {
// Simplify the output if the exact class could be determined implicitely
continue;
}
}
if ($sAttCode == $this->GetExtKeyToMe()) {
continue;
}
if ($oAttDef->IsExternalField()) {
continue;
}
if (!$oAttDef->IsBasedOnDBColumns()) {
continue;
}
if (!$oAttDef->IsScalar()) {
continue;
}
$sAttValue = $oObj->GetAsCSV($sAttCode, $sSepValue, '', $bLocalize);
if (strlen($sAttValue) > 0) {
$sAttributeData = str_replace($sAttributeQualifier, $sAttributeQualifier . $sAttributeQualifier,
$sAttCode . $sSepValue . $sAttValue);
$aAttributes[] = $sAttributeQualifier . $sAttributeData . $sAttributeQualifier;
}
}
$sAttributes = implode($sSepAttribute, $aAttributes);
$aItems[] = $sAttributes;
}
$sRes = implode($sSepItem, $aItems);
} else {
$sRes = '';
}
$sRes = str_replace($sTextQualifier, $sTextQualifier . $sTextQualifier, $sRes);
$sRes = $sTextQualifier . $sRes . $sTextQualifier;
return $sRes;
}
/**
* List the available verbs for 'GetForTemplate'
*/
public function EnumTemplateVerbs()
{
return array(
'' => 'Plain text (unlocalized) representation',
'html' => 'HTML representation (unordered list)',
);
}
/**
* Get various representations of the value, for insertion into a template (e.g. in Notifications)
*
* @param mixed $value The current value of the field
* @param string $sVerb The verb specifying the representation of the value
* @param DBObject $oHostObject The object
* @param bool $bLocalize Whether or not to localize the value
*
* @return string
* @throws \Exception
*/
public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
{
$sRemoteName = $this->IsIndirect() ?
/** @var \AttributeLinkedSetIndirect $this */
$this->GetExtKeyToRemote() . '_friendlyname' : 'friendlyname';
$oLinkSet = clone $value; // Workaround/Safety net for Trac #887
$iLimit = MetaModel::GetConfig()->Get('max_linkset_output');
$iCount = 0;
$aNames = array();
foreach ($oLinkSet as $oItem) {
if (($iLimit > 0) && ($iCount == $iLimit)) {
$iTotal = $oLinkSet->Count();
$aNames[] = '... ' . Dict::Format('UI:TruncatedResults', $iCount, $iTotal);
break;
}
$aNames[] = $oItem->Get($sRemoteName);
$iCount++;
}
switch ($sVerb) {
case '':
return implode("\n", $aNames);
case 'html':
return '<ul><li>' . implode("</li><li>", $aNames) . '</li></ul>';
default:
throw new Exception("Unknown verb '$sVerb' for attribute " . $this->GetCode() . ' in class ' . get_class($oHostObject));
}
}
public function DuplicatesAllowed()
{
return false;
} // No duplicates for 1:n links, never
public function GetImportColumns()
{
$aColumns = array();
$aColumns[$this->GetCode()] = 'MEDIUMTEXT' . CMDBSource::GetSqlStringColumnDefinition();
return $aColumns;
}
/**
* @param string $sProposedValue
* @param bool $bLocalizedValue
* @param string $sSepItem
* @param string $sSepAttribute
* @param string $sSepValue
* @param string $sAttributeQualifier
*
* @return \DBObjectSet|mixed
* @throws \CSVParserException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \Exception
*/
public function MakeValueFromString(
$sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null,
$sAttributeQualifier = null
)
{
if (is_null($sSepItem) || empty($sSepItem)) {
$sSepItem = MetaModel::GetConfig()->Get('link_set_item_separator');
}
if (is_null($sSepAttribute) || empty($sSepAttribute)) {
$sSepAttribute = MetaModel::GetConfig()->Get('link_set_attribute_separator');
}
if (is_null($sSepValue) || empty($sSepValue)) {
$sSepValue = MetaModel::GetConfig()->Get('link_set_value_separator');
}
if (is_null($sAttributeQualifier) || empty($sAttributeQualifier)) {
$sAttributeQualifier = MetaModel::GetConfig()->Get('link_set_attribute_qualifier');
}
$sTargetClass = $this->Get('linked_class');
$sInput = str_replace($sSepItem, "\n", $sProposedValue);
$oCSVParser = new CSVParser($sInput, $sSepAttribute, $sAttributeQualifier);
$aInput = $oCSVParser->ToArray(0 /* do not skip lines */);
$aLinks = array();
foreach ($aInput as $aRow) {
// 1st - get the values, split the extkey->searchkey specs, and eventually get the finalclass value
$aExtKeys = array();
$aValues = array();
foreach ($aRow as $sCell) {
$iSepPos = strpos($sCell, $sSepValue);
if ($iSepPos === false) {
// Houston...
throw new CoreException('Wrong format for link attribute specification', array('value' => $sCell));
}
$sAttCode = trim(substr($sCell, 0, $iSepPos));
$sValue = substr($sCell, $iSepPos + strlen($sSepValue));
if (preg_match('/^(.+)->(.+)$/', $sAttCode, $aMatches)) {
$sKeyAttCode = $aMatches[1];
$sRemoteAttCode = $aMatches[2];
$aExtKeys[$sKeyAttCode][$sRemoteAttCode] = $sValue;
if (!MetaModel::IsValidAttCode($sTargetClass, $sKeyAttCode)) {
throw new CoreException('Wrong attribute code for link attribute specification',
array('class' => $sTargetClass, 'attcode' => $sKeyAttCode));
}
/** @var \AttributeExternalKey $oKeyAttDef */
$oKeyAttDef = MetaModel::GetAttributeDef($sTargetClass, $sKeyAttCode);
$sRemoteClass = $oKeyAttDef->GetTargetClass();
if (!MetaModel::IsValidAttCode($sRemoteClass, $sRemoteAttCode)) {
throw new CoreException('Wrong attribute code for link attribute specification',
array('class' => $sRemoteClass, 'attcode' => $sRemoteAttCode));
}
} else {
if (!MetaModel::IsValidAttCode($sTargetClass, $sAttCode)) {
throw new CoreException('Wrong attribute code for link attribute specification',
array('class' => $sTargetClass, 'attcode' => $sAttCode));
}
$oAttDef = MetaModel::GetAttributeDef($sTargetClass, $sAttCode);
$aValues[$sAttCode] = $oAttDef->MakeValueFromString($sValue, $bLocalizedValue, $sSepItem,
$sSepAttribute, $sSepValue, $sAttributeQualifier);
}
}
// 2nd - Instanciate the object and set the value
if (isset($aValues['finalclass'])) {
$sLinkClass = $aValues['finalclass'];
if (!is_subclass_of($sLinkClass, $sTargetClass)) {
throw new CoreException('Wrong class for link attribute specification',
array('requested_class' => $sLinkClass, 'expected_class' => $sTargetClass));
}
} elseif (MetaModel::IsAbstract($sTargetClass)) {
throw new CoreException('Missing finalclass for link attribute specification');
} else {
$sLinkClass = $sTargetClass;
}
$oLink = MetaModel::NewObject($sLinkClass);
foreach ($aValues as $sAttCode => $sValue) {
$oLink->Set($sAttCode, $sValue);
}
// 3rd - Set external keys from search conditions
foreach ($aExtKeys as $sKeyAttCode => $aReconciliation) {
$oKeyAttDef = MetaModel::GetAttributeDef($sTargetClass, $sKeyAttCode);
$sKeyClass = $oKeyAttDef->GetTargetClass();
$oExtKeyFilter = new DBObjectSearch($sKeyClass);
$aReconciliationDesc = array();
foreach ($aReconciliation as $sRemoteAttCode => $sValue) {
$oExtKeyFilter->AddCondition($sRemoteAttCode, $sValue, '=');
$aReconciliationDesc[] = "$sRemoteAttCode=$sValue";
}
$oExtKeySet = new DBObjectSet($oExtKeyFilter);
switch ($oExtKeySet->Count()) {
case 0:
$sReconciliationDesc = implode(', ', $aReconciliationDesc);
throw new CoreException("Found no match",
array('ext_key' => $sKeyAttCode, 'reconciliation' => $sReconciliationDesc));
break;
case 1:
$oRemoteObj = $oExtKeySet->Fetch();
$oLink->Set($sKeyAttCode, $oRemoteObj->GetKey());
break;
default:
$sReconciliationDesc = implode(', ', $aReconciliationDesc);
throw new CoreException("Found several matches",
array('ext_key' => $sKeyAttCode, 'reconciliation' => $sReconciliationDesc));
// Found several matches, ambiguous
}
}
// Check (roughly) if such a link is valid
$aErrors = array();
foreach (MetaModel::ListAttributeDefs($sTargetClass) as $sAttCode => $oAttDef) {
if ($oAttDef->IsExternalKey()) {
/** @var \AttributeExternalKey $oAttDef */
if (($oAttDef->GetTargetClass() == $this->GetHostClass()) || (is_subclass_of($this->GetHostClass(),
$oAttDef->GetTargetClass()))) {
continue; // Don't check the key to self
}
}
if ($oAttDef->IsWritable() && $oAttDef->IsNull($oLink->Get($sAttCode)) && !$oAttDef->IsNullAllowed()) {
$aErrors[] = $sAttCode;
}
}
if (count($aErrors) > 0) {
throw new CoreException("Missing value for mandatory attribute(s): " . implode(', ', $aErrors));
}
$aLinks[] = $oLink;
}
$oSet = DBObjectSet::FromArray($sTargetClass, $aLinks);
return $oSet;
}
/**
* @inheritDoc
*
* @param \ormLinkSet $value
*/
public function GetForJSON($value)
{
$aRet = array();
if (is_object($value) && ($value instanceof ormLinkSet)) {
$value->Rewind();
while ($oObj = $value->Fetch()) {
$sObjClass = get_class($oObj);
// Show only relevant information (hide the external key to the current object)
$aAttributes = array();
foreach (MetaModel::ListAttributeDefs($sObjClass) as $sAttCode => $oAttDef) {
if ($sAttCode == 'finalclass') {
if ($sObjClass == $this->GetLinkedClass()) {
// Simplify the output if the exact class could be determined implicitely
continue;
}
}
if ($sAttCode == $this->GetExtKeyToMe()) {
continue;
}
if ($oAttDef->IsExternalField()) {
continue;
}
if (!$oAttDef->IsBasedOnDBColumns()) {
continue;
}
if (!$oAttDef->IsScalar()) {
continue;
}
$attValue = $oObj->Get($sAttCode);
$aAttributes[$sAttCode] = $oAttDef->GetForJSON($attValue);
}
$aRet[] = $aAttributes;
}
}
return $aRet;
}
/**
* @inheritDoc
*
* @return \DBObjectSet
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \Exception
*/
public function FromJSONToValue($json)
{
$sTargetClass = $this->Get('linked_class');
$aLinks = array();
foreach ($json as $aValues) {
if (isset($aValues['finalclass'])) {
$sLinkClass = $aValues['finalclass'];
if (!is_subclass_of($sLinkClass, $sTargetClass)) {
throw new CoreException('Wrong class for link attribute specification',
array('requested_class' => $sLinkClass, 'expected_class' => $sTargetClass));
}
} elseif (MetaModel::IsAbstract($sTargetClass)) {
throw new CoreException('Missing finalclass for link attribute specification');
} else {
$sLinkClass = $sTargetClass;
}
$oLink = MetaModel::NewObject($sLinkClass);
foreach ($aValues as $sAttCode => $sValue) {
$oLink->Set($sAttCode, $sValue);
}
// Check (roughly) if such a link is valid
$aErrors = array();
foreach (MetaModel::ListAttributeDefs($sTargetClass) as $sAttCode => $oAttDef) {
if ($oAttDef->IsExternalKey()) {
/** @var AttributeExternalKey $oAttDef */
if (($oAttDef->GetTargetClass() == $this->GetHostClass()) || (is_subclass_of($this->GetHostClass(),
$oAttDef->GetTargetClass()))) {
continue; // Don't check the key to self
}
}
if ($oAttDef->IsWritable() && $oAttDef->IsNull($oLink->Get($sAttCode)) && !$oAttDef->IsNullAllowed()) {
$aErrors[] = $sAttCode;
}
}
if (count($aErrors) > 0) {
throw new CoreException("Missing value for mandatory attribute(s): " . implode(', ', $aErrors));
}
$aLinks[] = $oLink;
}
$oSet = DBObjectSet::FromArray($sTargetClass, $aLinks);
return $oSet;
}
/**
* @param $proposedValue
* @param $oHostObj
*
* @return mixed
* @throws \Exception
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
if ($proposedValue === null) {
$sLinkedClass = $this->GetLinkedClass();
$aLinkedObjectsArray = array();
$oSet = DBObjectSet::FromArray($sLinkedClass, $aLinkedObjectsArray);
return new ormLinkSet(
get_class($oHostObj),
$this->GetCode(),
$oSet
);
}
return $proposedValue;
}
/**
* @param ormLinkSet $val1
* @param ormLinkSet $val2
*
* @return bool
*/
public function Equals($val1, $val2)
{
if ($val1 === $val2) {
$bAreEquivalent = true;
} else {
$bAreEquivalent = ($val2->HasDelta() === false);
}
return $bAreEquivalent;
}
/**
* Find the corresponding "link" attribute on the target class, if any
*
* @return null | AttributeDefinition
* @throws \Exception
*/
public function GetMirrorLinkAttribute()
{
$oRemoteAtt = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToMe());
return $oRemoteAtt;
}
public static function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\LinkedSetField';
}
/**
* @param \DBObject $oObject
* @param \Combodo\iTop\Form\Field\LinkedSetField $oFormField
*
* @return \Combodo\iTop\Form\Field\LinkedSetField
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \Exception
*/
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
if ($oFormField === null) {
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
// Setting target class
if (!$this->IsIndirect()) {
$sTargetClass = $this->GetLinkedClass();
} else {
/** @var \AttributeExternalKey $oRemoteAttDef */
/** @var \AttributeLinkedSetIndirect $this */
$oRemoteAttDef = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToRemote());
$sTargetClass = $oRemoteAttDef->GetTargetClass();
/** @var \AttributeLinkedSetIndirect $this */
$oFormField->SetExtKeyToRemote($this->GetExtKeyToRemote());
}
$oFormField->SetTargetClass($sTargetClass);
$oFormField->SetLinkedClass($this->GetLinkedClass());
$oFormField->SetIndirect($this->IsIndirect());
// Setting attcodes to display
$aAttCodesToDisplay = MetaModel::FlattenZList(MetaModel::GetZListItems($sTargetClass, 'list'));
// - Adding friendlyname attribute to the list is not already in it
$sTitleAttCode = MetaModel::GetFriendlyNameAttributeCode($sTargetClass);
if (($sTitleAttCode !== null) && !in_array($sTitleAttCode, $aAttCodesToDisplay)) {
$aAttCodesToDisplay = array_merge(array($sTitleAttCode), $aAttCodesToDisplay);
}
// - Adding attribute properties
$aAttributesToDisplay = array();
foreach ($aAttCodesToDisplay as $sAttCodeToDisplay) {
$oAttDefToDisplay = MetaModel::GetAttributeDef($sTargetClass, $sAttCodeToDisplay);
$aAttributesToDisplay[$sAttCodeToDisplay] = [
'att_code' => $sAttCodeToDisplay,
'label' => $oAttDefToDisplay->GetLabel(),
];
}
$oFormField->SetAttributesToDisplay($aAttributesToDisplay);
// Append lnk attributes (filtered from zlist)
if ($this->IsIndirect()) {
$aLnkAttDefToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectLinkClass($this->m_sHostClass, $this->m_sCode);
$aLnkAttributesToDisplay = array();
foreach ($aLnkAttDefToDisplay as $oLnkAttDefToDisplay) {
$aLnkAttributesToDisplay[$oLnkAttDefToDisplay->GetCode()] = [
'att_code' => $oLnkAttDefToDisplay->GetCode(),
'label' => $oLnkAttDefToDisplay->GetLabel(),
'mandatory' => !$oLnkAttDefToDisplay->IsNullAllowed(),
];
}
$oFormField->SetLnkAttributesToDisplay($aLnkAttributesToDisplay);
}
parent::MakeFormField($oObject, $oFormField);
return $oFormField;
}
public function IsPartOfFingerprint()
{
return false;
}
/**
* @inheritDoc
* @param \ormLinkSet $proposedValue
*/
public function HasAValue($proposedValue): bool
{
// Protection against wrong value type
if (false === ($proposedValue instanceof ormLinkSet)) {
return parent::HasAValue($proposedValue);
}
// We test if there is at least 1 item in the linkset (new or existing), not if an item is being added to it.
return $proposedValue->Count() > 0;
}
/**
* SearchSpecificLabel.
*
* @param string $sDictEntrySuffix
* @param string $sDefault
* @param bool $bUserLanguageOnly
* @param ...$aArgs
* @return string
* @since 3.1.0
*/
public function SearchSpecificLabel(string $sDictEntrySuffix, string $sDefault, bool $bUserLanguageOnly, ...$aArgs): string
{
try {
$sNextClass = $this->m_sHostClass;
do {
$sKey = "Class:{$sNextClass}/Attribute:{$this->m_sCode}/{$sDictEntrySuffix}";
if (Dict::S($sKey, null, $bUserLanguageOnly) !== $sKey) {
return Dict::Format($sKey, ...$aArgs);
}
$sNextClass = MetaModel::GetParentClass($sNextClass);
} while ($sNextClass !== null);
if (Dict::S($sDictEntrySuffix, null, $bUserLanguageOnly) !== $sKey) {
return Dict::Format($sDictEntrySuffix, ...$aArgs);
} else {
return $sDefault;
}
} catch (Exception $e) {
ExceptionLog::LogException($e);
return $sDefault;
}
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* Set of objects linked to an object (n-n), and being part of its definition
*
* @package iTopORM
*/
class AttributeLinkedSetIndirect extends AttributeLinkedSet
{
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("ext_key_to_remote"));
}
public function IsIndirect()
{
return true;
}
public function GetExtKeyToRemote()
{
return $this->Get('ext_key_to_remote');
}
public function GetEditClass()
{
return "LinkedSet";
}
public function DuplicatesAllowed()
{
return $this->GetOptional("duplicates", false);
} // The same object may be linked several times... or not...
public function GetTrackingLevel()
{
return $this->GetOptional('tracking_level',
MetaModel::GetConfig()->Get('tracking_level_linked_set_indirect_default'));
}
/**
* Find the corresponding "link" attribute on the target class, if any
*
* @return null | AttributeDefinition
* @throws \CoreException
*/
public function GetMirrorLinkAttribute()
{
$oRet = null;
/** @var \AttributeExternalKey $oExtKeyToRemote */
$oExtKeyToRemote = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToRemote());
$sRemoteClass = $oExtKeyToRemote->GetTargetClass();
foreach (MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef) {
if (!$oRemoteAttDef instanceof AttributeLinkedSetIndirect) {
continue;
}
if ($oRemoteAttDef->GetLinkedClass() != $this->GetLinkedClass()) {
continue;
}
if ($oRemoteAttDef->GetExtKeyToMe() != $this->GetExtKeyToRemote()) {
continue;
}
if ($oRemoteAttDef->GetExtKeyToRemote() != $this->GetExtKeyToMe()) {
continue;
}
$oRet = $oRemoteAttDef;
break;
}
return $oRet;
}
/** @inheritDoc */
public static function IsBulkModifyCompatible(): bool
{
return true;
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Map a log to an attribute
*
* @package iTopORM
*/
class AttributeLongText extends AttributeText
{
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
protected function GetSQLCol($bFullSpec = false)
{
return "LONGTEXT" . CMDBSource::GetSqlStringColumnDefinition();
}
public function GetMaxSize()
{
// Is there a way to know the current limitation for mysql?
// See mysql_field_len()
return 65535 * 1024; // Limited... still 64 MB!
}
protected function GetChangeRecordClassName(): string
{
return ($this->GetFormat() === 'html')
? CMDBChangeOpSetAttributeHTML::class
: CMDBChangeOpSetAttributeLongText::class;
}
}

View File

@@ -0,0 +1,137 @@
<?php
/**
* A meta enum is an aggregation of enum from subclasses into an enum of a base class
* It has been designed is to cope with the fact that statuses must be defined in leaf classes, while it makes sense to
* have a superstatus available on the root classe(s)
*
* @package iTopORM
*/
class AttributeMetaEnum extends AttributeEnum
{
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public static function ListExpectedParams()
{
return array('allowed_values', 'sql', 'default_value', 'mapping');
}
public function IsNullAllowed()
{
return false; // Well... this actually depends on the mapping
}
public function IsWritable()
{
return false;
}
public function RequiresIndex()
{
return true;
}
public function GetPrerequisiteAttributes($sClass = null)
{
if (is_null($sClass)) {
$sClass = $this->GetHostClass();
}
$aMappingData = $this->GetMapRule($sClass);
if ($aMappingData == null) {
$aRet = array();
} else {
$aRet = array($aMappingData['attcode']);
}
return $aRet;
}
/**
* Overload the standard so as to leave the data unsorted
*
* @param array $aArgs
* @param string $sContains
*
* @return array|null
*/
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
$oValSetDef = $this->GetValuesDef();
if (!$oValSetDef) {
return null;
}
$aRawValues = $oValSetDef->GetValueList();
if (is_null($aRawValues)) {
return null;
}
$aLocalizedValues = array();
foreach ($aRawValues as $sKey => $sValue) {
$aLocalizedValues[$sKey] = $this->GetValueLabel($sKey);
}
return $aLocalizedValues;
}
/**
* Returns the meta value for the given object.
* See also MetaModel::RebuildMetaEnums() that must be maintained when MapValue changes
*
* @param $oObject
*
* @return mixed
* @throws Exception
*/
public function MapValue($oObject)
{
$aMappingData = $this->GetMapRule(get_class($oObject));
if ($aMappingData == null) {
$sRet = $this->GetDefaultValue();
} else {
$sAttCode = $aMappingData['attcode'];
$value = $oObject->Get($sAttCode);
if (array_key_exists($value, $aMappingData['values'])) {
$sRet = $aMappingData['values'][$value];
} elseif ($this->GetDefaultValue() != '') {
$sRet = $this->GetDefaultValue();
} else {
throw new Exception('AttributeMetaEnum::MapValue(): mapping not found for value "' . $value . '" in ' . get_class($oObject) . ', on attribute ' . MetaModel::GetAttributeOrigin($this->GetHostClass(),
$this->GetCode()) . '::' . $this->GetCode());
}
}
return $sRet;
}
public function GetMapRule($sClass)
{
$aMappings = $this->Get('mapping');
if (array_key_exists($sClass, $aMappings)) {
$aMappingData = $aMappings[$sClass];
} else {
$sParent = MetaModel::GetParentClass($sClass);
if (is_null($sParent)) {
$aMappingData = null;
} else {
$aMappingData = $this->GetMapRule($sParent);
}
}
return $aMappingData;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Specialization of a string: OQL expression
*
* @package iTopORM
*/
class AttributeOQL extends AttributeText
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public function GetEditClass()
{
return "OQLExpression";
}
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* An external key for which the class is defined as the value of another attribute
*
* @package iTopORM
*/
class AttributeObjectKey extends AttributeDBFieldVoid
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_EXTERNAL_KEY;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array('class_attcode', 'is_null_allowed'));
}
public function GetEditClass()
{
return "String";
}
protected function GetSQLCol($bFullSpec = false)
{
return "INT(11)" . ($bFullSpec ? " DEFAULT 0" : "");
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
return 0;
}
public function IsNullAllowed()
{
return $this->Get("is_null_allowed");
}
public function GetBasicFilterOperators()
{
return parent::GetBasicFilterOperators();
}
public function GetBasicFilterLooseOperator()
{
return parent::GetBasicFilterLooseOperator();
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
return parent::GetBasicFilterSQLExpr($sOpCode, $value);
}
public function GetNullValue()
{
return 0;
}
public function IsNull($proposedValue)
{
return ($proposedValue == 0);
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
return ((int)$proposedValue) !== 0;
}
/**
* @inheritDoc
*
* @param int|DBObject $proposedValue Object key or valid ({@see MetaModel::IsValidObject()}) datamodel object
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue)) {
return 0;
}
if ($proposedValue === '') {
return 0;
}
if (MetaModel::IsValidObject($proposedValue)) {
return $proposedValue->GetKey();
}
return (int)$proposedValue;
}
}

View File

@@ -0,0 +1,35 @@
<?php
class AttributeObsolescenceDate extends AttributeDate
{
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
* @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited
* @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9
*
* @param string $sCode
* @param array $aParams
*
* @throws \Exception
* @noinspection SenselessProxyMethodInspection
*/
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
}
public function GetLabel($sDefault = null)
{
$sDefault = Dict::S('Core:AttributeObsolescenceDate/Label', $sDefault);
return parent::GetLabel($sDefault);
}
public function GetDescription($sDefault = null)
{
$sDefault = Dict::S('Core:AttributeObsolescenceDate/Label+', $sDefault);
return parent::GetDescription($sDefault);
}
}

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