Compare commits

..

956 Commits

Author SHA1 Message Date
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
Stephen Abello
a941e5f752 Merge branch 'develop' of https://github.com/Combodo/iTop into develop 2020-01-29 11:49:38 +01:00
Stephen Abello
b1878f7265 Update version number for 2.7.0-beta2 2020-01-29 11:49:20 +01:00
Molkobain
b106a54c50 N°2314 - Markup extensibility: Add 2 additional themes for the backoffice
Basic color changes to identify different environments (tests, production, ...)
2020-01-29 11:04:20 +01:00
Molkobain
002da0b387 N°2314 - Markup extensibility: Rework some SCSS variables 2020-01-29 11:04:20 +01:00
acognet
6b9e723a45 Merge remote-tracking branch 'origin/develop' into develop 2020-01-29 09:55:44 +01:00
acognet
b54e457cbb N°2038 - New dashlet Kanban 2020-01-29 09:54:56 +01:00
jbostoen
e750dd53d8 NL translations for iTop 2.7.0 (#94)
Made by @jbostoen & @Hipska in PR #94 . Many thanks to them !
2020-01-29 09:51:11 +01:00
Eric
da6a55504e N°985 - AttributeEnumSet (portal support) 2020-01-28 17:37:17 +01:00
Eric
b58356c42e N°985 - Add applicable contexts on Trigger (search) 2020-01-28 17:37:17 +01:00
Molkobain
524e43b8c4 N°2313 - Markup extensibility: Add metadata on session messages in the end-users portal 2020-01-28 17:27:32 +01:00
Eric
a80bd6f2b9 N°985 - Add applicable contexts on Trigger (display read-only) 2020-01-28 15:29:12 +01:00
Eric
08eb9ee630 🌐 update dictionaries for 2.7.0-beta2 2020-01-28 15:29:12 +01:00
Eric
05485b838e N°985 - Add applicable contexts on Trigger (display read-only) 2020-01-28 15:29:12 +01:00
Eric
029fe6882d N°985 - Add applicable contexts on Trigger (Fix regression) 2020-01-28 15:29:12 +01:00
Eric
b31eb6aab9 Comment 2020-01-28 15:29:12 +01:00
Molkobain
a96c194676 N°2313 - Markup extensibility: Add CSS classes on object details and lists in the end-users portal 2020-01-28 15:25:12 +01:00
bruno DA SILVA
0d85331bca 1627 - Ticket ref sometimes duplicate
🐛 INSERT/UPDATE do not require to free the results
2020-01-28 11:59:04 +01:00
Pierre Goiffon
e9dec8ae05 N°330 Attachments as table : fix table sorting on size and date
* portal : use data-order attribute for the DataTable plugin (https://datatables.net/manual/data/orthogonal-data)
* console : add textExtraction override for the attachments table (https://mottie.github.io/tablesorter/docs/example-option-text-extraction.html)
2020-01-28 10:11:29 +01:00
bruno DA SILVA
6a6a0ffa24 1627 - Ticket ref sometimes duplicate
🐛 fix ref. generation when inside a transaction (by opening a new connection).
note: the portal make uses of such a transaction.
2020-01-27 17:58:30 +01:00
Molkobain
87623fba3d PHPDoc 2020-01-27 16:58:55 +01:00
Molkobain
a7619f2820 N°2313 - Markup extensibility: Add metadata on admin. console object lists 2020-01-27 16:58:55 +01:00
bruno DA SILVA
cff53d71ba N°2154 & N°2720 & N°2684 - config integrity during setup 2020-01-27 15:21:50 +01:00
Molkobain
71708cfbc7 Internal: Fix setup headers size 2020-01-27 14:54:53 +01:00
acognet
e05b3a5fb9 N°2618 - Fix missing scroll bar in DataModel Viewer for class with large number of attributs 2020-01-27 10:47:19 +01:00
acognet
bfcb1fdb30 N°2618 - Fix missing scroll bar in DataModel Viewer for class with large number of attributs 2020-01-27 10:47:19 +01:00
Eric
cc4e1ea104 N°985 - Add applicable contexts on Trigger (Add portal contexts) 2020-01-24 17:43:59 +01:00
Molkobain
5485897bbb N°2313 - Markup extensibility: Fix raw value and attribute label not always being escaped 2020-01-24 17:38:18 +01:00
Molkobain
27f343e543 N°330 - Attachments: Align table rendering to linkset tables rendering 2020-01-24 17:02:02 +01:00
Molkobain
cca79735fc N°330 - Attachments: Fix empty "Delete" column displayed all the time 2020-01-24 17:02:02 +01:00
Molkobain
c48bbfd32a N°2313 - Markup extensibility: Add metadata on attachments 2020-01-24 17:02:02 +01:00
Molkobain
3ae2058f6f N°2314 - Markup extensibility: Refactor utils::GetCSSFromSASS() to enable SCSS compilation out of a file 2020-01-24 17:02:02 +01:00
Molkobain
0a63568715 PHPDoc 2020-01-24 17:02:02 +01:00
Molkobain
f878eea68d N°330 - Attachments: Update MS Office and OpenOffice file icons with more modern versions 2020-01-24 17:02:02 +01:00
Eric
8ad2b8091c N°2657 - MTP : Progress Bar has disappeared 2020-01-23 11:28:52 +01:00
bruno DA SILVA
d6ca08efb8 N°2730 - Cannot log callstack with callback into EventIssue 2020-01-22 17:37:44 +01:00
Molkobain
ba8a2c1b15 N°2710 - Fix setup crash due to PHP notices (regression introduced in 59678955) 2020-01-22 13:32:31 +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
bruno DA SILVA
0ae0336e04 N°1627 - Ticket ref sometimes duplicate
🐛 Ticket creation no more crash if the current user have limited read access
2020-01-21 18:31:09 +01:00
Molkobain
8df0ef6af9 N°2723 - Fix double scrollbar in search criterion 2020-01-21 17:40:11 +01:00
Molkobain
d77c77c03b PHPDoc 2020-01-21 17:20:12 +01:00
Molkobain
5967895561 N°2710 - Fix extremely slow page load for first user after setup (regression introduced in N°2314) 2020-01-21 17:19:16 +01:00
Molkobain
6e754d4fa5 Setup: Fix graphiz detection feedback message on Windows systems 2020-01-21 15:50:33 +01:00
Molkobain
165fd0e700 N°2314 - Setup: Improve UI of user message when CRON is running 2020-01-21 15:35:09 +01:00
Molkobain
d100ce8005 PHPDoc and code formatting 2020-01-21 15:35:09 +01:00
Eric
770f5a7b67 N°985 - Add applicable contexts on Trigger (order values in DB) 2020-01-21 14:55:03 +01:00
Molkobain
a993f6a80b PHPDoc 2020-01-21 14:19:45 +01:00
Molkobain
c8bb710d21 N°2314 - Markup extensibility: Fix regression introduced in the previous commit (used a PHP 7.x function) 2020-01-21 14:17:44 +01:00
Molkobain
d963fbd8cf N°2314 - Markup extensibility: Fix crash when no <theme> defined in datamodel 2020-01-21 12:20:59 +01:00
Eric
beda8e2810 N°985 - Add applicable contexts on Trigger (fix MTP warnings) 2020-01-21 12:01:42 +01:00
Pierre Goiffon
083f8d69c2 Revert "N°2618 DataModel viewer : fix no vertical scrollbar in MSIE"
Was introducing a regression in Chrome & Fx (cannot change tab anymore)
This reverts commit 8a666b09d6.
2020-01-21 10:51:16 +01:00
Eric
1c16eeb5e4 N°2240 - Supportability - Maintenance mode (setup reset maintenance mode) 2020-01-21 10:27:47 +01:00
Stephen Abello
ecc0b57b31 Merge branch 'master' into develop
# Conflicts:
#	css/css-variables.scss
#	css/light-grey.css
#	datamodels/2.x/authent-external/module.authent-external.php
#	datamodels/2.x/authent-ldap/module.authent-ldap.php
#	datamodels/2.x/authent-local/module.authent-local.php
#	datamodels/2.x/itop-attachments/module.attachments.php
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php
#	datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
#	datamodels/2.x/itop-config/module.itop-config.php
#	datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php
#	datamodels/2.x/itop-full-itil/module.itop-full-itil.php
#	datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal-base/module.itop-portal-base.php
#	datamodels/2.x/itop-portal/module.itop-portal.php
#	datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php
#	datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/module.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php
#	datamodels/2.x/version.xml
2020-01-20 16:42:42 +01:00
Stephen Abello
be9f6eff29 Merge branch 'develop' of https://github.com/Combodo/iTop into develop 2020-01-20 16:41:44 +01:00
Stephen Abello
50a8af4082 Update version number for 2.6.3 2020-01-20 16:30:51 +01:00
Stephen Abello
6a1125875b Merge branch 'support/2.5'
# Conflicts:
#	css/css-variables.scss
#	css/light-grey.css
#	datamodels/2.x/authent-external/module.authent-external.php
#	datamodels/2.x/authent-ldap/module.authent-ldap.php
#	datamodels/2.x/authent-local/module.authent-local.php
#	datamodels/2.x/itop-attachments/module.attachments.php
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php
#	datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
#	datamodels/2.x/itop-config/module.itop-config.php
#	datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php
#	datamodels/2.x/itop-full-itil/module.itop-full-itil.php
#	datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal-base/module.itop-portal-base.php
#	datamodels/2.x/itop-portal/module.itop-portal.php
#	datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php
#	datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/module.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php
#	datamodels/2.x/version.xml
2020-01-20 16:10:21 +01:00
Stephen Abello
878c23892d Update version number for 2.5.4 2020-01-20 15:59:08 +01:00
Eric
900e8ac6d7 N°985 - Add applicable contexts on Trigger 2020-01-20 15:50:08 +01:00
Stephen Abello
248dab9289 N°2633 - Security hardening 2020-01-20 15:46:04 +01:00
Molkobain
2fcea4d02e N°2313 - Markup extensibility: Add field label in its metadata 2020-01-20 14:43:40 +01:00
Stephen Abello
1927fc743a N°2314 - Fix left padding for menu entries 2020-01-20 11:20:56 +01:00
Molkobain
6aff09eaf7 N°2708 - Internal: Fix regression introduced with N°2313 "Undefined index 'prefix'" 2020-01-20 11:16:07 +01:00
Molkobain
54e9830a3b PHPDoc 2020-01-20 09:53:22 +01:00
Molkobain
a45819dbf0 N°2682 - Portal: Fix transaction ID not being removed 2020-01-17 17:40:37 +01:00
Molkobain
e8aaec5789 N°2060 - Regression: Fix missing PORTAL_ID constant 2020-01-17 17:33:52 +01:00
Molkobain
d16c0ffef9 N°2313 - Markup extensibility: Add metadata on admin. console object creation forms 2020-01-17 14:16:31 +01:00
Molkobain
24ad593dc8 N°2313 - Markup extensibility: Add password attributes to exclude list in metadata 2020-01-17 09:03:14 +01:00
Molkobain
0b67828ab9 Add comments in standard end-users portal XML 2020-01-17 09:03:14 +01:00
Eric
8a1a78444d N°2249 - Supportability - Updater module (unified version name) 2020-01-16 18:13:58 +01:00
Eric
4552bc0778 N°2249 - Supportability - Updater module (changed version name in the priv_module_install table) 2020-01-16 17:42:02 +01:00
Eric
3a113e31fb N°2249 - Supportability - Updater module (run setup when error occurs) 2020-01-16 17:25:28 +01:00
Pierre Goiffon
8a666b09d6 N°2618 DataModel viewer : fix no vertical scrollbar in MSIE
The scrollbar is present on the right side but after viewport limit so not visible !
Can't change div.ui-layout-pane.ui-layout-center width as it is calculated dynamically by JS Layout...
So added another container with a margin. The CSS rules are added to MSIE only using the media query tip :/
2020-01-16 16:40:36 +01:00
Pierre Goiffon
0d9dc34a08 Schema.php : replace <br/> to <br> (might break on MSIE) 2020-01-16 15:06:04 +01:00
Pierre Goiffon
c7ca1eeab5 🎨 Code formatting on pages/schema.php 2020-01-16 15:06:04 +01:00
Eric
02265135e3 N°2249 - Supportability - Updater module (run setup when error occurs) 2020-01-16 11:43:17 +01:00
Eric
2c1bf665c3 N°2249 - Supportability - Updater module (Add read-only warning) 2020-01-16 11:34:50 +01:00
Molkobain
60b6fcc783 N°2313 - Markup extensibility: Better display of success messages on form validation 2020-01-16 11:34:08 +01:00
Molkobain
53adb37f43 N°2060 - Fix session messages and SCSS compilation services being cached 2020-01-16 11:34:08 +01:00
Molkobain
a6fe564a95 Add comments in standard end-users portal XML 2020-01-16 11:34:08 +01:00
Pierre Goiffon
4945f25d49 N°2602 Portal : fix no more border around new caselog editor
With the new version of CKE N°2271, container is span.cke on MSIE instead of div.cke
2020-01-16 11:27:24 +01:00
Eric
e5f3daf88a N°2249 - Supportability - Updater module (Add read-only warning) 2020-01-16 11:19:30 +01:00
Eric
cc3e6d64e1 N°2249 - Supportability - Updater module (Allow to run setup in case of failure) 2020-01-16 10:49:49 +01:00
Eric
8024aad43d N°2249 - Supportability - Updater module (Add read-only warning) 2020-01-16 10:47:37 +01:00
Pierre Goiffon
757dbb8b25 N°2311 login page : add autofocus attribute to the id field
https://caniuse.com/#feat=autofocus
2020-01-16 10:38:08 +01:00
Eric
b370deaac9 N°2313 - Markup extensibility: Add support for both code AND title in admin. console tabs 2020-01-16 09:56:22 +01:00
Eric
026b7e1836 N°2249 - Supportability - Updater module (split ajax calls) 2020-01-15 17:09:31 +01:00
Eric
d03b924240 N°2249 - Supportability - Updater module (split ajax calls) 2020-01-15 16:58:36 +01:00
Eric
97a047e38f N°2249 - Supportability - Updater module (split ajax calls) 2020-01-15 16:42:47 +01:00
Eric
5be800cfce N°2249 - Supportability - Updater module (split ajax calls) 2020-01-15 15:48:54 +01:00
Molkobain
956b597e50 Fix how user data is retrieved for "Form Prefill" in the end-users portal 2020-01-15 15:16:50 +01:00
Molkobain
57100dee9f N°2060 - WIP: Fix cached part of the portal (sync. commit) 2020-01-15 12:53:40 +01:00
Eric
c3cc1afec1 🙈 remove unnecessary info 2020-01-15 11:37:10 +01:00
Pierre Goiffon
7fe24f58f3 N°330 Display attachments as table : portal remove author column 2020-01-15 10:26:25 +01:00
Molkobain
e4160c7cf2 N°2702 - Portal: Fix origin modal not closing when switching to editing of an object 2020-01-15 10:15:36 +01:00
Molkobain
7d87768ec4 PHPDoc and warnings suppression 2020-01-15 10:15:36 +01:00
Molkobain
9946e6c41a Fix typo 2020-01-15 10:15:36 +01:00
Stephen Abello
02b483e33e Setup's cursor style on label wasn't present in .scss file and was lost since 7b6481e 2020-01-15 10:14:59 +01:00
Stephen Abello
7b6481efbd N°2112: Setup alert message introduced by 83ba909 stopped working since 797893d🐒 🐒 2020-01-15 10:09:46 +01:00
Molkobain
efef582119 N°2306 - Security hardening (Fix regression introduced in f3b66a44, thanks to @bruno-ds !) 2020-01-15 08:42:35 +01:00
Molkobain
5056e561fe PHPDoc 2020-01-15 08:42:35 +01:00
Molkobain
4400cfde62 N°2313 - Markup extensibility: Update usages of admin. console tabs to have codes and titles 2020-01-15 08:42:35 +01:00
Molkobain
5a39581c60 N°2313 - Markup extensibility: Add support for both code AND title in admin. console tabs 2020-01-15 08:42:35 +01:00
Pierre Goiffon
4eab0e6450 N°330 Display attachments as table : portal improvements
* display attachments count in section title, updated on each add/delete
* remove "no attchments message" on adding new attachment
2020-01-14 11:56:20 +01:00
Molkobain
f3b66a44ee N°2306 - Security hardening 2020-01-14 11:50:50 +01:00
Molkobain
558f108520 N°2314 - Change breadcrumb icons color to black instead of Combodo's orange 2020-01-14 11:09:01 +01:00
Molkobain
07a93d12e2 Cleanup: Remove old/unused images/CSS files (Exhaustive list in migration notes) 2020-01-14 11:09:01 +01:00
Eric
ce127278bb N°2434 - Track field Comment in core/delete - API REST 2020-01-14 10:46:21 +01:00
Molkobain
887946144c N°2696 - Upgrade ArchiveTar to v1.4.9 (PHP 7.4 compatibility) 2020-01-14 10:35:53 +01:00
Molkobain
cc887c29fd N°2696 - Upgrade SCSSPHP to v1.0.6 (PHP 7.4 compatibility) 2020-01-14 10:35:53 +01:00
Molkobain
460836852e N°2696 - Upgrade SwiftMailer to v5.4.12 (Allow explicit tls1.0, tls1.1, tls1.2 for startTLS) 2020-01-14 10:35:53 +01:00
Eric
208d7ee7ba N°2093 - ApplyStimulus return true when stimuli is not applicable 2020-01-14 10:27:55 +01:00
Eric
3d92b73ae5 :globe-with-meridian: changed filesystem into files for itop-core-update 2020-01-14 10:14:03 +01:00
Stephen Abello
deddb0824b N°2315: Forgot password sent page needed stylization 2020-01-14 09:26:58 +01:00
Vladimir Kunin
3718899663 🌐 Update Russian translations for 2.7.0-beta 2020-01-13 17:52:15 +01:00
Pierre Goiffon
7f30d74f30 N°2269 Update Font Awesome to 5.12.0 2020-01-13 10:47:57 +01:00
Eric
21199fce34 N°2240 - Supportability - Maintenance mode exit on MTP or core update error 2020-01-10 15:32:29 +01:00
Pierre Goiffon
ad821e7d9c N°2651 rollback gitignore for lib tests dirs
Too dangerous ! We'll work properly on this but for 2.8
2020-01-10 15:23:15 +01:00
Eric
881fc2a1de N°2240 - Supportability - Maintenance mode exit on MTP or core update error 2020-01-10 14:14:20 +01:00
Vincent Dumas
44ee6baddb N°2675: Fix AdminTools DataSynchro creation
User with a profile enabling write access on the group id="AdminTools" was not able to create a DataSynchro also it should have been.
2020-01-09 16:07:47 +01:00
Federico Lazcano
42d782740e Paste error! 2020-01-09 10:05:22 +01:00
Federico Lazcano
cf16229948 🌐 Typos and new translations in ES CR 2020-01-09 10:05:22 +01:00
Federico Lazcano
a25427f4c6 Change from alias to real name
Sorry @Molkobain i changed my mind :-|
2020-01-09 10:01:03 +01:00
Federico Lazcano
04b2f7c836 🌐 Added ES CR translations 2020-01-09 09:28:36 +01:00
Molkobain
0f917af55a 👥 Add @lazki to our contributors list. Thanks! 👏 2020-01-09 09:12:27 +01:00
Federico Lazcano
daaed4696e 🌐 Typo in ES CR Translation 2020-01-09 09:08:34 +01:00
Pierre Goiffon
5f52c273d9 N°2329 PHP 7.4 compat : update setup requirements
This PHP version should be ok : we will give it a try during the beta program
2020-01-09 09:05:24 +01:00
Molkobain
b8d35e4783 👥 Add Guy Couronné (@GurneyHallack) to our contributors list! 2020-01-08 19:53:12 +01:00
Molkobain
149dff4b4d N°2313 - Markup extensibility: Add markup hooks on BrowseBrick and ManageBrick tables 2020-01-08 19:48:45 +01:00
Molkobain
f235e0cd66 Update copyright 2020-01-08 19:48:45 +01:00
Pierre Goiffon
e47e02932a N°2329 PHP 7.4 compat : fix warnings in TCPDF
Integrate last TCPDF fork version (6.3.2)
2020-01-08 17:50:51 +01:00
Eric
4544bba652 N°2240 - Supportability - Maintenance mode (setup CRON message) 2020-01-08 17:29:53 +01:00
Eric
9445e12254 MSIE 11 minimum 2020-01-08 17:23:36 +01:00
Eric
951945a607 N°2240 - Supportability - Maintenance mode (Better REST/Export message) 2020-01-08 17:17:34 +01:00
Eric
34f8fff01c Fetch() ignore row when sub-class does not exist 2020-01-08 16:38:11 +01:00
Stephen Abello
8d45e48ce1 🥅 N°1192 Portal: Increase navigation rules checks robustness 2020-01-08 15:31:19 +01:00
Eric
ed26f1cecc Fix menu creation flags 2020-01-08 15:10:10 +01:00
Molkobain
bc298afda3 Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	datamodels/2.x/combodo-db-tools/cs.dict.combodo-db-tools.php
#	datamodels/2.x/combodo-db-tools/da.dict.combodo-db-tools.php
#	datamodels/2.x/combodo-db-tools/datamodel.combodo-db-tools.xml
#	datamodels/2.x/combodo-db-tools/db_analyzer.class.inc.php
#	datamodels/2.x/combodo-db-tools/dbtools.php
#	datamodels/2.x/combodo-db-tools/de.dict.combodo-db-tools.php
#	datamodels/2.x/combodo-db-tools/default.css
#	datamodels/2.x/combodo-db-tools/default.scss
#	datamodels/2.x/combodo-db-tools/en.dict.combodo-db-tools.php
#	datamodels/2.x/combodo-db-tools/es_cr.dict.combodo-db-tools.php
#	datamodels/2.x/combodo-db-tools/fr.dict.combodo-db-tools.php
#	datamodels/2.x/combodo-db-tools/hu.dict.combodo-db-tools.php
#	datamodels/2.x/combodo-db-tools/it.dict.combodo-db-tools.php
#	datamodels/2.x/combodo-db-tools/ja.dict.combodo-db-tools.php
#	datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php
#	datamodels/2.x/combodo-db-tools/nl.dict.combodo-db-tools.php
#	datamodels/2.x/combodo-db-tools/pt_br.dict.combodo-db-tools.php
#	datamodels/2.x/combodo-db-tools/ru.dict.combodo-db-tools.php
#	datamodels/2.x/combodo-db-tools/tr.dict.combodo-db-tools.php
#	datamodels/2.x/combodo-db-tools/zh_cn.dict.combodo-db-tools.php
2020-01-08 12:03:55 +01:00
Molkobain
4f0e3430c0 Merge remote-tracking branch 'origin/support/2.5' 2020-01-08 11:58:15 +01:00
Molkobain
3347f400b8 Internal: Revert files deleted by mistake 🙈 2020-01-08 11:57:29 +01:00
Molkobain
6082308e20 Add combodo-db-tools/1.0.7 module as a default module 2020-01-08 11:40:35 +01:00
odain
1fc290587c N°2154: Fix security breach (scratch install usecase) 2020-01-08 10:10:49 +01:00
Molkobain
77fa02fcf9 PHPDoc and warnings suppression 2020-01-08 10:06:03 +01:00
Molkobain
d445551031 N°1986 - Fix regression introduced in b91183e9, creation form should never be read-only 2020-01-08 10:05:33 +01:00
Molkobain
ebfe9da464 N°2306 - Security hardening 2020-01-07 20:59:09 +01:00
odain
fdd79e91f0 N°2154: Fix security breach (scratch install usecase) 2020-01-07 17:44:39 +01:00
Molkobain
94a09493b0 N°2306 - Security hardening (BC break for some portal extensions, see migration notes) 2020-01-07 17:40:03 +01:00
Molkobain
56dbbb09dc Fix dependencies between modules 2020-01-07 17:00:19 +01:00
Pierre Goiffon
f019e05af5 N°2042 deprecated chrono extensivity 2020-01-07 16:07:25 +01:00
bruno DA SILVA
838c4f123c 👌 peer review
mostly coding convention,

thanks @molkobain
2020-01-07 15:34:27 +01:00
bruno DA SILVA
2043010aad N°2293 - API OnDBUpdate and AfterUpdate need modified fields and previous data
- add a getter for the protected property DBObject::$m_aChanges
2020-01-07 15:05:29 +01:00
bruno DA SILVA
cda18b950e N°524 - Password policy
- removal of a forgotten console.debug
2020-01-07 14:28:20 +01:00
Molkobain
acf28ca4aa N°2306 - Security hardening 2020-01-07 13:54:16 +01:00
Eric
864ded2102 Refactor Core Update (+8 squashed commit)
Squashed commit:

[b907bb759] Refactor Core Update

[5da2473aa] Refactor Core Update

[3fce45615] Refactor Core Update

[5f050a828] Refactor Core Update

[4b9b85174] Refactor Core Update

[f637ed358] Refactor Core Update

[56543edce] Refactor Core Update

[7f06900ef] Refactor Core Update
2020-01-07 10:00:14 +01:00
Eric
5cdc58846b Allow browsing developed OQL class tree 2020-01-07 10:00:14 +01:00
Eric
45cd96eb0d More informative message when class does not exist 2020-01-07 10:00:14 +01:00
Eric
c8335499fd Allow browsing developed OQL class tree 2020-01-07 10:00:14 +01:00
Eric
7f3efe59ab Refactor Core Update (+3 squashed commit)
Squashed commit:

[e1cbfe93f] Refactor Core Update

[41ec2adf7] Refactor Core Update

[ca6cefca3] Refactor Core Update
2020-01-07 10:00:13 +01:00
bruno DA SILVA
69551378c2 n°524 - password policy
- ajax message is now translated in the user's language
 - prevent the form submission if the password policy is not respected
2020-01-06 18:33:47 +01:00
odain
f3fd4bde87 💚 2020-01-06 16:09:41 +01:00
bruno DA SILVA
c115f64cb5 N°2154 - Security breach 2020-01-06 15:31:31 +01:00
Stephen Abello
ee61c1e8fb N°524: Fix style for inputs' feedback on "change password" page 2020-01-06 13:51:40 +01:00
bruno DA SILVA
e716fb118b N°2574 - enable Password expiry 2020-01-06 12:14:34 +01:00
bruno DA SILVA
b0c76346a5 N° 524 - password policy
better colors for : "change pwd" page: add feedback during the password typing
2020-01-06 11:45:48 +01:00
Pierre Goiffon
56807fd941 Person sample file : fix Descartes email
Fix typo introduced in 906e309791
2020-01-06 11:17:57 +01:00
Molkobain
b56f248b79 👥 Add Pimkie to our contributors list (BR / IT translations) 2020-01-06 09:50:15 +01:00
bruno DA SILVA
7a85201a07 524 - password policy
"change pwd" page: add feedback during the password typing
2020-01-06 09:31:28 +01:00
Purple Grape
97ebffd5fb improved chinese translations
1 improved chinese translations
2 fix some missing enries
3 correct line number against english language
2020-01-06 08:43:25 +01:00
Eric
46c239c211 typo 2020-01-03 16:40:49 +01:00
Vincent Dumas
906e309791 Replace Erri De Luca by René Descartes 2020-01-03 11:10:44 +01:00
bruno DA SILVA
4ad1ca0fc6 add option classmap-authoritative to composer.json 2020-01-02 14:19:00 +01:00
bruno DA SILVA
015955f396 N°2306 - Security hardening 2019-12-30 17:31:50 +01:00
Pierre Goiffon
9bee1905c8 N°2329 PHP 7.4 compat : remove get_magic_quotes_gpc/get_magic_quotes_runtime calls
Methods are now deprecated and since PHP 5.4 were always returning false
2019-12-24 17:30:12 +01:00
Pierre Goiffon
8ab157eae4 N°2329 PHP 7.4 compat, AttributeDefinition : fix visibilities of members called by AttributeDefinition::__construct
Not enough to get rid of child classes constructors though :/
2019-12-24 15:28:36 +01:00
Molkobain
17978b829b Internal: Simplify classes FQN 2019-12-24 15:08:45 +01:00
Molkobain
4ddb23cd7c N°2654 - Portal: Fix filter on external key when coming from filter brick 2019-12-24 15:08:45 +01:00
Pierre Goiffon
e27eb7419e N°2329 PHP 7.4 compat, AttributeDefinition : add __construct() to child classes
Shouldn't be necessary but if not present PHP 7.4.0/7.4.1 is crashing when executing new Attribute...(...)
2019-12-24 12:22:49 +01:00
Pierre Goiffon
64ef572429 🎨 AttributeDefinition : some little inspections fixes, and fix misordered function modifiers
According to PSR, we should use public static and not static public
See https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Acoding_standards
2019-12-24 12:22:49 +01:00
Pierre Goiffon
2f81e0fd6f N°2329 PHP 7.4 compat : remove deprecated array with curly braces 2019-12-23 18:44:52 +01:00
odain
987f1f7dbf N°2568 - Log_KPI viewer in the console - Fix broken links 2019-12-23 17:17:10 +01:00
Pierre Goiffon
b53c91f7f3 Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	Jenkinsfile
#	setup/setuputils.class.inc.php
2019-12-23 11:34:55 +01:00
Pierre Goiffon
5fce2a2c1c Setup : fix MySQL TLS wiki URL 2019-12-23 11:27:56 +01:00
Pierre Goiffon
13d31ac211 utils::GetDefaultUrlAppRoot : make comparison case insentitive, add a test 2019-12-23 11:06:29 +01:00
Pierre Goiffon
57ae29cf2f \SetupPage::error : remove "error" title as existing calls already add it 2019-12-23 09:54:07 +01:00
Pierre Goiffon
0b3895e39e Core update : fix CanUpdateCore result never displayed on server with warning enabled
Was caused by a call to :
\Combodo\iTop\FilesInformation\Service\FilesInformationUtils::Scan
With path set to ''. Made from :
\Combodo\iTop\FilesInformation\Service\FilesInformation::CanUpdateCore
2019-12-20 18:37:51 +01:00
Pierre Goiffon
0d231d9b94 N°2651 Remove lib test files from index 2019-12-20 17:07:20 +01:00
Molkobain
27a6abeeb3 Internal: Add Benjamin to the sample data to welcome him! 2019-12-20 12:21:02 +01:00
Pierre Goiffon
c75e6960a7 N°2651 Remove lib test files from index 2019-12-20 11:57:18 +01:00
Pierre Goiffon
4766ca3fd0 N°2650 fix run_query error handling incompatible with PHP < 7.3.0 2019-12-20 09:21:00 +01:00
odain
523dd97eca N°2570 Update iTop license list 2019-12-19 16:40:17 +01:00
bruno DA SILVA
c09bd2bfc6 2626 - log modularity: filterable logs using minimal log level per channel
🔊 Adding log level Trace, which is not logged by default (as for Debug)
2019-12-19 11:17:18 +01:00
Molkobain
bd662eaf19 Update README to add 2.7.0-beta section 2019-12-18 16:47:11 +01:00
Pierre Goiffon
ff75ecfe27 💄 Setup : add styling on prerequisites summary title 2019-12-18 11:12:21 +01:00
Pierre Goiffon
54f8b74383 📝 restore WizardStep documentation
Do not add documentation in a doc block containing @copyright O:)
2019-12-18 11:05:41 +01:00
Pierre Goiffon
18a506673f 💄 Setup : restore label{ cursor:pointer;} 2019-12-18 10:44:28 +01:00
Pierre Goiffon
a108be8517 N°330 Fix first column not centered when creating attachment on new ticket 2019-12-18 09:56:33 +01:00
Eric
3e57c3b1e9 N°2249 - Supportability - Updater module (application upgrade) 2019-12-18 09:45:34 +01:00
Stephen Abello
f18e27a183 N°2314 Setup progress bar is now cuter (following 01cb88a) 2019-12-18 09:40:48 +01:00
Pierre Goiffon
358efb0f2f N°2518 Change log level for non existent legacy log file when trying to rename 2019-12-18 09:28:11 +01:00
bruno DA SILVA
723fc917f1 🐛 change back to an authoritative autoloader 2019-12-18 09:25:35 +01:00
Pierre Goiffon
01f34eea29 N°330 Attachments display as table : portal. Table is now responsive, attachment section is collapsable (collapsed by default) 2019-12-17 18:14:21 +01:00
bruno DA SILVA
705d941979 ⬆️ Upgrading dependencies 2019-12-17 17:43:18 +01:00
Stephen Abello
691acb45e6 N°2314 2 css variables were not overridable 2019-12-17 15:23:02 +01:00
Pierre Goiffon
97e4ff30ff N°330 attachments size column : right align (both on console and portal)
Allows easier comparison to see bigger files
2019-12-17 15:12:28 +01:00
Pierre Goiffon
34c76c735a N°330 Attachments display as table : portal. Various improvements
* update CSS for table layout (vertical align, change text-align)
* tooltip : fix max size was not used anymore (tooltip was removing the style attributes)
* tooltip : fix tooltip always available
* delete button background color set to same as validate button
* remove "type" column
* reduce attachment line height
2019-12-17 15:08:22 +01:00
Pierre Goiffon
47d8e35639 N°330 Improve \ormDocument::GetFormattedSize
* Fix typo in method name (many thanks @jbostoen !)
* Use \utils::BytesToFriendlyFormat
2019-12-17 15:08:22 +01:00
odain
d4dc739b30 Finalize license update file 2019-12-17 14:56:13 +01:00
acognet
b84859b07e optimizing the display of the label in FieldsExpression 2019-12-17 13:07:21 +01:00
bruno DA SILVA
3f154fa765 2626 - log modularity: filterable logs using minimal log level per channel
🐛 logs are no more written twice.
2019-12-17 12:33:34 +01:00
Eric
f31e32d7a9 N°2249 - Supportability - Updater module (disable submit when no file provided) 2019-12-17 11:53:29 +01:00
Molkobain
1589535a45 Internal: Add automatic refresh of the maintenance page every 30s to redirect user to iTop when done 2019-12-17 11:48:27 +01:00
odain
53353ec9e1 Merge branch 'develop' of https://github.com/Combodo/iTop into develop 2019-12-17 11:44:17 +01:00
odain
a601b1c59a release: css part of community-licenses.xml 2019-12-17 11:44:13 +01:00
odain
e093abcb2b release: lib part of community-licenses.xml 2019-12-17 11:28:05 +01:00
Molkobain
01cb88a661 N°2314 Regression: Add progress bar color change while on going to show user it's not stucked 2019-12-17 11:15:00 +01:00
Molkobain
5ef32b6b31 N°2314 Regression: fix invisible icons due to wrong FontAwesome calls 2019-12-17 11:09:10 +01:00
Stephen Abello
ba1c719568 N°2314 Regression: fix invisible icons due to wrong FontAwesome calls 2019-12-17 10:55:05 +01:00
Eric
0507e4f4a8 🔖 version 2.7.0-beta 2019-12-17 10:35:15 +01:00
Eric
60eb312e68 🔖 version 2.7.0-beta 2019-12-17 10:28:13 +01:00
Eric
044a8926b3 🔖 version 2.7.0-beta 2019-12-17 10:25:28 +01:00
odain
acf8c9d49e release: js part of community-licenses.xml 2019-12-17 10:22:07 +01:00
Eric
d99e79eb3f 🔖 xml version 1.7 2019-12-17 10:22:03 +01:00
Eric
843c8ccd38 🔖 version 2.7.0 2019-12-17 10:18:15 +01:00
Eric
d45326606d 🌐 Update translations 2019-12-17 10:09:10 +01:00
Molkobain
5886d038e3 Regression: Fix iTop Hub connector not being installed (caused by dd5ac38dd4) 2019-12-17 09:50:27 +01:00
Pierre Goiffon
8fed7c7005 💄 Setup : TLS message style was hardcoded, get back to standard CSS classes to be aligned with the rest 2019-12-17 09:16:10 +01:00
Molkobain
604522aa61 N°2314 Regression: Work on Setup stylesheet 2019-12-16 18:07:59 +01:00
Molkobain
7af35c2d09 Internal: Improve how CSS and JS files are loaded in the DOM to optimize rendering and avoid glitches 2019-12-16 17:53:43 +01:00
Stephen Abello
87497eb491 N°2314 Regression: extracted setup's stylesheet in its own file and pimped it up 2019-12-16 17:05:36 +01:00
Pierre Goiffon
434ed0dd4e N°330 Attachments display as table : portal 2019-12-16 15:51:56 +01:00
Pierre Goiffon
473a55bde6 🎨 bsfileuploader : code formatting 2019-12-16 15:25:32 +01:00
Pierre Goiffon
0b65b36e74 N°330 Attachments : add attributes in Attachment object
* creation date
* external key to creator User
2019-12-16 15:25:32 +01:00
Pierre Goiffon
4aeb78ccac N°330 Attachments display as table : console 2019-12-16 15:25:31 +01:00
Pierre Goiffon
21d5de1756 Fix run_query throwing exception when inccorect query and no suggestions returned 2019-12-16 15:07:26 +01:00
Stephen Abello
797893d317 N°2314 Regression: fix crash on setup ⌨️ 🐒 2019-12-16 14:16:29 +01:00
Eric
dd5ac38dd4 N°2249 - Supportability - Updater module (application upgrade) 2019-12-13 17:28:35 +01:00
Eric
2741a446ea Integrate database integrity module 2019-12-13 17:28:08 +01:00
Molkobain
da3d886bd7 N°2618 - Fix missing scroll bar in DataModel Viewer for class with large number of attributs 2019-12-12 16:41:09 +01:00
Stephen Abello
ad40e02fee N°2314 Forgot to commit these changes with 611e82 🙈 2019-12-12 15:55:06 +01:00
Vincent Dumas
7cc63e21dd Prevent trigger without friendlyname
Make 'description' mandatory as it is used as friendly name, and a blank value is not clickable in the UI
2019-12-12 15:47:39 +01:00
Stephen Abello
611e828d1a N°2314 Introduce custom themes for iTop's console 2019-12-12 15:46:03 +01:00
odain
3abcd59b03 remove redundant test on log api 2019-12-12 14:41:26 +01:00
odain-cbd
48f4cd8943 Merge pull request #101 from Combodo/feature/faf_log_modularity
Feature/faf log modularity
2019-12-12 14:44:02 +01:00
odain
abd5e27c2e fix and complete testcase 2019-12-12 14:22:49 +01:00
Eric
ccb3a21c68 N°2595 - Reorganize/rename admin. console menus (hide empty sub-menus) 2019-12-12 10:20:29 +01:00
Molkobain
aa060746d7 N°2009 - Fix non editable dashboard when wrong attribute code used in its definition 2019-12-12 09:50:13 +01:00
odain
6ec441e501 peer review: added tests but not passed yet 2019-12-12 08:43:01 +01:00
Molkobain
52d3d8cfe7 Internal: PHPDoc, warnings suppression and typos 2019-12-11 20:54:45 +01:00
Molkobain
081ba68af4 N°2634 - Fix non editable dashlets in dashboards 2019-12-11 20:54:45 +01:00
odain
38d5889979 log kpi: class name added to next page buttons in order to use them in itop-log-mgt 2019-12-11 17:58:49 +01:00
Molkobain
5b4808c378 Internal: PHPDoc 2019-12-11 15:58:47 +01:00
Molkobain
2af22d3387 N°2060 Fix regression introduced during migration (iPortalUIExtension extensions not working) 2019-12-11 15:54:01 +01:00
Eric
7c1a8c90da N°2595 - Reorganize/rename admin. console menus 2019-12-11 13:59:31 +01:00
Molkobain
e7726a17db N°2630 - Portal: Fix wrong "apply stimulus" form being used in a branch of classes 2019-12-10 21:51:28 +01:00
Molkobain
57da9a848d Internal: Optimize regexp to remove warning 2019-12-10 21:51:28 +01:00
Molkobain
464ee46631 Internal: Refactor a small piece of code for better readability 2019-12-10 21:51:28 +01:00
Molkobain
46358b6e36 N°1982 - Fix resolution date not updated as expected when concurrent access from both portal and admin. console 2019-12-10 21:51:28 +01:00
Pierre Goiffon
46d6779562 🎨 utils : some code formatting + 1 @var 2019-12-10 17:57:55 +01:00
bruno DA SILVA
ea708e1e05 995 - Searching in OQL, URs with a particular request template field value
added AllowDelete in order to bypass the user's rights
2019-12-10 16:59:44 +01:00
Eric
374946505a N°2381 - Not possible to create a ticket in "resolved" with lnk objects 2019-12-10 15:30:53 +01:00
Pierre Goiffon
18db31f138 Log : rename config parameter from 'min_log_level' to 'log_level_min' 2019-12-10 09:46:42 +01:00
Pierre Goiffon
332c6eb33c log : factorize level & context 2019-12-10 09:39:46 +01:00
bruno DA SILVA
e5c49e3bd4 filterable logs using min_log_level optionnaly per channels 2019-12-10 09:03:14 +01:00
Molkobain
60769dc4b7 Internal: Add Dimitri to the sample data to welcome them! 2019-12-09 16:15:56 +01:00
Pierre Goiffon
9e652ef214 Update iPortalUIExtension param (not using Silex anymore but Symfony) 2019-12-09 16:11:29 +01:00
Pierre Goiffon
d2825c1b24 iTop Hub Connector : remove installation.xml
Was commited by mistake (duplicate of datamodels/2.x/installation.xml)
2019-12-09 16:02:39 +01:00
Eric
8482be7068 N°2623 - Fix setup request error 2019-12-09 14:56:42 +01:00
Eric
9dc56b727e N°1987 - Fix search equals 0 for integer fails 2019-12-06 17:01:46 +01:00
Eric
c6759220b9 🎨 clean warnings and add KPI in ajax.render.php 2019-12-06 15:42:03 +01:00
Pierre Goiffon
470af54acb Add some HTML in errors thrown by \MetaModel::DBCheckFormat 2019-12-05 17:56:42 +01:00
Eric
344f74f444 N°1213 - Allow NOT IN SELECT in OQL syntax - Fix UNION queries 2019-12-05 17:33:47 +01:00
Molkobain
0d1ca1bc0e N°2611 - Make itop-portal-base module mandatory during setup 2019-12-05 17:30:30 +01:00
Molkobain
d9e3684d3d Internal: Add Marie-Annette to the sample data to welcome them! 👋 2019-12-05 17:07:05 +01:00
Molkobain
e59db3f3d9 Internal: Add Alexandre, Anne-Catherine and Olivier to the sample data to welcome them! 👋 2019-12-05 15:47:46 +01:00
Molkobain
c883d618c3 N°1192 - Change default behavior for navigation rules 2019-12-05 15:02:36 +01:00
Eric
8911a9a3ed N°2381 - Not possible to create a ticket in "resolved" with lnk objects 2019-12-05 13:06:23 +01:00
Molkobain
366d2754ef N°2060 - Fix regression making url pointing to the portal not working in notification anymore 2019-12-05 12:39:28 +01:00
Molkobain
4db114f64d N°2313 - Markup extensibility: Add meta information and hooks
Admin. console:
- Class and id on object form
- Class current and target state on object transition form
- Object form mode (view/edit/create/stimulus)
- Object attributes: Attribute code, attribute definition class and raw value
- Navigation menu: Their IDs on group and items
- Actions menu: Their IDs on items

Portal:
- Object attributes: Attribute code, attribute definition class and raw value
2019-12-05 12:27:02 +01:00
Pierre Goiffon
d36340a3cd TagSet tests : use expectException method 2019-12-05 11:50:58 +01:00
Pierre Goiffon
7e6de5d8dd 🐛 \SetupUtils::builddir : avoid infinite loops 2019-12-05 10:27:22 +01:00
bruno DA SILVA
490eda4f4a n°524 - password policy
bugfix : Enter Password phrase no more lack the userName and is properly displayed
2019-12-05 09:55:44 +01:00
Eric
b5b4d70c2d N°1213 - Allow NOT IN SELECT in OQL syntax - Add unit tests 2019-12-05 09:02:11 +01:00
Eric
a74cff6902 N°1213 - Allow NOT IN SELECT in OQL syntax - Fix unit tests 2019-12-04 15:39:44 +01:00
Eric
1010274c48 N°1213 - Allow NOT IN SELECT in OQL syntax - Fix search init & code cleanup 2019-12-04 14:50:36 +01:00
Eric
c39ff13217 N°1213 - Allow NOT IN SELECT in OQL syntax - Fix search init 2019-12-04 14:40:53 +01:00
Eric
58da108e85 N°1213 - Allow NOT IN SELECT in OQL syntax 2019-12-04 14:27:02 +01:00
Eric
8b4fdb54ea N°1213 - Allow NOT IN SELECT in OQL syntax - support of UNION queries 2019-12-04 11:17:51 +01:00
Pierre Goiffon
1877cb5e93 N°2614 Fix datasynchro error when running synchro_import.php 2019-12-04 11:08:18 +01:00
bruno DA SILVA
1530b3f2ca 🐛 apc_clear_cache & opcache_reset can both be called
especially since not calling apc_clear_cache may result in invalid apcu entries...
2019-12-04 10:39:30 +01:00
Eric
4c3c5228aa N°1213 - Allow NOT IN SELECT in OQL syntax 2019-12-03 17:23:14 +01:00
Eric
3ae4ca89f4 N°1213 - Allow NOT IN SELECT in OQL syntax - support of UNION queries and "nested nested" queries 2019-12-03 17:14:53 +01:00
Eric
b415b1eeae N°1213 - Allow NOT IN SELECT in OQL syntax - support of UNION requests 2019-12-03 12:11:00 +01:00
Eric
c0ae983faa N°1213 - Allow NOT IN SELECT in OQL syntax 2019-12-03 12:11:00 +01:00
acognet
7845cbcc55 update tests 2019-12-03 12:11:00 +01:00
acognet
a33977251e N°1213 - Allow NOT IN SELECT in OQL syntax 2019-12-03 12:11:00 +01:00
odain
b0d668b124 N°1213 - Allow NOT IN SELECT in OQL syntax - add unit tests 2019-12-03 12:11:00 +01:00
odain
b0856c1abf fix HTMLDOMSanitizerTest due to merge 2019-12-03 12:10:26 +01:00
Pierre Goiffon
2bffa3cf17 N°2615 PHP versions setup requirements/warnings 2019-12-03 11:54:13 +01:00
odain
5ab05c6a6a Merge branch 'develop' of https://github.com/Combodo/iTop into develop 2019-12-03 11:42:08 +01:00
odain
72af2b7cd6 comment OQL section in phpunit.dist.xml 2019-12-03 11:41:57 +01:00
Molkobain
143c0a5b07 N°2616 - Fix hyperlink placeholder not working in notifications for other portals 2019-12-03 11:39:14 +01:00
Molkobain
daf79f2324 N°2272 Fix calls to DBObjectSet::OptimizeColumnLoad() 2019-12-03 11:39:14 +01:00
odain
15d11c6c86 reomve echo that prevented phpunit execution 2019-12-03 11:26:35 +01:00
odain
b298a1fa82 fix invalid merge 2019-12-03 11:23:25 +01:00
odain
007b8147c7 fix phpunit step 2019-12-03 11:17:01 +01:00
odain
4013b76c9e Merge branch 'speedup_jenkins' into develop 2019-12-03 11:09:30 +01:00
odain
ed81391aff !y# This is a combination of 4 commits.
run OQL tests depending on jenkins param

try sth

try sth

try sth

try sth

try sth

fix parameter to run OQL tests or not

run OQL tests depending on jenkins param

try sth

try sth

try sth

try sth

try sth

fix parameter to run OQL tests or not
2019-12-03 11:02:15 +01:00
Pierre Goiffon
28818010d6 N°2558 Test for the HTMLDOMSanitizer white list 2019-12-03 10:58:21 +01:00
Eric
928c19f923 move OQL2SQL tests to another folder 2019-12-03 10:58:21 +01:00
Eric
34aa240840 🎨 Fix bad parameter init 2019-12-03 10:58:21 +01:00
Eric
6872dbd180 🎨 Fix bad parameter init 2019-12-03 10:58:21 +01:00
Pierre Goiffon
c5bea30c64 N°2329 PHP 7.4 compat : remove deprecated get_magic_quotes_gpc() function call 2019-12-03 10:58:21 +01:00
Molkobain
071a254611 N°2311 Fix focus set to sumbit button when pressing "tab" key after filing inputs 2019-12-03 10:58:21 +01:00
Molkobain
d2d203df34 💄 Increase blur effect on portal modal backdrop 2019-12-03 10:58:21 +01:00
Molkobain
e1ffdea172 N°2272 Fix call to DBObjectSet::OptimizeColumnLoad() in the portal (doesn't support "id" attribute anymore) 2019-12-03 10:08:46 +01:00
Pierre Goiffon
9abcf40df7 N°2558 Test for the HTMLDOMSanitizer white list : remove new lines
the parser gives different results depending on the PHP version
Didn't manage to get it right :
- no php.ini difference
- playing with the parser preserveWhitespace/formatOutput parser options didn't help

So we're removing new lines on both sides :/
2019-12-03 09:47:45 +01:00
Pierre Goiffon
b67dc888fe N°2558 Test for the HTMLDOMSanitizer white list 2019-12-03 08:42:25 +01:00
Eric
c7b101d169 move OQL2SQL tests to another folder 2019-12-03 08:38:57 +01:00
Eric
df5a7440a4 🎨 Fix bad parameter init 2019-12-03 08:38:57 +01:00
Eric
a59a8c0ec5 🎨 Fix bad parameter init 2019-12-03 08:38:57 +01:00
Pierre Goiffon
bf9f43da8b N°2329 PHP 7.4 compat : remove deprecated get_magic_quotes_gpc() function call 2019-12-02 13:47:44 +01:00
Molkobain
928b82a9c8 N°2311 Fix focus set to sumbit button when pressing "tab" key after filing inputs 2019-11-29 17:40:07 +01:00
Molkobain
b9008d2459 💄 Increase blur effect on portal modal backdrop 2019-11-29 17:38:20 +01:00
odain
50dddaaa9b add debug mode as param + use params in phpunit.sh 2019-11-29 16:46:59 +01:00
odain
1b168501af add a parameter to Jenkins pipeline 2019-11-29 16:37:01 +01:00
bruno DA SILVA
35a49bad60 n°524 - password policy
typos

(thanks @jbostoen)
2019-11-29 08:56:18 +01:00
Pierre Goiffon
97c1ff55e9 📝 Comment to explain why Uniqueness isn't protected by a lock 2019-11-28 17:16:01 +01:00
Molkobain
ffead92d5a N°2613 - Portal: Fix crash in object form having empty AttributeBlob field 2019-11-28 17:05:05 +01:00
Molkobain
81ea2b1fe4 Internal: Add comment on suspected dead code 2019-11-28 17:05:05 +01:00
Molkobain
9cdfe0ecb4 Internal: PHPDoc and warning suppression 2019-11-28 17:05:05 +01:00
bruno DA SILVA
0473269132 n°1617 - iTop Fence
- iTop Fence now lock user fetch/creation per login to prevent duplicates
 - iTop now provide a mean to create safe lock when used untrusted strings
 - better log message & function names
 - reduced cyclomatic complexity

(thanks @piRGoif)
2019-11-28 17:03:31 +01:00
Eric
4798bf2f79 N°2408 - Fix FromOQL() with NULL 2019-11-28 11:54:42 +01:00
bruno DA SILVA
c3c5c56dd8 n°524 - password policy
- changed translation key to be more conventional
2019-11-28 11:40:28 +01:00
bruno DA SILVA
4fb9bbb831 n°524 - password policy
- "change password" is now handled gracefully by the portal
2019-11-28 09:05:20 +01:00
bruno DA SILVA
267cdd2aee n°524 - password policy
- "password reset" workflow now handle gracefully the policy enforcement.
2019-11-28 08:52:02 +01:00
bruno DA SILVA
fe0bd1a4b8 n°524 - password policy
- translations added
 - fields are now visible in the detail view
 - minor translation modification for the User object
2019-11-28 08:52:02 +01:00
Molkobain
9fa510d2a8 Internal: PHPDoc and warning suppression 2019-11-27 20:40:39 +01:00
Molkobain
fd29986354 N°2000 - Fix blank page when displaying a synchronized object 2019-11-27 20:07:17 +01:00
Molkobain
8ec6bb4758 N°1666 - Fix missing scroll bar missing in modal window "Create a new field" from Request Template 2019-11-27 19:35:19 +01:00
Molkobain
f3306f5fb4 N°1616 - Fix truncated caselog entry with large HTML table or word 2019-11-27 19:05:37 +01:00
Molkobain
20683fdf50 N°1192 Fix transition form always redirecting to object no matter the navigation rule 2019-11-27 17:21:01 +01:00
Molkobain
603ae8c0e1 N°2272 Fix calls to DBObjectSet::OptimizeColumnLoad() in the portal (now requires ClassAlias to always be specified) 2019-11-27 17:21:01 +01:00
Eric
f1d0418e48 🐛 Fix error when no cache is configured 2019-11-27 16:02:46 +01:00
Eric
9c8c306df3 🐛 Fix DBSearch::Intersect (de-duplicate aliases) 2019-11-27 15:39:33 +01:00
Eric
d2543e9c67 🐛 Fix bad calls to OptimizeColumnLoad 2019-11-27 15:38:30 +01:00
Pierre Goiffon
55a0d910fa N°2490 MariaDB compat : changes after code review
* DEFAULT value unquoting is done with preg_replace now (clearer that we want to do a string replacement)
* DEFAULT value unquoting works even if DEFAULT is not the last keyword
* change test datasource for more readability (use double quotes when needed)
2019-11-27 11:51:47 +01:00
Pierre Goiffon
bd8144a67c 👷 Jenkins : config file is now picked by the unattended install
The problem was that we had an absolute path in the XML : changing it to the file name is sufficient ! The copy introduced in 443763de was unecessary.
2019-11-27 11:45:30 +01:00
Pierre Goiffon
004d1a7245 🎨 Rename constant 2019-11-27 10:15:24 +01:00
Pierre Goiffon
09fb99ed58 Rollback 2f431a0d : might not be a typo, we don't want to cause trouble close to a beta version O:) 2019-11-26 17:31:41 +01:00
Eric
ae8071f707 add unit tests for intersect 2019-11-26 17:24:23 +01:00
Eric
c1cf084e43 fix unit tests - Support Microsoft encoding of non breaking line in UTF-8 2019-11-26 16:32:19 +01:00
Eric
87c794b22e fix unit tests - Support Microsoft encoding of non breaking line in UTF-8 2019-11-26 15:44:17 +01:00
Molkobain
a382d6ad35 N°1192 Change "close rule" behaviour to redirect to homepage if browser doesn't let us close the window 2019-11-26 14:57:35 +01:00
Eric
2d86599a19 N°2519 - ev_timeout from "New" to "Escalated TTO" doesn't work 2019-11-26 13:55:26 +01:00
Pierre Goiffon
914971b30d 💚 Jenkins : Fix default config database name 2019-11-26 12:15:02 +01:00
Pierre Goiffon
443763de48 💚 Jenkins : copy default config after unattended install
The generated config file had too many default values, and not the wanted ones
2019-11-26 12:09:03 +01:00
Stephen Abello
ff3c7ebe54 N°971 Portal: Fix Browse brick n:n level links in tree and mosaic display 2019-11-26 11:39:25 +01:00
Stephen Abello
c0f82f25a3 N°956 Portal: Increase robustness 💪 2019-11-26 11:39:25 +01:00
Eric
f90381d412 Support Microsoft encoding of non breaking line in UTF-8 2019-11-26 08:57:47 +01:00
bruno DA SILVA
d367d2e864 n°524 - password policy
💚 fix CI regression (data provider no longer include classes with side effect)
2019-11-25 18:02:25 +01:00
bruno DA SILVA
9d20eba2ad 2574 - enable Password expiry
- Extensibility: The UserLocal now provide the fields needed for an extension to be able to properly handle the expiration of the password
2019-11-25 17:37:34 +01:00
bruno DA SILVA
863746852f n°524 - password policy
💚 fix CI : tests must be runned into a separate process in order to eliminate side effects of mocking the config
2019-11-25 16:53:04 +01:00
bruno DA SILVA
f416f994c9 n°2371 deprecate \MetaModel::EnumLinksClasses and \MetaModel::EnumLinkingClasses
- phpdoc: move title the to beginning of the block
2019-11-25 16:35:46 +01:00
bruno DA SILVA
70dfbbc15e n°524 - password policy
- The code now uses the standard extension method (using interfaces)
 - the metamodel can now filter on iModuleExtension in order to leverage extensions modularity (see MetaModel::EnumPlugins second param)
 - during the setup, there is no pawsord policy control
 - there is now a default policy
 - new (more precie) translation reflecting the default policy
 - fix CI?
2019-11-25 16:25:38 +01:00
Molkobain
85932eab98 N°1192 Fix crash with default parameters of the rule 2019-11-25 15:33:23 +01:00
Pierre Goiffon
0ee77d8c88 N°2163 DB*Tracked methods : modifications after review with Romain
Previous commit : 24eb82d1
Use \CMDBObject::SetTrackInfo
Move \CMDBObject::SetCurrentChange calls at the top most level of the stacks
Restore old behaviors that were removed in previous commit
2019-11-25 14:58:59 +01:00
Molkobain
2f2d9547b7 N°2603 - Portal: Fix crash when having comments in some parts of the XML 2019-11-25 12:38:34 +01:00
Molkobain
32b065708b N°1192 Add some ready-to-use navigation rules to the standard portal (and comments) 2019-11-25 09:45:31 +01:00
Molkobain
18285df154 Internal: Fix typo 2019-11-22 18:44:27 +01:00
Molkobain
b1b6c9f426 N°1192 Introduce navigation_rules in the end-users portal 2019-11-22 18:44:27 +01:00
Molkobain
ff884533f9 Internal: Add utils::ToCamelCase($sInput) function 2019-11-22 18:44:27 +01:00
Molkobain
417e80fe8d N°1192 Deprecate usage of <submit> and <cancel> tags in action rules 2019-11-22 18:44:27 +01:00
Pierre Goiffon
c203e6c7be Fix utils::StartsWith when needle bigger that value 2019-11-22 14:43:33 +01:00
bruno DA SILVA
730a0d1c98 n°524 - password policy
💚 fix CI : tests must be runned into a separate process in order to eliminate side effects of mocking the config
2019-11-22 14:38:36 +01:00
bruno DA SILVA
d9b374f723 n°524 - password policy
💚 fix CI regression (previous tests are sometimes incompatible with the default password policy)
2019-11-22 14:14:44 +01:00
bruno DA SILVA
b9cb692504 n°524 - password policy
bugfix: no rule does work properly
2019-11-22 12:31:14 +01:00
bruno DA SILVA
23fc4bb4f7 n°524 - password policy 2019-11-22 12:23:00 +01:00
Pierre Goiffon
4ae035dd51 🔧 Modify visual vertical guides setting 2019-11-22 11:42:43 +01:00
Pierre Goiffon
3a791162c5 setup module browsing perf improvement
When a module descriptor file is found, do not dig anymore in subdirectories
2nd fix, should be better than previous one (4042a12d reverted with 5ebc290b)
2019-11-21 17:44:04 +01:00
Pierre Goiffon
06791b06c4 Fix utils::EndsWith when needle bigger that value 2019-11-21 11:57:24 +01:00
Eric
5ebc290b94 Revert Setup : only scan necessary dirs for extensions 2019-11-21 11:37:22 +01:00
Pierre Goiffon
675221a15e N°2490 Setup/toolkit : no longer generates useless ALTER TABLE queries on MariaDB >= 10.2
* case insensitive SQL data type comparison
* some options have also case differences (example 'int(11) unsigned')
* DEFAULT 'NULL' added by MariaDB on all nullable fields
* default values are always surrounded with single quotes on MariaDB

This is a Combodo implementation of PR #91
2019-11-21 11:24:14 +01:00
Pierre Goiffon
2f431a0d14 🐛 Fix SynchroAttLinkSet.attribute_qualifier default value
Old typo (in a galaxy far, far away) : 1e688706
2019-11-21 10:44:54 +01:00
Molkobain
fd4e41950c N°2092 Portal: Fix missing scrollbar in tall form modals 2019-11-20 15:41:11 +01:00
Molkobain
41d5ae704a PHPDoc 2019-11-20 15:41:11 +01:00
Molkobain
83fc069bf4 Portal: Improve callback calls in CombodoPortalToolbox 2019-11-20 15:41:11 +01:00
Pierre Goiffon
32c5cd245b N°2533 Check modules manual install dir on Setup for iTop products 2019-11-20 12:04:54 +01:00
Molkobain
a259be9033 N°2311 Fix focus set to the "login" input field when pressing "tab" key 2019-11-20 12:00:52 +01:00
Molkobain
fbbdee242a Portal: Update code to use the CombodoPortalToolbox.OpenModal() helper 2019-11-20 11:38:42 +01:00
Molkobain
4a12635ea5 Portal: Introduce "CombodoPortalToolbox", helpers to ease JS manipulations especially through the iPopupMenuExtension.
- CombodoPortalToolbox.CloseAllModals() : Close all modal on the page
- CombodoPortalToolbox.OpenUrlInModal(sTargetUrl, bCloseOtherModals) : Open an URL in a modal, typically opening an object form
- CombodoPortalToolbox.OpenModal(oOptions) : Generic method to open modals in the portal with various options
2019-11-20 11:37:39 +01:00
Molkobain
db7278e9c1 🐛 Fix compiler crashing on setup due to coment in XML 2019-11-19 17:59:20 +01:00
Molkobain
10056aa4ca ✏️ Fix typos in comments 2019-11-19 17:33:07 +01:00
Pierre Goiffon
bb566df432 📝 WizardSteps documentation v2
With an ascii art schema
Thanks to http://asciiflow.com/
2019-11-19 16:31:26 +01:00
Pierre Goiffon
4042a12d39 Setup : only scan necessary dirs for extensions
We were browsing the whole hierarchy but only the root module dir was needed
This could be slow (especially when developing with a .git dir)
2019-11-19 15:27:06 +01:00
Pierre Goiffon
5ae4f9ade8 📝 WizardSteps documentation 2019-11-19 15:21:27 +01:00
Molkobain
1636f9839c Updating Symfony lib and dependencies: forgot a few files + composer.lock 2019-11-18 18:08:40 +01:00
Molkobain
c76cccd2e7 Updating Symfony lib and dependencies:
Package operations: 2 installs, 23 updates, 0 removals
  - Updating psr/log (1.1.0 => 1.1.2)
  - Updating symfony/debug (v3.4.30 => v3.4.35)
  - Updating symfony/console (v3.4.30 => v3.4.35)
  - Updating symfony/dotenv (v3.4.30 => v3.4.35)
  - Updating symfony/routing (v3.4.30 => v3.4.35)
  - Updating symfony/finder (v3.4.30 => v3.4.35)
  - Updating symfony/filesystem (v3.4.30 => v3.4.35)
  - Installing symfony/polyfill-util (v1.12.0)
  - Installing symfony/polyfill-php56 (v1.12.0)
  - Updating symfony/http-foundation (v3.4.30 => v3.4.35)
  - Updating symfony/event-dispatcher (v3.4.30 => v3.4.35)
  - Updating symfony/http-kernel (v3.4.30 => v3.4.35)
  - Updating symfony/config (v3.4.30 => v3.4.35)
  - Updating symfony/dependency-injection (v3.4.30 => v3.4.35)
  - Updating symfony/class-loader (v3.4.30 => v3.4.35)
  - Updating symfony/cache (v3.4.30 => v3.4.35)
  - Updating symfony/framework-bundle (v3.4.30 => v3.4.35)
  - Updating twig/twig (v1.42.2 => v1.42.4)
  - Updating symfony/twig-bridge (v3.4.30 => v3.4.35)
  - Updating symfony/twig-bundle (v3.4.30 => v3.4.35)
  - Updating symfony/yaml (v3.4.30 => v3.4.35)
  - Updating symfony/stopwatch (v3.4.30 => v3.4.35)
  - Updating symfony/var-dumper (v3.4.30 => v3.4.35)
  - Updating symfony/web-profiler-bundle (v3.4.30 => v3.4.35)
  - Updating symfony/css-selector (v3.4.30 => v3.4.35)
2019-11-18 18:04:32 +01:00
Eric
532eb466a1 N°1436 - Access control updated for grant_by_profile categories of classes -
Fix access to internal classes from the core engine
2019-11-18 15:36:42 +01:00
Pierre Goiffon
e8879a0455 🎨 Code formatting 2019-11-18 15:29:03 +01:00
Pierre Goiffon
ef5b4e212c N°2371 deprecate \MetaModel::EnumLinksClasses and \MetaModel::EnumLinkingClasses 2019-11-18 10:20:18 +01:00
Pierre Goiffon
24eb82d140 N°2361 Deprecate DB*Tracked methods
* update methods PHPDoc
* DBInsertTracked update callers
* DBInsertTrackedNoReload update callers
* DBUpdateTracked update callers
* DBDeleteTracked update callers
2019-11-15 17:56:04 +01:00
Pierre Goiffon
cdc8edb56b Fix backup regression "Undefined class constant 'DEFAULT_MODULE_SETTING_TIME'"
Was introduced in PR #89, woops
2019-11-15 16:44:38 +01:00
Pierre Goiffon
7235c63445 Abstract implementation for iScheduledProcess (#89)
* 📝 little PHPDoc in BackupExec
* ♻️ Create a extendable implementation of iScheduledProcess
* create AbstractWeeklyScheduledProcess
* move schedule methods to the new abstract class
* create ProcessInvalidConfigException
* in cron.php skip abstract class
2019-11-15 14:41:00 +01:00
Pierre Goiffon
fd3b33b04b N°2586 Test for iTopDesignFormat 2019-11-14 18:07:17 +01:00
Pierre Goiffon
e8815e5653 N°1283 Add migration script 2019-11-14 18:04:22 +01:00
Pierre Goiffon
551b9a3b76 \MFDocument::saveXML : $options were not passed to the callee 2019-11-14 18:04:22 +01:00
bruno DA SILVA
1c6b639992 n°1617 - iTop Fence
various improvement both in iTop and in the extension.
2019-11-14 17:02:52 +01:00
bruno DA SILVA
6fb7587d95 🔧 write the tooling methods enabling migration to/from iTop 2.7 2019-11-14 16:59:08 +01:00
Eric
3953e74cb4 🎨 refactor approot 2019-11-14 15:43:29 +01:00
Pierre Goiffon
a8f616bba7 N°2577 Setup : remove prefix for table prefix collapsible section 2019-11-14 14:46:58 +01:00
bruno DA SILVA
560eb5e071 🔧 write the tooling methods enabling migration to/from iTop 2.7 2019-11-14 12:15:30 +01:00
Eric
c665bb4761 💚 Fix unit tests 2019-11-14 09:37:39 +01:00
Denis Flaven
36584092e5 (Experimental) Export a DBSearch as an array/JSON structure. 2019-11-13 18:03:58 +01:00
Eric
30430bb7dc N°2135 - Setup callbacks for MTP 2019-11-13 17:33:56 +01:00
Eric
fdf5cff12a N°2135 - Setup callbacks for MTP 2019-11-13 15:45:33 +01:00
Eric
4816b2b0fe N°2311 - Authentication extensibility in iTop (login on specific pages and traces) 2019-11-13 15:08:34 +01:00
Eric
9c808bf2ed N°2240 - Supportability - Maintenance mode 2019-11-13 13:50:48 +01:00
Eric
7a12c2c615 N°2240 - Supportability - Maintenance mode 2019-11-13 12:00:50 +01:00
Eric
149bc9f4ef fix typo 2019-11-13 11:41:40 +01:00
Stephen Abello
b91183e9ec N°1986 Portal: Fix read-only forms from da5ccaa to work with auto-generated ones 2019-11-13 11:19:26 +01:00
Eric
b67639f9ec N°2249 - Supportability - Updater module 2019-11-13 11:08:08 +01:00
Eric
cdbcc9ce8c N°2249 - Supportability - Updater module 2019-11-13 10:42:30 +01:00
Pierre Goiffon
6c7d094921 N°2577 Setup : table prefix option is hidden also on iTop first install
Previous commit 3e785687 was hidding it only on iTop update
2019-11-12 08:54:29 +01:00
Stephen Abello
23466f6e00 📝 typo in PHPDoc 2019-11-08 17:13:15 +01:00
bruno DA SILVA
08c1f4f072 autoload rework
- bootstrap.inc.php is now included by approot.inc.php
 - remove all unescessaries includes of bootstrap.inc.php
 - in bootstrap.inc.php autoload can be bypassed using a feature flag because "why not"
2019-11-08 16:51:57 +01:00
Pierre Goiffon
1194c5c7fe 🎨 sla-computation : fix PHPDoc + format code 2019-11-08 15:30:47 +01:00
Eric
f63fb16233 N°2135 - Setup callbacks for MTP 2019-11-08 14:05:24 +01:00
Eric
a4143df36a N°2240 - Supportability - Maintenance mode 2019-11-07 16:45:52 +01:00
Pierre Goiffon
3e78568755 N°2577 Setup : table prefix option is now hidden by default
Should be used only by a few portion of users
2019-11-07 16:34:25 +01:00
Eric
9b14cd7633 N°2240 - Supportability - Maintenance mode 2019-11-07 15:23:42 +01:00
Eric
2b2488f376 N°2240 - Supportability - Maintenance mode 2019-11-07 15:21:48 +01:00
Pierre Goiffon
fd77554cb7 N°2555 action_rule : fix add_to_list regression
Loop was removed in 5c483efd but we need it !
Sample rule that needs such a loop :
				<action_rule id="N2555_multiple_results_addtolist" _delta="define">
					<source_oql>SELECT Person WHERE id > 0</source_oql>
					<presets>
						<preset id="1">add_to_list(id, contacts_list)</preset>
					</presets>
				</action_rule>
2019-11-07 14:56:39 +01:00
Eric
2ee3d27ba8 N°2240 - Supportability - Maintenance mode (revert) 2019-11-07 14:15:34 +01:00
Eric
af24f46803 N°2249 - Supportability - Updater module 2019-11-07 13:24:48 +01:00
Eric
da1684a8b9 N°2249 - Supportability - Updater module 2019-11-07 13:14:20 +01:00
Eric
5e7ae930c5 N°2240 - Supportability - Maintenance mode 2019-11-07 12:36:35 +01:00
Stephen Abello
541226356c 📝 typo in PHPDoc 2019-11-07 11:38:08 +01:00
Eric
a30345c96c N°2249 - Supportability - Updater module 2019-11-07 10:00:05 +01:00
Eric
d035130d00 N°2240 - Supportability - Maintenance mode 2019-11-07 09:16:06 +01:00
Pierre Goiffon
7d8181c44f N°2576 Fix "invalid numeric value" when inserting/updating AttributeDecimal
Was happening on certain configurations with a non EN-US user locale
2019-11-06 18:15:40 +01:00
bruno DA SILVA
45536cf957 🐛 fix regression introduced by the PR n°98
the realpath removed the trailing / wich is required
2019-11-06 10:00:55 +01:00
Stephen Abello
da5ccaaa85 N°1986 Portal: Forms with only transition buttons are now read-only 2019-11-05 12:17:54 +01:00
jbostoen
7c773991e7 Version 20191104-1550
* fix setup crashing due to realpath() returning lowercase path on Windows
2019-11-05 08:55:41 +01:00
bruno DA SILVA
bf976e5b8f typo (bis)
ahhh!
2019-11-04 15:07:47 +01:00
bruno DA SILVA
d9ad3f5e98 typo & unused variable removal
thank you @Hipska !
2019-11-04 15:02:19 +01:00
Eric
9054dcb9ff N°2517 - Supportability : system report (user name) 2019-11-04 12:06:49 +01:00
bruno DA SILVA
57116ef054 1627 - Ticket ref sometimes duplicate
This code is a drop in replacement based on  an abstract counter based on an abstract keyValue store.
The counter can be
 - class based (in this case the counter is initialized on max(id) + 1
 - key based (in this case the counter starts at 0)

Important: on both cases the counter is no more kept aligned with the primary key.

This lead to a MySQL8 compatible implementation.
2019-11-04 11:50:36 +01:00
bruno DA SILVA
c6f5b8b1f9 1627 - Ticket ref sometimes duplicate
This code is a drop in replacement based on  an abstract counter based on an abstract keyValue store.
The counter can be
 - class based (in this case the counter is initialized on max(id) + 1
 - key based (in this case the counter starts at 0)

Important: on both cases the counter is no more kept aligned with the primary key.

This lead to a MySQL8 compatible implementation.
2019-11-04 11:50:36 +01:00
Pierre Goiffon
9e015ba59a 📝 UI better log on catch 2019-11-04 09:22:43 +01:00
Pierre Goiffon
6144cfad3d 📝 Link between Dict::Format and TemplateString 2019-10-31 10:35:48 +01:00
Stephen Abello
3844a18b86 N°971 Portal: Allow n:n links for Browse Brick's levels 2019-10-30 16:41:58 +01:00
Stephen Abello
e88c6e9583 📝 typo in PHPDoc 2019-10-30 16:41:58 +01:00
Pierre Goiffon
6793fb2a35 N°2293 update PHPDoc on callbacks 2019-10-30 16:26:30 +01:00
bruno DA SILVA
4834c326aa 2498 - restrict access to assets into env-*
- allow static html into extensions/ and datamodels/
 - allow direct access to php into env-* for legacy code taht do not use exec.php
2019-10-30 15:46:33 +01:00
Pierre Goiffon
e6167adefd N°2518 ACE Editor : SQL mode 2019-10-30 10:11:33 +01:00
Pierre Goiffon
a8c3756ba7 Rename licenses file
Fix typo in file name
2019-10-30 10:11:33 +01:00
Pierre Goiffon
34b47f0239 N°2518 update licenses to reflect ACE editor moved from itop-config to iTop core 2019-10-30 10:11:33 +01:00
bruno DA SILVA
0d13d9eabe 2498 - restrict access to assets into env-*
- allow static content within datamodels/ and extensions/ (reason: the setup make use of images within this directory)
 - simplified .htaccess and web.content generation during the compilation
 - it now also allow fonts
2019-10-30 09:44:28 +01:00
Pierre Goiffon
358adb2109 📝 Some PHPDoc complement 2019-10-28 16:19:30 +01:00
odain
92285b55b6 add few tests on OQL group by 2019-10-28 15:50:53 +01:00
Pierre Goiffon
dda91be6d1 N°2555 action_rules : an invalid rule won't prevent anymore others to execute
This blocking scenario was added in 5c483efd
2019-10-25 15:56:26 +02:00
Eric
bec4dbafd2 N°2315 - Login screen extensibility API refactor 2019-10-25 14:35:24 +02:00
Eric
dcb46990c2 N°1436 - Search retrieve users belonging to not allowed Org 2019-10-25 10:31:47 +02:00
Pierre Goiffon
fe03342b6f 🌐 Fix typo
SF#1812, many thanks Lars Scheibling !
2019-10-25 09:37:22 +02:00
Eric
ee037acd34 N°2315 - Login screen extensibility API refactor 2019-10-25 08:32:29 +02:00
Pierre Goiffon
d4a696cb6b N°2555 Specific exception for invalid action_rules 2019-10-24 18:05:58 +02:00
Pierre Goiffon
5c483efd15 N°2555 action_rule performance
* do not execute scope_oql that have no conditions
* pick the first result only
2019-10-24 17:24:09 +02:00
Pierre Goiffon
9aeba1df9b N°2555 New \DBSearch::GetFirstResult method 2019-10-24 17:24:09 +02:00
bruno DA SILVA
e98683ae1c fix regression introduced in the carbon branch 2019-10-24 16:19:07 +02:00
Eric
c552e73d20 N°2315 - Login screen extensibility API refactor 2019-10-24 14:56:01 +02:00
Stephen Abello
1851163cee N°1881 Portal: Show confirmation dialog when closing forms with unsaved data 2019-10-24 10:51:49 +02:00
Pierre Goiffon
3667f95b7c N°2558 center is back in sanitizer white list
Reverts 4450d6af (2.5.0)
Was causing troubles when integrating emails
2019-10-24 10:20:47 +02:00
Eric
b6796d2742 N°679 - Database inconsistency during INSERT
N°2499
2019-10-24 09:00:56 +02:00
Eric
047983cb91 🎨 Login screen extensibility 2019-10-23 17:27:04 +02:00
Eric
8455abdfe9 N°2315 - Markup extensibility: interface refactor 2019-10-23 16:49:00 +02:00
Eric
b7c3fbb176 💚 Refactor unit tests 2019-10-23 16:41:28 +02:00
Eric
5642552f9a N°1888 - Filter search with another search 2019-10-23 12:56:02 +02:00
Eric
6f6b654dba N°1113 - Global search only searchable attributes 2019-10-23 10:24:37 +02:00
Pierre Goiffon
c8b791efd3 Merge remote-tracking branch 'origin/itop-carbon' into develop
# Conflicts:
#	application/utils.inc.php
#	setup/ajax.dataloader.php
2019-10-22 18:27:13 +02:00
Pierre Goiffon
52fd58cd93 Remove "s" JQueryUI resizable handle override
Better to override locally O:)
2019-10-22 16:45:25 +02:00
Pierre Goiffon
66d638e224 💄 N°2518 New JQueryUI "s" handler style 2019-10-22 16:06:32 +02:00
Pierre Goiffon
16c4f18a81 N°2518 fix log call is crashing when invalid log_filename_builder_impl value set in config 2019-10-22 15:15:40 +02:00
Pierre Goiffon
cd6104ddb3 N°2518 If switching to log file rotation, rename setup/error legacy files 2019-10-22 15:08:48 +02:00
Pierre Goiffon
4fe7cd5adc 🎨 Config file code formating 2019-10-22 15:08:48 +02:00
Eric
3f59141407 N°1114 - config-itop.php access rights enforcement 2019-10-22 15:02:51 +02:00
Pierre Goiffon
03b8ed5ce4 Merge branch 'support/2.6.2' 2019-10-07 09:41:08 +02:00
Pierre Goiffon
a625733885 👷 Jenkins : fix jenkinsfile filename case 2019-09-10 14:23:36 +02:00
Eric
b0414748cb Typo 2019-06-13 11:55:10 +02:00
Molkobain
b6418d95e7 Add PHPDoc for type hinting as iTop replaces \DOMDocument with \MFDocument 2019-05-28 10:36:18 +02:00
Molkobain
2d6251e5df 💄 Add warning message CSS class (like error message) 2019-05-15 17:53:48 +02:00
Molkobain
91f410a85c 💄 Fix images being too large in icon selector (dashboards and Designer)
Note: The widget still needs a more aggressive refactoring to render nicely...
2019-05-02 16:42:21 +02:00
Eric
0a48696cd8 Carbon: N°1855 - Fix: Cannot remove last 'dependencies' with UI
Note that multi-select value when no entry is selected is "" and not []
in order to be posted, so multi-select values are not always arrays.
2019-04-30 15:46:44 +02:00
Eric
2f71570390 Carbon: N°1855 - fix "depends on" displaying fields from children 2019-04-30 12:02:13 +02:00
Molkobain
78b6c03af7 💡 Add some PHPDoc 2019-04-25 12:46:57 +02:00
Molkobain
22342cdc05 🐛 N°2184 Fix validation issue when several label fiels in a dashlet/designer form 2019-04-25 12:42:16 +02:00
Molkobain
dcf4963e0c N°2152 Fix bad XML generation when adding a dashboard attribute on a new class 2019-04-16 17:15:15 +02:00
Eric
9ec36a76f6 Carbon: N°1589 - Check migration 2019-04-10 12:12:31 +02:00
Eric
09b470e6c7 Better output 2019-04-09 10:43:39 +02:00
Molkobain
40151c7a43 N°2147 Fix non working impact relation when based on default value 2019-04-05 16:25:26 +02:00
Molkobain
34c030b501 N°2070 Extend ModelFactory implementations to optionally check meta classes (PHP) along with regular XML classes 2019-04-05 15:48:29 +02:00
Molkobain
c59d3cc624 Fix unreachable log message on exception 2019-04-01 16:52:22 +02:00
3322 changed files with 77265 additions and 156391 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,3 +1,5 @@
root = true
[*]
charset = utf-8
end_of_line = lf
@@ -11,7 +13,7 @@ ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_visual_guides = 140
ij_visual_guides = 80,120
ij_wrap_on_typing = true
[*.css]
@@ -20,7 +22,8 @@ ij_smart_tabs = true
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,52 +34,8 @@ 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
@@ -84,6 +43,7 @@ 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,6 +54,7 @@ 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]
@@ -101,6 +62,7 @@ indent_style = tab
ij_smart_tabs = true
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
@@ -112,6 +74,36 @@ 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_xml_align_attributes = true
ij_xml_align_text = false
ij_xml_attribute_wrap = normal
ij_xml_block_comment_at_first_column = true
ij_xml_keep_blank_lines = 2
ij_xml_keep_indents_on_empty_lines = false
ij_xml_keep_line_breaks = false
ij_xml_keep_line_breaks_in_text = true
ij_xml_keep_whitespaces = false
ij_xml_keep_whitespaces_around_cdata = preserve
ij_xml_keep_whitespaces_inside_cdata = true
ij_xml_line_comment_at_first_column = true
ij_xml_space_after_tag_name = false
ij_xml_space_around_equals_in_attribute = false
ij_xml_space_inside_empty_tag = false
ij_xml_text_wrap = normal
[{*.bash,*.sh,*.zsh}]
indent_size = 2
tab_width = 2
ij_shell_binary_ops_start_line = false
ij_shell_keep_column_alignment_padding = false
ij_shell_minify_program = false
ij_shell_redirect_followed_by_space = false
ij_shell_switch_cases_indented = false
[{*.cjs,*.js}]
indent_style = tab
ij_continuation_indent_size = 4
@@ -134,13 +126,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,7 +140,7 @@ 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_do_while_brace_force = always
ij_javascript_else_on_new_line = true
ij_javascript_enforce_trailing_comma = keep
ij_javascript_extends_keyword_wrap = off
@@ -156,7 +148,7 @@ 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 +184,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,11 +267,11 @@ 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
@@ -296,12 +291,13 @@ 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_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 +314,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 +325,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 +340,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 +361,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 +370,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 +381,10 @@ 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_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,8 +401,8 @@ 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
@@ -433,6 +435,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 +468,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,7 +484,20 @@ 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_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_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
@@ -503,209 +519,18 @@ 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
[{*.yml,*.yaml}]
[{*.yaml,*.yml}]
indent_size = 2
ij_continuation_indent_size = 2
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_space_before_colon = true
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,79 +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="SCSS">
<indentOptions>
<option name="USE_TAB_CHARACTER" 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,48 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Combodo" />
<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="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,13 +0,0 @@
#!/usr/bin/env bash
set -x
whoami
pwd
ls
echo "$BRANCH_NAME:${BRANCH_NAME}"
echo "printenv :"
printenv

View File

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

View File

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

View File

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

View File

@@ -1,209 +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.'/bootstrap.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;
}

6
.make/README.md Normal file
View File

@@ -0,0 +1,6 @@
= Make Doc =
.make folder is meant to gather tools for releasing process. Maybe other new purposes will come as well....
== license ==
- updateLicenses.php: used to update community-licenses.xml easily based on composer.json files
- sortLicenceXml.php: used to sort licenses based on scope + product name

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

@@ -0,0 +1,90 @@
#/bin/bash
#git diff --name-status 2.6.2..HEAD js |grep 'A\sjs/' |awk -F/ '{printf("lib/%s/%s\n",$2,$3)}'|sort |uniq >/tmp/toto
#git diff --name-status 2.6.2..HEAD lib |grep 'A\slib/' |awk -F/ '{printf("lib/%s/%s\n",$2,$3)}'|sort |uniq >/tmp/toto
function HELP(){
echo " Syntax: bash $0 /var/www/html/iTop"
}
if [ $# -eq 0 ]
then
echo "no iTop path provided"
HELP
exit 1
fi
iTopPath=$1
if [ ! -d $iTopPath ]
then
echo "$iTopPath is not an iTop path."
HELP
exit 1
fi
echo "<?xml version=\"1.0\"?>
<licenses>"
for subfolder in lib datamodels
do
for l in $(find $iTopPath/$subfolder/ -name composer.json|sed 's|/composer.json||')
do
if [ ! -d $l ]
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
lictype=$(cd $l && composer licenses --format json |jq .license[] |sed 's|\"||g')
authors=""
if [ -f $l/composer.json ]
then
author_nb=$(grep -c authors $l/composer.json|sed 's| ||g')
if [ "x$author_nb" != "x0" ]
then
OLDIFS=$IFS
IFS=$'\n'
for a in $(cat $l/composer.json |jq .authors[].name|sed 's|\"||g')
do
authors="$authors$a - "
done
authors="$authors#"
authors=$(echo $authors |sed 's| - #||')
IFS=$OLDIFS
fi
fi
lic=""
for licf in $(find $l -name LICEN*)
do
lic=$(cat $licf)
break
done
#if [ "x$lic" == "x" ]
#then
# echo "============== no license found $l"
#fi
echo " <license>
<product scope=\"$subfolder\">$prod</product>
<author>$authors</author>
<license_type>$lictype</license_type>
<text><![CDATA[
$lic
]]></text>
</license>"
done
done
echo "</licenses>"

View File

@@ -0,0 +1,64 @@
<?php
/**
* script used to sort license file (usefull for autogeneration)
* Example:
*/
$iTopFolder = __DIR__ . "/../../" ;
$xmlFilePath = $iTopFolder . "setup/licenses/community-licenses.xml";
$dom = new DOMDocument();
$dom->load($xmlFilePath);
$xp = new DOMXPath($dom);
$licenseList = $xp->query('/licenses/license');
$licenses = iterator_to_array($licenseList);
function get_scope($product_node)
{
$scope = $product_node->getAttribute("scope");
if ($scope === "")
{ //put iTop first
return "aaaaaaaaa";
}
return $scope;
}
function get_product_node($license_node)
{
foreach ($license_node->childNodes as $child)
{
if (is_a($child, 'DomElement') && $child->tagName === "product")
{
return $child;
}
}
return null;
}
function sort_by_product($a, $b)
{
$aProductNode = get_product_node($a);
$bProductNode = get_product_node($b);
$res = strcmp(get_scope($aProductNode), get_scope($bProductNode));
if ($res !== 0)
{
return $res;
}
//sort on node product name
return strcmp($aProductNode->nodeValue, $bProductNode->nodeValue);
}
usort($licenses, 'sort_by_product');
$newdom = new DOMDocument("1.0");
$newdom->formatOutput = true;
$root = $newdom->createElement("licenses");
$newdom->appendChild($root);
foreach ($licenses as $b) {
$node = $newdom->importNode($b,true);
$root->appendChild($newdom->importNode($b,true));
}
$newdom->save($xmlFilePath);

View File

@@ -0,0 +1,89 @@
<?php
/**
* script used to sort license file (usefull for autogeneration)
* Example: php
*/
$iTopFolder = __DIR__ . "/../../" ;
$xmlFilePath = $iTopFolder . "setup/licenses/community-licenses.xml";
function get_scope($product_node)
{
$scope = $product_node->getAttribute("scope");
if ($scope === "")
{ //put iTop first
return "aaaaaaaaa";
}
return $scope;
}
function get_product_node($license_node)
{
foreach ($license_node->childNodes as $child)
{
if (is_a($child, 'DomElement') && $child->tagName === "product")
{
return $child;
}
}
return null;
}
function sort_by_product($a, $b)
{
$aProductNode = get_product_node($a);
$bProductNode = get_product_node($b);
$res = strcmp(get_scope($aProductNode), get_scope($bProductNode));
if ($res !== 0)
{
return $res;
}
//sort on node product name
return strcmp($aProductNode->nodeValue, $bProductNode->nodeValue);
}
function get_license_nodes($file_path)
{
$dom = new DOMDocument();
$dom->load($file_path);
$xp = new DOMXPath($dom);
$licenseList = $xp->query('/licenses/license');
$licenses = iterator_to_array($licenseList);
usort($licenses, 'sort_by_product');
return $licenses;
}
$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);
$new_licenses = get_license_nodes($generated_license_file_path);
exec("rm -f ". $generated_license_file_path);
foreach ($old_licenses as $b) {
$aProductNode = get_product_node($b);
if (get_scope($aProductNode) !== "lib" && get_scope($aProductNode) !== "datamodels" )
{
$new_licenses[] = $b;
}
}
usort($new_licenses, 'sort_by_product');
$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));
}
$new_dom->save($xmlFilePath);

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:

