Compare commits

...

282 Commits

Author SHA1 Message Date
Pierre Goiffon
8295eaed90 Merge remote-tracking branch 'origin/support/2.5' into support/2.6 2023-07-26 12:06:32 +02:00
Pierre Goiffon
219b970703 N°4478 Fix linkedset widget in portal when adding new items with already selected ones
Was already committed to develop with e59d472c
2021-12-10 15:56:33 +01:00
Pierre Goiffon
76c139253e 🎨 Fix language injection 2021-12-10 15:24:16 +01:00
Pierre Goiffon
10cfb373f2 N°4481 Fix badly escaped dialog tooltip
Was commited to develop first (99a0e0c5 and 4f27f3ac)
2021-12-10 13:38:24 +01:00
Pierre Goiffon
97d6d413bb N°4502 Fix dashboard page not refreshed after saving customm dashboard 2021-12-10 12:30:33 +01:00
Pierre Goiffon
3f8f57fa9a N°4502 Fix cannot create new or edit existing custom dashboard
Regression brought by dbaf9241
2021-12-10 09:15:43 +01:00
Pierre Goiffon
f916f9cde8 N°4289 Allow to use privUITransactionFile when no user logged
Before we were throwing a SecurityException, which was blocking for combodo-unauthenticated-form for example
2021-12-08 17:16:12 +01:00
Molkobain
8a65a592f3 N°4360 - Rename class to match other classes convention 2021-11-26 13:47:05 +01:00
Pierre Goiffon
5e48400cb1 N°4478 Fix line selection (global and unique) not checking checkbox anymore 2021-11-26 11:44:32 +01:00
Pierre Goiffon
252562ace9 N°4478 Fix "Requested unknown parameter '' for row 0, column 0" when opening search on related object
Forgotten file :/
2021-11-26 11:08:25 +01:00
Pierre Goiffon
770ac8ffe5 N°4478 Fix "Requested unknown parameter '' for row 0, column 0" when opening search on related object 2021-11-26 10:58:17 +01:00
Pierre Goiffon
ed3c387712 N°4478 Update Datatables lib 2021-11-25 10:55:48 +01:00
Pierre Goiffon
81a2a9278c N°4360 Fix SvgDOMSanitizer expected data 2021-11-23 17:38:30 +01:00
Pierre Goiffon
e15d4bfab6 N°4360 Security hardening 2021-11-23 17:25:50 +01:00
Pierre Goiffon
3e8dd2f4a5 N°4286 Setup : fix loop in first steps
Setup token wasn't removed at the right place :/
2021-11-18 08:54:10 +01:00
Pierre Goiffon
51a49dfce8 Remove warnings, use finally block, formatting 2021-11-17 16:10:50 +01:00
Pierre Goiffon
066b71686d N°4286 Setup : restore backup download on WizStepDone
Setup token was put with N°2016 (6b5cc7c)
But later on we refactored the token handling in SetupUtils methods, and we had token removal in WizStepDone (43daa2ef) : so the backup download cannot be done :/
2021-11-17 14:39:44 +01:00
Pierre Goiffon
be633001a5 Revert "N°4360 Security hardening"
This reverts commit 8adf743cc7.

