Compare commits

...

625 Commits

Author SHA1 Message Date
Pierre Goiffon
d90b1a3d82 🐛 N°4020 Fix syntax error for PHP < 7.1
`syntax error, unexpected 'const' (T_CONST), expecting variable (T_VARIABLE) in /var/www/itop274/setup/compiler.class.inc.php on line 61`
Was added in 1059befa
2021-07-02 09:26:03 +02:00
Pierre Goiffon
3694108f42 N°3870 updateLicenses : fix generating wrong product names on Windows
Was including paths fragments.

Example :
<product scope="datamodels">C:\Dev\wamp64\www\itop-dev\.make\license/../..//datamodels/2.x/authent-cas/vendor/apereo/phpcas</product>

Instead of :
<product scope="datamodels">apereo/phpcas</product>
2021-07-01 17:20:55 +02:00
Pierre Goiffon
8cf75f826f 🔨 updateLicenses : add logs and replace rm -f by unlink() 2021-07-01 15:30:33 +02:00
Pierre Goiffon
ad9726b64c 🔧 .editorConfig : restore old ij_visual_guides value
Was overwritten by mistake in 19505649
2021-06-30 10:24:19 +02:00
Pierre Goiffon
e32e275f40 🎨 Align dataprovider elements 2021-06-29 16:45:34 +02:00
Pierre Goiffon
195056492e 🔧 .editorConfig : enable var alignement 2021-06-29 16:45:18 +02:00
Pierre Goiffon
af338de17f 🔨 Changelog generation script 2021-06-29 11:05:59 +02:00
Pierre Goiffon
a6aa183e26 🔖 Prepare 2.7.5 2021-06-28 15:03:17 +02:00
Pierre Goiffon
b5074c4cee N°3806 Fix saying memory_limit isn't enough in CLI scripts and setup
This was caused by the strict comparison in \utils::IsMemoryLimitOk for the special "-1" value of memory_limit, which was added in c2f5cafa.
Fix was to change \utils::ConvertToBytes : it was returning original value when input was numeric (so if input is '-1' output was '-1')  now it always returns an int (input '-1' output -1)
2021-06-28 14:53:39 +02:00
acognet
8b9589744b N°4020 - timeout on updating the hierarchical key during setup - php 5.6 compatibility 2021-06-28 13:56:49 +02:00
Pierre Goiffon
8259a79cd2 🎨 Factorize LogAPI channels value in LogChannels class 2021-06-25 17:13:35 +02:00
acognet
a23ea9a01f N°3678 - Portal : a modification of field cannot hide another one 2021-06-24 14:11:41 +02:00
Eric
949b213f9d N°3513 - revert crud sequence 2021-06-24 13:32:43 +02:00
acognet
a940adc4ba N°3678 - Portal : a modification of field cannot hide another one 2021-06-24 10:35:01 +02:00
Pierre Goiffon
5d994edd62 N°4012 Fix debug trace syntax
Thanks @Molkobain !
2021-06-23 17:26:34 +02:00
Eric
b1ca1f2630 N°3513 - ObjectFormManager : remove transaction
* Add automatic tests
* Fix object consistency
2021-06-22 16:09:41 +02:00
acognet
58e315d7f6 N°4020 - timeout on updating the hierarchical key during setup - fix git ignore 2021-06-21 17:16:08 +02:00
acognet
1059befa39 N°4020 - timeout on updating the hierarchical key during setup 2021-06-21 16:39:58 +02:00
Eric
0f5130611d Fix log API tests 2021-06-21 16:07:36 +02:00
Eric
a1271da74a N°3513 - ObjectFormManager : remove transaction 2021-06-21 15:03:17 +02:00
Denis
0d40235791 🗃️ N°3968 Fix mutex being silently released after connection timeout (#209)
Mutex are using their own DB connection
Because the `wait_timeout` isn't specified when opening the connection, it could be closed before we released the lock : if so the lock is silently released !
We are now setting this variable directly when opening the connection to avoid such case (setting 86400s, so 1 day : this should be enough !)
2021-06-21 12:33:15 +02:00
Pierre Goiffon
dd63f2b817 N°4012 Debug trace for objects lists in portal : ManageBrick and BrowseBrick
'portal' channel, debug level
2021-06-18 10:58:04 +02:00
Pierre Goiffon
8f84c3b84b 🎨 LogAPI code formatting 2021-06-18 10:58:04 +02:00
Pierre Goiffon
00c58bb245 💡 updateLicenses.php : copy phpdoc from develop branch 2021-06-17 17:11:30 +02:00
Eric
3a876d5c75 Log complete error on Memory Limit or max execution time 2021-06-11 09:36:01 +02:00
Pierre Goiffon
147916062b 🎨 Fix local variable names
Was copy/paste and wasn't consistent with the functionnality :/
2021-06-10 16:28:17 +02:00
Pierre Goiffon
0de6f98add 🔊 When ordering objects search list, log problems due to low max_input_vars php.ini option (#211)
iTop is sending large volume of data to the server, and those data can be truncated due to this php.ini option.
Now we are checking for common issues : if the data is truncated a log is done (IssueLog, warning level, no channel) and the corresponding column is set as not selected.
User will still see an inconsistent result, but we won't have PHP notices generated anymore, and a log can tell what caused the issue.
2021-06-08 18:43:43 +02:00
Pierre Goiffon
a076792e77 ⬆️ N°3973 Update update pear/archive_tar 2021-06-08 18:16:12 +02:00
acognet
2d2a6857de N°3797 - [MariaDB] crash while converting utf8mb4 + add index 2021-06-04 09:10:28 +02:00
Pierre Goiffon
373641e01d 🎨 Code formatting 2021-06-03 17:31:34 +02:00
Eric
d11eceac62 N°4052 - OQL parser limit for huge request - fix CI 2021-06-03 17:07:17 +02:00
Eric
3965806fa0 N°4052 - OQL parser limit for huge request - add better feedback 2021-06-03 16:49:21 +02:00
Pierre Goiffon
2625d2da80 💡 PHPDoc 2021-06-03 15:29:13 +02:00
Eric
02d32a556d N°4052 - OQL parser limit for huge request 2021-06-02 15:15:00 +02:00
Eric
71fcc6f026 N°4031 - OQL Error when AttributeObjectKey is used in JOIN condition 2021-06-02 14:45:03 +02:00
Purple Grape
7168860a0b 🌐 Improved chinese for 2.7 (#218) 2021-06-02 08:06:16 +02:00
acognet
684c88e0b8 N°4038 - Pop up erreur portail utilisateur 2021-05-31 23:43:58 +02:00
Eric
84741c19f0 N°4002 - code hardening (merge from support/2.6) 2021-05-27 16:36:00 +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
e7b5953feb documentation 2021-05-27 11:52:38 +02:00
Eric
e441e5e78a documentation 2021-05-27 11:49:32 +02:00
Eric
6be9a87c15 N°3952 - code hardening (merged from support/2.6) 2021-05-27 09:55:16 +02:00
Eric
43daa2ef08 N°3952 - code hardening 2021-05-27 09:29:50 +02:00
Pierre Goiffon
caa2a05bf4 🔧 restore .editorconfig
Was moved to /test by mistake
And also editorconfig syntax was removed :(
Those mistakes were done in 7f15eed9
Thanks Molkobain, good catch !
2021-05-26 15:19:40 +02:00
Pierre Goiffon
fc39d8aca9 💡 PHPDoc type hinting 2021-05-26 14:24:29 +02:00
Eric
cf12578289 N°3452 - DB tools : better formatting 2021-05-26 12:14:14 +02:00
Pierre Goiffon
44952d1ea0 Fix \UtilsTest::testIsMemoryLimit 2021-05-25 17:04:32 +02:00
Pierre Goiffon
7f15eed9a8 🔧 Update .editorconfig
* Preserve XML line breaks
* Add markdown
* Add editorconfig
2021-05-25 15:50:23 +02:00
BGdu38
c2f5cafaf3 Avoid setting memory_limit to lower value than the one already configured (#215)
Some scripts are setting the memory_limit PHP option : setup, csvimport and XLSX export. This was done to avoid crashing when dealing with such large amount of data.
But sometimes we were setting the value without any prior check, so we could actually lower the memory_limit value :/

Now this memory_limit change is done using \utils::SetMinMemoryLimit, which will call ini_set if and only if the current value is lower than the one to be set.

Setup calls (setup/ajax.dataloader.php and webservices/backoffice.dataloader.php) were left as is as they weren't subject to this bug, and also they are more complex (logging done on each case).
2021-05-25 12:03:19 +02:00
Pierre Goiffon
81822efa0f 💡 PHPDoc for \DBObject::ApplyStimulus 2021-05-25 10:52:12 +02:00
Pierre Goiffon
923a025f1c 🌐 N°4017 Report pt_br translations for TTO/TTR made in combodo-dispatch-incident
Those dict keys were wrongly duplicated in this module, but they are core iTop !
Original commit 75f00d993c
Thanks to @rokam !
2021-05-20 15:51:21 +02:00
Pierre Goiffon
a4b6f4e37c 👥 Add @rokam to contributors list
Thanks to him for his PT-BR translations !
2021-05-20 15:44:24 +02:00
Pierre Goiffon
f0c73451a2 🌐 N°4017 Report pt_br translations for TTO/TTR made in combodo-dispatch-userrequest
Those dict keys were wrongly duplicated in this module, but they are core iTop !
Original commit 9725897623
Thanks to @rokam !
2021-05-20 15:30:00 +02:00
acognet
db6e813cba N°3945 - Password database is visible in the setup process 2021-05-18 17:34:57 +02:00
Pierre Goiffon
d74e3e6b42 💡 ItopTestCase : some PHPDoc 2021-05-18 17:16:04 +02:00
acognet
b740cb2afd N°2540 - prevent the mysql password to appear on misconfigured servers 2021-05-12 08:17:35 +02:00
Molkobain
6ad3c40c42 Merge branch 'support/2.7.4' into support/2.7 2021-05-11 12:14:36 +02:00
Molkobain
f49c8ce188 Merge branch 'support/2.7.3' into support/2.7.4 2021-05-11 12:13:29 +02:00
Molkobain
acf828b72e N°3995 - Portal: Fix loader not displaying in BrowseBrick for tree/mosaic modes with huge amount of data 2021-05-11 12:10:31 +02:00
Pierre Goiffon
bac92716f3 📄 Update Font Awesome license
Was duplicated...
And also not the correct license !
Source for new license text : https://github.com/FortAwesome/Font-Awesome/blob/master/LICENSE.txt

(modification already committed to develop : f8f9c2d7)
2021-05-04 08:58:34 +02:00
bruno-ds
07257cc2d2 N°3671 - fix a code comment too evasive
thanks @Hipska
2021-04-21 09:25:10 +02:00
Pierre Goiffon
87ba67225a 💡 Add since phpdoc tags for \DeadLockLog::Log 2021-04-16 08:36:21 +02:00
Pierre Goiffon
2ad3b3c27e 🎨 Fix \DeadLockLog::Log parameter name + PHPDoc
Thanks @Hipska !
2021-04-15 15:03:13 +02:00
Molkobain
92a640e41a Merge branch 'support/2.7.4' into support/2.7 2021-04-15 10:37:17 +02:00
Molkobain
842df7646b Merge branch 'support/2.7.3' into support/2.7.4 2021-04-15 10:35:56 +02:00
Molkobain
01b38d2ed6 N°3869 - Portal: Fix the Notice "Undefined index: max_display_limit" 2021-04-15 10:34:46 +02:00
Molkobain
9af4846372 N°3810 - Avoid syntax highlighting that shouldn't take place 2021-03-23 16:39:22 +01:00
bruno-ds
91fc2d2e2b N°3671 - reformat tests 2021-03-17 08:46:41 +01:00
Pierre Goiffon
2432ff77a3 💡 More details in PHPDoc for \utils::RealPath 2021-03-17 08:34:25 +01:00
odain
d229e08f02 prepare iTop release 2.7.4 2021-03-16 09:00:06 +01:00
odain
b5adb2e82b N°3671: fix and make test more lisible 2021-03-15 19:01:09 +01:00
odain
386c90c601 N°3668 - URL direct error: renamed trust_proxies<-behind_reverse_proxy 2021-03-15 14:56:16 +01:00
odain
5d0c61178b N°3671 : persist absolute URL when setup context (force trustproxy enabled) 2021-03-15 14:34:56 +01:00
odain
3bcae734e5 N°3671 : persist absolute URL when setup context (force trustproxy enabled) 2021-03-15 12:27:05 +01:00
odain
842e8f9e01 php doc 2021-03-10 15:32:44 +01:00
odain
52cd4f7c5e N°3788 - timeout/excessive duration during MTP - fix PostDbCreation 2021-03-09 11:44:25 +01:00
odain
995619af9b N°3788 - timeout/excessive duration during MTP - increase timeout from 5 to 30s 2021-03-09 08:27:33 +01:00
odain
c842162fe2 N°3788 - timeout/excessive duration during MTP 2021-03-09 08:13:57 +01:00
odain
83f99642e0 N°3793 - Cleanup of orphan CMDBChange can hang the setup 2021-03-08 11:38:20 +01:00
bruno-ds
ae6a264d6d N°3671 - fix typo in HTTP header name 2021-03-05 16:57:03 +01:00
bruno-ds
a06bf6ea7c coding convention (thanks @molkobain) 2021-03-05 09:20:04 +01:00
bruno-ds
bb8d4a92cb fix an indentation problem (thanks @Hipska) 2021-03-04 09:56:05 +01:00
bruno-ds
1429792690 N°3668 - fix an improper redirection to the homepage when iTop is behind a reverse proxy 2021-03-04 09:39:48 +01:00
bruno-ds
1f26b59d90 N°3671 - add an API endpoint (it will be used by N°3668 and N°3760) + some code cleanup asked by @molkobain 2021-03-04 09:32:13 +01:00
bruno-ds
7b093a6bba N°3671 - app_root_url: handle reverse proxies during the setup and preserve existing configuration during an upgrade. 2021-03-03 11:55:18 +01:00
odain
d4607ee815 N°3065 - Failed enum comparison when values contains parenthesis : add a warning 2021-03-02 07:33:36 +01:00
odain
5c0e92d51a N°3065 - Failed enum comparison when values contains parenthesis 2021-03-01 17:06:49 +01:00
bruno-ds
cd4b3fdaab N°3764 - fix CI 2021-03-01 16:27:40 +01:00
bruno-ds
0030d5c2b8 N°3764 - add transactions_gc_threshold in order to tune CSRF token GC load 2021-03-01 15:30:40 +01:00
bruno-ds
95a0efedcf N°3728 - security hardening 2021-03-01 15:28:34 +01:00
bruno-ds
13a1d32f56 N°3453 - portal export header fields are now localized 2021-02-26 11:47:34 +01:00
Pierre Goiffon
35155e4b7a 💡 N°3065 comments modifications 2021-02-26 10:06:29 +01:00
Eric
77710f1613 Revert "#1946 Fix Twig templates logging too much"
This reverts commit 2763b991
2021-02-25 17:57:39 +01:00
Eric
2763b99142 #1946 Fix Twig templates logging too much 2021-02-25 14:44:16 +01:00
bruno-ds
db13c105ad N°3473 - PHPdoc
as requested by @piRGoif
2021-02-24 17:38:54 +01:00
bruno-ds
2276539f24 N°3430 - code cleanup 2021-02-24 16:50:14 +01:00
bruno-ds
9b7cd20d47 N°3473 - security hardening 2021-02-24 16:50:13 +01:00
bruno-ds
e1d644c33b Merge remote-tracking branch 'origin/support/2.7' into support/2.7 2021-02-24 12:06:40 +01:00
bruno-ds
c601082a5e 3548 - disable core update if a file integrity problem is detected 2021-02-24 12:05:11 +01:00
Molkobain
5836be7131 Fix unit test 2021-02-24 09:49:16 +01:00
Molkobain
6f40bb4c35 Change check level to "warning" in order to keep consistency with the others 2021-02-24 09:29:42 +01:00
bruno-ds
241bd1cdeb N°3430 - code cleanup
- during the code review @dflaven preferred the reference rather than the return alternative
 - typo
2021-02-22 09:43:42 +01:00
Molkobain
71c5f47cd8 PHPDoc 2021-02-19 09:37:53 +01:00
odain
74246a8278 N°3065 - Failed enum comparison when values contains parenthesis - enhance db model parsing used during setup comparison with expected one to generate SQL migration queries 2021-02-18 18:24:09 +01:00
bruno-ds
c450c9426c Merge remote-tracking branch 'origin/support/2.7' into support/2.7 2021-02-18 16:29:44 +01:00
odain
46f9fe743c fix ci: adapt test to make sure config date_and_time is set properly before 2021-02-18 16:27:38 +01:00
odain
c31df5fff3 fix ci: adapt test to make sure config date_and_time is set properly before 2021-02-18 16:07:37 +01:00
Pierre Goiffon
6e0af1a3b7 💡 Add variable typing 2021-02-18 13:21:06 +01:00
bruno-ds
e9e18513be N°3430 - fix preference page's warning and add missing token generation
- fix the warning (ajax call interrupted) if preference form ajax call is way faster than the one of the 2 other by adding a new timeout_duration option before the redirect.
2021-02-18 12:18:38 +01:00
Molkobain
9d2fc883b8 Fix test name 2021-02-17 10:31:59 +01:00
odain
913ea0cef2 N°3412 - Command Injection vulnerability in the Setup Wizard - renaming 2021-02-17 10:22:21 +01:00
odain
82ba7f25b0 N°3412 - Command Injection vulnerability in the Setup Wizard - do not use escapeshellcmd before execution in Windows envt 2021-02-17 10:18:28 +01:00
odain
bb877a244b N°3412 - Command Injection vulnerability in the Setup Wizard - do not use escapeshellcmd before execution in Windows envt 2021-02-17 10:09:39 +01:00
odain
a12959d60e N°3412 - Command Injection vulnerability in the Setup Wizard - handle empty path 2021-02-17 07:50:18 +01:00
bruno-ds
83434b5506 N°3430 - add translations 2021-02-16 17:42:11 +01:00
bruno-ds
dcd4abe72b N°3430 - security hardening 2021-02-16 17:33:49 +01:00
odain
571520815a N°3412 - Command Injection vulnerability in the Setup Wizard - include test to CI 2021-02-16 17:25:45 +01:00
odain
e9cff0920b N°3412 - Command Injection vulnerability in the Setup Wizard - fix test and code 2021-02-16 17:12:41 +01:00
odain
905ee19519 N°3412 - Command Injection vulnerability in the Setup Wizard 2021-02-16 15:57:12 +01:00
bruno-ds
0b95220d1b N°3466 - Add (missing) translations 2021-02-16 09:46:04 +01:00
bruno-ds
e1b2a767f5 N°3142 - fix typos 2021-02-15 17:49:25 +01:00
bruno-ds
3058b2eb00 N°3142 - Add (missing) translations 2021-02-15 17:08:47 +01:00
Molkobain
38bc2d9d58 🔧 Change max line length in .editorConfig 2021-02-15 13:49:39 +01:00
Eric
c8e8778d7b N°3468 - Fix extension.xml preventing extensions installation
(cherry picked from commit 92c8af1b19)
2021-02-11 17:48:29 +01:00
bruno-ds
656fa3208a N°3721 - revert the feature (will only be available on the 3.0) 2021-02-10 15:33:01 +01:00
bruno-ds
f647ce61c2 N°3721 - toolkit's "update iTop" with the "Create symbolic links" option checked now empty the compiled directory as expected 2021-02-10 14:34:21 +01:00
Eric
6b76e5a853 N°3618 - Count on union with different conditions fails (php doc) 2021-02-08 09:39:24 +01:00
Eric
dbb6e43751 N°3618 - Count on union with different conditions fails (Fix unit tests) 2021-02-05 11:28:40 +01:00
Eric
f07f0ba1c7 N°3618 - Count on union with different conditions fails (Fix multi-column attributes sql generation) 2021-02-05 10:15:14 +01:00
Pierre Goiffon
a5894c1a4c Rename \Combodo\iTop\Test\UnitTest\ItopTestCase::InvokeInvisible* to InvokeNonPublic* 2021-02-04 09:41:56 +01:00
Eric
e06996a2e4 N°3660 - Fix JOIN without condition on child joined table is ignored (check done in optimizer) 2021-02-03 15:58:54 +01:00
Eric
2f0e7c6d29 N°3586 - Fix login window not correctly displayed 2021-02-03 09:15:56 +01:00
Eric
7115a6ae7d N°3660 - Fix JOIN without condition on child joined table is ignored (remove unnecessary check) 2021-02-03 09:07:59 +01:00
Pierre Goiffon
765560d1f5 ItopTestCase : helpers to call invisble methods 2021-02-02 17:57:40 +01:00
Eric
bc024d9ed0 N°3660 - Fix JOIN without condition on child joined table is ignored 2021-02-02 17:30:03 +01:00
jbostoen
37a4a3eb47 🌐 Fix typo in Dutch translations (#189)
Co-authored-by: jbostoen <->
2021-02-01 16:21:10 +01:00
Pierre Goiffon
54e9bd5c8e Merge branch 'support/2.6' into support/2.7
# Conflicts:
#	.editorconfig
2021-01-25 09:13:43 +01:00
Pierre Goiffon
066a6d8b36 🔧 Use same .editorconfig in all supported branches 2021-01-25 09:12:38 +01:00
jbostoen
4123c6213d 🌐 NL : distinguish between approval (goedkeuring) <=> acceptance (acceptatie) (#182)
Co-authored-by: jbostoen <->
2020-12-21 14:08:29 +01:00
Pierre Goiffon
8265b9b034 N°3416 fix PHPDoc 2020-12-10 18:10:45 +01:00
Pierre Goiffon
c4756e8cec Upgrade version n° 2020-12-08 18:47:24 +01:00
odain
37351d6b3e N°3464: fix ci 2020-12-07 16:23:17 +01:00
odain
57a085eec1 N°3464: move fix in itop-fence + fix/enhance rest api test 2020-12-07 15:56:35 +01:00
odain
0019595923 N°3464: fix ci 2020-12-07 00:44:39 +01:00
odain
4d61c14f80 N°3464 add test in phpunit.xml.dit to validate the fix 2020-12-07 00:12:31 +01:00
odain
cf1b613923 N°3464 REST comment field not working anymore 2020-12-06 23:54:27 +01:00
Pierre Goiffon
1304e2eb2d N°3416 Updates after code review v2 :) 2020-12-04 08:51:07 +01:00
Pierre Goiffon
3cf16627c1 Merge remote-tracking branch 'origin/support/2.6' into support/2.7
# Conflicts:
#	pages/ajax.render.php
2020-12-03 18:18:36 +01:00
Pierre Goiffon
4aaa237bf9 🔖 Prepare 2.7.3 version 2020-12-03 18:15:58 +01:00
Pierre Goiffon
cece15d10c N°3416 Updates after code review
Many thanks @bruno-ds !
* add comments to explain intentions
* fix indentations
2020-12-03 17:45:44 +01:00
Pierre Goiffon
aa15e009cb 🔖 Prepare 2.7.2-2 version 2020-12-03 10:05:37 +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
80e1e0e61a N°3426 Fix no navigation menu on User object creation
Caused by a typo in js/forms-json-utils.js
Thanks @Molkobain !
2020-12-02 18:02:00 +01:00
Pierre Goiffon
ecebe4ecd5 N°3416 XFrame and cache headers optimizations
* Remove XFrame header set in \WebPage::no_cache : not this method responsability, was confusing :/
* Remove no_cache() calls when already set in page constructor (ajax_page mainly)
* Also calls everywhere the \WebPage::no_cache method instead of setting headers manually
2020-12-02 17:19:05 +01:00
Pierre Goiffon
8bfcb14d0c N°3416 XFrame-Options header is now set using a config parameter, defaults to SAMEORIGIN
Also adds an indirection (\WebPage::add_xframe_options) to set header
2020-12-02 17:17:11 +01:00
Molkobain
1cf1473d6b N°3469 - Fix variable declaration (let => var) 🤭 2020-12-02 17:01:00 +01:00
Molkobain
aa43425df3 N°3469 - Portal: Fix modal created without an ID 2020-12-02 16:59:39 +01:00
Pierre Goiffon
35d77ff642 N°3416 Fix DocumentFile preview not working anymore
Was caused by X-Frame-Options http header added with N°3317
2020-12-02 15:44:58 +01:00
acognet
539fa43503 N°3461 - Setup Broken with Chrome v87 2020-11-30 18:27:25 +01:00
acognet
eb537f45f4 N°3421 - Attributes of class Person are not accessible from :current_contact in portal anymore. Only attributes of class Contact are. 2020-11-30 09:24:35 +01:00
acognet
a2a4cd4e7a N°3426 - Wrong tab is displayed when a creation or modification form is invalidated 2020-11-27 15:20:20 +01:00
Pierre Goiffon
35215cf62f 🌐 Fix typo in comma (2 "m" !!) 2020-11-26 18:34:07 +01:00
Pierre Goiffon
66273ebd39 Merge remote-tracking branch 'origin/support/2.7.2' into support/2.7 2020-10-30 18:08:01 +01:00
Eric
eebc29d2bb N°3111 - Fix Portal export
(cherry picked from commit d3b57c3bda)
2020-10-30 14:16:57 +01:00
Pierre Goiffon
512b415bd6 N°3065 add test case in comment 2020-10-30 11:30:22 +01:00
Pierre Goiffon
906c8855b0 🔊 When error during CoreUpdate, show full file path instead of only basename 2020-10-28 18:32:49 +01:00
Pierre Goiffon
97d322a059 📝 N°3218 Add some PHPDoc on current change set/get 2020-10-27 15:17:17 +01:00
acognet
ada7f30793 N°3139 - import csv : hyperlink not clickable - replace htmlentities with utils::HtmlEntities() to ensure that the same options are used application wide. 2020-10-27 09:59:05 +01:00
Pierre Goiffon
b065d13374 Integration tests : add itop-community group 2020-10-27 09:46:03 +01:00
Pierre Goiffon
1f092f8418 🎨 Integration test code formatting 2020-10-27 09:45:26 +01:00
Pierre Goiffon
65d6947e52 🔖 Prepare 2.7.2 version 2020-10-22 15:06:48 +02:00
Pierre Goiffon
ba54b47f7d Merge remote-tracking branch 'origin/support/2.6' into support/2.7 2020-10-22 10:27:36 +02: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
adb4e77c8d 🎨 MetaModel : function modifiers order + little formatting 2020-10-19 15:36:56 +02:00
Pierre Goiffon
d3cf7176da 📝 MetaModel : add comment on @deprecated added in 9c75cb4537 2020-10-19 15:34:15 +02:00
Pierre Goiffon
1cfb52d220 🐛 Fix CoreException constructor generating a warning on PHP >= 7.2
In the CoreException constructor, we're using the $aContextData parameter to do a count(), a foreach(), and uses values as string.
Only a null check was done.
Now we are also checking that the value is_array().
As others checks (Countable, Iterable, __toString() impl) are quite difficult depending on the PHP version we're running, we didn't add any other checks.

The call in \MatchExpression::__construct (added in 05a0d612) was passing directly an Expression object. We could embed it in an array, but the object hierarchy isn't implementing __toString so we would have another bug.
In consequence we removed this parameter.
2020-10-19 11:57:53 +02:00
Pierre Goiffon
18d5231900 N°3332 Security hardening 2020-10-19 09:25:30 +02:00
Pierre Goiffon
e6539ccb6e 🔧 Update .editorconfig : braces on next line for classes and functions 2020-10-19 09:09:31 +02:00
acognet
96332b7885 N°3139 - import csv : hyperlink not clickable 2020-10-16 14:15:51 +02:00
acognet
557b9be795 N°3377 - Allow to get data of current user - Fix Exception when using :current_user->... for non admin users 2020-10-15 17:31:25 +02:00
Pierre Goiffon
75ebecddd5 Merge remote-tracking branch 'origin/support/2.6' into support/2.7
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/controllers/userprofilebrickcontroller.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/helpers/applicationhelper.class.inc.php
2020-10-14 09:19:07 +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
2d130cbba8 Merge remote-tracking branch 'origin/support/2.6' into support/2.7
# Conflicts:
#	test/core/UserRightsTest.php
#	test/setup_params/default-params.xml
2020-10-12 12:47:58 +02:00
Pierre Goiffon
8b1c20cc11 N°3332 Security hardening 2020-10-12 12:40:51 +02:00
odain
df5aacca42 💚 use new ci validation 2020-10-09 10:08:31 +02:00
Pierre Goiffon
06acac97ba Fix tests
* update datamodel XML version
* Remove \Combodo\iTop\Test\UnitTest\Core\OQLTest::testTypeErrorQueryParser
2020-10-07 16:21:36 +02:00
acognet
2d6d1132c7 N°3262 - Avoid PHP notices on DBObject core code 2020-10-07 13:26:38 +02:00
acognet
ed0e16494d N°3335 - Fix test 2020-10-05 16:23:56 +02:00
Pierre Goiffon
a765eb8725 Merge remote-tracking branch 'origin/support/2.6' into support/2.7
# Conflicts:
#	application/ajaxwebpage.class.inc.php
#	application/csvpage.class.inc.php
2020-10-05 16:12:49 +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
BenGrenoble
5d7ae38adf Merge remote-tracking branch 'origin/support/2.7' into support/2.7 2020-10-02 10:51:59 +02:00
BenGrenoble
2e08ae571a 3354 change sie by Sie 2020-10-02 10:51:25 +02:00
Pierre Goiffon
37522459a8 N°3351 restore LogKPI calls in portal index 2020-10-01 18:02:31 +02:00
Pierre Goiffon
db8c26da17 📝 update PHPDoc for \Expression::IsTrue 2020-10-01 17:02:22 +02:00
BenGrenoble
93c91c4077 3354 remove now from "Bitte bestätigen sie, dass jetzt ein Backup erstellen wollen now." 2020-10-01 15:11:07 +02:00
Pierre Goiffon
e4b3871947 📝 PHPDoc for \Expression::IsTrue 2020-10-01 14:51:28 +02:00
Pierre Goiffon
b2474d3368 N°3324 Portal fix ignore_silo when using nested query in scopes
The AllowAllData attribute wasn't updated in the nested queries.
It is now set both when calling DBObjectSearch::AllowAllData and when creating a new nested query (\DBObjectSearch::AddConditionExpression)
2020-10-01 12:15:34 +02:00
Pierre Goiffon
6cd0670d6b 🎨 Fix parameter for all DBSearch::AllowAllData impl 2020-10-01 10:09:34 +02:00
Pierre Goiffon
e9f81bd978 🔧 Update .Editorconfig for braces always at eol 2020-10-01 10:09:34 +02:00
Eric
0cc5dc0471 N°3317 - Add http headers 2020-09-30 10:18:44 +02:00
odain
20ce42b24b Reintegrate validation tests to ease iTop release management from develop
- N°3053 - Check XML conversion methods
     - N°3059 - Automatically set the documentation URLs
     - N°3052 - Check community modules XML version against latest version
     - N°3054 - Check community modules version against major version
     - N°3062 - setup.css file integrity test
     - N°3060 - Check consistency between the list of modules and installation.xml
     - N°3061 - Automatically check the installation.xml consistency
     - N°3268 Add test to check dictionary files: make sure that the Dict::Add declarations match the file name
2020-09-29 14:43:51 +02:00
Eric
d86e904e18 N°3317 - Add http headers 2020-09-29 14:11:11 +02:00
Eric
bef1832ac7 N°3317 - Add http headers 2020-09-29 14:07:24 +02:00
Eric
5a46bb8461 N°3320: Do not display empty tabs 2020-09-29 11:23:49 +02:00
acognet
05a0d61244 N°3335 - Notifications on threshold don't work when trigger is created on iTop 2.7.1 - nicer fix 2020-09-29 10:27:05 +02:00
acognet
80b3212a19 N°3335 - Notifications on threshold don't work when trigger is created on iTop 2.7.1 2020-09-29 09:12:47 +02:00
Molkobain
794d4f1e0e N°3310 - Fix corrupted backups when a file has a size which is a multiple of 512 bytes 2020-09-28 14:31:36 +02:00
Molkobain
389b61d3a8 Fix missing author information in composer.json for TCPDF lib. 2020-09-28 14:29:51 +02:00
Molkobain
0948e80060 N°3320 - Fix empty tabs being displayed (misuse of the API or user rights) 2020-09-28 14:10:19 +02:00
odain
9520d2794f 💚 fix ci Serialization of 'ReflectionClass' is not allowed 2020-09-25 10:29:48 +02:00
Pierre Goiffon
e2c67dfcc4 Merge remote-tracking branch 'origin/support/2.6' into support/2.7 2020-09-25 08:57:08 +02:00
odain
4e0eed6e13 N°3270 Notify on expiration not sending notification (trigger exception)
add boilerplate function and use it to intercept/enrich trigger exception loops
2020-09-25 07:18:14 +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
Eric
94b9a9bb75 N°3309 - Spelling mistake 2020-09-22 12:51:43 +02:00
Eric
0c90b701ea N°3283 - Spelling mistake 2020-09-22 12:45:19 +02:00
Eric
58961cd4ec N°3318 - don't display error details 2020-09-22 11:57:43 +02:00
Eric
1453558f3e N°3317 - Add http headers 2020-09-22 11:39:19 +02:00
Eric
c6df0b6d7d N°3311 - Stop capturing output before sending backup file (avoid memory problem) 2020-09-22 11:03:59 +02:00
Pierre Goiffon
7c3659d5ba 🔧 Remove versionned PHPStorm files
Those files were duplicates of the .editorconfig file

.editorconfig was pushed with ecd8f40c0f for 2.7.0 and should be the only source of the IDE / editor configuration

This generic format is implemented in PHPStorm since v2019.2 (see https://blog.jetbrains.com/idea/2019/06/managing-code-style-on-a-directory-level-with-editorconfig/)
To check if your IDE / editor supports it, check https://editorconfig.org/
2020-09-18 18:26:31 +02:00
Pierre Goiffon
dd942997cb 🎨 Fix invalid formatting introduced in 827b4b5bbe 2020-09-18 14:22:28 +02:00
Pierre Goiffon
57fea03745 🔧 Fix editorconfig for JS files 2020-09-18 14:21:47 +02:00
Pierre Goiffon
827b4b5bbe 🎨 Fix string delimiter
Thanks @jbostoen for pointing this out (see discussion in 0773660ef2)
2020-09-18 14:15:41 +02:00
Pierre Goiffon
7de59c1977 Update .editorconfig 2020-09-18 10:19:38 +02:00
Pierre Goiffon
c34c4bc09d 📝 Fix CRUD wiki page URL
was linking to Combodo private wiki :/, now is the public one \o/
Many thanks @Hipska !
2020-09-14 15:05:44 +02:00
Pierre Goiffon
bced819b3f 📝 N°2293 PHPDoc for DBObject::GetOriginal 2020-09-14 14:26:29 +02:00
acognet
23136bdf00 N°3303 - Bug on Mass update of actions (notification) 2020-09-11 09:53:23 +02:00
odain
98c371c5cf add new code style for brackets 2020-09-07 16:12:12 +02:00
acognet
1e0415e902 3234 - php 5.6 compatibility 2020-09-07 12:14:38 +02:00
Eric
dbada2f72a N°3238 - Fix multi-words search in FilterBrick and ManageBrick 2020-09-04 17:34:59 +02:00
Eric
9694e9848d N°3285 - Fix Standard Global Search: multiple words search 2020-09-04 09:27:39 +02:00
bruno-ds
fe87700135 Un-deprecate ItopExtensionsExtraRoutes::AddRoutes(...)
we had imagined it as a compatibility layer for migrating from Silex to Symfony,
but it must'nt be deprecated until we provide a new API (ideally based on a scan of yaml configuration file?)
2020-09-03 16:43:30 +02:00
Eric
7107c2f616 N°3260 - Fix rendering of an ExternalField on a Text with XML content (format transitivity) 2020-09-02 18:15:31 +02:00
Eric
92e0f101d7 N°3260 - Fix rendering of an ExternalField on a Text with XML content (format transitivity) 2020-09-02 17:58:04 +02:00
acognet
29624bc5c5 N°3163 - Portal Filters doesn't work 2020-09-01 14:16:16 +02:00
acognet
d09f3f4f83 N°3149 - Change Color of Brick Search on Portail with extension Custom 2020-09-01 10:55:48 +02:00
acognet
f774a90b7e N°3146 - Affichage des class user dans l'import CSV 2020-08-31 17:15:59 +02:00
acognet
fef8038f70 N°3261 - Configure this list : sort icon disappears when descending sort is selected 2020-08-31 15:11:47 +02:00
bruno-ds
2806a76c1d :greenheart: test readability 2020-08-31 14:47:55 +02:00
Eric
328ec52c88 N°3162 - Remove default admin phone number (can be incompatible with validation pattern) 2020-08-31 11:38:24 +02:00
Eric
70734e2b71 N°3188 - Fix LIfeCycle visualization details
Fix js broken by the fix of N°309
2020-08-28 17:28:52 +02:00
Eric
bb892cc180 N°3078 - Fix error on login while in maintenance mode 2020-08-27 17:10:37 +02:00
acognet
4618f12d8a N°3234 - Notify on expiration not sending notifications 2020-08-21 18:50:27 +02:00
odain
d12e2e592a N°3265 - Log stacktrace when cron exception raised with debug enabled 2020-08-21 10:25:05 +02:00
Eric
6a25933744 N°3248 - code hardening 2020-08-18 17:21:48 +02:00
Pierre Goiffon
208ccfe3ab N°3257 Fix cannot create objects with AttributeImage from extkey widget on PHP 7.4 2020-08-18 17:13:37 +02:00
Eric
f74c78d61c N°3248 - code hardening 2020-08-18 17:02:46 +02:00
Eric
6176af089c N°3256 - Invalid filter parameter, when using & (ampersand) in filter parameter (OQL Query) 2020-08-18 14:41:18 +02:00
odain
a35b2d83b7 Cancel functionnal changes to make sure they are ok in next release (develop/2.8) first 2020-08-07 15:17:24 +02:00
odain
8902d6e532 CI migration/automation + new test to ease iTop release management
- new Jenkinsfile and .jenkins removal to launch phpunit/behat tests
triggered on both iTop build and push.
 - N°3053 - Check XML conversion methods
 - N°3057 - New build recipe
 - N°3059 - Automatically set the documentation URLs
 - N°3052 - Check community modules XML version against latest version
 - N°3054 - Check community modules version against major version
 - N°3062 - setup.css file integrity test
 - N°3060 - Check consistency between the list of modules and installation.xml
 - Add exclusion group for CI
 - N°3061 - Automatically check the installation.xml consistency
2020-08-07 14:48:51 +02:00
Pierre Goiffon
07bd6b8539 N°3219 cron : reset CMDBChange for each process 2020-08-04 09:44:20 +02:00
Pierre Goiffon
1148449bb7 📝 Add missing @since on \DBSearch::GetFirstResult 2020-07-29 17:21:15 +02:00
jbostoen
11d418fd49 🌐 Dutch translations: fix use of ITOP_APPLICATION, ITOP_APPLICATION_SHORT
* Fix incorrect ITOP_APPLICATION, ITOP_APPLICATION_SHORT
2020-07-28 16:26:16 +02:00
Eric
ace676dc24 N°2585 - Fix alias problem in portal scopes
The re-aliasing map structure now allows multiple mapping for the same alias (used for the translations of UNIONS)
2020-07-23 16:41:57 +02:00
Eric
8122270476 N°3176 - OQL: Fix malformed UNION queries in portal scopes
Fix regression in Unit tests
2020-07-22 17:30:38 +02:00
Eric
1f66d53ab4 N°3176 - OQL: Fix malformed UNION queries in portal scopes
Fixed AddCondition_ReferencedBy() for unions (regression introduced by N°2970)
2020-07-22 16:09:08 +02:00
Eric
dfaeca43e4 N°3148 - OQL request malformed
Fix variables in ListExpression
2020-07-22 10:06:11 +02:00
Eric
5b04143711 N°3111 - Fix Portal export 2020-07-21 16:39:55 +02:00
Eric
bd14096d43 N°3150 - Wrong count for archived objects 2020-07-21 14:21:55 +02:00
Eric
3b20be05cb 3189 - DBTools enhancements
* Add CLI command bin/report.php to generate report offline
* Keep the latest report in log/dbtools-report.log in order to visualize it with "Log management" menu
2020-07-21 14:08:36 +02:00
Eric
fdec608c3e N°3174 - Remove stack trace from MySQLException 2020-07-16 18:09:44 +02:00
Eric
72cb3de50d N°3173 - Installation issue with PHP 7.4 (fix php notice) 2020-07-16 17:23:21 +02:00
Eric
c03d32b423 N°3180 - Allow HTML in dictionary for login screen ('UI:Login:About') 2020-07-16 15:28:49 +02:00
Eric
94f9b16c03 N°2589 - Infinite loops when logging with a Contact having a non empty TagSet field
Add ListParameters to DBSearch for nested queries
2020-06-24 15:18:11 +02:00
acognet
311aeb0b07 N°2589 - Infinite loops when logging with a Contact having a non empty TagSet field 2020-06-24 12:09:55 +02:00
acognet
68fe3f01be Spelling corrections 2020-06-23 17:38:24 +02:00
acognet
7ce94486bd Spelling correction 2020-06-23 13:49:53 +02:00
acognet
6523b34d58 Update version number for 2.7.1 2020-06-23 11:19:44 +02:00
bruno DA SILVA
be20705449 Add unit test.
unit test the behaviour of the removal of the blacklisted html tags

this is in fact an adaptation of the test added for the rolled-back feature of the n°2556.
This feature has been postponed to the 2.8 due to performance scaling issues.
2020-06-22 16:13:31 +02:00
acognet
e7abaa2838 Update dictionnaries 2020-06-22 15:50:18 +02:00
Pierre Goiffon
8d73eb6dff Revert "N°2556 - Html sanitization preserve content of removed tags (except for a forbidden list)"
This reverts commit 746b47bb0e.
Revert "N°2556 - Repair CI"

This reverts commit 79909fadc0.
2020-06-22 11:36:46 +02:00
acognet
f84995a58f N°309 - Afficher les arbres pliés ou dépliés 2020-06-19 18:45:26 +02:00
acognet
7f66e26b5f Merge remote-tracking branch 'origin/support/2.7' into support/2.7 2020-06-19 12:27:35 +02:00
acognet
a6639b067f N°309 - Afficher les arbres pliés ou dépliés 2020-06-19 12:26:08 +02:00
Pierre Goiffon
8a6d66effd 📝 Fix PHPDoc 2020-06-18 11:08:18 +02:00
Pierre Goiffon
6885d64124 📝 N°1418 DBObject PHPDoc 2020-06-17 19:03:29 +02:00
acognet
6fa153ae8b N°3107 - Remove code merged by mistake 2020-06-17 15:20:52 +02:00
acognet
e226222c2a N°3102 - widget regression: OQL syntax error now crash the page instead of displaying an error in place of the widget 2020-06-17 11:11:48 +02:00
Pierre Goiffon
aca0143e89 📝 PHPDoc for BackgroundProcess exceptions 2020-06-17 09:25:38 +02:00
Pierre Goiffon
1968c60770 📝 \DBObjectSet::ToArray PHPDoc 2020-06-16 12:32:57 +02:00
Pierre Goiffon
26014f410a Set back version for 2.7.1
Was set to 2.8 by mistake in 23afee51 (PR #125 that was rebased in GitHub web)
2020-06-15 15:49:01 +02:00
Pierre Goiffon
8912618732 Revert "N°2214 Add PHP check in CLI scripts"
This reverts commit c768e18e2b.
No risk taken for the 2.7.1 : this will be included but for 2.8 !
2020-06-15 15:18:26 +02:00
Eric
7bee718a13 N°3775 - Dashboard Definition with unknown class leads to an error 2020-06-15 14:53:58 +02:00
Pierre Goiffon
2705543efd N°2997 new test for AbstractWeeklyScheduledProcess
Document the way GetNextOccurrence works O:)
2020-06-12 18:20:07 +02:00
Pierre Goiffon
1e6b885301 SetupUtils : add missing public access keyword for methods 2020-06-12 16:50:04 +02:00
Pierre Goiffon
c768e18e2b N°2214 Add PHP check in CLI scripts
It is quite common that the PHP interpreter that is launched in CLI is different that the one used by the webserver. So iTop code launched by CLI could run in a context that doesn't meet iTop requirements !

This adds in the following scripts the same control that is done on the setup wizard first step :
* cron.php
* backup, check-backup
* export, exportv2
* bulk import
* synchro-exec, synchro-import

If the check throws at least one error then the script is stopped with an appropriate message, and a log is made (IssueLog, Error level, CLI channel)
2020-06-12 16:46:37 +02:00
Eric
d4b93f3bf0 N°2641 - Create a dedicated ErrorPage for fatal errors 2020-06-11 17:16:47 +02:00
acognet
6354c62c2b N°3012 - Fix blocking MTT/MTP when /extensions 2020-06-11 14:13:27 +02:00
acognet
cf8a12fe95 PMP light first version - small evolutions 2020-06-10 10:44:18 +02:00
acognet
36804dfcf4 N°3098 - Portal with IE : apply a transition ends with blank page 2020-06-10 10:44:17 +02:00
Eric
6966c0498a N°3071 - fix missing index for AttributeSet (for migration) 2020-06-08 16:50:32 +02:00
Eric
bbffc40ee0 N°3074 - Fix dashlet creation for IE 2020-06-08 16:20:41 +02:00
acognet
1b7473365d N°3075 - Fix syntax error with PHP 5.6 and TCPDF 6.3.4 2020-06-05 17:03:46 +02:00
Eric
0b84e809f6 Add cache to twig templates 2020-06-05 15:52:26 +02:00
acognet
a858362622 N°3080 - Portal cannot display more 10 attachments 2020-06-05 09:42:04 +02:00
Eric
d195c2b4c9 N°3071 - fix missing index for AttributeSet 2020-06-04 17:04:07 +02:00
acognet
28b75f29e5 N°3020 - Recurring PHP Notice with itop-fence "Undefined index: login_temp_auth_user 2020-06-04 16:21:07 +02:00
Eric
9d8a7bf561 N°3007 - Warn the user that installing a patch on a non conform install is not recommended 2020-06-04 10:38:58 +02:00
Eric
8064a20718 N°2970 - Reset conditions of joined filter because they can be used later by the Filter() method 2020-06-03 11:47:12 +02:00
acognet
f301a283e2 N°3015 - Fix "Undefined index: login_mode" Notice 2020-06-02 16:04:50 +02:00
Pierre Goiffon
e6a8f492d5 N°3049 Fix notice when having an ENUM field with values containing parenthesis 2020-05-28 15:38:21 +02:00
Pierre Goiffon
336637a7a4 SetupLog : ease changing manually the default level
In setup no conf file available so the log_level_min config option cannot be read
A solution is to manually change this constant
2020-05-28 11:40:22 +02:00
acognet
0e5a501b2a N°3012 - Fix blocking MTT/MTP when /extensions 2020-05-27 10:13:44 +02:00
acognet
59af58a173 N°3008 - Align transition form markup metadata to regular form in the backoffice 2020-05-27 10:00:20 +02:00
acognet
7f922560ba N°1976 - Duplicate Service on Customer Contract - formating code 2020-05-27 09:35:02 +02:00
acognet
d2e286345e N°1976 - Duplicate Service on Customer Contract 2020-05-27 09:30:52 +02:00
Pierre Goiffon
fb120bdc7c Merge remote-tracking branch 'origin/support/2.7.0' into support/2.7 2020-05-26 08:44:21 +02:00
Pierre Goiffon
5548997f3e 📝 README : fix for 2.7.0-2 2020-05-26 08:43:11 +02:00
bruno DA SILVA
156828c448 Merge branch 'feature/2958_unescape_slack' into support/2.7 2020-05-25 16:07:05 +02:00
bruno DA SILVA
04ef2b0454 2958 - test a restore 2020-05-25 15:52:37 +02:00
bruno DA SILVA
076d2e3d46 2958 - test a failure 2020-05-25 15:51:42 +02:00
bruno DA SILVA
0c6ab86e54 2958 - Slack notification : fix escaped branch name 2020-05-25 15:34:18 +02:00
bruno DA SILVA
876db3e58f 2958 - Slack notification : fix escaped branch name 2020-05-25 15:32:16 +02:00
Molkobain
5f7fe345cc Update README with iTop 2.7.0-2 information 2020-05-20 10:23:18 +02:00
Molkobain
cb6f78c9e3 Update README with iTop 2.7.0-2 information 2020-05-20 10:22:34 +02:00
acognet
8c86908652 N°3023 - Portal: Fix filter brick input not working in IE11 2020-05-19 10:17:27 +02:00
acognet
7e69256cb4 N°2668 - Notifications - Export wrong attribut format in html 2020-05-18 21:52:31 +02:00
acognet
83e3c089a4 N°1976 - Duplicate Service on Customer Contract 2020-05-18 21:51:29 +02:00
Pierre Goiffon
0d1059a8fc Merge remote-tracking branch 'origin/support/2.6' into support/2.7
# Conflicts:
#	application/utils.inc.php
#	conf/web.config
#	datamodels/2.x/itop-backup/ajax.backup.php
#	datamodels/2.x/itop-backup/status.php
2020-05-18 09:24:46 +02:00
Eric
e2f15ca24a 🌐 Add ES_CR translations to Application Upgrade Menu
From PR#128 by Federico Lazcano
2020-05-15 14:39:22 +02:00
Eric
7628b85b70 🌐 Add ES CR translations for DB Tools
From PR#127 by Federico Lazcano
2020-05-15 14:13:24 +02:00
Eric
525f600c18 🌐 Config Menu title should be uppercase
From PR#126 by Federico Lazcano
2020-05-15 14:04:15 +02:00
Pierre Goiffon
0ffa2850ea Deadlock log : log inside a dedicated log file instead of creating an EventIssue object (#139)
First log implementation (75730ee) was creating EventIssue objects, and was rollbacking transaction if it exists

The new one has some benefits :

* always log one line by default in log/error.log, but details must be activated though config (channels `Deadlock-WaitTimeout` and `Deadlock-Found`)
* detailed logs are in a dedicated file (log/deadlock.log) : 
  - easier for our clients to get and share
  - has rotation by default
  - looking at the file size is a direct way to know if error happened
  - more compliant to industry standards !
* the transaction stays untouched, so that the consumer can do whatever it prefers
2020-05-14 17:49:05 +02:00
Eric
fa3610cfee N°2641 - Create a dedicated ErrorPage for fatal errors
Fix fatal errors being logged in setup.log instead of error.log
2020-05-14 14:37:38 +02:00
acognet
898ee95a2c N°1997 - dbClick to exit the "description" field when creating an incident on the portal 2020-05-14 13:03:49 +02:00
Pierre Goiffon
730570f1f8 📝 MFCompiler language injection 2020-05-14 11:43:57 +02:00
Pierre Goiffon
80ce1eb125 N°2984 Security hardening 2020-05-14 11:33:48 +02:00
Pierre Goiffon
228a945da9 N°2984 Security hardening 2020-05-14 11:26:35 +02:00
bruno DA SILVA
79909fadc0 N°2556 - Repair CI 2020-05-14 10:49:31 +02:00
bruno DA SILVA
746b47bb0e N°2556 - Html sanitization preserve content of removed tags (except for a forbidden list)
forbidden list: see $aTagsContentRemovableList
2020-05-14 10:33:30 +02:00
acognet
150d3e096d N°2346 - Function GetTrackOrigin() doesn't return good value during csvimport 2020-05-13 23:55:26 +02:00
Thomas Casteleyn
23afee514d 🌐 Update nl.dictionary.itop.ui.php (#125) 2020-05-13 14:38:32 +02:00
acognet
48c5698f08 N°2934 - Backoffice theme: Add variable for menu group background color 2020-05-13 12:22:35 +02:00
acognet
1a4ee0f977 N°1953 - Dashlet Title alignment not consistent : Left on List, Center on Table/Pie/Chart 2020-05-13 12:22:35 +02:00
Eric
1ca39618e1 N°1610 - Fix [DBObject] ExecAction - apply_stimulus
removed unnecessary test
2020-05-13 11:38:22 +02:00
Eric
7bb1f9f423 N°2937 - fix export error on EventIssue object 2020-05-13 11:24:34 +02:00
Pierre Goiffon
834297e675 N°2985 Security hardening (#140)
Thanks @bruno-ds  for the review !
2020-05-13 10:04:40 +02:00
bruno DA SILVA
21c2574cd9 N°2358 - Fix deletion of a single replica within a list 2020-05-13 09:37:36 +02:00
Pierre Goiffon
6d9923be68 AbstractWeeklyScheduledProcess fix typo and add @noinspection 2020-05-13 08:45:03 +02:00
bruno DA SILVA
839bbc425f N°2901 Add log to help diagnose lost InlineImage
they are disabled by default, use this to enable:
 ```
 'log_level_min' => array(
 		'InlineImage' => LogAPI::LEVEL_TRACE,
 		'UserRequest' => LogAPI::LEVEL_TRACE,
 	),
```
2020-05-12 15:34:13 +02:00
acognet
70cc19768a N°1953 - Dashlet Title alignment not consistent : Left on List, Center on Table/Pie/Chart 2020-05-12 14:48:56 +02:00
acognet
873d109b98 N°1910 - iTop - Search on Text contains "_" not working - move correction in other place 2020-05-12 14:48:16 +02:00
Eric
a81950571a N°1598 - Fix regression on modify 2020-05-12 14:21:34 +02:00
Eric
bcd9679957 N°3006 - Fix filtering an UNION with parent class 2020-05-12 12:08:18 +02:00
Eric
2c10913fe5 N°2093 - Keep object values when a stimulus action fails 2020-05-12 11:29:30 +02:00
Eric
0342b89481 N°1598 - warning for bad stimulus instead of fatal error 2020-05-12 11:01:04 +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
acognet
3fd55c6dd6 N°1693 - the history of AttributeEncryptedString must not interpret HTML tags 2020-05-11 12:14:55 +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
Eric
c15b3462d1 N°2945 - Adding an empty file as an attachment is generating a fatal error
Changed error message
2020-05-07 08:49:05 +02:00
Pierre Goiffon
32f05ea917 👥 Added Pascal Schirrmann as contributor (N°2980, thanks to him !) 2020-05-07 08:36:53 +02:00
Molkobain
6a50b55a2a N°1598 - Improve user feedback on invalid transition: Display a better error message to the user in the portal 2020-05-06 16:58:25 +02:00
Eric
72f11c6a4d N°2815 - Fix basic authentication with Apache
Added support for REDIRECT_HTTP_AUTHORIZATION
2020-05-06 11:35:56 +02:00
Eric
609ea47f7b PHPDoc 2020-05-06 10:29:47 +02:00
Pierre Goiffon
74b3cfd46c Merge remote-tracking branch 'origin/support/2.7.0' into support/2.7 2020-05-06 10:13:15 +02:00
acognet
f7ea6c09cd N°2589 - Infinite loops when logging with a Contact having a non empty TagSet field 2020-05-05 19:00:24 +02:00
acognet
526a7f9817 N°1910 - iTop - Search on Text contains "_" not working - convert _ to \_ in javascript 2020-05-05 18:36:38 +02:00
Eric
5ccb1ef72a N°1662 - Fix Auto-complete on external key ignore obsolescence user preference
ValueSetObjects now consider obsolete data
2020-05-05 11:14:59 +02:00
Pierre Goiffon
180da03f08 N°2980 Fix backup not executed anymore
Regression introduced by #89
2020-05-05 09:00:40 +02:00
Pierre Goiffon
7ec7626aa0 N°2977 PHP Doc change 2020-05-04 18:13:40 +02:00
Eric
f92a980b4d N°2974 - Fix Global Search doesn't search in external field.
For External Field, allow the search also for FriendlyNames.
2020-05-04 18:13:18 +02:00
Pierre Goiffon
5d7582bb6f N°2977 LogAPI : restore default log level to OK, and really allow LEVEL_DEFAULT overloads
* Level was changed by mistake to trace with refactoring in 289171b9
Thanks @v-dumas !

* self wouldn't allow to override
see https://www.php.net/manual/fr/language.oop5.late-static-bindings.php
Thanks @bruno-ds !

* improve PHPDoc !
2020-05-04 16:55:46 +02:00
bruno DA SILVA
7a40db94fb 2424 - Better messages when an object update fail & removed an unwanted webserver error log entry 2020-05-04 12:00:30 +02:00
Eric
843798505a N°2974 - Fix Global Search doesn't search in external field
The IsSearchable() check was wrong for some attributes
2020-05-04 11:40:02 +02:00
Pierre Goiffon
bf13f9fc8a N°2975 improve RotatingLogFileNameBuilder next cron occurrence computation 2020-04-30 08:41:55 +02:00
Pierre Goiffon
289171b9f1 N°2977 LogAPI : allow to overwrite the default log level 2020-04-29 15:16:45 +02:00
Pierre Goiffon
9b065ffb0a Merge remote-tracking branch 'origin/support/2.7.0' into support/2.7
# Conflicts:
#	datamodels/2.x/itop-attachments/renderers.itop-attachments.php
2020-04-29 09:00:10 +02:00
Pierre Goiffon
96d888fcf3 N°2968 fix email-reply notification not updated
- add a specific container for attachments list, upload button and #attachment_plugin hidden input is outside of it
- refactor code between abstract class and implementation, add some comments
- now refreshes only the attachment list instead of the whole content
2020-04-28 17:47:20 +02:00
Vladimir Kunin
a182a37139 Add Russian translations for 2.7.0-1 (rebased) 2020-04-28 08:42:17 +02:00
Pierre Goiffon
23c15c1b6c Revert "N°2902 - Intersect with union generates unwanted alias renaming"
This reverts commit 866e4ab995.

Fix isn't yet commited, so we don't want to break the build.
The fix will be done in the hotfix/2902_intersect_alias branch
2020-04-27 09:36:38 +02:00
Eric
866e4ab995 N°2902 - Intersect with union generates unwanted alias renaming 2020-04-24 18:32:40 +02:00
Eric
75730eeea0 Log database deadlocks in EventIssue 2020-04-23 15:25:12 +02:00
Pierre Goiffon
58fd8709be Merge remote-tracking branch 'origin/support/2.6' into support/2.7
# 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/combodo-db-tools/module.combodo-db-tools.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-04-22 11:14:59 +02:00
Eric
f18ea18a5b N°2936 - TLs option is not set for restore function 2020-04-21 16:59:16 +02:00
Pierre Goiffon
1904bfdba6 css-variables : update to 2.7.0-2 2020-04-21 16:35:41 +02:00
acognet
e1949cd3eb N°2509 - Change Columns via "Configure this list" show obsolete data though user preferences is "not shown obsolete data" 2020-04-21 12:45:59 +02:00
Eric
1b2d3d1e84 N°2952 - Provisioning for hybrid auth fails
Changed Origin for change to an allowed value
2020-04-21 11:59:48 +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
acognet
0b95dbee7f N°1588 - Count on Managed Brick sometimes wrong 2020-04-20 16:31:56 +02:00
Pierre Goiffon
db593ff85e Merge remote-tracking branch 'origin/support/2.6' into support/2.7
# Conflicts:
#	application/loginwebpage.class.inc.php
#	application/menunode.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/controllers/aggregatepagebrickcontroller.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/controllers/userprofilebrickcontroller.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/views/bricks/manage/popup-export-excel.html.twig
#	pages/ajax.render.php
2020-04-20 16:05:56 +02:00
Eric Espié
1f750bb12d N°2902 Fix alias renaming when already exists in one OQL of an UNION
The legacy impl is not modified
2020-04-20 14:59:56 +02:00
Pierre Goiffon
4ee66377ce Merge commit '15e5e21a89a3d3214dace82b8765a47e304a8f29' into support/2.7 2020-04-20 14:21:55 +02:00
Eric
432a950f8c N°2945 - Fix fatal error when adding empty attachment
alert when empty attachment is detected
2020-04-20 10:54:27 +02:00
acognet
bbc751bee4 N°2383 - GetAttributeFlag ignored on form refresh with dependent field 2020-04-14 17:56:27 +02:00
acognet
a77ba2fbab N°2564 - Stop copy after "<" character in a Copy operation on a Transition - change only in Copy function 2020-04-14 17:56:27 +02:00
Eric
5b60ec9edf N°2919 - Dashboard - Fix dashboard not saved
the sanitization was too strong. Some names can contain ':'
2020-04-10 18:11:36 +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
Molkobain
3103f361a4 Update 2.7.0-1 release date 2020-04-08 11:02:03 +02:00
Eric
3a37e24496 N°2306 - Security hardening 2020-04-08 09:28:20 +02:00
acognet
621295199c N°1402 - Attribut File cannot be emptied Add a trash next to the name of the file 2020-04-06 16:36:46 +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
Eric
c1a7a36896 Compatibility with MySQL 5.6 2020-04-06 09:02:06 +02:00
acognet
d5fe653e51 N°2848 - Align creation and update message on portal to console message - add a comment for next time 2020-04-02 20:31:06 +02:00
acognet
fc2fb235a2 N°1344 - Save without all mandatory attributes (ajax reload not finished) 2020-04-02 17:46:18 +02:00
acognet
d7211509bd N°1062 - Portal : autocomplete and search = broken : change the max size of the list 2020-04-02 17:29:39 +02:00
acognet
c182b1a01f N°2848 - Align creation and update message on portal to console message 2020-04-02 11:50:01 +02:00
Eric
15e5e21a89 Compatibility with MySQL 5.6 2020-04-01 17:37:55 +02:00
acognet
ee0d231426 N°2895 - Tab dictionnary entries not taken in account in "Printer Friendly Version" screen 2020-04-01 01:05:13 +02:00
acognet
3282b46c9b N°2395 - Error in file light-gray.scss 2020-04-01 00:01:26 +02:00
acognet
05649ba50f Merge branch 'master' of github.com:Combodo/iTop 2020-03-31 23:48:35 +02:00
acognet
40efc4cbb1 N°1062 - Portal : autocomplete and search = broken 2020-03-31 23:47:46 +02:00
Molkobain
30034d381b Update version number to 2.7.0-1 2020-03-31 09:47:37 +02:00
Molkobain
986eb90546 N°2893 - Fix DataModel Viewer not supporting special chars in class name (eg. ") 2020-03-31 09:40:31 +02:00
Pierre Goiffon
eb41d3e2ef 📝 Fix erroneous PHPDoc for InlineImageGC 2020-03-30 17:09:04 +02:00
acognet
c6b16bb52e N°2119 - Dashlet Header statistic on ExternalKey, display id instead of name 2020-03-30 16:24:12 +02:00
acognet
95adbbb58f N°1181 - List of searchable classes in SearchMenuNode - add user rights tests 2020-03-30 16:23:23 +02:00
acognet
60f5c60059 N°1796 - Search : false criteria after using the magnifier 2020-03-30 16:19:23 +02:00
acognet
c0284ecc3b N°1953 - Dashlet Title alignment not consistent : Left on List, Center on Table/Pie/Chart 2020-03-30 16:18:43 +02:00
acognet
fc7b772ba3 N°1910 - iTop - Search on Text contains "_" not working
_ is a special caracter in mysql -> replace with \_
2020-03-30 16:13:47 +02:00
Pierre Goiffon
011d742ae3 N°2891 📌 add mbstring as optional extension 2020-03-27 14:41:59 +01:00
Pierre Goiffon
5b496f4d15 N°2866 Change "cron" case in labels as it is not an acronym
Thanks @Hipska for the feedback done in Combodo/iTop#124 !
2020-03-26 11:15:47 +01:00
Pierre Goiffon
97f4c32271 N°2881 Improve robustnedd of \ModuleInstallerAPI::RenameEnumValueInDB
Was causing errors when migrating from datamodels where the fields were not enum yet (this method is usually launched using \ModuleInstallerAPI::BeforeDatabaseCreation, so before an ALTER could be done to the column)
2020-03-26 10:18:34 +01:00
Pierre Goiffon
c002ca7902 setup.css : update .css that was not up to date :/ 2020-03-26 10:13:10 +01:00
Pierre Goiffon
ff22074418 🎨 setup.css : remove warnings & unused code
Removed unused legacy code :
* #header>H1 : a noline-height ugly when renamed to line-height, seems totally unused when noline-height
* table.formTable : cellpadding & cellspacing
2020-03-26 10:09:08 +01:00
Pierre Goiffon
84968ff550 Merge remote-tracking branch 'origin/release/2.7.0' 2020-03-26 08:50:14 +01:00
bruno DA SILVA
46151c87c0 N°2888 Check password policy only if field set with a string
Some callers are setting the field using an ormPassword object containing hashed password + salt

examples:
 - csv import
 - data synchro
 - ...
2020-03-25 12:43:41 +01:00
Pierre Goiffon
75a900c6f8 🚀 Tool to batch update XML datamodel version 2020-03-24 16:46:48 +01:00
Molkobain
e8c9d99783 Increase XML version to v1.7 2020-03-24 14:06:41 +01:00
Eric
1600302ad9 N°2869 - Removed Check for bad finalclass in root classes (already done) 2020-03-19 15:40:52 +01:00
Eric
a9c3a1b782 N°2869 - Check for bad finalclass in root classes (Allow all non-abstract child classes) 2020-03-19 10:01:16 +01:00
Eric
74848254a4 N°2869 - Check for bad finalclass in root classes 2020-03-18 14:10:35 +01:00
Eric
d7d9bfe0fd N°2869 - Check for bad finalclass in intermediate classes 2020-03-18 10:09:05 +01:00
odain
dd96dec100 Fix license file generation; exclude itop-portal-base 2020-03-18 08:42:55 +01:00
Pierre Goiffon
16ff51f3b7 📄 Update licenses after generation tool upgrade
See 76d26e8e
2020-03-18 08:15:09 +01:00
Pierre Goiffon
27c651b33c 📄 Remove itop-portal-base from license file 2020-03-17 18:08:49 +01:00
Pierre Goiffon
32375265cb 📄 Remove 2.x/authent-cas from license file 2020-03-17 17:47:25 +01:00
Pierre Goiffon
0cba163dc9 🔖 Update version to final in iTop files 2020-03-17 16:50:45 +01:00
Pierre Goiffon
fd1e17cc32 🚀 Release tool to update versions 2020-03-17 16:49:05 +01:00
Eric
d85e1906b7 N°2746 - New Attribute Enum Set
XML migration from 1.7 to 1.6
2020-03-17 12:03:10 +01:00
Pierre Goiffon
f8df84aa7b Update dict for 2.7.0-RC 2020-03-17 10:55:53 +01:00
Eric
c26b9459bb N°2869 - Fix 2.7 Migration
Run UPDATE requests just after the corresponding ALTER TABLE requests
2020-03-16 18:49:12 +01:00
Molkobain
4f7676c42d N°2735 - Rollback previous "fixes" to keep the simple ID policy in the Designer and a unique ID generation at runtime 2020-03-16 12:17:09 +01:00
Molkobain
ceddafaebe N°2735 - Rename parameter for better consistency 2020-03-16 12:17:09 +01:00
Stephen Abello
950640babe N°1986 - Revert feature 2020-03-13 10:24:25 +01:00
jbostoen
11e6be1037 🌐 Added NL translations (#124)
Co-authored-by: jbostoen <->
2020-03-13 09:42:37 +01:00
Molkobain
29d963317f N°2735 - Fix dashlet ID generation to have the "CUSTOM" prefix only at runtime 2020-03-12 16:46:15 +01:00
Molkobain
dd300e075c N°2735 - Fix dashlet edition in the Designer (property form ID was not matching dashlet's) 2020-03-12 16:46:03 +01:00
Molkobain
774ace2302 Fix icon select widget to be compatible with iTop 2.7 2020-03-12 14:16:03 +01:00
Molkobain
bbfddea93d Open new_dashlet_id operation for Designer 2020-03-12 14:16:02 +01:00
Stephen Abello
c5c7fd5c85 N°2855 - Security hardening 2020-03-12 14:13:17 +01:00
jbostoen
5d4b9f4a89 🌐 Fix typos in English translation (#123) 2020-03-12 08:51:40 +01:00
Stephen Abello
d01caaf4e4 N°2853 - Security hardening 2020-03-10 10:23:38 +01:00
Pierre Goiffon
f895821db9 ⚗️ CONTRIBUTING : added some emoji O:) 2020-03-06 20:52:33 +01:00
bruno DA SILVA
19f34d1a72 composer reflexion: list outdated packages 2020-03-05 11:33:36 +01:00
Pierre Goiffon
7ff1a03a3c N°2820 monthly log rotation : restore default config 2020-03-04 16:04:19 +01:00
Pierre Goiffon
eadc3b72c2 📝 N°2793 log rotation add PHPDoc about timezones 2020-03-04 14:23:34 +01:00
odain
c06f8e9a98 N°2793 log rotation test : fix timezone issues 2020-03-04 12:05:42 +01:00
Pierre Goiffon
6675d7d42a N°2793 Test log rotation 2020-03-04 09:21:05 +01:00
Eric
afc118e9c2 🐛 fix GetAsPlainText() on EnumSet 2020-03-03 17:34:15 +01:00
Pierre Goiffon
f36fcb2a2d N°2820 Log rotation : change default from weekly to monthly 2020-03-03 15:31:11 +01:00
Eric
f062af367d N°2826 - Bad SQL request for group by with data-localizer
Unit tests to check the fix in data-localizer
2020-03-03 15:25:12 +01:00
Pierre Goiffon
29d24faf52 N°2793 Log rotation : fix no rotation :/
Was caused by erroneous file exists test
2020-03-03 10:18:09 +01:00
Pierre Goiffon
33f3f2810e N°2793 Log rotation : add file exists check in the lock 2020-03-02 18:33:00 +01:00
Pierre Goiffon
fad00200b6 🔧 PHPStorm remove is_null() rewrite inspection 2020-03-02 15:56:26 +01:00
Pierre Goiffon
56ef6feadf N°2820 Log rotation : new MonthlyRotatingLogFileNameBuilder class 2020-03-02 15:52:59 +01:00
Pierre Goiffon
2be16f9078 N°2793 Log rotation (#117)
Now log file name is unchanged : current log is still /log/error.log \o/

Rotation check (using file last modification time) is done :
* on each file write : we don't want to miss calls if session last from 23:59:59 to 00:01 for example ! Though the filemtime() call is done once per session to lower performance impacts
* using a new background process (LogFileRotationProcess)

File renaming on setup is therefore removed.
Also the interface is renamed (from ILogFileNameBuilder to iLogFileNameBuilder) to conform to iTop convention.
2020-03-02 15:01:12 +01:00
Eric
6874aed4a2 N°1627 - Ticket ref sometimes duplicate
add MakeInsertQuery() to legacy
2020-03-02 12:04:12 +01:00
Pierre Goiffon
07b8830436 N°2814 Fix cannot authenticate in some HTTP calls
basic mode was forced in 0dd1f26b
scripts concerned :
* synchro/synchro_import.php
* webservices/cron.php
* webservices/import.php
2020-03-02 11:56:00 +01:00
Molkobain
39d3e00ba1 N°2822 - Fix timeout message through AJAX calls in the Portal 2020-02-28 16:53:16 +01:00
Stephen Abello
ffa43160bf N°1164 #1491 - Add padding and border to code blocks 2020-02-28 16:42:46 +01:00
Pierre Goiffon
a45d1336f4 🎨 Change \ormStopWatch::ComputeGoal for IDE convencience, add phpdoc 2020-02-28 12:02:20 +01:00
Federico Lazcano
5a287fabba 🌐 Typo in ES_CR User Requests 2020-02-28 08:23:34 +01:00
Federico Lazcano
da86ee4114 🌐 Typo in ES_CR Incidents 2020-02-28 08:22:59 +01:00
Federico Lazcano
5157788afe 🌐 Typo in ES_CR User Requests 2020-02-28 08:22:14 +01:00
Molkobain
386e25efd6 N°2314 - Remove basque-red, ocean-blue and test-blue from default themes 2020-02-27 15:41:54 +01:00
Molkobain
649e2f8e6a Internal: Remove unused import 2020-02-27 15:10:41 +01:00
Molkobain
3c3d744747 N°2314 - Refactor part of the compilation in dedicated helpers 2020-02-27 15:09:57 +01:00
Molkobain
1371eee826 N°2735 - Continue rework of the dashlet id generation: Dashlet could not be added in the Designer 2020-02-27 11:54:20 +01:00
Molkobain
6645a5053f N°2806 - Fix errors on legacy portal "portal" tag during migration to iTop 2.7 2020-02-26 17:17:56 +01:00
Molkobain
e2a3e0e74f N°2735 - Continue rework of the dashlet id generation:
- Move generation from DashboardLayout to Dashboard
- Migrate dashlet user preference in RuntimeDashboard only (and not in DesignTimeDashboard)
2020-02-26 16:29:32 +01:00
Molkobain
401f82062a N°2735 - Make sure to always have the dashboard (sanitized) id for dashlets rendering 2020-02-26 12:10:18 +01:00
Molkobain
5a01a76f80 N°2735 - Add new sanitize filter ('element_identifier') for dashboard identifier 2020-02-26 12:10:18 +01:00
Pierre Goiffon
3e5520d079 N°2735 Fix new dashlet id didn't contain dashboard id 2020-02-26 09:13:19 +01:00
Pierre Goiffon
beef2a89a3 N°2684 Remove upgrade from another repository
This upgrade procedure was :
* dangerous : running two iTop of different versions on the same database should not be done
* insufficient : just /extensions/* was copied, not any Hub or Designer data, no log, no instance.txt, ...
2020-02-25 18:01:59 +01:00
Molkobain
2f920cbb46 Internal: PHPDoc and warnings suppression 2020-02-25 17:45:18 +01:00
Pierre Goiffon
feae36e5b8 N°2735 Fix dashlet id duplicates when moving dashlet from one cell to another 2020-02-25 15:43:20 +01:00
Stephen Abello
92ae0e72e1 N°2314 - Markup extensibility: Add a variable for hovered table lines background color 2020-02-25 15:14:12 +01:00
Stephen Abello
ed030403aa N°2112 - Remove unused legacy portal conf variable and its usage 2020-02-25 14:00:58 +01:00
Pierre Goiffon
dfc894f6fd N°2735 Fix cannot edit new dashlet properties regression
Was introduced by cf83bc73
2020-02-25 11:17:52 +01:00
Stephen Abello
368b49ef8f N°2314 - Markup extensibility: Fix table sorter icons in html export pages 2020-02-25 10:36:56 +01:00
Stephen Abello
ccfd3848fb N°1164 #1491 - Fix syntax code highlighting display in CaseLog/HTML fields 2020-02-25 09:54:18 +01:00
Molkobain
ea59f7bc23 N°2314 - Markup extensibility: Add metadata to caselogs in the admin. console 2020-02-24 18:22:01 +01:00
Molkobain
9d6ed7f489 N°2806 - Fix errors on legacy portal constants during migration to iTop 2.7 2020-02-24 17:03:13 +01:00
Molkobain
0aa006f7c4 Internal: Fix typo in PHPDoc 2020-02-24 16:47:10 +01:00
Molkobain
c669d6951b PHPDoc and warnings suppression 2020-02-24 16:36:31 +01:00
Molkobain
9412f260ae PHPDoc 2020-02-24 11:01:24 +01:00
Molkobain
9781a11988 N°2803 - Regression: Fix "forgot_password" parameter not working anymore 2020-02-24 10:58:24 +01:00
Molkobain
1ed0210fe2 N°2799 - Fix double encoding in "top-list" display mode of the ManageBrick 2020-02-24 09:47:22 +01:00
Molkobain
1ce5ec73ea N°2798 - Fix unable to submit portal forms (Regression from dba6e8ce) 2020-02-24 09:24:23 +01:00
Eric
98304e2bda N°2596 - Allow '1' as true value for boolean in XML files 2020-02-21 18:14:03 +01:00
Pierre Goiffon
04fc58b55c 📝 Some @since annotations were missing complete version (ex 2.5 instead of 2.5.0) 2020-02-21 18:05:30 +01:00
Eric
096c3a3f13 N°2772 - Revert the loading of JS Dict in setup pages 2020-02-21 17:15:12 +01:00
Eric
87e22163d7 N°2037 - Add Twig template rendering to the WebPage 2020-02-21 14:35:25 +01:00
acognet
4cc8b89f4e N°2037 - New dashlet Gantt - add method to insert twig in an existing page 2020-02-21 12:09:15 +01:00
Pierre Goiffon
19809249a2 📝 Update PHPDoc for StopWatches interfaces 2020-02-20 18:01:09 +01:00
Molkobain
7347eed3ac PHPDoc 2020-02-20 17:43:14 +01:00
Molkobain
69d816e345 N°2275 - Add XML delta cleanup on datamodel BC breaking changes introduced in 2.7.0 2020-02-20 17:43:14 +01:00
Vincent Dumas
4008cb7688 Add blocks to enable customization 2020-02-20 17:18:39 +01:00
Lars Hippler
41a1bede70 🌐 Update DE-dictionary for iTop 2.7.0 (#113)
Many thanks @r0ert !
2020-02-20 16:04:24 +01:00
odain
e12845e412 N°2651 - Remove test directories from lib 2020-02-20 15:03:36 +01:00
odain
b30ad45792 N°2651 - Fix missing autoload 2020-02-20 14:58:39 +01:00
odain
84a11fb3c1 added namespace + mv iTopComposer + optimize FileIterator 2020-02-20 14:56:08 +01:00
Pierre Goiffon
ee39a387db N°2651 Remove tests from lib : browse dirs using SPL classes instead of GLOB 2020-02-20 14:56:08 +01:00
bruno DA SILVA
e3c6ac814e N°2651 - Removal of "Test" dirs within dependencies handled using composer 2020-02-20 14:56:08 +01:00
Eric
d668d65c70 N°2772 - Fix errors during upgrade. Prevent JS Dict load for setup pages. 2020-02-20 14:45:39 +01:00
Pierre Goiffon
e21e7c9cf0 🌐 N°2795 Fix dict typos 2020-02-20 09:36:33 +01:00
bruno DA SILVA
27a0de1da1 N°2154 - fix server crash in rare cases
Under undetermined circumstances, `exec('php -v')` called the current script triggering an infinite loop crashing the server
problem reported by @molkobain
see: https://stackoverflow.com/questions/43728378/running-php-files-through-shell-exec
2020-02-19 15:43:33 +01:00
Molkobain
d76e54996c PHPDoc 2020-02-19 11:54:50 +01:00
Pierre Goiffon
a4710f7542 N°2760 Abstract classes for extension API interfaces : remove return; for @return void methods 2020-02-18 18:15:45 +01:00
Eric
98a9c680c5 🐛 Updated rest example 2020-02-18 17:02:13 +01:00
Pierre Goiffon
a92157f763 N°2790 fix collapsibleLabel
* change icon when label closed
* fix switch in about dialog for licenses details
2020-02-18 16:34:51 +01:00
bruno DA SILVA
412f1a394f N°2574 - 💚 fix unit test
The behaviours has changed since the "password_renewed_date" is not changed only after the inter/update and no more just aftyer the $oUserLocal->Set('password')
2020-02-18 14:49:53 +01:00
Molkobain
dba6e8ce1a Fix images being too wide in HTML fields and caselogs in the end-users portal
Regression introduced in a previous version of iTop.
2020-02-17 16:29:21 +01:00
Molkobain
a127ca9ca0 N°2313 - Fix regression: No more validation message on password update in the end-users portal 2020-02-17 15:51:38 +01:00
Molkobain
0b5ee1e05c Internal: Fix typo in PHPDoc 2020-02-17 11:24:20 +01:00
Eric
f94e86ecea 🐛 Add missing function 2020-02-14 17:18:00 +01:00
Pierre Goiffon
fe770f36c5 N°2634 / N°2735 Migrate dashlet user prefs to new dashlet ID format 2020-02-14 15:59:09 +01:00
Pierre Goiffon
cf83bc7364 N°2634 / N°2735 Fix dashlets identifiers : was causing prb on widget init, prefs save
Dashlet id now includes :
* "CUSTOM-" if dashlet is contained in a custom dashboard, nothing elsewhere
* the ID of the dashboard
  - for menus : menu id escaped for HTML
  - for AttributeDashboard : <class>__<field>
* the row / cell / dashlet idx

Examples :
CUSTOM-UserRequestOverview_IDrow1-col0-0
Organization__overview_IDrow1-col0-12
2020-02-14 15:59:09 +01:00
Eric
76982a2846 Revert Last change. The values are already protected at this stage. 2020-02-14 15:42:05 +01:00
bruno DA SILVA
4cedd30625 N°2574 - bugfix and UI
- 🐛 fix regression preventing automatic update of password_renewed_date
 - 💄 add a "general information" fieldset
2020-02-13 15:23:56 +01:00
bruno DA SILVA
a86079c477 N°2154 - 🐛 fix an awful typo producing a nonsense
I'm sorry!
2020-02-13 15:21:01 +01:00
Eric
128a237392 N°2746 - Fix Tags configuration screen (removed EnumSet from tag editable list) 2020-02-13 12:31:22 +01:00
Eric
0ecfffe413 N°2746 - Fix export separator 2020-02-13 12:12:35 +01:00
Eric
ef3bdd63a4 N°2746 - Fix search from shortcut 2020-02-13 11:56:19 +01:00
Eric
585135c6c7 N°2758 - Keep AddCondition to avoid BC break 2020-02-13 11:56:01 +01:00
Eric
b3faa96a45 🌐 Add Trigger context label 2020-02-13 09:53:55 +01:00
Eric
6f04525cdf 🎨 cleanup code 2020-02-13 09:49:58 +01:00
Pierre Goiffon
03834fedb8 N°2369 deprecate MySQL views 2020-02-12 18:11:12 +01:00
Vincent Dumas
6bde8e867f Move menu "Universal Search" under "Query" 2020-02-12 18:01:38 +01:00
Molkobain
0e3d195250 N°2275 - Fix XML delta computation putting flags on wrong XML levels 2020-02-12 17:40:53 +01:00
Pierre Goiffon
fae8c9edbd N°2780 Add ContextTag::TAG_CONSOLE for ajax operations 2020-02-12 17:20:10 +01:00
Pierre Goiffon
133d267aca N°2329 Update TCPDF to version fixing unlink bug
Was updated to 6.3.2 fot PHP 7.4 compat, but this version had a regression (issue 159 in the original repo)
This commit integrates 6.3.4 that includes a fix for issue 159
2020-02-12 15:23:57 +01:00
Stephen Abello
166986f336 N°2314 - Markup extensibility: Replace some hardcoded values by overloadable variables 2020-02-12 14:53:19 +01:00
Stephen Abello
f76d649d1a Wee cleanup 2020-02-12 14:53:19 +01:00
Stephen Abello
30747b92c7 N°2755 - Security hardening 2020-02-12 14:53:19 +01:00
Stephen Abello
12ce718662 Internal: Add HtmlEntityDecode() to utils, a counterpart to HtmlEntities() 2020-02-12 14:53:19 +01:00
Molkobain
a1cdb46663 Internal: Refactor newsroom SCSS rules to real SCSS 2020-02-12 14:20:27 +01:00
Pierre Goiffon
824d8398a3 N°2634 / N°2735 Allow saving list prefs for all DashletObjectList
The id generated for the dashlets in the markup is the one used in the saved appUserPreferences. As no control is done during compilation nor in the Designer editor, we could have duplicates.
The first fix (081ba68a) was adding a generated suffix, but for default dashlet this was generated each time so the id was different on every page load ! For custom dashlets as their definition was saved in a XML file it was ok.
This new fix adds a prefix containing row and col id, so every time the id is the same. No duplicates should be found in the same cell.
2020-02-12 14:07:57 +01:00
Eric
406774aa15 N°2746 - Fix Import/Export using labels or code 2020-02-12 12:08:40 +01:00
Pierre Goiffon
dd8712e2e8 📝 Add more doc for \DBObject::GetAsHTML 2020-02-12 11:44:00 +01:00
Eric
767bcdf117 N°2746 - Fix unit tests (typo) 2020-02-11 17:00:55 +01:00
Eric
5e060737df N°2746 - Fix unit tests 2020-02-11 16:46:24 +01:00
Eric
d9bf0fe012 N°2746 - Fix breadcrumb for search of enumSet 2020-02-11 16:02:55 +01:00
Eric
93c9783b1a N°2746 - Fix empty search for TagSet 2020-02-11 14:51:22 +01:00
Eric
e9c1467026 N°2746 - Fix "Modify All" fatal error 2020-02-11 14:35:38 +01:00
Eric
863cb4cad6 N°2758 - Allow only one condition on ValueSetDef and restore cache 2020-02-11 11:54:00 +01:00
Pierre Goiffon
94b70fc473 N°2776 ObjectFormManager : change transaction scope
* move ApplyStimulus & triggers out of the transaction
* move \utils::RemoveTransaction to a finally block
2020-02-11 11:36:59 +01:00
Pierre Goiffon
4dc383cba8 N°2684 Fix setup broke when upgrading with a config file from another directory
In the moduleschoice screens we were using a wrong approot_url !
2020-02-11 09:41:48 +01:00
Pierre Goiffon
55d8a2316a 📝 GetAttributeFlags PHPDoc revised 2020-02-11 08:34:57 +01:00
Stephen Abello
fe8f274c14 N°2314 - Markup extensibility: Replace a hardcoded value by an overloadable variable 2020-02-10 15:33:47 +01:00
Stephen Abello
72fad49c4e N°2314 - Markup extensibility: Add default color to body node 2020-02-10 15:33:47 +01:00
Eric
888d0775e6 N°2758 - Removed ValueSetDef cache 2020-02-10 14:29:28 +01:00
Molkobain
db19f71758 N°2771 - Fix "Unknown form type" when changing user language in portal 2020-02-10 14:20:11 +01:00
Molkobain
a259443735 N°2314 - Markup extensibility: Add attribute flags as metadata to object forms 2020-02-10 13:27:36 +01:00
Pierre Goiffon
58e8ca1f50 📝 GetAttributeFlags PHPDoc 2020-02-10 09:56:31 +01:00
Pierre Goiffon
ab79426508 N°2293 some PHPDoc update 2020-02-07 18:21:02 +01:00
bruno DA SILVA
7e61917521 N°524 - password validity message can be superseded with conf 2020-02-07 17:25:17 +01:00
Molkobain
e42aab30a5 Internal: Fix regression introduced in 3d2a844f ("Close" button always displayed in object forms in IE) 2020-02-07 17:22:41 +01:00
Molkobain
a79ef0bd51 Update (massively) translations before iTop 2.7 release 2020-02-07 16:51:21 +01:00
Eric
5d88391109 N°2758 - Reset ValueSetDef cache when modifying some parameters 2020-02-07 14:27:22 +01:00
Molkobain
c56c04d84d N°2760 - Ease API interfaces implementation through abstract classes 2020-02-06 18:09:59 +01:00
Molkobain
f2b8f50a94 Internal: Add Anne to the sample data to welcome her! 👋 2020-02-06 18:07:51 +01:00
Molkobain
9de11a29fb PHPDoc 2020-02-06 16:25:25 +01:00
Molkobain
6537e00453 Internal: Add Matthieu to the sample data to welcome him! 👋 2020-02-06 15:53:26 +01:00
Eric
dd5f4909da Fix warning 2020-02-06 15:01:02 +01:00
Stephen Abello
ed67df734f N°2755 - Security hardening 2020-02-06 14:50:27 +01:00
Stephen Abello
44894526f1 N°2742 - HTML files preview are now raw text only 2020-02-06 14:27:13 +01:00
xtophe38
de78963b30 Fix DataAdministration translation to be aligned with other menus 2020-02-06 14:25:52 +01:00
bruno DA SILVA
948fd6f0ce N°2154 - improve robustness of submitted config validator
thanks to @molkobain 's awful provider's using PHP 4.4 within CLI
2020-02-06 14:07:39 +01:00
bruno DA SILVA
214dbeef5b N°2154 - var into string patterns can now also be enabled using server vars
- usage: $_SERVER['ITOP_CONFIG_PLACEHOLDERS']
 - plus removal of useless log Trace since this code is too early in iTop's init process for this feature
2020-02-06 14:05:08 +01:00
bruno DA SILVA
f2fbd8457d N°2498 - Authorize map extension
so as `.js.map` is not forbidden by apache
2020-02-06 14:05:08 +01:00
Molkobain
6a432c6a25 N°2757 - Fix count in group by dashlets 2020-02-06 12:12:27 +01:00
Molkobain
e96a8387a0 N°2750 - Regression: Fix default user profile image not shown in portal due to N°2060 2020-02-06 10:06:18 +01:00
Pierre Goiffon
f3576cffb0 📝 README : remove 2.4.* version as this branch isn't supported anymore by Combodo 2020-02-05 14:51:46 +01:00
Molkobain
c5625e6a8d Internal: Fix setup headers style due to 71708cf 2020-02-05 14:49:33 +01:00
Molkobain
3d2a844fef N°2313 - Markup extensibility: Improve success message display during the workflow 2020-02-05 12:10:15 +01:00
Molkobain
110a030902 PHPDoc 2020-02-05 12:10:15 +01:00
Stephen Abello
5ccd885607 Remove DB Tools from excluded modules 2020-02-05 12:04:26 +01:00
Stephen Abello
e5c6efbe69 Merge branch 'master' into develop
# Conflicts:
#	README.md
2020-02-05 11:24:44 +01:00
Stephen Abello
bd083d632f Update readme for 2.6.3 release 2020-02-05 11:22:39 +01:00
Stephen Abello
65b8132914 N°2314 - Markup extensibility: Fix collapsible icons in "About iTop" modal 2020-02-05 09:47:27 +01:00
Eric
3c2130aa72 N°2321 - Fix SQL request generation for inherited magic attributes 2020-02-04 15:54:03 +01:00
Stephen Abello
e70a2f75d3 N°2748 - Fix regression introduced by 71f5d29c, CKEditor's paragraph spacing wasn't coherent with how it's displayed in iTop 2020-02-04 11:41:10 +01:00
Molkobain
fe8e6ba4b0 N°2314 - Markup extensibility: Fix UI elements not using main colors variables 2020-02-04 10:32:57 +01:00
Eric
008614fde6 N°2321 - Fix SQL request generation for inherited magic attributes 2020-02-04 10:28:35 +01:00
Molkobain
ac6e60f5a1 N°2595 - Reorganize admin. console menus: Change new menu groups IDs to avoid collision with existing extensions 2020-02-04 09:50:11 +01:00
Stephen Abello
bf18d623d6 N°2314 - Markup extensibility: Add 2 additional themes for the backoffice
Adds a colored top bar to easily identify different environments (tests, production, ...)
2020-02-03 16:17:46 +01:00
Stephen Abello
10d04756ee N°2314 - Markup extensibility: Fall back on iTop's default theme when a non existing theme is selected 2020-02-03 15:12:59 +01:00
bruno DA SILVA
6e927114e0 N°2154 - 💚 fix tests
- the correct file is now versioned
2020-02-03 12:04:51 +01:00
Pierre Goiffon
682c24a873 N°2293 DBUpdate : save changed fields and corresponding previous values (#111)
* N°2293 DBUpdate : save changed fields and corresponding previous values for callbacks
* update PHPDoc
* remove m_aChanges and ListChangesUpdated() that were introduced in 2.7.0-beta
* add m_aPreviousValuesForUpdatedAttributes and ListPreviousValuesForUpdatedAttributes()

* :memo Woops forgot to change one PHPDoc

* 📝 Some more PHPDoc O:)

* 📝 Add more info in .doc README

* 📝 Well, again some PHPDoc O:)

* 📝 Replace inline @link by @see
@link are for URI, see https://docs.phpdoc.org/latest/references/phpdoc/inline-tags/link.html
2020-01-31 18:01:26 +01:00
bruno DA SILVA
d4b4ced649 🌐 update dictionaries
- set the translation as requested by Q&A
2020-01-31 17:29:38 +01:00
bruno DA SILVA
c2589492d9 📝 documentation generator dependencies handling improvement
- ignore /.doc/vendor
 - uses a lock file (at /.doc/composer.lock)
2020-01-31 17:29:38 +01:00
bruno DA SILVA
15c9cf926e 2154 - preserve "var" in conf
- add possibility to inject var using string patterns (ie: `'%env(DB_HOST)?:localhost%`)
 - on WriteToFile, preserve the non interpreted value when the interpreted value is kept the same
 - added unit tests for both behaviours
 - minor bugfix (default value in comment was wrong) and code readability improvements
2020-01-31 17:29:37 +01:00
Molkobain
78d4c8c7c7 Internal: Fix typo 2020-01-31 17:22:57 +01:00
Eric
d9e8eed084 💚 Fix CI on TagSet search (request have changed) 2020-01-31 16:13:59 +01:00
Eric
ebe86d09ee N°985 - Add applicable contexts on Trigger (logs) 2020-01-30 16:18:49 +01:00
Eric
5e5d368299 N°2657 - MTP : Progress Bar has disappeared (Search exact match) 2020-01-30 16:02:16 +01:00
Molkobain
f990a83453 N°2060 - Migrate error page to the Symfony framework 2020-01-30 13:56:32 +01:00
Molkobain
c6325dce8e Internal: Fix autoloader path for Symfony bin/console utility 2020-01-30 13:56:32 +01:00
Eric
bbca1625fb N°2657 - MTP : Progress Bar has disappeared (Search exact match) 2020-01-30 12:31:22 +01:00
Pierre Goiffon
53975d1d8f 📝 Replace inline @link by @see
@link are for URI, see https://docs.phpdoc.org/latest/references/phpdoc/inline-tags/link.html
2020-01-30 09:35:25 +01:00
Pierre Goiffon
1358bf9b7f 📝 Well, again some PHPDoc O:) 2020-01-30 09:25:53 +01:00
Pierre Goiffon
7c17be4db6 📝 Add more info in .doc README 2020-01-30 09:22:38 +01:00
Pierre Goiffon
367a92b711 📝 Some more PHPDoc O:) 2020-01-30 08:39:18 +01:00
Pierre Goiffon
0a3201dd41 :memo Woops forgot to change one PHPDoc 2020-01-29 18:43:46 +01:00
Pierre Goiffon
d82690dd84 N°2293 DBUpdate : save changed fields and corresponding previous values for callbacks
* update PHPDoc
* remove m_aChanges and ListChangesUpdated() that were introduced in 2.7.0-beta
* add m_aPreviousValuesForUpdatedAttributes and ListPreviousValuesForUpdatedAttributes()
2020-01-29 18:36:46 +01:00
Eric
7f9e4385ac N°2657 - MTP : Progress Bar has disappeared (support any code length) 2020-01-29 18:01:17 +01:00
Stephen Abello
aa3e284af3 Update README for 2.7.0-beta2 2020-01-29 16:59:16 +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
2672 changed files with 22898 additions and 160866 deletions

View File

@@ -23,7 +23,7 @@ Some iTop specific tags were added :
### known limitations:
#### `@see` tags must be very specific:
* always prefix class members with `ClassName::`
* always prefix class members (attributes or methods) with `ClassName::` (do not use self)
* for methods always suffix them with `()`,
* do not reference variables since they are not documented. If you have to, always prefix them with `$`

3015
.doc/composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,30 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 140
max_line_length = 300
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, 140
ij_visual_guides = 300
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 = 0
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
@@ -31,59 +35,18 @@ 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_value_alignment = 0
[*.csv]
max_line_length = 2147483647
ij_wrap_on_typing = false
ij_csv_wrap_long_lines = false
[*.feature]
indent_size = 2
ij_gherkin_keep_indents_on_empty_lines = false
[*.less]
indent_size = 2
ij_less_align_closing_brace_with_properties = false
ij_less_blank_lines_around_nested_selector = 1
ij_less_blank_lines_between_blocks = 1
ij_less_brace_placement = 0
ij_less_hex_color_long_format = false
ij_less_hex_color_lower_case = false
ij_less_hex_color_short_format = false
ij_less_hex_color_upper_case = false
ij_less_keep_blank_lines_in_code = 2
ij_less_keep_indents_on_empty_lines = false
ij_less_keep_single_line_blocks = false
ij_less_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_less_space_after_colon = true
ij_less_space_before_opening_brace = true
ij_less_value_alignment = 0
[*.sass]
indent_size = 2
ij_sass_align_closing_brace_with_properties = false
ij_sass_blank_lines_around_nested_selector = 1
ij_sass_blank_lines_between_blocks = 1
ij_sass_brace_placement = 0
ij_sass_hex_color_long_format = false
ij_sass_hex_color_lower_case = false
ij_sass_hex_color_short_format = false
ij_sass_hex_color_upper_case = false
ij_sass_keep_blank_lines_in_code = 2
ij_sass_keep_indents_on_empty_lines = false
ij_sass_keep_single_line_blocks = false
ij_sass_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_sass_space_after_colon = true
ij_sass_space_before_opening_brace = true
ij_sass_value_alignment = 0
ij_css_use_double_quotes = true
ij_css_value_alignment = do_not_align
[*.scss]
indent_style = tab
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
@@ -94,17 +57,20 @@ 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]
indent_style = tab
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
@@ -112,10 +78,45 @@ 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 = true
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
ij_shell_use_unix_line_separator = true
[{*.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
@@ -134,13 +135,13 @@ 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/**/*
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 = next_line
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
@@ -148,15 +149,15 @@ 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 = never
ij_javascript_else_on_new_line = true
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 = never
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
@@ -192,6 +193,9 @@ 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
@@ -272,21 +276,21 @@ 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 = never
ij_javascript_while_brace_force = always
ij_javascript_while_on_new_line = false
ij_javascript_wrap_comments = false
[{*.module,*.hphp,*.phtml,*.php5,*.php4,*.php,*.ctp,*.inc}]
[{*.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_assignments = true
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_key_value_pairs = true
ij_php_align_multiline_array_initializer_expression = true
ij_php_align_multiline_binary_operation = false
ij_php_align_multiline_chained_methods = false
ij_php_align_multiline_extends_list = false
@@ -296,12 +300,14 @@ 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_author_weight = 7
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
@@ -318,7 +324,8 @@ 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_block_brace_style = next_line
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
@@ -328,11 +335,11 @@ 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 = 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 = true
ij_php_example_weight = 3
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
@@ -343,6 +350,8 @@ 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
@@ -362,7 +371,8 @@ 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 = 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
@@ -370,6 +380,7 @@ 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
@@ -380,9 +391,11 @@ 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 = 4
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
@@ -399,11 +412,12 @@ 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 = 5
ij_php_see_weight = 2
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
@@ -417,6 +431,7 @@ 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
@@ -433,6 +448,7 @@ 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
@@ -465,11 +481,11 @@ 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 = 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 = 6
ij_php_throws_weight = 7
ij_php_todo_weight = 28
ij_php_unknown_tag_weight = 28
ij_php_upper_case_boolean_const = false
@@ -481,9 +497,24 @@ ij_php_version_weight = 28
ij_php_while_brace_force = always
ij_php_while_on_new_line = false
[{*.sht,*.htm,*.html,*.shtm,*.shtml}]
[{*.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
@@ -503,209 +534,38 @@ 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 = double
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
[{*.ts,*.ats}]
ij_continuation_indent_size = 4
ij_typescript_align_imports = false
ij_typescript_align_multiline_array_initializer_expression = false
ij_typescript_align_multiline_binary_operation = false
ij_typescript_align_multiline_chained_methods = false
ij_typescript_align_multiline_extends_list = false
ij_typescript_align_multiline_for = true
ij_typescript_align_multiline_parameters = true
ij_typescript_align_multiline_parameters_in_calls = false
ij_typescript_align_multiline_ternary_operation = false
ij_typescript_align_object_properties = 0
ij_typescript_align_union_types = false
ij_typescript_align_var_statements = 0
ij_typescript_array_initializer_new_line_after_left_brace = false
ij_typescript_array_initializer_right_brace_on_new_line = false
ij_typescript_array_initializer_wrap = off
ij_typescript_assignment_wrap = off
ij_typescript_binary_operation_sign_on_next_line = false
ij_typescript_binary_operation_wrap = off
ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**/*
ij_typescript_blank_lines_after_imports = 1
ij_typescript_blank_lines_around_class = 1
ij_typescript_blank_lines_around_field = 0
ij_typescript_blank_lines_around_field_in_interface = 0
ij_typescript_blank_lines_around_function = 1
ij_typescript_blank_lines_around_method = 1
ij_typescript_blank_lines_around_method_in_interface = 1
ij_typescript_block_brace_style = end_of_line
ij_typescript_call_parameters_new_line_after_left_paren = false
ij_typescript_call_parameters_right_paren_on_new_line = false
ij_typescript_call_parameters_wrap = off
ij_typescript_catch_on_new_line = false
ij_typescript_chained_call_dot_on_new_line = true
ij_typescript_class_brace_style = end_of_line
ij_typescript_comma_on_new_line = false
ij_typescript_do_while_brace_force = never
ij_typescript_else_on_new_line = false
ij_typescript_enforce_trailing_comma = keep
ij_typescript_extends_keyword_wrap = off
ij_typescript_extends_list_wrap = off
ij_typescript_field_prefix = _
ij_typescript_file_name_style = relaxed
ij_typescript_finally_on_new_line = false
ij_typescript_for_brace_force = never
ij_typescript_for_statement_new_line_after_left_paren = false
ij_typescript_for_statement_right_paren_on_new_line = false
ij_typescript_for_statement_wrap = off
ij_typescript_force_quote_style = false
ij_typescript_force_semicolon_style = false
ij_typescript_function_expression_brace_style = end_of_line
ij_typescript_if_brace_force = never
ij_typescript_import_merge_members = global
ij_typescript_import_prefer_absolute_path = global
ij_typescript_import_sort_members = true
ij_typescript_import_sort_module_name = false
ij_typescript_import_use_node_resolution = true
ij_typescript_imports_wrap = on_every_item
ij_typescript_indent_case_from_switch = true
ij_typescript_indent_chained_calls = true
ij_typescript_indent_package_children = 0
ij_typescript_jsdoc_include_types = false
ij_typescript_jsx_attribute_value = braces
ij_typescript_keep_blank_lines_in_code = 2
ij_typescript_keep_first_column_comment = true
ij_typescript_keep_indents_on_empty_lines = false
ij_typescript_keep_line_breaks = true
ij_typescript_keep_simple_blocks_in_one_line = false
ij_typescript_keep_simple_methods_in_one_line = false
ij_typescript_line_comment_add_space = true
ij_typescript_line_comment_at_first_column = false
ij_typescript_method_brace_style = end_of_line
ij_typescript_method_call_chain_wrap = off
ij_typescript_method_parameters_new_line_after_left_paren = false
ij_typescript_method_parameters_right_paren_on_new_line = false
ij_typescript_method_parameters_wrap = off
ij_typescript_object_literal_wrap = on_every_item
ij_typescript_parentheses_expression_new_line_after_left_paren = false
ij_typescript_parentheses_expression_right_paren_on_new_line = false
ij_typescript_place_assignment_sign_on_next_line = false
ij_typescript_prefer_as_type_cast = false
ij_typescript_prefer_parameters_wrap = false
ij_typescript_reformat_c_style_comments = false
ij_typescript_space_after_colon = true
ij_typescript_space_after_comma = true
ij_typescript_space_after_dots_in_rest_parameter = false
ij_typescript_space_after_generator_mult = true
ij_typescript_space_after_property_colon = true
ij_typescript_space_after_quest = true
ij_typescript_space_after_type_colon = true
ij_typescript_space_after_unary_not = false
ij_typescript_space_before_async_arrow_lparen = true
ij_typescript_space_before_catch_keyword = true
ij_typescript_space_before_catch_left_brace = true
ij_typescript_space_before_catch_parentheses = true
ij_typescript_space_before_class_lbrace = true
ij_typescript_space_before_class_left_brace = true
ij_typescript_space_before_colon = true
ij_typescript_space_before_comma = false
ij_typescript_space_before_do_left_brace = true
ij_typescript_space_before_else_keyword = true
ij_typescript_space_before_else_left_brace = true
ij_typescript_space_before_finally_keyword = true
ij_typescript_space_before_finally_left_brace = true
ij_typescript_space_before_for_left_brace = true
ij_typescript_space_before_for_parentheses = true
ij_typescript_space_before_for_semicolon = false
ij_typescript_space_before_function_left_parenth = true
ij_typescript_space_before_generator_mult = false
ij_typescript_space_before_if_left_brace = true
ij_typescript_space_before_if_parentheses = true
ij_typescript_space_before_method_call_parentheses = false
ij_typescript_space_before_method_left_brace = true
ij_typescript_space_before_method_parentheses = false
ij_typescript_space_before_property_colon = false
ij_typescript_space_before_quest = true
ij_typescript_space_before_switch_left_brace = true
ij_typescript_space_before_switch_parentheses = true
ij_typescript_space_before_try_left_brace = true
ij_typescript_space_before_type_colon = false
ij_typescript_space_before_unary_not = false
ij_typescript_space_before_while_keyword = true
ij_typescript_space_before_while_left_brace = true
ij_typescript_space_before_while_parentheses = true
ij_typescript_spaces_around_additive_operators = true
ij_typescript_spaces_around_arrow_function_operator = true
ij_typescript_spaces_around_assignment_operators = true
ij_typescript_spaces_around_bitwise_operators = true
ij_typescript_spaces_around_equality_operators = true
ij_typescript_spaces_around_logical_operators = true
ij_typescript_spaces_around_multiplicative_operators = true
ij_typescript_spaces_around_relational_operators = true
ij_typescript_spaces_around_shift_operators = true
ij_typescript_spaces_around_unary_operator = false
ij_typescript_spaces_within_array_initializer_brackets = false
ij_typescript_spaces_within_brackets = false
ij_typescript_spaces_within_catch_parentheses = false
ij_typescript_spaces_within_for_parentheses = false
ij_typescript_spaces_within_if_parentheses = false
ij_typescript_spaces_within_imports = false
ij_typescript_spaces_within_interpolation_expressions = false
ij_typescript_spaces_within_method_call_parentheses = false
ij_typescript_spaces_within_method_parentheses = false
ij_typescript_spaces_within_object_literal_braces = false
ij_typescript_spaces_within_object_type_braces = true
ij_typescript_spaces_within_parentheses = false
ij_typescript_spaces_within_switch_parentheses = false
ij_typescript_spaces_within_type_assertion = false
ij_typescript_spaces_within_union_types = true
ij_typescript_spaces_within_while_parentheses = false
ij_typescript_special_else_if_treatment = true
ij_typescript_ternary_operation_signs_on_next_line = false
ij_typescript_ternary_operation_wrap = off
ij_typescript_union_types_wrap = on_every_item
ij_typescript_use_chained_calls_group_indents = false
ij_typescript_use_double_quotes = true
ij_typescript_use_explicit_js_extension = global
ij_typescript_use_path_mapping = always
ij_typescript_use_public_modifier = false
ij_typescript_use_semicolon_after_statement = true
ij_typescript_var_declaration_wrap = normal
ij_typescript_while_brace_force = never
ij_typescript_while_on_new_line = false
ij_typescript_wrap_comments = false
[{*.markdown,*.md}]
ij_visual_guides = none
ij_markdown_force_one_space_after_blockquote_symbol = true
ij_markdown_force_one_space_after_header_symbol = true
ij_markdown_force_one_space_after_list_bullet = true
ij_markdown_force_one_space_between_words = true
ij_markdown_keep_indents_on_empty_lines = false
ij_markdown_max_lines_around_block_elements = 1
ij_markdown_max_lines_around_header = 1
ij_markdown_max_lines_between_paragraphs = 1
ij_markdown_min_lines_around_block_elements = 1
ij_markdown_min_lines_around_header = 1
ij_markdown_min_lines_between_paragraphs = 1
[{*.yml,*.yaml}]
[{*.yaml,*.yml}]
indent_size = 2
ij_continuation_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
[{*.zsh,*.bash,*.sh}]
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
[{.stylelintrc,.eslintrc,.babelrc,jest.config,*.bowerrc,*.jsb3,*.jsb2,*.json}]
indent_size = 2
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
[{phpunit.xml.dist,*.jhm,*.rng,*.wsdl,*.fxml,*.xslt,*.jrxml,*.ant,*.xul,*.xsl,*.xsd,*.tld,*.jnlp,*.xml}]
indent_size = 2
indent_style = tab
tab_width = 2
ij_smart_tabs = true
ij_xml_block_comment_at_first_column = true
ij_xml_keep_indents_on_empty_lines = false
ij_xml_line_comment_at_first_column = 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/

15
.gitignore vendored
View File

@@ -16,6 +16,11 @@
vendor/*
test/vendor/*
# all conf but listing prevention
/conf/**
!/conf/.htaccess
!/conf/web.config
# all datas but listing prevention
/data/**
!/data/.htaccess
@@ -35,11 +40,10 @@ test/vendor/*
# Jetbrains
/.idea/**
!/.idea/encodings.xml
!/.idea/codeStyles
!/.idea/codeStyles/*
!/.idea/inspectionProfiles
!/.idea/inspectionProfiles/*
# doc. generation
/.doc/vendor
#phpdocumentor temp file
ast.dump
@@ -130,4 +134,3 @@ local.properties
.cache-main
.scala_dependencies
.worksheet

View File

@@ -1,74 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="LINE_SEPARATOR" value="&#10;" />
<option name="RIGHT_MARGIN" value="140" />
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
<option name="SOFT_MARGINS" value="140" />
<HTMLCodeStyleSettings>
<option name="HTML_DO_NOT_INDENT_CHILDREN_OF" value="html,body,thead,tbody,tfoot,style,script,head" />
</HTMLCodeStyleSettings>
<JSCodeStyleSettings version="0">
<option name="USE_CHAINED_CALLS_GROUP_INDENTS" value="true" />
</JSCodeStyleSettings>
<PHPCodeStyleSettings>
<option name="CONCAT_SPACES" value="false" />
<option name="COMMA_AFTER_LAST_ARRAY_ELEMENT" value="true" />
<option name="PHPDOC_BLANK_LINE_BEFORE_TAGS" value="true" />
<option name="PHPDOC_BLANK_LINES_AROUND_PARAMETERS" value="true" />
<option name="PHPDOC_WRAP_LONG_LINES" value="true" />
<option name="THROWS_WEIGHT" value="6" />
<option name="PARAM_WEIGHT" value="4" />
<option name="RETURN_WEIGHT" value="5" />
<option name="AUTHOR_WEIGHT" value="7" />
<option name="INTERNAL_WEIGHT" value="0" />
<option name="API_WEIGHT" value="1" />
<option name="EXAMPLE_WEIGHT" value="3" />
<option name="SEE_WEIGHT" value="2" />
<option name="LOWER_CASE_BOOLEAN_CONST" value="true" />
<option name="LOWER_CASE_NULL_CONST" value="true" />
<option name="BLANK_LINES_BEFORE_RETURN_STATEMENT" value="1" />
<option name="KEEP_RPAREN_AND_LBRACE_ON_ONE_LINE" value="true" />
<option name="PHPDOC_USE_FQCN" value="true" />
</PHPCodeStyleSettings>
<XML>
<option name="XML_TEXT_WRAP" value="0" />
<option name="XML_KEEP_LINE_BREAKS" value="false" />
<option name="XML_KEEP_WHITE_SPACES_INSIDE_CDATA" value="true" />
</XML>
<codeStyleSettings language="JavaScript">
<option name="BRACE_STYLE" value="2" />
<option name="ELSE_ON_NEW_LINE" value="true" />
<option name="SPACE_AROUND_ADDITIVE_OPERATORS" value="false" />
<option name="IF_BRACE_FORCE" value="3" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="PHP">
<option name="BLANK_LINES_AFTER_PACKAGE" value="1" />
<option name="BRACE_STYLE" value="2" />
<option name="ELSE_ON_NEW_LINE" value="true" />
<option name="CATCH_ON_NEW_LINE" value="true" />
<option name="FINALLY_ON_NEW_LINE" value="true" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_WRAP" value="5" />
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML">
<option name="WRAP_ON_TYPING" value="1" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Combodo" />
</state>
</component>

6
.idea/encodings.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

View File

@@ -1,170 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Combodo" />
<inspection_tool class="CascadeStringReplacementInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ForgottenDebugOutputInspection" enabled="true" level="ERROR" enabled_by_default="true">
<option name="configuration">
<list>
<option value="\Codeception\Util\Debug::debug" />
<option value="\Codeception\Util\Debug::pause" />
<option value="\Doctrine\Common\Util\Debug::dump" />
<option value="\Doctrine\Common\Util\Debug::export" />
<option value="\Illuminate\Support\Debug\Dumper::dump" />
<option value="\Symfony\Component\Debug\Debug::enable" />
<option value="\Symfony\Component\Debug\DebugClassLoader::enable" />
<option value="\Symfony\Component\Debug\ErrorHandler::register" />
<option value="\Symfony\Component\Debug\ExceptionHandler::register" />
<option value="\TYPO3\CMS\Core\Utility\DebugUtility::debug" />
<option value="\Zend\Debug\Debug::dump" />
<option value="\Zend\Di\Display\Console::export" />
<option value="dd" />
<option value="debug_print_backtrace" />
<option value="debug_zval_dump" />
<option value="dpm" />
<option value="dpq" />
<option value="dsm" />
<option value="dump" />
<option value="dvm" />
<option value="error_log" />
<option value="kpr" />
<option value="phpinfo" />
<option value="print_r" />
<option value="var_dump" />
<option value="var_export" />
<option value="xdebug_break" />
<option value="xdebug_call_class" />
<option value="xdebug_call_file" />
<option value="xdebug_call_function" />
<option value="xdebug_call_line" />
<option value="xdebug_code_coverage_started" />
<option value="xdebug_debug_zval" />
<option value="xdebug_debug_zval_stdout" />
<option value="xdebug_dump_superglobals" />
<option value="xdebug_enable" />
<option value="xdebug_get_code_coverage" />
<option value="xdebug_get_collected_errors" />
<option value="xdebug_get_declared_vars" />
<option value="xdebug_get_function_stack" />
<option value="xdebug_get_headers" />
<option value="xdebug_get_monitored_functions" />
<option value="xdebug_get_profiler_filename" />
<option value="xdebug_get_stack_depth" />
<option value="xdebug_get_tracefile_name" />
<option value="xdebug_is_enabled" />
<option value="xdebug_memory_usage" />
<option value="xdebug_peak_memory_usage" />
<option value="xdebug_print_function_stack" />
<option value="xdebug_start_code_coverage" />
<option value="xdebug_start_error_collection" />
<option value="xdebug_start_function_monitor" />
<option value="xdebug_start_trace" />
<option value="xdebug_stop_code_coverage" />
<option value="xdebug_stop_error_collection" />
<option value="xdebug_stop_function_monitor" />
<option value="xdebug_stop_trace" />
<option value="xdebug_time_index" />
<option value="xdebug_var_dump" />
</list>
</option>
<option name="migratedIntoUserSpace" value="true" />
</inspection_tool>
<inspection_tool class="HtmlRequiredAltAttribute" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="HtmlRequiredLangAttribute" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MysqlParsingInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpComposerExtensionStubsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpIncludeInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpMethodParametersCountMismatchInspection" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="PhpShortOpenTagInspection" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="PhpTooManyParametersInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="limit" value="7" />
</inspection_tool>
<inspection_tool class="PhpUndefinedClassInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="DONT_REPORT_MULTI_RESOLVE" value="true" />
</inspection_tool>
<inspection_tool class="PhpUndefinedMethodInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpUnhandledExceptionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpUnusedLocalVariableInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="DONT_REPORT_INSIDE_LIST" value="true" />
</inspection_tool>
<inspection_tool class="PhpUnusedParameterInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="DONT_REPORT_ABSTRACT_CLASS" value="true" />
</inspection_tool>
<inspection_tool class="SecurityAdvisoriesInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="optionConfiguration">
<list>
<option value="barryvdh/laravel-debugbar" />
<option value="behat/behat" />
<option value="brianium/paratest" />
<option value="codeception/codeception" />
<option value="codedungeon/phpunit-result-printer" />
<option value="composer/composer" />
<option value="doctrine/coding-standard" />
<option value="filp/whoops" />
<option value="friendsofphp/php-cs-fixer" />
<option value="humbug/humbug" />
<option value="infection/infection" />
<option value="jakub-onderka/php-parallel-lint" />
<option value="johnkary/phpunit-speedtrap" />
<option value="kalessil/production-dependencies-guard" />
<option value="mikey179/vfsStream" />
<option value="mockery/mockery" />
<option value="mybuilder/phpunit-accelerator" />
<option value="orchestra/testbench" />
<option value="pdepend/pdepend" />
<option value="phan/phan" />
<option value="phing/phing" />
<option value="phpcompatibility/php-compatibility" />
<option value="phpmd/phpmd" />
<option value="phpro/grumphp" />
<option value="phpspec/phpspec" />
<option value="phpspec/prophecy" />
<option value="phpstan/phpstan" />
<option value="phpunit/phpunit" />
<option value="povils/phpmnd" />
<option value="roave/security-advisories" />
<option value="satooshi/php-coveralls" />
<option value="sebastian/phpcpd" />
<option value="slevomat/coding-standard" />
<option value="spatie/phpunit-watcher" />
<option value="squizlabs/php_codesniffer" />
<option value="sstalle/php7cc" />
<option value="symfony/debug" />
<option value="symfony/maker-bundle" />
<option value="symfony/phpunit-bridge" />
<option value="symfony/var-dumper" />
<option value="vimeo/psalm" />
<option value="wimg/php-compatibility" />
<option value="wp-coding-standards/wpcs" />
<option value="yiisoft/yii2-coding-standards" />
<option value="yiisoft/yii2-debug" />
<option value="yiisoft/yii2-gii" />
<option value="zendframework/zend-coding-standard" />
<option value="zendframework/zend-debug" />
<option value="zendframework/zend-test" />
</list>
</option>
</inspection_tool>
<inspection_tool class="SqlAddNotNullColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAmbiguousColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAutoIncrementDuplicateInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlCheckUsingColumnsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlConstantConditionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDeprecateTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDerivedTableAliasInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDialectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDropIndexedColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlIdentifierInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlInsertValuesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlNullComparisonInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlPostgresqlSelectFromProcedureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlResolveInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlShouldBeInGroupByInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlSideEffectsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlSignatureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlStorageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlUnusedVariableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

@@ -1,19 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PhpIncludeInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpMethodParametersCountMismatchInspection" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="PhpTooManyParametersInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="limit" value="7" />
</inspection_tool>
<inspection_tool class="PhpUndefinedClassInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="DONT_REPORT_MULTI_RESOLVE" value="true" />
</inspection_tool>
<inspection_tool class="PhpUnhandledExceptionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpUnusedParameterInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="DONT_REPORT_ABSTRACT_CLASS" value="true" />
</inspection_tool>
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

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

View File

@@ -1,16 +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 https://www.combodo.com/documentation/iTopDataModelToolkit-2.3.zip > toolkit.zip
unzip toolkit.zip
rm toolkit.zip
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 -a # => Not needed anymore (libs were added to git with N°2435)
# under the test dir
cd test
composer install

View File

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

View File

@@ -1,22 +0,0 @@
#!/usr/bin/env bash
set -x
cd test
export DEBUG_UNIT_TEST=0
RUN_NONREG_TESTS=0
if [ $# -ge 1 -a "x$1" == "xtrue" ]
then
export DEBUG_UNIT_TEST=1
else
export DEBUG_UNIT_TEST=0
fi
if [ $# -ge 2 -a "x$2" == "xtrue" ]
then
php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml --teamcity
else
#echo php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml --teamcity
php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml --exclude-group OQL --teamcity
fi

View File

@@ -1,8 +0,0 @@
#!/usr/bin/env bash
set -x
chmod 666 conf/production/config-itop.php
cd toolkit
php unattended_install.php --response_file=default-params.xml --clean=true

View File

@@ -1,284 +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',
'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',
// 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(
'authent-local' => array (
'password_validation.pattern' => '',
),
'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,208 +0,0 @@
<?php
/**
* Copyright (C) 2013-2019 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
*/
//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,90 @@
<?php
$iBeginTime = time();
chdir(__DIR__);
$aCommands = [
'php composer/rmDeniedTestDir.php',
'php build/commands/setupCssCompiler.php',
// 'bash /tmp/gabuzomeu.sh',
];
$aFailedCommands=[];
foreach ($aCommands as $sCommand)
{
if (!ExecCommand($sCommand))
{
$aFailedCommands[] = $sCommand;
}
}
$iElapsed = time() - $iBeginTime;
if (count($aFailedCommands))
{
fwrite(STDERR, "\nafterBuild execution failed! (in ${iElapsed}s)\n");
fwrite(STDERR, "List of failling commands:\n - " . implode("\n - ", $aFailedCommands) . "\n");
exit(1);
}
echo "\nDone (${iElapsed}s)\n";
exit(0);
/**
* Executes a command and returns an array with exit code, stdout and stderr content
*
* @param string $cmd - Command to execute
*
* @return bool
* @throws \Exception
*/
function ExecCommand($cmd) {
$iBeginTime = time();
echo sprintf("command: %s", str_pad("$cmd ", 50));
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w"), // stderr
);
$process = proc_open($cmd, $descriptorspec, $pipes, __DIR__ . '/..', null);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$iCode = proc_close($process);
$bSuccess = (0 === $iCode);
$iElapsed = time() - $iBeginTime;
if (!$bSuccess) {
fwrite(STDERR, sprintf(
"\nCOMMAND FAILED! (%s) \n - status:%s \n - stderr:%s \n - stdout: %s\n - elapsed:%ss\n\n",
$cmd,
$iCode,
rtrim($stderr),
rtrim($stdout),
$iElapsed
));
}
else
{
echo "| elapsed:${iElapsed}s \n";
}
if (!empty($stderr))
{
fwrite(STDERR, "$stderr\n");
}
if (!empty($stdout))
{
echo "stdout :$stdout\n\n";
}
return $bSuccess;
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Copyright (C) 2010-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
* along with iTop. If not, see <http: *www.gnu.org/licenses/>
*
*/
use Combodo\iTop\Composer\iTopComposer;
$iTopFolder = __DIR__."/../../../";
require_once("$iTopFolder/approot.inc.php");
require_once(APPROOT."/application/utils.inc.php");
if (php_sapi_name() !== 'cli')
{
throw new \Exception('This script can only run from CLI');
}
$sCssFile = APPROOT.'/css/setup.css';
if (file_exists($sCssFile))
{
fwrite(STDERR, "$sCssFile already exists (it should not), removing it.");
if (!unlink($sCssFile))
{
fwrite(STDERR, "Failed to remove $sCssFile, exiting.");
exit(1);
}
}
$sCssRelPath = utils::GetCSSFromSASS('css/setup.scss');
if (!file_exists($sCssFile))
{
fwrite(STDERR, "Failed to compile $sCssFile, exiting.");
exit(1);
}

View File

@@ -0,0 +1,98 @@
<?php
/**
* Copyright (C) 2010-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
* along with iTop. If not, see <http: *www.gnu.org/licenses/>
*
*/
$iTopFolder = __DIR__ . "/../../" ;
require_once ("$iTopFolder/approot.inc.php");
$sApproot = APPROOT;
$aTrace = array();
$aParamsConfig = array(
'composer-path' => array(
'default' => 'composer.phar',
)
);
$aParamsConfigNotFound = array_flip(array_keys($aParamsConfig));
$aGivenArgs = $argv;
unset($aGivenArgs[0]);
$aParams = array();
foreach ($aParamsConfig as $sParam => $aConfig)
{
$bParamsFound = false;
foreach ($aGivenArgs as $sGivenArg)
{
if (preg_match("/--$sParam(?:=(?<value>.*))?$/", $sGivenArg, $aMatches))
{
$aParams[$sParam] =
isset($aMatches['value'])
? $aMatches['value']
: true
;
$bParamsFound = true;
unset($aGivenArgs[$sGivenArg]);
}
}
if ($bParamsFound)
{
unset($aParamsConfigNotFound[$sParam]);
}
}
foreach ($aParamsConfigNotFound as $sParamsConfigNotFound => $void)
{
if (isset($aParamsConfig[$sParamsConfigNotFound]['default']))
{
$aParams[$sParamsConfigNotFound] = $aParamsConfig[$sParamsConfigNotFound]['default'];
$aTrace[] = "\e[1;30mUsing default value '{$aParams[$sParamsConfigNotFound]}' for '$sParamsConfigNotFound'\e[0m\n";
continue;
}
die("Missing '$sParamsConfigNotFound'");
}
echo "This command aims at helping you find upgradable dependencies\n";
echo "\e[0;33mBeware of the version colored in orange, they probably introduce BC breaks!\e[0m\n";
$sCommand = "{$aParams['composer-path']} show -loD --working-dir=$sApproot --ansi";
$execCode = exec($sCommand, $output);
$sOutput = implode("\n", $output)."\n";
if (!$execCode)
{
echo "\e[41mFailed to execute '$sCommand'\e[0m\n";
echo "Trace: \n".implode("\n", $aTrace);
}
else
{
$iCountDepdendenciesFound = count($output);
$iCountBc = substr_count($sOutput, '[33m');
echo sprintf("Found \033[44m%d\033[0m upgradable dependencies, including \e[41m%s BC break\e[0m 😱 :\n\n", $iCountDepdendenciesFound, $iCountBc);
}
echo $sOutput;

View File

@@ -0,0 +1,57 @@
<?php
/**
* Copyright (C) 2010-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
* along with iTop. If not, see <http: *www.gnu.org/licenses/>
*
*/
use Combodo\iTop\Composer\iTopComposer;
$iTopFolder = __DIR__ . "/../../" ;
require_once ("$iTopFolder/approot.inc.php");
require_once (APPROOT."/setup/setuputils.class.inc.php");
if (php_sapi_name() !== 'cli')
{
throw new \Exception('This script can only run from CLI');
}
clearstatcache();
$oiTopComposer = new iTopComposer();
$aDeniedButStillPresent = $oiTopComposer->ListDeniedButStillPresent();
foreach ($aDeniedButStillPresent as $sDir)
{
if (! preg_match('#[tT]ests?/?$#', $sDir))
{
echo "\nfound INVALID denied test dir: '$sDir'\n";
throw new \Exception("$sDir must end with /Test/ or /test/");
}
try
{
SetupUtils::rrmdir($sDir);
echo "Remove denied test dir: '$sDir'\n";
}
catch (\Exception $e)
{
echo "\nFAILED to remove denied test dir: '$sDir'\n";
}
}

View File

@@ -34,7 +34,13 @@ do
then
continue
fi
if [ "$subfolder" == "datamodels" ]
then
if [ $(find $l -name module*.php|wc -l) -ne 0 -o $(echo "$l"|grep -c "itop-portal-base") -ne 0 ]
then
continue
fi
fi
dir=$(dirname $(dirname $l))
prod=$(echo $l| sed "s|$dir/||1")
echo $l $subfolder

View File

@@ -1,7 +1,22 @@
<?php
/**
* script used to sort license file (usefull for autogeneration)
* Example: php
* script used to sort license file (useful for autogeneration)
*
* Requirements :
* * bash (on Windows, use Git Bash)
* * composer (if you use the phar version, mind to create a `Composer` alias !)
* * JQ command
* to install on Windows :
* `curl -L -o /usr/bin/jq.exe https://github.com/stedolan/jq/releases/latest/download/jq-win64.exe`
* this is a Windows port : https://stedolan.github.io/jq/
*
* Licenses sources :
* * `composer licenses --format json` (see https://getcomposer.org/doc/03-cli.md#licenses)
* * keep every existing nodes with `/licenses/license[11]/product/@scope` not in ['lib', 'datamodels']
* ⚠ If licenses were added manually, they might be removed by this tool ! Be very careful to check for the result before pushing !
*
* To launch, check requirements and run `php updateLicenses.php`
* The target license file path is in `$xmlFilePath`
*/
$iTopFolder = __DIR__ . "/../../" ;
@@ -51,39 +66,83 @@ function get_license_nodes($file_path)
$xp = new DOMXPath($dom);
$licenseList = $xp->query('/licenses/license');
$licenses = iterator_to_array($licenseList);
$licenses = iterator_to_array($licenseList);
usort($licenses, 'sort_by_product');
return $licenses;
}
/** @noinspection SuspiciousAssignmentsInspection */
function fix_product_name(DOMNode &$oProductNode)
{
$sProductNameOrig = $oProductNode->nodeValue;
// sample : `C:\Dev\wamp64\www\itop-27\.make\license/../..//lib/symfony/polyfill-ctype`
$sProductNameFixed = remove_dir_from_string($sProductNameOrig, 'lib/');
// sample : `C:\Dev\wamp64\www\itop-27\.make\license/../..//datamodels/2.x/authent-cas/vendor/apereo/phpcas`
$sProductNameFixed = remove_dir_from_string($sProductNameFixed, 'vendor/');
$oProductNode->nodeValue = $sProductNameFixed;
}
function remove_dir_from_string($sString, $sNeedle)
{
if (strpos($sString, $sNeedle) === false) {
return $sString;
}
$sStringTmp = strstr($sString, $sNeedle);
$sStringFixed = str_replace($sNeedle, '', $sStringTmp);
// DEBUG trace O:)
// echo "$sNeedle = $sString => $sStringFixed\n";
return $sStringFixed;
}
$old_licenses = get_license_nodes($xmlFilePath);
//generate file with updated licenses
$generated_license_file_path = __DIR__."/provfile.xml";
exec("bash " . __DIR__ . "/gen-community-license.sh $iTopFolder > ". $generated_license_file_path);
echo "- Generating licences...";
exec("bash ".__DIR__."/gen-community-license.sh $iTopFolder > ".$generated_license_file_path);
echo "OK!\n";
echo "- Get licenses nodes...";
$new_licenses = get_license_nodes($generated_license_file_path);
exec("rm -f ". $generated_license_file_path);
unlink($generated_license_file_path);
foreach ($old_licenses as $b) {
$aProductNode = get_product_node($b);
if (get_scope($aProductNode) !== "lib" && get_scope($aProductNode) !== "datamodels" )
{
if (get_scope($aProductNode) !== "lib" && get_scope($aProductNode) !== "datamodels") {
$new_licenses[] = $b;
}
}
usort($new_licenses, 'sort_by_product');
echo "OK!\n";
echo "- Overwritting Combodo license file...";
$new_dom = new DOMDocument("1.0");
$new_dom->formatOutput = true;
$root = $new_dom->createElement("licenses");
$new_dom->appendChild($root);
foreach ($new_licenses as $b) {
$node = $new_dom->importNode($b,true);
$root->appendChild($new_dom->importNode($b,true));
$node = $new_dom->importNode($b, true);
// N°3870 fix when running script in Windows
// fix should be in gen-community-license.sh but it is easier to do it here !
if (strncasecmp(PHP_OS, 'WIN', 3) === 0) {
$oProductNodeOrig = get_product_node($node);
fix_product_name($oProductNodeOrig);
}
$root->appendChild($node);
}
$new_dom->save($xmlFilePath);
$new_dom->save($xmlFilePath);
echo "OK!\n";

View File

@@ -0,0 +1,77 @@
<?php
/**
* Usage :
* `php changelog.php 2.7.4`
*
* As argument is passed the git ref (tag name or sha1) we want to use as reference
*
* Outputs :
*
* 1. List of bugs as CSV :
* bug ref;link
* Example :
* <code>
* Bug_ref;Bug_URL;sha1
* 1234;https://support.combodo.com/pages/UI.php?operation=details&class=Bug&id=1234;949b213f9|b1ca1f263|a1271da74
* </code>
*
* 2. List of commits sha1/message without bug ref
* Example :
* <code>
* sha1;subject
* a6aa183e2;:bookmark: Prepare 2.7.5
* </code>
*/
if (count($argv) === 1) {
echo '⚠ You must pass the base tag/sha1 as parameter';
exit(1);
}
$sBaseReference = $argv[1];
//--- Get log
$sGitLogCommand = 'git log --decorate --pretty="%h;%s" --date-order --no-merges '.$sBaseReference.'..HEAD';
$sGitLogRaw = shell_exec($sGitLogCommand);
//--- Analyze log
$aGitLogLines = preg_split('/\n/', trim($sGitLogRaw));;
$aLogLinesWithBugRef = [];
$aLogLineNoBug = [];
foreach ($aGitLogLines as $sLogLine) {
$sBugRef = preg_match('/[nN]°(\d{3,4})/', $sLogLine, $aLineBugRef);
if (($sBugRef === false) || empty($aLineBugRef)) {
$aLogLineNoBug[] = $sLogLine;
continue;
}
$iBugId = $aLineBugRef[1];
$sSha = substr($sLogLine, 0, 9);
if (array_key_exists($iBugId, $aLogLinesWithBugRef)) {
$aBugShaRefs = $aLogLinesWithBugRef[$iBugId];
$aBugShaRefs[] = $sSha;
$aLogLinesWithBugRef[$iBugId] = $aBugShaRefs;
} else {
$aLogLinesWithBugRef[$iBugId] = [$sSha];
}
}
$aBugsList = array_keys($aLogLinesWithBugRef);
sort($aBugsList, SORT_NUMERIC);
//-- Output results
echo "# Bugs included\n";
echo "Bug_ref;Bug_URL;sha1\n";
foreach ($aBugsList as $sBugRef) {
$sShaRefs = implode('|', $aLogLinesWithBugRef[$sBugRef]);
echo "{$sBugRef};https://support.combodo.com/pages/UI.php?operation=details&class=Bug&id={$sBugRef};$sShaRefs\n";
}
echo "\n";
echo "# Logs line without bug referenced\n";
echo "sha1;subject\n";
foreach ($aLogLineNoBug as $sLogLine) {
echo "$sLogLine\n";
}

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
);
}
}

View File

@@ -10,8 +10,8 @@ Here are some guidelines that will help us integrate your work!
### Subjects
You are welcome to create pull requests on any of those subjects:
* 🐛 `:bug:` bug fix
* 🌐 `:globe_with_meridians:` translation / i18n / l10n
* 🐛 bug fix
* 🌐 translation / i18n / l10n
If you want to implement a **new feature**, please [create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) for review.
If you ever want to begin implementation, do so in a fork, and add a link to the corresponding commits in the ticket.
@@ -27,7 +27,7 @@ If you have an idea you're sure would benefit to all of iTop users, you may
[create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) to submit it, but be warned that there are lots of good
reasons to refuse such changes.
### License
### 📄 License
iTop is distributed under the AGPL-3.0 license (see the [license.txt] file),
your code must comply with this license.
@@ -37,58 +37,69 @@ If you want to use another license, you may [create an extension][wiki new ext].
[wiki new ext]: https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Astart#by_writing_your_own_extension
## Branch model
## 🔀 iTop branch model
TL;DR:
> **create a fork from iTop main repository,
> create a branch based on the develop branch**
When we first start with Git, we were using the [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) branch model. As
there was some confusions about branches to use for current developed release and previous maintained release, and also because we were
using just a very few of the GitFlow commands, we decided to add just a little modification to this branch model : since april 2020
we don't have anymore a `master` branch.
We are using the [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) branch model. That means we have in our repo those
main branches:
Here are the branches we use and their meaning :
- develop: ongoing development version
- release/\*: if present, that means we are working on a beta version
- master: previous stable version
- support/\*: maintenance branches for older versions
- `develop`: ongoing development version
- `release/*`: if present, that means we are working on a alpha/beta/rc version for shipping
- `support/*`: maintenance branches for older versions
For example, if no beta version is currently ongoing we could have:
For example, if no version is currently prepared for shipping we could have:
- develop containing future 2.8.0 version
- master containing 2.7.x maintenance version
- support/2.6 containing 2.6.x maintenance version
- support/2.5 containing 2.5.x maintenance version
- `develop` containing future 2.8.0 version
- `support/2.7`: 2.7.x maintenance version
- `support/2.6`: 2.6.x maintenance version
- `support/2.5`: 2.5.x maintenance version
In this example, when 2.8.0-beta is shipped that will become:
- develop: future 2.9.0 version
- release/2.8: 2.8.0-beta
- master: 2.7.x maintenance version
- support/2.6 containing 2.6.x maintenance version
- support/2.5 containing 2.5.x maintenance version
- `develop`: future 2.9.0 version
- `release/2.8`: 2.8.0-beta
- `support/2.7`: 2.7.x maintenance version
- `support/2.6`: 2.6.x maintenance version
- `support/2.5`: 2.5.x maintenance version
And when 2.8.0 final will be out:
- develop: future 2.9.0 version
- master: 2.8.x maintenance version
- support/2.7 : 2.7.x maintenance version
- support/2.6 containing 2.6.x maintenance version
- support/2.5 containing 2.5.x maintenance version
- `develop`: future 2.9.0 version
- `support/2.8`: 2.8.x maintenance version (will host developments for 2.8.1)
- `support/2.7`: 2.7.x maintenance version
- `support/2.6`: 2.6.x maintenance version
- `support/2.5`: 2.5.x maintenance version
Most of the time you should based your developments on the develop branch.
That may be different if you want to fix a bug, please use develop anyway and ask in your PR if rebase is possible.
Also note that we have a "micro-version" concept : each of those versions have a very small amount of modifications. They are made from
`support/*` branches as well. For example 2.6.2-1 and 2.6.2-2 were made from the `support/2.6.2` branch.
## Coding
### PHP styleguide
Please follow [our guidelines](https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Acoding_standards).
### 🌐 Translations
A [dedicated page](https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Atranslation) is available in the official wiki.
### Tests
### Where to start ?
1. Create a fork from our repository (see [Working with forks - GitHub Help](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/working-with-forks))
2. Create a branch in this fork, based on the develop branch
3. Code !
Do create a dedicated branch for each modification you want to propose : if you don't it will be very hard to merge back your work !
Most of the time you should based your developments on the develop branch.
That may be different if you want to fix a bug, please use develop anyway and ask in your PR if rebase is possible.
### 🎨 PHP styleguide
Please follow [our guidelines](https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Acoding_standards).
### ✅ Tests
Please create tests that covers as much as possible the code you're submitting.
@@ -117,7 +128,7 @@ Our tests are located in the `test/` directory, containing a PHPUnit config file
* 💄 `:lipstick:` Updating the UI and style files.
## Pull request
## 👥 Pull request
When your code is working, please:

74
Jenkinsfile vendored
View File

@@ -1,69 +1,11 @@
pipeline {
agent any
parameters {
booleanParam(name: 'debugMode', defaultValue: 'false', description: 'Debug mode?')
booleanParam(name: 'runNonRegOQLTests', defaultValue: 'false', description: 'Do You want to run legacy OQL regression tests?')
}
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 ${debugMode} ${runNonRegOQLTests}'
}
}
}
}
}
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

@@ -52,16 +52,16 @@ iTop also offers mass import tools and web services to integrate with your IT
## Last releases
### Versions 2.7.*
- 2.7.0-beta published on December 18, 2019
- 2.7.1 published on April 8, 2020
- [Changes since the previous version][62]
- [New features][63]
- [Migration notes][64]
- [Download iTop 2.7.0-beta][65]
- [Download iTop 2.7.0-2][65]
[62]: https://www.itophub.io/wiki/page?id=2_7_0:release:change_log
[63]: https://www.itophub.io/wiki/page?id=2_7_0:release:2_7_whats_new
[64]: https://www.itophub.io/wiki/page?id=2_7_0:install:260_to_270_migration_notes
[65]: https://sourceforge.net/projects/itop/files/itop/2.7.0-beta
[65]: https://sourceforge.net/projects/itop/files/itop/2.7.0-2
### Versions 2.6.*
@@ -69,12 +69,12 @@ iTop also offers mass import tools and web services to integrate with your IT
- [Changes since the previous version][58]
- [New features][59]
- [Migration notes][60]
- [Download iTop 2.6.1][61]
- [Download iTop 2.6.3][61]
[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.1
[61]: https://sourceforge.net/projects/itop/files/itop/2.6.3
### Versions 2.5.*
@@ -88,20 +88,6 @@ iTop also offers mass import tools and web services to integrate with your IT
[55]: https://www.itophub.io/wiki/page?id=2_5_0:release:2_5_whats_new
[56]: https://www.itophub.io/wiki/page?id=2_5_0:install:240_to_250_migration_notes
[57]: https://sourceforge.net/projects/itop/files/itop/2.5.1
### Versions 2.4.*
- 2.4.0 published on November 16, 2017
- [Changes since the previous version][50]
- [New features][51]
- [Migration notes][52]
- [Download iTop 2.4.1][53]
[50]: https://www.itophub.io/wiki/page?id=2_4_0:release:change_log
[51]: https://www.itophub.io/wiki/page?id=2_4_0:release:2_4_whats_new
[52]: https://www.itophub.io/wiki/page?id=2_4_0:install:230_to_240_migration_notes
[53]: https://sourceforge.net/projects/itop/files/itop/2.4.1
## About Us
@@ -137,8 +123,9 @@ We would like to give a special thank you to the people from the community who c
- Lassiter, Dennis
- Lazcano, Federico
- Lucas, Jonathan
- Malik, Remie
- Rosenke, Stephan
- Malik, Remie
- Mindêllo de Andrade, Lucas (a.k.a @rokam)
- Rosenke, Stephan
- Seki, Shoji
- Shilov, Vladimir
- Tulio, Marco

View File

@@ -434,7 +434,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Support drastic data model changes: no organization class (or not writable)!
if (MetaModel::IsValidClass('Organization') && !MetaModel::IsAbstract('Organization'))
{
$oOrg = new Organization();
$oOrg = MetaModel::NewObject('Organization');
$oOrg->Set('name', 'My Company/Department');
$oOrg->Set('code', 'SOMECODE');
$iOrgId = $oOrg->DBInsertNoReload();
@@ -442,17 +442,13 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Support drastic data model changes: no Person class (or not writable)!
if (MetaModel::IsValidClass('Person') && !MetaModel::IsAbstract('Person'))
{
$oContact = new Person();
$oContact = MetaModel::NewObject('Person');
$oContact->Set('name', 'My last name');
$oContact->Set('first_name', 'My first name');
if (MetaModel::IsValidAttCode('Person', 'org_id'))
{
$oContact->Set('org_id', $iOrgId);
}
if (MetaModel::IsValidAttCode('Person', 'phone'))
{
$oContact->Set('phone', '+00 000 000 000');
}
$oContact->Set('email', 'my.email@foo.org');
$iContactId = $oContact->DBInsertNoReload();
}
@@ -561,7 +557,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
/**
* @param $oUser User
* @return array
* @return bool
*/
public function IsAdministrator($oUser)
{
@@ -571,16 +567,22 @@ class UserRightsProfile extends UserRightsAddOnAPI
/**
* @param $oUser User
* @return array
* @return bool
*/
public function IsPortalUser($oUser)
{
// UserRights caches the list for us
return UserRights::HasProfile(PORTAL_PROFILE_NAME, $oUser);
}
/**
* @param $oUser User
* @return bool
*
* @return array
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public function ListProfiles($oUser)
{

View File

@@ -535,7 +535,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Support drastic data model changes: no organization class (or not writable)!
if (MetaModel::IsValidClass('Organization') && !MetaModel::IsAbstract('Organization'))
{
$oOrg = new Organization();
$oOrg = MetaModel::NewObject('Organization');
$oOrg->Set('name', 'My Company/Department');
$oOrg->Set('code', 'SOMECODE');
$oOrg::SetCurrentChange($oChange);
@@ -544,17 +544,13 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Support drastic data model changes: no Person class (or not writable)!
if (MetaModel::IsValidClass('Person') && !MetaModel::IsAbstract('Person'))
{
$oContact = new Person();
$oContact = MetaModel::NewObject('Person');
$oContact->Set('name', 'My last name');
$oContact->Set('first_name', 'My first name');
if (MetaModel::IsValidAttCode('Person', 'org_id'))
{
$oContact->Set('org_id', $iOrgId);
}
if (MetaModel::IsValidAttCode('Person', 'phone'))
{
$oContact->Set('phone', '+00 000 000 000');
}
$oContact->Set('email', 'my.email@foo.org');
$oContact::SetCurrentChange($oChange);
$iContactId = $oContact->DBInsertNoReload();
@@ -711,7 +707,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
public function LoadCache()
{
if (!is_null($this->m_aProfiles)) return;
if (!is_null($this->m_aProfiles)) return false;
// Could be loaded in a shared memory (?)
$oKPI = new ExecutionKPI();

View File

@@ -24,31 +24,32 @@ class ajax_page extends WebPage implements iTabbedPage
/**
* Jquery style ready script
* @var array
*/
*/
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->no_cache();
$this->add_xframe_options();
$this->m_oTabs = new TabManager();
$this->sContentType = 'text/html';
$this->sContentDisposition = 'inline';
$this->m_sMenu = "";
utils::InitArchiveMode();
}
}
/**
* @inheritDoc

View File

@@ -1,20 +1,22 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Copyright (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
*/
use Symfony\Component\DependencyInjection\Container;
@@ -30,6 +32,7 @@ require_once(APPROOT.'application/newsroomprovider.class.inc.php');
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @package Extensibility
* @since 2.7.0
*/
interface iLoginExtension
{
@@ -41,6 +44,9 @@ interface iLoginExtension
public function ListSupportedLoginModes();
}
/**
* @since 2.7.0
*/
interface iLoginFSMExtension extends iLoginExtension
{
/**
@@ -58,17 +64,22 @@ interface iLoginFSMExtension extends iLoginExtension
public function LoginAction($sLoginState, &$iErrorCode);
}
/**
* @since 2.7.0
*/
abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
{
public abstract function ListSupportedLoginModes();
/**
* @inheritDoc
*/
abstract public function ListSupportedLoginModes();
/**
* @inheritDoc
*/
public function LoginAction($sLoginState, &$iErrorCode)
{
switch ($sLoginState)
{
switch ($sLoginState) {
case LoginWebPage::LOGIN_STATE_START:
return $this->OnStart($iErrorCode);
@@ -151,27 +162,50 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
*/
protected function OnCredentialsOK(&$iErrorCode)
{
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
*/
protected function OnUsersOK(&$iErrorCode)
{
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
*/
protected function OnConnected(&$iErrorCode)
{
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
*/
protected function OnError(&$iErrorCode)
{
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
}
/**
* @since 2.7.0
*/
interface iLogoutExtension extends iLoginExtension
{
/**
@@ -180,6 +214,9 @@ interface iLogoutExtension extends iLoginExtension
public function LogoutAction();
}
/**
* @since 2.7.0
*/
interface iLoginUIExtension extends iLoginExtension
{
/**
@@ -188,7 +225,11 @@ interface iLoginUIExtension extends iLoginExtension
public function GetTwigContext();
}
/**
* @api
* @package Extensibility
* @since 2.7.0
*/
interface iPreferencesExtension
{
/**
@@ -206,6 +247,33 @@ interface iPreferencesExtension
public function ApplyPreferences(WebPage $oPage, $sOperation);
}
/**
* Extend this class instead of implementing iPreferencesExtension if you don't need to overload all methods
*
* @api
* @package Extensibility
* @since 2.7.0
*/
abstract class AbstractPreferencesExtension implements iPreferencesExtension
{
/**
* @inheritDoc
*/
public function DisplayPreferences(WebPage $oPage)
{
// Do nothing
}
/**
* @inheritDoc
*/
public function ApplyPreferences(WebPage $oPage, $sOperation)
{
// Do nothing
}
}
/**
* Implement this interface to change the behavior of the GUI for some objects.
*
@@ -366,6 +434,77 @@ interface iApplicationUIExtension
public function EnumAllowedActions(DBObjectSet $oSet);
}
/**
* Extend this class instead of implementing iApplicationUIExtension if you don't need to overload
*
* @api
* @package Extensibility
* @since 2.7.0
*/
abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
{
/**
* @inheritDoc
*/
public function OnDisplayProperties($oObject, WebPage $oPage, $bEditMode = false)
{
}
/**
* @inheritDoc
*/
public function OnDisplayRelations($oObject, WebPage $oPage, $bEditMode = false)
{
}
/**
* @inheritDoc
*/
public function OnFormSubmit($oObject, $sFormPrefix = '')
{
}
/**
* @inheritDoc
*/
public function OnFormCancel($sTempId)
{
}
/**
* @inheritDoc
*/
public function EnumUsedAttributes($oObject)
{
return array();
}
/**
* @inheritDoc
*/
public function GetIcon($oObject)
{
return '';
}
/**
* @inheritDoc
*/
public function GetHilightClass($oObject)
{
return HILIGHT_CLASS_NONE;
}
/**
* @inheritDoc
*/
public function EnumAllowedActions(DBObjectSet $oSet)
{
return array();
}
}
/**
* Implement this interface to perform specific things when objects are manipulated
*
@@ -393,7 +532,7 @@ interface iApplicationObjectExtension
public function OnIsModified($oObject);
/**
* Invoked to determine wether an object can be written to the database
* Invoked to determine whether an object can be written to the database
*
* The GUI calls this verb and reports any issue.
* Anyhow, this API can be called in other contexts such as the CSV import tool.
@@ -421,7 +560,10 @@ interface iApplicationObjectExtension
* Invoked when an object is updated into the database. The method is called right <b>after</b> the object has been written to the
* database.
*
* Changes made to the object can be get using {@link $oObject::ListChangesUpdated()}. Do not call {@link \DBObject::ListChanges} for this purpose because it will be empty as the object has already be written to DB!
* Useful methods you can call on $oObject :
*
* * {@see DBObject::ListPreviousValuesForUpdatedAttributes()} : list of changed attributes and their values before the change
* * {@see DBObject::Get()} : for a given attribute the new value that was persisted
*
* @param \cmdbAbstractObject $oObject The target object
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
@@ -429,7 +571,7 @@ interface iApplicationObjectExtension
*
* @return void
*
* @since 2.7.0 N°2293 can access object changes by calling {@link $oObject::ListChangesUpdated()}
* @since 2.7.0 N°2293 can access object changes by calling {@see DBObject::ListPreviousValuesForUpdatedAttributes()} on $oObject
*/
public function OnDBUpdate($oObject, $oChange = null);
@@ -460,6 +602,62 @@ interface iApplicationObjectExtension
public function OnDBDelete($oObject, $oChange = null);
}
/**
* Extend this class instead of iApplicationObjectExtension if you don't need to overload all methods
*
* @api
* @package Extensibility
* @since 2.7.0
*/
abstract class AbstractApplicationObjectExtension implements iApplicationObjectExtension
{
/**
* @inheritDoc
*/
public function OnIsModified($oObject)
{
return false;
}
/**
* @inheritDoc
*/
public function OnCheckToWrite($oObject)
{
return array();
}
/**
* @inheritDoc
*/
public function OnCheckToDelete($oObject)
{
return array();
}
/**
* @inheritDoc
*/
public function OnDBUpdate($oObject, $oChange = null)
{
}
/**
* @inheritDoc
*/
public function OnDBInsert($oObject, $oChange = null)
{
}
/**
* @inheritDoc
*/
public function OnDBDelete($oObject, $oChange = null)
{
}
}
/**
* New extension to add menu items in the "popup" menus inside iTop. Provides a greater flexibility than
* iApplicationUIExtension::EnumAllowedActions.
@@ -586,7 +784,6 @@ abstract class ApplicationPopupMenuItem
*
* @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
*/
public function __construct($sUID, $sLabel)
{
@@ -848,6 +1045,41 @@ interface iPageUIExtension
public function GetBannerHtml(iTopWebPage $oPage);
}
/**
* Extend this class instead of iPageUIExtension if you don't need to overload all methods
*
* @api
* @package Extensibility
* @since 2.7.0
*/
abstract class AbstractPageUIExtension implements iPageUIExtension
{
/**
* @inheritDoc
*/
public function GetNorthPaneHtml(iTopWebPage $oPage)
{
return '';
}
/**
* @inheritDoc
*/
public function GetSouthPaneHtml(iTopWebPage $oPage)
{
return '';
}
/**
* @inheritDoc
*/
public function GetBannerHtml(iTopWebPage $oPage)
{
return '';
}
}
/**
* Implement this interface to add content to any enhanced portal page
*
@@ -855,7 +1087,7 @@ interface iPageUIExtension
*
* @api
* @package Extensibility
* @since 2.4
* @since 2.4.0
*/
interface iPortalUIExtension
{
@@ -1079,11 +1311,6 @@ class RestResult
/**
* Default constructor - ok!
*
* @param DBObject $oObject The object being reported
* @param string $sAttCode The attribute code (must be valid)
*
* @return string A scalar representation of the value
*/
public function __construct()
{

View File

@@ -150,7 +150,7 @@ EOF
* @param bool $bMustNotExist
*
* @see SetSessionMessage()
* @since 2.6
* @since 2.6.0
*/
protected function SetSessionMessageFromInstance($sMessageId, $sMessage, $sSeverity, $fRank, $bMustNotExist = false)
{
@@ -239,8 +239,11 @@ EOF
foreach($_SESSION['obj_messages'][$sMessageKey] as $sMessageId => $aMessageData)
{
$sMsgClass = 'message_'.$aMessageData['severity'];
$aMessages[] = "<div class=\"header_message $sMsgClass\">".$aMessageData['message']."</div>";
$aRanks[] = $aMessageData['rank'];
if(!in_array("<div class=\"header_message $sMsgClass\">".$aMessageData['message']."</div>",$aMessages))
{
$aMessages[] = "<div class=\"header_message $sMsgClass\">".$aMessageData['message']."</div>";
$aRanks[] = $aMessageData['rank'];
}
}
unset($_SESSION['obj_messages'][$sMessageKey]);
}
@@ -495,7 +498,10 @@ EOF
$bCanEdit = UserRights::IsAdministrator() || $oAttDef->IsUserEditable();
$sDivId = $oDashboard->GetId();
$oPage->add('<div class="dashboard_contents" id="'.$sDivId.'">');
$aExtraParams = array('query_params' => $this->ToArgsForQuery());
$aExtraParams = array(
'query_params' => $this->ToArgsForQuery(),
'dashboard_div_id' => $sDivId,
);
$oDashboard->Render($oPage, false, $aExtraParams, $bCanEdit);
$oPage->add('</div>');
}
@@ -552,12 +558,6 @@ EOF
$oLinkSet = $oOrmLinkSet->ToDBObjectSet(utils::ShowObsoleteData());
$iCount = $oLinkSet->Count();
$sCount = '';
if ($iCount != 0)
{
$sCount = " ($iCount)";
}
$oPage->SetCurrentTab('Class:'.$sClass.'/Attribute:'.$sAttCode, $oAttDef->GetLabel().$sCount);
if ($this->IsNew())
{
$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
@@ -603,6 +603,9 @@ EOF
continue;
}
$sCount = ($iCount != 0) ? " ($iCount)" : "";
$oPage->SetCurrentTab('Class:'.$sClass.'/Attribute:'.$sAttCode, $oAttDef->GetLabel().$sCount);
$aArgs = array('this' => $this);
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE));
if ($bEditMode && (!$bReadOnly))
@@ -943,6 +946,7 @@ EOF
$val['attcode'] = $sAttCode;
$val['atttype'] = $sAttDefClass;
$val['attlabel'] = $sAttLabel;
$val['attflags'] = ($bEditMode) ? $this->GetFormAttributeFlags($sAttCode) : OPT_ATT_READONLY;
// - How the field should be rendered
$val['layout'] = (in_array($oAttDef->GetEditClass(), static::GetAttEditClassesToRenderAsLargeField())) ? 'large' : 'small';
@@ -2144,20 +2148,30 @@ EOF
$iMaxFileSize = utils::ConvertToBytes(ini_get('upload_max_filesize'));
$sHTMLValue = "<div class=\"field_input_zone field_input_document\">\n";
$sHTMLValue .= "<input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"$iMaxFileSize\" />\n";
$sHTMLValue .= "<input type=\"hidden\" id=\"do_remove_{$iId}\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}[remove]\" value=\"0\"/>\n";
$sHTMLValue .= "<input name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}[filename]\" type=\"hidden\" id=\"$iId\" \" value=\"".htmlentities($sFileName,
ENT_QUOTES, 'UTF-8')."\"/>\n";
$sHTMLValue .= "<span id=\"name_$iInputId\"'>".htmlentities($sFileName, ENT_QUOTES,
'UTF-8')."</span><br/>\n";
$sHTMLValue .= "<span id=\"name_$iInputId\"' >".htmlentities($sFileName, ENT_QUOTES,
'UTF-8')."</span>&#160;&#160;";
$sHTMLValue .= "<div title=\"".htmlentities(Dict::S('UI:Button:RemoveDocument'), ENT_QUOTES, 'UTF-8'). "\" id=\"remove_attr_$iId\" class=\"button\" onClick=\"$('#file_$iId').val('');UpdateFileName('$iId', '');\" style=\"display: contents;\">";
$sHTMLValue .= "<div class=\"ui-icon ui-icon-trash\"></div></div>";
$sHTMLValue .= "</div>";
$sHTMLValue .= "<br/>\n";
$sHTMLValue .= "<input title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}[fcontents]\" type=\"file\" id=\"file_$iId\" onChange=\"UpdateFileName('$iId', this.value)\"/>\n";
$sHTMLValue .= "</div>\n";
$sHTMLValue .= "{$sValidationSpan}{$sReloadSpan}\n";
if ($sFileName == '')
{
$oPage->add_ready_script("$('#remove_attr_{$iId}').hide();");
}
break;
case 'Image':
$aEventsList[] = 'validate';
$aEventsList[] = 'change';
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/edit_image.js');
$oDocument = $value; // Value is an ormDocument object
$oDocument = $value; // Value is an ormDocument objectm
$sDefaultUrl = $oAttDef->Get('default_image');
if (is_object($oDocument) && !$oDocument->IsEmpty())
{
@@ -2998,10 +3012,38 @@ HTML
$sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef,
$this->Get($sAttCode), $this->GetEditValue($sAttCode), 'att_'.$iFieldIndex, '', $iExpectCode,
$aArgs);
$aDetails[] = array(
$aAttrib = array(
'label' => '<span>'.$oAttDef->GetLabel().'</span>',
'value' => "<span id=\"field_att_$iFieldIndex\">$sHTMLValue</span>",
);
//add attrib for data-attribute
// Prepare metadata attributes
$sAttCode = $oAttDef->GetCode();
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sAttDefClass = get_class($oAttDef);
$sAttLabel = MetaModel::GetLabel($sClass, $sAttCode);
$aAttrib['attcode'] = $sAttCode;
$aAttrib['atttype'] = $sAttDefClass;
$aAttrib['attlabel'] = $sAttLabel;
// - Attribute flags
$aAttrib['attflags'] = $this->GetFormAttributeFlags($sAttCode) ;
// - How the field should be rendered
$aAttrib['layout'] = (in_array($oAttDef->GetEditClass(), static::GetAttEditClassesToRenderAsLargeField())) ? 'large' : 'small';
// - For simple fields, we get the raw (stored) value as well
$bExcludeRawValue = false;
foreach (static::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as $sAttDefClassToExclude)
{
if (is_a($sAttDefClass, $sAttDefClassToExclude, true))
{
$bExcludeRawValue = true;
break;
}
}
$aAttrib['value_raw'] = ($bExcludeRawValue === false) ? $this->Get($sAttCode) : '';
$aDetails[] = $aAttrib;
$aFieldsMap[$sAttCode] = 'att_'.$iFieldIndex;
$iFieldIndex++;
}
@@ -3171,11 +3213,18 @@ EOF
if ($oAttDef->GetEditClass() == 'Document')
{
$oDocument = $this->Get($sAttCode);
$sDisplayValue = $this->GetAsHTML($sAttCode);
$sDisplayValue .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_',
$oDocument->GetDisplayLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
$sDisplayValue .= "<br/>".Dict::Format('UI:DownloadDocument_',
$oDocument->GetDownloadLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
if (!$oDocument->IsEmpty())
{
$sDisplayValue = $this->GetAsHTML($sAttCode);
$sDisplayValue .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_',
$oDocument->GetDisplayLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
$sDisplayValue .= "<br/>".Dict::Format('UI:DownloadDocument_',
$oDocument->GetDownloadLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
}
else
{
$sDisplayValue ='';
}
}
elseif ($oAttDef instanceof AttributeDashboard)
{
@@ -3207,17 +3256,15 @@ EOF
*/
public function DisplayDocumentInline(WebPage $oPage, $sAttCode)
{
/** @var \ormDocument $oDoc */
$oDoc = $this->Get($sAttCode);
$sClass = get_class($this);
$Id = $this->GetKey();
switch ($oDoc->GetMainMimeType())
{
switch ($oDoc->GetMainMimeType()) {
case 'text':
case 'html':
$data = $oDoc->GetData();
switch ($oDoc->GetMimeType())
{
case 'text/html':
switch ($oDoc->GetMimeType()) {
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;
@@ -3515,14 +3562,6 @@ EOF
switch ($oAttDef->GetEditClass())
{
case 'Document':
// There should be an uploaded file with the named attr_<attCode>
$oDocument = $value['fcontents'];
if (!$oDocument->IsEmpty())
{
// A new file has been uploaded
$this->Set($sAttCode, $oDocument);
}
break;
case 'Image':
// There should be an uploaded file with the named attr_<attCode>
if ($value['remove'])
@@ -3748,23 +3787,29 @@ EOF
switch ($oAttDef->GetEditClass())
{
case 'Document':
$value = array('fcontents' => utils::ReadPostedDocument("attr_{$sFormPrefix}{$sAttCode}", 'fcontents'));
$aOtherData = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null, 'raw_data');
$value = array('fcontents' => utils::ReadPostedDocument("attr_{$sFormPrefix}{$sAttCode}", 'fcontents'), 'remove' => $aOtherData['remove']);
break;
case 'Image':
$value = null;
$oImage = utils::ReadPostedDocument("attr_{$sFormPrefix}{$sAttCode}", 'fcontents');
$aSize = utils::GetImageSize($oImage->GetData());
$oImage = utils::ResizeImageToFit($oImage, $aSize[0], $aSize[1], $oAttDef->Get('storage_max_width'),
$oAttDef->Get('storage_max_height'));
if (!is_null($oImage->GetData()))
{
$aSize = utils::GetImageSize($oImage->GetData());
$oImage = utils::ResizeImageToFit(
$oImage,
$aSize[0],
$aSize[1],
$oAttDef->Get('storage_max_width'),
$oAttDef->Get('storage_max_height')
);
}
$aOtherData = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null, 'raw_data');
if (is_array($aOtherData))
{
$value = array('fcontents' => $oImage, 'remove' => $aOtherData['remove']);
}
else
{
$value = null;
}
break;
case 'RedundancySetting':
@@ -4022,7 +4067,7 @@ EOF
/**
* @param string $sMessageIdPrefix
*
* @since 2.6
* @since 2.6.0
*/
protected function SetWarningsAsSessionMessages($sMessageIdPrefix)
{
@@ -4203,6 +4248,7 @@ EOF
{
$oPage->SetCurrentTab('UI:PropertiesTab');
$sClass = get_class($this);
if ($this->IsNew())
{
$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
@@ -4211,6 +4257,7 @@ EOF
{
$iFlags = $this->GetAttributeFlags($sAttCode);
}
if ($iFlags & OPT_ATT_HIDDEN)
{
// The case log is hidden do nothing
@@ -4218,6 +4265,16 @@ EOF
else
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
$sAttDefClass = get_class($oAttDef);
$sAttLabel = $oAttDef->GetLabel();
$sAttMetaDataLabel = utils::HtmlEntities($sAttLabel);
$sAttMetaDataFlagHidden = (($iFlags & OPT_ATT_HIDDEN) === OPT_ATT_HIDDEN) ? 'true' : 'false';
$sAttMetaDataFlagReadOnly = (($iFlags & OPT_ATT_READONLY) === OPT_ATT_READONLY) ? 'true' : 'false';
$sAttMetaDataFlagMandatory = (($iFlags & OPT_ATT_MANDATORY) === OPT_ATT_MANDATORY) ? 'true' : 'false';
$sAttMetaDataFlagMustChange = (($iFlags & OPT_ATT_MUSTCHANGE) === OPT_ATT_MUSTCHANGE) ? 'true' : 'false';
$sAttMetaDataFlagMustPrompt = (($iFlags & OPT_ATT_MUSTPROMPT) === OPT_ATT_MUSTPROMPT) ? 'true' : 'false';
$sAttMetaDataFlagSlave = (($iFlags & OPT_ATT_SLAVE) === OPT_ATT_SLAVE) ? 'true' : 'false';
$sInputId = $this->m_iFormId.'_'.$sAttCode;
if ((!$bEditMode) || ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE)))
@@ -4254,19 +4311,32 @@ EOF
$sValue = $this->Get($sAttCode);
$sDisplayValue = $this->GetEditValue($sAttCode);
$aArgs = array('this' => $this, 'formPrefix' => $sPrefix);
$sHTMLValue = '';
if ($sComment != '')
{
$sHTMLValue = '<span>'.$sComment.'</span><br/>';
}
$sHTMLValue .= "<span style=\"font-family:Tahoma,Verdana,Arial,Helvetica;font-size:12px;\" id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage,
$sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags,
$aArgs).'</span>';
$sCommentAsHtml = ($sComment != '') ? '<span>'.$sComment.'</span><br/>' : '';
$sFieldAsHtml = self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs);
$sHTMLValue = <<<HTML
<div class="field_data">
<div class="field_value">
$sCommentAsHtml
$sFieldAsHtml
</div>
</div>
HTML;
$aFieldsMap[$sAttCode] = $sInputId;
}
$oPage->add('<fieldset><legend>'.$oAttDef->GetLabel().'</legend>');
$oPage->add($sHTMLValue);
$oPage->add('</fieldset>');
$oPage->add(<<<HTML
<fieldset>
<legend>{$sAttLabel}</legend>
<div class="field_container field_large" data-attribute-code="{$sAttCode}" data-attribute-type="{$sAttDefClass}" data-attribute-label="{$sAttMetaDataLabel}"
data-attribute-flag-hidden="{$sAttMetaDataFlagHidden}" data-attribute-flag-read-only="{$sAttMetaDataFlagReadOnly}" data-attribute-flag-mandatory="{$sAttMetaDataFlagMandatory}"
data-attribute-flag-must-change="{$sAttMetaDataFlagMustChange}" data-attribute-flag-must-prompt="{$sAttMetaDataFlagMustPrompt}" data-attribute-flag-slave="{$sAttMetaDataFlagSlave}">
{$sHTMLValue}
</div>
</fieldset>
HTML
);
}
}

View File

@@ -29,13 +29,13 @@ require_once(APPROOT."/application/webpage.class.inc.php");
class CSVPage extends WebPage
{
function __construct($s_title)
{
parent::__construct($s_title);
$this->add_header("Content-type: text/plain; charset=".self::PAGES_CHARSET);
$this->add_header("Cache-control: no-cache");
function __construct($s_title) {
parent::__construct($s_title);
$this->add_header("Content-type: text/plain; charset=".self::PAGES_CHARSET);
$this->no_cache();
$this->add_xframe_options();
//$this->add_header("Content-Transfer-Encoding: binary");
}
}
public function output()
{

View File

@@ -176,12 +176,6 @@ abstract class Dashboard
protected function InitDashletFromDOMNode($oDomNode)
{
$sId = $oDomNode->getAttribute('id');
// To avoid collision with other dashlets with the same ID we suffix it. Collisions typically happen with extensions.
// Note: The check is done so we don't append it at each save of the dashboard.
if(strpos($sId, 'uniqid_') === false)
{
$sId .= '_uniqid_' . uniqid();
}
$sDashletType = $oDomNode->getAttribute('xsi:type');
@@ -341,6 +335,25 @@ abstract class Dashboard
}
/**
* @return mixed
*/
public function GetId()
{
return $this->sId;
}
/**
* Return a sanitize ID for usages in XML/HTML attributes
*
* @return string
* @since 2.7.0
*/
public function GetSanitizedId()
{
return utils::Sanitize($this->GetId(), '', 'element_identifier');
}
/**
* @return string
*/
@@ -516,10 +529,25 @@ EOF
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
{
if (!array_key_exists('dashboard_div_id', $aExtraParams))
{
$aExtraParams['dashboard_div_id'] = utils::Sanitize($this->GetId(), '', 'element_identifier');
}
$oPage->add('<div class="dashboard-title-line"><div class="dashboard-title">'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</div></div>');
$oLayout = new $this->sLayoutClass;
/** @var \DashboardLayoutMultiCol $oLayout */
$oLayout = new $this->sLayoutClass();
foreach($this->aCells as $iCellIdx => $aDashlets)
{
foreach($aDashlets as $oDashlet)
{
$aDashletCoordinates = $oLayout->GetDashletCoordinates($iCellIdx);
$this->PrepareDashletForRendering($oDashlet, $aDashletCoordinates, $aExtraParams);
}
}
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
if (!$bEditMode)
{
@@ -561,19 +589,21 @@ EOF
// Toolbox/palette to edit the properties of each dashlet
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:DashletProperties').'</div>');
/** @var \DashboardLayoutMultiCol $oLayout */
$oLayout = new $this->sLayoutClass();
$oPage->add('<div id="dashlet_properties" style="text-align:center">');
foreach($this->aCells as $aCell)
foreach($this->aCells as $iCellIdx => $aCell)
{
/** @var \Dashlet $oDashlet */
foreach($aCell as $oDashlet)
{
$sId = $oDashlet->GetID();
if ($oDashlet->IsVisible())
{
$oPage->add('<div class="dashlet_properties" id="dashlet_properties_'.$sId.'" style="display:none">');
$oPage->add('<div class="dashlet_properties" id="dashlet_properties_'.$oDashlet->GetID().'" style="display:none">');
$oForm = $oDashlet->GetForm();
$this->SetFormParams($oForm, $aExtraParams);
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
$oPage->add('</div>');
}
}
@@ -633,6 +663,18 @@ EOF
return $iNewId + 1;
}
/**
* Prepare dashlet for rendering (eg. change its ID or another processing).
* Meant to be overloaded.
*
* @param \Dashlet $oDashlet
* @param array $aCoordinates
* @param array $aExtraParams
*
* @return void
*/
abstract protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = array());
/**
* @param \DesignerForm $oForm
* @param array $aExtraParams
@@ -657,11 +699,34 @@ EOF
}
/**
* @return mixed
* N°2634: we must have a unique id per dashlet!
* To avoid collision with other dashlets with the same ID we prefix it with row/cell id
* Collisions typically happen with extensions.
*
* @param boolean $bIsCustomized
* @param string $sDashboardDivId
* @param int $iRow
* @param int $iCol
* @param string $sDashletOrigId
*
* @return string
*
* @since 2.7.0 N°2735
*/
public function GetId()
public static function GetDashletUniqueId($bIsCustomized, $sDashboardDivId, $iRow, $iCol, $sDashletOrigId)
{
return $this->sId;
if(strpos($sDashletOrigId, '_ID_row') !== false)
{
return $sDashletOrigId;
}
$sDashletId = $sDashboardDivId."_ID_row".$iRow."_col".$iCol."_".$sDashletOrigId;
if ($bIsCustomized)
{
$sDashletId = 'CUSTOM_'.$sDashletId;
}
return $sDashletId;
}
}
@@ -670,12 +735,12 @@ EOF
*/
class RuntimeDashboard extends Dashboard
{
/** @var bool $bCustomized */
protected $bCustomized;
/** @var string $sDefinitionFile */
private $sDefinitionFile = '';
/** @var null $sReloadURL */
private $sReloadURL = null;
/** @var bool $bCustomized */
protected $bCustomized;
/**
* @inheritDoc
@@ -683,12 +748,22 @@ class RuntimeDashboard extends Dashboard
public function __construct($sId)
{
parent::__construct($sId);
$this->bCustomized = false;
$this->oMetaModel = new ModelReflectionRuntime();
$this->bCustomized = false;
}
/**
* @return bool
* @since 2.7.0
*/
public function GetCustomFlag()
{
return $this->bCustomized;
}
/**
* @param bool $bCustomized
* @since 2.7.0
*/
public function SetCustomFlag($bCustomized)
{
@@ -846,7 +921,7 @@ class RuntimeDashboard extends Dashboard
if (!$bEditMode && !$oPage->IsPrintableVersion())
{
$sId = $this->GetId();
$sDivId = preg_replace('/[^a-zA-Z0-9_]/', '', $sId);
$sDivId = utils::Sanitize($sId, '', 'element_identifier');
if ($this->GetAutoReload())
{
$sFile = addslashes($this->GetDefinitionFile());
@@ -909,7 +984,7 @@ EOF
protected function RenderSelector($oPage, $aAjaxParams = array())
{
$sId = $this->GetId();
$sDivId = preg_replace('/[^a-zA-Z0-9_]/', '', $sId);
$sDivId = utils::Sanitize($sId, '', 'element_identifier');
$sExtraParams = json_encode($aAjaxParams);
$sSelectorHtml = '<div class="dashboard-selector">';
@@ -1098,9 +1173,11 @@ EOF
{
$aRenderParams = $aExtraParams;
}
$aRenderParams['dashboard_div_id'] = $aExtraParams['dashboard_div_id'];
$sJSExtraParams = json_encode($aExtraParams);
$oPage->add('<div id="dashboard_editor">');
$oPage->add('<div class="ui-layout-center">');
$this->SetCustomFlag(true);
$this->Render($oPage, true, $aRenderParams);
$oPage->add('</div>');
$oPage->add('<div class="ui-layout-east">');
@@ -1129,7 +1206,7 @@ EOF
$sAutoApplyConfirmationMessage = addslashes(Dict::S('UI:AutoApplyConfirmationMessage'));
$oPage->add_ready_script(
<<<EOF
<<<JS
window.bLeavingOnUserAction = false;
$('#dashboard_editor').dialog({
@@ -1172,10 +1249,15 @@ $('#dashboard_editor').dialog({
});
$('#dashboard_editor .ui-layout-center').runtimedashboard({
dashboard_id: '$sId', layout_class: '$sLayoutClass', title: '$sTitle',
auto_reload: $sAutoReload, auto_reload_sec: $sAutoReloadSec,
submit_to: '$sUrl', submit_parameters: {operation: 'save_dashboard', file: '$sFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
render_to: '$sUrl', render_parameters: {operation: 'render_dashboard', file: '$sFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
dashboard_id: '$sId',
layout_class: '$sLayoutClass',
title: '$sTitle',
auto_reload: $sAutoReload,
auto_reload_sec: $sAutoReloadSec,
submit_to: '$sUrl',
submit_parameters: {operation: 'save_dashboard', file: '$sFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
render_to: '$sUrl',
render_parameters: {operation: 'render_dashboard', file: '$sFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
new_dashlet_parameters: {operation: 'new_dashlet'}
});
@@ -1214,7 +1296,7 @@ window.onbeforeunload = function() {
}
// return nothing ! safer for IE
};
EOF
JS
);
$oPage->add_ready_script("");
}
@@ -1354,7 +1436,7 @@ EOF
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$oPage->add_ready_script(
<<<EOF
<<<JS
$('#dashlet_creation_dlg').dialog({
width: 600,
modal: true,
@@ -1383,7 +1465,7 @@ $('#dashlet_creation_dlg').dialog({
],
close: function() { $(this).remove(); }
});
EOF
JS
);
}
@@ -1418,4 +1500,90 @@ EOF
{
$this->sReloadURL = $sReloadURL;
}
/**
* @inheritDoc
*/
protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = array())
{
$sDashletIdOrig = $oDashlet->GetID();
$sDashboardSanitizedId = $this->GetSanitizedId();
$sDashletIdNew = static::GetDashletUniqueId($this->GetCustomFlag(), $sDashboardSanitizedId, $aCoordinates[1], $aCoordinates[0], $sDashletIdOrig);
$oDashlet->SetID($sDashletIdNew);
$this->UpdateDashletUserPrefs($oDashlet, $sDashletIdOrig, $aExtraParams);
}
/**
* Migrate dashlet specific prefs to new format
* Before 2.7.0 we were using the same for dashboard menu or dashboard attributes, standard or custom :
* <alias>-<class>|Dashlet<idx_dashlet>
* Since 2.7.0 it is the following, with a "CUSTOM_" prefix if necessary :
* * dashboard menu : <dashboard_id>_IDrow<row_idx>-col<col_idx>-<dashlet_idx>
* * dashboard attribute : <class>__<attcode>_IDrow<row_idx>-col<col_idx>-<dashlet_idx>
*
* @param \Dashlet $oDashlet
* @param string $sDashletIdOrig
*
* @param array $aExtraParams
*
* @since 2.7.0 N°2735
*/
private function UpdateDashletUserPrefs(Dashlet $oDashlet, $sDashletIdOrig, array $aExtraParams)
{
$bIsDashletWithListPref = ($oDashlet instanceof DashletObjectList);
if (!$bIsDashletWithListPref)
{
return;
}
/** @var \DashletObjectList $oDashlet */
$bDashletIdInNewFormat = ($sDashletIdOrig === $oDashlet->GetID());
if ($bDashletIdInNewFormat)
{
return;
}
$sNewPrefKey = $this->GetDashletObjectListAppUserPreferencesPrefix($oDashlet, $aExtraParams, $oDashlet->GetID());
$sPrefValueForNewKey = appUserPreferences::GetPref($sNewPrefKey, null);
$bHasPrefInNewFormat = ($sPrefValueForNewKey !== null);
if ($bHasPrefInNewFormat)
{
return;
}
$sOldPrefKey = $this->GetDashletObjectListAppUserPreferencesPrefix($oDashlet, $aExtraParams, $sDashletIdOrig);
$sPrefValueForOldKey = appUserPreferences::GetPref($sOldPrefKey, null);
$bHasPrefInOldFormat = ($sPrefValueForOldKey !== null);
if (!$bHasPrefInOldFormat)
{
return;
}
appUserPreferences::SetPref($sNewPrefKey, $sPrefValueForOldKey);
appUserPreferences::UnsetPref($sOldPrefKey);
}
/**
* @param \DashletObjectList $oDashlet
* @param array $aExtraParams
* @param string $sDashletId
*
* @return string
* @since 2.7.0
*/
private function GetDashletObjectListAppUserPreferencesPrefix(DashletObjectList $oDashlet, $aExtraParams, $sDashletId)
{
$sDataTableId = Dashlet::APPUSERPREFERENCES_PREFIX.$sDashletId;
$aClassAliases = array();
try{
$oFilter = $oDashlet->GetDBSearch($aExtraParams);
$aClassAliases = $oFilter->GetSelectedClasses();
}
catch (Exception $e)
{
//on error, return default value
return null;
}
return DataTableSettings::GetAppUserPreferenceKey($aClassAliases, $sDataTableId);
}
}

View File

@@ -26,14 +26,17 @@
abstract class DashboardLayout
{
public function __construct()
{
}
abstract public function Render($oPage, $aDashlets, $bEditMode = false);
/**
* @param int $iCellIdx
*
* @return array Containing 2 scalars: Col number and row number (starting from 0)
* @since 2.7.0
*/
abstract public function GetDashletCoordinates($iCellIdx);
static public function GetInfo()
public static function GetInfo()
{
return array(
'label' => '',
@@ -51,7 +54,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
{
$this->iNbCols = 1;
}
protected function TrimCell($aDashlets)
{
$aKeys = array_reverse(array_keys($aDashlets));
@@ -61,7 +64,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
{
/** @var \Dashlet $oDashlet */
$oDashlet = $aDashlets[$aKeys[$idx]];
if ($oDashlet->IsVisible())
if ($oDashlet::IsVisible())
{
$bNoVisibleFound = false;
}
@@ -110,20 +113,21 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
{
// Trim the list of cells to remove the invisible/empty ones at the end of the array
$aCells = $this->TrimCellsArray($aCells);
$oPage->add('<table style="width:100%;table-layout:fixed;"><tbody>');
$iCellIdx = 0;
$fColSize = 100 / $this->iNbCols;
$sStyle = $bEditMode ? 'border: 1px #ccc dashed; width:'.$fColSize.'%;' : 'width: '.$fColSize.'%;';
$sClass = $bEditMode ? 'layout_cell edit_mode' : 'dashboard';
$iNbRows = ceil(count($aCells) / $this->iNbCols);
for($iRows = 0; $iRows < $iNbRows; $iRows++)
{
$oPage->add('<tr>');
$oPage->add("<tr data-dashboard-row-index=\"$iRows\">");
for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
{
$sCellClass = ($iRows == $iNbRows-1) ? $sClass.' layout_last_used_rank' : $sClass;
$oPage->add("<td style=\"$sStyle\" class=\"$sCellClass\" data-dashboard-cell-index=\"$iCellIdx\">");
$oPage->add("<td style=\"$sStyle\" class=\"$sCellClass\" data-dashboard-column-index=\"$iCols\" data-dashboard-cell-index=\"$iCellIdx\">");
if (array_key_exists($iCellIdx, $aCells))
{
$aDashlets = $aCells[$iCellIdx];
@@ -132,7 +136,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
/** @var \Dashlet $oDashlet */
foreach($aDashlets as $oDashlet)
{
if ($oDashlet->IsVisible())
if ($oDashlet::IsVisible())
{
$oDashlet->DoRender($oPage, $bEditMode, true /* bEnclosingDiv */, $aExtraParams);
}
@@ -155,10 +159,10 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
if ($bEditMode) // Add one row for extensibility
{
$sStyle = 'style="border: 1px #ccc dashed; width:'.$fColSize.'%;" class="layout_cell edit_mode layout_extension" data-dashboard-cell-index="'.$iCellIdx.'"';
$oPage->add('<tr>');
$oPage->add("<tr data-dashboard-row-index=\"$iRows\">");
for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
{
$oPage->add("<td $sStyle>");
$oPage->add("<td $sStyle data-dashboard-column-index=\"$iCols\">");
$oPage->add('&nbsp;');
$oPage->add('</td>');
}
@@ -166,6 +170,17 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
}
$oPage->add('</tbody></table>');
}
/**
* @inheritDoc
*/
public function GetDashletCoordinates($iCellIdx)
{
$iColNumber = (int) $iCellIdx % $this->iNbCols;
$iRowNumber = (int) floor($iCellIdx / $this->iNbCols);
return array($iColNumber, $iRowNumber);
}
}
class DashboardLayoutOneCol extends DashboardLayoutMultiCol

View File

@@ -26,6 +26,9 @@ require_once(APPROOT.'application/forms.class.inc.php');
*/
abstract class Dashlet
{
/** @var string */
const APPUSERPREFERENCES_PREFIX = 'Dashlet';
protected $oModelReflection;
protected $sId;
protected $bRedrawNeeded;
@@ -456,17 +459,21 @@ EOF
$sAttType = $aTargetAttCodes[$sTargetAttCode];
$sExtFieldAttCode = $sTargetAttCode;
}
if (is_a($sAttType, 'AttributeLinkedSet', true))
{
continue;
}
if (is_a($sAttType, 'AttributeFriendlyName', true))
{
continue;
}
if (is_a($sAttType, 'AttributeOneWayPassword', true))
{
continue;
$aForbidenAttType = [
'AttributeLinkedSet',
'AttributeFriendlyName',
'iAttributeNoGroupBy', //we cannot only use iAttributeNoGroupBy since this method is also used by the designer who do not have access to the classes' PHP reflection API. So the known classes has to be listed altogether
'AttributeOneWayPassword',
'AttributeEncryptedString',
'AttributePassword',
];
foreach ($aForbidenAttType as $sForbidenAttType) {
if (is_a($sAttType, $sForbidenAttType, true))
{
continue 2;
}
}
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
@@ -514,7 +521,7 @@ EOF
*
* Used as a fallback in iTop for unknown dashlet classes.
*
* @since 2.5
* @since 2.5.0
*/
class DashletUnknown extends Dashlet
{
@@ -613,12 +620,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>');
@@ -633,12 +640,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>');
@@ -774,12 +781,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>');
@@ -860,7 +867,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;
@@ -913,15 +920,28 @@ class DashletObjectList extends Dashlet
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
$sTitle = $this->aProperties['title'];
$sQuery = $this->aProperties['query'];
$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>');
$oPage->add('<div class="main_header"><h1>&nbsp;'.$sHtmlTitle.'</h1></div>');
}
$oFilter = $this->GetDBSearch($aExtraParams);
$oBlock = new DisplayBlock($oFilter, 'list');
$aParams = array(
'menu' => $sShowMenu,
'table_id' => self::APPUSERPREFERENCES_PREFIX.$this->sId,
);
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
$oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams));
$oPage->add('</div>');
}
public function GetDBSearch($aExtraParams = array())
{
$sQuery = $this->aProperties['query'];
if (isset($aExtraParams['query_params']))
{
$aQueryParams = $aExtraParams['query_params'];
@@ -935,15 +955,8 @@ class DashletObjectList extends Dashlet
{
$aQueryParams = array();
}
$oFilter = DBObjectSearch::FromOQL($sQuery, $aQueryParams);
$oBlock = new DisplayBlock($oFilter, 'list');
$aParams = array(
'menu' => $sShowMenu,
'table_id' => 'Dashlet'.$this->sId,
);
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
$oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams));
$oPage->add('</div>');
return DBObjectSearch::FromOQL($sQuery, $aQueryParams);
}
/**
@@ -956,7 +969,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>');
@@ -1112,7 +1125,7 @@ abstract class DashletGroupBy extends Dashlet
$this->sFunction = null;
}
if (empty($this->aProperties['order_direction']))
if ((!is_null($this->sClass)) && empty($this->aProperties['order_direction']))
{
$aAttributeTypes = $this->oModelReflection->ListAttributes($this->sClass);
if (isset($aAttributeTypes[$this->sGroupByAttCode]))
@@ -1249,7 +1262,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,
@@ -1263,10 +1276,10 @@ abstract class DashletGroupBy extends Dashlet
break;
}
$oPage->add('<div style="text-align:center" class="dashlet-content">');
$oPage->add('<div class="dashlet-content">');
if ($sHtmlTitle != '')
{
$oPage->add('<h1>'.$sHtmlTitle.'</h1>');
$oPage->add('<div class="main_header"><h1>&nbsp;'.$sHtmlTitle.'</h1></div>');
}
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
$oBlock = new DisplayBlock($oFilter, $sType);
@@ -1686,7 +1699,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();
@@ -1758,7 +1771,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();
@@ -1859,11 +1872,11 @@ class DashletGroupByTable extends DashletGroupBy
$iTotal += $aDisplayData['value'];
}
$oPage->add('<div class="dashlet-content">');
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
$oPage->add('<div id="'.$sBlockId.'" class="display_block">');
$oPage->add('<div class="dashlet-content">');
$oPage->add('<p>'.Dict::Format('UI:Pagination:HeaderNoSelection', $iTotal).'</p>');
$oPage->add('<table class="listResults">');
$oPage->add('<thead>');
@@ -1907,17 +1920,17 @@ 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('<h1>'.$this->oModelReflection->DictString($sTitle).'</h1>');
$oPage->add('<img src="'.$sIconPath.'">');
$oPage->add('<div class="main_header"><h1>&nbsp;'.$this->oModelReflection->DictString($sTitle).'</h1></div>');
$oPage->add('</div>');
$oPage->add('</div>');
@@ -2037,14 +2050,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)
@@ -2072,7 +2085,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']))
{
@@ -2101,9 +2114,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'];
@@ -2111,12 +2124,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)
@@ -2147,8 +2160,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

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.6">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.7">
<portals>
<portal id="backoffice" _delta="define">
<url>pages/UI.php</url>
@@ -15,7 +15,7 @@
<menu id="AdminTools" xsi:type="MenuGroup" _delta="define">
<rank>80</rank>
</menu>
<menu id="System" xsi:type="MenuGroup" _delta="define">
<menu id="SystemTools" xsi:type="MenuGroup" _delta="define">
<rank>100</rank>
<enable_class>ResourceSystemMenu</enable_class>
<enable_action>UR_ACTION_MODIFY</enable_action>

View File

@@ -20,6 +20,8 @@
class DataTable
{
protected $iListId; // Unique ID inside the web page
/** @var string */
private $sDatatableContainerId;
protected $sTableId; // identifier for saving the settings (combined with the class aliases)
protected $oSet; // The set of objects to display
protected $aClassAliases; // The aliases (alias => class) inside the set
@@ -29,10 +31,10 @@ class DataTable
protected $bShowObsoleteData;
/**
* @param $iListId mixed Unique ID for this div/table in the page
* @param $oSet DBObjectSet The set of data to display
* @param $aClassAliases array The list of classes/aliases to be displayed in this set $sAlias => $sClassName
* @param $sTableId mixed A string (or null) identifying this table in order to persist its settings
* @param string $iListId Unique ID for this div/table in the page
* @param DBObjectSet $oSet The set of data to display
* @param array$aClassAliases The list of classes/aliases to be displayed in this set $sAlias => $sClassName
* @param string $sTableId A string (or null) identifying this table in order to persist its settings
*
* @throws \CoreException
* @throws \MissingQueryArgument
@@ -42,6 +44,7 @@ class DataTable
public function __construct($iListId, $oSet, $aClassAliases, $sTableId = null)
{
$this->iListId = utils::GetSafeId($iListId); // Make a "safe" ID for jQuery
$this->sDatatableContainerId = 'datatable_'.utils::GetSafeId($iListId);
$this->oSet = $oSet;
$this->aClassAliases = $aClassAliases;
$this->sTableId = $sTableId;
@@ -165,7 +168,7 @@ class DataTable
$sDataTable = $this->GetHTMLTable($oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
$sConfigDlg = $this->GetTableConfigDlg($oPage, $aColumns, $bViewLink, $iDefaultPageSize);
$sHtml = "<table id=\"datatable_{$this->iListId}\" class=\"datatable\">";
$sHtml = "<table id=\"{$this->sDatatableContainerId}\" class=\"datatable\">";
$sHtml .= "<tr><td>";
$sHtml .= "<table style=\"width:100%;\">";
$sHtml .= "<tr><td class=\"pagination_container\">$sObjectsCount</td><td class=\"menucontainer\">$sToolkitMenu $sActionsMenu</td></tr>";
@@ -201,7 +204,7 @@ class DataTable
$aOptions['oDefaultSettings'] = $this->GetAsHash($this->oDefaultSettings);
}
$sJSOptions = json_encode($aOptions);
$oPage->add_ready_script("$('#datatable_{$this->iListId}').datatable($sJSOptions);");
$oPage->add_ready_script("$('#{$this->sDatatableContainerId}').datatable($sJSOptions);");
return $sHtml;
}
@@ -418,15 +421,15 @@ EOF;
$sHtml .= "<input id=\"dtbl_dlg_all_{$this->iListId}\" type=\"radio\" name=\"scope\" $sGenericChecked value=\"defaults\"><label for=\"dtbl_dlg_all_{$this->iListId}\">&nbsp;".Dict::S('UI:ForAllLists').'</label></p>';
$sHtml .= "</fieldset>";
$sHtml .= '<table style="width:100%"><tr><td style="text-align:center;">';
$sHtml .= '<button type="button" onclick="$(\'#datatable_'.$this->iListId.'\').datatable(\'onDlgCancel\'); $(\'#datatable_dlg_'.$this->iListId.'\').dialog(\'close\')">'.Dict::S('UI:Button:Cancel').'</button>';
$sHtml .= '<button type="button" onclick="$(\'#'.$this->sDatatableContainerId.'\').datatable(\'onDlgCancel\'); $(\'#datatable_dlg_'.$this->iListId.'\').dialog(\'close\')">'.Dict::S('UI:Button:Cancel').'</button>';
$sHtml .= '</td><td style="text-align:center;">';
$sHtml .= '<button type="submit" onclick="$(\'#datatable_'.$this->iListId.'\').datatable(\'onDlgOk\');$(\'#datatable_dlg_'.$this->iListId.'\').dialog(\'close\');">'.Dict::S('UI:Button:Ok').'</button>';
$sHtml .= '<button type="submit" onclick="$(\'#'.$this->sDatatableContainerId.'\').datatable(\'onDlgOk\');$(\'#datatable_dlg_'.$this->iListId.'\').dialog(\'close\');">'.Dict::S('UI:Button:Ok').'</button>';
$sHtml .= '</td></tr></table>';
$sHtml .= "</form>";
$sHtml .= "</div>";
$sDlgTitle = addslashes(Dict::S('UI:ListConfigurationTitle'));
$oPage->add_ready_script("$('#datatable_dlg_{$this->iListId}').dialog({autoOpen: false, title: '$sDlgTitle', width: 500, close: function() { $('#datatable_{$this->iListId}').datatable('onDlgCancel'); } });");
$oPage->add_ready_script("$('#datatable_dlg_{$this->iListId}').dialog({autoOpen: false, title: '$sDlgTitle', width: 500, close: function() { $('#{$this->sDatatableContainerId}').datatable('onDlgCancel'); } });");
return $sHtml;
}
@@ -745,12 +748,25 @@ EOF;
}
$sOQL = addslashes($this->oSet->GetFilter()->serialize());
$oPage->add_ready_script(
<<<EOF
var oTable = $('#{$this->iListId} table.listResults');
<<<JS
var oTable = $('#{$this->sDatatableContainerId} 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, table_id: '{$this->iListId}', columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount});
EOF
);
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->sDatatableContainerId}',
columns: $sJSColumns,
class_aliases: $sJSClassAliases $sCssCount
});
JS
);
if ($sFakeSortList != '')
{
$oPage->add_ready_script("oTable.trigger(\"fakesorton\", [$sFakeSortList]);");
@@ -1158,9 +1174,18 @@ class DataTableSettings implements Serializable
*/
protected function GetPrefsKey($sTableId = null)
{
if ($sTableId == null) $sTableId = '*';
return static::GetAppUserPreferenceKey($this->aClassAliases, $sTableId);
}
public static function GetAppUserPreferenceKey($aClassAliases, $sTableId)
{
if ($sTableId === null)
{
$sTableId = '*';
}
$aKeys = array();
foreach($this->aClassAliases as $sAlias => $sClass)
foreach($aClassAliases as $sAlias => $sClass)
{
$aKeys[] = $sAlias.'-'.$sClass;
}

View File

@@ -446,8 +446,21 @@ class DisplayBlock
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, $aOrderBy, $aQueryParams);
}
$this->m_oSet->SetShowObsoleteData($this->m_bShowObsoleteData);
switch($this->m_sStyle)
{
switch($this->m_sStyle) {
case 'list_search':
case 'list':
break;
default:
// N°3473: except for 'list_search' and 'list' (which have more granularity, see the other switch below),
// refuse to render if the user is not allowed to see the class.
if (! UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) {
$sHtml .= $oPage->GetP(Dict::Format('UI:Error:ReadNotAllowedOn_Class', $this->m_oSet->GetClass()));
return $sHtml;
}
}
switch ($this->m_sStyle) {
case 'count':
if (isset($aExtraParams['group_by']))
{
@@ -837,7 +850,8 @@ class DisplayBlock
foreach($aStates as $sStateValue)
{
$aStateLabels[$sStateValue] = htmlentities($oAttDef->GetValueLabel($sStateValue), ENT_QUOTES, 'UTF-8');
$sHtmlValue=$aGroupBy['group1']->MakeValueLabel($this->m_oFilter, $sStateValue, $sStateValue);
$aStateLabels[$sStateValue] = html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8');
$aCounts[$sStateValue] = (array_key_exists($sStateValue, $aCountsQueryResults))
? $aCountsQueryResults[$sStateValue]
@@ -946,7 +960,7 @@ class DisplayBlock
$iChartCounter++;
$sChartType = isset($aExtraParams['chart_type']) ? $aExtraParams['chart_type'] : 'pie';
$sTitle = isset($aExtraParams['chart_title']) ? '<h1 style="text-align:center">'.htmlentities(Dict::S($aExtraParams['chart_title']), ENT_QUOTES, 'UTF-8').'</h1>' : '';
$sTitle = isset($aExtraParams['chart_title']) ? '<div class="main_header"><h1>&#160;'.htmlentities(Dict::S($aExtraParams['chart_title']), ENT_QUOTES, 'UTF-8').'</h1></div>' : '';
$sHtml = "$sTitle<div style=\"height:200px;width:100%\" class=\"dashboard_chart\" id=\"my_chart_$sId{$iChartCounter}\"><div style=\"height:200px;line-height:200px;vertical-align:center;text-align:center;width:100%\"><img src=\"../images/indicator.gif\"></div></div>\n";
$sGroupBy = isset($aExtraParams['group_by']) ? $aExtraParams['group_by'] : '';
$sGroupByExpr = isset($aExtraParams['group_by_expr']) ? '&params[group_by_expr]='.$aExtraParams['group_by_expr'] : '';

View File

@@ -0,0 +1,82 @@
<?php
/**
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ErrorPage extends NiceWebPage
{
public function __construct($sTitle)
{
parent::__construct($sTitle);
$this->add_linked_script("../js/jquery.blockUI.js");
$this->add_linked_script("../setup/setup.js");
$this->add_saas("css/setup.scss");
}
public function info($sText)
{
$this->add("<p class=\"info\">$sText</p>\n");
$this->log_info($sText);
}
public function ok($sText)
{
$this->add("<div class=\"message message-valid\"><span class=\"message-title\">Success:</span>$sText</div>");
$this->log_ok($sText);
}
public function warning($sText)
{
$this->add("<div class=\"message message-warning\"><span class=\"message-title\">Warning:</span>$sText</div>");
$this->log_warning($sText);
}
public function error($sText)
{
$this->add("<div class=\"message message-error\">$sText</div>");
$this->log_error($sText);
}
public function output()
{
$sLogo = utils::GetAbsoluteUrlAppRoot().'/images/itop-logo.png';
$sTimeStamp = utils::GetCacheBusterTimestamp();
$sTitle = utils::HtmlEntities($this->s_title);
$this->s_content = <<<HTML
<div id="header" class="error_page">
<h1><a href="http://www.combodo.com/itop" target="_blank"><img title="iTop by Combodo" alt=" " src="{$sLogo}?t={$sTimeStamp}"></a>&nbsp;{$sTitle}</h1>
</div>
<div id="setup" class="error_page">
{$this->s_content}
</div>
HTML;
return parent::output();
}
public static function log_error($sText)
{
IssueLog::Error($sText);
}
public static function log_warning($sText)
{
IssueLog::Warning($sText);
}
public static function log_info($sText)
{
IssueLog::Info($sText);
}
public static function log_ok($sText)
{
IssueLog::Ok($sText);
}
public static function log($sText)
{
IssueLog::Ok($sText);
}
}

View File

@@ -60,8 +60,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
// Create a breadcrumb entry for the current page, but get its title as late as possible (page title could be changed later)
$this->bBreadCrumbEnabled = true;
}
else
{
else {
$this->bBreadCrumbEnabled = false;
}
@@ -71,7 +70,8 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
$this->m_aMessages = array();
$this->SetRootUrl(utils::GetAbsoluteUrlAppRoot());
$this->add_header("Content-type: text/html; charset=".self::PAGES_CHARSET);
$this->add_header("Cache-control: no-cache");
$this->no_cache();
$this->add_xframe_options();
$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");
@@ -80,6 +80,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
$this->add_linked_stylesheet("../css/c3.min.css");
$this->add_linked_stylesheet("../css/font-awesome/css/all.min.css");
$this->add_linked_stylesheet("../css/font-awesome/css/v4-shims.min.css");
$this->add_linked_stylesheet("../js/ckeditor/plugins/codesnippet/lib/highlight/styles/obsidian.css");
$this->add_linked_script('../js/jquery.layout.min.js');
$this->add_linked_script('../js/jquery.ba-bbq.min.js');
@@ -93,6 +94,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
$this->add_linked_script("../js/swfobject.js");
$this->add_linked_script("../js/ckeditor/ckeditor.js");
$this->add_linked_script("../js/ckeditor/adapters/jquery.js");
$this->add_linked_script("../js/ckeditor/plugins/codesnippet/lib/highlight/highlight.pack.js");
$this->add_linked_script("../js/jquery.qtip-1.0.min.js");
$this->add_linked_script('../js/property_field.js');
$this->add_linked_script('../js/icon_select.js');
@@ -350,6 +352,21 @@ JS
.magnificPopup({type: 'image', closeOnContentClick: true });
JS
);
// Highlight code content created with CKEditor
// Note: We check for the <code> tag inside the <pre> tag to only target code from CKEditor, otherwise we might highlight some others things. See N°3810
$this->add_ready_script(
<<<JS
// Highlight code content for HTML AttributeText
$("[data-attribute-type='AttributeText'] .HTML pre > code").parent().each(function(i, block) {
hljs.highlightBlock(block);
});
// Highlight code content for CaseLogs
$("[data-attribute-type='AttributeCaseLog'] .caselog_entry_html pre > code").parent().each(function(i, block) {
hljs.highlightBlock(block);
});
JS
);
$this->add_init_script(
<<< JS
@@ -1645,7 +1662,7 @@ EOF;
* @param string $sCssClasses CSS classes to add to the container
*
* @throws \Exception
* @since 2.6
* @since 2.6.0
*/
public function AddHeaderMessage($sContent, $sCssClasses = 'message_info')
{

View File

@@ -26,6 +26,10 @@ class LoginBasic extends AbstractLoginFSMExtension
{
$_SESSION['login_mode'] = 'basic';
}
elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
{
$_SESSION['login_mode'] = 'basic';
}
elseif (isset($_SERVER['PHP_AUTH_USER']))
{
$_SESSION['login_mode'] = 'basic';
@@ -36,7 +40,7 @@ class LoginBasic extends AbstractLoginFSMExtension
protected function OnReadCredentials(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'basic')
if (!isset($_SESSION['login_mode']) || $_SESSION['login_mode'] == 'basic')
{
list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword();
$_SESSION['login_temp_auth_user'] = $sAuthUser;
@@ -92,9 +96,19 @@ class LoginBasic extends AbstractLoginFSMExtension
{
$sAuthUser = '';
$sAuthPwd = null;
$sAuthorization = '';
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION']))
{
list($sAuthUser, $sAuthPwd) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
$sAuthorization = $_SERVER['HTTP_AUTHORIZATION'];
}
elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
{
$sAuthorization = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
}
if (!empty($sAuthorization))
{
list($sAuthUser, $sAuthPwd) = explode(':', base64_decode(substr($sAuthorization, 6)));
}
else
{
@@ -121,4 +135,4 @@ class LoginBasic extends AbstractLoginFSMExtension
}
return array($sAuthUser, $sAuthPwd);
}
}
}

View File

@@ -67,6 +67,15 @@ class LoginExternal extends AbstractLoginFSMExtension
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnError(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'external')
{
LoginWebPage::HTTP401Error();
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @return bool
*/

View File

@@ -1,12 +1,15 @@
<?php
/**
* Class LoginForm
*
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* Class LoginForm
*
* @since 2.7.0
*/
class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
{
private $bForceFormOnError = false;
@@ -21,6 +24,9 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
return array('form');
}
/**
* @inheritDoc
*/
protected function OnReadCredentials(&$iErrorCode)
{
if (!isset($_SESSION['login_mode']) || ($_SESSION['login_mode'] == 'form'))
@@ -51,6 +57,9 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @inheritDoc
*/
protected function OnCheckCredentials(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'form')
@@ -66,6 +75,9 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @inheritDoc
*/
protected function OnCredentialsOK(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'form')
@@ -85,6 +97,9 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @inheritDoc
*/
protected function OnError(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'form')
@@ -94,6 +109,9 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @inheritDoc
*/
protected function OnConnected(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'form')
@@ -105,7 +123,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
}
/**
* @return LoginTwigContext
* @inheritDoc
* @throws \Exception
*/
public function GetTwigContext()
@@ -125,7 +143,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
$oLoginContext->AddBlockExtension('login_submit', new LoginBlockExtension('extensionblock/loginformsubmit.html.twig'));
$oLoginContext->AddBlockExtension('login_form_footer', new LoginBlockExtension('extensionblock/loginformfooter.html.twig'));
$bEnableResetPassword = empty(MetaModel::GetConfig()->Get('forgot_password')) ? true : MetaModel::GetConfig()->Get('forgot_password');
$bEnableResetPassword = MetaModel::GetConfig()->Get('forgot_password');
$sResetPasswordUrl = utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?loginop=forgot_pwd';
$aData = array(
'bEnableResetPassword' => $bEnableResetPassword,

View File

@@ -78,14 +78,14 @@ 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->no_cache();
$this->add_xframe_options();
}
public function SetStyleSheet()
@@ -316,7 +316,7 @@ class LoginWebPage extends NiceWebPage
{
$aVars['bBadToken'] = false;
// 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
$aVars['sUrl'] = utils::GetAbsoluteUrlAppRoot();
@@ -685,7 +685,7 @@ class LoginWebPage extends NiceWebPage
public static function HTTPReload()
{
$sOriginURL = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$sOriginURL = utils::GetCurrentAbsoluteUrl();
if (!utils::StartsWith($sOriginURL, utils::GetAbsoluteUrlAppRoot()))
{
// If the found URL does not start with the configured AppRoot URL
@@ -790,12 +790,13 @@ class LoginWebPage extends NiceWebPage
$oPerson = null;
try
{
$sOrigin = 'External User provisioning';
CMDBObject::SetTrackOrigin('custom-extension');
$sInfo = 'External User provisioning';
if (isset($_SESSION['login_mode']))
{
$sOrigin .= " ({$_SESSION['login_mode']})";
$sInfo .= " ({$_SESSION['login_mode']})";
}
CMDBObject::SetTrackOrigin($sOrigin);
CMDBObject::SetTrackInfo($sInfo);
$oPerson = MetaModel::NewObject('Person');
$oPerson->Set('first_name', $sFirstName);
@@ -843,6 +844,14 @@ class LoginWebPage extends NiceWebPage
$oUser = null;
try
{
CMDBObject::SetTrackOrigin('custom-extension');
$sInfo = 'External User provisioning';
if (isset($_SESSION['login_mode']))
{
$sInfo .= " ({$_SESSION['login_mode']})";
}
CMDBObject::SetTrackInfo($sInfo);
$oUser = MetaModel::GetObjectByName('UserExternal', $sAuthUser, false);
if (is_null($oUser))
{
@@ -985,7 +994,7 @@ class LoginWebPage extends NiceWebPage
else
{
require_once(APPROOT.'/setup/setuppage.class.inc.php');
$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
$oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError'));
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n");
$oP->p("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>");
$oP->output();

View File

@@ -34,7 +34,7 @@ function _MaintenanceSetupPageMessage($sTitle, $sMessage)
@include_once(APPROOT.'setup/setuppage.class.inc.php');
if (class_exists('SetupPage'))
{
$oP = new SetupPage($sTitle);
$oP = new ErrorPage($sTitle);
$oP->p("<h2 class=\"center\">$sMessage</h2>");
$oP->add_ready_script(
<<<JS

View File

@@ -128,7 +128,7 @@ class ApplicationMenu
if (is_null($oMenuNode) || !$oMenuNode->IsEnabled())
{
require_once(APPROOT.'/setup/setuppage.class.inc.php');
$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
$oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError'));
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessRestricted')."</h1>\n");
$oP->p("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>");
$oP->output();
@@ -263,12 +263,15 @@ EOF
/**
* Handles the display of the sub-menus (called recursively if necessary)
*
* @param \WebPage $oPage
* @param array $aMenus
* @param array $aExtraParams
* @param int $iActiveMenu
*
* @return true if the currently selected menu is one of the submenus
* @throws DictExceptionMissingString
* @throws \Exception
*/
static protected function DisplaySubMenu($oPage, $aMenus, $aExtraParams, $iActiveMenu = -1)
{
@@ -301,7 +304,7 @@ EOF
$sLinkTarget .= ' target="_blank"';
}
$sURL = '"'.$oMenu->GetHyperlink($aExtraParams).'"'.$sLinkTarget;
$sTitle = $oMenu->GetTitle();
$sTitle = utils::HtmlEntities($oMenu->GetTitle());
$sItemHtml .= "<a href={$sURL}>{$sTitle}</a>";
}
else
@@ -692,8 +695,8 @@ abstract class MenuNode
public abstract function RenderContent(WebPage $oPage, $aExtraParams = array());
/**
* @param $sHyperlink
* @param $aExtraParams
* @param string $sHyperlink
* @param array $aExtraParams
* @return string
*/
protected function AddParams($sHyperlink, $aExtraParams)
@@ -737,8 +740,7 @@ class MenuGroup extends MenuNode
}
/**
* @param WebPage $oPage
* @param array $aExtraParams
* @inheritDoc
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
@@ -776,8 +778,7 @@ class TemplateMenuNode extends MenuNode
}
/**
* @param $aExtraParams
* @return string
* @inheritDoc
*/
public function GetHyperlink($aExtraParams)
{
@@ -786,10 +787,8 @@ class TemplateMenuNode extends MenuNode
}
/**
* @param WebPage $oPage
* @param array $aExtraParams
* @return mixed|void
* @throws DictExceptionMissingString
* @inheritDoc
* @throws \Exception
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
@@ -878,12 +877,8 @@ class OQLMenuNode extends MenuNode
}
/**
* @param WebPage $oPage
* @param array $aExtraParams
* @return mixed|void
* @throws CoreException
* @throws DictExceptionMissingString
* @throws OQLException
* @inheritDoc
* @throws \Exception
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
@@ -902,11 +897,11 @@ class OQLMenuNode extends MenuNode
}
/**
* @param $sOql
* @param $sTitle
* @param $sUsageId
* @param $bSearchPane
* @param $bSearchOpen
* @param string $sOql
* @param string $sTitle
* @param string $sUsageId
* @param bool $bSearchPane
* @param bool $bSearchOpen
* @param WebPage $oPage
* @param array $aExtraParams
* @param bool $bEnableBreadcrumb
@@ -927,7 +922,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);
@@ -979,11 +974,9 @@ class SearchMenuNode extends MenuNode
}
/**
* @param \iTopWebPage $oPage
* @param array $aExtraParams
* @return mixed|void
* @throws DictExceptionMissingString
* @throws Exception
* @inheritDoc
* @throws \DictExceptionMissingString
* @throws \Exception
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
@@ -1039,8 +1032,7 @@ class WebPageMenuNode extends MenuNode
}
/**
* @param array $aExtraParams
* @return string
* @inheritDoc
*/
public function GetHyperlink($aExtraParams)
{
@@ -1048,14 +1040,16 @@ class WebPageMenuNode extends MenuNode
return $this->AddParams( $this->sHyperlink, $aExtraParams);
}
/**
* @inheritDoc
*/
public function IsHyperLinkInNewWindow()
{
return $this->bIsLinkInNewWindow;
}
/**
* @param WebPage $oPage
* @param array $aExtraParams
* @inheritDoc
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
@@ -1096,10 +1090,7 @@ class NewObjectMenuNode extends MenuNode
}
/**
* @param string[] $aExtraParams
*
* @return string
* @throws \Exception
* @inheritDoc
*/
public function GetHyperlink($aExtraParams)
{
@@ -1133,8 +1124,7 @@ class NewObjectMenuNode extends MenuNode
}
/**
* @param WebPage $oPage
* @param string[] $aExtraParams
* @inheritDoc
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
@@ -1172,8 +1162,7 @@ class DashboardMenuNode extends MenuNode
}
/**
* @param string[] $aExtraParams
* @return string
* @inheritDoc
*/
public function GetHyperlink($aExtraParams)
{
@@ -1192,10 +1181,8 @@ class DashboardMenuNode extends MenuNode
}
/**
* @param \iTopWebPage $oPage
* @param string[] $aExtraParams
* @throws CoreException
* @throws Exception
* @inheritDoc
* @throws \Exception
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
@@ -1203,8 +1190,9 @@ class DashboardMenuNode extends MenuNode
$oDashboard = $this->GetDashboard();
if ($oDashboard != null)
{
$sDivId = preg_replace('/[^a-zA-Z0-9_]/', '', $this->sMenuId);
$sDivId = utils::Sanitize($this->sMenuId, '', 'element_identifier');
$oPage->add('<div class="dashboard_contents" id="'.$sDivId.'">');
$aExtraParams['dashboard_div_id'] = $sDivId;
$oDashboard->SetReloadURL($this->GetHyperlink($aExtraParams));
$oDashboard->Render($oPage, false, $aExtraParams);
$oPage->add('</div>');
@@ -1289,8 +1277,7 @@ class DashboardMenuNode extends MenuNode
class ShortcutContainerMenuNode extends MenuNode
{
/**
* @param string[] $aExtraParams
* @return string
* @inheritDoc
*/
public function GetHyperlink($aExtraParams)
{
@@ -1298,15 +1285,14 @@ class ShortcutContainerMenuNode extends MenuNode
}
/**
* @param WebPage $oPage
* @param string[] $aExtraParams
* @return mixed|void
* @inheritDoc
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
}
/**
* @inheritDoc
* @throws CoreException
* @throws Exception
*/
@@ -1361,9 +1347,7 @@ class ShortcutMenuNode extends MenuNode
}
/**
* @param string[] $aExtraParams
* @return string
* @throws CoreException
* @inheritDoc
*/
public function GetHyperlink($aExtraParams)
{
@@ -1381,10 +1365,8 @@ class ShortcutMenuNode extends MenuNode
}
/**
* @param WebPage $oPage
* @param string[] $aExtraParams
* @return mixed|void
* @throws DictExceptionMissingString
* @inheritDoc
* @throws \Exception
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
@@ -1393,8 +1375,9 @@ class ShortcutMenuNode extends MenuNode
}
/**
* @return string
* @throws CoreException
* @inheritDoc
*
* @throws \Exception
*/
public function GetTitle()
{
@@ -1402,8 +1385,9 @@ class ShortcutMenuNode extends MenuNode
}
/**
* @return string
* @throws CoreException
* @inheritDoc
*
* @throws \Exception
*/
public function GetLabel()
{

View File

@@ -256,7 +256,7 @@ EOF
*/
protected function LoadTheme()
{
$sCssThemeUrl = ThemeHandler::GetDefaultThemeUrl();
$sCssThemeUrl = ThemeHandler::GetCurrentThemeUrl();
$this->add_linked_stylesheet($sCssThemeUrl);
}
}

View File

@@ -40,7 +40,7 @@ class iTopPDF extends TCPDF
*
* @uses \TCPDF::SetFont()
* @uses \iTopPDF::GetPdfFont()
* @since 2.7
* @since 2.7.0
*/
public function SetFontParams($style, $size, $fontfile='', $subset='default', $out=true)
{

View File

@@ -35,7 +35,17 @@ register_shutdown_function(function()
$sReservedMemory = null;
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR))
{
IssueLog::error($err['message']);
// Remove stack trace from MySQLException
$sMessage = $err['message'];
if (strpos($sMessage, 'MySQLException') !== false)
{
$iStackTracePos = strpos($sMessage, 'Stack trace:');
if ($iStackTracePos !== false)
{
$sMessage = substr($sMessage, 0, $iStackTracePos);
}
}
IssueLog::error($sMessage, null, $err);
if (strpos($err['message'], 'Allowed memory size of') !== false)
{
$sLimit = ini_get('memory_limit');

View File

@@ -17,8 +17,6 @@
* You should have received a copy of the GNU Affero General Public License
*/
use ScssPhp\ScssPhp\Compiler;
/**
* Class ThemeHandler
*
@@ -28,15 +26,94 @@ use ScssPhp\ScssPhp\Compiler;
class ThemeHandler
{
/**
* Return the absolute URL for the default theme CSS file
* Return default theme name and parameters
*
* @return array
* @since 2.7.0
*/
public static function GetDefaultThemeInformation()
{
return array(
'name' => 'light-grey',
'parameters' => array(
'variables' => array(),
'imports' => array(
'css-variables' => '../css/css-variables.scss',
),
'stylesheets' => array(
'jqueryui' => '../css/ui-lightness/jqueryui.scss',
'main' => '../css/light-grey.scss',
),
),
);
}
/**
* Return the ID of the theme currently defined in the config. file
*
* @return string
*/
public static function GetCurrentThemeId()
{
try
{
if (is_null(MetaModel::GetConfig()))
{
throw new CoreException('no config');
}
$sThemeId = MetaModel::GetConfig()->Get('backoffice_default_theme');
}
catch(CoreException $oCompileException)
{
// Fallback on our default theme in case the config. is not available yet
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
$sThemeId = $aDefaultTheme['name'];
}
return $sThemeId;
}
/**
* Return the absolute path of the compiled theme folder.
*
* @param string $sThemeId
*
* @return string
*/
public static function GetCompiledThemeFolderAbsolutePath($sThemeId)
{
return APPROOT.'env-'.utils::GetCurrentEnvironment().'/branding/themes/'.$sThemeId.'/';
}
/**
* Return the absolute URL for the current theme CSS file
*
* @return string
* @throws \Exception
*/
public static function GetDefaultThemeUrl()
public static function GetCurrentThemeUrl()
{
$sThemeId = MetaModel::GetConfig()->Get('backoffice_default_theme');
static::CompileTheme($sThemeId);
try
{
// Try to compile theme defined in the configuration
$sThemeId = static::GetCurrentThemeId();
static::CompileTheme($sThemeId);
}
catch(CoreException $oCompileException)
{
// Fallback on our default theme (should always be compilable) in case the previous theme doesn't exists
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
$sThemeId = $aDefaultTheme['name'];
$sDefaultThemeDirPath = static::GetCompiledThemeFolderAbsolutePath($sThemeId);
// Create our theme dir if it doesn't exist (XML theme node removed, renamed etc..)
if(!is_dir($sDefaultThemeDirPath))
{
SetupUtils::builddir($sDefaultThemeDirPath);
}
static::CompileTheme($sThemeId, $aDefaultTheme['parameters']);
}
// Return absolute url to theme compiled css
return utils::GetAbsoluteUrlModulesRoot().'/branding/themes/'.$sThemeId.'/main.css';
@@ -72,12 +149,12 @@ class ThemeHandler
$sThemeFolderPath = $sWorkingPath.'/branding/themes/'.$sThemeId.'/';
$sThemeCssPath = $sThemeFolderPath.'main.css';
// Save parameters if passed...
// Save parameters if passed... (typically during DM compilation)
if(is_array($aThemeParameters))
{
file_put_contents($sThemeFolderPath.'/theme-parameters.json', json_encode($aThemeParameters));
}
// ... Otherwise, retrieve them from compiled DM
// ... Otherwise, retrieve them from compiled DM (typically when switching current theme in the config. file)
else
{
$aThemeParameters = json_decode(@file_get_contents($sThemeFolderPath.'theme-parameters.json'), true);

View File

@@ -234,7 +234,14 @@ class privUITransactionFile
*/
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)
@@ -290,11 +297,19 @@ class privUITransactionFile
* Cleanup old transactions which have been pending since more than 24 hours
* Use filemtime instead of filectime since filectime may be affected by operations on the directory (like changing the access rights)
*/
protected static function CleanupOldTransactions()
protected static function CleanupOldTransactions($sTransactionDir = null)
{
$iLimit = time() - 24*3600;
$iThreshold = (int) MetaModel::GetConfig()->Get('transactions_gc_threshold');
$iThreshold = min(100, $iThreshold);
$iThreshold = max(1, $iThreshold);
if ((100 != $iThreshold) && (rand(1, 100) > $iThreshold)) {
return;
}
clearstatcache();
$aTransactions = glob(APPROOT.'data/transactions/*-*');
$iLimit = time() - 24*3600;
$sPattern = $sTransactionDir ? "$sTransactionDir/*" : APPROOT.'data/transactions/*';
$aTransactions = glob($sPattern);
foreach($aTransactions as $sFileName)
{
if (filemtime($sFileName) < $iLimit)

View File

@@ -636,15 +636,22 @@ HTML
$oSet->SetShowObsoleteData(utils::ShowObsoleteData());
$sHKAttCode = MetaModel::IsHierarchicalClass($this->sTargetClass);
$this->DumpTree($oPage, $oSet, $sHKAttCode, $currValue);
$bHasChildLeafs = $this->DumpTree($oPage, $oSet, $sHKAttCode, $currValue);
$oPage->add('</td></tr></table>');
$oPage->add('</div>');
if ($bHasChildLeafs)
{
$oPage->add('<div class="treecontrol" id="treecontrolid"><a href="?#">'.Dict::S("UI:Treeview:CollapseAll").'</a> | <a href="?#">'.Dict::S("UI:Treeview:ExpandAll").'</a></div>');
}
$oPage->add("<input type=\"button\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_tree_{$this->iId}').dialog('close');\">&nbsp;&nbsp;");
$oPage->add("<input type=\"button\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\" onClick=\"oACWidget_{$this->iId}.DoHKOk();\">");
$oPage->add('</div></div>');
$oPage->add_ready_script("\$('#tree_$this->iId ul').treeview();\n");
$oPage->add_ready_script("\$('#tree_$this->iId ul').treeview({ control: '#treecontrolid', persist: 'false'});\n");
$oPage->add_ready_script("\$('#dlg_tree_$this->iId').dialog({ width: 'auto', height: 'auto', autoOpen: true, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.OnHKResize, close: oACWidget_{$this->iId}.OnHKClose });\n");
}
@@ -673,6 +680,18 @@ HTML
}
}
/**
* @param WebPage $oP
* @param \DBObjectSet $oSet
* @param string $sParentAttCode
* @param string $currValue
*
* @return bool true if there are at least one child leaf, false if only roots nodes are present
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
function DumpTree($oP, $oSet, $sParentAttCode, $currValue)
{
$aTree = array();
@@ -701,6 +720,9 @@ HTML
{
$this->DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue);
}
$bHasOnlyRootNodes = (count($aTree) === 1);
return !$bHasOnlyRootNodes;
}
function DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue)
@@ -728,7 +750,7 @@ HTML
$sSelect = '<input id="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'" type="radio" value="'.$aNodes[$id]->GetKey().'" name="selectObject" '.$sChecked.'>&nbsp;';
}
}
$oP->add('<li>'.$sSelect.'<label for="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'">'.$aNodes[$id]->GetName().'</label>');
$oP->add('<li class="closed">'.$sSelect.'<label for="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'">'.$aNodes[$id]->GetName().'</label>');
$this->DumpNodes($oP, $id, $aTree, $aNodes, $currValue);
$oP->add("</li>\n");
}

View File

@@ -34,7 +34,7 @@ require_once(APPROOT.'/core/userrights.class.inc.php');
*/
class appUserPreferences extends DBObject
{
static $oUserPrefs = null; // Local cache
private static $oUserPrefs = null; // Local cache
/**
* Get the value of the given property/preference
@@ -43,7 +43,7 @@ class appUserPreferences extends DBObject
* @param string $sDefaultValue The default value
* @return string The value of the property for the current user
*/
static function GetPref($sCode, $sDefaultValue)
public static function GetPref($sCode, $sDefaultValue)
{
if (self::$oUserPrefs == null)
{
@@ -65,7 +65,7 @@ class appUserPreferences extends DBObject
* @param string $sCode Code/Name of the property/preference to set
* @param string $sValue Value to set
*/
static function SetPref($sCode, $sValue)
public static function SetPref($sCode, $sValue)
{
if (self::$oUserPrefs == null)
{
@@ -83,13 +83,13 @@ class appUserPreferences extends DBObject
self::Save();
}
}
/**
* Clears the value for a given preference (or list of preferences that matches a pattern), and updates the database
* @param string $sPattern Code/Pattern of the properties/preferences to reset
* @param string $sCodeOrPattern Code/Pattern of the properties/preferences to reset
* @param boolean $bPattern Whether or not the supplied code is a PCRE pattern
*/
static function UnsetPref($sCodeOrPattern, $bPattern = false)
public static function UnsetPref($sCodeOrPattern, $bPattern = false)
{
if (self::$oUserPrefs == null)
{
@@ -124,7 +124,7 @@ class appUserPreferences extends DBObject
* Call this function to get all the preferences for the user, packed as a JSON object
* @return string JSON representation of the preferences
*/
static function GetAsJSON()
public static function GetAsJSON()
{
if (self::$oUserPrefs == null)
{
@@ -137,19 +137,19 @@ class appUserPreferences extends DBObject
/**
* Call this function if the user has changed (like when doing a logoff...)
*/
static public function ResetPreferences()
public static function ResetPreferences()
{
self::$oUserPrefs = null;
}
/**
* Call this function to ERASE all the preferences from the current user
*/
static public function ClearPreferences()
public static function ClearPreferences()
{
self::$oUserPrefs = null;
}
static protected function Save()
protected static function Save()
{
if (self::$oUserPrefs != null)
{
@@ -166,7 +166,7 @@ class appUserPreferences extends DBObject
* Loads the preferences for the current user, creating the record in the database
* if needed
*/
static protected function Load()
protected static function Load()
{
if (self::$oUserPrefs != null) return;
$oSearch = new DBObjectSearch('appUserPreferences');

View File

@@ -275,13 +275,14 @@ class utils
/**
* @param string|string[] $value
* @param string $sSanitizationFilter one of : integer, class, string, context_param, parameter, field_name,
* transaction_id, parameter, raw_data
* element_identifier, 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
* @since 2.7.0 new 'element_identifier' filter
*/
protected static function Sanitize_Internal($value, $sSanitizationFilter)
{
@@ -306,6 +307,7 @@ class utils
case 'context_param':
case 'parameter':
case 'field_name':
case 'transaction_id':
if (is_array($value))
{
$retValue = array();
@@ -351,6 +353,11 @@ class utils
}
break;
// For XML / HTML node identifiers
case 'element_identifier':
$retValue = preg_replace('/[^a-zA-Z0-9_]/', '', $value);
break;
default:
case 'raw_data':
$retValue = $value;
@@ -485,6 +492,18 @@ class utils
// Paginated selection
$aSelectedIds = utils::ReadParam('storedSelection', array());
$aSelectedObjIds = utils::ReadParam('selectObject', array());
//it means that the user has selected all the results of the search query
if (count($aSelectedObjIds) > 0 )
{
$sFilter=utils::ReadParam("sFilter",'',false,'raw_data');
if ($sFilter!='')
{
$oFullSetFilter=DBSearch::unserialize($sFilter);
}
}
if (count($aSelectedIds) > 0 )
{
if ($sSelectionMode == 'positive')
@@ -544,48 +563,93 @@ class utils
public static function ReadFromFile($sFileName)
{
if (!file_exists($sFileName)) return false;
if (!file_exists($sFileName)) {
return false;
}
return file_get_contents($sFileName);
}
/**
* Helper function to convert a value expressed in a 'user friendly format'
* as in php.ini, e.g. 256k, 2M, 1G etc. Into a number of bytes
* @param mixed $value The value as read from php.ini
* @return number
* @param mixed $value The value as read from php.ini (eg 256k, 2M, 1G etc.)
*
* @return int conversion to number of bytes
*
* @since 2.7.5 3.0.0 convert to int numeric values
*
* @link https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes Shorthand bytes value reference in PHP.net FAQ
*/
public static function ConvertToBytes( $value )
public static function ConvertToBytes($value)
{
$iReturn = $value;
if ( !is_numeric( $value ) )
{
$iLength = strlen( $value );
$iReturn = substr( $value, 0, $iLength - 1 );
$sUnit = strtoupper( substr( $value, $iLength - 1 ) );
switch ( $sUnit )
{
case 'G':
$iReturn *= 1024;
case 'M':
$iReturn *= 1024;
case 'K':
$iReturn *= 1024;
}
}
return $iReturn;
}
/**
* Checks if the memory limit is at least what is required
*
* @param int $memoryLimit set limit in bytes
* @param int $requiredLimit required limit in bytes
* @return bool
*/
public static function IsMemoryLimitOk($memoryLimit, $requiredLimit)
{
return ($memoryLimit >= $requiredLimit) || ($memoryLimit == -1);
}
if (!is_numeric($value)) {
$iLength = strlen($value);
$iReturn = substr($value, 0, $iLength - 1);
$sUnit = strtoupper(substr($value, $iLength - 1));
switch ($sUnit) {
case 'G':
$iReturn *= 1024;
case 'M':
$iReturn *= 1024;
case 'K':
$iReturn *= 1024;
}
} else {
$iReturn = (int)$value;
}
return $iReturn;
}
/**
* Checks if the memory limit is at least what is required
*
* @param int $iMemoryLimit set limit in bytes, use {@link utils::ConvertToBytes()} to convert current php.ini value
* @param int $iRequiredLimit required limit in bytes
*
* @return bool
*/
public static function IsMemoryLimitOk($iMemoryLimit, $iRequiredLimit)
{
if ($iMemoryLimit === -1) {
// -1 means : no limit (see https://www.php.net/manual/fr/ini.core.php#ini.memory-limit)
return true;
}
return ($iMemoryLimit >= $iRequiredLimit);
}
/**
* Set memory_limit to required value
*
* @param string $sRequiredLimit required limit, for example '512M'
*
* @return bool|null null if nothing was done, true if modifying memory_limit was successful, false otherwise
*
* @uses utils::ConvertToBytes()
* @uses \ini_get('memory_limit')
* @uses \ini_set()
* @uses utils::ConvertToBytes()
*
* @since 2.7.5 N°3806
*/
public static function SetMinMemoryLimit($sRequiredLimit)
{
$iRequiredLimit = static::ConvertToBytes($sRequiredLimit);
$sMemoryLimit = trim(ini_get('memory_limit'));
if (empty($sMemoryLimit)) {
// On some PHP installations, memory_limit does not exist as a PHP setting!
// (encountered on a 5.2.0 under Windows)
// In that case, ini_set will not work
return false;
}
$iMemoryLimit = static::ConvertToBytes($sMemoryLimit);
if (static::IsMemoryLimitOk($iMemoryLimit, $iRequiredLimit)) {
return null;
}
return ini_set('memory_limit', $iRequiredLimit);
}
/**
* Format a value into a more friendly format (KB, MB, GB, TB) instead a juste a Bytes amount.
@@ -714,7 +778,7 @@ class utils
* @throws \ConfigException
* @throws \CoreException
*
* @since 2.7.0 N°2478 always call {@link MetaModel::GetConfig} first, cache is only set when loading from disk
* @since 2.7.0 N°2478 this method will now always call {@link MetaModel::GetConfig} first, and cache in this class is only set when loading from disk
*/
public static function GetConfig()
{
@@ -763,22 +827,42 @@ class utils
}
}
/**
* @return bool The boolean value of the conf. "behind_reverse_proxy" (except if there is no REMOTE_ADDR int his case, it return false)
*
* @since 2.7.4
*/
public static function IsProxyTrusted()
{
if (empty($_SERVER['REMOTE_ADDR'])) {
return false;
}
$bTrustProxies = (bool) self::GetConfig()->Get('behind_reverse_proxy');
return $bTrustProxies;
}
/**
* Returns the absolute URL to the application root path
*
* @param bool $bForceTrustProxy
*
* @return string The absolute URL to the application root, without the first slash
*
* @throws \Exception
*
* @since 2.7.4 $bForceTrustProxy param added
*/
public static function GetAbsoluteUrlAppRoot()
public static function GetAbsoluteUrlAppRoot($bForceTrustProxy = false)
{
static $sUrl = null;
if ($sUrl === null)
if ($sUrl === null || $bForceTrustProxy)
{
$sUrl = self::GetConfig()->Get('app_root_url');
if ($sUrl == '')
{
$sUrl = self::GetDefaultUrlAppRoot();
$sUrl = self::GetDefaultUrlAppRoot($bForceTrustProxy);
}
elseif (strpos($sUrl, SERVER_NAME_PLACEHOLDER) > -1)
{
@@ -802,31 +886,116 @@ class utils
* For most usages, when an root url is needed, use utils::GetAbsoluteUrlAppRoot() instead as uses this only as a fallback when the
* app_root_url conf parameter is not defined.
*
* @param bool $bForceTrustProxy
*
* @return string
*
* @throws \Exception
*/
public static function GetDefaultUrlAppRoot()
*
* @since 2.7.4 $bForceTrustProxy param added
*/
public static function GetDefaultUrlAppRoot($bForceTrustProxy = false)
{
$sAbsoluteUrl = self::GetCurrentAbsoluteUrl($bForceTrustProxy, true);
$sCurrentScript = realpath($_SERVER['SCRIPT_FILENAME']);
$sAppRoot = realpath(APPROOT);
return self::GetAppRootUrl($sCurrentScript, $sAppRoot, $sAbsoluteUrl);
}
/**
* Build the current absolute URL from the server's variables.
*
* For almost every usage, you should use the more secure utils::GetAbsoluteUrlAppRoot() : instead of reading the current uri, it provide you the configured application's root URL (this is done during the setup and chn be changed in the configuration file)
*
* @see utils::GetAbsoluteUrlAppRoot
*
* @param bool $bForceTrustProxy
* @param bool $bTrimQueryString
*
* @return string
*
* @since 2.7.4
*/
public static function GetCurrentAbsoluteUrl($bForceTrustProxy = false, $bTrimQueryString = false)
{
// Build an absolute URL to this page on this server/port
$sServerName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
$sProtocol = self::IsConnectionSecure() ? 'https' : 'http';
$iPort = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80;
if ($sProtocol == 'http')
{
$sServerName = self::GetServerName($bForceTrustProxy);
$bIsSecure = self::IsConnectionSecure($bForceTrustProxy);
$sProtocol = $bIsSecure ? 'https' : 'http';
$iPort = self::GetServerPort($bForceTrustProxy);
if ($bIsSecure) {
$sPort = ($iPort == 443) ? '' : ':'.$iPort;
} else {
$sPort = ($iPort == 80) ? '' : ':'.$iPort;
}
else
{
$sPort = ($iPort == 443) ? '' : ':'.$iPort;
$sPath = self::GetRequestUri($bForceTrustProxy);
if ($bTrimQueryString) {
// remove all the parameters from the query string
$iQuestionMarkPos = strpos($sPath, '?');
if ($iQuestionMarkPos !== false) {
$sPath = substr($sPath, 0, $iQuestionMarkPos);
}
}
$sAbsoluteUrl = "$sProtocol://{$sServerName}{$sPort}{$sPath}";
return $sAbsoluteUrl;
}
/**
* @param bool $bForceTrustProxy
*
* @return string
*
* @since 2.7.4
*/
public static function GetServerName($bForceTrustProxy = false)
{
$bTrustProxy = $bForceTrustProxy || self::IsProxyTrusted();
$sServerName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
if ($bTrustProxy) {
$sServerName = isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : $sServerName;
}
return $sServerName;
}
/**
* @param bool $bForceTrustProxy
*
* @return int|mixed
* @since 2.7.4
*/
public static function GetServerPort($bForceTrustProxy = false)
{
$bTrustProxy = $bForceTrustProxy || self::IsProxyTrusted();
$sServerPort = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80;
if ($bTrustProxy) {
$sServerPort = isset($_SERVER['HTTP_X_FORWARDED_PORT']) ? $_SERVER['HTTP_X_FORWARDED_PORT'] : $sServerPort;
}
return $sServerPort;
}
/**
* @return string
*
* @since 2.7.4
*/
public static function GetRequestUri()
{
// $_SERVER['REQUEST_URI'] is empty when running on IIS
// Let's use Ivan Tcholakov's fix (found on www.dokeos.com)
if (!empty($_SERVER['REQUEST_URI']))
{
$sPath = $_SERVER['REQUEST_URI'];
}
else
if (empty($_SERVER['REQUEST_URI']))
{
$sPath = $_SERVER['SCRIPT_NAME'];
if (!empty($_SERVER['QUERY_STRING']))
@@ -837,18 +1006,7 @@ class utils
}
$sPath = $_SERVER['REQUEST_URI'];
// remove all the parameters from the query string
$iQuestionMarkPos = strpos($sPath, '?');
if ($iQuestionMarkPos !== false)
{
$sPath = substr($sPath, 0, $iQuestionMarkPos);
}
$sAbsoluteUrl = "$sProtocol://{$sServerName}{$sPort}{$sPath}";
$sCurrentScript = realpath($_SERVER['SCRIPT_FILENAME']);
$sAppRoot = realpath(APPROOT);
return self::GetAppRootUrl($sCurrentScript, $sAppRoot, $sAbsoluteUrl);
return $sPath;
}
/**
@@ -892,19 +1050,36 @@ class utils
/**
* Helper to handle the variety of HTTP servers
* See N°286 (fixed in [896]), and N°634 (this fix)
*
*
* Though the official specs says 'a non empty string', some servers like IIS do set it to 'off' !
* nginx set it to an empty string
* Others might leave it unset (no array entry)
*/
public static function IsConnectionSecure()
* Others might leave it unset (no array entry)
*
* @param bool $bForceTrustProxy
*
* @return bool
*
* @since 2.7.4 reverse proxies handling
*/
public static function IsConnectionSecure($bForceTrustProxy = false)
{
$bSecured = false;
if (!empty($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off'))
$bTrustProxy = $bForceTrustProxy || self::IsProxyTrusted();
if ($bTrustProxy && !empty($_SERVER['HTTP_X_FORWARDED_PROTO']))
{
$bSecured = ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
}
elseif ($bTrustProxy && !empty($_SERVER['HTTP_X_FORWARDED_PROTOCOL']))
{
$bSecured = ($_SERVER['HTTP_X_FORWARDED_PROTOCOL'] === 'https');
}
elseif ((!empty($_SERVER['HTTPS'])) && (strtolower($_SERVER['HTTPS']) != 'off'))
{
$bSecured = true;
}
return $bSecured;
}
@@ -1505,6 +1680,17 @@ class utils
public static function HtmlEntities($sValue)
{
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');
}
/**
@@ -2059,6 +2245,41 @@ class utils
return COMPILATION_TIMESTAMP;
}
/**
* @return string eg : '2_7_0' ITOP_VERSION is '2.7.1-dev'
*/
public static function GetItopVersionWikiSyntax()
{
$sMinorVersion = self::GetItopMinorVersion();
return str_replace('.', '_', $sMinorVersion).'_0';
}
/**
* @return string eg 2.7 if ITOP_VERSION is '2.7.0-dev'
* @throws \Exception
*/
public static function GetItopMinorVersion()
{
$sPatchVersion = self::GetItopPatchVersion();
$aExplodedVersion = explode('.', $sPatchVersion);
if (empty($aExplodedVersion[0]) || empty($aExplodedVersion[1]))
{
throw new Exception('iTop version is wrongfully configured!');
}
return sprintf('%d.%d', $aExplodedVersion[0], $aExplodedVersion[1]);
}
/**
* @return string eg '2.7.0' if ITOP_VERSION is '2.7.0-dev'
*/
public static function GetItopPatchVersion()
{
$aExplodedVersion = explode('-', ITOP_VERSION);
return $aExplodedVersion[0];
}
/**
* Check if the given class if configured as a high cardinality class.
*
@@ -2156,16 +2377,22 @@ class utils
}
/**
* @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/'
* @param string $sPath for example `/var/www/html/itop/data/backups/manual/itop_27-2019-10-03_15_35.tar.gz`
* **Warning**, if path is a symlink, it will be resolved !
* So `C:\Dev\wamp64\www\itop-dev/env-production/itop-hub-connector/land.php`
* Will become `C:\Dev\wamp64\www\itop-dev\datamodels\2.x\itop-hub-connector\land.php`
* @param string $sBasePath for example `/var/www/html/itop/data/`
*
* @return bool false if path :
* @return bool|string false if path :
* * invalid
* * not allowed
* * not contained in base path
* Otherwise return the real path (see realpath())
*
* @since 2.7.0 N°2538
* @uses \realpath()
* @uses static::StartsWith
* @since 2.6.5 2.7.0 N°2538
* @since 2.7.5 details in PHPDoc about symlink resolution
*/
final public static function RealPath($sPath, $sBasePath)
{
@@ -2250,4 +2477,35 @@ class utils
{
return str_replace(' ', '', ucwords(strtr($sInput, '_-', ' ')));
}
/**
* @param \cmdbAbstractObject $oCmdbAbstract
* @param \Exception $oException
*
* @throws \Exception
* @since 2.7.2/ 2.8.0
*/
public static function EnrichRaisedException($oCmdbAbstract, $oException)
{
if (is_null($oCmdbAbstract) ||
! is_a($oCmdbAbstract, \cmdbAbstractObject::class))
{
throw $oException;
}
$sCmdbAbstractInfo = str_replace("\n", '', "" . $oCmdbAbstract);
$sMessage = $oException->getMessage() . " (" . $sCmdbAbstractInfo . ")";
$e = new CoreException($sMessage, null, '', $oException);
throw $e;
}
/**
* @return bool : indicate whether we run under a windows environnement or not
* @since 2.7.4 : N°3412
*/
public static function IsWindowsEnvironment(){
return (substr(PHP_OS,0,3) === 'WIN');
}
}

View File

@@ -17,6 +17,8 @@
* You should have received a copy of the GNU Affero General Public License
*/
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
/**
* Generic interface common to CLI and Web pages
*/
@@ -120,6 +122,7 @@ class WebPage implements Page
protected $a_OutputOptions;
protected $bPrintable;
protected $bHasCollapsibleSection;
protected $bAddJSDict;
/**
* WebPage constructor.
@@ -150,6 +153,7 @@ class WebPage implements Page
$this->a_OutputOptions = array();
$this->bHasCollapsibleSection = false;
$this->bPrintable = $bPrintable;
$this->bAddJSDict = true;
ob_start(); // Start capturing the output
}
@@ -183,6 +187,21 @@ class WebPage implements Page
$this->s_content .= $s_html;
}
/**
* Add any rendered text or HTML fragment to the body of the page using a twig template
*
* @param string $sViewPath Absolute path of the templates folder
* @param string $sTemplateName Name of the twig template, ie MyTemplate for MyTemplate.html.twig
* @param array $aParams Params used by the twig template
* @param string $sDefaultType default type of the template ('html', 'xml', ...)
*
* @throws \Exception
*/
public function add_twig_template($sViewPath, $sTemplateName, $aParams = array(), $sDefaultType = 'html')
{
TwigHelper::RenderIntoPage($this, $sViewPath, $sTemplateName, $aParams, $sDefaultType);
}
/**
* Add any text or HTML fragment (identified by an ID) at the end of the body of the page
* This is useful to add hidden content, DIVs or FORMs that should not
@@ -463,13 +482,31 @@ class WebPage implements Page
$this->a_headers[] = $s_header;
}
/**
* @param string|null $sHeaderValue for example `SAMESITE`. If null will set the header using the config parameter value.
*
* @since 2.7.3 3.0.0 N°3416
* @uses security_header_xframe config parameter
* @uses \utils::GetConfig()
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
*/
public function add_xframe_options($sHeaderValue = null)
{
if (is_null($sHeaderValue)) {
$sHeaderValue = utils::GetConfig()->Get('security_header_xframe');
}
$this->add_header('X-Frame-Options: '.$sHeaderValue);
}
/**
* Add needed headers to the page so that it will no be cached
*/
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');
}
/**
@@ -528,16 +565,37 @@ class WebPage implements Page
*/
public function GetDetails($aFields)
{
$aPossibleAttFlags = MetaModel::EnumPossibleAttributeFlags();
$sHtml = "<div class=\"details\">\n";
foreach ($aFields as $aAttrib)
{
$sLayout = isset($aAttrib['layout']) ? $aAttrib['layout'] : 'small';
// Prepare metadata attributes
$sDataAttributeCode = isset($aAttrib['attcode']) ? 'data-attribute-code="'.$aAttrib['attcode'].'"' : '';
$sDataAttributeType = isset($aAttrib['atttype']) ? 'data-attribute-type="'.$aAttrib['atttype'].'"' : '';
$sDataAttributeLabel = isset($aAttrib['attlabel']) ? 'data-attribute-label="'.utils::HtmlEntities($aAttrib['attlabel']).'"' : '';
// - Attribute flags
$sDataAttributeFlags = '';
if(isset($aAttrib['attflags']))
{
foreach($aPossibleAttFlags as $sFlagCode => $iFlagValue)
{
// Note: Skip normal flag as we don't need it.
if($sFlagCode === 'normal')
{
continue;
}
$sFormattedFlagCode = str_ireplace('_', '-', $sFlagCode);
$sFormattedFlagValue = (($aAttrib['attflags'] & $iFlagValue) === $iFlagValue) ? 'true' : 'false';
$sDataAttributeFlags .= 'data-attribute-flag-'.$sFormattedFlagCode.'="'.$sFormattedFlagValue.'" ';
}
}
// - Value raw
$sDataValueRaw = isset($aAttrib['value_raw']) ? 'data-value-raw="'.utils::HtmlEntities($aAttrib['value_raw']).'"' : '';
$sHtml .= "<div class=\"field_container field_{$sLayout}\" $sDataAttributeCode $sDataAttributeType $sDataAttributeLabel $sDataValueRaw>\n";
$sHtml .= "<div class=\"field_container field_{$sLayout}\" $sDataAttributeCode $sDataAttributeType $sDataAttributeLabel $sDataAttributeFlags $sDataValueRaw>\n";
$sHtml .= "<div class=\"field_label label\">{$aAttrib['label']}</div>\n";
$sHtml .= "<div class=\"field_data\">\n";
@@ -736,7 +794,10 @@ class WebPage implements Page
}
// Dict entries for JS
$this->output_dict_entries();
if ($this->bAddJSDict)
{
$this->output_dict_entries();
}
// JS files
foreach ($this->a_linked_scripts as $s_script)
@@ -1503,6 +1564,12 @@ class TabManager
{
unset($aTabs['tabs'][$sTabCode]);
}
// N°3320: Do not display empty tabs
if (empty($aTabData['html']) && empty($aTabData['url']))
{
unset($aTabs['tabs'][$sTabCode]);
}
}
// Render tabs
@@ -1541,7 +1608,7 @@ EOF
$sTabs .= "<div class=\"printable-tab\" id=\"$sTabId\"><h2 class=\"printable-tab-title\">$sTabTitleForHtml</h2><div class=\"printable-tab-content\">".$sTabHtml."</div></div>\n";
$oPage->add_ready_script(
<<< EOF
oHiddeableChapters['$sTabId'] = '$sTabCodeForJs';
oHiddeableChapters['$sTabId'] = '$sTabTitleForHtml';
EOF
);
$i++;

View File

@@ -43,9 +43,10 @@ class XMLPage extends WebPage
$this->m_bPassThrough = $bPassThrough;
$this->m_bHeaderSent = false;
$this->add_header("Content-type: text/xml; charset=".self::PAGES_CHARSET);
$this->add_header("Cache-control: no-cache");
$this->no_cache();
$this->add_xframe_options();
$this->add_header("Content-location: export.xml");
}
}
public function output()
{

View File

@@ -10,9 +10,9 @@
"ext-json": "*",
"ext-mysqli": "*",
"ext-soap": "*",
"combodo/tcpdf": "6.3.2",
"combodo/tcpdf": "6.3.5",
"nikic/php-parser": "^3.1",
"pear/archive_tar": "1.4.9",
"pear/archive_tar": "1.4.13",
"pelago/emogrifier": "2.1.0",
"scssphp/scssphp": "1.0.6",
"swiftmailer/swiftmailer": "5.4.12",
@@ -50,7 +50,9 @@
"classmap": [
"core",
"application",
"sources/application"
"sources/application",
"sources/Composer",
"sources/Controller"
],
"exclude-from-classmap": [
"core/dbobjectsearch.class.php",
@@ -74,5 +76,10 @@
"allow-contrib": false,
"require": "3.4.*"
}
},
"scripts": {
"post-install-cmd": ["@rmDeniedTestDir"],
"post-update-cmd": ["@rmDeniedTestDir"],
"rmDeniedTestDir": "@php .make/composer/rmDeniedTestDir.php"
}
}

46
composer.lock generated
View File

@@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "3e413c47265b246174add07c2c91b5e9",
"content-hash": "8c7f3127435b1afb67965369c50d0898",
"packages": [
{
"name": "combodo/tcpdf",
"version": "6.3.2",
"version": "6.3.5",
"source": {
"type": "git",
"url": "https://github.com/combodo-itop-libs/TCPDF.git",
"reference": "2723050de47c8cbd78293656d896c0000442e23a"
"reference": "aedd4b7b8cf7fcc24e617c405c9d3304150f4b94"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/combodo-itop-libs/TCPDF/zipball/2723050de47c8cbd78293656d896c0000442e23a",
"reference": "2723050de47c8cbd78293656d896c0000442e23a",
"url": "https://api.github.com/repos/combodo-itop-libs/TCPDF/zipball/aedd4b7b8cf7fcc24e617c405c9d3304150f4b94",
"reference": "aedd4b7b8cf7fcc24e617c405c9d3304150f4b94",
"shasum": ""
},
"require": {
@@ -49,9 +49,14 @@
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0"
"LGPL-3.0-only"
],
"authors": [
{
"name": "Nicola Asuni",
"email": "info@tecnick.com",
"role": "lead"
},
{
"name": "Combodo",
"email": "contact@combodo.com"
@@ -59,7 +64,7 @@
],
"description": "TCPDF fork adding requirements for iTop: Specific fonts.",
"homepage": "https://github.com/combodo-itop-libs/TCPDF",
"time": "2020-01-08T16:22:40+00:00"
"time": "2020-09-28T12:19:09+00:00"
},
{
"name": "nikic/php-parser",
@@ -163,16 +168,16 @@
},
{
"name": "pear/archive_tar",
"version": "1.4.9",
"version": "1.4.13",
"source": {
"type": "git",
"url": "https://github.com/pear/Archive_Tar.git",
"reference": "c5b00053770e1d72128252c62c2c1a12c26639f0"
"reference": "2b87b41178cc6d4ad3cba678a46a1cae49786011"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pear/Archive_Tar/zipball/c5b00053770e1d72128252c62c2c1a12c26639f0",
"reference": "c5b00053770e1d72128252c62c2c1a12c26639f0",
"url": "https://api.github.com/repos/pear/Archive_Tar/zipball/2b87b41178cc6d4ad3cba678a46a1cae49786011",
"reference": "2b87b41178cc6d4ad3cba678a46a1cae49786011",
"shasum": ""
},
"require": {
@@ -225,7 +230,21 @@
"archive",
"tar"
],
"time": "2019-12-04T10:17:28+00:00"
"support": {
"issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Archive_Tar",
"source": "https://github.com/pear/Archive_Tar"
},
"funding": [
{
"url": "https://github.com/mrook",
"type": "github"
},
{
"url": "https://www.patreon.com/michielrook",
"type": "patreon"
}
],
"time": "2021-02-16T10:50:50+00:00"
},
{
"name": "pear/console_getopt",
@@ -2585,5 +2604,6 @@
"platform-dev": [],
"platform-overrides": {
"php": "5.6.0"
}
},
"plugin-api-version": "2.0.0"
}

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

@@ -18,6 +18,7 @@
*/
use Combodo\iTop\Form\Field\LabelField;
use Combodo\iTop\Form\Field\TextAreaField;
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
use Combodo\iTop\Form\Validator\Validator;
@@ -102,6 +103,14 @@ define('LINKSET_EDITMODE_ACTIONS', 2); // Show the usual 'Actions' popup menu
define('LINKSET_EDITMODE_INPLACE', 3); // The "linked" objects can be created/modified/deleted in place
define('LINKSET_EDITMODE_ADDREMOVE', 4); // The "linked" objects can be added/removed in place
/**
* Attributes implementing this interface won't be accepted as `group by` field
* @since 2.7.4 N°3473
*/
interface iAttributeNoGroupBy
{
//no method, just a contract on implement
}
/**
* Attribute definition API, implemented in and many flavours (Int, String, Enum, etc.)
@@ -156,7 +165,7 @@ abstract class AttributeDefinition
*/
public function IsSearchable()
{
return static::SEARCH_WIDGET_TYPE != static::SEARCH_WIDGET_TYPE_RAW;
return $this->GetSearchType() != static::SEARCH_WIDGET_TYPE_RAW;
}
/** @var string */
@@ -1033,6 +1042,20 @@ abstract class AttributeDefinition
$oFormField->AddMetadata('attribute-code', $this->GetCode());
$oFormField->AddMetadata('attribute-type', get_class($this));
$oFormField->AddMetadata('attribute-label', utils::HtmlEntities($this->GetLabel()));
// - Attribute flags
$aPossibleAttFlags = MetaModel::EnumPossibleAttributeFlags();
foreach($aPossibleAttFlags as $sFlagCode => $iFlagValue)
{
// Note: Skip normal flag as we don't need it.
if($sFlagCode === 'normal')
{
continue;
}
$sFormattedFlagCode = str_ireplace('_', '-', $sFlagCode);
$sFormattedFlagValue = (($iFlags & $iFlagValue) === $iFlagValue) ? 'true' : 'false';
$oFormField->AddMetadata('attribute-flag-'.$sFormattedFlagCode, $sFormattedFlagValue);
}
// - Value raw
if ($this::IsScalar())
{
$oFormField->AddMetadata('value-raw', utils::HtmlEntities($oObject->Get($this->GetCode())));
@@ -3726,7 +3749,7 @@ class AttributeFinalClass extends AttributeString
/**
* @return bool
* @since 2.7
* @since 2.7.0 N°2272 OQL perf finalclass in all intermediary tables
*/
public function CopyOnAllTables()
{
@@ -3746,7 +3769,7 @@ class AttributeFinalClass extends AttributeString
*
* @package iTopORM
*/
class AttributePassword extends AttributeString
class AttributePassword extends AttributeString implements iAttributeNoGroupBy
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
@@ -3822,7 +3845,7 @@ class AttributePassword extends AttributeString
*
* @package iTopORM
*/
class AttributeEncryptedString extends AttributeString
class AttributeEncryptedString extends AttributeString implements iAttributeNoGroupBy
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
@@ -4380,7 +4403,7 @@ class AttributeLongText extends AttributeText
{
// Is there a way to know the current limitation for mysql?
// See mysql_field_len()
return 65535 * 1024; // Limited... still 64 Mb!
return 65535 * 1024; // Limited... still 64 MB!
}
}
@@ -6884,7 +6907,7 @@ class AttributeHierarchicalKey extends AttributeExternalKey
if ($oFilter)
{
$oValSetDef = $this->GetValuesDef();
$oValSetDef->AddCondition($oFilter);
$oValSetDef->SetCondition($oFilter);
return $oValSetDef->GetValues($aArgs, $sContains);
}
@@ -6900,7 +6923,7 @@ class AttributeHierarchicalKey extends AttributeExternalKey
$oFilter = $this->GetHierachicalFilter($aArgs, $sContains, $iAdditionalValue);
if ($oFilter)
{
$oValSetDef->AddCondition($oFilter);
$oValSetDef->SetCondition($oFilter);
}
$oSet = $oValSetDef->ToObjectSet($aArgs, $sContains, $iAdditionalValue);
@@ -7001,6 +7024,15 @@ class AttributeExternalField extends AttributeDefinition
return self::SEARCH_WIDGET_TYPE_RAW;
}
function IsSearchable()
{
if ($this->IsFriendlyName())
{
return true;
}
return parent::IsSearchable();
}
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("extkey_attcode", "target_attcode"));
@@ -7426,6 +7458,12 @@ class AttributeExternalField extends AttributeDefinition
}
}
parent::MakeFormField($oObject, $oFormField);
if ($oFormField instanceof TextAreaField) {
if (method_exists($oRemoteAttDef, 'GetFormat')) {
/** @var \Combodo\iTop\Form\Field\TextAreaField $oFormField */
$oFormField->SetFormat($oRemoteAttDef->GetFormat());
}
}
// Manually setting for remote ExternalKey, otherwise, the id would be displayed.
if ($oRemoteAttDef instanceof AttributeExternalKey)
@@ -7443,6 +7481,16 @@ class AttributeExternalField extends AttributeDefinition
{
return false;
}
public function GetFormat()
{
$oRemoteAttDef = $this->GetExtAttDef();
if (method_exists($oRemoteAttDef, 'GetFormat')) {
/** @var \Combodo\iTop\Form\Field\TextAreaField $oFormField */
return $oRemoteAttDef->GetFormat();
}
return 'text';
}
}
@@ -7692,10 +7740,17 @@ class AttributeBlob extends AttributeDefinition
// (temporary tables created on disk)
// We will have to remove the blobs from the list of attributes when doing the select
// then the use of Get() should finalize the load
if ($value instanceOf ormDocument && !$value->IsEmpty())
if ($value instanceOf ormDocument)
{
$aValues = array();
$aValues[$this->GetCode().'_data'] = $value->GetData();
if (!$value->IsEmpty())
{
$aValues[$this->GetCode().'_data'] = $value->GetData();
}
else
{
$aValues[$this->GetCode().'_data'] = '';
}
$aValues[$this->GetCode().'_mimetype'] = $value->GetMimeType();
$aValues[$this->GetCode().'_filename'] = $value->GetFileName();
}
@@ -9170,7 +9225,7 @@ class AttributeSubItem extends AttributeDefinition
/**
* One way encrypted (hashed) password
*/
class AttributeOneWayPassword extends AttributeDefinition
class AttributeOneWayPassword extends AttributeDefinition implements iAttributeNoGroupBy
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
@@ -9611,7 +9666,7 @@ class AttributePropertySet extends AttributeTable
$sValue = '*****';
}
$sRes .= "<TR>";
$sCell = str_replace("\n", "<br>\n", Str::pure2html((string)$sValue));
$sCell = str_replace("\n", "<br>\n", Str::pure2html(@(string)$sValue));
$sRes .= "<TD class=\"label\">$sProperty</TD><TD>$sCell</TD>";
$sRes .= "</TR>";
}
@@ -9705,21 +9760,6 @@ abstract class AttributeSet extends AttributeDBFieldVoid
return array_merge(parent::ListExpectedParams(), array('is_null_allowed', 'max_items'));
}
/**
* Allowed values are mandatory for this attribute to be modified
*
* @param array $aArgs
* @param string $sContains
*
* @return array|null
* @throws \CoreException
* @throws \OQLException
*/
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
return parent::GetAllowedValues($aArgs, $sContains);
}
/**
* Allowed different values for the set values are mandatory for this attribute to be modified
*
@@ -9852,16 +9892,16 @@ abstract class AttributeSet extends AttributeDBFieldVoid
return 255;
}
public function FromStringToArray($proposedValue)
public function FromStringToArray($proposedValue, $sDefaultSepItem = ',')
{
$aValues = array();
if (!empty($proposedValue))
{
$sSepItem = MetaModel::GetConfig()->Get('tag_set_item_separator');
// convert also , separated strings
if ($sSepItem !== ',')
if ($sSepItem !== $sDefaultSepItem)
{
$proposedValue = str_replace(',', $sSepItem, $proposedValue);
$proposedValue = str_replace($sDefaultSepItem, $sSepItem, $proposedValue);
}
foreach(explode($sSepItem, $proposedValue) as $sCode)
{
@@ -9904,10 +9944,18 @@ abstract class AttributeSet extends AttributeDBFieldVoid
public function MakeRealValue($proposedValue, $oHostObj, $bIgnoreErrors = false)
{
$oSet = new ormSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
$aAllowedValues = $this->GetPossibleValues();
if (is_string($proposedValue) && !empty($proposedValue))
{
$proposedValue = trim("$proposedValue");
$aValues = $this->FromStringToArray($proposedValue);
foreach ($aValues as $i => $sValue)
{
if (!isset($aAllowedValues[$sValue]))
{
unset($aValues[$i]);
}
}
$oSet->SetValues($aValues);
}
elseif ($proposedValue instanceof ormSet)
@@ -10109,10 +10157,6 @@ abstract class AttributeSet extends AttributeDBFieldVoid
$aValues = $value->GetValues();
}
$sRes = implode($sSepItem, $aValues);
if (!empty($sRes))
{
$sRes = "{$sSepItem}{$sRes}{$sSepItem}";
}
}
else
{
@@ -10136,14 +10180,32 @@ abstract class AttributeSet extends AttributeDBFieldVoid
class AttributeEnumSet extends AttributeSet
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_TAG_SET;
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array('possible_values', 'is_null_allowed', 'max_items'));
}
public function GetMaxSize()
{
$aRawValues = $this->GetRawPossibleValues();
$iMaxItems = $this->GetMaxItems();
$aLengths = array();
foreach (array_keys($aRawValues) as $sKey)
{
$aLengths[] = strlen($sKey);
}
rsort($aLengths, SORT_NUMERIC);
$iMaxSize = 2;
for ($i = 0; $i < min($iMaxItems, count($aLengths)); $i++)
{
$iMaxSize += $aLengths[$i] + 1;
}
return max(255, $iMaxSize);
}
private function GetRawPossibleValues($aArgs = array(), $sContains = '')
{
/** @var ValueSetEnumPadded $oValSetDef */
$oValSetDef = $this->Get('possible_values');
if (!$oValSetDef)
{
@@ -10167,8 +10229,13 @@ class AttributeEnumSet extends AttributeSet
public function GetValueLabel($sValue)
{
if ($sValue instanceof ormSet)
{
$sValue = implode(', ', $sValue->GetValues());
}
$aValues = $this->GetRawPossibleValues();
if (isset($aValues[$sValue]))
if (is_array($aValues) && is_string($sValue) && isset($aValues[$sValue]))
{
$sValue = $aValues[$sValue];
}
@@ -10184,9 +10251,14 @@ class AttributeEnumSet extends AttributeSet
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sValue, null, true /*user lang*/);
if (is_null($sLabel))
{
$sDefault = str_replace('_', ' ', $sValue);
// Browse the hierarchy again, accepting default (english) translations
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sValue, $sDefault, false);
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sValue, null, false);
if (is_null($sLabel))
{
$sDefault = trim(str_replace('_', ' ', $sValue));
// Browse the hierarchy again, accepting default (english) translations
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sDefault, $sDefault, false);
}
}
}
@@ -10245,6 +10317,122 @@ class AttributeEnumSet extends AttributeSet
return $sRes;
}
/**
* @param ormSet $value
* @param string $sSeparator
* @param string $sTextQualifier
* @param \DBObject $oHostObject
* @param bool $bLocalize
* @param bool $bConvertToPlainText
*
* @return mixed|string
* @throws \Exception
*/
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
{
$sSepItem = MetaModel::GetConfig()->Get('tag_set_item_separator');
if (is_object($value) && ($value instanceof ormSet))
{
$aValues = $value->GetValues();
if ($bLocalize)
{
$aLocalizedValues = array();
foreach($aValues as $sValue)
{
$aLocalizedValues[] = $this->GetValueLabel($sValue);
}
$aValues = $aLocalizedValues;
}
$sRes = implode($sSepItem, $aValues);
}
else
{
$sRes = '';
}
return "{$sTextQualifier}{$sRes}{$sTextQualifier}";
}
/**
* Get the value from a given string (plain text, CSV import)
*
* @param string $sProposedValue
* @param bool $bLocalizedValue
* @param string $sSepItem
* @param string $sSepAttribute
* @param string $sSepValue
* @param string $sAttributeQualifier
*
* @return mixed null if no match could be found
* @throws \Exception
*/
public function MakeValueFromString($sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null, $sAttributeQualifier = null)
{
if ($bLocalizedValue)
{
// Lookup for the values matching the input
//
$aValues = $this->FromStringToArray($sProposedValue);
$aFoundValues = array();
$aRawValues = $this->GetPossibleValues();
foreach ($aValues as $sValue)
{
$bFound = false;
foreach ($aRawValues as $sCode => $sRawValue)
{
if ($sValue == $sRawValue)
{
$aFoundValues[] = $sCode;
$bFound = true;
break;
}
}
if (!$bFound)
{
// Not found, break the import
return null;
}
}
return $this->MakeRealValue(implode(',', $aFoundValues), null);
}
else
{
return $this->MakeRealValue($sProposedValue, null, false);
}
}
/**
* @param string $proposedValue Search string used for MATCHES
*
* @param string $sDefaultSepItem word separator to extract items
*
* @return array of EnumSet codes
* @throws \Exception
*/
public function FromStringToArray($proposedValue, $sDefaultSepItem = ',')
{
$aValues = array();
if (!empty($proposedValue))
{
$sSepItem = MetaModel::GetConfig()->Get('tag_set_item_separator');
// convert also other separators
if ($sSepItem !== $sDefaultSepItem)
{
$proposedValue = str_replace($sDefaultSepItem, $sSepItem, $proposedValue);
}
foreach(explode($sSepItem, $proposedValue) as $sCode)
{
$sValue = trim($sCode);
if (strlen($sValue) > 2)
{
$sLabel = $this->GetValueLabel($sValue);
$aValues[$sLabel] = $sValue;
}
}
}
return $aValues;
}
}
@@ -10687,7 +10875,7 @@ class AttributeQueryAttCodeSet extends AttributeSet
* Multi value list of tags
*
* @see TagSetFieldData
* @since 2.6 N°931 tag fields
* @since 2.6.0 N°931 tag fields
*/
class AttributeTagSet extends AttributeSet
{
@@ -10766,7 +10954,7 @@ class AttributeTagSet extends AttributeSet
return json_encode($aJson);
}
public function FromStringToArray($proposedValue)
public function FromStringToArray($proposedValue, $sDefaultSepItem = ',')
{
$aValues = array();
if (!empty($proposedValue))

View File

@@ -1,29 +1,23 @@
<?php
// Copyright (C) 2010-2013 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* interface iProcess
* Something that can be executed
* Copyright (C) 2010-2020 Combodo SARL
*
* @copyright Copyright (C) 2010-2012 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
*/
interface iProcess
{
/**
@@ -78,21 +72,27 @@ interface iScheduledProcess extends iProcess
* * week_days
* * time
*
* Param names and some of their default values are in constant that can be overriden.
* Param names and some of their default values are in constant that can be overridden.
*
* Other info (module name and time default value) should be provided using a method that needs to be implemented.
*
* @since 2.7.0
* @since 2.7.0 PR #89
* @since 2.7.0-2 N°2580 Fix {@link GetNextOccurrence} returning wrong value
*/
abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
{
// param have default names/values but can be overriden
// param have default names/values but can be overridden
const MODULE_SETTING_ENABLED = 'enabled';
const DEFAULT_MODULE_SETTING_ENABLED = true;
const MODULE_SETTING_WEEKDAYS = 'week_days';
const DEFAULT_MODULE_SETTING_WEEKDAYS = 'monday, tuesday, wednesday, thursday, friday, saturday, sunday';
const MODULE_SETTING_TIME = 'time';
/**
* @var Config can be used to mock config for tests
*/
protected $oConfig;
/**
* Module must be declared in each implementation
*
@@ -106,6 +106,20 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
*/
abstract protected function GetDefaultModuleSettingTime();
/**
* @return \Config
*/
public function getOConfig()
{
if (!isset($this->oConfig))
{
$this->oConfig = MetaModel::GetConfig();
}
return $this->oConfig;
}
/**
* Interpret current setting for the week days
*
@@ -124,7 +138,7 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
'sunday' => 7,
);
$aDays = array();
$sWeekDays = MetaModel::GetConfig()->GetModuleSetting(
$sWeekDays = $this->getOConfig()->GetModuleSetting(
$this->GetModuleName(),
static::MODULE_SETTING_WEEKDAYS,
static::DEFAULT_MODULE_SETTING_WEEKDAYS
@@ -157,21 +171,26 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
}
/**
* Gives the exact time at which the process must be run next time
* @param string $sCurrentTime Date string to extract time dependency
* this parameter is not present in the interface but as it is optional it's ok
*
* @return DateTime
* @throws Exception
* @return DateTime the exact time at which the process must be run next time
* @throws \ProcessInvalidConfigException
*/
public function GetNextOccurrence()
public function GetNextOccurrence($sCurrentTime = 'now')
{
$bEnabled = MetaModel::GetConfig()->GetModuleSetting(
$bEnabled = $this->getOConfig()->GetModuleSetting(
$this->GetModuleName(),
static::MODULE_SETTING_ENABLED,
static::DEFAULT_MODULE_SETTING_ENABLED
);
$sItopTimeZone = $this->getOConfig()->Get('timezone');
$timezone = new DateTimeZone($sItopTimeZone);
if (!$bEnabled)
{
return new DateTime('3000-01-01');
return new DateTime('3000-01-01', $timezone);
}
// 1st - Interpret the list of days as ordered numbers (monday = 1)
@@ -180,7 +199,7 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
// 2nd - Find the next active week day
//
$sProcessTime = MetaModel::GetConfig()->GetModuleSetting(
$sProcessTime = $this->getOConfig()->GetModuleSetting(
$this->GetModuleName(),
static::MODULE_SETTING_TIME,
static::GetDefaultModuleSettingTime()
@@ -189,9 +208,11 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
{
throw new ProcessInvalidConfigException($this->GetModuleName().": wrong format for setting '".static::MODULE_SETTING_TIME."' (found '$sProcessTime')");
}
$oNow = new DateTime();
$oNow = new DateTime($sCurrentTime, $timezone);
$iNextPos = false;
for ($iDay = $oNow->format('N'); $iDay <= 7; $iDay++)
$sDay = $oNow->format('N');
for ($iDay = (int) $sDay; $iDay <= 7; $iDay++)
{
$iNextPos = array_search($iDay, $aDays, true);
if ($iNextPos !== false)
@@ -223,6 +244,7 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
$oRet->modify('+'.$iMove.' days');
}
list($sHours, $sMinutes) = explode(':', $sProcessTime);
/** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection non used new parameter in PHP 7.1 */
$oRet->setTime((int)$sHours, (int)$sMinutes);
return $oRet;
@@ -241,13 +263,14 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
/**
* Exception for {@link iProcess} implementations.<br>
* An error happened during the processing but we can go on with the next implementations.
* @since 2.5.0 N°1195
*/
class ProcessException extends CoreException
{
}
/**
* @since 2.7.0
* @since 2.7.0 PR #89
*/
class ProcessInvalidConfigException extends ProcessException
{
@@ -257,6 +280,7 @@ class ProcessInvalidConfigException extends ProcessException
* Class ProcessFatalException
* Exception for iProcess implementations.<br>
* A big error occurred, we have to stop the iProcess processing.
* @since 2.5.0 N°1195
*/
class ProcessFatalException extends CoreException
{

View File

@@ -26,6 +26,24 @@
*/
class BackgroundTask extends DBObject
{
protected $bDebug = false;
/**
* @return bool
*/
public function IsDebug()
{
return $this->bDebug;
}
/**
* @param bool $bDebug
*/
public function SetDebug($bDebug)
{
$this->bDebug = $bDebug;
}
public static function Init()
{
$aParams = array

View File

@@ -309,7 +309,7 @@ class BulkChange
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
}
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
$aResults[$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
}
$oExtObjects = new CMDBObjectSet($oReconFilter);
@@ -363,6 +363,7 @@ class BulkChange
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
{
// Default reporting
// $aRowData[$iCol] is always null
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
}
if ($oExtKey->IsNullAllowed())
@@ -395,7 +396,7 @@ class BulkChange
}
$aCacheKeys[] = $value;
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
$aResults[$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
}
$sCacheKey = implode('_|_', $aCacheKeys); // Unique key for this query...
$iForeignKey = null;
@@ -465,7 +466,7 @@ class BulkChange
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
{
// Report the change on reconciliation values as well
$aResults[$iCol] = new CellStatus_Modify($aRowData[$iCol]);
$aResults[$iCol] = new CellStatus_Modify(utils::HtmlEntities($aRowData[$iCol]));
}
}
}
@@ -538,7 +539,7 @@ class BulkChange
{
if ($sAttCode == 'id')
{
$aResults[$iCol]= new CellStatus_Void($aRowData[$iCol]);
$aResults[$iCol]= new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
}
else
{
@@ -554,7 +555,7 @@ class BulkChange
}
if (isset($aErrors[$sAttCode]))
{
$aResults[$iCol]= new CellStatus_Issue($aRowData[$iCol], $sOrigValue, $aErrors[$sAttCode]);
$aResults[$iCol]= new CellStatus_Issue(utils::HtmlEntities($aRowData[$iCol]), $sOrigValue, $aErrors[$sAttCode]);
}
elseif (array_key_exists($sAttCode, $aChangedFields))
{
@@ -577,7 +578,7 @@ class BulkChange
}
else
{
$aResults[$iCol]= new CellStatus_Void($aRowData[$iCol]);
$aResults[$iCol]= new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
}
}
}
@@ -924,7 +925,7 @@ class BulkChange
{
// Leave the cell unchanged
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
$aResult[$iRow][$sAttCode] = new CellStatus_Issue(null, $this->m_aData[$iRow][$iCol], Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
$aResult[$iRow][$sAttCode] = new CellStatus_Issue(null, utils::HtmlEntities($this->m_aData[$iRow][$iCol]), Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
}
}
}
@@ -1082,7 +1083,7 @@ class BulkChange
{
if (!array_key_exists($iCol, $aResult[$iRow]))
{
$aResult[$iRow][$iCol] = new CellStatus_Void($aRowData[$iCol]);
$aResult[$iRow][$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
}
}
foreach($this->m_aExtKeys as $sAttCode => $aForeignAtts)
@@ -1096,7 +1097,7 @@ class BulkChange
if (!array_key_exists($iCol, $aResult[$iRow]))
{
// The foreign attribute is one of our reconciliation key
$aResult[$iRow][$iCol] = new CellStatus_Void($aRowData[$iCol]);
$aResult[$iRow][$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
}
}
}

View File

@@ -345,10 +345,10 @@ abstract class BulkExport
$this->oBulkExportResult->Set('format', $this->sFormatCode);
$this->oBulkExportResult->Set('search', $this->oSearch->serialize());
$this->oBulkExportResult->Set('chunk_size', $this->iChunkSize);
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
$this->oBulkExportResult->Set('localize_output', $this->bLocalizeOutput);
}
$this->oBulkExportResult->Set('status_info', json_encode($this->GetStatusInfo()));
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
utils::PushArchiveMode(false);
$ret = $this->oBulkExportResult->DBWrite();
utils::PopArchiveMode();
@@ -420,6 +420,11 @@ abstract class BulkExport
public function GetStatistics()
{
}
public function SetFields($sFields)
{
}
public function GetDownloadFileName()

View File

@@ -544,7 +544,7 @@ class CMDBChangeOpSetAttributeEncrypted extends CMDBChangeOpSetAttribute
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$sPrevString = $this->Get('prevstring');
$sPrevString = $this->GetAsHTML('prevstring');
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sPrevString);
}
return $sResult;

View File

@@ -95,11 +95,21 @@ abstract class CMDBObject extends DBObject
protected static $m_oCurrChange = null;
protected static $m_sInfo = null; // null => the information is built in a standard way
protected static $m_sOrigin = null; // null => the origin is 'interactive'
/**
* Specify another change (this is mainly for backward compatibility)
* Specify the change to be used by the API to attach any CMDBChangeOp* object created
*
* @see SetTrackInfo if CurrentChange is null, then a new one will be create using trackinfo
*
* @param CMDBChange|null $oChange use null so that the API will recreate a new CMDBChange using TrackInfo & TrackOrigin
* If providing a CMDBChange, you should persist it first ! Indeed the API will automatically create CMDBChangeOp (see
* \CMDBObject::RecordObjCreation / RecordAttChange / RecordObjDeletion for example) and link them to the current change : in
* consequence this CMDBChange must have a key set !
*
* @since 2.7.2 N°3219 can now reset CMDBChange by passing null
* @since 2.7.2 N°3218 PHPDoc about persisting the $oChange parameter first
*/
public static function SetCurrentChange(CMDBChange $oChange)
public static function SetCurrentChange($oChange)
{
self::$m_oCurrChange = $oChange;
}
@@ -112,7 +122,11 @@ abstract class CMDBObject extends DBObject
// GetCurrentChange to create a default change if not already done in the current context
//
/**
* Get a change record (create it if not existing)
* @param bool $bAutoCreate if true calls {@link CreateChange} to get a new persisted object
*
* @return \CMDBChange
*
* @uses CreateChange
*/
public static function GetCurrentChange($bAutoCreate = true)
{
@@ -126,11 +140,15 @@ abstract class CMDBObject extends DBObject
/**
* Override the additional information (defaulting to user name)
* A call to this verb should replace every occurence of
* $oMyChange = MetaModel::NewObject("CMDBChange");
* $oMyChange = MetaModel::NewObject("CMDBChange");
* $oMyChange->Set("date", time());
* $oMyChange->Set("userinfo", 'this is done by ... for ...');
* $iChangeId = $oMyChange->DBInsert();
*/
*
* @see SetCurrentChange to specify a CMDBObject instance instead
*
* @param string $sInfo
*/
public static function SetTrackInfo($sInfo)
{
self::$m_sInfo = $sInfo;
@@ -138,8 +156,13 @@ abstract class CMDBObject extends DBObject
/**
* Provides information about the origin of the change
* @param $sOrigin String: one of: interactive, csv-interactive, csv-import.php, webservice-soap, webservice-rest, syncho-data-source, email-processing, custom-extension
*/
*
* @see SetTrackInfo
* @see SetCurrentChange to specify a CMDBObject instance instead
*
* @param $sOrigin String: one of: interactive, csv-interactive, csv-import.php, webservice-soap, webservice-rest, syncho-data-source,
* email-processing, custom-extension
*/
public static function SetTrackOrigin($sOrigin)
{
self::$m_sOrigin = $sOrigin;
@@ -174,10 +197,12 @@ abstract class CMDBObject extends DBObject
return self::$m_sOrigin;
}
}
/**
* Create a standard change record (done here 99% of the time, and nearly once per page)
*/
* Set to {@link $m_oCurrChange} a standard change record (done here 99% of the time, and nearly once per page)
*
* The CMDBChange is persisted so that it has a key > 0, and any new CMDBChangeOp can link to it
*/
protected static function CreateChange()
{
self::$m_oCurrChange = MetaModel::NewObject("CMDBChange");

View File

@@ -39,6 +39,7 @@ class MySQLException extends CoreException
*/
public function __construct($sIssue, $aContext, $oException = null, $oMysqli = null)
{
if ($oException != null)
{
$aContext['mysql_errno'] = $oException->getCode();
@@ -58,13 +59,18 @@ class MySQLException extends CoreException
$aContext['mysql_error'] = CMDBSource::GetError();
}
parent::__construct($sIssue, $aContext);
//if is connection error, don't log the default message with password in
if (mysqli_connect_errno()) {
error_log($this->message);
error_reporting(0);
}
}
}
/**
* Class MySQLQueryHasNoResultException
*
* @since 2.5
* @since 2.5.0
*/
class MySQLQueryHasNoResultException extends MySQLException
{
@@ -74,7 +80,7 @@ class MySQLQueryHasNoResultException extends MySQLException
/**
* Class MySQLHasGoneAwayException
*
* @since 2.5
* @since 2.5.0
* @see itop bug 1195
* @see https://dev.mysql.com/doc/refman/5.7/en/gone-away.html
*/
@@ -119,24 +125,36 @@ class CMDBSource
const ENUM_DB_VENDOR_MYSQL = 'MySQL';
const ENUM_DB_VENDOR_MARIADB = 'MariaDB';
const ENUM_DB_VENDOR_PERCONA = 'Percona';
/**
* Error: 1205 SQLSTATE: HY000 (ER_LOCK_WAIT_TIMEOUT)
* Message: Lock wait timeout exceeded; try restarting transaction
*/
const MYSQL_ERRNO_WAIT_TIMEOUT = 1205;
/**
* Error: 1213 SQLSTATE: 40001 (ER_LOCK_DEADLOCK)
* Message: Deadlock found when trying to get lock; try restarting transaction
*/
const MYSQL_ERRNO_DEADLOCK = 1213;
protected static $m_sDBHost;
protected static $m_sDBUser;
protected static $m_sDBPwd;
protected static $m_sDBName;
/**
* @var boolean
* @since 2.5 N°1260 MySQL TLS first implementation
* @since 2.5.0 N°1260 MySQL TLS first implementation
*/
protected static $m_bDBTlsEnabled;
/**
* @var string
* @since 2.5 N°1260 MySQL TLS first implementation
* @since 2.5.0 N°1260 MySQL TLS first implementation
*/
protected static $m_sDBTlsCA;
/** @var mysqli $m_oMysqli */
protected static $m_oMysqli;
protected static $oMySQLiForQuery;
/**
* @var int number of level for nested transactions : 0 if no transaction was ever opened, +1 for each 'START TRANSACTION' sent
@@ -203,6 +221,7 @@ class CMDBSource
self::$m_sDBTlsCA = empty($sTlsCA) ? null : $sTlsCA;
self::$m_oMysqli = self::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, true);
self::SetMySQLiForQuery(self::$m_oMysqli);
}
/**
@@ -220,8 +239,6 @@ class CMDBSource
public static function GetMysqliInstance(
$sDbHost, $sUser, $sPwd, $sSource = '', $bTlsEnabled = false, $sTlsCa = null, $bCheckTlsAfterConnection = false
) {
$oMysqli = null;
$sServer = null;
$iPort = null;
self::InitServerAndPort($sDbHost, $sServer, $iPort);
@@ -251,7 +268,7 @@ class CMDBSource
}
catch(mysqli_sql_exception $e)
{
throw new MySQLException('Could not connect to the DB server', array('host' => $sServer, 'user' => $sUser), $e);
throw new MySQLException('Could not connect to the DB server', array('host' => $sServer, 'user' => $sUser),$e);
}
if ($bTlsEnabled
@@ -519,6 +536,24 @@ class CMDBSource
return self::$m_oMysqli;
}
/**
* @return
*/
private static function GetMySQLiForQuery()
{
return self::$oMySQLiForQuery;
}
/**
* Used for test purpose (mysqli mock)
* @param $oMySQLi
*/
private static function SetMySQLiForQuery($oMySQLi)
{
self::$oMySQLiForQuery = $oMySQLi;
}
public static function GetErrNo()
{
if (self::$m_oMysqli->errno != 0)
@@ -654,13 +689,19 @@ class CMDBSource
*/
private static function DBQuery($sSql)
{
$sShortSQL = substr(preg_replace("/\s+/", " ", substr($sSql, 0, 180)), 0, 150);
if (substr_compare($sShortSQL, "SELECT", 0, strlen("SELECT")) !== 0) {
IssueLog::Trace("$sShortSQL", 'cmdbsource');
}
$oKPI = new ExecutionKPI();
try
{
$oResult = self::$m_oMysqli->query($sSql);
$oResult = self::GetMySQLiForQuery()->query($sSql);
}
catch (mysqli_sql_exception $e)
{
self::LogDeadLock($e);
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
}
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
@@ -674,13 +715,55 @@ class CMDBSource
{
throw new MySQLHasGoneAwayException(self::GetError(), $aContext);
}
throw new MySQLException('Failed to issue SQL query', $aContext);
$e = new MySQLException('Failed to issue SQL query', $aContext);
self::LogDeadLock($e);
throw $e;
}
return $oResult;
}
/**
* @param \Exception $e
*
* @since 2.7.1
*/
private static function LogDeadLock(Exception $e)
{
// checks MySQL error code
$iMySqlErrorNo = self::$m_oMysqli->errno;
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK)))
{
return;
}
// Get error info
$sUser = UserRights::GetUser();
$oError = self::$m_oMysqli->query('SHOW ENGINE INNODB STATUS');
if ($oError !== false)
{
$aData = $oError->fetch_all(MYSQLI_ASSOC);
$sInnodbStatus = $aData[0];
}
else
{
$sInnodbStatus = 'Get status query cannot execute';
}
// log !
$sMessage = "deadlock detected: user= $sUser; errno=$iMySqlErrorNo";
$aLogContext = array(
'userinfo' => $sUser,
'errno' => $iMySqlErrorNo,
'ex_msg' => $e->getMessage(),
'callstack' => $e->getTraceAsString(),
'data' => $sInnodbStatus,
);
DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext);
IssueLog::Error($sMessage, LogChannels::DEADLOCK, $e->getMessage());
}
/**
* If nested transaction, we are not starting a new one : only one global transaction will exist.
*
@@ -694,10 +777,15 @@ class CMDBSource
*/
private static function StartTransaction()
{
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
$bHasExistingTransactions = self::IsInsideTransaction();
if (!$bHasExistingTransactions)
{
IssueLog::Trace("START TRANSACTION $sCaller", 'cmdbsource');
self::DBQuery('START TRANSACTION');
} else {
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") START TRANSACTION $sCaller", 'cmdbsource');
}
self::AddTransactionLevel();
@@ -715,9 +803,12 @@ class CMDBSource
*/
private static function Commit()
{
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
if (!self::IsInsideTransaction())
{
// should not happen !
IssueLog::Error("No Transaction COMMIT $sCaller", 'cmdbsource');
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
}
@@ -725,8 +816,10 @@ class CMDBSource
if (self::IsInsideTransaction())
{
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") COMMIT $sCaller", 'cmdbsource');
return;
}
IssueLog::Trace("COMMIT $sCaller", 'cmdbsource');
self::DBQuery('COMMIT');
}
@@ -745,17 +838,22 @@ class CMDBSource
*/
private static function Rollback()
{
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
if (!self::IsInsideTransaction())
{
// should not happen !
IssueLog::Error("No Transaction ROLLBACK $sCaller", 'cmdbsource');
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
}
self::RemoveLastTransactionLevel();
if (self::IsInsideTransaction())
{
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") ROLLBACK $sCaller", 'cmdbsource');
return;
}
IssueLog::Trace("ROLLBACK $sCaller", 'cmdbsource');
self::DBQuery('ROLLBACK');
}
@@ -805,6 +903,17 @@ class CMDBSource
self::$m_iTransactionLevel = 0;
}
public static function IsDeadlockException(Exception $e)
{
while ($e instanceof Exception) {
if (($e instanceof MySQLException) && ($e->getCode() == 1213)) {
return true;
}
$e = $e->getPrevious();
}
return false;
}
/**
*
* @deprecated 2.7.0 N°1627 use ItopCounter instead
@@ -868,7 +977,7 @@ class CMDBSource
$oKPI = new ExecutionKPI();
try
{
$oResult = self::$m_oMysqli->query($sSql);
$oResult = self::GetMySQLiForQuery()->query($sSql);
}
catch(mysqli_sql_exception $e)
{
@@ -908,7 +1017,7 @@ class CMDBSource
$oKPI = new ExecutionKPI();
try
{
$oResult = self::$m_oMysqli->query($sSql);
$oResult = self::GetMySQLiForQuery()->query($sSql);
}
catch(mysqli_sql_exception $e)
{
@@ -990,7 +1099,7 @@ class CMDBSource
{
try
{
$oResult = self::$m_oMysqli->query($sSql);
$oResult = self::GetMySQLiForQuery()->query($sSql);
}
catch(mysqli_sql_exception $e)
{
@@ -1127,17 +1236,24 @@ class CMDBSource
}
/**
* There may have some differences between DB : for example in MySQL 5.7 we have "INT", while in MariaDB >= 10.2 you get "int DEFAULT 'NULL'"
* There may have some differences between DB ! For example in :
* * MySQL 5.7 we have `INT`
* * MariaDB >= 10.2 you get `int DEFAULT 'NULL'`
*
* We still do a case sensitive comparison for enum values !
* We still need to do a case sensitive comparison for enum values !
*
* A better solution would be to generate SQL field definitions ({@link GetFieldSpec} method) based on the DB used... But for
* now (N°2490 / SF #1756 / PR #91) we did implement this simpler solution
*
* @param string $sItopGeneratedFieldType
* @see GetFieldDataTypeAndOptions extracts all info from the SQL field definition
*
* @param string $sDbFieldType
*
* @param string $sItopGeneratedFieldType
*
* @return bool true if same type and options (case sensitive comparison only for type options), false otherwise
*
* @throws \CoreException
* @since 2.7.0 N°2490
*/
public static function IsSameFieldTypes($sItopGeneratedFieldType, $sDbFieldType)
@@ -1185,24 +1301,68 @@ class CMDBSource
}
/**
* @param string $sCompleteFieldType sql field type, for example 'VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 0'
* @see \self::GetEnumOptions() specific processing for ENUM fields
*
* @param string $sCompleteFieldType sql field type, for example `VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 0`
*
* @return string[] consisting of 3 items :
* 1. data type : for example 'VARCHAR'
* 2. type value : for example '255'
* 3. other options : for example ' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 0'
* 1. data type : for example `VARCHAR`
* 2. type value : for example `255`
* 3. other options : for example `CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 0`
*
* @throws \CoreException
*/
private static function GetFieldDataTypeAndOptions($sCompleteFieldType)
{
preg_match('/^([a-zA-Z]+)(\(([^\)]+)\))?( .+)?$/', $sCompleteFieldType, $aMatches);
preg_match('/^([a-zA-Z]+)(\(([^\)]+)\))?( .+)?/', $sCompleteFieldType, $aMatches);
$sDataType = isset($aMatches[1]) ? $aMatches[1] : '';
if (strcasecmp($sDataType, 'ENUM') === 0){
try{
return self::GetEnumOptions($sDataType, $sCompleteFieldType);
}catch(CoreException $e){
//do nothing ; especially do not block setup.
IssueLog::Warning("enum was not parsed properly: $sCompleteFieldType. it should not happen during setup.");
}
}
$sDataType = $aMatches[1];
$sTypeOptions = isset($aMatches[2]) ? $aMatches[3] : '';
$sOtherOptions = isset($aMatches[4]) ? $aMatches[4] : '';
return array($sDataType, $sTypeOptions, $sOtherOptions);
}
/**
* @param string $sDataType for example `ENUM`
* @param string $sCompleteFieldType Example:
* `ENUM('CSP A','CSP (aaaa) M','NA','OEM(ROC)','OPEN(VL)','RETAIL (Boite)') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`
*
* @return string[] consisting of 3 items :
* 1. data type : ENUM or enum here
* 2. type value : in-between EUM parenthesis
* 3. other options : for example ' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 0'
* @throws \CoreException
* @since 2.7.4 N°3065 specific processing for enum fields : fix no alter table when enum values containing parenthesis
* Handle ENUM options
*/
private static function GetEnumOptions($sDataType, $sCompleteFieldType)
{
$iFirstOpeningParenthesis = strpos($sCompleteFieldType, '(');
$iLastEndingParenthesis = strrpos($sCompleteFieldType, ')');
if ($iFirstOpeningParenthesis === false || $iLastEndingParenthesis === false ){
//should never happen as GetFieldDataTypeAndOptions regexp matched.
//except if regexp is modiied/broken somehow one day...
throw new CoreException("GetEnumOptions issue with $sDataType parsing : " . $sCompleteFieldType);
}
$sTypeOptions = substr($sCompleteFieldType, $iFirstOpeningParenthesis + 1, $iLastEndingParenthesis - 1);
$sOtherOptions = substr($sCompleteFieldType, $iLastEndingParenthesis + 1);
return array($sDataType, $sTypeOptions, $sOtherOptions);
}
/**
* @param string $sTable
* @param string $sField
@@ -1390,7 +1550,7 @@ class CMDBSource
* @return string query to upgrade table charset and collation if needed, null if not
* @throws \MySQLException
*
* @since 2.5 N°1001 switch to utf8mb4
* @since 2.5.0 N°1001 switch to utf8mb4
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-table.html
*/
public static function DBCheckTableCharsetAndCollation($sTableName)
@@ -1426,7 +1586,7 @@ class CMDBSource
$sSql = "SELECT * FROM `$sTable`";
try
{
$oResult = self::$m_oMysqli->query($sSql);
$oResult = self::GetMySQLiForQuery()->query($sSql);
}
catch(mysqli_sql_exception $e)
{
@@ -1540,7 +1700,7 @@ class CMDBSource
* @return string query to upgrade database charset and collation if needed, null if not
* @throws \MySQLException
*
* @since 2.5 N°1001 switch to utf8mb4
* @since 2.5.0 N°1001 switch to utf8mb4
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-database.html
*/
public static function DBCheckCharsetAndCollation()

View File

@@ -1,35 +1,44 @@
<?php
// Copyright (C) 2010-2014 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Copyright (C) 2010-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
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
/**
* Any extension to compute things like a stop watch deadline or working hours
* Any extension to compute things like a stop watch deadline or working hours
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* Metric computing for stop watches
* Metric computing for stop watches.
* Can be used for AttributeStopWatch goal (iTop XML node xpath: /itop_design/classes/class/fields/field/goal)
*/
interface iMetricComputer
{
public static function GetDescription();
/**
* @param \DBObject $oObject
*
* @return float number of seconds for the time limit
*/
public function ComputeMetric($oObject);
}
@@ -41,21 +50,21 @@ interface iWorkingTimeComputer
public static function GetDescription();
/**
* Get the date/time corresponding to a given delay in the future from the present
* considering only the valid (open) hours for a specified object
* @param $oObject DBObject The object for which to compute the deadline
* @param $iDuration integer The duration (in seconds) in the future
* @param $oStartDate DateTime The starting point for the computation
* @return DateTime The date/time for the deadline
* @param DBObject $oObject The object for which to compute the deadline
* @param integer $iDuration The duration (in seconds) in the future
* @param DateTime $oStartDate The starting point for the computation
*
* @return DateTime The date/time corresponding to a given delay in the future from the present
* considering only the valid (open) hours for a specified object
*/
public function GetDeadline($oObject, $iDuration, DateTime $oStartDate);
/**
* Get duration (considering only open hours) elapsed bewteen two given DateTimes
* @param $oObject DBObject The object for which to compute the duration
* @param $oStartDate DateTime The starting point for the computation (default = now)
* @param $oEndDate DateTime The ending point for the computation (default = now)
* @return integer The duration (number of seconds) of open hours elapsed between the two dates
* @param DBObject $oObject The object for which to compute the duration
* @param DateTime $oStartDate The starting point for the computation (default = now)
* @param DateTime $oEndDate The ending point for the computation (default = now)
*
* @return integer The duration (number of seconds) elapsed between two given dates, considering only open hours
*/
public function GetOpenDuration($oObject, DateTime $oStartDate, DateTime $oEndDate);
}
@@ -87,12 +96,7 @@ class DefaultWorkingTimeComputer implements iWorkingTimeComputer
}
/**
* Get the date/time corresponding to a given delay in the future from the present
* considering only the valid (open) hours for a specified object
* @param $oObject DBObject The object for which to compute the deadline
* @param $iDuration integer The duration (in seconds) in the future
* @param $oStartDate DateTime The starting point for the computation
* @return DateTime The date/time for the deadline
* @inheritDoc
*/
public function GetDeadline($oObject, $iDuration, DateTime $oStartDate)
{
@@ -113,11 +117,7 @@ class DefaultWorkingTimeComputer implements iWorkingTimeComputer
}
/**
* Get duration (considering only open hours) elapsed bewteen two given DateTimes
* @param $oObject DBObject The object for which to compute the duration
* @param $oStartDate DateTime The starting point for the computation (default = now)
* @param $oEndDate DateTime The ending point for the computation (default = now)
* @return integer The duration (number of seconds) of open hours elapsed between the two dates
* @inheritDoc
*/
public function GetOpenDuration($oObject, DateTime $oStartDate, DateTime $oEndDate)
{
@@ -134,6 +134,3 @@ class DefaultWorkingTimeComputer implements iWorkingTimeComputer
return $iDuration;
}
}
?>

View File

@@ -22,7 +22,7 @@
define('ITOP_APPLICATION', 'iTop');
define('ITOP_APPLICATION_SHORT', 'iTop');
define('ITOP_VERSION', '2.7.0-dev');
define('ITOP_VERSION', '2.7.0-dev'); // @see utils::GetItopVersionShort() and utils::GetItopVersionWikiSyntax()
define('ITOP_REVISION', 'svn');
define('ITOP_BUILD_DATE', '$WCNOW$');
define('ITOP_VERSION_FULL', ITOP_VERSION.'-'.ITOP_REVISION);
@@ -90,7 +90,16 @@ class Config
protected $m_aWebServiceCategories;
protected $m_aAddons;
/** @var ConfigPlaceholdersResolver */
private $oConfigPlaceholdersResolver;
protected $m_aModuleSettings;
/**
* @var \iTopConfigParser|null
*/
private $oItopConfigParser;
//for each conf entry, whether the non interpreted value can be kept in case is is written back to the disk.
private $m_aCanOverrideSettings;
/**
* New way to store the settings !
@@ -352,7 +361,7 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'export_pdf_font' => array( // @since 2.7 PR #49
'export_pdf_font' => array( // @since 2.7.0 PR #49 / N°1947
'type' => 'string',
'description' => 'Font used when generating a PDF file',
'default' => 'DejaVuSans', // DejaVuSans is a UTF-8 Unicode font, embedded in the TCPPDF lib we're using
@@ -396,8 +405,8 @@ class Config
),
'log_filename_builder_impl' => array(
'type' => 'string',
'description' => 'Name of the ILogFileNameBuilder to use',
'default' => 'WeeklyRotatingLogFileNameBuilder',
'description' => 'Name of the iLogFileNameBuilder to use',
'default' => 'MonthlyRotatingLogFileNameBuilder',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
@@ -931,15 +940,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'portal_tickets' => array(
'type' => 'string',
'description' => 'CSV list of classes supported in the portal',
// examples... not used
'default' => 'UserRequest',
'value' => 'UserRequest',
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'portal_dispatch_urls' => array(
'type' => 'array',
'description' => 'Associative array of sPortalId => Home page URL (relatively to the application root)',
@@ -1065,6 +1065,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'transactions_gc_threshold' => array(
'type' => 'integer',
'description' => 'probability in percent for the garbage collector to be triggered (100 mean always)',
'default' => 10, // added in itop 2.7.4, before the GC was always called
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'log_transactions' => array(
'type' => 'bool',
'description' => 'Whether or not to enable the debug log for the transactions.',
@@ -1249,11 +1257,25 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'security_header_xframe' => [
'type' => 'string',
'description' => 'Value of the X-Frame-Options HTTP header sent by iTop. Possible values : DENY, SAMEORIGIN, or empty string.',
'default' => 'SAMEORIGIN',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'behind_reverse_proxy' => [
'type' => 'bool',
'description' => 'If true, then proxies custom header (X-Forwarded-*) are taken into account. Use only if the webserver is not publicly accessible (reachable only by the reverse proxy)',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => true,
],
);
/**
* @var \iTopConfigParser|null
*/
private $oItopConfigParser;
public function IsProperty($sPropCode)
{
@@ -1283,12 +1305,16 @@ class Config
* @param string $sPropCode
* @param mixed $value
* @param string $sSourceDesc mandatory for variables with show_in_conf_sample=false
* @param bool $bCanOverride whether the written to file value can still be the non evaluated version on must be the literal
*
* @throws \CoreException
*/
public function Set($sPropCode, $value, $sSourceDesc = 'unknown', $bCanOverride = false)
{
$sType = $this->m_aSettings[$sPropCode]['type'];
$value = $this->oConfigPlaceholdersResolver->Resolve($value);
switch ($sType)
{
case 'bool':
@@ -1308,6 +1334,13 @@ class Config
default:
throw new CoreException('Unknown type for setting', array('property' => $sPropCode, 'type' => $sType));
}
if ($this->m_aSettings[$sPropCode]['value'] == $value)
{
//when you set the exact same value than the previous one, then, you still can preserve the non evaluated version and so on preserve vars/jokers.
$bCanOverride = true;
}
$this->m_aSettings[$sPropCode]['value'] = $value;
$this->m_aSettings[$sPropCode]['source_of_value'] = $sSourceDesc;
$this->m_aCanOverrideSettings[$sPropCode] = $bCanOverride;
@@ -1404,6 +1437,8 @@ class Config
*/
public function __construct($sConfigFile = null, $bLoadConfig = true)
{
$this->oConfigPlaceholdersResolver = new ConfigPlaceholdersResolver();
$this->m_sFile = $sConfigFile;
if (is_null($sConfigFile))
{
@@ -2001,16 +2036,7 @@ class Config
if (isset($aSettingInfo['default']))
{
if (isset($this->m_aCanOverrideSettings[$sPropCode]) && $this->m_aCanOverrideSettings[$sPropCode])
{
$aParserValue = $this->oItopConfigParser->GetVarValue('MySettings', $sPropCode);
}
else
{
$aParserValue = array('found' => false);
}
$sComment = self::PrettyVarExport($aParserValue,$aSettingInfo['default'], "\t//\t\t", true);
$sComment = self::PrettyVarExport(null,$aSettingInfo['default'], "\t//\t\t", true);
fwrite($hFile,"\t//\tdefault: {$sComment}\n");
}
@@ -2020,7 +2046,7 @@ class Config
}
else
{
$aParserValue = array('found' => false);
$aParserValue = null;
}
$sSeenAs = self::PrettyVarExport($aParserValue,$aSettingInfo['value'], "\t");
fwrite($hFile, "\t'$sPropCode' => $sSeenAs,\n");
@@ -2069,7 +2095,6 @@ class Config
$bReturn = fclose($hFile);
utils::SetConfig($this);
FileLog::RenameLegacyLogFiles();
return $bReturn;
}
@@ -2266,7 +2291,7 @@ class Config
*/
protected static function PrettyVarExport($aParserValue, $value, $sIndentation, $bForceIndentation = false)
{
if ($aParserValue['found'])
if (is_array($aParserValue) && $aParserValue['found'])
{
return $aParserValue['value'];
}
@@ -2290,3 +2315,99 @@ class Config
}
}
class ConfigPlaceholdersResolver
{
/**
* @var null|array
*/
private $aEnv;
/**
* @var null|array
*/
private $aServer;
public function __construct($aEnv = null, $aServer = null)
{
$this->aEnv = $aEnv ?: $_ENV;
$this->aServer = $aServer ?: $_SERVER;
}
public function Resolve($rawValue)
{
if (empty($this->aEnv['ITOP_CONFIG_PLACEHOLDERS']) && empty($this->aServer['ITOP_CONFIG_PLACEHOLDERS']))
{
return $rawValue;
}
if (is_array($rawValue))
{
$aResolvedRawValue = array();
foreach ($rawValue as $key => $value)
{
$aResolvedRawValue[$key] = $this->Resolve($value);
}
return $aResolvedRawValue;
}
if (!is_string($rawValue))
{
return $rawValue;
}
$sPattern = '/\%(env|server)\((\w+)\)(?:\?:(\w*))?\%/'; //3 capturing groups, ie `%env(HTTP_PORT)?:8080%` produce: `env` `HTTP_PORT` and `8080`.
if (! preg_match_all($sPattern, $rawValue, $aMatchesCollection, PREG_SET_ORDER))
{
return $rawValue;
}
$sValue = $rawValue;
foreach ($aMatchesCollection as $aMatches)
{
$sWholeMask = $aMatches[0];
$sSource = $aMatches[1];
$sKey = $aMatches[2];
$sDefault = isset($aMatches[3]) ? $aMatches[3] : null;
$sReplacement = $this->Get($sSource, $sKey, $sDefault, $sWholeMask);
$sValue = str_replace($sWholeMask, $sReplacement, $sValue);
}
return $sValue;
}
private function Get($sSourceName, $sKey, $sDefault, $sWholeMask)
{
if ('env' == $sSourceName)
{
$aSource = $this->aEnv;
}
else if ('server' == $sSourceName)
{
$aSource = $this->aServer;
}
else
{
$sErrorMessage = sprintf('unsupported source name "%s" into "%s"', $sSourceName, $sWholeMask);
IssueLog::Error($sErrorMessage, self::class, array($sSourceName, $sKey, $sDefault, $sWholeMask));
throw new ConfigException($sErrorMessage);
}
if (array_key_exists($sKey, $aSource))
{
return $aSource[$sKey];
}
if (null !== $sDefault)
{
return $sDefault;
}
$sErrorMessage = sprintf('key "%s" not found into "%s" while expanding', $sSourceName, $sWholeMask);
IssueLog::Error($sErrorMessage, self::class, array($sSourceName, $sKey, $sDefault, $sWholeMask));
throw new ConfigException($sErrorMessage);
}
}

View File

@@ -28,31 +28,39 @@
class CoreException extends Exception
{
public function __construct($sIssue, $aContextData = null, $sImpact = '')
/**
* CoreException constructor.
*
* @param string $sIssue error message
* @param array|null $aContextData key/value array, value MUST implements _toString
* @param string $sImpact
* @param Exception|null $oPrevious
*/
public function __construct($sIssue, $aContextData = null, $sImpact = '', $oPrevious = null)
{
$this->m_sIssue = $sIssue;
$this->m_sImpact = $sImpact;
$this->m_aContextData = $aContextData ? $aContextData : array();
if (is_array($aContextData)) {
$this->m_aContextData = $aContextData;
} else {
$this->m_aContextData = [];
}
$sMessage = $sIssue;
if (!empty($sImpact)) $sMessage .= "($sImpact)";
if (count($this->m_aContextData) > 0)
{
if (!empty($sImpact)) {
$sMessage .= "($sImpact)";
}
if (count($this->m_aContextData) > 0) {
$sMessage .= ": ";
$aContextItems = array();
foreach($this->m_aContextData as $sKey => $value)
{
if (is_array($value))
{
foreach ($this->m_aContextData as $sKey => $value) {
if (is_array($value)) {
$aPairs = array();
foreach($value as $key => $val)
{
if (is_array($val))
{
foreach ($value as $key => $val) {
if (is_array($val)) {
$aPairs[] = $key.'=>('.implode(', ', $val).')';
}
else
{
} else {
$aPairs[] = $key.'=>'.$val;
}
}
@@ -66,7 +74,7 @@ class CoreException extends Exception
}
$sMessage .= implode(', ', $aContextItems);
}
parent::__construct($sMessage, 0);
parent::__construct($sMessage, 0, $oPrevious);
}
/**
@@ -81,6 +89,16 @@ class CoreException extends Exception
return $this->getMessage();
}
/**
* getTraceAsString() cannot be overrided and it is limited as only current exception stack is returned.
* we need stack of all previous exceptions
* @uses __tostring() already does the work.
* @since 2.7.2/ 2.8.0
*/
public function getFullStackTraceAsString(){
return "" . $this;
}
public function getTraceAsHtml()
{
$aBackTrace = $this->getTrace();
@@ -115,7 +133,7 @@ class CoreException extends Exception
* @see \DBObject::DBInsertNoReload()
* @see \DBObject::DBUpdate()
*
* @since 2.6 N°659 uniqueness constraint
* @since 2.6.0 N°659 uniqueness constraint
*/
class CoreCannotSaveObjectException extends CoreException
{
@@ -131,14 +149,14 @@ class CoreCannotSaveObjectException extends CoreException
*
* @param array $aContextData containing at least those keys : issues, id, class
*/
public function __construct($aContextData)
public function __construct($aContextData, $oPrevious = null)
{
$this->aIssues = $aContextData['issues'];
$this->iObjectId = $aContextData['id'];
$this->sObjectClass = $aContextData['class'];
$sIssues = implode(', ', $this->aIssues);
parent::__construct($sIssues, $aContextData);
parent::__construct($sIssues, $aContextData, '', $oPrevious);
}
/**

View File

@@ -1,11 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.6">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.7">
<user_rights>
<profiles>
<profile id="1024" _delta="define">
<name>REST Services User</name>
<description>Only users having this profile are allowed to use the REST Web Services (unless 'secure_rest_services' is set to false in the configuration file).</description>
<groups />
<description>Only users having this profile are allowed to use the REST Web Services (unless 'secure_rest_services' is set to false
in the configuration file).
</description>
<groups/>
</profile>
</profiles>
</user_rights>

View File

@@ -83,8 +83,9 @@ abstract class DBObject implements iDisplay
/** @var bool true IF the object is mapped to a DB record */
protected $m_bIsInDB = false;
protected $m_iKey = null;
/** @var array key: attcode, value: corresponding current value (in memory, before persisting object) */
/** @var array attcode => value : corresponding current value (the new value passed to {@see DBObject::Set()}). Reset during {@see DBObject::DBUpdate()} */
private $m_aCurrValues = array();
/** @var array attcode => value : previous values before the {@see DBObject::Set()} call. Array is reset at the end of {@see DBObject::DBUpdate()} */
protected $m_aOrigValues = array();
protected $m_aExtendedData = null;
@@ -97,7 +98,8 @@ abstract class DBObject implements iDisplay
private $m_bDirty = false;
/**
* @var boolean|null true if the object has been verified and is consistent with integrity rules. If null, then the check has to be performed again to know the status
* @var boolean|null true if the object has been verified and is consistent with integrity rules.
* If null, then the check has to be performed again to know the status
* @see CheckToWrite()
*/
private $m_bCheckStatus = null;
@@ -114,7 +116,7 @@ abstract class DBObject implements iDisplay
/**
* @var null|string[] list of warnings thrown during DB write
* @see CheckToWrite()
* @since 2.6 N°659 uniqueness constraints
* @since 2.6.0 N°659 uniqueness constraints
*/
protected $m_aCheckWarnings = null;
protected $m_aDeleteIssues = null;
@@ -134,10 +136,11 @@ abstract class DBObject implements iDisplay
*/
protected $m_aModifiedAtt = array();
/**
* @var array attname => currentvalue Persists changes for {@link DBUpdate}
* @var array attname => value : value before the last {@see DBObject::Set()} call. Set at the beginning of {@see DBObject::DBUpdate()}.
* @see DBObject::ListPreviousValuesForUpdatedAttributes() getter for this attribute
* @since 2.7.0 N°2293
*/
protected $m_aChanges;
protected $m_aPreviousValuesForUpdatedAttributes;
/**
* @var array Set of Synch data related to this object
* <ul>
@@ -642,7 +645,7 @@ abstract class DBObject implements iDisplay
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \Exception
* @since 2.6
* @since 2.6.0
*/
public function SetIfNull($sAttCode, $value)
{
@@ -698,21 +701,22 @@ abstract class DBObject implements iDisplay
return $oAttDef->GetLabel();
}
/**
* Getter : get a value from the current object of from a related object
*
* Get the value of the attribute $sAttCode
* This call may involve an object reload if the object was not completely loaded (lazy loading)
*
* @api
*
* @param string $sAttCode Could be an extended attribute code in the form extkey_id->anotherkey_id->remote_attr
*
* @return mixed|string
*
* @throws ArchivedObjectException
* @throws CoreException
*/
/**
* Getter : get a value from the current object of from a related object
*
* Get the value of the attribute $sAttCode
* This call may involve an object reload if the object was not completely loaded (lazy loading)
*
* @api
*
* @param string $sAttCode Could be an extended attribute code in the form extkey_id->anotherkey_id->remote_attr
*
* @return mixed|string
*
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \Exception
*/
public function Get($sAttCode)
{
if (($iPos = strpos($sAttCode, '->')) === false)
@@ -852,17 +856,17 @@ abstract class DBObject implements iDisplay
}
/**
* Get the value as it was before change with Set
*
* The original value vary according to the persisted state
* - not persisted: NULL
* - persisted: the "in DB" value
*
* @see \DBObject::ListPreviousValuesForUpdatedAttributes() to get previous values anywhere in the CRUD stack
* @see https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Asequence_crud iTop CRUD stack documentation
*
* @param string $sAttCode
*
* @return mixed|null the original value
* @return mixed|null the value as it was before changed with {@see DBObject::Set()}.
* Returns null if the attribute wasn't changed.
* Values are reset during {@see DBObject::DBUpdate()}
*
* @throws CoreException
* @throws CoreException if the attribute is unknown for the current object
* @uses DBObject::$m_aOrigValues
*/
public function GetOriginal($sAttCode)
{
@@ -1072,21 +1076,20 @@ abstract class DBObject implements iDisplay
}
/**
* Get $sAttCode formatted as HTML
*
* The returned string is already escaped, and as such is protected against XSS
* The markup relies on a few assumptions (CSS) that could change without notice
*
* @api
*
* @param string $sAttCode
* @param bool $bLocalize
*
* @return string
* @return string $sAttCode formatted as HTML for the console details forms (when viewing, not when editing !)
* The returned string is already escaped, and as such is protected against XSS
* The markup relies on a few assumptions (CSS) that could change without notice
*
* @throws ArchivedObjectException
* @throws CoreException
* @throws DictExceptionMissingString
*
* @see \Combodo\iTop\Form\Field\Field for rendering in portal forms
*/
public function GetAsHTML($sAttCode, $bLocalize = true)
{
@@ -1671,6 +1674,7 @@ abstract class DBObject implements iDisplay
*
* @return integer the binary combination of flags for the given attribute in the given state of the object.
* Values can be one of the OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY, ... (see define in metamodel.class.php)
* Combine multiple values using the "|" operator, for example `OPT_ATT_READONLY | OPT_ATT_HIDDEN`.
*
* @throws \CoreException
*
@@ -1988,7 +1992,7 @@ abstract class DBObject implements iDisplay
* @throws \CoreException
* @throws \OQLException
*
* @since 2.6 N°659 uniqueness constraint
* @since 2.6.0 N°659 uniqueness constraint
* @api
*/
protected function DoCheckUniqueness()
@@ -2036,7 +2040,7 @@ abstract class DBObject implements iDisplay
* @return string dict key : Class:$sClassName/UniquenessRule:$sUniquenessRuleId if none then will use Core:UniquenessDefaultError
* Dictionary keys can contain "$this" placeholders
*
* @since 2.6 N°659 uniqueness constraint
* @since 2.6.0 N°659 uniqueness constraint
*/
protected function GetUniquenessRuleMessage($sUniquenessRuleId)
{
@@ -2088,7 +2092,7 @@ abstract class DBObject implements iDisplay
* @return \DBSearch
* @throws \CoreException
* @throws \OQLException
* @since 2.6 N°659 uniqueness constraint
* @since 2.6.0 N°659 uniqueness constraint
* @api
*/
protected function GetSearchForUniquenessRule($sUniquenessRuleId, $aUniquenessRuleProperties)
@@ -2237,7 +2241,7 @@ abstract class DBObject implements iDisplay
/**
* Check if it is allowed to delete the existing object from the database
*
* an array of displayable error is added in {@link $m_aDeleteIssues}
* an array of displayable error is added in {@see DBObject::$m_aDeleteIssues}
*
* @internal
*
@@ -2375,14 +2379,16 @@ abstract class DBObject implements iDisplay
return $aDelta;
}
/**
* List the attributes that have been changed since the object has been loaded from the DB
*
* @api
* @api-advanced
*
* @return array attname => currentvalue
* @throws Exception
/**
* @api
* @api-advanced
*
* @see \DBObject::ListPreviousValuesForUpdatedAttributes() to get previous values anywhere in the CRUD stack
* @see https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Asequence_crud iTop CRUD stack documentation
* @return array attname => currentvalue List the attributes that have been changed using {@see DBObject::Set()}.
* Reset during {@see DBObject::DBUpdate()}
* @throws Exception
* @uses m_aCurrValues
*/
public function ListChanges()
{
@@ -2390,27 +2396,32 @@ abstract class DBObject implements iDisplay
{
return $this->ListChangedValues($this->m_aCurrValues);
}
else
{
return $this->m_aCurrValues;
}
return $this->m_aCurrValues;
}
/**
* List the changed attributes that were persisted by an update.
*
* @see \DBObject::ListChanges() use DBObject::ListChanges() if your code is BEFORE the update
*
* @return array
* @api
* @api-advanced
*
* To be used during the {@link \DBObject::DBUpdate()} call stack.
*
* To get values that were set to the changed fields, simply use {@link \DBObject::Get()}
*
* @see \DBObject::ListChanges() old method, but using data that are reset during DBObject::DBUpdate
* @return array attname => value : value that was present before the last {@see DBObject::Set()} call.
* This array is set at the beginning of {@see DBObject::DBpdate()} using {@see DBObject::InitPreviousValuesForUpdatedAttributes()}.
* @uses m_aPreviousValuesForUpdatedAttributes
* @since 2.7.0 N°2293
*/
public function ListChangesUpdated()
public function ListPreviousValuesForUpdatedAttributes()
{
if (empty($this->m_aChanges))
if (empty($this->m_aPreviousValuesForUpdatedAttributes))
{
return array();
}
return $this->m_aChanges;
return $this->m_aPreviousValuesForUpdatedAttributes;
}
@@ -2660,7 +2671,7 @@ abstract class DBObject implements iDisplay
*
* @return int key of the newly created object
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException if {@link CheckToWrite()} returns issues
* @throws \CoreCannotSaveObjectException if {@see DBObject::CheckToWrite()} returns issues
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
@@ -2728,50 +2739,72 @@ abstract class DBObject implements iDisplay
}
}
$iTransactionRetry = 1;
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
try
if ($bIsTransactionEnabled)
{
if ($bIsTransactionEnabled)
{
CMDBSource::Query('START TRANSACTION');
}
// First query built upon on the root class, because the ID must be created first
$this->m_iKey = $this->DBInsertSingleTable($sRootClass);
// Then do the leaf class, if different from the root class
if ($sClass != $sRootClass)
{
$this->DBInsertSingleTable($sClass);
}
// Then do the other classes
foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass)
{
if ($sParentClass == $sRootClass)
{
continue;
}
$this->DBInsertSingleTable($sParentClass);
}
$this->OnObjectKeyReady();
$this->DBWriteLinks();
$this->WriteExternalAttributes();
if ($bIsTransactionEnabled)
{
CMDBSource::Query('COMMIT');
}
// TODO Deep clone this object before the transaction (to use it in case of rollback)
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
$iTransactionRetryCount = 1;
$iTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
$iTransactionRetry = $iTransactionRetryCount;
}
catch (Exception $e)
{
if ($bIsTransactionEnabled)
{
CMDBSource::Query('ROLLBACK');
while ($iTransactionRetry > 0) {
try {
$iTransactionRetry--;
if ($bIsTransactionEnabled) {
CMDBSource::Query('START TRANSACTION');
}
// First query built upon on the root class, because the ID must be created first
$this->m_iKey = $this->DBInsertSingleTable($sRootClass);
// Then do the leaf class, if different from the root class
if ($sClass != $sRootClass) {
$this->DBInsertSingleTable($sClass);
}
// Then do the other classes
foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass) {
if ($sParentClass == $sRootClass) {
continue;
}
$this->DBInsertSingleTable($sParentClass);
}
$this->OnObjectKeyReady();
$this->DBWriteLinks();
$this->WriteExternalAttributes();
if ($bIsTransactionEnabled) {
CMDBSource::Query('COMMIT');
}
break;
}
catch (Exception $e) {
IssueLog::Error($e->getMessage());
if ($bIsTransactionEnabled)
{
CMDBSource::Query('ROLLBACK');
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
{
// Deadlock found when trying to get lock; try restarting transaction (only in main transaction)
if ($iTransactionRetry > 0)
{
// wait and retry
IssueLog::Error("Insert TRANSACTION Retrying...");
usleep(random_int(1, 5) * 1000 * $iTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
continue;
}
else
{
IssueLog::Error("Insert Deadlock TRANSACTION prevention failed.");
}
}
}
throw $e;
}
throw $e;
}
$this->m_bIsInDB = true;
@@ -2794,7 +2827,14 @@ abstract class DBObject implements iDisplay
while ($oTrigger = $oSet->Fetch())
{
/** @var \Trigger $oTrigger */
$oTrigger->DoActivate($this->ToArgs('this'));
try
{
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch(Exception $e)
{
utils::EnrichRaisedException($oTrigger, $e);
}
}
$this->RecordObjCreation();
@@ -2909,9 +2949,9 @@ abstract class DBObject implements iDisplay
}
}
/*
* Persist an object to the DB, for the first time
*
/**
* Persist an object to the DB, for the first time
*
* @api
* @see DBWrite
*
@@ -2945,8 +2985,8 @@ abstract class DBObject implements iDisplay
/**
* @internal
*
* @deprecated 2.7.0 N°2361 simply use {@link DBInsert} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@link CMDBObject::SetCurrentChange} before.
* @deprecated 2.7.0 N°2361 simply use {@see DBObject::DBInsert()} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before.
*
* @param CMDBChange $oChange
*
@@ -2962,8 +3002,8 @@ abstract class DBObject implements iDisplay
/**
* @internal
*
* @deprecated 2.7.0 N°2361 simply use {@link DBInsertNoReload} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@link CMDBObject::SetCurrentChange} before.
* @deprecated 2.7.0 N°2361 simply use {@see DBObject::DBInsertNoReload()} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before.
*
* @param CMDBChange $oChange
*
@@ -3025,13 +3065,14 @@ abstract class DBObject implements iDisplay
/**
* Update an object in DB
*
* @api
* @see DBWrite
*
* @api
* @see DBObject::DBWrite()
*
* @return int object key
*
*
* @throws \CoreException
* @throws \CoreCannotSaveObjectException if CheckToWrite() returns issues
* @throws \Exception
*/
public function DBUpdate()
{
@@ -3048,7 +3089,8 @@ abstract class DBObject implements iDisplay
}
$aUpdateReentrance[$sKey] = true;
$this->m_aChanges = array(); // reset attribute to avoid stack collisions
$this->InitPreviousValuesForUpdatedAttributes();
try
{
$this->DoComputeValues();
@@ -3103,8 +3145,13 @@ abstract class DBObject implements iDisplay
array(), $aParams);
while ($oTrigger = $oSet->Fetch())
{
/** @var \Trigger $oTrigger */
$oTrigger->DoActivate($this->ToArgs('this'));
/** @var \TriggerOnObjectUpdate $oTrigger */
try {
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
utils::EnrichRaisedException($oTrigger, $e);
}
}
$bHasANewExternalKeyValue = false;
@@ -3131,9 +3178,11 @@ abstract class DBObject implements iDisplay
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
if ($bIsTransactionEnabled)
{
$iIsTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
// TODO Deep clone this object before the transaction (to use it in case of rollback)
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
$iTransactionRetryCount = 1;
$iIsTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
$iTransactionRetry = $iIsTransactionRetryCount;
$iTransactionRetry = $iTransactionRetryCount;
}
while ($iTransactionRetry > 0)
{
@@ -3201,11 +3250,6 @@ abstract class DBObject implements iDisplay
$this->DBWriteLinks();
$this->WriteExternalAttributes();
$this->m_aChanges = $this->ListChanges(); // N°2293 save changes for use in user callbacks
$this->m_bDirty = false;
$this->m_aTouchedAtt = array();
$this->m_aModifiedAtt = array();
if (count($aChanges) != 0)
{
$this->RecordAttChanges($aChanges, $aOriginalValues);
@@ -3219,18 +3263,18 @@ abstract class DBObject implements iDisplay
}
catch (MySQLException $e)
{
IssueLog::Error($e->getMessage());
if ($bIsTransactionEnabled)
{
CMDBSource::Query('ROLLBACK');
if ($e->getCode() == 1213)
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
{
// Deadlock found when trying to get lock; try restarting transaction
IssueLog::Error($e->getMessage());
// Deadlock found when trying to get lock; try restarting transaction (only in main transaction)
if ($iTransactionRetry > 0)
{
// wait and retry
IssueLog::Error("Update TRANSACTION Retrying...");
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iIsTransactionRetryCount - $iTransactionRetry));
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
continue;
}
else
@@ -3244,10 +3288,11 @@ abstract class DBObject implements iDisplay
'id' => $this->GetKey(),
'class' => get_class($this),
'issues' => $aErrors
));
), $e);
}
catch (CoreCannotSaveObjectException $e)
{
IssueLog::Error($e->getMessage());
if ($bIsTransactionEnabled)
{
CMDBSource::Query('ROLLBACK');
@@ -3256,6 +3301,7 @@ abstract class DBObject implements iDisplay
}
catch (Exception $e)
{
IssueLog::Error($e->getMessage());
if ($bIsTransactionEnabled)
{
CMDBSource::Query('ROLLBACK');
@@ -3269,6 +3315,13 @@ abstract class DBObject implements iDisplay
}
}
// following lines are resetting changes (so after this {@see DBObject::ListChanges()} won't return changes anymore)
// new values are already in the object (call {@see DBObject::Get()} to get them)
// call {@see DBObject::ListPreviousValuesForUpdatedAttributes()} to get changed fields and previous values
$this->m_bDirty = false;
$this->m_aTouchedAtt = array();
$this->m_aModifiedAtt = array();
try
{
$this->AfterUpdate();
@@ -3305,12 +3358,36 @@ abstract class DBObject implements iDisplay
return $this->m_iKey;
}
/**
* @internal
* Save updated fields previous values for {@see DBObject::DBUpdate()} callbacks
* @see DBObject::ListPreviousValuesForUpdatedAttributes() to get the data in the callbacks
* @uses ListChanges
* @uses m_aOrigValues
* @uses m_aPreviousValuesForUpdatedAttributes
* @since 2.7.0 N°2293
* @throws \Exception
*/
private function InitPreviousValuesForUpdatedAttributes()
{
$aChanges= $this->ListChanges();
if (empty($aChanges))
{
$this->m_aPreviousValuesForUpdatedAttributes = array();
return;
}
$aPreviousValuesForUpdatedAttributes = array_intersect_key($this->m_aOrigValues, $aChanges);
$this->m_aPreviousValuesForUpdatedAttributes = $aPreviousValuesForUpdatedAttributes;
}
/**
*
* @internal
*
* @deprecated 2.7.0 N°2361 simply use {@link DBUpdate} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@link CMDBObject::SetCurrentChange} before.
* @deprecated 2.7.0 N°2361 simply use {@see DBObject::DBUpdate()} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before.
*
* @param CMDBChange $oChange
*
@@ -3394,7 +3471,14 @@ abstract class DBObject implements iDisplay
while ($oTrigger = $oSet->Fetch())
{
/** @var \Trigger $oTrigger */
$oTrigger->DoActivate($this->ToArgs('this'));
try
{
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch(Exception $e)
{
utils::EnrichRaisedException($oTrigger, $e);
}
}
$this->RecordObjDeletion($this->m_iKey); // May cause a reload for storing history information
@@ -3436,9 +3520,11 @@ abstract class DBObject implements iDisplay
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
if ($bIsTransactionEnabled)
{
$iIsTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
$iIsTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
$iTransactionRetry = $iIsTransactionRetryCount;
// TODO Deep clone this object before the transaction (to use it in case of rollback)
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
$iTransactionRetryCount = 1;
$iTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
$iTransactionRetry = $iTransactionRetryCount;
}
while ($iTransactionRetry > 0)
{
@@ -3461,18 +3547,18 @@ abstract class DBObject implements iDisplay
}
catch (MySQLException $e)
{
IssueLog::Error($e->getMessage());
if ($bIsTransactionEnabled)
{
CMDBSource::Query('ROLLBACK');
if ($e->getCode() == 1213)
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
{
// Deadlock found when trying to get lock; try restarting transaction
IssueLog::Error($e->getMessage());
if ($iTransactionRetry > 0)
{
// wait and retry
IssueLog::Error("Delete TRANSACTION Retrying...");
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iIsTransactionRetryCount - $iTransactionRetry));
usleep(random_int(1, 5) * 1000 * $iTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
continue;
}
else
@@ -3581,8 +3667,8 @@ abstract class DBObject implements iDisplay
/**
* @internal
*
* @deprecated 2.7.0 N°2361 simply use {@link DBDelete} instead.
* If you need to persist your own, call {@link CMDBObject::SetCurrentChange} before.
* @deprecated 2.7.0 N°2361 simply use {@see DBObject::DBDelete()} instead.
* If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before.
*
* @param CMDBChange $oChange
* @param boolean $bSkipStrongSecurity
@@ -3649,34 +3735,55 @@ abstract class DBObject implements iDisplay
/**
* Apply a stimulus (workflow)
*
* @api
*
* @param string $sStimulusCode
* @param bool $bDoNotWrite
*
*
* @api
*
* @param string $sStimulusCode
* @param bool $bDoNotWrite if true we won't save the object !
*
* @return bool
*
*
* @throws CoreException
* @throws CoreUnexpectedValue
*
* @uses \AttributeStopWatch::Start
* @uses \AttributeStopWatch::Stop
* @uses \DBObject::DBWrite
* @uses \TriggerOnStateLeave::DoActivate
* @uses \TriggerOnStateEnter::DoActivate
*/
public function ApplyStimulus($sStimulusCode, $bDoNotWrite = false)
{
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
$sClass = get_class($this);
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
if (empty($sStateAttCode))
{
throw new CoreException('No lifecycle for the class '.get_class($this));
throw new CoreException('No lifecycle for the class '.$sClass);
}
MyHelpers::CheckKeyInArray('object lifecycle stimulus', $sStimulusCode, MetaModel::EnumStimuli(get_class($this)));
MyHelpers::CheckKeyInArray('object lifecycle stimulus', $sStimulusCode, MetaModel::EnumStimuli($sClass));
$aStateTransitions = $this->EnumTransitions();
if (!array_key_exists($sStimulusCode, $aStateTransitions))
{
// This simulus has no effect in the current state... do nothing
IssueLog::Error(get_class($this).": Transition $sStimulusCode is not allowed in ".$this->Get($sStateAttCode));
// This stimulus has no effect in the current state... do nothing
IssueLog::Error("$sClass: Transition $sStimulusCode is not allowed in ".$this->Get($sStateAttCode));
return false;
}
// save current object values in case of an action failure (in memory rollback)
$aBackupValues = array();
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
if (isset($this->m_aCurrValues[$sAttCode])) {
$value = $this->m_aCurrValues[$sAttCode];
if (is_object($value)) {
$aBackupValues[$sAttCode] = clone $value;
} else {
$aBackupValues[$sAttCode] = $value;
}
}
}
$aTransitionDef = $aStateTransitions[$sStimulusCode];
// Change the state before proceeding to the actions, this is necessary because an action might
@@ -3695,11 +3802,11 @@ abstract class DBObject implements iDisplay
{
// Old (pre-2.1.0 modules) action definition without any parameter
$aActionCallSpec = array($this, $actionHandler);
$sActionDesc = get_class($this).'::'.$actionHandler;
$sActionDesc = $sClass.'::'.$actionHandler;
if (!is_callable($aActionCallSpec))
{
throw new CoreException("Unable to call action: ".get_class($this)."::$actionHandler");
throw new CoreException("Unable to call action: $sClass::$actionHandler");
}
$bRet = call_user_func($aActionCallSpec, $sStimulusCode);
}
@@ -3707,7 +3814,7 @@ abstract class DBObject implements iDisplay
{
// New syntax: 'verb' and typed parameters
$sAction = $actionHandler['verb'];
$sActionDesc = get_class($this).'::'.$sAction;
$sActionDesc = "$sClass::$sAction";
$aParams = array();
foreach($actionHandler['params'] as $aDefinition)
{
@@ -3743,14 +3850,12 @@ abstract class DBObject implements iDisplay
// (in case there is no returned value, null is obtained and means "ok")
if ($bRet === false)
{
IssueLog::Info("Lifecycle action $sActionDesc returned false on object #".$this->GetKey());
IssueLog::Info("Lifecycle action $sActionDesc returned false on object #$sClass:".$this->GetKey());
$bSuccess = false;
}
}
if ($bSuccess)
{
$sClass = get_class($this);
// Stop watches
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
@@ -3769,8 +3874,7 @@ abstract class DBObject implements iDisplay
}
}
if (!$bDoNotWrite)
{
if (!$bDoNotWrite) {
$this->DBWrite();
}
@@ -3778,19 +3882,36 @@ abstract class DBObject implements iDisplay
$aParams = array(
'class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL),
'previous_state' => $sPreviousState,
'new_state' => $sNewState);
'new_state' => $sNewState,
);
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class IN (:class_list) AND t.state=:previous_state"), array(), $aParams);
while ($oTrigger = $oSet->Fetch())
{
/** @var \Trigger $oTrigger */
$oTrigger->DoActivate($this->ToArgs('this'));
while ($oTrigger = $oSet->Fetch()) {
/** @var \TriggerOnStateLeave $oTrigger */
try {
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
utils::EnrichRaisedException($oTrigger, $e);
}
}
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class IN (:class_list) AND t.state=:new_state"), array(), $aParams);
while ($oTrigger = $oSet->Fetch())
while ($oTrigger = $oSet->Fetch()) {
/** @var \TriggerOnStateEnter $oTrigger */
try {
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
utils::EnrichRaisedException($oTrigger, $e);
}
}
}
else
{
// At least one action failed, rollback the object value to its previous value
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
/** @var \Trigger $oTrigger */
$oTrigger->DoActivate($this->ToArgs('this'));
$this->m_aCurrValues[$sAttCode] = $aBackupValues[$sAttCode];
}
}
@@ -3818,7 +3939,32 @@ abstract class DBObject implements iDisplay
*/
public function Copy($sDestAttCode, $sSourceAttCode)
{
$this->Set($sDestAttCode, $this->Get($sSourceAttCode));
$oTypeValueToCopy = MetaModel::GetAttributeDef(get_class($this), $sSourceAttCode);
$oTypeValueDest = MetaModel::GetAttributeDef(get_class($this), $sDestAttCode);
if ($oTypeValueToCopy instanceof AttributeText && $oTypeValueDest instanceof AttributeText)
{
if ($oTypeValueToCopy->GetFormat() == $oTypeValueDest->GetFormat())
{
$sValueToCopy = $this->Get($sSourceAttCode);
}
else
{
if ($oTypeValueToCopy->GetFormat() == 'text')// and $oTypeValueDest->GetFormat()=='HTML'
{
$sValueToCopy = $this->GetAsHTML($sSourceAttCode);
}
else
{// $oTypeValueToCopy->GetFormat() == 'HTML' and $oTypeValueDest->GetFormat()=='Text'
$sValueToCopy = utils::HtmlToText($this->Get($sSourceAttCode));
}
}
}
else
{
$sValueToCopy = $this->Get($sSourceAttCode);
}
$this->Set($sDestAttCode, $sValueToCopy);
return true;
}
@@ -4135,13 +4281,15 @@ abstract class DBObject implements iDisplay
}
/**
* This method is called after the object is updated into DB. You can get changes using @link m_aChanges}.
*
* Warning : do not use {@link ListChanges} as it will return an empty array.
*
* @overwritable-hook You can extend this method in order to provide your own logic.
*
* @since 2.7.0 N°2293 can access object changes using {@link m_aChanges}
* This method is called after the object is updated into DB, and just before the {@see DBObject::Reload()} call.
*
* Warning : do not use {@see DBObject::ListChanges()} as it will return an empty array !
* Use instead {@see DBObject::ListPreviousValuesForUpdatedAttributes()} to get modified fields and their previous values,
* and {@see DBObject::Get()} to get the persisted value for a given attribute.
*
* @since 2.7.0 N°2293 can access object changes by calling {@see DBObject::ListPreviousValuesForUpdatedAttributes()}
*/
protected function AfterUpdate()
{
@@ -5174,10 +5322,6 @@ abstract class DBObject implements iDisplay
throw new Exception('Missing argument #1: stimulus');
}
$sStimulus = $aParams[0];
if (!in_array($sStimulus, MetaModel::EnumStimuli(get_class($this))))
{
throw new Exception("Unknown stimulus ".get_class($this)."::".$sStimulus);
}
$this->ApplyStimulus($sStimulus);
break;

View File

@@ -63,9 +63,15 @@ class DBObjectSearch extends DBSearch
{
parent::__construct();
if (is_null($sClassAlias)) $sClassAlias = $sClass;
if(!is_string($sClass)) throw new Exception('DBObjectSearch::__construct called with a non-string parameter: $sClass = '.print_r($sClass, true));
if(!MetaModel::IsValidClass($sClass)) throw new Exception('DBObjectSearch::__construct called for an invalid class: "'.$sClass.'"');
if (is_null($sClassAlias)) {
$sClassAlias = $sClass;
}
if (!is_string($sClass)) {
throw new Exception('DBObjectSearch::__construct called with a non-string parameter: $sClass = '.print_r($sClass, true));
}
if (!MetaModel::IsValidClass($sClass)) {
throw new Exception('DBObjectSearch::__construct called for an invalid class: "'.$sClass.'"');
}
$this->m_aSelectedClasses = array($sClassAlias => $sClass);
$this->m_aClasses = array($sClassAlias => $sClass);
@@ -75,30 +81,43 @@ class DBObjectSearch extends DBSearch
$this->m_aReferencedBy = array();
}
public function AllowAllData($bAllowAllData = true) {$this->m_bAllowAllData = $bAllowAllData;}
public function IsAllDataAllowed() {return $this->m_bAllowAllData;}
protected function IsDataFiltered() {return $this->m_bDataFiltered; }
protected function SetDataFiltered() {$this->m_bDataFiltered = true;}
public function AllowAllData($bAllowAllData = true) {
$this->m_bAllowAllData = $bAllowAllData;
$this->m_oSearchCondition->Browse(function ($oThisExpression) use ($bAllowAllData) {
ExpressionHelper::ExpressionAllowAllDataCallback($oThisExpression, $bAllowAllData);
});
}
public function IsAllDataAllowed() {
return $this->m_bAllowAllData;
}
protected function IsDataFiltered() {
return $this->m_bDataFiltered;
}
protected function SetDataFiltered() {
$this->m_bDataFiltered = true;
}
// Create a search definition that leads to 0 result, still a valid search object
static public function FromEmptySet($sClass)
{
public static function FromEmptySet($sClass) {
$oResultFilter = new DBObjectSearch($sClass);
$oResultFilter->m_oSearchCondition = new FalseExpression;
return $oResultFilter;
}
public function GetJoinedClasses() {return $this->m_aClasses;}
public function GetJoinedClasses() {
return $this->m_aClasses;
}
public function GetClassName($sAlias)
{
if (array_key_exists($sAlias, $this->m_aSelectedClasses))
{
public function GetClassName($sAlias) {
if (array_key_exists($sAlias, $this->m_aSelectedClasses)) {
return $this->m_aSelectedClasses[$sAlias];
}
else
{
} else {
throw new CoreException("Invalid class alias '$sAlias'");
}
}
@@ -223,9 +242,9 @@ class DBObjectSearch extends DBSearch
public function RenameAlias($sOldName, $sNewName)
{
$bFound = false;
if (array_key_exists($sOldName, $this->m_aClasses))
if (!array_key_exists($sOldName, $this->m_aClasses))
{
$bFound = true;
return false;
}
if (array_key_exists($sNewName, $this->m_aClasses))
{
@@ -313,6 +332,11 @@ class DBObjectSearch extends DBSearch
return true;
}
/**
* Move conditions from $oFilter to $this
* @param \DBSearch $oFilter
* @param $aTranslation
*/
protected function TransferConditionExpression($oFilter, $aTranslation)
{
// Prevent collisions in the parameter names by renaming them if needed
@@ -335,6 +359,7 @@ class DBObjectSearch extends DBSearch
$oTranslated = $oFilter->GetCriteria()->Translate($aTranslation, false, false /* leave unresolved fields */);
$this->AddConditionExpression($oTranslated);
$this->m_aParams = array_merge($this->m_aParams, $oFilter->m_aParams);
$oFilter->ResetCondition();
}
public function RenameParam($sOldName, $sNewName)
@@ -352,37 +377,35 @@ class DBObjectSearch extends DBSearch
}
foreach($this->m_aReferencedBy as $sForeignClass => $aReferences)
{
foreach($aReferences as $sForeignExtKeyAttCode => $aFiltersByOperator)
{
foreach ($aFiltersByOperator as $iOperatorCode => $aFilters)
{
foreach ($aFilters as $oForeignFilter)
{
foreach($aReferences as $sForeignExtKeyAttCode => $aFiltersByOperator) {
foreach ($aFiltersByOperator as $iOperatorCode => $aFilters) {
foreach ($aFilters as $oForeignFilter) {
$oForeignFilter->RenameParam($sOldName, $sNewName);
}
}
}
}
}
public function ResetCondition()
{
public function ResetCondition() {
$this->m_oSearchCondition = new TrueExpression();
// ? is that usefull/enough, do I need to rebuild the list after the subqueries ?
}
public function MergeConditionExpression($oExpression)
{
$this->m_oSearchCondition = $this->m_oSearchCondition->LogOr($oExpression);
public function MergeConditionExpression($oExpression) {
$this->m_oSearchCondition = $this->m_oSearchCondition->LogOr($oExpression);
}
public function AddConditionExpression($oExpression)
{
$this->m_oSearchCondition = $this->m_oSearchCondition->LogAnd($oExpression);
public function AddConditionExpression($oExpression) {
$this->m_oSearchCondition = $this->m_oSearchCondition->LogAnd($oExpression);
$bRootSearchAllowAllData = $this->IsAllDataAllowed();
$oExpression->Browse(function ($oThisExpression) use ($bRootSearchAllowAllData) {
ExpressionHelper::ExpressionAllowAllDataCallback($oThisExpression, $bRootSearchAllowAllData);
});
}
public function AddNameCondition($sName)
{
public function AddNameCondition($sName) {
$oValueExpr = new ScalarExpression($sName);
$oNameExpr = new FieldExpression('friendlyname', $this->GetClassAlias());
$oNewCondition = new BinaryExpression($oNameExpr, '=', $oValueExpr);
@@ -437,7 +460,6 @@ class DBObjectSearch extends DBSearch
case '<|':
case '=|':
throw new CoreException('Deprecated operator, please consider using OQL (SQL) expressions like "(TO_DAYS(NOW()) - TO_DAYS(x)) AS AgeDays"', array('operator' => $sOpCode));
break;
case 'IN':
if (!is_array($value)) $value = array($value);
@@ -522,13 +544,15 @@ class DBObjectSearch extends DBSearch
}
/**
* Helper method for IN / NOT IN conditions : values won't be parsed in the expression tree, that will save some time !
*
* @param string $sFilterCode attribute code to use
* @param array $aValues
* @param bool $bPositiveMatch if true will add a IN filter, else a NOT IN
*
* @throws \CoreException
*
* @since 2.5 N°1418
* @since 2.5.0 N°1418
*/
public function AddConditionForInOperatorUsingParam($sFilterCode, $aValues, $bPositiveMatch = true)
{
@@ -617,22 +641,40 @@ class DBObjectSearch extends DBSearch
public function AddCondition_FullText($sNeedle)
{
// Transform the full text condition into additional condition expression
$aFullTextFields = array();
foreach (MetaModel::ListAttributeDefs($this->GetClass()) as $sAttCode => $oAttDef)
{
$aAttCodes = [];
foreach (MetaModel::ListAttributeDefs($this->GetClass()) as $sAttCode => $oAttDef) {
if (!$oAttDef->IsScalar()) continue;
if ($oAttDef->IsExternalKey()) continue;
if (!$oAttDef->IsSearchable()) continue;
$aAttCodes[] = $sAttCode;
}
$this->AddCondition_FullTextOnAttributes($aAttCodes, $sNeedle);
}
/**
* @param array $aAttCodes array of attCodes to search into
* @param string $sNeedle one word to be searched
*
* @throws \CoreException
*/
public function AddCondition_FullTextOnAttributes(array $aAttCodes, $sNeedle)
{
$aFullTextFields = [];
foreach ($aAttCodes as $sAttCode) {
$aFullTextFields[] = new FieldExpression($sAttCode, $this->GetClassAlias());
}
$oTextFields = new CharConcatWSExpression(' ', $aFullTextFields);
$sQueryParam = 'needle';
$sQueryParam = str_replace('.', '', uniqid('needle_', true));
$oFlexNeedle = new CharConcatExpression(array(new ScalarExpression('%'), new VariableExpression($sQueryParam), new ScalarExpression('%')));
$oNewCond = new BinaryExpression($oTextFields, 'LIKE', $oFlexNeedle);
$this->AddConditionExpression($oNewCond);
$this->m_aParams[$sQueryParam] = $sNeedle;
//replace in order to search the character "_" ("_" in mysql is like "%" for only one character).
$sFullText = str_replace('_', '\_', $sNeedle);
$this->m_aParams[$sQueryParam] = $sFullText;
}
protected function AddToNameSpace(&$aClassAliases, &$aAliasTranslation, $bTranslateMainAlias = true)
@@ -781,10 +823,11 @@ class DBObjectSearch extends DBSearch
* Helper to
* - convert a translation table (format optimized for the translation in an expression tree) into simple hash
* - compile over an eventually existing map
* - accept multiple translations for the same alias for unions
*
* @param array $aRealiasingMap Map to update
* @param array $aAliasTranslation Translation table resulting from calls to MergeWith_InNamespace
* @return void of <old-alias> => <new-alias>
* @return void of [old-alias][] => new-alias (@since 2.7.2)
*/
protected function UpdateRealiasingMap(&$aRealiasingMap, $aAliasTranslation)
{
@@ -792,17 +835,33 @@ class DBObjectSearch extends DBSearch
{
foreach ($aAliasTranslation as $sPrevAlias => $aRules)
{
if (isset($aRules['*']))
if (!isset($aRules['*']))
{
$sNewAlias = $aRules['*'];
$sOriginalAlias = array_search($sPrevAlias, $aRealiasingMap);
if ($sOriginalAlias !== false)
continue;
}
$sNewAlias = $aRules['*'];
$bOriginalFound = false;
$iIndex = 0;
foreach ($aRealiasingMap as $sOriginalAlias => $aAliases)
{
$iIndex = array_search($sPrevAlias, $aAliases);
if ($iIndex !== false)
{
$aRealiasingMap[$sOriginalAlias] = $sNewAlias;
$bOriginalFound = true;
break;
}
else
}
if ($bOriginalFound)
{
$aRealiasingMap[$sOriginalAlias][$iIndex] = $sNewAlias;
}
else
{
if (!isset($aRealiasingMap[$sPrevAlias]) || !in_array($sNewAlias, $aRealiasingMap[$sPrevAlias]))
{
$aRealiasingMap[$sPrevAlias] = $sNewAlias;
$aRealiasingMap[$sPrevAlias][] = $sNewAlias;
}
}
}
@@ -848,7 +907,7 @@ class DBObjectSearch extends DBSearch
}
/**
* @param DBObjectSearch $oFilter
* @param DBObjectSearch $oFilter (can be modified)
* @param $sExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
@@ -940,7 +999,7 @@ class DBObjectSearch extends DBSearch
}
/**
* @param DBObjectSearch $oFilter
* @param DBObjectSearch $oFilter (can be modified)
* @param $sForeignExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
@@ -1036,7 +1095,7 @@ class DBObjectSearch extends DBSearch
public function Filter($sClassAlias, DBSearch $oFilter)
{
// If the conditions are the correct ones for Intersect
if (($this->GetFirstJoinedClass() == $oFilter->GetFirstJoinedClass()))
if (MetaModel::IsParentClass($oFilter->GetFirstJoinedClass(),$this->GetFirstJoinedClass()))
{
return $this->Intersect($oFilter);
}
@@ -1068,7 +1127,6 @@ class DBObjectSearch extends DBSearch
{
if (($oSearch->GetFirstJoinedClassAlias() == $sClassAlias))
{
$oSearch->ResetCondition();
$oSearch = $oSearch->IntersectSubClass($oFilter, $aRootClasses);
return $oSearch->GetCriteria();
}
@@ -2087,4 +2145,9 @@ class DBObjectSearch extends DBSearch
}
return $oExpression;
}
public function ListParameters()
{
return $this->GetCriteria()->ListParameters();
}
}

View File

@@ -424,7 +424,7 @@ class DBObjectSet implements iDBObjectSetIterator
*
* @api
*
* @param bool $bWithId
* @param bool $bWithId if true array key will be set to object id
*
* @return DBObject[]
*

View File

@@ -97,42 +97,40 @@ abstract class DBSearch
/**
* Perform a deep clone (as opposed to "clone" which does copy a reference to the underlying objects)
*
* @internal
*
* @internal
*
* @return \DBSearch
**/
**/
public function DeepClone()
{
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();
/**
* @api
* @see IsAllDataAllowed()
*
* @param bool $bAllowAllData whether or not some information should be hidden to the current user.
*/
abstract public function AllowAllData($bAllowAllData = true);
/**
* Current state of AllowAllData
*
* @internal
* @see AllowAllData()
*
* @return mixed
*/
/**
* Current state of AllowAllData
*
* @internal
* @see AllowAllData()
*
* @return mixed
*/
abstract public function IsAllDataAllowed();
/**
* Should the archives be fetched
*
* @internal
*
* @param $bEnable
*/
/**
* Should the archives be fetched
*
* @internal
*
* @param $bEnable
*/
public function SetArchiveMode($bEnable)
{
$this->m_bArchiveMode = $bEnable;
@@ -238,6 +236,12 @@ abstract class DBSearch
*/
abstract public function GetClassAlias();
/**
* @return string
* @internal
*/
abstract public function GetFirstJoinedClass();
/**
* Change the class
*
@@ -398,7 +402,9 @@ abstract class DBSearch
*/
abstract public function AddCondition_FullText($sFullText);
/**
abstract public function AddCondition_FullTextOnAttributes(array $aAttCodes, $sNeedle);
/**
* Perform a join, the remote class being matched by the mean of its primary key
*
* The join is performed
@@ -501,6 +507,7 @@ abstract class DBSearch
}
else
{
/** @var \DBObjectSearch $oFilter */
if ($iDirection === static::JOIN_POINTING_TO)
{
$oSourceFilter->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
@@ -624,7 +631,7 @@ abstract class DBSearch
}
$sOql = $this->ToOql($bDevelopParams, $aContextParams);
return json_encode(array($sOql, $aQueryParams, $this->m_aModifierProperties));
return urlencode(json_encode(array($sOql, $aQueryParams, $this->m_aModifierProperties)));
}
/**
@@ -641,7 +648,7 @@ abstract class DBSearch
*/
static public function unserialize($sValue)
{
$aData = json_decode($sValue, true);
$aData = json_decode(urldecode($sValue), true);
if (is_null($aData))
{
throw new CoreException("Invalid filter parameter");
@@ -973,7 +980,7 @@ abstract class DBSearch
$aAttToLoad = array();
$oSQLQuery = $oQueryFilter->GetSQLQuery(array(), $aArgs, $aAttToLoad, null, 0, 0, false, $aGroupByExpr, $aSelectExpr);
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams(), $this->GetExpectedArguments());
try
{
$bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
@@ -991,12 +998,14 @@ abstract class DBSearch
return $sRes;
}
function GetExpectedArguments()
{
return $this->GetCriteria()->ListParameters();
}
/**
* Generate a SQL query from the current search
*
* @internal
*
* Generate a SQL query from the current search
*
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
* @param array $aArgs
* @param null $aAttToLoad
@@ -1004,12 +1013,16 @@ abstract class DBSearch
* @param int $iLimitCount
* @param int $iLimitStart
* @param bool $bGetCount
* @param bool $bBeautifulSQL
*
* @return string
* @throws CoreException
* @throws Exception
* @throws MissingQueryArgument
* @throws \ConfigException
* @throws \CoreException
* @throws \MissingQueryArgument
* @internal
*
*/
public function MakeSelectQuery($aOrderBy = array(), $aArgs = array(), $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false)
public function MakeSelectQuery($aOrderBy = array(), $aArgs = array(), $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false, $bBeautifulSQL = true)
{
// Check the order by specification, and prefix with the class alias
// and make sure that the ordering columns are going to be selected
@@ -1070,12 +1083,11 @@ abstract class DBSearch
else
{
// The complete list of arguments will include magic arguments (e.g. current_user->attcode)
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams(), $this->GetExpectedArguments());
}
try
{
// $bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, true);
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, $bBeautifulSQL);
if ($sClassAlias == '_itop_')
{
IssueLog::Info('SQL Query (_itop_): '.$sRes);
@@ -1101,6 +1113,8 @@ abstract class DBSearch
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*
* @since 2.7.0 N°2555
*/
public function GetFirstResult($bMustHaveOneResultMax = true, $aOrderBy = array(), $aSearchParams = array())
{
@@ -1168,12 +1182,35 @@ abstract class DBSearch
if (is_object($oVisibleObjects))
{
$oVisibleObjects->AllowAllData();
$oSearch = $this->Filter($sClassAlias, $oVisibleObjects);
/** @var DBSearch $oSearch */
$oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects);
$oSearch->SetDataFiltered();
}
}
}
if (is_array($aGroupByExpr))
{
foreach($aGroupByExpr as $sAlias => $oGroupByExp)
{
/** @var \Expression $oGroupByExp */
$aFields = $oGroupByExp->ListRequiredFields();
foreach($aFields as $sFieldAlias)
{
$aMatches = array();
if (preg_match('/^([^.]+)\\.([^.]+)$/', $sFieldAlias, $aMatches))
{
$sFieldClass = $this->GetClassName($aMatches[1]);
$oAttDef = MetaModel::GetAttributeDef($sFieldClass, $aMatches[2]);
if ( $oAttDef instanceof iAttributeNoGroupBy)
{
throw new Exception("Grouping on '$sFieldClass' fields is not supported.");
}
}
}
}
}
$oSQLQuery = $oSearch->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, null, $aSelectExpr);
$oSQLQuery->SetSourceOQL($oSearch->ToOQL());
@@ -1220,6 +1257,8 @@ abstract class DBSearch
*/
public abstract function GetCriteria();
public abstract function ListParameters();
/**
* Shortcut to add efficient IN condition
*

View File

@@ -65,9 +65,7 @@ class DBUnionSearch extends DBSearch
{
$this->aSearches[] = $oSubSearch->DeepClone();
}
}
else
{
} else {
$this->aSearches[] = $oSearch->DeepClone();
}
}
@@ -75,17 +73,16 @@ class DBUnionSearch extends DBSearch
$this->ComputeSelectedClasses();
}
public function AllowAllData()
public function AllowAllData($bAllowAllData = true)
{
foreach ($this->aSearches as $oSearch)
{
foreach ($this->aSearches as $oSearch) {
$oSearch->AllowAllData();
}
}
public function IsAllDataAllowed()
{
foreach ($this->aSearches as $oSearch)
{
foreach ($this->aSearches as $oSearch) {
if ($oSearch->IsAllDataAllowed() === false) return false;
}
return true;
@@ -161,6 +158,11 @@ class DBUnionSearch extends DBSearch
return $this->aSearches;
}
public function GetFirstJoinedClass()
{
return $this->GetClass();
}
/**
* Limited to the selected classes
*/
@@ -371,19 +373,26 @@ class DBUnionSearch extends DBSearch
}
}
/**
public function AddCondition_FullTextOnAttributes(array $aAttCodes, $sNeedle)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddCondition_FullTextOnAttributes($aAttCodes, $sNeedle);
}
}
/**
* @param DBObjectSearch $oFilter
* @param $sExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
* @throws CoreException
* @throws CoreWarning
* @param null $aRealiasingMap array of [old-alias][] => <new-alias>, for each alias that has changed (@since 2.7.2)
*/
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
$oConditionFilter = $oFilter->DeepClone();
$oSearch->AddCondition_PointingTo($oConditionFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
}
}
@@ -391,13 +400,14 @@ class DBUnionSearch extends DBSearch
* @param DBObjectSearch $oFilter
* @param $sForeignExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
* @param null $aRealiasingMap array of [old-alias][] => <new-alias>, for each alias that has changed (@since 2.7.2)
*/
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddCondition_ReferencedBy($oFilter, $sForeignExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
$oConditionFilter = $oFilter->DeepClone();
$oSearch->AddCondition_ReferencedBy($oConditionFilter, $sForeignExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
}
}
@@ -662,6 +672,16 @@ class DBUnionSearch extends DBSearch
return $oSQLQuery;
}
function GetExpectedArguments()
{
$aVariableCriteria = array();
foreach ($this->aSearches as $oSearch)
{
$aVariableCriteria = array_merge($aVariableCriteria, $oSearch->GetExpectedArguments());
}
return $aVariableCriteria;
}
/**
* @return \Expression
*/
@@ -713,4 +733,14 @@ class DBUnionSearch extends DBSearch
$oSearch->AddConditionExpression($oInCondition);
}
}
public function ListParameters()
{
$aParameters = array();
foreach ($this->aSearches as $oSearch)
{
$aParameters = array_merge($aParameters, $oSearch->ListParameters());
}
return $aParameters;
}
}

View File

@@ -304,8 +304,12 @@ class EMail
$oHeaders = $this->m_oMessage->getHeaders();
switch(strtolower($sKey))
{
case 'return-path':
$this->m_oMessage->setReturnPath($sValue);
break;
default:
$oHeaders->addTextHeader($sKey, $sValue);
$oHeaders->addTextHeader($sKey, $sValue);
}
}
}

View File

@@ -215,7 +215,7 @@ class EventIssue extends Event
MetaModel::Init_AddAttribute(new AttributePropertySet("data", array("allowed_values"=>null, "sql"=>"data", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'issue', 'impact', 'page', 'arguments_post', 'arguments_get', 'callstack', 'data')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo', 'issue', 'impact', 'page', 'arguments_post', 'arguments_get', 'callstack', 'data')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'issue', 'impact')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form

View File

@@ -193,7 +193,7 @@ EOF
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
}
else if ($value instanceOf ormTagSet)
else if ($value instanceOf ormSet)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
@@ -216,7 +216,14 @@ EOF
}
else if (array_key_exists('formatted_text', $this->aStatusInfo) && $this->aStatusInfo['formatted_text'])
{
$sRet = $oAttDef->GetEditValue($value, $oObj);
if ($oAttDef instanceof AttributeText && $oAttDef->GetFormat()=='html')
{
$sRet = str_replace("&gt;", ">", $value);
}
else
{
$sRet = $oAttDef->GetEditValue($value, $oObj);
}
}
else
{
@@ -346,7 +353,8 @@ EOF
$fStartExcel = microtime(true);
$writer = new XLSXWriter();
$oDateTimeFormat = new DateTimeFormat($this->aStatusInfo['date_format']);
$sDateFormat = isset($this->aStatusInfo['date_format']) ? $this->aStatusInfo['date_format'] : (string)AttributeDateTime::GetFormat();
$oDateTimeFormat = new DateTimeFormat($sDateFormat);
$writer->setDateTimeFormat($oDateTimeFormat->ToExcel());
$oDateFormat = new DateTimeFormat($oDateTimeFormat->ToDateFormat());
$writer->setDateFormat($oDateFormat->ToExcel());
@@ -386,4 +394,4 @@ EOF
{
return array('xlsx' => Dict::S('Core:BulkExport:XLSXFormat'));
}
}
}

View File

@@ -183,7 +183,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
'h4' => array('style'),
'nav' => array('style'),
'section' => array('style'),
'code' => array('style'),
'code' => array('style', 'class'),
'table' => array('style', 'width', 'summary', 'align', 'border', 'cellpadding', 'cellspacing'),
'thead' => array('style'),
'tbody' => array('style'),

View File

@@ -176,26 +176,30 @@ class InlineImage extends DBObject
$sOQL = 'SELECT InlineImage WHERE temp_id = :temp_id';
$oSearch = DBObjectSearch::FromOQL($sOQL);
$oSet = new DBObjectSet($oSearch, array(), array('temp_id' => $sTempId));
while($oInlineImage = $oSet->Fetch())
{
$aInlineImagesId = array();
while ($oInlineImage = $oSet->Fetch()) {
$aInlineImagesId[] = $oInlineImage->GetKey();
$oInlineImage->SetItem($oObject);
$oInlineImage->Set('temp_id', '');
$oInlineImage->DBUpdate();
}
IssueLog::Trace('FinalizeInlineImages (see $aInlineImagesId for the id list)', LogChannels::INLINE_IMAGE, array(
'$sObjectClass' => get_class($oObject),
'$sTransactionId' => $iTransactionId,
'$sTempId' => $sTempId,
'$aInlineImagesId' => $aInlineImagesId,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
));
}
// For tracing issues with Inline Images... but beware not all updates are interactive, so this trace happens when creating objects non-interactively (REST, Synchro...)
// else
// {
// IssueLog::Error('InlineImage: Error during FinalizeInlineImages(), no transaction ID for object '.get_class($oObject).'#'.$oObject->GetKey().'.');
//
// IssueLog::Error('|- Call stack:');
// $oException = new Exception();
// $sStackTrace = $oException->getTraceAsString();
// IssueLog::Error($sStackTrace);
//
// IssueLog::Error('|- POST vars:');
// IssueLog::Error(print_r($_POST, true));
// }
else {
IssueLog::Trace('FinalizeInlineImages "error" $iTransactionId is null', LogChannels::INLINE_IMAGE, array(
'$sObjectClass' => get_class($oObject),
'$sTransactionId' => $iTransactionId,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
));
}
}
/**
@@ -208,10 +212,18 @@ class InlineImage extends DBObject
$sOQL = 'SELECT InlineImage WHERE temp_id = :temp_id';
$oSearch = DBObjectSearch::FromOQL($sOQL);
$oSet = new DBObjectSet($oSearch, array(), array('temp_id' => $sTempId));
$aInlineImagesId = array();
while($oInlineImage = $oSet->Fetch())
{
$aInlineImagesId[] = $oInlineImage->GetKey();
$oInlineImage->DBDelete();
}
IssueLog::Trace('OnFormCancel', LogChannels::INLINE_IMAGE, array(
'$sTempId' => $sTempId,
'$aInlineImagesId' => $aInlineImagesId,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
));
}
/**
@@ -548,19 +560,72 @@ EOF
JS
;
}
protected function AfterInsert()
{
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
'id' => $this->GetKey(),
'expire' => $this->Get('expire'),
'temp_id' => $this->Get('temp_id'),
'item_class' => $this->Get('item_class'),
'item_id' => $this->Get('item_id'),
'item_org_id' => $this->Get('item_org_id'),
'secret' => $this->Get('secret'),
'user' => $sUser = UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
parent::AfterInsert();
}
protected function AfterUpdate()
{
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
'id' => $this->GetKey(),
'expire' => $this->Get('expire'),
'temp_id' => $this->Get('temp_id'),
'item_class' => $this->Get('item_class'),
'item_id' => $this->Get('item_id'),
'item_org_id' => $this->Get('item_org_id'),
'secret' => $this->Get('secret'),
'user' => $sUser = UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
parent::AfterUpdate();
}
protected function AfterDelete()
{
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
'id' => $this->GetKey(),
'expire' => $this->Get('expire'),
'temp_id' => $this->Get('temp_id'),
'item_class' => $this->Get('item_class'),
'item_id' => $this->Get('item_id'),
'item_org_id' => $this->Get('item_org_id'),
'secret' => $this->Get('secret'),
'user' => $sUser = UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
parent::AfterDelete();
}
}
/**
* Garbage collector for cleaning "old" temporary InlineImages (and Attachments).
* This background process runs every hour and deletes all temporary InlineImages and Attachments
* whic are are older than one hour.
*/
class InlineImageGC implements iBackgroundProcess
{
public function GetPeriodicity()
{
return 1; // Runs every 8 hours
return 1;
}
/**
@@ -593,6 +658,9 @@ class InlineImageGC implements iBackgroundProcess
}
/**
* Remove $sClass instance based on their `expire` field value.
* This `expire` field contains current time + draft_attachments_lifetime config parameter, it is initialized on object creation.
*
* @param string $sClass
* @param int $iTimeLimit
* @param string $sDateLimit

View File

@@ -546,7 +546,7 @@ class DBObjectSearch extends DBSearch
*
* @throws \CoreException
*
* @since 2.5 N°1418
* @since 2.5.0 N°1418
*/
public function AddConditionForInOperatorUsingParam($sFilterCode, $aValues, $bPositiveMatch = true)
{
@@ -1678,6 +1678,25 @@ class DBObjectSearch extends DBSearch
return $sRet;
}
/**
* Generate an INSERT statement.
* Note : unlike `RenderUpdate` and `RenderSelect`, it is limited to one and only one table.
*
* @param array $aValues is an array of $sAttCode => $value
* @param array $aArgs
*
* @return string
* @throws \CoreException
*/
public function MakeInsertQuery($aValues, $aArgs = array())
{
$oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($this);
$oSQLQuery = $oSQLObjectQueryBuilder->MakeSQLObjectUpdateQuery($aValues);
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
$sRet = $oSQLQuery->RenderInsert($aScalarArgs);
return $sRet;
}
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
{
// Hide objects that are not visible to the current user
@@ -2583,5 +2602,18 @@ class DBObjectSearch extends DBSearch
return $oExpression;
}
/**
* @param array $aAttCodes array of attCodes to search into
* @param string $sNeedle one word to be searched
*
* @throws \CoreException
*/
public function AddCondition_FullTextOnAttributes(array $aAttCodes, $sNeedle)
{
}
public function ListParameters()
{
return $this->GetCriteria()->ListParameters();
}
}

View File

@@ -18,24 +18,36 @@
/**
* @since 2.7.0 N°2518
* @since 2.7.0 N°2518 N°2793
*/
interface ILogFileNameBuilder
interface iLogFileNameBuilder
{
public function __construct($sFileFullPath);
/**
* @param string $sLogFileFullPath full path name for the log file
*/
public function __construct($sLogFileFullPath = null);
/**
* @return string log file path we will write new log entry to
*/
public function GetLogFilePath();
}
class DefaultLogFileNameBuilder implements ILogFileNameBuilder
class DefaultLogFileNameBuilder implements iLogFileNameBuilder
{
private $sLogFileFullPath;
public function __construct($sFileFullPath)
/**
* @inheritDoc
*/
public function __construct($sLogFileFullPath = null)
{
$this->sLogFileFullPath = $sFileFullPath;
$this->sLogFileFullPath = $sLogFileFullPath;
}
/**
* @inheritDoc
*/
public function GetLogFilePath()
{
return $this->sLogFileFullPath;
@@ -45,60 +57,363 @@ class DefaultLogFileNameBuilder implements ILogFileNameBuilder
/**
* Adds a suffix to the filename
*
* @since 2.7.0 N°2518
* @since 2.7.0 N°2518 N°2793
*/
abstract class RotatingLogFileNameBuilder implements ILogFileNameBuilder
abstract class RotatingLogFileNameBuilder implements iLogFileNameBuilder
{
/**
* Test is done each time to cover edge case like session beginning at 23:59 and ending at 00:01
* We are caching the file mtime though
* @var array with full file path as key and DateTime (file last modification time) as value
*/
protected static $aLogFileLastModified = array();
/** @var string */
protected $sLogFileFullPath;
/** @var string */
protected $sFilePath;
/** @var string */
protected $sFileBaseName;
/** @var string */
protected $sFileExtension;
public function __construct($sFileFullPath)
/**
* @inheritDoc
*/
public function __construct($sLogFileFullPath = null)
{
$aPathParts = pathinfo($sFileFullPath);
$this->sLogFileFullPath = $sLogFileFullPath;
}
protected function GetLastModifiedDateForFile()
{
if (isset(static::$aLogFileLastModified[$this->sLogFileFullPath]))
{
return static::$aLogFileLastModified[$this->sLogFileFullPath];
}
return null;
}
protected function SetLastModifiedDateForFile($oDateTime)
{
static::$aLogFileLastModified[$this->sLogFileFullPath] = $oDateTime;
}
/**
* Need to be called when the file is rotated : actually the next call will need to check on the real date modified instead of using
* the previously cached value !
*/
public function ResetLastModifiedDateForFile()
{
static::$aLogFileLastModified[$this->sLogFileFullPath] = null;
}
/**
* @inheritDoc
*
* Doing the check before opening and writing the log file. There is also a iProcess but cron can be disabled...
*
* @see \LogFileRotationProcess the iProcess impl
*/
public function GetLogFilePath()
{
$this->CheckAndRotateLogFile();
return $this->sLogFileFullPath;
}
/**
* Check log last date modified. If too old then rotate the log file (move it to a new name with a suffix)
*
* @uses filemtime() to get log file date last modified
*/
public function CheckAndRotateLogFile()
{
$oConfig = utils::GetConfig();
$sItopTimeZone = $oConfig->Get('timezone');
$timezone = new DateTimeZone($sItopTimeZone);
if ($this->GetLastModifiedDateForFile() === null)
{
if (!$this->IsLogFileExists())
{
return;
}
$iLogDateLastModifiedTimeStamp = filemtime($this->sLogFileFullPath);
if ($iLogDateLastModifiedTimeStamp === false)
{
return;
}
$oDateTime = DateTime::createFromFormat('U', $iLogDateLastModifiedTimeStamp);
$oDateTime->setTimezone($timezone);
$this->SetLastModifiedDateForFile($oDateTime);
}
$oNow = new DateTime('now', $timezone);
$bShouldRotate = $this->ShouldRotate($this->GetLastModifiedDateForFile(), $oNow);
if (!$bShouldRotate)
{
return;
}
$this->RotateLogFile($this->GetLastModifiedDateForFile());
}
/**
* Rotate current log file
*
* @param DateTime $oLogFileLastModified date when the log file was last modified
*
* @uses \iTopMutex instead of flock as doing a rename on a file with a flock cause an error on PHP 5.6.40 Windows (ok on 7.3.15 though)
* @uses GetRotatedFileName to get rotated file name
*/
protected function RotateLogFile($oLogFileLastModified)
{
if (!$this->IsLogFileExists()) // extra check, but useful for cron also !
{
return;
}
$oLock = null;
try
{
$oLock = new iTopMutex('log_rotation_'.$this->sLogFileFullPath);
$oLock->Lock();
if (!$this->IsLogFileExists()) // extra extra check if we were blocked and another process moved the file in the meantime
{
$oLock->Unlock();
return;
}
$this->ResetLastModifiedDateForFile();
$sNewLogFileName = $this->GetRotatedFileName($oLogFileLastModified);
rename($this->sLogFileFullPath, $sNewLogFileName);
}
catch (Exception $e)
{
// nothing to do, cannot log... file will be renamed on the next call O:)
return;
}
finally
{
if (!is_null($oLock)) { $oLock->Unlock();}
}
}
/**
* @param DateTime $oLogFileLastModified date when the log file was last modified
*
* @return string the full path of the rotated log file
* @uses static::$oLogFileLastModified
* @uses GetFileSuffix
*/
public function GetRotatedFileName($oLogFileLastModified)
{
$aPathParts = pathinfo($this->sLogFileFullPath);
$this->sFilePath = $aPathParts['dirname'];
$this->sFileBaseName = $aPathParts['filename'];
$this->sFileExtension = $aPathParts['extension'];
}
public function GetLogFilePath()
{
$sFileSuffix = $this->GetFileSuffix();
$sFileSuffix = $this->GetFileSuffix($oLogFileLastModified);
return $this->sFilePath
.'/'
return $this->sFilePath.DIRECTORY_SEPARATOR
.$this->sFileBaseName
.'.'.$sFileSuffix
.'.'.$this->sFileExtension;
}
abstract protected function GetFileSuffix();
/**
* @return bool true if file exists and is readable
*/
public function IsLogFileExists()
{
if (!file_exists($this->sLogFileFullPath))
{
return false;
}
if (!is_readable($this->sLogFileFullPath))
{
return false;
}
return true;
}
/**
* **Warning :** both DateTime params must have the same timezone set ! Should use the iTop timezone ('timezone' config parameter)
*
* @param DateTime $oLogFileLastModified date when the log file was last modified
* @param DateTime $oNow date/time of the log we want to write
*
* @return bool true if the file has older informations and we need to move it to an archive (rotate), false if we don't have to
*/
abstract public function ShouldRotate($oLogFileLastModified, $oNow);
/**
* @param DateTime $oDate log file last modification date
*
* @return string suffix for the rotated log file
*/
abstract protected function GetFileSuffix($oDate);
/**
* @see \LogFileRotationProcess
*
* @param \DateTime $oNow current date
*
* @return DateTime time when the cron process should run
*/
abstract public function GetCronProcessNextOccurrence(DateTime $oNow);
}
/**
* @since 2.7.0 N°2518
* @since 2.7.0 N°2518 N°2793
*/
class DailyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder
{
protected function GetFileSuffix()
/**
* @inheritDoc
*/
protected function GetFileSuffix($oDate)
{
return date('Y-m-d');
return $oDate->format('Y-m-d');
}
/**
* @inheritDoc
*/
public function ShouldRotate($oLogFileLastModified, $oNow)
{
$iLogYear = $oLogFileLastModified->format('Y');
$iLogDay = $oLogFileLastModified->format('z');
$iNowYear = $oNow->format('Y');
$iNowDay = $oNow->format('z');
if ($iLogYear !== $iNowYear)
{
return true;
}
if ($iLogDay !== $iNowDay)
{
return true;
}
return false;
}
/**
* @inheritDoc
*/
public function GetCronProcessNextOccurrence(DateTime $oNow)
{
$oOccurrence = clone $oNow;
$oOccurrence->modify('tomorrow midnight');
return $oOccurrence;
}
}
/**
* @since 2.7.0 N°2518
* @since 2.7.0 N°2518 N°2793
*/
class WeeklyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder
{
protected function GetFileSuffix()
/**
* @inheritDoc
*/
protected function GetFileSuffix($oDate)
{
$sWeekYear = date('o');
$sWeekNumber = date('W');
$sWeekYear = $oDate->format('o');
$sWeekNumber = $oDate->format('W');
return $sWeekYear.'-week'.$sWeekNumber;
}
/**
* @inheritDoc
*/
public function ShouldRotate($oLogFileLastModified, $oNow)
{
$iLogYear = $oLogFileLastModified->format('Y');
$iLogWeek = $oLogFileLastModified->format('W');
$iNowYear = $oNow->format('Y');
$iNowWeek = $oNow->format('W');
if ($iLogYear !== $iNowYear)
{
return true;
}
if ($iLogWeek !== $iNowWeek)
{
return true;
}
return false;
}
/**
* @inheritDoc
*/
public function GetCronProcessNextOccurrence(DateTime $oNow)
{
$oOccurrence = clone $oNow;
$oOccurrence->modify('Monday next week midnight');
return $oOccurrence;
}
}
/**
* @since 2.7.0 N°2820
*/
class MonthlyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder
{
/**
* @inheritDoc
*/
public function ShouldRotate($oLogFileLastModified, $oNow)
{
$iLogYear = $oLogFileLastModified->format('Y');
$iLogMonth = $oLogFileLastModified->format('n');
$iNowYear = $oNow->format('Y');
$iNowMonth = $oNow->format('n');
if ($iLogYear !== $iNowYear)
{
return true;
}
if ($iLogMonth !== $iNowMonth)
{
return true;
}
return false;
}
/**
* @inheritDoc
*/
protected function GetFileSuffix($oDate)
{
$sMonthYear = $oDate->format('o');
$sMonthNumber = $oDate->format('m');
return $sMonthYear.'-month'.$sMonthNumber;
}
/**
* @inheritDoc
*/
public function GetCronProcessNextOccurrence(DateTime $oNow)
{
$oOccurrence = clone $oNow;
$oOccurrence->modify('first day of next month midnight');
return $oOccurrence;
}
}
/**
@@ -111,7 +426,7 @@ class LogFileNameBuilderFactory
*
* @param string $sFileFullPath
*
* @return \ILogFileNameBuilder
* @return \iLogFileNameBuilder
* @throws \ConfigException
* @throws \CoreException
*/
@@ -119,7 +434,7 @@ class LogFileNameBuilderFactory
{
$oConfig = utils::GetConfig();
$sFileNameBuilderImpl = $oConfig->Get('log_filename_builder_impl');
if (empty($sFileNameBuilderImpl) || !class_exists($sFileNameBuilderImpl))
if (!is_a($sFileNameBuilderImpl, iLogFileNameBuilder::class, true))
{
$sFileNameBuilderImpl = 'DefaultLogFileNameBuilder';
}
@@ -134,7 +449,7 @@ class LogFileNameBuilderFactory
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @since 2.7.0 allow to rotate file (N°2518)
* @since 2.7.0 N°2518 N°2793 file log rotation
*/
class FileLog
{
@@ -153,52 +468,6 @@ class FileLog
$this->oFileNameBuilder = LogFileNameBuilderFactory::GetInstance($sFileName);
}
/**
* Since 2.7.0 with the 'log_filename_builder_impl' param the logs will output to different files name
* As now by default iTop will use {@link WeeklyRotatingLogFileNameBuilder} (rotation each week), to avoid confusion, we're renaming
* the legacy error.log / setup.log.
*
* @since 2.7.0 N°2518
* @uses utils::GetConfig() the config must be persisted !
*/
public static function RenameLegacyLogFiles()
{
$oConfig = utils::GetConfig();
IssueLog::Enable(APPROOT.'log/error.log'); // refresh log file used
$sLogFileNameParam = $oConfig->Get('log_filename_builder_impl');
$aConfigValuesNoRotation = array('', 'DefaultLogFileNameBuilder');
$bIsLogRotationActivated = (!in_array($sLogFileNameParam, $aConfigValuesNoRotation, true));
if (!$bIsLogRotationActivated)
{
return;
}
IssueLog::Warning("Log name builder set to '$sLogFileNameParam', renaming legacy log files");
$aLogFilesToRename = array(
'log/setup.log' => 'log/setup.LEGACY.log',
'log/error.log' => 'log/error.LEGACY.log',
);
foreach ($aLogFilesToRename as $sLogCurrentName => $sLogNewName)
{
$sSource = APPROOT.$sLogCurrentName;
if (!file_exists($sSource))
{
IssueLog::Debug("Log file '$sLogCurrentName' (legacy) does not exists, renaming skipped");
continue;
}
$sDestination = APPROOT.$sLogNewName;
$bResult = rename($sSource, $sDestination);
if (!$bResult)
{
IssueLog::Error("Log file '$sLogCurrentName' (legacy) cannot be renamed to '$sLogNewName'");
continue;
}
IssueLog::Info("Log file '$sLogCurrentName' (legacy) renamed to '$sLogNewName'");
}
}
public function Error($sText, $sChannel = '', $aContext = array())
{
$this->Write($sText, __FUNCTION__, $sChannel, $aContext);
@@ -233,6 +502,7 @@ class FileLog
protected function Write($sText, $sLevel = '', $sChannel = '', $aContext = array())
{
$sTextPrefix = empty($sLevel) ? '' : (str_pad($sLevel, 7).' | ');
$sTextPrefix .= str_pad(UserRights::GetUserId(), 5)." | ";
$sTextSuffix = empty($sChannel) ? '' : " | $sChannel";
$sText = "{$sTextPrefix}{$sText}{$sTextSuffix}";
$sLogFilePath = $this->oFileNameBuilder->GetLogFilePath();
@@ -247,12 +517,9 @@ class FileLog
{
flock($hLogFile, LOCK_EX);
$sDate = date('Y-m-d H:i:s');
if (empty($aContext))
{
if (empty($aContext)) {
fwrite($hLogFile, "$sDate | $sText\n");
}
else
{
} else {
$sContext = var_export($aContext, true);
fwrite($hLogFile, "$sDate | $sText\n$sContext\n");
}
@@ -263,6 +530,21 @@ class FileLog
}
}
/**
* Simple enum like class to factorize channels values as constants
* Channels are used especially as parameters in {@see \LogAPI} methods
*
* @since 2.7.5 3.0.0 N°4012
*/
class LogChannels
{
const DEADLOCK = 'DeadLock';
const INLINE_IMAGE = 'InlineImage';
const PORTAL = 'portal';
}
abstract class LogAPI
{
const CHANNEL_DEFAULT = '';
@@ -273,6 +555,12 @@ abstract class LogAPI
const LEVEL_OK = 'Ok';
const LEVEL_DEBUG = 'Debug';
const LEVEL_TRACE = 'Trace';
/**
* @see GetMinLogLevel
* @var string default log level, can be overrided
* @since 2.7.1 N°2977
*/
const LEVEL_DEFAULT = self::LEVEL_OK;
protected static $aLevelsPriority = array(
self::LEVEL_ERROR => 400,
@@ -329,36 +617,29 @@ abstract class LogAPI
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array())
{
if (! static::$m_oFileLog)
{
if (!static::$m_oFileLog) {
return;
}
if (! isset(self::$aLevelsPriority[$sLevel]))
{
if (!isset(self::$aLevelsPriority[$sLevel])) {
IssueLog::Error("invalid log level '{$sLevel}'");
return;
}
if (is_null($sChannel))
{
if (is_null($sChannel)) {
$sChannel = static::CHANNEL_DEFAULT;
}
$sMinLogLevel = self::GetMinLogLevel($sChannel);
if ($sMinLogLevel === false || $sMinLogLevel === 'false')
{
if ($sMinLogLevel === false || $sMinLogLevel === 'false') {
return;
}
if (is_string($sMinLogLevel))
{
if (! isset(self::$aLevelsPriority[$sMinLogLevel]))
{
if (is_string($sMinLogLevel)) {
if (!isset(self::$aLevelsPriority[$sMinLogLevel])) {
throw new Exception("invalid configuration for log_level '{$sMinLogLevel}' is not within the list: ".implode(',', array_keys(self::$aLevelsPriority)));
}
elseif (self::$aLevelsPriority[$sLevel] < self::$aLevelsPriority[$sMinLogLevel])
{
} elseif (self::$aLevelsPriority[$sLevel] < self::$aLevelsPriority[$sMinLogLevel]) {
//priority too low regarding the conf, do not log this
return;
}
@@ -370,21 +651,22 @@ abstract class LogAPI
/**
* @param $sChannel
*
* @return mixed|null
* @return string one of the LEVEL_* const value
* @uses \LogAPI::LEVEL_DEFAULT
*/
private static function GetMinLogLevel($sChannel)
{
$oConfig = (static::$m_oMockMetaModelConfig !== null) ? static::$m_oMockMetaModelConfig : \MetaModel::GetConfig();
if (!$oConfig instanceof Config)
{
return self::LEVEL_OK;
return static::LEVEL_DEFAULT;
}
$sLogLevelMin = $oConfig->Get('log_level_min');
if (empty($sLogLevelMin))
{
return self::LEVEL_OK;
return static::LEVEL_DEFAULT;
}
if (!is_array($sLogLevelMin))
@@ -402,7 +684,7 @@ abstract class LogAPI
return $sLogLevelMin[$sChannel];
}
return self::LEVEL_OK;
return static::LEVEL_DEFAULT;
}
}
@@ -410,6 +692,12 @@ abstract class LogAPI
class SetupLog extends LogAPI
{
const CHANNEL_DEFAULT = 'SetupLog';
/**
* @inheritDoc
*
* As this object is used during setup, without any conf file available, customizing the level can be done by changing this constant !
*/
const LEVEL_DEFAULT = self::LEVEL_INFO;
protected static $m_oFileLog = null;
}
@@ -427,3 +715,129 @@ class ToolsLog extends LogAPI
protected static $m_oFileLog = null;
}
/**
* @see \CMDBSource::LogDeadLock()
* @since 2.7.1
*/
class DeadLockLog extends LogAPI
{
const CHANNEL_WAIT_TIMEOUT = 'Deadlock-WaitTimeout';
const CHANNEL_DEADLOCK_FOUND = 'Deadlock-Found';
const CHANNEL_DEFAULT = self::CHANNEL_WAIT_TIMEOUT;
/** @var \FileLog we want our own instance ! */
protected static $m_oFileLog = null;
public static function Enable($sTargetFile = null)
{
if (empty($sTargetFile))
{
$sTargetFile = APPROOT.'log/deadlocks.log';
}
parent::Enable($sTargetFile);
}
private static function GetChannelFromMysqlErrorNo($iMysqlErrorNo)
{
switch ($iMysqlErrorNo)
{
case 1205:
return self::CHANNEL_WAIT_TIMEOUT;
break;
case 1213:
return self::CHANNEL_DEADLOCK_FOUND;
break;
default:
return self::CHANNEL_DEFAULT;
break;
}
}
/**
* @param string $sLevel
* @param string $sMessage
* @param int $iMysqlErrorNumber will be converted to channel using {@link GetChannelFromMysqlErrorNo}
* @param array $aContext
*
* @throws \Exception
* @noinspection PhpParameterNameChangedDuringInheritanceInspection
*
* @since 2.7.1 method creation
* @since 2.7.5 3.0.0 rename param names and fix phpdoc (thanks Hipska !)
*/
public static function Log($sLevel, $sMessage, $iMysqlErrorNumber = null, $aContext = array())
{
$sChannel = self::GetChannelFromMysqlErrorNo($iMysqlErrorNumber);
parent::Log($sLevel, $sMessage, $sChannel, $aContext);
}
}
class LogFileRotationProcess implements iScheduledProcess
{
/**
* Cannot get this list from anywhere as log file name is provided by the caller using LogAPI::Enable
* @var string[]
*/
const LOGFILES_TO_ROTATE = array(
'setup.log',
'error.log',
'tools.log',
'itop-fence.log',
);
/**
* @inheritDoc
*/
public function Process($iUnixTimeLimit)
{
$sLogFileNameBuilder = $this->GetLogFileNameBuilderClassName();
foreach (self::LOGFILES_TO_ROTATE as $sLogFileName)
{
$sLogFileFullPath = APPROOT
.DIRECTORY_SEPARATOR.'log'
.DIRECTORY_SEPARATOR.$sLogFileName;
/** @var \RotatingLogFileNameBuilder $oLogFileNameBuilder */
$oLogFileNameBuilder = new $sLogFileNameBuilder($sLogFileFullPath);
$oLogFileNameBuilder->ResetLastModifiedDateForFile();
$oLogFileNameBuilder->CheckAndRotateLogFile();
}
}
/**
* @inheritDoc
*/
public function GetNextOccurrence()
{
try
{
$sLogFileNameBuilder = $this->GetLogFileNameBuilderClassName();
}
catch (ProcessException $e)
{
return new DateTime('3000-01-01');
}
/** @var \RotatingLogFileNameBuilder $oLogFileNameBuilder */
$oLogFileNameBuilder = new $sLogFileNameBuilder();
return $oLogFileNameBuilder->GetCronProcessNextOccurrence(new DateTime());
}
/**
* @return string RotatingLogFileNameBuilder implementation configured
* @throws \ProcessException if the class is invalid
*/
private function GetLogFileNameBuilderClassName()
{
$sLogFileNameBuilder = MetaModel::GetConfig()->Get('log_filename_builder_impl');
if (is_a($sLogFileNameBuilder, RotatingLogFileNameBuilder::class, true))
{
return $sLogFileNameBuilder;
}
throw new ProcessException(self::class.' : The configured filename builder is invalid (log_filename_builder_impl="'.$sLogFileNameBuilder.'")');
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -242,6 +242,8 @@ class iTopMutex
*
* @throws \Exception
* @throws \MySQLException
*
* @since 2.7.5 3.0.0 N°3968 specify `wait_timeout` for the mutex dedicated connection
*/
public function InitMySQLSession()
{
@@ -254,10 +256,36 @@ class iTopMutex
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
if (!$this->hDBLink)
{
if (!$this->hDBLink) {
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
}
// Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection,
// since the lock will be released if/when the connection times out.
// Source https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html :
// > A lock obtained with GET_LOCK() is released explicitly by executing RELEASE_LOCK() or implicitly when your session terminates
//
// BEWARE: If you want to check the value of this variable, when run from an interactive console `SHOW VARIABLES LIKE 'wait_timeout'`
// will actually returns the value of the variable `interactive_timeout` which may be quite different.
$sSql = "SHOW VARIABLES LIKE 'wait_timeout'";
$result = mysqli_query($this->hDBLink, $sSql);
if (!$result) {
throw new Exception("Failed to issue MySQL query '".$sSql."': ".mysqli_error($this->hDBLink).' (mysql errno: '.mysqli_errno($this->hDBLink).')');
}
if ($aRow = mysqli_fetch_array($result, MYSQLI_BOTH)) {
$iTimeout = (int)$aRow[1];
} else {
mysqli_free_result($result);
throw new Exception("No result for query '".$sSql."'");
}
mysqli_free_result($result);
if ($iTimeout < 86400) {
$result = mysqli_query($this->hDBLink, 'SET SESSION wait_timeout=86400');
if ($result === false) {
throw new Exception("Failed to issue MySQL query '".$sSql."': ".mysqli_error($this->hDBLink).' (mysql errno: '.mysqli_errno($this->hDBLink).')');
}
}
}

View File

@@ -1,32 +1,52 @@
<?php
// Copyright (c) 2010-2018 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
//
/*
* Copyright (C) 2010-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
*/
class MissingQueryArgument extends CoreException
{
class MissingQueryArgument extends CoreException {
}
class ExpressionHelper {
/**
* Callback to be used with {@link Expression::Browse}, to update the AllowAllData attribute in the NestedQueryExpression that are
* present in the Expression tree
*
* @param \Expression $oExpression
* @param boolean $bAllowAllData
*
* @uses \DBSearch::AllowAllData()
*
* @since 2.7.2 2.8.0 N°3324
*/
public static function ExpressionAllowAllDataCallback($oExpression, $bAllowAllData) {
if (!($oExpression instanceof NestedQueryExpression)) {
return;
}
$oExpression->AllowAllData($bAllowAllData);
}
}
/**
* @method Check($oModelReflection, array $aAliases, $sSourceQuery)
*/
abstract class Expression
{
abstract class Expression {
const OPERATOR_BINARY = 'binary';
const OPERATOR_BOOLEAN = 'boolean_binary';
const OPERATOR_FIELD = 'field';
@@ -139,9 +159,13 @@ abstract class Expression
}
/**
* Recursively browse the expression tree
* @param Closure $callback
* @return mixed
* Recursively browse the expression tree.
*
* To access variables, specify them using the `use` keyword and the `&` to pass by reference if necessary
*
* @see https://www.php.net/manual/fr/functions.anonymous.php
*
* @param Closure $callback with current expression as parameter
*/
abstract public function Browse(Closure $callback);
@@ -153,13 +177,18 @@ abstract class Expression
// recursively list field parents ($aTable = array of sParent => dummy)
abstract public function CollectUsedParents(&$aTable);
/**
* @return boolean true if the expression's value is constant and evaluates to true, false otherwise
*/
abstract public function IsTrue();
// recursively builds an array of [classAlias][fieldName] => value
abstract public function ListConstantFields();
public function RequiresField($sClass, $sFieldName)
{
// recursively builds an array of parameters to give to current request
abstract public function ListParameters();
public function RequiresField($sClass, $sFieldName) {
// #@# todo - optimize : this is called quite often when building a single query !
$aRequired = $this->ListRequiredFields();
if (!in_array($sClass.'.'.$sFieldName, $aRequired)) return false;
@@ -354,6 +383,11 @@ class SQLExpression extends Expression
return array();
}
public function ListParameters()
{
return array();
}
public function RenameParam($sOldName, $sNewName)
{
// Do nothing, since there is nothing to rename
@@ -593,6 +627,13 @@ class BinaryExpression extends Expression
return $aResult;
}
public function ListParameters()
{
$aLeft = $this->GetLeftExpr()->ListParameters();
$aRight = $this->GetRightExpr()->ListParameters();
return array_merge($aLeft, $aRight);
}
public function RenameParam($sOldName, $sNewName)
{
$this->GetLeftExpr()->RenameParam($sOldName, $sNewName);
@@ -811,7 +852,7 @@ class BinaryExpression extends Expression
/**
* @since 2.6 N°931 tag fields
* @since 2.6.0 N°931 tag fields
*/
class MatchExpression extends BinaryExpression
{
@@ -824,12 +865,15 @@ class MatchExpression extends BinaryExpression
* MatchExpression constructor.
*
* @param \FieldExpression $oLeftExpr
* @param \ScalarExpression $oRightExpr
* @param \Expression $oRightExpr
*
* @throws \CoreException
*/
public function __construct(FieldExpression $oLeftExpr, ScalarExpression $oRightExpr)
public function __construct(FieldExpression $oLeftExpr, Expression $oRightExpr)
{
if (!$oRightExpr instanceof ScalarExpression && !$oRightExpr instanceof VariableExpression) {
throw new CoreException('Only instance of ScalarExpression or VariableExpression are allowed in MATCHES '.get_class($oRightExpr).' found');
}
parent::__construct($oLeftExpr, 'MATCHES', $oRightExpr);
}
@@ -934,6 +978,11 @@ class UnaryExpression extends Expression
return array();
}
public function ListParameters()
{
return array();
}
public function RenameParam($sOldName, $sNewName)
{
// Do nothing
@@ -1146,6 +1195,38 @@ class ScalarExpression extends UnaryExpression
IssueLog::Error($e->getMessage());
}
break;
case ($oAttDef instanceof AttributeEnumSet):
try
{
if (!empty($this->GetValue()))
{
$aValues = array();
$sValue = $this->GetValue();
if (is_string($sValue))
{
$aTags = $oAttDef->FromStringToArray($sValue, ' ');
}
else
{
$aTags = array();
}
foreach($aTags as $sLabel => $sValue)
{
$aValue['label'] = $sLabel;
$aValue['value'] = $sValue;
$aValues[] = $aValue;
}
$aCriterion['values'] = $aValues;
}
else
{
$aCriterion['has_undefined'] = true;
}
} catch (Exception $e)
{
IssueLog::Error($e->getMessage());
}
break;
case $oAttDef->IsExternalKey():
try
{
@@ -1591,6 +1672,39 @@ class FieldExpression extends UnaryExpression
// Has been resolved into an SQL expression
class FieldExpressionResolved extends FieldExpression
{
protected $m_aAdditionalExpressions;
public function __construct($mExpression, $sParent = '')
{
$this->m_aAdditionalExpressions = array();
if (is_array($mExpression))
{
foreach ($mExpression as $sSuffix => $sExpression)
{
if ($sSuffix == '')
{
$sName = $sExpression;
}
$this->m_aAdditionalExpressions[$sSuffix] = new FieldExpressionResolved($sExpression, $sParent);
}
}
else
{
$sName = $mExpression;
}
parent::__construct($sName, $sParent);
}
/**
* @return array of additional expressions for muti-column attributes
* @since 2.7.4
*/
public function AdditionalExpressions()
{
return $this->m_aAdditionalExpressions;
}
public function GetUnresolvedFields($sAlias, &$aUnresolved)
{
}
@@ -1816,6 +1930,12 @@ class VariableExpression extends UnaryExpression
}
return $oRet;
}
public function ListParameters()
{
return array($this);
}
}
// Temporary, until we implement functions and expression casting!
@@ -1914,7 +2034,15 @@ class ListExpression extends Expression
{
if ($oExpr instanceof VariableExpression)
{
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar($aArgs);
$oVarExpr = $oExpr->GetAsScalar($aArgs);
if ($oVarExpr instanceof ListExpression)
{
$this->m_aExpressions = $oVarExpr->GetItems();
}
else
{
$this->m_aExpressions[$idx] = $oVarExpr;
}
}
else
{
@@ -1969,6 +2097,16 @@ class ListExpression extends Expression
return $aRes;
}
public function ListParameters()
{
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
$aRes = array_merge($aRes, $oExpr->ListParameters());
}
return $aRes;
}
public function RenameParam($sOldName, $sNewName)
{
foreach ($this->m_aExpressions as $key => $oExpr)
@@ -2078,55 +2216,62 @@ class NestedQueryExpression extends Expression
}
/**/
public function ApplyParameters($aArgs)
{
public function ApplyParameters($aArgs) {
$this->m_oNestedQuery->ApplyParameters($aArgs);
}
/**/
public function GetUnresolvedFields($sAlias, &$aUnresolved)
{
public function GetUnresolvedFields($sAlias, &$aUnresolved) {
}
/**/
public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
{
public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true) {
// Check and prepare the select information
$this->m_oNestedQuery->TranslateConditions($aTranslationData, $bMatchAll , $bMarkFieldsAsResolved );
$this->m_oNestedQuery->TranslateConditions($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
return clone $this;
}
public function ListRequiredFields()
{
public function ListRequiredFields() {
return array();
}
public function CollectUsedParents(&$aTable)
{
public function CollectUsedParents(&$aTable) {
}
public function ListConstantFields()
{
public function ListConstantFields() {
return $this->m_oNestedQuery->ListConstantFields();
}
public function RenameParam($sOldName, $sNewName)
{
public function ListParameters() {
return $this->m_oNestedQuery->ListParameters();
}
public function RenameParam($sOldName, $sNewName) {
$this->m_oNestedQuery->RenameParam($sOldName, $sNewName);
}
public function RenameAlias($sOldName, $sNewName)
{
public function RenameAlias($sOldName, $sNewName) {
$this->m_oNestedQuery->RenameAlias($sOldName, $sNewName);
}
/**
* @inheritDoc
*/
public function ToJSON(&$aArgs = null, $bRetrofitParams = false)
{
public function ToJSON(&$aArgs = null, $bRetrofitParams = false) {
return $this->m_oNestedQuery->ToJSON();
}
/**
* Simple indirection to {@link \DBObjectSearch::AllowAllData()}
*
* @param bool $bAllowAllData
*
* @uses \DBSearch::AllowAllData()
*/
public function AllowAllData($bAllowAllData = true) {
$this->m_oNestedQuery->AllowAllData($bAllowAllData);
}
}
class FunctionExpression extends Expression
@@ -2257,6 +2402,17 @@ class FunctionExpression extends Expression
return $aRes;
}
public function ListParameters()
{
$aRes = array();
foreach ($this->m_aArgs as $oExpr)
{
$aRes = array_merge($aRes, $oExpr->ListParameters());
}
return $aRes;
}
public function RenameParam($sOldName, $sNewName)
{
foreach ($this->m_aArgs as $key => $oExpr)
@@ -2538,6 +2694,11 @@ class IntervalExpression extends Expression
return array();
}
public function ListParameters()
{
return $this->m_oValue->ListParameters();
}
public function RenameParam($sOldName, $sNewName)
{
$this->m_oValue->RenameParam($sOldName, $sNewName);
@@ -2684,6 +2845,16 @@ class CharConcatExpression extends Expression
return $aRes;
}
public function ListParameters()
{
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
$aRes = array_merge($aRes, $oExpr->ListParameters());
}
return $aRes;
}
public function RenameParam($sOldName, $sNewName)
{
foreach ($this->m_aExpressions as $key => $oExpr)

View File

@@ -597,7 +597,7 @@ static public $yy_action = array(
** defined, then do no error processing.
*/
const YYNOCODE = 119;
const YYSTACKDEPTH = 100;
const YYSTACKDEPTH = 1000;
const YYNSTATE = 175;
const YYNRULE = 125;
const YYERRORSYMBOL = 76;
@@ -1175,6 +1175,10 @@ static public $yy_action = array(
}
/* Here code is inserted which will execute if the parser
** stack ever overflows */
#line 30 "..\oql-parser.y"
throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
#line 1186 "..\oql-parser.php"
return;
}
$yytos = new OQLParser_yyStackEntry;
@@ -1474,116 +1478,116 @@ static public $yy_action = array(
** function yy_r0($yymsp){ ... } // User supplied code
** #line <lineno> <thisfile>
*/
#line 29 "..\oql-parser.y"
#line 37 "..\oql-parser.y"
function yy_r0(){ $this->my_result = $this->yystack[$this->yyidx + 0]->minor; }
#line 1483 "..\oql-parser.php"
#line 33 "..\oql-parser.y"
#line 1488 "..\oql-parser.php"
#line 41 "..\oql-parser.y"
function yy_r3(){
$this->_retvalue = new OqlUnionQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
}
#line 1488 "..\oql-parser.php"
#line 40 "..\oql-parser.y"
#line 1493 "..\oql-parser.php"
#line 48 "..\oql-parser.y"
function yy_r5(){
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, array($this->yystack[$this->yyidx + -2]->minor));
}
#line 1493 "..\oql-parser.php"
#line 43 "..\oql-parser.y"
#line 1498 "..\oql-parser.php"
#line 51 "..\oql-parser.y"
function yy_r6(){
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, array($this->yystack[$this->yyidx + -2]->minor));
}
#line 1498 "..\oql-parser.php"
#line 47 "..\oql-parser.y"
#line 1503 "..\oql-parser.php"
#line 55 "..\oql-parser.y"
function yy_r7(){
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -4]->minor);
}
#line 1503 "..\oql-parser.php"
#line 50 "..\oql-parser.y"
#line 1508 "..\oql-parser.php"
#line 58 "..\oql-parser.y"
function yy_r8(){
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -6]->minor);
}
#line 1508 "..\oql-parser.php"
#line 55 "..\oql-parser.y"
#line 1513 "..\oql-parser.php"
#line 63 "..\oql-parser.y"
function yy_r9(){
$this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor);
}
#line 1513 "..\oql-parser.php"
#line 58 "..\oql-parser.y"
#line 1518 "..\oql-parser.php"
#line 66 "..\oql-parser.y"
function yy_r10(){
array_push($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
$this->_retvalue = $this->yystack[$this->yyidx + -2]->minor;
}
#line 1519 "..\oql-parser.php"
#line 63 "..\oql-parser.y"
#line 1524 "..\oql-parser.php"
#line 71 "..\oql-parser.y"
function yy_r11(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
#line 1522 "..\oql-parser.php"
#line 64 "..\oql-parser.y"
#line 1527 "..\oql-parser.php"
#line 72 "..\oql-parser.y"
function yy_r12(){ $this->_retvalue = null; }
#line 1525 "..\oql-parser.php"
#line 66 "..\oql-parser.y"
#line 1530 "..\oql-parser.php"
#line 74 "..\oql-parser.y"
function yy_r13(){
// insert the join statement on top of the existing list
array_unshift($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor);
// and return the updated array
$this->_retvalue = $this->yystack[$this->yyidx + 0]->minor;
}
#line 1533 "..\oql-parser.php"
#line 72 "..\oql-parser.y"
#line 1538 "..\oql-parser.php"
#line 80 "..\oql-parser.y"
function yy_r14(){
$this->_retvalue = Array($this->yystack[$this->yyidx + 0]->minor);
}
#line 1538 "..\oql-parser.php"
#line 78 "..\oql-parser.y"
#line 1543 "..\oql-parser.php"
#line 86 "..\oql-parser.y"
function yy_r16(){
// create an array with one single item
$this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
}
#line 1544 "..\oql-parser.php"
#line 83 "..\oql-parser.y"
#line 1549 "..\oql-parser.php"
#line 91 "..\oql-parser.y"
function yy_r17(){
// create an array with one single item
$this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
}
#line 1550 "..\oql-parser.php"
#line 88 "..\oql-parser.y"
function yy_r18(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, '=', $this->yystack[$this->yyidx + 0]->minor); }
#line 1553 "..\oql-parser.php"
#line 89 "..\oql-parser.y"
function yy_r19(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW', $this->yystack[$this->yyidx + 0]->minor); }
#line 1556 "..\oql-parser.php"
#line 90 "..\oql-parser.y"
function yy_r20(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
#line 1559 "..\oql-parser.php"
#line 91 "..\oql-parser.y"
function yy_r21(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW', $this->yystack[$this->yyidx + 0]->minor); }
#line 1562 "..\oql-parser.php"
#line 92 "..\oql-parser.y"
function yy_r22(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
#line 1565 "..\oql-parser.php"
#line 93 "..\oql-parser.y"
function yy_r23(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
#line 1568 "..\oql-parser.php"
#line 94 "..\oql-parser.y"
function yy_r24(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
#line 1571 "..\oql-parser.php"
#line 95 "..\oql-parser.y"
function yy_r25(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
#line 1574 "..\oql-parser.php"
#line 1555 "..\oql-parser.php"
#line 96 "..\oql-parser.y"
function yy_r26(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
#line 1577 "..\oql-parser.php"
function yy_r18(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, '=', $this->yystack[$this->yyidx + 0]->minor); }
#line 1558 "..\oql-parser.php"
#line 97 "..\oql-parser.y"
function yy_r19(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW', $this->yystack[$this->yyidx + 0]->minor); }
#line 1561 "..\oql-parser.php"
#line 98 "..\oql-parser.y"
function yy_r27(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
#line 1580 "..\oql-parser.php"
function yy_r20(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
#line 1564 "..\oql-parser.php"
#line 99 "..\oql-parser.y"
function yy_r21(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW', $this->yystack[$this->yyidx + 0]->minor); }
#line 1567 "..\oql-parser.php"
#line 100 "..\oql-parser.y"
function yy_r22(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
#line 1570 "..\oql-parser.php"
#line 101 "..\oql-parser.y"
function yy_r23(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
#line 1573 "..\oql-parser.php"
#line 102 "..\oql-parser.y"
function yy_r24(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
#line 1576 "..\oql-parser.php"
#line 103 "..\oql-parser.y"
function yy_r31(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); }
#line 1583 "..\oql-parser.php"
function yy_r25(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
#line 1579 "..\oql-parser.php"
#line 104 "..\oql-parser.y"
function yy_r32(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; }
#line 1586 "..\oql-parser.php"
#line 105 "..\oql-parser.y"
function yy_r33(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); }
#line 1589 "..\oql-parser.php"
function yy_r26(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
#line 1582 "..\oql-parser.php"
#line 106 "..\oql-parser.y"
function yy_r27(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
#line 1585 "..\oql-parser.php"
#line 111 "..\oql-parser.y"
function yy_r31(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); }
#line 1588 "..\oql-parser.php"
#line 112 "..\oql-parser.y"
function yy_r32(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; }
#line 1591 "..\oql-parser.php"
#line 113 "..\oql-parser.y"
function yy_r33(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); }
#line 1594 "..\oql-parser.php"
#line 119 "..\oql-parser.y"
function yy_r37(){
if ($this->yystack[$this->yyidx + -1]->minor == 'MATCHES')
{
@@ -1594,44 +1598,44 @@ static public $yy_action = array(
$this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor);
}
}
#line 1601 "..\oql-parser.php"
#line 128 "..\oql-parser.y"
#line 1606 "..\oql-parser.php"
#line 136 "..\oql-parser.y"
function yy_r42(){
$this->_retvalue = new ListOqlExpression($this->yystack[$this->yyidx + -1]->minor);
}
#line 1606 "..\oql-parser.php"
#line 132 "..\oql-parser.y"
#line 1611 "..\oql-parser.php"
#line 139 "..\oql-parser.y"
function yy_r43(){
$this->_retvalue = new NestedQueryOqlExpression($this->yystack[$this->yyidx + -1]->minor);
}
#line 1611 "..\oql-parser.php"
#line 147 "..\oql-parser.y"
#line 1616 "..\oql-parser.php"
#line 154 "..\oql-parser.y"
function yy_r47(){
$this->_retvalue = array();
}
#line 1616 "..\oql-parser.php"
#line 158 "..\oql-parser.y"
#line 1621 "..\oql-parser.php"
#line 165 "..\oql-parser.y"
function yy_r51(){ $this->_retvalue = new IntervalOqlExpression($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); }
#line 1619 "..\oql-parser.php"
#line 171 "..\oql-parser.y"
#line 1624 "..\oql-parser.php"
#line 178 "..\oql-parser.y"
function yy_r61(){ $this->_retvalue = new ScalarOqlExpression($this->yystack[$this->yyidx + 0]->minor); }
#line 1622 "..\oql-parser.php"
#line 173 "..\oql-parser.y"
function yy_r63(){ $this->_retvalue = new ScalarOqlExpression(null); }
#line 1625 "..\oql-parser.php"
#line 175 "..\oql-parser.y"
function yy_r64(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor); }
#line 1628 "..\oql-parser.php"
#line 176 "..\oql-parser.y"
function yy_r65(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -2]->minor); }
#line 1631 "..\oql-parser.php"
#line 177 "..\oql-parser.y"
function yy_r66(){ $this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; }
#line 1634 "..\oql-parser.php"
#line 1627 "..\oql-parser.php"
#line 180 "..\oql-parser.y"
function yy_r67(){ $this->_retvalue = new VariableOqlExpression(substr($this->yystack[$this->yyidx + 0]->minor, 1)); }
#line 1637 "..\oql-parser.php"
function yy_r63(){ $this->_retvalue = new ScalarOqlExpression(null); }
#line 1630 "..\oql-parser.php"
#line 182 "..\oql-parser.y"
function yy_r64(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor); }
#line 1633 "..\oql-parser.php"
#line 183 "..\oql-parser.y"
function yy_r65(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -2]->minor); }
#line 1636 "..\oql-parser.php"
#line 184 "..\oql-parser.y"
function yy_r66(){ $this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; }
#line 1639 "..\oql-parser.php"
#line 187 "..\oql-parser.y"
function yy_r67(){ $this->_retvalue = new VariableOqlExpression(substr($this->yystack[$this->yyidx + 0]->minor, 1)); }
#line 1642 "..\oql-parser.php"
#line 189 "..\oql-parser.y"
function yy_r68(){
if ($this->yystack[$this->yyidx + 0]->minor[0] == '`')
{
@@ -1643,22 +1647,22 @@ static public $yy_action = array(
}
$this->_retvalue = new OqlName($name, $this->m_iColPrev);
}
#line 1650 "..\oql-parser.php"
#line 193 "..\oql-parser.y"
#line 1655 "..\oql-parser.php"
#line 200 "..\oql-parser.y"
function yy_r69(){$this->_retvalue=(int)$this->yystack[$this->yyidx + 0]->minor; }
#line 1653 "..\oql-parser.php"
#line 194 "..\oql-parser.y"
#line 1658 "..\oql-parser.php"
#line 201 "..\oql-parser.y"
function yy_r70(){$this->_retvalue=(int)-$this->yystack[$this->yyidx + 0]->minor; }
#line 1656 "..\oql-parser.php"
#line 195 "..\oql-parser.y"
#line 1661 "..\oql-parser.php"
#line 202 "..\oql-parser.y"
function yy_r71(){$this->_retvalue=new OqlHexValue($this->yystack[$this->yyidx + 0]->minor); }
#line 1659 "..\oql-parser.php"
#line 196 "..\oql-parser.y"
#line 1664 "..\oql-parser.php"
#line 203 "..\oql-parser.y"
function yy_r72(){$this->_retvalue=stripslashes(substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2)); }
#line 1662 "..\oql-parser.php"
#line 199 "..\oql-parser.y"
#line 1667 "..\oql-parser.php"
#line 206 "..\oql-parser.y"
function yy_r73(){$this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; }
#line 1665 "..\oql-parser.php"
#line 1670 "..\oql-parser.php"
/**
* placeholder for the left hand side in a reduce operation.
@@ -1759,6 +1763,10 @@ static public $yy_action = array(
}
/* Here code is inserted which will be executed whenever the
** parser fails */
#line 33 "..\oql-parser.y"
throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
#line 1775 "..\oql-parser.php"
}
/**
@@ -1772,8 +1780,8 @@ static public $yy_action = array(
{
#line 25 "..\oql-parser.y"
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
#line 1781 "..\oql-parser.php"
throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
#line 1791 "..\oql-parser.php"
}
/**
@@ -1940,19 +1948,47 @@ throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCo
} while ($yymajor != self::YYNOCODE && $this->yyidx >= 0);
}
}
#line 264 "..\oql-parser.y"
#line 271 "..\oql-parser.y"
class OQLParserException extends OQLException
{
public function __construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue)
{
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
}
}
class OQLParserSyntaxErrorException extends OQLParserException
{
public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue)
{
$sIssue = "Unexpected token $sTokenName";
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
}
}
class OQLParserStackOverFlowException extends OQLParserException
{
public function __construct($sInput, $iLine, $iCol)
{
$sIssue = "Stack overflow";
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
}
}
class OQLParserParseFailureException extends OQLParserException
{
public function __construct($sInput, $iLine, $iCol)
{
$sIssue = "Unexpected token $sTokenName";
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
}
}
class OQLParser extends OQLParserRaw
{
// dirty, but working for us (no other mean to get the final result :-(
@@ -2005,4 +2041,4 @@ class OQLParser extends OQLParserRaw
}
}
#line 2014 "..\oql-parser.php"
#line 2052 "..\oql-parser.php"

View File

@@ -23,7 +23,15 @@ later : solve the 2 remaining shift-reduce conflicts (JOIN)
%name OQLParser_
%declare_class {class OQLParserRaw}
%syntax_error {
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
}
/* Bug N°4052 Parser stack size too small for huge OQL requests */
%stack_size 1000
%stack_overflow {
throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
}
%parse_failure {
throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
}
result ::= union(X). { $this->my_result = X; }
@@ -263,15 +271,43 @@ func_name(A) ::= F_INET_NTOA(X). { A=X; }
%code {
class OQLParserException extends OQLException
{
public function __construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue)
{
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
}
}
class OQLParserSyntaxErrorException extends OQLParserException
{
public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue)
{
$sIssue = "Unexpected token $sTokenName";
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
}
}
class OQLParserStackOverFlowException extends OQLParserException
{
public function __construct($sInput, $iLine, $iCol)
{
$sIssue = "Stack overflow";
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
}
}
class OQLParserParseFailureException extends OQLParserException
{
public function __construct($sInput, $iLine, $iCol)
{
$sIssue = "Unexpected token $sTokenName";
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
}
}
class OQLParser extends OQLParserRaw
{
// dirty, but working for us (no other mean to get the final result :-(

View File

@@ -173,7 +173,7 @@ class MatchOqlExpression extends MatchExpression implements CheckableExpression
throw new OqlNormalizeException('Only "field MATCHES string" syntax is allowed', $sSourceQuery, new OqlName($this->m_oLeftExpr->RenderExpression(true), 0));
}
// Only field MATCHES scalar is allowed
if (!$this->m_oRightExpr instanceof ScalarExpression)
if (!$this->m_oRightExpr instanceof ScalarExpression && !$this->m_oRightExpr instanceof VariableOqlExpression)
{
throw new OqlNormalizeException('Only "field MATCHES string" syntax is allowed', $sSourceQuery, new OqlName($this->m_oRightExpr->RenderExpression(true), 0));
}

View File

@@ -1 +1 @@
2019-12-03
2021-06-03

View File

@@ -64,12 +64,26 @@ class OQLActualClassTreeResolver
$aTranslateFields = array();
foreach ($aExpectedAttributes as $sAttCode => $oExpression)
{
if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
// 'id' is managed later
if ($sAttCode == 'id')
{
continue;
}
$sOriginClass = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
if (is_null($aClassAndAncestorsNodes[$sOriginClass]))
// Attributes can be stored in attributes list or for magic ones into filter codes list.
$sOriginClass = null;
if (MetaModel::IsValidAttCode($sClass, $sAttCode))
{
$sOriginClass = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
}
else if (MetaModel::IsValidFilterCode($sClass, $sAttCode))
{
$sOriginClass = MetaModel::GetFilterCodeOrigin($sClass, $sAttCode);
}
else
{
continue;
}
if (!isset($aClassAndAncestorsNodes[$sOriginClass]) || is_null($aClassAndAncestorsNodes[$sOriginClass]))
{
if ($sOriginClass == $sClass)
{
@@ -198,4 +212,4 @@ class OQLActualClassTreeResolver
}
}
}
}
}

View File

@@ -331,4 +331,12 @@ class OQLJoin
return $this->sRightField;
}
/**
* @return string
*/
public function GetLeftField()
{
return $this->sLeftField;
}
}

View File

@@ -50,8 +50,15 @@ class OQLClassTreeOptimizer
{
if ($oJoin->IsOutbound())
{
// The join is not used, remove from tree
$oCurrentClassNode->RemoveJoin($sLeftKey, $index);
// If joined class in not the same class than the external key target class
// then the join cannot be removed because it is used to filter the request
$sJoinedClass = $oJoin->GetOOQLClassNode()->GetNodeClass();
$sExtKeyAttCode = $oJoin->GetLeftField();
$oExtKeyAttDef = MetaModel::GetAttributeDef($oCurrentClassNode->GetNodeClass(), $sExtKeyAttCode);
if (($oExtKeyAttDef instanceof AttributeExternalKey) && ($sJoinedClass == $oExtKeyAttDef->GetTargetClass())) {
// The join is not used, remove from tree
$oCurrentClassNode->RemoveJoin($sLeftKey, $index);
}
}
else
{

View File

@@ -145,7 +145,8 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
/**
* @param DBObject $oObject
* @param string $sClassAlias
* @deprecated Since iTop 2.4, use ormLinkset->AddItem() instead.
*
* @deprecated Since iTop 2.4, use {@link \ormLinkSet::AddItem()} instead.
*/
public function AddObject(DBObject $oObject, $sClassAlias = '')
{
@@ -813,6 +814,17 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
$oNotObsoleteRemote->AddConditionExpression($oNotObsolete);
$oLinkSearch->AddCondition_PointingTo($oNotObsoleteRemote, $sExtKeyToRemote);
}
if (!utils::IsArchiveMode() && MetaModel::IsArchivable($sTargetClass))
{
$oNotArchived = new BinaryExpression(
new FieldExpression('archive_flag', $sTargetClass),
'=',
new ScalarExpression(0)
);
$oNotArchivedRemote = new DBObjectSearch($sTargetClass);
$oNotArchivedRemote->AddConditionExpression($oNotArchived);
$oLinkSearch->AddCondition_PointingTo($oNotArchivedRemote, $sExtKeyToRemote);
}
}
$oLinkSet = new DBObjectSet($oLinkSearch);
$oLinkSet->SetShowObsoleteData($bShowObsolete);
@@ -823,4 +835,4 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
return $oLinkSet;
}
}
}

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