70
Jenkinsfile vendored
View File

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

View File

@@ -51,17 +51,30 @@ iTop also offers mass import tools and web services to integrate with your IT
## Last releases
### Versions 2.7.*
- 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-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-2
### Versions 2.6.*
- 2.6.0 published on January 9, 2019
- [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.*
@@ -75,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
@@ -112,6 +111,7 @@ We would like to give a special thank you to the people from the community who c
- Casteleyn, Thomas
- Castro, Randall Badilla
- Colantoni, Maria Laura
- Couronné, Guy
- Dvořák, Lukáš
- Goethals, Stefan
- Gumble, David
@@ -121,6 +121,7 @@ We would like to give a special thank you to the people from the community who c
- Konečný, Kamil
- Kunin, Vladimir
- Lassiter, Dennis
- Lazcano, Federico
- Lucas, Jonathan
- Malik, Remie
- Rosenke, Stephan
@@ -143,4 +144,5 @@ We would like to give a special thank you to the people from the community who c
### Companies
- Hardis
- ITOMIG
- Pimkie

View File

@@ -121,20 +121,15 @@ class UserRightsMatrix extends UserRightsAddOnAPI
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
{
// Maybe we should check that no other user with userid == 0 exists
CMDBObject::SetTrackInfo('Initialization');
$oUser = new UserLocal();
$oUser->Set('login', $sAdminUser);
$oUser->Set('password', $sAdminPwd);
$oUser->Set('contactid', 1); // one is for root !
$oUser->Set('language', $sLanguage); // Language was chosen during the installation
// Create a change to record the history of the User object
$oChange = MetaModel::NewObject("CMDBChange");
$oChange->Set("date", time());
$oChange->Set("userinfo", "Initialization");
$iChangeId = $oChange->DBInsert();
// Now record the admin user object
$iUserId = $oUser->DBInsertTrackedNoReload($oChange, true /* skip security */);
$iUserId = $oUser->DBInsertNoReload();
$this->SetupUser($iUserId, true);
return true;
}

View File

@@ -1,27 +1,20 @@
<?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/>
/**
* UserRightsProfile
* User management Module, basing the right on profiles and a matrix (similar to UserRightsMatrix, but profiles and other decorations have been added)
* Copyright (C) 2013-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
*/
define('ADMIN_PROFILE_NAME', 'Administrator');
@@ -179,7 +172,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
parent::DisplayBareRelations($oPage, $bEditMode);
if (!$bEditMode)
{
$oPage->SetCurrentTab(Dict::S('UI:UserManagement:GrantMatrix'));
$oPage->SetCurrentTab('UI:UserManagement:GrantMatrix');
$this->DoShowGrantSumary($oPage);
}
}
@@ -437,33 +430,27 @@ class UserRightsProfile extends UserRightsAddOnAPI
{
CMDBObject::SetTrackInfo('Initialization');
$oChange = CMDBObject::GetCurrentChange();
$iContactId = 0;
// 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->DBInsertTrackedNoReload($oChange, true /* skip security */);
$iOrgId = $oOrg->DBInsertNoReload();
// 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->DBInsertTrackedNoReload($oChange, true /* skip security */);
$iContactId = $oContact->DBInsertNoReload();
}
}
@@ -482,14 +469,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
if (is_object($oAdminProfile))
{
$oUserProfile = new URP_UserProfile();
//$oUserProfile->Set('userid', $iUserId);
$oUserProfile->Set('profileid', $oAdminProfile->GetKey());
$oUserProfile->Set('reason', 'By definition, the administrator must have the administrator profile');
//$oUserProfile->DBInsertTrackedNoReload($oChange, true /* skip security */);
$oSet = DBObjectSet::FromObject($oUserProfile);
$oUser->Set('profile_list', $oSet);
}
$iUserId = $oUser->DBInsertTrackedNoReload($oChange, true /* skip security */);
$iUserId = $oUser->DBInsertNoReload();
return true;
}
@@ -572,7 +557,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
/**
* @param $oUser User
* @return array
* @return bool
*/
public function IsAdministrator($oUser)
{
@@ -582,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

@@ -1,27 +1,20 @@
<?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/>
/**
* UserRightsProfile
* User management Module, basing the right on profiles and a matrix (similar to UserRightsMatrix, but profiles and other decorations have been added)
* Copyright (C) 2013-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
*/
define('ADMIN_PROFILE_NAME', 'Administrator');
@@ -321,7 +314,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
parent::DisplayBareRelations($oPage, $bEditMode);
if (!$bEditMode)
{
$oPage->SetCurrentTab(Dict::S('UI:UserManagement:GrantMatrix'));
$oPage->SetCurrentTab('UI:UserManagement:GrantMatrix');
$this->DoShowGrantSumary($oPage);
}
}
@@ -533,36 +526,34 @@ class UserRightsProfile extends UserRightsAddOnAPI
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
{
// Create a change to record the history of the User object
/** @var \CMDBChange $oChange */
$oChange = MetaModel::NewObject("CMDBChange");
$oChange->Set("date", time());
$oChange->Set("userinfo", "Initialization");
$iChangeId = $oChange->DBInsert();
$iContactId = 0;
// 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->DBInsertTrackedNoReload($oChange, true /* skip security */);
$oOrg::SetCurrentChange($oChange);
$iOrgId = $oOrg->DBInsertNoReload();
// 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->DBInsertTrackedNoReload($oChange, true /* skip security */);
$oContact::SetCurrentChange($oChange);
$iContactId = $oContact->DBInsertNoReload();
}
}
@@ -581,14 +572,13 @@ class UserRightsProfile extends UserRightsAddOnAPI
if (is_object($oAdminProfile))
{
$oUserProfile = new URP_UserProfile();
//$oUserProfile->Set('userid', $iUserId);
$oUserProfile->Set('profileid', $oAdminProfile->GetKey());
$oUserProfile->Set('reason', 'By definition, the administrator must have the administrator profile');
//$oUserProfile->DBInsertTrackedNoReload($oChange, true /* skip security */);
$oSet = DBObjectSet::FromObject($oUserProfile);
$oUser->Set('profile_list', $oSet);
}
$iUserId = $oUser->DBInsertTrackedNoReload($oChange, true /* skip security */);
$oUser::SetCurrentChange($oChange);
$iUserId = $oUser->DBInsertNoReload();
return true;
}
@@ -717,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