We will implement a different solution later (hopefully for 2.6.5 / 2.7.6 / 3.0.0 as well)
2021-11-17 11:13:29 +01:00
Pierre Goiffon
84426c6634 N°4365 Security hardening 2021-11-17 10:15:12 +01:00
Pierre Goiffon
dbaf924171 N°4363 Security hardening 2021-11-16 17:19:19 +01:00
Pierre Goiffon
8adf743cc7 N°4360 Security hardening 2021-11-16 12:01:16 +01:00
Pierre Goiffon
75450ded1d N°4359 Security hardening 2021-11-15 16:38:11 +01:00
Pierre Goiffon
2beb795f9a N°4304 Security hardening 2021-11-09 11:32:53 +01:00
Pierre Goiffon
e8d314e1f6 N°4367 Fix \privUITransactionFileTest::testIsTransactionValid
* change user name for when password policy is active
* admin user doesn't exist on Jenkins : create a second user
* test UserRights::Login return value
* document that we depend on the sample data
2021-11-03 10:50:25 +01:00
Pierre Goiffon
e29f1825be N°4367 Fix "redeclaration of const CombodoSanitizer"
The utils.js can be included more than once in old iTop branches :( This is fixed in 3.0.0 (develop branch)

Also add missing ";"
2021-11-02 17:14:16 +01:00
Pierre Goiffon
9b854dbcc7 N°4289 skip test (not working on Jenkins) 2021-10-21 14:52:59 +02:00
Pierre Goiffon
7757f1f2d2 N°4289 Security hardening 2021-10-21 12:43:03 +02:00
Pierre Goiffon
a353317746 N°4289 Fix privUITransactionFile generating error if MetaModel not loaded 2021-10-20 17:26:32 +02:00
Pierre Goiffon
723eb90160 N°4289 privUITransactionSession phpDoc 2021-10-20 17:25:58 +02:00
Pierre Goiffon
b3f827ed5e N°4367 Security hardening 2021-10-18 14:27:58 +02:00
Pierre Goiffon
eaf8a187aa N°3332 report function rename
The method was renamed in 18d52319 but only on support/2.7 and above
2021-10-18 11:36:17 +02:00
Pierre Goiffon
34f64c61f6 privUITransaction fix inspections errors + formatting 2021-10-18 11:32:38 +02:00
Molkobain
92a9a8c65f N°4129 - Security hardening 2021-08-18 15:57:18 +02:00
Pierre Goiffon
834ac00d37 📝 README : update latest releases
Was made in #143 but on develop only, but we are still maintaining older branches !
2021-07-21 12:15:22 +02:00
Eric
5691ca0327 Fix CI 2021-05-28 08:48:47 +02:00
Eric
86f649affc N°4002 - code hardening 2021-05-27 16:13:27 +02:00
Eric
4f5c987d8b N°4002 - code hardening 2021-05-27 15:57:04 +02:00
Eric
e441e5e78a documentation 2021-05-27 11:49:32 +02:00
Eric
43daa2ef08 N°3952 - code hardening 2021-05-27 09:29:50 +02:00
acognet
db6e813cba N°3945 - Password database is visible in the setup process 2021-05-18 17:34:57 +02:00
Pierre Goiffon
066a6d8b36 🔧 Use same .editorconfig in all supported branches 2021-01-25 09:12:38 +01:00
Pierre Goiffon
b9ca2ac13d N°3416 Fix DocumentFile preview not working anymore
Was caused by X-Frame-Options http header added with N°3317

(cherry picked from commit 35d77ff642)

# Conflicts:
#	pages/ajax.render.php
2020-12-03 08:20:51 +01:00
Pierre Goiffon
65e43e8d04 🔧 remove .gitflow as we don't have any master branch anymore 2020-10-22 10:10:15 +02:00
Pierre Goiffon
5fee2438ab Fix comments : iTop 2.8.0 renamed to 3.0.0 2020-10-14 09:06:07 +02:00
Pierre Goiffon
8b1c20cc11 N°3332 Security hardening 2020-10-12 12:40:51 +02:00
odain
0001e8ffc4 💚 use new ci validation 2020-10-09 10:13:51 +02:00
odain
df5aacca42 💚 use new ci validation 2020-10-09 10:08:31 +02:00
Eric
1f53757318 N°3248 - code hardening
(cherry picked from commit 6a25933744)
(cherry picked from commit f74c78d61c)
2020-10-05 14:54:17 +02:00
Pierre Goiffon
090119147c 🎨 PHP formatting 2020-10-05 14:42:03 +02:00
Pierre Goiffon
1551694198 N°3317 Security hardening 2020-10-05 14:42:03 +02:00
Eric
bef1832ac7 N°3317 - Add http headers 2020-09-29 14:07:24 +02:00
Pierre Goiffon
45e366745d N°3333 Security hardening 2020-09-24 17:34:57 +02:00
Pierre Goiffon
1e634a8bba N°3332 Security hardening 2020-09-23 17:17:05 +02:00
Pierre Goiffon
228a945da9 N°2984 Security hardening 2020-05-14 11:26:35 +02:00
Pierre Goiffon
834297e675 N°2985 Security hardening (#140)
Thanks @bruno-ds  for the review !
2020-05-13 10:04:40 +02:00
Pierre Goiffon
3c9318d56a N°2990 Fix count warning on audit OQL error 2020-05-12 09:41:24 +02:00
Pierre Goiffon
30d10b6f11 N°2990 Security hardening 2020-05-12 09:40:58 +02:00
Pierre Goiffon
f8e39877b3 N°2988 Security hardening 2020-05-07 11:49:58 +02:00
Pierre Goiffon
0a3f7d7ef7 N°2989 ajax.backup small updates
* update copyright
* in messages replace iTop by constant
2020-05-07 11:18:21 +02:00
Pierre Goiffon
222eb47bd2 N°2989 ajax.backup : refactor exit conditions
Adding a die() call so that we are sure to exit on errors !
2020-05-07 10:49:05 +02:00
Pierre Goiffon
c5b1f02d2b 🔖 Update versions to 2.6.4 2020-04-21 08:52:42 +02:00
Pierre Goiffon
f81ab4d71a 🚀 Release tool to update versions
Was already comitted in 2.7 branch (fd1e17cc)
2020-04-21 08:50:25 +02:00
Eric
b88b9dabdb N°2919 - Dashboard - Fix dashboard not saved
The sanitization was too strong. Some names can contain ':'
2020-04-09 17:59:52 +02:00
Eric
06b17e82db N°2755 - Security hardening 2020-04-09 11:03:07 +02:00
Eric
2add79a473 N°2853 - Security hardening 2020-04-09 10:55:17 +02:00
Eric
3a37e24496 N°2306 - Security hardening 2020-04-08 09:28:20 +02:00
Eric
b1d703bff3 N°1671 Portal: Fix Aggregate Brick when user profile is not allowed to see one of the sub-brick 2020-04-06 14:07:42 +02:00
Eric
a3a34a94e7 N°1355 - Security hardening 2020-04-06 11:47:57 +02:00
Stephen Abello
6edc365685 N°2742 - HTML files preview are now raw text only 2020-04-06 09:47:24 +02:00
Stephen Abello
4b7f736af0 N°2755 - Security hardening 2020-04-06 09:42:41 +02:00
Stephen Abello
016fbaed36 N°2755 - Security hardening 2020-04-06 09:42:15 +02:00
Stephen Abello
bfcd137e52 N°2853 - Security hardening
(cherry picked from commit d01caaf4e4)
2020-04-06 09:37:58 +02:00
Stephen Abello
f9af8fc912 N°2855 - Security hardening
(cherry picked from commit c5c7fd5c85)
2020-04-06 09:20:02 +02:00
Stephen Abello
bd083d632f Update readme for 2.6.3 release 2020-02-05 11:22:39 +01:00
Molkobain
bd9da07734 Merge branch 'support/2.5' 2020-01-22 09:55:50 +01:00
Molkobain
3dbbf296b8 Exclude combodo-db-tools module from packages by default 2020-01-22 09:10:54 +01:00
Stephen Abello
50a8af4082 Update version number for 2.6.3 2020-01-20 16:30:51 +01:00
Stephen Abello
6a1125875b Merge branch 'support/2.5'
# Conflicts:
#	css/css-variables.scss
#	css/light-grey.css
#	datamodels/2.x/authent-external/module.authent-external.php
#	datamodels/2.x/authent-ldap/module.authent-ldap.php
#	datamodels/2.x/authent-local/module.authent-local.php
#	datamodels/2.x/itop-attachments/module.attachments.php
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php
#	datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
#	datamodels/2.x/itop-config/module.itop-config.php
#	datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php
#	datamodels/2.x/itop-full-itil/module.itop-full-itil.php
#	datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal-base/module.itop-portal-base.php
#	datamodels/2.x/itop-portal/module.itop-portal.php
#	datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php
#	datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/module.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php
#	datamodels/2.x/version.xml
2020-01-20 16:10:21 +01:00
Stephen Abello
878c23892d Update version number for 2.5.4 2020-01-20 15:59:08 +01:00
Stephen Abello
248dab9289 N°2633 - Security hardening 2020-01-20 15:46:04 +01:00
Molkobain
4f0e3430c0 Merge remote-tracking branch 'origin/support/2.5' 2020-01-08 11:58:15 +01:00
Molkobain
3347f400b8 Internal: Revert files deleted by mistake 🙈 2020-01-08 11:57:29 +01:00
Molkobain
6082308e20 Add combodo-db-tools/1.0.7 module as a default module 2020-01-08 11:40:35 +01:00
Pierre Goiffon
5fce2a2c1c Setup : fix MySQL TLS wiki URL 2019-12-23 11:27:56 +01:00
Pierre Goiffon
03b8ed5ce4 Merge branch 'support/2.6.2' 2019-10-07 09:41:08 +02:00
Stephen Abello
31a34c247c N°2482 Regression on mandatory external field with only 1 value 2019-09-13 10:42:55 +02:00
Pierre Goiffon
a625733885 👷 Jenkins : fix jenkinsfile filename case 2019-09-10 14:23:36 +02:00
Stephen Abello
d7fad4b646 Merge branch 'support/2.6.2' 2019-07-25 17:07:49 +02:00
Stephen Abello
f4d538ef6c N°2410 Fix regression in mysqldump call introduced in 2.6.2 2019-07-24 16:02:20 +02:00
Pierre Goiffon
aadb605dec Merge remote-tracking branch 'origin/support/2.6.0' 2019-07-22 15:58:58 +02:00
Pierre Goiffon
f63f2bd445 N°1802 backup : remove old itop_root config parameter
Was renamed to itop_backup_incident in 2.6.0
2019-07-22 15:57:30 +02:00
Pierre Goiffon
fefd9aae95 N°2399 backup : throw exception and log error if cannot create archive
(before error was silently ignored)
2019-07-16 17:44:56 +02:00
Pierre Goiffon
878b87b68c N°2349 fix GroupBy dashlet on classes with ExternalField to ExternalField 2019-07-05 12:10:04 +02:00
Pierre Goiffon
14ae9f0809 Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	css/css-variables.scss
#	datamodels/2.x/authent-external/module.authent-external.php
#	datamodels/2.x/authent-ldap/module.authent-ldap.php
#	datamodels/2.x/authent-local/module.authent-local.php
#	datamodels/2.x/itop-attachments/module.attachments.php
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php
#	datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
#	datamodels/2.x/itop-config/module.itop-config.php
#	datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php
#	datamodels/2.x/itop-full-itil/module.itop-full-itil.php
#	datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal-base/module.itop-portal-base.php
#	datamodels/2.x/itop-portal/module.itop-portal.php
#	datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php
#	datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/module.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php
#	datamodels/2.x/version.xml
2019-07-02 15:09:11 +02:00
Pierre Goiffon
b3369c8b0e Update version number for 2.5.3 beta 2019-07-02 14:54:36 +02:00
Pierre Goiffon
33903f570b N°2174 ExternalField to friendlyname : restore behavior without dict key
* displaying "class -> field" was breaking exports (columns labels for ext fields to friendlynames in default datamodel, for example UserRequest.agent_id_friendlyname)
* dict key is always searched first, so a custom label can always be set
2019-06-28 15:13:15 +02:00
bruno DA SILVA
127809a836 Merge remote-tracking branch 'origin/support/2.5' 2019-06-26 15:41:13 +02:00
bruno DA SILVA
6c948873ff N°2323.6 Fix regression introduced in previous commit 2019-06-26 15:38:42 +02:00
Stephen Abello
a93be39aeb N°2166: Fix regression introduced in b157fad 2019-06-26 14:15:04 +02:00
Eric
7c93d116ec 2.6.2 fix module version 2019-06-25 16:56:15 +02:00
Stephen Abello
f948d6e026 N°2336: Set right parameter for --ssl-mode 2019-06-21 14:37:48 +02:00
Stephen Abello
a222ead43c N°2336: Use --ssl-mode instead of --ssl with MySQL >= 5.7.0 2019-06-21 13:59:36 +02:00
Stephen Abello
d6bfbbcd30 N°1529: Allow parameter OpenSSLMcryptCompatibility to be used 2019-06-21 11:08:23 +02:00
Molkobain
17df9d0f9d Merge remote-tracking branch 'origin/support/2.5' 2019-06-21 10:03:17 +02:00
Molkobain
93099ea3c7 N°2323.5 Fix regression introduced in previous commit
Could not upload images in HTML field anymore
2019-06-21 10:01:31 +02:00
Molkobain
2f9e050e2b N°2323.4 Fix regression introduced in previous commit
Current user picture was no longer displayed in the portal

(cherry picked from commit 56b9eb6cf3)
2019-06-20 17:52:26 +02:00
Eric
5c341138e1 N°2129 - Fix lost dict. entries by toolkit updating script 2019-06-20 15:48:41 +02:00
Molkobain
56b9eb6cf3 N°2323.4 Fix regression introduced in previous commit
Current user picture was no longer displayed in the portal
2019-06-20 10:34:10 +02:00
Pierre Goiffon
db20244212 N°2174 External field label retrieval : fix default value
\AttributeDefinition::GetLabel with default null was returning att code !! Added PHPDoc and changed default value
2019-06-19 16:13:29 +02:00
Pierre Goiffon
6f9f74e72f N°2230 BR translations
Many thanks to our client Pimkie !
2019-06-19 15:55:05 +02:00
Eric
8070d5b9b7 2.6.2-beta 2019-06-19 15:08:40 +02:00
Molkobain
6197ecbaf4 Update version number for 2.6.2 beta 2019-06-19 14:43:21 +02:00
Eric
ca585d3f42 N°2044 - Search Form Prefill not effective when searching objects to add to an AttributeLinkedSet (n:1) 2019-06-18 15:53:48 +02:00
Eric
02c78d4044 N°2278 - Object-copier: Fix n:n link attributes set to default on copy
(cherry picked from commit 6564d84a2f)
2019-06-18 14:57:45 +02:00
Pierre Goiffon
9e5d668c02 N°2328 TagSet : allow to create codes with 3 characters
(was wrongly 4 min before)
2019-06-18 14:36:47 +02:00
Pierre Goiffon
051656f295 N°2174 External field : change label retrieval
* Now the  dict entry will be used even for friendly names
* If no dict keys exists, then the class path plus field name will be returned
2019-06-18 11:27:02 +02:00
Eric
9a51a44549 Merge remote-tracking branch 'origin/support/2.5' 2019-06-18 10:50:04 +02:00
Eric
5102b113ed N°2323 - Fix calls to ajax endpoints
(cherry picked from commit c723d19e01)
2019-06-18 10:45:50 +02:00
Eric
f1e4d94499 N°2323 - Fix calls to ajax endpoints for portal 2019-06-18 10:43:45 +02:00
Pierre Goiffon
d04102bab3 N°2243 IT translations : final versions 2019-06-18 08:56:06 +02:00
Eric
c723d19e01 N°2323 - Fix calls to ajax endpoints 2019-06-17 15:59:43 +02:00
Eric
dac77e0606 Revert id in select 2019-06-13 10:24:01 +02:00
Eric
02b98543d9 Fix Bad db_key_field definition 2019-06-13 10:06:52 +02:00
Eric
519aaadd9a N°2157 - Fix backup manual warning 2019-06-13 09:44:40 +02:00
bruno DA SILVA
fe27fef530 Merge remote-tracking branch 'origin/master' 2019-06-12 18:27:11 +02:00
bruno DA SILVA
72dbb6f937 N°1373 - supportability: log stack trace on DBObject::Reload error 2019-06-12 18:26:35 +02:00
Pierre Goiffon
ccb29d10ae N°2243 IT translations : keys in parent classes 2019-06-12 18:09:41 +02:00
Pierre Goiffon
e479775833 N°2243 IT translations
Thanks to our client Pimkie !
2019-06-12 18:09:41 +02:00
Stephen Abello
86a7192f5a N°2160: Correctly initialize fields depending on a select field with only 1 value 2019-06-12 14:39:29 +02:00
Pierre Goiffon
4a1be13904 N°2208 new IT translations 2019-06-12 11:02:49 +02:00
Pierre Goiffon
0f3347f64d 📝 Fix some PhpDoc 2019-06-12 10:39:30 +02:00
Eric
6564d84a2f N°2278 - Object-copier: Fix n:n link attributes set to default on copy 2019-06-12 10:17:19 +02:00
Eric
f4747c5cef PHPStorm 2019-06-12 09:46:21 +02:00
Eric
1e92c2f28a Sanitize error message 2019-06-11 17:18:36 +02:00
Eric
e4b097b196 Add KPIs to search 2019-06-11 17:18:35 +02:00
Eric
85653c9ffc Sanityzation:
- no use of cache to build queries when the flag is set
- Use class key instead of 'id' for default select column
2019-06-11 17:18:35 +02:00
Stephen Abello
da5d8b20fa N°2044: Enable Search form prefill when adding objects to 1:n 2019-06-11 16:59:27 +02:00
Molkobain
9a5a5f858f N°2235.2 Fix emptied URLs on object modification when URL starts with a placeholder 2019-06-11 16:13:53 +02:00
Stephen Abello
a0cd281c42 ⬆️ iTop >=2.6.1 supports PHP 7.3.x 2019-06-07 16:16:23 +02:00
Stephen Abello
6bb5606c00 N°2216: Set the mandatory icon inline with its field 2019-06-06 15:56:05 +02:00
Eric
a6ceb88fca N°2216 - Request Template Mandatory fields are not marked as "Mandatory" 2019-06-06 15:14:31 +02:00
Eric
7bb7a94fbc N°2291 - Fix blinking of warning image on mandatory HTML field 2019-06-06 15:14:31 +02:00
Pierre Goiffon
e4324cedb4 N°2235 Enable notification placeholders in hyperlinks
(cherry picked from commit bc55bfbee1)
2019-06-06 10:59:44 +02:00
Eric
07781c7c9d 1023 - Email Outlook : Fix iTop page broken by tag <o:p> 2019-06-06 09:56:31 +02:00
Stephen Abello
ea58a807fc N°2044: Forgot to add a variable in 7c1290f 2019-06-06 09:44:20 +02:00
Pierre Goiffon
fb6806a1c1 🎨 DataSynchro : some phpdoc and formatting 2019-06-05 17:26:00 +02:00
Pierre Goiffon
166451c4e9 N°532 autocomplete : fix regressions introduced in ebb541e4
* multiple separator
* XSS filter
* search field value reset
2019-06-04 11:32:08 +02:00
Stephen Abello
7c1290f684 N°2044: Enable Search form prefill when adding objects to 1:n 2019-06-04 11:18:30 +02:00
Stephen Abello
179c774ba8 N°2207: Copy the content from the right source file into production.delta.prev.xml 2019-06-03 12:36:05 +02:00
Stephen Abello
e886d80de6 N°2215: Set autocomplete minimum width (instead of its width) to its parent field width 2019-05-31 12:14:41 +02:00
Pierre Goiffon
3791b2dd24 📝 Change return type for \MetaModel::GetObject 2019-05-28 17:18:07 +02:00
Pierre Goiffon
12c916f4e5 📝 Add @var for iApplicationUIExtension loops 2019-05-28 17:16:02 +02:00
Pierre Goiffon
ba7e00e130 N°2211 DataSynchro : rollback oChange remove in DBUpdate() + added log
Be conservative : this is a fix only release... oChange will be done in the future, it's a work that should be done everywhere in iTop and not only DataSynchro
2019-05-28 17:16:02 +02:00
Eric
bada955725 N°2211 - DataSynchro : fix delete rules
- code review bug fixes and comments
2019-05-28 10:40:00 +02:00
Stephen Abello
f9e18675f3 N°2062: When a table displaying all rows is rebuilt (eg: by adding a column), reordering using columns now works 2019-05-24 16:20:31 +02:00
bruno DA SILVA
22416cc0be :note: PhpDoc generation for the public API
- typo (tunning => tuning)
 - rework of the README.md
2019-05-24 14:05:22 +02:00
Pierre Goiffon
ec086ad94a 📝 Fix /doc readme (thanks @jbostoen !) 2019-05-23 18:25:26 +02:00
Pierre Goiffon
17d4b570e8 N°2211 DataSynchro fix regression in DoJob2 method (create new iTop objects)
The \SynchroExecution::$m_oLastFullLoadStartDate is also used in DoJob2... So move back its instantiation in \SynchroExecution::PrepareProcessing, but with more variables and comment : the attribute is null until PrepareProcessing()
2019-05-23 18:15:50 +02:00
Pierre Goiffon
2dfad12553 Revert "🎨 DataSynchro : remove some warnings, code formating"
This reverts commit 26dcaa0ded.
Such modifications does not belong to a fix only branch !!
2019-05-23 11:58:10 +02:00
Pierre Goiffon
26dcaa0ded 🎨 DataSynchro : remove some warnings, code formating 2019-05-23 10:48:54 +02:00
Pierre Goiffon
b5d3ddb7e3 N°2211 DataSynchro : fix deletion rules regression when using synchro_exec.php
* fix regression : no update if exec phase only and full load interval <= 0
* fix regression : update if exec phase only and full load interval > 0
* some PHPDoc
* move back \SynchroExecution::$m_oLastFullLoadStartDate init to constructor
* add a boolean member to indicate if LastFullLoadStartDate was passed by caller
* factorize database current datetime retrieval
2019-05-23 10:48:54 +02:00
Pierre Goiffon
8b178914b3 Fix \DBObject::DBDelete PHPDoc 2019-05-22 14:46:53 +02:00
OИUЯd da silva
ce6fd4d775 bugfix: remove lower cased version of the README.md 2019-05-22 11:44:45 +02:00
OИUЯd da silva
9c75cb4537 :note: PhpDoc generation for the public API (#63)
- generate phpdoc to a dokuwiki compatible format
- add/update the phpdoc of a selection of class methods
2019-05-21 12:05:52 +02:00
bruno DA SILVA
633fa343a5 N°775 - request template : fix mandatory field list with only one possible value 2019-05-20 17:47:15 +02:00
bruno DA SILVA
a1d01e252b N°2191 - Stopwatch sub-items no more available as search criteria 2019-05-20 17:03:25 +02:00
Stephen Abello
b157fad0b6 N°2166: Portal: fields in CKEditor modals are inactive 2019-05-15 15:41:38 +02:00
Stephen Abello
70d2bb163c N°2179: Strengthen data/setup directory creation 2019-05-15 11:27:53 +02:00
Pierre Goiffon
fae6c89e9a Merge remote-tracking branch 'origin/support/2.5' 2019-05-13 08:53:50 +02:00
Pierre Goiffon
89d310258b N°2198 ReloadBlock call and parameter escaping improvements
Use an anonymous function instead of a string to get rid of a useless level of escaping
Use json_encode instead of addslashes
(many thanks for the tips @bruno-ds !!)
2019-05-09 10:37:34 +02:00
Pierre Goiffon
a58529f46c N°2198 Fix "invalid filter" error when refreshing "Requests assigned to me" 2019-05-07 18:39:38 +02:00
Pierre Goiffon
12a2035791 N°2192 table-selectable-lines : add forgotten param 2019-05-07 11:53:39 +02:00
Pierre Goiffon
b1ff7f0e9b N°2192 table-selectable-lines : replace another :has selector, and use existing constant 2019-05-07 11:44:41 +02:00
OИUЯd da silva
23cf2b91f4 make demo_mode effect more expressive
closes #71
2019-05-06 11:42:27 +02:00
Pierre Goiffon
3c4fe338b6 N°2192 fix freeze when lots of lines in a table with selectable lines
Many thanks to Jeffrey Bostoen (@jbostoen) for the bug report !
2019-05-03 17:43:36 +02:00
Pierre Goiffon
6159ab33b7 🔊 DBSearch ajax update : better handling of invalid queries 2019-05-02 17:33:41 +02:00
Pierre Goiffon
77f757995e Merge remote-tracking branch 'origin/support/2.5' 2019-04-29 11:26:37 +02:00
Thomas Casteleyn
2858d13fd5 🐛 Fix default usage of iTopMutex when TLS is enabled
See R-021467
2019-04-29 11:18:54 +02:00
Stephen Abello
99f398a87e N°1529: Correct wrong constant name for Mcrypt, handle iv generation fails to avoid data corruption 2019-04-24 11:44:43 +02:00
Pierre Goiffon
87ee731dbe 📝 Update readme for 2.6.1 2019-04-10 16:24:34 +02:00
Molkobain
f2380ae354 🌐 Add slovak dictionary files in all modules for future translation 2019-03-28 11:07:18 +01:00
Molkobain
2d039af278 🎨 Format dictionary entries 2019-03-28 11:05:15 +01:00
Molkobain
831879fe37 🐛 N°2122 Fix missing dictionary entries for "Service families" menu of "Service Mgmt Provider" module 2019-03-28 10:31:48 +01:00
Molkobain
ea1dfd8933 👥 Add Martin Kincel to the contributors. Thanks for the slovak translations! 2019-03-27 11:43:35 +01:00
Molkobain
24519c69a4 🌐 Add new language for main features thanks to Martin Kincel! 2019-03-27 11:43:35 +01:00
Vladimir Kunin
b5c4801beb 🌐 Russian translations for 2.6.1 (#70) 2019-03-25 16:29:05 +01:00
Molkobain
3eff8f62b1 Merge branch 'support/2.5' 2019-03-25 15:47:51 +01:00
Molkobain
16c8466841 N°2115 Fix regression introduced in N°1443: Left pane menu not showing due a JS error 2019-03-25 15:39:45 +01:00
Eric
b83e5e2b72 N°1618 - Fix custom date format: Fix issue with in-line creation 2019-03-22 15:42:11 +01:00
Eric
764b0f8e31 N°1846 - Fix Object Copier: Create Ticket from CI, duplicate links
(cherry picked from commit 44b7821015)
2019-03-22 11:06:23 +01:00
Pierre Goiffon
cac0da4e3d Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	application/utils.inc.php
#	css/css-variables.scss
#	css/light-grey.css
#	datamodels/2.x/authent-external/module.authent-external.php
#	datamodels/2.x/authent-ldap/module.authent-ldap.php
#	datamodels/2.x/authent-local/module.authent-local.php
#	datamodels/2.x/itop-attachments/module.attachments.php
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php
#	datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
#	datamodels/2.x/itop-config/module.itop-config.php
#	datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php
#	datamodels/2.x/itop-full-itil/module.itop-full-itil.php
#	datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal-base/module.itop-portal-base.php
#	datamodels/2.x/itop-portal/module.itop-portal.php
#	datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php
#	datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/module.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php
#	datamodels/2.x/version.xml
2019-03-21 17:39:26 +01:00
Pierre Goiffon
7e064365eb N°1968 Uniqueness : fix only root disabled class was removed from searches
Now all of the following hierarchy is excluded
2019-03-21 17:25:51 +01:00
Pierre Goiffon
7c7382f372 N°1835 add new 'transaction_id' sanitize filter 2019-03-21 14:28:24 +01:00
Pierre Goiffon
4918b9c83a 💡 Add phpdoc for N°1835 new Sanitize param 2019-03-21 12:04:31 +01:00
Pierre Goiffon
ed95f4e05f 📦 Update CSS for 2.6.1 2019-03-19 11:58:48 +01:00
Pierre Goiffon
9f0e8dc49b 📦 Update CSS for 2.5.2 2019-03-19 11:47:29 +01:00
Pierre Goiffon
693fdfdc5b 📦 Update versions for 2.6.1, woops wrong version on previous commit 2019-03-19 11:39:11 +01:00
Pierre Goiffon
b8d5c01382 📦 Update versions for 2.5.2 2019-03-19 11:37:15 +01:00
Pierre Goiffon
01108ca83d 📦 Update versions for 2.6.1 2019-03-19 11:35:42 +01:00
Pierre Goiffon
3d5b7197f6 📦 Update modules versions for 2.6.1 2019-03-19 11:34:01 +01:00
Pierre Goiffon
d3db77c675 📦 Update modules versions for 2.5.2 2019-03-19 11:29:37 +01:00
Pierre Goiffon
54c027823b 🌐 Fix dict automatic update 2019-03-19 11:14:39 +01:00
Pierre Goiffon
f63aceeabe Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	datamodels/2.x/authent-external/da.dict.authent-external.php
#	datamodels/2.x/authent-external/de.dict.authent-external.php
#	datamodels/2.x/authent-external/fr.dict.authent-external.php
#	datamodels/2.x/authent-external/hu.dict.authent-external.php
#	datamodels/2.x/authent-external/ja.dict.authent-external.php
#	datamodels/2.x/authent-external/nl.dict.authent-external.php
#	datamodels/2.x/authent-external/pt_br.dict.authent-external.php
#	datamodels/2.x/authent-external/zh_cn.dict.authent-external.php
#	datamodels/2.x/authent-ldap/cs.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/da.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/de.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/fr.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/hu.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/ja.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/nl.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/pt_br.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/ru.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/zh_cn.dict.authent-ldap.php
#	datamodels/2.x/authent-local/da.dict.authent-local.php
#	datamodels/2.x/authent-local/de.dict.authent-local.php
#	datamodels/2.x/authent-local/fr.dict.authent-local.php
#	datamodels/2.x/authent-local/hu.dict.authent-local.php
#	datamodels/2.x/authent-local/ja.dict.authent-local.php
#	datamodels/2.x/authent-local/nl.dict.authent-local.php
#	datamodels/2.x/authent-local/pt_br.dict.authent-local.php
#	datamodels/2.x/authent-local/ru.dict.authent-local.php
#	datamodels/2.x/authent-local/zh_cn.dict.authent-local.php
#	datamodels/2.x/itop-attachments/cs.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/da.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/de.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/en.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/es_cr.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/fr.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/hu.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/it.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/ja.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/nl.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/pt_br.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/tr.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/zh_cn.dict.itop-attachments.php
#	datamodels/2.x/itop-backup/da.dict.itop-backup.php
#	datamodels/2.x/itop-backup/de.dict.itop-backup.php
#	datamodels/2.x/itop-backup/es_cr.dict.itop-backup.php
#	datamodels/2.x/itop-backup/hu.dict.itop-backup.php
#	datamodels/2.x/itop-backup/it.dict.itop-backup.php
#	datamodels/2.x/itop-backup/ja.dict.itop-backup.php
#	datamodels/2.x/itop-backup/nl.dict.itop-backup.php
#	datamodels/2.x/itop-backup/tr.dict.itop-backup.php
#	datamodels/2.x/itop-change-mgmt-itil/da.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/de.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/fr.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/hu.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/it.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/ja.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/nl.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/ru.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/zh_cn.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/da.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/de.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/fr.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/hu.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/it.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/ja.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/nl.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/pt_br.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/ru.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/tr.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/zh_cn.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/cs.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/da.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/de.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/es_cr.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/fr.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/hu.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/it.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/ja.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/nl.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/pt_br.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/ru.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/tr.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/zh_cn.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config/da.dict.itop-config.php
#	datamodels/2.x/itop-config/de.dict.itop-config.php
#	datamodels/2.x/itop-config/es_cr.dict.itop-config.php
#	datamodels/2.x/itop-config/fr.dict.itop-config.php
#	datamodels/2.x/itop-config/hu.dict.itop-config.php
#	datamodels/2.x/itop-config/it.dict.itop-config.php
#	datamodels/2.x/itop-config/ja.dict.itop-config.php
#	datamodels/2.x/itop-config/nl.dict.itop-config.php
#	datamodels/2.x/itop-config/pt_br.dict.itop-config.php
#	datamodels/2.x/itop-config/tr.dict.itop-config.php
#	datamodels/2.x/itop-datacenter-mgmt/cs.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/da.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/de.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/es_cr.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/fr.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/hu.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/it.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/ja.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/nl.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/pt_br.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/ru.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/tr.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/zh_cn.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/cs.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/da.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/de.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/es_cr.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/fr.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/hu.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/it.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/ja.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/nl.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/pt_br.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/ru.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/tr.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/zh_cn.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-hub-connector/cs.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/da.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/de.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/es_cr.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/fr.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/hu.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/it.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/ja.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/nl.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/pt_br.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/ru.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/tr.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/zh_cn.dict.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/cs.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/da.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/de.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/es_cr.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/hu.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/it.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/ja.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/nl.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/pt_br.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/ru.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/tr.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/zh_cn.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/cs.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/da.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/de.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/hu.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/it.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/ja.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/nl.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/tr.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/zh_cn.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal-base/da.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/de.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/hu.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/it.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/ja.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/nl.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/ru.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/tr.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/zh_cn.dict.itop-portal-base.php
#	datamodels/2.x/itop-problem-mgmt/da.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/de.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/fr.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/hu.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/it.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/ja.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/nl.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/ru.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/zh_cn.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-request-mgmt-itil/cs.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/da.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/de.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/en.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/es_cr.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/fr.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/hu.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/it.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/ja.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/pt_br.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/ru.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/tr.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/zh_cn.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/cs.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/da.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/de.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/fr.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/hu.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/it.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/ja.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/nl.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/ru.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/tr.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/zh_cn.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/cs.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/da.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/de.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/en.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/fr.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/hu.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/it.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/ja.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/nl.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/pt_br.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/ru.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/tr.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/zh_cn.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/cs.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/da.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/de.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/en.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/fr.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/hu.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/it.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/ja.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/nl.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/pt_br.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/ru.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/tr.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/zh_cn.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/cs.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/da.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/de.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/es_cr.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/fr.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/hu.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/it.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/ja.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/nl.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/pt_br.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/ru.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/tr.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/zh_cn.dict.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/cs.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/da.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/de.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/es_cr.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/fr.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/hu.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/it.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/ja.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/nl.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/pt_br.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/ru.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/tr.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/zh_cn.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/cs.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/da.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/de.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/hu.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/it.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/ja.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/nl.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/ru.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/tr.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/zh_cn.dict.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/cs.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/da.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/de.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/es_cr.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/fr.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/hu.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/it.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/ja.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/nl.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/pt_br.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/ru.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/tr.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/zh_cn.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/cs.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/da.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/de.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/en.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/es_cr.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/fr.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/hu.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/it.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/ja.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/nl.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/pt_br.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/ru.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/tr.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/zh_cn.dict.itop-welcome-itil.php
#	dictionaries/cs.dictionary.itop.core.php
#	dictionaries/cs.dictionary.itop.ui.php
#	dictionaries/da.dictionary.itop.core.php
#	dictionaries/da.dictionary.itop.ui.php
#	dictionaries/de.dictionary.itop.core.php
#	dictionaries/de.dictionary.itop.ui.php
#	dictionaries/es_cr.dictionary.itop.core.php
#	dictionaries/es_cr.dictionary.itop.ui.php
#	dictionaries/fr.dictionary.itop.core.php
#	dictionaries/fr.dictionary.itop.ui.php
#	dictionaries/hu.dictionary.itop.core.php
#	dictionaries/hu.dictionary.itop.ui.php
#	dictionaries/it.dictionary.itop.core.php
#	dictionaries/it.dictionary.itop.ui.php
#	dictionaries/ja.dictionary.itop.core.php
#	dictionaries/ja.dictionary.itop.ui.php
#	dictionaries/nl.dictionary.itop.core.php
#	dictionaries/nl.dictionary.itop.ui.php
#	dictionaries/pt_br.dictionary.itop.core.php
#	dictionaries/pt_br.dictionary.itop.ui.php
#	dictionaries/ru.dictionary.itop.core.php
#	dictionaries/ru.dictionary.itop.ui.php
#	dictionaries/tr.dictionary.itop.core.php
#	dictionaries/tr.dictionary.itop.ui.php
#	dictionaries/zh_cn.dictionary.itop.core.php
#	dictionaries/zh_cn.dictionary.itop.ui.php
2019-03-19 10:54:06 +01:00
Pierre Goiffon
533e65fcd1 🌐 Fix dict automatic update 2019-03-19 10:52:43 +01:00
Pierre Goiffon
3fb0c768e6 🌐 Update dictionnaries for iTop 2.5.2 2019-03-19 10:10:28 +01:00
Pierre Goiffon
243aab1030 N°1968 Uniqueness : do not allow invalid rule overrides definition
On overrides disabled key must has a value
2019-03-18 16:07:36 +01:00
bruno DA SILVA
22dba9ae07 🐛 composer.json dependencies correctness 2019-03-18 15:00:35 +01:00
Pierre Goiffon
02857a86fd Merge remote-tracking branch 'origin/support/2.5' 2019-03-15 17:22:04 +01:00
Stephen Abello
d663d01798 N°1966: Added missing strings to dict files 2019-03-15 17:02:24 +01:00
Stephen Abello
3602163b38 Revert N°1919 and its incorrect translations 2019-03-15 17:00:53 +01:00
Molkobain
3322074ce7 🐛 N°1889 Portal: Wrong encoding of special chars like in dashlets (eg. "ö", "&", ...)
(cherry picked from commit 83bb3b6d72)
2019-03-13 17:01:57 +01:00
Molkobain
83bb3b6d72 🐛 N°1889 Portal: Wrong encoding of special chars like in dashlets (eg. "ö", "&", ...) 2019-03-13 16:56:04 +01:00
Eric
229f800266 N° 1837 - Fix Synchro Obsolescence
(cherry picked from commit 75737b4ffe)
2019-03-13 14:19:25 +01:00
Eric
75737b4ffe N° 1837 - Fix Synchro Obsolescence 2019-03-13 11:01:13 +01:00
Molkobain
a5340917a7 🐛 N°1956 Portal: Fix message content in user profile when password edition is disabled 2019-03-13 10:31:04 +01:00
Molkobain
914d19e7e4 🐛 N°2072 Fix missing/empty error message when uploading too large attachment 2019-03-13 09:51:49 +01:00
Pierre Goiffon
eb49dbbdc8 N°1968 uniqueness rules : fix search for classes hierarchy, disallow 'attributes' property overrides 2019-03-12 18:12:01 +01:00
Molkobain
912bab5a43 🐛 N°2091: Portal: Fix regression introduced in 2.5, better error message when user logged out 2019-03-12 17:08:12 +01:00
Pierre Goiffon
94092f445f Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	application/applicationextension.inc.php
#	webservices/rest.php
2019-03-08 14:35:02 +01:00
Eric
856c037bb0 N°2064 - Fix abstract class state list for notification triggers 2019-03-07 15:56:29 +01:00
Eric
388896b963 N°941 - Backup/Windows %, ! or " not allowed in password 2019-03-07 15:26:53 +01:00
Eric
1d8addf675 typo 2019-03-06 18:21:19 +01:00
Eric
c8c3d32b18 Error logs and corresponding eml stored by message and accessible from the console 2019-03-06 17:46:47 +01:00
Molkobain
75a0979eee N°2045 Portal: Fix regression introduced with N°1980: New request with 2 Request Templates does not display fields 2019-03-06 14:46:01 +01:00
Pierre Goiffon
d6a0a279a5 N°1449 Update REST API version for new pagination params 2019-03-04 16:00:36 +01:00
Pierre Goiffon
44f5d71e1b 💡 PHPDoc for REST API 2019-03-04 16:00:35 +01:00
Pierre Goiffon
bc841dd239 N°1921 Process InlineImage from another iTop as external images
* Notifications : do not embed InlineImage with wrong secret
* HtmlSanitizer : remove data-img-* attributes if not the same iTop (using approot from Config)
* move \HTMLDOMSanitizer::ProcessImage to \InlineImage::ProcessImageTag
* data-img-* attributes name are now InlineImage class constants

(cherry picked from commit 0aab80917a)
2019-03-04 14:59:38 +01:00
Stephen Abello
9c71d32964 typo in last merge 2019-03-04 12:01:52 +01:00
Eric
d199d84b27 🌐 french typo fixed 2019-03-04 09:41:09 +01:00
Pierre Goiffon
24aca83de4 Merge remote-tracking branch 'origin/support/2.5' 2019-03-04 09:16:53 +01:00
Pierre Goiffon
3de7aa1ada 🙈 Update ignore file 2019-03-04 09:15:38 +01:00
Stephen Abello
5a0edb5c39 Merge branch 'support/2.5'
# Conflicts:
#	js/jquery.tablesorter.pager.js
2019-03-01 17:06:44 +01:00
Stephen Abello
d4fec14123 N°1443: Handle disabled selectable rows in datatables when "All checking/unchecking" 2019-03-01 12:18:07 +01:00
Eric
38951fab1a N°1618 - Custom date format: Fix issue with in-line creation 2019-02-28 17:40:49 +01:00
Pierre Goiffon
e2c8237beb Allow params "limit" and "page" in REST-API
PR #25, code author Dennis Lassiter, many thanks !
Was commited to develop first but decided to retrofit this on support/2.5 (N°1449)
(cherry picked from commit fd55bdf9a8)
(cherry picked from commit 49e31ddb3d)
2019-02-28 10:06:42 +01:00
Pierre Goiffon
1224570fa5 Merge remote-tracking branch 'origin/support/2.5' 2019-02-28 08:45:30 +01:00
Pierre Goiffon
afb99c0f4b N°2054 fix search with sSearchValue GET param in BrowseBrick 2019-02-27 16:21:29 +01:00
Eric
733c908e34 N°2043 - Fix: CSV Import reconciliation using ExternalField broken in service management for provider 2019-02-27 16:00:47 +01:00
Eric
377b4b038c N°2041 - Fix: Reset(LinkedSetIndirect) breaks data integrity 2019-02-27 09:57:01 +01:00
Eric
3f7fd6f9f9 N°1954 - Recent change on impact analysis - old change appears on result 2019-02-26 15:36:56 +01:00
Eric
1cb36621a1 N°1963 - Dashlet GroupBy: allow ExternalField selection in UI 2019-02-26 15:31:31 +01:00
Eric
ddd9188eb7 Fix non-existing variable 2019-02-26 15:24:15 +01:00
Eric
02254eac67 N°1966 - Missing dictionaries entries (recover lost translation) 2019-02-25 17:40:08 +01:00
Eric
ebe026b2e9 N°1966 - Missing dictionaries entries (other languages) 2019-02-25 17:20:47 +01:00
Eric
efc7c5b0f4 N°1966 - Missing dictionaries entries 2019-02-25 16:44:51 +01:00
Pierre Goiffon
f68a77450d Merge remote-tracking branch 'origin/support/2.5' 2019-02-25 12:10:30 +01:00
Stephen Abello
c5943c6c28 Internal: Fix a regression introduced by 6b5cc7c on dot path 2019-02-22 10:06:10 +01:00
Pierre Goiffon
bc3b50ad23 Fix wrong method call 2019-02-22 10:00:23 +01:00
Pierre Goiffon
1f7923beae Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	setup/setup.js
#	setup/wizardsteps.class.inc.php
2019-02-21 17:18:29 +01:00
Pierre Goiffon
2f15bbdaf3 N°2033 backup : tar generation simplify buffer size computation
(cherry picked from commit 5b46644786)
2019-02-21 17:12:18 +01:00
Eric
a2d34d1779 N°1954 - Fix recent change on impact analysis 2019-02-21 09:41:36 +01:00
Pierre Goiffon
cdba1e0d36 N°2033 backup : fix corrupted archive for files which size is a multiple of 1024 bytes
(cherry picked from commit 3356856a5f)
2019-02-20 17:29:10 +01:00
Eric
48f15d7781 N°1974 - Fix: Stimuli can be applied through URL even if the access rights are set to deny 2019-02-20 16:11:49 +01:00
Eric
d0a766d424 N°1975 - Fix: Change Menu rights to "Admin only" leads in crash test
(cherry picked from commit a89bca4626)
2019-02-20 15:06:57 +01:00
Eric
a89bca4626 N°1975 - Fix: Change Menu rights to "Admin only" leads in crash test 2019-02-20 14:57:39 +01:00
Pierre Goiffon
23ec21e494 N°2031 backup : now logs using IssueLog, and remove debug config property 2019-02-19 14:46:44 +01:00
Eric
63a36fd0f6 N°2030 - Fix function CopyAttribute for external fields 2019-02-19 14:09:14 +01:00
Pierre Goiffon
02617e8976 🔊 itop-backup : add some more logs 2019-02-19 12:21:53 +01:00
Eric
7cf7e55454 N°1823 - Fix tags not saved in case of error 2019-02-19 10:16:45 +01:00
Eric
5067c867b8 N°2014 - Fix Object modification refused when a n-n relation is locked by datasynchro 2019-02-18 15:41:06 +01:00
Stephen Abello
6b5cc7ca4b N°1877 & N°2012: Fix regression backup link on setup, security hardening 2019-02-18 10:40:51 +01:00
Eric
7bb49893ee N°1884 - Admin Tools Manager no longer has access to 'Schedule Backup' and 'Configuration' menus 2019-02-15 17:34:38 +01:00
Eric
bf62b63173 N°1884 - Admin Tools Manager no longer has access to 'Schedule Backup' and 'Configuration' menus 2019-02-15 17:33:47 +01:00
Eric
b8fb1fa78a N°1884 - Admin Tools Manager no longer has access to 'Schedule Backup' and 'Configuration' menus
(cherry picked from commit 818b4d08da)
2019-02-15 17:31:37 +01:00
Eric
818b4d08da N°1884 - Admin Tools Manager no longer has access to 'Schedule Backup' and 'Configuration' menus 2019-02-15 17:28:55 +01:00
Eric
f438fbd06f N°941 - fix regex 2019-02-15 17:06:50 +01:00
Eric
297a45d477 N°941 - Backup/Windows %, ! or " not allowed in password 2019-02-15 17:03:07 +01:00
Eric
17fe9dfd5f N°1906 - Enhancement Request: Handling a lot of many to many relations in form element (edit mode) 2019-02-15 15:33:22 +01:00
Eric
9d0cbca497 N°941 - Check DB password in configuration when saving 2019-02-14 10:57:36 +01:00
Eric
a613b4b101 Merge branch 'hotfix/N941-2' 2019-02-14 09:48:48 +01:00
Eric
c989e2eda5 N°941 - Check DB password also in configuration 2019-02-14 09:47:22 +01:00
Eric
c6fd381b01 Merge branch 'hotfix/N941' 2019-02-13 17:45:44 +01:00
Eric
a784661025 N°941 - Backup/Windows % not allowed in password 2019-02-13 17:43:32 +01:00
Eric
1f9a638bc1 N°941 - Backup/Windows % not allowed in password 2019-02-13 17:39:04 +01:00
Stephen Abello
545504c0de (retrofit from master) N°1443 : Add table_id used by tables paging
(cherry picked from commit 43b0747b83)
2019-02-13 15:13:13 +01:00
Stephen Abello
32f1e97bcd (retrofit from master) N°1148: Fix regression on export
(cherry picked from commit 90e128f951)
2019-02-13 15:13:13 +01:00
Stephen Abello
43b0747b83 N°1443 : Add table_id used by tables paging 2019-02-13 14:43:25 +01:00
Eric
9637e75f97 N°2011: Fix Issue with "ExecAsyncTask: async_task_retries" 2019-02-13 14:07:13 +01:00
Eric
84767692b0 Merge branch 'hotfix/N2016' 2019-02-13 14:04:25 +01:00
Eric
d484614c0f N°2016 - Fix Issue with "ExecAsyncTask: async_task_retries" 2019-02-13 14:03:22 +01:00
Eric
b403bace6c Merge branch 'hotfix/N2011' 2019-02-13 12:49:25 +01:00
Eric
0b751a9dd6 N°2011 - Fix search auto-completion bug 2019-02-13 12:48:05 +01:00
567 changed files with 53605 additions and 8158 deletions

103
.doc/README.md Normal file
View File

@@ -0,0 +1,103 @@
# Phpdoc dokuwiki template
This directory contains a template rendering iTop phpdoc as wiki pages.
conventional tag that you should use:
* `@internal` : exclude from the documentation.
* `@api` : it means that a method is an api, thus it may be interacted with.
* `@see` : it points to another documented method
* `@link` : external url
* if you point to another page of the wiki, please use relative links.
* `@example` : let you provide example of code
* `@param`, `@return`, `@throws`, ...
## Special instructions
some tags where added :
* `@api-advanced`: it means that a method is an `@api` but mark it also as "complex" to use
* `@overwritable-hook`: used to mark a method as "designed to be extended"
* `@extension-hook`: not used for now
* `@phpdoc-tuning-exclude-inherited`: once this tag is present on a class, it's inherited methods won't be showed.
### known limitations:
#### `@see` tags must be very specific:
* always prefix class members with `ClassName::`
* for methods always suffix them with `()`,
* do not reference variables since they are not documented. If you have to, always prefix them with `$`
examples:
```
/**
* @see DBObject
* @see DBObject::Get()
* @see DBObject::$foo
*/
```
#### Do not use inline tags, they do not work properly, example:
```
/**
* This is a texts with ans inline tag {@see [FQSEN] [<description>]} it must never be used
*/
```
#### The `@example` tag must respect this very precise syntax
* the sentence in the first line (next to the tag) is the title, it must be enclose by double quotes
* the following lines are the sample code.
* 💔 since we simply hack the official tag, this syntax must be respected carefully 💔
example:
```
/**
* @example "This is the title of the multiline example"
* $foo = DBObject::Get('foo');
* DBObject::Set('foo', ++$foo);
*/
```
## How content is included into the documentation
**For a class** those requirements have to be respected:
- the file containing the class must be listed in `/phpdoc/files/file[]` of `.doc/phpdoc-objects-manipulation.dist.xml`
- the class **must not** have the tag `@internal`
- the class **must** have at least one of: `@api`, `@api-advanced`, `@overwritable-hook`, `@extension-hook`
Then, **for a method** of an eligible class:
- **public** methods **must** have at least one of: `@api`, `@api-advanced`, `@overwritable-hook`, `@extension-hook`
- **protected** methods **must** have at least one of: `@overwritable-hook`, `@extension-hook`
- **private** methods are **always excluded**
**Class properties** and **constants** are never documented (this is subject to change).
## A note about the rendering engine
:notebook: as spaces are used to mark code, the templates (`.doc/phpdoc-templates/combodo-wiki/*`) have very few indentation, thus they are awful to read (sorry).
## Installation
```
composer require phpdocumentor/phpdocumentor:~2 --dev
```
## Generation
`.doc/bin/build-doc-object-manipulation` and `.doc/bin/build-doc-extensions` contains examples of doc. generation, beware: they have to be called from iTop root directory:
```shell
cd /path/to/itop/
./.doc/bin/build-doc-object-manipulation
```
the resulting documentation is written into `data/phpdocumentor/output`
## Dokuwiki requirements
* the template uses the [wrap plugin](https://www.dokuwiki.org/plugin:wrap).
* the generated files have to be placed under an arbitrary directory of `[/path/to/dokuwiki]/data/pages`.
* the html has to be activated [config:htmlok](https://www.dokuwiki.org/config:htmlok)
* the generated files have to be in lowercase

7
.doc/bin/build-doc-extensions Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh -x
rm -rf /tmp/phpdoc-twig-cache/ && rm -rf data/phpdocumentor/output/extensions/ && rm -rf data/phpdocumentor/temp/extensions/ && vendor/bin/phpdoc -c .doc/phpdoc-extensions.dist.xml -vvv
# now wee need to lowercase every generated file because dokuwiki can't handle uppercase
cd data/phpdocumentor/output/extensions/
for i in $( ls | grep [A-Z] ); do mv -i $i `echo $i | tr 'A-Z' 'a-z'`; done

View File

@@ -0,0 +1,8 @@
#!/bin/sh -x
rm -rf /tmp/phpdoc-twig-cache/ && rm -rf data/phpdocumentor/output/objects-manipulation/ && rm -rf data/phpdocumentor/temp/objects-manipulation/ && vendor/bin/phpdoc -c .doc/phpdoc-objects-manipulation.dist.xml -vvv
# now wee need to lowercase every generated file because dokuwiki can't handle uppercase
cd data/phpdocumentor/output/objects-manipulation/
for i in $( ls | grep [A-Z] ); do mv -i $i `echo $i | tr 'A-Z' 'a-z'`; done

20
.doc/phpdoc-extensions.dist.xml Executable file
View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<phpdoc>
<title><![CDATA[iTop extensions]]></title>
<parser>
<target>../data/phpdocumentor/temp/extensions</target>
</parser>
<transformer>
<target>../data/phpdocumentor/output/extensions</target>
</transformer>
<transformations>
<template name="phpdoc-templates/combodo-wiki"/>
</transformations>
<files>
<file>../application/applicationextension.inc.php</file>
</files>
</phpdoc>

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" ?>
<phpdoc>
<!--
/**
The documentation of this file can be found here : https://docs.phpdoc.org/references/configuration.html
it has to be completed by the CLI parameters documentation which is more comprehensive: https://docs.phpdoc.org/references/commands/project_run.html#usage
usage:
vendor/bin/phpdoc -c phpdoc-objects-manipulation.dist.xml
*/
-->
<title><![CDATA[iTop's objects manipulation API]]></title>
<parser>
<default-package-name>iTopORM</default-package-name>
<target>../data/phpdocumentor/temp/objects-manipulation</target>
<visibility>public,protected</visibility>
<markers>
<!--<item>TODO</item>-->
<!--<item>FIXME</item>-->
</markers>
<extensions>
<extension>php</extension>
</extensions>
</parser>
<transformer>
<target>../data/phpdocumentor/output/objects-manipulation</target>
</transformer>
<transformations>
<template name="phpdoc-templates/combodo-wiki"/>
</transformations>
<!--<logging>-->
<!--<level>warn</level>-->
<!--<paths>-->
<!--&lt;!&ndash;<default>data/phpdocumentor/log/objects-manipulation/{DATE}.log</default>&ndash;&gt;-->
<!--&lt;!&ndash;<errors>data/phpdocumentor/log/objects-manipulation/{DATE}.errors.log</errors>&ndash;&gt;-->
<!--<default>{APP_ROOT}/data/log/{DATE}.log</default>-->
<!--<errors>{APP_ROOT}/data/log/{DATE}.errors.log</errors>-->
<!--</paths>-->
<!--</logging>-->
<files>
<file>../core/dbobject.class.php</file>
<file>../core/dbobjectsearch.class.php</file>
<file>../core/metamodel.class.php</file>
<file>../core/dbobjectset.class.php</file>
<file>../core/dbsearch.class.php</file>
<file>../core/dbunionsearch.class.php</file>
</files>
</phpdoc>

View File

@@ -0,0 +1,136 @@
{% extends 'layout.txt.twig' %}
{% block content %}
<wrap button>[[start|🔙 Back]]</wrap>
{% if node.tags['internal'] is defined %}
====== {{ node.name }} ======
<WRAP alert>This class is "internal", and thus is not documented!</WRAP>
{% elseif node.tags['api'] is not defined and node.tags['api-advanced'] is not defined and node.tags['overwritable-hook'] is not defined and node.tags['extension-hook'] is not defined %}
====== {{ node.name }} ======
<WRAP alert>This class is neither "api", "api-advanced", "overwritable-hook" or "extension-hook", and thus is not documented!</WRAP>
{% else %}
====== {{ node.name }} ======
{% if node.deprecated %}<wrap danger>deprecated</wrap>{% endif %}
{% if node.abstract %}<wrap warning>abstract</wrap>{% endif %}
{% if node.final %}<wrap notice>final</wrap>{% endif %}
{% include 'includes/wrap-tags.txt.twig' with {structure:node, wrap: 'safety', wrapTags: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
{% if node.deprecated %}
=== **<del>Deprecated</del>**===
//{{ node.tags.deprecated[0].description }}//
{% endif %}
== {{ node.summary|replace({"\n":""})|raw }} ==
<html>{{ node.description|markdown|raw }}</html>
{% include 'includes/code-examples.txt.twig' with {structure:node, title_level: '====='} %}
{% set class = node.parent %}
{% block hierarchy_element %}
{% if class and class.name is defined and class.name|trim != '' %}
==== parent ====
{% set child = class %}
{% set class = class.parent %}
{{ block('hierarchy_element') }}
[[{{ child.name }}|{{ child.name }}]]
{% endif %}
{% endblock %}
{% for interface in node.interfaces|sort_asc %}
{% if loop.first %}
==== Implements ====
{% endif %}
{% if loop.length > 1 %} * {% endif %}{{ interface.fullyQualifiedStructuralElementName ?: interface }}
{% endfor %}
{% for trait in node.usedTraits|sort_asc %}
{% if loop.first %}
==== Uses traits ====
{% endif %}
{% if loop.length > 1 %} * {% endif %}{{ trait.fullyQualifiedStructuralElementName ?: trait }}
{% endfor %}
{% include 'includes/see-also.txt.twig' with {structure:node, title_level: '==='} %}
{% include 'includes/tags.txt.twig' with {structure:node, title_level: '=====', blacklist: ['link', 'see', 'abstract', 'example', 'method', 'property', 'property-read', 'property-write', 'package', 'subpackage', 'phpdoc-tuning-exclude-inherited', 'api', 'api-advanced', 'overwritable-hook', 'extension-hook', 'copyright', 'license', 'code-example']} %}
{% set methods = node.inheritedMethods.merge(node.methods.merge(node.magicMethods)) %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'api'} %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'api-advanced'} %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'overwritable-hook'} %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'extension-hook'} %}
{% include 'includes/code-examples.txt.twig' with {structure:node, title_level: '=====', sub_title_level: '=='} %}
<WRAP clear />
{% for method in methods|sort_asc
if method.visibility == 'public'
and (
method.tags['api'] is defined
or method.tags['api-advanced'] is defined
or method.tags['overwritable-hook'] is defined
or method.tags['extension-hook'] is defined
)
and (
node.tags['phpdoc-tuning-exclude-inherited'] is not defined
or method.parent.name == node.name
)
%}
{%- if loop.first %}
===== Public methods =====
{% endif %}
{{ block('method') }}
{% endfor %}
{% for method in methods|sort_asc if method.visibility == 'protected' and (method.tags['overwritable-hook'] is defined or method.tags['extension-hook'] is defined) %}
{%- if loop.first %}
===== Protected methods =====
{% endif %}
{{ block('method') }}
{% endfor %}
{% set constants = node.inheritedConstants.merge(node.constants) %}
{% if constants|length > 0 %}
===== Constants =====
{% for constant in constants|sort_asc %}
{{ block('constant') }}
{% endfor %}
{% endif %}
{#{% set properties = node.inheritedProperties.merge(node.properties.merge(node.magicProperties)) %}#}
{#{% for property in properties|sort_asc if property.visibility == 'public' %}#}
{#{%- if loop.first %}#}
{#===== Public properties =====#}
{#{% endif %}#}
{#{{ block('property') }}#}
{#{% endfor %}#}
{#{% for property in properties|sort_asc if property.visibility == 'protected' %}#}
{#{%- if loop.first %}#}
{#===== Protected properties =====#}
{#{% endif %}#}
{#{{ block('property') }}#}
{#{% endfor %}#}
{%- endif %} {#{% elseif node.tags['xxx'] is not defined and ... #}
<wrap button>[[start|🔙 Back]]</wrap>
{% endblock %}

View File

@@ -0,0 +1,31 @@
{% block constant %}
<WRAP group box >
<WRAP twothirds column >
==== {{ constant.name }} ====
</WRAP>{# twothirds column#}
<WRAP third column>
{% if constant.deprecated %}<wrap danger>deprecated</wrap> {% endif %}
{% if (node.parent is not null and constant.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}<wrap notice>inherited</wrap> {% endif %}
</WRAP>{# third column#}
== {{ constant.summary|replace({"\n":""})|raw }} ==
<html>{{ constant.description|markdown|raw }}</html>
{% if constant.deprecated %}
=== Deprecated ===
{{ constant.tags.deprecated[0].description|raw }}
{% endif %}
{% include 'includes/inherited-from.txt.twig' with {structure:constant} %}
{% include 'includes/see-also.txt.twig' with {structure:constant, title_level: '=='} %}
{% include 'includes/uses.txt.twig' with {structure:constant, title_level: '=='} %}
{% include 'includes/tags.txt.twig' with {structure:constant, title_level: '==', blacklist: ['link', 'see', 'var', 'deprecated', 'uses', 'package', 'subpackage', 'todo', 'code-example']} %}
</WRAP>{# group #}
{% endblock %}

View File

@@ -0,0 +1,95 @@
{% block method %}
<WRAP group box >
<WRAP twothirds column >
==== {{ method.name }} ====
</WRAP>{# twothirds column#}
<WRAP third column >
{% include 'includes/wrap-tags.txt.twig' with {structure:method, wrap: 'safety', wrapTags: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
{% if method.deprecated %}<wrap danger>deprecated</wrap> {% endif %}
{% if (node.parent is not null and method.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}<wrap notice>inherited</wrap> {% endif %}
{% if method.abstract %}<wrap warning>abstract</wrap> {% endif %}
{% if method.final %}<wrap notice>final</wrap> {% endif %}
<wrap notice>{{ method.visibility }}</wrap>
{% if method.static %}<wrap warning>static</wrap> {% endif %}
</WRAP>{# third column#}
== {{ method.summary|replace({"\n":""})|raw }} ==
<html>{{ method.description|markdown|raw }}</html>
<code php>{% if method.abstract %}abstract {% endif %}{% if method.final %}final {% endif %}{{ method.visibility }} {% if method.static %}static {% endif %}{{ method.name }}({% for argument in method.arguments %}{{ argument.isVariadic ? '...' }}{{ argument.name }}{{ argument.default ? (' = '~argument.default)|raw }}{% if not loop.last %}, {% endif %}{% endfor %})</code>
<WRAP twothirds column >
=== Parameters ===
{% if method.arguments|length > 0 -%}
^ types ^ name ^ default ^ description ^
{% for argument in method.arguments -%}
| **<nowiki>{{ argument.types|join('|')|raw }}</nowiki>** | {{ argument.name }} {{ argument.isVariadic ? '<small style="color: gray">variadic</small>' }} | <nowiki>{{ argument.default|raw }}</nowiki> | {{ argument.description|trim|replace("\n", ' ')|raw }} |{{ "\r\n" }}
{%- endfor %}
{% else %}
//none//
{% endif %}
{#=== Parameters ===#}
{#{% if method.arguments|length > 0 -%}#}
{#{% for argument in method.arguments -%}#}
{#== {{ argument.name }} ==#}
{#{% set varDesc %}#}
{#<span style="margin:0 10px; 0 20px; font-weight: bold;">{{ argument.types|join('|') }}</span>#}
{#{{ argument.isVariadic ? '<small style="color: gray">variadic</small>' }}#}
{#{{ argument.description|raw }}#}
{#{% endset %}#}
{#<html>{{ varDesc|markdown|raw }}</html>#}
{#{%- endfor %}#}
{#{% else %}#}
{#<wrap tip>This method has no parameter</wrap>#}
{#{% endif %}#}
{% if method.response and method.response.types|join() != 'void' %}
=== Returns ===
<html>{{ ('**' ~ method.response.types|join('|')|trim ~ '** ' ~ method.response.description)|markdown|raw }}</html>
{% endif %}
</WRAP>{# twothirds column#}
<WRAP third column >
{% if method.tags.throws|length > 0 or method.tags.throw|length > 0 %}
=== Throws ===
{% for exception in method.tags.throws -%}
{% if loop.length > 1 %} * {% endif %}''{{ exception.types|join('|')|raw }}'' <nowiki>{{ exception.description|raw }}</nowiki>
{% endfor %}
{% endif %}
{% include 'includes/inherited-from.txt.twig' with {structure:method} %}
{% include 'includes/see-also.txt.twig' with {structure:method, title_level: '==='} %}
{% include 'includes/uses.txt.twig' with {structure:method, title_level: '==='} %}
{% include 'includes/used-by.txt.twig' with {structure:method, title_level: '==='} %}
{% include 'includes/tags-with-description.txt.twig' with {structure:method, title_level: '===', WRAP: 'info', tagsWithDescription: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
{% include 'includes/tags.txt.twig' with {structure:method, title_level: '===', blacklist: ['todo', 'link', 'see', 'abstract', 'example', 'param', 'return', 'access', 'deprecated', 'throws', 'throw', 'uses', 'api', 'api-advanced', 'overwritable-hook', 'extension-hook', 'used-by', 'inheritdoc', 'code-example']} %}
</WRAP>{# third column#}
{% include 'includes/code-examples.txt.twig' with {structure:method, title_level: '==='} %}
</WRAP>{# group #}
{% endblock %}

View File

@@ -0,0 +1,49 @@
{% block property %}
<WRAP group box>
<WRAP twothirds column >
==== ${{ property.name }} ====
</WRAP>{# twothirds column#}
<WRAP third column>
{% if property.deprecated %}<wrap danger>deprecated</wrap> {% endif %}
{% if (node.parent is not null and property.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}<wrap notice>inherited</wrap> {% endif %}
</WRAP>{# third column#}
== {{ property.summary|replace({"\n":""})|raw }} ==
<html>{{ property.description|markdown|raw }}</html>
{% if property.var.0.description %}<html>{{ property.var.0.description|markdown|raw }}</html>{% endif %}
{#{% if property.types %}#}
{#== Type ==#}
{#{% for type in property.types %}#}
{#{% if loop.length > 1 %} * {% endif %}{{ type|raw }} : {{ type.description|raw }}#}
{#{% endfor %}#}
{#{{ property.types|join('|')|raw }}#}
{#{% endif %}#}
{% if property.deprecated %}
== Deprecated ==
{{ property.tags.deprecated[0].description }}
{% endif %}
{% include 'includes/inherited-from.txt.twig' with {structure:property} %}
{% include 'includes/see-also.txt.twig' with {structure:property, title_level: '=='} %}
{% include 'includes/uses.txt.twig' with {structure:property, title_level: ''} %}
{% include 'includes/tags.txt.twig' with {structure:property, title_level: '==', blacklist: ['link', 'see', 'access', 'var', 'deprecated', 'uses', 'todo', 'code-example']} %}
<code php>{{ property.visibility }} ${{ property.name }}{% if property.types %} : {{ property.types|join('|')|raw }}{% endif %}</code>
</WRAP>{# group #}
{% endblock %}

View File

@@ -0,0 +1 @@
{{ node.source|raw }}

View File

@@ -0,0 +1,122 @@
{% extends 'layout.txt.twig' %}
{% block javascripts %}
{% endblock %}
{% block content %}
{#<section class="row-fluid">#}
{#<div class="span2 sidebar">#}
{#{% set namespace = project.namespace %}#}
{#{{ block('sidebarNamespaces') }}#}
{#</div>#}
{#</section>#}
{#<section class="row-fluid">#}
====== {{ node.path|split('/')|slice(0,-1)|join('/') }}{{ node.name }} ======
{{ node.summary }}
<html>{{ node.description|markdown|raw }}</html>
{% if node.traits|length > 0 %}
===== Traits =====
{% for trait in node.traits %}
<tr>
<td>{{ trait|raw }}</td>
<td><em>{{ trait.summary }}</em></td>
</tr>
{% endfor %}
{% endif %}
{% if node.interfaces|length > 0 %}
===== Interfaces =====
{% for interface in node.interfaces %}
<tr>
<td>{{ interface|raw }}</td>
<td><em>{{ interface.summary }}</em></td>
</tr>
{% endfor %}
{% endif %}
{% if node.classes|length > 0 %}
===== Classes =====
{% for class in node.classes %}
{{ class|raw }}
<em>{{ class.summary }}</em>
{% endfor %}
{% endif %}
{% if node.package is not empty and node.package != '\\' %}
===== Package =====
{{ node.subpackage ? (node.package ~ '\\' ~ node.subpackage) : node.package }}
{% endif %}
{% for tagName,tags in node.tags if tagName in ['link', 'see'] %}
{% if loop.first %}
===== See also =====
{% endif %}
{% for tag in tags %}
<dd><a href="{{ tag.reference ?: tag.link }}"><div class="namespace-wrapper">{{ tag.description ?: tag.reference }}</div></a></dd>
{% endfor %}
{% endfor %}
<h2>Tags</h2>
<table class="table table-condensed">
{% for tagName,tags in node.tags if tagName not in ['link', 'see', 'package', 'subpackage'] %}
<tr>
<th>
{{ tagName }}
</th>
<td>
{% for tag in tags %}
{{ tag.description|markdown|raw }}
{% endfor %}
</td>
</tr>
{% else %}
<tr><td colspan="2"><em>None found</em></td></tr>
{% endfor %}
</table>
</aside>
</div>
{% if node.constants|length > 0 %}
<div class="row-fluid">
<section class="span8 content file">
<h2>Constants</h2>
</section>
<aside class="span4 detailsbar"></aside>
</div>
{% for constant in node.constants %}
{{ block('constant') }}
{% endfor %}
{% endif %}
{% if node.functions|length > 0 %}
<div class="row-fluid">
<section class="span8 content file">
<h2>Functions</h2>
</section>
<aside class="span4 detailsbar"></aside>
</div>
{% for method in node.functions %}
{{ block('method') }}
{% endfor %}
{% endif %}
</div>
</section>
<div id="source-view" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="source-view-label" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="source-view-label">{{ node.file.name }}</h3>
</div>
<div class="modal-body">
<pre data-src="{{ path('files/' ~ node.path ~ '.txt')|raw }}" class="language-php line-numbers"></pre>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,42 @@
{% extends 'layout.html.twig' %}
{% block stylesheets %}
<link href="{{ path('css/jquery.iviewer.css') }}" rel="stylesheet" media="all"/>
<style>
#viewer {
position: relative;
width: 100%;
}
.wrapper {
overflow: hidden;
}
</style>
{% endblock %}
{% block javascripts %}
<script src="{{ path('js/jquery.mousewheel.js') }}" type="text/javascript"></script>
<script src="{{ path('js/jquery.iviewer.js') }}" type="text/javascript"></script>
<script type="text/javascript">
$(window).resize(function(){
$("#viewer").height($(window).height() - 100);
});
$(document).ready(function() {
$("#viewer").iviewer({src: '{{ path('graphs/classes.svg') }}', zoom_animation: false});
$('#viewer img').bind('dragstart', function(event){
event.preventDefault();
});
$(window).resize();
});
</script>
{% endblock %}
{% block content %}
<div class="row-fluid">
<div class="span12">
<div class="wrapper">
<div id="viewer" class="viewer"></div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,5 @@
# Fixes a vulnerability in CentOS: http://stackoverflow.com/questions/20533279/prevent-php-from-parsing-non-php-files-such-as-somefile-php-txt
<FilesMatch \.php\.txt$>
RemoveHandler .php
ForceType text/plain
</FilesMatch>

View File

@@ -0,0 +1,34 @@
{% if title_level is not defined %}
{%- set title_level = '==' -%}
{% endif %}
{% if sub_title_level is not defined %}
{%- set sub_title_level = title_level|slice(1) -%}
{% endif %}
{% if sub_title_level == '=' %}
{%- set sub_title_level = '' -%}
{% endif %}
{#{% for tagName,tags in structure.tags if tagName in ['code-example'] %}#}
{#{% if loop.first %}#}
{#{{title_level}} Examples {{title_level}}#}
{#{% endif %}#}
{#{% for tag in tags %}#}
{#{%- set descToken = tag.description|split("\n", 2) -%}#}
{#{%- set title = descToken[0] -%}#}
{#{%- set code = descToken[1] -%}#}
{#{{sub_title_level}} {{ title }} {{sub_title_level}}#}
{#<code php>{{ code|raw }}</code>#}
{#{% endfor %}#}
{#{% endfor %}#}
{% for tagName,tags in structure.tags if tagName in ['example'] %}
{% if loop.first %}
{{title_level}} Examples {{title_level}}
{% endif %}
{% for tag in tags %}
{{ sub_title_level }} {{ tag.filePath|escape }}{{ sub_title_level }}
<code php>{{ tag.description|raw }}</code>
{% endfor %}
{% endfor %}

View File

@@ -0,0 +1,12 @@
{% if title_level is not defined %}
{% set title_level='' %}
{% endif %}
{% if (node.parent is null) %}
{{title_level}} File {{ structure.path }} {{title_level}}
{% endif %}
{% if (node.parent is not null and structure.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}
{{title_level}} Inherited from {{title_level}}
[[{{structure.parent}}|{{structure.parent}}]]
{% endif %}

View File

@@ -0,0 +1,26 @@
{% for structure in structures|sort_asc if structure.tags['internal'] is not defined and (structure.tags['api'] is defined or structure.tags['api-advanced'] is defined or structure.tags['overwritable-hook'] is defined or structure.tags['extension-hook'] is defined ) %}
{#{{ structure|raw }}#}
{% set structureName = structure|trim('\\', 'left') %}
<WRAP group box>
<WRAP twothirds column >
==== {{ structureName }} ====
</WRAP>{# twothirds column#}
<WRAP third column>
{% if structure.deprecated %}<wrap danger>deprecated</wrap>{% endif %}
{% if structure.abstract %}<wrap warning>abstract</wrap>{% endif %}
{% if structure.final %}<wrap notice>final</wrap>{% endif %}
{% if (node.parent is not null and structure.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}<wrap notice>inherited</wrap> {% endif %}
{% include 'includes/wrap-tags.txt.twig' with {structure:structure, wrap: 'safety', wrapTags: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
</WRAP>{# third column#}
{{ structure.summary|raw }}
[[{{structureName}}|More informations]]
</WRAP>{# group #}
{% endfor %}

View File

@@ -0,0 +1,26 @@
{% if title_level is not defined %}
{%- set title_level='==' -%}
{% endif %}
{% for tagName,tags in structure.tags if tagName in ['link', 'see'] %}
{% if loop.first %}
{{title_level}} See also {{title_level}}
{% endif %}
{% for tag in tags %}
{%- set linkTag = tag.reference|trim('\\', 'left') -%}
{% if not('()' in linkTag or '$' in linkTag or node.name in linkTag or '::' in linkTag ) %}
{%- set linkTag = linkTag|lower -%}
{% elseif node.name~'::' in linkTag %}
{%- set linkTag = linkTag|replace({(node.name~'::'): '#'})|lower -%}
{% elseif '::' in linkTag -%}
{%- set linkTag = linkTag|replace({'::': '#'})|lower -%}
{% else %}
{%- set linkTag = '#' ~ linkTag|lower -%}
{%- endif %}
{% if loop.length > 1 %} * {% endif %}{% if tag.reference is not empty -%}
[[{{linkTag}}|{{ (tag.reference)|trim('\\', 'left') }}]] {% if tag.description|trim is not empty %}: {{ tag.description|trim('\\', 'left') }} {% endif %}
{%- else -%}
{#{{ tag.description|trim('\\', 'left') }}#}
{% endif %}
{% endfor %}
{% endfor %}

View File

@@ -0,0 +1,56 @@
{% if tag is not defined -%}
{# Do not display @api if @api-advanced is also present #}
{%- set tag = "api" -%}
{%- endif %}
{% if hidden_by is not defined -%}
{# Do not display @api if @api-advanced is also present #}
{%- set hidden_by = {"api" : "api-advanced"} -%}
{%- endif %}
{% for method in methods|sort_asc
if (method.visibility == 'public')
and (
method.tags[tag] is defined
and (
hidden_by[tag] is not defined or method.tags[hidden_by[tag]] is not defined
)
)
%}
{%- if loop.first %}
{% if tag == 'api' %}
===== API synthesis =====
<WRAP>
List of the public API methods.
When manipulating {{ node.name }}, You can call those methods:
</WRAP>
{% elseif tag == 'api-advanced' %}
===== Advanced API synthesis =====
<WRAP>
List of advanced API methods
Beware they usage is recommended to advanced users only.
</WRAP>
{% elseif tag == 'overwritable-hook' %}
===== overwritable-hook synthesis =====
<WRAP >When inheriting from {{ node.name }},
you can overwrite those methods in order to add custom logic:
</WRAP>
{% elseif tag == 'extension-hook' %}
===== extension-hook synthesis =====
<WRAP >
When inheriting from {{ node.name }},
you can extend the behaviour of iTop by implementing:
</WRAP>
{% endif %}
{% endif %}
{% set sanitizedMethod = method|trim('\\', 'left')|replace({(node.name~'::'): ''}) %}
{% if '::' in sanitizedMethod -%}
{%- if node.tags['phpdoc-tuning-exclude-inherited'] is not defined %}
* [[{{sanitizedMethod|replace({'::': '#'})|lower}}|↪{{sanitizedMethod}}]] — {{ method.summary|replace({"\n":""})|raw }}
{% endif %}
{%- else %}
* [[#{{sanitizedMethod}}|{{sanitizedMethod}}]] — {{ method.summary|replace({"\n":""})|raw }}
{% endif %}
{% endfor %}

View File

@@ -0,0 +1,20 @@
{% if title_level is not defined %}
{% set title_level = '==' %}
{% endif %}
{%- for tagName,tags in structure.tags if tagName in tagsWithDescription -%}
{%- for tag in tags -%}
{%- if tag.description is not empty -%}
{%- if WRAP is defined -%}
<WRAP {{WRAP}}>
{%- endif -%}
{{title_level}} {{ tagName }} {{title_level}}
{{ tag.description|escape }}
{%- if WRAP is defined -%}
</WRAP>
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- endfor -%}

View File

@@ -0,0 +1,22 @@
{% if title_level is not defined %}
{% set title_level='=====' %}
{% endif %}
{% if blacklist is not defined %}
{% set blacklist =['link', 'see', 'abstract', 'example', 'method', 'property', 'property-read', 'property-write', 'package', 'subpackage', 'api', 'api-advanced', 'todo', 'code-example'] %}
{% endif %}
{% if hidden_by is not defined -%}
{# Do not display @api if @api-advanced is also present #}
{%- set hidden_by = {"api" : "api-advanced"} -%}
{%- endif %}
{#^ {% for tagName,tags in structure.tags if tagName not in blacklist -%}#}
{#{{ tagName }} ^#}
{#{%- endfor %}#}
{% for tagName,tags in structure.tags if tagName not in blacklist and (hidden_by[tagName] is not defined or structure.tags[hidden_by[tagName]] is not defined) %}
{%- if loop.first %}
{{title_level}} Tags {{title_level}}
{% endif %}
^ {{ tagName }} | {% for tag in tags %}{{ tag.version ? tag.version ~ ' ' : '' }}{{ tag.description}}{% endfor %} |
{% endfor %}

View File

@@ -0,0 +1,24 @@
{% if title_level is not defined %}
{% set title_level='' %}
{% endif %}
{% for tagName,tags in structure.tags if tagName in ['used-by'] %}
{% if loop.first %}
{{title_level}} Used by {{title_level}}
{% endif %}
{% for tag in tags %}
{% if loop.length > 1 %} * {% endif %}{{ tag.reference ?: tag.link }} : {{ tag.description ?: tag.reference }}
{% endfor %}
{% endfor %}
{#{% for tagName,tags in method.tags if tagName in ['uses'] %}#}
{#{% if loop.first %}#}
{#<dt>Uses</dt>#}
{#{% endif %}#}
{#{% for tag in tags %}#}
{#<dd>{{ tag.reference|raw }}</dd>#}
{#{% endfor %}#}
{#{% endfor %}#}

View File

@@ -0,0 +1,24 @@
{% if title_level is not defined %}
{% set title_level='' %}
{% endif %}
{% for tagName,tags in structure.tags if tagName in ['uses'] %}
{% if loop.first %}
{{title_level}} Uses {{title_level}}
{% endif %}
{% for tag in tags %}
{% if loop.length > 1 %} * {% endif %}{{ tag.reference ?: tag.link }} : {{ tag.description ?: tag.reference }}
{% endfor %}
{% endfor %}
{#{% for tagName,tags in method.tags if tagName in ['uses'] %}#}
{#{% if loop.first %}#}
{#<dt>Uses</dt>#}
{#{% endif %}#}
{#{% for tag in tags %}#}
{#<dd>{{ tag.reference|raw }}</dd>#}
{#{% endfor %}#}
{#{% endfor %}#}

View File

@@ -0,0 +1,11 @@
{% if wrap is not defined -%}
{% set wrap = 'notice' %}
{%- endif -%}
{% if hidden_by is not defined -%}
{# Do not display @api if @api-advanced is also present #}
{%- set hidden_by = {"api" : "api-advanced"} -%}
{%- endif %}
{%- for tagName,tags in structure.tags if tagName in wrapTags and (hidden_by[tagName] is not defined or structure.tags[hidden_by[tagName]] is not defined) %}
<wrap {{wrap}}>{{tagName}}</wrap>
{% endfor %}

View File

@@ -0,0 +1,121 @@
{% extends 'layout.txt.twig' %}
{% block content %}
<wrap button>[[start|🔙 Back]]</wrap>
{% if node.tags['internal'] is defined %}
====== {{ node.name }} ======
<WRAP alert>This interface is "internal", and thus is not documented!</WRAP>
{% elseif node.tags['api'] is not defined and node.tags['api-advanced'] is not defined and node.tags['overwritable-hook'] is not defined and node.tags['extension-hook'] is not defined %}
====== {{ node.name }} ======
<WRAP alert>This interface is neither "api", "overwritable-hook" or "extension-hook", and thus is not documented!</WRAP>
{% else %}
====== {{ node.name }} ======
{% if node.deprecated %}<wrap danger>deprecated</wrap>{% endif %}
{% if node.abstract %}<wrap warning>abstract</wrap>{% endif %}
{% if node.final %}<wrap notice>final</wrap>{% endif %}
{% include 'includes/wrap-tags.txt.twig' with {structure:node, wrap: 'safety', wrapTags: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
{% if node.deprecated %}
=== **<del>Deprecated</del>**===
//{{ node.tags.deprecated[0].description }}//
{% endif %}
== {{ node.summary|replace({"\n":""})|raw }} ==
<html>{{ node.description|markdown|raw }}</html>
{% include 'includes/code-examples.txt.twig' with {structure:node, title_level: '====='} %}
{% set class = node.parent %}
{% block hierarchy_element %}
{% if class and class.name is defined and class.name|trim != '' %}
==== parent ====
{% set child = class %}
{% set class = class.parent %}
{{ block('hierarchy_element') }}
[[{{ child.name }}|{{ child.name }}]]
{% endif %}
{% endblock %}
{% for interface in node.interfaces|sort_asc %}
{% if loop.first %}
==== Implements ====
{% endif %}
{% if loop.length > 1 %} * {% endif %}{{ interface.fullyQualifiedStructuralElementName ?: interface }}
{% endfor %}
{% for trait in node.usedTraits|sort_asc %}
{% if loop.first %}
==== Uses traits ====
{% endif %}
{% if loop.length > 1 %} * {% endif %}{{ trait.fullyQualifiedStructuralElementName ?: trait }}
{% endfor %}
{% include 'includes/see-also.txt.twig' with {structure:node, title_level: '==='} %}
{% include 'includes/tags.txt.twig' with {structure:node, title_level: '=====', blacklist: ['link', 'see', 'abstract', 'example', 'method', 'property', 'property-read', 'property-write', 'package', 'subpackage', 'phpdoc-tuning-exclude-inherited', 'api', 'api-advanced', 'overwritable-hook', 'extension-hook', 'copyright', 'license', 'code-example']} %}
{% set methods = node.inheritedMethods.merge(node.methods) %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'api'} %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'api-advanced'} %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'overwritable-hook'} %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'extension-hook'} %}
<WRAP clear />
{% for method in methods|sort_asc if method.visibility == 'public' %}
{%- if loop.first %}
===== Public methods =====
{% endif %}
{{ block('method') }}
{% endfor %}
{% for method in methods|sort_asc if method.visibility == 'protected' %}
{%- if loop.first %}
===== Protected methods =====
{% endif %}
{{ block('method') }}
{% endfor %}
{% set constants = node.inheritedConstants.merge(node.constants) %}
{% if constants|length > 0 %}
===== Constants =====
{% for constant in constants|sort_asc %}
{{ block('constant') }}
{% endfor %}
{% endif %}
{#{% set properties = node.inheritedProperties.merge(node.properties) %}#}
{#{% for property in properties|sort_asc if property.visibility == 'public' %}#}
{#{%- if loop.first %}#}
{#===== Public properties =====#}
{#{% endif %}#}
{#{{ block('property') }}#}
{#{% endfor %}#}
{#{% for property in properties|sort_asc if property.visibility == 'protected' %}#}
{#{%- if loop.first %}#}
{#===== Protected properties =====#}
{#{% endif %}#}
{#{{ block('property') }}#}
{#{% endfor %}#}
{%- endif %} {#{% elseif node.tags['xxx'] is not defined and ... #}
<wrap button>[[start|🔙 Back]]</wrap>
{% endblock %}

View File

@@ -0,0 +1,5 @@
{% use 'elements/constant.txt.twig' %}
{% use 'elements/property.txt.twig' %}
{% use 'elements/method.txt.twig' %}
{% block content %}{% endblock %}

View File

@@ -0,0 +1,51 @@
{% extends 'layout.txt.twig' %}
{% block content %}
{% set namespace = project.namespace %}
{{ block('sidebarNamespaces') }}
{#{{ node.parent|raw }}#}
{#====== {{ node.parent.fullyQualifiedStructuralElementName }}{{ node.name }} ======#}
{% if node.children|length > 0 %}
=====Namespaces=====
{% include 'includes/namespace-structure-toc.html.twig' with {structures: node.children} %}
----
{% endif %}
{% if node.traits|length > 0 %}
===== Traits =====
{% include 'includes/namespace-structure-toc.html.twig' with {structures: node.traits} %}
----
{%- endif %}
{% if node.interfaces|length > 0 %}
===== Interfaces =====
{% include 'includes/namespace-structure-toc.html.twig' with {structures: node.interfaces} %}
----
{% endif %}
{% if node.classes|length > 0 %}
===== Classes =====
{% include 'includes/namespace-structure-toc.html.twig' with {structures: node.classes} %}
----
{% endif %}
{#{% if node.constants|length > 0 %}#}
{#===== Constants =====#}
{#{% for constant in node.constants|sort_asc %}#}
{# {{ block('constant') }}#}
{#{% endfor %}#}
{#{% endif %}#}
{#{% if node.functions|length > 0 %}#}
{#===== Functions =====#}
{#{% for method in node.functions|sort_asc %}#}
{# {{ block('method') }}#}
{#{% endfor %}#}
{#{% endif %}#}
{% endblock %}

View File

@@ -0,0 +1,49 @@
====== Deprecated elements ======
{#{% for element in project.indexes.elements if element.deprecated %}#}
{#{% if element.file.path != previousPath %}#}
{#<li><a href="#{{ element.file.path }}"><i class="icon-file"></i> {{ element.file.path }}</a></li>#}
{#{% endif %}#}
{#{% set previousPath = element.file.path %}#}
{#{% endfor %}#}
{% for element in project.indexes.elements if element.deprecated %}
{% if element.file.path != previousPath %}
{% if previousPath %}
</WRAP>{# group #}
{% endif %}
{#<a name="{{ element.file.path }}" id="{{ element.file.path }}"></a>#}
===== {{ element.file.path }} ({{ element.tags.deprecated.count }} found)=====
<WRAP group >
<WRAP third column >
Element
</WRAP>{# third column#}
<WRAP third column >
Line
</WRAP>{# third column#}
<WRAP third column >
Description
</WRAP>{# third column#}
{% endif %}
{% for tag in element.tags.deprecated %}
<WRAP group >
<WRAP third column >
{{ element.fullyQualifiedStructuralElementName }}
</WRAP>{# third column#}
<WRAP third column >
{{ element.line }}
</WRAP>{# third column#}
<WRAP third column >
{{ tag.description }}
</WRAP>{# third column#}
{% endfor %}
</WRAP>{# group #}
{% set previousPath = element.file.path %}
{% else %}
<WRAP info>No deprecated elements have been found in this project.</WRAP>
{% endfor %}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<template>
<author>Bruno DA SILVA</author>
<email>contact [at] combodo.com</email>
<version>1.0.0</version>
<copyright>Combodo 2018</copyright>
<description><![CDATA[
Forked from the clean theme of https://github.com/phpDocumentor/phpDocumentor2 provided under the MIT licence.
The original work is copyright "Mike van Riel".
------------------------------------------------------------------------------------------------------------------
To improve performance you can add the following to your .htaccess:
<ifModule mod_deflate.c>
<filesMatch "\.(js|css|html)$">
SetOutputFilter DEFLATE
</filesMatch>
</ifModule>
]]></description>
<transformations>
<transformation writer="twig" query="namespace" source="templates/combodo-wiki/namespace.txt.twig" artifact="start.txt"/>
<transformation writer="twig" query="indexes.classes" source="templates/combodo-wiki/class.txt.twig" artifact="{{name}}.txt"/>
<transformation writer="twig" query="indexes.interfaces" source="templates/combodo-wiki/interface.txt.twig" artifact="{{name}}.txt" />
</transformations>
</template>

556
.editorconfig Normal file
View File

@@ -0,0 +1,556 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 140
tab_width = 4
ij_continuation_indent_size = 8
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_visual_guides = 80,120
ij_wrap_on_typing = true
[*.css]
indent_style = tab
ij_smart_tabs = true
ij_visual_guides = none
ij_css_align_closing_brace_with_properties = false
ij_css_blank_lines_around_nested_selector = 1
ij_css_blank_lines_between_blocks = 1
ij_css_brace_placement = end_of_line
ij_css_enforce_quotes_on_format = false
ij_css_hex_color_long_format = false
ij_css_hex_color_lower_case = false
ij_css_hex_color_short_format = false
ij_css_hex_color_upper_case = false
ij_css_keep_blank_lines_in_code = 2
ij_css_keep_indents_on_empty_lines = false
ij_css_keep_single_line_blocks = false
ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
ij_css_space_after_colon = true
ij_css_space_before_opening_brace = true
ij_css_use_double_quotes = true
ij_css_value_alignment = do_not_align
[*.scss]
indent_size = 2
tab_width = 2
ij_visual_guides = none
ij_scss_align_closing_brace_with_properties = false
ij_scss_blank_lines_around_nested_selector = 1
ij_scss_blank_lines_between_blocks = 1
ij_scss_brace_placement = 0
ij_scss_enforce_quotes_on_format = false
ij_scss_hex_color_long_format = false
ij_scss_hex_color_lower_case = false
ij_scss_hex_color_short_format = false
ij_scss_hex_color_upper_case = false
ij_scss_keep_blank_lines_in_code = 2
ij_scss_keep_indents_on_empty_lines = false
ij_scss_keep_single_line_blocks = false
ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
ij_scss_space_after_colon = true
ij_scss_space_before_opening_brace = true
ij_scss_use_double_quotes = true
ij_scss_value_alignment = 0
[*.twig]
ij_smart_tabs = true
ij_visual_guides = none
ij_wrap_on_typing = false
ij_twig_keep_indents_on_empty_lines = false
ij_twig_spaces_inside_comments_delimiters = true
ij_twig_spaces_inside_delimiters = true
ij_twig_spaces_inside_variable_delimiters = true
[.editorconfig]
ij_visual_guides = none
ij_editorconfig_align_group_field_declarations = false
ij_editorconfig_space_after_colon = false
ij_editorconfig_space_after_comma = true
ij_editorconfig_space_before_colon = false
ij_editorconfig_space_before_comma = false
ij_editorconfig_spaces_around_assignment_operators = true
[{*.ant, *.fxml, *.jhm, *.jnlp, *.jrxml, *.rng, *.tld, *.wsdl, *.xml, *.xsd, *.xsl, *.xslt, *.xul, phpunit.xml.dist}]
indent_size = 2
tab_width = 2
ij_smart_tabs = true
ij_visual_guides = none
ij_wrap_on_typing = false
ij_xml_align_attributes = true
ij_xml_align_text = false
ij_xml_attribute_wrap = normal
ij_xml_block_comment_at_first_column = true
ij_xml_keep_blank_lines = 2
ij_xml_keep_indents_on_empty_lines = false
ij_xml_keep_line_breaks = false
ij_xml_keep_line_breaks_in_text = true
ij_xml_keep_whitespaces = false
ij_xml_keep_whitespaces_around_cdata = preserve
ij_xml_keep_whitespaces_inside_cdata = true
ij_xml_line_comment_at_first_column = true
ij_xml_space_after_tag_name = false
ij_xml_space_around_equals_in_attribute = false
ij_xml_space_inside_empty_tag = false
ij_xml_text_wrap = off
[{*.bash,*.sh,*.zsh}]
indent_size = 2
tab_width = 2
ij_visual_guides = none
ij_shell_binary_ops_start_line = false
ij_shell_keep_column_alignment_padding = false
ij_shell_minify_program = false
ij_shell_redirect_followed_by_space = false
ij_shell_switch_cases_indented = false
[{*.cjs,*.js}]
indent_style = tab
ij_continuation_indent_size = 4
ij_smart_tabs = true
ij_visual_guides = none
ij_javascript_align_imports = false
ij_javascript_align_multiline_array_initializer_expression = false
ij_javascript_align_multiline_binary_operation = false
ij_javascript_align_multiline_chained_methods = false
ij_javascript_align_multiline_extends_list = false
ij_javascript_align_multiline_for = true
ij_javascript_align_multiline_parameters = true
ij_javascript_align_multiline_parameters_in_calls = false
ij_javascript_align_multiline_ternary_operation = false
ij_javascript_align_object_properties = 0
ij_javascript_align_union_types = false
ij_javascript_align_var_statements = 0
ij_javascript_array_initializer_new_line_after_left_brace = false
ij_javascript_array_initializer_right_brace_on_new_line = false
ij_javascript_array_initializer_wrap = off
ij_javascript_assignment_wrap = off
ij_javascript_binary_operation_sign_on_next_line = false
ij_javascript_binary_operation_wrap = off
ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
ij_javascript_blank_lines_after_imports = 1
ij_javascript_blank_lines_around_class = 1
ij_javascript_blank_lines_around_field = 0
ij_javascript_blank_lines_around_function = 1
ij_javascript_blank_lines_around_method = 1
ij_javascript_block_brace_style = end_of_line
ij_javascript_call_parameters_new_line_after_left_paren = false
ij_javascript_call_parameters_right_paren_on_new_line = false
ij_javascript_call_parameters_wrap = off
ij_javascript_catch_on_new_line = false
ij_javascript_chained_call_dot_on_new_line = true
ij_javascript_class_brace_style = end_of_line
ij_javascript_comma_on_new_line = false
ij_javascript_do_while_brace_force = always
ij_javascript_else_on_new_line = false
ij_javascript_enforce_trailing_comma = keep
ij_javascript_extends_keyword_wrap = off
ij_javascript_extends_list_wrap = off
ij_javascript_field_prefix = _
ij_javascript_file_name_style = relaxed
ij_javascript_finally_on_new_line = false
ij_javascript_for_brace_force = always
ij_javascript_for_statement_new_line_after_left_paren = false
ij_javascript_for_statement_right_paren_on_new_line = false
ij_javascript_for_statement_wrap = off
ij_javascript_force_quote_style = false
ij_javascript_force_semicolon_style = false
ij_javascript_function_expression_brace_style = end_of_line
ij_javascript_if_brace_force = always
ij_javascript_import_merge_members = global
ij_javascript_import_prefer_absolute_path = global
ij_javascript_import_sort_members = true
ij_javascript_import_sort_module_name = false
ij_javascript_import_use_node_resolution = true
ij_javascript_imports_wrap = on_every_item
ij_javascript_indent_case_from_switch = true
ij_javascript_indent_chained_calls = true
ij_javascript_indent_package_children = 0
ij_javascript_jsx_attribute_value = braces
ij_javascript_keep_blank_lines_in_code = 2
ij_javascript_keep_first_column_comment = true
ij_javascript_keep_indents_on_empty_lines = false
ij_javascript_keep_line_breaks = true
ij_javascript_keep_simple_blocks_in_one_line = false
ij_javascript_keep_simple_methods_in_one_line = false
ij_javascript_line_comment_add_space = true
ij_javascript_line_comment_at_first_column = false
ij_javascript_method_brace_style = end_of_line
ij_javascript_method_call_chain_wrap = off
ij_javascript_method_parameters_new_line_after_left_paren = false
ij_javascript_method_parameters_right_paren_on_new_line = false
ij_javascript_method_parameters_wrap = off
ij_javascript_object_literal_wrap = on_every_item
ij_javascript_parentheses_expression_new_line_after_left_paren = false
ij_javascript_parentheses_expression_right_paren_on_new_line = false
ij_javascript_place_assignment_sign_on_next_line = false
ij_javascript_prefer_as_type_cast = false
ij_javascript_prefer_explicit_types_function_expression_returns = false
ij_javascript_prefer_explicit_types_function_returns = false
ij_javascript_prefer_explicit_types_vars_fields = false
ij_javascript_prefer_parameters_wrap = false
ij_javascript_reformat_c_style_comments = false
ij_javascript_space_after_colon = true
ij_javascript_space_after_comma = true
ij_javascript_space_after_dots_in_rest_parameter = false
ij_javascript_space_after_generator_mult = true
ij_javascript_space_after_property_colon = true
ij_javascript_space_after_quest = true
ij_javascript_space_after_type_colon = true
ij_javascript_space_after_unary_not = false
ij_javascript_space_before_async_arrow_lparen = true
ij_javascript_space_before_catch_keyword = true
ij_javascript_space_before_catch_left_brace = true
ij_javascript_space_before_catch_parentheses = true
ij_javascript_space_before_class_lbrace = true
ij_javascript_space_before_class_left_brace = true
ij_javascript_space_before_colon = true
ij_javascript_space_before_comma = false
ij_javascript_space_before_do_left_brace = true
ij_javascript_space_before_else_keyword = true
ij_javascript_space_before_else_left_brace = true
ij_javascript_space_before_finally_keyword = true
ij_javascript_space_before_finally_left_brace = true
ij_javascript_space_before_for_left_brace = true
ij_javascript_space_before_for_parentheses = true
ij_javascript_space_before_for_semicolon = false
ij_javascript_space_before_function_left_parenth = true
ij_javascript_space_before_generator_mult = false
ij_javascript_space_before_if_left_brace = true
ij_javascript_space_before_if_parentheses = true
ij_javascript_space_before_method_call_parentheses = false
ij_javascript_space_before_method_left_brace = true
ij_javascript_space_before_method_parentheses = false
ij_javascript_space_before_property_colon = false
ij_javascript_space_before_quest = true
ij_javascript_space_before_switch_left_brace = true
ij_javascript_space_before_switch_parentheses = true
ij_javascript_space_before_try_left_brace = true
ij_javascript_space_before_type_colon = false
ij_javascript_space_before_unary_not = false
ij_javascript_space_before_while_keyword = true
ij_javascript_space_before_while_left_brace = true
ij_javascript_space_before_while_parentheses = true
ij_javascript_spaces_around_additive_operators = false
ij_javascript_spaces_around_arrow_function_operator = true
ij_javascript_spaces_around_assignment_operators = true
ij_javascript_spaces_around_bitwise_operators = true
ij_javascript_spaces_around_equality_operators = true
ij_javascript_spaces_around_logical_operators = true
ij_javascript_spaces_around_multiplicative_operators = true
ij_javascript_spaces_around_relational_operators = true
ij_javascript_spaces_around_shift_operators = true
ij_javascript_spaces_around_unary_operator = false
ij_javascript_spaces_within_array_initializer_brackets = false
ij_javascript_spaces_within_brackets = false
ij_javascript_spaces_within_catch_parentheses = false
ij_javascript_spaces_within_for_parentheses = false
ij_javascript_spaces_within_if_parentheses = false
ij_javascript_spaces_within_imports = false
ij_javascript_spaces_within_interpolation_expressions = false
ij_javascript_spaces_within_method_call_parentheses = false
ij_javascript_spaces_within_method_parentheses = false
ij_javascript_spaces_within_object_literal_braces = false
ij_javascript_spaces_within_object_type_braces = true
ij_javascript_spaces_within_parentheses = false
ij_javascript_spaces_within_switch_parentheses = false
ij_javascript_spaces_within_type_assertion = false
ij_javascript_spaces_within_union_types = true
ij_javascript_spaces_within_while_parentheses = false
ij_javascript_special_else_if_treatment = true
ij_javascript_ternary_operation_signs_on_next_line = false
ij_javascript_ternary_operation_wrap = off
ij_javascript_union_types_wrap = on_every_item
ij_javascript_use_chained_calls_group_indents = true
ij_javascript_use_double_quotes = true
ij_javascript_use_explicit_js_extension = global
ij_javascript_use_path_mapping = always
ij_javascript_use_public_modifier = false
ij_javascript_use_semicolon_after_statement = true
ij_javascript_var_declaration_wrap = normal
ij_javascript_while_brace_force = always
ij_javascript_while_on_new_line = false
ij_javascript_wrap_comments = false
[{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}]
indent_style = tab
ij_continuation_indent_size = 4
ij_smart_tabs = true
ij_wrap_on_typing = false
ij_php_align_assignments = false
ij_php_align_class_constants = false
ij_php_align_group_field_declarations = false
ij_php_align_inline_comments = false
ij_php_align_key_value_pairs = false
ij_php_align_multiline_array_initializer_expression = false
ij_php_align_multiline_binary_operation = false
ij_php_align_multiline_chained_methods = false
ij_php_align_multiline_extends_list = false
ij_php_align_multiline_for = true
ij_php_align_multiline_parameters = false
ij_php_align_multiline_parameters_in_calls = false
ij_php_align_multiline_ternary_operation = false
ij_php_align_phpdoc_comments = false
ij_php_align_phpdoc_param_names = false
ij_php_anonymous_brace_style = end_of_line
ij_php_api_weight = 1
ij_php_array_initializer_new_line_after_left_brace = true
ij_php_array_initializer_right_brace_on_new_line = true
ij_php_array_initializer_wrap = on_every_item
ij_php_assignment_wrap = off
ij_php_attributes_wrap = off
ij_php_author_weight = 8
ij_php_binary_operation_sign_on_next_line = false
ij_php_binary_operation_wrap = off
ij_php_blank_lines_after_class_header = 0
ij_php_blank_lines_after_function = 1
ij_php_blank_lines_after_imports = 1
ij_php_blank_lines_after_opening_tag = 0
ij_php_blank_lines_after_package = 1
ij_php_blank_lines_around_class = 1
ij_php_blank_lines_around_constants = 0
ij_php_blank_lines_around_field = 0
ij_php_blank_lines_around_method = 1
ij_php_blank_lines_before_class_end = 0
ij_php_blank_lines_before_imports = 1
ij_php_blank_lines_before_method_body = 0
ij_php_blank_lines_before_package = 1
ij_php_blank_lines_before_return_statement = 1
ij_php_blank_lines_between_imports = 0
ij_php_block_brace_style = end_of_line
ij_php_call_parameters_new_line_after_left_paren = false
ij_php_call_parameters_right_paren_on_new_line = false
ij_php_call_parameters_wrap = normal
ij_php_catch_on_new_line = true
ij_php_category_weight = 28
ij_php_class_brace_style = next_line
ij_php_comma_after_last_array_element = true
ij_php_concat_spaces = false
ij_php_copyright_weight = 28
ij_php_deprecated_weight = 2
ij_php_do_while_brace_force = always
ij_php_else_if_style = as_is
ij_php_else_on_new_line = false
ij_php_example_weight = 4
ij_php_extends_keyword_wrap = off
ij_php_extends_list_wrap = off
ij_php_fields_default_visibility = private
ij_php_filesource_weight = 28
ij_php_finally_on_new_line = true
ij_php_for_brace_force = always
ij_php_for_statement_new_line_after_left_paren = false
ij_php_for_statement_right_paren_on_new_line = false
ij_php_for_statement_wrap = off
ij_php_force_short_declaration_array_style = false
ij_php_getters_setters_naming_style = camel_case
ij_php_getters_setters_order_style = getters_first
ij_php_global_weight = 28
ij_php_group_use_wrap = on_every_item
ij_php_if_brace_force = always
ij_php_if_lparen_on_next_line = false
ij_php_if_rparen_on_next_line = false
ij_php_ignore_weight = 28
ij_php_import_sorting = alphabetic
ij_php_indent_break_from_case = true
ij_php_indent_case_from_switch = true
ij_php_indent_code_in_php_tags = false
ij_php_internal_weight = 0
ij_php_keep_blank_lines_after_lbrace = 2
ij_php_keep_blank_lines_before_right_brace = 2
ij_php_keep_blank_lines_in_code = 2
ij_php_keep_blank_lines_in_declarations = 2
ij_php_keep_control_statement_in_one_line = true
ij_php_keep_first_column_comment = true
ij_php_keep_indents_on_empty_lines = false
ij_php_keep_line_breaks = true
ij_php_keep_rparen_and_lbrace_on_one_line = false
ij_php_keep_simple_classes_in_one_line = false
ij_php_keep_simple_methods_in_one_line = false
ij_php_lambda_brace_style = end_of_line
ij_php_license_weight = 28
ij_php_line_comment_add_space = false
ij_php_line_comment_at_first_column = true
ij_php_link_weight = 28
ij_php_lower_case_boolean_const = true
ij_php_lower_case_keywords = true
ij_php_lower_case_null_const = true
ij_php_method_brace_style = next_line
ij_php_method_call_chain_wrap = off
ij_php_method_parameters_new_line_after_left_paren = true
ij_php_method_parameters_right_paren_on_new_line = true
ij_php_method_parameters_wrap = normal
ij_php_method_weight = 28
ij_php_modifier_list_wrap = false
ij_php_multiline_chained_calls_semicolon_on_new_line = false
ij_php_namespace_brace_style = 1
ij_php_new_line_after_php_opening_tag = false
ij_php_null_type_position = in_the_end
ij_php_package_weight = 28
ij_php_param_weight = 5
ij_php_parameters_attributes_wrap = off
ij_php_parentheses_expression_new_line_after_left_paren = false
ij_php_parentheses_expression_right_paren_on_new_line = false
ij_php_phpdoc_blank_line_before_tags = true
ij_php_phpdoc_blank_lines_around_parameters = true
ij_php_phpdoc_keep_blank_lines = true
ij_php_phpdoc_param_spaces_between_name_and_description = 1
ij_php_phpdoc_param_spaces_between_tag_and_type = 1
ij_php_phpdoc_param_spaces_between_type_and_name = 1
ij_php_phpdoc_use_fqcn = true
ij_php_phpdoc_wrap_long_lines = true
ij_php_place_assignment_sign_on_next_line = false
ij_php_place_parens_for_constructor = 0
ij_php_property_read_weight = 28
ij_php_property_weight = 28
ij_php_property_write_weight = 28
ij_php_return_type_on_new_line = false
ij_php_return_weight = 6
ij_php_see_weight = 3
ij_php_since_weight = 28
ij_php_sort_phpdoc_elements = true
ij_php_space_after_colon = true
ij_php_space_after_colon_in_named_argument = true
ij_php_space_after_colon_in_return_type = true
ij_php_space_after_comma = true
ij_php_space_after_for_semicolon = true
ij_php_space_after_quest = true
ij_php_space_after_type_cast = false
ij_php_space_after_unary_not = false
ij_php_space_before_array_initializer_left_brace = false
ij_php_space_before_catch_keyword = true
ij_php_space_before_catch_left_brace = true
ij_php_space_before_catch_parentheses = true
ij_php_space_before_class_left_brace = true
ij_php_space_before_closure_left_parenthesis = true
ij_php_space_before_colon = true
ij_php_space_before_colon_in_named_argument = false
ij_php_space_before_colon_in_return_type = false
ij_php_space_before_comma = false
ij_php_space_before_do_left_brace = true
ij_php_space_before_else_keyword = true
ij_php_space_before_else_left_brace = true
ij_php_space_before_finally_keyword = true
ij_php_space_before_finally_left_brace = true
ij_php_space_before_for_left_brace = true
ij_php_space_before_for_parentheses = true
ij_php_space_before_for_semicolon = false
ij_php_space_before_if_left_brace = true
ij_php_space_before_if_parentheses = true
ij_php_space_before_method_call_parentheses = false
ij_php_space_before_method_left_brace = true
ij_php_space_before_method_parentheses = false
ij_php_space_before_quest = true
ij_php_space_before_short_closure_left_parenthesis = false
ij_php_space_before_switch_left_brace = true
ij_php_space_before_switch_parentheses = true
ij_php_space_before_try_left_brace = true
ij_php_space_before_unary_not = false
ij_php_space_before_while_keyword = true
ij_php_space_before_while_left_brace = true
ij_php_space_before_while_parentheses = true
ij_php_space_between_ternary_quest_and_colon = false
ij_php_spaces_around_additive_operators = true
ij_php_spaces_around_arrow = false
ij_php_spaces_around_assignment_in_declare = false
ij_php_spaces_around_assignment_operators = true
ij_php_spaces_around_bitwise_operators = true
ij_php_spaces_around_equality_operators = true
ij_php_spaces_around_logical_operators = true
ij_php_spaces_around_multiplicative_operators = true
ij_php_spaces_around_null_coalesce_operator = true
ij_php_spaces_around_relational_operators = true
ij_php_spaces_around_shift_operators = true
ij_php_spaces_around_unary_operator = false
ij_php_spaces_around_var_within_brackets = false
ij_php_spaces_within_array_initializer_braces = false
ij_php_spaces_within_brackets = false
ij_php_spaces_within_catch_parentheses = false
ij_php_spaces_within_for_parentheses = false
ij_php_spaces_within_if_parentheses = false
ij_php_spaces_within_method_call_parentheses = false
ij_php_spaces_within_method_parentheses = false
ij_php_spaces_within_parentheses = false
ij_php_spaces_within_short_echo_tags = true
ij_php_spaces_within_switch_parentheses = false
ij_php_spaces_within_while_parentheses = false
ij_php_special_else_if_treatment = true
ij_php_subpackage_weight = 28
ij_php_ternary_operation_signs_on_next_line = false
ij_php_ternary_operation_wrap = off
ij_php_throws_weight = 7
ij_php_todo_weight = 28
ij_php_unknown_tag_weight = 28
ij_php_upper_case_boolean_const = false
ij_php_upper_case_null_const = false
ij_php_uses_weight = 28
ij_php_var_weight = 28
ij_php_variable_naming_style = mixed
ij_php_version_weight = 28
ij_php_while_brace_force = always
ij_php_while_on_new_line = false
[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,composer.lock,jest.config}]
indent_size = 2
ij_visual_guides = none
ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false
ij_json_keep_line_breaks = true
ij_json_space_after_colon = true
ij_json_space_after_comma = true
ij_json_space_before_colon = true
ij_json_space_before_comma = false
ij_json_spaces_within_braces = false
ij_json_spaces_within_brackets = false
ij_json_wrap_long_lines = false
[{*.htm,*.html,*.sht,*.shtm,*.shtml}]
indent_style = tab
ij_smart_tabs = true
ij_visual_guides = none
ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3
ij_html_align_attributes = true
ij_html_align_text = false
ij_html_attribute_wrap = normal
ij_html_block_comment_at_first_column = true
ij_html_do_not_align_children_of_min_lines = 0
ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot,style,script,head
ij_html_enforce_quotes = false
ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
ij_html_keep_blank_lines = 2
ij_html_keep_indents_on_empty_lines = false
ij_html_keep_line_breaks = true
ij_html_keep_line_breaks_in_text = true
ij_html_keep_whitespaces = false
ij_html_keep_whitespaces_inside = span,pre,textarea
ij_html_line_comment_at_first_column = true
ij_html_new_line_after_last_attribute = never
ij_html_new_line_before_first_attribute = never
ij_html_quote_style = none
ij_html_remove_new_line_before_tags = br
ij_html_space_after_tag_name = false
ij_html_space_around_equality_in_attribute = false
ij_html_space_inside_empty_tag = false
ij_html_text_wrap = normal
ij_html_uniform_ident = false
[{*.yaml, *.yml}]
indent_size = 2
ij_visual_guides = none
ij_yaml_align_values_properties = do_not_align
ij_yaml_autoinsert_sequence_marker = true
ij_yaml_block_mapping_on_new_line = false
ij_yaml_indent_sequence_value = true
ij_yaml_keep_indents_on_empty_lines = false
ij_yaml_keep_line_breaks = true
ij_yaml_sequence_on_new_line = false
ij_yaml_space_before_colon = false
ij_yaml_spaces_within_braces = true
ij_yaml_spaces_within_brackets = true

View File

@@ -1,9 +0,0 @@
[gitflow "branch"]
master = master
develop = develop
[gitflow "prefix"]
feature = feature/
release = release/
hotfix = hotfix/
versiontag =
support = support/

44
.gitignore vendored
View File

@@ -1,38 +1,44 @@
# no slash at the end to handle also symlinks
/toolkit
/conf/*
/env-*/*
/env-*
# composer reserver directory, from sources, populate/update using "composer install"
vendor/*
test/vendor/*
# all conf but listing prevention
/conf/**
!/conf/.htaccess
!/conf/web.config
# all datas but listing prevention
data/*
!data/.htaccess
!data/index.php
!data/web.config
/data/**
!/data/.htaccess
!/data/index.php
!/data/web.config
# iTop extensions
extensions/*
!extensions/readme.txt
/extensions/**
!/extensions/readme.txt
# all logs but listing prevention
log/*
!log/.htaccess
!log/index.php
!log/web.config
/log/**
!/log/.htaccess
!/log/index.php
!/log/web.config
# Jetbrains
.idea/**
!.idea/encodings.xml
!.idea/codeStyles
!.idea/codeStyles/*
!.idea/inspectionProfiles
!.idea/inspectionProfiles/*
/.idea/**
!/.idea/encodings.xml
!/.idea/codeStyles
!/.idea/codeStyles/*
!/.idea/inspectionProfiles
!/.idea/inspectionProfiles/*
#phpdocumentor temp file
ast.dump
# CMake
cmake-build-*/

View File

@@ -1,6 +1,5 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="projectProfile" value="Combodo" />
<option name="PROJECT_PROFILE" value="Combodo" />
<version value="1.0" />
</settings>

View File

@@ -1,14 +0,0 @@
#!/usr/bin/env bash
set -x
# create target dirs
mkdir -p var
mkdir -p toolkit
# cleanup target dirs
rm -rf toolkit/*
# fill target dirs
curl http://www.combodo.com/documentation/iTopDataModelToolkit-2.3.zip | tar xvz --directory toolkit
cp -r .jenkins/configuration/default-environment/unattended_install/* toolkit

View File

@@ -1,11 +0,0 @@
#!/usr/bin/env bash
set -x
# on the root dir
composer install
# under the test dir
cd test
composer install

View File

@@ -1,13 +0,0 @@
#!/usr/bin/env bash
set -x
whoami
pwd
ls
echo "$BRANCH_NAME:${BRANCH_NAME}"
echo "printenv :"
printenv

View File

@@ -1,8 +0,0 @@
#!/usr/bin/env bash
set -x
cd test
export DEBUG_UNIT_TEST="0"
php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml --teamcity

View File

@@ -1,6 +0,0 @@
#!/usr/bin/env bash
set -x
cd toolkit
php unattended_install.php default-params.xml

View File

@@ -1,285 +0,0 @@
<?php
/**
*
* Configuration file, generated by the iTop configuration wizard
*
* The file is used in MetaModel::LoadConfig() which does all the necessary initialization job
*
*/
$MySettings = array(
// access_message: Message displayed to the users when there is any access restriction
// default: 'iTop is temporarily frozen, please wait... (the admin team)'
'access_message' => 'iTop is temporarily frozen, please wait... (the admin team)',
// access_mode: Access mode: ACCESS_READONLY = 0, ACCESS_ADMIN_WRITE = 2, ACCESS_FULL = 3
// default: 3
'access_mode' => 3,
'allowed_login_types' => 'form|basic|external',
// apc_cache.enabled: If set, the APC cache is allowed (the PHP extension must also be active)
// default: true
'apc_cache.enabled' => true,
// apc_cache.query_ttl: Time to live set in APC for the prepared queries (seconds - 0 means no timeout)
// default: 3600
'apc_cache.query_ttl' => 3600,
// app_root_url: Root URL used for navigating within the application, or from an email to the application (you can put $SERVER_NAME$ as a placeholder for the server's name)
// default: ''
'app_root_url' => 'http://127.0.0.1/itop/svn/trunk/',
// buttons_position: Position of the forms buttons: bottom | top | both
// default: 'both'
'buttons_position' => 'both',
// cas_include_path: The path where to find the phpCAS library
// default: '/usr/share/php'
'cas_include_path' => '/usr/share/php',
// cron_max_execution_time: Duration (seconds) of the page cron.php, must be shorter than php setting max_execution_time and shorter than the web server response timeout
// default: 600
'cron_max_execution_time' => 600,
// csv_file_default_charset: Character set used by default for downloading and uploading data as a CSV file. Warning: it is case sensitive (uppercase is preferable).
// default: 'ISO-8859-1'
'csv_file_default_charset' => 'ISO-8859-1',
'csv_import_charsets' => array (
),
// csv_import_history_display: Display the history tab in the import wizard
// default: false
'csv_import_history_display' => false,
// date_and_time_format: Format for date and time display (per language)
// default: array (
// 'default' =>
// array (
// 'date' => 'Y-m-d',
// 'time' => 'H:i:s',
// 'date_time' => '$date $time',
// ),
// )
'date_and_time_format' => array (
'default' =>
array (
'date' => 'Y-m-d',
'time' => 'H:i:s',
'date_time' => '$date $time',
),
'FR FR' =>
array (
'date' => 'd/m/Y',
'time' => 'H:i:s',
'date_time' => '$date $time',
),
),
'db_host' => '',
'db_name' => 'itop_ci_main',
'db_pwd' => 'IKnowYouSeeMeInJenkinsConf',
'db_subname' => '',
'db_user' => 'jenkins_itop',
// deadline_format: The format used for displaying "deadline" attributes: any string with the following placeholders: $date$, $difference$
// default: '$difference$'
'deadline_format' => '$difference$',
'default_language' => 'EN US',
// disable_attachments_download_legacy_portal: Disable attachments download from legacy portal
// default: true
'disable_attachments_download_legacy_portal' => true,
// draft_attachments_lifetime: Lifetime (in seconds) of drafts' attachments and inline images: after this duration, the garbage collector will delete them.
// default: 3600
'draft_attachments_lifetime' => 3600,
// email_asynchronous: If set, the emails are sent off line, which requires cron.php to be activated. Exception: some features like the email test utility will force the serialized mode
// default: false
'email_asynchronous' => false,
// email_default_sender_address: Default address provided in the email from header field.
// default: ''
'email_default_sender_address' => '',
// email_default_sender_label: Default label provided in the email from header field.
// default: ''
'email_default_sender_label' => '',
// email_transport: Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocole)
// default: 'PHPMail'
'email_transport' => 'SMTP',
// email_transport_smtp.host: host name or IP address (optional)
// default: 'localhost'
'email_transport_smtp.host' => 'smtp.combodo.com',
// email_transport_smtp.password: Authentication password (optional)
// default: ''
'email_transport_smtp.password' => 'IDoNotWork',
// email_transport_smtp.port: port number (optional)
// default: 25
'email_transport_smtp.port' => 25,
// email_transport_smtp.username: Authentication user (optional)
// default: ''
'email_transport_smtp.username' => 'test2@combodo.com',
// email_validation_pattern: Regular expression to validate/detect the format of an eMail address
// default: '[a-zA-Z0-9._&\'-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9-]{2,}'
'email_validation_pattern' => '[a-zA-Z0-9._&\'-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9-]{2,}',
'encryption_key' => '@iT0pEncr1pti0n!',
'ext_auth_variable' => '$_SERVER[\'REMOTE_USER\']',
'fast_reload_interval' => '60',
// graphviz_path: Path to the Graphviz "dot" executable for graphing objects lifecycle
// default: '/usr/bin/dot'
'graphviz_path' => '/usr/bin/dot',
// inline_image_max_display_width: The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.
// default: '250'
'inline_image_max_display_width' => 250,
// inline_image_max_storage_width: The maximum width (in pixels) when uploading images to be used inside an HTML formatted attribute. Images larger than the given size will be downsampled before storing them in the database.
// default: '1600'
'inline_image_max_storage_width' => 1600,
// link_set_attribute_qualifier: Link set from string: attribute qualifier (encloses both the attcode and the value)
// default: '\''
'link_set_attribute_qualifier' => '\'',
// link_set_attribute_separator: Link set from string: attribute separator
// default: ';'
'link_set_attribute_separator' => ';',
// link_set_item_separator: Link set from string: line separator
// default: '|'
'link_set_item_separator' => '|',
// link_set_value_separator: Link set from string: value separator (between the attcode and the value itself
// default: ':'
'link_set_value_separator' => ':',
'log_global' => true,
'log_issue' => true,
'log_notification' => true,
'log_web_service' => true,
// max_combo_length: The maximum number of elements in a drop-down list. If more then an autocomplete will be used
// default: 50
'max_combo_length' => 50,
'max_display_limit' => '15',
// max_linkset_output: Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.
// default: 100
'max_linkset_output' => 100,
'min_display_limit' => '10',
// online_help: Hyperlink to the online-help web page
// default: 'http://www.combodo.com/itop-help'
'online_help' => 'http://www.combodo.com/itop-help',
// php_path: Path to the php executable in CLI mode
// default: 'php'
'php_path' => 'php',
// portal_tickets: CSV list of classes supported in the portal
// default: 'UserRequest'
'portal_tickets' => 'UserRequest',
'query_cache_enabled' => true,
// search_manual_submit: Force manual submit of search requests (class => true)
// default: false
'search_manual_submit' => array (
'Person' => true,
),
'secure_connection_required' => false,
// session_name: The name of the cookie used to store the PHP session id
// default: 'iTop'
'session_name' => 'iTop',
// shortcut_actions: Actions that are available as direct buttons next to the "Actions" menu
// default: 'UI:Menu:Modify,UI:Menu:New'
'shortcut_actions' => 'UI:Menu:Modify,UI:Menu:New',
// source_dir: Source directory for the datamodel files. (which gets compiled to env-production).
// default: ''
'source_dir' => 'datamodels/2.x/',
'standard_reload_interval' => '300',
// synchro_trace: Synchronization details: none, display, save (includes 'display')
// default: 'none'
'synchro_trace' => 'none',
// timezone: Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitely configured in PHP
// default: 'Europe/Paris'
'timezone' => 'Europe/Paris',
// tracking_level_linked_set_default: Default tracking level if not explicitely set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)
// default: 1
'tracking_level_linked_set_default' => 0,
// url_validation_pattern: Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)
// default: '(https?|ftp)\\://([a-zA-Z0-9+!*(),;?&=\\$_.-]+(\\:[a-zA-Z0-9+!*(),;?&=\\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\\:[0-9]{2,5})?(/([a-zA-Z0-9%+\\$_-]\\.?)+)*/?(\\?[a-zA-Z+&\\$_.-][a-zA-Z0-9;:[\\]@&%=+/\\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\\$_.-]*)?'
'url_validation_pattern' => '(https?|ftp)\\://([a-zA-Z0-9+!*(),;?&=\\$_.-]+(\\:[a-zA-Z0-9+!*(),;?&=\\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\\:[0-9]{2,5})?(/([a-zA-Z0-9%+\\$_-]\\.?)+)*/?(\\?[a-zA-Z+&\\$_.-][a-zA-Z0-9;:[\\]@&%=+/\\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\\$_.-]*)?',
);
/**
*
* Modules specific settings
*
*/
$MyModuleSettings = array(
'itop-attachments' => array (
'allowed_classes' => array (
0 => 'Ticket',
),
'position' => 'relations',
'preview_max_width' => 290,
),
'itop-backup' => array (
'mysql_bindir' => '',
'week_days' => 'monday, tuesday, wednesday, thursday, friday',
'time' => '23:30',
'retention_count' => 5,
'enabled' => true,
'debug' => false,
),
'molkobain-console-tooltips' => array (
'decoration_class' => 'fas fa-question',
'enabled' => true,
),
);
/**
*
* Data model modules to be loaded. Names are specified as relative paths
*
*/
$MyModules = array(
'addons' => array (
'user rights' => 'addons/userrights/userrightsprofile.class.inc.php',
),
);
?>

View File

@@ -1,190 +0,0 @@
<?php
//this scrit will be run under the ./toolkit directory, relatively to the document root
require_once('../approot.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/application/clipage.class.inc.php');
require_once(APPROOT.'/core/config.class.inc.php');
require_once(APPROOT.'/core/log.class.inc.php');
require_once(APPROOT.'/core/kpi.class.inc.php');
require_once(APPROOT.'/core/cmdbsource.class.inc.php');
require_once(APPROOT.'/setup/setuppage.class.inc.php');
require_once(APPROOT.'/setup/wizardcontroller.class.inc.php');
require_once(APPROOT.'/setup/wizardsteps.class.inc.php');
require_once(APPROOT.'/setup/applicationinstaller.class.inc.php');
/////////////////////////////////////////////////
$sParamFile = utils::ReadParam('response_file', 'default-params.xml', true /* CLI allowed */, 'raw_data');
$bCheckConsistency = (utils::ReadParam('check_consistency', '0', true /* CLI allowed */) == '1');
$oParams = new XMLParameters($sParamFile);
$sMode = $oParams->Get('mode');
if ($sMode == 'install')
{
echo "Installation mode detected.\n";
$bClean = utils::ReadParam('clean', false, true /* CLI allowed */);
if ($bClean)
{
echo "Cleanup mode detected.\n";
$sTargetEnvironment = $oParams->Get('target_env', '');
if ($sTargetEnvironment == '')
{
$sTargetEnvironment = 'production';
}
$sTargetDir = APPROOT.'env-'.$sTargetEnvironment;
// Configuration file
$sConfigFile = APPCONF.$sTargetEnvironment.'/'.ITOP_CONFIG_FILE;
if (file_exists($sConfigFile))
{
echo "Trying to delete the configuration file: '$sConfigFile'.\n";
@chmod($sConfigFile, 0770); // RWX for owner and group, nothing for others
unlink($sConfigFile);
}
else
{
echo "No config file to delete ($sConfigFile does not exist).\n";
}
// env-xxx directory
if (file_exists($sTargetDir))
{
if (is_dir($sTargetDir))
{
echo "Emptying the target directory '$sTargetDir'.\n";
SetupUtils::tidydir($sTargetDir);
}
else
{
die("ERROR the target dir '$sTargetDir' exists, but is NOT a directory !!!\nExiting.\n");
}
}
else
{
echo "No target directory to delete ($sTargetDir does not exist).\n";
}
// Database
$aDBSettings = $oParams->Get('database', array());
$sDBServer = $aDBSettings['server'];
$sDBUser = $aDBSettings['user'];
$sDBPwd = $aDBSettings['pwd'];
$sDBName = $aDBSettings['name'];
$sDBPrefix = $aDBSettings['prefix'];
if ($sDBPrefix != '')
{
die("Cleanup not implemented for a partial database (prefix= '$sDBPrefix')\nExiting.");
}
$oMysqli = new mysqli($sDBServer, $sDBUser, $sDBPwd);
if ($oMysqli->connect_errno)
{
die("Cannot connect to the MySQL server (".$mysqli->connect_errno . ") ".$mysqli->connect_error."\nExiting");
}
else
{
if ($oMysqli->select_db($sDBName))
{
echo "Deleting database '$sDBName'\n";
$oMysqli->query("DROP DATABASE `$sDBName`");
}
else
{
echo "The database '$sDBName' does not seem to exist. Nothing to cleanup.\n";
}
}
}
}
$bHasErrors = false;
$aChecks = SetupUtils::CheckBackupPrerequisites(APPROOT.'data'); // mmm should be the backup destination dir
$aSelectedModules = $oParams->Get('selected_modules');
$sSourceDir = $oParams->Get('source_dir', 'datamodels/latest');
$sExtensionDir = $oParams->Get('extensions_dir', 'extensions');
$aChecks = array_merge($aChecks, SetupUtils::CheckSelectedModules($sSourceDir, $sExtensionDir, $aSelectedModules));
foreach($aChecks as $oCheckResult)
{
switch($oCheckResult->iSeverity)
{
case CheckResult::ERROR:
$bHasErrors = true;
$sHeader = "Error";
break;
case CheckResult::WARNING:
$sHeader = "Warning";
break;
case CheckResult::INFO:
default:
$sHeader = "Info";
break;
}
echo $sHeader.": ".$oCheckResult->sLabel;
if (strlen($oCheckResult->sDescription))
{
echo ' - '.$oCheckResult->sDescription;
}
echo "\n";
}
if ($bHasErrors)
{
echo "Encountered stopper issues. Aborting...\n";
die;
}
$bFoundIssues = false;
$bInstall = utils::ReadParam('install', true, true /* CLI allowed */);
if ($bInstall)
{
echo "Starting the unattended installation...\n";
$oWizard = new ApplicationInstaller($oParams);
$bRes = $oWizard->ExecuteAllSteps();
if (!$bRes)
{
echo "\nencountered installation issues!";
$bFoundIssues = true;
}
}
else
{
echo "No installation requested.\n";
}
if (!$bFoundIssues && $bCheckConsistency)
{
echo "Checking data model consistency.\n";
ob_start();
$sCheckRes = '';
try
{
MetaModel::CheckDefinitions(false);
$sCheckRes = ob_get_clean();
}
catch(Exception $e)
{
$sCheckRes = ob_get_clean()."\nException: ".$e->getMessage();
}
if (strlen($sCheckRes) > 0)
{
echo $sCheckRes;
echo "\nfound consistency issues!";
$bFoundIssues = true;
}
}
if (!$bFoundIssues)
{
// last line: used to check the install
// the only way to track issues in case of Fatal error or even parsing error!
echo "\ninstalled!";
exit;
}

View File

@@ -0,0 +1,47 @@
<?php
/*******************************************************************************
* Tool to automate version update before release
*
* Will update version in the following files :
*
* * datamodels/2.x/.../module.*.php
* * datamodels/2.x/version.xml
* * css/css-variables.scss $version
*
* Usage :
* `php .make\release\update-versions.php "2.7.0-rc"`
*
* @since 2.7.0
******************************************************************************/
require_once (__DIR__.'/../../approot.inc.php');
require_once (__DIR__.DIRECTORY_SEPARATOR.'update.classes.inc.php');
/** @var \FileVersionUpdater[] $aFilesUpdaters */
$aFilesUpdaters = array(
new iTopVersionFileUpdater(),
new CssVariablesFileUpdater(),
new DatamodelsModulesFiles(),
);
if (count($argv) === 1)
{
echo '/!\ You must pass the new version as parameter';
exit(1);
}
$sVersionLabel = $argv[1];
if (empty($sVersionLabel))
{
echo 'Version passed as parameter is empty !';
exit(2);
}
foreach ($aFilesUpdaters as $oFileVersionUpdater)
{
$oFileVersionUpdater->UpdateAllFiles($sVersionLabel);
}

View File

@@ -0,0 +1,36 @@
<?php
/*******************************************************************************
* Tool to automate datamodel version update in XML
*
* Will update version in the following files :
*
* datamodels/2.x/.../datamodel.*.xml
*
* Usage :
* `php .make\release\update-xml.php "1.7"`
*
* @since 2.7.0
******************************************************************************/
require_once (__DIR__.'/../../approot.inc.php');
require_once (__DIR__.DIRECTORY_SEPARATOR.'update.classes.inc.php');
if (count($argv) === 1)
{
echo '/!\ You must pass the new version as parameter';
exit(1);
}
$sVersionLabel = $argv[1];
if (empty($sVersionLabel))
{
echo 'Version passed as parameter is empty !';
exit(2);
}
$oFileVersionUpdater = new DatamodelsXmlFiles();
$oFileVersionUpdater->UpdateAllFiles($sVersionLabel);

View File

@@ -0,0 +1,169 @@
<?php
/*******************************************************************************
* Classes for updater tools
*
* @see update-versions.php
* @see update-xml.php
******************************************************************************/
require_once (__DIR__.'/../../approot.inc.php');
abstract class FileVersionUpdater
{
/**
* @return string[] full path of files to modify
*/
abstract public function GetFiles();
/**
* Warnign : will consume lots of memory on larger files !
*
* @param string $sVersionLabel
* @param string $sFileContent
* @param string $sFileFullPath
*
* @return string file content with replaced values
*/
abstract public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath);
public function UpdateAllFiles($sVersionLabel)
{
$aFilesToUpdate = $this->GetFiles();
$sFileUpdaterName = get_class($this);
echo "# Updater : $sFileUpdaterName\n";
foreach ($aFilesToUpdate as $sFileToUpdateFullPath)
{
try
{
$sCurrentFileContent = file_get_contents($sFileToUpdateFullPath);
$sNewFileContent = $this->UpdateFileContent($sVersionLabel, $sCurrentFileContent, $sFileToUpdateFullPath);
file_put_contents($sFileToUpdateFullPath, $sNewFileContent);
echo " - $sFileToUpdateFullPath : OK !\n";
}
catch (Exception $e)
{
echo " - $sFileToUpdateFullPath : Error :(\n";
}
}
}
}
abstract class AbstractSingleFileVersionUpdater extends FileVersionUpdater
{
private $sFileToUpdate;
public function __construct($sFileToUpdate)
{
$this->sFileToUpdate = $sFileToUpdate;
}
public function GetFiles()
{
return array(APPROOT.$this->sFileToUpdate);
}
}
class iTopVersionFileUpdater extends AbstractSingleFileVersionUpdater
{
public function __construct()
{
parent::__construct('datamodels/2.x/version.xml');
}
/**
* @inheritDoc
*/
public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath)
{
return preg_replace(
'/(<version>)[^<]*(<\/version>)/',
'${1}'.$sVersionLabel.'${2}',
$sFileContent
);
}
}
class CssVariablesFileUpdater extends AbstractSingleFileVersionUpdater
{
public function __construct()
{
parent::__construct('css/css-variables.scss');
}
/**
* @inheritDoc
*/
public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath)
{
return preg_replace(
'/(\$version: "v)[^"]*(";)/',
'${1}'.$sVersionLabel.'${2}',
$sFileContent
);
}
}
abstract class AbstractGlobFileVersionUpdater extends FileVersionUpdater
{
protected $sGlobPattern;
public function __construct($sGlobPattern)
{
$this->sGlobPattern = $sGlobPattern;
}
public function GetFiles()
{
return glob($this->sGlobPattern);
}
}
class DatamodelsModulesFiles extends AbstractGlobFileVersionUpdater
{
public function __construct()
{
parent::__construct(APPROOT.'datamodels/2.x/*/module.*.php');
}
/**
* @inheritDoc
*/
public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath)
{
$sModulePath = realpath($sFileFullPath);
$sModuleFileName = basename($sModulePath, 1);
$sModuleName = preg_replace('/[^.]+\.([^.]+)\.php/', '$1', $sModuleFileName);
return preg_replace(
"/('$sModuleName\/)[^']+(')/",
'${1}'.$sVersionLabel.'${2}',
$sFileContent
);
}
}
class DatamodelsXmlFiles extends AbstractGlobFileVersionUpdater
{
public function __construct()
{
parent::__construct(APPROOT.'datamodels/2.x/*/datamodel.*.xml');
}
/**
* @inheritDoc
*/
public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath)
{
return preg_replace(
'/(<itop_design .* version=")[^"]+(">)/',
'${1}'.$sVersionLabel.'${2}',
$sFileContent
);
}
}

70
Jenkinsfile vendored
View File

@@ -1,65 +1,11 @@
pipeline {
agent any
stages {
def infra
stage('init') {
parallel {
stage('debug') {
steps {
sh './.jenkins/bin/init/debug.sh'
}
}
stage('append files to project') {
steps {
sh './.jenkins/bin/init/append_files.sh'
}
}
stage('composer install') {
steps {
sh './.jenkins/bin/init/composer_install.sh'
}
}
}
}
node(){
checkout scm
stage('unattended_install') {
parallel {
stage('unattended_install default env') {
steps {
sh './.jenkins/bin/unattended_install/default_env.sh'
}
}
}
}
stage('test') {
parallel {
stage('phpunit') {
steps {
sh './.jenkins/bin/tests/phpunit.sh'
}
}
}
}
}
post {
always {
junit 'var/test/phpunit-log.junit.xml'
}
failure {
slackSend(channel: "#jenkins-itop", color: '#FF0000', message: "Ho no! Build failed! (${currentBuild.result}), Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
fixed {
slackSend(channel: "#jenkins-itop", color: '#FFa500', message: "Yes! Build repaired! (${currentBuild.result}), Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
}
environment {
DEBUG_UNIT_TEST = '0'
}
options {
timeout(time: 20, unit: 'MINUTES')
}
infra = load '/var/lib/jenkins/workspace/itop-test-infra_master/src/Infra.groovy'
}
infra.call()

View File

@@ -21,6 +21,19 @@ iTop also offers mass import tools and web services to integrate with your IT
- [Data synchronization][18] (for data federation)
## Latest release
- [Changes since the previous version][62]
- [New features][63]
- [Installation notes][64]
- [Download][65]
[62]: https://www.itophub.io/wiki/page?id=latest:release:change_log
[63]: https://www.itophub.io/wiki/page?id=latest:release:start
[64]: https://www.itophub.io/wiki/page?id=latest:install:start
[65]: https://sourceforge.net/projects/itop/files/latest/download
## Resources
- [iTop Forums][1]: for support request
@@ -30,28 +43,6 @@ iTop also offers mass import tools and web services to integrate with your IT
- [iTop extensions][5] for discovering and installing extensions
## Releases
### Version 2.6
- [Changes since the previous version][58]
- [New features][59]
- [Migration notes][60]
- [Download iTop 2.6.0][61]
### Version 2.5
- [Changes since the previous version][54]
- [New features][55]
- [Migration notes][56]
- [Download iTop 2.5.1][57]
### Version 2.4
- [Changes since the previous version][50]
- [New features][51]
- [Migration notes][52]
- [Download iTop 2.4.1][53]
# About Us
iTop development is sponsored, led and supported by [Combodo][0].
@@ -74,6 +65,7 @@ We would like to give a special thank you to the people from the community who c
- Gumble, David
- Hippler, Lars
- Khamit, Shamil
- Kincel, Martin
- Konečný, Kamil
- Kunin, Vladimir
- Lassiter, Dennis
@@ -137,4 +129,4 @@ We would like to give a special thank you to the people from the community who c
[58]: https://www.itophub.io/wiki/page?id=2_6_0:release:change_log
[59]: https://www.itophub.io/wiki/page?id=2_6_0:release:2_6_whats_new
[60]: https://www.itophub.io/wiki/page?id=2_6_0:install:250_to_260_migration_notes
[61]: https://sourceforge.net/projects/itop/files/itop/2.6.0
[61]: https://sourceforge.net/projects/itop/files/itop/2.6.3

View File

@@ -30,32 +30,35 @@ class ajax_page extends WebPage implements iTabbedPage
{
/**
* Jquery style ready script
* @var Hash
*/
* @var Hash
*/
protected $m_sReadyScript;
protected $m_oTabs;
private $m_sMenu; // If set, then the menu will be updated
/**
* constructor for the web page
* @param string $s_title Not used
*/
function __construct($s_title)
{
/**
* constructor for the web page
*
* @param string $s_title Not used
*/
function __construct($s_title) {
$sPrintable = utils::ReadParam('printable', '0');
$bPrintable = ($sPrintable == '1');
parent::__construct($s_title, $bPrintable);
$this->m_sReadyScript = "";
parent::__construct($s_title, $bPrintable);
$this->m_sReadyScript = "";
//$this->add_header("Content-type: text/html; charset=utf-8");
$this->add_header("Cache-control: no-cache");
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
$this->add_header('Pragma: no-cache');
$this->add_header('Expires: 0');
$this->add_header('X-Frame-Options: deny');
$this->m_oTabs = new TabManager();
$this->sContentType = 'text/html';
$this->sContentDisposition = 'inline';
$this->m_sMenu = "";
utils::InitArchiveMode();
}
}
public function AddTabContainer($sTabContainer, $sPrefix = '')
{

View File

@@ -365,7 +365,7 @@ interface iPopupMenuExtension
* Base class for the various types of custom menus
*
* @package Extensibility
* @internal
* @api
* @since 2.0
*/
abstract class ApplicationPopupMenuItem
@@ -378,8 +378,10 @@ abstract class ApplicationPopupMenuItem
protected $aCssClasses;
/**
* Constructor
*
* Constructor
*
* @api
*
* @param string $sUID The unique identifier of this menu in iTop... make sure you pass something unique enough
* @param string $sLabel The display label of the menu (must be localized)
* @param array $aCssClasses The CSS classes to add to the menu
@@ -509,6 +511,9 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
/**
* Class for adding an item that triggers some Javascript code
*
* @api
*
* @param string $sUID The unique identifier of this menu in iTop... make sure you pass something unique enough
* @param string $sLabel The display label of the menu (must be localized)
* @param string $sJSCode In case the menu consists in executing some havascript code inside the page, pass it here. If supplied $sURL ans $sTarget will be ignored
@@ -825,6 +830,10 @@ class RestResult
* Result: the requested operation cannot be performed because it can cause data (integrity) loss
*/
const UNSAFE = 12;
/**
* Result: the request page number is not valid. It must be an integer greater than 0
*/
const INVALID_PAGE = 13;
/**
* Result: the operation could not be performed, see the message for troubleshooting
*/
@@ -1091,10 +1100,13 @@ class RestUtils
*
* @param string $sClass Name of the class
* @param mixed $key Either search criteria (substructure), or an object or an OQL string.
* @param int $iLimit The limit of results to return
* @param int $iOffset The offset of results to return
*
* @return DBObjectSet The search result set
* @throws Exception If the input structure is not valid
*/
public static function GetObjectSetFromKey($sClass, $key)
public static function GetObjectSetFromKey($sClass, $key, $iLimit = 0, $iOffset = 0)
{
if (is_object($key))
{
@@ -1128,7 +1140,7 @@ class RestUtils
{
throw new Exception("Wrong format for key");
}
$oObjectSet = new DBObjectSet($oSearch);
$oObjectSet = new DBObjectSet($oSearch, array(), array(), null, $iLimit, $iOffset);
return $oObjectSet;
}

View File

@@ -385,6 +385,7 @@ EOF
if (!isset($aExtraParams['disable_plugins']) || !$aExtraParams['disable_plugins'])
{
/** @var iApplicationUIExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$oExtensionInstance->OnDisplayProperties($this, $oPage, $bEditMode);
@@ -648,6 +649,7 @@ EOF
}
$oPage->SetCurrentTab('');
/** @var \iApplicationUIExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$oExtensionInstance->OnDisplayRelations($this, $oPage, $bEditMode);
@@ -908,7 +910,7 @@ EOF
/**
* @param \iTopWebPage $oPage
* @param \WebPage $oPage
* @param bool $bEditMode
*
* @throws \CoreException
@@ -918,8 +920,9 @@ EOF
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @throws \Exception
*/
function DisplayDetails(WebPage $oPage, $bEditMode = false)
public function DisplayDetails(WebPage $oPage, $bEditMode = false)
{
$sTemplate = Utils::ReadFromFile(MetaModel::GetDisplayTemplate(get_class($this)));
if (!empty($sTemplate))
@@ -941,6 +944,7 @@ EOF
// template not found display the object using the *old style*
$oPage->add('<div id="search-widget-results-outer">');
$this->DisplayBareHeader($oPage, $bEditMode);
/** @var \iTopWebPage $oPage */
$oPage->AddTabContainer(OBJECT_PROPERTIES_TAB);
$oPage->SetCurrentTabContainer(OBJECT_PROPERTIES_TAB);
$oPage->SetCurrentTab(Dict::S('UI:PropertiesTab'));
@@ -2101,7 +2105,7 @@ EOF
$('#{$iId}_console_form').console_form_handler('alignColumns');
$('#{$iId}_console_form').console_form_handler('option', 'field_set', $('#{$iId}_field_set'));
// field_change must be processed to refresh the hidden value at anytime
$('#{$iId}_console_form').bind('value_change', function() { $('#{$iId}').val(JSON.stringify($('#{$iId}_field_set').triggerHandler('get_current_values'))); console.error($('#{$iId}').val()); });
$('#{$iId}_console_form').bind('value_change', function() { $('#{$iId}').val(JSON.stringify($('#{$iId}_field_set').triggerHandler('get_current_values'))); });
// Initialize the hidden value with current state
// update_value is triggered when preparing the wizard helper object for ajax calls
$('#{$iId}').bind('update_value', function() { $(this).val(JSON.stringify($('#{$iId}_field_set').triggerHandler('get_current_values'))); });
@@ -2174,6 +2178,14 @@ EOF
if ((count($aAllowedValues) == 1) && ($bMandatory == 'true'))
{
// When there is only once choice, select it by default
if($value != $key)
{
$oPage->add_ready_script(
<<<EOF
$('#$iId').attr('data-validate','dependencies');
EOF
);
}
$sSelected = ' selected';
}
else
@@ -2954,7 +2966,6 @@ EOF
$data = $oDoc->GetData();
switch ($oDoc->GetMimeType())
{
case 'text/html':
case 'text/xml':
$oPage->add("<iframe id='preview_$sAttCode' src=\"".utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=display_document&class=$sClass&id=$Id&field=$sAttCode\" width=\"100%\" height=\"400\">Loading...</iframe>\n");
break;
@@ -3032,6 +3043,7 @@ EOF
$current = parent::GetHilightClass(); // Default computation
// Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information
/** @var \iApplicationUIExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$new = $oExtensionInstance->GetHilightClass($this);
@@ -3453,6 +3465,7 @@ EOF
}
// Invoke extensions after the update of the object from the form
/** @var \iApplicationUIExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$oExtensionInstance->OnFormSubmit($this, $sFormPrefix);
@@ -3524,21 +3537,13 @@ EOF
{
if (preg_match("/^attr_$sSubFormPrefix(.*)$/", $sKey, $aMatches))
{
$sLinkClass = $oAttDef->GetLinkedClass();
if ($oAttDef->IsIndirect())
$oLinkAttDef = MetaModel::GetAttributeDef($sObjClass, $aMatches[1]);
// Recursing over n:n link datetime attributes
// Note: We might need to do it with other attribute types, like Document or redundancy setting.
if ($oLinkAttDef instanceof AttributeDateTime)
{
$oLinkAttDef = MetaModel::GetAttributeDef($sLinkClass, $aMatches[1]);
// Recursing over n:n link datetime attributes
// Note: We might need to do it with other attribute types, like Document or redundancy setting.
if ($oLinkAttDef instanceof AttributeDateTime)
{
$aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix,
$aMatches[1], $sLinkClass, $aData);
}
else
{
$aObjData[$aMatches[1]] = $value;
}
$aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix,
$aMatches[1], $sObjClass, $aData);
}
else
{
@@ -3555,26 +3560,19 @@ EOF
foreach($aRawToBeModified as $iObjKey => $aData)
{
$sSubFormPrefix = $aData['formPrefix'];
$sObjClass = isset($aData['class']) ? $aData['class'] : $oAttDef->GetLinkedClass();
$aObjData = array();
foreach($aData as $sKey => $value)
{
if (preg_match("/^attr_$sSubFormPrefix(.*)$/", $sKey, $aMatches))
{
$sLinkClass = $oAttDef->GetLinkedClass();
if ($oAttDef->IsIndirect())
$oLinkAttDef = MetaModel::GetAttributeDef($sObjClass, $aMatches[1]);
// Recursing over n:n link datetime attributes
// Note: We might need to do it with other attribute types, like Document or redundancy setting.
if ($oLinkAttDef instanceof AttributeDateTime)
{
$oLinkAttDef = MetaModel::GetAttributeDef($sLinkClass, $aMatches[1]);
// Recursing over n:n link datetime attributes
// Note: We might need to do it with other attribute types, like Document or redundancy setting.
if ($oLinkAttDef instanceof AttributeDateTime)
{
$aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix,
$aMatches[1], $sLinkClass, $aData);
}
else
{
$aObjData[$aMatches[1]] = $value;
}
$aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix,
$aMatches[1], $sObjClass, $aData);
}
else
{
@@ -4221,6 +4219,7 @@ EOF
$oDummyObj->Set($sAttCode, $currValue);
/** @var ormTagSet $oTagSet */
$oTagSet = $oDummyObj->Get($sAttCode);
$oTagSet->SetDisplayPartial(true);
foreach($aKeys as $iIndex => $sValues)
{
if ($iIndex == 0)

View File

@@ -29,13 +29,15 @@ require_once(APPROOT."/application/webpage.class.inc.php");
class CSVPage extends WebPage
{
function __construct($s_title)
{
parent::__construct($s_title);
function __construct($s_title) {
parent::__construct($s_title);
$this->add_header("Content-type: text/plain; charset=utf-8");
$this->add_header("Cache-control: no-cache");
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
$this->add_header('Pragma: no-cache');
$this->add_header('Expires: 0');
$this->add_header('X-Frame-Options: deny');
//$this->add_header("Content-Transfer-Encoding: binary");
}
}
public function output()
{

View File

@@ -674,28 +674,29 @@ class RuntimeDashboard extends Dashboard
{
$bCustomized = false;
if (!appUserPreferences::GetPref('display_original_dashboard_'.$sDashBoardId, false))
{
$sDashboardFileSanitized = utils::RealPath($sDashboardFile, APPROOT);
if (false === $sDashboardFileSanitized) {
throw new SecurityException('Invalid dashboard file !');
}
if (!appUserPreferences::GetPref('display_original_dashboard_'.$sDashBoardId, false)) {
// Search for an eventual user defined dashboard
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oUDSearch->AddCondition('menu_code', $sDashBoardId, '=');
$oUDSet = new DBObjectSet($oUDSearch);
if ($oUDSet->Count() > 0)
{
if ($oUDSet->Count() > 0) {
// Assuming there is at most one couple {user, menu}!
$oUserDashboard = $oUDSet->Fetch();
$sDashboardDefinition = $oUserDashboard->Get('contents');
$bCustomized = true;
}
else
{
$sDashboardDefinition = @file_get_contents($sDashboardFile);
} else {
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
}
}
else
{
$sDashboardDefinition = @file_get_contents($sDashboardFile);
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
}
if ($sDashboardDefinition !== false)
@@ -703,7 +704,7 @@ class RuntimeDashboard extends Dashboard
$oDashboard = new RuntimeDashboard($sDashBoardId);
$oDashboard->FromXml($sDashboardDefinition);
$oDashboard->SetCustomFlag($bCustomized);
$oDashboard->SetDefinitionFile($sDashboardFile);
$oDashboard->SetDefinitionFile($sDashboardFileSanitized);
}
else
{

View File

@@ -444,6 +444,18 @@ EOF
$sClass = $oQuery->GetClass();
foreach($this->oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType)
{
// For external fields, find the real type of the target
$sExtFieldAttCode = $sAttCode;
$sTargetClass = $sClass;
while (is_a($sAttType, 'AttributeExternalField', true))
{
$sExtKeyAttCode = $this->oModelReflection->GetAttributeProperty($sTargetClass, $sExtFieldAttCode, 'extkey_attcode');
$sTargetAttCode = $this->oModelReflection->GetAttributeProperty($sTargetClass, $sExtFieldAttCode, 'target_attcode');
$sTargetClass = $this->oModelReflection->GetAttributeProperty($sTargetClass, $sExtKeyAttCode, 'targetclass');
$aTargetAttCodes = $this->oModelReflection->ListAttributes($sTargetClass);
$sAttType = $aTargetAttCodes[$sTargetAttCode];
$sExtFieldAttCode = $sTargetAttCode;
}
if (is_a($sAttType, 'AttributeLinkedSet', true))
{
continue;
@@ -456,15 +468,6 @@ EOF
{
continue;
}
// For external fields, find the real type of the target
while (is_a($sAttType, 'AttributeExternalField', true))
{
$sExtKeyAttCode = $this->oModelReflection->GetAttributeProperty($sClass, $sAttCode, 'extkey_attcode');
$sTargetClass = $this->oModelReflection->GetAttributeProperty($sClass, $sExtKeyAttCode, 'targetclass');
$sTargetAttCode = $this->oModelReflection->GetAttributeProperty($sClass, $sAttCode, 'target_attcode');
$aTargetAttCodes = $this->oModelReflection->ListAttributes($sTargetClass);
$sAttType = $aTargetAttCodes[$sTargetAttCode];
}
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
if (!in_array($sLabel, $aGroupBy))
@@ -610,12 +613,12 @@ class DashletUnknown extends Dashlet
{
$aInfos = static::GetInfo();
$sIconUrl = utils::GetAbsoluteUrlAppRoot().$aInfos['icon'];
$sIconUrl = utils::HtmlEntities(utils::GetAbsoluteUrlAppRoot().$aInfos['icon']);
$sExplainText = ($bEditMode) ? Dict::Format('UI:DashletUnknown:RenderText:Edit', $this->GetDashletType()) : Dict::S('UI:DashletUnknown:RenderText:View');
$oPage->add('<div class="dashlet-content">');
$oPage->add('<div class="dashlet-ukn-image"><img src="'.utils::HtmlEntities($sIconUrl).'" /></div>');
$oPage->add('<div class="dashlet-ukn-image"><img src="'.$sIconUrl.'" /></div>');
$oPage->add('<div class="dashlet-ukn-text">'.$sExplainText.'</div>');
$oPage->add('</div>');
@@ -630,12 +633,12 @@ class DashletUnknown extends Dashlet
{
$aInfos = static::GetInfo();
$sIconUrl = utils::GetAbsoluteUrlAppRoot().$aInfos['icon'];
$sIconUrl = utils::HtmlEntities(utils::GetAbsoluteUrlAppRoot().$aInfos['icon']);
$sExplainText = Dict::Format('UI:DashletUnknown:RenderNoDataText:Edit', $this->GetDashletType());
$oPage->add('<div class="dashlet-content">');
$oPage->add('<div class="dashlet-ukn-image"><img src="'.utils::HtmlEntities($sIconUrl).'" /></div>');
$oPage->add('<div class="dashlet-ukn-image"><img src="'.$sIconUrl.'" /></div>');
$oPage->add('<div class="dashlet-ukn-text">'.$sExplainText.'</div>');
$oPage->add('</div>');
@@ -771,12 +774,12 @@ class DashletProxy extends DashletUnknown
{
$aInfos = static::GetInfo();
$sIconUrl = utils::GetAbsoluteUrlAppRoot().$aInfos['icon'];
$sIconUrl = utils::HtmlEntities(utils::GetAbsoluteUrlAppRoot().$aInfos['icon']);
$sExplainText = Dict::Format('UI:DashletProxy:RenderNoDataText:Edit', $this->GetDashletType());
$oPage->add('<div class="dashlet-content">');
$oPage->add('<div class="dashlet-pxy-image"><img src="'.utils::HtmlEntities($sIconUrl).'" /></div>');
$oPage->add('<div class="dashlet-pxy-image"><img src="'.$sIconUrl.'" /></div>');
$oPage->add('<div class="dashlet-pxy-text">'.$sExplainText.'</div>');
$oPage->add('</div>');
@@ -857,7 +860,7 @@ class DashletPlainText extends Dashlet
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
$sText = htmlentities($this->aProperties['text'], ENT_QUOTES, 'UTF-8');
$sText = utils::HtmlEntities($this->aProperties['text']);
$sText = str_replace(array("\r\n", "\n", "\r"), "<br/>", $sText);
$sId = 'plaintext_'.($bEditMode? 'edit_' : '').$this->sId;
@@ -914,7 +917,7 @@ class DashletObjectList extends Dashlet
$sShowMenu = $this->aProperties['menu'] ? '1' : '0';
$oPage->add('<div class="dashlet-content">');
$sHtmlTitle = htmlentities(Dict::S($sTitle), ENT_QUOTES, 'UTF-8'); // done in the itop block
$sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block
if ($sHtmlTitle != '')
{
$oPage->add('<h1>'.$sHtmlTitle.'</h1>');
@@ -953,7 +956,7 @@ class DashletObjectList extends Dashlet
$bShowMenu = $this->aProperties['menu'];
$oPage->add('<div class="dashlet-content">');
$sHtmlTitle = htmlentities($this->oModelReflection->DictString($sTitle), ENT_QUOTES, 'UTF-8'); // done in the itop block
$sHtmlTitle = utils::HtmlEntities($this->oModelReflection->DictString($sTitle)); // done in the itop block
if ($sHtmlTitle != '')
{
$oPage->add('<h1>'.$sHtmlTitle.'</h1>');
@@ -1246,7 +1249,7 @@ abstract class DashletGroupBy extends Dashlet
case 'table':
default:
$sHtmlTitle = htmlentities(Dict::S($sTitle), ENT_QUOTES, 'UTF-8'); // done in the itop block
$sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block
$sType = 'count';
$aParams = array(
'group_by' => $this->sGroupByExpr,
@@ -1683,7 +1686,7 @@ class DashletGroupByPie extends DashletGroupBy
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
$HTMLsTitle = ($sTitle != '') ? '<h1 style="text-align:center">'.htmlentities($sTitle, ENT_QUOTES, 'UTF-8').'</h1>' : '';
$HTMLsTitle = ($sTitle != '') ? '<h1 style="text-align:center">'.utils::HtmlEntities($sTitle).'</h1>' : '';
$oPage->add("<div style=\"background-color:#fff;padding:0.25em;\">$HTMLsTitle<div id=\"$sBlockId\" style=\"background-color:#fff;\"></div></div>");
$aDisplayValues = $this->MakeSimulatedData();
@@ -1755,7 +1758,7 @@ class DashletGroupByBars extends DashletGroupBy
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
$HTMLsTitle = ($sTitle != '') ? '<h1 style="text-align:center">'.htmlentities($sTitle, ENT_QUOTES, 'UTF-8').'</h1>' : '';
$HTMLsTitle = ($sTitle != '') ? '<h1 style="text-align:center">'.utils::HtmlEntities($sTitle).'</h1>' : '';
$oPage->add("<div style=\"background-color:#fff;padding:0.25em;\">$HTMLsTitle<div id=\"$sBlockId\" style=\"background-color:#fff;\"></div></div>");
$aDisplayValues = $this->MakeSimulatedData();
@@ -1904,16 +1907,16 @@ class DashletHeaderStatic extends Dashlet
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
$sTitle = $this->aProperties['title'];
$sTitle = utils::HtmlEntities($this->aProperties['title']);
$sIcon = $this->aProperties['icon'];
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
$sIconPath = $oIconSelect->MakeFileUrl($sIcon);
$sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon));
$oPage->add('<div class="dashlet-content">');
$oPage->add('<div class="main_header">');
$oPage->add('<img src="'.utils::HtmlEntities($sIconPath).'">');
$oPage->add('<img src="'.$sIconPath.'">');
$oPage->add('<h1>'.$this->oModelReflection->DictString($sTitle).'</h1>');
$oPage->add('</div>');
@@ -2034,14 +2037,14 @@ class DashletHeaderDynamic extends Dashlet
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
$sTitle = $this->aProperties['title'];
$sTitle = utils::HtmlEntities($this->aProperties['title']);
$sIcon = $this->aProperties['icon'];
$sSubtitle = $this->aProperties['subtitle'];
$sSubtitle = utils::HtmlEntities($this->aProperties['subtitle']);
$sQuery = $this->aProperties['query'];
$sGroupBy = $this->aProperties['group_by'];
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
$sIconPath = $oIconSelect->MakeFileUrl($sIcon);
$sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon));
$aValues = $this->GetValues();
if (count($aValues) > 0)
@@ -2069,7 +2072,7 @@ class DashletHeaderDynamic extends Dashlet
$oPage->add('<div class="dashlet-content">');
$oPage->add('<div class="main_header">');
$oPage->add('<img src="'.utils::HtmlEntities($sIconPath).'">');
$oPage->add('<img src="'.$sIconPath.'">');
if (isset($aExtraParams['query_params']))
{
@@ -2098,9 +2101,9 @@ class DashletHeaderDynamic extends Dashlet
*/
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = array())
{
$sTitle = $this->aProperties['title'];
$sTitle = utils::HtmlEntities($this->aProperties['title']);
$sIcon = $this->aProperties['icon'];
$sSubtitle = $this->aProperties['subtitle'];
$sSubtitle = utils::HtmlEntities($this->aProperties['subtitle']);
$sQuery = $this->aProperties['query'];
$sGroupBy = $this->aProperties['group_by'];
@@ -2108,12 +2111,12 @@ class DashletHeaderDynamic extends Dashlet
$sClass = $oQuery->GetClass();
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
$sIconPath = $oIconSelect->MakeFileUrl($sIcon);
$sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon));
$oPage->add('<div class="dashlet-content">');
$oPage->add('<div class="main_header">');
$oPage->add('<img src="'.utils::HtmlEntities($sIconPath).'">');
$oPage->add('<img src="'.$sIconPath.'">');
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
@@ -2144,8 +2147,8 @@ class DashletHeaderDynamic extends Dashlet
$sTitle = $this->oModelReflection->DictString($sTitle);
$sSubtitle = $this->oModelReflection->DictFormat($sSubtitle, $iTotal);
$oPage->add('<h1>'.$sTitle.'</h1>');
$oPage->add('<a class="summary">'.$sSubtitle.'</a>');
$oPage->add('<h1>'.utils::HtmlEntities($sTitle).'</h1>');
$oPage->add('<a class="summary">'.utils::HtmlEntities($sSubtitle).'</a>');
$oPage->add('</div>');
$oPage->add('</div>');

View File

@@ -184,6 +184,10 @@ class DataTable
*/
public function GetAsHTMLTableRows(WebPage $oPage, $iPageSize, $aColumns, $sSelectMode, $bViewLink, $aExtraParams)
{
if ($iPageSize < 1)
{
$iPageSize = -1; // convention: no pagination
}
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
@@ -222,14 +226,21 @@ class DataTable
}
$sCombo = '<select class="pagesize">';
for($iPage = 1; $iPage < 5; $iPage++)
if($iPageSize < 1)
{
$iNbItems = $iPage * $iDefaultPageSize;
$sSelected = ($iNbItems == $iPageSize) ? 'selected="selected"' : '';
$sCombo .= "<option $sSelected value=\"$iNbItems\">$iNbItems</option>";
$sCombo .= "<option selected=\"selected\" value=\"-1\">".Dict::S('UI:Pagination:All')."</option>";
}
$sSelected = ($iPageSize < 1) ? 'selected="selected"' : '';
$sCombo .= "<option $sSelected value=\"-1\">".Dict::S('UI:Pagination:All')."</option>";
else
{
for($iPage = 1; $iPage < 5; $iPage++)
{
$iNbItems = $iPage * $iDefaultPageSize;
$sSelected = ($iNbItems == $iPageSize) ? 'selected="selected"' : '';
$sCombo .= "<option $sSelected value=\"$iNbItems\">$iNbItems</option>";
}
$sCombo .= "<option value=\"-1\">".Dict::S('UI:Pagination:All')."</option>";
}
$sCombo .= '</select>';
$sPages = Dict::S('UI:Pagination:PagesLabel');
@@ -431,7 +442,7 @@ EOF;
}
else
{
$aRow['form::select'] = "<input type=\"checkBox\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject[]\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
$aRow['form::select'] = "<input type=\"checkbox\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject[]\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
}
}
foreach($aColumns[$sAlias] as $sAttCode => $aData)
@@ -565,7 +576,7 @@ EOF;
<<<EOF
var oTable = $('#{$this->iListId} table.listResults');
oTable.tableHover();
oTable.tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager{$this->iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectModeJS', displayKey: $sDisplayKey, columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount});
oTable.tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager{$this->iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectModeJS', displayKey: $sDisplayKey, table_id: '{$this->iListId}', columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount});
EOF
);
if ($sFakeSortList != '')
@@ -577,8 +588,8 @@ EOF
public function UpdatePager(WebPage $oPage, $iDefaultPageSize, $iStart)
{
$iPageSize = ($iDefaultPageSize < 1) ? 1 : $iDefaultPageSize;
$iPageIndex = 1 + floor($iStart / $iPageSize);
$iPageSize = $iDefaultPageSize;
$iPageIndex = 0;
$sHtml = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex);
$oPage->add_ready_script("$('#pager{$this->iListId}').html('".json_encode($sHtml)."');");
if ($iDefaultPageSize < 1)
@@ -936,4 +947,4 @@ class DataTableSettings implements Serializable
}
return $ret;
}
}
}

View File

@@ -500,7 +500,10 @@ class DisplayBlock
}
$aAttribs =array(
'group' => array('label' => $sGroupByLabel, 'description' => ''),
'value' => array('label'=> Dict::S('UI:GroupBy:'.$sAggregationFunction), 'description' => Dict::Format('UI:GroupBy:'.$sAggregationFunction.'+', $sAggregationAttr))
'value' => array(
'label' => Dict::S('UI:GroupBy:'.$sAggregationFunction),
'description' => Dict::Format('UI:GroupBy:'.$sAggregationFunction.'+', $sAggregationAttr),
),
);
$sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection';
$sHtml .= $oPage->GetP(Dict::Format($sFormat, $iTotalCount));
@@ -699,7 +702,7 @@ class DisplayBlock
'breadcrumb_label' => MetaModel::GetName($this->m_oSet->GetClass()),
'breadcrumb_max_count' => utils::GetConfig()->Get('breadcrumb.max_count'),
'breadcrumb_instance_id' => MetaModel::GetConfig()->GetItopInstanceid(),
'breadcrumb_icon' => utils::GetAbsoluteUrlAppRoot().'images/breadcrumb-search.png'
'breadcrumb_icon' => utils::GetAbsoluteUrlAppRoot().'images/breadcrumb-search.png',
));
$oPage->add_ready_script("$('body').trigger('update_history.itop', [$seventAttachedData])");
@@ -1172,16 +1175,26 @@ EOF
}
if (($bAutoReload) && ($this->m_sStyle != 'search')) // Search form do NOT auto-reload
{
$sFilter = addslashes(str_replace('"', "'", $this->m_oFilter->serialize())); // Used either for asynchronous or auto_reload
$sExtraParams = addslashes(str_replace('"', "'", json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them
// Used either for asynchronous or auto_reload
// does a json_encode twice to get a string usable as function parameter
$sFilterBefore = $this->m_oFilter->serialize();
$sFilter = json_encode($sFilterBefore);
$sExtraParams = json_encode(json_encode($aExtraParams));
$oPage->add_script('if (typeof window.oAutoReloadBlock == "undefined") {
window.oAutoReloadBlock = {};
}
if (typeof window.oAutoReloadBlock[\''.$sId.'\'] != "undefined") {
clearInterval(window.oAutoReloadBlock[\''.$sId.'\']);
}
window.oAutoReloadBlock[\''.$sId.'\'] = setInterval("ReloadBlock(\''.$sId.'\', \''.$this->m_sStyle.'\', \"'.$sFilter.'\", \"'.$sExtraParams.'\")", '.$iReloadInterval.');');
$oPage->add_script(
<<<JS
if (typeof window.oAutoReloadBlock == "undefined") {
window.oAutoReloadBlock = {};
}
if (typeof window.oAutoReloadBlock['$sId'] != "undefined") {
clearInterval(window.oAutoReloadBlock['$sId']);
}
window.oAutoReloadBlock['$sId'] = setInterval(function() {
ReloadBlock('$sId', '{$this->m_sStyle}', $sFilter, $sExtraParams);
}, '$iReloadInterval');
JS
);
}
return $sHtml;
@@ -1719,6 +1732,7 @@ class MenuBlock extends DisplayBlock
*/
}
$this->AddMenuSeparator($aActions);
/** @var \iApplicationUIExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$oSet->Rewind();
@@ -1814,6 +1828,7 @@ class MenuBlock extends DisplayBlock
}
$this->AddMenuSeparator($aActions);
/** @var \iApplicationUIExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$oSet->Rewind();

View File

@@ -3,7 +3,7 @@
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -72,7 +72,10 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
$this->m_aMessages = array();
$this->SetRootUrl(utils::GetAbsoluteUrlAppRoot());
$this->add_header("Content-type: text/html; charset=utf-8");
$this->add_header("Cache-control: no-cache");
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
$this->add_header('Pragma: no-cache');
$this->add_header('Expires: 0');
$this->add_header('X-Frame-Options: deny');
$this->add_linked_stylesheet("../css/jquery.treeview.css");
$this->add_linked_stylesheet("../css/jquery.autocomplete.css");
$this->add_linked_stylesheet("../css/jquery-ui-timepicker-addon.css");
@@ -337,7 +340,7 @@ EOF
.magnificPopup({type: 'image', closeOnContentClick: true });
EOF
);
$this->add_init_script(
<<< EOF
try

View File

@@ -62,14 +62,16 @@ class LoginWebPage extends NiceWebPage
public function __construct($sTitle = null)
{
if($sTitle === null)
{
$sTitle = Dict::S('UI:Login:Title');
}
if ($sTitle === null) {
$sTitle = Dict::S('UI:Login:Title');
}
parent::__construct($sTitle);
$this->SetStyleSheet();
$this->add_header("Cache-control: no-cache");
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
$this->add_header('Pragma: no-cache');
$this->add_header('Expires: 0');
$this->add_header('X-Frame-Options: deny');
}
public function SetStyleSheet()
@@ -156,7 +158,7 @@ class LoginWebPage extends NiceWebPage
$this->add("<table>\n");
$sForgotPwd = $this->EnableResetPassword() ? $this->ForgotPwdLink() : '';
$this->add("<tr><td style=\"text-align:right\"><label for=\"user\">".Dict::S('UI:Login:UserNamePrompt').":</label></td><td style=\"text-align:left\"><input id=\"user\" type=\"text\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" /></td></tr>\n");
$this->add("<tr><td style=\"text-align:right\"><label for=\"pwd\">".Dict::S('UI:Login:PasswordPrompt').":</label></td><td style=\"text-align:left\"><input id=\"pwd\" type=\"password\" name=\"auth_pwd\" value=\"".htmlentities($sAuthPwd, ENT_QUOTES, 'UTF-8')."\" /></td></tr>\n");
$this->add("<tr><td style=\"text-align:right\"><label for=\"pwd\">".Dict::S('UI:Login:PasswordPrompt').":</label></td><td style=\"text-align:left\"><input id=\"pwd\" type=\"password\" autocomplete=\"off\" name=\"auth_pwd\" value=\"".htmlentities($sAuthPwd, ENT_QUOTES, 'UTF-8')."\" /></td></tr>\n");
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"submit\" value=\"".Dict::S('UI:Button:Login')."\" /></span></td></tr>\n");
if (strlen($sForgotPwd) > 0)
{
@@ -384,7 +386,7 @@ EOF
else
{
// Trash the token and change the password
$oUser->Set('reset_pwd_token', '');
$oUser->Set('reset_pwd_token', new ormPassword());
$oUser->AllowWrite(true);
$oUser->SetPassword($sNewPwd); // Does record the change into the DB
@@ -707,6 +709,7 @@ EOF
{
// No rights to be here, redirect to the portal
header('Location: '.$ret);
die();
}
}
}
@@ -846,8 +849,8 @@ EOF
{
$sAuthUser = $_SESSION['auth_user'];
UserRights::Login($sAuthUser); // Set the user's language
$sOldPwd = utils::ReadPostedParam('old_pwd', '', false, 'raw_data');
$sNewPwd = utils::ReadPostedParam('new_pwd', '', false, 'raw_data');
$sOldPwd = utils::ReadPostedParam('old_pwd', '', 'raw_data');
$sNewPwd = utils::ReadPostedParam('new_pwd', '', 'raw_data');
if (UserRights::CanChangePassword() && ((!UserRights::CheckCredentials($sAuthUser, $sOldPwd)) || (!UserRights::ChangePassword($sOldPwd, $sNewPwd))))
{
$oPage = self::NewLoginWebPage();

View File

@@ -3,7 +3,7 @@
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -293,7 +293,8 @@ EOF
$sHyperlink = $oMenu->GetHyperlink($aExtraParams);
if ($sHyperlink != '')
{
$oPage->AddToMenu('<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'" '.$sCSSClass.'><a href="'.$oMenu->GetHyperlink($aExtraParams).'">'.$oMenu->GetTitle().'</a></li>');
$sTitle = utils::HtmlEntities($oMenu->GetTitle());
$oPage->AddToMenu('<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'" '.$sCSSClass.'><a href="'.$oMenu->GetHyperlink($aExtraParams).'">'.$sTitle.'</a></li>');
}
else
{
@@ -905,7 +906,7 @@ class OQLMenuNode extends MenuNode
$oBlock->Display($oPage, 0);
}
$oPage->add("<p class=\"page-header\">$sIcon ".Dict::S($sTitle)."</p>");
$oPage->add("<p class=\"page-header\">$sIcon ".utils::HtmlEntities(Dict::S($sTitle))."</p>");
$aParams = array_merge(array('table_id' => $sUsageId), $aExtraParams);
$oBlock = new DisplayBlock($oSearch, 'list', false /* Asynchronous */, $aParams);

View File

@@ -399,6 +399,7 @@ class ObjectDetailsTemplate extends DisplayTemplate
$aPlugInProperties = $aMatches[1];
foreach($aPlugInProperties as $sPlugInClass)
{
/** @var \iApplicationUIExtension $oInstance */
$oInstance = MetaModel::GetPlugins('iApplicationUIExtension', $sPlugInClass);
if ($oInstance != null) // Safety check...
{

View File

@@ -26,8 +26,6 @@
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class privUITransaction
{
/**
@@ -99,9 +97,10 @@ class privUITransaction
}
/**
* The original (and by default) mechanism for storing transaction information
* as an array in the $_SESSION variable
* The original mechanism for storing transaction information as an array in the $_SESSION variable
*
* Warning, since 2.6.0 the session is regenerated on each login (see PR #20) !
* Also, we saw some problems when using memcached as the PHP session implementation (see N°1835)
*/
class privUITransactionSession
{
@@ -194,10 +193,35 @@ class privUITransactionSession
*/
class privUITransactionFile
{
/** @var int Value to use when no user logged */
const UNAUTHENTICATED_USER_ID = -666;
/**
* @return int current user id, or {@see self::UNAUTHENTICATED_USER_ID} if no user logged
*
* @since 2.6.5 2.7.6 3.0.0 N°4289 method creation
*/
private static function GetCurrentUserId()
{
$iCurrentUserId = UserRights::GetConnectedUserId();
if ('' === $iCurrentUserId) {
$iCurrentUserId = static::UNAUTHENTICATED_USER_ID;
}
return $iCurrentUserId;
}
/**
* Create a new transaction id, store it in the session and return its id
*
* @param void
*
* @return int The identifier of the new transaction
*
* @throws \SecurityException
* @throws \Exception
*
* @since 2.6.5 2.7.6 3.0.0 security hardening + throws SecurityException if no user logged
*/
public static function GetNewTransactionId()
{
@@ -207,82 +231,102 @@ class privUITransactionFile
{
throw new Exception('The directory "'.APPROOT.'data" must be writable to the application.');
}
/** @noinspection MkdirRaceConditionInspection */
if (!@mkdir(APPROOT.'data/transactions'))
{
throw new Exception('Failed to create the directory "'.APPROOT.'data/transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
}
}
if (!is_writable(APPROOT.'data/transactions'))
{
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
}
self::CleanupOldTransactions();
$id = basename(tempnam(APPROOT.'data/transactions', static::GetUserPrefix()));
self::Info('GetNewTransactionId: Created transaction: '.$id);
return (string)$id;
$iCurrentUserId = static::GetCurrentUserId();
self::CleanupOldTransactions();
$sTransactionIdFullPath = tempnam(APPROOT.'data/transactions', static::GetUserPrefix());
file_put_contents($sTransactionIdFullPath, $iCurrentUserId, LOCK_EX);
$sTransactionIdFileName = basename($sTransactionIdFullPath);
self::Info('GetNewTransactionId: Created transaction: '.$sTransactionIdFileName);
return $sTransactionIdFileName;
}
/**
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
* the session so that another call to IsTransactionValid for the same transaction id
* will return false
*
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
* @param bool $bRemoveTransaction True if the transaction must be removed
*
* @return bool True if the transaction is valid, false otherwise
*
* @since 2.6.5 2.7.6 3.0.0 N°4289 security hardening
*/
public static function IsTransactionValid($id, $bRemoveTransaction = true)
{
$sFilepath = APPROOT.'data/transactions/'.$id;
// Constraint the transaction file within APPROOT.'data/transactions'
$sTransactionDir = realpath(APPROOT.'data/transactions');
$sFilepath = utils::RealPath($sTransactionDir.'/'.$id, $sTransactionDir);
if (($sFilepath === false) || (strlen($sTransactionDir) == strlen($sFilepath)))
{
return false;
}
clearstatcache(true, $sFilepath);
$bResult = file_exists($sFilepath);
if ($bResult)
if (false === $bResult) {
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions:\n".implode("\n", self::GetPendingTransactions()));
return false;
}
$iCurrentUserId = static::GetCurrentUserId();
$sTransactionIdUserId = file_get_contents($sFilepath);
if ($iCurrentUserId != $sTransactionIdUserId) {
self::Info("IsTransactionValid: Transaction '$id' not existing for current user. Pending transactions:\n".implode("\n", self::GetPendingTransactions()));
return false;
}
if ($bRemoveTransaction)
{
if ($bRemoveTransaction)
$bResult = @unlink($sFilepath);
if (!$bResult)
{
$bResult = @unlink($sFilepath);
if (!$bResult)
{
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
}
else
{
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
}
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
}
else
{
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
}
}
else
{
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
}
return $bResult;
}
/**
* Removes the transaction specified by its id
* @param int $id The Identifier (as returned by GetNewTransactionId) of the transaction to be removed.
* @return void
* @return bool true if the token can be removed
*
* @since 2.6.5 2.7.6 3.0.0 N°4289 security hardening
*/
public static function RemoveTransaction($id)
{
$bSuccess = true;
$sFilepath = APPROOT.'data/transactions/'.$id;
clearstatcache(true, $sFilepath);
if(!file_exists($sFilepath))
{
$bSuccess = false;
self::Error("RemoveTransaction: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
/** @noinspection PhpRedundantOptionalArgumentInspection */
$bResult = static::IsTransactionValid($id, true);
if (false === $bResult) {
self::Error("RemoveTransaction: Transaction '$id' is invalid. Pending transactions:\n"
.implode("\n", self::GetPendingTransactions()));
return false;
}
$bSuccess = @unlink($sFilepath);
if (!$bSuccess)
{
self::Error('RemoveTransaction: FAILED to remove transaction '.$id);
}
else
{
self::Info('RemoveTransaction: OK '.$id);
}
return $bSuccess;
return true;
}
/**
@@ -347,22 +391,35 @@ class privUITransactionFile
{
self::Write('Error | '.$sText);
}
protected static function IsLogEnabled() {
$oConfig = MetaModel::GetConfig();
if (is_null($oConfig)) {
return false;
}
$bLogTransactions = $oConfig->Get('log_transactions');
if (true === $bLogTransactions) {
return true;
}
return false;
}
protected static function Write($sText)
{
$bLogEnabled = MetaModel::GetConfig()->Get('log_transactions');
if ($bLogEnabled)
{
if (false === static::IsLogEnabled()) {
return;
}
$hLogFile = @fopen(APPROOT.'log/transactions.log', 'a');
if ($hLogFile !== false)
{
if ($hLogFile !== false) {
flock($hLogFile, LOCK_EX);
$sDate = date('Y-m-d H:i:s');
fwrite($hLogFile, "$sDate | $sText\n");
fflush($hLogFile);
flock($hLogFile, LOCK_UN);
fclose($hLogFile);
}
}
}
}

View File

@@ -208,6 +208,14 @@ class UIExtKeyWidget
{
// When there is only once choice, select it by default
$sSelected = 'selected';
if($value != $key)
{
$oPage->add_ready_script(
<<<EOF
$('#$this->iId').attr('data-validate','dependencies');
EOF
);
}
}
else
{
@@ -365,10 +373,10 @@ EOF
$sHTML .= "</form>\n";
$sHTML .= '</div></div>';
$sDialogTitle = addslashes($sTitle);
$sDialogTitleSanitized = addslashes(utils::HtmlToText($sTitle));
$oPage->add_ready_script(
<<<EOF
$('#ac_dlg_{$this->iId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.UpdateSizes, close: oACWidget_{$this->iId}.OnClose });
$('#ac_dlg_{$this->iId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '$sDialogTitleSanitized', resizeStop: oACWidget_{$this->iId}.UpdateSizes, close: oACWidget_{$this->iId}.OnClose });
$('#fs_{$this->iId}').bind('submit.uiAutocomplete', oACWidget_{$this->iId}.DoSearchObjects);
$('#dc_{$this->iId}').resize(oACWidget_{$this->iId}.UpdateSizes);
EOF

View File

@@ -286,11 +286,13 @@ class UILinksWidgetDirect
* @param DBObject $oCurrentObj
* @param $aAlreadyLinked
*
* @param array $aPrefillFormParam
*
* @throws \CoreException
* @throws \Exception
* @throws \MissingQueryArgument
* @throws \OQLException
*/
public function GetObjectsSelectionDlg($oPage, $oCurrentObj, $aAlreadyLinked)
public function GetObjectsSelectionDlg($oPage, $oCurrentObj, $aAlreadyLinked, $aPrefillFormParam = array())
{
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
@@ -333,6 +335,8 @@ class UILinksWidgetDirect
$aArgs = array_merge($oCurrentObj->ToArgs('this'), $oFilter->GetInternalParams());
$oFilter->SetInternalParams($aArgs);
$aPrefillFormParam['filter'] = $oFilter;
$oCurrentObj->PrefillForm('search', $aPrefillFormParam);
}
$oBlock = new DisplayBlock($oFilter, 'search', false);
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->sInputid}",
@@ -359,13 +363,17 @@ class UILinksWidgetDirect
/**
* Search for objects to be linked to the current object (i.e "remote" objects)
*
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of $this->sLinkedClass
* @param array $aAlreadyLinked Array of indentifiers of objects which are already linke to the current object (or about to be linked)
* @param DBObject $oCurrentObj The object currently being edited... if known...
* @throws Exception
* @param array $aPrefillFormParam
*
* @throws \CoreException
* @throws \OQLException
*/
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinked = array(), $oCurrentObj = null)
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinked = array(), $oCurrentObj = null, $aPrefillFormParam = array())
{
if ($sRemoteClass == '')
{
@@ -395,16 +403,19 @@ class UILinksWidgetDirect
$oFilter->AddCondition('id', $oCurrentObj->GetKey(), '!=');
}
}
if (count($aAlreadyLinked) > 0)
{
$oFilter->AddCondition('id', $aAlreadyLinked, 'NOTIN');
}
if ($oCurrentObj != null)
{
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$aArgs = array_merge($oCurrentObj->ToArgs('this'), $oFilter->GetInternalParams());
$oFilter->SetInternalParams($aArgs);
$aPrefillFormParam['filter'] = $oFilter;
$oCurrentObj->PrefillForm('search', $aPrefillFormParam);
}
if (count($aAlreadyLinked) > 0)
{
$oFilter->AddCondition('id', $aAlreadyLinked, 'NOTIN');
}
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, "ResultsToAdd_{$this->sInputid}", array('menu' => false, 'cssCount'=> '#count_'.$this->sInputid , 'selection_mode' => true, 'table_id' => 'add_'.$this->sInputid)); // Don't display the 'Actions' menu on the results

View File

@@ -133,14 +133,15 @@ class UILinksWidget
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
$aRow = array();
$aFieldsMap = array();
$iKey = 0;
if(is_object($linkObjOrId) && (!$linkObjOrId->IsNew()))
{
$key = $linkObjOrId->GetKey();
$iKey = $linkObjOrId->GetKey();
$iRemoteObjKey = $linkObjOrId->Get($this->m_sExtKeyToRemote);
$sPrefix .= "[$key][";
$sPrefix .= "[$iKey][";
$sNameSuffix = "]"; // To make a tabular form
$aArgs['prefix'] = $sPrefix;
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$key}";
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$iKey}";
$aArgs['this'] = $linkObjOrId;
if($bReadOnly)
@@ -154,7 +155,7 @@ class UILinksWidget
}
else
{
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$key\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$key\">";
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$iKey\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$iKey\">";
foreach($this->m_aEditableFields as $sFieldCode)
{
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId->GetKey().']';
@@ -195,7 +196,30 @@ class UILinksWidget
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".($iUniqueId < 0 ? -$iUniqueId : $iUniqueId);
$aArgs['this'] = $oNewLinkObj;
$sInputValue = $iUniqueId > 0 ? "-$iUniqueId" : "$iUniqueId";
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$sInputValue\">";
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"0\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$sInputValue\">";
if ($iUniqueId > 0)
{
// Rows created with ajax call need OnLinkAdded call.
//
$oP->add_ready_script(
<<<EOF
PrepareWidgets();
oWidget{$this->m_iInputId}.OnLinkAdded($iUniqueId, $iRemoteObjKey);
EOF
);
}
else
{
// Rows added before loading the form don't have to call OnLinkAdded.
// Listeners are already present and DOM is not recreated
$iPositiveUniqueId = -$iUniqueId;
$oP->add_ready_script(<<<EOF
oWidget{$this->m_iInputId}.AddLink($iPositiveUniqueId, $iRemoteObjKey);
EOF
);
}
foreach($this->m_aEditableFields as $sFieldCode)
{
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.-$iUniqueId.']';
@@ -207,20 +231,12 @@ class UILinksWidget
cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $sValue, $sDisplayValue, $sSafeId /* id */, $sNameSuffix, 0, $aArgs).
'</div></div></div>';
$aFieldsMap[$sFieldCode] = $sSafeId;
$oP->add_ready_script(<<<EOF
oWidget{$this->m_iInputId}.OnValueChange($iKey, $iUniqueId, '$sFieldCode', '$sValue');
EOF
);
}
$sState = '';
// Rows created with ajax call need OnLinkAdded call.
// Rows added before loading the form cannot call OnLinkAdded.
if ($iUniqueId > 0)
{
$oP->add_ready_script(
<<<EOF
PrepareWidgets();
oWidget{$this->m_iInputId}.OnLinkAdded($iUniqueId, $iRemoteObjKey);
EOF
);
}
}
if(!$bReadOnly)
@@ -337,8 +353,19 @@ EOF
$sHtmlValue .= "<input type=\"hidden\" id=\"{$sFormPrefix}{$this->m_iInputId}\">\n";
$oValue->Rewind();
$aForm = array();
$iAddedId = 1; // Unique id for new links
$aAddedLinks = array();
$iAddedId = -1; // Unique id for new links
$sDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
// Don't automatically launch the search if the table is huge
$bDoSearch = !utils::IsHighCardinality($this->m_sRemoteClass);
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
$oPage->add_ready_script(<<<EOF
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates, $sWizHelper, '{$this->m_sExtKeyToRemote}', $sJSDoSearch);
oWidget{$this->m_iInputId}.Init();
EOF
);
while($oCurrentLink = $oValue->Fetch())
{
// We try to retrieve the remote object as usual
@@ -357,9 +384,7 @@ EOF
if ($oCurrentLink->IsNew())
{
$key = -($iAddedId++);
$iUniqueId = -$key;
$aAddedLinks[] = array('iAddedId' => $iUniqueId, 'iRemote' => $oCurrentLink->Get($this->m_sExtKeyToRemote));
$key = $iAddedId--;
}
else
{
@@ -368,24 +393,6 @@ EOF
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly);
}
$sHtmlValue .= $this->DisplayFormTable($oPage, $this->m_aTableConfig, $aForm);
$sDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
// Don't automatically launch the search if the table is huge
$bDoSearch = !utils::IsHighCardinality($this->m_sRemoteClass);
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
$oPage->add_ready_script(<<<EOF
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates, $sWizHelper, '{$this->m_sExtKeyToRemote}', $sJSDoSearch);
oWidget{$this->m_iInputId}.Init();
EOF
);
foreach ($aAddedLinks as $aAddedLink)
{
$oPage->add_ready_script(<<<EOF
oWidget{$this->m_iInputId}.AddLink({$aAddedLink['iAddedId']}, {$aAddedLink['iRemote']});
EOF
);
}
$sHtmlValue .= "<span style=\"float:left;\">&nbsp;&nbsp;&nbsp;<img src=\"../images/tv-item-last.gif\">&nbsp;&nbsp;<input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"oWidget{$this->m_iInputId}.RemoveSelected();\" >";
$sHtmlValue .= "&nbsp;&nbsp;&nbsp;<input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sRemoteClass))."\" onClick=\"oWidget{$this->m_iInputId}.AddObjects();\"><span id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_indicatorAdd\"></span></span>\n";

View File

@@ -273,79 +273,99 @@ class utils
}
return $retValue;
}
/**
* @param string|string[] $value
* @param string $sSanitizationFilter one of : integer, class, string, context_param, parameter, field_name,
* transaction_id, parameter, raw_data
*
* @return string|string[]|bool boolean for :
* * the 'class' filter (true if valid, false otherwise)
* * if the filter fails (@see \filter_var())
*
* @since 2.5.2 2.6.0 new 'transaction_id' filter
*/
protected static function Sanitize_Internal($value, $sSanitizationFilter)
{
switch($sSanitizationFilter)
switch ($sSanitizationFilter)
{
case 'integer':
$retValue = filter_var($value, FILTER_SANITIZE_NUMBER_INT);
break;
$retValue = filter_var($value, FILTER_SANITIZE_NUMBER_INT);
break;
case 'class':
$retValue = $value;
if (!MetaModel::IsValidClass($value))
{
$retValue = false;
}
break;
$retValue = $value;
if (!MetaModel::IsValidClass($value))
{
$retValue = false;
}
break;
case 'string':
$retValue = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
break;
$retValue = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
break;
case 'context_param':
case 'parameter':
case 'field_name':
if (is_array($value))
{
$retValue = array();
foreach($value as $key => $val)
case 'transaction_id':
if (is_array($value))
{
$retValue[$key] = self::Sanitize_Internal($val, $sSanitizationFilter); // recursively check arrays
if ($retValue[$key] === false)
$retValue = array();
foreach ($value as $key => $val)
{
$retValue = false;
break;
$retValue[$key] = self::Sanitize_Internal($val, $sSanitizationFilter); // recursively check arrays
if ($retValue[$key] === false)
{
$retValue = false;
break;
}
}
}
}
else
{
switch($sSanitizationFilter)
else
{
case 'transaction_id':
// same as parameter type but keep the dot character
// see N°1835 : when using file transaction_id on Windows you get *.tmp tokens
// it must be included at the regexp beginning otherwise you'll get an invalid character error
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[\. A-Za-z0-9_=-]*$/')));
break;
switch ($sSanitizationFilter)
{
case 'transaction_id':
// same as parameter type but keep the dot character
// see N°1835 : when using file transaction_id on Windows you get *.tmp tokens
// it must be included at the regexp beginning otherwise you'll get an invalid character error
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[\. A-Za-z0-9_=-]*$/')));
break;
case 'parameter':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[ A-Za-z0-9_=-]*$/'))); // the '=', '%3D, '%2B', '%2F'
// characters are used in serialized filters (starting 2.5, only the url encoded versions are presents, but the "=" is kept for BC)
break;
case 'parameter':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[ A-Za-z0-9_=-]*$/'))); // the '=', '%3D, '%2B', '%2F'
// characters are used in serialized filters (starting 2.5, only the url encoded versions are presents, but the "=" is kept for BC)
break;
case 'field_name':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options"=>array("regexp"=>'/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name
break;
case 'context_param':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options"=>array("regexp"=>'/^[ A-Za-z0-9_=%:+-]*$/')));
break;
case 'field_name':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name
break;
case 'context_param':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[ A-Za-z0-9_=%:+-]*$/')));
break;
}
}
}
break;
// For XML / HTML node identifiers
case 'element_identifier':
$retValue = preg_replace('/[^a-zA-Z0-9_]/', '', $value);
break;
default:
case 'raw_data':
$retValue = $value;
$retValue = $value;
// Do nothing
}
return $retValue;
return $retValue;
}
/**
@@ -1459,6 +1479,17 @@ class utils
return htmlentities($sValue, ENT_QUOTES, 'UTF-8');
}
/**
* Helper to encapsulation iTop's html_entity_decode
* @param string $sValue
* @return string
* @since 2.7.0
*/
public static function HtmlEntityDecode($sValue)
{
return html_entity_decode($sValue, ENT_QUOTES, 'UTF-8');
}
/**
* Convert a string containing some (valid) HTML markup to plain text
* @param string $sHtml
@@ -2018,4 +2049,66 @@ class utils
{
return ITOP_REVISION === 'svn';
}
/**
* helper to test if a string starts with another
* @param $haystack
* @param $needle
*
* @return bool
*/
final public static function StartsWith($haystack, $needle)
{
if (strlen($needle) > strlen($haystack))
{
return false;
}
return substr_compare($haystack, $needle, 0, strlen($needle)) === 0;
}
/**
* helper to test if a string ends with another
* @param $haystack
* @param $needle
*
* @return bool
*/
final public static function EndsWith($haystack, $needle) {
if (strlen($needle) > strlen($haystack))
{
return false;
}
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
}
/**
* @param string $sPath for example '/var/www/html/itop/data/backups/manual/itop_27-2019-10-03_15_35.tar.gz'
* @param string $sBasePath for example '/var/www/html/itop/data/'
*
* @return bool|string false if path :
* * invalid
* * not allowed
* * not contained in base path
* Otherwise return the real path (see realpath())
*
* @since 2.6.5 2.7.0 N°2538
*/
final public static function RealPath($sPath, $sBasePath)
{
$sFileRealPath = realpath($sPath);
if ($sFileRealPath === false)
{
return false;
}
$sRealBasePath = realpath($sBasePath); // avoid problems when having '/' on Windows for example
if (!self::StartsWith($sFileRealPath, $sRealBasePath))
{
return false;
}
return $sFileRealPath;
}
}

View File

@@ -355,8 +355,10 @@ class WebPage implements Page
*/
public function no_cache()
{
$this->add_header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
$this->add_header("Expires: Fri, 17 Jul 1970 05:00:00 GMT"); // Date in the past
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
$this->add_header('Pragma: no-cache');
$this->add_header('Expires: 0');
$this->add_header('X-Frame-Options: deny');
}
/**

View File

@@ -174,7 +174,7 @@ Class XLSXWriter
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.self::convert_date_time($value).'</v></c>');
} else if ($value==''){
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'"/>');
} else if ($value{0}=='='){
} else if ($value[0]=='='){
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="s"><f>'.self::xmlspecialchars($value).'</f></c>');
} else if ($value!==''){
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="s"><v>'.self::xmlspecialchars($this->setSharedString($value)).'</v></c>');

View File

@@ -43,9 +43,12 @@ class XMLPage extends WebPage
$this->m_bPassThrough = $bPassThrough;
$this->m_bHeaderSent = false;
$this->add_header("Content-type: text/xml; charset=utf-8");
$this->add_header("Cache-control: no-cache");
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
$this->add_header('Pragma: no-cache');
$this->add_header('Expires: 0');
$this->add_header('X-Frame-Options: deny');
$this->add_header("Content-location: export.xml");
}
}
public function output()
{
@@ -53,7 +56,7 @@ class XMLPage extends WebPage
{
// Get the unexpected output but do nothing with it
$sTrash = $this->ob_get_clean_safe();
$this->s_content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n".trim($this->s_content);
$this->add_header("Content-Length: ".strlen($this->s_content));
foreach($this->a_headers as $s_header)

View File

@@ -1,10 +1,6 @@
{
"require": {
"php": ">=5.6.0",
"ext-mysql": "*",
"ext-ldap": "*",
"ext-mcrypt": "*",
"ext-cli": "*",
"ext-soap": "*",
"ext-json": "*",
"ext-zip": "*",
@@ -13,6 +9,14 @@
"ext-iconv": "*",
"ext-gd": "*"
},
"suggest": {
"ext-libsodium": "Required to use the AttributeEncryptedString.",
"ext-openssl": "Can be used as a polyfill if libsodium is not installed",
"ext-mcrypt": "Can be used as a polyfill if either libsodium and openssl are not installed (libsodium and openssl are more secure)",
"ext-ldap": "Required to use LDAP as an identity provider",
"ext-posix": "Not required by the core, but some extensions uses it.",
"ext-imap": "Required by the extension \"Mail to ticket automation\""
},
"config": {
"platform": {
"php": "5.6.0"

3054
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

13
conf/.htaccess Normal file
View File

@@ -0,0 +1,13 @@
# Apache 2.4
<ifModule mod_authz_core.c>
Require all denied
</ifModule>
# Apache 2.2
<ifModule !mod_authz_core.c>
deny from all
Satisfy All
</ifModule>
# Apache 2.2 and 2.4
IndexIgnore *

13
conf/web.config Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<security>
<requestFiltering>
<fileExtensions applyToWebDAV="false" allowUnlisted="false"></fileExtensions>
</requestFiltering>
<authorization>
<deny users="*" /> <!-- Denies all users -->
</authorization>
</security>
</system.webServer>
</configuration>

View File

@@ -314,42 +314,54 @@ class ActionEmail extends ActionNotification
{
$this->m_iRecipients = 0;
$this->m_aMailErrors = array();
$bRes = false; // until we do succeed in sending the email
// Determine recicipients
//
$sTo = $this->FindRecipients('to', $aContextArgs);
$sCC = $this->FindRecipients('cc', $aContextArgs);
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
$sFrom = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
$sReplyTo = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
$oObj = $aContextArgs['this->object()'];
$sMessageId = sprintf('iTop_%s_%d_%f@%s.openitop.org', get_class($oObj), $oObj->GetKey(), microtime(true /* get as float*/), MetaModel::GetEnvironmentId());
$sMessageId = sprintf('iTop_%s_%d_%f@%s.openitop.org', get_class($oObj), $oObj->GetKey(), microtime(true /* get as float*/),
MetaModel::GetEnvironmentId());
$sReference = '<'.$sMessageId.'>';
}
catch(Exception $e)
{
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
throw $e;
}
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
if (!is_null($oLog))
{
catch (Exception $e) {
/** @noinspection PhpUnhandledExceptionInspection */
throw $e;
}
finally {
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
}
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($sTo)) $oLog->Set('to', $sTo);
if (isset($sCC)) $oLog->Set('cc', $sCC);
if (isset($sBCC)) $oLog->Set('bcc', $sBCC);
if (isset($sFrom)) $oLog->Set('from', $sFrom);
if (isset($sSubject)) $oLog->Set('subject', $sSubject);
if (isset($sBody)) $oLog->Set('body', $sBody);
if (isset($sTo)) {
$oLog->Set('to', $sTo);
}
if (isset($sCC)) {
$oLog->Set('cc', $sCC);
}
if (isset($sBCC)) {
$oLog->Set('bcc', $sBCC);
}
if (isset($sFrom)) {
$oLog->Set('from', $sFrom);
}
if (isset($sSubject)) {
$oLog->Set('subject', $sSubject);
}
if (isset($sBody)) {
$oLog->Set('body', $sBody);
}
}
$sStyles = file_get_contents(APPROOT.'css/email.css');
$sStyles .= MetaModel::GetConfig()->Get('email_css');
@@ -439,4 +451,3 @@ class ActionEmail extends ActionNotification
}
}
}
?>

View File

@@ -150,7 +150,7 @@ abstract class AsyncTask extends DBObject
public function GetRetryDelay($iErrorCode = null)
{
$iRetryDelay = 600;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
$aRetries = MetaModel::GetConfig()->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
@@ -162,12 +162,13 @@ abstract class AsyncTask extends DBObject
public function GetMaxRetries($iErrorCode = null)
{
$iMaxRetries = 0;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
$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'];
}
return $iMaxRetries;
}
/**
@@ -380,6 +381,6 @@ class AsyncSendEmail extends AsyncTask
case EMAIL_SEND_ERROR:
return "Failed: ".implode(', ', $aIssues);
}
return '';
}
}
?>

View File

@@ -512,7 +512,7 @@ abstract class AttributeDefinition
}
/**
* @param string|null $sDefault
* @param string|null $sDefault if null, will return the attribute code replacing "_" by " "
*
* @return string
*
@@ -1374,6 +1374,11 @@ class AttributeLinkedSet extends AttributeDefinition
*/
public function GetDefaultValue(DBObject $oHostObject = null)
{
if ($oHostObject === null)
{
return null;
}
$sLinkClass = $this->GetLinkedClass();
$sExtKeyToMe = $this->GetExtKeyToMe();
@@ -3264,10 +3269,13 @@ class AttributeClassState extends AttributeString
$sClass = $oHostObj->Get($sTargetClass);
$aAllowedStates = array();
$aValues = MetaModel::EnumStates($sClass);
foreach(array_keys($aValues) as $sState)
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sChildClass)
{
$aAllowedStates[$sState] = $sState.' ('.MetaModel::GetStateLabel($sClass, $sState).')';
$aValues = MetaModel::EnumStates($sChildClass);
foreach (array_keys($aValues) as $sState)
{
$aAllowedStates[$sState] = $sState.' ('.MetaModel::GetStateLabel($sChildClass, $sState).')';
}
}
return $aAllowedStates;
}
@@ -3286,9 +3294,15 @@ class AttributeClassState extends AttributeString
{
$sTargetClass = $this->Get('class_field');
$sClass = $oHostObject->Get($sTargetClass);
$sHTML = '<span class="attribute-set-item" data-code="'.$sValue.'" data-label="'.$sValue.' ('.MetaModel::GetStateLabel($sClass, $sValue).')'.'" data-description="">'.$sValue.'</span>';
return $sHTML;
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sChildClass)
{
$aValues = MetaModel::EnumStates($sChildClass);
if (in_array($sValue, $aValues))
{
$sHTML = '<span class="attribute-set-item" data-code="'.$sValue.'" data-label="'.$sValue.' ('.MetaModel::GetStateLabel($sChildClass, $sValue).')'.'" data-description="">'.$sValue.'</span>';
return $sHTML;
}
}
}
return $sValue;
@@ -6242,6 +6256,15 @@ class AttributeExternalKey extends AttributeDBFieldVoid
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);
}
}
/**
@@ -6502,26 +6525,60 @@ class AttributeExternalField extends AttributeDefinition
}
}
/**
* @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 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;
}
else
{
$sLabel = parent::GetLabel('');
if (strlen($sLabel) == 0)
{
$oRemoteAtt = $this->GetExtAttDef();
$sLabel = $oRemoteAtt->GetLabel($this->m_sCode);
$oKeyAtt = $this->GetKeyAttDef();
$sKeyLabel = $oKeyAtt->GetLabel($this->GetKeyAttCode());
$sLabel = "{$sKeyLabel}->{$sLabel}";
}
}
$oRemoteAtt = $this->GetExtAttDef();
$sLabel = $oRemoteAtt->GetLabel($this->m_sCode);
$oKeyAtt = $this->GetKeyAttDef();
$sKeyLabel = $oKeyAtt->GetLabel($this->GetKeyAttCode());
$sLabel = "{$sKeyLabel}->{$sLabel}";
return $sLabel;
}
@@ -7280,6 +7337,13 @@ class AttributeImage extends AttributeBlob
{
$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;
}
@@ -7813,6 +7877,87 @@ class AttributeStopWatch extends AttributeDefinition
throw new CoreException("Unknown item code '$sItemCode' for attribute ".$this->GetHostClass().'::'.$this->GetCode());
}
public function GetSubItemSearchType($sItemCode)
{
switch ($sItemCode)
{
case 'timespent':
return static::SEARCH_WIDGET_TYPE_NUMERIC; //seconds
case 'started':
case 'laststart':
case 'stopped':
return static::SEARCH_WIDGET_TYPE_DATE_TIME; //timestamp
}
foreach($this->ListThresholds() as $iThreshold => $aFoo)
{
$sThPrefix = $iThreshold.'_';
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix)
{
// The current threshold is concerned
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
switch ($sThresholdCode)
{
case 'deadline':
return static::SEARCH_WIDGET_TYPE_DATE_TIME; //timestamp
case 'passed':
case 'triggered':
return static::SEARCH_WIDGET_TYPE_ENUM; //booleans, used in conjuction with GetSubItemAllowedValues and IsSubItemNullAllowed
case 'overrun':
return static::SEARCH_WIDGET_TYPE_NUMERIC; //seconds
}
}
}
return static::SEARCH_WIDGET_TYPE_RAW;
}
public function GetSubItemAllowedValues($sItemCode, $aArgs = array(), $sContains = '')
{
foreach($this->ListThresholds() as $iThreshold => $aFoo)
{
$sThPrefix = $iThreshold.'_';
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix)
{
// The current threshold is concerned
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
switch ($sThresholdCode)
{
case 'passed':
case 'triggered':
return array(
0 => $this->GetBooleanLabel(0),
1 => $this->GetBooleanLabel(1),
);
}
}
}
return null;
}
public function IsSubItemNullAllowed($sItemCode, $bDefaultValue)
{
foreach($this->ListThresholds() as $iThreshold => $aFoo)
{
$sThPrefix = $iThreshold.'_';
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix)
{
// The current threshold is concerned
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
switch ($sThresholdCode)
{
case 'passed':
case 'triggered':
return false;
}
}
}
return $bDefaultValue;
}
protected function GetBooleanLabel($bValue)
{
$sDictKey = $bValue ? 'yes' : 'no';
@@ -8162,9 +8307,39 @@ class AttributeStopWatch extends AttributeDefinition
*/
class AttributeSubItem extends AttributeDefinition
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
/**
* Return the search widget type corresponding to this attribute
* the computation is made by AttributeStopWatch::GetSubItemSearchType
*
* @return string
*/
public function GetSearchType()
{
/** @var AttributeStopWatch $oParent */
$oParent = $this->GetTargetAttDef();
static public function ListExpectedParams()
return $oParent->GetSubItemSearchType($this->Get('item_code'));
}
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
/** @var AttributeStopWatch $oParent */
$oParent = $this->GetTargetAttDef();
return $oParent->GetSubItemAllowedValues($this->Get('item_code'), $aArgs, $sContains);
}
public function IsNullAllowed()
{
/** @var AttributeStopWatch $oParent */
$oParent = $this->GetTargetAttDef();
$bDefaultValue = parent::IsNullAllowed();
return $oParent->IsSubItemNullAllowed($this->Get('item_code'), $bDefaultValue);
}
static public function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array('target_attcode', 'item_code'));
}
@@ -9655,14 +9830,27 @@ class AttributeTagSet extends AttributeSet
{
$aJson['partial_values'] = array();
$aJson['orig_value'] = array();
$aJson['added'] = array();
$aJson['removed'] = array();
}
else
{
$aJson['partial_values'] = $oValue->GetModified();
$aJson['orig_value'] = array_merge($oValue->GetValues(), $oValue->GetModified());
$aJson['added'] = $oValue->GetAdded();
$aJson['removed'] = $oValue->GetRemoved();
if ($oValue->DisplayPartial())
{
// For bulk updates
$aJson['partial_values'] = $oValue->GetModified();
}
else
{
// For simple updates
$aJson['partial_values'] = array();
}
}
$aJson['added'] = array();
$aJson['removed'] = array();
$iMaxTags = $this->GetMaxItems();
$aJson['max_items_allowed'] = $iMaxTags;

View File

@@ -696,6 +696,8 @@ abstract class CMDBObject extends DBObject
* TODO: investigate how to get rid of this class that was made to workaround some language limitation... or a poor design!
*
* @package iTopORM
*
* @internal
*/
class CMDBObjectSet extends DBObjectSet
{

View File

@@ -19,7 +19,7 @@
define('ITOP_APPLICATION', 'iTop');
define('ITOP_APPLICATION_SHORT', 'iTop');
define('ITOP_VERSION', '2.6.0');
define('ITOP_VERSION', '2.6.2');
define('ITOP_REVISION', 'svn');
define('ITOP_BUILD_DATE', '$WCNOW$');
@@ -1049,6 +1049,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'svg_sanitizer' => array(
'type' => 'string',
'description' => 'The class to use for SVG sanitization : allow to provide a custom made sanitizer',
'default' => 'SVGDOMSanitizer',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'inline_image_max_display_width' => array(
'type' => 'integer',
'description' => 'The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.',
@@ -1161,6 +1169,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'security.disable_inline_documents_sandbox' => array(
'type' => 'bool',
'description' => 'If true then the sandbox for documents displayed in a browser tab will be disabled; enabling scripts and other interactive content. Note that setting this to true will open the application to potential XSS attacks!',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
);
public function IsProperty($sPropCode)

File diff suppressed because it is too large Load Diff

View File

@@ -17,9 +17,22 @@
// along with iTop. If not, see <http://www.gnu.org/licenses/>
//
// Dev hack for disabling the some query build optimizations (Folding/Merging)
/** @internal Dev hack for disabling some query build optimizations (Folding/Merging) */
define('ENABLE_OPT', true);
/**
* A search over a DBObject
*
* This is the most common search cases, the other class representing a search is DBUnionSearch.
* For clarity purpose, since only the constructor vary between DBObjectSearch and DBUnionSearch, all the API is documented on the common ancestor: DBSearch
* Please refer to DBSearch's documentation
*
* @package iTopORM
* @phpdoc-tuning-exclude-inherited this tag prevent PHPdoc from displaying inherited methods. This is done in order to force the API doc. location into DBSearch only.
* @api
* @see DBSearch
* @see DBUnionSearch
*/
class DBObjectSearch extends DBSearch
{
private $m_aClasses; // queried classes (alias => class name), the first item is the class corresponding to this filter (the rest is coming from subfilters)
@@ -29,11 +42,23 @@ class DBObjectSearch extends DBSearch
private $m_aPointingTo;
private $m_aReferencedBy;
// By default, some information may be hidden to the current user
// But it may happen that we need to disable that feature
/**
* @var bool whether or not some information should be hidden to the current user. Default to false == hide information.
* @see AllowAllData()
*/
protected $m_bAllowAllData = false;
protected $m_bDataFiltered = false;
/**
* DBObjectSearch constructor.
*
* @api
*
* @param string $sClass
* @param string|null $sClassAlias
*
* @throws Exception
*/
public function __construct($sClass, $sClassAlias = null)
{
parent::__construct();
@@ -524,7 +549,7 @@ class DBObjectSearch extends DBSearch
/**
* Specify a condition on external keys or link sets
* @param string $sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
* Example: infra_list->ci_id->location_id->country
* Example: infra_list->ci_id->location_id->country
* @param $value
* @return void
* @throws \CoreException
@@ -1642,7 +1667,7 @@ class DBObjectSearch extends DBSearch
// Query caching
//
$sOqlAPCCacheId = null;
if (self::$m_bQueryCacheEnabled)
if (self::$m_bQueryCacheEnabled && $bCanCache)
{
// Warning: using directly the query string as the key to the hash array can FAIL if the string
// is long and the differences are only near the end... so it's safer (but not bullet proof?)
@@ -2344,8 +2369,8 @@ class DBObjectSearch extends DBSearch
}
/**
* Get the expression for the class and its subclasses (if finalclass = 'subclass' ...)
* Simplifies the final expression by grouping classes having the same expression
* Get the expression for the class and its subclasses (if finalclass = 'subclass' ...)
* Simplifies the final expression by grouping classes having the same expression
* @param $sClass
* @param $sAttCode
* @return \FunctionExpression|mixed|null

View File

@@ -27,9 +27,13 @@ require_once('dbobjectiterator.php');
/**
* A set of persistent objects, could be heterogeneous as long as the objects in the set have a common ancestor class
* A set of persistent objects
*
* Created against a DBObjectSearch with additional information not relevant for the DBObjectSearch (ie: order, limit, ...)
* This set could be heterogeneous as long as the objects in the set have a common ancestor class.
*
* @package iTopORM
* @api
*/
class DBObjectSet implements iDBObjectSetIterator
{
@@ -81,6 +85,8 @@ class DBObjectSet implements iDBObjectSetIterator
/**
* Create a new set based on a Search definition.
*
* @api
*
* @param DBSearch $oFilter The search filter defining the objects which are part of the set (multiple columns/objects per row are supported)
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
@@ -110,6 +116,9 @@ class DBObjectSet implements iDBObjectSetIterator
$this->m_oSQLResult = null;
}
/**
* @internal
*/
public function __destruct()
{
if (is_object($this->m_oSQLResult))
@@ -119,6 +128,8 @@ class DBObjectSet implements iDBObjectSetIterator
}
/**
* @internal
*
* @return string
*
* @throws \Exception
@@ -145,6 +156,9 @@ class DBObjectSet implements iDBObjectSetIterator
return $sRet;
}
/**
* @internal
*/
public function __clone()
{
$this->m_oFilter = $this->m_oFilter->DeepClone();
@@ -158,6 +172,7 @@ class DBObjectSet implements iDBObjectSetIterator
/**
* Called when unserializing a DBObjectSet
* @internal
*/
public function __wakeup()
{
@@ -168,18 +183,30 @@ class DBObjectSet implements iDBObjectSetIterator
$this->m_oSQLResult = null;
}
/**
* @internal
* @param $bShow
*/
public function SetShowObsoleteData($bShow)
{
$this->m_oFilter->SetShowObsoleteData($bShow);
}
/**
* @internal
* @return bool
*/
public function GetShowObsoleteData()
{
return $this->m_oFilter->GetShowObsoleteData();
}
/**
* Specify the subset of attributes to load (for each class of objects) before performing the SQL query for retrieving the rows from the DB
* Specify the subset of attributes to load
* this subset is specified for each class of objects,
* this has to be done before the actual fetch.
*
* @api
*
* @param array $aAttToLoad Format: alias => array of attribute_codes
*
@@ -262,6 +289,8 @@ class DBObjectSet implements iDBObjectSetIterator
/**
* Create a set (in-memory) containing just the given object
*
* @internal
*
* @param \DBobject $oObject
*
* @return \DBObjectSet The singleton set
@@ -278,6 +307,8 @@ class DBObjectSet implements iDBObjectSetIterator
/**
* Create an empty set (in-memory), for the given class (and its subclasses) of objects
*
* @internal
*
* @param string $sClass The class (or an ancestor) for the objects to be added in this set
*
* @return \DBObjectSet The empty set
@@ -297,6 +328,8 @@ class DBObjectSet implements iDBObjectSetIterator
/**
* Create a set (in-memory) with just one column (i.e. one object per row) and filled with the given array of objects
*
* @internal
*
* @param string $sClass The class of the objects (must be a common ancestor to all objects in the set)
* @param array $aObjects The list of objects to add into the set
*
@@ -314,9 +347,11 @@ class DBObjectSet implements iDBObjectSetIterator
/**
* Create a set in-memory with several classes of objects per row (with one alias per "column")
*
* Limitation:
* **Limitation:**
* The filter/OQL query representing such a set can not be rebuilt (only the first column will be taken into account)
*
* @internal
*
* @param array $aClasses Format: array of (alias => class)
* @param array $aObjects Format: array of (array of (classalias => object))
*
@@ -345,6 +380,9 @@ class DBObjectSet implements iDBObjectSetIterator
}
/**
*
* @internal
*
* @param $oObject
* @param string $sLinkSetAttCode
* @param string $sExtKeyToRemote
@@ -371,11 +409,15 @@ class DBObjectSet implements iDBObjectSetIterator
}
/**
* Fetch all as array of DBObject
*
* Note: After calling this method, the set cursor will be at the end of the set. You might want to rewind it.
*
* @api
*
* @param bool $bWithId
*
* @return array
* @return DBObject[]
*
* @throws \Exception
* @throws \CoreException
@@ -401,7 +443,14 @@ class DBObjectSet implements iDBObjectSetIterator
}
/**
* @return array
* Fetch all as a structured array
*
* Unlike ToArray, ToArrayOfValues return the objects as an array.
* Only the scalar values will be presents (see AttributeDefinition::IsScalar())
*
* @api
*
* @return array[]
*
* @throws \Exception
* @throws \CoreException
@@ -773,6 +822,7 @@ class DBObjectSet implements iDBObjectSetIterator
* May actually perform the SQL query SELECT COUNT... if the set was not previously loaded, or loaded with a
* SetLimit
*
* @api
* @return int The total number of rows for this set.
*
* @throws \CoreException
@@ -796,11 +846,13 @@ class DBObjectSet implements iDBObjectSetIterator
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ??
}
/** Check if the count exceeds a given limit
/**
* Check if the count exceeds a given limit
*
* @param $iLimit
*
* @return bool
*
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
@@ -831,11 +883,13 @@ class DBObjectSet implements iDBObjectSetIterator
return ($iCount > $iLimit);
}
/** Count only up to the given limit
/**
* Count only up to the given limit
*
* @param $iLimit
*
* @return int
*
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
@@ -877,9 +931,11 @@ class DBObjectSet implements iDBObjectSetIterator
}
/**
* Fetch the object (with the given class alias) at the current position in the set and move the cursor to the next position.
* Fetch an object (with the given class alias) at the current position in the set and move the cursor to the next position.
*
* @param string $sRequestedClassAlias The class alias to fetch (if there are several objects/classes per row)
* @api
*
* @param string $sRequestedClassAlias The class alias to fetch (defaults to the first selected class)
*
* @return \DBObject The fetched object or null when at the end
*
@@ -933,6 +989,8 @@ class DBObjectSet implements iDBObjectSetIterator
/**
* Fetch the whole row of objects (if several classes have been specified in the query) and move the cursor to the next position
*
* @api
*
* @return array An associative with the format 'classAlias' => $oObj representing the current row of the set. Returns null when at the end.
*
* @throws \CoreException
@@ -981,8 +1039,10 @@ class DBObjectSet implements iDBObjectSetIterator
/**
* Position the cursor (for iterating in the set) to the first position (equivalent to Seek(0))
*
* @throws \Exception
*
* @api
*
* @throws \Exception
*/
public function Rewind()
{
@@ -1200,9 +1260,9 @@ class DBObjectSet implements iDBObjectSetIterator
* @param \DBObjectSet $oObjectSet
*
* @return \DBObjectSet The "delta" set.
*
* @throws \Exception
* @throws \CoreException
*
* @throws \Exception
* @throws \CoreException
*/
public function CreateDelta(DBObjectSet $oObjectSet)
{
@@ -1445,6 +1505,8 @@ class DBObjectSet implements iDBObjectSetIterator
/**
* Helper function to perform a custom sort of a hash array
*
* @internal
*/
function HashCountComparison($a, $b) // Sort descending on 'count'
{
@@ -1464,6 +1526,11 @@ function HashCountComparison($a, $b) // Sort descending on 'count'
* LIMITATIONS:
* - only DBObjectSets with one column (i.e. one class of object selected) are supported
* - the first set must be the one loaded from the database
*
* @internal
*
* @package iTopORM
*
*/
class DBObjectSetComparator
{
@@ -1508,6 +1575,8 @@ class DBObjectSetComparator
/**
* Builds the lists of fingerprints and initializes internal structures, if it was not already done
*
* @internal
*
* @throws \CoreException
*/
protected function ComputeFingerprints()
@@ -1557,6 +1626,9 @@ class DBObjectSetComparator
/**
* Tells if the sets are equivalent or not. Returns as soon as the first difference is found.
*
* @internal
*
* @return boolean true if the set have an equivalent content, false otherwise
*
* @throws \CoreException
@@ -1603,8 +1675,10 @@ class DBObjectSetComparator
/**
* Get the list of differences between the two sets. In ordeer to write back into the database only the minimum changes
* THE FIRST SET MUST BE THE ONE LOADED FROM THE DATABASE
* Returns a hash: 'added' => DBObject(s), 'removed' => DBObject(s), 'modified' => DBObjects(s)
* @return array
*
* @internal
*
* @return array 'added' => DBObject(s), 'removed' => DBObject(s), 'modified' => DBObjects(s)
*
* @throws \Exception
* @throws \CoreException
@@ -1659,7 +1733,9 @@ class DBObjectSetComparator
}
/**
* Helpr to clone (in memory) an object and to apply to it the values taken from a second object
* Helper to clone (in memory) an object and to apply to it the values taken from a second object
*
* @internal
*
* @param \DBObject $oObjToClone
* @param \DBObject $oObjWithValues
@@ -1682,4 +1758,4 @@ class DBObjectSetComparator
}
return $oObj;
}
}
}

View File

@@ -22,24 +22,35 @@ require_once('dbunionsearch.class.php');
/**
* An object search
*
*
* DBSearch provides an API that leverage the possibility to construct a search against iTop's persisted objects.
* In order to do so, it let you declare the classes you want to fetch, the conditions you want to apply, ...
*
* Note: in the ancient times of iTop, a search was named after DBObjectSearch.
* When the UNION has been introduced, it has been decided to:
* - declare a hierarchy of search classes, with two leafs :
* - one class to cope with a single query (A JOIN B... WHERE...)
* - and the other to cope with several queries (query1 UNION query2)
* - in order to preserve forward/backward compatibility of the existing modules
* - keep the name of DBObjectSearch even if it a little bit confusing
* - do not provide a type-hint for function parameters defined in the modules
* - leave the statements DBObjectSearch::FromOQL in the modules, though DBSearch is more relevant
* When the UNION has been introduced, it has been decided to:
* * declare a hierarchy of search classes : `DBObjectSearch` & `DBUnionSearch`
* * DBObjectSearch cope with single query (A JOIN B... WHERE...)
* * DBUnionSearch cope with several queries (query1 UNION query2)
* * in order to preserve forward/backward compatibility of the existing modules
* * keep the name of DBObjectSearch even if it a little bit confusing
* * do not provide a type-hint for function parameters defined in the modules
* * leave the statements DBObjectSearch::FromOQL in the modules, though DBSearch is more relevant
*
* @copyright Copyright (C) 2015-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*
*
* @package iTopORM
* @api
* @see DBObjectSearch::__construct()
* @see DBUnionSearch::__construct()
*/
abstract class DBSearch
{
/** @internal */
const JOIN_POINTING_TO = 0;
/** @internal */
const JOIN_REFERENCED_BY = 1;
protected $m_bNoContextParameters = false;
@@ -47,14 +58,23 @@ abstract class DBSearch
protected $m_bArchiveMode = false;
protected $m_bShowObsoleteData = true;
/**
* DBSearch constructor.
*
* @api
* @see DBSearch::FromOQL()
*/
public function __construct()
{
$this->Init();
}
/**
* called by the constructor
* @internal Set the obsolete and archive modes to the default ones
*/
protected function Init()
{
// Set the obsolete and archive modes to the default ones
$this->m_bArchiveMode = utils::IsArchiveMode();
$this->m_bShowObsoleteData = true;
}
@@ -62,6 +82,8 @@ abstract class DBSearch
/**
* Perform a deep clone (as opposed to "clone" which does copy a reference to the underlying objects)
*
* @internal
*
* @return \DBSearch
**/
public function DeepClone()
@@ -69,22 +91,62 @@ abstract class DBSearch
return unserialize(serialize($this)); // Beware this serializes/unserializes the search and its parameters as well
}
/**
* whether or not some information should be hidden to the current user.
*
* @api
* @see IsAllDataAllowed()
*
* @return mixed
*/
abstract public function AllowAllData();
/**
* Current state of AllowAllData
*
* @internal
* @see AllowAllData()
*
* @return mixed
*/
abstract public function IsAllDataAllowed();
/**
* Should the archives be fetched
*
* @internal
*
* @param $bEnable
*/
public function SetArchiveMode($bEnable)
{
$this->m_bArchiveMode = $bEnable;
}
/**
* @internal
* @return bool
*/
public function GetArchiveMode()
{
return $this->m_bArchiveMode;
}
/**
* Should the obsolete data be fetched
*
* @internal
* @param $bShow
*/
public function SetShowObsoleteData($bShow)
{
$this->m_bShowObsoleteData = $bShow;
}
/**
* @internal
* @return bool
*/
public function GetShowObsoleteData()
{
if ($this->m_bArchiveMode || $this->IsAllDataAllowed())
@@ -99,14 +161,36 @@ abstract class DBSearch
return $bRet;
}
/**
* @internal
*/
public function NoContextParameters() {$this->m_bNoContextParameters = true;}
/**
* @internal
* @return bool
*/
public function HasContextParameters() {return $this->m_bNoContextParameters;}
/**
* @internal
*
* @param $sPluginClass
* @param $sProperty
* @param $value
*/
public function SetModifierProperty($sPluginClass, $sProperty, $value)
{
$this->m_aModifierProperties[$sPluginClass][$sProperty] = $value;
}
/**
* @internal
*
* @param $sPluginClass
*
* @return array|mixed
*/
public function GetModifierProperties($sPluginClass)
{
if (array_key_exists($sPluginClass, $this->m_aModifierProperties))
@@ -119,18 +203,44 @@ abstract class DBSearch
}
}
/**
* @internal
* @param $sAlias
*
* @return mixed
*/
abstract public function GetClassName($sAlias);
/**
* @internal
* @return mixed
*/
abstract public function GetClass();
/**
* @internal
* @return mixed
*/
abstract public function GetClassAlias();
/**
* Change the class (only subclasses are supported as of now, because the conditions must fit the new class)
* Defaults to the first selected class (most of the time it is also the first joined class
*/
* Change the class
*
* Defaults to the first selected class (most of the time it is also the first joined class
* only subclasses are supported as of now, because the conditions must fit the new class
*
* @internal
*/
abstract public function ChangeClass($sNewClass, $sAlias = null);
/**
* @internal
* @return mixed
*/
abstract public function GetSelectedClasses();
/**
* @internal
* @param array $aSelectedClasses array of aliases
* @throws CoreException
*/
@@ -139,64 +249,207 @@ abstract class DBSearch
/**
* Change any alias of the query tree
*
* @internal
*
* @param $sOldName
* @param $sNewName
* @return bool True if the alias has been found and changed
*/
abstract public function RenameAlias($sOldName, $sNewName);
/**
* @internal
* @return mixed
*/
abstract public function IsAny();
/**
* @deprecated use ToOQL() instead
* @internal
* @return string
*/
public function Describe(){return 'deprecated - use ToOQL() instead';}
/**
* @deprecated use ToOQL() instead
* @internal
* @return string
*/
public function DescribeConditionPointTo($sExtKeyAttCode, $aPointingTo){return 'deprecated - use ToOQL() instead';}
/**
* @deprecated use ToOQL() instead
* @internal
* @return string
*/
public function DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode){return 'deprecated - use ToOQL() instead';}
/**
* @deprecated use ToOQL() instead
* @internal
* @return string
*/
public function DescribeConditionRelTo($aRelInfo){return 'deprecated - use ToOQL() instead';}
/**
* @deprecated use ToOQL() instead
* @internal
* @return string
*/
public function DescribeConditions(){return 'deprecated - use ToOQL() instead';}
/**
* @deprecated use ToOQL() instead
* @internal
* @return string
*/
public function __DescribeHTML(){return 'deprecated - use ToOQL() instead';}
/**
* @internal
* @return mixed
*/
abstract public function ResetCondition();
/**
* add $oExpression as a OR
*
* @api
* @see DBSearch::AddConditionExpression()
*
* @param Expression $oExpression
*
* @return mixed
*/
abstract public function MergeConditionExpression($oExpression);
/**
* add $oExpression as a AND
*
* @api
* @see DBSearch::MergeConditionExpression()
*
* @param Expression $oExpression
*
* @return mixed
*/
abstract public function AddConditionExpression($oExpression);
/**
* Condition on the friendlyname
*
* Restrict the query to only the corresponding selected class' friendlyname
*
* @internal
*
* @param string $sName the desired friendlyname
*
* @return mixed
*/
abstract public function AddNameCondition($sName);
/**
* Add a condition
*
* This is the simplest way to express a AND condition. For complex use cases, use MergeConditionExpression or AddConditionExpression instead
*
* @api
*
* @param string $sFilterCode
* @param mixed $value
* @param string $sOpCode operator to use : '=' (default), '!=', 'IN', 'NOT IN'
*
* @throws \CoreException
*
*/
abstract public function AddCondition($sFilterCode, $value, $sOpCode = null);
/**
* Specify a condition on external keys or link sets
* @param sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
*
* @internal
*
* @param string $sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
* Example: infra_list->ci_id->location_id->country
* @param value The value to match (can be an array => IN(val1, val2...)
* @param mixed $value The value to match (can be an array => IN(val1, val2...)
* @return void
*/
abstract public function AddConditionAdvanced($sAttSpec, $value);
/**
* @internal
*
* @param string $sFullText
*
* @return mixed
*/
abstract public function AddCondition_FullText($sFullText);
/**
* Perform a join, the remote class being matched by the mean of its primary key
*
* The join is performed
* * from the searched class, based on the $sExtKeyAttCode attribute
* * against the oFilter searched class, based on its primary key
* Note : if several classes have already being joined (SELECT a join b ON...), the first joined class (a in the example) is considered as being the searched class.
*
* @api
* @see AddCondition_ReferencedBy()
*
* @param DBObjectSearch $oFilter
* @param $sExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
* @param string $sExtKeyAttCode
* @param int $iOperatorCode the comparison operator to use. For the list of all possible values, see the constant defined in core/oql/oqlquery.class.inc.php
* @param array|null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed in the newly attached oFilter (in case of collisions between the two filters)
*
* @throws CoreException
* @throws CoreWarning
*/
abstract public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null);
/**
* Inverse operation of AddCondition_PointingTo
*
* The join is performed
* * from the olFilter searched class, based on the $sExtKeyAttCode attribute
* * against the searched class, based on its primary key
* Note : if several classes have already being joined (SELECT a join b ON...), the first joined class (a in the example) is considered as being the searched class.
*
*
* @api
* @see AddCondition_PointingTo()
*
* @param DBObjectSearch $oFilter
* @param $sForeignExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
* @param array|null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed in the newly attached oFilter (in case of collisions between the two filters)
*/
abstract public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null);
/**
* Filter the result
*
* The filter is performed by returning only the values in common with the given $oFilter
* The impact on the resulting query performance/viability can be significant.
*
* @internal
*
* @param DBSearch $oFilter
*
* @return mixed
*/
abstract public function Intersect(DBSearch $oFilter);
/**
* @param DBSearch $oFilter
* @param integer $iDirection
* @param string $sExtKeyAttCode
* @param integer $iOperatorCode
* @param array &$RealisasingMap Map of aliases from the attached query, that could have been renamed by the optimization process
* @return DBSearch
*/
/**
* Perform a join
*
* The join is performed against $oFilter selected class using $sExtKeyAttCode of the current selected class
*
* @internal
*
* @param DBSearch $oFilter The join is performed against $oFilter selected class
* @param integer $iDirection can be either DBSearch::JOIN_POINTING_TO or DBSearch::JOIN_REFERENCED_BY
* @param string $sExtKeyAttCode The join is performed against $sExtKeyAttCode wetheir it is compared aginst the current DBSearch or $oFilter depend of $iDirection
* @param integer $iOperatorCode See DBSearch::AddCondition_PointingTo()
* @param array|null $aRealiasingMap Map of aliases from the attached query, that could have been renamed by the optimization process
*
* @return DBSearch
* @throws CoreException
* @throws CoreWarning
*/
public function Join(DBSearch $oFilter, $iDirection, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
{
$oSourceFilter = $this->DeepClone();
@@ -231,21 +484,68 @@ abstract class DBSearch
return $oRet;
}
/**
* Set the internal params.
*
* If any params pre-existed, they are lost.
*
* @internal
*
* @param mixed[string] $aParams array of mixed params index by string name
*
* @return mixed
*/
abstract public function SetInternalParams($aParams);
/**
* @internal
* @return mixed
*/
abstract public function GetInternalParams();
/**
* @internal
*
* @param bool $bExcludeMagicParams
*
* @return mixed
*/
abstract public function GetQueryParams($bExcludeMagicParams = true);
/**
* @internal
* @return mixed
*/
abstract public function ListConstantFields();
/**
* Turn the parameters (:xxx) into scalar values in order to easily
* serialize a search
* Turn the parameters (:xxx) into scalar values
*
* The goal is to easily serialize a search
*
* @internal
*
* @param array $aArgs
*
* @return string
*/
abstract public function ApplyParameters($aArgs);
/**
* Convert a query to a string representation
*
* This operation can be revert back to a DBSearch using DBSearch::unserialize()
*
* @api
* @see DBSearch::unserialize()
*
* @param bool $bDevelopParams
* @param array $aContextParams
*
* @return false|string
* @throws ArchivedObjectException
* @throws CoreException
*/
public function serialize($bDevelopParams = false, $aContextParams = array())
{
$aQueryParams = $this->GetQueryParams();
@@ -293,6 +593,10 @@ abstract class DBSearch
}
/**
* Convert a serialized query back to an instance of DBSearch
*
* @api
*
* @param string $sValue Serialized OQL query
*
* @return \DBSearch
@@ -336,11 +640,13 @@ abstract class DBSearch
/**
* Create a new DBObjectSearch from $oSearch with a new alias $sAlias
*
* Note : This has not be tested with UNION queries.
* @internal Note : This has not be tested with UNION queries.
*
* @param DBSearch $oSearch
* @param string $sAlias
* @param string $sAlias
*
* @return DBObjectSearch
* @throws CoreException
*/
static public function CloneWithAlias(DBSearch $oSearch, $sAlias)
{
@@ -349,12 +655,37 @@ abstract class DBSearch
return $oSearchWithAlias;
}
/**
* Convert the DBSearch to an OQL representation
*
* @api
* @see DBSearch::FromOQL()
*
* @param bool $bDevelopParams
* @param null $aContextParams
* @param bool $bWithAllowAllFlag
*
* @return mixed
*/
abstract public function ToOQL($bDevelopParams = false, $aContextParams = null, $bWithAllowAllFlag = false);
static protected $m_aOQLQueries = array();
// Do not filter out depending on user rights
// In particular when we are currently in the process of evaluating the user rights...
/**
* FromOQL with AllowAllData enabled
*
* The goal is to not filter out depending on user rights.
* In particular when we are currently in the process of evaluating the user rights...
*
* @internal
* @see DBSearch::FromOQL()
*
* @param string $sQuery
* @param null $aParams
*
* @return DBSearch
* @throws OQLException
*/
static public function FromOQL_AllData($sQuery, $aParams = null)
{
$oRes = self::FromOQL($sQuery, $aParams);
@@ -363,9 +694,19 @@ abstract class DBSearch
}
/**
* @param string $sQuery
* @param array $aParams
* @return self
* Create a new DBSearch from the given OQL.
*
* This is the simplest way to create a DBSearch.
* For almost every cases, this is the easiest way.
*
* @api
* @see DBSearch::ToOQL()
*
* @param string $sQuery The OQL to convert to a DBSearch
* @param mixed[string] $aParams array of <mixed> params index by <string> name
*
* @return DBObjectSearch|DBUnionSearch
*
* @throws OQLException
*/
static public function FromOQL($sQuery, $aParams = null)
@@ -442,14 +783,20 @@ abstract class DBSearch
}
/**
* Fetch the result has an array structure.
*
* Alternative to object mapping: the data are transfered directly into an array
* This is 10 times faster than creating a set of objects, and makes sense when optimization is required
* But this speed comes at the cost of not obtaining the easy to manipulates DBObject instances but simple array structure.
*
* @internal
*
* @param array $aColumns
* @param array $aColumns The columns you'd like to fetch.
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
* @param array $aArgs
*
* @return array|void
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
@@ -506,7 +853,11 @@ abstract class DBSearch
protected static $m_aQueryStructCache = array();
/** Generate a Group By SQL request from a search
/**
* Generate a Group By SQL query from the current search
*
* @internal
*
* @param array $aArgs
* @param array $aGroupByExpr array('alias' => Expression)
* @param bool $bExcludeNullValues
@@ -514,7 +865,9 @@ abstract class DBSearch
* @param array $aOrderBy array('alias' => bool) true = ASC false = DESC
* @param int $iLimitCount
* @param int $iLimitStart
*
* @return string SQL query generated
*
* @throws Exception
*/
public function MakeGroupByQuery($aArgs, $aGroupByExpr, $bExcludeNullValues = false, $aSelectExpr = array(), $aOrderBy = array(), $iLimitCount = 0, $iLimitStart = 0)
@@ -590,6 +943,10 @@ abstract class DBSearch
/**
* Generate a SQL query from the current search
*
* @internal
*
* @param array|hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
* @param array $aArgs
* @param null $aAttToLoad
@@ -684,9 +1041,33 @@ abstract class DBSearch
return $sRes;
}
/**
* @internal
* @return mixed
*/
protected abstract function IsDataFiltered();
/**
* @internal
* @return mixed
*/
protected abstract function SetDataFiltered();
/**
* @internal
*
* @param $aOrderBy
* @param $aArgs
* @param $aAttToLoad
* @param $aExtendedDataSpec
* @param $iLimitCount
* @param $iLimitStart
* @param $bGetCount
* @param null $aGroupByExpr
* @param null $aSelectExpr
*
* @return mixed
*/
protected function GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $aGroupByExpr = null, $aSelectExpr = null)
{
$oSearch = $this;
@@ -727,18 +1108,46 @@ abstract class DBSearch
return $oSQLQuery;
}
/**
* @internal
*
* @param $aAttToLoad
* @param $bGetCount
* @param null $aGroupByExpr
* @param null $aSelectedClasses
* @param null $aSelectExpr
*
* @return mixed
*/
public abstract function GetSQLQueryStructure(
$aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null
);
/**
* Get the current search conditions
*
* @internal
* @see DBSearch $m_oSearchCondition
*
* @return \Expression
*/
public abstract function GetCriteria();
/**
* Shortcut to add efficient IN condition
*
* @internal
*
* @param $sFilterCode
* @param $aValues
* @param bool $bPositiveMatch if true a `IN` is performed, if false, a `NOT IN` is performed
*
* @return mixed
*/
public abstract function AddConditionForInOperatorUsingParam($sFilterCode, $aValues, $bPositiveMatch = true);
/**
* @internal
* @return string a unique param name
*/
protected function GenerateUniqueParamName() {
@@ -759,36 +1168,78 @@ abstract class DBSearch
protected static $m_bIndentQueries = false;
protected static $m_bOptimizeQueries = false;
/**
* @internal
*/
public static function StartDebugQuery()
{
$aBacktrace = debug_backtrace();
self::$m_bDebugQuery = true;
}
/**
* @internal
*/
public static function StopDebugQuery()
{
self::$m_bDebugQuery = false;
}
/**
* @internal
*
* @param bool $bEnabled
* @param bool $bUseAPC
* @param int $iTimeToLive
*/
public static function EnableQueryCache($bEnabled, $bUseAPC, $iTimeToLive = 3600)
{
self::$m_bQueryCacheEnabled = $bEnabled;
self::$m_bUseAPCCache = $bUseAPC;
self::$m_iQueryCacheTTL = $iTimeToLive;
}
/**
* @internal
* @param $bEnabled
*/
public static function EnableQueryTrace($bEnabled)
{
self::$m_bTraceQueries = $bEnabled;
}
/**
* @internal
* @param $bEnabled
*/
public static function EnableQueryIndentation($bEnabled)
{
self::$m_bIndentQueries = $bEnabled;
}
/**
* @internal
* @param $bEnabled
*/
public static function EnableOptimizeQuery($bEnabled)
{
self::$m_bOptimizeQueries = $bEnabled;
}
/**
* @internal
*
* @param $aOrderBy
* @param $aArgs
* @param $aAttToLoad
* @param $aExtendedDataSpec
* @param $iLimitCount
* @param $iLimitStart
* @param $bGetCount
* @param $sSql
*
* @throws MySQLException
*/
protected function AddQueryTraceSelect($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $sSql)
{
if (self::$m_bTraceQueries)
@@ -808,7 +1259,16 @@ abstract class DBSearch
self::AddQueryTrace($aQueryData, $sOql, $sSql);
}
}
/**
* @internal
*
* @param $aArgs
* @param $aGroupByExpr
* @param $sSql
*
* @throws MySQLException
*/
protected function AddQueryTraceGroupBy($aArgs, $aGroupByExpr, $sSql)
{
if (self::$m_bTraceQueries)
@@ -824,6 +1284,15 @@ abstract class DBSearch
}
}
/**
* @internal
*
* @param $aQueryData
* @param $sOql
* @param $sSql
*
* @throws MySQLException
*/
protected static function AddQueryTrace($aQueryData, $sOql, $sSql)
{
if (self::$m_bTraceQueries)
@@ -854,6 +1323,9 @@ abstract class DBSearch
}
}
/**
* @internal
*/
public static function RecordQueryTrace()
{
if (!self::$m_bTraceQueries)
@@ -914,6 +1386,10 @@ abstract class DBSearch
file_put_contents($sAllQueries, $sLog);
}
/**
* @internal
* @param $value
*/
protected static function DbgTrace($value)
{
if (!self::$m_bDebugQuery)
@@ -949,7 +1425,9 @@ abstract class DBSearch
/**
* Experimental!
* todo: implement the change tracking
* @todo implement the change tracking
*
* @internal
*
* @param $bArchive
* @throws Exception
@@ -1025,6 +1503,9 @@ abstract class DBSearch
}
}
/**
* @internal
*/
public function UpdateContextFromUser()
{
$this->SetShowObsoleteData(utils::ShowObsoleteData());

View File

@@ -18,10 +18,21 @@
/**
* A union of DBObjectSearches
* A union of DBObjectSearches
*
* This search class represent an union over a collection of DBObjectSearch.
* For clarity purpose, since only the constructor vary between DBObjectSearch and DBUnionSearch, all the API is documented on the common ancestor: DBSearch
* Please refer to DBSearch's documentation
*
* @copyright Copyright (C) 2015-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*
*
* @package iTopORM
* @phpdoc-tuning-exclude-inherited this tag prevent PHPdoc from displaying inherited methods. This is done in order to force the API doc. location into DBSearch only.
* @api
* @see DBSearch
* @see DBObjectSearch
*/
class DBUnionSearch extends DBSearch
@@ -29,6 +40,15 @@ class DBUnionSearch extends DBSearch
protected $aSearches; // source queries
protected $aSelectedClasses; // alias => classes (lowest common ancestors) computed at construction
/**
* DBUnionSearch constructor.
*
* @api
*
* @param $aSearches
*
* @throws CoreException
*/
public function __construct($aSearches)
{
if (count ($aSearches) == 0)

View File

@@ -78,6 +78,7 @@ EOF;
}
EOF;
SetupUtils::builddir(dirname($sFilePath));
file_put_contents($sFilePath, $content);
}
}

View File

@@ -34,43 +34,51 @@ abstract class HTMLSanitizer
/**
* Sanitize an HTML string with the configured sanitizer, falling back to HTMLDOMSanitizer in case of Exception or invalid configuration
*
* @param string $sHTML
* @param string $sConfigKey
*
* @return string
* @noinspection SelfClassReferencingInspection
*/
public static function Sanitize($sHTML)
public static function Sanitize($sHTML, $sConfigKey = 'html_sanitizer')
{
$sSanitizerClass = MetaModel::GetConfig()->Get('html_sanitizer');
if(!class_exists($sSanitizerClass))
{
IssueLog::Warning('The configured "html_sanitizer" class "'.$sSanitizerClass.'" is not a valid class. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = MetaModel::GetConfig()->Get($sConfigKey);
if (!class_exists($sSanitizerClass)) {
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a valid class. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = 'HTMLDOMSanitizer';
} else if (false === is_subclass_of($sSanitizerClass, HTMLSanitizer::class)) {
if ($sConfigKey === 'html_sanitizer') {
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of HTMLSanitizer. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = 'HTMLDOMSanitizer';
}
if ($sConfigKey === 'svg_sanitizer') {
IssueLog::Error('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of '.HTMLSanitizer::class.' ! Won\'t sanitize string.');
return $sHTML;
}
}
else if(!is_subclass_of($sSanitizerClass, 'HTMLSanitizer'))
{
IssueLog::Warning('The configured "html_sanitizer" class "'.$sSanitizerClass.'" is not a subclass of HTMLSanitizer. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = 'HTMLDOMSanitizer';
}
try
{
try {
$oSanitizer = new $sSanitizerClass();
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
}
catch(Exception $e)
{
if($sSanitizerClass != 'HTMLDOMSanitizer')
{
IssueLog::Warning('Failed to sanitize an HTML string with "'.$sSanitizerClass.'". The following exception occured: '.$e->getMessage());
IssueLog::Warning('Will try to sanitize with HTMLDOMSanitizer.');
// try again with the HTMLDOMSanitizer
$oSanitizer = new HTMLDOMSanitizer();
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
}
else
{
IssueLog::Error('Failed to sanitize an HTML string with "HTMLDOMSanitizer". The following exception occured: '.$e->getMessage());
IssueLog::Error('The HTML will NOT be sanitized.');
$sCleanHTML = $sHTML;
catch(Exception $e) {
if ($sConfigKey === 'html_sanitizer') {
if ($sSanitizerClass !== HTMLDOMSanitizer::class) {
IssueLog::Warning('Failed to sanitize an HTML string with "'.$sSanitizerClass.'". The following exception occured: '.$e->getMessage());
IssueLog::Warning('Will try to sanitize with HTMLDOMSanitizer.');
// try again with the HTMLDOMSanitizer
$oSanitizer = new HTMLDOMSanitizer();
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
} else {
IssueLog::Error('Failed to sanitize an HTML string with "HTMLDOMSanitizer". The following exception occured: '.$e->getMessage());
IssueLog::Error('The HTML will NOT be sanitized.');
$sCleanHTML = $sHTML;
}
} else {
IssueLog::Error('Failed to sanitize string with "'.$sSanitizerClass.'", will return original value ! Exception: '.$e->getMessage());
$sCleanHTML = $sHTML;
}
}
return $sCleanHTML;
@@ -94,67 +102,167 @@ class HTMLNullSanitizer extends HTMLSanitizer
{
return $sHTML;
}
}
/**
* A standard-compliant HTMLSanitizer based on the HTMLPurifier library by Edward Z. Yang
* Complete but quite slow
* http://htmlpurifier.org
* Common implementation for sanitizer using DOM parsing
*/
/*
class HTMLPurifierSanitizer extends HTMLSanitizer
abstract class DOMSanitizer extends HTMLSanitizer
{
protected static $oPurifier = null;
public function __construct()
{
if (self::$oPurifier == null)
{
$sLibPath = APPROOT.'lib/htmlpurifier/HTMLPurifier.auto.php';
if (!file_exists($sLibPath))
{
throw new Exception("Missing library '$sLibPath', cannot use HTMLPurifierSanitizer.");
}
require_once($sLibPath);
$oPurifierConfig = HTMLPurifier_Config::createDefault();
$oPurifierConfig->set('Core.Encoding', 'UTF-8'); // defaults to 'UTF-8'
$oPurifierConfig->set('HTML.Doctype', 'XHTML 1.0 Strict'); // defaults to 'XHTML 1.0 Transitional'
$oPurifierConfig->set('URI.AllowedSchemes', array (
'http' => true,
'https' => true,
'data' => true, // This one is not present by default
));
$sPurifierCache = APPROOT.'data/HTMLPurifier';
if (!is_dir($sPurifierCache))
{
mkdir($sPurifierCache);
}
if (!is_dir($sPurifierCache))
{
throw new Exception("Could not create the cache directory '$sPurifierCache'");
}
$oPurifierConfig->set('Cache.SerializerPath', $sPurifierCache); // no trailing slash
self::$oPurifier = new HTMLPurifier($oPurifierConfig);
}
}
/** @var DOMDocument */
protected $oDoc;
abstract public function GetTagsWhiteList();
abstract public function GetTagsBlackList();
abstract public function GetAttrsWhiteList();
abstract public function GetAttrsBlackList();
abstract public function GetStylesWhiteList();
public function DoSanitize($sHTML)
{
$sCleanHtml = self::$oPurifier->purify($sHTML);
return $sCleanHtml;
$this->oDoc = new DOMDocument();
$this->oDoc->preserveWhitespace = true;
// MS outlook implements empty lines by the mean of <p><o:p></o:p></p>
// We have to transform that into <p><br></p> (which is how Thunderbird implements empty lines)
// Unfortunately, DOMDocument::loadHTML does not take the tag namespaces into account (once loaded there is no way to know if the tag did have a namespace)
// therefore we have to do the transformation upfront
$sHTML = preg_replace('@<o:p>(\s|&nbsp;)*</o:p>@', '<br>', $sHTML);
$this->LoadDoc($sHTML);
$this->CleanNode($this->oDoc);
$sCleanHtml = $this->PrintDoc();
return $sCleanHtml;
}
abstract public function LoadDoc($sHTML);
/**
* @return string cleaned source
* @uses \DOMSanitizer::oDoc
*/
abstract public function PrintDoc();
protected function CleanNode(DOMNode $oElement)
{
$aAttrToRemove = array();
// Gather the attributes to remove
if ($oElement->hasAttributes()) {
foreach ($oElement->attributes as $oAttr) {
$sAttr = strtolower($oAttr->name);
if ((false === empty($this->GetAttrsBlackList()))
&& (in_array($sAttr, $this->GetAttrsBlackList(), true))) {
$aAttrToRemove[] = $oAttr->name;
} else if ((false === empty($this->GetTagsWhiteList()))
&& (false === in_array($sAttr, $this->GetTagsWhiteList()[strtolower($oElement->tagName)]))) {
$aAttrToRemove[] = $oAttr->name;
} else if (!$this->IsValidAttributeContent($sAttr, $oAttr->value)) {
// Invalid content
$aAttrToRemove[] = $oAttr->name;
} else if ($sAttr == 'style') {
// Special processing for style tags
$sCleanStyle = $this->CleanStyle($oAttr->value);
if ($sCleanStyle == '') {
// Invalid content
$aAttrToRemove[] = $oAttr->name;
} else {
$oElement->setAttribute($oAttr->name, $sCleanStyle);
}
}
}
// Now remove them
foreach($aAttrToRemove as $sName)
{
$oElement->removeAttribute($sName);
}
}
if ($oElement->hasChildNodes())
{
$aChildElementsToRemove = array();
// Gather the child noes to remove
foreach($oElement->childNodes as $oNode) {
if ($oNode instanceof DOMElement) {
$sNodeTagName = strtolower($oNode->tagName);
}
if (($oNode instanceof DOMElement)
&& (false === empty($this->GetTagsBlackList()))
&& (in_array($sNodeTagName, $this->GetTagsBlackList(), true))) {
$aChildElementsToRemove[] = $oNode;
} else if (($oNode instanceof DOMElement)
&& (false === empty($this->GetTagsWhiteList()))
&& (false === array_key_exists($sNodeTagName, $this->GetTagsWhiteList()))) {
$aChildElementsToRemove[] = $oNode;
} else if ($oNode instanceof DOMComment) {
$aChildElementsToRemove[] = $oNode;
} else {
// Recurse
$this->CleanNode($oNode);
if (($oNode instanceof DOMElement) && (strtolower($oNode->tagName) == 'img')) {
InlineImage::ProcessImageTag($oNode);
}
}
}
// Now remove them
foreach($aChildElementsToRemove as $oDomElement)
{
$oElement->removeChild($oDomElement);
}
}
}
protected function IsValidAttributeContent($sAttributeName, $sValue)
{
if ((false === empty($this->GetAttrsBlackList()))
&& (in_array($sAttributeName, $this->GetAttrsBlackList(), true))) {
return true;
}
if (array_key_exists($sAttributeName, $this->GetAttrsWhiteList())) {
return preg_match($this->GetAttrsWhiteList()[$sAttributeName], $sValue);
}
return true;
}
protected function CleanStyle($sStyle)
{
if (empty($this->GetStylesWhiteList())) {
return $sStyle;
}
$aAllowedStyles = array();
$aItems = explode(';', $sStyle);
{
foreach ($aItems as $sItem) {
$aElements = explode(':', trim($sItem));
if (in_array(trim(strtolower($aElements[0])), $this->GetStylesWhiteList())) {
$aAllowedStyles[] = trim($sItem);
}
}
}
return implode(';', $aAllowedStyles);
}
}
*/
class HTMLDOMSanitizer extends HTMLSanitizer
class HTMLDOMSanitizer extends DOMSanitizer
{
protected $oDoc;
/**
* @var array
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
* @var array
*/
protected static $aTagsWhiteList = array(
'html' => array(),
@@ -203,6 +311,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
'q' => array(),
'hr' => array('style'),
'pre' => array(),
'center' => array(),
);
protected static $aAttrsWhiteList = array(
@@ -210,8 +319,8 @@ class HTMLDOMSanitizer extends HTMLSanitizer
);
/**
* @var array
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
* @var array
*/
protected static $aStylesWhiteList = array(
'background-color',
@@ -235,154 +344,199 @@ class HTMLDOMSanitizer extends HTMLSanitizer
'white-space',
);
public function GetTagsWhiteList()
{
return static::$aTagsWhiteList;
}
public function GetTagsBlackList()
{
return [];
}
public function GetAttrsWhiteList()
{
return static::$aAttrsWhiteList;
}
public function GetAttrsBlackList()
{
return [];
}
public function GetStylesWhiteList()
{
return static::$aStylesWhiteList;
}
public function __construct()
{
parent::__construct();
// Building href validation pattern from url and email validation patterns as the patterns are not used the same way in HTML content than in standard attributes value.
// eg. "foo@bar.com" vs "mailto:foo@bar.com?subject=Title&body=Hello%20world"
if (!array_key_exists('href', self::$aAttrsWhiteList))
{
if (!array_key_exists('href', self::$aAttrsWhiteList)) {
// Regular urls
$sUrlPattern = utils::GetConfig()->Get('url_validation_pattern');
// Mailto urls
$sMailtoPattern = '(mailto:(' . utils::GetConfig()->Get('email_validation_pattern') . ')(?:\?(?:subject|body)=([a-zA-Z0-9+\$_.-]*)(?:&(?:subject|body)=([a-zA-Z0-9+\$_.-]*))?)?)';
$sPattern = $sUrlPattern . '|' . $sMailtoPattern;
// Mailto urls
$sMailtoPattern = '(mailto:('.utils::GetConfig()->Get('email_validation_pattern').')(?:\?(?:subject|body)=([a-zA-Z0-9+\$_.-]*)(?:&(?:subject|body)=([a-zA-Z0-9+\$_.-]*))?)?)';
// Notification placeholders
// eg. $this->caller_id$, $this->hyperlink()$, $this->hyperlink(portal)$, $APP_URL$, $MODULES_URL$, ...
// Note: Authorize both $xxx$ and %24xxx%24 as the latter one is encoded when used in HTML attributes (eg. a[href])
$sPlaceholderPattern = '(\$|%24)[\w-]*(->[\w]*(\([\w-]*?\))?)?(\$|%24)';
$sPattern = $sUrlPattern.'|'.$sMailtoPattern.'|'.$sPlaceholderPattern;
$sPattern = '/'.str_replace('/', '\/', $sPattern).'/i';
self::$aAttrsWhiteList['href'] = $sPattern;
}
}
public function DoSanitize($sHTML)
public function LoadDoc($sHTML)
{
$this->oDoc = new DOMDocument();
$this->oDoc->preserveWhitespace = true;
// MS outlook implements empty lines by the mean of <p><o:p></o:p></p>
// We have to transform that into <p><br></p> (which is how Thunderbird implements empty lines)
// Unfortunately, DOMDocument::loadHTML does not take the tag namespaces into account (once loaded there is no way to know if the tag did have a namespace)
// therefore we have to do the transformation upfront
$sHTML = preg_replace('@<o:p>\s*</o:p>@', '<br>', $sHTML);
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified
$this->CleanNode($this->oDoc);
}
public function PrintDoc()
{
$oXPath = new DOMXPath($this->oDoc);
$sXPath = "//body";
$oNodesList = $oXPath->query($sXPath);
if ($oNodesList->length == 0)
{
if ($oNodesList->length == 0) {
// No body, save the whole document
$sCleanHtml = $this->oDoc->saveHTML();
}
else
{
} else {
// Export only the content of the body tag
$sCleanHtml = $this->oDoc->saveHTML($oNodesList->item(0));
// remove the body tag itself
$sCleanHtml = str_replace( array('<body>', '</body>'), '', $sCleanHtml);
$sCleanHtml = str_replace(array('<body>', '</body>'), '', $sCleanHtml);
}
return $sCleanHtml;
}
protected function CleanNode(DOMNode $oElement)
}
/**
* @since 2.6.5 2.7.6 3.0.0 N°4360
*/
class SVGDOMSanitizer extends DOMSanitizer
{
public function GetTagsWhiteList()
{
$aAttrToRemove = array();
// Gather the attributes to remove
if ($oElement->hasAttributes())
{
foreach($oElement->attributes as $oAttr)
{
$sAttr = strtolower($oAttr->name);
if (!in_array($sAttr, self::$aTagsWhiteList[strtolower($oElement->tagName)]))
{
// Forbidden (or unknown) attribute
$aAttrToRemove[] = $oAttr->name;
}
else if (!$this->IsValidAttributeContent($sAttr, $oAttr->value))
{
// Invalid content
$aAttrToRemove[] = $oAttr->name;
}
else if ($sAttr == 'style')
{
// Special processing for style tags
$sCleanStyle = $this->CleanStyle($oAttr->value);
if ($sCleanStyle == '')
{
// Invalid content
$aAttrToRemove[] = $oAttr->name;
}
else
{
$oElement->setAttribute($oAttr->name, $sCleanStyle);
}
}
}
// Now remove them
foreach($aAttrToRemove as $sName)
{
$oElement->removeAttribute($sName);
}
}
if ($oElement->hasChildNodes())
{
$aChildElementsToRemove = array();
// Gather the child noes to remove
foreach($oElement->childNodes as $oNode)
{
if (($oNode instanceof DOMElement) && (!array_key_exists(strtolower($oNode->tagName), self::$aTagsWhiteList)))
{
$aChildElementsToRemove[] = $oNode;
}
else if ($oNode instanceof DOMComment)
{
$aChildElementsToRemove[] = $oNode;
}
else
{
// Recurse
$this->CleanNode($oNode);
if (($oNode instanceof DOMElement) && (strtolower($oNode->tagName) == 'img'))
{
InlineImage::ProcessImageTag($oNode);
}
}
}
// Now remove them
foreach($aChildElementsToRemove as $oDomElement)
{
$oElement->removeChild($oDomElement);
}
}
return [];
}
protected function CleanStyle($sStyle)
/**
* @return string[]
* @link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/script
*/
public function GetTagsBlackList()
{
$aAllowedStyles = array();
$aItems = explode(';', $sStyle);
{
foreach($aItems as $sItem)
{
$aElements = explode(':', trim($sItem));
if (in_array(trim(strtolower($aElements[0])), static::$aStylesWhiteList))
{
$aAllowedStyles[] = trim($sItem);
}
}
}
return implode(';', $aAllowedStyles);
return [
'script',
];
}
protected function IsValidAttributeContent($sAttributeName, $sValue)
public function GetAttrsWhiteList()
{
if (array_key_exists($sAttributeName, self::$aAttrsWhiteList))
{
return preg_match(self::$aAttrsWhiteList[$sAttributeName], $sValue);
}
return true;
return [];
}
}
/**
* @return string[]
* @link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Events#document_event_attributes
*/
public function GetAttrsBlackList()
{
return [
'onbegin',
'onbegin',
'onrepeat',
'onabort',
'onerror',
'onerror',
'onscroll',
'onunload',
'oncopy',
'oncut',
'onpaste',
'oncancel',
'oncanplay',
'oncanplaythrough',
'onchange',
'onclick',
'onclose',
'oncuechange',
'ondblclick',
'ondrag',
'ondragend',
'ondragenter',
'ondragleave',
'ondragover',
'ondragstart',
'ondrop',
'ondurationchange',
'onemptied',
'onended',
'onerror',
'onfocus',
'oninput',
'oninvalid',
'onkeydown',
'onkeypress',
'onkeyup',
'onload',
'onloadeddata',
'onloadedmetadata',
'onloadstart',
'onmousedown',
'onmouseenter',
'onmouseleave',
'onmousemove',
'onmouseout',
'onmouseover',
'onmouseup',
'onmousewheel',
'onpause',
'onplay',
'onplaying',
'onprogress',
'onratechange',
'onreset',
'onresize',
'onscroll',
'onseeked',
'onseeking',
'onselect',
'onshow',
'onstalled',
'onsubmit',
'onsuspend',
'ontimeupdate',
'ontoggle',
'onvolumechange',
'onwaiting',
'onactivate',
'onfocusin',
'onfocusout',
];
}
public function GetStylesWhiteList()
{
return [];
}
public function LoadDoc($sHTML)
{
@$this->oDoc->loadXml($sHTML, LIBXML_NOBLANKS);
}
public function PrintDoc()
{
return $this->oDoc->saveXML();
}
}

View File

@@ -97,8 +97,9 @@ define('MYSQL_ENGINE', 'innodb');
/**
* (API) The objects definitions as well as their mapping to the database
* The objects definitions as well as their mapping to the database
*
* @api
* @package iTopORM
*/
abstract class MetaModel
@@ -528,13 +529,15 @@ abstract class MetaModel
/**
* @param string $sClass
* @param bool $bClassDefinitionOnly if true then will only return properties defined in the specified class on not the properties
* from its parent classes
*
* @return array
* @return array rule id as key, rule properties as value
* @throws \CoreException
*
* @since 2.6 N°659 uniqueness constraint
* @see #SetUniquenessRuleRootClass that fixes a specific 'root_class' property to know which class is root per rule
*/
final public static function GetUniquenessRules($sClass)
final public static function GetUniquenessRules($sClass, $bClassDefinitionOnly = false)
{
if (!isset(self::$m_aClassParams[$sClass]))
{
@@ -548,6 +551,11 @@ abstract class MetaModel
$aCurrentUniquenessRules = self::$m_aClassParams[$sClass]['uniqueness_rules'];
}
if ($bClassDefinitionOnly)
{
return $aCurrentUniquenessRules;
}
$sParentClass = self::GetParentClass($sClass);
if ($sParentClass)
{
@@ -581,6 +589,22 @@ abstract class MetaModel
return $aCurrentUniquenessRules;
}
/**
* @param string $sRootClass
* @param string $sRuleId
*
* @throws \CoreException
* @since 2.6.1 N°1918 (sous les pavés, la plage) initialize in 'root_class' property the class that has the first
* definition of the rule in the hierarchy
*/
final private static function SetUniquenessRuleRootClass($sRootClass, $sRuleId)
{
foreach (self::EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_ALL) as $sClass)
{
self::$m_aClassParams[$sClass]['uniqueness_rules'][$sRuleId]['root_class'] = $sClass;
}
}
/**
* @param string $sRuleId
* @param string $sLeafClassName
@@ -608,6 +632,49 @@ abstract class MetaModel
return $sFirstClassWithRuleId;
}
/**
* @param string $sRootClass
* @param string $sRuleId
*
* @return string[] child classes with the rule disabled, and that are concrete classes
*
* @throws \CoreException
* @since 2.6.1 N°1968 (soyez réalistes, demandez l'impossible)
*/
final public static function GetChildClassesWithDisabledUniquenessRule($sRootClass, $sRuleId)
{
$aClassesWithDisabledRule = array();
foreach (self::EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_EXCLUDETOP) as $sChildClass)
{
if (array_key_exists($sChildClass, $aClassesWithDisabledRule))
{
continue;
}
if (!array_key_exists('uniqueness_rules', self::$m_aClassParams[$sChildClass]))
{
continue;
}
if (!array_key_exists($sRuleId, self::$m_aClassParams[$sChildClass]['uniqueness_rules']))
{
continue;
}
if (self::$m_aClassParams[$sChildClass]['uniqueness_rules'][$sRuleId]['disabled'] === true)
{
$aDisabledClassChildren = self::EnumChildClasses($sChildClass, ENUM_CHILD_CLASSES_ALL);
foreach ($aDisabledClassChildren as $sDisabledClassChild)
{
if (!self::IsAbstract($sDisabledClassChild))
{
$aClassesWithDisabledRule[] = $sDisabledClassChild;
}
}
}
}
return $aClassesWithDisabledRule;
}
/**
* @param array $aRuleProperties
*
@@ -1167,6 +1234,7 @@ abstract class MetaModel
}
/**
* @deprecated
* @param string $sClass
*
* @return array
@@ -1219,9 +1287,12 @@ abstract class MetaModel
}
/**
* @param string $sClass
* @param string $sAttCode
* @param bool $bExtended
* Check it the given attribute exists in the specified class
*
* @api
* @param string $sClass Class name
* @param string $sAttCode Attribute code
* @param bool $bExtended Allow the extended syntax: extkey_id->remote_attcode
*
* @return bool
* @throws \Exception
@@ -1275,6 +1346,7 @@ abstract class MetaModel
}
/**
* @deprecated
* @param string $sClass
* @param string $sFilterCode
*
@@ -1290,6 +1362,9 @@ abstract class MetaModel
}
/**
* Check if the given class name is actually a persistent class
*
* @api
* @param string $sClass
*
* @return bool
@@ -1564,17 +1639,20 @@ abstract class MetaModel
/**
* array of ("classname" => array filterdef)
*
* @deprecated
* @var array
*/
private static $m_aFilterDefs = array();
/**
* array of ("classname" => array of ("attcode"=>"sourceclass"))
*
* @deprecated
* @var array
*/
private static $m_aFilterOrigins = array();
/**
* @deprecated
* @param string $sClass
*
* @return mixed
@@ -1587,6 +1665,7 @@ abstract class MetaModel
}
/**
* @deprecated
* @param string $sClass
* @param string $sFilterCode
*
@@ -1604,6 +1683,7 @@ abstract class MetaModel
}
/**
* @deprecated
* @param string $sClass
* @param string $sFilterCode
*
@@ -1622,6 +1702,7 @@ abstract class MetaModel
}
/**
* @deprecated
* @param string $sClass
* @param string $sFilterCode
*
@@ -1639,6 +1720,7 @@ abstract class MetaModel
}
/**
* @deprecated
* @param string $sClass
* @param string $sFilterCode
*
@@ -1656,6 +1738,7 @@ abstract class MetaModel
}
/**
* @deprecated
* @param string $sClass
* @param string $sFilterCode
*
@@ -1674,6 +1757,7 @@ abstract class MetaModel
}
/**
* @deprecated
* @param string $sClass
* @param string $sFilterCode
* @param string $sOpCode
@@ -1693,6 +1777,7 @@ abstract class MetaModel
}
/**
* @deprecated
* @param string $sFilterCode
*
* @return string
@@ -1790,7 +1875,7 @@ abstract class MetaModel
private static $m_aRelationInfos = array();
/**
* TO BE DEPRECATED: use EnumRelationsEx instead
* @deprecated Use EnumRelationsEx instead
*
* @param string $sClass
*
@@ -2779,21 +2864,26 @@ abstract class MetaModel
}
}
$aCurrentClassUniquenessRules = MetaModel::GetUniquenessRules($sPHPClass);
$aCurrentClassUniquenessRules = MetaModel::GetUniquenessRules($sPHPClass, true);
if (!empty($aCurrentClassUniquenessRules))
{
$aClassFields = self::GetAttributesList($sPHPClass);
foreach ($aCurrentClassUniquenessRules as $sUniquenessRuleId => $aUniquenessRuleProperties)
{
$bHasSameRuleInParent = self::HasSameUniquenessRuleInParent($sPHPClass, $sUniquenessRuleId);
$bIsRuleOverride = self::HasSameUniquenessRuleInParent($sPHPClass, $sUniquenessRuleId);
try
{
self::CheckUniquenessRuleValidity($aUniquenessRuleProperties, $bHasSameRuleInParent, $aClassFields);
self::CheckUniquenessRuleValidity($aUniquenessRuleProperties, $bIsRuleOverride, $aClassFields);
}
catch (CoreUnexpectedValue $e)
{
throw new Exception("Invalid uniqueness rule declaration : class={$sPHPClass}, rule=$sUniquenessRuleId, reason={$e->getMessage()}");
}
if (!$bIsRuleOverride)
{
self::SetUniquenessRuleRootClass($sPHPClass, $sUniquenessRuleId);
}
}
}
@@ -3092,71 +3182,77 @@ abstract class MetaModel
/**
* @param array $aUniquenessRuleProperties
* @param bool $bRuleOverride if false then control an original declaration validity,
* otherwise an override validity (can have only the disabled key)
* otherwise an override validity (can only have the 'disabled' key)
* @param string[] $aExistingClassFields if non empty, will check that all fields declared in the rules exists in the class
*
* @throws \CoreUnexpectedValue if the rule is invalid
*
* @since 2.6 N°659 uniqueness constraint
* @since 2.6.1 N°1968 (joli mois de mai...) disallow overrides of 'attributes' properties
*/
public static function CheckUniquenessRuleValidity($aUniquenessRuleProperties, $bRuleOverride = true, $aExistingClassFields = array())
{
$MANDATORY_ATTRIBUTES = array('attributes');
$UNIQUENESS_MANDATORY_KEYS_NB = count($MANDATORY_ATTRIBUTES);
$bHasDisabledKey = false;
$bHasMissingMandatoryKey = true;
$iMissingMandatoryKeysNb = $UNIQUENESS_MANDATORY_KEYS_NB;
$bHasAllMandatoryKeysMissing = false;
/** @var boolean $bHasNonDisabledKeys true if rule contains at least one key that is not 'disabled' */
$bHasNonDisabledKeys = false;
$bDisabledKeyValue = null;
foreach ($aUniquenessRuleProperties as $sUniquenessRuleKey => $aUniquenessRuleProperty)
{
if (($sUniquenessRuleKey === 'disabled') && (!is_null($aUniquenessRuleProperty)))
if ($sUniquenessRuleKey === 'disabled')
{
$bDisabledKeyValue = $aUniquenessRuleProperty;
if (!is_null($aUniquenessRuleProperty))
{
continue;
}
}
if (is_null($aUniquenessRuleProperty))
{
$bHasDisabledKey = true;
continue;
}
$bHasNonDisabledKeys = true;
if (in_array($sUniquenessRuleKey, $MANDATORY_ATTRIBUTES, true)) {
$bHasMissingMandatoryKey = false;
$iMissingMandatoryKeysNb--;
}
if (($sUniquenessRuleKey === 'attributes') && (!empty($aExistingClassFields)))
if ($sUniquenessRuleKey === 'attributes')
{
foreach ($aUniquenessRuleProperties[$sUniquenessRuleKey] as $sRuleAttribute)
if (!empty($aExistingClassFields))
{
if (!in_array($sRuleAttribute, $aExistingClassFields, true))
foreach ($aUniquenessRuleProperties[$sUniquenessRuleKey] as $sRuleAttribute)
{
throw new CoreUnexpectedValue("Uniqueness rule : non existing field '$sRuleAttribute'");
if (!in_array($sRuleAttribute, $aExistingClassFields, true))
{
throw new CoreUnexpectedValue("Uniqueness rule : non existing field '$sRuleAttribute'");
}
}
}
}
}
if ($iMissingMandatoryKeysNb == $UNIQUENESS_MANDATORY_KEYS_NB)
if ($iMissingMandatoryKeysNb === 0)
{
$bHasAllMandatoryKeysMissing = true;
$bHasMissingMandatoryKey = false;
}
if ($bHasDisabledKey)
if ($bRuleOverride && $bHasNonDisabledKeys)
{
if ($bRuleOverride && $bHasAllMandatoryKeysMissing && !$bHasNonDisabledKeys)
{
return;
}
if ($bHasMissingMandatoryKey)
{
throw new CoreUnexpectedValue('Uniqueness rule : missing mandatory properties');
}
return;
throw new CoreUnexpectedValue('Uniqueness rule : only the \'disabled\' key can be overridden');
}
if ($bHasMissingMandatoryKey)
if ($bRuleOverride && is_null($bDisabledKeyValue))
{
throw new CoreUnexpectedValue('Uniqueness rule : missing mandatory properties');
throw new CoreUnexpectedValue('Uniqueness rule : when overriding a rule, value must be set for the \'disabled\' key');
}
if (!$bRuleOverride && $bHasMissingMandatoryKey)
{
throw new CoreUnexpectedValue('Uniqueness rule : missing mandatory property');
}
}
@@ -3810,7 +3906,7 @@ abstract class MetaModel
/**
* @param string $sClass
* @param int $iOption
* @param int $iOption one of ENUM_CHILD_CLASSES_EXCLUDETOP, ENUM_CHILD_CLASSES_ALL
*
* @return array
* @throws \CoreException
@@ -6566,22 +6662,24 @@ abstract class MetaModel
}
/**
* Search for the specified class and id.
* Instantiate an object already persisted to the Database.
*
* @api
* @see MetaModel::GetObjectWithArchive to get object even if it's archived
* @see utils::PushArchiveMode() to enable search on archived objects
*
* @param string $sClass
* @param int $iKey id value of the object to retrieve
* @param bool $bMustBeFound see throws ArchivedObjectException
* @param bool $bAllowAllData if true then no rights filtering
* @param bool $bAllowAllData if true then user rights will be bypassed - use with care!
* @param null $aModifierProperties
*
* @return DBObject|null null if : (the object is not found) or (archive mode disabled and object is archived and
* @return \cmdbAbstractObject null if : (the object is not found) or (archive mode disabled and object is archived and
* $bMustBeFound=false)
* @throws CoreException if no result found and $bMustBeFound=true
* @throws ArchivedObjectException if archive mode disabled and result is archived and $bMustBeFound=true
* @throws \Exception
*
* @see MetaModel::GetObjectWithArchive to get object even if it's archived
* @see utils::PushArchiveMode() to enable search on archived objects
*/
public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
{
@@ -6782,8 +6880,11 @@ abstract class MetaModel
}
/**
* @param string $sClass
* @param array|null $aValues array of attcode => value
* Instantiate a persistable object (not yet persisted)
*
* @api
* @param string $sClass A persistable class
* @param array|null $aValues array of attcode => attribute value to preset
*
* @return DBObject
* @throws \CoreException
@@ -6828,6 +6929,8 @@ abstract class MetaModel
* @todo: protect it against forbidden usages (in such a case, delete objects one by one)
*
* @param \DBObjectSearch $oFilter
* @deprecated
* @experimental
*
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
@@ -6845,6 +6948,8 @@ abstract class MetaModel
* @param DBObjectSearch $oFilter
* @param array $aValues array of attcode => value
*
* @deprecated
* @experimental
* @return int Modified objects
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
@@ -7117,9 +7222,11 @@ abstract class MetaModel
* @param string $sInput
* @param array $aParams
*
* @return mixed
* @return string
*
* @throws \Exception
*/
static public function ApplyParams($sInput, $aParams)
public static function ApplyParams($sInput, $aParams)
{
$aParams = static::AddMagicPlaceholders($aParams);
@@ -7129,7 +7236,7 @@ abstract class MetaModel
$aSearches = array();
$aReplacements = array();
foreach($aParams as $sSearch => $replace)
foreach ($aParams as $sSearch => $replace)
{
// Some environment parameters are objects, we just need scalars
if (is_object($replace))
@@ -7139,6 +7246,10 @@ abstract class MetaModel
{
// Expand the parameters for the object
$sName = substr($sSearch, 0, $iPos);
// Note: Capturing
// 1 - The delimiter
// 2 - The arrow
// 3 - The attribute code
$aRegExps = array(
'/(\\$)'.$sName.'-(>|&gt;)([^\\$]+)\\$/', // Support both syntaxes: $this->xxx$ or $this-&gt;xxx$ for HTML compatibility
'/(%24)'.$sName.'-(>|&gt;)([^%24]+)%24/', // Support for urlencoded in HTML attributes (%20this-&gt;xxx%20)
@@ -7173,8 +7284,28 @@ abstract class MetaModel
}
else
{
$aSearches[] = '$'.$sSearch.'$';
$aReplacements[] = (string)$replace;
$aRegExps = array(
'/(\$)'.$sSearch.'\$/', // Support for regular placeholders (eg. $APP_URL$)
'/(%24)'.$sSearch.'%24/', // Support for urlencoded in HTML attributes (eg. %24APP_URL%24)
);
foreach($aRegExps as $sRegExp)
{
if(preg_match_all($sRegExp, $sInput, $aMatches))
{
foreach($aMatches[1] as $idx => $sDelimiter)
{
try
{
$aReplacements[] = (string) $replace;
$aSearches[] = $aMatches[1][$idx] . $sSearch . $aMatches[1][$idx];
}
catch(Exception $e)
{
// No replacement will occur
}
}
}
}
}
}
return str_replace($aSearches, $aReplacements, $sInput);

View File

@@ -45,7 +45,7 @@ class iTopMutex
static protected $aAcquiredLocks = array(); // Number of instances of the Mutex, having the lock, in this page
public function __construct(
$sName, $sDBHost = null, $sDBUser = null, $sDBPwd = null, $bDBTlsEnabled = false, $sDBTlsCA = null
$sName, $sDBHost = null, $sDBUser = null, $sDBPwd = null, $bDBTlsEnabled = null, $sDBTlsCA = null
)
{
// Compute the name of a lock for mysql

View File

@@ -126,6 +126,7 @@ class ormDocument
*/
public function GetDisplayURL($sClass, $Id, $sAttCode)
{
// TODO: When refactoring this with the URLMaker system, mind to also change calls in the portal (look for the "p_object_document_display" route)
return utils::GetAbsoluteUrlAppRoot() . "pages/ajax.render.php?operation=display_document&class=$sClass&id=$Id&field=$sAttCode";
}
@@ -137,6 +138,7 @@ class ormDocument
{
// Compute a signature to reset the cache anytime the data changes (this is acceptable if used only with icon files)
$sSignature = md5($this->GetData());
// TODO: When refactoring this with the URLMaker system, mind to also change calls in the portal (look for the "p_object_document_display" route)
return utils::GetAbsoluteUrlAppRoot() . "pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode&s=$sSignature&cache=86400";
}

View File

@@ -26,6 +26,9 @@
*/
final class ormTagSet extends ormSet
{
private $m_bDisplayPartial = false;
/**
* ormTagSet constructor.
*
@@ -299,6 +302,82 @@ final class ormTagSet extends ormSet
return $aModifiedTagCodes;
}
/**
* @return string[] list of codes for added entries
*/
public function GetAdded()
{
$aAddedTagCodes = array_keys($this->aAdded);
sort($aAddedTagCodes);
return $aAddedTagCodes;
}
/**
* @return string[] list of codes for removed entries
*/
public function GetRemoved()
{
$aRemovedTagCodes = array_keys($this->aRemoved);
sort($aRemovedTagCodes);
return $aRemovedTagCodes;
}
/**
* Apply a delta to the current ItemSet
* $aDelta['added] = array of added items
* $aDelta['removed'] = array of removed items
*
* @param $aDelta
*
* @throws \CoreException
*/
public function ApplyDelta($aDelta)
{
if (isset($aDelta['removed']))
{
foreach($aDelta['removed'] as $oItem)
{
$this->Remove($oItem);
}
}
if (isset($aDelta['added']))
{
foreach($aDelta['added'] as $oItem)
{
$this->Add($oItem);
}
}
}
/**
* Populates the added and removed arrays for bulk edit
*
* @param string[] $aItems
*
* @throws \CoreException
*/
public function GenerateDiffFromArray($aItems)
{
foreach($this->GetValues() as $oCurrentItem)
{
if (!in_array($oCurrentItem, $aItems))
{
$this->Remove($oCurrentItem);
}
}
foreach($aItems as $oNewItem)
{
$this->Add($oNewItem);
}
// Keep only the aModified list
$this->aRemoved = array();
$this->aAdded = array();
}
/**
* Check whether a tag code is valid or not for this TagSet
*
@@ -479,4 +558,21 @@ final class ormTagSet extends ormSet
return TagSetFieldData::GetTagDataClassName($this->sClass, $this->sAttCode);
}
/**
* @return bool
*/
public function DisplayPartial()
{
return $this->m_bDisplayPartial;
}
/**
* @param bool $m_bDisplayPartial
*/
public function SetDisplayPartial($m_bDisplayPartial)
{
$this->m_bDisplayPartial = $m_bDisplayPartial;
}
}

View File

@@ -257,13 +257,14 @@ class CoreServices implements iRestServiceProvider
*/
public function ListOperations($sVersion)
{
// 1.4 - iTop 2.5.2, 2.6.1, 2.7.0, Verb 'core/get': added pagination parameters limit and page
// 1.3 - iTop 2.2.0, Verb 'get_related': added the options 'redundancy' and 'direction' to take into account the redundancy in the impact analysis
// 1.2 - was documented in the wiki but never released ! Same as 1.3
// 1.1 - In the reply, objects have a 'key' entry so that it is no more necessary to split class::key programmaticaly
// 1.0 - Initial implementation in iTop 2.0.1
//
$aOps = array();
if (in_array($sVersion, array('1.0', '1.1', '1.2', '1.3')))
if (in_array($sVersion, array('1.0', '1.1', '1.2', '1.3', '1.4')))
{
$aOps[] = array(
'verb' => 'core/create',
@@ -299,9 +300,16 @@ class CoreServices implements iRestServiceProvider
/**
* Enumerate services delivered by this class
*
* @param string $sVersion The version (e.g. 1.0) supported by the services
* @param string $sVerb
* @param $aParams
*
* @return RestResult The standardized result structure (at least a message)
* @throws Exception in case of internal failure.
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \SimpleGraphException
* @throws \Exception
*/
public function ExecOperation($sVersion, $sVerb, $aParams)
{
@@ -436,8 +444,10 @@ class CoreServices implements iRestServiceProvider
$key = RestUtils::GetMandatoryParam($aParams, 'key');
$aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
$bExtendedOutput = (RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+');
$iLimit = (int)RestUtils::GetOptionalParam($aParams, 'limit', 0);
$iPage = (int)RestUtils::GetOptionalParam($aParams, 'page', 1);
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key, $iLimit, self::getOffsetFromLimitAndPage($iLimit, $iPage));
$sTargetClass = $oObjectSet->GetFilter()->GetClass();
if (UserRights::IsActionAllowed($sTargetClass, UR_ACTION_READ) != UR_ALLOWED_YES)
@@ -450,6 +460,11 @@ class CoreServices implements iRestServiceProvider
$oResult->code = RestResult::UNAUTHORIZED;
$oResult->message = "The current user does not have enough permissions for exporting data of class $sTargetClass";
}
elseif ($iPage < 1)
{
$oResult->code = RestResult::INVALID_PAGE;
$oResult->message = "The request page number is not valid. It must be an integer greater than 0";
}
else
{
while ($oObject = $oObjectSet->Fetch())
@@ -774,4 +789,15 @@ class CoreServices implements iRestServiceProvider
$oResult->message = $sRes;
}
}
/**
* @param int $iLimit
* @param int $iPage
*
* @return int Offset for a given page number
*/
protected static function getOffsetFromLimitAndPage($iLimit, $iPage)
{
return $iLimit * max(0, $iPage - 1);
}
}

View File

@@ -3,7 +3,7 @@
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -66,7 +66,7 @@ class SimpleCrypt
* 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){
@@ -88,7 +88,8 @@ class SimpleCrypt
}
break;
case 'OpenSSL':
if(!function_exists('openssl_decrypt')){
case 'OpenSSLMcryptCompatibility':
if(!function_exists('openssl_decrypt')){
$sEngineName = 'Simple';
}
break;
@@ -101,30 +102,30 @@ class SimpleCrypt
$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);
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
* @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"
@@ -135,9 +136,9 @@ class SimpleCrypt
{
// 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)
@@ -156,14 +157,14 @@ class SimpleCrypt
{
$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);
$sRandomBits = md5($sBase64RandomBits, TRUE);
}
}
catch (Exception $ex)
@@ -182,10 +183,10 @@ class SimpleCrypt
{
$sRandomBits .= sprintf('%04x', mt_rand(0, 65535));
}
}
return $sRandomBits;
return $sRandomBits;
}
}
@@ -221,7 +222,7 @@ class SimpleCryptSimpleEngine implements CryptEngine
$char = chr(ord($char)+ord($keychar));
$result.=$char;
}
return $result;
return $result;
}
public function Decrypt($key, $encrypted_data)
@@ -235,7 +236,7 @@ class SimpleCryptSimpleEngine implements CryptEngine
$result.=$char;
}
return $result;
}
}
}
/**
@@ -258,10 +259,13 @@ class SimpleCryptMcryptEngine implements CryptEngine
{
$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_RAND_URANDOM); // MCRYPT_RAND_URANDOM is now useable since itop requires php >= 5.6
$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))
{
@@ -275,7 +279,7 @@ class SimpleCryptMcryptEngine implements CryptEngine
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));
$string = substr($encrypted_data, mcrypt_enc_get_iv_size($this->td));
$r = mcrypt_generic_init($this->td, $key, $iv);
if (($r < 0) || ($r === false))
{
@@ -288,7 +292,7 @@ class SimpleCryptMcryptEngine implements CryptEngine
}
return $decrypted_data;
}
public function __destruct()
{
mcrypt_module_close($this->td);

View File

@@ -57,7 +57,7 @@ abstract class TagSetFieldData extends cmdbAbstractObject
"default_value" => '',
"is_null_allowed" => false,
"depends_on" => array(),
"validation_pattern" => '^[a-zA-Z][a-zA-Z0-9]{3,}$',
"validation_pattern" => '^[a-zA-Z][a-zA-Z0-9]{2,}$',
)));
MetaModel::Init_AddAttribute(new AttributeString("label", array(
"allowed_values" => null,

View File

@@ -951,6 +951,21 @@ class UserRights
return self::$m_oRealUser;
}
/**
* @return int|string ID of the connected user : if impersonate then use {@see m_oRealUser}, else {@see m_oUser}. If no user set then return ''
* @since 2.6.5 2.7.6 3.0.0 N°4289 method creation
*/
public static function GetConnectedUserId() {
if (false === is_null(static::$m_oRealUser)) {
return static::$m_oRealUser->GetKey();
}
if (false === is_null(static::$m_oUser)) {
return static::$m_oUser->GetKey();
}
return '';
}
public static function GetRealUserId()
{
if (is_null(self::$m_oRealUser))
@@ -1211,7 +1226,7 @@ class UserRights
elseif ((self::$m_oUser !== null) && ($oUser->GetKey() == self::$m_oUser->GetKey()))
{
// Data about the current user can be found into the session data
if (array_key_exists('profile_list', $_SESSION))
if ((false === utils::IsModeCLI()) && array_key_exists('profile_list', $_SESSION))
{
$aProfiles = $_SESSION['profile_list'];
}
@@ -1343,9 +1358,8 @@ class UserRights
// The bug has been fixed in PHP 7.2, but in case session_regenerate_id()
// fails we just silently ignore the error and keep the same session id...
$old_error_handler = set_error_handler(array(__CLASS__, 'VoidErrorHandler'));
session_regenerate_id();
if ($old_error_handler !== null)
{
session_regenerate_id(true);
if ($old_error_handler !== null) {
set_error_handler($old_error_handler);
}
}

View File

@@ -1,5 +1,5 @@
// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
$version: "v2.6.0";
$version: "v2.6.4";
// Base colors
$gray-base: #000 !default;

View File

@@ -343,10 +343,10 @@ a.small_action {
padding-left: 5px;
padding-top: 2px;
padding-bottom: 2px;
background: #ea7d1e url(../images/actions_left.png?v=v2.6.0) no-repeat left;
background: #ea7d1e url(../images/actions_left.png?v=v2.6.3) no-repeat left;
}
.actions_details span {
background: url(../images/actions_right.png?v=v2.6.0) no-repeat right;
background: url(../images/actions_right.png?v=v2.6.3) no-repeat right;
color: #fff;
font-weight: bold;
padding-top: 2px;
@@ -520,7 +520,7 @@ div.actions_menu > ul {
nowidth: 70px;
padding-left: 5px;
/* Nasty work-around for IE... en attendant mieux */
background: #ea7d1e url(../images/actions_left.png?v=v2.6.0) no-repeat top left;
background: #ea7d1e url(../images/actions_left.png?v=v2.6.3) no-repeat top left;
cursor: pointer;
margin: 0;
}
@@ -532,7 +532,7 @@ div.actions_menu > ul > li {
height: 17px;
padding-right: 16px;
padding-left: 4px;
background: url(../images/actions_right.png?v=v2.6.0) no-repeat top right transparent;
background: url(../images/actions_right.png?v=v2.6.3) no-repeat top right transparent;
font-weight: bold;
color: #fff;
vertical-align: middle;
@@ -678,7 +678,7 @@ td a.dp-choose-date, a.dp-choose-date, td a.dp-choose-date:hover, a.dp-choose-da
display: block;
text-indent: -2000px;
overflow: hidden;
background: url(../images/calendar.png?v=v2.6.0) no-repeat;
background: url(../images/calendar.png?v=v2.6.3) no-repeat;
}
td a.dp-choose-date.dp-disabled, a.dp-choose-date.dp-disabled {
background-position: 0 -20px;
@@ -1332,19 +1332,19 @@ input.dp-applied {
}
/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */
table.listResults tr.odd td.truncated, table.listResults tr td.truncated, .wizContainer table.listResults tr.odd td.truncated, .wizContainer table.listResults tr td.truncated {
background: url(../images/truncated.png?v=v2.6.0) bottom repeat-x;
background: url(../images/truncated.png?v=v2.6.3) bottom repeat-x;
}
/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */
table.listResults tr.even td.truncated, .wizContainer table.listResults tr.even td.truncated {
background: #f9f9f1 url(../images/truncated.png?v=v2.6.0) bottom repeat-x;
background: #f9f9f1 url(../images/truncated.png?v=v2.6.3) bottom repeat-x;
}
/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */
table.listResults tr.even td.hover.truncated, .wizContainer table.listResults tr.even td.hover.truncated {
background: #fdf5d0 url(../images/truncated.png?v=v2.6.0) bottom repeat-x;
background: #fdf5d0 url(../images/truncated.png?v=v2.6.3) bottom repeat-x;
}
/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */
table.listResults tr.odd td.hover.truncated, table.listResults tr td.hover.truncated, .wizContainer table.listResults tr.odd td.hover.truncated, .wizContainer table.listResults tr td.hover.truncated {
background: #fdf5d0 url(../images/truncated.png?v=v2.6.0) bottom repeat-x;
background: #fdf5d0 url(../images/truncated.png?v=v2.6.3) bottom repeat-x;
}
table.listResults.truncated {
border-bottom: 0;
@@ -1452,7 +1452,7 @@ div#logo {
div#logo div {
height: 88px;
width: 244px;
background: url(../images/itop-logo-2.png?v=v2.6.0) left no-repeat;
background: url(../images/itop-logo-2.png?v=v2.6.3) left no-repeat;
}
#left-pane .ui-layout-north {
overflow: hidden;
@@ -1544,7 +1544,7 @@ div#logo div {
}
#global-search-image {
vertical-align: middle;
background: url(../images/search.png?v=v2.6.0) center center no-repeat;
background: url(../images/search.png?v=v2.6.3) center center no-repeat;
display: inline-block;
width: 28px;
height: 30px;
@@ -1573,7 +1573,7 @@ span.ui-icon {
margin: 0 2px;
}
.ui-layout-button-pin-down {
background: url(../images/splitter-bkg.png?v=v2.6.0) transparent;
background: url(../images/splitter-bkg.png?v=v2.6.3) transparent;
width: 16px;
background-position: -144px -144px;
}
@@ -1830,6 +1830,9 @@ fieldset .details > .field_container {
padding-left: 0.4em;
vertical-align: middle;
}
.field_container > div > div.field_value .attribute-edit .form-field-container .form-field-content > .form_validation, .field_container > div > div.field_value .attribute-edit .form-field-container .form-field-content > .field_status {
display: inline;
}
.field_container > div > div.field_value .attribute-edit .field_input_zone {
width: 100%;
/* auto; */
@@ -2089,7 +2092,7 @@ img.prev, img.first, img.next, img.last {
}
div.actions_button {
float: right;
background: #ea7d1e url("../images/actions_left.png?v=v2.6.0") no-repeat scroll left top;
background: #ea7d1e url("../images/actions_left.png?v=v2.6.3") no-repeat scroll left top;
padding-left: 5px;
margin-top: 0;
margin-right: 10px;
@@ -2097,7 +2100,7 @@ div.actions_button {
vertical-align: middle;
}
div.actions_button a, .actions_button a:hover, .actions_button a:visited {
background: #ea7d1e url(../images/actions_bkg.png?v=v2.6.0) no-repeat scroll right top;
background: #ea7d1e url(../images/actions_bkg.png?v=v2.6.3) no-repeat scroll right top;
color: #fff;
padding-right: 8px;
cursor: pointer;
@@ -2121,10 +2124,10 @@ select#org_id {
cursor: not-allowed;
}
.dragHover {
background: url(./ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=v2.6.0);
background: url(./ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=v2.6.3);
}
.edit_mode .dashlet {
background: url(./ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=v2.6.0);
background: url(./ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=v2.6.3);
padding: 5px;
margin: 0;
position: relative;
@@ -2169,7 +2172,7 @@ table.prop_table {
top: 0;
right: 0;
z-index: 10;
background: transparent url(../images/delete.png?v=v2.6.0) no-repeat center;
background: transparent url(../images/delete.png?v=v2.6.3) no-repeat center;
}
td.prop_value {
text-align: left;
@@ -2390,17 +2393,17 @@ a.summary, a.summary:hover {
}
.message_info {
border: 1px solid #993;
background: url(../images/info-mini.png?v=v2.6.0) 1em 1em no-repeat #ffc;
background: url(../images/info-mini.png?v=v2.6.3) 1em 1em no-repeat #ffc;
padding-left: 3em;
}
.message_ok {
border: 1px solid #393;
background: url(../images/ok.png?v=v2.6.0) 1em 1em no-repeat #cfc;
background: url(../images/ok.png?v=v2.6.3) 1em 1em no-repeat #cfc;
padding-left: 3em;
}
.message_error {
border: 1px solid #933;
background: url(../images/error.png?v=v2.6.0) 1em 1em no-repeat #fcc;
background: url(../images/error.png?v=v2.6.3) 1em 1em no-repeat #fcc;
padding-left: 3em;
}
.fg-menu a img {
@@ -2531,18 +2534,18 @@ div.explain-printable {
}
#hiddeable_chapters .ui-tabs .ui-tabs-nav li.hideable-chapter span {
padding-left: 20px;
background: url(../images/eye-open-555.png?v=v2.6.0) 2px center no-repeat;
background: url(../images/eye-open-555.png?v=v2.6.3) 2px center no-repeat;
}
#hiddeable_chapters .ui-tabs .ui-tabs-nav li.hideable-chapter.strikethrough span {
text-decoration: line-through;
background: url(../images/eye-closed-555.png?v=v2.6.0) 2px center no-repeat;
background: url(../images/eye-closed-555.png?v=v2.6.3) 2px center no-repeat;
}
.printable-version legend {
padding-left: 26px;
background: #1c94c4 url(../images/eye-open-fff.png?v=v2.6.0) 8px center no-repeat;
background: #1c94c4 url(../images/eye-open-fff.png?v=v2.6.3) 8px center no-repeat;
}
.printable-version .strikethrough legend {
background: #1c94c4 url(../images/eye-closed-fff.png?v=v2.6.0) 8px center no-repeat;
background: #1c94c4 url(../images/eye-closed-fff.png?v=v2.6.3) 8px center no-repeat;
}
.printable-version fieldset.strikethrough span {
display: none;
@@ -2693,7 +2696,7 @@ span.search-button, span.refresh-button {
#itop-breadcrumb .breadcrumb-item a::after {
content: '';
position: absolute;
background-image: url(../images/breadcrumb-separator.png?v=v2.6.0);
background-image: url(../images/breadcrumb-separator.png?v=v2.6.3);
background-repeat: no-repeat;
width: 8px;
height: 16px;

View File

@@ -2115,6 +2115,11 @@ fieldset .details>.field_container {
padding-left: 0.4em;
vertical-align: middle;
}
.form-field-container .form-field-content{
> .form_validation, > .field_status {
display: inline;
}
}
.field_input_zone{
width: 100%; /* auto; */

View File

@@ -1,2 +1,20 @@
<?php
/**
* Copyright (C) 2013-2020 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
echo 'Access denied';

View File

@@ -1,8 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<authorization>
<deny users="*" /> <!-- Denies all users -->
</authorization>
</system.web>
<system.webServer>
<security>
<requestFiltering>
<fileExtensions applyToWebDAV="false" allowUnlisted="false"></fileExtensions>
</requestFiltering>
<authorization>
<deny users="*" /> <!-- Denies all users -->
</authorization>
</security>
</system.webServer>
</configuration>

View File

@@ -3,7 +3,7 @@
* Localized data
*
* @copyright Copyright (C) 2010-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @license http://opensource.org/licenses/AGPL-3.0
*
* This file is part of iTop.
*

View File

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

View File

@@ -5,7 +5,7 @@
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @license http://opensource.org/licenses/AGPL-3.0
*
*/
//

View File

@@ -0,0 +1,38 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2010-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*
* 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/>
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// Class:<class_name>/Attribute:<attribute_code>+
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+
// Class:<class_name>/Stimulus:<stimulus_code>
// Class:<class_name>/Stimulus:<stimulus_code>+
//
// Class: UserExternal
//
Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
'Class:UserExternal' => 'Externý užívateľ',
'Class:UserExternal+' => '',
));

View File

@@ -2,6 +2,7 @@
/**
* Localized data
*
* @author Robert Deng <denglx@gmail.com>
* @copyright Copyright (C) 2010-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*
@@ -20,7 +21,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
@@ -30,11 +30,9 @@
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+
// Class:<class_name>/Stimulus:<stimulus_code>
// Class:<class_name>/Stimulus:<stimulus_code>+
//
// Class: UserExternal
//
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:UserExternal' => '外部用户',
'Class:UserExternal+' => '用户在iTop 外部验证身份',

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