@@ -1,27 +1,20 @@
<?php
// Copyright (C) 2010-2012 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/>
/**
* UserRightsProjection
* User management Module, basing the right on profiles and a matrix (similar to UserRightsProfile, but enhanced with dimensions and projection of classes and profile over the dimensions)
* Copyright (C) 2013-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
*/
define('ADMIN_PROFILE_ID', 1);
@@ -153,7 +146,7 @@ class URP_Profiles extends UserRightsBaseClass
parent::DisplayBareRelations($oPage, $bEditMode);
if (!$bEditMode)
{
$oPage->SetCurrentTab(Dict::S('UI:UserManagement:GrantMatrix'));
$oPage->SetCurrentTab('UI:UserManagement:GrantMatrix');
$this->DoShowGrantSumary($oPage);
}
}
@@ -593,25 +586,12 @@ class UserRightsProjection extends UserRightsAddOnAPI
$oChange = MetaModel::NewObject("CMDBChange");
$oChange->Set("date", time());
$oChange->Set("userinfo", "Initialization");
$iChangeId = $oChange->DBInsert();
$oOrg = new Organization();
$oOrg->Set('name', 'My Company/Department');
$oOrg->Set('code', 'SOMECODE');
// $oOrg->Set('status', 'implementation');
//$oOrg->Set('parent_id', xxx);
$iOrgId = $oOrg->DBInsertTrackedNoReload($oChange, true /* skip strong security */);
// Location : optional
//$oLocation = new bizLocation();
//$oLocation->Set('name', 'MyOffice');
//$oLocation->Set('status', 'implementation');
//$oLocation->Set('org_id', $iOrgId);
//$oLocation->Set('severity', 'high');
//$oLocation->Set('address', 'my building in my city');
//$oLocation->Set('country', 'my country');
//$oLocation->Set('parent_location_id', xxx);
//$iLocationId = $oLocation->DBInsertNoReload();
$oOrg::SetCurrentChange($oChange);
$iOrgId = $oOrg->DBInsertNoReload();
$oContact = new Person();
$oContact->Set('name', 'My last name');
@@ -619,24 +599,24 @@ class UserRightsProjection extends UserRightsAddOnAPI
//$oContact->Set('status', 'available');
$oContact->Set('org_id', $iOrgId);
$oContact->Set('email', 'my.email@foo.org');
//$oContact->Set('phone', '');
//$oContact->Set('location_id', $iLocationId);
//$oContact->Set('employee_number', '');
$iContactId = $oContact->DBInsertTrackedNoReload($oChange, true /* skip security */);
$oContact::SetCurrentChange($oChange);
$iContactId = $oContact->DBInsertNoReload();
$oUser = new UserLocal();
$oUser->Set('login', $sAdminUser);
$oUser->Set('password', $sAdminPwd);
$oUser->Set('contactid', $iContactId);
$oUser->Set('language', $sLanguage); // Language was chosen during the installation
$iUserId = $oUser->DBInsertTrackedNoReload($oChange, true /* skip security */);
$oUser::SetCurrentChange($oChange);
$iUserId = $oUser->DBInsertNoReload();
// Add this user to the very specific 'admin' profile
$oUserProfile = new URP_UserProfile();
$oUserProfile->Set('userid', $iUserId);
$oUserProfile->Set('profileid', ADMIN_PROFILE_ID);
$oUserProfile->Set('reason', 'By definition, the administrator must have the administrator profile');
$oUserProfile->DBInsertTrackedNoReload($oChange, true /* skip security */);
$oUserProfile::SetCurrentChange($oChange);
$oUserProfile->DBInsertNoReload();
return true;
}

View File

@@ -1,27 +1,20 @@
<?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/>
/**
* Simple web page with no includes, header or fancy formatting, useful to
* generate HTML fragments when called by an AJAX method
* Copyright (C) 2013-2020 Combodo SARL
*
* @copyright Copyright (C) 2010-2017 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
*/
require_once(APPROOT."/application/webpage.class.inc.php");
@@ -30,83 +23,97 @@ class ajax_page extends WebPage implements iTabbedPage
{
/**
* Jquery style ready script
* @var Hash
*/
* @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->add_header('Cache-control: no-cache, no-store, must-revalidate');
$this->add_header('Pragma: no-cache');
$this->add_header('Expires: 0');
$this->add_header('X-Frame-Options: deny');
$this->m_oTabs = new TabManager();
$this->sContentType = 'text/html';
$this->sContentDisposition = 'inline';
$this->m_sMenu = "";
utils::InitArchiveMode();
}
}
/**
* @inheritDoc
* @throws \Exception
*/
public function AddTabContainer($sTabContainer, $sPrefix = '')
{
$this->add($this->m_oTabs->AddTabContainer($sTabContainer, $sPrefix));
}
public function AddToTab($sTabContainer, $sTabLabel, $sHtml)
/**
* @inheritDoc
* @throws \Exception
*/
public function AddToTab($sTabContainer, $sTabCode, $sHtml)
{
$this->add($this->m_oTabs->AddToTab($sTabContainer, $sTabLabel, $sHtml));
$this->add($this->m_oTabs->AddToTab($sTabContainer, $sTabCode, $sHtml));
}
/**
* @inheritDoc
*/
public function SetCurrentTabContainer($sTabContainer = '')
{
return $this->m_oTabs->SetCurrentTabContainer($sTabContainer);
}
public function SetCurrentTab($sTabLabel = '')
{
return $this->m_oTabs->SetCurrentTab($sTabLabel);
}
/**
* Add a tab which content will be loaded asynchronously via the supplied URL
*
* Limitations:
* Cross site scripting is not not allowed for security reasons. Use a normal tab with an IFRAME if you want to pull content from another server.
* Static content cannot be added inside such tabs.
*
* @param string $sTabLabel The (localised) label of the tab
* @param string $sUrl The URL to load (on the same server)
* @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. flase will cause the tab to be reloaded upon each activation.
* @since 2.0.3
* @inheritDoc
*/
public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true)
public function SetCurrentTab($sTabCode = '', $sTabTitle = null)
{
$this->add($this->m_oTabs->AddAjaxTab($sTabLabel, $sUrl, $bCache));
return $this->m_oTabs->SetCurrentTab($sTabCode, $sTabTitle);
}
/**
* @inheritDoc
* @throws \Exception
*/
public function AddAjaxTab($sTabCode, $sUrl, $bCache = true, $sTabTitle = null)
{
$this->add($this->m_oTabs->AddAjaxTab($sTabCode, $sUrl, $bCache, $sTabTitle));
}
/**
* @inheritDoc
*/
public function GetCurrentTab()
{
return $this->m_oTabs->GetCurrentTab();
}
public function RemoveTab($sTabLabel, $sTabContainer = null)
/**
* @inheritDoc
*/
public function RemoveTab($sTabCode, $sTabContainer = null)
{
$this->m_oTabs->RemoveTab($sTabLabel, $sTabContainer);
$this->m_oTabs->RemoveTab($sTabCode, $sTabContainer);
}
/**
* Finds the tab whose title matches a given pattern
* @return mixed The name of the tab as a string or false if not found
* @inheritDoc
*/
public function FindTab($sPattern, $sTabContainer = null)
{
@@ -119,21 +126,23 @@ class ajax_page extends WebPage implements iTabbedPage
* that we are using this is not supported... TO DO upgrade
* the whole jquery bundle...
*/
public function SelectTab($sTabContainer, $sTabLabel)
public function SelectTab($sTabContainer, $sTabCode)
{
$this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabLabel));
$this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabCode));
}
/**
* @param string $sHtml
*/
public function AddToMenu($sHtml)
{
$this->m_sMenu .= $sHtml;
}
/**
* Echoes the content of the whole page
* @return void
*/
public function output()
/**
* @inheritDoc
*/
public function output()
{
if (!empty($this->sContentType))
{
@@ -216,8 +225,9 @@ EOF
}
$this->outputCollapsibleSectionInit();
$oKPI = new ExecutionKPI();
$s_captured_output = $this->ob_get_clean_safe();
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
{
// inline content != attachment && html => filter all scripts for malicious XSS scripts
echo self::FilterXSS($this->s_content);
@@ -287,6 +297,8 @@ EOF
echo self::FilterXSS($s_captured_output);
}
$oKPI->ComputeAndReport('Echoing');
if (class_exists('DBSearch'))
{
DBSearch::RecordQueryTrace();
@@ -307,7 +319,11 @@ EOF
{
}
public function add($sHtml)
/**
* @inheritDoc
* @throws \Exception
*/
public function add($sHtml)
{
if (($this->m_oTabs->GetCurrentTabContainer() != '') && ($this->m_oTabs->GetCurrentTab() != ''))
{
@@ -320,10 +336,9 @@ EOF
}
/**
* Records the current state of the 'html' part of the page output
* @return mixed The current state of the 'html' output
*/
public function start_capture()
* @inheritDoc
*/
public function start_capture()
{
$sCurrentTabContainer = $this->m_oTabs->GetCurrentTabContainer();
$sCurrentTab = $this->m_oTabs->GetCurrentTab();
@@ -339,13 +354,10 @@ EOF
}
}
/**
* Returns the part of the html output that occurred since the call to start_capture
* and removes this part from the current html output
* @param $offset mixed The value returned by start_capture
* @return string The part of the html output that was added since the call to start_capture
*/
public function end_capture($offset)
/**
* @inheritDoc
*/
public function end_capture($offset)
{
if (is_array($offset))
{
@@ -366,11 +378,9 @@ EOF
}
/**
* 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
* be embedded into each other.
* @inheritDoc
*/
public function add_at_the_end($s_html, $sId = '')
public function add_at_the_end($s_html, $sId = '')
{
if ($sId != '')
{
@@ -378,27 +388,27 @@ EOF
}
$this->s_deferred_content .= $s_html;
}
/**
* Adds a script to be executed when the DOM is ready (typical JQuery use)
* NOT implemented in this version of the class.
* @return void
*/
* @inheritDoc
*/
public function add_ready_script($sScript)
{
$this->m_sReadyScript .= $sScript."\n";
}
/**
* Cannot be called in this context, since Ajax pages do not share
* any context with the calling page !!
* @inheritDoc
*/
public function GetUniqueId()
{
assert(false);
return 0;
}
/**
* @inheritDoc
*/
public static function FilterXSS($sHTML)
{
return str_ireplace(array('<script', '</script>'), array('<!-- <removed-script', '</removed-script> -->'), $sHTML);

View File

@@ -1,20 +1,24 @@
<?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;
require_once(APPROOT.'application/newsroomprovider.class.inc.php');
@@ -28,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
{
@@ -39,6 +44,9 @@ interface iLoginExtension
public function ListSupportedLoginModes();
}
/**
* @since 2.7.0
*/
interface iLoginFSMExtension extends iLoginExtension
{
/**
@@ -56,8 +64,14 @@ interface iLoginFSMExtension extends iLoginExtension
public function LoginAction($sLoginState, &$iErrorCode);
}
/**
* @since 2.7.0
*/
abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
{
/**
* @inheritDoc
*/
public abstract function ListSupportedLoginModes();
/**
@@ -149,27 +163,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
{
/**
@@ -178,15 +215,22 @@ interface iLogoutExtension extends iLoginExtension
public function LogoutAction();
}
interface iLoginDataExtension extends iLoginExtension
/**
* @since 2.7.0
*/
interface iLoginUIExtension extends iLoginExtension
{
/**
* @return LoginTwigData
* @return LoginTwigContext
*/
public function GetLoginData();
public function GetTwigContext();
}
/**
* @api
* @package Extensibility
* @since 2.7.0
*/
interface iPreferencesExtension
{
/**
@@ -204,6 +248,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.
*
@@ -364,6 +435,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
*
@@ -391,7 +533,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.
@@ -419,7 +561,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 \cmdbAbstractObject::$m_aChanges}
* 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
@@ -427,7 +572,7 @@ interface iApplicationObjectExtension
*
* @return void
*
* @since 2.7.0 N°2293 can access object changes by calling {@link \cmdbAbstractObject::$m_aChanges}
* @since 2.7.0 N°2293 can access object changes by calling {@see DBObject::ListPreviousValuesForUpdatedAttributes()} on $oObject
*/
public function OnDBUpdate($oObject, $oChange = null);
@@ -458,6 +603,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.
@@ -584,7 +785,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)
{
@@ -846,6 +1046,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
*
@@ -853,7 +1088,7 @@ interface iPageUIExtension
*
* @api
* @package Extensibility
* @since 2.4
* @since 2.4.0
*/
interface iPortalUIExtension
{
@@ -864,65 +1099,65 @@ interface iPortalUIExtension
/**
* Returns an array of CSS file urls
*
* @param \Silex\Application $oApp
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return array
*/
public function GetCSSFiles(\Silex\Application $oApp);
public function GetCSSFiles(Container $oContainer);
/**
* Returns inline (raw) CSS
*
* @param \Silex\Application $oApp
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
*/
public function GetCSSInline(\Silex\Application $oApp);
public function GetCSSInline(Container $oContainer);
/**
* Returns an array of JS file urls
*
* @param \Silex\Application $oApp
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return array
*/
public function GetJSFiles(\Silex\Application $oApp);
public function GetJSFiles(Container $oContainer);
/**
* Returns raw JS code
*
* @param \Silex\Application $oApp
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
*/
public function GetJSInline(\Silex\Application $oApp);
public function GetJSInline(Container $oContainer);
/**
* Returns raw HTML code to put at the end of the <body> tag
*
* @param \Silex\Application $oApp
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
*/
public function GetBodyHTML(\Silex\Application $oApp);
public function GetBodyHTML(Container $oContainer);
/**
* Returns raw HTML code to put at the end of the #main-wrapper element
*
* @param \Silex\Application $oApp
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
*/
public function GetMainContentHTML(\Silex\Application $oApp);
public function GetMainContentHTML(Container $oContainer);
/**
* Returns raw HTML code to put at the end of the #topbar and #sidebar elements
*
* @param \Silex\Application $oApp
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
*/
public function GetNavigationMenuHTML(\Silex\Application $oApp);
public function GetNavigationMenuHTML(Container $oContainer);
}
/**
@@ -933,7 +1168,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
/**
* @inheritDoc
*/
public function GetCSSFiles(\Silex\Application $oApp)
public function GetCSSFiles(Container $oContainer)
{
return array();
}
@@ -941,7 +1176,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
/**
* @inheritDoc
*/
public function GetCSSInline(\Silex\Application $oApp)
public function GetCSSInline(Container $oContainer)
{
return null;
}
@@ -949,7 +1184,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
/**
* @inheritDoc
*/
public function GetJSFiles(\Silex\Application $oApp)
public function GetJSFiles(Container $oContainer)
{
return array();
}
@@ -957,7 +1192,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
/**
* @inheritDoc
*/
public function GetJSInline(\Silex\Application $oApp)
public function GetJSInline(Container $oContainer)
{
return null;
}
@@ -965,7 +1200,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
/**
* @inheritDoc
*/
public function GetBodyHTML(\Silex\Application $oApp)
public function GetBodyHTML(Container $oContainer)
{
return null;
}
@@ -973,7 +1208,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
/**
* @inheritDoc
*/
public function GetMainContentHTML(\Silex\Application $oApp)
public function GetMainContentHTML(Container $oContainer)
{
return null;
}
@@ -981,7 +1216,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
/**
* @inheritDoc
*/
public function GetNavigationMenuHTML(\Silex\Application $oApp)
public function GetNavigationMenuHTML(Container $oContainer)
{
return null;
}
@@ -1077,11 +1312,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()
{
@@ -1528,3 +1758,16 @@ class RestUtils
return $oObject;
}
}
/**
* Helpers for modules extensibility, with discover performed by the MetaModel.
*
*
* @api
* @package Extensibility
*/
interface iModuleExtension
{
public function __construct();
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,20 +1,21 @@
<?php
// Copyright (C) 2010-2017 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-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
*/
require_once(APPROOT.'application/dashboardlayout.class.inc.php');
require_once(APPROOT.'application/dashlet.class.inc.php');
@@ -24,21 +25,33 @@ require_once(APPROOT.'core/modelreflection.class.inc.php');
*
* A user editable dashboard page
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class Dashboard
{
/** @var string $sTitle*/
protected $sTitle;
/** @var bool $bAutoReload */
protected $bAutoReload;
/** @var float|int $iAutoReloadSec */
protected $iAutoReloadSec;
/** @var string $sLayoutClass */
protected $sLayoutClass;
/** @var array $aWidgetsData */
protected $aWidgetsData;
/** @var \DOMNode|null $oDOMNode */
protected $oDOMNode;
/** @var string $sId */
protected $sId;
/** @var array $aCells */
protected $aCells;
/** @var \ModelReflection $oMetaModel */
protected $oMetaModel;
/**
* Dashboard constructor.
*
* @param string $sId
*/
public function __construct($sId)
{
$this->sTitle = '';
@@ -51,7 +64,7 @@ abstract class Dashboard
}
/**
* @param $sXml
* @param string $sXml
*
* @throws \Exception
*/
@@ -64,7 +77,10 @@ abstract class Dashboard
restore_error_handler();
$this->FromDOMDocument($oDoc);
}
/**
* @param \DOMDocument $oDoc
*/
public function FromDOMDocument(DOMDocument $oDoc)
{
$this->oDOMNode = $oDoc->getElementsByTagName('dashboard')->item(0);
@@ -160,9 +176,10 @@ abstract class Dashboard
protected function InitDashletFromDOMNode($oDomNode)
{
$sId = $oDomNode->getAttribute('id');
$sDashletType = $oDomNode->getAttribute('xsi:type');
// Test if dashlet can be instanciated, otherwise (uninstalled, broken, ...) we display a placeholder
// Test if dashlet can be instantiated, otherwise (uninstalled, broken, ...) we display a placeholder
$sClass = static::GetDashletClassFromType($sDashletType);
/** @var \Dashlet $oNewDashlet */
$oNewDashlet = new $sClass($this->oMetaModel, $sId);
@@ -172,7 +189,13 @@ abstract class Dashboard
return $oNewDashlet;
}
static function SortOnRank($aItem1, $aItem2)
/**
* @param array $aItem1
* @param array $aItem2
*
* @return int
*/
public static function SortOnRank($aItem1, $aItem2)
{
return ($aItem1['rank'] > $aItem2['rank']) ? +1 : -1;
}
@@ -200,6 +223,10 @@ abstract class Dashboard
}
}
/**
* @return string
* @throws \Exception
*/
public function ToXml()
{
$oDoc = new DOMDocument();
@@ -268,7 +295,9 @@ abstract class Dashboard
}
}
/**
* @param array $aParams
*/
public function FromParams($aParams)
{
$this->sLayoutClass = $aParams['layout_class'];
@@ -305,42 +334,85 @@ 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
*/
public function GetLayout()
{
return $this->sLayoutClass;
}
/**
* @param string $sLayoutClass
*/
public function SetLayout($sLayoutClass)
{
$this->sLayoutClass = $sLayoutClass;
}
/**
* @return string
*/
public function GetTitle()
{
return $this->sTitle;
}
/**
* @param string $sTitle
*/
public function SetTitle($sTitle)
{
$this->sTitle = $sTitle;
}
/**
* @return bool
*/
public function GetAutoReload()
{
return $this->bAutoReload;
}
/**
* @param bool $bAutoReload
*/
public function SetAutoReload($bAutoReload)
{
$this->bAutoReload = $bAutoReload;
}
/**
* @return float|int
*/
public function GetAutoReloadInterval()
{
return $this->iAutoReloadSec;
}
/**
* @param bool $iAutoReloadSec
*/
public function SetAutoReloadInterval($iAutoReloadSec)
{
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$iAutoReloadSec);
@@ -356,12 +428,13 @@ abstract class Dashboard
$this->aCells[] = array($oDashlet);
}
/**
* @param \WebPage $oPage *
* @param array $aExtraParams
*
* @throws \ReflectionException
*/
/**
* @param \WebPage $oPage *
* @param array $aExtraParams
*
* @throws \ReflectionException
* @throws \Exception
*/
public function RenderProperties($oPage, $aExtraParams = array())
{
// menu to pick a layout and edit other properties of the dashboard
@@ -449,17 +522,32 @@ EOF
}
/**
* @param \iTopWebPage $oPage
* @param \WebPage $oPage
* @param bool $bEditMode
* @param array $aExtraParams
* @param bool $bCanEdit
*/
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)
{
@@ -468,6 +556,12 @@ EOF
}
}
/**
* @param \WebPage $oPage
*
* @throws \ReflectionException
* @throws \Exception
*/
public function RenderDashletsSelection(WebPage $oPage)
{
// Toolbox/palette to drag and drop dashlets
@@ -485,25 +579,31 @@ EOF
$oPage->add('</div>');
$oPage->add_ready_script("$('.dashlet_icon').draggable({helper: 'clone', appendTo: 'body', zIndex: 10000, revert:'invalid'});");
}
/**
* @param \WebPage $oPage
* @param array $aExtraParams
*/
public function RenderDashletsProperties(WebPage $oPage, $aExtraParams = array())
{
// 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>');
}
}
@@ -545,7 +645,10 @@ EOF
return $aDashlets;
}
/**
* @return int|mixed
*/
protected function GetNewDashletId()
{
$iNewId = 0;
@@ -560,14 +663,32 @@ 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 $oForm
* @param \DesignerForm $oForm
* @param array $aExtraParams
*
* @return mixed
*/
abstract protected function SetFormParams($oForm, $aExtraParams = array());
/**
* @param string $sType
* @param \ModelFactory|null $oFactory
*
* @return string
*/
public static function GetDashletClassFromType($sType, $oFactory = null)
{
if (is_subclass_of($sType, 'Dashlet'))
@@ -578,45 +699,90 @@ 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;
}
}
/**
* Class RuntimeDashboard
*/
class RuntimeDashboard extends Dashboard
{
protected $bCustomized;
/** @var string $sDefinitionFile */
private $sDefinitionFile = '';
/** @var null $sReloadURL */
private $sReloadURL = null;
/** @var bool $bCustomized */
protected $bCustomized;
/**
* @inheritDoc
*/
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)
{
$this->bCustomized = $bCustomized;
}
/**
* @param \DesignerForm $oForm
*
* @param array $aExtraParams
*
* @inheritDoc
* @throws \Exception
*/
protected function SetFormParams($oForm, $aExtraParams = array())
{
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property', 'extra_params' => $aExtraParams));
}
/**
* @inheritDoc
* @throws \Exception
*/
public function Save()
{
$sXml = $this->ToXml();
@@ -632,7 +798,7 @@ class RuntimeDashboard extends Dashboard
}
else
{
// No such customized dasboard for the current user, let's create a new record
// No such customized dashboard for the current user, let's create a new record
$oUserDashboard = new UserDashboard();
$oUserDashboard->Set('user_id', UserRights::GetUserId());
$oUserDashboard->Set('menu_code', $this->sId);
@@ -642,7 +808,18 @@ class RuntimeDashboard extends Dashboard
$oUserDashboard->DBWrite();
utils::PopArchiveMode();
}
/**
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DeleteException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
public function Revert()
{
$oUDSearch = new DBObjectSearch('UserDashboard');
@@ -669,6 +846,7 @@ class RuntimeDashboard extends Dashboard
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \Exception
*/
public static function GetDashboard($sDashboardFile, $sDashBoardId)
{
@@ -713,10 +891,7 @@ class RuntimeDashboard extends Dashboard
}
/**
* @param \iTopWebPage $oPage
* @param bool $bEditMode
* @param array $aExtraParams (class and id of the current object
*
* @inheritDoc
* @throws \Exception
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
@@ -746,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());
@@ -809,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">';
@@ -851,6 +1026,9 @@ EOF
);
}
/**
* @return bool
*/
protected function HasCustomDashboard()
{
try
@@ -879,7 +1057,7 @@ EOF
{
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.iframe-transport.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js');
$sEditMenu = "<div id=\"DashboardMenu\"><ul><li><img src=\"../images/pencil-menu.png\"><ul>";
$sEditMenu = "<div id=\"DashboardMenu\"><ul><li><i class=\"top-right-icon icon-additional-arrow fas fa-pencil-alt\"></i><ul>";
$aActions = array();
$sFile = addslashes($this->sDefinitionFile);
@@ -939,9 +1117,7 @@ EOF
}
/**
* @param \WebPage $oPage
*
* @throws \ReflectionException
* @inheritDoc
*/
public function RenderProperties($oPage, $aExtraParams = array())
{
@@ -977,7 +1153,7 @@ EOF
/**
* @param \iTopWebPage $oPage
* @param \WebPage $oPage
*
* @param array $aExtraParams
*
@@ -997,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">');
@@ -1028,7 +1206,7 @@ EOF
$sAutoApplyConfirmationMessage = addslashes(Dict::S('UI:AutoApplyConfirmationMessage'));
$oPage->add_ready_script(
<<<EOF
<<<JS
window.bLeavingOnUserAction = false;
$('#dashboard_editor').dialog({
@@ -1071,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'}
});
@@ -1113,11 +1296,18 @@ window.onbeforeunload = function() {
}
// return nothing ! safer for IE
};
EOF
JS
);
$oPage->add_ready_script("");
}
/**
* @param string|null $sOQL
*
* @return \DesignerForm
* @throws \DictExceptionMissingString
* @throws \ReflectionException
*/
public static function GetDashletCreationForm($sOQL = null)
{
$oAppContext = new ApplicationContext();
@@ -1228,6 +1418,9 @@ EOF
/**
* @param \WebPage $oPage
* @param $sOQL
*
* @throws \DictExceptionMissingString
* @throws \ReflectionException
*/
public static function GetDashletCreationDlgFromOQL($oPage, $sOQL)
{
@@ -1243,7 +1436,7 @@ EOF
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$oPage->add_ready_script(
<<<EOF
<<<JS
$('#dashlet_creation_dlg').dialog({
width: 600,
modal: true,
@@ -1272,7 +1465,7 @@ $('#dashlet_creation_dlg').dialog({
],
close: function() { $(this).remove(); }
});
EOF
JS
);
}
@@ -1292,13 +1485,105 @@ EOF
$this->sDefinitionFile = $sDefinitionFile;
}
/**
* @return string|null
*/
public function GetReloadURL()
{
return $this->sReloadURL;
}
/**
* @param string $sReloadURL
*/
public function SetReloadURL($sReloadURL)
{
$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;
@@ -514,7 +517,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 +616,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 +636,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 +777,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 +863,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 +916,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 +951,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 +965,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 +1121,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 +1258,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 +1272,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 +1695,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 +1767,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 +1868,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 +1916,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 +2046,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 +2081,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 +2110,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 +2120,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 +2156,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,5 +15,10 @@
<menu id="AdminTools" xsi:type="MenuGroup" _delta="define">
<rank>80</rank>
</menu>
<menu id="SystemTools" xsi:type="MenuGroup" _delta="define">
<rank>100</rank>
<enable_class>ResourceSystemMenu</enable_class>
<enable_action>UR_ACTION_MODIFY</enable_action>
</menu>
</menus>
</itop_design>

View File

@@ -1,30 +1,27 @@
<?php
// Copyright (C) 2010-2017 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/>
/**
* Data Table to display a set of objects in a tabular manner in HTML
* Copyright (C) 2013-2020 Combodo SARL
*
* @copyright Copyright (C) 2010-2017 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
*/
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
@@ -34,14 +31,20 @@ 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 Hash 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
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
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;
@@ -50,7 +53,19 @@ class DataTable
$this->oDefaultSettings = null;
$this->bShowObsoleteData = $oSet->GetShowObsoleteData();
}
/**
* @param \WebPage $oPage
* @param \DataTableSettings $oSettings
* @param $bActionsMenu
* @param $sSelectMode
* @param $bViewLink
* @param $aExtraParams
*
* @return string
* @throws \CoreException
* @throws \MySQLException
*/
public function Display(WebPage $oPage, DataTableSettings $oSettings, $bActionsMenu, $sSelectMode, $bViewLink, $aExtraParams)
{
$this->oDefaultSettings = $oSettings;
@@ -119,7 +134,23 @@ class DataTable
return $this->GetAsHTML($oPage, $oCustomSettings->iDefaultPageSize, $oCustomSettings->iDefaultPageSize, 0, $oCustomSettings->aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams);
}
/**
* @param \WebPage $oPage
* @param $iPageSize
* @param $iDefaultPageSize
* @param $iPageIndex
* @param $aColumns
* @param $bActionsMenu
* @param $bToolkitMenu
* @param $sSelectMode
* @param $bViewLink
* @param $aExtraParams
*
* @return string
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function GetAsHTML(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex, $aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams)
{
$sObjectsCount = $this->GetObjectCount($oPage, $sSelectMode);
@@ -137,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>";
@@ -173,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;
}
@@ -198,7 +229,13 @@ class DataTable
}
return $sHtml;
}
/**
* @param \WebPage $oPage
* @param $sSelectMode
*
* @return string
*/
protected function GetObjectCount(WebPage $oPage, $sSelectMode)
{
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
@@ -211,6 +248,15 @@ class DataTable
}
return $sHtml;
}
/**
* @param \WebPage $oPage
* @param $iPageSize
* @param $iDefaultPageSize
* @param $iPageIndex
*
* @return string
*/
protected function GetPager(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex)
{
$sHtml = '';
@@ -295,7 +341,17 @@ class DataTable
EOF;
return $sHtml;
}
/**
* @param \WebPage $oPage
* @param $aExtraParams
*
* @return string
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \MissingQueryArgument
* @throws \MySQLException
*/
protected function GetActionsMenu(WebPage $oPage, $aExtraParams)
{
$oMenuBlock = new MenuBlock($this->oSet->GetFilter(), 'list');
@@ -303,13 +359,20 @@ EOF;
$sHtml = $oMenuBlock->GetRenderContent($oPage, $aExtraParams, $this->iListId);
return $sHtml;
}
/**
* @param \WebPage $oPage
* @param $aExtraParams
*
* @return string
* @throws \Exception
*/
protected function GetToolkitMenu(WebPage $oPage, $aExtraParams)
{
if (!$oPage->IsPrintableVersion())
{
$sMenuTitle = Dict::S('UI:ConfigureThisList');
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><img src="../images/toolkit_menu.png?t='.utils::GetCacheBusterTimestamp().'"><ul>';
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><i class="fas fa-tools"></i><i class="fas fa-caret-down"></i><ul>';
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
$aActions = array(
@@ -326,7 +389,15 @@ EOF;
}
return $sHtml;
}
/**
* @param \WebPage $oPage
* @param $aColumns
* @param $bViewLink
* @param $iDefaultPageSize
*
* @return string
*/
protected function GetTableConfigDlg(WebPage $oPage, $aColumns, $bViewLink, $iDefaultPageSize)
{
$sHtml = "<div id=\"datatable_dlg_{$this->iListId}\" style=\"display: none;\">";
@@ -350,35 +421,54 @@ 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;
}
/**
* @param $oSetting
*
* @return array
*/
public function GetAsHash($oSetting)
{
$aSettings = array('iDefaultPageSize' => $oSetting->iDefaultPageSize, 'oColumns' => $oSetting->aColumns);
return $aSettings;
}
/**
* @param array $aColumns
* @param string $sSelectMode
* @param bool $bViewLink
*
* @return array
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \Exception
*/
protected function GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink)
{
$aAttribs = array();
if ($sSelectMode == 'multiple')
{
$aAttribs['form::select'] = array('label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->iListId}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>", 'description' => Dict::S('UI:SelectAllToggle+'));
$aAttribs['form::select'] = array(
'label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->iListId}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>",
'description' => Dict::S('UI:SelectAllToggle+'),
'metadata' => array(),
);
}
else if ($sSelectMode == 'single')
{
$aAttribs['form::select'] = array('label' => "", 'description' => '');
$aAttribs['form::select'] = array('label' => '', 'description' => '', 'metadata' => array());
}
foreach($this->aClassAliases as $sAlias => $sClassName)
@@ -389,19 +479,55 @@ EOF;
{
if ($sAttCode == '_key_')
{
$aAttribs['key_'.$sAlias] = array('label' => MetaModel::GetName($sClassName), 'description' => '');
$sAttLabel = MetaModel::GetName($sClassName);
$aAttribs['key_'.$sAlias] = array(
'label' => $sAttLabel,
'description' => '',
'metadata' => array(
'object_class' => $sClassName,
'attribute_label' => $sAttLabel,
),
);
}
else
{
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode);
$aAttribs[$sAttCode.'_'.$sAlias] = array('label' => MetaModel::GetLabel($sClassName, $sAttCode), 'description' => $oAttDef->GetOrderByHint());
$sAttDefClass = get_class($oAttDef);
$sAttLabel = MetaModel::GetLabel($sClassName, $sAttCode);
$aAttribs[$sAttCode.'_'.$sAlias] = array(
'label' => $sAttLabel,
'description' => $oAttDef->GetOrderByHint(),
'metadata' => array(
'object_class' => $sClassName,
'attribute_code' => $sAttCode,
'attribute_type' => $sAttDefClass,
'attribute_label' => $sAttLabel,
),
);
}
}
}
}
return $aAttribs;
}
/**
* @param $aColumns
* @param $sSelectMode
* @param $iPageSize
* @param $bViewLink
* @param $aExtraParams
*
* @return array
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \Exception
*/
protected function GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
{
$bLocalize = true;
@@ -411,6 +537,7 @@ EOF;
}
$aValues = array();
$aAttDefsCache = array();
$this->oSet->Seek(0);
$iMaxObjects = $iPageSize;
while (($aObjects = $this->oSet->FetchAssoc()) && ($iMaxObjects != 0))
@@ -451,11 +578,41 @@ EOF;
{
if ($sAttCode == '_key_')
{
$aRow['key_'.$sAlias] = $aObjects[$sAlias]->GetHyperLink();
$aRow['key_'.$sAlias] = array(
'value_raw' => $aObjects[$sAlias]->GetKey(),
'value_html' => $aObjects[$sAlias]->GetHyperLink(),
);
}
else
{
$aRow[$sAttCode.'_'.$sAlias] = $aObjects[$sAlias]->GetAsHTML($sAttCode, $bLocalize);
// Prepare att. def. classes cache to avoid retrieving AttDef for each row
if(!isset($aAttDefsCache[$sClassName][$sAttCode]))
{
$aAttDefClassesCache[$sClassName][$sAttCode] = get_class(MetaModel::GetAttributeDef($sClassName, $sAttCode));
}
// Only retrieve raw (stored) value for simple fields
$bExcludeRawValue = false;
foreach (cmdbAbstractObject::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as $sAttDefClassToExclude)
{
if (is_a($aAttDefClassesCache[$sClassName][$sAttCode], $sAttDefClassToExclude, true))
{
$bExcludeRawValue = true;
break;
}
}
if($bExcludeRawValue)
{
$aRow[$sAttCode.'_'.$sAlias] = $aObjects[$sAlias]->GetAsHTML($sAttCode, $bLocalize);
}
else
{
$aRow[$sAttCode.'_'.$sAlias] = array(
'value_raw' => $aObjects[$sAlias]->Get($sAttCode),
'value_html' => $aObjects[$sAlias]->GetAsHTML($sAttCode, $bLocalize),
);
}
}
}
}
@@ -484,7 +641,25 @@ EOF;
}
return $aValues;
}
/**
* @param \WebPage $oPage
* @param $aColumns
* @param $sSelectMode
* @param $iPageSize
* @param $bViewLink
* @param $aExtraParams
*
* @return string
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DictExceptionMissingString
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \Exception
*/
public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
{
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
@@ -496,7 +671,7 @@ EOF;
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
$sHtml = '<table class="listContainer">';
$sHtml = '<table class="listContainer object-list">';
foreach($this->oSet->GetFilter()->GetInternalParams() as $sName => $sValue)
{
@@ -573,19 +748,37 @@ 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]);");
}
return $sHtml;
}
/**
* @param \WebPage $oPage
* @param $iDefaultPageSize
* @param $iStart
*/
public function UpdatePager(WebPage $oPage, $iDefaultPageSize, $iStart)
{
$iPageSize = $iDefaultPageSize;
@@ -609,11 +802,48 @@ EOF
*/
class PrintableDataTable extends DataTable
{
/**
* @param \WebPage $oPage
* @param $iPageSize
* @param $iDefaultPageSize
* @param $iPageIndex
* @param $aColumns
* @param $bActionsMenu
* @param $bToolkitMenu
* @param $sSelectMode
* @param $bViewLink
* @param $aExtraParams
*
* @return string
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DictExceptionMissingString
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public function GetAsHTML(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex, $aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams)
{
return $this->GetHTMLTable($oPage, $aColumns, $sSelectMode, -1, $bViewLink, $aExtraParams);
}
/**
* @param \WebPage $oPage
* @param $aColumns
* @param $sSelectMode
* @param $iPageSize
* @param $bViewLink
* @param $aExtraParams
*
* @return string
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DictExceptionMissingString
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
{
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
@@ -638,7 +868,13 @@ class DataTableSettings implements Serializable
public $iDefaultPageSize;
public $aColumns;
/**
* DataTableSettings constructor.
*
* @param $aClassAliases
* @param null $sTableId
*/
public function __construct($aClassAliases, $sTableId = null)
{
$this->aClassAliases = $aClassAliases;
@@ -646,14 +882,22 @@ class DataTableSettings implements Serializable
$this->iDefaultPageSize = 10;
$this->aColumns = array();
}
/**
* @param $iDefaultPageSize
* @param $aSortOrder
* @param $aColumns
*/
protected function Init($iDefaultPageSize, $aSortOrder, $aColumns)
{
$this->iDefaultPageSize = $iDefaultPageSize;
$this->aColumns = $aColumns;
$this->FixVisibleColumns();
}
/**
* @return string
*/
public function serialize()
{
// Save only the 'visible' columns
@@ -679,7 +923,12 @@ class DataTableSettings implements Serializable
)
);
}
/**
* @param string $sData
*
* @throws \Exception
*/
public function unserialize($sData)
{
$aData = unserialize($sData);
@@ -712,7 +961,16 @@ class DataTableSettings implements Serializable
}
$this->FixVisibleColumns();
}
/**
* @param $aClassAliases
* @param $bViewLink
* @param $aDefaultLists
*
* @return \DataTableSettings
* @throws \CoreException
* @throws \DictExceptionMissingString
*/
static public function GetDataModelSettings($aClassAliases, $bViewLink, $aDefaultLists)
{
$oSettings = new DataTableSettings($aClassAliases);
@@ -762,7 +1020,10 @@ class DataTableSettings implements Serializable
$oSettings->Init($iDefaultPageSize, $aSortOrder, $aColumns);
return $oSettings;
}
/**
* @throws \CoreException
*/
protected function FixVisibleColumns()
{
foreach($this->aClassAliases as $sAlias => $sClass)
@@ -799,7 +1060,15 @@ class DataTableSettings implements Serializable
}
}
}
/**
* @param $aClassAliases
* @param null $sTableId
* @param bool $bOnlyOnTable
*
* @return \DataTableSettings|null
* @throws \Exception
*/
static public function GetTableSettings($aClassAliases, $sTableId = null, $bOnlyOnTable = false)
{
$pref = null;
@@ -828,7 +1097,10 @@ class DataTableSettings implements Serializable
return $oSettings;
}
/**
* @return array
*/
public function GetSortOrder()
{
$aSortOrder = array();
@@ -846,7 +1118,12 @@ class DataTableSettings implements Serializable
}
return $aSortOrder;
}
/**
* @param null $sTargetTableId
*
* @return bool
*/
public function Save($sTargetTableId = null)
{
$sSaveId = is_null($sTargetTableId) ? $this->sTableId : $sTargetTableId;
@@ -857,6 +1134,9 @@ class DataTableSettings implements Serializable
return true;
}
/**
* @return bool
*/
public function SaveAsDefault()
{
$sSettings = $this->serialize();
@@ -886,18 +1166,43 @@ class DataTableSettings implements Serializable
}
return true;
}
/**
* @param null $sTableId
*
* @return string
*/
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;
}
return implode('/', $aKeys).'|'.$sTableId;
}
/**
* @param $sAlias
* @param $sAttCode
* @param $oAttDef
* @param $bChecked
* @param $sSort
*
* @return array|bool
* @throws \CoreException
* @throws \DictExceptionMissingString
*/
protected function GetFieldData($sAlias, $sAttCode, $oAttDef, $bChecked, $sSort)
{
$ret = false;

View File

@@ -1,26 +1,20 @@
<?php
// Copyright (C) 2010-2017 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/>
/**
* DisplayBlock and derived class
* Copyright (C) 2013-2019 Combodo SARL
*
* @copyright Copyright (C) 2010-2017 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
*/
require_once(APPROOT.'/application/webpage.class.inc.php');
@@ -91,7 +85,7 @@ class DisplayBlock
{
$oDummyFilter = new DBObjectSearch($oSet->GetClass());
$aKeys = array();
$oSet->OptimizeColumnLoad(array('id')); // No need to load all the columns just to get the id
$oSet->OptimizeColumnLoad(array($oSet->GetClassAlias() => array())); // No need to load all the columns just to get the id
while($oObject = $oSet->Fetch())
{
$aKeys[] = $oObject->GetKey();
@@ -843,7 +837,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]
@@ -952,7 +947,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'] : '';
@@ -1886,11 +1881,11 @@ class MenuBlock extends DisplayBlock
{
if (count($aFavoriteActions) > 0)
{
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>".Dict::S('UI:Menu:OtherActions')."\n<ul>\n";
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>".Dict::S('UI:Menu:OtherActions')."<i class=\"fas fa-caret-down\"></i>"."\n<ul>\n";
}
else
{
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>".Dict::S('UI:Menu:Actions')."\n<ul>\n";
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>".Dict::S('UI:Menu:Actions')."<i class=\"fas fa-caret-down\"></i>"."\n<ul>\n";
}
$sHtml .= $oPage->RenderPopupMenuItems($aActions, $aFavoriteActions);

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

@@ -395,6 +395,7 @@ EOF
{
foreach($aFields as $oField)
{
/** @var \DesignerFormField $oField */
$oField->ReadParam($aValues);
}
}
@@ -679,18 +680,34 @@ class DesignerTabularForm extends DesignerForm
class DesignerFormField
{
/** @var string $sLabel */
protected $sLabel;
/** @var string $sCode */
protected $sCode;
/** @var mixed $defaultValue */
protected $defaultValue;
/** @var \DesignerForm $oForm */
protected $oForm;
/** @var bool $bMandatory */
protected $bMandatory;
/** @var bool $bReadOnly */
protected $bReadOnly;
/** @var bool $bAutoApply */
protected $bAutoApply;
/** @var array $aCSSClasses */
protected $aCSSClasses;
/** @var bool $bDisplayed */
protected $bDisplayed;
/** @var array $aWidgetExtraParams */
protected $aWidgetExtraParams;
/**
* DesignerFormField constructor.
*
* @param string $sCode
* @param string $sLabel
* @param mixed $defaultValue
*/
public function __construct($sCode, $sLabel, $defaultValue)
{
$this->sLabel = $sLabel;
@@ -703,7 +720,10 @@ class DesignerFormField
$this->bDisplayed = true;
$this->aWidgetExtraParams = array();
}
/**
* @return string
*/
public function GetCode()
{
return $this->sCode;
@@ -712,69 +732,108 @@ class DesignerFormField
/**
* @param \DesignerForm $oForm
*/
public function SetForm(\DesignerForm $oForm)
public function SetForm(DesignerForm $oForm)
{
$this->oForm = $oForm;
}
/**
* @param bool $bMandatory
*/
public function SetMandatory($bMandatory = true)
{
$this->bMandatory = $bMandatory;
}
/**
* @param bool $bReadOnly
*/
public function SetReadOnly($bReadOnly = true)
{
$this->bReadOnly = $bReadOnly;
}
/**
* @return bool
*/
public function IsReadOnly()
{
return ($this->oForm->IsReadOnly() || $this->bReadOnly);
}
/**
* @param bool $bAutoApply
*/
public function SetAutoApply($bAutoApply)
{
$this->bAutoApply = $bAutoApply;
}
/**
* @return bool
*/
public function IsAutoApply()
{
return $this->bAutoApply;
}
/**
* @param bool $bDisplayed
*/
public function SetDisplayed($bDisplayed)
{
$this->bDisplayed = $bDisplayed;
}
/**
* @return bool
*/
public function IsDisplayed()
{
return $this->bDisplayed;
}
/**
* @return string
*/
public function GetFieldId()
{
return $this->oForm->GetFieldId($this->sCode);
}
/**
* @return string
*/
public function GetWidgetClass()
{
return 'property_field';
}
/**
* @return array
*/
public function GetWidgetExtraParams()
{
return $this->aWidgetExtraParams;
}
/**
* @param \WebPage $oP
* @param string $sFormId
* @param string $sRenderMode
*
* @return array
*/
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
{
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
return array('label' => $this->sLabel, 'value' => "<input type=\"text\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
}
/**
* @param array $aValues
*/
public function ReadParam(&$aValues)
{
if ($this->IsReadOnly())
@@ -801,12 +860,18 @@ class DesignerFormField
}
}
}
/**
* @return bool
*/
public function IsVisible()
{
return true;
}
/**
* @param string $sCSSClass
*/
public function AddCSSClass($sCSSClass)
{
$this->aCSSClasses[] = $sCSSClass;
@@ -814,6 +879,8 @@ class DesignerFormField
/**
* A way to set/change the default value after constructing the field
*
* @param array $aAllDefaultValue
*/
public function SetDefaultValueFrom($aAllDefaultValue)
{
@@ -822,7 +889,12 @@ class DesignerFormField
$this->defaultValue = $aAllDefaultValue[$this->GetCode()];
}
}
/**
* @param $sFieldCode
*
* @return \DesignerFormField|false
*/
public function FindField($sFieldCode)
{
if ($this->sCode == $sFieldCode)
@@ -832,11 +904,17 @@ class DesignerFormField
return false;
}
/**
* @return string
*/
public function GetHandlerEquals()
{
return 'null';
}
/**
* @return string
*/
public function GetHandlerGetValue()
{
return 'null';
@@ -845,25 +923,43 @@ class DesignerFormField
class DesignerLabelField extends DesignerFormField
{
/** @var int $iCount A counter to automatically make the field code */
protected static $iCount = 0;
/** @var string $sDescription */
protected $sDescription;
/**
* @inheritdoc
*/
public function __construct($sLabel, $sDescription)
{
parent::__construct('', $sLabel, '');
// Increase counter
static::$iCount++;
parent::__construct('label_number_' . static::$iCount, $sLabel, '');
$this->sDescription = $sDescription;
}
/**
* @inheritdoc
*/
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
{
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
return array('label' => $this->sLabel, 'value' => $this->sDescription);
}
/**
* @inheritdoc
*/
public function ReadParam(&$aValues)
{
}
/**
* @inheritdoc
*/
public function IsVisible()
{
return true;
@@ -1334,7 +1430,8 @@ class DesignerIconSelectionField extends DesignerFormField
$sPostUploadTo = ($this->sUploadUrl == null) ? 'null' : "'{$this->sUploadUrl}'";
if (!$this->IsReadOnly())
{
$sValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$this->defaultValue}\"/>";
$sDefaultValue = ($this->defaultValue !== '') ? $this->defaultValue : $this->aAllowedValues[$idx]['value'];
$sValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$sDefaultValue}\"/>";
$oP->add_ready_script(
<<<EOF
$('#$sId').icon_select({current_idx: $idx, items: $sJSItems, post_upload_to: $sPostUploadTo});
@@ -1396,6 +1493,7 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField
$sAvailableIcons .= ' static $sKey = '.var_export($sKey, true).';'.PHP_EOL;
$sAvailableIcons .= ' static $aIconFiles = '.var_export($aFiles, true).';'.PHP_EOL;
$sAvailableIcons .= '}'.PHP_EOL;
SetupUtils::builddir(dirname($sCacheFile));
file_put_contents($sCacheFile, $sAvailableIcons, LOCK_EX);
}
return $aFiles;

View File

@@ -1,27 +1,20 @@
<?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/>
/**
* Class iTopWebPage
* Copyright (C) 2013-2020 Combodo SARL
*
* @copyright Copyright (C) 2010-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
require_once(APPROOT."/application/nicewebpage.class.inc.php");
@@ -46,11 +39,19 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
protected $sBreadCrumbEntryIcon;
protected $oCtx;
/**
* iTopWebPage constructor.
*
* @param string $sTitle
* @param bool $bPrintable
*
* @throws \Exception
*/
public function __construct($sTitle, $bPrintable = false)
{
parent::__construct($sTitle, $bPrintable);
$this->m_oTabs = new TabManager();
$this->oCtx = new ContextTag('GUI:Console');
$this->oCtx = new ContextTag(ContextTag::TAG_CONSOLE);
ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker');
@@ -70,7 +71,10 @@ 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->add_header('Cache-control: no-cache, no-store, must-revalidate');
$this->add_header('Pragma: no-cache');
$this->add_header('Expires: 0');
$this->add_header('X-Frame-Options: deny');
$this->add_linked_stylesheet("../css/jquery.treeview.css");
$this->add_linked_stylesheet("../css/jquery.autocomplete.css");
$this->add_linked_stylesheet("../css/jquery-ui-timepicker-addon.css");
@@ -79,6 +83,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');
@@ -92,6 +97,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');
@@ -150,6 +156,9 @@ EOF
}
}
/**
* @return bool
*/
protected function IsMenuPaneVisible()
{
$bLeftPaneOpen = true;
@@ -172,6 +181,9 @@ EOF
return $bLeftPaneOpen;
}
/**
*
*/
protected function PrepareLayout()
{
if (MetaModel::GetConfig()->Get('demo_mode'))
@@ -343,6 +355,20 @@ JS
.magnificPopup({type: 'image', closeOnContentClick: true });
JS
);
// Highlight code content created with CKEditor
$this->add_ready_script(
<<<JS
// Highlight code content for HTML AttributeText
$("[data-attribute-type='AttributeText'] .HTML pre").each(function(i, block) {
hljs.highlightBlock(block);
});
// Highlight code content for CaseLogs
$("[data-attribute-type='AttributeCaseLog'] .caselog_entry_html pre").each(function(i, block) {
hljs.highlightBlock(block);
});
JS
);
$this->add_init_script(
<<< JS
@@ -736,11 +762,22 @@ JS
$this->sBreadCrumbEntryIcon = null;
}
/**
* @param string $sHtml
*/
public function AddToMenu($sHtml)
{
$this->m_sMenu .= $sHtml;
}
/**
* @return string
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
public function GetSiloSelectionForm()
{
// List of visible Organizations
@@ -801,6 +838,9 @@ JS
return $sHtml;
}
/**
* @throws \DictExceptionMissingString
*/
public function DisplayMenu()
{
// Display the menu
@@ -816,6 +856,8 @@ JS
protected function InitNewsroom()
{
$sNewsroomInitialImage = '';
$aProviderParams = array();
if (MetaModel::GetConfig()->Get('newsroom_enabled') !== false)
{
$oUser = UserRights::GetUserObject();
@@ -823,30 +865,31 @@ JS
* @var iNewsroomProvider[] $aProviders
*/
$aProviders = MetaModel::EnumPlugins('iNewsroomProvider');
$aProviderParams = array();
foreach($aProviders as $oProvider)
{
$oProvider->SetConfig(MetaModel::GetConfig());
$bProviderEnabled = appUserPreferences::GetPref('newsroom_provider_'.get_class($oProvider), true);
if ($bProviderEnabled && $oProvider->IsApplicable($oUser))
{
$aProviderParams[] = array(
'label' => $oProvider->GetLabel(),
'fetch_url' => $oProvider->GetFetchURL(),
'view_all_url' => $oProvider->GetViewAllURL(),
'mark_all_as_read_url' => $oProvider->GetMarkAllAsReadURL(),
'placeholders' => $oProvider->GetPlaceholders(),
'ttl' => $oProvider->GetTTL(),
);
$bProviderEnabled = appUserPreferences::GetPref('newsroom_provider_'.get_class($oProvider),true);
if ($bProviderEnabled && $oProvider->IsApplicable($oUser))
{
$aProviderParams[] = array(
'label' => $oProvider->GetLabel(),
'fetch_url' => $oProvider->GetFetchURL(),
'view_all_url' => $oProvider->GetViewAllURL(),
'mark_all_as_read_url' => $oProvider->GetMarkAllAsReadURL(),
'placeholders' => $oProvider->GetPlaceholders(),
'ttl' => $oProvider->GetTTL(),
);
}
}
}
// Show newsroom only if there are some providers
if (count($aProviderParams) > 0)
{
$sImageUrl= '../images/newsroom_menu.png';
$sPlaceholderImageUrl= '../images/newsroom-message.svg';
$sImageUrl= 'fas fa-comment-dots';
$sPlaceholderImageUrl= 'far fa-envelope';
$aParams = array(
'image_url' => $sImageUrl,
'placeholder_image_url' => $sPlaceholderImageUrl,
'image_icon' => $sImageUrl,
'placeholder_image_icon' => $sPlaceholderImageUrl,
'cache_uuid' => 'itop-newsroom-'.UserRights::GetUserId().'-'.md5(APPROOT),
'providers' => $aProviderParams,
'display_limit' => (int)appUserPreferences::GetPref('newsroom_display_size', 7),
@@ -862,20 +905,16 @@ JS
$('#top-left-newsroom-cell').newsroom_menu($sParams);
EOF
);
$sNewsroomInitialImage = '<img style="opacity:0.4" src="../images/newsroom_menu.png">';
$sNewsroomInitialImage = '<i style="opacity:0.4" class="top-right-icon fas fa-comment-dots"></i>';
}
else
{
// No newsroom menu at all
}
}
// else no newsroom menu
return $sNewsroomInitialImage;
// else no newsroom menu
return $sNewsroomInitialImage;
}
/**
* Outputs (via some echo) the complete HTML page by assembling all its elements
* @inheritDoc
* @throws \Exception
*/
public function output()
{
@@ -1109,7 +1148,7 @@ EOF
{
$sBodyClass = 'printable-version';
}
$sHtml .= "<body class=\"$sBodyClass\">\n";
$sHtml .= "<body class=\"$sBodyClass\" data-gui-type=\"backoffice\">\n";
if ($this->IsPrintableVersion())
{
$sHtml .= "<div class=\"explain-printable not-printable\">";
@@ -1146,7 +1185,7 @@ EOF;
}
// Render the revision number
if (ITOP_REVISION == '$WCREV$')
if (ITOP_REVISION == 'svn')
{
// This is NOT a version built using the buil system, just display the main version
$sVersionString = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
@@ -1182,7 +1221,7 @@ EOF;
{
$sLogonMessage = Dict::Format('UI:LoggedAsMessage', $sUserName);
}
$sLogOffMenu = "<span id=\"logOffBtn\"><ul><li><img src=\"../images/on-off-menu.png\"><ul>";
$sLogOffMenu = "<span id=\"logOffBtn\"><ul><li><i class=\"top-right-icon icon-additional-arrow fas fa-power-off\"></i><ul>";
$sLogOffMenu .= "<li><span>$sLogonMessage</span></li>\n";
$aActions = array();
@@ -1344,10 +1383,10 @@ EOF;
$sHtml .= ' <table id="top-bar-table">';
$sHtml .= ' <tr>';
$sHtml .= ' <td id="open-left-pane" class="menu-pane-exclusive" style="'.$GoHomeInitialStyle.'" onclick="$(\'body\').layout().open(\'west\');">';
$sHtml .= ' <img src="../images/menu.png">';
$sHtml .= ' <i class="fas fa-bars"></i>';
$sHtml .= ' </td>';
$sHtml .= ' <td id="go-home" class="menu-pane-exclusive" style="'.$GoHomeInitialStyle.'">';
$sHtml .= ' <a href="'.utils::GetAbsoluteUrlAppRoot().'pages/UI.php"><img src="../images/home.png"></a>';
$sHtml .= ' <a href="'.utils::GetAbsoluteUrlAppRoot().'pages/UI.php"><i class="fas fa-home"></i></a>';
$sHtml .= ' </td>';
$sHtml .= ' <td class="top-bar-spacer menu-pane-exclusive" style="'.$GoHomeInitialStyle.'">';
$sHtml .= ' </td>';
@@ -1357,8 +1396,8 @@ EOF;
$sHtml .= ' <td id="top-bar-table-search">';
$sHtml .= ' <div id="global-search"><form action="'.utils::GetAbsoluteUrlAppRoot().'pages/UI.php">';
$sHtml .= ' <table id="top-left-buttons-area"><tr>';
$sHtml .= ' <td id="top-left-global-search-cell"><div id="global-search-area"><input id="global-search-input" type="text" name="text" placeholder="'.$sDefaultPlaceHolder.'" value="'.$sText.'"></input><div '.$sOnClick.' id="global-search-image"><input type="hidden" name="operation" value="full_text"/></div></div></td>';
$sHtml .= ' <td id="top-left-help-cell"><a id="help-link" href="'.$sOnlineHelpUrl.'" target="_blank"><img title="'.Dict::S('UI:Help').'" src="../images/help.png?t='.utils::GetCacheBusterTimestamp().'"/></td>';
$sHtml .= ' <td id="top-left-global-search-cell"><div id="global-search-area"><input id="global-search-input" type="text" name="text" placeholder="'.$sDefaultPlaceHolder.'" value="'.$sText.'"></input><div '.$sOnClick.' id="global-search-image"><i class="top-right-icon fa-flip-horizontal fas fa-search"></i><input type="hidden" name="operation" value="full_text"/></div></div></td>';
$sHtml .= ' <td id="top-left-help-cell"><a id="help-link" href="'.$sOnlineHelpUrl.'" target="_blank" title="'.Dict::S('UI:Help').'"><i class="top-right-icon fas fa-question-circle"></i></a></td>';
$sHtml .= ' <td id="top-left-newsroom-cell">'.$sNewsRoomInitialImage.'</td>';
$sHtml .= ' <td id="top-left-logoff-cell">'.self::FilterXSS($sLogOffMenu).'</td>';
$sHtml .= ' </tr></table></form></div>';
@@ -1413,9 +1452,12 @@ EOF;
{
if ($this->GetOutputFormat() == 'pdf' && $this->IsOutputFormatAvailable('pdf'))
{
// Note: Apparently this was a demand from ITOMIG a while back, so it's not "dead code" per say.
// The last trace we got is in R-007989. Do not remove this without checking before with the concerned parties if it is still used!
if (@is_readable(APPROOT.'lib/MPDF/mpdf.php'))
{
require_once(APPROOT.'lib/MPDF/mpdf.php');
/** @noinspection PhpUndefinedClassInspection Check above comment */
$oMPDF = new mPDF('c');
$oMPDF->mirroMargins = false;
if ($this->a_base['href'] != '')
@@ -1442,59 +1484,68 @@ EOF;
ExecutionKPI::ReportStats();
}
/**
* @inheritDoc
* @throws \Exception
*/
public function AddTabContainer($sTabContainer, $sPrefix = '')
{
$this->add($this->m_oTabs->AddTabContainer($sTabContainer, $sPrefix));
}
public function AddToTab($sTabContainer, $sTabLabel, $sHtml)
/**
* @inheritDoc
* @throws \Exception
*/
public function AddToTab($sTabContainer, $sTabCode, $sHtml)
{
$this->add($this->m_oTabs->AddToTab($sTabContainer, $sTabLabel, $sHtml));
$this->add($this->m_oTabs->AddToTab($sTabContainer, $sTabCode, $sHtml));
}
/**
* @inheritDoc
*/
public function SetCurrentTabContainer($sTabContainer = '')
{
return $this->m_oTabs->SetCurrentTabContainer($sTabContainer);
}
public function SetCurrentTab($sTabLabel = '')
/**
* @inheritDoc
*/
public function SetCurrentTab($sTabCode = '', $sTabTitle = null)
{
return $this->m_oTabs->SetCurrentTab($sTabLabel);
return $this->m_oTabs->SetCurrentTab($sTabCode, $sTabTitle);
}
/**
* Add a tab which content will be loaded asynchronously via the supplied URL
*
* Limitations:
* Cross site scripting is not not allowed for security reasons. Use a normal tab with an IFRAME if you want to pull content from
* another server. Static content cannot be added inside such tabs.
*
* @param string $sTabLabel The (localised) label of the tab
* @param string $sUrl The URL to load (on the same server)
* @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. flase will cause the tab to be
* reloaded upon each activation.
*
* @inheritDoc
* @throws \Exception
* @since 2.0.3
*/
public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true)
public function AddAjaxTab($sTabCode, $sUrl, $bCache = true, $sTabTitle = null)
{
$this->add($this->m_oTabs->AddAjaxTab($sTabLabel, $sUrl, $bCache));
$this->add($this->m_oTabs->AddAjaxTab($sTabCode, $sUrl, $bCache, $sTabTitle));
}
/**
* @inheritDoc
*/
public function GetCurrentTab()
{
return $this->m_oTabs->GetCurrentTab();
}
public function RemoveTab($sTabLabel, $sTabContainer = null)
/**
* @inheritDoc
*/
public function RemoveTab($sTabCode, $sTabContainer = null)
{
$this->m_oTabs->RemoveTab($sTabLabel, $sTabContainer);
$this->m_oTabs->RemoveTab($sTabCode, $sTabContainer);
}
/**
* Finds the tab whose title matches a given pattern
*
* @return mixed The name of the tab as a string or false if not found
* @inheritDoc
*/
public function FindTab($sPattern, $sTabContainer = null)
{
@@ -1506,12 +1557,19 @@ EOF;
* DOES NOT WORK: apparently in the *old* version of jquery
* that we are using this is not supported... TO DO upgrade
* the whole jquery bundle...
*
* @param string $sTabContainer
* @param string $sTabCode
*/
public function SelectTab($sTabContainer, $sTabLabel)
public function SelectTab($sTabContainer, $sTabCode)
{
$this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabLabel));
$this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabCode));
}
/**
* @inheritDoc
* @throws \Exception
*/
public function add($sHtml)
{
if (($this->m_oTabs->GetCurrentTabContainer() != '') && ($this->m_oTabs->GetCurrentTab() != ''))
@@ -1525,9 +1583,7 @@ EOF;
}
/**
* Records the current state of the 'html' part of the page output
*
* @return mixed The current state of the 'html' output
* @inheritDoc
*/
public function start_capture()
{
@@ -1547,12 +1603,7 @@ EOF;
}
/**
* Returns the part of the html output that occurred since the call to start_capture
* and removes this part from the current html output
*
* @param $offset mixed The value returned by start_capture
*
* @return string The part of the html output that was added since the call to start_capture
* @inheritDoc
*/
public function end_capture($offset)
{
@@ -1577,6 +1628,8 @@ EOF;
/**
* Set the message to be displayed in the 'app-banner' section at the top of the page
*
* @param string $sHtmlMessage
*/
public function SetMessage($sHtmlMessage)
{
@@ -1586,6 +1639,10 @@ EOF;
/**
* Add message to be displayed in the 'app-banner' section at the top of the page
*
* @param string $sHtmlMessage
* @param string|null $sHtmlIcon
* @param string|null $sTip
*/
public function AddApplicationMessage($sHtmlMessage, $sHtmlIcon = null, $sTip = null)
{
@@ -1606,7 +1663,8 @@ EOF;
* @param string $sContent
* @param string $sCssClasses CSS classes to add to the container
*
* @since 2.6
* @throws \Exception
* @since 2.6.0
*/
public function AddHeaderMessage($sContent, $sCssClasses = 'message_info')
{
@@ -1619,6 +1677,8 @@ EOF
/**
* Adds a script to be executed when the DOM is ready (typical JQuery use), right before add_ready_script
*
* @param string $sScript
*
* @return void
*/
public function add_init_script($sScript)

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,13 +1,16 @@
<?php
/**
* Class LoginForm
*
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class LoginForm extends AbstractLoginFSMExtension implements iLoginDataExtension
/**
* Class LoginForm
*
* @since 2.7.0
*/
class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
{
private $bForceFormOnError = false;
@@ -21,6 +24,9 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginDataExtension
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 iLoginDataExtension
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @inheritDoc
*/
protected function OnCheckCredentials(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'form')
@@ -66,6 +75,9 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginDataExtension
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @inheritDoc
*/
protected function OnCredentialsOK(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'form')
@@ -85,6 +97,9 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginDataExtension
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @inheritDoc
*/
protected function OnError(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'form')
@@ -94,6 +109,9 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginDataExtension
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @inheritDoc
*/
protected function OnConnected(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'form')
@@ -105,14 +123,14 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginDataExtension
}
/**
* @return LoginTwigData
* @inheritDoc
* @throws \Exception
*/
public function GetLoginData()
public function GetTwigContext()
{
$aPostedVars = array('auth_user', 'auth_pwd');
$oLoginData = new LoginTwigData($aPostedVars);
$oLoginContext = new LoginTwigContext();
$oLoginContext->AddPostedVar('auth_user');
$oLoginContext->AddPostedVar('auth_pwd');
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
$sAuthPwd = utils::ReadParam('suggest_pwd', '', true, 'raw_data');
@@ -121,19 +139,18 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginDataExtension
'sAuthUser' => $sAuthUser,
'sAuthPwd' => $sAuthPwd,
);
$oLoginData->AddBlockData('login_input', new LoginBlockData('loginforminput.html.twig', $aData));
$oLoginData->AddBlockData('login_submit', new LoginBlockData('loginformsubmit.html.twig'));
$oLoginData->AddBlockData('login_form_footer', new LoginBlockData('loginformfooter.html.twig'));
$oLoginContext->AddBlockExtension('login_input', new LoginBlockExtension('extensionblock/loginforminput.html.twig', $aData));
$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,
'sResetPasswordUrl' => $sResetPasswordUrl,
);
$oLoginData->AddBlockData('login_links', new LoginBlockData('loginformlinks.html.twig', $aData));
$oLoginContext->AddBlockExtension('login_links', new LoginBlockExtension('extensionblock/loginformlinks.html.twig', $aData));
return $oLoginData;
return $oLoginContext;
}
}

View File

@@ -9,61 +9,130 @@
use Combodo\iTop\TwigExtension;
class LoginTwigData
/**
* Twig context for modules extending the login screen
* Class LoginTwigContext
*/
class LoginTwigContext
{
private $aBlockData;
/** @var array */
private $aBlockExtension;
/** @var array */
private $aPostedVars;
/** @var string */
private $sTwigLoaderPath;
private $sCSSFile;
/** @var array */
private $aCSSFiles;
/** @var array */
private $aJsFiles;
private $sTwigNameSpace;
/**
* LoginTwigData constructor.
* Build a context to display the twig files used
* to extend the login screens
*
* @param array $aPostedVars
* @param string $sLoaderPath
* @param string $sCSSFile
* @param array $aJsFiles
* LoginTwigContext constructor.
* @api
*/
public function __construct($aPostedVars = array(), $sLoaderPath = null, $sCSSFile = null, $aJsFiles = array())
public function __construct()
{
$this->aBlockData = array();
$this->aPostedVars = $aPostedVars;
$this->sTwigLoaderPath = $sLoaderPath;
$this->sCSSFile = $sCSSFile;
$this->aJsFiles = $aJsFiles;
$this->aBlockExtension = array();
$this->aPostedVars = array();
$this->sTwigLoaderPath = null;
$this->aCSSFiles = array();
$this->aJsFiles = array();
$this->sTwigNameSpace = null;
}
/**
* Set the absolute path on disk of the folder containing the twig templates
*
* @param string $sPath absolute path of twig templates directory
* @api
*/
public function SetLoaderPath($sPath)
{
$this->sTwigLoaderPath = $sPath;
}
/**
* Add a Twig block extension
*
* @param string $sBlockName
* @param LoginBlockExtension $oBlockExtension
*/
public function AddBlockExtension($sBlockName, $oBlockExtension)
{
$this->aBlockExtension[$sBlockName] = $oBlockExtension;
}
/**
* Add a variable intended to be posted on URL (and managed) by the module.
* Declaring the posted variables will prevent the core engine to manipulate these variables.
*
* @param string $sPostedVar Name of the posted variable
* @api
*/
public function AddPostedVar($sPostedVar)
{
$this->aPostedVars[] = $sPostedVar;
}
/**
* Add the URL of a CSS file to link to the login screen
*
* @param string $sFile URL of the CSS file to link
* @api
*/
public function AddCSSFile($sFile)
{
$this->aCSSFiles[] = $sFile;
}
/**
* Add the URL of a javascript file to link to the login screen
* @param string $sFile URL of the javascript file to link
* @api
*/
public function AddJsFile($sFile)
{
$this->aJsFiles[] = $sFile;
}
/**
* @param string $sBlockName
* @param LoginBlockData $oBlockData
*
* @return \LoginBlockExtension
*/
public final function AddBlockData($sBlockName, $oBlockData)
public function GetBlockExtension($sBlockName)
{
$this->aBlockData[$sBlockName] = $oBlockData;
/** @var LoginBlockExtension $oBlockExtension */
$oBlockExtension = isset($this->aBlockExtension[$sBlockName]) ? $this->aBlockExtension[$sBlockName] : null;
return $oBlockExtension;
}
public final function GetBlockData($sBlockName)
{
/** @var LoginBlockData $oBlockData */
$oBlockData = isset($this->aBlockData[$sBlockName]) ? $this->aBlockData[$sBlockName] : null;
return $oBlockData;
}
public final function GetPostedVars()
/**
* @return array
*/
public function GetPostedVars()
{
return $this->aPostedVars;
}
public final function GetTwigLoaderPath()
/**
* @return string
*/
public function GetTwigLoaderPath()
{
return $this->sTwigLoaderPath;
}
public final function GetCSSFile()
/**
* @return array
*/
public function GetCSSFiles()
{
return $this->sCSSFile;
return $this->aCSSFiles;
}
/**
@@ -73,18 +142,33 @@ class LoginTwigData
{
return $this->aJsFiles;
}
}
class LoginBlockData
/**
* Twig block description for login screen extension
* The login screen can be extended by adding twig templates
* to specific blocks of the login screens
*
* Class LoginBlockExtension
*/
class LoginBlockExtension
{
private $sTwig;
private $aData;
/**
* LoginBlockData constructor.
* Create a new twig extension block
* The given twig template can be HTML, CSS or JavaScript.
* CSS goes to the block named 'css' and is inline in the page.
* JavaScript goes to the blocks named 'script' or 'ready_script' and are inline in the page.
* HTML goes to everywhere else
*
* @param string $sTwig
* @param array $aData
* LoginBlockExtension constructor.
*
* @param string $sTwig name of the twig file relative to the path given to the LoginTwigContext
* @param array $aData Data given to the twig template (into the variable {{ aData }})
* @api
*/
public function __construct($sTwig, $aData = array())
{
@@ -92,18 +176,22 @@ class LoginBlockData
$this->aData = $aData;
}
public final function GetTwig()
public function GetTwig()
{
return $this->sTwig;
}
public final function GetData()
public function GetData()
{
return $this->aData;
}
}
class LoginTwigContext
/**
* Used by LoginWebPage to display the login screen
* Class LoginTwigRenderer
*/
class LoginTwigRenderer
{
private $aLoginPluginList;
private $aPluginFormData;
@@ -112,21 +200,27 @@ class LoginTwigContext
public function __construct()
{
$this->aLoginPluginList = LoginWebPage::GetLoginPluginList('iLoginDataExtension', false);
$this->aLoginPluginList = LoginWebPage::GetLoginPluginList('iLoginUIExtension', false);
$this->aPluginFormData = array();
$aTwigLoaders = array();
$this->aPostedVars = array();
foreach ($this->aLoginPluginList as $oLoginPlugin)
{
/** @var \iLoginDataExtension $oLoginPlugin */
$oLoginData = $oLoginPlugin->GetLoginData();
$this->aPluginFormData[] = $oLoginData;
$sTwigLoaderPath = $oLoginData->GetTwigLoaderPath();
/** @var \iLoginUIExtension $oLoginPlugin */
$oLoginContext = $oLoginPlugin->GetTwigContext();
if (is_null($oLoginContext))
{
continue;
}
$this->aPluginFormData[] = $oLoginContext;
$sTwigLoaderPath = $oLoginContext->GetTwigLoaderPath();
if ($sTwigLoaderPath != null)
{
$aTwigLoaders[] = new Twig_Loader_Filesystem($sTwigLoaderPath);
$oExtensionLoader = new Twig_Loader_Filesystem();
$oExtensionLoader->setPaths($sTwigLoaderPath);
$aTwigLoaders[] = $oExtensionLoader;
}
$this->aPostedVars = array_merge($this->aPostedVars, $oLoginData->GetPostedVars());
$this->aPostedVars = array_merge($this->aPostedVars, $oLoginContext->GetPostedVars());
}
$oCoreLoader = new Twig_Loader_Filesystem(array(), APPROOT.'templates');
@@ -178,9 +272,9 @@ class LoginTwigContext
// Render CSS links
foreach ($this->aPluginFormData as $oFormData)
{
/** @var \LoginTwigData $oFormData */
$sCSSFile = $oFormData->GetCSSFile();
if (!empty($sCSSFile))
/** @var \LoginTwigContext $oFormData */
$aCSSFiles = $oFormData->GetCSSFiles();
foreach ($aCSSFiles as $sCSSFile)
{
$oPage->add_linked_stylesheet($sCSSFile);
}
@@ -224,4 +318,4 @@ class LoginTwigContext
{
return $this->oTwig;
}
}
}

View File

@@ -78,14 +78,16 @@ class LoginWebPage extends NiceWebPage
public function __construct($sTitle = null)
{
if($sTitle === null)
{
$sTitle = Dict::S('UI:Login:Title');
}
if ($sTitle === null) {
$sTitle = Dict::S('UI:Login:Title');
}
parent::__construct($sTitle);
$this->SetStyleSheet();
$this->add_header("Cache-control: no-cache");
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
$this->add_header('Pragma: no-cache');
$this->add_header('Expires: 0');
$this->add_header('X-Frame-Options: deny');
}
public function SetStyleSheet()
@@ -118,7 +120,7 @@ class LoginWebPage extends NiceWebPage
public function DisplayLoginForm($bFailedLogin = false)
{
$oTwigContext = new LoginTwigContext();
$oTwigContext = new LoginTwigRenderer();
$aPostedVars = array_merge(array('login_mode', 'loginop'), $oTwigContext->GetPostedVars());
$sMessage = Dict::S('UI:Login:IdentifyYourself');
@@ -171,7 +173,7 @@ class LoginWebPage extends NiceWebPage
{
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
$oTwigContext = new LoginTwigContext();
$oTwigContext = new LoginTwigRenderer();
$aVars = $oTwigContext->GetDefaultVars();
$aVars['sAuthUser'] = $sAuthUser;
$aVars['bFailedToReset'] = $bFailedToReset;
@@ -189,53 +191,54 @@ class LoginWebPage extends NiceWebPage
UserRights::Login($sAuthUser); // Set the user's language (if possible!)
/** @var UserInternal $oUser */
$oUser = UserRights::GetUserObject();
if ($oUser == null)
if ($oUser != null)
{
throw new Exception(Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser));
}
if (!MetaModel::IsValidAttCode(get_class($oUser), 'reset_pwd_token'))
{
throw new Exception(Dict::S('UI:ResetPwd-Error-NotPossible'));
}
if (!$oUser->CanChangePassword())
{
throw new Exception(Dict::S('UI:ResetPwd-Error-FixedPwd'));
}
$sTo = $oUser->GetResetPasswordEmail(); // throws Exceptions if not allowed
if ($sTo == '')
{
throw new Exception(Dict::S('UI:ResetPwd-Error-NoEmail'));
if (!MetaModel::IsValidAttCode(get_class($oUser), 'reset_pwd_token'))
{
throw new Exception(Dict::S('UI:ResetPwd-Error-NotPossible'));
}
if (!$oUser->CanChangePassword())
{
throw new Exception(Dict::S('UI:ResetPwd-Error-FixedPwd'));
}
$sTo = $oUser->GetResetPasswordEmail(); // throws Exceptions if not allowed
if ($sTo == '')
{
throw new Exception(Dict::S('UI:ResetPwd-Error-NoEmail'));
}
// This token allows the user to change the password without knowing the previous one
$sToken = substr(md5(APPROOT.uniqid()), 0, 16);
$oUser->Set('reset_pwd_token', $sToken);
CMDBObject::SetTrackInfo('Reset password');
$oUser->AllowWrite(true);
$oUser->DBUpdate();
$oEmail = new Email();
$oEmail->SetRecipientTO($sTo);
$sFrom = MetaModel::GetConfig()->Get('forgot_password_from');
$oEmail->SetRecipientFrom($sFrom);
$oEmail->SetSubject(Dict::S('UI:ResetPwd-EmailSubject', $oUser->Get('login')));
$sResetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=reset_pwd&auth_user='.urlencode($oUser->Get('login')).'&token='.urlencode($sToken);
$oEmail->SetBody(Dict::Format('UI:ResetPwd-EmailBody', $sResetUrl, $oUser->Get('login')));
$iRes = $oEmail->Send($aIssues, true /* force synchronous exec */);
switch ($iRes)
{
//case EMAIL_SEND_PENDING:
case EMAIL_SEND_OK:
break;
case EMAIL_SEND_ERROR:
default:
IssueLog::Error('Failed to send the email with the NEW password for '.$oUser->Get('friendlyname').': '.implode(', ', $aIssues));
throw new Exception(Dict::S('UI:ResetPwd-Error-Send'));
}
}
// This token allows the user to change the password without knowing the previous one
$sToken = substr(md5(APPROOT.uniqid()), 0, 16);
$oUser->Set('reset_pwd_token', $sToken);
CMDBObject::SetTrackInfo('Reset password');
$oUser->AllowWrite(true);
$oUser->DBUpdate();
$oEmail = new Email();
$oEmail->SetRecipientTO($sTo);
$sFrom = MetaModel::GetConfig()->Get('forgot_password_from');
$oEmail->SetRecipientFrom($sFrom);
$oEmail->SetSubject(Dict::S('UI:ResetPwd-EmailSubject', $oUser->Get('login')));
$sResetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=reset_pwd&auth_user='.urlencode($oUser->Get('login')).'&token='.urlencode($sToken);
$oEmail->SetBody(Dict::Format('UI:ResetPwd-EmailBody', $sResetUrl, $oUser->Get('login')));
$iRes = $oEmail->Send($aIssues, true /* force synchronous exec */);
switch ($iRes)
{
//case EMAIL_SEND_PENDING:
case EMAIL_SEND_OK:
break;
case EMAIL_SEND_ERROR:
default:
IssueLog::Error('Failed to send the email with the NEW password for '.$oUser->Get('friendlyname').': '.implode(', ', $aIssues));
throw new Exception(Dict::S('UI:ResetPwd-Error-Send'));
}
$oTwigContext = new LoginTwigContext();
$oTwigContext = new LoginTwigRenderer();
$aVars = $oTwigContext->GetDefaultVars();
$oTwigContext->Render($this, 'forgotpwdsent.html.twig', $aVars);
}
@@ -245,7 +248,7 @@ class LoginWebPage extends NiceWebPage
}
}
public function DisplayResetPwdForm()
public function DisplayResetPwdForm($sErrorMessage = null)
{
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sToken = utils::ReadParam('token', '', false, 'raw_data');
@@ -253,11 +256,13 @@ class LoginWebPage extends NiceWebPage
UserRights::Login($sAuthUser); // Set the user's language
$oUser = UserRights::GetUserObject();
$oTwigContext = new LoginTwigContext();
$oTwigContext = new LoginTwigRenderer();
$aVars = $oTwigContext->GetDefaultVars();
$aVars['sAuthUser'] = $sAuthUser;
$aVars['sToken'] = $sToken;
$aVars['sErrorMessage'] = $sErrorMessage;
if (($oUser == null))
{
$aVars['bNoUser'] = true;
@@ -265,6 +270,7 @@ class LoginWebPage extends NiceWebPage
else
{
$aVars['bNoUser'] = false;
$aVars['sUserName'] = $oUser->GetFriendlyName();
$oEncryptedToken = $oUser->Get('reset_pwd_token');
if (!$oEncryptedToken->CheckPassword($sToken))
@@ -274,7 +280,6 @@ class LoginWebPage extends NiceWebPage
else
{
$aVars['bBadToken'] = false;
$aVars['sUserName'] = $oUser->GetFriendlyName();
}
}
@@ -288,9 +293,10 @@ class LoginWebPage extends NiceWebPage
$sNewPwd = utils::ReadPostedParam('new_pwd', '', 'raw_data');
UserRights::Login($sAuthUser); // Set the user's language
/** @var \UserLocal $oUser */
$oUser = UserRights::GetUserObject();
$oTwigContext = new LoginTwigContext();
$oTwigContext = new LoginTwigRenderer();
$aVars = $oTwigContext->GetDefaultVars();
$aVars['sAuthUser'] = $sAuthUser;
@@ -312,7 +318,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();
@@ -322,11 +328,12 @@ class LoginWebPage extends NiceWebPage
$oTwigContext->Render($this, 'resetpwddone.html.twig', $aVars);
}
public function DisplayChangePwdForm($bFailedLogin = false)
public function DisplayChangePwdForm($bFailedLogin = false, $sIssue = null)
{
$oTwigContext = new LoginTwigContext();
$oTwigContext = new LoginTwigRenderer();
$aVars = $oTwigContext->GetDefaultVars();
$aVars['bFailedLogin'] = $bFailedLogin;
$aVars['sIssue'] = $sIssue;
$oTwigContext->Render($this, 'changepwdform.html.twig', $aVars);
}
@@ -337,7 +344,7 @@ class LoginWebPage extends NiceWebPage
$sTitle = empty($sTitle) ? Dict::S('UI:LogOff:ThankYou') : $sTitle;
$sMessage = Dict::S('UI:LogOff:ClickHereToLoginAgain');
$oTwigContext = new LoginTwigContext();
$oTwigContext = new LoginTwigRenderer();
$aVars = $oTwigContext->GetDefaultVars();
$aVars['sUrl'] = $sUrl;
$aVars['sTitle'] = $sTitle;
@@ -410,6 +417,7 @@ class LoginWebPage extends NiceWebPage
if ($bLoginDebug)
{
IssueLog::Info("---------------------------------");
IssueLog::Info($_SERVER['REQUEST_URI']);
IssueLog::Info("--> Entering Login FSM with state: [$sLoginState]");
$sSessionLog = session_id().' '.utils::GetSessionLog();
IssueLog::Info("SESSION: $sSessionLog");
@@ -494,6 +502,7 @@ class LoginWebPage extends NiceWebPage
foreach (MetaModel::EnumPlugins($sInterface) as $oLoginExtensionInstance)
{
$aLoginModes = $oLoginExtensionInstance->ListSupportedLoginModes();
$aLoginModes = (is_array($aLoginModes) ? $aLoginModes : array());
foreach ($aLoginModes as $sLoginMode)
{
// Keep only the plugins for the current login mode + before + after
@@ -783,6 +792,14 @@ class LoginWebPage extends NiceWebPage
$oPerson = null;
try
{
CMDBObject::SetTrackOrigin('custom-extension');
$sInfo = 'External User provisioning';
if (isset($_SESSION['login_mode']))
{
$sInfo .= " ({$_SESSION['login_mode']})";
}
CMDBObject::SetTrackInfo($sInfo);
$oPerson = MetaModel::NewObject('Person');
$oPerson->Set('first_name', $sFirstName);
$oPerson->Set('name', $sLastName);
@@ -797,17 +814,7 @@ class LoginWebPage extends NiceWebPage
{
$oPerson->Set($sAttCode, $sValue);
}
/** @var CMDBChange $oMyChange */
$oMyChange = MetaModel::NewObject('CMDBChange');
$oMyChange->Set("date", time());
$sOrigin = 'External User provisioning';
if (isset($_SESSION['login_mode']))
{
$sOrigin .= " ({$_SESSION['login_mode']})";
}
$oMyChange->Set('userinfo', $sOrigin);
$oMyChange->DBInsert();
$oPerson->DBInsertTracked($oMyChange);
$oPerson->DBInsert();
}
catch (Exception $e)
{
@@ -839,6 +846,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))
{
@@ -889,19 +904,7 @@ class LoginWebPage extends NiceWebPage
$oUser->Set('profile_list', $oProfilesSet);
if ($oUser->IsModified())
{
/** @var \CMDBChange $oMyChange */
$oMyChange = MetaModel::NewObject("CMDBChange");
$oMyChange->Set("date", time());
$oMyChange->Set('userinfo', $sOrigin);
$oMyChange->DBInsert();
if ($oUser->IsNew())
{
$oUser->DBInsertTracked($oMyChange);
}
else
{
$oUser->DBUpdateTracked($oMyChange);
}
$oUser->DBWrite();
}
}
catch (Exception $e)
@@ -938,6 +941,7 @@ class LoginWebPage extends NiceWebPage
{
// No rights to be here, redirect to the portal
header('Location: '.$ret);
die();
}
}
return self::EXIT_CODE_OK;
@@ -992,7 +996,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();
@@ -1044,8 +1048,17 @@ class LoginWebPage extends NiceWebPage
}
else if ($operation == 'do_reset_pwd')
{
$oPage = self::NewLoginWebPage();
$oPage->DoResetPassword();
try {
$oPage = self::NewLoginWebPage();
$oPage->DoResetPassword();
}
catch (CoreCannotSaveObjectException $e)
{
$oPage = self::NewLoginWebPage();
$oPage->DisplayResetPwdForm($e->getIssue());
}
$oPage->output();
exit;
}
@@ -1061,6 +1074,24 @@ class LoginWebPage extends NiceWebPage
exit;
}
}
else if ($operation == 'check_pwd_policy')
{
$sAuthUser = $_SESSION['auth_user'];
UserRights::Login($sAuthUser); // Set the user's language
$aPwdMap = array();
foreach (array('new_pwd', 'retype_new_pwd') as $postedPwd)
{
$oUser = new UserLocal();
$oUser->ValidatePassword($_POST[$postedPwd]);
$aPwdMap[$postedPwd]['isValid'] = $oUser->IsPasswordValid();
$aPwdMap[$postedPwd]['message'] = $oUser->getPasswordValidityMessage();
}
echo json_encode($aPwdMap);
die();
}
if ($operation == 'do_change_pwd')
{
if (isset($_SESSION['auth_user']))
@@ -1069,10 +1100,21 @@ class LoginWebPage extends NiceWebPage
UserRights::Login($sAuthUser); // Set the user's language
$sOldPwd = utils::ReadPostedParam('old_pwd', '', 'raw_data');
$sNewPwd = utils::ReadPostedParam('new_pwd', '', 'raw_data');
if (UserRights::CanChangePassword() && ((!UserRights::CheckCredentials($sAuthUser, $sOldPwd)) || (!UserRights::ChangePassword($sOldPwd, $sNewPwd))))
try
{
if (UserRights::CanChangePassword() && ((!UserRights::CheckCredentials($sAuthUser, $sOldPwd)) || (!UserRights::ChangePassword($sOldPwd, $sNewPwd))))
{
$oPage = self::NewLoginWebPage();
$oPage->DisplayChangePwdForm(true); // old pwd was wrong
$oPage->output();
exit;
}
}
catch (CoreCannotSaveObjectException $e)
{
$oPage = self::NewLoginWebPage();
$oPage->DisplayChangePwdForm(true); // old pwd was wrong
$oPage->DisplayChangePwdForm(true, $e->getIssue()); // password policy was not met.
$oPage->output();
exit;
}

View File

@@ -1,7 +1,20 @@
<?php
/**
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* 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
*/
@@ -18,12 +31,18 @@
function _MaintenanceSetupPageMessage($sTitle, $sMessage)
{
// Web Page
@include_once(APPROOT.'bootstrap.inc.php');
@include_once(APPROOT.'setup/setuppage.class.inc.php');
if (class_exists('SetupPage'))
{
$oP = new SetupPage($sTitle);
$oP->p("<h2>$sMessage</h2>");
$oP = new ErrorPage($sTitle);
$oP->p("<h2 class=\"center\">$sMessage</h2>");
$oP->add_ready_script(
<<<JS
// Reload in 30s to check if maintenance is over
setTimeout(function(){ window.location.reload(); }, 30000);
JS
);
$oP->output();
}
else
@@ -58,7 +77,6 @@ function _MaintenanceHtmlMessage($sMessage)
*/
function _MaintenanceJsonMessage($sTitle, $sMessage)
{
@include_once(APPROOT.'bootstrap.inc.php');
@include_once(APPROOT."/application/ajaxwebpage.class.inc.php");
if (class_exists('ajax_page'))
{

View File

@@ -1,27 +1,20 @@
<?php
// Copyright (C) 2010-2016 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/>
/**
* Construction and display of the application's main menu
* Copyright (C) 2013-2019 Combodo SARL
*
* @copyright Copyright (C) 2010-2016 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
*/
require_once(APPROOT.'/application/utils.inc.php');
@@ -135,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();
@@ -201,7 +194,7 @@ class ApplicationMenu
/**
* Entry point to display the whole menu into the web page, used by iTopWebPage
* @param \iTopWebPage $oPage
* @param \WebPage $oPage
* @param $aExtraParams
* @throws DictExceptionMissingString
*/
@@ -217,7 +210,7 @@ class ApplicationMenu
{
if (!self::CanDisplayMenu($aMenu)) { continue; }
$oMenuNode = self::GetMenuNode($aMenu['index']);
$oPage->AddToMenu('<h3 id="'.utils::GetSafeId('AccordionMenu_'.$oMenuNode->GetMenuID()).'">'.$oMenuNode->GetTitle().'</h3>');
$oPage->AddToMenu('<h3 id="'.utils::GetSafeId('AccordionMenu_'.$oMenuNode->GetMenuID()).'" class="navigation-menu-group" data-menu-id="'.$oMenuNode->GetMenuId().'">'.$oMenuNode->GetTitle().'</h3>');
$oPage->AddToMenu('<div>');
$oPage->AddToMenu('<ul>');
$aChildren = self::GetChildren($aMenu['index']);
@@ -270,12 +263,15 @@ EOF
/**
* Handles the display of the sub-menus (called recursively if necessary)
* @param \iTopWebPage $oPage
*
* @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)
{
@@ -284,14 +280,22 @@ EOF
usort($aMenus, array('ApplicationMenu', 'CompareOnRank'));
foreach($aMenus as $aMenu)
{
if (!self::CanDisplayMenu($aMenu))
{
continue;
}
$index = $aMenu['index'];
$oMenu = self::GetMenuNode($index);
if ($oMenu->IsEnabled())
{
$aChildren = self::GetChildren($index);
$sCSSClass = (count($aChildren) > 0) ? ' class="submenu"' : '';
$aCSSClasses = array('navigation-menu-item');
if (count($aChildren) > 0)
{
$aCSSClasses[] = 'submenu';
}
$sHyperlink = $oMenu->GetHyperlink($aExtraParams);
$sItemHtml = '<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'" '.$sCSSClass.'>';
$sItemHtml = '<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'" class="'.implode(' ', $aCSSClasses).'" data-menu-id="'.$oMenu->GetMenuID().'">';
if ($sHyperlink != '')
{
$sLinkTarget = '';
@@ -299,7 +303,9 @@ EOF
{
$sLinkTarget .= ' target="_blank"';
}
$sItemHtml .= '<a href="'.$oMenu->GetHyperlink($aExtraParams).'"'.$sLinkTarget.'>'.$oMenu->GetTitle().'</a>';
$sURL = '"'.$oMenu->GetHyperlink($aExtraParams).'"'.$sLinkTarget;
$sTitle = utils::HtmlEntities($oMenu->GetTitle());
$sItemHtml .= "<a href={$sURL}>{$sTitle}</a>";
}
else
{
@@ -607,7 +613,9 @@ abstract class MenuNode
/**
* @param $aExtraParams
*
* @return string
* @throws \Exception
*/
public function GetHyperlink($aExtraParams)
{
@@ -687,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)
@@ -732,8 +740,7 @@ class MenuGroup extends MenuNode
}
/**
* @param WebPage $oPage
* @param array $aExtraParams
* @inheritDoc
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
@@ -771,8 +778,7 @@ class TemplateMenuNode extends MenuNode
}
/**
* @param $aExtraParams
* @return string
* @inheritDoc
*/
public function GetHyperlink($aExtraParams)
{
@@ -781,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())
{
@@ -873,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())
{
@@ -897,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
@@ -922,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);
@@ -974,16 +974,14 @@ 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())
{
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
$oPage->SetBreadCrumbEntry("menu-".$this->sMenuId, $this->GetTitle(), '', '', utils::GetAbsoluteUrlAppRoot().'images/search.png');
$oPage->SetBreadCrumbEntry("menu-".$this->sMenuId, $this->GetTitle(), '', '', utils::GetAbsoluteUrlAppRoot().'images/breadcrumb-search.png');
$oSearch = new DBObjectSearch($this->sClass);
$aParams = array_merge(array('table_id' => 'Menu_'.utils::GetSafeId($this->GetMenuId())), $aExtraParams);
@@ -1034,8 +1032,7 @@ class WebPageMenuNode extends MenuNode
}
/**
* @param array $aExtraParams
* @return string
* @inheritDoc
*/
public function GetHyperlink($aExtraParams)
{
@@ -1043,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())
{
@@ -1091,10 +1090,7 @@ class NewObjectMenuNode extends MenuNode
}
/**
* @param string[] $aExtraParams
*
* @return string
* @throws \Exception
* @inheritDoc
*/
public function GetHyperlink($aExtraParams)
{
@@ -1128,8 +1124,7 @@ class NewObjectMenuNode extends MenuNode
}
/**
* @param WebPage $oPage
* @param string[] $aExtraParams
* @inheritDoc
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
@@ -1167,8 +1162,7 @@ class DashboardMenuNode extends MenuNode
}
/**
* @param string[] $aExtraParams
* @return string
* @inheritDoc
*/
public function GetHyperlink($aExtraParams)
{
@@ -1187,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())
{
@@ -1198,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>');
@@ -1284,8 +1277,7 @@ class DashboardMenuNode extends MenuNode
class ShortcutContainerMenuNode extends MenuNode
{
/**
* @param string[] $aExtraParams
* @return string
* @inheritDoc
*/
public function GetHyperlink($aExtraParams)
{
@@ -1293,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
*/
@@ -1356,9 +1347,7 @@ class ShortcutMenuNode extends MenuNode
}
/**
* @param string[] $aExtraParams
* @return string
* @throws CoreException
* @inheritDoc
*/
public function GetHyperlink($aExtraParams)
{
@@ -1376,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())
{
@@ -1388,8 +1375,9 @@ class ShortcutMenuNode extends MenuNode
}
/**
* @return string
* @throws CoreException
* @inheritDoc
*
* @throws \Exception
*/
public function GetTitle()
{
@@ -1397,8 +1385,9 @@ class ShortcutMenuNode extends MenuNode
}
/**
* @return string
* @throws CoreException
* @inheritDoc
*
* @throws \Exception
*/
public function GetLabel()
{

View File

@@ -1,27 +1,20 @@
<?php
// Copyright (C) 2010-2016 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/>
/**
* Class NiceWebPage
* Copyright (C) 2013-2020 Combodo SARL
*
* @copyright Copyright (C) 2010-2016 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
*/
require_once(APPROOT."/application/webpage.class.inc.php");
@@ -46,8 +39,7 @@ class NiceWebPage extends WebPage
{
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate.prod.min.js');
}
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/ui-lightness/jquery-ui-1.11.4.custom.css');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-ui-1.11.4.custom.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-ui-1.11.4.custom.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/utils.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/hovertip.js');
// table sorting
@@ -124,7 +116,7 @@ class NiceWebPage extends WebPage
$("table.listResults").tableHover(); // hover tables
EOF
);
$this->add_saas("css/light-grey.scss");
$this->LoadTheme();
$this->m_sRootUrl = $this->GetAbsoluteUrlAppRoot();
$sAbsURLAppRoot = addslashes($this->m_sRootUrl);
@@ -257,6 +249,14 @@ EOF
}
parent::output();
}
}
?>
/**
* @throws \Exception
* @since 2.7.0
*/
protected function LoadTheme()
{
$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);
if (strpos($err['message'], 'Allowed memory size of') !== false)
{
$sLimit = ini_get('memory_limit');

View File

@@ -1,27 +1,20 @@
<?php
// Copyright (C) 2010-2017 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/>
/**
* Class DisplayTemplate
* Copyright (C) 2013-2020 Combodo SARL
*
* @copyright Copyright (C) 2010-2017 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
*/
require_once(APPROOT.'/application/displayblock.class.inc.php');
@@ -191,7 +184,7 @@ class DisplayTemplate
break;
case 'itoptab':
$oPage->SetCurrentTab(Dict::S(str_replace('_', ' ', $aAttributes['name'])));
$oPage->SetCurrentTab($aAttributes['name'], str_replace('_', ' ', $aAttributes['name']));
$oTemplate = new DisplayTemplate($sContent);
$oTemplate->Render($oPage, array()); // no params to apply, they have already been applied
//$oPage->p('iTop Tab Content:<pre>'.htmlentities($sContent, ENT_QUOTES, 'UTF-8').'</pre>');

View File

@@ -0,0 +1,194 @@
<?php
/**
* Copyright (C) 2013-2020 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
/**
* Class ThemeHandler
*
* @author Stephen Abello <stephen.abello@combodo.com>
* @since 2.7.0
*/
class ThemeHandler
{
/**
* 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 GetCurrentThemeUrl()
{
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';
}
/**
* Compile the $sThemeId theme
*
* @param string $sThemeId
* @param array|null $aThemeParameters Parameters (variables, imports, stylesheets) for the theme, if not passed, will be retrieved from compiled DM
* @param array|null $aImportsPaths Paths where imports can be found. Must end with '/'
* @param string|null $sWorkingPath Path of the folder used during compilation. Must end with a '/'
*
* @throws \CoreException
*/
public static function CompileTheme($sThemeId, $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null)
{
// Default working path
if($sWorkingPath === null)
{
$sWorkingPath = APPROOT.'env-'.utils::GetCurrentEnvironment().'/';
}
// Default import paths (env-*)
if($aImportsPaths === null)
{
$aImportsPaths = array(
APPROOT.'env-'.utils::GetCurrentEnvironment().'/',
);
}
// Note: We do NOT check that the folder exists!
$sThemeFolderPath = $sWorkingPath.'/branding/themes/'.$sThemeId.'/';
$sThemeCssPath = $sThemeFolderPath.'main.css';
// 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 (typically when switching current theme in the config. file)
else
{
$aThemeParameters = json_decode(@file_get_contents($sThemeFolderPath.'theme-parameters.json'), true);
if ($aThemeParameters === null)
{
throw new CoreException('Could not load "'.$sThemeId.'" theme parameters from file, check that it has been compiled correctly');
}
}
$sTmpThemeScssContent = '';
$iStyleLastModified = 0;
clearstatcache();
// Loading files to import and stylesheet to compile, also getting most recent modification time on overall files
foreach ($aThemeParameters['imports'] as $sImport)
{
$sTmpThemeScssContent .= '@import "'.$sImport.'";'."\n";
$iImportLastModified = @filemtime($sWorkingPath.$sImport);
$iStyleLastModified = $iStyleLastModified < $iImportLastModified ? $iImportLastModified : $iStyleLastModified;
}
foreach ($aThemeParameters['stylesheets'] as $sStylesheet)
{
$sTmpThemeScssContent .= '@import "'.$sStylesheet.'";'."\n";
$iStylesheetLastModified = @filemtime($sWorkingPath.$sStylesheet);
$iStyleLastModified = $iStyleLastModified < $iStylesheetLastModified ? $iStylesheetLastModified : $iStyleLastModified;
}
// Checking if our compiled css is outdated
if (!file_exists($sThemeCssPath) || (is_writable($sThemeFolderPath) && (@filemtime($sThemeCssPath) < $iStyleLastModified)))
{
$sTmpThemeCssContent = utils::CompileCSSFromSASS($sTmpThemeScssContent, $aImportsPaths, $aThemeParameters['variables']);
file_put_contents($sThemeCssPath, $sTmpThemeCssContent);
}
}
}

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)

View File

@@ -122,5 +122,19 @@ class TwigExtension
$oConfig = MetaModel::GetConfig();
return $oConfig->Get($sParamName);
}));
// Function to get the URL of a static page in a module
// Usage in twig: {{ get_static_page_module_url('itop-my-module', 'path-to-my-page') }}
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_static_page_module_url', function($sModuleName, $sPage)
{
return utils::GetAbsoluteUrlModulesRoot().$sModuleName.'/'.$sPage;
}));
// Function to get the URL of a php page in a module
// Usage in twig: {{ get_page_module_url('itop-my-module', 'path-to-my-my-page.php') }}
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_page_module_url', function($sModuleName, $sPage)
{
return utils::GetAbsoluteUrlModulePage($sModuleName, $sPage);
}));
}
}
}

View File

@@ -1,23 +1,28 @@
<?php
// Copyright (C) 2010-2017 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
*/
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
/**
* Class UIExtKeyWidget
* UI wdiget for displaying and editing external keys when
* UI widget for displaying and editing external keys when
* A simple drop-down list is not enough...
*
* The layout is the following
@@ -54,13 +59,7 @@
* | | +--------+ +-----+ | |
* | +--------------------------------------------+ |
* +------------------------------------------------+
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
class UIExtKeyWidget
{
const ENUM_OUTPUT_FORMAT_CSV = 'csv';
@@ -274,7 +273,7 @@ EOF
// the input for the auto-complete
$sHTMLValue .= "<input class=\"field_autocomplete\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif?t=".utils::GetCacheBusterTimestamp()."\" onClick=\"oACWidget_{$this->iId}.Search();\"/></span>";
$sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_search_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Search();\"><i class=\"fas fa-search\"></i></div></span>";
// another hidden input to store & pass the object's Id
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
@@ -298,7 +297,7 @@ EOF
}
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false)
{
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif?t=".utils::GetCacheBusterTimestamp()."\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/></span>";
$sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_tree_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"><i class=\"fas fa-sitemap\"></i></div></span>";
$oPage->add_ready_script(
<<<EOF
if ($('#ac_tree_{$this->iId}').length == 0)
@@ -312,7 +311,7 @@ EOF
{
$sCallbackName = (MetaModel::IsAbstract($this->sTargetClass)) ? 'SelectObjectClass' : 'CreateObject';
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif?t=".utils::GetCacheBusterTimestamp()."\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\"/></span>";
$sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_add_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\"><i class=\"fas fa-plus\"></i></div></span>";
$oPage->add_ready_script(
<<<EOF
if ($('#ajax_{$this->iId}').length == 0)
@@ -578,8 +577,21 @@ EOF
$oNewObj->UpdateObjectFromArg('default');
$sDialogTitle = '';
$oPage->add('<div id="ac_create_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div id="dcr_'.$this->iId.'">');
$oPage->add("<h1>".MetaModel::GetClassIcon($this->sTargetClass)."&nbsp;".Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sTargetClass))."</h1>\n");
$sClassLabel = MetaModel::GetName($this->sTargetClass);
$sClassIcon = MetaModel::GetClassIcon($this->sTargetClass);
$sObjClass = get_class($oNewObj);
$sObjKey = $oNewObj->GetKey();
$sHeaderTitle = Dict::Format('UI:CreationTitle_Class', $sClassLabel);
$oPage->add(<<<HTML
<div id="ac_create_{$this->iId}">
<!-- Beginning of object-details -->
<div class="object-details" data-object-class="$sObjClass" data-object-id="$sObjKey" data-object-mode="create">
<!-- Beginning of wizContainer -->
<div class="wizContainer" style="vertical-align:top;">
<div id="dcr_{$this->iId}">
<h1>$sClassIcon&nbsp;$sHeaderTitle</h1>
HTML
);
$aFieldsFlags = array();
$aFieldsComments = array();
foreach(MetaModel::ListAttributeDefs($this->sTargetClass) as $sAttCode => $oAttDef)
@@ -591,7 +603,13 @@ EOF
}
}
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), array('formPrefix' => $this->iId, 'noRelations' => true, 'fieldsFlags' => $aFieldsFlags, 'fieldsComments' => $aFieldsComments));
$oPage->add('</div></div></div>');
$oPage->add(<<<HTML
</div>
</div><!-- End of wizContainer -->
</div><!-- End of object-details -->
</div>
HTML
);
// $oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: $(window).width()*0.8, height: 'auto', autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
$oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
$oPage->add_ready_script("$('#dcr_{$this->iId} form').removeAttr('onsubmit');");
@@ -618,15 +636,22 @@ EOF
$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");
}
@@ -655,6 +680,18 @@ EOF
}
}
/**
* @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();
@@ -683,6 +720,9 @@ EOF
{
$this->DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue);
}
$bHasOnlyRootNodes = (count($aTree) === 1);
return !$bHasOnlyRootNodes;
}
function DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue)
@@ -710,7 +750,7 @@ EOF
$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

@@ -346,7 +346,7 @@ EOF
* @throws \CoreException
* @throws \DictExceptionMissingString
*/
public function Display(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
public function Display(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj)
{
$sHtmlValue = '';
$sHtmlValue .= "<div id=\"linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix}\">\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

@@ -1,6 +1,6 @@
<?php
/**
* Copyright (C) 2013-2019 Combodo SARL
* Copyright (C) 2013-2020 Combodo SARL
*
* This file is part of iTop.
*
@@ -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')
@@ -591,9 +610,11 @@ class utils
* Format a value into a more friendly format (KB, MB, GB, TB) instead a juste a Bytes amount.
*
* @param float $value
* @param int $iPrecision
*
* @return string
*/
public static function BytesToFriendlyFormat($value)
public static function BytesToFriendlyFormat($value, $iPrecision = 0)
{
$sReturn = '';
// Kilobytes
@@ -601,6 +622,9 @@ class utils
{
$sReturn = 'K';
$value = $value / 1024;
if ($iPrecision === 0) {
$iPrecision = 1;
}
}
// Megabytes
if ($value >= 1024)
@@ -621,16 +645,18 @@ class utils
$value = $value / 1024;
}
$value = round($value, 1);
$value = round($value, $iPrecision);
return $value . '' . $sReturn . 'B';
}
/**
* Helper function to convert a string to a date, given a format specification. It replaces strtotime which does not allow for specifying a date in a french format (for instance)
* Example: StringToTime('01/05/11 12:03:45', '%d/%m/%y %H:%i:%s')
* Helper function to convert a string to a date, given a format specification. It replaces strtotime which does not allow for
* specifying a date in a french format (for instance) Example: StringToTime('01/05/11 12:03:45', '%d/%m/%y %H:%i:%s')
*
* @param string $sDate
* @param string $sFormat
*
* @return string|false false if the input format is not correct, timestamp otherwise
*/
public static function StringToTime($sDate, $sFormat)
@@ -790,13 +816,14 @@ class utils
return $sUrl;
}
/**
* Builds an root url from the server's variables.
* 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.
*
* @return string
*
* @throws \Exception
/**
* Builds an root url from the server's variables.
* 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.
*
* @return string
*
* @throws \Exception
*/
public static function GetDefaultUrlAppRoot()
{
@@ -838,10 +865,25 @@ class utils
$sAbsoluteUrl = "$sProtocol://{$sServerName}{$sPort}{$sPath}";
$sCurrentScript = realpath($_SERVER['SCRIPT_FILENAME']);
$sAppRoot = realpath(APPROOT);
return self::GetAppRootUrl($sCurrentScript, $sAppRoot, $sAbsoluteUrl);
}
/**
* @param $sCurrentScript
* @param $sAppRoot
* @param $sAbsoluteUrl
*
* @return false|string
* @throws \Exception
*/
public static function GetAppRootUrl($sCurrentScript, $sAppRoot, $sAbsoluteUrl)
{
$sCurrentScript = str_replace('\\', '/', $sCurrentScript); // canonical path
$sAppRoot = str_replace('\\', '/', APPROOT); // canonical path
$sCurrentRelativePath = str_replace($sAppRoot, '', $sCurrentScript);
$sAppRoot = str_replace('\\', '/', $sAppRoot).'/'; // canonical path with the trailing '/' appended
$sCurrentRelativePath = str_ireplace($sAppRoot, '', $sCurrentScript);
$sAppRootPos = strpos($sAbsoluteUrl, $sCurrentRelativePath);
if ($sAppRootPos !== false)
{
@@ -850,7 +892,7 @@ class utils
else
{
// Second attempt without index.php at the end...
$sCurrentRelativePath = str_replace('index.php', '', $sCurrentRelativePath);
$sCurrentRelativePath = str_ireplace('index.php', '', $sCurrentRelativePath);
$sAppRootPos = strpos($sAbsoluteUrl, $sCurrentRelativePath);
if ($sAppRootPos !== false)
{
@@ -860,8 +902,9 @@ class utils
{
// No luck...
throw new Exception("Failed to determine application root path $sAbsoluteUrl ($sCurrentRelativePath) APPROOT:'$sAppRoot'");
}
}
}
return $sAppRootUrl;
}
@@ -1144,16 +1187,17 @@ class utils
break;
default:
// Unknown type of menu, do nothing
$aResult = array();
// Unknown type of menu, do nothing
$aResult = array();
}
foreach($aResult as $oMenuItem)
foreach ($aResult as $oMenuItem)
{
$aActions[$oMenuItem->GetUID()] = $oMenuItem->GetMenuItem();
}
// Invoke the plugins
//
/** @var \iPopupMenuExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iPopupMenuExtension') as $oExtensionInstance)
{
if (is_object($param) && !($param instanceof DBObject))
@@ -1480,6 +1524,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');
}
/**
@@ -1541,18 +1596,40 @@ class utils
clearstatcache();
if (!file_exists($sCssPath) || (is_writable($sCssPath) && (filemtime($sCssPath) < filemtime($sSassPath))))
{
$oScss = new Compiler();
$oScss->setImportPaths($aImportPaths);
$oScss->setFormatter('ScssPhp\\ScssPhp\\Formatter\\Expanded');
// Temporary disabling max exec time while compiling
$iCurrentMaxExecTime = (int) ini_get('max_execution_time');
set_time_limit(0);
$sCss = $oScss->compile(file_get_contents($sSassPath));
set_time_limit($iCurrentMaxExecTime);
$sCss = static::CompileCSSFromSASS(file_get_contents($sSassPath), $aImportPaths);
file_put_contents($sCssPath, $sCss);
}
return $sCssRelPath;
}
/**
* Return a string of CSS compiled from the $sSassContent
*
* @param string $sSassContent
* @param array $aImportPaths
* @param array $aVariables
*
* @return string
*
* @since 2.7.0
*/
public static function CompileCSSFromSASS($sSassContent, $aImportPaths = array(), $aVariables = array())
{
$oSass = new Compiler();
$oSass->setFormatter('ScssPhp\\ScssPhp\\Formatter\\Expanded');
// Setting our variables
$oSass->setVariables($aVariables);
// Setting our imports paths
$oSass->setImportPaths($aImportPaths);
// Temporary disabling max exec time while compiling
$iCurrentMaxExecTime = (int) ini_get('max_execution_time');
set_time_limit(0);
// Compiling SASS
$sCss = $oSass->compile($sSassContent);
set_time_limit($iCurrentMaxExecTime);
return $sCss;
}
public static function GetImageSize($sImageData)
{
@@ -1890,17 +1967,17 @@ class utils
'doc' => 'application/msword',
'dot' => 'application/msword',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'vsd' => 'application/x-visio',
'vdx' => 'application/visio.drawing',
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'zip' => 'application/zip',
'txt' => 'text/plain',
'htm' => 'text/html',
'html' => 'text/html',
'exe' => 'application/octet-stream'
'ppt' => 'application/vnd.ms-powerpoint',
'vsd' => 'application/x-visio',
'vdx' => 'application/visio.drawing',
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'zip' => 'application/zip',
'txt' => 'text/plain',
'htm' => 'text/html',
'html' => 'text/html',
'exe' => 'application/octet-stream',
);
$sData = null;
@@ -2012,6 +2089,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.
*
@@ -2084,6 +2196,11 @@ class utils
*/
final public static function StartsWith($haystack, $needle)
{
if (strlen($needle) > strlen($haystack))
{
return false;
}
return substr_compare($haystack, $needle, 0, strlen($needle)) === 0;
}
@@ -2095,6 +2212,11 @@ class utils
* @return bool
*/
final public static function EndsWith($haystack, $needle) {
if (strlen($needle) > strlen($haystack))
{
return false;
}
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
}
@@ -2102,13 +2224,13 @@ 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/'
*
* @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
* @since 2.6.5 2.7.0 N°2538
*/
final public static function RealPath($sPath, $sBasePath)
{
@@ -2131,14 +2253,14 @@ class utils
* Returns the local path relative to the iTop installation of an existing file
* Dir separator is changed to '/' for consistency among the different OS
*
* @param string $sPath absolute path
* @param string $sAbsolutePath absolute path
*
* @return false|string
*/
final public static function LocalPath($sPath)
final public static function LocalPath($sAbsolutePath)
{
$sRootPath = realpath(APPROOT);
$sFullPath = realpath($sPath);
$sFullPath = realpath($sAbsolutePath);
if (($sFullPath === false) || !self::StartsWith($sFullPath, $sRootPath))
{
return false;
@@ -2165,4 +2287,54 @@ class utils
}
return $sFullPath;
}
public static function GetAbsoluteModulePath($sModule)
{
return APPROOT.'env-'.utils::GetCurrentEnvironment().'/'.$sModule.'/';
}
public static function GetCurrentUserName()
{
if (function_exists('posix_getpwuid'))
{
return posix_getpwuid(posix_geteuid())['name'];
}
return getenv('username');
}
/**
* Transform a snake_case $sInput into a CamelCase string
*
* @since 2.7.0
* @param string $sInput
*
* @return string
*/
public static function ToCamelCase($sInput)
{
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;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -273,10 +273,6 @@ class WizardHelper
static public function FromJSON($sJSON)
{
$oWizHelper = new WizardHelper();
if (get_magic_quotes_gpc())
{
$sJSON = stripslashes($sJSON);
}
$aData = json_decode($sJSON, true); // true means hash array instead of object
$oWizHelper->m_aData = $aData;
return $oWizHelper;

View File

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

View File

@@ -43,9 +43,12 @@ class XMLPage extends WebPage
$this->m_bPassThrough = $bPassThrough;
$this->m_bHeaderSent = false;
$this->add_header("Content-type: text/xml; charset=".self::PAGES_CHARSET);
$this->add_header("Cache-control: no-cache");
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
$this->add_header('Pragma: no-cache');
$this->add_header('Expires: 0');
$this->add_header('X-Frame-Options: deny');
$this->add_header("Content-location: export.xml");
}
}
public function output()
{

View File

@@ -2,67 +2,5 @@
define('APPROOT', dirname(__FILE__).'/');
define('APPCONF', APPROOT.'conf/');
define('ITOP_DEFAULT_ENV', 'production');
define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
if (function_exists('microtime'))
{
$fItopStarted = microtime(true);
}
else
{
$fItopStarted = 1000 * time();
}
//
// Maintenance mode
//
// Use 'maintenance' parameter to bypass maintenance mode
if (!isset($bBypassMaintenance))
{
$bBypassMaintenance = isset($_REQUEST['maintenance']) ? boolval($_REQUEST['maintenance']) : false;
}
if (file_exists(MAINTENANCE_MODE_FILE) && !$bBypassMaintenance)
{
$sMessage = 'This application is currently under maintenance.';
$sTitle = 'Maintenance';
http_response_code(503);
// Display message depending on the request
include(APPROOT.'application/maintenancemsg.php');
switch (true)
{
case isset($_SERVER['REQUEST_URI']) && EndsWith($_SERVER['REQUEST_URI'], '/pages/ajax.searchform.php'):
_MaintenanceHtmlMessage($sMessage);
break;
case array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER):
case isset($_SERVER['REQUEST_URI']) && EndsWith($_SERVER['REQUEST_URI'], '/webservices/soapserver.php'):
case isset($_SERVER['REQUEST_URI']) && EndsWith($_SERVER['REQUEST_URI'], '/webservices/rest.php'):
_MaintenanceTextMessage($sMessage);
break;
case isset($_SERVER['CONTENT_TYPE']) && ($_SERVER['CONTENT_TYPE'] == 'application/json'):
_MaintenanceJsonMessage($sTitle, $sMessage);
break;
default:
_MaintenanceSetupPageMessage($sTitle, $sMessage);
break;
}
exit();
}
/**
* helper to test if a string ends with another
* @param $haystack
* @param $needle
*
* @return bool
*/
function EndsWith($haystack, $needle) {
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
}
require_once APPROOT.'bootstrap.inc.php';

View File

@@ -18,7 +18,76 @@
* You should have received a copy of the GNU Affero General Public License
*/
require_once __DIR__.'/approot.inc.php';
require_once APPROOT.'/lib/autoload.php';
define('ITOP_DEFAULT_ENV', 'production');
define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
define('READONLY_MODE_FILE', APPROOT.'data/.readonly');
// Require here files containing PHP instructions
if (function_exists('microtime'))
{
$fItopStarted = microtime(true);
}
else
{
$fItopStarted = 1000 * time();
}
if (! isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false)
{
require_once APPROOT.'/lib/autoload.php';
}
//
// Maintenance mode
//
// Use 'maintenance' parameter to bypass maintenance mode
if (!isset($bBypassMaintenance))
{
$bBypassMaintenance = isset($_REQUEST['maintenance']) ? boolval($_REQUEST['maintenance']) : false;
}
if (file_exists(MAINTENANCE_MODE_FILE) && !$bBypassMaintenance)
{
$sTitle = 'Maintenance';
$sMessage = 'This application is currently under maintenance.';
http_response_code(503);
// Display message depending on the request
include(APPROOT.'application/maintenancemsg.php');
$sSAPIName = strtoupper(trim(php_sapi_name()));
switch (true)
{
case isset($_SERVER['REQUEST_URI']) && EndsWith($_SERVER['REQUEST_URI'], '/pages/ajax.searchform.php'):
_MaintenanceHtmlMessage($sMessage);
break;
case $sSAPIName == 'CLI':
case array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER):
case isset($_SERVER['REQUEST_URI']) && (strpos($_SERVER['REQUEST_URI'], '/webservices/soapserver.php') !== false):
case isset($_SERVER['REQUEST_URI']) && (strpos($_SERVER['REQUEST_URI'], '/webservices/export-v2.php') !== false):
_MaintenanceTextMessage($sMessage);
break;
case isset($_SERVER['REQUEST_URI']) && (strpos($_SERVER['REQUEST_URI'], '/webservices/rest.php') !== false):
case isset($_SERVER['CONTENT_TYPE']) && ($_SERVER['CONTENT_TYPE'] == 'application/json'):
_MaintenanceJsonMessage($sTitle, $sMessage);
break;
default:
_MaintenanceSetupPageMessage($sTitle, $sMessage);
break;
}
exit();
}
/**
* helper to test if a string ends with another
* @param $haystack
* @param $needle
*
* @return bool
*/
function EndsWith($haystack, $needle) {
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
}

View File

@@ -3,26 +3,25 @@
"license": "AGPLv3",
"require": {
"php": ">=5.6.0",
"ext-soap": "*",
"ext-ctype": "*",
"ext-dom": "*",
"ext-gd": "*",
"ext-iconv": "*",
"ext-json": "*",
"ext-mysqli": "*",
"ext-dom": "*",
"ext-iconv": "*",
"ext-gd": "*",
"ext-ctype": "*",
"scssphp/scssphp": "1.0.0",
"swiftmailer/swiftmailer": "5.4.9",
"ext-soap": "*",
"combodo/tcpdf": "6.3.5",
"nikic/php-parser": "^3.1",
"pear/archive_tar": "1.4.10",
"pelago/emogrifier": "2.1.0",
"combodo/tcpdf": "6.3.0",
"pear/archive_tar": "1.4.7",
"scssphp/scssphp": "1.0.6",
"swiftmailer/swiftmailer": "5.4.12",
"symfony/console": "3.4.*",
"symfony/dotenv": "3.4.*",
"symfony/framework-bundle": "3.4.*",
"symfony/polyfill-php70": "1.*",
"symfony/twig-bundle": "3.4.*",
"symfony/yaml": "3.4.*",
"symfony/polyfill-php70": "1.*"
"symfony/yaml": "3.4.*"
},
"require-dev": {
"symfony/stopwatch": "3.4.*",
@@ -44,12 +43,15 @@
"preferred-install": {
"*": "dist"
},
"sort-packages": true
"sort-packages": true,
"classmap-authoritative": true
},
"autoload": {
"classmap": [
"core",
"application"
"application",
"sources/application",
"sources/Composer"
],
"exclude-from-classmap": [
"core/dbobjectsearch.class.php",
@@ -73,5 +75,10 @@
"allow-contrib": false,
"require": "3.4.*"
}
},
"scripts": {
"post-install-cmd": ["@rmDeniedTestDir"],
"post-update-cmd": ["@rmDeniedTestDir"],
"rmDeniedTestDir": "@php .make/composer/rmDeniedTestDir.php"
}
}
}

626
composer.lock generated

File diff suppressed because it is too large Load Diff

2
composer.readme Normal file
View File

@@ -0,0 +1,2 @@
To regenerate the autoload, run:
composer dump-autoload -a

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

@@ -457,7 +457,7 @@ class Str
public static function gpc2pure($gpc)
{
if (ini_get('magic_quotes_sybase')) $pure = str_replace("''", "'", $gpc);
else $pure = get_magic_quotes_gpc() ? stripslashes($gpc) : $gpc;
else $pure = $gpc;
return $pure;
}
public static function html2pure($html)

File diff suppressed because it is too large Load Diff

View File

@@ -33,6 +33,7 @@ MetaModel::IncludeModule('core/tagsetfield.class.inc.php');
MetaModel::IncludeModule('synchro/synchrodatasource.class.inc.php');
MetaModel::IncludeModule('core/backgroundtask.class.inc.php');
MetaModel::IncludeModule('core/inlineimage.class.inc.php');
MetaModel::IncludeModule('core/counter.class.inc.php');
MetaModel::IncludeModule('webservices/webservices.basic.php');

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
{
/**
@@ -56,8 +50,9 @@ interface iBackgroundProcess extends iProcess
* interface iScheduledProcess
* A variant of process that must be called at specific times
*
* @copyright Copyright (C) 2013 Combodo SARL
* @see \AbstractWeeklyScheduledProcess for a bootstrap implementation
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2013 Combodo SARL
*/
interface iScheduledProcess extends iProcess
{
@@ -67,22 +62,226 @@ interface iScheduledProcess extends iProcess
public function GetNextOccurrence();
}
/**
* Class ProcessException
* Exception for iProcess implementations.<br>
* Implementation of {@link iScheduledProcess}, using config parameters for module
*
* Use these parameters :
*
* * enabled
* * week_days
* * time
*
* 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 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 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
*
* @return string
*/
abstract protected function GetModuleName();
/**
* @return string default value for {@link MODULE_SETTING_TIME} config param.
* example '23:30'
*/
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
*
* @returns int[] (monday = 1)
* @throws ProcessInvalidConfigException
*/
public function InterpretWeekDays()
{
static $aWEEKDAYTON = array(
'monday' => 1,
'tuesday' => 2,
'wednesday' => 3,
'thursday' => 4,
'friday' => 5,
'saturday' => 6,
'sunday' => 7,
);
$aDays = array();
$sWeekDays = $this->getOConfig()->GetModuleSetting(
$this->GetModuleName(),
static::MODULE_SETTING_WEEKDAYS,
static::DEFAULT_MODULE_SETTING_WEEKDAYS
);
if ($sWeekDays !== '')
{
$aWeekDaysRaw = explode(',', $sWeekDays);
foreach ($aWeekDaysRaw as $sWeekDay)
{
$sWeekDay = strtolower(trim($sWeekDay));
if (array_key_exists($sWeekDay, $aWEEKDAYTON))
{
$aDays[] = $aWEEKDAYTON[$sWeekDay];
}
else
{
throw new ProcessInvalidConfigException($this->GetModuleName().": wrong format for setting '".static::MODULE_SETTING_WEEKDAYS."' (found '$sWeekDay')");
}
}
}
if (count($aDays) === 0)
{
throw new ProcessInvalidConfigException($this->GetModuleName().': missing setting \''.static::MODULE_SETTING_WEEKDAYS.'\'');
}
$aDays = array_unique($aDays);
sort($aDays);
return $aDays;
}
/**
* @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 the exact time at which the process must be run next time
* @throws \ProcessInvalidConfigException
*/
public function GetNextOccurrence($sCurrentTime = 'now')
{
$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', $timezone);
}
// 1st - Interpret the list of days as ordered numbers (monday = 1)
//
$aDays = $this->InterpretWeekDays();
// 2nd - Find the next active week day
//
$sProcessTime = $this->getOConfig()->GetModuleSetting(
$this->GetModuleName(),
static::MODULE_SETTING_TIME,
static::GetDefaultModuleSettingTime()
);
if (!preg_match('/[0-2]\d:[0-5]\d/', $sProcessTime))
{
throw new ProcessInvalidConfigException($this->GetModuleName().": wrong format for setting '".static::MODULE_SETTING_TIME."' (found '$sProcessTime')");
}
$oNow = new DateTime($sCurrentTime, $timezone);
$iNextPos = false;
$sDay = $oNow->format('N');
for ($iDay = (int) $sDay; $iDay <= 7; $iDay++)
{
$iNextPos = array_search($iDay, $aDays, true);
if ($iNextPos !== false)
{
if (($iDay > $oNow->format('N')) || ($oNow->format('H:i') < $sProcessTime))
{
break;
}
$iNextPos = false; // necessary on sundays
}
}
// 3rd - Compute the result
//
if ($iNextPos === false)
{
// Jump to the first day within the next week
$iFirstDayOfWeek = $aDays[0];
$iDayMove = $oNow->format('N') - $iFirstDayOfWeek;
$oRet = clone $oNow;
$oRet->modify('-'.$iDayMove.' days');
$oRet->modify('+1 weeks');
}
else
{
$iNextDayOfWeek = $aDays[$iNextPos];
$iMove = $iNextDayOfWeek - $oNow->format('N');
$oRet = clone $oNow;
$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;
}
/**
* @see \iProcess
*
* @param int $iUnixTimeLimit
*
* @return string
*/
abstract public function Process($iUnixTimeLimit);
}
/**
* 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 PR #89
*/
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]));
}
}
}
@@ -654,7 +655,7 @@ class BulkChange
return $aResults;
}
protected function CreateObject(&$aResult, $iRow, $aRowData, CMDBChange $oChange = null)
{
$oTargetObj = MetaModel::NewObject($this->m_sClass);
@@ -726,25 +727,38 @@ class BulkChange
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-MissingExtKey', $sMissingKeys));
return $oTargetObj;
}
// Optionaly record the results
// Optionally record the results
//
if ($oChange)
{
$newID = $oTargetObj->DBInsertTrackedNoReload($oChange);
$aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj();
$aResult[$iRow]["finalclass"] = get_class($oTargetObj);
$aResult[$iRow]["id"] = new CellStatus_Void($newID);
$newID = $oTargetObj->DBInsert();
}
else
{
$aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj();
$aResult[$iRow]["finalclass"] = get_class($oTargetObj);
$aResult[$iRow]["id"] = new CellStatus_Void(0);
$newID = 0;
}
$aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj();
$aResult[$iRow]["finalclass"] = get_class($oTargetObj);
$aResult[$iRow]["id"] = new CellStatus_Void($newID);
return $oTargetObj;
}
/**
* @param array $aResult
* @param int $iRow
* @param \CMDBObject $oTargetObj
* @param array $aRowData
* @param \CMDBChange $oChange
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
protected function UpdateObject(&$aResult, $iRow, $oTargetObj, $aRowData, CMDBChange $oChange = null)
{
$aResult[$iRow] = $this->PrepareObject($oTargetObj, $aRowData, $aErrors);
@@ -772,7 +786,7 @@ class BulkChange
{
try
{
$oTargetObj->DBUpdateTracked($oChange);
$oTargetObj->DBUpdate();
}
catch(CoreException $e)
{
@@ -786,6 +800,14 @@ class BulkChange
}
}
/**
* @param array $aResult
* @param int $iRow
* @param \CMDBObject $oTargetObj
* @param \CMDBChange $oChange
*
* @throws \BulkChangeException
*/
protected function UpdateMissingObject(&$aResult, $iRow, $oTargetObj, CMDBChange $oChange = null)
{
$aResult[$iRow] = $this->PrepareMissingObject($oTargetObj, $aErrors);
@@ -813,7 +835,7 @@ class BulkChange
{
try
{
$oTargetObj->DBUpdateTracked($oChange);
$oTargetObj->DBUpdate();
}
catch(CoreException $e)
{
@@ -829,6 +851,11 @@ class BulkChange
public function Process(CMDBChange $oChange = null)
{
if ($oChange)
{
CMDBObject::SetCurrentChange($oChange);
}
// Note: $oChange can be null, in which case the aim is to check what would be done
// Debug...
@@ -898,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'));
}
}
}
@@ -1056,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)
@@ -1070,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

@@ -1,20 +1,21 @@
<?php
// Copyright (C) 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-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
*/
define('EXPORTER_DEFAULT_CHUNK_SIZE', 1000);
@@ -109,7 +110,7 @@ class BulkExportResultGC implements iBackgroundProcess
{
// Next one ?
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array(), null, 1 /* limit count */);
$oSet->OptimizeColumnLoad(array('temp_file_path'));
$oSet->OptimizeColumnLoad(array('BulkExportResult' => array('temp_file_path')));
$oResult = $oSet->Fetch();
if (is_null($oResult))
{
@@ -170,6 +171,7 @@ abstract class BulkExport
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->isSubclassOf('BulkExport') && !$oRefClass->isAbstract())
{
/** @var BulkExport $oBulkExporter */
$oBulkExporter = new $sPHPClass();
if ($oBulkExporter->IsFormatSupported($sFormatCode, $oSearch))
{
@@ -189,7 +191,7 @@ abstract class BulkExport
*
* @param int $iPersistentToken The identifier of the BulkExportResult object storing the information
*
* @return iBulkExport|null
* @return BulkExport|null
* @throws ArchivedObjectException
* @throws CoreException
* @throws ReflectionException
@@ -343,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();
@@ -418,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");
@@ -513,7 +538,23 @@ abstract class CMDBObject extends DBObject
}
}
/**
* @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.
*
* @param \CMDBChange $oChange
* @param null $bSkipStrongSecurity
*
* @return int|null
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
* @throws \MySQLException
* @throws \OQLException
* @throws \SecurityException
*/
public function DBInsertTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
@@ -521,7 +562,24 @@ abstract class CMDBObject extends DBObject
$ret = $this->DBInsertTracked_Internal();
return $ret;
}
/**
* @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.
*
* @param \CMDBChange $oChange
* @param null $bSkipStrongSecurity
*
* @return int
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
* @throws \MySQLException
* @throws \OQLException
* @throws \SecurityException
*/
public function DBInsertTrackedNoReload(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
@@ -529,13 +587,20 @@ abstract class CMDBObject extends DBObject
$ret = $this->DBInsertTracked_Internal(true);
return $ret;
}
/**
* To Be Obsoleted: DO NOT rely on an overload of this method since
* DBInsertTracked (resp. DBInsertTrackedNoReload) may call directly
* DBInsert (resp. DBInsertNoReload) in future versions of iTop.
* @deprecated 2.7.0 N°2361 simply use {@link DBInsert} or {@link DBInsertNoReload} instead
*
* @param bool $bDoNotReload
*
* @return integer Identifier of the created object
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \CoreWarning
* @throws \MySQLException
* @throws \OQLException
*/
protected function DBInsertTracked_Internal($bDoNotReload = false)
{
@@ -582,6 +647,18 @@ abstract class CMDBObject extends DBObject
return $ret;
}
/**
* @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.
*
* @param \CMDBChange $oChange
* @param null $bSkipStrongSecurity
*
* @return int|void
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \SecurityException
*/
public function DBUpdateTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
@@ -593,13 +670,38 @@ abstract class CMDBObject extends DBObject
* @param null $oDeletionPlan
*
* @return \DeletionPlan|null
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DeleteException
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
public function DBDelete(&$oDeletionPlan = null)
{
return $this->DBDeleteTracked_Internal($oDeletionPlan);
}
/**
* @deprecated 2.7.0 N°2361 simply use {@link DBDelete} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@link CMDBObject::SetCurrentChange} before.
*
* @param \CMDBChange $oChange
* @param null $bSkipStrongSecurity
* @param null $oDeletionPlan
*
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DeleteException
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @throws \SecurityException
*/
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
{
self::SetCurrentChange($oChange);
@@ -611,11 +713,17 @@ abstract class CMDBObject extends DBObject
* @param null $oDeletionPlan
*
* @return \DeletionPlan|null
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DeleteException
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
{
$prevkey = $this->GetKey();
$ret = parent::DBDelete($oDeletionPlan);
return $ret;
}

View File

@@ -64,7 +64,7 @@ class MySQLException extends CoreException
/**
* Class MySQLQueryHasNoResultException
*
* @since 2.5
* @since 2.5.0
*/
class MySQLQueryHasNoResultException extends MySQLException
{
@@ -74,7 +74,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,19 +119,30 @@ 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;
@@ -576,11 +587,6 @@ class CMDBSource
return $aRes;
}
// Stripslashes
if (get_magic_quotes_gpc())
{
$value = stripslashes($value);
}
// Quote if not a number or a numeric string
if ($bAlways || is_string($value))
{
@@ -589,6 +595,24 @@ class CMDBSource
return $value;
}
/**
* MariaDB returns "'value'" for enum, while MySQL returns "value" (without the surrounding single quotes)
*
* @param string $sValue
*
* @return string without the surrounding quotes
* @since 2.7.0 N°2490
*/
private static function RemoveSurroundingQuotes($sValue)
{
if (utils::StartsWith($sValue, '\'') && utils::EndsWith($sValue, '\''))
{
$sValue = substr($sValue, 1, -1);
}
return $sValue;
}
/**
* @param string $sSQLQuery
*
@@ -648,6 +672,7 @@ class CMDBSource
}
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);
@@ -661,13 +686,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, 'DeadLock', $e->getMessage());
}
/**
* If nested transaction, we are not starting a new one : only one global transaction will exist.
*
@@ -793,6 +860,9 @@ class CMDBSource
}
/**
*
* @deprecated 2.7.0 N°1627 use ItopCounter instead
*
* @param string $sTable
*
* @return int
@@ -1110,6 +1180,83 @@ class CMDBSource
return false;
}
/**
* 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'"
*
* We still 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
* @param string $sDbFieldType
*
* @return bool true if same type and options (case sensitive comparison only for type options), false otherwise
* @since 2.7.0 N°2490
*/
public static function IsSameFieldTypes($sItopGeneratedFieldType, $sDbFieldType)
{
list($sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
list($sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sDbFieldType);
if (strcasecmp($sItopFieldDataType, $sDbFieldDataType) !== 0)
{
return false;
}
if (strcmp($sItopFieldTypeOptions, $sDbFieldTypeOptions) !== 0)
{
// case sensitive comp as we need to check case for enum possible values for example
return false;
}
// remove the default value NULL added by MariadDB
$sMariaDbDefaultNull = ' DEFAULT \'NULL\'';
if (utils::EndsWith($sDbFieldOtherOptions, $sMariaDbDefaultNull))
{
$sDbFieldOtherOptions = substr($sDbFieldOtherOptions, 0, -strlen($sMariaDbDefaultNull));
}
// remove quotes around default values (always present in MariaDB)
$sDbFieldOtherOptions = preg_replace_callback(
'/( DEFAULT )\'([^\']+)\'/',
function ($aMatches) use ($sItopFieldDataType) {
// ENUM default values should keep quotes, but all other numeric values don't have quotes
if (is_numeric($aMatches[2]) && ($sItopFieldDataType !== 'ENUM'))
{
return $aMatches[1].$aMatches[2];
}
return $aMatches[0];
},
$sDbFieldOtherOptions);
if (strcasecmp($sItopFieldOtherOptions, $sDbFieldOtherOptions) !== 0)
{
return false;
}
return true;
}
/**
* @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'
*/
private static function GetFieldDataTypeAndOptions($sCompleteFieldType)
{
preg_match('/^([a-zA-Z]+)(\(([^\)]+)\))?( .+)?/', $sCompleteFieldType, $aMatches);
$sDataType = isset($aMatches[1]) ? $aMatches[1] : '';
$sTypeOptions = isset($aMatches[2]) ? $aMatches[3] : '';
$sOtherOptions = isset($aMatches[4]) ? $aMatches[4] : '';
return array($sDataType, $sTypeOptions, $sOtherOptions);
}
/**
* @param string $sTable
* @param string $sField
@@ -1161,7 +1308,8 @@ class CMDBSource
}
elseif (is_string($aFieldData["Default"]) == 'string')
{
$sRet .= ' DEFAULT '.self::Quote($aFieldData["Default"]);
$sDefaultValue = static::RemoveSurroundingQuotes($aFieldData["Default"]);
$sRet .= ' DEFAULT '.self::Quote($sDefaultValue);
}
return $sRet;
@@ -1296,7 +1444,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)
@@ -1446,7 +1594,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,9 +22,10 @@
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);
define('ACCESS_USER_WRITE', 1);
define('ACCESS_ADMIN_WRITE', 2);
@@ -70,14 +71,14 @@ define('DEFAULT_ALLOWED_LOGIN_TYPES', 'form|external|basic');
define('DEFAULT_EXT_AUTH_VARIABLE', '$_SERVER[\'REMOTE_USER\']');
define('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random generated key later (if possible)
define('DEFAULT_ENCRYPTION_LIB', 'Mcrypt'); // We'll define the best encryption available later
/**
* Config
* configuration data (this class cannot not be localized, because it is responsible for loading the dictionaries)
*
* @package iTopORM
*
* @see \MetaModel::GetConfig() to get the config, if the metamodel was already loaded
* @see utils::GetConfig() to load config from the current env, if metamodel is not loaded
* @package iTopORM
*/
class Config
{
@@ -89,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 !
@@ -99,6 +109,14 @@ class Config
* @since 2.7.0 export_pdf_font param
*/
protected $m_aSettings = array(
'log_level_min' => array(
'type' => 'array',
'description' => 'Optional min log level per channel',
'default' => '',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'app_env_label' => array(
'type' => 'string',
'description' => 'Label displayed to describe the current application environment, defaults to the environment name (e.g. "production")',
@@ -343,12 +361,12 @@ 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
// Standard PDF fonts like helvetica or times newroman are NOT Unicode
// A new DroidSansFallback can be used to improve CJK support (se PR #49)
// Standard PDF fonts like helvetica or times newroman are NOT Unicode
// A new DroidSansFallback can be used to improve CJK support (se PR #49)
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
@@ -387,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,
@@ -588,7 +606,7 @@ class Config
'Asia/Istanbul',
'Asia/Singapore',
'Africa/Casablanca',
'Australia/Sydney'
'Australia/Sydney',
),
'default' => 'Europe/Paris',
'value' => 'Europe/Paris',
@@ -831,46 +849,46 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'email_validation_pattern' => array(
'type' => 'string',
'description' => '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,}",
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'email_decoration_class' => array(
'type' => 'string',
'description' => 'CSS class(es) to use as decoration for the HTML rendering of the attribute. eg. "fas fa-envelope" will put a mail icon.',
'default' => 'fas fa-envelope',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'phone_number_validation_pattern' => array(
'type' => 'string',
'description' => 'Regular expression to validate/detect the format of a phone number',
'default' => "[0-9.\-\ \+\(\)]+",
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'phone_number_url_pattern' => array(
'type' => 'string',
'description' => 'Format for phone number url, use %1$s as a placeholder for the value. eg. "tel:%1$s" for regular phone applications or "callto:%1$s" for Skype. Default is "tel:%1$s".',
'default' => 'tel:%1$s',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'phone_number_decoration_class' => array(
'type' => 'string',
'description' => 'CSS class(es) to use as decoration for the HTML rendering of the attribute. eg. "fas fa-phone" will put a phone icon.',
'default' => 'fas fa-phone',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'email_validation_pattern' => array(
'type' => 'string',
'description' => '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,}",
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'email_decoration_class' => array(
'type' => 'string',
'description' => 'CSS class(es) to use as decoration for the HTML rendering of the attribute. eg. "fas fa-envelope" will put a mail icon.',
'default' => 'fas fa-envelope',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'phone_number_validation_pattern' => array(
'type' => 'string',
'description' => 'Regular expression to validate/detect the format of a phone number',
'default' => "[0-9.\-\ \+\(\)]+",
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'phone_number_url_pattern' => array(
'type' => 'string',
'description' => 'Format for phone number url, use %1$s as a placeholder for the value. eg. "tel:%1$s" for regular phone applications or "callto:%1$s" for Skype. Default is "tel:%1$s".',
'default' => 'tel:%1$s',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'phone_number_decoration_class' => array(
'type' => 'string',
'description' => 'CSS class(es) to use as decoration for the HTML rendering of the attribute. eg. "fas fa-phone" will put a phone icon.',
'default' => 'fas fa-phone',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'log_kpi_duration' => array(
'type' => 'integer',
'description' => 'Level of logging for troubleshooting performance issues (1 to enable, 2 +blame callers) new: add "log_kpi_slow_queries" to limit the stats',
@@ -922,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)',
@@ -1232,8 +1241,18 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'backoffice_default_theme' => array(
'type' => 'string',
'description' => 'Default theme used for '.ITOP_APPLICATION_SHORT.'\'s console',
'default' => 'light-grey',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
);
public function IsProperty($sPropCode)
{
return (array_key_exists($sPropCode, $this->m_aSettings));
@@ -1262,12 +1281,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')
public function Set($sPropCode, $value, $sSourceDesc = 'unknown', $bCanOverride = false)
{
$sType = $this->m_aSettings[$sPropCode]['type'];
$value = $this->oConfigPlaceholdersResolver->Resolve($value);
switch ($sType)
{
case 'bool':
@@ -1287,19 +1310,27 @@ 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;
}
/**
* @param string $sPropCode
*
* @return mixed
*/
public function Get($sPropCode)
{
return $this->m_aSettings[$sPropCode]['value'];
}
/**
* @param string $sPropCode
*
* @return mixed
*/
public function Get($sPropCode)
{
return $this->m_aSettings[$sPropCode]['value'];
}
/**
* Event log options (see LOG_... definition)
@@ -1371,17 +1402,19 @@ class Config
*/
protected $m_aCharsets;
/**
* Config constructor.
*
* @param string|null $sConfigFile
* @param bool $bLoadConfig
*
* @throws \ConfigException
* @throws \CoreException
*/
public function __construct($sConfigFile = null, $bLoadConfig = true)
/**
* Config constructor.
*
* @param string|null $sConfigFile
* @param bool $bLoadConfig
*
* @throws \ConfigException
* @throws \CoreException
*/
public function __construct($sConfigFile = null, $bLoadConfig = true)
{
$this->oConfigPlaceholdersResolver = new ConfigPlaceholdersResolver();
$this->m_sFile = $sConfigFile;
if (is_null($sConfigFile))
{
@@ -1416,7 +1449,7 @@ class Config
//define default encryption params according to php install
$aEncryptParams = SimpleCrypt::GetNewDefaultParams();
$this->m_sEncryptionLibrary = isset($aEncryptParams['lib']) ? $aEncryptParams['lib'] : DEFAULT_ENCRYPTION_LIB;
$this->m_sEncryptionKey= isset($aEncryptParams['key']) ? $aEncryptParams['key'] : DEFAULT_ENCRYPTION_KEY;
$this->m_sEncryptionKey = isset($aEncryptParams['key']) ? $aEncryptParams['key'] : DEFAULT_ENCRYPTION_KEY;
$this->m_aModuleSettings = array();
@@ -1442,13 +1475,13 @@ class Config
*/
}
/**
* @param string $sPurpose
* @param string $sFileName
*
* @throws \ConfigException
*/
protected function CheckFile($sPurpose, $sFileName)
/**
* @param string $sPurpose
* @param string $sFileName
*
* @throws \ConfigException
*/
protected function CheckFile($sPurpose, $sFileName)
{
if (!file_exists($sFileName))
{
@@ -1491,7 +1524,7 @@ class Config
$sNoise = trim(ob_get_contents());
ob_end_clean();
}
catch(Error $e)
catch (Error $e)
{
// PHP 7
throw new ConfigException('Error in configuration file',
@@ -1542,10 +1575,15 @@ class Config
{
$value = $rawvalue;
}
$this->Set($sPropCode, $value, $sConfigFile);
$this->Set($sPropCode, $value, $sConfigFile, true);
}
}
if (file_exists(READONLY_MODE_FILE))
{
$this->Set('access_mode', ACCESS_READONLY, READONLY_MODE_FILE);
}
$this->m_bLogGlobal = isset($MySettings['log_global']) ? (bool)trim($MySettings['log_global']) : DEFAULT_LOG_GLOBAL;
$this->m_bLogNotification = isset($MySettings['log_notification']) ? (bool)trim($MySettings['log_notification']) : DEFAULT_LOG_NOTIFICATION;
$this->m_bLogIssue = isset($MySettings['log_issue']) ? (bool)trim($MySettings['log_issue']) : DEFAULT_LOG_ISSUE;
@@ -1575,12 +1613,14 @@ class Config
}
/**
* @param string $sModule
* @see \MetaModel::GetModuleParameter()
*
* @param string $sProperty
* @param mixed $defaultvalue
*
* @param string $sModule
*
* @return mixed|null if present, value defined in the configuration file, if not module parameter from XML
* @see \MetaModel::GetModuleParameter()
*/
public function GetModuleSetting($sModule, $sProperty, $defaultvalue = null)
{
@@ -1593,16 +1633,18 @@ class Config
return $this->GetModuleParameter($sModule, $sProperty, $defaultvalue);
}
/**
* @param string $sModule
* @param string $sProperty
* @param mixed $defaultvalue
*
* @return mixed|null parameter value defined in the XML
* @see \MetaModel::GetModuleSetting() to get from the configuration file first
* @link https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Axml_reference#modules_parameters
*/
public function GetModuleParameter($sModule, $sProperty, $defaultvalue = null)
/**
* @see \MetaModel::GetModuleSetting() to get from the configuration file first
*
* @param string $sProperty
* @param mixed $defaultvalue
*
* @param string $sModule
*
* @return mixed|null parameter value defined in the XML
* @link https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Axml_reference#modules_parameters
*/
public function GetModuleParameter($sModule, $sProperty, $defaultvalue = null)
{
$ret = $defaultvalue;
if (class_exists('ModulesXMLParameters'))
@@ -1848,22 +1890,46 @@ class Config
return $aSettings;
}
/**
* Write the configuration to a file (php format) that can be reloaded later
* By default write to the same file that was specified when constructing the object
*
* @param string $sFileName string Name of the file to write to (emtpy to write to the same file)
*
* @return boolean True otherwise throws an Exception
/**
* Write the configuration to a file (php format) that can be reloaded later
* By default write to the same file that was specified when constructing the object
*
* @throws \ConfigException
*/
* @param string $sFileName string Name of the file to write to (emtpy to write to the same file)
*
* @return boolean True otherwise throws an Exception
*
* @throws \ConfigException
*/
public function WriteToFile($sFileName = '')
{
if (empty($sFileName))
{
$sFileName = $this->m_sFile;
}
$oHandle = null;
$sConfig = null;
if (is_file($this->m_sFile))
{
$oHandle = fopen($this->m_sFile, 'r');
$index = 0;
while (!flock($oHandle, LOCK_SH))
{
if ($index > 50)
{
throw new ConfigException("Could not read to configuration file", array('file' => $this->m_sFile));
}
usleep(100000);
$index++;
}
$sConfig = file_get_contents($this->m_sFile);
}
$this->oItopConfigParser = new iTopConfigParser($sConfig);
if ($oHandle !==null)
{
flock($oHandle, LOCK_UN);
}
$hFile = @fopen($sFileName, 'w');
if ($hFile !== false)
{
@@ -1937,30 +2003,28 @@ class Config
// Write all values that are either always visible or present in the cloned config file
if ($aSettingInfo['show_in_conf_sample'] || (!empty($aSettingInfo['source_of_value']) && ($aSettingInfo['source_of_value'] != 'unknown')))
{
$sType = $aSettingInfo['type'];
switch ($sType)
{
case 'bool':
$sSeenAs = $aSettingInfo['value'] ? 'true' : 'false';
break;
default:
$sSeenAs = self::PrettyVarExport($aSettingInfo['value'], "\t");
}
fwrite($hFile, "\n");
if (isset($aSettingInfo['description']))
{
fwrite($hFile, "\t// $sPropCode: {$aSettingInfo['description']}\n");
}
if (isset($aSettingInfo['default']))
{
$default = $aSettingInfo['default'];
if ($aSettingInfo['type'] == 'bool')
{
$default = $default ? 'true' : 'false';
}
fwrite($hFile,
"\t//\tdefault: ".self::PrettyVarExport($aSettingInfo['default'], "\t//\t\t", true)."\n");
$sComment = self::PrettyVarExport(null,$aSettingInfo['default'], "\t//\t\t", true);
fwrite($hFile,"\t//\tdefault: {$sComment}\n");
}
if (isset($this->m_aCanOverrideSettings[$sPropCode]) && $this->m_aCanOverrideSettings[$sPropCode])
{
$aParserValue = $this->oItopConfigParser->GetVarValue('MySettings', $sPropCode);
}
else
{
$aParserValue = null;
}
$sSeenAs = self::PrettyVarExport($aParserValue,$aSettingInfo['value'], "\t");
fwrite($hFile, "\t'$sPropCode' => $sSeenAs,\n");
}
}
@@ -1974,7 +2038,7 @@ class Config
fwrite($hFile, "\t'$sModule' => array (\n");
foreach ($aProperties as $sProperty => $value)
{
$sNiceExport = self::PrettyVarExport($value, "\t\t");
$sNiceExport = self::PrettyVarExport($this->oItopConfigParser->GetVarValue('MyModuleSettings', $sProperty), $value, "\t\t");
fwrite($hFile, "\t\t'$sProperty' => $sNiceExport,\n");
}
fwrite($hFile, "\t),\n");
@@ -1987,16 +2051,28 @@ class Config
fwrite($hFile, " *\n");
fwrite($hFile, " */\n");
fwrite($hFile, "\$MyModules = array(\n");
fwrite($hFile, "\t'addons' => array (\n");
foreach ($this->m_aAddons as $sKey => $sFile)
$aParserValue = $this->oItopConfigParser->GetVarValue('MyModules', 'addons');
if ($aParserValue['found'])
{
fwrite($hFile, "\t\t'$sKey' => '$sFile',\n");
fwrite($hFile, "\t'addons' => {$aParserValue['value']},\n");
}
else
{
fwrite($hFile, "\t'addons' => array (\n");
foreach ($this->m_aAddons as $sKey => $sFile)
{
fwrite($hFile, "\t\t'$sKey' => '$sFile',\n");
}
fwrite($hFile, "\t),\n");
}
fwrite($hFile, "\t),\n");
fwrite($hFile, ");\n");
fwrite($hFile, '?'.'>'); // Avoid perturbing the syntax highlighting !
return fclose($hFile);
$bReturn = fclose($hFile);
utils::SetConfig($this);
return $bReturn;
}
else
{
@@ -2004,16 +2080,16 @@ class Config
}
}
/**
* Helper function to initialize a configuration from the page arguments
*
* @param array $aParamValues
* @param string|null $sModulesDir
* @param bool $bPreserveModuleSettings
*
* @throws \Exception
* @throws \CoreException
*/
/**
* Helper function to initialize a configuration from the page arguments
*
* @param array $aParamValues
* @param string|null $sModulesDir
* @param bool $bPreserveModuleSettings
*
* @throws \Exception
* @throws \CoreException
*/
public function UpdateFromParams($aParamValues, $sModulesDir = null, $bPreserveModuleSettings = false)
{
if (isset($aParamValues['application_path']))
@@ -2048,7 +2124,7 @@ class Config
$this->Set('db_name', $sDBName);
$this->Set('db_subname', $aParamValues['db_prefix']);
$bDbTlsEnabled = (bool) $aParamValues['db_tls_enabled'];
$bDbTlsEnabled = (bool)$aParamValues['db_tls_enabled'];
if ($bDbTlsEnabled)
{
$this->Set('db_tls.enabled', $bDbTlsEnabled, 'UpdateFromParams');
@@ -2059,9 +2135,12 @@ class Config
$this->Set('db_tls.enabled', $bDbTlsEnabled, null);
}
$sDbTlsCa = $bDbTlsEnabled ? $aParamValues['db_tls_ca'] : null;
if (isset($sDbTlsCa) && !empty($sDbTlsCa)) {
if (isset($sDbTlsCa) && !empty($sDbTlsCa))
{
$this->Set('db_tls.ca', $sDbTlsCa, 'UpdateFromParams');
} else {
}
else
{
// empty parameter : we don't want it in the file
$this->Set('db_tls.ca', null, null);
}
@@ -2145,13 +2224,13 @@ class Config
$this->SetAddOns($aAddOns);
}
/**
* Helper: for an array of string, change the prefix when found
*
* @param array $aStrings
* @param string $sSearchPrefix
* @param string $sNewPrefix
*/
/**
* Helper: for an array of string, change the prefix when found
*
* @param array $aStrings
* @param string $sSearchPrefix
* @param string $sNewPrefix
*/
protected static function ChangePrefix(&$aStrings, $sSearchPrefix, $sNewPrefix)
{
foreach ($aStrings as &$sFile)
@@ -2163,13 +2242,13 @@ class Config
}
}
/**
* Obsolete: kept only for backward compatibility of the Toolkit
* Quick and dirty way to clone a config file into another environment
*
* @param string $sSourceEnv
* @param string $sTargetEnv
*/
/**
* Obsolete: kept only for backward compatibility of the Toolkit
* Quick and dirty way to clone a config file into another environment
*
* @param string $sSourceEnv
* @param string $sTargetEnv
*/
public function ChangeModulesPath($sSourceEnv, $sTargetEnv)
{
// Now does nothing since the includes are built into the environment itself
@@ -2178,6 +2257,7 @@ class Config
/**
* Pretty format a var_export'ed value so that (if possible) the identation is preserved on every line
*
* @param array $aParserValue
* @param mixed $value The value to export
* @param string $sIndentation The string to use to indent the text
* @param bool $bForceIndentation Forces the identation (enven if it breaks/changes an eval, for example to ouput a
@@ -2185,8 +2265,13 @@ class Config
*
* @return string The indented export string
*/
protected static function PrettyVarExport($value, $sIndentation, $bForceIndentation = false)
protected static function PrettyVarExport($aParserValue, $value, $sIndentation, $bForceIndentation = false)
{
if (is_array($aParserValue) && $aParserValue['found'])
{
return $aParserValue['value'];
}
$sExport = var_export($value, true);
$sNiceExport = str_replace(array("\r\n", "\n", "\r"), "\n".$sIndentation, trim($sExport));
if (!$bForceIndentation)
@@ -2206,3 +2291,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

@@ -19,14 +19,14 @@
/**
* Simple helper class for keeping track of the context inside the call stack
*
*
* To check (anywhere in the code) if a particular context tag is present
* in the call stack simply do:
*
*
* if (ContextTag::Check(<the_tag>)) ...
*
*
* For example to know if the code is being executed in the context of a portal do:
*
*
* if (ContextTag::Check('GUI:Portal'))
*
* @copyright Copyright (C) 2016-2017 Combodo SARL
@@ -35,8 +35,15 @@
class ContextTag
{
const TAG_PORTAL = 'GUI:Portal';
const TAG_CRON = 'CRON';
const TAG_CONSOLE = 'GUI:Console';
const TAG_SETUP = 'Setup';
const TAG_SYNCHRO = 'Synchro';
const TAG_REST = 'REST/JSON';
protected static $aStack = array();
/**
* Store a context tag on the stack
* @param string $sTag
@@ -46,6 +53,11 @@ class ContextTag
static::$aStack[] = $sTag;
}
public static function AddContext($sTag)
{
static::$aStack[] = $sTag;
}
/**
* Cleanup the context stack
*/
@@ -53,7 +65,7 @@ class ContextTag
{
array_pop(static::$aStack);
}
/**
* Check if a given tag is present in the stack
* @param string $sTag
@@ -63,13 +75,53 @@ class ContextTag
{
return in_array($sTag, static::$aStack);
}
/**
* Get the whole stack as an array
* @return hash
* @return array
*/
public static function GetStack()
{
return static::$aStack;
}
}
/**
* Get all the predefined context tags
* @return array
*/
public static function GetTags()
{
$aRawTags = array(
ContextTag::TAG_REST,
ContextTag::TAG_SYNCHRO,
ContextTag::TAG_SETUP,
ContextTag::TAG_CONSOLE,
ContextTag::TAG_CRON,
ContextTag::TAG_PORTAL);
$aTags = array();
foreach ($aRawTags as $sRawTag)
{
$aTags[$sRawTag] = Dict::S("Core:Context={$sRawTag}");
}
$aPortalsConf = PortalDispatcherData::GetData();
$aDispatchers = array();
foreach ($aPortalsConf as $sPortalId => $aConf)
{
$sHandlerClass = $aConf['handler'];
$aDispatchers[$sPortalId] = new $sHandlerClass($sPortalId);
}
foreach ($aDispatchers as $sPortalId => $oDispatcher)
{
if ($sPortalId != 'backoffice')
{
$aTags['Portal:'.$sPortalId] = $oDispatcher->GetLabel();
}
}
return $aTags;
}
}

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
{
@@ -183,6 +201,30 @@ class CoreCannotSaveObjectException extends CoreException
}
}
/**
* @since 2.7.0 N°2555
*/
class CorePortalInvalidActionRuleException extends CoreException
{
}
/**
* @since 2.7.0 N°2555
*/
class CoreOqlException extends CoreException
{
}
/**
* @since 2.7.0 N°2555
*/
class CoreOqlMultipleResultsForbiddenException extends CoreOqlException
{
}
class CoreWarning extends CoreException
{
}
@@ -213,3 +255,13 @@ class ArchivedObjectException extends CoreException
class InvalidConfigParamException extends CoreException
{
}
/**
* Throwned when the password is not valid
*
* @since 2.7.0
*/
class InvalidPasswordAttributeOneWayPassword extends CoreException
{
}

251
core/counter.class.inc.php Normal file
View File

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

View File

@@ -1,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
*
@@ -1912,7 +1916,7 @@ abstract class DBObject implements iDisplay
return "Bad type";
}
elseif ($oAtt instanceof AttributeClassAttCodeSet)
elseif (($oAtt instanceof AttributeClassAttCodeSet) || ($oAtt instanceof AttributeEnumSet))
{
if (is_string($toCheck))
{
@@ -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()
@@ -2003,6 +2007,8 @@ abstract class DBObject implements iDisplay
continue;
}
// No iTopMutex so there might be concurrent access !
// But the necessary lock would have a high performance cost :(
$bHasDuplicates = $this->HasObjectsInDbForUniquenessRule($sUniquenessRuleId, $aUniquenessRuleProperties);
if ($bHasDuplicates)
{
@@ -2034,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)
{
@@ -2086,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)
@@ -2235,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
*
@@ -2373,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()
{
@@ -2388,12 +2396,35 @@ abstract class DBObject implements iDisplay
{
return $this->ListChangedValues($this->m_aCurrValues);
}
else
{
return $this->m_aCurrValues;
}
return $this->m_aCurrValues;
}
/**
* @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 ListPreviousValuesForUpdatedAttributes()
{
if (empty($this->m_aPreviousValuesForUpdatedAttributes))
{
return array();
}
return $this->m_aPreviousValuesForUpdatedAttributes;
}
/**
* Whether or not an object was modified since last read from the DB
* (ie: does it differ from the DB ?)
@@ -2640,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
@@ -2735,6 +2766,11 @@ abstract class DBObject implements iDisplay
$this->DBInsertSingleTable($sParentClass);
}
$this->OnObjectKeyReady();
$this->DBWriteLinks();
$this->WriteExternalAttributes();
if ($bIsTransactionEnabled)
{
CMDBSource::Query('COMMIT');
@@ -2749,11 +2785,6 @@ abstract class DBObject implements iDisplay
throw $e;
}
$this->OnObjectKeyReady();
$this->DBWriteLinks();
$this->WriteExternalAttributes();
$this->m_bIsInDB = true;
$this->m_bDirty = false;
foreach ($this->m_aCurrValues as $sAttCode => $value)
@@ -2774,7 +2805,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();
@@ -2889,9 +2927,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
*
@@ -2925,16 +2963,13 @@ abstract class DBObject implements iDisplay
/**
* @internal
*
* @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
*
* @return int|null
* @throws ArchivedObjectException
* @throws CoreCannotSaveObjectException
* @throws CoreException
* @throws CoreUnexpectedValue
* @throws CoreWarning
* @throws MySQLException
* @throws OQLException
*/
public function DBInsertTracked(CMDBChange $oChange)
{
@@ -2945,6 +2980,9 @@ abstract class DBObject implements iDisplay
/**
* @internal
*
* @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
*
* @return int
@@ -3005,13 +3043,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()
{
@@ -3028,7 +3067,8 @@ abstract class DBObject implements iDisplay
}
$aUpdateReentrance[$sKey] = true;
$this->m_aChanges = array(); // reset attribute to avoid stack collisions
$this->InitPreviousValuesForUpdatedAttributes();
try
{
$this->DoComputeValues();
@@ -3084,7 +3124,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);
}
}
$bHasANewExternalKeyValue = false;
@@ -3181,7 +3228,9 @@ abstract class DBObject implements iDisplay
$this->DBWriteLinks();
$this->WriteExternalAttributes();
$this->m_aChanges = $this->ListChanges(); // N°2293 save changes for use in user callbacks
// 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();
@@ -3285,10 +3334,37 @@ 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 {@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
*
* @return int
@@ -3371,7 +3447,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
@@ -3558,9 +3641,12 @@ abstract class DBObject implements iDisplay
/**
* @internal
*
* @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 null $bSkipStrongSecurity
* @param null $oDeletionPlan
* @param boolean $bSkipStrongSecurity
* @param \DeletionPlan $oDeletionPlan
*
* @throws ArchivedObjectException
* @throws CoreCannotSaveObjectException
@@ -3636,20 +3722,36 @@ abstract class DBObject implements iDisplay
*/
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
return true;
// 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
@@ -3668,11 +3770,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);
}
@@ -3680,7 +3782,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)
{
@@ -3716,14 +3818,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)
{
@@ -3756,14 +3856,35 @@ 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);
}
}
$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())
{
/** @var \Trigger $oTrigger */
$oTrigger->DoActivate($this->ToArgs('this'));
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)
{
$this->m_aCurrValues[$sAttCode] = $aBackupValues[$sAttCode];
}
}
@@ -3791,7 +3912,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;
}
@@ -4108,11 +4254,15 @@ abstract class DBObject implements iDisplay
}
/**
* This method is called after the object is updated into DB. You can get changes by calling {@link ListChanges}.
*
* @overwritable-hook You can extend this method in order to provide your own logic.
*
* @since 2.7.0 N°2293 can access object changes by calling {@link ListChanges}
* 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()
{
@@ -5145,10 +5295,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,9 +359,10 @@ 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();
}
protected function RenameParam($sOldName, $sNewName)
public function RenameParam($sOldName, $sNewName)
{
$this->m_oSearchCondition->RenameParam($sOldName, $sNewName);
foreach($this->m_aPointingTo as $sExtKeyAttCode=>$aPointingTo)
@@ -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 instanceof AttributePassword) 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)
@@ -699,6 +741,37 @@ class DBObjectSearch extends DBSearch
}
}
/**
* Rename aliases of nested queries to avoid duplicates with main query
*
* @param array $aClassAliases array of alias => class for the main query
* @param array $aAliasTranslation translation table of main query to apply to nested queries
*/
public function RenameNestedQueriesAliasesInNameSpace($aClassAliases, $aAliasTranslation = array())
{
$this->GetCriteria()->Browse(function ($oNode) use ($aClassAliases, $aAliasTranslation) {
if ($oNode instanceof NestedQueryExpression)
{
$oNestedQuery = $oNode->GetNestedQuery();
$oNestedQuery->RenameAliasesInNameSpace($aClassAliases, $aAliasTranslation);
}
});
}
public function RenameAliasesInNameSpace($aClassAliases, $aAliasTranslation = array())
{
// Recurse in nested queries
$this->RenameNestedQueriesAliasesInNameSpace($aClassAliases, $aAliasTranslation);
$this->AddToNameSpace($aClassAliases, $aAliasTranslation);
$this->TranslateConditions($aAliasTranslation, false, false);
}
public function TranslateConditions($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
{
$oExpression = $this->GetCriteria()->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
$this->ResetCondition();
$this->AddConditionExpression($oExpression);
}
// Browse the tree nodes recursively
//
@@ -750,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)
{
@@ -761,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;
}
}
}
@@ -817,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
@@ -873,6 +963,15 @@ class DBObjectSearch extends DBSearch
return $res;
}
/**
* @param \DBObjectSearch $oFilter
* @param string $sExtKeyAttCode
* @param array $aClassAliases
* @param array $aAliasTranslation
* @param int $iOperatorCode
*
* @throws \CoreException
*/
protected function AddCondition_PointingTo_InNameSpace(DBObjectSearch $oFilter, $sExtKeyAttCode, &$aClassAliases, &$aAliasTranslation, $iOperatorCode)
{
// Find the node on which the new tree must be attached (most of the time it is "this")
@@ -883,9 +982,10 @@ class DBObjectSearch extends DBSearch
{
foreach ($oReceivingFilter->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode] as $oExisting)
{
/** @var DBObjectSearch $oExisting */
if ($oExisting->GetClass() == $oFilter->GetClass())
{
$oExisting->MergeWith_InNamespace($oFilter, $oExisting->m_aClasses, $aAliasTranslation);
$oExisting->MergeWith_InNamespace($oFilter, $aClassAliases, $aAliasTranslation);
$bMerged = true;
break;
}
@@ -899,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
@@ -953,7 +1053,7 @@ class DBObjectSearch extends DBSearch
$this->RecomputeClassList($this->m_aClasses);
}
protected function AddCondition_ReferencedBy_InNameSpace(DBSearch $oFilter, $sForeignExtKeyAttCode, &$aClassAliases, &$aAliasTranslation, $iOperatorCode)
protected function AddCondition_ReferencedBy_InNameSpace(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, &$aClassAliases, &$aAliasTranslation, $iOperatorCode)
{
$sForeignClass = $oFilter->GetClass();
@@ -967,7 +1067,7 @@ class DBObjectSearch extends DBSearch
{
if ($oExisting->GetClass() == $oFilter->GetClass())
{
$oExisting->MergeWith_InNamespace($oFilter, $oExisting->m_aClasses, $aAliasTranslation);
$oExisting->MergeWith_InNamespace($oFilter, $aClassAliases, $aAliasTranslation);
$bMerged = true;
break;
}
@@ -980,10 +1080,114 @@ class DBObjectSearch extends DBSearch
}
}
/**
* Filter this search with another search.
* Initial search is unmodified.
* The difference with Intersect, is that an alias can be provided,
* the filtered class does not need to be the first joined class.
*
* @param string $sClassAlias class being filtered
* @param DBSearch $oFilter Filter to apply
*
* @return DBSearch The filtered search
* @throws \CoreException
*/
public function Filter($sClassAlias, DBSearch $oFilter)
{
// If the conditions are the correct ones for Intersect
if (MetaModel::IsParentClass($oFilter->GetFirstJoinedClass(),$this->GetFirstJoinedClass()))
{
return $this->Intersect($oFilter);
}
/** @var \DBObjectSearch $oFilteredSearch */
$oFilteredSearch = $this->DeepClone();
$oFilterExpression = self::FilterSubClass($oFilteredSearch, $sClassAlias, $oFilter, $this->m_aClasses);
if ($oFilterExpression === false)
{
throw new CoreException("Limitation: cannot filter search");
}
$oFilteredSearch->AddConditionExpression($oFilterExpression);
return $oFilteredSearch;
}
/**
* Filter "in place" the search (filtered part is replaced in the initial search)
*
* @param DBObjectSearch $oSearch Search to filter, modified with the given filter
* @param string $sClassAlias class to filter
* @param \DBSearch $oFilter Filter to apply
*
* @return \Expression|false
* @throws \CoreException
*/
private static function FilterSubClass(DBObjectSearch &$oSearch, $sClassAlias, DBSearch $oFilter, $aRootClasses)
{
if (($oSearch->GetFirstJoinedClassAlias() == $sClassAlias))
{
$oSearch = $oSearch->IntersectSubClass($oFilter, $aRootClasses);
return $oSearch->GetCriteria();
}
/** @var Expression $oFilterExpression */
// Search in the filter tree where is the correct DBSearch
foreach ($oSearch->m_aPointingTo as $sExtKey => $aPointingTo)
{
foreach ($aPointingTo as $iOperatorCode => $aFilters)
{
foreach ($aFilters as $index => $oExtFilter)
{
$oFilterExpression = self::FilterSubClass($oExtFilter, $sClassAlias, $oFilter, $aRootClasses);
if ($oFilterExpression !== false)
{
$oSearch->m_aPointingTo[$sExtKey][$iOperatorCode][$index] = $oExtFilter;
return $oFilterExpression;
}
}
}
}
foreach($oSearch->m_aReferencedBy as $sForeignClass => $aReferences)
{
foreach($aReferences as $sForeignExtKeyAttCode => $aFiltersByOperator)
{
foreach ($aFiltersByOperator as $iOperatorCode => $aFilters)
{
foreach ($aFilters as $index => $oForeignFilter)
{
$oFilterExpression = self::FilterSubClass($oForeignFilter, $sClassAlias, $oFilter, $aRootClasses);
if ($oFilterExpression !== false)
{
$oSearch->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode][$iOperatorCode][$index] = $oForeignFilter;
return $oFilterExpression;
}
}
}
}
}
return false;
}
/**
* @inheritDoc
* @throws \CoreException
*/
public function Intersect(DBSearch $oFilter)
{
return $this->IntersectSubClass($oFilter, $this->m_aClasses);
}
/**
* @param \DBSearch $oFilter
* @param array $aRootClasses classes of the root search (for aliases)
*
* @return \DBUnionSearch|mixed
* @throws \CoreException
*/
protected function IntersectSubClass(DBSearch $oFilter, $aRootClasses)
{
if ($oFilter instanceof DBUnionSearch)
{
@@ -999,15 +1203,12 @@ class DBObjectSearch extends DBSearch
foreach ($aFilters as $oRightFilter)
{
// Limitation: the queried class must be the first declared class
if ($this->GetFirstJoinedClassAlias() != $this->GetClassAlias())
{
throw new CoreException("Limitation: cannot merge two queries if the queried class ({$this->GetClass()} AS {$this->GetClassAlias()}) is not the first joined class ({$this->GetFirstJoinedClass()} AS {$this->GetFirstJoinedClassAlias()})");
}
if ($oRightFilter->GetFirstJoinedClassAlias() != $oRightFilter->GetClassAlias())
{
throw new CoreException("Limitation: cannot merge two queries if the queried class ({$oRightFilter->GetClass()} AS {$oRightFilter->GetClassAlias()}) is not the first joined class ({$oRightFilter->GetFirstJoinedClass()} AS {$oRightFilter->GetFirstJoinedClassAlias()})");
}
/** @var \DBObjectSearch $oLeftFilter */
$oLeftFilter = $this->DeepClone();
$oRightFilter = $oRightFilter->DeepClone();
@@ -1017,14 +1218,14 @@ class DBObjectSearch extends DBSearch
$oLeftFilter->AllowAllData();
}
if ($oLeftFilter->GetClass() != $oRightFilter->GetClass())
if ($oLeftFilter->GetFirstJoinedClass() != $oRightFilter->GetClass())
{
if (MetaModel::IsParentClass($oLeftFilter->GetClass(), $oRightFilter->GetClass()))
if (MetaModel::IsParentClass($oLeftFilter->GetFirstJoinedClass(), $oRightFilter->GetClass()))
{
// Specialize $oLeftFilter
$oLeftFilter->ChangeClass($oRightFilter->GetClass());
$oLeftFilter->ChangeClass($oRightFilter->GetClass(), $oLeftFilter->GetFirstJoinedClassAlias());
}
elseif (MetaModel::IsParentClass($oRightFilter->GetClass(), $oLeftFilter->GetClass()))
elseif (MetaModel::IsParentClass($oRightFilter->GetFirstJoinedClass(), $oLeftFilter->GetClass()))
{
// Specialize $oRightFilter
$oRightFilter->ChangeClass($oLeftFilter->GetClass());
@@ -1036,7 +1237,9 @@ class DBObjectSearch extends DBSearch
}
$aAliasTranslation = array();
$oLeftFilter->MergeWith_InNamespace($oRightFilter, $oLeftFilter->m_aClasses, $aAliasTranslation);
$oLeftFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation);
$oLeftFilter->MergeWith_InNamespace($oRightFilter, $aRootClasses, $aAliasTranslation);
$oRightFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation);
$oLeftFilter->TransferConditionExpression($oRightFilter, $aAliasTranslation);
$aSearches[] = $oLeftFilter;
}
@@ -1051,15 +1254,22 @@ class DBObjectSearch extends DBSearch
}
}
/**
* @param DBObjectSearch $oFilter
* @param array $aClassAliases
* @param array $aAliasTranslation
*
* @throws CoreException
*/
protected function MergeWith_InNamespace($oFilter, &$aClassAliases, &$aAliasTranslation)
{
if ($this->GetClass() != $oFilter->GetClass())
if ($this->GetFirstJoinedClass() != $oFilter->GetClass())
{
throw new CoreException("Attempting to merge a filter of class '{$this->GetClass()}' with a filter of class '{$oFilter->GetClass()}'");
throw new CoreException("Attempting to merge a filter of class '{$this->GetFirstJoinedClass()}' with a filter of class '{$oFilter->GetClass()}'");
}
// Translate search condition into our aliasing scheme
$aAliasTranslation[$oFilter->GetClassAlias()]['*'] = $this->GetClassAlias();
$aAliasTranslation[$oFilter->GetClassAlias()]['*'] = $this->GetFirstJoinedClassAlias();
foreach($oFilter->m_aPointingTo as $sExtKeyAttCode=>$aPointingTo)
{
@@ -1399,12 +1609,97 @@ class DBObjectSearch extends DBSearch
{
return new IntervalExpression($oExpression->GetValue(), $oExpression->GetUnit());
}
elseif ($oExpression instanceof NestedQueryOqlExpression)
{
return NestedQueryExpression::FromOQLObjectQuery($oExpression->GetOQLObjectQuery());
}
else
{
throw new CoreException('Unknown expression type', array('class'=>get_class($oExpression), 'query'=>$sQuery));
}
}
/**
* {@inheritDoc}
* @see DBSearch::ToJSON()
*/
public function ToJSON()
{
$aRet = array('selects' => array(), 'joins' => array(), 'where' => array());
$aParams = array_merge($this->m_aParams);
$aParams = MetaModel::PrepareQueryArguments($aParams);
foreach ($this->m_aSelectedClasses as $sAlias => $sClass)
{
$aRet['selects'][] = array('class' => $sClass, 'alias' => $sAlias);
}
$this->JoinsToJSON($aRet);
$aRet['condition'] = $this->m_oSearchCondition->ToJSON($aParams, true);
return $aRet;
}
/**
* Export the JOIN operations to a structure (array of arrays) suitable for JSON export
*
* @internal
*
* @param mixed[string] $aRet
* @return void
*/
protected function JoinsToJSON(&$aRet)
{
foreach($this->m_aPointingTo as $sExtKey => $aPointingTo)
{
foreach($aPointingTo as $iOperatorCode => $aFilter)
{
$sOperator = $this->OperatorCodeToOQL($iOperatorCode);
foreach($aFilter as $oFilter)
{
$aRet['joins'][] = array(
'src' => $this->GetFirstJoinedClass(),
'src_alias' => $this->GetFirstJoinedClassAlias(),
'target' => $oFilter->GetFirstJoinedClass(),
'target_alias' => $oFilter->GetFirstJoinedClassAlias(),
'foreign_key' => $sExtKey,
'operator' => $sOperator,
);
$oFilter->JoinsToJSON($aRet);
}
}
}
foreach($this->m_aReferencedBy as $aReferences)
{
foreach($aReferences as $sForeignExtKeyAttCode => $aFiltersByOperator)
{
foreach ($aFiltersByOperator as $iOperatorCode => $aFilters)
{
$sOperator = $this->OperatorCodeToOQL($iOperatorCode);
foreach ($aFilters as $oForeignFilter)
{
$aRet['joins'][] = array(
'src' => $oForeignFilter->GetFirstJoinedClass(),
'src_alias' => $oForeignFilter->GetFirstJoinedClassAlias(),
'target' => $this->GetFirstJoinedClass(),
'target_alias' => $this->GetFirstJoinedClassAlias(),
'foreign_key' => $sForeignExtKeyAttCode,
'operator' => $sOperator,
);
$oForeignFilter->JoinsToJSON($aRet);
}
}
}
}
}
/**
* @param \OqlQuery $oOqlQuery
* @param string $sQuery
*
* @throws \CoreException
* @throws \Exception
*/
public function InitFromOqlQuery(OqlQuery $oOqlQuery, $sQuery)
{
$oModelReflection = new ModelReflectionRuntime();
@@ -1521,6 +1816,10 @@ class DBObjectSearch extends DBSearch
foreach ($oOqlQuery->GetSelectedClasses() as $oClassDetails)
{
$sClassToSelect = $oClassDetails->GetValue();
if (!array_key_exists($sClassToSelect, $aAliases))
{
throw new CoreException("$sClassToSelect is not a valid alias");
}
$this->m_aSelectedClasses[$sClassToSelect] = $aAliases[$sClassToSelect];
}
$this->m_aClasses = $aAliases;
@@ -1557,6 +1856,24 @@ 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;
}
/**
* Get an SQLObjectQuery from the search. This SQLObjectQuery can be rendered as a select, select group by, update or delete
@@ -1828,4 +2145,9 @@ class DBObjectSearch extends DBSearch
}
return $oExpression;
}
public function ListParameters()
{
return $this->GetCriteria()->ListParameters();
}
}

View File

@@ -89,7 +89,8 @@ class DBObjectSet implements iDBObjectSetIterator
* @api
*
* @param DBSearch $oFilter The search filter defining the objects which are part of the set (multiple columns/objects per row are supported)
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending (true for ASC, false, for DESC)
* Example : array('name' => true, 'id' => false)
* @param array $aArgs Values to substitute for the search/query parameters (if any). Format: param_name => value
* @param array $aExtendedDataSpec
* @param int $iLimitCount Maximum number of rows to load (i.e. equivalent to MySQL's LIMIT start, count)
@@ -217,73 +218,81 @@ class DBObjectSet implements iDBObjectSetIterator
*/
public function OptimizeColumnLoad($aAttToLoad)
{
if (is_null($aAttToLoad))
// Check that the structure is an array of array
if (!is_array($aAttToLoad))
{
$this->m_aAttToLoad = null;
return;
}
else
foreach ($aAttToLoad as $sAlias => $aAttCodes)
{
// Complete the attribute list with the attribute codes
$aAttToLoadWithAttDef = array();
foreach($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass)
if (!is_array($aAttCodes))
{
$aAttToLoadWithAttDef[$sClassAlias] = array();
if (array_key_exists($sClassAlias, $aAttToLoad))
$this->m_aAttToLoad = null;
return;
}
}
// Complete the attribute list with the attribute codes
$aAttToLoadWithAttDef = array();
foreach($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass)
{
$aAttToLoadWithAttDef[$sClassAlias] = array();
if (array_key_exists($sClassAlias, $aAttToLoad))
{
$aAttList = $aAttToLoad[$sClassAlias];
foreach($aAttList as $sAttToLoad)
{
$aAttList = $aAttToLoad[$sClassAlias];
foreach($aAttList as $sAttToLoad)
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad);
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad] = $oAttDef;
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad);
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad] = $oAttDef;
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
// Add the external key friendly name anytime
$oFriendlyNameAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_friendlyname');
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_friendlyname'] = $oFriendlyNameAttDef;
if (MetaModel::IsArchivable($oAttDef->GetTargetClass(EXTKEY_ABSOLUTE)))
{
// Add the external key friendly name anytime
$oFriendlyNameAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_friendlyname');
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_friendlyname'] = $oFriendlyNameAttDef;
// Add the archive flag if necessary
$oArchiveFlagAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_archive_flag');
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_archive_flag'] = $oArchiveFlagAttDef;
}
if (MetaModel::IsArchivable($oAttDef->GetTargetClass(EXTKEY_ABSOLUTE)))
{
// Add the archive flag if necessary
$oArchiveFlagAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_archive_flag');
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_archive_flag'] = $oArchiveFlagAttDef;
}
if (MetaModel::IsObsoletable($oAttDef->GetTargetClass(EXTKEY_ABSOLUTE)))
{
// Add the obsolescence flag if necessary
$oObsoleteFlagAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_obsolescence_flag');
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_obsolescence_flag'] = $oObsoleteFlagAttDef;
}
if (MetaModel::IsObsoletable($oAttDef->GetTargetClass(EXTKEY_ABSOLUTE)))
{
// Add the obsolescence flag if necessary
$oObsoleteFlagAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_obsolescence_flag');
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_obsolescence_flag'] = $oObsoleteFlagAttDef;
}
}
}
// Add the friendly name anytime
$oFriendlyNameAttDef = MetaModel::GetAttributeDef($sClass, 'friendlyname');
$aAttToLoadWithAttDef[$sClassAlias]['friendlyname'] = $oFriendlyNameAttDef;
}
// Add the friendly name anytime
$oFriendlyNameAttDef = MetaModel::GetAttributeDef($sClass, 'friendlyname');
$aAttToLoadWithAttDef[$sClassAlias]['friendlyname'] = $oFriendlyNameAttDef;
if (MetaModel::IsArchivable($sClass))
{
// Add the archive flag if necessary
$oArchiveFlagAttDef = MetaModel::GetAttributeDef($sClass, 'archive_flag');
$aAttToLoadWithAttDef[$sClassAlias]['archive_flag'] = $oArchiveFlagAttDef;
}
if (MetaModel::IsObsoletable($sClass))
{
// Add the obsolescence flag if necessary
$oObsoleteFlagAttDef = MetaModel::GetAttributeDef($sClass, 'obsolescence_flag');
$aAttToLoadWithAttDef[$sClassAlias]['obsolescence_flag'] = $oObsoleteFlagAttDef;
}
// Make sure that the final class is requested anytime, whatever the specification (needed for object construction!)
if (!MetaModel::IsStandaloneClass($sClass) && !array_key_exists('finalclass', $aAttToLoadWithAttDef[$sClassAlias]))
{
$aAttToLoadWithAttDef[$sClassAlias]['finalclass'] = MetaModel::GetAttributeDef($sClass, 'finalclass');
}
if (MetaModel::IsArchivable($sClass))
{
// Add the archive flag if necessary
$oArchiveFlagAttDef = MetaModel::GetAttributeDef($sClass, 'archive_flag');
$aAttToLoadWithAttDef[$sClassAlias]['archive_flag'] = $oArchiveFlagAttDef;
}
$this->m_aAttToLoad = $aAttToLoadWithAttDef;
if (MetaModel::IsObsoletable($sClass))
{
// Add the obsolescence flag if necessary
$oObsoleteFlagAttDef = MetaModel::GetAttributeDef($sClass, 'obsolescence_flag');
$aAttToLoadWithAttDef[$sClassAlias]['obsolescence_flag'] = $oObsoleteFlagAttDef;
}
// Make sure that the final class is requested anytime, whatever the specification (needed for object construction!)
if (!MetaModel::IsStandaloneClass($sClass) && !array_key_exists('finalclass', $aAttToLoadWithAttDef[$sClassAlias]))
{
$aAttToLoadWithAttDef[$sClassAlias]['finalclass'] = MetaModel::GetAttributeDef($sClass, 'finalclass');
}
}
$this->m_aAttToLoad = $aAttToLoadWithAttDef;
}
/**
@@ -415,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[]
*
@@ -971,7 +980,15 @@ class DBObjectSet implements iDBObjectSetIterator
}
else
{
$oRetObj = MetaModel::GetObjectByRow($sClass, $aRow, $sClassAlias, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
try
{
$oRetObj = MetaModel::GetObjectByRow($sClass, $aRow, $sClassAlias, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
}
catch (CoreException $e)
{
$this->m_iCurrRow++;
$oRetObj = $this->Fetch($sRequestedClassAlias);
}
}
break;
}

View File

@@ -1,20 +1,21 @@
<?php
// Copyright (C) 2015-2017 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-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
*/
$bUseLegacyDBSearch = utils::GetConfig()->Get('use_legacy_dbsearch');
@@ -96,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;
@@ -237,6 +236,12 @@ abstract class DBSearch
*/
abstract public function GetClassAlias();
/**
* @return string
* @internal
*/
abstract public function GetFirstJoinedClass();
/**
* Change the class
*
@@ -271,6 +276,10 @@ abstract class DBSearch
*/
abstract public function RenameAlias($sOldName, $sNewName);
abstract public function RenameAliasesInNameSpace($aClassAliases, $aAliasTranslation = array());
abstract public function TranslateConditions($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true);
/**
* @internal
* @return mixed
@@ -393,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
@@ -433,11 +444,27 @@ abstract class DBSearch
*/
abstract public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null);
/**
/**
* Filter this search with another search.
* Initial search is unmodified.
* The difference with Intersect, is that an alias can be provided,
* the filtered class does not need to be the first joined class,
* it can be any class of the search.
*
* @param string $sClassAlias class being filtered
* @param DBSearch $oFilter Filter to apply
*
* @return DBSearch The filtered search
* @throws \CoreException
*/
abstract public function Filter($sClassAlias, DBSearch $oFilter);
/**
* Filter the result
*
* The filter is performed by returning only the values in common with the given $oFilter
* The impact on the resulting query performance/viability can be significant.
* Only the first joined class can be filtered.
*
* @internal
*
@@ -456,7 +483,7 @@ abstract class DBSearch
*
* @param DBSearch $oFilter The join is performed against $oFilter selected class
* @param integer $iDirection can be either DBSearch::JOIN_POINTING_TO or DBSearch::JOIN_REFERENCED_BY
* @param string $sExtKeyAttCode The join is performed against $sExtKeyAttCode wetheir it is compared aginst the current DBSearch or $oFilter depend of $iDirection
* @param string $sExtKeyAttCode The join is performed against $sExtKeyAttCode whether it is compared against the current DBSearch or $oFilter depend of $iDirection
* @param integer $iOperatorCode See DBSearch::AddCondition_PointingTo()
* @param array|null $aRealiasingMap Map of aliases from the attached query, that could have been renamed by the optimization process
*
@@ -480,6 +507,7 @@ abstract class DBSearch
}
else
{
/** @var \DBObjectSearch $oFilter */
if ($iDirection === static::JOIN_POINTING_TO)
{
$oSourceFilter->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
@@ -603,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)));
}
/**
@@ -620,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");
@@ -683,6 +711,15 @@ abstract class DBSearch
*/
abstract public function ToOQL($bDevelopParams = false, $aContextParams = null, $bWithAllowAllFlag = false);
/**
* Export the DBSearch as a structure (array of arrays...) suitable for a conversion to JSON
*
* @internal
*
* @return mixed[string]
*/
abstract public function ToJSON();
static protected $m_aOQLQueries = array();
/**
@@ -718,12 +755,13 @@ abstract class DBSearch
*
* @param string $sQuery The OQL to convert to a DBSearch
* @param mixed[string] $aParams array of <mixed> params index by <string> name
* @param ModelReflection|null $oMetaModel The MetaModel to use when checking the consistency of the OQL
*
* @return DBObjectSearch|DBUnionSearch
*
* @throws OQLException
*/
static public function FromOQL($sQuery, $aParams = null)
static public function FromOQL($sQuery, $aParams = null, ModelReflection $oMetaModel=null)
{
if (empty($sQuery))
{
@@ -765,7 +803,10 @@ abstract class DBSearch
$oOql = new OqlInterpreter($sQuery);
$oOqlQuery = $oOql->ParseQuery();
$oMetaModel = new ModelReflectionRuntime();
if ($oMetaModel === null)
{
$oMetaModel = new ModelReflectionRuntime();
}
$oOqlQuery->Check($oMetaModel, $sQuery); // Exceptions thrown in case of issue
$oResultFilter = $oOqlQuery->ToDBSearch($sQuery);
@@ -939,13 +980,15 @@ 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;
$sRes = $oSQLQuery->RenderGroupBy($aScalarArgs, $bBeautifulSQL, $aOrderBy, $iLimitCount, $iLimitStart);
}
catch (Exception $e)
// Catch CoreException to add info before throwing again
// Other exceptions will be thrown directly
catch (CoreException $e)
{
// Add some information...
$e->addInfo('OQL', $this->ToOQL());
@@ -955,6 +998,10 @@ abstract class DBSearch
return $sRes;
}
function GetExpectedArguments()
{
return $this->GetCriteria()->ListParameters();
}
/**
* Generate a SQL query from the current search
@@ -1034,7 +1081,7 @@ 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
{
@@ -1055,6 +1102,41 @@ abstract class DBSearch
return $sRes;
}
/**
* @param bool $bMustHaveOneResultMax if true will throw a CoreOqlMultipleResultsFound if multiple results
* @param array $aOrderBy
* @param array $aSearchParams
*
* @return null|\DBObject query result
* @throws \CoreOqlMultipleResultsForbiddenException if multiple results found and parameter enforce the check
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*
* @since 2.7.0 N°2555
*/
public function GetFirstResult($bMustHaveOneResultMax = true, $aOrderBy = array(), $aSearchParams = array())
{
$oSet = new DBObjectSet($this, array(), $aSearchParams, null, 2);
$oFirstResult = $oSet->Fetch();
if ($oFirstResult === null) // useless but here for readability ;)
{
return null;
}
if ($bMustHaveOneResultMax)
{
$oSecondResult = $oSet->Fetch();
if ($oSecondResult !== null)
{
throw new CoreOqlMultipleResultsForbiddenException(
'Search returned multiple results, this is forbidden. Query was: '.$this->ToOQL());
}
}
return $oFirstResult;
}
/**
* @internal
* @return mixed
@@ -1067,27 +1149,28 @@ abstract class DBSearch
*/
protected abstract function SetDataFiltered();
/**
* @internal
*
* @param $aOrderBy
* @param $aArgs
* @param $aAttToLoad
* @param $aExtendedDataSpec
* @param $iLimitCount
* @param $iLimitStart
* @param $bGetCount
* @param null $aGroupByExpr
* @param null $aSelectExpr
*
* @return mixed
*/
/**
* @param $aOrderBy
* @param $aArgs
* @param $aAttToLoad
* @param $aExtendedDataSpec
* @param $iLimitCount
* @param $iLimitStart
* @param $bGetCount
* @param null $aGroupByExpr
* @param null $aSelectExpr
*
* @return SQLObjectQuery
* @throws \CoreException
* @internal
*
*/
protected function GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $aGroupByExpr = null, $aSelectExpr = null)
{
$oSearch = $this;
if (!$this->IsAllDataAllowed() && !$this->IsDataFiltered())
{
foreach ($this->GetSelectedClasses() as $sClass)
foreach ($this->GetSelectedClasses() as $sClassAlias => $sClass)
{
$oVisibleObjects = UserRights::GetSelectFilter($sClass, $this->GetModifierProperties('UserRightsGetSelectFilter'));
if ($oVisibleObjects === false)
@@ -1098,8 +1181,7 @@ abstract class DBSearch
if (is_object($oVisibleObjects))
{
$oVisibleObjects->AllowAllData();
$oSearch = $this->Intersect($oVisibleObjects);
/** @var DBSearch $oSearch */
$oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects);
$oSearch->SetDataFiltered();
}
}
@@ -1150,6 +1232,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
*/
@@ -258,6 +260,24 @@ class DBUnionSearch extends DBSearch
return $bRet;
}
public function RenameAliasesInNameSpace($aClassAliases, $aAliasTranslation = array())
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->RenameAliasesInNameSpace($aClassAliases, $aAliasTranslation);
}
}
public function TranslateConditions($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->TranslateConditions($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
}
}
public function IsAny()
{
$bIsAny = true;
@@ -353,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);
}
}
@@ -373,16 +400,27 @@ 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);
}
}
public function Filter($sClassAlias, DBSearch $oFilter)
{
$aSearches = array();
foreach ($this->aSearches as $oSearch)
{
$aSearches[] = $oSearch->Filter($sClassAlias, $oFilter);
}
return new DBUnionSearch($aSearches);
}
public function Intersect(DBSearch $oFilter)
{
$aSearches = array();
@@ -453,6 +491,20 @@ class DBUnionSearch extends DBSearch
return $sRet;
}
/**
* {@inheritDoc}
* @see DBSearch::ToJSON()
*/
public function ToJSON()
{
$sRet = array('unions' => array());
foreach ($this->aSearches as $oSearch)
{
$sRet['unions'][] = $oSearch->ToJSON();
}
return $sRet;
}
/**
* Returns a new DBUnionSearch object where duplicates queries have been removed based on their OQLs
*
@@ -620,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
*/
@@ -652,6 +714,8 @@ class DBUnionSearch extends DBSearch
}
}
public function AddConditionForInOperatorUsingParam($sFilterCode, $aValues, $bPositiveMatch = true)
{
$sInParamName = $this->GenerateUniqueParamName();
@@ -669,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

@@ -192,7 +192,11 @@ class Dict
/**
* Formats a localized string with numbered placeholders (%1$s...) for the additional arguments
* See vsprintf for more information about the syntax of the placeholders
*
* @see \TemplateString to use placeholders
*
* @param string $sFormatCode
*
* @return string
*/
public static function Format($sFormatCode /*, ... arguments ....*/)

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