Compare commits

..

617 Commits

Author SHA1 Message Date
Pierre Goiffon
8b37f503c7 N°1455 change hidden obsolete data message 2019-10-22 11:16:32 +02:00
Eric
7c9353d299 N°1888 - Circumvention of the restriction of rights by organization 2019-10-22 10:49:22 +02:00
Eric
dfc901ffa4 1355 - [Security] Password Autocomplete in Browser 2019-10-22 10:31:57 +02:00
Eric
611bf3035f N°1113 - AttributePassword content searchable in Global Search
The global search now avoid AttributePassword type of attributes
2019-10-22 09:56:09 +02:00
Eric
35bb2da6bd N°1114 - config-itop.php access rights enforcement 2019-10-21 18:14:44 +02:00
Eric
53e034ce4f 💚 Fix unit tests 2019-10-21 16:37:56 +02:00
Eric
cd4db1db06 💚 Fix unit tests 2019-10-21 16:32:43 +02:00
Pierre Goiffon
b1dbddffb9 N°2518 Integrating ACE in iTop 2019-10-17 19:21:30 +02:00
Pierre Goiffon
221c10aa9b itop-config : syntax highlighting for JS 2019-10-17 17:35:41 +02:00
bruno DA SILVA
15ecd7bccf itop-portal-base: .env.local is no more versioned 2019-10-17 16:47:18 +02:00
Eric
0d8dd0dc17 N°2517 - Supportability - file System integrity 2019-10-17 16:00:14 +02:00
Eric
ec986e0fbc N°2517 - Supportability - file System integrity 2019-10-17 15:55:33 +02:00
Eric
f0e0c73e9b N°2517 - Supportability - file System integrity 2019-10-17 15:51:41 +02:00
bruno DA SILVA
d9a24d2e14 2498 - restrict access to assets into env-*
iis bugfix: the configuration is now valid
2019-10-17 15:08:30 +02:00
Eric
93618f974d 💚 Fix unit tests 2019-10-17 12:45:50 +02:00
Eric
4dfc217992 N°2517 - Supportability - OQL Explain 2019-10-17 12:21:12 +02:00
Eric
5a1a6cf6f0 💚 Fix unit tests 2019-10-17 12:19:23 +02:00
Eric
36b325f71d N°2517 - Supportability - OQL Explain 2019-10-17 11:06:31 +02:00
bruno DA SILVA
74a3712116 n°2311 - prenvent cas logo to be fullscreen during the login page loading 2019-10-17 10:53:35 +02:00
Eric
1fe7eb6cf4 🎨 Fix Login CSS colors for ie 2019-10-17 10:46:47 +02:00
Pierre Goiffon
0d0f8e9ffc 💚 N°2538 Fix test
Was broken since ce207e5c
2019-10-17 10:45:12 +02:00
Stephen Abello
0a2063ce69 N°2060: Fix data-localizer and archive mode initialization for Symfony portal 2019-10-17 10:12:40 +02:00
Pierre Goiffon
ce207e5c56 Restore old APPROOT var init
Rollback change in 607d355c
2019-10-17 10:09:21 +02:00
bruno DA SILVA
f22eaae457 ✏️ fix a typo 2019-10-17 10:00:35 +02:00
Eric
fbc5280add 💚 Fix unit tests 2019-10-17 09:23:32 +02:00
bruno DA SILVA
65512ca984 2498 - restrict access to module's assets
- into env-*
 - into datamodels
 - into extensions
2019-10-16 18:01:33 +02:00
Pierre Goiffon
385b4f8d4a 🎨 \utils::RealPath Move var init next to its use 2019-10-16 17:33:49 +02:00
Pierre Goiffon
f65f22f333 N°2538 check path validity little improvements
* ajax-backup : change code to be more readable
* does a realpath() call on basepath to avoid troubles when havin '/' on Windows
2019-10-16 11:37:50 +02:00
Pierre Goiffon
607d355c61 N°2538 enforce generic method to check path validity
Now uses realpath() and StartsWith
2019-10-16 11:13:19 +02:00
Pierre Goiffon
29c30c1f89 🎨 Code format 2019-10-16 11:13:19 +02:00
Stephen Abello
2ff771c16a N°2311: Fix change password page, correctly display buttons with long string 2019-10-16 10:43:20 +02:00
Pierre Goiffon
5e641f2273 N°2538 generic method to check path validity 2019-10-15 18:41:07 +02:00
Eric
d52254bbf0 🎨 Make some room in the menu 2019-10-15 12:10:16 +02:00
bruno DA SILVA
a97c8be31a 2498 - restrict access to assets into env-*
this is done by adding a .htaccess file on a per module basis.
NB: if there is already a .htaccess file, it is kept as it, so you can see this commit as adding a "default" htaccess.
2019-10-15 11:46:18 +02:00
bruno DA SILVA
9c067e5645 💡 type an @return for better code completion 2019-10-15 11:46:18 +02:00
bruno DA SILVA
24d6857069 👌 comiled PHP files have no more spaces before the PHP open tag. 2019-10-15 11:46:18 +02:00
Eric
ed9259df9e 🐛 Fix access to change password page
🎨 Code cleanup
2019-10-15 11:43:47 +02:00
Eric
75794fb4d9 Fix count with Archive mode 2019-10-15 09:50:33 +02:00
Pierre Goiffon
4c386da7d2 📝 Fix typo (thanks @jbostoen !) 2019-10-15 08:44:47 +02:00
Denis Flaven
abbd2e64c9 Fixed the doc generation and some typos in the README 2019-10-14 16:44:43 +02:00
Pierre Goiffon
300a723fca 📝 Add some @var PHPDoc for iQueryModifier calls 2019-10-11 18:09:00 +02:00
Eric
ddf42d0358 Access right on display explain search 2019-10-11 17:08:35 +02:00
Eric
88be0d7638 N°2261 - Log KPI not available in lnk window 2019-10-11 16:27:01 +02:00
Stephen Abello
e26428a0eb Fix visual glitch on portal's modal-backdrops 2019-10-11 16:08:19 +02:00
Eric
7b36852d7a N°2261 - Log KPI not available in lnk window 2019-10-11 15:56:32 +02:00
bruno DA SILVA
d1eb674314 n°1617 - meets iTop fence requirements 2019-10-09 18:53:40 +02:00
Pierre Goiffon
4afed39b0e N°2529 Fix charset sent by logout page 2019-10-09 18:07:08 +02:00
Stephen Abello
c1460cfdc7 N°2142 Portal: Fix list tabs and on charts click when a Manage brick has a chart as default display mode 2019-10-09 17:15:07 +02:00
Vincent Dumas
85a94ee1e7 Fix products setup icons hyperlinks
The 3 icons at the end of the Setup were pointing to broken url links
2019-10-09 15:36:25 +02:00
Eric
68895551b2 N°2517 - Supportability - system report (Added cron user) 2019-10-09 12:11:17 +02:00
Stephen Abello
eece09e5ed N°2311: Avoid new SSO buttons to flicker on page loading 2019-10-09 10:21:38 +02:00
Eric
0dd1f26b39 N°2311 - Authentication extensibility in iTop 2019-10-08 15:23:24 +02:00
Eric
1b958a3c4d N°2240 - Supportability - Maintenance mode 2019-10-08 15:07:34 +02:00
Eric
a1d23cddc5 N°2248 - Supportability - Integrity module 2019-10-08 09:29:49 +02:00
Pierre Goiffon
14c25e3d9b 🎨 SetupPage code formatting 2019-10-08 08:42:41 +02:00
Pierre Goiffon
c3915ee48e N°2518 Implementation to rotate logs on a weekly basis 2019-10-08 08:42:41 +02:00
Pierre Goiffon
21ff03e28f N°2518 Possibiliy to rotate log files based on days
See the 'log_filename_builder_impl' config parameter
2019-10-08 08:42:41 +02:00
Pierre Goiffon
2c8887398d 🎨 Log classes : PHPDoc & fix wrong self:: uses 2019-10-08 08:42:41 +02:00
Eric
f51cd65b1f N°2249 - Supportability - Updater module 2019-10-07 17:44:17 +02:00
Stephen Abello
dbb5a5191b N°1146 Portal: Correctly display external field targeting enum field 2019-10-07 12:04:14 +02:00
Pierre Goiffon
e9844aed45 Merge branch 'support/2.6.2' into develop 2019-10-07 09:37:03 +02:00
Eric
aaf6289953 N°2249 - Supportability - Updater module 2019-10-04 18:26:21 +02:00
Eric
b0d0223821 N°2249 - Supportability - Updater module 2019-10-04 18:03:27 +02:00
Pierre Goiffon
f271606e5e N°729 Update DBObject::Prefill* methods PHPDoc 2019-10-04 11:09:16 +02:00
Eric
23a9bae391 N°2249 - Supportability - Updater module 2019-10-03 15:31:23 +02:00
Eric
b7c795c313 N°2240 - Supportability - Maintenance mode 2019-10-02 10:18:07 +02:00
Eric
46c553fbad N°2249 - Supportability - Updater module 2019-10-02 08:39:32 +02:00
Pierre Goiffon
fc5bbfbed2 N°2269 fix obsolete icon color in lists 2019-10-01 18:10:45 +02:00
Eric
afe760a8bc N°2240 - Supportability - Maintenance mode 2019-10-01 13:53:34 +02:00
Vincent Dumas
7e78c35d23 N°2479: fix typo in user message 2019-09-30 18:30:03 +02:00
Vincent Dumas
56f0e95a22 Fix typo in a comment 2019-09-30 17:05:43 +02:00
Eric
7f9024465f N°2311 - Refactor Login FSM for errors 2019-09-30 15:10:29 +02:00
Eric
75dc11b882 N°2311 - Refactor Login FSM for errors 2019-09-30 13:45:50 +02:00
Eric
db0a5bd071 N°2240 - Supportability - Maintenance mode 2019-09-27 18:00:09 +02:00
Eric
64434f8065 🎨 remove warnings 2019-09-27 17:14:04 +02:00
Eric
044623309c N°2240 - Supportability - Maintenance mode 2019-09-27 17:13:25 +02:00
Pierre Goiffon
a54695b2e0 🎨 Some InlineImage comment / formatting 2019-09-27 16:11:42 +02:00
Eric
7acb53a22f N°2311 - Refactor Login FSM for errors 2019-09-27 13:12:00 +02:00
Vincent Dumas
a2d05e8119 Fix typo on French message when object not found (#92) 2019-09-27 11:25:48 +02:00
Pierre Goiffon
dfcebfcbea 🎨 Remove useless consecutive call 2019-09-27 09:24:20 +02:00
Eric
3f165c9803 N°2456 - Deadlock during concurrent updates 2019-09-26 18:18:52 +02:00
Eric
85971ea9f3 N°2272 - OQL performance (hierarchical keys) 2019-09-26 08:30:57 +02:00
Eric
ff3ec219ef N°2272 - OQL performance (empty class alias) 2019-09-25 17:43:20 +02:00
Eric
e0374dd186 N°2272 - OQL performance (unit tests) 2019-09-25 14:39:12 +02:00
Eric
51ee3b31cb N°2272 - OQL performance (SQL Generation) 2019-09-25 10:57:35 +02:00
Eric
496ea830c5 N°2272 - OQL performance (unit tests) 2019-09-24 13:20:55 +02:00
Eric
1e911b5094 N°2272 - OQL performance (SQL Generation) 2019-09-24 11:40:08 +02:00
Eric
6073be25de N°2272 - OQL performance (OQL class tree optimizer) 2019-09-19 13:44:15 +02:00
Eric
93a736e42a N°2272 - OQL performance (OQL class tree wip) 2019-09-19 13:44:15 +02:00
Eric
128afc8a56 N°2272 - OQL performance (comments) 2019-09-19 13:44:15 +02:00
Stephen Abello
a8d5630030 N°967 Portal: Browse brick actions are now correctly ordered even without a rank tag 2019-09-19 10:53:42 +02:00
Pierre Goiffon
d83a256b9d N°2293 DBUpdate changes finalisation
* ListChanges are back where they were (extra precaution to prevent AfterUpdate callback impl regressions)
* aChanges reset is done after the reentrance test
2019-09-17 15:06:47 +02:00
Pierre Goiffon
5af33ffe0a N°2293 Saves changes in DBObjet::DBUpdate, just before the AfterUpdate call
This will allow to get changes made in ComputeValues, OnUpdate, etc
2019-09-17 11:59:17 +02:00
Pierre Goiffon
ffd37d7802 🎨 cmdbAbstract : add missing visibility keywords for some function 2019-09-13 11:32:31 +02:00
Stephen Abello
31a34c247c N°2482 Regression on mandatory external field with only 1 value 2019-09-13 10:42:55 +02:00
Eric
4ccd842bdf N°2272 - OQL performance (unit tests) 2019-09-13 10:35:41 +02:00
Eric
f186c9e242 N°2272 - OQL performance (code refactor) 2019-09-13 08:39:50 +02:00
Pierre Goiffon
4e66c9fc23 N°2478 clearer comment 2019-09-12 15:52:13 +02:00
Pierre Goiffon
99e21652a0 🎨 clarify \Config::UpdateIncludes with $sModulesDir=null 2019-09-12 09:54:06 +02:00
Pierre Goiffon
24a0cc2f64 N°2478 Fix install not working anymore when having no env-* 2019-09-12 09:29:24 +02:00
Pierre Goiffon
e9dee86b7c Remove unecessary require() calls in utils class
We still need manual require() for LoginForm and associated
2019-09-11 18:03:44 +02:00
Pierre Goiffon
42d7901828 🎨 some code formatting in utils class 2019-09-11 17:51:23 +02:00
Pierre Goiffon
7f156e961d N°2478 Fix unattended install
* remove require() calls (now we have an autoloader \o/)
* change cache policy in utils::GetConfig
* set config in utils class from ApplicationInstaller
2019-09-11 16:41:50 +02:00
bruno DA SILVA
d71b3b1893 N°1455 - obsolescence: show hint in the search bar 2019-09-10 15:45:57 +02:00
Pierre Goiffon
cb1488c17f 👷 Jenkins : no composer install needed anymore 2019-09-10 14:28:35 +02:00
Eric
e13bcba89a N°2311 - Login Page extensibility (constant used for application name) 2019-09-10 13:42:37 +02:00
Eric
7d8e8df0c5 N°2272 - OQL performance (fix 2.5 regression) 2019-09-10 13:06:47 +02:00
Pierre Goiffon
0e5c0ff46d 👷 Jenkins : fix toolkit unzip 2019-09-10 12:06:28 +02:00
Pierre Goiffon
67bff3e19d Jenlinks : change toolkit URL (http to https) 2019-09-10 11:46:55 +02:00
Eric
19d9c69fb8 N°2311 - Login Page extensibility (CSS reworked) 2019-09-10 08:40:28 +02:00
Eric
cb772a9527 N°2311 - Login Page extensibility (CAS button) 2019-09-06 17:54:02 +02:00
Eric
ee621c1b92 N°2311 - Login Page extensibility 2019-09-06 17:40:29 +02:00
Eric
20aa1bfdd6 cleanup warnings 2019-09-06 15:06:08 +02:00
Eric
9c52f6b949 N°2272 - OQL performance (add finalclass on all intermediate tables) 2019-09-06 14:56:37 +02:00
Eric
0f890ad228 N°2272 - OQL performance (Expression cache is configurable) 2019-09-06 14:30:42 +02:00
Eric
aac6ab0fc6 N°2272 - OQL performance (testability) 2019-09-06 14:30:42 +02:00
Eric
3fde778c0c N°2311 - Login Page extensibility 2019-09-06 14:30:42 +02:00
bruno DA SILVA
348bdbdc0d typo 2019-09-05 16:38:40 +02:00
Eric
3e9223a0bc N°2311 - Preferences extensibility 2019-09-02 17:55:55 +02:00
Eric
a905a8a3c1 N°2311 - Dictionary 2019-09-02 11:39:06 +02:00
Eric
b2ab07aa69 N°2311 - Login Page extensibility 2019-08-30 15:07:51 +02:00
Eric
9bd1da95e0 N°2456 - Deadlock during concurrent updates 2019-08-27 09:34:50 +02:00
Eric
83c0df2157 N°2456 - Deadlock during concurrent updates 2019-08-26 15:50:40 +02:00
Eric
8d7c64be66 N°2455 - Wrong Request Template query validation
Avoid blocking a form if a RequestTemplate reference a bad attribute (e.g. :this->id)
2019-08-23 17:30:54 +02:00
Eric
0625a01a4f Force MySQL port to int (for MySQL connector) 2019-08-23 15:13:29 +02:00
Pierre Goiffon
208a8723ff 🎨 Code formatting for iApplicationObjectExtension 2019-08-23 10:24:48 +02:00
Pierre Goiffon
f4c2a9ca7d N°2293 Some PHPDoc (@since for changes availability) 2019-08-23 10:18:22 +02:00
Pierre Goiffon
c97fd63e6d N°2293 Object update hooks now have access to object changes
* new \cmdbAbstractObject::$m_aChanges for \iApplicationObjectExtension::OnDBUpdate calls
* calling ListChanges() from within \DBObject::AfterUpdate will now give the right informations
* update PHPDoc in iApplicationObjectExtension
2019-08-23 10:13:44 +02:00
Eric
58402cdda8 N°2311 - User Provisioning API documentation 2019-08-22 14:38:07 +02:00
Eric
59fa3e10a3 N°2311 - User Provisioning API 2019-08-22 14:00:54 +02:00
Eric
0831a427cc N°2311 - authent-cas compatibility with 2.6 configuration 2019-08-22 14:00:53 +02:00
Pierre Goiffon
08517f0c7e 📝 Some PHPDoc in ExternalKey field rendering 2019-08-22 11:01:59 +02:00
Eric
97e58c2d79 N°2311 - combodo-cas renamed authent-cas 2019-08-21 12:21:24 +02:00
Eric
6693ec48a0 N°2311 - Add combodo-cas 2019-08-21 10:46:58 +02:00
Eric
9a13d4ce04 N°2311 - Code cleanup 2019-08-20 18:04:44 +02:00
Pierre Goiffon
51bbe1f79d Handle nested transactions (#90)
* starting a new transaction will send nothing in the DB (only one global transaction : merge nested transactions)
* same for COMMIT or ROLLBACK if more than 1 transaction is opened
* transactions are kept inside \DBObject::DBInsertNoReload, but they can be disabled using config flag db_core_transactions_enabled=false (true by default, hidden by default)
2019-08-20 10:47:29 +02:00
Eric
5dd92ab506 N°2311 - Add logs to logout 2019-08-20 10:26:43 +02:00
Eric
7120201469 N°2311 - Extend logout/error page 2019-08-20 09:53:11 +02:00
Stephen Abello
046eeb03f2 N°1671 Portal: Fix Aggregate Brick when user profile is not allowed to see one of the sub-brick 2019-08-19 11:28:53 +02:00
Eric
ce22dc9309 N°2311 - HybridAuth Extension 2019-08-16 18:42:04 +02:00
Eric
953c9e588e N°2311 - CAS Extension 2019-08-16 17:39:48 +02:00
Eric
2ceb4068ad N°2311 - Refactor Login FSM Extensions 2019-08-16 17:39:48 +02:00
Eric
11f62063a6 N°2311 - Debug login FSM 2019-08-16 17:39:47 +02:00
Eric
7885d712a6 N°2311 - Authentication extensibility in iTop 2019-08-16 17:39:47 +02:00
Molkobain
a6ca282ff4 Internal: Remove unused function in portal form handler 2019-08-16 17:05:37 +02:00
Molkobain
8ef67dee3b N°1881.1 Portal: Add DOM attribute (and widget helper) on object form that fields have been touched 2019-08-16 17:02:40 +02:00
Molkobain
fb1b730bd5 N°2435.8 Manage TCPDF lib using composer 2019-08-16 10:38:30 +02:00
Stephen Abello
af9c45849e N°1320: Add option to show/hide linkedsets out of user's scopes in portal 2019-08-16 10:05:45 +02:00
Pierre Goiffon
a711a67f4c Fix throw inside transaction without rollback 2019-08-14 14:43:40 +02:00
Molkobain
b743b7e2fb N°2435.7 Manage ArchiveTar lib using composer 2019-08-14 14:06:56 +02:00
Molkobain
3db92359e5 N°2435.6 Add .gitignore to remove non necessary folders from dependancies (eg. examples, tests, docs, ...) 2019-08-14 14:06:56 +02:00
Stephen Abello
088f08b315 N°967 Portal: Browse brick actions are now ordered following a rank tag 2019-08-14 12:39:32 +02:00
Molkobain
71cd61dfe4 Internal: Remove ClassLoader affectation from bootstrap.inc.php as we don't use it yet. 2019-08-14 10:30:43 +02:00
Molkobain
947e26d864 Internal: Change how the bootstrap.inc.php file is included in endpoints (This completes commit ec095896) 2019-08-13 17:38:51 +02:00
Molkobain
e3995a130f PHPDoc 2019-08-13 17:25:04 +02:00
Molkobain
384641e274 N°895 Portal: Filter linkedsets on remote object scopes 2019-08-13 16:59:25 +02:00
Molkobain
0985415e11 N°2435.5 Manage SwiftMailer lib using composer 2019-08-13 14:09:16 +02:00
Molkobain
3e13c9e825 N°2435.4 Manage SwiftMailer lib using composer 2019-08-13 13:51:41 +02:00
Molkobain
ec09589646 N°2439 Add real autoloader for framework files (not modules) 2019-08-13 13:46:19 +02:00
Molkobain
83e3321a48 N°2435.3 Security hardening: Avoid direct access to lib directory 2019-08-13 10:52:15 +02:00
Molkobain
bb4c8ea52d N°2435.2 Manage SCSSPHP lib using composer 2019-08-13 10:50:54 +02:00
Molkobain
5960dc6245 N°2435.1 Portal: Split portal composer.json in 2
- Autoloader for portal files in the itop-portal-base module
- Dependencies moved to root composer.json
- Add autoloader for /core and /application content
2019-08-13 10:34:22 +02:00
Molkobain
ca92316e7d N°919 Portal: Make portal denial based on user profiles work again 2019-08-12 16:15:09 +02:00
Molkobain
b096472ccf PHPDoc 2019-08-12 11:45:33 +02:00
Molkobain
261498d225 Internal: Move expression cache files in a dedicated directory 2019-08-12 11:45:33 +02:00
Stephen Abello
320a6b8a16 N°2238 Portal: Track in object's history attachments addition and deletion 2019-08-09 15:37:52 +02:00
Stephen Abello
b3cadaf314 PHP doc + typo 2019-08-09 10:49:55 +02:00
Stephen Abello
660852115e Portal: Rename an endpoint with a typo 2019-08-09 10:49:55 +02:00
Stephen Abello
543e57ed7d N°2432 Portal: Manage and Browse brick filters now apply on subclasses fields in lazy mode 2019-08-09 10:49:55 +02:00
Pierre Goiffon
4d78b7ca13 📝 add comment on prefill API call 2019-08-08 09:44:17 +02:00
Pierre Goiffon
a32bdf3f2f 📝 fix phpdoc (see call in UI.php operation=new) 2019-08-07 18:10:20 +02:00
Pierre Goiffon
5382d2006c N°2429 change visibility of \DBObject::GetReferencingObjects internal method
In iTop, only called by \DBObject::MakeDeletionPlan which is private
Not called in our extensions
Not called/redefined in any client customization
2019-08-07 16:08:59 +02:00
Pierre Goiffon
ae1d60d11e 📝 little PHPDoc modification 2019-08-07 11:50:59 +02:00
Stephen Abello
e8c0bcfbb2 N°769 Portal: Add parameter to set default list length in ManageBrick and BrowseBrick 2019-08-07 10:28:25 +02:00
Molkobain
b8a04cb842 Create README.md 2019-08-07 10:12:46 +02:00
Pierre Goiffon
b4ffa8c045 📝 Fix wrong phpdoc 2019-08-06 15:27:32 +02:00
Pierre Goiffon
7e540f16f9 🎨 \DBObject::DBDeleteSingleObject : isolate exit condition at the top 2019-08-06 14:45:05 +02:00
Stephen Abello
e69275c6c5 N°956 Portal: Add an icon to copy object name and url next to the form title
* Add a generic utility to build iTop clipboard widget
* Can be used in portal, console and extensions
2019-08-06 12:29:18 +02:00
Stephen Abello
16c123df49 Trigger bootstrap modal loaded event in 66287757 refactor 2019-08-06 12:29:18 +02:00
Pierre Goiffon
446eee79fc 🎨 Unwrap useless else statement 2019-08-06 11:49:19 +02:00
Pierre Goiffon
cbc96d8a58 📝 Attachment : add some @var on object init 2019-08-05 11:04:26 +02:00
Stephen Abello
d2015b7d7b N°1232 Portal: Harmonize right checks for external url in forms 2019-08-02 11:04:32 +02:00
Stephen Abello
305b236f41 N°2060: Add portal's ContextTag lost in Symfony migration 2019-08-02 10:58:52 +02:00
Pierre Goiffon
e172bd13df N°2211 DataSynchro : remove DBUpdate() arguments for future 2.7.0 2019-08-01 17:04:01 +02:00
Stephen Abello
94cb4a2bb4 N°2060: Fix regression introduced in migration 2019-08-01 14:37:50 +02:00
Pierre Goiffon
7abbbf6b7b 📝 PHPDoc for BackgroundTask 2019-08-01 09:54:47 +02:00
Molkobain
66287757b3 Portal: Refactor creation of modal dialog through a common helper (CreatePortalModal(oOptions)) 2019-07-31 15:51:25 +02:00
Molkobain
661ecc57c5 PHPDoc 2019-07-31 15:51:25 +02:00
Molkobain
15f9f79a24 Fix unit test to be used in the ITSM Designer (Shame shame shame 🔔🙈) 2019-07-31 15:51:25 +02:00
Molkobain
286374fe7c N°1232 Portal: Allow external keys to be opened in object forms (only if user has permissions)
- Created new JS method to handle modals (bootstrap-portal-modal.js > CreatePortalModal)
- Moved from global modal hacks to the same file
2019-07-31 15:51:25 +02:00
Pierre Goiffon
ea288c2194 🌐 Fix english label
Many thanks @jbostoen who suggested the change in 60863c5fcf
2019-07-31 09:04:38 +02:00
Molkobain
927cd60ad2 N°1736.2 Fix sBrickSubtitle not being defined by default in most bricks 2019-07-30 17:41:09 +02:00
Molkobain
fb6e47e42a N°1736 Portal: Add option to display ManageBrick's current tab description as the brick subtitle
Actually, every brick can now display a subtitle if they populate the sBrickSubtitle variable for the template.
2019-07-30 17:19:07 +02:00
Pierre Goiffon
34d4f6b1f9 N°2240 add /.maintenance in ignore 2019-07-30 08:59:52 +02:00
Stephen Abello
f7170953fb N°2353: Typo in French dictionary 2019-07-29 15:33:48 +02:00
Pierre Goiffon
e80d52cc0f N°2416 fix datepicker not opening anymore
This was brought by PR #40 integration in 06592d7d37
2019-07-29 12:12:34 +02:00
Pierre Goiffon
75da1ce7a7 🎨 iTopWebPage : some syntax highlighting in HEREDOC JS 2019-07-29 11:09:33 +02:00
Pierre Goiffon
7894c872dc N°2414 Remove \DBObject::RegisterCallback
This was experimental and never used. The official way is to use iApplicationObjectExtension !
2019-07-26 16:24:06 +02:00
Pierre Goiffon
14b2d2ed4c 🔧 Update EditorConfig file with idea specific options
Available since PHPStorm 2019.2
@see https://blog.jetbrains.com/phpstorm/2019/07/phpstorm-2019-2-release/#editorconfig
2019-07-26 12:05:08 +02:00
Pierre Goiffon
a7b5077e0c N°2010 \SetupUtils::MYSQL_NOT_VALIDATED_VERSION is now empty 2019-07-25 17:33:41 +02:00
Pierre Goiffon
de2b88b707 📝 PHPDoc for archiving remove @api
Public API is currently under review, we shouldn't change the review perimeter !
2019-07-25 17:33:41 +02:00
Stephen Abello
130734bec7 Merge branch 'master' into develop
# Conflicts:
#	setup/backup.class.inc.php
2019-07-25 17:18:49 +02:00
Stephen Abello
d7fad4b646 Merge branch 'support/2.6.2' 2019-07-25 17:07:49 +02:00
Stephen Abello
db4c241cba N°680 Fix 'G', 'd', 'j' DateTime format in regexp generation 2019-07-25 15:12:44 +02:00
Pierre Goiffon
be09909976 📝 PHPDoc for archiving : rephrase for clarity
Thanks @bruno-ds !!
2019-07-24 17:04:10 +02:00
Stephen Abello
f4d538ef6c N°2410 Fix regression in mysqldump call introduced in 2.6.2 2019-07-24 16:02:20 +02:00
Molkobain
a6b1da393b N°2269 Font Awesome v5: Fix files integration in portal 2019-07-23 18:10:00 +02:00
Pierre Goiffon
501c20a34d 📝 Some PHPDoc on object archiving 2019-07-23 18:05:05 +02:00
Molkobain
b858ba3786 N°1268 Add support for abstract classes creation in browse brick
* Refactored parts of the create brick into the object controller
2019-07-23 11:45:33 +02:00
Molkobain
60863c5fcf 🌐 Fix English dictionary entries 2019-07-23 11:45:33 +02:00
Pierre Goiffon
1ee3f4a984 Merge remote-tracking branch 'origin/master' into develop 2019-07-22 15:59:21 +02:00
Pierre Goiffon
aadb605dec Merge remote-tracking branch 'origin/support/2.6.0' 2019-07-22 15:58:58 +02:00
Pierre Goiffon
f63f2bd445 N°1802 backup : remove old itop_root config parameter
Was renamed to itop_backup_incident in 2.6.0
2019-07-22 15:57:30 +02:00
Molkobain
0205f150a3 N°902.3 Portal: Extract column sorting helper to a specific service 2019-07-22 11:45:31 +02:00
Pierre Goiffon
cdbdf580c8 📝 CONTRIBUTING : small changes
* security wasn't in bold
* more explanations on branches, ask to base always on develop
2019-07-22 11:34:48 +02:00
Pierre Goiffon
e4ba2b0828 N°2366 Remove unused iTopArchive class 2019-07-22 09:02:45 +02:00
Pierre Goiffon
28d00cc7c9 N°2366 Remove zip from mandatory PHP ext 2019-07-22 09:02:44 +02:00
Pierre Goiffon
b897da8f6f N°2404 remove charset/collation DB parameters
Are constants since N°1001 (iTop 2.5.0)
2019-07-19 16:58:25 +02:00
Pierre Goiffon
dc868b16ab N°2366 remove DBBackup::CreateZip
* remove method and its dependencies
* create \utils::GetFileMimeType
* in \DBBackup::DownloadBackup mime type isn't hardcoded anymore
2019-07-19 15:51:05 +02:00
Pierre Goiffon
ee2e109769 N°2269 Font Awesome v5 : kept old CSS name for compat 2019-07-19 10:43:58 +02:00
Pierre Goiffon
2b955ddd53 N°2269 Font Awesome v5 : move back lib from /lib to /css
Thanks to the team, yeah you're right, this wasn't a good idea (did because there is a way to use Font Awesome in JS, but we'll see if we do so in the future, for now we don't :) )
2019-07-19 10:16:22 +02:00
Molkobain
33cb4fd42b N°902.2 Portal: Add support for columns sorting in BrowseBrick's "lazy" mode
(And start refactoring in ManageBrick)
2019-07-18 12:11:09 +02:00
Pierre Goiffon
a03af7e9ef 🐛 N°2240 Fix startup.inc.php cannot be called alone anymore 2019-07-17 16:50:48 +02:00
Molkobain
90dbc35d41 N°902 Portal: Add support for columns sorting in ManageBrick's "lazy" mode 2019-07-17 15:40:44 +02:00
Molkobain
66e9fc2068 N°2396 Autocomplete: Harmonize accent handling 2019-07-17 15:40:43 +02:00
Molkobain
0a9b376684 N°2324 Remove legacy portal security check 2019-07-17 15:40:43 +02:00
Molkobain
83ba909c08 N°2112 Remove legacy end-user portal
- Accessing the /portal URL redirects to the new portal (/pages/exec.php?exec_module=itop-portal-base&...)
- Removed /itop_design/portals/portal[@id="legacy_portal"] from standard datamodel, adapt your XML accordingly
- Removed /itop_design/constants/constant[@id^="PORTAL_"] from standard datamodel, adapt your XML accordingly
- Removed PortalWebPage & TransactionException classes
- Added a warning in setup when no portal is selected
2019-07-17 15:40:43 +02:00
Pierre Goiffon
9ed33f16dd N°2269 Font Awesome v5 : fix new lines breaking code :(
Those regressions were introduced in a4743901
Saw with a JS error in schema.php
2019-07-17 11:02:26 +02:00
Pierre Goiffon
e9fdb61bb5 N°2269 Font Awesome v5 : remove unused CSS 2019-07-17 10:24:07 +02:00
Pierre Goiffon
073b1cd303 N°2018 Backup : fix version check for MySQL8
Now uses \CMDBSource::GetDBVersion instead of mysqldump -V
2019-07-16 18:05:31 +02:00
Pierre Goiffon
e712791f43 Merge remote-tracking branch 'origin/master' into develop 2019-07-16 17:45:24 +02:00
Pierre Goiffon
fefd9aae95 N°2399 backup : throw exception and log error if cannot create archive
(before error was silently ignored)
2019-07-16 17:44:56 +02:00
Pierre Goiffon
a4743901a3 N°2269 Font Awesome : update iTop for new v5 icons
* remove useless css in light-grey (weren't used)
* change icons class names from v4 to v5
2019-07-16 12:20:59 +02:00
Pierre Goiffon
ced9489643 N°2269 Add Font-Awesome v5.9.0
Also add v4-shims for compatibility. Will be removed with N°2393
2019-07-16 09:57:51 +02:00
Pierre Goiffon
0c45a0ca49 N°2269 Remove Font-Awesome v4 2019-07-16 09:57:51 +02:00
Pierre Goiffon
f3572e82ec N°2060 fix portal crash if css not yet compiled
Was using services.yaml parameters before they were defined
2019-07-16 09:57:30 +02:00
Pierre Goiffon
dd620022a8 📝 new iTop security policy (#85) 2019-07-16 08:47:30 +02:00
Molkobain
2237ec581c N°2323.7 Fix images display in browse brick as a list 2019-07-15 13:41:36 +02:00
Eric
a0cd70ae71 N°2240 - Supportability - Maintenance mode (setup) 2019-07-12 16:13:13 +02:00
Eric
80fce579a0 N°2240 - Supportability - Maintenance mode (soap message removed) 2019-07-12 15:35:20 +02:00
Pierre Goiffon
1313484ebe 💄 setup : update progress bar if error occurs 2019-07-12 11:14:53 +02:00
Pierre Goiffon
95aa541293 N°2150 Archive_Tar update : fix warnings on overloaded methods
In Archive_Tar the methods signatures did change... But the overrides were useless (same code or direct call to parent)
2019-07-12 10:34:23 +02:00
Molkobain
c4702f6a87 Merge remote-tracking branch 'origin/develop' into develop 2019-07-12 10:25:17 +02:00
Molkobain
5c0fc0bec5 N°2323.6 Reintegrate fixes in the new Symfony portal 2019-07-12 10:17:58 +02:00
Stephen Abello
d8de7b19cb N°2226: Add missing files for Scss v1.0.0 2019-07-12 10:13:00 +02:00
Molkobain
38640b01a8 Merge branch 'feature/b2060-migrate-silex-to-symfony' into develop
# Conflicts:
#	core/dbsearch.class.php
#	datamodels/2.x/itop-portal-base/module.itop-portal-base.php
#	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
#	datamodels/2.x/itop-portal/module.itop-portal.php
2019-07-12 09:57:16 +02:00
Eric
a11e783867 N°2240 - Supportability - Maintenance mode (soap message) 2019-07-12 09:04:10 +02:00
Molkobain
8ca2fffa78 N°2060 [WIP] Initialisation of the portal application: Remove Silex framework files from embedded libs 2019-07-11 17:50:36 +02:00
Molkobain
3f3cbd17ed N°2060 [WIP] Initialisation of the portal application: Remove Silex portal files 2019-07-11 17:50:31 +02:00
Molkobain
5a18769336 N°2060 [WIP] Initialisation of the portal application: Make AggregatePageBrick work again 2019-07-11 17:45:43 +02:00
Molkobain
cd6fe171cd N°2060 [WIP] Initialisation of the portal application: Refactor way brick controllers forward actions between each others 2019-07-11 17:45:21 +02:00
Molkobain
ee45e546a8 N°2060 [WIP] Initialisation of the portal application: Code cleanup 2019-07-11 16:44:22 +02:00
Molkobain
c8be217a1d N°2060 [WIP] Initialisation of the portal application: Make CreateBrick work again 2019-07-11 16:04:06 +02:00
Molkobain
618df6de1d N°2060 [WIP] Initialisation of the portal application: Fix class extended by ObjectController 2019-07-11 16:03:44 +02:00
Pierre Goiffon
e6e79df8db N°2150 update Archive_Tar to 1.4.7
Now we don't have anymore some Combodo specific code inside the lib \o/
2019-07-11 14:30:30 +02:00
Eric
509ca47b36 N°2240 - Supportability - Maintenance mode 2019-07-11 12:04:11 +02:00
Eric
066353e1e7 N°2240 - Supportability - Maintenance mode 2019-07-11 12:00:15 +02:00
Eric
a6737afb2f N°2240 - Supportability - Maintenance mode 2019-07-11 10:22:39 +02:00
Molkobain
d5b3a62df5 N°2060 [WIP] Initialisation of the portal application: Code cleanup 2019-07-10 11:55:47 +02:00
Eric
7f82faefe1 N°679 - DB inconsistency protection 2019-07-10 11:49:44 +02:00
Molkobain
08731857e5 N°2060 [WIP] Initialisation of the portal application: Code cleanup 2019-07-10 11:45:47 +02:00
Molkobain
90062acc35 N°2060 [WIP] Initialisation of the portal application: Fix typo 2019-07-10 11:38:22 +02:00
Molkobain
030d912820 Code cleanup
- Format code accordingly to coding conventions
- Add / update PHPDoc all over the place
- Suppress most of the warnings that did not have a big impact on code's logic
2019-07-09 19:10:16 +02:00
Molkobain
9e9187b169 N°2060 [WIP] Initialisation of the portal application: Huge code cleanup
- Rename variables and methods in iTop files to match coding conventions
- Format code accordingly to coding conventions
- Add / update PHPDoc all over the place
- Suppress most of the warnings that did not have a big impact on code's logic
2019-07-09 19:08:40 +02:00
Molkobain
c1258d0527 N°2060 [WIP] Initialisation of the portal application:
- Fix attachment download: Controller's action was still using the Silex\Application object to return a response.
- Push all cleanup modifications as well by mistake...
2019-07-09 17:54:37 +02:00
Molkobain
b7039c81ba N°2060 [WIP] Initialisation of the portal application: Object's routes
- Fix autocomplete search route path was incorrect (missing parameters)
- Fix generic search route by removing the default controller as it didn't exist
- Remove regular search route as it was never used / implemented
2019-07-09 16:15:14 +02:00
Molkobain
7088b96c16 N°2060 [WIP] Initialisation of the portal application: Enable webprofiler for easier debug! 2019-07-08 16:59:46 +02:00
Molkobain
d31273dff5 N°2060 [WIP] Initialisation of the portal application: PHPDoc 2019-07-08 16:04:25 +02:00
Molkobain
85460ef6e2 N°2060 [WIP] Initialisation of the portal application:
- Remove old composer.json that was not necessary
- Add empty model.itop-portal-base.php file for future XML snippets to be loaded
- Increase module version number to 2.7.0
2019-07-08 15:49:07 +02:00
Molkobain
5ab059c404 N°2060 [WIP] Initialisation of the portal application:
- Simplify PortalUrlMaker to avoid necessity to copy most of the code. Drawback: BC break, check migration notes.
- Fix multiple portal instances (in the same running process)
- Refactor portal constants into env. vars
- Fix cache path for services (ScopeValidator & LifecycleValidator)
- Change evalution order of the portal id ($_ENV['PORTAL_ID'] > $_GET('portal_id'] > PORTAL_ID)
2019-07-08 15:44:39 +02:00
Molkobain
322ea1870d N°2060 [WIP] Initialisation of the portal application:
- Refactor kernel bootstrapping:
  - Make bin/console from SF work
  - Make iTopPortalEditUrlMaker / iTopPortalViewUrlMaker work again
- Add classmap to /application in composer.json
2019-07-05 15:53:05 +02:00
Pierre Goiffon
878b87b68c N°2349 fix GroupBy dashlet on classes with ExternalField to ExternalField 2019-07-05 12:10:04 +02:00
Molkobain
ab3024d98a N°2060 [WIP] Initialisation of the portal application: Restore ApplicationHelper.php history 2019-07-05 11:42:39 +02:00
Molkobain
d447c96385 N°2060 [WIP] Initialisation of the portal application: Temporary delete ApplicationHelper.php in order to restore history 2019-07-05 11:36:02 +02:00
Molkobain
e3ac4d1039 N°2060 [WIP] Initialisation of the portal application:
- Make ManageBrick work again!
- Make ObjectController::CreateFromFactory() work again
- Refactor object form rendering from Twig string (in memory) in the "object_form_handler" service
- Fix ApplicationHelper::GetLoadedListFromClass()
2019-07-05 11:30:08 +02:00
Molkobain
8fc4aa7240 N°2060 [WIP] Initialisation of the portal application: Fix template paths in ObjectController 2019-07-04 17:12:53 +02:00
Molkobain
aff9d8cbca N°2060 [WIP] Initialisation of the portal application:
- Fix object_brick.yaml actions path (Double slashes)
- Add declaration for "security_helper" service
- Refactor ObjectFormManager and ObjectFormHandlerHelper for SF
2019-07-04 17:06:58 +02:00
Molkobain
260c15c6b6 N°2060 [WIP] Initialisation of the portal application:
- Refactor of BrickCollection to store properties at the root level, hence having easier API
- Code cleanup (Coding conventions, PHPDoc)
- Extract ObjectController::HandleForm() into new "object_form_handler" service
- Migrate ObjectController and UserProfileBrickController to SF
- Remove route/action "Search hierarchy" from ObjectController as it was never implemented
2019-07-04 15:14:46 +02:00
Purple Grape
dab48146bf Update zh_cn.dict.itop-config-mgmt.php
translation correction
2019-07-03 08:42:10 +02:00
Pierre Goiffon
14ae9f0809 Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	css/css-variables.scss
#	datamodels/2.x/authent-external/module.authent-external.php
#	datamodels/2.x/authent-ldap/module.authent-ldap.php
#	datamodels/2.x/authent-local/module.authent-local.php
#	datamodels/2.x/itop-attachments/module.attachments.php
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php
#	datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
#	datamodels/2.x/itop-config/module.itop-config.php
#	datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php
#	datamodels/2.x/itop-full-itil/module.itop-full-itil.php
#	datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal-base/module.itop-portal-base.php
#	datamodels/2.x/itop-portal/module.itop-portal.php
#	datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php
#	datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/module.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php
#	datamodels/2.x/version.xml
2019-07-02 15:09:11 +02:00
Pierre Goiffon
b3369c8b0e Update version number for 2.5.3 beta 2019-07-02 14:54:36 +02:00
Molkobain
1115cdd2ec Portal: Fix sidebar menu entries being to narrow when only "Home" displayed 2019-07-01 22:33:15 +02:00
Molkobain
44673b9fd2 N°2060 [WIP] Initialisation of the portal application: Replace SF default UrlGenerator with our own 2019-07-01 17:50:25 +02:00
Molkobain
faf9b32176 N°2060 [WIP] Initialisation of the portal application 2019-07-01 17:47:45 +02:00
Molkobain
a93c1092fc N°2060 [WIP] Initialisation of the portal application:
- Refactor SecurityHelper into SF service (DI)
- Make BrowseBrick work (again!)
- Extract methods from BrowseBrickController to a dedicated service (BrowseBrickHelper)
2019-07-01 17:41:12 +02:00
Pierre Goiffon
6c81163d20 Merge remote-tracking branch 'origin/master' into develop 2019-07-01 17:30:21 +02:00
Molkobain
f71edbf45b N°2060 [WIP] Initialisation of the portal application: Refactor to make services 'combodo.current_user' and 'combodo.current_contact.photo_url' work 2019-06-28 17:26:30 +02:00
Molkobain
0d3e48475e N°2060 [WIP] Initialisation of the portal application: Code cleanup 2019-06-28 17:26:30 +02:00
Molkobain
ae8451e837 N°2060 [WIP] Initialisation of the portal application:
- Default env. is now production.
- Debug mode through url param. is now available like in Silex version
2019-06-28 17:26:24 +02:00
Pierre Goiffon
33903f570b N°2174 ExternalField to friendlyname : restore behavior without dict key
* displaying "class -> field" was breaking exports (columns labels for ext fields to friendlynames in default datamodel, for example UserRequest.agent_id_friendlyname)
* dict key is always searched first, so a custom label can always be set
2019-06-28 15:13:15 +02:00
Molkobain
123e734046 N°2060 [WIP] Initialisation of the portal application: Fix file format (tab used instead of spaces) 2019-06-28 15:09:06 +02:00
Molkobain
008261e918 N°2060 [WIP] Initialisation of the portal application: Part of the "brick_collection" service refactoring 2019-06-28 15:08:30 +02:00
Molkobain
d388086baa N°2060 [WIP] Initialisation of the portal application: Fix composer.json file
- Reupgrade Symfony/* to v3.4.* instead of v3.4.12 thanks to symfony/polyfill-php70
- Remove "replace" key that was necessary for Flex
- Add "classmap" key to auload all classes from <itop>/core (eg. \ModuleDesign)
2019-06-28 15:07:12 +02:00
Stephen Abello
f7af705bb5 Fix scss and css errors 2019-06-28 14:25:25 +02:00
Stephen Abello
a827cb7546 N°2226: Upgrade ScssPHP to v1.0.0 2019-06-28 14:24:56 +02:00
Stephen Abello
c7fe6f388a N°2270: Upgrade bootstrap to v3.4.1 2019-06-28 14:22:35 +02:00
Molkobain
a04080a93e N°2060 [WIP] Initialisation of the portal application: Migrate routes to YAML format 2019-06-27 17:11:04 +02:00
Molkobain
04158f5589 N°2060 [WIP] Initialisation of the portal application: Fix of the composer.json file
- Temporary downgrade Symfony/* from v3.4.* to v3.4.12 because of a PHP7 dependency in Symfony/http-foundation
- Remove Flex as it was only compatible with PHP7
- Remove auto scripts relying on Flex (post update and post install)
- Rename rogue itop-portal-base/composer.json that should not be used (will be removed later)
2019-06-27 16:32:45 +02:00
bruno DA SILVA
127809a836 Merge remote-tracking branch 'origin/support/2.5' 2019-06-26 15:41:13 +02:00
bruno DA SILVA
6c948873ff N°2323.6 Fix regression introduced in previous commit 2019-06-26 15:38:42 +02:00
Stephen Abello
a93be39aeb N°2166: Fix regression introduced in b157fad 2019-06-26 14:15:04 +02:00
Stephen Abello
6de6c38834 N°2268: Upgrade jQuery to v3.4.1 and jQuery migrate to v3.1.0. Remove version number from both libraries filename 2019-06-26 10:36:40 +02:00
Pierre Goiffon
a5745ba72d N°2345 privUITransactionFile : avoid create dir race condition 2019-06-26 10:17:47 +02:00
Eric
7c93d116ec 2.6.2 fix module version 2019-06-25 16:56:15 +02:00
Molkobain
0867d8a3c4 N°2060 [WIP] Initialisation of the portal application 2019-06-25 11:58:38 +02:00
Stephen Abello
71f5d29cba N°2271: Upgrade CKEditor to v4.11.4 2019-06-24 15:52:26 +02:00
Stephen Abello
f948d6e026 N°2336: Set right parameter for --ssl-mode 2019-06-21 14:37:48 +02:00
Stephen Abello
a222ead43c N°2336: Use --ssl-mode instead of --ssl with MySQL >= 5.7.0 2019-06-21 13:59:36 +02:00
Stephen Abello
d6bfbbcd30 N°1529: Allow parameter OpenSSLMcryptCompatibility to be used 2019-06-21 11:08:23 +02:00
Molkobain
17df9d0f9d Merge remote-tracking branch 'origin/support/2.5' 2019-06-21 10:03:17 +02:00
Molkobain
93099ea3c7 N°2323.5 Fix regression introduced in previous commit
Could not upload images in HTML field anymore
2019-06-21 10:01:31 +02:00
Molkobain
2f9e050e2b N°2323.4 Fix regression introduced in previous commit
Current user picture was no longer displayed in the portal

(cherry picked from commit 56b9eb6cf3)
2019-06-20 17:52:26 +02:00
Eric
e68340273b Merge branch 'master' into develop
# Conflicts:
#	core/config.class.inc.php
#	core/htmlsanitizer.class.inc.php
#	css/css-variables.scss
#	css/light-grey.css
#	datamodels/2.x/version.xml
#	dictionaries/zh_cn.dictionary.itop.ui.php
#	synchro/synchrodatasource.class.inc.php
2019-06-20 16:11:38 +02:00
Eric
5c341138e1 N°2129 - Fix lost dict. entries by toolkit updating script 2019-06-20 15:48:41 +02:00
Molkobain
56b9eb6cf3 N°2323.4 Fix regression introduced in previous commit
Current user picture was no longer displayed in the portal
2019-06-20 10:34:10 +02:00
Pierre Goiffon
db20244212 N°2174 External field label retrieval : fix default value
\AttributeDefinition::GetLabel with default null was returning att code !! Added PHPDoc and changed default value
2019-06-19 16:13:29 +02:00
Pierre Goiffon
6f9f74e72f N°2230 BR translations
Many thanks to our client Pimkie !
2019-06-19 15:55:05 +02:00
Eric
8070d5b9b7 2.6.2-beta 2019-06-19 15:08:40 +02:00
Molkobain
6197ecbaf4 Update version number for 2.6.2 beta 2019-06-19 14:43:21 +02:00
Eric
ca585d3f42 N°2044 - Search Form Prefill not effective when searching objects to add to an AttributeLinkedSet (n:1) 2019-06-18 15:53:48 +02:00
Eric
02c78d4044 N°2278 - Object-copier: Fix n:n link attributes set to default on copy
(cherry picked from commit 6564d84a2f)
2019-06-18 14:57:45 +02:00
Pierre Goiffon
9e5d668c02 N°2328 TagSet : allow to create codes with 3 characters
(was wrongly 4 min before)
2019-06-18 14:36:47 +02:00
Pierre Goiffon
051656f295 N°2174 External field : change label retrieval
* Now the  dict entry will be used even for friendly names
* If no dict keys exists, then the class path plus field name will be returned
2019-06-18 11:27:02 +02:00
Eric
9a51a44549 Merge remote-tracking branch 'origin/support/2.5' 2019-06-18 10:50:04 +02:00
Eric
5102b113ed N°2323 - Fix calls to ajax endpoints
(cherry picked from commit c723d19e01)
2019-06-18 10:45:50 +02:00
Eric
f1e4d94499 N°2323 - Fix calls to ajax endpoints for portal 2019-06-18 10:43:45 +02:00
Pierre Goiffon
d04102bab3 N°2243 IT translations : final versions 2019-06-18 08:56:06 +02:00
Eric
c723d19e01 N°2323 - Fix calls to ajax endpoints 2019-06-17 15:59:43 +02:00
Eric
dac77e0606 Revert id in select 2019-06-13 10:24:01 +02:00
Eric
02b98543d9 Fix Bad db_key_field definition 2019-06-13 10:06:52 +02:00
Eric
519aaadd9a N°2157 - Fix backup manual warning 2019-06-13 09:44:40 +02:00
bruno DA SILVA
fe27fef530 Merge remote-tracking branch 'origin/master' 2019-06-12 18:27:11 +02:00
bruno DA SILVA
72dbb6f937 N°1373 - supportability: log stack trace on DBObject::Reload error 2019-06-12 18:26:35 +02:00
Pierre Goiffon
ccb29d10ae N°2243 IT translations : keys in parent classes 2019-06-12 18:09:41 +02:00
Pierre Goiffon
e479775833 N°2243 IT translations
Thanks to our client Pimkie !
2019-06-12 18:09:41 +02:00
Stephen Abello
86a7192f5a N°2160: Correctly initialize fields depending on a select field with only 1 value 2019-06-12 14:39:29 +02:00
Pierre Goiffon
4a1be13904 N°2208 new IT translations 2019-06-12 11:02:49 +02:00
Pierre Goiffon
0f3347f64d 📝 Fix some PhpDoc 2019-06-12 10:39:30 +02:00
Eric
6564d84a2f N°2278 - Object-copier: Fix n:n link attributes set to default on copy 2019-06-12 10:17:19 +02:00
Eric
f4747c5cef PHPStorm 2019-06-12 09:46:21 +02:00
Eric
1e92c2f28a Sanitize error message 2019-06-11 17:18:36 +02:00
Eric
e4b097b196 Add KPIs to search 2019-06-11 17:18:35 +02:00
Eric
85653c9ffc Sanityzation:
- no use of cache to build queries when the flag is set
- Use class key instead of 'id' for default select column
2019-06-11 17:18:35 +02:00
Pierre Goiffon
b1761e04b2 🎨 DataSynchro : remove some warnings, code formating
(cherry picked with small modifications from commit 26dcaa0ded : it was reverted on master as this branch contains a fix only version, ok to commit it on develop though)
2019-06-11 17:01:08 +02:00
Stephen Abello
da5d8b20fa N°2044: Enable Search form prefill when adding objects to 1:n 2019-06-11 16:59:27 +02:00
Molkobain
9a5a5f858f N°2235.2 Fix emptied URLs on object modification when URL starts with a placeholder 2019-06-11 16:13:53 +02:00
Pierre Goiffon
d6b194b0aa 📝 Change back CONTRIBUTING file name to uppercase 2019-06-11 14:53:56 +02:00
Pierre Goiffon
50ed52bacc Fix ticket ref uniqueness rule declaration (many thanks @jbostoen !) 2019-06-11 09:08:36 +02:00
Thomas Casteleyn
daa906a697 Only set Ticket ref if not yet present via import or synchro (#82)
New non blocking uniqueness rule on Ticket.ref to warn when having ref duplicates
2019-06-10 11:28:24 +02:00
Stephen Abello
a0cd281c42 ⬆️ iTop >=2.6.1 supports PHP 7.3.x 2019-06-07 16:16:23 +02:00
Pierre Goiffon
ecd8f40c0f 🔧 Create a .editorconfig
See https://editorconfig.org/
2019-06-07 15:22:21 +02:00
Pierre Goiffon
616c1b9b73 🔧 Update PhpStorm formatter 2019-06-07 15:21:59 +02:00
Stephen Abello
6bb5606c00 N°2216: Set the mandatory icon inline with its field 2019-06-06 15:56:05 +02:00
Eric
a6ceb88fca N°2216 - Request Template Mandatory fields are not marked as "Mandatory" 2019-06-06 15:14:31 +02:00
Eric
7bb7a94fbc N°2291 - Fix blinking of warning image on mandatory HTML field 2019-06-06 15:14:31 +02:00
Pierre Goiffon
e4324cedb4 N°2235 Enable notification placeholders in hyperlinks
(cherry picked from commit bc55bfbee1)
2019-06-06 10:59:44 +02:00
Eric
07781c7c9d 1023 - Email Outlook : Fix iTop page broken by tag <o:p> 2019-06-06 09:56:31 +02:00
Stephen Abello
ea58a807fc N°2044: Forgot to add a variable in 7c1290f 2019-06-06 09:44:20 +02:00
Pierre Goiffon
fb6806a1c1 🎨 DataSynchro : some phpdoc and formatting 2019-06-05 17:26:00 +02:00
Pierre Goiffon
d24870e0ae Merge remote-tracking branch 'origin/master' into develop 2019-06-04 16:23:58 +02:00
Pierre Goiffon
166451c4e9 N°532 autocomplete : fix regressions introduced in ebb541e4
* multiple separator
* XSS filter
* search field value reset
2019-06-04 11:32:08 +02:00
Stephen Abello
7c1290f684 N°2044: Enable Search form prefill when adding objects to 1:n 2019-06-04 11:18:30 +02:00
Stephen Abello
179c774ba8 N°2207: Copy the content from the right source file into production.delta.prev.xml 2019-06-03 12:36:05 +02:00
Guy Couronné
b57c224052 📈 Add KPI on API Rest (#67) 2019-06-03 09:07:25 +02:00
Stephen Abello
e886d80de6 N°2215: Set autocomplete minimum width (instead of its width) to its parent field width 2019-05-31 12:14:41 +02:00
Pierre Goiffon
3791b2dd24 📝 Change return type for \MetaModel::GetObject 2019-05-28 17:18:07 +02:00
Pierre Goiffon
12c916f4e5 📝 Add @var for iApplicationUIExtension loops 2019-05-28 17:16:02 +02:00
Pierre Goiffon
ba7e00e130 N°2211 DataSynchro : rollback oChange remove in DBUpdate() + added log
Be conservative : this is a fix only release... oChange will be done in the future, it's a work that should be done everywhere in iTop and not only DataSynchro
2019-05-28 17:16:02 +02:00
Molkobain
895abde39c N°2060 [WIP] Renamed Silex portal dir before starting Symfony integration 2019-05-28 12:04:34 +02:00
Eric
bada955725 N°2211 - DataSynchro : fix delete rules
- code review bug fixes and comments
2019-05-28 10:40:00 +02:00
Stephen Abello
f9e18675f3 N°2062: When a table displaying all rows is rebuilt (eg: by adding a column), reordering using columns now works 2019-05-24 16:20:31 +02:00
bruno DA SILVA
22416cc0be :note: PhpDoc generation for the public API
- typo (tunning => tuning)
 - rework of the README.md
2019-05-24 14:05:22 +02:00
Pierre Goiffon
ec086ad94a 📝 Fix /doc readme (thanks @jbostoen !) 2019-05-23 18:25:26 +02:00
Pierre Goiffon
17d4b570e8 N°2211 DataSynchro fix regression in DoJob2 method (create new iTop objects)
The \SynchroExecution::$m_oLastFullLoadStartDate is also used in DoJob2... So move back its instantiation in \SynchroExecution::PrepareProcessing, but with more variables and comment : the attribute is null until PrepareProcessing()
2019-05-23 18:15:50 +02:00
Pierre Goiffon
2dfad12553 Revert "🎨 DataSynchro : remove some warnings, code formating"
This reverts commit 26dcaa0ded.
Such modifications does not belong to a fix only branch !!
2019-05-23 11:58:10 +02:00
Pierre Goiffon
b7dc55604e 🎨 DataSynchro : rename DoJob1/2/3 methods 2019-05-23 11:19:18 +02:00
Pierre Goiffon
991bc359cb Merge remote-tracking branch 'origin/master' into develop 2019-05-23 10:52:34 +02:00
Pierre Goiffon
26dcaa0ded 🎨 DataSynchro : remove some warnings, code formating 2019-05-23 10:48:54 +02:00
Pierre Goiffon
b5d3ddb7e3 N°2211 DataSynchro : fix deletion rules regression when using synchro_exec.php
* fix regression : no update if exec phase only and full load interval <= 0
* fix regression : update if exec phase only and full load interval > 0
* some PHPDoc
* move back \SynchroExecution::$m_oLastFullLoadStartDate init to constructor
* add a boolean member to indicate if LastFullLoadStartDate was passed by caller
* factorize database current datetime retrieval
2019-05-23 10:48:54 +02:00
Pierre Goiffon
8b178914b3 Fix \DBObject::DBDelete PHPDoc 2019-05-22 14:46:53 +02:00
OИUЯd da silva
ce6fd4d775 bugfix: remove lower cased version of the README.md 2019-05-22 11:44:45 +02:00
Molkobain
bc55bfbee1 N°2235 Enable notification placeholders in hyperlinks 2019-05-21 17:38:28 +02:00
OИUЯd da silva
9c75cb4537 :note: PhpDoc generation for the public API (#63)
- generate phpdoc to a dokuwiki compatible format
- add/update the phpdoc of a selection of class methods
2019-05-21 12:05:52 +02:00
bruno DA SILVA
633fa343a5 N°775 - request template : fix mandatory field list with only one possible value 2019-05-20 17:47:15 +02:00
bruno DA SILVA
a1d01e252b N°2191 - Stopwatch sub-items no more available as search criteria 2019-05-20 17:03:25 +02:00
Stephen Abello
e9119e95ac N°1164, SF#1491: Add code snippets with syntax highlighting to CaseLog/HTML fields 2019-05-17 15:59:01 +02:00
Molkobain
af332a34d6 ♻️ Make ticket reference generation working with new sub-classes (#78) 2019-05-17 11:21:24 +02:00
Stephen Abello
b157fad0b6 N°2166: Portal: fields in CKEditor modals are inactive 2019-05-15 15:41:38 +02:00
Stephen Abello
70d2bb163c N°2179: Strengthen data/setup directory creation 2019-05-15 11:27:53 +02:00
Pierre Goiffon
d9bf3339d2 linkswidget : remove dead (unreachable) code 2019-05-13 09:57:24 +02:00
Pierre Goiffon
b4ee5cd59c Merge branch 'master' into develop 2019-05-13 08:54:00 +02:00
Pierre Goiffon
fae6c89e9a Merge remote-tracking branch 'origin/support/2.5' 2019-05-13 08:53:50 +02:00
Pierre Goiffon
89d310258b N°2198 ReloadBlock call and parameter escaping improvements
Use an anonymous function instead of a string to get rid of a useless level of escaping
Use json_encode instead of addslashes
(many thanks for the tips @bruno-ds !!)
2019-05-09 10:37:34 +02:00
Pierre Goiffon
a58529f46c N°2198 Fix "invalid filter" error when refreshing "Requests assigned to me" 2019-05-07 18:39:38 +02:00
Pierre Goiffon
12a2035791 N°2192 table-selectable-lines : add forgotten param 2019-05-07 11:53:39 +02:00
Pierre Goiffon
b1ff7f0e9b N°2192 table-selectable-lines : replace another :has selector, and use existing constant 2019-05-07 11:44:41 +02:00
OИUЯd da silva
23cf2b91f4 make demo_mode effect more expressive
closes #71
2019-05-06 11:42:27 +02:00
Pierre Goiffon
3c4fe338b6 N°2192 fix freeze when lots of lines in a table with selectable lines
Many thanks to Jeffrey Bostoen (@jbostoen) for the bug report !
2019-05-03 17:43:36 +02:00
Pierre Goiffon
6159ab33b7 🔊 DBSearch ajax update : better handling of invalid queries 2019-05-02 17:33:41 +02:00
Pierre Goiffon
58ffd37f9e N°1283 Add option to open WebPageMenuNode in new window 2019-04-30 16:11:29 +02:00
Thomas Casteleyn
5c9d98d12c Fix cron crash when MySQL connection lost (#80) 2019-04-30 11:02:02 +02:00
Pierre Goiffon
15362df69a Merge branch 'master' into develop 2019-04-29 11:26:52 +02:00
Pierre Goiffon
77f757995e Merge remote-tracking branch 'origin/support/2.5' 2019-04-29 11:26:37 +02:00
Thomas Casteleyn
2858d13fd5 🐛 Fix default usage of iTopMutex when TLS is enabled
See R-021467
2019-04-29 11:18:54 +02:00
Thomas Casteleyn
ab0c97621a Add support to optionally mention username in password reset mail (#76)
Can now use login in reset password email dict keys
* UI:ResetPwd-EmailSubject
* UI:ResetPwd-EmailBody
2019-04-29 11:12:19 +02:00
Stephen Abello
99f398a87e N°1529: Correct wrong constant name for Mcrypt, handle iv generation fails to avoid data corruption 2019-04-24 11:44:43 +02:00
Molkobain
dccdd84c25 ♻️ Fix unit tests for compatibility with CI 2019-04-17 14:52:58 +02:00
Guy Couronné
d4d16f43ac Add status.php for getting iTop's status (#56)
Allow for HAProxy and monitoring to get iTop's status
 Add tests for status

Signed-off-by: Guy Couronné <gcouronne:@sapiens.biz>
2019-04-17 09:21:45 +02:00
Pierre Goiffon
a773a4957a Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	README.md
2019-04-10 16:26:37 +02:00
Pierre Goiffon
87ee731dbe 📝 Update readme for 2.6.1 2019-04-10 16:24:34 +02:00
Pierre Goiffon
5dc8283229 💡 Some doc for Config::GetModule* methods 2019-04-10 11:27:11 +02:00
Pierre Goiffon
e9a2528b13 📝 CONTRIBUTING : use emoji codes instead of characters 2019-04-03 15:33:15 +02:00
Pierre Goiffon
e9d72bd022 💡 Fix some PhpDoc for \utils::DoPostRequest 2019-04-03 15:29:06 +02:00
Pierre Goiffon
b166686a15 📝 CONTRIBUTING : add a note on datamodel modifications (#68)
* %memo% CONTRIBUTING : suggest use SF tickets for default datamodel modifications
2019-04-02 09:02:02 +02:00
Pierre Goiffon
ca95060b05 idea update for 2019.1 2019-03-29 16:38:02 +01:00
Molkobain
f2380ae354 🌐 Add slovak dictionary files in all modules for future translation 2019-03-28 11:07:18 +01:00
Molkobain
2d039af278 🎨 Format dictionary entries 2019-03-28 11:05:15 +01:00
Molkobain
831879fe37 🐛 N°2122 Fix missing dictionary entries for "Service families" menu of "Service Mgmt Provider" module 2019-03-28 10:31:48 +01:00
Pierre Goiffon
0451ae07c8 Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	dictionaries/sk.dictionary.itop.core.php
2019-03-27 16:33:43 +01:00
Molkobain
ea1dfd8933 👥 Add Martin Kincel to the contributors. Thanks for the slovak translations! 2019-03-27 11:43:35 +01:00
Molkobain
24519c69a4 🌐 Add new language for main features thanks to Martin Kincel! 2019-03-27 11:43:35 +01:00
Molkobain
3e55693bfa 👥 Add Martin Kincel to the contributors. Thanks for the slovak translations! 2019-03-27 11:34:32 +01:00
Molkobain
f5d0947b27 🌐 Add new language for main features thanks to Martin Kincel! 2019-03-27 11:33:08 +01:00
Vladimir Kunin
b5c4801beb 🌐 Russian translations for 2.6.1 (#70) 2019-03-25 16:29:05 +01:00
Molkobain
3eff8f62b1 Merge branch 'support/2.5' 2019-03-25 15:47:51 +01:00
Molkobain
16c8466841 N°2115 Fix regression introduced in N°1443: Left pane menu not showing due a JS error 2019-03-25 15:39:45 +01:00
Pierre Goiffon
2da181a399 Combodo PHPStorm settings : some accessibility inspections are now weak warns instead of warns 2019-03-22 18:00:54 +01:00
Thomas Casteleyn
6e8bcf7b69 Make setup backup location and name similar as other backups (#61) 2019-03-22 16:02:17 +01:00
Eric
b83e5e2b72 N°1618 - Fix custom date format: Fix issue with in-line creation 2019-03-22 15:42:11 +01:00
Pierre Goiffon
da6791d75f 💄 Setup wizard backup path : larger input widget 2019-03-22 15:38:18 +01:00
Eric
764b0f8e31 N°1846 - Fix Object Copier: Create Ticket from CI, duplicate links
(cherry picked from commit 44b7821015)
2019-03-22 11:06:23 +01:00
Pierre Goiffon
2a0928b4be Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	css/css-variables.scss
#	css/light-grey.css
#	datamodels/2.x/version.xml
2019-03-21 17:46:30 +01:00
Pierre Goiffon
cac0da4e3d Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	application/utils.inc.php
#	css/css-variables.scss
#	css/light-grey.css
#	datamodels/2.x/authent-external/module.authent-external.php
#	datamodels/2.x/authent-ldap/module.authent-ldap.php
#	datamodels/2.x/authent-local/module.authent-local.php
#	datamodels/2.x/itop-attachments/module.attachments.php
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php
#	datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
#	datamodels/2.x/itop-config/module.itop-config.php
#	datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php
#	datamodels/2.x/itop-full-itil/module.itop-full-itil.php
#	datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal-base/module.itop-portal-base.php
#	datamodels/2.x/itop-portal/module.itop-portal.php
#	datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php
#	datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/module.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php
#	datamodels/2.x/version.xml
2019-03-21 17:39:26 +01:00
Pierre Goiffon
7e064365eb N°1968 Uniqueness : fix only root disabled class was removed from searches
Now all of the following hierarchy is excluded
2019-03-21 17:25:51 +01:00
Pierre Goiffon
7c7382f372 N°1835 add new 'transaction_id' sanitize filter 2019-03-21 14:28:24 +01:00
Pierre Goiffon
4918b9c83a 💡 Add phpdoc for N°1835 new Sanitize param 2019-03-21 12:04:31 +01:00
Thomas Casteleyn
9bb365e60d Added example php location argument 2019-03-20 10:14:54 +01:00
Thomas Casteleyn
b3c80e6ecf Added changes as requested 2019-03-20 10:14:54 +01:00
Thomas Casteleyn
e5c77f64aa Update cron.cmd to have better defaults and remove references to old php version 2019-03-20 10:14:54 +01:00
Pierre Goiffon
ed95f4e05f 📦 Update CSS for 2.6.1 2019-03-19 11:58:48 +01:00
Pierre Goiffon
9f0e8dc49b 📦 Update CSS for 2.5.2 2019-03-19 11:47:29 +01:00
Pierre Goiffon
693fdfdc5b 📦 Update versions for 2.6.1, woops wrong version on previous commit 2019-03-19 11:39:11 +01:00
Pierre Goiffon
b8d5c01382 📦 Update versions for 2.5.2 2019-03-19 11:37:15 +01:00
Pierre Goiffon
01108ca83d 📦 Update versions for 2.6.1 2019-03-19 11:35:42 +01:00
Pierre Goiffon
3d5b7197f6 📦 Update modules versions for 2.6.1 2019-03-19 11:34:01 +01:00
Pierre Goiffon
d3db77c675 📦 Update modules versions for 2.5.2 2019-03-19 11:29:37 +01:00
Pierre Goiffon
c313ed2efc Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	composer.json
2019-03-19 11:18:18 +01:00
Pierre Goiffon
54c027823b 🌐 Fix dict automatic update 2019-03-19 11:14:39 +01:00
Pierre Goiffon
f63aceeabe Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	datamodels/2.x/authent-external/da.dict.authent-external.php
#	datamodels/2.x/authent-external/de.dict.authent-external.php
#	datamodels/2.x/authent-external/fr.dict.authent-external.php
#	datamodels/2.x/authent-external/hu.dict.authent-external.php
#	datamodels/2.x/authent-external/ja.dict.authent-external.php
#	datamodels/2.x/authent-external/nl.dict.authent-external.php
#	datamodels/2.x/authent-external/pt_br.dict.authent-external.php
#	datamodels/2.x/authent-external/zh_cn.dict.authent-external.php
#	datamodels/2.x/authent-ldap/cs.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/da.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/de.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/fr.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/hu.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/ja.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/nl.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/pt_br.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/ru.dict.authent-ldap.php
#	datamodels/2.x/authent-ldap/zh_cn.dict.authent-ldap.php
#	datamodels/2.x/authent-local/da.dict.authent-local.php
#	datamodels/2.x/authent-local/de.dict.authent-local.php
#	datamodels/2.x/authent-local/fr.dict.authent-local.php
#	datamodels/2.x/authent-local/hu.dict.authent-local.php
#	datamodels/2.x/authent-local/ja.dict.authent-local.php
#	datamodels/2.x/authent-local/nl.dict.authent-local.php
#	datamodels/2.x/authent-local/pt_br.dict.authent-local.php
#	datamodels/2.x/authent-local/ru.dict.authent-local.php
#	datamodels/2.x/authent-local/zh_cn.dict.authent-local.php
#	datamodels/2.x/itop-attachments/cs.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/da.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/de.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/en.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/es_cr.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/fr.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/hu.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/it.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/ja.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/nl.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/pt_br.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/tr.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/zh_cn.dict.itop-attachments.php
#	datamodels/2.x/itop-backup/da.dict.itop-backup.php
#	datamodels/2.x/itop-backup/de.dict.itop-backup.php
#	datamodels/2.x/itop-backup/es_cr.dict.itop-backup.php
#	datamodels/2.x/itop-backup/hu.dict.itop-backup.php
#	datamodels/2.x/itop-backup/it.dict.itop-backup.php
#	datamodels/2.x/itop-backup/ja.dict.itop-backup.php
#	datamodels/2.x/itop-backup/nl.dict.itop-backup.php
#	datamodels/2.x/itop-backup/tr.dict.itop-backup.php
#	datamodels/2.x/itop-change-mgmt-itil/da.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/de.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/fr.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/hu.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/it.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/ja.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/nl.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/ru.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt-itil/zh_cn.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/da.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/de.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/fr.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/hu.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/it.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/ja.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/nl.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/pt_br.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/ru.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/tr.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-change-mgmt/zh_cn.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/cs.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/da.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/de.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/es_cr.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/fr.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/hu.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/it.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/ja.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/nl.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/pt_br.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/ru.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/tr.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config-mgmt/zh_cn.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config/da.dict.itop-config.php
#	datamodels/2.x/itop-config/de.dict.itop-config.php
#	datamodels/2.x/itop-config/es_cr.dict.itop-config.php
#	datamodels/2.x/itop-config/fr.dict.itop-config.php
#	datamodels/2.x/itop-config/hu.dict.itop-config.php
#	datamodels/2.x/itop-config/it.dict.itop-config.php
#	datamodels/2.x/itop-config/ja.dict.itop-config.php
#	datamodels/2.x/itop-config/nl.dict.itop-config.php
#	datamodels/2.x/itop-config/pt_br.dict.itop-config.php
#	datamodels/2.x/itop-config/tr.dict.itop-config.php
#	datamodels/2.x/itop-datacenter-mgmt/cs.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/da.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/de.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/es_cr.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/fr.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/hu.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/it.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/ja.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/nl.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/pt_br.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/ru.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/tr.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-datacenter-mgmt/zh_cn.dict.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/cs.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/da.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/de.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/es_cr.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/fr.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/hu.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/it.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/ja.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/nl.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/pt_br.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/ru.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/tr.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-endusers-devices/zh_cn.dict.itop-endusers-devices.php
#	datamodels/2.x/itop-hub-connector/cs.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/da.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/de.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/es_cr.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/fr.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/hu.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/it.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/ja.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/nl.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/pt_br.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/ru.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/tr.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/zh_cn.dict.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/cs.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/da.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/de.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/es_cr.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/hu.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/it.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/ja.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/nl.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/pt_br.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/ru.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/tr.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-incident-mgmt-itil/zh_cn.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/cs.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/da.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/de.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/hu.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/it.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/ja.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/nl.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/tr.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/zh_cn.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal-base/da.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/de.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/hu.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/it.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/ja.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/nl.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/ru.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/tr.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal-base/zh_cn.dict.itop-portal-base.php
#	datamodels/2.x/itop-problem-mgmt/da.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/de.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/fr.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/hu.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/it.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/ja.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/nl.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/ru.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-problem-mgmt/zh_cn.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-request-mgmt-itil/cs.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/da.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/de.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/en.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/es_cr.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/fr.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/hu.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/it.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/ja.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/pt_br.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/ru.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/tr.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/zh_cn.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/cs.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/da.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/de.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/fr.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/hu.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/it.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/ja.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/nl.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/ru.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/tr.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-request-mgmt/zh_cn.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/cs.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/da.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/de.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/en.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/fr.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/hu.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/it.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/ja.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/nl.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/pt_br.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/ru.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/tr.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt-provider/zh_cn.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/cs.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/da.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/de.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/en.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/fr.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/hu.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/it.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/ja.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/nl.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/pt_br.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/ru.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/tr.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-service-mgmt/zh_cn.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/cs.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/da.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/de.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/es_cr.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/fr.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/hu.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/it.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/ja.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/nl.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/pt_br.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/ru.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/tr.dict.itop-sla-computation.php
#	datamodels/2.x/itop-sla-computation/zh_cn.dict.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/cs.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/da.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/de.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/es_cr.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/fr.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/hu.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/it.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/ja.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/nl.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/pt_br.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/ru.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/tr.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-storage-mgmt/zh_cn.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/cs.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/da.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/de.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/hu.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/it.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/ja.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/nl.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/ru.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/tr.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/zh_cn.dict.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/cs.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/da.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/de.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/es_cr.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/fr.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/hu.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/it.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/ja.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/nl.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/pt_br.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/ru.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/tr.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-virtualization-mgmt/zh_cn.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/cs.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/da.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/de.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/en.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/es_cr.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/fr.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/hu.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/it.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/ja.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/nl.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/pt_br.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/ru.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/tr.dict.itop-welcome-itil.php
#	datamodels/2.x/itop-welcome-itil/zh_cn.dict.itop-welcome-itil.php
#	dictionaries/cs.dictionary.itop.core.php
#	dictionaries/cs.dictionary.itop.ui.php
#	dictionaries/da.dictionary.itop.core.php
#	dictionaries/da.dictionary.itop.ui.php
#	dictionaries/de.dictionary.itop.core.php
#	dictionaries/de.dictionary.itop.ui.php
#	dictionaries/es_cr.dictionary.itop.core.php
#	dictionaries/es_cr.dictionary.itop.ui.php
#	dictionaries/fr.dictionary.itop.core.php
#	dictionaries/fr.dictionary.itop.ui.php
#	dictionaries/hu.dictionary.itop.core.php
#	dictionaries/hu.dictionary.itop.ui.php
#	dictionaries/it.dictionary.itop.core.php
#	dictionaries/it.dictionary.itop.ui.php
#	dictionaries/ja.dictionary.itop.core.php
#	dictionaries/ja.dictionary.itop.ui.php
#	dictionaries/nl.dictionary.itop.core.php
#	dictionaries/nl.dictionary.itop.ui.php
#	dictionaries/pt_br.dictionary.itop.core.php
#	dictionaries/pt_br.dictionary.itop.ui.php
#	dictionaries/ru.dictionary.itop.core.php
#	dictionaries/ru.dictionary.itop.ui.php
#	dictionaries/tr.dictionary.itop.core.php
#	dictionaries/tr.dictionary.itop.ui.php
#	dictionaries/zh_cn.dictionary.itop.core.php
#	dictionaries/zh_cn.dictionary.itop.ui.php
2019-03-19 10:54:06 +01:00
Pierre Goiffon
533e65fcd1 🌐 Fix dict automatic update 2019-03-19 10:52:43 +01:00
Pierre Goiffon
3fb0c768e6 🌐 Update dictionnaries for iTop 2.5.2 2019-03-19 10:10:28 +01:00
Pierre Goiffon
243aab1030 N°1968 Uniqueness : do not allow invalid rule overrides definition
On overrides disabled key must has a value
2019-03-18 16:07:36 +01:00
bruno DA SILVA
22dba9ae07 🐛 composer.json dependencies correctness 2019-03-18 15:00:35 +01:00
Thomas Casteleyn
d8f75495fe Don't display organization name in menu bar if it's the only one 2019-03-18 11:12:05 +01:00
Pierre Goiffon
2240743100 %memo% README : fix iTop Hub docs links (from 2.5.0 to latest) 2019-03-15 17:39:59 +01:00
Pierre Goiffon
a6a2410c50 Merge branch 'master' into develop
# Conflicts:
#	datamodels/2.x/itop-attachments/nl.dict.itop-attachments.php
2019-03-15 17:23:04 +01:00
Pierre Goiffon
02857a86fd Merge remote-tracking branch 'origin/support/2.5' 2019-03-15 17:22:04 +01:00
Stephen Abello
d663d01798 N°1966: Added missing strings to dict files 2019-03-15 17:02:24 +01:00
Stephen Abello
3602163b38 Revert N°1919 and its incorrect translations 2019-03-15 17:00:53 +01:00
Thomas Casteleyn
34751a52a8 Replaced first name by last name in default person list view 2019-03-15 16:16:59 +01:00
Lars Hippler
cb7c382b99 Secure the server: prevent the users from browsing/getting files from the conf directories.
With Apache, it is still a must to enable htaccess with the spec "AllowOverride All". The index.php files are here to prevent from browsing whatever the HTTP server config.
2019-03-15 14:32:43 +01:00
Molkobain
3322074ce7 🐛 N°1889 Portal: Wrong encoding of special chars like in dashlets (eg. "ö", "&", ...)
(cherry picked from commit 83bb3b6d72)
2019-03-13 17:01:57 +01:00
Molkobain
83bb3b6d72 🐛 N°1889 Portal: Wrong encoding of special chars like in dashlets (eg. "ö", "&", ...) 2019-03-13 16:56:04 +01:00
Eric
229f800266 N° 1837 - Fix Synchro Obsolescence
(cherry picked from commit 75737b4ffe)
2019-03-13 14:19:25 +01:00
Eric
75737b4ffe N° 1837 - Fix Synchro Obsolescence 2019-03-13 11:01:13 +01:00
Molkobain
a5340917a7 🐛 N°1956 Portal: Fix message content in user profile when password edition is disabled 2019-03-13 10:31:04 +01:00
Molkobain
914d19e7e4 🐛 N°2072 Fix missing/empty error message when uploading too large attachment 2019-03-13 09:51:49 +01:00
Pierre Goiffon
eb49dbbdc8 N°1968 uniqueness rules : fix search for classes hierarchy, disallow 'attributes' property overrides 2019-03-12 18:12:01 +01:00
Molkobain
912bab5a43 🐛 N°2091: Portal: Fix regression introduced in 2.5, better error message when user logged out 2019-03-12 17:08:12 +01:00
Pierre Goiffon
76c3f640db Merge remote-tracking branch 'origin/master' into develop 2019-03-08 14:35:35 +01:00
Pierre Goiffon
94092f445f Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	application/applicationextension.inc.php
#	webservices/rest.php
2019-03-08 14:35:02 +01:00
Eric
f5b56d9855 composer warnings 2019-03-08 09:10:35 +01:00
Pierre Goiffon
b25a8b4c9f 💡 add missing @var 2019-03-08 09:06:12 +01:00
Pierre Goiffon
7ad9b9dd08 🎨 Use finally instead of duplicating lines
see http://php.net/manual/fr/language.exceptions.php#language.exceptions.finally : finally was added in PHP 5.5
2019-03-07 16:03:51 +01:00
Eric
856c037bb0 N°2064 - Fix abstract class state list for notification triggers 2019-03-07 15:56:29 +01:00
Eric
388896b963 N°941 - Backup/Windows %, ! or " not allowed in password 2019-03-07 15:26:53 +01:00
Eric
1d8addf675 typo 2019-03-06 18:21:19 +01:00
Eric
c8c3d32b18 Error logs and corresponding eml stored by message and accessible from the console 2019-03-06 17:46:47 +01:00
Molkobain
75a0979eee N°2045 Portal: Fix regression introduced with N°1980: New request with 2 Request Templates does not display fields 2019-03-06 14:46:01 +01:00
Pierre Goiffon
d6a0a279a5 N°1449 Update REST API version for new pagination params 2019-03-04 16:00:36 +01:00
Pierre Goiffon
44f5d71e1b 💡 PHPDoc for REST API 2019-03-04 16:00:35 +01:00
Pierre Goiffon
bc841dd239 N°1921 Process InlineImage from another iTop as external images
* Notifications : do not embed InlineImage with wrong secret
* HtmlSanitizer : remove data-img-* attributes if not the same iTop (using approot from Config)
* move \HTMLDOMSanitizer::ProcessImage to \InlineImage::ProcessImageTag
* data-img-* attributes name are now InlineImage class constants

(cherry picked from commit 0aab80917a)
2019-03-04 14:59:38 +01:00
Stephen Abello
9c71d32964 typo in last merge 2019-03-04 12:01:52 +01:00
Eric
d199d84b27 🌐 french typo fixed 2019-03-04 09:41:09 +01:00
Pierre Goiffon
065895aa73 Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	datamodels/2.x/itop-portal-base/zh_cn.dict.itop-portal-base.php
#	dictionaries/zh_cn.dictionary.itop.core.php
#	dictionaries/zh_cn.dictionary.itop.ui.php
2019-03-04 09:21:14 +01:00
Pierre Goiffon
24aca83de4 Merge remote-tracking branch 'origin/support/2.5' 2019-03-04 09:16:53 +01:00
Pierre Goiffon
3de7aa1ada 🙈 Update ignore file 2019-03-04 09:15:38 +01:00
Stephen Abello
5a0edb5c39 Merge branch 'support/2.5'
# Conflicts:
#	js/jquery.tablesorter.pager.js
2019-03-01 17:06:44 +01:00
Stephen Abello
d4fec14123 N°1443: Handle disabled selectable rows in datatables when "All checking/unchecking" 2019-03-01 12:18:07 +01:00
Eric
38951fab1a N°1618 - Custom date format: Fix issue with in-line creation 2019-02-28 17:40:49 +01:00
Pierre Goiffon
e2c8237beb Allow params "limit" and "page" in REST-API
PR #25, code author Dennis Lassiter, many thanks !
Was commited to develop first but decided to retrofit this on support/2.5 (N°1449)
(cherry picked from commit fd55bdf9a8)
(cherry picked from commit 49e31ddb3d)
2019-02-28 10:06:42 +01:00
Pierre Goiffon
1224570fa5 Merge remote-tracking branch 'origin/support/2.5' 2019-02-28 08:45:30 +01:00
Pierre Goiffon
afb99c0f4b N°2054 fix search with sSearchValue GET param in BrowseBrick 2019-02-27 16:21:29 +01:00
Eric
733c908e34 N°2043 - Fix: CSV Import reconciliation using ExternalField broken in service management for provider 2019-02-27 16:00:47 +01:00
Pierre Goiffon
06592d7d37 🐛 Fix datepicker locale not set correctly for ZH CN and PT BR
Reused code made by @annProg in PR #40, many thanks !
2019-02-27 12:53:37 +01:00
Eric
377b4b038c N°2041 - Fix: Reset(LinkedSetIndirect) breaks data integrity 2019-02-27 09:57:01 +01:00
Pierre Goiffon
49e31ddb3d 🎨 REST service : change var names for new pagination params (thanks @jbostoen !) 2019-02-27 09:43:38 +01:00
Dennis Lassiter
fd55bdf9a8 Allow params "limit" and "page" in REST-API
PR #25, code author Dennis Lassiter, many thanks !
2019-02-26 17:06:44 +01:00
Eric
3f7fd6f9f9 N°1954 - Recent change on impact analysis - old change appears on result 2019-02-26 15:36:56 +01:00
Eric
1cb36621a1 N°1963 - Dashlet GroupBy: allow ExternalField selection in UI 2019-02-26 15:31:31 +01:00
Eric
ddd9188eb7 Fix non-existing variable 2019-02-26 15:24:15 +01:00
Eric
02254eac67 N°1966 - Missing dictionaries entries (recover lost translation) 2019-02-25 17:40:08 +01:00
Eric
ebe026b2e9 N°1966 - Missing dictionaries entries (other languages) 2019-02-25 17:20:47 +01:00
Eric
efc7c5b0f4 N°1966 - Missing dictionaries entries 2019-02-25 16:44:51 +01:00
Pierre Goiffon
6eb3a243df Merge remote-tracking branch 'origin/master' into develop 2019-02-25 12:11:15 +01:00
Pierre Goiffon
f68a77450d Merge remote-tracking branch 'origin/support/2.5' 2019-02-25 12:10:30 +01:00
Stephen Abello
c5943c6c28 Internal: Fix a regression introduced by 6b5cc7c on dot path 2019-02-22 10:06:10 +01:00
Pierre Goiffon
bc3b50ad23 Fix wrong method call 2019-02-22 10:00:23 +01:00
Pierre Goiffon
b52e972a39 🐛 Fix login page in chinese instead of english
Introduced by PR #39 (commit 6ad27b43)
2019-02-22 09:54:56 +01:00
Pierre Goiffon
2d344e0209 Fix merge error again......... 2019-02-21 17:38:47 +01:00
Pierre Goiffon
519252efd4 Fix merge error (wooops) 2019-02-21 17:29:53 +01:00
Pierre Goiffon
e59e62fb1b Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	core/expressioncache.class.inc.php
2019-02-21 17:24:37 +01:00
Pierre Goiffon
1f7923beae Merge remote-tracking branch 'origin/support/2.5'
# Conflicts:
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	setup/setup.js
#	setup/wizardsteps.class.inc.php
2019-02-21 17:18:29 +01:00
Pierre Goiffon
2f15bbdaf3 N°2033 backup : tar generation simplify buffer size computation
(cherry picked from commit 5b46644786)
2019-02-21 17:12:18 +01:00
Pierre Goiffon
a35690d13c 📌 Identify & remove Combodo code from ArchiveTar lib 2019-02-21 17:07:31 +01:00
Pierre Goiffon
4857569813 🔧 PhpStorm settings : disallow php short open tags 2019-02-21 15:39:13 +01:00
Eric
a2d34d1779 N°1954 - Fix recent change on impact analysis 2019-02-21 09:41:36 +01:00
Pierre Goiffon
cdba1e0d36 N°2033 backup : fix corrupted archive for files which size is a multiple of 1024 bytes
(cherry picked from commit 3356856a5f)
2019-02-20 17:29:10 +01:00
Eric
48f15d7781 N°1974 - Fix: Stimuli can be applied through URL even if the access rights are set to deny 2019-02-20 16:11:49 +01:00
Eric
d0a766d424 N°1975 - Fix: Change Menu rights to "Admin only" leads in crash test
(cherry picked from commit a89bca4626)
2019-02-20 15:06:57 +01:00
Eric
a89bca4626 N°1975 - Fix: Change Menu rights to "Admin only" leads in crash test 2019-02-20 14:57:39 +01:00
Pierre Goiffon
23ec21e494 N°2031 backup : now logs using IssueLog, and remove debug config property 2019-02-19 14:46:44 +01:00
Eric
63a36fd0f6 N°2030 - Fix function CopyAttribute for external fields 2019-02-19 14:09:14 +01:00
Pierre Goiffon
02617e8976 🔊 itop-backup : add some more logs 2019-02-19 12:21:53 +01:00
Eric
7cf7e55454 N°1823 - Fix tags not saved in case of error 2019-02-19 10:16:45 +01:00
Eric
5067c867b8 N°2014 - Fix Object modification refused when a n-n relation is locked by datasynchro 2019-02-18 15:41:06 +01:00
Stephen Abello
6b5cc7ca4b N°1877 & N°2012: Fix regression backup link on setup, security hardening 2019-02-18 10:40:51 +01:00
Eric
acf80b4b92 Merge branch 'master' into develop 2019-02-15 17:44:59 +01:00
Eric
7bb49893ee N°1884 - Admin Tools Manager no longer has access to 'Schedule Backup' and 'Configuration' menus 2019-02-15 17:34:38 +01:00
Eric
bf62b63173 N°1884 - Admin Tools Manager no longer has access to 'Schedule Backup' and 'Configuration' menus 2019-02-15 17:33:47 +01:00
Eric
b8fb1fa78a N°1884 - Admin Tools Manager no longer has access to 'Schedule Backup' and 'Configuration' menus
(cherry picked from commit 818b4d08da)
2019-02-15 17:31:37 +01:00
Eric
818b4d08da N°1884 - Admin Tools Manager no longer has access to 'Schedule Backup' and 'Configuration' menus 2019-02-15 17:28:55 +01:00
Eric
f438fbd06f N°941 - fix regex 2019-02-15 17:06:50 +01:00
Eric
297a45d477 N°941 - Backup/Windows %, ! or " not allowed in password 2019-02-15 17:03:07 +01:00
Eric
17fe9dfd5f N°1906 - Enhancement Request: Handling a lot of many to many relations in form element (edit mode) 2019-02-15 15:33:22 +01:00
Eric
084d12bb45 Merge branch 'master' into develop 2019-02-14 10:58:42 +01:00
Eric
9d0cbca497 N°941 - Check DB password in configuration when saving 2019-02-14 10:57:36 +01:00
Eric
1587218c6d Merge tag 'N941-2' into develop
Tagging hotfix N941-2 N941-2
2019-02-14 09:48:50 +01:00
Eric
a613b4b101 Merge branch 'hotfix/N941-2' 2019-02-14 09:48:48 +01:00
Eric
c989e2eda5 N°941 - Check DB password also in configuration 2019-02-14 09:47:22 +01:00
Eric
12d3e36887 Merge tag 'N941' into develop
Tagging hotfix N941 N941
2019-02-13 17:45:46 +01:00
Eric
c6fd381b01 Merge branch 'hotfix/N941' 2019-02-13 17:45:44 +01:00
Eric
a784661025 N°941 - Backup/Windows % not allowed in password 2019-02-13 17:43:32 +01:00
Eric
1f9a638bc1 N°941 - Backup/Windows % not allowed in password 2019-02-13 17:39:04 +01:00
Stephen Abello
545504c0de (retrofit from master) N°1443 : Add table_id used by tables paging
(cherry picked from commit 43b0747b83)
2019-02-13 15:13:13 +01:00
Stephen Abello
32f1e97bcd (retrofit from master) N°1148: Fix regression on export
(cherry picked from commit 90e128f951)
2019-02-13 15:13:13 +01:00
Stephen Abello
456b51d7f8 Merge branch 'master' into develop 2019-02-13 15:03:50 +01:00
Stephen Abello
43b0747b83 N°1443 : Add table_id used by tables paging 2019-02-13 14:43:25 +01:00
Eric
9637e75f97 N°2011: Fix Issue with "ExecAsyncTask: async_task_retries" 2019-02-13 14:07:13 +01:00
Eric
6f0effcc66 Merge tag 'N2016' into develop
Tagging hotfix N2016 N2016
2019-02-13 14:04:27 +01:00
Eric
84767692b0 Merge branch 'hotfix/N2016' 2019-02-13 14:04:25 +01:00
Eric
d484614c0f N°2016 - Fix Issue with "ExecAsyncTask: async_task_retries" 2019-02-13 14:03:22 +01:00
Eric
3dd8f214bf Merge tag 'N2011' into develop
Tagging hotfix N2011 N2011
2019-02-13 12:49:27 +01:00
Eric
b403bace6c Merge branch 'hotfix/N2011' 2019-02-13 12:49:25 +01:00
Eric
0b751a9dd6 N°2011 - Fix search auto-completion bug 2019-02-13 12:48:05 +01:00
Eric
d3700ac9f8 Merge tag 'N1963' into develop
Tagging hotfix N1963 N1963
2019-02-13 11:01:42 +01:00
Pierre Goiffon
7aa5d84ff4 💡 Fix PHPDoc for \CMDBSource::GetSqlStringColumnDefinition 2019-02-07 15:06:57 +01:00
Molkobain
46e99bfb40 🐛 Fix notifications tab in objects no longer displaying items (regression introduced by a9bd5a8) 2019-02-07 14:06:06 +01:00
Molkobain
fb5f59e72e 🚨 Fix wrong method visiblity 2019-02-07 14:06:06 +01:00
Molkobain
8af1a53721 💡 PHPDoc 2019-02-07 14:06:06 +01:00
Pierre Goiffon
7034ffea39 N°2010 Fix case for information_schema column names
In MySQL 8, we need to use same case as column declaration, or use an alias
2019-02-07 11:17:19 +01:00
Pierre Goiffon
94ed0354c2 Merge branch 'master' into develop 2019-01-31 18:43:31 +01:00
Pierre Goiffon
2cf3408023 💡 add PhpDoc to AttributeImage methods 2019-01-29 11:08:01 +01:00
Molkobain
3b16d33775 N°1990 Fix setup crash when class has an empty zlist tag (eg. <details />)
Compiler crashed due to PHP warning being output depending on the error reporting level.
2019-01-29 10:36:17 +01:00
Pierre Goiffon
3ff6374ace Merge branch 'master' into develop 2019-01-28 16:04:44 +01:00
Pierre Goiffon
0ea2fed481 💡 fix phpdoc on \utils::GetCurrentModuleDir 2019-01-28 15:58:59 +01:00
Pierre Goiffon
6bbc543ac1 🚸 Change AttributeImage methods visibility to allow overrides 2019-01-28 13:56:20 +01:00
Eric
1ca4f993b0 💚 Fix apc-emulation 2019-01-24 18:07:50 +01:00
Eric Espié
44f448fec4 faster directory creation 2019-01-24 17:22:49 +01:00
Eric
abf7e65816 Avoid unnecessary warnings 2019-01-24 17:22:49 +01:00
Molkobain
55bce63cea Internal: Update compiled CSS file for administration console that seems to not have been committed correctly someday
Fixes:
* Browser dev. tools error about "data:0" on navigation menu
* Newsroom menu
2019-01-24 16:30:36 +01:00
Pierre Goiffon
710e5afe08 🔥 Remove iTopPDF::SetFontSize as there is TCPDF::SetFontSize 2019-01-23 17:36:05 +01:00
Pierre Goiffon
cbf37677cb 📝 README little improvements 2019-01-23 12:06:49 +01:00
Pierre Goiffon
efb98c6414 🔧 Fix invalid composer.json 2019-01-23 12:00:51 +01:00
Pierre Goiffon
01fbcb7044 🎨 replace TCPPDF:SetFont calls by iTopPDF::SetFontParams
This ensures that the same font is used in every caller
2019-01-23 11:46:52 +01:00
Pierre Goiffon
53d2f6320b 💡 Some comments on TCPPDF fonts 2019-01-23 11:44:23 +01:00
Pierre Goiffon
9d02da9d9c 🎨 PDF : new helper method to set only font size 2019-01-23 11:05:12 +01:00
Molkobain
a0841d76db 💄 Portal: Improve modal backdrop UX 2019-01-21 16:03:51 +01:00
Pierre Goiffon
e3c4d611c3 New InvalidConfigParamException
To be used for example in extensions like notify-on-expiration
2019-01-18 11:05:29 +01:00
Pierre Goiffon
79e8c48824 📝 README : update release section 2019-01-18 09:54:35 +01:00
Pierre Goiffon
9ef298608e 🎨 README : move links to corresponding sections 2019-01-18 09:54:35 +01:00
Eric
3ee8d5125b Support for extensions unit tests (scans env-production/*/test) 2019-01-15 11:52:05 +01:00
Eric
59c490fcea Add test for search with 'NOT LIKE' 2019-01-15 11:51:03 +01:00
Eric
b8b468195c PR#39 Fix support of expressions (friendlyname) in different languages context (use class name by language for the expression caches) 2019-01-15 11:24:48 +01:00
Pierre Goiffon
d873a5e68b Remove php ext from composer.json
Not really mandatory, can cause trouble when really using composer !
2019-01-15 09:18:58 +01:00
Eric
6ad27b43ab PR#39 Fix support of expressions (friendlyname) in different language contexts 2019-01-14 11:30:01 +01:00
purplegrape
1df19f65c4 small translation improvement for chinese
1 symbols should not be translated
2 small improvement
2019-01-11 17:31:36 +01:00
Pierre Goiffon
c22a20c1ef Finish readme_2.5.1 2019-01-10 10:29:18 +01:00
Pierre Goiffon
c140ab970e Finish 2.6.0
# Conflicts:
#	core/dbobject.class.php
#	install.txt
2019-01-09 17:27:07 +01:00
Pierre Goiffon
5723e9a77e 🎨 PDF : new helper method to set only font weight and size 2019-01-09 09:25:40 +01:00
Pierre Goiffon
bdedd83368 🔧 New "export_pdf_font" config param 2019-01-09 09:25:40 +01:00
Pierre Goiffon
2f5f92ddca Adding DroidSansFallback font to TCPDF
Modification proposed by @purplegrape with PR #49, many thanks !
2019-01-09 09:24:07 +01:00
Denis Flaven
87a98fe382 🐛 N°1946 - newsroom cache shared between users
The cache should be per user, even if two users share the same
machine/browser.
2019-01-08 15:16:50 +01:00
Stephen Abello
8894ff0fda N°1909: PHP 7.3 compatibility 2019-01-07 13:56:42 +01:00
Eric
488d2ed886 Debug OQL for search is accessible directly for the administrators 2019-01-07 09:43:44 +01:00
Molkobain
48f190447c 🐛 N°1939 REST/JSON: Fix must_exists flag for remote object of indirect linkedset 2019-01-04 17:36:26 +01:00
Duarte Sotto-Mayor Ribeirinho
63184d0bf5 Update link to always reflect latest version
This way there is no need for further updates of version number on link
2019-01-04 11:36:34 +01:00
Eric
a9bd5a8bb0 Avoid scalar values in OQL, replace them with variables 2019-01-02 17:41:56 +01:00
Eric
6c9850b8f6 1814 - Search with criteria : always add default criteria 2019-01-02 14:19:48 +01:00
Eric
e89b4e070c Security update 2019-01-02 10:11:03 +01:00
Pierre Goiffon
911d84d513 N°1885 add php-gd as a mandatory extension
(was made on release/2.6 branch but then rollbacked as this is an infrastructure change that needs to be in the release notes)
2018-12-26 11:05:25 +01:00
Pierre Goiffon
1a3f836a5a Merge remote-tracking branch 'origin/release/2.6' into develop
# Conflicts:
#	application/itopwebpage.class.inc.php
#	core/config.class.inc.php
#	css/css-variables.scss
#	css/light-grey.css
#	datamodels/2.x/itop-attachments/nl.dict.itop-attachments.php
#	datamodels/2.x/itop-incident-mgmt-itil/nl.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/version.xml
2018-12-21 10:26:17 +01:00
Pierre Goiffon
bb0e797cee Remove Config deprecated methods in v2.7 2018-11-27 15:48:47 +01:00
Molkobain
05d866f0f7 Add chifu1324 to the contributors list! Thanks ✌ 2018-11-13 13:46:29 +01:00
Denis Flaven
e7fbb2273d N°1718 Make attribute image zoomable 2018-11-09 16:56:26 +01:00
chifu1234
91f159841b Update metamodel.class.php
Update metamodel.class.php
2018-11-07 11:05:53 +01:00
Pierre Goiffon
905dba67c3 Some PhpDoc to document api (just a start !) 2018-11-05 17:15:44 +01:00
Thomas Casteleyn
676b83519a Fix typo Incident Impact 2018-11-05 09:43:32 +01:00
Molkobain
0e2c8ff220 Merge pull request #7 from Super-Visions/feature/dutch-dict
Another update for Dutch dictionary
2018-10-29 12:08:15 +01:00
Eric
e39a6b96bd Fixed execution warning in search (not visible in production) 2018-10-26 10:59:21 +02:00
Pierre Goiffon
da1268fc05 update versions 2018-10-24 10:35:01 +02:00
Molkobain
fb07d19a64 Merge branch 'develop' into feature/dutch-dict 2018-10-22 17:28:55 +02:00
Thomas Casteleyn
487e0c8769 Improved error message translation 2018-10-22 10:39:16 +02:00
Thomas Casteleyn
5ee125c593 Actually, kilo should be lowercase 2018-10-18 17:27:14 +02:00
Thomas Casteleyn
4876ae7c22 The use of Bytes instead of octets is more common in Dutch 2018-10-18 17:09:55 +02:00
Thomas Casteleyn
e9b04b923e Merge develop into feature/dutch-dict 2018-09-26 11:22:07 +02:00
Thomas Casteleyn
5d8b9fd4db Added Dutch dictionary for incident management 2018-09-20 16:39:37 +02:00
7290 changed files with 421555 additions and 247542 deletions

103
.doc/README.md Normal file
View File

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

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

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

View File

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

6
.doc/composer.json Normal file
View File

@@ -0,0 +1,6 @@
{
"require-dev": {
"phpdocumentor/phpdocumentor": "~2",
"jms/serializer": "1.7.*"
}
}

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

711
.editorconfig Normal file
View File

@@ -0,0 +1,711 @@
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 140
tab_width = 4
ij_continuation_indent_size = 8
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_visual_guides = 140
ij_wrap_on_typing = true
[*.css]
indent_style = tab
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_hex_color_long_format = false
ij_css_hex_color_lower_case = false
ij_css_hex_color_short_format = false
ij_css_hex_color_upper_case = false
ij_css_keep_blank_lines_in_code = 2
ij_css_keep_indents_on_empty_lines = false
ij_css_keep_single_line_blocks = false
ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
ij_css_space_after_colon = true
ij_css_space_before_opening_brace = true
ij_css_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
[*.scss]
indent_style = tab
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_hex_color_long_format = false
ij_scss_hex_color_lower_case = false
ij_scss_hex_color_short_format = false
ij_scss_hex_color_upper_case = false
ij_scss_keep_blank_lines_in_code = 2
ij_scss_keep_indents_on_empty_lines = false
ij_scss_keep_single_line_blocks = false
ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
ij_scss_space_after_colon = true
ij_scss_space_before_opening_brace = true
ij_scss_value_alignment = 0
[*.twig]
indent_style = tab
ij_smart_tabs = true
ij_wrap_on_typing = false
ij_twig_keep_indents_on_empty_lines = false
ij_twig_spaces_inside_delimiters = true
ij_twig_spaces_inside_variable_delimiters = true
[.editorconfig]
ij_editorconfig_align_group_field_declarations = false
ij_editorconfig_space_after_colon = false
ij_editorconfig_space_after_comma = true
ij_editorconfig_space_before_colon = false
ij_editorconfig_space_before_comma = false
ij_editorconfig_spaces_around_assignment_operators = true
[{*.cjs,*.js}]
indent_style = tab
ij_continuation_indent_size = 4
ij_smart_tabs = true
ij_javascript_align_imports = false
ij_javascript_align_multiline_array_initializer_expression = false
ij_javascript_align_multiline_binary_operation = false
ij_javascript_align_multiline_chained_methods = false
ij_javascript_align_multiline_extends_list = false
ij_javascript_align_multiline_for = true
ij_javascript_align_multiline_parameters = true
ij_javascript_align_multiline_parameters_in_calls = false
ij_javascript_align_multiline_ternary_operation = false
ij_javascript_align_object_properties = 0
ij_javascript_align_union_types = false
ij_javascript_align_var_statements = 0
ij_javascript_array_initializer_new_line_after_left_brace = false
ij_javascript_array_initializer_right_brace_on_new_line = false
ij_javascript_array_initializer_wrap = off
ij_javascript_assignment_wrap = off
ij_javascript_binary_operation_sign_on_next_line = false
ij_javascript_binary_operation_wrap = off
ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**/*
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_call_parameters_new_line_after_left_paren = false
ij_javascript_call_parameters_right_paren_on_new_line = false
ij_javascript_call_parameters_wrap = off
ij_javascript_catch_on_new_line = false
ij_javascript_chained_call_dot_on_new_line = true
ij_javascript_class_brace_style = end_of_line
ij_javascript_comma_on_new_line = false
ij_javascript_do_while_brace_force = never
ij_javascript_else_on_new_line = true
ij_javascript_enforce_trailing_comma = keep
ij_javascript_extends_keyword_wrap = off
ij_javascript_extends_list_wrap = off
ij_javascript_field_prefix = _
ij_javascript_file_name_style = relaxed
ij_javascript_finally_on_new_line = false
ij_javascript_for_brace_force = never
ij_javascript_for_statement_new_line_after_left_paren = false
ij_javascript_for_statement_right_paren_on_new_line = false
ij_javascript_for_statement_wrap = off
ij_javascript_force_quote_style = false
ij_javascript_force_semicolon_style = false
ij_javascript_function_expression_brace_style = end_of_line
ij_javascript_if_brace_force = always
ij_javascript_import_merge_members = global
ij_javascript_import_prefer_absolute_path = global
ij_javascript_import_sort_members = true
ij_javascript_import_sort_module_name = false
ij_javascript_import_use_node_resolution = true
ij_javascript_imports_wrap = on_every_item
ij_javascript_indent_case_from_switch = true
ij_javascript_indent_chained_calls = true
ij_javascript_indent_package_children = 0
ij_javascript_jsx_attribute_value = braces
ij_javascript_keep_blank_lines_in_code = 2
ij_javascript_keep_first_column_comment = true
ij_javascript_keep_indents_on_empty_lines = false
ij_javascript_keep_line_breaks = true
ij_javascript_keep_simple_blocks_in_one_line = false
ij_javascript_keep_simple_methods_in_one_line = false
ij_javascript_line_comment_add_space = true
ij_javascript_line_comment_at_first_column = false
ij_javascript_method_brace_style = end_of_line
ij_javascript_method_call_chain_wrap = off
ij_javascript_method_parameters_new_line_after_left_paren = false
ij_javascript_method_parameters_right_paren_on_new_line = false
ij_javascript_method_parameters_wrap = off
ij_javascript_object_literal_wrap = on_every_item
ij_javascript_parentheses_expression_new_line_after_left_paren = false
ij_javascript_parentheses_expression_right_paren_on_new_line = false
ij_javascript_place_assignment_sign_on_next_line = false
ij_javascript_prefer_as_type_cast = false
ij_javascript_prefer_parameters_wrap = false
ij_javascript_reformat_c_style_comments = false
ij_javascript_space_after_colon = true
ij_javascript_space_after_comma = true
ij_javascript_space_after_dots_in_rest_parameter = false
ij_javascript_space_after_generator_mult = true
ij_javascript_space_after_property_colon = true
ij_javascript_space_after_quest = true
ij_javascript_space_after_type_colon = true
ij_javascript_space_after_unary_not = false
ij_javascript_space_before_async_arrow_lparen = true
ij_javascript_space_before_catch_keyword = true
ij_javascript_space_before_catch_left_brace = true
ij_javascript_space_before_catch_parentheses = true
ij_javascript_space_before_class_lbrace = true
ij_javascript_space_before_class_left_brace = true
ij_javascript_space_before_colon = true
ij_javascript_space_before_comma = false
ij_javascript_space_before_do_left_brace = true
ij_javascript_space_before_else_keyword = true
ij_javascript_space_before_else_left_brace = true
ij_javascript_space_before_finally_keyword = true
ij_javascript_space_before_finally_left_brace = true
ij_javascript_space_before_for_left_brace = true
ij_javascript_space_before_for_parentheses = true
ij_javascript_space_before_for_semicolon = false
ij_javascript_space_before_function_left_parenth = true
ij_javascript_space_before_generator_mult = false
ij_javascript_space_before_if_left_brace = true
ij_javascript_space_before_if_parentheses = true
ij_javascript_space_before_method_call_parentheses = false
ij_javascript_space_before_method_left_brace = true
ij_javascript_space_before_method_parentheses = false
ij_javascript_space_before_property_colon = false
ij_javascript_space_before_quest = true
ij_javascript_space_before_switch_left_brace = true
ij_javascript_space_before_switch_parentheses = true
ij_javascript_space_before_try_left_brace = true
ij_javascript_space_before_type_colon = false
ij_javascript_space_before_unary_not = false
ij_javascript_space_before_while_keyword = true
ij_javascript_space_before_while_left_brace = true
ij_javascript_space_before_while_parentheses = true
ij_javascript_spaces_around_additive_operators = false
ij_javascript_spaces_around_arrow_function_operator = true
ij_javascript_spaces_around_assignment_operators = true
ij_javascript_spaces_around_bitwise_operators = true
ij_javascript_spaces_around_equality_operators = true
ij_javascript_spaces_around_logical_operators = true
ij_javascript_spaces_around_multiplicative_operators = true
ij_javascript_spaces_around_relational_operators = true
ij_javascript_spaces_around_shift_operators = true
ij_javascript_spaces_around_unary_operator = false
ij_javascript_spaces_within_array_initializer_brackets = false
ij_javascript_spaces_within_brackets = false
ij_javascript_spaces_within_catch_parentheses = false
ij_javascript_spaces_within_for_parentheses = false
ij_javascript_spaces_within_if_parentheses = false
ij_javascript_spaces_within_imports = false
ij_javascript_spaces_within_interpolation_expressions = false
ij_javascript_spaces_within_method_call_parentheses = false
ij_javascript_spaces_within_method_parentheses = false
ij_javascript_spaces_within_object_literal_braces = false
ij_javascript_spaces_within_object_type_braces = true
ij_javascript_spaces_within_parentheses = false
ij_javascript_spaces_within_switch_parentheses = false
ij_javascript_spaces_within_type_assertion = false
ij_javascript_spaces_within_union_types = true
ij_javascript_spaces_within_while_parentheses = false
ij_javascript_special_else_if_treatment = true
ij_javascript_ternary_operation_signs_on_next_line = false
ij_javascript_ternary_operation_wrap = off
ij_javascript_union_types_wrap = on_every_item
ij_javascript_use_chained_calls_group_indents = true
ij_javascript_use_double_quotes = true
ij_javascript_use_explicit_js_extension = global
ij_javascript_use_path_mapping = always
ij_javascript_use_public_modifier = false
ij_javascript_use_semicolon_after_statement = true
ij_javascript_var_declaration_wrap = normal
ij_javascript_while_brace_force = never
ij_javascript_while_on_new_line = false
ij_javascript_wrap_comments = false
[{*.module,*.hphp,*.phtml,*.php5,*.php4,*.php,*.ctp,*.inc}]
indent_style = tab
ij_continuation_indent_size = 4
ij_smart_tabs = true
ij_wrap_on_typing = false
ij_php_align_assignments = false
ij_php_align_class_constants = false
ij_php_align_group_field_declarations = false
ij_php_align_inline_comments = false
ij_php_align_key_value_pairs = false
ij_php_align_multiline_array_initializer_expression = false
ij_php_align_multiline_binary_operation = false
ij_php_align_multiline_chained_methods = false
ij_php_align_multiline_extends_list = false
ij_php_align_multiline_for = true
ij_php_align_multiline_parameters = false
ij_php_align_multiline_parameters_in_calls = false
ij_php_align_multiline_ternary_operation = false
ij_php_align_phpdoc_comments = false
ij_php_align_phpdoc_param_names = false
ij_php_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_binary_operation_sign_on_next_line = false
ij_php_binary_operation_wrap = off
ij_php_blank_lines_after_class_header = 0
ij_php_blank_lines_after_function = 1
ij_php_blank_lines_after_imports = 1
ij_php_blank_lines_after_opening_tag = 0
ij_php_blank_lines_after_package = 1
ij_php_blank_lines_around_class = 1
ij_php_blank_lines_around_constants = 0
ij_php_blank_lines_around_field = 0
ij_php_blank_lines_around_method = 1
ij_php_blank_lines_before_class_end = 0
ij_php_blank_lines_before_imports = 1
ij_php_blank_lines_before_method_body = 0
ij_php_blank_lines_before_package = 1
ij_php_blank_lines_before_return_statement = 1
ij_php_block_brace_style = next_line
ij_php_call_parameters_new_line_after_left_paren = false
ij_php_call_parameters_right_paren_on_new_line = false
ij_php_call_parameters_wrap = normal
ij_php_catch_on_new_line = true
ij_php_category_weight = 28
ij_php_class_brace_style = next_line
ij_php_comma_after_last_array_element = true
ij_php_concat_spaces = false
ij_php_copyright_weight = 28
ij_php_deprecated_weight = 28
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_extends_keyword_wrap = off
ij_php_extends_list_wrap = off
ij_php_fields_default_visibility = private
ij_php_filesource_weight = 28
ij_php_finally_on_new_line = true
ij_php_for_brace_force = always
ij_php_for_statement_new_line_after_left_paren = false
ij_php_for_statement_right_paren_on_new_line = false
ij_php_for_statement_wrap = off
ij_php_force_short_declaration_array_style = false
ij_php_global_weight = 28
ij_php_group_use_wrap = on_every_item
ij_php_if_brace_force = always
ij_php_if_lparen_on_next_line = false
ij_php_if_rparen_on_next_line = false
ij_php_ignore_weight = 28
ij_php_import_sorting = alphabetic
ij_php_indent_break_from_case = true
ij_php_indent_case_from_switch = true
ij_php_indent_code_in_php_tags = false
ij_php_internal_weight = 0
ij_php_keep_blank_lines_after_lbrace = 2
ij_php_keep_blank_lines_before_right_brace = 2
ij_php_keep_blank_lines_in_code = 2
ij_php_keep_blank_lines_in_declarations = 2
ij_php_keep_control_statement_in_one_line = true
ij_php_keep_first_column_comment = true
ij_php_keep_indents_on_empty_lines = false
ij_php_keep_line_breaks = true
ij_php_keep_rparen_and_lbrace_on_one_line = true
ij_php_keep_simple_methods_in_one_line = false
ij_php_lambda_brace_style = end_of_line
ij_php_license_weight = 28
ij_php_line_comment_add_space = false
ij_php_line_comment_at_first_column = true
ij_php_link_weight = 28
ij_php_lower_case_boolean_const = true
ij_php_lower_case_null_const = true
ij_php_method_brace_style = next_line
ij_php_method_call_chain_wrap = off
ij_php_method_parameters_new_line_after_left_paren = true
ij_php_method_parameters_right_paren_on_new_line = true
ij_php_method_parameters_wrap = normal
ij_php_method_weight = 28
ij_php_modifier_list_wrap = false
ij_php_multiline_chained_calls_semicolon_on_new_line = false
ij_php_namespace_brace_style = 1
ij_php_null_type_position = in_the_end
ij_php_package_weight = 28
ij_php_param_weight = 4
ij_php_parentheses_expression_new_line_after_left_paren = false
ij_php_parentheses_expression_right_paren_on_new_line = false
ij_php_phpdoc_blank_line_before_tags = true
ij_php_phpdoc_blank_lines_around_parameters = true
ij_php_phpdoc_keep_blank_lines = true
ij_php_phpdoc_param_spaces_between_name_and_description = 1
ij_php_phpdoc_param_spaces_between_tag_and_type = 1
ij_php_phpdoc_param_spaces_between_type_and_name = 1
ij_php_phpdoc_use_fqcn = true
ij_php_phpdoc_wrap_long_lines = true
ij_php_place_assignment_sign_on_next_line = false
ij_php_place_parens_for_constructor = 0
ij_php_property_read_weight = 28
ij_php_property_weight = 28
ij_php_property_write_weight = 28
ij_php_return_type_on_new_line = false
ij_php_return_weight = 5
ij_php_see_weight = 2
ij_php_since_weight = 28
ij_php_sort_phpdoc_elements = true
ij_php_space_after_colon = true
ij_php_space_after_colon_in_return_type = true
ij_php_space_after_comma = true
ij_php_space_after_for_semicolon = true
ij_php_space_after_quest = true
ij_php_space_after_type_cast = false
ij_php_space_after_unary_not = false
ij_php_space_before_array_initializer_left_brace = false
ij_php_space_before_catch_keyword = true
ij_php_space_before_catch_left_brace = true
ij_php_space_before_catch_parentheses = true
ij_php_space_before_class_left_brace = true
ij_php_space_before_closure_left_parenthesis = true
ij_php_space_before_colon = true
ij_php_space_before_colon_in_return_type = false
ij_php_space_before_comma = false
ij_php_space_before_do_left_brace = true
ij_php_space_before_else_keyword = true
ij_php_space_before_else_left_brace = true
ij_php_space_before_finally_keyword = true
ij_php_space_before_finally_left_brace = true
ij_php_space_before_for_left_brace = true
ij_php_space_before_for_parentheses = true
ij_php_space_before_for_semicolon = false
ij_php_space_before_if_left_brace = true
ij_php_space_before_if_parentheses = true
ij_php_space_before_method_call_parentheses = false
ij_php_space_before_method_left_brace = true
ij_php_space_before_method_parentheses = false
ij_php_space_before_quest = true
ij_php_space_before_switch_left_brace = true
ij_php_space_before_switch_parentheses = true
ij_php_space_before_try_left_brace = true
ij_php_space_before_unary_not = false
ij_php_space_before_while_keyword = true
ij_php_space_before_while_left_brace = true
ij_php_space_before_while_parentheses = true
ij_php_space_between_ternary_quest_and_colon = false
ij_php_spaces_around_additive_operators = true
ij_php_spaces_around_arrow = false
ij_php_spaces_around_assignment_in_declare = false
ij_php_spaces_around_assignment_operators = true
ij_php_spaces_around_bitwise_operators = true
ij_php_spaces_around_equality_operators = true
ij_php_spaces_around_logical_operators = true
ij_php_spaces_around_multiplicative_operators = true
ij_php_spaces_around_null_coalesce_operator = true
ij_php_spaces_around_relational_operators = true
ij_php_spaces_around_shift_operators = true
ij_php_spaces_around_unary_operator = false
ij_php_spaces_around_var_within_brackets = false
ij_php_spaces_within_array_initializer_braces = false
ij_php_spaces_within_brackets = false
ij_php_spaces_within_catch_parentheses = false
ij_php_spaces_within_for_parentheses = false
ij_php_spaces_within_if_parentheses = false
ij_php_spaces_within_method_call_parentheses = false
ij_php_spaces_within_method_parentheses = false
ij_php_spaces_within_parentheses = false
ij_php_spaces_within_short_echo_tags = true
ij_php_spaces_within_switch_parentheses = false
ij_php_spaces_within_while_parentheses = false
ij_php_special_else_if_treatment = false
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_todo_weight = 28
ij_php_unknown_tag_weight = 28
ij_php_upper_case_boolean_const = false
ij_php_upper_case_null_const = false
ij_php_uses_weight = 28
ij_php_var_weight = 28
ij_php_variable_naming_style = mixed
ij_php_version_weight = 28
ij_php_while_brace_force = always
ij_php_while_on_new_line = false
[{*.sht,*.htm,*.html,*.shtm,*.shtml}]
indent_style = tab
ij_smart_tabs = true
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
ij_html_align_attributes = true
ij_html_align_text = false
ij_html_attribute_wrap = normal
ij_html_block_comment_at_first_column = true
ij_html_do_not_align_children_of_min_lines = 0
ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot,style,script,head
ij_html_enforce_quotes = false
ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
ij_html_keep_blank_lines = 2
ij_html_keep_indents_on_empty_lines = false
ij_html_keep_line_breaks = true
ij_html_keep_line_breaks_in_text = true
ij_html_keep_whitespaces = false
ij_html_keep_whitespaces_inside = span,pre,textarea
ij_html_line_comment_at_first_column = true
ij_html_new_line_after_last_attribute = never
ij_html_new_line_before_first_attribute = never
ij_html_quote_style = double
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
[{*.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}]
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

48
.gitignore vendored
View File

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

View File

@@ -1,37 +1,56 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="LINE_SEPARATOR" value="&#10;" />
<option name="RIGHT_MARGIN" value="320" />
<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,script,style" />
<option name="HTML_DO_NOT_ALIGN_CHILDREN_OF_MIN_LINES" value="4" />
<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="RIGHT_MARGIN" value="320" />
<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="SPACE_BEFORE_FOR_PARENTHESES" 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" />
@@ -48,10 +67,13 @@
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML">
<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,10 +1,14 @@
<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>

View File

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

View File

@@ -10,5 +10,7 @@ mkdir -p toolkit
rm -rf toolkit/*
# fill target dirs
curl http://www.combodo.com/documentation/iTopDataModelToolkit-2.3.zip | tar xvz --directory toolkit
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

@@ -3,7 +3,7 @@
set -x
# on the root dir
composer install
# composer install -a # => Not needed anymore (libs were added to git with N°2435)
# under the test dir

View File

@@ -94,10 +94,6 @@ $MySettings = array(
'default_language' => 'EN US',
// disable_attachments_download_legacy_portal: Disable attachments download from legacy portal
// default: true
'disable_attachments_download_legacy_portal' => true,
// draft_attachments_lifetime: Lifetime (in seconds) of drafts' attachments and inline images: after this duration, the garbage collector will delete them.
// default: 3600
'draft_attachments_lifetime' => 3600,

View File

@@ -1,7 +1,26 @@
<?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');

View File

@@ -11,12 +11,22 @@ Here are some guidelines that will help us integrate your work!
You are welcome to create pull requests on any of those subjects:
* 🐛 `:bug:` bug fix
* 🔒 `:lock:` security
* 🌐 `:globe_with_meridians:` 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.
For all **security related subjects**, please see our [security policy](SECURITY.md).
All **datamodel modification** should be done in an extension. Beware that such change would
impact all existing customers, and could prevent them from
upgrading!
Combodo has a long experience of datamodel changes: they are very disruptive!
This is why we avoid them in iTop core, especially the changes on existing objects/fields.
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
iTop is distributed under the AGPL-3.0 license (see the [license.txt] file),
your code must comply with this license.
@@ -31,7 +41,7 @@ If you want to use another license, you may [create an extension][wiki new ext].
TL;DR:
> **create a fork from iTop main repository,
> create a branch based on either release branch if present, or develop otherwise**
> create a branch based on the develop 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:
@@ -39,23 +49,33 @@ main branches:
- 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
For example, if no beta version is currently ongoing 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
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
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
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.
## Coding
@@ -80,7 +100,9 @@ Our tests are located in the `test/` directory, containing a PHPUnit config file
* Use the present tense ("Add feature" not "Added feature")
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
* Limit the first line to 72 characters or less
* Please start the commit message with an applicable emoji code (following the [Gitmoji guide](https://gitmoji.carloscuesta.me/)). For example :
* Please start the commit message with an applicable emoji code (following the [Gitmoji guide](https://gitmoji.carloscuesta.me/)).
Beware to use the code (for example `:bug:`) and not the character (🐛) as Unicode support in git clients is very poor for now...
Emoji examples :
* 🌐 `:globe_with_meridians:` for translations
* 🎨 `:art:` when improving the format/structure of the code
* ⚡️ `:zap:` when improving performance
@@ -92,7 +114,8 @@ Our tests are located in the `test/` directory, containing a PHPUnit config file
* ⬆️ `:arrow_up:` when upgrading dependencies
* ⬇️ `:arrow_down:` when downgrading dependencies
* ♻️ `:recycle:` code refactoring
* 💄 `:lipstick:` Updating the UI and style files.
* 💄 `:lipstick:` Updating the UI and style files.
## Pull request

108
README.md
View File

@@ -23,43 +23,86 @@ iTop also offers mass import tools and web services to integrate with your IT
## Resources
- [iTop Forums][1]: for support request
- [iTop Forums][1]: community support
- [iTop Tickets][2]: for feature requests and bug reports
- [Releases download][3]
- [iTop documentation][4] for iTop and official extensions
- [iTop extensions][5] for discovering and installing extensions
- [Documentation][4] covering both iTop and its official extensions
- [iTop Hub][5] : discover and install extensions !
## Releases
### Version 2.6
[1]: https://sourceforge.net/p/itop/discussion/
[2]: https://sourceforge.net/p/itop/tickets/
[3]: https://sourceforge.net/projects/itop/files/itop/
[4]: https://www.itophub.io/wiki
[5]: https://store.itophub.io/en_US/
[10]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#configuration_management_cmdb
[11]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#ticketing
[12]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#service_management
[13]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#change_management
[14]: https://www.itophub.io/wiki/page?id=latest%3Aimplementation%3Astart#service_level_agreements_and_targets
[15]: https://www.itophub.io/wiki/page?id=latest%3Auser%3Aactions#relations
[16]: https://www.itophub.io/wiki/page?id=latest%3Auser%3Abulk_modify#uploading_data
[17]: https://www.itophub.io/wiki/page?id=latest%3Aadmin%3Aaudit
[18]: https://www.itophub.io/wiki/page?id=latest%3Aadvancedtopics%3Adata_synchro_overview
## Last releases
### 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.0][61]
- [Download iTop 2.6.1][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
### Version 2.5
### Versions 2.5.*
- 2.5.0 published on July 11, 2018
- [Changes since the previous version][54]
- [New features][55]
- [Migration notes][56]
- [Download iTop 2.5.1][57]
[54]: https://www.itophub.io/wiki/page?id=2_5_0:release:change_log
[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
### Version 2.4
### 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
## About Us
iTop development is sponsored, led and supported by [Combodo][0].
[0]: https://www.combodo.com
# Contributors
## Contributors
We would like to give a special thank you to the people from the community who contributed to this project, including:
### Names
- Alves, David
- Beck, Pedro
- Bilger, Jean-François
@@ -74,6 +117,7 @@ We would like to give a special thank you to the people from the community who c
- Gumble, David
- Hippler, Lars
- Khamit, Shamil
- Kincel, Martin
- Konečný, Kamil
- Kunin, Vladimir
- Lassiter, Dennis
@@ -85,7 +129,7 @@ We would like to give a special thank you to the people from the community who c
- Tulio, Marco
- Turrubiates, Miguel
#### Aliases
### Aliases
- chifu1234
- cprobst
- Karkoff1212
@@ -96,45 +140,7 @@ We would like to give a special thank you to the people from the community who c
- theBigOne
- ulmerspatz
#### Companies
### Companies
- Hardis
- ITOMIG
[0]: https://www.combodo.com
[1]: https://sourceforge.net/p/itop/discussion/
[2]: https://sourceforge.net/p/itop/tickets/
[3]: https://sourceforge.net/projects/itop/files/itop/
[4]: https://www.itophub.io/wiki
[5]: https://store.itophub.io/en_US/
[10]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#configuration_management_cmdb
[11]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#ticketing
[12]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#service_management
[13]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#change_management
[14]: https://www.itophub.io/wiki/page?id=2_5_0%3Aimplementation%3Astart#service_level_agreements_and_targets
[15]: https://www.itophub.io/wiki/page?id=2_5_0%3Auser%3Aactions#relations
[16]: https://www.itophub.io/wiki/page?id=2_5_0%3Auser%3Abulk_modify#uploading_data
[17]: https://www.itophub.io/wiki/page?id=2_5_0%3Aadmin%3Aaudit
[18]: https://www.itophub.io/wiki/page?id=2_5_0%3Aadvancedtopics%3Adata_synchro_overview
[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
[54]: https://www.itophub.io/wiki/page?id=2_5_0:release:change_log
[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
[58]: https://www.itophub.io/wiki/page?id=2_6_0:release:change_log
[59]: https://www.itophub.io/wiki/page?id=2_6_0:release:2_6_whats_new
[60]: https://www.itophub.io/wiki/page?id=2_6_0:install:250_to_260_migration_notes
[61]: https://sourceforge.net/projects/itop/files/itop/2.6.0

36
SECURITY.md Normal file
View File

@@ -0,0 +1,36 @@
# 🔒 Reporting vulnerabilities
We take all security bugs seriously. Thank you for improving the security of iTop! We appreciate your efforts and
responsible disclosure and will make every effort to acknowledge your contributions.
## ✉️ How to report
### iTop vulnerabilities
Please send a procedure to reproduce iTop vulnerabilities to [itop-security@combodo.com](mailto:itop-security@combodo.com).
You can send us a standard "given / then / when" report, including iTop version, impacts, and maybe installed modules or data if they are
needed to reproduce.
### Dependencies vulnerabilities
Report security bugs in third-party modules to the person or team maintaining the module, and notify us of this report by sending an email
to [itop-security@combodo.com](mailto:itop-security@combodo.com).
## 📆 Disclosure Policy
Report sent to us will be acknowledged within the week.
Then, a Combodo developer will be assigned to the reported issue and will:
* confirm the problem and determine the affected iTop versions
* audit the code to search any potential similar problems
* try to find a workaround if any
* create fixes for all releases still under maintenance
* send you the commit(s) for review
* send you the next version(s) that will contain the fix, and the estimated release dates
Security issues always take precedence over bug fixes and feature work.
The assignee will keep you informed of the resolution progress, and may ask you for additional information or guidance.

View File

@@ -214,7 +214,9 @@ PrepareWidgets();
EOF
);
}
$s_captured_output = $this->ob_get_clean_safe();
$this->outputCollapsibleSectionInit();
$s_captured_output = $this->ob_get_clean_safe();
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
{
// inline content != attachment && html => filter all scripts for malicious XSS scripts
@@ -289,6 +291,10 @@ EOF
{
DBSearch::RecordQueryTrace();
}
if (class_exists('ExecutionKPI'))
{
ExecutionKPI::ReportStats();
}
}
/**

File diff suppressed because it is too large Load Diff

View File

@@ -54,7 +54,7 @@ require_once(APPROOT.'sources/application/search/criterionconversion/criterionto
abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
{
protected $m_iFormId; // The ID of the form used to edit the object (when in edition mode !)
static $iGlobalFormId = 1;
protected static $iGlobalFormId = 1;
protected $aFieldsMap;
/**
@@ -176,7 +176,7 @@ EOF
}
}
function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
public function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
{
// Standard Header with name, actions menu and history block
//
@@ -321,7 +321,7 @@ EOF
}
$sLabel = htmlentities(Dict::S('Tag:Synchronized'), ENT_QUOTES, 'UTF-8');
$sSynchroTagId = 'synchro_icon-'.$this->GetKey();
$aIcons[] = "<div class=\"tag\" id=\"$sSynchroTagId\"><span class=\"object-synchronized fa fa-lock fa-1x\">&nbsp;</span>&nbsp;$sLabel</div>";
$aIcons[] = "<div class=\"tag\" id=\"$sSynchroTagId\"><span class=\"object-synchronized fas fa-lock fa-1x\">&nbsp;</span>&nbsp;$sLabel</div>";
$sTip = addslashes($sTip);
$oPage->add_ready_script("$('#$sSynchroTagId').qtip( { content: '$sTip', show: 'mouseover', hide: { fixed: true }, style: { name: 'dark', tip: 'topLeft' }, position: { corner: { target: 'bottomMiddle', tooltip: 'topLeft' }} } );");
}
@@ -331,13 +331,13 @@ EOF
{
$sLabel = htmlentities(Dict::S('Tag:Archived'), ENT_QUOTES, 'UTF-8');
$sTitle = htmlentities(Dict::S('Tag:Archived+'), ENT_QUOTES, 'UTF-8');
$aIcons[] = "<div class=\"tag\" title=\"$sTitle\"><span class=\"object-archived fa fa-archive fa-1x\">&nbsp;</span>&nbsp;$sLabel</div>";
$aIcons[] = "<div class=\"tag\" title=\"$sTitle\"><span class=\"object-archived fas fa-archive fa-1x\">&nbsp;</span>&nbsp;$sLabel</div>";
}
elseif ($this->IsObsolete())
{
$sLabel = htmlentities(Dict::S('Tag:Obsolete'), ENT_QUOTES, 'UTF-8');
$sTitle = htmlentities(Dict::S('Tag:Obsolete+'), ENT_QUOTES, 'UTF-8');
$aIcons[] = "<div class=\"tag\" title=\"$sTitle\"><span class=\"object-obsolete fa fa-eye-slash fa-1x\">&nbsp;</span>&nbsp;$sLabel</div>";
$aIcons[] = "<div class=\"tag\" title=\"$sTitle\"><span class=\"object-obsolete fas fa-eye-slash fa-1x\">&nbsp;</span>&nbsp;$sLabel</div>";
}
$sObjectIcon = $this->GetIcon();
@@ -367,7 +367,7 @@ EOF
);
}
function DisplayBareHistory(WebPage $oPage, $bEditMode = false, $iLimitCount = 0, $iLimitStart = 0)
public function DisplayBareHistory(WebPage $oPage, $bEditMode = false, $iLimitCount = 0, $iLimitStart = 0)
{
// history block (with as a tab)
$oHistoryFilter = new DBObjectSearch('CMDBChangeOp');
@@ -378,13 +378,14 @@ EOF
$oBlock->Display($oPage, 'history');
}
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
public function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
$aFieldsMap = $this->GetBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
if (!isset($aExtraParams['disable_plugins']) || !$aExtraParams['disable_plugins'])
{
/** @var iApplicationUIExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$oExtensionInstance->OnDisplayProperties($this, $oPage, $bEditMode);
@@ -460,7 +461,7 @@ EOF
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
{
$aRedundancySettings = $this->FindVisibleRedundancySettings();
@@ -648,6 +649,7 @@ EOF
}
$oPage->SetCurrentTab('');
/** @var \iApplicationUIExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$oExtensionInstance->OnDisplayRelations($this, $oPage, $bEditMode);
@@ -671,14 +673,15 @@ EOF
if (count($aTriggers) > 0)
{
$iId = $this->GetKey();
$sTriggersList = implode(',', $aTriggers);
$aParams = array('triggers' => $aTriggers, 'id' => $iId);
$aNotifSearches = array();
$iNotifsCount = 0;
$aNotificationClasses = MetaModel::EnumChildClasses('EventNotification', ENUM_CHILD_CLASSES_EXCLUDETOP);
foreach($aNotificationClasses as $sNotifClass)
{
$aNotifSearches[$sNotifClass] = DBObjectSearch::FromOQL("SELECT $sNotifClass AS Ev JOIN Trigger AS T ON Ev.trigger_id = T.id WHERE T.id IN ($sTriggersList) AND Ev.object_id = $iId");
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass]);
$aNotifSearches[$sNotifClass] = DBObjectSearch::FromOQL("SELECT $sNotifClass AS Ev JOIN Trigger AS T ON Ev.trigger_id = T.id WHERE T.id IN (:triggers) AND Ev.object_id = :id");
$aNotifSearches[$sNotifClass]->SetInternalParams($aParams);
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass], array());
$iNotifsCount += $oNotifSet->Count();
}
// Display notifications regarding the object: on block per subclass to have the intersting columns
@@ -695,7 +698,7 @@ EOF
}
}
function GetBareProperties(WebPage $oPage, $bEditMode, $sPrefix, $aExtraParams = array())
public function GetBareProperties(WebPage $oPage, $bEditMode, $sPrefix, $aExtraParams = array())
{
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
$sClass = get_class($this);
@@ -908,7 +911,7 @@ EOF
/**
* @param \iTopWebPage $oPage
* @param \WebPage $oPage
* @param bool $bEditMode
*
* @throws \CoreException
@@ -918,8 +921,9 @@ EOF
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @throws \Exception
*/
function DisplayDetails(WebPage $oPage, $bEditMode = false)
public function DisplayDetails(WebPage $oPage, $bEditMode = false)
{
$sTemplate = Utils::ReadFromFile(MetaModel::GetDisplayTemplate(get_class($this)));
if (!empty($sTemplate))
@@ -941,6 +945,7 @@ EOF
// template not found display the object using the *old style*
$oPage->add('<div id="search-widget-results-outer">');
$this->DisplayBareHeader($oPage, $bEditMode);
/** @var \iTopWebPage $oPage */
$oPage->AddTabContainer(OBJECT_PROPERTIES_TAB);
$oPage->SetCurrentTabContainer(OBJECT_PROPERTIES_TAB);
$oPage->SetCurrentTab(Dict::S('UI:PropertiesTab'));
@@ -954,7 +959,7 @@ EOF
}
}
function DisplayPreview(WebPage $oPage)
public function DisplayPreview(WebPage $oPage)
{
$aDetails = array();
$sClass = get_class($this);
@@ -1268,12 +1273,12 @@ EOF
return $oDataTable->Display($oPage, $oSettings, $bDisplayMenu, $sSelectMode, $bViewLink, $aExtraParams);
}
static function DisplaySetAsCSV(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array(), $sCharset = 'UTF-8')
public static function DisplaySetAsCSV(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array(), $sCharset = 'UTF-8')
{
$oPage->add(self::GetSetAsCSV($oSet, $aParams, $sCharset));
}
static function GetSetAsCSV(DBObjectSet $oSet, $aParams = array(), $sCharset = 'UTF-8')
public static function GetSetAsCSV(DBObjectSet $oSet, $aParams = array(), $sCharset = 'UTF-8')
{
$sSeparator = isset($aParams['separator']) ? $aParams['separator'] : ','; // default separator is comma
$sTextQualifier = isset($aParams['text_qualifier']) ? $aParams['text_qualifier'] : '"'; // default text qualifier is double quote
@@ -1402,7 +1407,7 @@ EOF
return $sHtml;
}
static function DisplaySetAsHTMLSpreadsheet(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
public static function DisplaySetAsHTMLSpreadsheet(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
{
$oPage->add(self::GetSetAsHTMLSpreadsheet($oSet, $aParams));
}
@@ -1411,7 +1416,7 @@ EOF
* Spreadsheet output: designed for end users doing some reporting
* Then the ids are excluded and replaced by the corresponding friendlyname
*/
static function GetSetAsHTMLSpreadsheet(DBObjectSet $oSet, $aParams = array())
public static function GetSetAsHTMLSpreadsheet(DBObjectSet $oSet, $aParams = array())
{
$aFields = null;
if (isset($aParams['fields']) && (strlen($aParams['fields']) > 0))
@@ -1595,7 +1600,7 @@ EOF
return $sHtml;
}
static function DisplaySetAsXML(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
public static function DisplaySetAsXML(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
{
$bLocalize = true;
if (isset($aParams['localize_values']))
@@ -1899,7 +1904,7 @@ EOF
$sLanguage = strtolower(trim(UserRights::GetUserLanguage()));
$aConfig['language'] = $sLanguage;
$aConfig['contentsLanguage'] = $sLanguage;
$aConfig['extraPlugins'] = 'disabler';
$aConfig['extraPlugins'] = 'disabler,codesnippet';
$aConfig['placeholder'] = Dict::S('UI:CaseLogTypeYourTextHere');
$sConfigJS = json_encode($aConfig);
@@ -2101,7 +2106,7 @@ EOF
$('#{$iId}_console_form').console_form_handler('alignColumns');
$('#{$iId}_console_form').console_form_handler('option', 'field_set', $('#{$iId}_field_set'));
// field_change must be processed to refresh the hidden value at anytime
$('#{$iId}_console_form').bind('value_change', function() { $('#{$iId}').val(JSON.stringify($('#{$iId}_field_set').triggerHandler('get_current_values'))); console.error($('#{$iId}').val()); });
$('#{$iId}_console_form').bind('value_change', function() { $('#{$iId}').val(JSON.stringify($('#{$iId}_field_set').triggerHandler('get_current_values'))); });
// Initialize the hidden value with current state
// update_value is triggered when preparing the wizard helper object for ajax calls
$('#{$iId}').bind('update_value', function() { $(this).val(JSON.stringify($('#{$iId}_field_set').triggerHandler('get_current_values'))); });
@@ -2174,6 +2179,14 @@ EOF
if ((count($aAllowedValues) == 1) && ($bMandatory == 'true'))
{
// When there is only once choice, select it by default
if($value != $key)
{
$oPage->add_ready_script(
<<<EOF
$('#$iId').attr('data-validate','dependencies');
EOF
);
}
$sSelected = ' selected';
}
else
@@ -2852,7 +2865,7 @@ EOF
return $aDetails;
}
static function FlattenZList($aList)
public static function FlattenZList($aList)
{
$aResult = array();
foreach($aList as $value)
@@ -3032,6 +3045,7 @@ EOF
$current = parent::GetHilightClass(); // Default computation
// Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information
/** @var \iApplicationUIExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$new = $oExtensionInstance->GetHilightClass($this);
@@ -3453,6 +3467,7 @@ EOF
}
// Invoke extensions after the update of the object from the form
/** @var \iApplicationUIExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$oExtensionInstance->OnFormSubmit($this, $sFormPrefix);
@@ -3524,21 +3539,13 @@ EOF
{
if (preg_match("/^attr_$sSubFormPrefix(.*)$/", $sKey, $aMatches))
{
$sLinkClass = $oAttDef->GetLinkedClass();
if ($oAttDef->IsIndirect())
$oLinkAttDef = MetaModel::GetAttributeDef($sObjClass, $aMatches[1]);
// Recursing over n:n link datetime attributes
// Note: We might need to do it with other attribute types, like Document or redundancy setting.
if ($oLinkAttDef instanceof AttributeDateTime)
{
$oLinkAttDef = MetaModel::GetAttributeDef($sLinkClass, $aMatches[1]);
// Recursing over n:n link datetime attributes
// Note: We might need to do it with other attribute types, like Document or redundancy setting.
if ($oLinkAttDef instanceof AttributeDateTime)
{
$aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix,
$aMatches[1], $sLinkClass, $aData);
}
else
{
$aObjData[$aMatches[1]] = $value;
}
$aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix,
$aMatches[1], $sObjClass, $aData);
}
else
{
@@ -3555,26 +3562,19 @@ EOF
foreach($aRawToBeModified as $iObjKey => $aData)
{
$sSubFormPrefix = $aData['formPrefix'];
$sObjClass = isset($aData['class']) ? $aData['class'] : $oAttDef->GetLinkedClass();
$aObjData = array();
foreach($aData as $sKey => $value)
{
if (preg_match("/^attr_$sSubFormPrefix(.*)$/", $sKey, $aMatches))
{
$sLinkClass = $oAttDef->GetLinkedClass();
if ($oAttDef->IsIndirect())
$oLinkAttDef = MetaModel::GetAttributeDef($sObjClass, $aMatches[1]);
// Recursing over n:n link datetime attributes
// Note: We might need to do it with other attribute types, like Document or redundancy setting.
if ($oLinkAttDef instanceof AttributeDateTime)
{
$oLinkAttDef = MetaModel::GetAttributeDef($sLinkClass, $aMatches[1]);
// Recursing over n:n link datetime attributes
// Note: We might need to do it with other attribute types, like Document or redundancy setting.
if ($oLinkAttDef instanceof AttributeDateTime)
{
$aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix,
$aMatches[1], $sLinkClass, $aData);
}
else
{
$aObjData[$aMatches[1]] = $value;
}
$aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix,
$aMatches[1], $sObjClass, $aData);
}
else
{
@@ -3752,17 +3752,19 @@ EOF
{
// Invoke extensions after the update (could be before)
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
}
} catch (Exception $e)
}
catch (Exception $e)
{
unset($aUpdateReentrance[$sKey]);
throw $e;
}
unset($aUpdateReentrance[$sKey]);
finally
{
unset($aUpdateReentrance[$sKey]);
}
return $res;
}
@@ -3886,6 +3888,9 @@ EOF
}
}
/**
* @inheritDoc
*/
protected function DoCheckToDelete(&$oDeletionPlan)
{
parent::DoCheckToDelete($oDeletionPlan);
@@ -4221,6 +4226,7 @@ EOF
$oDummyObj->Set($sAttCode, $currValue);
/** @var ormTagSet $oTagSet */
$oTagSet = $oDummyObj->Get($sAttCode);
$oTagSet->SetDisplayPartial(true);
foreach($aKeys as $iIndex => $sValues)
{
if ($iIndex == 0)

View File

@@ -32,7 +32,7 @@ class CSVPage extends WebPage
function __construct($s_title)
{
parent::__construct($s_title);
$this->add_header("Content-type: text/plain; charset=utf-8");
$this->add_header("Content-type: text/plain; charset=".self::PAGES_CHARSET);
$this->add_header("Cache-control: no-cache");
//$this->add_header("Content-Transfer-Encoding: binary");
}

View File

@@ -444,6 +444,18 @@ EOF
$sClass = $oQuery->GetClass();
foreach($this->oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType)
{
// For external fields, find the real type of the target
$sExtFieldAttCode = $sAttCode;
$sTargetClass = $sClass;
while (is_a($sAttType, 'AttributeExternalField', true))
{
$sExtKeyAttCode = $this->oModelReflection->GetAttributeProperty($sTargetClass, $sExtFieldAttCode, 'extkey_attcode');
$sTargetAttCode = $this->oModelReflection->GetAttributeProperty($sTargetClass, $sExtFieldAttCode, 'target_attcode');
$sTargetClass = $this->oModelReflection->GetAttributeProperty($sTargetClass, $sExtKeyAttCode, 'targetclass');
$aTargetAttCodes = $this->oModelReflection->ListAttributes($sTargetClass);
$sAttType = $aTargetAttCodes[$sTargetAttCode];
$sExtFieldAttCode = $sTargetAttCode;
}
if (is_a($sAttType, 'AttributeLinkedSet', true))
{
continue;
@@ -456,15 +468,6 @@ EOF
{
continue;
}
// For external fields, find the real type of the target
while (is_a($sAttType, 'AttributeExternalField', true))
{
$sExtKeyAttCode = $this->oModelReflection->GetAttributeProperty($sClass, $sAttCode, 'extkey_attcode');
$sTargetClass = $this->oModelReflection->GetAttributeProperty($sClass, $sExtKeyAttCode, 'targetclass');
$sTargetAttCode = $this->oModelReflection->GetAttributeProperty($sClass, $sAttCode, 'target_attcode');
$aTargetAttCodes = $this->oModelReflection->ListAttributes($sTargetClass);
$sAttType = $aTargetAttCodes[$sTargetAttCode];
}
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
if (!in_array($sLabel, $aGroupBy))

View File

@@ -1,14 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.6">
<portals>
<portal id="legacy_portal" _delta="define">
<url>portal/index.php</url>
<rank>1.0</rank>
<handler/>
<allow>
</allow>
<deny/>
</portal>
<portal id="backoffice" _delta="define">
<url>pages/UI.php</url>
<rank>2.0</rank>

View File

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

View File

@@ -249,7 +249,7 @@ class DisplayBlock
$sHtml .= $this->GetRenderContent($oPage, $aExtraParams, $sId);
} catch (Exception $e)
{
IssueLog::Error('Exception during GetDisplay: ' . $e->getMessage());
}
$sHtml .= "</div>\n";
}
@@ -500,7 +500,10 @@ class DisplayBlock
}
$aAttribs =array(
'group' => array('label' => $sGroupByLabel, 'description' => ''),
'value' => array('label'=> Dict::S('UI:GroupBy:'.$sAggregationFunction), 'description' => Dict::Format('UI:GroupBy:'.$sAggregationFunction.'+', $sAggregationAttr))
'value' => array(
'label' => Dict::S('UI:GroupBy:'.$sAggregationFunction),
'description' => Dict::Format('UI:GroupBy:'.$sAggregationFunction.'+', $sAggregationAttr),
),
);
$sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection';
$sHtml .= $oPage->GetP(Dict::Format($sFormat, $iTotalCount));
@@ -699,7 +702,7 @@ class DisplayBlock
'breadcrumb_label' => MetaModel::GetName($this->m_oSet->GetClass()),
'breadcrumb_max_count' => utils::GetConfig()->Get('breadcrumb.max_count'),
'breadcrumb_instance_id' => MetaModel::GetConfig()->GetItopInstanceid(),
'breadcrumb_icon' => utils::GetAbsoluteUrlAppRoot().'images/breadcrumb-search.png'
'breadcrumb_icon' => utils::GetAbsoluteUrlAppRoot().'images/breadcrumb-search.png',
));
$oPage->add_ready_script("$('body').trigger('update_history.itop', [$seventAttachedData])");
@@ -1172,16 +1175,26 @@ EOF
}
if (($bAutoReload) && ($this->m_sStyle != 'search')) // Search form do NOT auto-reload
{
$sFilter = addslashes(str_replace('"', "'", $this->m_oFilter->serialize())); // Used either for asynchronous or auto_reload
$sExtraParams = addslashes(str_replace('"', "'", json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them
// Used either for asynchronous or auto_reload
// does a json_encode twice to get a string usable as function parameter
$sFilterBefore = $this->m_oFilter->serialize();
$sFilter = json_encode($sFilterBefore);
$sExtraParams = json_encode(json_encode($aExtraParams));
$oPage->add_script('if (typeof window.oAutoReloadBlock == "undefined") {
window.oAutoReloadBlock = {};
}
if (typeof window.oAutoReloadBlock[\''.$sId.'\'] != "undefined") {
clearInterval(window.oAutoReloadBlock[\''.$sId.'\']);
}
window.oAutoReloadBlock[\''.$sId.'\'] = setInterval("ReloadBlock(\''.$sId.'\', \''.$this->m_sStyle.'\', \"'.$sFilter.'\", \"'.$sExtraParams.'\")", '.$iReloadInterval.');');
$oPage->add_script(
<<<JS
if (typeof window.oAutoReloadBlock == "undefined") {
window.oAutoReloadBlock = {};
}
if (typeof window.oAutoReloadBlock['$sId'] != "undefined") {
clearInterval(window.oAutoReloadBlock['$sId']);
}
window.oAutoReloadBlock['$sId'] = setInterval(function() {
ReloadBlock('$sId', '{$this->m_sStyle}', $sFilter, $sExtraParams);
}, '$iReloadInterval');
JS
);
}
return $sHtml;
@@ -1719,6 +1732,7 @@ class MenuBlock extends DisplayBlock
*/
}
$this->AddMenuSeparator($aActions);
/** @var \iApplicationUIExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$oSet->Rewind();
@@ -1814,6 +1828,7 @@ class MenuBlock extends DisplayBlock
}
$this->AddMenuSeparator($aActions);
/** @var \iApplicationUIExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$oSet->Rewind();
@@ -1883,7 +1898,8 @@ class MenuBlock extends DisplayBlock
if ($this->m_sStyle == 'details')
{
$sSearchAction = "window.location=\"{$sRootUrl}pages/UI.php?operation=search_form&do_search=0&class=$sClass{$sContext}\"";
$sHtml .= "<div class=\"actions_button icon_actions_button\" title=\"".htmlentities(Dict::Format('UI:SearchFor_Class', MetaModel::GetName($sClass)), ENT_QUOTES, 'UTF-8')."\"><span class=\"search-button fa fa-search\" onclick='$sSearchAction'></span></div>";
$sHtml .= "<div class=\"actions_button icon_actions_button\" title=\"".htmlentities(Dict::Format('UI:SearchFor_Class',
MetaModel::GetName($sClass)), ENT_QUOTES, 'UTF-8')."\"><span class=\"search-button fas fa-search\" onclick='$sSearchAction'></span></div>";
}
@@ -1894,7 +1910,8 @@ class MenuBlock extends DisplayBlock
}
if (!$oPage->IsPrintableVersion() && ($sRefreshAction!=''))
{
$sHtml .= "<div class=\"actions_button icon_actions_button\" title=\"".htmlentities(Dict::S('UI:Button:Refresh'), ENT_QUOTES, 'UTF-8')."\"><span class=\"refresh-button fa fa-refresh\" onclick=\"$sRefreshAction\"></span></div>";
$sHtml .= "<div class=\"actions_button icon_actions_button\" title=\"".htmlentities(Dict::S('UI:Button:Refresh'),
ENT_QUOTES, 'UTF-8')."\"><span class=\"refresh-button fas fa-sync\" onclick=\"$sRefreshAction\"></span></div>";
}

View File

@@ -46,8 +46,6 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
protected $sBreadCrumbEntryIcon;
protected $oCtx;
protected $bHasCollapsibleSection = false;
public function __construct($sTitle, $bPrintable = false)
{
parent::__construct($sTitle, $bPrintable);
@@ -71,7 +69,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
$this->m_sMenu = "";
$this->m_aMessages = array();
$this->SetRootUrl(utils::GetAbsoluteUrlAppRoot());
$this->add_header("Content-type: text/html; charset=utf-8");
$this->add_header("Content-type: text/html; charset=".self::PAGES_CHARSET);
$this->add_header("Cache-control: no-cache");
$this->add_linked_stylesheet("../css/jquery.treeview.css");
$this->add_linked_stylesheet("../css/jquery.autocomplete.css");
@@ -79,7 +77,8 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
$this->add_linked_stylesheet("../css/jquery.multiselect.css");
$this->add_linked_stylesheet("../css/magnific-popup.css");
$this->add_linked_stylesheet("../css/c3.min.css");
$this->add_linked_stylesheet("../css/font-awesome/css/font-awesome.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_script('../js/jquery.layout.min.js');
$this->add_linked_script('../js/jquery.ba-bbq.min.js');
@@ -221,7 +220,6 @@ EOF;
);
$sTimeFormat = AttributeDateTime::GetFormat()->ToTimeFormat();
$oTimeFormat = new DateTimeFormat($sTimeFormat);
$sJSLangShort = json_encode(strtolower(substr(Dict::GetUserLanguage(), 0, 2)));
// Date picker options
$aPickerOptions = array(
@@ -239,29 +237,38 @@ EOF;
$sJSDatePickerOptions = json_encode($aPickerOptions);
// Time picker additional options
$sUserLang = Dict::GetUserLanguage();
$sUserLangShort = strtolower(
substr($sUserLang, 0, 2)
);
// PR #40 : we are picking correct values for specific cases in dict files
// some languages are using codes like zh-CN or pt-BR
$sTimePickerLang = json_encode(
Dict::S('INTERNAL:JQuery-DatePicker:LangCode', $sUserLangShort)
);
$aPickerOptions['showOn'] = '';
$aPickerOptions['buttonImage'] = null;
$aPickerOptions['timeFormat'] = $oTimeFormat->ToDatePicker();
$aPickerOptions['controlType'] = 'select';
$aPickerOptions['closeText'] = Dict::S('UI:Button:Ok');
$sJSDateTimePickerOptions = json_encode($aPickerOptions);
if ($sJSLangShort != '"en"')
if ($sTimePickerLang != '"en"')
{
// More options that cannot be passed via json_encode since they must be evaluated client-side
$aMoreJSOptions = ",
'timeText': $.timepicker.regional[$sJSLangShort].timeText,
'hourText': $.timepicker.regional[$sJSLangShort].hourText,
'minuteText': $.timepicker.regional[$sJSLangShort].minuteText,
'secondText': $.timepicker.regional[$sJSLangShort].secondText,
'currentText': $.timepicker.regional[$sJSLangShort].currentText
'timeText': $.timepicker.regional[$sTimePickerLang].timeText,
'hourText': $.timepicker.regional[$sTimePickerLang].hourText,
'minuteText': $.timepicker.regional[$sTimePickerLang].minuteText,
'secondText': $.timepicker.regional[$sTimePickerLang].secondText,
'currentText': $.timepicker.regional[$sTimePickerLang].currentText
}";
$sJSDateTimePickerOptions = substr($sJSDateTimePickerOptions, 0, -1).$aMoreJSOptions;
}
$this->add_script(
<<< EOF
<<< JS
function GetUserLanguage()
{
return $sJSLangShort;
return $sTimePickerLang;
}
function PrepareWidgets()
{
@@ -293,12 +300,12 @@ EOF;
});
});
}
EOF
JS
);
// Attribute set tooltip on items
$this->add_ready_script(
<<<EOF
<<<JS
$('.attribute-set-item').each(function(){
// Encoding only title as the content is already sanitized by the HTML attribute.
var sLabel = $('<div/>').text($(this).attr('data-label')).html();
@@ -325,25 +332,24 @@ EOF
position: { corner: { target: 'topMiddle', tooltip: 'bottomLeft' }}
});
});
EOF
JS
);
// Make image attributes zoomable
$this->add_ready_script(
<<<EOF
<<<JS
$('.view-image img').each(function(){
$(this).attr('href', $(this).attr('src'))
})
.magnificPopup({type: 'image', closeOnContentClick: true });
EOF
JS
);
$this->add_init_script(
<<< EOF
<<< JS
try
{
var myLayout; // a var is required because this page utilizes: myLayout.allowOverflow() method
// Layout
paneSize = GetUserPreference('menu_size', 300);
if ($('body').length > 0)
@@ -449,11 +455,11 @@ EOF
// Do something with the error !
alert(err);
}
EOF
JS
);
$this->add_ready_script(
<<< EOF
<<< JS
// Adjust initial size
$('.v-resizable').each( function()
@@ -614,7 +620,7 @@ EOF
});
}
});
EOF
JS
);
$this->add_ready_script(InlineImage::FixImagesWidth());
/*
@@ -625,7 +631,7 @@ EOF
$sUserPrefs = appUserPreferences::GetAsJSON();
$this->add_script(
<<<EOF
<<<JS
// // for JQuery history
// function history_callback(hash)
// {
@@ -695,7 +701,7 @@ EOF
{
$('.ui-layout-center, .ui-layout-north, .ui-layout-south').css({display: 'block'});
}
EOF
JS
);
}
@@ -761,15 +767,9 @@ EOF
switch ($iCount)
{
case 0:
// No such dimension/silo => nothing to select
$sHtml = '<div id="SiloSelection"><!-- nothing to select --></div>';
break;
case 1:
// Only one possible choice... no selection, but display the value
$oOrg = $oSet->Fetch();
$sHtml = '<div id="SiloSelection">'.$oOrg->GetName().'</div>';
$sHtml .= '';
// No such dimension/silo or only one possible choice => nothing to select
$sHtml = '<div id="SiloSelection"><!-- nothing to select --></div>';
break;
default:
@@ -847,7 +847,7 @@ EOF
$aParams = array(
'image_url' => $sImageUrl,
'placeholder_image_url' => $sPlaceholderImageUrl,
'cache_uuid' => 'itop-newsroom-'.md5(APPROOT),
'cache_uuid' => 'itop-newsroom-'.UserRights::GetUserId().'-'.md5(APPROOT),
'providers' => $aProviderParams,
'display_limit' => (int)appUserPreferences::GetPref('newsroom_display_size', 7),
'labels' => array(
@@ -954,8 +954,8 @@ EOF
$sNewEntry = json_encode(array(
'id' => $this->sBreadCrumbEntryId,
'url' => $this->sBreadCrumbEntryUrl,
'label' => htmlentities($this->sBreadCrumbEntryLabel, ENT_QUOTES, 'UTF-8'),
'description' => htmlentities($this->sBreadCrumbEntryDescription, ENT_QUOTES, 'UTF-8'),
'label' => htmlentities($this->sBreadCrumbEntryLabel, ENT_QUOTES, self::PAGES_CHARSET),
'description' => htmlentities($this->sBreadCrumbEntryDescription, ENT_QUOTES, self::PAGES_CHARSET),
'icon' => $this->sBreadCrumbEntryIcon,
));
}
@@ -988,8 +988,9 @@ EOF
$sHtml .= "<head>\n";
// Make sure that Internet Explorer renders the page using its latest/highest/greatest standards !
$sHtml .= "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n";
$sHtml .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
$sHtml .= "<title>".htmlentities($this->s_title, ENT_QUOTES, 'UTF-8')."</title>\n";
$sPageCharset = self::PAGES_CHARSET;
$sHtml .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=$sPageCharset\" />\n";
$sHtml .= "<title>".htmlentities($this->s_title, ENT_QUOTES, $sPageCharset)."</title>\n";
$sHtml .= $this->get_base_tag();
// Stylesheets MUST be loaded before any scripts otherwise
// jQuery scripts may face some spurious problems (like failing on a 'reload')
@@ -1115,9 +1116,11 @@ EOF
$sHtml .= '<p>'.Dict::Format('UI:ExplainPrintable',
'<img src="../images/eye-open-555.png" style="vertical-align:middle">').'</p>';
$sHtml .= "<div id=\"hiddeable_chapters\"></div>";
$sHtml .= '<button onclick="window.print()">'.htmlentities(Dict::S('UI:Button:GoPrint'), ENT_QUOTES, 'UTF-8').'</button>';
$sHtml .= '<button onclick="window.print()">'.htmlentities(Dict::S('UI:Button:GoPrint'), ENT_QUOTES,
self::PAGES_CHARSET).'</button>';
$sHtml .= '&nbsp;';
$sHtml .= '<button onclick="window.close()">'.htmlentities(Dict::S('UI:Button:Cancel'), ENT_QUOTES, 'UTF-8').'</button>';
$sHtml .= '<button onclick="window.close()">'.htmlentities(Dict::S('UI:Button:Cancel'), ENT_QUOTES,
self::PAGES_CHARSET).'</button>';
$sHtml .= '&nbsp;';
$sDefaultResolution = '27.7cm';
@@ -1155,7 +1158,7 @@ EOF;
}
// Render the text of the global search form
$sText = htmlentities(utils::ReadParam('text', '', false, 'raw_data'), ENT_QUOTES, 'UTF-8');
$sText = htmlentities(utils::ReadParam('text', '', false, 'raw_data'), ENT_QUOTES, self::PAGES_CHARSET);
$sOnClick = " onclick=\"if ($('#global-search-input').val() != '') { $('#global-search form').submit(); } \"";
$sDefaultPlaceHolder = Dict::S("UI:YourSearch");
@@ -1210,7 +1213,7 @@ EOF;
$oExitArchive = new JSPopupMenuItem('UI:ArchiveModeOff', Dict::S('UI:ArchiveModeOff'), 'return ArchiveMode(false);');
$aActions[$oExitArchive->GetUID()] = $oExitArchive->GetMenuItem();
$sIcon = '<span class="fa fa-lock fa-1x"></span>';
$sIcon = '<span class="fas fa-lock fa-1x"></span>';
$this->AddApplicationMessage(Dict::S('UI:ArchiveMode:Banner'), $sIcon, Dict::S('UI:ArchiveMode:Banner+'));
}
elseif (UserRights::CanBrowseArchive())
@@ -1256,8 +1259,8 @@ EOF;
$sIcon =
<<<EOF
<span class="fa-stack fa-sm">
<i class="fa fa-pencil fa-flip-horizontal fa-stack-1x"></i>
<i class="fa fa-ban fa-stack-2x text-danger"></i>
<i class="fas fa-pencil-alt fa-flip-horizontal fa-stack-1x"></i>
<i class="fas fa-ban fa-stack-2x text-danger"></i>
</span>
EOF;
@@ -1274,7 +1277,7 @@ EOF;
{
$sHtmlIcon = $aMessage['icon'] ? $aMessage['icon'] : '';
$sHtmlMessage = $aMessage['message'];
$sTitleAttr = $aMessage['tip'] ? 'title="'.htmlentities($aMessage['tip'], ENT_QUOTES, 'UTF-8').'"' : '';
$sTitleAttr = $aMessage['tip'] ? 'title="'.htmlentities($aMessage['tip'], ENT_QUOTES, self::PAGES_CHARSET).'"' : '';
$sApplicationMessages .= '<div class="app-message" '.$sTitleAttr.'><span class="app-message-icon">'.$sHtmlIcon.'</span><span class="app-message-body">'.$sHtmlMessage.'</div></span>';
}
@@ -1305,9 +1308,11 @@ EOF;
$sHtml .= '<!-- Beginning of the left pane -->';
$sHtml .= ' <div class="ui-layout-north">';
$sHtml .= ' <div id="header-logo">';
$sHtml .= ' <div id="top-left"></div><div id="logo"><a href="'.htmlentities($sIconUrl, ENT_QUOTES,
'UTF-8').'"><img src="'.$sDisplayIcon.'" title="'.htmlentities($sVersionString, ENT_QUOTES,
'UTF-8').'" style="border:0; margin-top:16px; margin-right:40px;"/></a></div>';
$sHtml .= ' <div id="top-left"></div><div id="logo"><a href="'
.htmlentities($sIconUrl, ENT_QUOTES, self::PAGES_CHARSET)
.'"><img src="'.$sDisplayIcon.'" title="'
.htmlentities($sVersionString, ENT_QUOTES, self::PAGES_CHARSET)
.'" style="border:0; margin-top:16px; margin-right:40px;"/></a></div>';
$sHtml .= ' </div>';
$sHtml .= ' <div class="header-menu">';
if (!MetaModel::GetConfig()->Get('demo_mode'))
@@ -1437,38 +1442,6 @@ EOF;
ExecutionKPI::ReportStats();
}
/**
* Adds init scripts for the collapsible sections
*/
private function outputCollapsibleSectionInit()
{
if (!$this->bHasCollapsibleSection)
{
return;
}
$this->add_script(<<<'EOD'
function initCollapsibleSection(iSectionId, bOpenedByDefault, sSectionStateStorageKey)
{
var bStoredSectionState = JSON.parse(localStorage.getItem(sSectionStateStorageKey));
var bIsSectionOpenedInitially = (bStoredSectionState == null) ? bOpenedByDefault : bStoredSectionState;
if (bIsSectionOpenedInitially) {
$("#LnkCollapse_"+iSectionId).toggleClass("open");
$("#Collapse_"+iSectionId).toggle();
}
$("#LnkCollapse_"+iSectionId).click(function(e) {
localStorage.setItem(sSectionStateStorageKey, !($("#Collapse_"+iSectionId).is(":visible")));
$("#LnkCollapse_"+iSectionId).toggleClass("open");
$("#Collapse_"+iSectionId).slideToggle("normal");
e.preventDefault(); // we don't want to do anything more (see #1030 : a non wanted tab switching was triggered)
});
}
EOD
);
}
public function AddTabContainer($sTabContainer, $sPrefix = '')
{
$this->add($this->m_oTabs->AddTabContainer($sTabContainer, $sPrefix));
@@ -1539,43 +1512,6 @@ EOD
$this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabLabel));
}
public function StartCollapsibleSection(
$sSectionLabel, $bOpenedByDefault = false, $sSectionStateStorageBusinessKey = ''
) {
$this->add($this->GetStartCollapsibleSection($sSectionLabel, $bOpenedByDefault,
$sSectionStateStorageBusinessKey));
}
private function GetStartCollapsibleSection(
$sSectionLabel, $bOpenedByDefault = false, $sSectionStateStorageBusinessKey = ''
) {
$this->bHasCollapsibleSection = true;
$sHtml = '';
static $iSectionId = 0;
$sHtml .= '<a id="LnkCollapse_'.$iSectionId.'" class="CollapsibleLabel" href="#">'.$sSectionLabel.'</a></br>'."\n";
$sHtml .= '<div id="Collapse_'.$iSectionId.'" style="display:none">'."\n";
$oConfig = MetaModel::GetConfig();
$sSectionStateStorageKey = $oConfig->GetItopInstanceid().'/'.$sSectionStateStorageBusinessKey.'/collapsible-'.$iSectionId;
$sSectionStateStorageKey = json_encode($sSectionStateStorageKey);
$sOpenedByDefault = ($bOpenedByDefault) ? 'true' : 'false';
$this->add_ready_script("initCollapsibleSection($iSectionId, $sOpenedByDefault, '$sSectionStateStorageKey');");
$iSectionId++;
return $sHtml;
}
public function EndCollapsibleSection()
{
$this->add($this->GetEndCollapsibleSection());
}
public function GetEndCollapsibleSection()
{
return "</div>";
}
public function add($sHtml)
{
if (($this->m_oTabs->GetCurrentTabContainer() != '') && ($this->m_oTabs->GetCurrentTab() != ''))
@@ -1644,7 +1580,7 @@ EOD
*/
public function SetMessage($sHtmlMessage)
{
$sHtmlIcon = '<span class="fa fa-comment fa-1x"></span>';
$sHtmlIcon = '<span class="fas fa-comment fa-1x"></span>';
$this->AddApplicationMessage($sHtmlMessage, $sHtmlIcon);
}

View File

@@ -0,0 +1,124 @@
<?php
/**
* Class LoginBasic
*
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class LoginBasic extends AbstractLoginFSMExtension
{
/**
* Return the list of supported login modes for this plugin
*
* @return array of supported login modes
*/
public function ListSupportedLoginModes()
{
return array('basic');
}
protected function OnModeDetection(&$iErrorCode)
{
if (!isset($_SESSION['login_mode']))
{
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION']))
{
$_SESSION['login_mode'] = 'basic';
}
elseif (isset($_SERVER['PHP_AUTH_USER']))
{
$_SESSION['login_mode'] = 'basic';
}
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnReadCredentials(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'basic')
{
list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword();
$_SESSION['login_temp_auth_user'] = $sAuthUser;
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnCheckCredentials(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'basic')
{
list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword();
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
{
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnCredentialsOK(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'basic')
{
list($sAuthUser) = $this->GetAuthUserAndPassword();
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnError(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'basic')
{
LoginWebPage::HTTP401Error();
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnConnected(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'basic')
{
$_SESSION['can_logoff'] = true;
return LoginWebPage::CheckLoggedUser($iErrorCode);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
private function GetAuthUserAndPassword()
{
$sAuthUser = '';
$sAuthPwd = null;
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION']))
{
list($sAuthUser, $sAuthPwd) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
}
else
{
if (isset($_SERVER['PHP_AUTH_USER']))
{
$sAuthUser = $_SERVER['PHP_AUTH_USER'];
// Unfortunately, the RFC is not clear about the encoding...
// IE and FF supply the user and password encoded in ISO-8859-1 whereas Chrome provides them encoded in UTF-8
// So let's try to guess if it's an UTF-8 string or not... fortunately all encodings share the same ASCII base
if (!LoginWebPage::LooksLikeUTF8($sAuthUser))
{
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
// Supposed to be harmless in case of a plain ASCII string...
$sAuthUser = iconv('iso-8859-1', 'utf-8', $sAuthUser);
}
$sAuthPwd = $_SERVER['PHP_AUTH_PW'];
if (!LoginWebPage::LooksLikeUTF8($sAuthPwd))
{
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
// Supposed to be harmless in case of a plain ASCII string...
$sAuthPwd = iconv('iso-8859-1', 'utf-8', $sAuthPwd);
}
}
}
return array($sAuthUser, $sAuthPwd);
}
}

View File

@@ -0,0 +1,129 @@
<?php
/**
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* Class LoginDefaultBefore
*/
class LoginDefaultBefore extends AbstractLoginFSMExtension
{
/**
* Must be executed before the other login plugins
*
* @return array of supported login modes
*/
public function ListSupportedLoginModes()
{
return array('before');
}
protected function OnStart(&$iErrorCode)
{
$iErrorCode = LoginWebPage::EXIT_CODE_OK;
unset($_SESSION['login_temp_auth_user']);
// Check if proposed login mode is present and allowed
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
$sProposedLoginMode = utils::ReadParam('login_mode', '');
$index = array_search($sProposedLoginMode, $aAllowedLoginTypes);
if ($index !== false)
{
// Force login mode
$_SESSION['login_mode'] = $sProposedLoginMode;
}
else
{
unset($_SESSION['login_mode']);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnReadCredentials(&$iErrorCode)
{
// Check if proposed login mode is present and allowed
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
$sProposedLoginMode = utils::ReadParam('login_mode', '');
$index = array_search($sProposedLoginMode, $aAllowedLoginTypes);
if ($index !== false)
{
// Force login mode
LoginWebPage::SetLoginModeAndReload($sProposedLoginMode);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
}
/**
* Class LoginDefaultAfter
*/
class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExtension
{
/**
* Must be executed after the other login plugins
*
* @return array of supported login modes
*/
public function ListSupportedLoginModes()
{
return array('after');
}
protected function OnError(&$iErrorCode)
{
self::ResetLoginSession();
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit == LoginWebPage::EXIT_RETURN)
{
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
elseif ($iOnExit == LoginWebPage::EXIT_HTTP_401)
{
LoginWebPage::HTTP401Error(); // Error, exit
}
// LoginWebPage::EXIT_PROMPT
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnCredentialsOk(&$iErrorCode)
{
if (!isset($_SESSION['login_mode']))
{
// If no plugin validated the user, exit
self::ResetLoginSession();
exit();
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* Execute all actions to log out properly
*/
public function LogoutAction()
{
self::ResetLoginSession();
}
protected function OnConnected(&$iErrorCode)
{
unset($_SESSION['login_temp_auth_user']);
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
// Hard reset of the session
private static function ResetLoginSession()
{
LoginWebPage::ResetSession();
foreach (array_keys($_SESSION) as $sKey)
{
if (utils::StartsWith($sKey, 'login_'))
{
unset($_SESSION[$sKey]);
}
}
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* Class LoginExternal
*
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class LoginExternal extends AbstractLoginFSMExtension
{
/**
* Return the list of supported login modes for this plugin
*
* @return array of supported login modes
*/
public function ListSupportedLoginModes()
{
return array('external');
}
protected function OnModeDetection(&$iErrorCode)
{
if (!isset($_SESSION['login_mode']))
{
$sAuthUser = $this->GetAuthUser();
if ($sAuthUser && (strlen($sAuthUser) > 0))
{
$_SESSION['login_mode'] = 'external';
}
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnCheckCredentials(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'external')
{
$sAuthUser = $this->GetAuthUser();
if (!UserRights::CheckCredentials($sAuthUser, '', $_SESSION['login_mode'], 'external'))
{
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnCredentialsOK(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'external')
{
$sAuthUser = $this->GetAuthUser();
LoginWebPage::OnLoginSuccess($sAuthUser, 'external', $_SESSION['login_mode']);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnConnected(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'external')
{
$_SESSION['can_logoff'] = false;
return LoginWebPage::CheckLoggedUser($iErrorCode);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @return bool
*/
private function GetAuthUser()
{
$sExtAuthVar = MetaModel::GetConfig()->GetExternalAuthenticationVariable(); // In which variable is the info passed ?
eval('$sAuthUser = isset('.$sExtAuthVar.') ? '.$sExtAuthVar.' : false;'); // Retrieve the value
/** @var string $sAuthUser */
return $sAuthUser; // Retrieve the value
}
}

View File

@@ -0,0 +1,139 @@
<?php
/**
* Class LoginForm
*
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class LoginForm extends AbstractLoginFSMExtension implements iLoginDataExtension
{
private $bForceFormOnError = false;
/**
* Return the list of supported login modes for this plugin
*
* @return array of supported login modes
*/
public function ListSupportedLoginModes()
{
return array('form');
}
protected function OnReadCredentials(&$iErrorCode)
{
if (!isset($_SESSION['login_mode']) || ($_SESSION['login_mode'] == 'form'))
{
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
if ($this->bForceFormOnError || empty($sAuthUser) || empty($sAuthPwd))
{
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER))
{
// X-Combodo-Ajax is a special header automatically added to all ajax requests
// Let's reply that we're currently logged-out
header('HTTP/1.0 401 Unauthorized');
exit;
}
// No credentials yet, display the form
$oPage = LoginWebPage::NewLoginWebPage();
$oPage->DisplayLoginForm($this->bForceFormOnError);
$oPage->output();
$this->bForceFormOnError = false;
exit;
}
$_SESSION['login_temp_auth_user'] = $sAuthUser;
$_SESSION['login_mode'] = 'form';
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnCheckCredentials(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'form')
{
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
{
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnCredentialsOK(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'form')
{
if (isset($_SESSION['auth_user']))
{
// If FSM reenter this state (example 2FA) then the auth_user is not resubmitted
$sAuthUser = $_SESSION['auth_user'];
}
else
{
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
}
// Store 'auth_user' in session for further use
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnError(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'form')
{
$this->bForceFormOnError = true;
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnConnected(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'form')
{
$_SESSION['can_logoff'] = true;
return LoginWebPage::CheckLoggedUser($iErrorCode);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
/**
* @return LoginTwigData
* @throws \Exception
*/
public function GetLoginData()
{
$aPostedVars = array('auth_user', 'auth_pwd');
$oLoginData = new LoginTwigData($aPostedVars);
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
$sAuthPwd = utils::ReadParam('suggest_pwd', '', true, 'raw_data');
$aData = array(
'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'));
$bEnableResetPassword = empty(MetaModel::GetConfig()->Get('forgot_password')) ? true : 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));
return $oLoginData;
}
}

View File

@@ -0,0 +1,227 @@
<?php
/**
*
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\TwigExtension;
class LoginTwigData
{
private $aBlockData;
private $aPostedVars;
private $sTwigLoaderPath;
private $sCSSFile;
/** @var array */
private $aJsFiles;
/**
* LoginTwigData constructor.
*
* @param array $aPostedVars
* @param string $sLoaderPath
* @param string $sCSSFile
* @param array $aJsFiles
*/
public function __construct($aPostedVars = array(), $sLoaderPath = null, $sCSSFile = null, $aJsFiles = array())
{
$this->aBlockData = array();
$this->aPostedVars = $aPostedVars;
$this->sTwigLoaderPath = $sLoaderPath;
$this->sCSSFile = $sCSSFile;
$this->aJsFiles = $aJsFiles;
}
/**
* @param string $sBlockName
* @param LoginBlockData $oBlockData
*/
public final function AddBlockData($sBlockName, $oBlockData)
{
$this->aBlockData[$sBlockName] = $oBlockData;
}
public final function GetBlockData($sBlockName)
{
/** @var LoginBlockData $oBlockData */
$oBlockData = isset($this->aBlockData[$sBlockName]) ? $this->aBlockData[$sBlockName] : null;
return $oBlockData;
}
public final function GetPostedVars()
{
return $this->aPostedVars;
}
public final function GetTwigLoaderPath()
{
return $this->sTwigLoaderPath;
}
public final function GetCSSFile()
{
return $this->sCSSFile;
}
/**
* @return array
*/
public function GetJsFiles()
{
return $this->aJsFiles;
}
}
class LoginBlockData
{
private $sTwig;
private $aData;
/**
* LoginBlockData constructor.
*
* @param string $sTwig
* @param array $aData
*/
public function __construct($sTwig, $aData = array())
{
$this->sTwig = $sTwig;
$this->aData = $aData;
}
public final function GetTwig()
{
return $this->sTwig;
}
public final function GetData()
{
return $this->aData;
}
}
class LoginTwigContext
{
private $aLoginPluginList;
private $aPluginFormData;
private $aPostedVars;
private $oTwig;
public function __construct()
{
$this->aLoginPluginList = LoginWebPage::GetLoginPluginList('iLoginDataExtension', 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();
if ($sTwigLoaderPath != null)
{
$aTwigLoaders[] = new Twig_Loader_Filesystem($sTwigLoaderPath);
}
$this->aPostedVars = array_merge($this->aPostedVars, $oLoginData->GetPostedVars());
}
$oCoreLoader = new Twig_Loader_Filesystem(array(), APPROOT.'templates');
$aCoreTemplatesPaths = array('login', 'login/password');
// Having this path declared after the plugins let the plugins replace the core templates
$oCoreLoader->setPaths($aCoreTemplatesPaths);
// Having the core templates accessible within a different namespace offer the possibility to extend them while replacing them
$oCoreLoader->setPaths($aCoreTemplatesPaths, 'ItopCore');
$aTwigLoaders[] = $oCoreLoader;
$oLoader = new Twig_Loader_Chain($aTwigLoaders);
$this->oTwig = new Twig_Environment($oLoader);
TwigExtension::RegisterTwigExtensions($this->oTwig);
}
public function GetDefaultVars()
{
$sLogo = 'itop-logo-external.png';
$sBrandingLogo = 'login-logo.png';
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo.'?t='.utils::GetCacheBusterTimestamp();
if (file_exists(MODULESROOT.'branding/'.$sBrandingLogo))
{
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo.'?t='.utils::GetCacheBusterTimestamp();
}
$aVars = array(
'sAppRootUrl' => utils::GetAbsoluteUrlAppRoot(),
'aPluginFormData' => $this->GetPluginFormData(),
'sItopVersion' => ITOP_VERSION,
'sVersionShort' => $sVersionShort,
'sIconUrl' => $sIconUrl,
'sDisplayIcon' => $sDisplayIcon,
);
return $aVars;
}
public function Render(NiceWebPage $oPage, $sTwigFile, $aVars = array())
{
$oTemplate = $this->GetTwig()->load($sTwigFile);
$oPage->add($oTemplate->renderBlock('body', $aVars));
$oPage->add_script($oTemplate->renderBlock('script', $aVars));
$oPage->add_ready_script($oTemplate->renderBlock('ready_script', $aVars));
$oPage->add_style($oTemplate->renderBlock('css', $aVars));
// Render CSS links
foreach ($this->aPluginFormData as $oFormData)
{
/** @var \LoginTwigData $oFormData */
$sCSSFile = $oFormData->GetCSSFile();
if (!empty($sCSSFile))
{
$oPage->add_linked_stylesheet($sCSSFile);
}
$aJsFiles = $oFormData->GetJsFiles();
foreach ($aJsFiles as $sJsFile)
{
$oPage->add_linked_script($sJsFile);
}
}
}
/**
* @return mixed
*/
public function GetLoginPluginList()
{
return $this->aLoginPluginList;
}
/**
* @return array
*/
public function GetPluginFormData()
{
return $this->aPluginFormData;
}
/**
* @return array
*/
public function GetPostedVars()
{
return $this->aPostedVars;
}
/**
* @return \Twig_Environment
*/
public function GetTwig()
{
return $this->oTwig;
}
}

View File

@@ -0,0 +1,93 @@
<?php
/**
* Class LoginURL
*
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class LoginURL extends AbstractLoginFSMExtension
{
/**
* @var bool
*/
private $bErrorOccurred = false;
/**
* Return the list of supported login modes for this plugin
*
* @return array of supported login modes
*/
public function ListSupportedLoginModes()
{
return array('url');
}
protected function OnModeDetection(&$iErrorCode)
{
if (!isset($_SESSION['login_mode']) && !$this->bErrorOccurred)
{
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
if (!empty($sAuthUser) && !empty($sAuthPwd))
{
$_SESSION['login_mode'] = 'url';
}
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnReadCredentials(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'url')
{
$_SESSION['login_temp_auth_user'] = utils::ReadParam('auth_user', '', false, 'raw_data');
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnCheckCredentials(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'url')
{
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
{
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnCredentialsOK(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'url')
{
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnError(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'url')
{
$this->bErrorOccurred = true;
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnConnected(&$iErrorCode)
{
if ($_SESSION['login_mode'] == 'url')
{
$_SESSION['can_logoff'] = true;
return LoginWebPage::CheckLoggedUser($iErrorCode);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,75 @@
<?php
/**
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
//
// Maintenance message display functions
// Only included by approot.inc.php
//
/**
* Use a setup page to display the maintenance message
* @param $sTitle
* @param $sMessage
*/
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->output();
}
else
{
_MaintenanceTextMessage($sMessage);
}
}
/**
* Use simple text to display the maintenance message
* @param $sMessage
*/
function _MaintenanceTextMessage($sMessage)
{
echo $sMessage;
}
/**
* Use a simple HTML to display the maintenance message
* @param $sMessage
*/
function _MaintenanceHtmlMessage($sMessage)
{
echo '<html><body><div>'.$sMessage.'</div></body></html>';
}
/**
* Use a simple JSON to display the maintenance message
*
* @param $sTitle
* @param $sMessage
*/
function _MaintenanceJsonMessage($sTitle, $sMessage)
{
@include_once(APPROOT.'bootstrap.inc.php');
@include_once(APPROOT."/application/ajaxwebpage.class.inc.php");
if (class_exists('ajax_page'))
{
$oP = new ajax_page($sTitle);
$oP->add_header('Access-Control-Allow-Origin: *');
$oP->SetContentType('application/json');
$oP->add('{"code":100, "message":"'.$sMessage.'"}');
$oP->Output();
}
else
{
_MaintenanceTextMessage($sMessage);
}
}

View File

@@ -291,14 +291,22 @@ EOF
$aChildren = self::GetChildren($index);
$sCSSClass = (count($aChildren) > 0) ? ' class="submenu"' : '';
$sHyperlink = $oMenu->GetHyperlink($aExtraParams);
$sItemHtml = '<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'" '.$sCSSClass.'>';
if ($sHyperlink != '')
{
$oPage->AddToMenu('<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'" '.$sCSSClass.'><a href="'.$oMenu->GetHyperlink($aExtraParams).'">'.$oMenu->GetTitle().'</a></li>');
$sLinkTarget = '';
if ($oMenu->IsHyperLinkInNewWindow())
{
$sLinkTarget .= ' target="_blank"';
}
$sItemHtml .= '<a href="'.$oMenu->GetHyperlink($aExtraParams).'"'.$sLinkTarget.'>'.$oMenu->GetTitle().'</a>';
}
else
{
$oPage->AddToMenu('<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'" '.$sCSSClass.'>'.$oMenu->GetTitle().'</li>');
$sItemHtml .= $oMenu->GetTitle();
}
$sItemHtml .= '</li>';
$oPage->AddToMenu($sItemHtml);
if ($iActiveMenu == $index)
{
$bActive = true;
@@ -606,6 +614,15 @@ abstract class MenuNode
$aExtraParams['c[menu]'] = $this->GetMenuId();
return $this->AddParams(utils::GetAbsoluteUrlAppRoot().'pages/UI.php', $aExtraParams);
}
/**
* @return bool true if the link should be opened in a new window
* @since 2.7.0 N°1283
*/
public function IsHyperLinkInNewWindow()
{
return false;
}
/**
* Add a limiting display condition for the same menu node. The conditions will be combined with a AND
@@ -989,8 +1006,12 @@ class WebPageMenuNode extends MenuNode
*/
protected $sHyperlink;
/** @var bool */
protected $bIsLinkInNewWindow;
/**
* Create a menu item that points to any web page (not only UI.php)
*
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
* @param string $sHyperlink URL to the page to load. Use relative URL if you want to keep the application portable !
* @param integer $iParentIndex ID of the parent menu
@@ -999,12 +1020,17 @@ class WebPageMenuNode extends MenuNode
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @param string $sEnableStimulus
* @param bool $bIsLinkInNewWindow for the {@link WebPageMenuNode::IsHyperLinkInNewWindow} method
*/
public function __construct($sMenuId, $sHyperlink, $iParentIndex, $fRank = 0.0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
public function __construct(
$sMenuId, $sHyperlink, $iParentIndex, $fRank = 0.0, $sEnableClass = null, $iActionCode = null,
$iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null, $bIsLinkInNewWindow = false
)
{
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sHyperlink = $sHyperlink;
$this->aReflectionProperties['url'] = $sHyperlink;
$this->bIsLinkInNewWindow = $bIsLinkInNewWindow;
}
/**
@@ -1017,6 +1043,11 @@ class WebPageMenuNode extends MenuNode
return $this->AddParams( $this->sHyperlink, $aExtraParams);
}
public function IsHyperLinkInNewWindow()
{
return $this->bIsLinkInNewWindow;
}
/**
* @param WebPage $oPage
* @param array $aExtraParams

View File

@@ -37,14 +37,14 @@ class NiceWebPage extends WebPage
{
parent::__construct($s_title, $bPrintable);
$this->m_aReadyScripts = array();
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-3.3.1.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.min.js');
if(utils::IsDevelopmentEnvironment()) // Needed since many other plugins still rely on oldies like $.browser
{
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-3.0.1.dev.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate.dev.js');
}
else
{
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-3.0.1.prod.min.js');
$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');
@@ -75,6 +75,8 @@ class NiceWebPage extends WebPage
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_abstract.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_time.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/clipboard.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/clipboardwidget.js');
$this->add_dict_entries('UI:Combo');
@@ -232,7 +234,8 @@ EOF
foreach($aChoices as $sKey => $sValue)
{
$sSelected = ($sKey == $sDefaultValue) ? " SELECTED" : "";
$this->add("<option style=\"width: ".$iWidthPx." px;\" value=\"".htmlspecialchars($sKey)."\"$sSelected>".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."</option>");
$this->add("<option style=\"width: ".$iWidthPx." px;\" value=\"".htmlspecialchars($sKey)."\"$sSelected>".htmlentities($sValue,
ENT_QUOTES, self::PAGES_CHARSET)."</option>");
}
$this->add("</select>");
}

View File

@@ -1,15 +1,53 @@
<?php
require_once(APPROOT.'lib/tcpdf/tcpdf.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
*/
require_once(APPROOT.'application/utils.inc.php');
/**
* Custom class derived from TCPDF for providing custom headers and footers
*
* @author denis
*
*/
class iTopPDF extends TCPDF
{
protected $sDocumentTitle;
/**
* Shortcut for {@link TCPDF::SetFont}, to use the font configured
*
* @param string $style
* @param int $size
* @param string $fontfile
* @param string $subset
* @param bool $out
*
* @uses \TCPDF::SetFont()
* @uses \iTopPDF::GetPdfFont()
* @since 2.7
*/
public function SetFontParams($style, $size, $fontfile='', $subset='default', $out=true)
{
$siTopFont = self::GetPdfFont();
$this->SetFont($siTopFont, $style, $size, $fontfile, $subset, $out);
}
public function SetDocumentTitle($sDocumentTitle)
{
$this->sDocumentTitle = $sDocumentTitle;
@@ -17,26 +55,29 @@ class iTopPDF extends TCPDF
/**
* Builds the custom header. Called for each new page.
*
* @see TCPDF::Header()
*/
public function Header()
{
// Title
// Set font
$this->SetFont('dejavusans', 'B', 10);
$this->SetFontParams('B', 10);
$iPageNumberWidth = 25;
$aMargins = $this->getMargins();
// Display the title (centered)
$this->SetXY($aMargins['left'] + $iPageNumberWidth, 0);
$this->MultiCell($this->getPageWidth() - $aMargins['left'] - $aMargins['right'] - 2*$iPageNumberWidth, 15, $this->sDocumentTitle, 0, 'C', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
$this->SetFont('dejavusans', '', 10);
$this->MultiCell($this->getPageWidth() - $aMargins['left'] - $aMargins['right'] - 2 * $iPageNumberWidth, 15, $this->sDocumentTitle,
0, 'C', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
$this->SetFontParams('', 10);
// Display the page number (right aligned)
// Warning: the 'R'ight alignment does not work when using placeholders like $this->getAliasNumPage() or $this->getAliasNbPages()
$this->MultiCell($iPageNumberWidth, 15, Dict::Format('Core:BulkExport:PDF:PageNumber' ,$this->page), 0, 'R', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
$this->MultiCell($iPageNumberWidth, 15, Dict::Format('Core:BulkExport:PDF:PageNumber', $this->page), 0, 'R', false, 0 /* $ln */, '',
'', true, 0, false, true, 15, 'M' /* $valign */);
// Branding logo
$sBrandingIcon = APPROOT.'images/itop-logo.png';
if (file_exists(MODULESROOT.'branding/main-logo.png'))
@@ -51,6 +92,18 @@ class iTopPDF extends TCPDF
{
// No footer
}
/**
* dejavusans is a UTF-8 Unicode font. Standard PDF fonts like helvetica or times new roman are NOT UTF-8
* @return string font in the config file (export_pdf_font)
*/
public static function GetPdfFont()
{
$oConfig = utils::GetConfig();
$sPdfFont = $oConfig->Get('export_pdf_font');
return $sPdfFont;
}
}
/**
@@ -58,49 +111,45 @@ class iTopPDF extends TCPDF
*/
class PDFPage extends WebPage
{
/**
* Instance of the TCPDF object for creating the PDF
* @var TCPDF
*/
/** @var \iTopPDF Instance of the TCPDF object for creating the PDF */
protected $oPdf;
public function __construct($s_title, $sPageFormat = 'A4', $sPageOrientation = 'L')
{
parent::__construct($s_title);
define(K_PATH_FONTS, APPROOT.'lib/tcpdf/fonts');
$this->oPdf = new iTopPDF($sPageOrientation, 'mm', $sPageFormat, true, 'UTF-8', false);
define(K_PATH_FONTS, APPROOT.'lib/combodo/tcpdf/fonts');
$this->oPdf = new iTopPDF($sPageOrientation, 'mm', $sPageFormat, true, self::PAGES_CHARSET, false);
// set document information
$this->oPdf->SetCreator(PDF_CREATOR);
$this->oPdf->SetAuthor('iTop');
$this->oPdf->SetTitle($s_title);
$this->oPdf->SetDocumentTitle($s_title);
$this->oPdf->setFontSubsetting(true);
// Set font
// dejavusans is a UTF-8 Unicode font. Standard PDF fonts like helvetica or times new roman are NOT UTF-8
$this->oPdf->SetFont('dejavusans', '', 10, '', true);
$this->oPdf->SetFontParams('', 10, '', true);
// set auto page breaks
$this->oPdf->SetAutoPageBreak(true, 15); // 15 mm break margin at the bottom
$this->oPdf->SetTopMargin(15);
// Add a page, we're ready to start
$this->oPdf->AddPage();
$this->SetContentDisposition('inline', $s_title.'.pdf');
$this->SetDefaultStyle();
}
/**
* Sets a default style (suitable for printing) to be included each time $this->oPdf->writeHTML() is called
*/
protected function SetDefaultStyle()
{
$this->add_style(
<<<EOF
<<<EOF
table {
padding: 2pt;
}
@@ -124,19 +173,21 @@ td.icon {
width: 30px;
}
EOF
);
);
}
/**
* Get access to the underlying TCPDF object
* @return TCPDF
*
* @return \iTopPDF
*/
public function get_tcpdf()
{
$this->flush();
return $this->oPdf;
}
/**
* Writes the currently buffered HTML content into the PDF. This can be useful:
* - to sync the flow in case you want to access the underlying TCPDF object for some specific/graphic output
@@ -156,39 +207,42 @@ EOF
$this->s_content = '';
}
}
/**
* Whether or not the page is a PDF page
*
* @return boolean
*/
public function is_pdf()
{
return true;
}
/**
* Generates the PDF document and returns the PDF content as a string
*
* @return string
* @see WebPage::output()
*/
public function output()
{
$this->add_header('Content-type: application/x-pdf');
if (!empty($this->sContentDisposition))
{
if (!empty($this->sContentDisposition))
{
$this->add_header('Content-Disposition: '.$this->sContentDisposition.'; filename="'.$this->sContentFileName.'"');
}
foreach($this->a_headers as $s_header)
{
header($s_header);
}
$this->flush();
}
foreach ($this->a_headers as $s_header)
{
header($s_header);
}
$this->flush();
echo $this->oPdf->Output($this->s_title.'.pdf', 'S');
}
public function get_pdf()
{
$this->flush();
return $this->oPdf->Output($this->s_title.'.pdf', 'S');
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -15,12 +15,15 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/core/contexttag.class.inc.php');
/**
* File to include to initialize the datamodel in memory
*
* @copyright Copyright (C) 2010-2016 Combodo SARL
* @copyright Copyright (C) 2010-2019 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -32,10 +35,16 @@ register_shutdown_function(function()
$sReservedMemory = null;
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR))
{
IssueLog::error($err['message']);
if (strpos($err['message'], 'Allowed memory size of') !== false)
{
$sLimit = ini_get('memory_limit');
echo "<p>iTop: Allowed memory size of $sLimit exhausted, contact your administrator to increase memory_limit in php.ini</p>\n";
echo "<p>iTop: Allowed memory size of $sLimit exhausted, contact your administrator to increase 'memory_limit' in php.ini</p>\n";
}
elseif (strpos($err['message'], 'Maximum execution time') !== false)
{
$sLimit = ini_get('max_execution_time');
echo "<p>iTop: Maximum execution time of $sLimit exceeded, contact your administrator to increase 'max_execution_time' in php.ini</p>\n";
}
else
{
@@ -44,9 +53,6 @@ register_shutdown_function(function()
}
});
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/core/contexttag.class.inc.php');
session_name('itop-'.md5(APPROOT));
session_start();
$sSwitchEnv = utils::ReadParam('switch_env', null);
@@ -79,4 +85,4 @@ else
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);

View File

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

View File

@@ -195,9 +195,8 @@ class privUITransactionSession
class privUITransactionFile
{
/**
* Create a new transaction id, store it in the session and return its id
* @param void
* @return int The identifier of the new transaction
* @return int The new transaction identifier
* @throws \Exception
*/
public static function GetNewTransactionId()
{
@@ -207,7 +206,9 @@ class privUITransactionFile
{
throw new Exception('The directory "'.APPROOT.'data" must be writable to the application.');
}
if (!@mkdir(APPROOT.'data/transactions'))
// condition avoids race condition N°2345
// See https://github.com/kalessil/phpinspectionsea/blob/master/docs/probable-bugs.md#mkdir-race-condition
if (!mkdir($concurrentDirectory = APPROOT.'data/transactions') && !is_dir($concurrentDirectory))
{
throw new Exception('Failed to create the directory "'.APPROOT.'data/transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
}

View File

@@ -0,0 +1,126 @@
<?php
namespace Combodo\iTop;
use AttributeDateTime;
use Dict;
use Exception;
use MetaModel;
use Twig_Environment;
use Twig_SimpleFilter;
use Twig_SimpleFunction;
use utils;
class TwigExtension
{
/**
* Registers Twig extensions such as filters or functions.
* It allows us to access some stuff directly in twig.
*
* @param \Twig_Environment $oTwigEnv
*/
public static function RegisterTwigExtensions(Twig_Environment &$oTwigEnv)
{
// Filter to translate a string via the Dict::S function
// Usage in twig: {{ 'String:ToTranslate'|dict_s }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_s',
function ($sStringCode, $sDefault = null, $bUserLanguageOnly = false) {
return Dict::S($sStringCode, $sDefault, $bUserLanguageOnly);
})
);
// Filter to format a string via the Dict::Format function
// Usage in twig: {{ 'String:ToTranslate'|dict_format() }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_format',
function ($sStringCode, $sParam01 = null, $sParam02 = null, $sParam03 = null, $sParam04 = null) {
return Dict::Format($sStringCode, $sParam01, $sParam02, $sParam03, $sParam04);
})
);
// Filter to format output
// example a DateTime is converted to user format
// Usage in twig: {{ 'String:ToFormat'|output_format }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('date_format',
function ($sDate) {
try
{
if (preg_match('@^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$@', trim($sDate)))
{
return AttributeDateTime::GetFormat()->Format($sDate);
}
}
catch (Exception $e)
{
}
return $sDate;
})
);
// Filter to format output
// example a DateTime is converted to user format
// Usage in twig: {{ 'String:ToFormat'|output_format }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('size_format',
function ($sSize) {
return utils::BytesToFriendlyFormat($sSize);
})
);
// Filter to enable base64 encode/decode
// Usage in twig: {{ 'String to encode'|base64_encode }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_encode', 'base64_encode'));
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_decode', 'base64_decode'));
// Filter to enable json decode (encode already exists)
// Usage in twig: {{ aSomeArray|json_decode }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('json_decode', function ($sJsonString, $bAssoc = false) {
return json_decode($sJsonString, $bAssoc);
})
);
// Filter to add itopversion to an url
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_itop_version', function ($sUrl) {
if (strpos($sUrl, '?') === false)
{
$sUrl = $sUrl."?itopversion=".ITOP_VERSION;
}
else
{
$sUrl = $sUrl."&itopversion=".ITOP_VERSION;
}
return $sUrl;
}));
// Filter to add a module's version to an url
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_module_version', function ($sUrl, $sModuleName) {
$sModuleVersion = utils::GetCompiledModuleVersion($sModuleName);
if (strpos($sUrl, '?') === false)
{
$sUrl = $sUrl."?moduleversion=".$sModuleVersion;
}
else
{
$sUrl = $sUrl."&moduleversion=".$sModuleVersion;
}
return $sUrl;
}));
// Function to check our current environment
// Usage in twig: {% if is_development_environment() %}
$oTwigEnv->addFunction(new Twig_SimpleFunction('is_development_environment', function()
{
return utils::IsDevelopmentEnvironment();
}));
// Function to get configuration parameter
// Usage in twig: {{ get_config_parameter('foo') }}
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_config_parameter', function($sParamName)
{
$oConfig = MetaModel::GetConfig();
return $oConfig->Get($sParamName);
}));
}
}

View File

@@ -208,6 +208,14 @@ class UIExtKeyWidget
{
// When there is only once choice, select it by default
$sSelected = 'selected';
if($value != $key)
{
$oPage->add_ready_script(
<<<EOF
$('#$this->iId').attr('data-validate','dependencies');
EOF
);
}
}
else
{

View File

@@ -74,7 +74,7 @@ class UIHTMLEditorWidget
$sLanguage = strtolower(trim(UserRights::GetUserLanguage()));
$aConfig['language'] = $sLanguage;
$aConfig['contentsLanguage'] = $sLanguage;
$aConfig['extraPlugins'] = 'disabler';
$aConfig['extraPlugins'] = 'disabler,codesnippet';
$sWidthSpec = addslashes(trim($this->m_oAttDef->GetWidth()));
if ($sWidthSpec != '')
{

View File

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

View File

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

View File

@@ -1,23 +1,23 @@
<?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
*/
use Leafo\ScssPhp\Compiler;
// 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/>
use ScssPhp\ScssPhp\Compiler;
/**
@@ -26,13 +26,6 @@ use Leafo\ScssPhp\Compiler;
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'core/metamodel.class.php');
require_once(APPROOT.'core/config.class.inc.php');
require_once(APPROOT.'application/transaction.class.inc.php');
require_once(APPROOT.'application/Html2Text.php');
require_once(APPROOT.'application/Html2TextException.php');
define('ITOP_CONFIG_FILE', 'config-itop.php');
define('ITOP_DEFAULT_CONFIG_FILE', APPCONF.ITOP_DEFAULT_ENV.'/'.ITOP_CONFIG_FILE);
@@ -51,8 +44,13 @@ class FileUploadException extends Exception
*/
class utils
{
/**
* Cache when getting config from disk or set externally (using {@link SetConfig})
* @internal
* @var Config $oConfig
* @see GetConfig
*/
private static $oConfig = null;
private static $m_bCASClient = false;
// Parameters loaded from a file, parameters of the page/command line still have precedence
private static $m_aParamsFromFile = null;
@@ -113,10 +111,10 @@ class utils
/**
* Return the source file from which the parameter has been found,
* usefull when it comes to pass user credential to a process executed
* in the background
* @param $sName Parameter name
* @return The file name if any, or null
* useful when it comes to pass user credential to a process executed
* in the background
* @param string $sName Parameter name
* @return string|null The file name if any, or null
*/
public static function GetParamSourceFile($sName)
{
@@ -273,87 +271,104 @@ class utils
}
return $retValue;
}
/**
* @param string|string[] $value
* @param string $sSanitizationFilter one of : integer, class, string, context_param, parameter, field_name,
* transaction_id, parameter, raw_data
*
* @return string|string[]|bool boolean for :
* * the 'class' filter (true if valid, false otherwise)
* * if the filter fails (@see \filter_var())
*
* @since 2.5.2 2.6.0 new 'transaction_id' filter
*/
protected static function Sanitize_Internal($value, $sSanitizationFilter)
{
switch($sSanitizationFilter)
switch ($sSanitizationFilter)
{
case 'integer':
$retValue = filter_var($value, FILTER_SANITIZE_NUMBER_INT);
break;
$retValue = filter_var($value, FILTER_SANITIZE_NUMBER_INT);
break;
case 'class':
$retValue = $value;
if (!MetaModel::IsValidClass($value))
{
$retValue = false;
}
break;
$retValue = $value;
if (!MetaModel::IsValidClass($value))
{
$retValue = false;
}
break;
case 'string':
$retValue = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
break;
$retValue = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
break;
case 'context_param':
case 'parameter':
case 'field_name':
if (is_array($value))
{
$retValue = array();
foreach($value as $key => $val)
if (is_array($value))
{
$retValue[$key] = self::Sanitize_Internal($val, $sSanitizationFilter); // recursively check arrays
if ($retValue[$key] === false)
$retValue = array();
foreach ($value as $key => $val)
{
$retValue = false;
break;
$retValue[$key] = self::Sanitize_Internal($val, $sSanitizationFilter); // recursively check arrays
if ($retValue[$key] === false)
{
$retValue = false;
break;
}
}
}
}
else
{
switch($sSanitizationFilter)
else
{
case 'transaction_id':
// same as parameter type but keep the dot character
// see N°1835 : when using file transaction_id on Windows you get *.tmp tokens
// it must be included at the regexp beginning otherwise you'll get an invalid character error
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[\. A-Za-z0-9_=-]*$/')));
break;
switch ($sSanitizationFilter)
{
case 'transaction_id':
// same as parameter type but keep the dot character
// see N°1835 : when using file transaction_id on Windows you get *.tmp tokens
// it must be included at the regexp beginning otherwise you'll get an invalid character error
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[\. A-Za-z0-9_=-]*$/')));
break;
case 'parameter':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[ A-Za-z0-9_=-]*$/'))); // the '=', '%3D, '%2B', '%2F'
// characters are used in serialized filters (starting 2.5, only the url encoded versions are presents, but the "=" is kept for BC)
break;
case 'parameter':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[ A-Za-z0-9_=-]*$/'))); // the '=', '%3D, '%2B', '%2F'
// characters are used in serialized filters (starting 2.5, only the url encoded versions are presents, but the "=" is kept for BC)
break;
case 'field_name':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options"=>array("regexp"=>'/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name
break;
case 'context_param':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options"=>array("regexp"=>'/^[ A-Za-z0-9_=%:+-]*$/')));
break;
case 'field_name':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name
break;
case 'context_param':
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[ A-Za-z0-9_=%:+-]*$/')));
break;
}
}
}
break;
break;
default:
case 'raw_data':
$retValue = $value;
$retValue = $value;
// Do nothing
}
return $retValue;
return $retValue;
}
/**
* Reads an uploaded file and turns it into an ormDocument object - Triggers an exception in case of error
* @param string $sName Name of the input used from uploading the file
* @param string $sIndex If Name is an array of posted files, then the index must be used to point out the file
*
* @param string $sName Name of the input used from uploading the file
* @param string $sIndex If Name is an array of posted files, then the index must be used to point out the file
*
* @return ormDocument The uploaded file (can be 'empty' if nothing was uploaded)
*/
* @throws \FileUploadException
*/
public static function ReadPostedDocument($sName, $sIndex = null)
{
$oDocument = new ormDocument(); // an empty document
@@ -370,24 +385,8 @@ class utils
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
$doc_content = file_get_contents($sTmpName);
if (function_exists('finfo_file'))
{
// as of PHP 5.3 the fileinfo extension is bundled within PHP
// in which case we don't trust the mime type provided by the browser
$rInfo = @finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
if ($rInfo !== false)
{
$sType = @finfo_file($rInfo, $sTmpName);
if ( ($sType !== false)
&& is_string($sType)
&& (strlen($sType)>0))
{
$sMimeType = $sType;
}
}
@finfo_close($rInfo);
}
$oDocument = new ormDocument($doc_content, $sMimeType, $sName);
$sMimeType = self::GetFileMimeType($sTmpName);
$oDocument = new ormDocument($doc_content, $sMimeType, $sName);
break;
case UPLOAD_ERR_NO_FILE:
@@ -424,14 +423,17 @@ class utils
}
return $oDocument;
}
/**
* Interprets the results posted by a normal or paginated list (in multiple selection mode)
*
* @param $oFullSetFilter DBSearch The criteria defining the whole sets of objects being selected
* @param DBSearch $oFullSetFilter The criteria defining the whole sets of objects being selected
*
* @return Array An array of object IDs corresponding to the objects selected in the set
*/
* @return array An array of object IDs corresponding to the objects selected in the set
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public static function ReadMultipleSelection($oFullSetFilter)
{
$aSelectedObj = utils::ReadParam('selectObject', array());
@@ -525,11 +527,11 @@ class utils
}
/**
* Returns a unique tmp id for the current upload based on the transaction system (db).
*
* Build as static::GetNewTransactionId()
*
* @return string
* @param string $sTransactionId
*
* @return string unique tmp id for the current upload based on the transaction system (db). Build as static::GetNewTransactionId()
*/
public static function GetUploadTempId($sTransactionId = null)
{
@@ -551,7 +553,7 @@ class utils
* as in php.ini, e.g. 256k, 2M, 1G etc. Into a number of bytes
* @param mixed $value The value as read from php.ini
* @return number
*/
*/
public static function ConvertToBytes( $value )
{
$iReturn = $value;
@@ -588,7 +590,7 @@ class utils
/**
* Format a value into a more friendly format (KB, MB, GB, TB) instead a juste a Bytes amount.
*
* @param type $value
* @param float $value
* @return string
*/
public static function BytesToFriendlyFormat($value)
@@ -629,8 +631,8 @@ class utils
* Example: StringToTime('01/05/11 12:03:45', '%d/%m/%y %H:%i:%s')
* @param string $sDate
* @param string $sFormat
* @return timestamp or false if the input format is not correct
*/
* @return string|false false if the input format is not correct, timestamp otherwise
*/
public static function StringToTime($sDate, $sFormat)
{
// Source: http://php.net/manual/fr/function.strftime.php
@@ -672,12 +674,12 @@ class utils
}
/**
* Convert an old date/time format specifciation (using % placeholders)
* Convert an old date/time format specification (using % placeholders)
* to a format compatible with DateTime::createFromFormat
* @param string $sOldDateTimeFormat
* @return string
*/
static public function DateTimeFormatToPHP($sOldDateTimeFormat)
public static function DateTimeFormatToPHP($sOldDateTimeFormat)
{
$aSearch = array('%d', '%m', '%y', '%Y', '%H', '%i', '%s');
$aReplacement = array('d', 'm', 'y', 'Y', 'H', 'i', 's');
@@ -685,31 +687,58 @@ class utils
}
/**
* @return \Config from the current environement, or if not existing from the production env, else new Config made from scratch
* @uses \MetaModel::GetConfig() don't forget to add the needed <code>require_once(APPROOT.'core/metamodel.class.php');</code>
* Allow to set cached config. Useful when running with {@link Parameters} for example.
* @param \Config $oConfig
*/
static public function GetConfig()
public static function SetConfig(Config $oConfig)
{
if (self::$oConfig == null)
self::$oConfig = $oConfig;
}
/**
* @return \Config Get object in the following order :
* <ol>
* <li>from {@link MetaModel::GetConfig} if loaded
* <li>{@link oConfig} attribute if set
* <li>from disk (current env, using {@link GetConfigFilePath}) => if loaded this will be stored in {@link oConfig} attribute
* <li>from disk, env production => if loaded this will be stored in {@link oConfig} attribute
* <li>default Config object
* </ol>
* @throws \ConfigException
* @throws \CoreException
*
* @since 2.7.0 N°2478 always call {@link MetaModel::GetConfig} first, cache is only set when loading from disk
*/
public static function GetConfig()
{
$oMetaModelConfig = MetaModel::GetConfig();
if ($oMetaModelConfig !== null)
{
self::$oConfig = MetaModel::GetConfig();
if (self::$oConfig == null)
{
$sConfigFile = self::GetConfigFilePath();
if (!file_exists($sConfigFile))
{
$sConfigFile = self::GetConfigFilePath('production');
if (!file_exists($sConfigFile))
{
$sConfigFile = null;
}
}
self::$oConfig = new Config($sConfigFile);
}
return $oMetaModelConfig;
}
return self::$oConfig;
if (self::$oConfig !== null)
{
return self::$oConfig;
}
$sCurrentEnvConfigPath = self::GetConfigFilePath();
if (file_exists($sCurrentEnvConfigPath))
{
$oCurrentEnvDiskConfig = new Config($sCurrentEnvConfigPath);
self::SetConfig($oCurrentEnvDiskConfig);
return self::$oConfig;
}
$sProductionEnvConfigPath = self::GetConfigFilePath('production');
if (file_exists($sProductionEnvConfigPath))
{
$oProductionEnvDiskConfig = new Config($sProductionEnvConfigPath);
self::SetConfig($oProductionEnvDiskConfig);
return self::$oConfig;
}
return new Config();
}
public static function InitTimeZone() {
@@ -734,7 +763,7 @@ class utils
*
* @throws \Exception
*/
static public function GetAbsoluteUrlAppRoot()
public static function GetAbsoluteUrlAppRoot()
{
static $sUrl = null;
if ($sUrl === null)
@@ -769,7 +798,7 @@ class utils
*
* @throws \Exception
*/
static public function GetDefaultUrlAppRoot()
public static function GetDefaultUrlAppRoot()
{
// Build an absolute URL to this page on this server/port
$sServerName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
@@ -844,7 +873,7 @@ class utils
* nginx set it to an empty string
* Others might leave it unset (no array entry)
*/
static public function IsConnectionSecure()
public static function IsConnectionSecure()
{
$bSecured = false;
@@ -864,67 +893,23 @@ class utils
*/
static function CanLogOff()
{
$bResult = false;
if(isset($_SESSION['login_mode']))
{
$sLoginMode = $_SESSION['login_mode'];
switch($sLoginMode)
{
case 'external':
$bResult = false;
break;
case 'form':
case 'basic':
case 'url':
case 'cas':
default:
$bResult = true;
}
}
return $bResult;
return (isset($_SESSION['can_logoff']) ? $_SESSION['can_logoff'] : false);
}
/**
* Initializes the CAS client
* Get the _SESSION variable for logging purpose
* @return false|string
*/
static function InitCASClient()
{
$sCASIncludePath = self::GetConfig()->Get('cas_include_path');
include_once($sCASIncludePath.'/CAS.php');
$bCASDebug = self::GetConfig()->Get('cas_debug');
if ($bCASDebug)
{
phpCAS::setDebug(APPROOT.'log/error.log');
}
if (!self::$m_bCASClient)
{
// Initialize phpCAS
$sCASVersion = self::GetConfig()->Get('cas_version');
$sCASHost = self::GetConfig()->Get('cas_host');
$iCASPort = self::GetConfig()->Get('cas_port');
$sCASContext = self::GetConfig()->Get('cas_context');
phpCAS::client($sCASVersion, $sCASHost, $iCASPort, $sCASContext, false /* session already started */);
self::$m_bCASClient = true;
$sCASCACertPath = self::GetConfig()->Get('cas_server_ca_cert_path');
if (empty($sCASCACertPath))
{
// If no certificate authority is provided, do not attempt to validate
// the server's certificate
// THIS SETTING IS NOT RECOMMENDED FOR PRODUCTION.
// VALIDATING THE CAS SERVER IS CRUCIAL TO THE SECURITY OF THE CAS PROTOCOL!
phpCAS::setNoCasServerValidation();
}
else
{
phpCAS::setCasServerCACert($sCASCACertPath);
}
}
}
public static function GetSessionLog()
{
ob_start();
print_r($_SESSION);
$sSessionLog = ob_get_contents();
ob_end_clean();
return $sSessionLog;
}
static function DebugBacktrace($iLimit = 5)
{
$aFullTrace = debug_backtrace();
@@ -943,10 +928,16 @@ class utils
* @param string $sScript Name and relative path to the file (relative to the iTop root dir)
* @param hash $aArguments Associative array of 'arg' => 'value'
* @return array(iCode, array(output lines))
*/
/**
*/
static function ExecITopScript($sScriptName, $aArguments)
/**
* @param string $sScriptName
* @param array $aArguments
*
* @return array
* @throws \ConfigException
* @throws \CoreException
*/
public static function ExecITopScript($sScriptName, $aArguments)
{
$aDisabled = explode(', ', ini_get('disable_functions'));
if (in_array('exec', $aDisabled))
@@ -1031,16 +1022,33 @@ class utils
}
/**
* Returns a path to a folder into which any module can store cache data
* @return string A path to a folder into which any module can store cache data
* The corresponding folder is created or cleaned upon code compilation
* @return string
*/
public static function GetCachePath()
{
return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/';
}
/**
* @return string A path to a folder into which any module can store log
* @since 2.7.0
*/
public static function GetLogPath()
{
return APPROOT.'log/';
}
/**
* Merge standard menu items with plugin provided menus items
*
* @param \WebPage $oPage
* @param int $iMenuId
* @param \DBObjectSet $param
* @param array $aActions
* @param string $sTableId
* @param string $sDataTableId
*
* @throws \Exception
*/
public static function GetPopupMenuItems($oPage, $iMenuId, $param, &$aActions, $sTableId = null, $sDataTableId = null)
{
@@ -1170,7 +1178,10 @@ class utils
}
}
}
/**
* @param string $sEnvironment
*
* @return string target configuration file name (including full path)
*/
public static function GetConfigFilePath($sEnvironment = null)
@@ -1181,7 +1192,10 @@ class utils
}
return APPCONF.$sEnvironment.'/'.ITOP_CONFIG_FILE;
}
/**
* @param string $sEnvironment
*
* @return string target configuration file name (including relative path)
*/
public static function GetConfigFilePathRelative($sEnvironment = null)
@@ -1193,10 +1207,11 @@ class utils
return "conf/".$sEnvironment.'/'.ITOP_CONFIG_FILE;
}
/**
* @return string the absolute URL to the modules root path
*/
static public function GetAbsoluteUrlModulesRoot()
/**
* @return string the absolute URL to the modules root path
* @throws \Exception
*/
public static function GetAbsoluteUrlModulesRoot()
{
$sUrl = self::GetAbsoluteUrlAppRoot().'env-'.self::GetCurrentEnvironment().'/';
return $sUrl;
@@ -1211,17 +1226,20 @@ class utils
* require_once(__DIR__.'/../../approot.inc.php');
* ```
*
* @param string $sModule
* @param string $sPage
* @see GetExecPageArguments can be used to submit using the GET method (see bug in N.1108)
* @see GetAbsoluteUrlExecPage
*
* @param string[] $aArguments
* @param string $sEnvironment
*
* @param string $sModule
* @param string $sPage
*
* @return string the URL to a page that will execute the requested module page, with query string values url encoded
*
* @see GetExecPageArguments can be used to submit using the GET method (see bug in N.1108)
* @see GetAbsoluteUrlExecPage
* @throws \Exception
*/
static public function GetAbsoluteUrlModulePage($sModule, $sPage, $aArguments = array(), $sEnvironment = null)
public static function GetAbsoluteUrlModulePage($sModule, $sPage, $aArguments = array(), $sEnvironment = null)
{
$aArgs = self::GetExecPageArguments($sModule, $sPage, $aArguments, $sEnvironment);
$sArgs = http_build_query($aArgs);
@@ -1238,7 +1256,7 @@ class utils
* @return string[] key/value pair for the exec page query string. <b>Warning</b> : values are not url encoded !
* @throws \Exception if one of the argument has a reserved name
*/
static public function GetExecPageArguments($sModule, $sPage, $aArguments = array(), $sEnvironment = null)
public static function GetExecPageArguments($sModule, $sPage, $aArguments = array(), $sEnvironment = null)
{
$sEnvironment = is_null($sEnvironment) ? self::GetCurrentEnvironment() : $sEnvironment;
$aArgs = array();
@@ -1259,18 +1277,20 @@ class utils
/**
* @return string
* @throws \Exception
*/
static public function GetAbsoluteUrlExecPage()
public static function GetAbsoluteUrlExecPage()
{
return self::GetAbsoluteUrlAppRoot().'pages/exec.php';
}
/**
* Returns a name unique amongst the given list
* @param string $sProposed The default value
* @param array $aExisting An array of existing values (strings)
* @param array $aExisting An array of existing values (strings)
*
* @return string a unique name amongst the given list
*/
static public function MakeUniqueName($sProposed, $aExisting)
public static function MakeUniqueName($sProposed, $aExisting)
{
if (in_array($sProposed, $aExisting))
{
@@ -1292,7 +1312,7 @@ class utils
* @param string $sId The ID to sanitize
* @return string The sanitized ID
*/
static public function GetSafeId($sId)
public static function GetSafeId($sId)
{
return str_replace(array(':', '[', ']', '+', '-'), '_', $sId);
}
@@ -1304,14 +1324,17 @@ class utils
* Does not require cUrl but requires openssl for performing https POSTs.
*
* @param string $sUrl The URL to POST the data to
* @param hash $aData The data to POST as an array('param_name' => value)
* @param array $aData The data to POST as an array('param_name' => value)
* @param string $sOptionnalHeaders Additional HTTP headers as a string with newlines between headers
* @param hash $aResponseHeaders An array to be filled with reponse headers: WARNING: the actual content of the array depends on the library used: cURL or fopen, test with both !! See: http://fr.php.net/manual/en/function.curl-getinfo.php
* @param hash $aCurlOptions An (optional) array of options to pass to curl_init. The format is 'option_code' => 'value'. These values have precedence over the default ones. Example: CURLOPT_SSLVERSION => CURL_SSLVERSION_SSLv3
* @param array $aResponseHeaders An array to be filled with reponse headers: WARNING: the actual content of the array depends on the
* library used: cURL or fopen, test with both !! See: http://fr.php.net/manual/en/function.curl-getinfo.php
* @param array $aCurlOptions An (optional) array of options to pass to curl_init. The format is 'option_code' => 'value'. These values
* have precedence over the default ones. Example: CURLOPT_SSLVERSION => CURL_SSLVERSION_SSLv3
*
* @return string The result of the POST request
* @throws Exception
* @throws Exception with a specific error message depending on the cause
*/
static public function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = array())
public static function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = array())
{
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
@@ -1454,7 +1477,7 @@ class utils
* @param string $sValue
* @return string
*/
static public function HtmlEntities($sValue)
public static function HtmlEntities($sValue)
{
return htmlentities($sValue, ENT_QUOTES, 'UTF-8');
}
@@ -1497,7 +1520,7 @@ class utils
* @param array $aImportPaths Array of absolute paths to load imports from
* @return string Relative path to the CSS file (<name>.css)
*/
static public function GetCSSFromSASS($sSassRelPath, $aImportPaths = null)
public static function GetCSSFromSASS($sSassRelPath, $aImportPaths = null)
{
// Avoiding compilation if file is already a css file.
if (preg_match('/\.css(\?.*)?$/', $sSassRelPath))
@@ -1518,10 +1541,9 @@ class utils
clearstatcache();
if (!file_exists($sCssPath) || (is_writable($sCssPath) && (filemtime($sCssPath) < filemtime($sSassPath))))
{
require_once(APPROOT.'lib/scssphp/scss.inc.php');
$oScss = new Compiler();
$oScss->setImportPaths($aImportPaths);
$oScss->setFormatter('Leafo\\ScssPhp\\Formatter\\Expanded');
$oScss->setFormatter('ScssPhp\\ScssPhp\\Formatter\\Expanded');
// Temporary disabling max exec time while compiling
$iCurrentMaxExecTime = (int) ini_get('max_execution_time');
set_time_limit(0);
@@ -1532,7 +1554,7 @@ class utils
return $sCssRelPath;
}
static public function GetImageSize($sImageData)
public static function GetImageSize($sImageData)
{
if (function_exists('getimagesizefromstring')) // PHP 5.4.0 or higher
{
@@ -1655,7 +1677,7 @@ class utils
* @param string $sPrefix
* @return string
*/
static public function CreateUUID($sPrefix = '')
public static function CreateUUID($sPrefix = '')
{
$uid = uniqid("", true);
$data = $sPrefix;
@@ -1679,10 +1701,10 @@ class utils
/**
* Returns the name of the module containing the file where the call to this function is made
* or an empty string if no such module is found (or not called within a module file)
* @param number $iCallDepth The depth of the module in the callstack. Zero when called directly from within the module
* @param int $iCallDepth The depth of the module in the callstack. Zero when called directly from within the module
* @return string
*/
static public function GetCurrentModuleName($iCallDepth = 0)
public static function GetCurrentModuleName($iCallDepth = 0)
{
$sCurrentModuleName = '';
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
@@ -1705,12 +1727,13 @@ class utils
}
/**
* Returns the relative (to APPROOT) path of the root directory of the module containing the file where the call to this function is made
* Returns the relative (to MODULESROOT) path of the root directory of the module containing the file where the call to
* this function is made
* or an empty string if no such module is found (or not called within a module file)
* @param number $iCallDepth The depth of the module in the callstack. Zero when called directly from within the module
* @return string
*/
static public function GetCurrentModuleDir($iCallDepth)
public static function GetCurrentModuleDir($iCallDepth)
{
$sCurrentModuleDir = '';
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
@@ -1731,13 +1754,13 @@ class utils
}
return $sCurrentModuleDir;
}
/**
* Returns the base URL for all files in the current module from which this method is called
* @return string the base URL for all files in the current module from which this method is called
* or an empty string if no such module is found (or not called within a module file)
* @return string
* @throws \Exception
*/
static public function GetCurrentModuleUrl()
public static function GetCurrentModuleUrl()
{
$sDir = static::GetCurrentModuleDir(1);
if ( $sDir !== '')
@@ -1748,23 +1771,21 @@ class utils
}
/**
* Get the value of a given setting for the current module
* @param string $sProperty The name of the property to retrieve
* @param mixed $defaultvalue
* @return mixed
* @return mixed the value of a given setting for the current module
*/
static public function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
{
$sModuleName = static::GetCurrentModuleName(1);
return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultvalue);
}
/**
* Get the compiled version of a given module, as it was seen by the compiler
* @param string $sModuleName
* @return string|NULL
* @return string|NULL compiled version of a given module, as it was seen by the compiler
*/
static public function GetCompiledModuleVersion($sModuleName)
public static function GetCompiledModuleVersion($sModuleName)
{
$aModulesInfo = GetModulesInfo();
if (array_key_exists($sModuleName, $aModulesInfo))
@@ -1978,10 +1999,9 @@ class utils
}
/**
* Return a string based on compilation time or (if not available because the datamodel has not been loaded)
* @return string a string based on compilation time or (if not available because the datamodel has not been loaded)
* the version of iTop. This string is useful to prevent browser side caching of content that may vary at each
* (re)installation of iTop (especially during development).
* @return string
* (re)installation of iTop (especially during development).
*/
public static function GetCacheBusterTimestamp()
{
@@ -1998,6 +2018,8 @@ class utils
* @param $sClass
*
* @return bool
* @throws \ConfigException
* @throws \CoreException
*/
public static function IsHighCardinality($sClass)
{
@@ -2018,4 +2040,129 @@ class utils
{
return ITOP_REVISION === 'svn';
}
/**
* @see https://php.net/manual/en/function.finfo-file.php
*
* @param string $sFilePath file full path
* @param string $sDefaultMimeType
*
* @return string mime type, defaults to <code>application/octet-stream</code>
* @uses finfo_file in FileInfo extension (bundled in PHP since version 5.3)
* @since 2.7.0 N°2366
*/
public static function GetFileMimeType($sFilePath, $sDefaultMimeType = 'application/octet-stream')
{
if (!function_exists('finfo_file'))
{
return $sDefaultMimeType;
}
$sMimeType = $sDefaultMimeType;
$rInfo = @finfo_open(FILEINFO_MIME_TYPE);
if ($rInfo !== false)
{
$sType = @finfo_file($rInfo, $sFilePath);
if (($sType !== false)
&& is_string($sType)
&& ($sType !== ''))
{
$sMimeType = $sType;
}
}
@finfo_close($rInfo);
return $sMimeType;
}
/**
* helper to test if a string starts with another
* @param $haystack
* @param $needle
*
* @return bool
*/
final public static function StartsWith($haystack, $needle)
{
return substr_compare($haystack, $needle, 0, strlen($needle)) === 0;
}
/**
* helper to test if a string ends with another
* @param $haystack
* @param $needle
*
* @return bool
*/
final public static function EndsWith($haystack, $needle) {
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
}
/**
* @param string $sPath for example '/var/www/html/itop/data/backups/manual/itop_27-2019-10-03_15_35.tar.gz'
* @param string $sBasePath for example '/var/www/html/itop/data/'
*
* @return bool false if path :
* * invalid
* * not allowed
* * not contained in base path
* Otherwise return the real path (see realpath())
*
* @since 2.7.0 N°2538
*/
final public static function RealPath($sPath, $sBasePath)
{
$sFileRealPath = realpath($sPath);
if ($sFileRealPath === false)
{
return false;
}
$sRealBasePath = realpath($sBasePath); // avoid problems when having '/' on Windows for example
if (!self::StartsWith($sFileRealPath, $sRealBasePath))
{
return false;
}
return $sFileRealPath;
}
/**
* 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
*
* @return false|string
*/
final public static function LocalPath($sPath)
{
$sRootPath = realpath(APPROOT);
$sFullPath = realpath($sPath);
if (($sFullPath === false) || !self::StartsWith($sFullPath, $sRootPath))
{
return false;
}
$sLocalPath = substr($sFullPath, strlen($sRootPath.DIRECTORY_SEPARATOR));
$sLocalPath = str_replace(DIRECTORY_SEPARATOR, '/', $sLocalPath);
return $sLocalPath;
}
/**
* return absolute path of an existing file located in iTop
*
* @param string $sPath relative iTop path
*
* @return string|false absolute path
*/
public static function AbsolutePath($sPath)
{
$sRootPath = realpath(APPROOT);
$sFullPath = realpath($sRootPath.DIRECTORY_SEPARATOR.$sPath);
if (($sFullPath === false) || !self::StartsWith($sFullPath, $sRootPath))
{
return false;
}
return $sFullPath;
}
}

View File

@@ -60,6 +60,10 @@ Interface Page
*/
class WebPage implements Page
{
/**
* @since 2.7.0 N°2529
*/
const PAGES_CHARSET = 'utf-8';
protected $s_title;
protected $s_content;
protected $s_deferred_content;
@@ -80,6 +84,8 @@ class WebPage implements Page
protected $s_sOutputFormat;
protected $a_OutputOptions;
protected $bPrintable;
protected $bHasCollapsibleSection;
public function __construct($s_title, $bPrintable = false)
{
@@ -102,6 +108,7 @@ class WebPage implements Page
$this->bTrashUnexpectedOutput = false;
$this->s_OutputFormat = utils::ReadParam('output_format', 'html');
$this->a_OutputOptions = array();
$this->bHasCollapsibleSection = false;
$this->bPrintable = $bPrintable;
ob_start(); // Start capturing the output
}
@@ -887,6 +894,73 @@ class WebPage implements Page
}
}
}
/**
* Adds init scripts for the collapsible sections
*/
protected function outputCollapsibleSectionInit()
{
if (!$this->bHasCollapsibleSection)
{
return;
}
$this->add_script(<<<'EOD'
function initCollapsibleSection(iSectionId, bOpenedByDefault, sSectionStateStorageKey)
{
var bStoredSectionState = JSON.parse(localStorage.getItem(sSectionStateStorageKey));
var bIsSectionOpenedInitially = (bStoredSectionState == null) ? bOpenedByDefault : bStoredSectionState;
if (bIsSectionOpenedInitially) {
$("#LnkCollapse_"+iSectionId).toggleClass("open");
$("#Collapse_"+iSectionId).toggle();
}
$("#LnkCollapse_"+iSectionId).click(function(e) {
localStorage.setItem(sSectionStateStorageKey, !($("#Collapse_"+iSectionId).is(":visible")));
$("#LnkCollapse_"+iSectionId).toggleClass("open");
$("#Collapse_"+iSectionId).slideToggle("normal");
e.preventDefault(); // we don't want to do anything more (see #1030 : a non wanted tab switching was triggered)
});
}
EOD
);
}
public function StartCollapsibleSection($sSectionLabel, $bOpenedByDefault = false, $sSectionStateStorageBusinessKey = '')
{
$this->add($this->GetStartCollapsibleSection($sSectionLabel, $bOpenedByDefault, $sSectionStateStorageBusinessKey));
}
public function GetStartCollapsibleSection($sSectionLabel, $bOpenedByDefault = false, $sSectionStateStorageBusinessKey = '')
{
$this->bHasCollapsibleSection = true;
$sHtml = '';
static $iSectionId = 0;
$sHtml .= '<a id="LnkCollapse_'.$iSectionId.'" class="CollapsibleLabel" href="#">'.$sSectionLabel.'</a></br>'."\n";
$sHtml .= '<div id="Collapse_'.$iSectionId.'" style="display:none">'."\n";
$oConfig = MetaModel::GetConfig();
$sSectionStateStorageKey = $oConfig->GetItopInstanceid().'/'.$sSectionStateStorageBusinessKey.'/collapsible-'.$iSectionId;
$sSectionStateStorageKey = json_encode($sSectionStateStorageKey);
$sOpenedByDefault = ($bOpenedByDefault) ? 'true' : 'false';
$this->add_ready_script("initCollapsibleSection($iSectionId, $sOpenedByDefault, '$sSectionStateStorageKey');");
$iSectionId++;
return $sHtml;
}
public function EndCollapsibleSection()
{
$this->add($this->GetEndCollapsibleSection());
}
public function GetEndCollapsibleSection()
{
return "</div>";
}
}
@@ -1040,9 +1114,11 @@ class TabManager
*
* @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
* @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. false will cause
* the tab to be reloaded upon each activation.
*
* @return string
*
* @since 2.0.3
*/
public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true)

View File

@@ -42,7 +42,7 @@ class XMLPage extends WebPage
parent::__construct($s_title);
$this->m_bPassThrough = $bPassThrough;
$this->m_bHeaderSent = false;
$this->add_header("Content-type: text/xml; charset=utf-8");
$this->add_header("Content-type: text/xml; charset=".self::PAGES_CHARSET);
$this->add_header("Cache-control: no-cache");
$this->add_header("Content-location: export.xml");
}
@@ -53,8 +53,9 @@ class XMLPage extends WebPage
{
// Get the unexpected output but do nothing with it
$sTrash = $this->ob_get_clean_safe();
$this->s_content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n".trim($this->s_content);
$sCharset = self::PAGES_CHARSET;
$this->s_content = "<?xml version=\"1.0\" encoding=\"$sCharset\"?".">\n".trim($this->s_content);
$this->add_header("Content-Length: ".strlen($this->s_content));
foreach($this->a_headers as $s_header)
{
@@ -87,7 +88,8 @@ class XMLPage extends WebPage
{
header($s_header);
}
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n";
$sCharset = self::PAGES_CHARSET;
echo "<?xml version=\"1.0\" encoding=\"$sCharset\"?".">\n";
echo trim($s_captured_output);
echo trim($this->s_content);
echo $sText;

View File

@@ -3,6 +3,7 @@
define('APPROOT', dirname(__FILE__).'/');
define('APPCONF', APPROOT.'conf/');
define('ITOP_DEFAULT_ENV', 'production');
define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
if (function_exists('microtime'))
{
@@ -12,4 +13,56 @@ 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;
}

24
bootstrap.inc.php Normal file
View File

@@ -0,0 +1,24 @@
<?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
*/
require_once __DIR__.'/approot.inc.php';
require_once APPROOT.'/lib/autoload.php';
// Require here files containing PHP instructions

View File

@@ -1,21 +1,77 @@
{
"type": "project",
"license": "AGPLv3",
"require": {
"php": ">=5.6.0",
"ext-mysql": "*",
"ext-ldap": "*",
"ext-mcrypt": "*",
"ext-cli": "*",
"ext-soap": "*",
"ext-json": "*",
"ext-zip": "*",
"ext-mysqli": "*",
"ext-dom": "*",
"ext-iconv": "*",
"ext-gd": "*"
"ext-gd": "*",
"ext-ctype": "*",
"scssphp/scssphp": "1.0.0",
"swiftmailer/swiftmailer": "5.4.9",
"pelago/emogrifier": "2.1.0",
"combodo/tcpdf": "6.3.0",
"pear/archive_tar": "1.4.7",
"symfony/console": "3.4.*",
"symfony/dotenv": "3.4.*",
"symfony/framework-bundle": "3.4.*",
"symfony/twig-bundle": "3.4.*",
"symfony/yaml": "3.4.*",
"symfony/polyfill-php70": "1.*"
},
"require-dev": {
"symfony/stopwatch": "3.4.*",
"symfony/web-profiler-bundle": "3.4.*"
},
"suggest": {
"ext-libsodium": "Required to use the AttributeEncryptedString.",
"ext-openssl": "Can be used as a polyfill if libsodium is not installed",
"ext-mcrypt": "Can be used as a polyfill if either libsodium and openssl are not installed (libsodium and openssl are more secure)",
"ext-ldap": "Required to use LDAP as an identity provider",
"ext-posix": "Not required by the core, but some extensions uses it.",
"ext-imap": "Required by the extension \"Mail to ticket automation\""
},
"config": {
"platform": {
"php": "5.6.0"
},
"vendor-dir": "lib",
"preferred-install": {
"*": "dist"
},
"sort-packages": true
},
"autoload": {
"classmap": [
"core",
"application"
],
"exclude-from-classmap": [
"core/dbobjectsearch.class.php",
"core/legacy/dbobjectsearchlegacy.class.php",
"core/querybuildercontext.class.inc.php",
"core/legacy/querybuildercontextlegacy.class.inc.php",
"core/querybuilderexpressions.class.inc.php",
"core/legacy/querybuilderexpressionslegacy.class.inc.php",
"application/loginform.class.inc.php",
"application/loginbasic.class.inc.php",
"application/logindefault.class.inc.php",
"application/loginexternal.class.inc.php",
"application/loginurl.class.inc.php"
]
},
"conflict": {
"symfony/symfony": "*"
},
"extra": {
"symfony": {
"allow-contrib": false,
"require": "3.4.*"
}
}
}

2426
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

13
conf/.htaccess Normal file
View File

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

2
conf/index.php Normal file
View File

@@ -0,0 +1,2 @@
<?php
echo 'Access denied';

8
conf/web.config Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<authorization>
<deny users="*" /> <!-- Denies all users -->
</authorization>
</system.web>
</configuration>

View File

@@ -166,7 +166,14 @@ class apcFile
}
else
{
if (!@unlink($sCache))
if (is_file($sCache))
{
if (!@unlink($sCache))
{
return false;
}
}
else
{
return false;
}
@@ -209,8 +216,14 @@ class apcFile
return false;
}
@unlink(self::GetCacheFileName($sKey));
@unlink(self::GetCacheFileName('-'.$sKey));
if (is_file(self::GetCacheFileName($sKey)))
{
@unlink(self::GetCacheFileName($sKey));
}
if (is_file(self::GetCacheFileName('-'.$sKey)))
{
@unlink(self::GetCacheFileName('-'.$sKey));
}
if ($iTTL > 0)
{
// hint for ttl management
@@ -312,6 +325,10 @@ class apcFile
*/
static protected function ReadCacheLocked($sFilename)
{
if (!is_file($sFilename))
{
return false;
}
$file = @fopen($sFilename, 'r');
if ($file === false)
{

View File

@@ -1,335 +0,0 @@
<?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/>
/**
* Utility to import/export the DB from/to a ZIP file
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* iTopArchive a class to manipulate (read/write) iTop archives with their catalog
* Each iTop archive is a zip file that contains (at the root of the archive)
* a file called catalog.xml holding the description of the archive
*/
class iTopArchive
{
const read = 0;
const create = ZipArchive::CREATE;
protected $m_sZipPath;
protected $m_oZip;
protected $m_sVersion;
protected $m_sTitle;
protected $m_sDescription;
protected $m_aPackages;
protected $m_aErrorMessages;
/**
* Construct an iTopArchive object
* @param $sArchivePath string The full path the archive file
* @param $iMode integrer Either iTopArchive::read for reading an existing archive or iTopArchive::create for creating a new one. Updating is not supported (yet)
*/
public function __construct($sArchivePath, $iMode = iTopArchive::read)
{
$this->m_sZipPath = $sArchivePath;
$this->m_oZip = new ZipArchive();
$this->m_oZip->open($this->m_sZipPath, $iMode);
$this->m_aErrorMessages = array();
$this->m_sVersion = '1.0';
$this->m_sTitle = '';
$this->m_sDescription = '';
$this->m_aPackages = array();
}
public function SetTitle($sTitle)
{
$this->m_sTitle = $sTitle;
}
public function SetDescription($sDescription)
{
$this->m_sDescription = $sDescription;
}
public function GetTitle()
{
return $this->m_sTitle;
}
public function GetDescription()
{
return $this->m_sDescription;
}
public function GetPackages()
{
return $this->m_aPackages;
}
public function __destruct()
{
$this->m_oZip->close();
}
/**
* Get the error message explaining the latest error encountered
* @return array All the error messages encountered during the validation
*/
public function GetErrors()
{
return $this->m_aErrorMessages;
}
/**
* Read the catalog from the archive (zip) file
* @param sPath string Path the the zip file
* @return boolean True in case of success, false otherwise
*/
public function ReadCatalog()
{
if ($this->IsValid())
{
$sXmlCatalog = $this->m_oZip->getFromName('catalog.xml');
$oParser = xml_parser_create();
xml_parse_into_struct($oParser, $sXmlCatalog, $aValues, $aIndexes);
xml_parser_free($oParser);
$iIndex = $aIndexes['ARCHIVE'][0];
$this->m_sVersion = $aValues[$iIndex]['attributes']['VERSION'];
$iIndex = $aIndexes['TITLE'][0];
$this->m_sTitle = $aValues[$iIndex]['value'];
$iIndex = $aIndexes['DESCRIPTION'][0];
if (array_key_exists('value', $aValues[$iIndex]))
{
// #@# implement a get_array_value(array, key, default) ?
$this->m_sDescription = $aValues[$iIndex]['value'];
}
foreach($aIndexes['PACKAGE'] as $iIndex)
{
$this->m_aPackages[$aValues[$iIndex]['attributes']['HREF']] = array( 'type' => $aValues[$iIndex]['attributes']['TYPE'], 'title'=> $aValues[$iIndex]['attributes']['TITLE'], 'description' => $aValues[$iIndex]['value']);
}
//echo "Archive path: {$this->m_sZipPath}<br/>\n";
//echo "Archive format version: {$this->m_sVersion}<br/>\n";
//echo "Title: {$this->m_sTitle}<br/>\n";
//echo "Description: {$this->m_sDescription}<br/>\n";
//foreach($this->m_aPackages as $aFile)
//{
// echo "{$aFile['title']} ({$aFile['type']}): {$aFile['description']}<br/>\n";
//}
}
return true;
}
public function WriteCatalog()
{
$sXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?".">\n"; // split the XML closing tag that disturbs PSPad's syntax coloring
$sXml .= "<archive version=\"1.0\">\n";
$sXml .= "<title>{$this->m_sTitle}</title>\n";
$sXml .= "<description>{$this->m_sDescription}</description>\n";
foreach($this->m_aPackages as $sFileName => $aFile)
{
$sXml .= "<package title=\"{$aFile['title']}\" type=\"{$aFile['type']}\" href=\"$sFileName\">{$aFile['description']}</package>\n";
}
$sXml .= "</archive>";
$this->m_oZip->addFromString('catalog.xml', $sXml);
}
/**
* Add a package to the archive
* @param string $sExternalFilePath The path to the file to be added to the archive as a package (directories are not yet implemented)
* @param string $sFilePath The name of the file inside the archive
* @param string $sTitle A short title for this package
* @param string $sType Type of the package. SQL scripts must be of type 'text/sql'
* @param string $sDescription A longer description of the purpose of this package
* @return none
*/
public function AddPackage($sExternalFilePath, $sFilePath, $sTitle, $sType, $sDescription)
{
$this->m_aPackages[$sFilePath] = array('title' => $sTitle, 'type' => $sType, 'description' => $sDescription);
$this->m_oZip->addFile($sExternalFilePath, $sFilePath);
}
/**
* Reads the contents of the given file from the archive
* @param string $sFileName The path to the file inside the archive
* @return string The content of the file read from the archive
*/
public function GetFileContents($sFileName)
{
return $this->m_oZip->getFromName($sFileName);
}
/**
* Extracts the contents of the given file from the archive
* @param string $sFileName The path to the file inside the archive
* @param string $sDestinationFileName The path of the file to write
* @return none
*/
public function ExtractToFile($sFileName, $sDestinationFileName)
{
$iBufferSize = 64 * 1024; // Read 64K at a time
$oZipStream = $this->m_oZip->getStream($sFileName);
$oDestinationStream = fopen($sDestinationFileName, 'wb');
while (!feof($oZipStream)) {
$sContents = fread($oZipStream, $iBufferSize);
fwrite($oDestinationStream, $sContents);
}
fclose($oZipStream);
fclose($oDestinationStream);
}
/**
* Apply a SQL script taken from the archive. The package must be listed in the catalog and of type text/sql
* @param string $sFileName The path to the SQL package inside the archive
* @return boolean false in case of error, true otherwise
*/
public function ImportSql($sFileName, $sDatabase = 'itop')
{
if ( ($this->m_oZip->locateName($sFileName) == false) || (!isset($this->m_aPackages[$sFileName])) || ($this->m_aPackages[$sFileName]['type'] != 'text/sql'))
{
// invalid type or not listed in the catalog
return false;
}
$sTempName = tempnam("../tmp/", "sql");
//echo "Extracting to: '$sTempName'<br/>\n";
$this->ExtractToFile($sFileName, $sTempName);
// Note: the command line below works on Windows with the right path to mysql !!!
$sCommandLine = 'type "'.$sTempName.'" | "/iTop/MySQL Server 5.0/bin/mysql.exe" -u root '.$sDatabase;
//echo "Executing: '$sCommandLine'<br/>\n";
exec($sCommandLine, $aOutput, $iRet);
//echo "Return code: $iRet<br/>\n";
//echo "Output:<br/><pre>\n";
//print_r($aOutput);
//echo "</pre><br/>\n";
unlink($sTempName);
return ($iRet == 0);
}
/**
* Dumps some part of the specified MySQL database into the archive as a text/sql package
* @param $sTitle string A short title for this SQL script
* @param $sDescription string A longer description of the purpose of this SQL script
* @param $sFileName string The name of the package inside the archive
* @param $sDatabase string name of the database
* @param $aTables array array or table names. If empty, all tables are dumped
* @param $bStructureOnly boolean Whether or not to dump the data or just the schema
* @return boolean False in case of error, true otherwise
*/
public function AddDatabaseDump($sTitle, $sDescription, $sFileName, $sDatabase = 'itop', $aTables = array(), $bStructureOnly = true)
{
$sTempName = tempnam("../tmp/", "sql");
$sNoData = $bStructureOnly ? "--no-data" : "";
$sCommandLine = "\"/iTop/MySQL Server 5.0/bin/mysqldump.exe\" --user=root --opt $sNoData --result-file=$sTempName $sDatabase ".implode(" ", $aTables);
//echo "Executing command: '$sCommandLine'<br/>\n";
exec($sCommandLine, $aOutput, $iRet);
//echo "Return code: $iRet<br/>\n";
//echo "Output:<br/><pre>\n";
//print_r($aOutput);
//echo "</pre><br/>\n";
if ($iRet == 0)
{
$this->AddPackage($sTempName, $sFileName, $sTitle, 'text/sql', $sDescription);
}
//unlink($sTempName);
return ($iRet == 0);
}
/**
* Check the consistency of the archive
* @return boolean True if the archive file is consistent
*/
public function IsValid()
{
// TO DO: use a DTD to validate the XML instead of this hand-made validation
$bResult = true;
$aMandatoryTags = array('ARCHIVE' => array('VERSION'),
'TITLE' => array(),
'DESCRIPTION' => array(),
'PACKAGE' => array('TYPE', 'HREF', 'TITLE'));
$sXmlCatalog = $this->m_oZip->getFromName('catalog.xml');
$oParser = xml_parser_create();
xml_parse_into_struct($oParser, $sXmlCatalog, $aValues, $aIndexes);
xml_parser_free($oParser);
foreach($aMandatoryTags as $sTag => $aAttributes)
{
// Check that all the required tags are present
if (!isset($aIndexes[$sTag]))
{
$this->m_aErrorMessages[] = "The XML catalog does not contain the mandatory tag $sTag.";
$bResult = false;
}
else
{
foreach($aIndexes[$sTag] as $iIndex)
{
switch($aValues[$iIndex]['type'])
{
case 'complete':
case 'open':
// Check that all the required attributes are present
foreach($aAttributes as $sAttribute)
{
if (!isset($aValues[$iIndex]['attributes'][$sAttribute]))
{
$this->m_aErrorMessages[] = "The tag $sTag ($iIndex) does not contain the required attribute $sAttribute.";
}
}
break;
default:
// ignore other type of tags: close or cdata
}
}
}
}
return $bResult;
}
}
/*
// Unit test - reading an archive
$sArchivePath = '../tmp/archive.zip';
$oArchive = new iTopArchive($sArchivePath, iTopArchive::read);
$oArchive->ReadCatalog();
$oArchive->ImportSql('full_backup.sql');
// Writing an archive --
$sArchivePath = '../tmp/archive2.zip';
$oArchive = new iTopArchive($sArchivePath, iTopArchive::create);
$oArchive->SetTitle('First Archive !');
$oArchive->SetDescription('This is just a test. Does not contain a lot of useful data.');
$oArchive->AddPackage('../tmp/schema.sql', 'test.sql', 'this is just a test', 'text/sql', 'My first attempt at creating an archive from PHP...');
$oArchive->WriteCatalog();
$sArchivePath = '../tmp/archive2.zip';
$oArchive = new iTopArchive($sArchivePath, iTopArchive::create);
$oArchive->SetTitle('First Archive !');
$oArchive->SetDescription('This is just a test. Does not contain a lot of useful data.');
$oArchive->AddDatabaseDump('Test', 'This is my first automatic dump', 'schema.sql', 'itop', array('objects'));
$oArchive->WriteCatalog();
*/
?>

View File

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

View File

@@ -73,7 +73,7 @@ define('EXTKEY_ABSOLUTE', 2);
define('DEL_MANUAL', 1);
/**
* Propagation of the deletion through an external key - ask the user to delete the referencing object
* Propagation of the deletion through an external key - remove linked objects if ext key has is_null_allowed=false
*
* @package iTopORM
*/
@@ -512,7 +512,7 @@ abstract class AttributeDefinition
}
/**
* @param string|null $sDefault
* @param string|null $sDefault if null, will return the attribute code replacing "_" by " "
*
* @return string
*
@@ -1299,7 +1299,7 @@ class AttributeDashboard extends AttributeDefinition
{
return '';
}
/**
* @inheritdoc
*/
@@ -1374,6 +1374,11 @@ class AttributeLinkedSet extends AttributeDefinition
*/
public function GetDefaultValue(DBObject $oHostObject = null)
{
if ($oHostObject === null)
{
return null;
}
$sLinkClass = $this->GetLinkedClass();
$sExtKeyToMe = $this->GetExtKeyToMe();
@@ -3264,10 +3269,13 @@ class AttributeClassState extends AttributeString
$sClass = $oHostObj->Get($sTargetClass);
$aAllowedStates = array();
$aValues = MetaModel::EnumStates($sClass);
foreach(array_keys($aValues) as $sState)
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sChildClass)
{
$aAllowedStates[$sState] = $sState.' ('.MetaModel::GetStateLabel($sClass, $sState).')';
$aValues = MetaModel::EnumStates($sChildClass);
foreach (array_keys($aValues) as $sState)
{
$aAllowedStates[$sState] = $sState.' ('.MetaModel::GetStateLabel($sChildClass, $sState).')';
}
}
return $aAllowedStates;
}
@@ -3286,9 +3294,15 @@ class AttributeClassState extends AttributeString
{
$sTargetClass = $this->Get('class_field');
$sClass = $oHostObject->Get($sTargetClass);
$sHTML = '<span class="attribute-set-item" data-code="'.$sValue.'" data-label="'.$sValue.' ('.MetaModel::GetStateLabel($sClass, $sValue).')'.'" data-description="">'.$sValue.'</span>';
return $sHTML;
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sChildClass)
{
$aValues = MetaModel::EnumStates($sChildClass);
if (in_array($sValue, $aValues))
{
$sHTML = '<span class="attribute-set-item" data-code="'.$sValue.'" data-label="'.$sValue.' ('.MetaModel::GetStateLabel($sChildClass, $sValue).')'.'" data-description="">'.$sValue.'</span>';
return $sHTML;
}
}
}
return $sValue;
@@ -3541,6 +3555,22 @@ class AttributeFinalClass extends AttributeString
return $aLocalizedValues;
}
/**
* @return bool
* @throws \CoreException
* @since 2.7
*/
public function CopyOnAllTables()
{
$sClass = self::GetHostClass();
if (MetaModel::IsLeafClass($sClass))
{
// Leaf class, no finalclass
return false;
}
return true;
}
}
@@ -6242,6 +6272,15 @@ class AttributeExternalKey extends AttributeDBFieldVoid
return $oFormField;
}
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if (!is_null($oHostObject))
{
return $oHostObject->GetAsHTML($this->GetCode(), $oHostObject);
}
return DBObject::MakeHyperLink($this->GetTargetClass(), $sValue);
}
}
/**
@@ -6502,26 +6541,60 @@ class AttributeExternalField extends AttributeDefinition
}
}
/**
* @param string $sDefault
*
* @return string dict entry if defined, otherwise :
* <ul>
* <li>if field is a friendlyname then display the label of the ExternalKey
* <li>the class hierarchy -> field name
*
* <p>For example, having this :
*
* <pre>
* +---------------------+ +--------------------+ +--------------+
* | Class A | | Class B | | Class C |
* +---------------------+ +--------------------+ +--------------+
* | foo <ExternalField>-------->c_id_friendly_name--------->friendlyname |
* +---------------------+ +--------------------+ +--------------+
* </pre>
*
* <p>The ExternalField foo points to a magical field that is brought by c_id ExternalKey in class B.
*
* <p>In the normal case the foo label would be : B -> C -> friendlyname<br>
* But as foo is a friendlyname its label will be the same as the one on A.b_id field
* This can be overrided with dict key Class:ClassA/Attribute:foo
*
* @throws \CoreException
* @throws \Exception
*/
public function GetLabel($sDefault = null)
{
$sLabelDefaultValue = '';
$sLabel = parent::GetLabel($sLabelDefaultValue);
if ($sLabelDefaultValue !== $sLabel)
{
return $sLabel;
}
if ($this->IsFriendlyName())
{
// This will be used even if we are pointing to a friendlyname in a distance > 1
// For example we can link to a magic friendlyname (like org_id_friendlyname)
// If a specific label is needed, use a Dict key !
// See N°2174
$sKeyAttCode = $this->Get("extkey_attcode");
$oExtKeyAttDef = MetaModel::GetAttributeDef($this->GetHostClass(), $sKeyAttCode);
$sLabel = $oExtKeyAttDef->GetLabel($this->m_sCode);
return $sLabel;
}
else
{
$sLabel = parent::GetLabel('');
if (strlen($sLabel) == 0)
{
$oRemoteAtt = $this->GetExtAttDef();
$sLabel = $oRemoteAtt->GetLabel($this->m_sCode);
$oKeyAtt = $this->GetKeyAttDef();
$sKeyLabel = $oKeyAtt->GetLabel($this->GetKeyAttCode());
$sLabel = "{$sKeyLabel}->{$sLabel}";
}
}
$oRemoteAtt = $this->GetExtAttDef();
$sLabel = $oRemoteAtt->GetLabel($this->m_sCode);
$oKeyAtt = $this->GetKeyAttDef();
$sKeyLabel = $oKeyAtt->GetLabel($this->GetKeyAttCode());
$sLabel = "{$sKeyLabel}->{$sLabel}";
return $sLabel;
}
@@ -6840,7 +6913,16 @@ class AttributeExternalField extends AttributeDefinition
{
$sFormFieldClass = $oRemoteAttDef::GetFormFieldClass();
}
/** @var \Combodo\iTop\Form\Field\Field $oFormField */
$oFormField = new $sFormFieldClass($this->GetCode());
switch ($sFormFieldClass)
{
case '\Combodo\iTop\Form\Field\SelectField':
$oFormField->SetChoices($oRemoteAttDef->GetAllowedValues($oObject->ToArgsForQuery()));
break;
default:
break;
}
}
parent::MakeFormField($oObject, $oFormField);
@@ -7334,7 +7416,17 @@ class AttributeImage extends AttributeBlob
return '<div class="'.$sCssClasses.'" style="width: '.$iMaxWidthPx.'; height: '.$iMaxHeightPx.';"><span class="helper-middle"></span>'.$sRet.'</div>';
}
private function GetHtmlForImageUrl($sUrl, $iMaxWidthPx, $iMaxHeightPx) {
/**
* @param string $sUrl
* @param int $iMaxWidthPx
* @param int $iMaxHeightPx
*
* @return string
*
* @since 2.6.0 new private method
* @since 2.7.0 change visibility to protected
*/
protected function GetHtmlForImageUrl($sUrl, $iMaxWidthPx, $iMaxHeightPx) {
return '<img src="'.$sUrl.'" style="max-width: '.$iMaxWidthPx.'; max-height: '.$iMaxHeightPx.'">';
}
@@ -7343,8 +7435,11 @@ class AttributeImage extends AttributeBlob
* @param \DBObject $oHostObject
*
* @return null|string
*
* @since 2.6.0 new private method
* @since 2.7.0 change visibility to protected
*/
private function GetAttributeImageFileUrl($value, $oHostObject) {
protected function GetAttributeImageFileUrl($value, $oHostObject) {
if (!is_object($value)) {
return null;
}
@@ -7813,6 +7908,87 @@ class AttributeStopWatch extends AttributeDefinition
throw new CoreException("Unknown item code '$sItemCode' for attribute ".$this->GetHostClass().'::'.$this->GetCode());
}
public function GetSubItemSearchType($sItemCode)
{
switch ($sItemCode)
{
case 'timespent':
return static::SEARCH_WIDGET_TYPE_NUMERIC; //seconds
case 'started':
case 'laststart':
case 'stopped':
return static::SEARCH_WIDGET_TYPE_DATE_TIME; //timestamp
}
foreach($this->ListThresholds() as $iThreshold => $aFoo)
{
$sThPrefix = $iThreshold.'_';
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix)
{
// The current threshold is concerned
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
switch ($sThresholdCode)
{
case 'deadline':
return static::SEARCH_WIDGET_TYPE_DATE_TIME; //timestamp
case 'passed':
case 'triggered':
return static::SEARCH_WIDGET_TYPE_ENUM; //booleans, used in conjuction with GetSubItemAllowedValues and IsSubItemNullAllowed
case 'overrun':
return static::SEARCH_WIDGET_TYPE_NUMERIC; //seconds
}
}
}
return static::SEARCH_WIDGET_TYPE_RAW;
}
public function GetSubItemAllowedValues($sItemCode, $aArgs = array(), $sContains = '')
{
foreach($this->ListThresholds() as $iThreshold => $aFoo)
{
$sThPrefix = $iThreshold.'_';
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix)
{
// The current threshold is concerned
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
switch ($sThresholdCode)
{
case 'passed':
case 'triggered':
return array(
0 => $this->GetBooleanLabel(0),
1 => $this->GetBooleanLabel(1),
);
}
}
}
return null;
}
public function IsSubItemNullAllowed($sItemCode, $bDefaultValue)
{
foreach($this->ListThresholds() as $iThreshold => $aFoo)
{
$sThPrefix = $iThreshold.'_';
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix)
{
// The current threshold is concerned
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
switch ($sThresholdCode)
{
case 'passed':
case 'triggered':
return false;
}
}
}
return $bDefaultValue;
}
protected function GetBooleanLabel($bValue)
{
$sDictKey = $bValue ? 'yes' : 'no';
@@ -8162,9 +8338,39 @@ class AttributeStopWatch extends AttributeDefinition
*/
class AttributeSubItem extends AttributeDefinition
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
/**
* Return the search widget type corresponding to this attribute
* the computation is made by AttributeStopWatch::GetSubItemSearchType
*
* @return string
*/
public function GetSearchType()
{
/** @var AttributeStopWatch $oParent */
$oParent = $this->GetTargetAttDef();
static public function ListExpectedParams()
return $oParent->GetSubItemSearchType($this->Get('item_code'));
}
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
/** @var AttributeStopWatch $oParent */
$oParent = $this->GetTargetAttDef();
return $oParent->GetSubItemAllowedValues($this->Get('item_code'), $aArgs, $sContains);
}
public function IsNullAllowed()
{
/** @var AttributeStopWatch $oParent */
$oParent = $this->GetTargetAttDef();
$bDefaultValue = parent::IsNullAllowed();
return $oParent->IsSubItemNullAllowed($this->Get('item_code'), $bDefaultValue);
}
static public function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array('target_attcode', 'item_code'));
}
@@ -9655,14 +9861,27 @@ class AttributeTagSet extends AttributeSet
{
$aJson['partial_values'] = array();
$aJson['orig_value'] = array();
$aJson['added'] = array();
$aJson['removed'] = array();
}
else
{
$aJson['partial_values'] = $oValue->GetModified();
$aJson['orig_value'] = array_merge($oValue->GetValues(), $oValue->GetModified());
$aJson['added'] = $oValue->GetAdded();
$aJson['removed'] = $oValue->GetRemoved();
if ($oValue->DisplayPartial())
{
// For bulk updates
$aJson['partial_values'] = $oValue->GetModified();
}
else
{
// For simple updates
$aJson['partial_values'] = array();
}
}
$aJson['added'] = array();
$aJson['removed'] = array();
$iMaxTags = $this->GetMaxItems();
$aJson['max_items_allowed'] = $iMaxTags;

View File

@@ -19,7 +19,7 @@
/**
* Class BackgroundTask
* A class to record information about the execution of background processes
* A class to record information about the execution of background processes ({@link iProcess} impl)
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
@@ -55,6 +55,7 @@ class BackgroundTask extends DBObject
MetaModel::Init_AddAttribute(new AttributeBoolean("running", array("allowed_values"=>null, "sql"=>"running", "default_value"=>false, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('active,paused'), "sql"=>"status", "default_value"=>'active', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("system_user", array("allowed_values"=>null, "sql"=>"system_user", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
}
public function ComputeDurations($fLatestDuration)
@@ -73,4 +74,4 @@ class BackgroundTask extends DBObject
}
$this->Set('latest_run_duration', sprintf('%.3f',$fLatestDuration));
}
}
}

View File

@@ -59,7 +59,6 @@ require_once('sqlobjectquery.class.inc.php');
require_once('sqlunionquery.class.inc.php');
require_once('dbobject.class.php');
require_once('dbsearch.class.php');
require_once('dbobjectset.class.php');
require_once('backgroundprocess.inc.php');
@@ -696,6 +695,8 @@ abstract class CMDBObject extends DBObject
* TODO: investigate how to get rid of this class that was made to workaround some language limitation... or a poor design!
*
* @package iTopORM
*
* @internal
*/
class CMDBObjectSet extends DBObjectSet
{

View File

@@ -99,6 +99,14 @@ class MySQLHasGoneAwayException extends MySQLException
}
}
/**
* @since 2.7.0 N°679
*/
class MySQLNoTransactionException extends MySQLException
{
}
/**
* CMDBSource
@@ -130,6 +138,12 @@ class CMDBSource
/** @var mysqli $m_oMysqli */
protected static $m_oMysqli;
/**
* @var int number of level for nested transactions : 0 if no transaction was ever opened, +1 for each 'START TRANSACTION' sent
* @since 2.7.0 N°679
*/
protected static $m_iTransactionLevel = 0;
/**
* SQL charset & collation declaration for text columns
*
@@ -137,7 +151,7 @@ class CMDBSource
* use expression as value)
*
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-column.html
* @since 2.5 N°1001 switch to utf8mb4
* @since 2.5.1 N°1001 switch to utf8mb4
*/
public static function GetSqlStringColumnDefinition()
{
@@ -289,11 +303,11 @@ class CMDBSource
$iConnectInfoCount = count($aConnectInfo);
if ($bUsePersistentConnection && ($iConnectInfoCount == 3))
{
$iPort = $aConnectInfo[2];
$iPort = (int)($aConnectInfo[2]);
}
else if (!$bUsePersistentConnection && ($iConnectInfoCount == 2))
{
$iPort = $aConnectInfo[1];
$iPort = (int)($aConnectInfo[1]);
}
else
{
@@ -410,6 +424,14 @@ class CMDBSource
return $aVersions[0];
}
/**
* @return string
*/
public static function GetServerInfo()
{
return mysqli_get_server_info ( self::$m_oMysqli );
}
/**
* Get the DB vendor between MySQL and its main forks
* @return string
@@ -570,25 +592,68 @@ class CMDBSource
/**
* @param string $sSQLQuery
*
* @return \mysqli_result
* @return \mysqli_result|null
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \CoreException
*
* @since 2.7.0 N°679 handles nested transactions
*/
public static function Query($sSQLQuery)
{
if (preg_match('/^START TRANSACTION;?$/i', $sSQLQuery))
{
self::StartTransaction();
return null;
}
if (preg_match('/^COMMIT;?$/i', $sSQLQuery))
{
self::Commit();
return null;
}
if (preg_match('/^ROLLBACK;?$/i', $sSQLQuery))
{
self::Rollback();
return null;
}
return self::DBQuery($sSQLQuery);
}
/**
* Send the query directly to the DB. **Be extra cautious with this !**
*
* Use {@link Query} if you're not sure.
*
* @internal
*
* @param string $sSql
*
* @return bool|\mysqli_result
* @throws \MySQLHasGoneAwayException
* @throws \MySQLException
*
* @since 2.7.0 N°679
*/
private static function DBQuery($sSql)
{
$oKPI = new ExecutionKPI();
try
{
$oResult = self::$m_oMysqli->query($sSQLQuery);
$oResult = self::$m_oMysqli->query($sSql);
}
catch(mysqli_sql_exception $e)
catch (mysqli_sql_exception $e)
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSQLQuery, $e));
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
}
$oKPI->ComputeStats('Query exec (mySQL)', $sSQLQuery);
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
if ($oResult === false)
{
$aContext = array('query' => $sSQLQuery);
$aContext = array('query' => $sSql);
$iMySqlErrorNo = self::$m_oMysqli->errno;
$aMySqlHasGoneAwayErrorCodes = MySQLHasGoneAwayException::getErrorCodes();
@@ -599,10 +664,134 @@ class CMDBSource
throw new MySQLException('Failed to issue SQL query', $aContext);
}
return $oResult;
}
/**
* If nested transaction, we are not starting a new one : only one global transaction will exist.
*
* Indeed [the official documentation](https://dev.mysql.com/doc/refman/5.6/en/commit.html) states :
*
* > Beginning a transaction causes any pending transaction to be committed
*
* @internal
* @see m_iTransactionLevel
* @since 2.7.0 N°679
*/
private static function StartTransaction()
{
$bHasExistingTransactions = self::IsInsideTransaction();
if (!$bHasExistingTransactions)
{
self::DBQuery('START TRANSACTION');
}
self::AddTransactionLevel();
}
/**
* Sends the COMMIT to the db only if we are at the root transaction level
*
* @internal
* @see m_iTransactionLevel
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \MySQLNoTransactionException if called with no opened transaction
* @since 2.7.0 N°679
*/
private static function Commit()
{
if (!self::IsInsideTransaction())
{
// should not happen !
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
}
self::RemoveLastTransactionLevel();
if (self::IsInsideTransaction())
{
return;
}
self::DBQuery('COMMIT');
}
/**
* Sends the ROLLBACK to the db only if we are at the root transaction level
*
* The parameter allows to send a ROLLBACK whatever the current transaction level is
*
* @internal
* @see m_iTransactionLevel
*
* @throws \MySQLNoTransactionException if called with no opened transaction
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @since 2.7.0 N°679
*/
private static function Rollback()
{
if (!self::IsInsideTransaction())
{
// should not happen !
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
}
self::RemoveLastTransactionLevel();
if (self::IsInsideTransaction())
{
return;
}
self::DBQuery('ROLLBACK');
}
/**
* @api
* @see m_iTransactionLevel
* @return bool true if there is one transaction opened, false otherwise (not a single 'START TRANSACTION' sent)
* @since 2.7.0 N°679
*/
public static function IsInsideTransaction()
{
return (self::$m_iTransactionLevel > 0);
}
/**
* @internal
* @see m_iTransactionLevel
* @since 2.7.0 N°679
*/
private static function AddTransactionLevel()
{
++self::$m_iTransactionLevel;
}
/**
* @internal
* @see m_iTransactionLevel
* @since 2.7.0 N°679
*/
private static function RemoveLastTransactionLevel()
{
if (self::$m_iTransactionLevel === 0)
{
return;
}
--self::$m_iTransactionLevel;
}
/**
* @internal
* @see m_iTransactionLevel
* @since 2.7.0 N°679
*/
private static function RemoveAllTransactionLevels()
{
self::$m_iTransactionLevel = 0;
}
/**
* @param string $sTable
*
@@ -638,6 +827,13 @@ class CMDBSource
return false;
}
/**
* @param $sSQLQuery
*
* @throws \CoreException
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public static function DeleteFrom($sSQLQuery)
{
self::Query($sSQLQuery);
@@ -1106,14 +1302,14 @@ class CMDBSource
public static function DBCheckTableCharsetAndCollation($sTableName)
{
$sDBName = self::DBName();
$sTableInfoQuery = "SELECT C.character_set_name, T.table_collation
$sTableInfoQuery = "SELECT C.CHARACTER_SET_NAME, T.TABLE_COLLATION
FROM information_schema.`TABLES` T inner join information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` C
ON T.table_collation = C.collation_name
WHERE T.table_schema = '$sDBName'
AND T.table_name = '$sTableName';";
$aTableInfo = self::QueryToArray($sTableInfoQuery);
$sTableCharset = $aTableInfo[0]['character_set_name'];
$sTableCollation = $aTableInfo[0]['table_collation'];
$sTableCharset = $aTableInfo[0]['CHARACTER_SET_NAME'];
$sTableCollation = $aTableInfo[0]['TABLE_COLLATION'];
if ((DEFAULT_CHARACTER_SET == $sTableCharset) && (DEFAULT_COLLATION == $sTableCollation))
{
@@ -1257,7 +1453,7 @@ class CMDBSource
{
$sDBName = CMDBSource::DBName();
$sDBInfoQuery = "SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '$sDBName';";
FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = '$sDBName';";
$aDBInfo = CMDBSource::QueryToArray($sDBInfoQuery);
$sDBCharset = $aDBInfo[0]['DEFAULT_CHARACTER_SET_NAME'];
$sDBCollation = $aDBInfo[0]['DEFAULT_COLLATION_NAME'];

View File

@@ -1,25 +1,28 @@
<?php
// Copyright (C) 2010-2018 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Copyright (C) 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('ITOP_APPLICATION', 'iTop');
define('ITOP_APPLICATION_SHORT', 'iTop');
define('ITOP_VERSION', '2.6.0');
define('ITOP_VERSION', '2.7.0-dev');
define('ITOP_REVISION', 'svn');
define('ITOP_BUILD_DATE', '$WCNOW$');
@@ -63,7 +66,7 @@ define('DEFAULT_MAX_DISPLAY_LIMIT', 15);
define('DEFAULT_STANDARD_RELOAD_INTERVAL', 5 * 60);
define('DEFAULT_FAST_RELOAD_INTERVAL', 1 * 60);
define('DEFAULT_SECURE_CONNECTION_REQUIRED', false);
define('DEFAULT_ALLOWED_LOGIN_TYPES', 'form|basic|external');
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
@@ -92,7 +95,8 @@ class Config
* New way to store the settings !
*
* @var array
* @since 2.5 db* variables
* @since 2.5.0 db* variables
* @since 2.7.0 export_pdf_font param
*/
protected $m_aSettings = array(
'app_env_label' => array(
@@ -170,19 +174,27 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'db_character_set' => array( // @deprecated to remove in 2.7 ? N°1001 utf8mb4 switch
'type' => 'string',
'description' => 'Deprecated since iTop 2.5 : now using utf8mb4',
'default' => 'DEPRECATED_2.5',
'value' => '',
'db_core_transactions_enabled' => array(
'type' => 'bool',
'description' => 'If true, CRUD transactions in iTop core will be enabled',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'db_collation' => array( // @deprecated to remove in 2.7 ? N°1001 utf8mb4 switch
'type' => 'string',
'description' => 'Deprecated since iTop 2.5 : now using utf8mb4_unicode_ci',
'default' => 'DEPRECATED_2.5',
'value' => '',
'db_core_transactions_retry_count' => array(
'type' => 'integer',
'description' => 'Number of times the current transaction is tried',
'default' => 3,
'value' => 3,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'db_core_transactions_retry_delay_ms' => array(
'type' => 'integer',
'description' => 'Base delay in milliseconds between transaction tries',
'default' => 500,
'value' => 500,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
@@ -331,6 +343,16 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'export_pdf_font' => array( // @since 2.7 PR #49
'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)
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'access_mode' => array(
'type' => 'integer',
'description' => 'Access mode: ACCESS_READONLY = 0, ACCESS_ADMIN_WRITE = 2, ACCESS_FULL = 3',
@@ -363,6 +385,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'log_filename_builder_impl' => array(
'type' => 'string',
'description' => 'Name of the ILogFileNameBuilder to use',
'default' => 'WeeklyRotatingLogFileNameBuilder',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'log_rest_service' => array(
'type' => 'bool',
'description' => 'Log the usage of the REST/JSON service',
@@ -682,6 +712,15 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'login_debug' => array(
'type' => 'bool',
'description' => 'Activate the login FSM debug',
// examples... not used (nor 'description')
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'forgot_password' => array(
'type' => 'bool',
'description' => 'Enable the "Forgot password" feature',
@@ -802,8 +841,8 @@ class Config
),
'email_decoration_class' => array(
'type' => 'string',
'description' => 'CSS class(es) to use as decoration for the HTML rendering of the attribute. eg. "fa fa-envelope" will put a mail icon.',
'default' => 'fa fa-envelope',
'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,
@@ -826,21 +865,29 @@ class Config
),
'phone_number_decoration_class' => array(
'type' => 'string',
'description' => 'CSS class(es) to use as decoration for the HTML rendering of the attribute. eg. "fa fa-phone" will put a phone icon.',
'default' => 'fa fa-phone',
'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)',
'description' => 'Level of logging for troubleshooting performance issues (1 to enable, 2 +blame callers) new: add "log_kpi_slow_queries" to limit the stats',
// examples... not used
'default' => 0,
'value' => 0,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'log_kpi_slow_queries' => array(
'type' => 'float',
'description' => 'Log only KPI duration stats lasting more than this value in seconds (0 for all)',
'default' => 1,
'value' => 1,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'log_kpi_memory' => array(
'type' => 'integer',
'description' => 'Level of logging for troubleshooting memory limit issues',
@@ -1105,14 +1152,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'disable_attachments_download_legacy_portal' => array(
'type' => 'bool',
'description' => 'Disable attachments download from legacy portal',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'secure_rest_services' => array(
'type' => 'bool',
'description' => 'When set to true, only the users with the profile "REST Services User" are allowed to use the REST web services.',
@@ -1161,6 +1200,38 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'use_legacy_dbsearch' => array(
'type' => 'bool',
'description' => 'If set, DBSearch will use legacy SQL query generation',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'query_cache_enabled' => array(
'type' => 'bool',
'description' => 'If set, DBSearch will use cache for query generation',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'expression_cache_enabled' => array(
'type' => 'bool',
'description' => 'If set, DBSearch will use cache for query expression generation',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'log_kpi_record_oql' => array(
'type' => 'integer',
'description' => '1 => Record OQL requests and parameters',
'default' => 0,
'value' => 0,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
);
public function IsProperty($sPropCode)
@@ -1172,6 +1243,7 @@ class Config
* @return string identifier that can be used for example to name WebStorage/SessionStorage keys (they
* are related to a whole domain, and a domain can host multiple itop)
* Beware: do not expose server side information to the client !
* @throws \Exception
*/
public function GetItopInstanceid()
{
@@ -1217,7 +1289,6 @@ class Config
}
$this->m_aSettings[$sPropCode]['value'] = $value;
$this->m_aSettings[$sPropCode]['source_of_value'] = $sSourceDesc;
}
/**
@@ -1420,6 +1491,12 @@ class Config
$sNoise = trim(ob_get_contents());
ob_end_clean();
}
catch(Error $e)
{
// PHP 7
throw new ConfigException('Error in configuration file',
array('file' => $sConfigFile, 'error' => $e->getMessage().' at line '.$e->getLine()));
}
catch (Exception $e)
{
// well, never reach in case of parsing error :-(
@@ -1427,12 +1504,6 @@ class Config
throw new ConfigException('Error in configuration file',
array('file' => $sConfigFile, 'error' => $e->getMessage()));
}
catch(Error $e)
{
// PHP 7
throw new ConfigException('Error in configuration file',
array('file' => $sConfigFile, 'error' => $e->getMessage().' at line '.$e->getLine()));
}
if (strlen($sNoise) > 0)
{
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
@@ -1503,6 +1574,14 @@ class Config
// (we have their final path at that point)
}
/**
* @param string $sModule
* @param string $sProperty
* @param mixed $defaultvalue
*
* @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)
{
if (isset($this->m_aModuleSettings[$sModule][$sProperty]))
@@ -1517,9 +1596,11 @@ class Config
/**
* @param string $sModule
* @param string $sProperty
* @param mixed|null $defaultvalue
* @param mixed $defaultvalue
*
* @return mixed|null
* @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)
{
@@ -1551,83 +1632,6 @@ class Config
$this->m_aAddons = $aAddons;
}
/**
* @return string
*
* @deprecated 2.5 will be removed in 2.6
* @see Config::Get() as a replacement
*/
public function GetDBHost()
{
return $this->Get('db_host');
}
/**
* @return string
*
* @deprecated 2.5 will be removed in 2.6
* @see Config::Get() as a replacement
*/
public function GetDBName()
{
return $this->Get('db_name');
}
/**
* @return string
*
* @deprecated 2.5 will be removed in 2.6
* @see Config::Get() as a replacement
*/
public function GetDBSubname()
{
return $this->Get('db_subname');
}
/**
* @return string
*
* @deprecated 2.5 will be removed in 2.6 N°1001 utf8mb4 switch
* @see Config::DEFAULT_CHARACTER_SET
*/
public function GetDBCharacterSet()
{
return DEFAULT_CHARACTER_SET;
}
/**
* @return string
*
* @deprecated 2.5 will be removed in 2.6 N°1001 utf8mb4 switch
* @see Config::DEFAULT_COLLATION
*/
public function GetDBCollation()
{
return DEFAULT_COLLATION;
}
/**
* @return string
*
* @deprecated 2.5 will be removed in 2.6
* @see Config::Get() as a replacement
*/
public function GetDBUser()
{
return $this->Get('db_user');
}
/**
* @return string
*
* @deprecated 2.5 will be removed in 2.6
* @see Config::Get() as a replacement
*/
public function GetDBPwd()
{
return $this->Get('db_pwd');
}
public function GetLogGlobal()
{
return $this->m_bLogGlobal;
@@ -1881,7 +1885,6 @@ class Config
'log_notification' => $this->m_bLogNotification,
'log_issue' => $this->m_bLogIssue,
'log_web_service' => $this->m_bLogWebService,
'query_cache_enabled' => $this->m_bQueryCacheEnabled,
'secure_connection_required' => $this->m_bSecureConnectionRequired,
);
foreach ($aBoolValues as $sKey => $bValue)
@@ -2085,7 +2088,7 @@ class Config
* selected modules
*
* @param string $sModulesDir The relative path to the directory to scan for modules (typically the 'env-xxx'
* directory resulting from the compilation)
* directory resulting from the compilation). If null nothing will be done.
* @param array $aSelectedModules An array of selected modules' identifiers. If null all modules found will be
* considered as installed
*
@@ -2093,51 +2096,53 @@ class Config
*/
public function UpdateIncludes($sModulesDir, $aSelectedModules = null)
{
if (!is_null($sModulesDir))
if ($sModulesDir === null)
{
// Initialize the arrays below with default values for the application...
$oEmptyConfig = new Config('dummy_file', false); // Do NOT load any config file, just set the default values
$aAddOns = $oEmptyConfig->GetAddOns();
return;
}
$aModules = ModuleDiscovery::GetAvailableModules(array(APPROOT.$sModulesDir));
foreach ($aModules as $sModuleId => $aModuleInfo)
// Initialize the arrays below with default values for the application...
$oEmptyConfig = new Config('dummy_file', false); // Do NOT load any config file, just set the default values
$aAddOns = $oEmptyConfig->GetAddOns();
$aModules = ModuleDiscovery::GetAvailableModules(array(APPROOT.$sModulesDir));
foreach ($aModules as $sModuleId => $aModuleInfo)
{
list ($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
if (is_null($aSelectedModules) || in_array($sModuleName, $aSelectedModules))
{
list ($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
if (is_null($aSelectedModules) || in_array($sModuleName, $aSelectedModules))
if (isset($aModuleInfo['settings']))
{
if (isset($aModuleInfo['settings']))
list ($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
foreach ($aModuleInfo['settings'] as $sProperty => $value)
{
list ($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
foreach ($aModuleInfo['settings'] as $sProperty => $value)
if (isset($this->m_aModuleSettings[$sName][$sProperty]))
{
if (isset($this->m_aModuleSettings[$sName][$sProperty]))
{
// Do nothing keep the original value
}
else
{
$this->SetModuleSetting($sName, $sProperty, $value);
}
// Do nothing keep the original value
}
}
if (isset($aModuleInfo['installer']))
{
$sModuleInstallerClass = $aModuleInfo['installer'];
if (!class_exists($sModuleInstallerClass))
else
{
throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not a PHP class - Module: ".$aModuleInfo['label']);
$this->SetModuleSetting($sName, $sProperty, $value);
}
if (!is_subclass_of($sModuleInstallerClass, 'ModuleInstallerAPI'))
{
throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not derived from 'ModuleInstallerAPI' - Module: ".$aModuleInfo['label']);
}
$aCallSpec = array($sModuleInstallerClass, 'BeforeWritingConfig');
call_user_func_array($aCallSpec, array($this));
}
}
if (isset($aModuleInfo['installer']))
{
$sModuleInstallerClass = $aModuleInfo['installer'];
if (!class_exists($sModuleInstallerClass))
{
throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not a PHP class - Module: ".$aModuleInfo['label']);
}
if (!is_subclass_of($sModuleInstallerClass, 'ModuleInstallerAPI'))
{
throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not derived from 'ModuleInstallerAPI' - Module: ".$aModuleInfo['label']);
}
$aCallSpec = array($sModuleInstallerClass, 'BeforeWritingConfig');
call_user_func_array($aCallSpec, array($this));
}
}
$this->SetAddOns($aAddOns);
}
$this->SetAddOns($aAddOns);
}
/**

View File

@@ -199,7 +199,17 @@ class SecurityException extends CoreException
* Throwned when querying on an object that exists in the database but is archived
*
* @see N.1108
* @since 2.5.1
*/
class ArchivedObjectException extends CoreException
{
}
/**
* A parameter stored in the {@link Config} is invalid
*
* @since 2.7.0
*/
class InvalidConfigParamException extends CoreException
{
}

View File

@@ -58,8 +58,8 @@ class DateTimeFormat
{
return array(
// Days
'd' => array('regexpr' => '(0[1-9]|[1-2][0-9]||3[0-1])', 'datepicker' => 'dd', 'excel' => 'dd', 'moment' => 'DD'), // Day of the month: 2 digits (with leading zero)
'j' => array('regexpr' => '([1-9]|[1-2][0-9]||3[0-1])', 'datepicker' => 'd', 'excel' => 'd', 'moment' => 'D'), // Day of the month: 1 or 2 digits (without leading zero)
'd' => array('regexpr' => '(0[1-9]|[1-2][0-9]|3[0-1])', 'datepicker' => 'dd', 'excel' => 'dd', 'moment' => 'DD'), // Day of the month: 2 digits (with leading zero)
'j' => array('regexpr' => '([1-9]|[1-2][0-9]|3[0-1])', 'datepicker' => 'd', 'excel' => 'd', 'moment' => 'D'), // Day of the month: 1 or 2 digits (without leading zero)
// Months
'm' => array('regexpr' => '(0[1-9]|1[0-2])', 'datepicker' => 'mm', 'excel' => 'MM', 'moment' => 'MM' ), // Month on 2 digits i.e. 01-12
'n' => array('regexpr' => '([1-9]|1[0-2])', 'datepicker' => 'm', 'excel' => 'm', 'moment' => 'M'), // Month on 1 or 2 digits 1-12
@@ -69,7 +69,7 @@ class DateTimeFormat
// Hours
'H' => array('regexpr' => '([0-1][0-9]|2[0-3])', 'datepicker' => 'HH', 'excel' => 'HH', 'moment' => 'HH'), // Hour 00..23
'h' => array('regexpr' => '(0[1-9]|1[0-2])', 'datepicker' => 'hh', 'excel' => 'hh', 'moment' => 'hh'), // Hour 01..12
'G' => array('regexpr' => '([1-9]|[1[0-9]|2[0-3])', 'datepicker' => 'H', 'excel' => 'H', 'moment' => 'H'), // Hour 0..23
'G' => array('regexpr' => '([0-9]|1[0-9]|2[0-3])', 'datepicker' => 'H', 'excel' => 'H', 'moment' => 'H'), // Hour 0..23
'g' => array('regexpr' => '([1-9]|1[0-2])', 'datepicker' => 'h', 'excel' => 'h', 'moment' => 'h'), // Hour 1..12
'a' => array('regexpr' => '(am|pm)', 'datepicker' => 'tt', 'excel' => 'am/pm', 'moment' => 'a'),
'A' => array('regexpr' => '(AM|PM)', 'datepicker' => 'TT', 'excel' => 'AM/PM', 'moment' => 'A'),

File diff suppressed because it is too large Load Diff

View File

@@ -17,9 +17,22 @@
// along with iTop. If not, see <http://www.gnu.org/licenses/>
//
// Dev hack for disabling the some query build optimizations (Folding/Merging)
/** @internal Dev hack for disabling some query build optimizations (Folding/Merging) */
define('ENABLE_OPT', true);
/**
* A search over a DBObject
*
* This is the most common search cases, the other class representing a search is DBUnionSearch.
* For clarity purpose, since only the constructor vary between DBObjectSearch and DBUnionSearch, all the API is documented on the common ancestor: DBSearch
* Please refer to DBSearch's documentation
*
* @package iTopORM
* @phpdoc-tuning-exclude-inherited this tag prevent PHPdoc from displaying inherited methods. This is done in order to force the API doc. location into DBSearch only.
* @api
* @see DBSearch
* @see DBUnionSearch
*/
class DBObjectSearch extends DBSearch
{
private $m_aClasses; // queried classes (alias => class name), the first item is the class corresponding to this filter (the rest is coming from subfilters)
@@ -29,11 +42,23 @@ class DBObjectSearch extends DBSearch
private $m_aPointingTo;
private $m_aReferencedBy;
// By default, some information may be hidden to the current user
// But it may happen that we need to disable that feature
/**
* @var bool whether or not some information should be hidden to the current user. Default to false == hide information.
* @see AllowAllData()
*/
protected $m_bAllowAllData = false;
protected $m_bDataFiltered = false;
/**
* DBObjectSearch constructor.
*
* @api
*
* @param string $sClass
* @param string|null $sClassAlias
*
* @throws Exception
*/
public function __construct($sClass, $sClassAlias = null)
{
parent::__construct();
@@ -524,7 +549,7 @@ class DBObjectSearch extends DBSearch
/**
* Specify a condition on external keys or link sets
* @param string $sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
* Example: infra_list->ci_id->location_id->country
* Example: infra_list->ci_id->location_id->country
* @param $value
* @return void
* @throws \CoreException
@@ -597,6 +622,7 @@ class DBObjectSearch extends DBSearch
{
if (!$oAttDef->IsScalar()) continue;
if ($oAttDef->IsExternalKey()) continue;
if ($oAttDef instanceof AttributePassword) continue;
$aFullTextFields[] = new FieldExpression($sAttCode, $this->GetClassAlias());
}
$oTextFields = new CharConcatWSExpression(' ', $aFullTextFields);
@@ -954,6 +980,9 @@ class DBObjectSearch extends DBSearch
}
}
/**
* @inheritDoc
*/
public function Intersect(DBSearch $oFilter)
{
if ($oFilter instanceof DBUnionSearch)
@@ -1068,7 +1097,12 @@ class DBObjectSearch extends DBSearch
if (!array_key_exists($sKeyAttCode, $this->m_aPointingTo)) return array();
return $this->m_aPointingTo[$sKeyAttCode];
}
protected function GetCriteria_ReferencedBy()
/**
* @internal
* @return array
*/
public function GetCriteria_ReferencedBy()
{
return $this->m_aReferencedBy;
}
@@ -1500,36 +1534,42 @@ class DBObjectSearch extends DBSearch
public function MakeDeleteQuery($aArgs = array())
{
$aModifierProperties = MetaModel::MakeModifierProperties($this);
$oBuild = new QueryBuilderContext($this, $aModifierProperties);
$oSQLQuery = $this->MakeSQLObjectQuery($oBuild, array($this->GetClassAlias() => array()), array());
$oSQLQuery->SetCondition($oBuild->m_oQBExpressions->GetCondition());
$oSQLQuery->SetSelect($oBuild->m_oQBExpressions->GetSelect());
$oSQLQuery->OptimizeJoins(array());
$oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($this);
$oSQLQuery = $oSQLObjectQueryBuilder->MakeSQLObjectDeleteQuery();
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
$sRet = $oSQLQuery->RenderDelete($aScalarArgs);
return $sRet;
}
/**
* @param array $aValues is an array of $sAttCode => $value
* @param array $aArgs
*
* @return string
* @throws \CoreException
*/
public function MakeUpdateQuery($aValues, $aArgs = array())
{
// $aValues is an array of $sAttCode => $value
$aModifierProperties = MetaModel::MakeModifierProperties($this);
$oBuild = new QueryBuilderContext($this, $aModifierProperties);
$aRequested = array(); // Requested attributes are the updated attributes
foreach ($aValues as $sAttCode => $value)
{
$aRequested[$sAttCode] = MetaModel::GetAttributeDef($this->GetClass(), $sAttCode);
}
$oSQLQuery = $this->MakeSQLObjectQuery($oBuild, array($this->GetClassAlias() => $aRequested), $aValues);
$oSQLQuery->SetCondition($oBuild->m_oQBExpressions->GetCondition());
$oSQLQuery->SetSelect($oBuild->m_oQBExpressions->GetSelect());
$oSQLQuery->OptimizeJoins(array());
$oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($this);
$oSQLQuery = $oSQLObjectQueryBuilder->MakeSQLObjectUpdateQuery($aValues);
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
$sRet = $oSQLQuery->RenderUpdate($aScalarArgs);
return $sRet;
}
/**
* Get an SQLObjectQuery from the search. This SQLObjectQuery can be rendered as a select, select group by, update or delete
*
* @param array $aAttToLoad array of 'attCode' => AttributeDefinition
* @param bool $bGetCount true for count requests
* @param null array $aGroupByExpr array of 'field name' => FieldOQLExpression
* @param null array $aSelectedClasses
* @param null array $aSelectExpr array of 'attCode' => Expression
*
* @return array|mixed|\SQLObjectQuery|null
* @throws \CoreException
*/
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
{
// Hide objects that are not visible to the current user
@@ -1595,7 +1635,7 @@ class DBObjectSearch extends DBSearch
}
$aContextData['sModifierProperties'] = $sModifierProperties;
$sRawId = $sOqlQuery.$sModifierProperties;
$sRawId = Dict::GetUserLanguage().'-'.$sOqlQuery.$sModifierProperties;
if (!is_null($aAttToLoad))
{
$sRawId .= json_encode($aAttToLoad);
@@ -1642,7 +1682,7 @@ class DBObjectSearch extends DBSearch
// Query caching
//
$sOqlAPCCacheId = null;
if (self::$m_bQueryCacheEnabled)
if (self::$m_bQueryCacheEnabled && $bCanCache)
{
// Warning: using directly the query string as the key to the hash array can FAIL if the string
// is long and the differences are only near the end... so it's safer (but not bullet proof?)
@@ -1681,7 +1721,8 @@ class DBObjectSearch extends DBSearch
if (!isset($oSQLQuery))
{
$oKPI = new ExecutionKPI();
$oSQLQuery = $oSearch->BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
$oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($oSearch);
$oSQLQuery = $oSQLObjectQueryBuilder->BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
$oKPI->ComputeStats('BuildSQLQueryStruct', $sOqlQuery);
if (self::$m_bQueryCacheEnabled)
@@ -1701,651 +1742,8 @@ class DBObjectSearch extends DBSearch
}
/**
* @param array $aAttToLoad
* @param bool $bGetCount
* @param array $aModifierProperties
* @param array $aGroupByExpr
* @param array $aSelectedClasses
* @param array $aSelectExpr
*
* @return null|SQLObjectQuery
* @throws \CoreException
*/
protected function BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
{
$oBuild = new QueryBuilderContext($this, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
$oSQLQuery = $this->MakeSQLObjectQuery($oBuild, $aAttToLoad, array());
$oSQLQuery->SetCondition($oBuild->m_oQBExpressions->GetCondition());
if (is_array($aGroupByExpr))
{
$aCols = $oBuild->m_oQBExpressions->GetGroupBy();
$oSQLQuery->SetGroupBy($aCols);
$oSQLQuery->SetSelect($aCols);
}
else
{
$oSQLQuery->SetSelect($oBuild->m_oQBExpressions->GetSelect());
}
if ($aSelectExpr)
{
// Get the fields corresponding to the select expressions
foreach($oBuild->m_oQBExpressions->GetSelect() as $sAlias => $oExpr)
{
if (key_exists($sAlias, $aSelectExpr))
{
$oSQLQuery->AddSelect($sAlias, $oExpr);
}
}
}
$aMandatoryTables = null;
if (self::$m_bOptimizeQueries)
{
if ($bGetCount)
{
// Simplify the query if just getting the count
$oSQLQuery->SetSelect(array());
}
$oBuild->m_oQBExpressions->GetMandatoryTables($aMandatoryTables);
$oSQLQuery->OptimizeJoins($aMandatoryTables);
}
// Filter tables as late as possible: do not interfere with the optimization process
foreach ($oBuild->GetFilteredTables() as $sTableAlias => $aConditions)
{
if ($aMandatoryTables && array_key_exists($sTableAlias, $aMandatoryTables))
{
foreach ($aConditions as $oCondition)
{
$oSQLQuery->AddCondition($oCondition);
}
}
}
return $oSQLQuery;
}
/**
* @param $oBuild
* @param null $aAttToLoad
* @param array $aValues
* @return null|SQLObjectQuery
* @throws \CoreException
*/
protected function MakeSQLObjectQuery(&$oBuild, $aAttToLoad = null, $aValues = array())
{
// Note: query class might be different than the class of the filter
// -> this occurs when we are linking our class to an external class (referenced by, or pointing to)
$sClass = $this->GetFirstJoinedClass();
$sClassAlias = $this->GetFirstJoinedClassAlias();
$bIsOnQueriedClass = array_key_exists($sClassAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
//self::DbgTrace("Entering: ".$this->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
//$sRootClass = MetaModel::GetRootClass($sClass);
$sKeyField = MetaModel::DBGetKey($sClass);
if ($bIsOnQueriedClass)
{
// default to the whole list of attributes + the very std id/finalclass
$oBuild->m_oQBExpressions->AddSelect($sClassAlias.'id', new FieldExpression('id', $sClassAlias));
if (is_null($aAttToLoad) || !array_key_exists($sClassAlias, $aAttToLoad))
{
$sSelectedClass = $oBuild->GetSelectedClass($sClassAlias);
$aAttList = MetaModel::ListAttributeDefs($sSelectedClass);
}
else
{
$aAttList = $aAttToLoad[$sClassAlias];
}
foreach ($aAttList as $sAttCode => $oAttDef)
{
if (!$oAttDef->IsScalar()) continue;
// keep because it can be used for sorting - if (!$oAttDef->LoadInObject()) continue;
if ($oAttDef->IsBasedOnOQLExpression())
{
$oBuild->m_oQBExpressions->AddSelect($sClassAlias.$sAttCode, new FieldExpression($sAttCode, $sClassAlias));
}
else
{
foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr)
{
$oBuild->m_oQBExpressions->AddSelect($sClassAlias.$sAttCode.$sColId, new FieldExpression($sAttCode.$sColId, $sClassAlias));
}
}
}
}
//echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."</pre></p>\n";
$aExpectedAtts = array(); // array of (attcode => fieldexpression)
//echo "<p>".__LINE__.": GetUnresolvedFields($sClassAlias, ...)</p>\n";
$oBuild->m_oQBExpressions->GetUnresolvedFields($sClassAlias, $aExpectedAtts);
// Compute a clear view of required joins (from the current class)
// Build the list of external keys:
// -> ext keys required by an explicit join
// -> ext keys mentionned in a 'pointing to' condition
// -> ext keys required for an external field
// -> ext keys required for a friendly name
//
$aExtKeys = array(); // array of sTableClass => array of (sAttCode (keys) => array of (sAttCode (fields)=> oAttDef))
//
// Optimization: could be partially computed once for all (cached) ?
//
if ($bIsOnQueriedClass)
{
// Get all Ext keys for the queried class (??)
foreach(MetaModel::GetKeysList($sClass) as $sKeyAttCode)
{
$sKeyTableClass = MetaModel::GetAttributeOrigin($sClass, $sKeyAttCode);
$aExtKeys[$sKeyTableClass][$sKeyAttCode] = array();
}
}
// Get all Ext keys used by the filter
foreach ($this->GetCriteria_PointingTo() as $sKeyAttCode => $aPointingTo)
{
if (array_key_exists(TREE_OPERATOR_EQUALS, $aPointingTo))
{
$sKeyTableClass = MetaModel::GetAttributeOrigin($sClass, $sKeyAttCode);
$aExtKeys[$sKeyTableClass][$sKeyAttCode] = array();
}
}
$aFNJoinAlias = array(); // array of (subclass => alias)
foreach ($aExpectedAtts as $sExpectedAttCode => $oExpression)
{
if (!MetaModel::IsValidAttCode($sClass, $sExpectedAttCode)) continue;
$oAttDef = MetaModel::GetAttributeDef($sClass, $sExpectedAttCode);
if ($oAttDef->IsBasedOnOQLExpression())
{
// To optimize: detect a restriction on child classes in the condition expression
// e.g. SELECT FunctionalCI WHERE finalclass IN ('Server', 'VirtualMachine')
$oExpression = static::GetPolymorphicExpression($sClass, $sExpectedAttCode);
$aRequiredFields = array();
$oExpression->GetUnresolvedFields('', $aRequiredFields);
$aTranslateFields = array();
foreach($aRequiredFields as $sSubClass => $aFields)
{
foreach($aFields as $sAttCode => $oField)
{
$oAttDef = MetaModel::GetAttributeDef($sSubClass, $sAttCode);
if ($oAttDef->IsExternalKey())
{
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode);
$aExtKeys[$sClassOfAttribute][$sAttCode] = array();
}
elseif ($oAttDef->IsExternalField())
{
$sKeyAttCode = $oAttDef->GetKeyAttCode();
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sKeyAttCode);
$aExtKeys[$sClassOfAttribute][$sKeyAttCode][$sAttCode] = $oAttDef;
}
else
{
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode);
}
if (MetaModel::IsParentClass($sClassOfAttribute, $sClass))
{
// The attribute is part of the standard query
//
$sAliasForAttribute = $sClassAlias;
}
else
{
// The attribute will be available from an additional outer join
// For each subclass (table) one single join is enough
//
if (!array_key_exists($sClassOfAttribute, $aFNJoinAlias))
{
$sAliasForAttribute = $oBuild->GenerateClassAlias($sClassAlias.'_fn_'.$sClassOfAttribute, $sClassOfAttribute);
$aFNJoinAlias[$sClassOfAttribute] = $sAliasForAttribute;
}
else
{
$sAliasForAttribute = $aFNJoinAlias[$sClassOfAttribute];
}
}
$aTranslateFields[$sSubClass][$sAttCode] = new FieldExpression($sAttCode, $sAliasForAttribute);
}
}
$oExpression = $oExpression->Translate($aTranslateFields, false);
$aTranslateNow = array();
$aTranslateNow[$sClassAlias][$sExpectedAttCode] = $oExpression;
$oBuild->m_oQBExpressions->Translate($aTranslateNow, false);
}
}
// Add the ext fields used in the select (eventually adds an external key)
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef)
{
if ($oAttDef->IsExternalField())
{
if (array_key_exists($sAttCode, $aExpectedAtts))
{
// Add the external attribute
$sKeyAttCode = $oAttDef->GetKeyAttCode();
$sKeyTableClass = MetaModel::GetAttributeOrigin($sClass, $sKeyAttCode);
$aExtKeys[$sKeyTableClass][$sKeyAttCode][$sAttCode] = $oAttDef;
}
}
}
$bRootFirst = MetaModel::GetConfig()->Get('optimize_requests_for_join_count');
if ($bRootFirst)
{
// First query built from the root, adding all tables including the leaf
// Before N.1065 we were joining from the leaf first, but this wasn't a good choice :
// most of the time (obsolescence, friendlyname, ...) we want to get a root attribute !
//
$oSelectBase = null;
$aClassHierarchy = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, true);
$bIsClassStandaloneClass = (count($aClassHierarchy) == 1);
foreach($aClassHierarchy as $sSomeClass)
{
if (!MetaModel::HasTable($sSomeClass))
{
continue;
}
self::DbgTrace("Adding join from root to leaf: $sSomeClass... let's call MakeSQLObjectQuerySingleTable()");
$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sSomeClass, $aExtKeys, $aValues);
if (is_null($oSelectBase))
{
$oSelectBase = $oSelectParentTable;
if (!$bIsClassStandaloneClass && (MetaModel::IsRootClass($sSomeClass)))
{
// As we're linking to root class first, we're adding a where clause on the finalClass attribute :
// COALESCE($sRootClassFinalClass IN ('$sExpectedClasses'), 1)
// If we don't, the child classes can be removed in the query optimisation phase, including the leaf that was queried
// So we still need to filter records to only those corresponding to the child classes !
// The coalesce is mandatory if we have a polymorphic query (left join)
$oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
$sFinalClassSqlColumnName = MetaModel::DBGetClassField($sSomeClass);
$oClassExpr = new FieldExpression($sFinalClassSqlColumnName, $oSelectBase->GetTableAlias());
$oInExpression = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
$oTrueExpression = new TrueExpression();
$aCoalesceAttr = array($oInExpression, $oTrueExpression);
$oFinalClassRestriction = new FunctionExpression("COALESCE", $aCoalesceAttr);
$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
}
}
else
{
$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sSomeClass));
}
}
}
else
{
// First query built upon on the leaf (ie current) class
//
self::DbgTrace("Main (=leaf) class, call MakeSQLObjectQuerySingleTable()");
if (MetaModel::HasTable($sClass))
{
$oSelectBase = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sClass, $aExtKeys, $aValues);
}
else
{
$oSelectBase = null;
// As the join will not filter on the expected classes, we have to specify it explicitely
$sExpectedClasses = implode("', '", MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
$oFinalClassRestriction = Expression::FromOQL("`$sClassAlias`.finalclass IN ('$sExpectedClasses')");
$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
}
// Then we join the queries of the eventual parent classes (compound model)
foreach(MetaModel::EnumParentClasses($sClass) as $sParentClass)
{
if (!MetaModel::HasTable($sParentClass)) continue;
self::DbgTrace("Parent class: $sParentClass... let's call MakeSQLObjectQuerySingleTable()");
$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sParentClass, $aExtKeys, $aValues);
if (is_null($oSelectBase))
{
$oSelectBase = $oSelectParentTable;
}
else
{
$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sParentClass));
}
}
}
// Filter on objects referencing me
//
foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences)
{
foreach($aReferences as $sForeignExtKeyAttCode => $aFiltersByOperator)
{
foreach ($aFiltersByOperator as $iOperatorCode => $aFilters)
{
foreach ($aFilters as $oForeignFilter)
{
$oForeignKeyAttDef = MetaModel::GetAttributeDef($sForeignClass, $sForeignExtKeyAttCode);
self::DbgTrace("Referenced by foreign key: $sForeignExtKeyAttCode... let's call MakeSQLObjectQuery()");
//self::DbgTrace($oForeignFilter);
//self::DbgTrace($oForeignFilter->ToOQL());
//self::DbgTrace($oSelectForeign);
//self::DbgTrace($oSelectForeign->RenderSelect(array()));
$sForeignClassAlias = $oForeignFilter->GetFirstJoinedClassAlias();
$oBuild->m_oQBExpressions->PushJoinField(new FieldExpression($sForeignExtKeyAttCode, $sForeignClassAlias));
if ($oForeignKeyAttDef instanceof AttributeObjectKey)
{
$sClassAttCode = $oForeignKeyAttDef->Get('class_attcode');
// Add the condition: `$sForeignClassAlias`.$sClassAttCode IN (subclasses of $sClass')
$oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
$oClassExpr = new FieldExpression($sClassAttCode, $sForeignClassAlias);
$oClassRestriction = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
$oBuild->m_oQBExpressions->AddCondition($oClassRestriction);
}
$oSelectForeign = $oForeignFilter->MakeSQLObjectQuery($oBuild, $aAttToLoad);
$oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField();
$sForeignKeyTable = $oJoinExpr->GetParent();
$sForeignKeyColumn = $oJoinExpr->GetName();
if ($iOperatorCode == TREE_OPERATOR_EQUALS)
{
$oSelectBase->AddInnerJoin($oSelectForeign, $sKeyField, $sForeignKeyColumn, $sForeignKeyTable);
}
else
{
// Hierarchical key
$KeyLeft = $oForeignKeyAttDef->GetSQLLeft();
$KeyRight = $oForeignKeyAttDef->GetSQLRight();
$oSelectBase->AddInnerJoinTree($oSelectForeign, $KeyLeft, $KeyRight, $KeyLeft, $KeyRight, $sForeignKeyTable, $iOperatorCode, true);
}
}
}
}
}
// Additional JOINS for Friendly names
//
foreach ($aFNJoinAlias as $sSubClass => $sSubClassAlias)
{
$oSubClassFilter = new DBObjectSearch($sSubClass, $sSubClassAlias);
$oSelectFN = $oSubClassFilter->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sSubClass, $aExtKeys, array());
$oSelectBase->AddLeftJoin($oSelectFN, $sKeyField, MetaModel::DBGetKey($sSubClass));
}
// That's all... cross fingers and we'll get some working query
//MyHelpers::var_dump_html($oSelectBase, true);
//MyHelpers::var_dump_html($oSelectBase->RenderSelect(), true);
if (self::$m_bDebugQuery) $oSelectBase->DisplayHtml();
return $oSelectBase;
}
protected function MakeSQLObjectQuerySingleTable(&$oBuild, $aAttToLoad, $sTableClass, $aExtKeys, $aValues)
{
// $aExtKeys is an array of sTableClass => array of (sAttCode (keys) => array of sAttCode (fields))
// Prepare the query for a single table (compound objects)
// Ignores the items (attributes/filters) that are not on the target table
// Perform an (inner or left) join for every external key (and specify the expected fields)
//
// Returns an SQLQuery
//
$sTargetClass = $this->GetFirstJoinedClass();
$sTargetAlias = $this->GetFirstJoinedClassAlias();
$sTable = MetaModel::DBGetTable($sTableClass);
$sTableAlias = $oBuild->GenerateTableAlias($sTargetAlias.'_'.$sTable, $sTable);
$aTranslation = array();
$aExpectedAtts = array();
$oBuild->m_oQBExpressions->GetUnresolvedFields($sTargetAlias, $aExpectedAtts);
$bIsOnQueriedClass = array_key_exists($sTargetAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
self::DbgTrace("Entering: tableclass=$sTableClass, filter=".$this->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
// 1 - SELECT and UPDATE
//
// Note: no need for any values nor fields for foreign Classes (ie not the queried Class)
//
$aUpdateValues = array();
// 1/a - Get the key and friendly name
//
// We need one pkey to be the key, let's take the first one available
$oSelectedIdField = null;
$oIdField = new FieldExpressionResolved(MetaModel::DBGetKey($sTableClass), $sTableAlias);
$aTranslation[$sTargetAlias]['id'] = $oIdField;
if ($bIsOnQueriedClass)
{
// Add this field to the list of queried fields (required for the COUNT to work fine)
$oSelectedIdField = $oIdField;
}
// 1/b - Get the other attributes
//
foreach(MetaModel::ListAttributeDefs($sTableClass) as $sAttCode=>$oAttDef)
{
// Skip this attribute if not defined in this table
if (MetaModel::GetAttributeOrigin($sTargetClass, $sAttCode) != $sTableClass) continue;
// Skip this attribute if not made of SQL columns
if (count($oAttDef->GetSQLExpressions()) == 0) continue;
// Update...
//
if ($bIsOnQueriedClass && array_key_exists($sAttCode, $aValues))
{
assert ($oAttDef->IsBasedOnDBColumns());
foreach ($oAttDef->GetSQLValues($aValues[$sAttCode]) as $sColumn => $sValue)
{
$aUpdateValues[$sColumn] = $sValue;
}
}
}
// 2 - The SQL query, for this table only
//
$oSelectBase = new SQLObjectQuery($sTable, $sTableAlias, array(), $bIsOnQueriedClass, $aUpdateValues, $oSelectedIdField);
// 3 - Resolve expected expressions (translation table: alias.attcode => table.column)
//
foreach(MetaModel::ListAttributeDefs($sTableClass) as $sAttCode=>$oAttDef)
{
// Skip this attribute if not defined in this table
if (MetaModel::GetAttributeOrigin($sTargetClass, $sAttCode) != $sTableClass) continue;
// Select...
//
if ($oAttDef->IsExternalField())
{
// skip, this will be handled in the joined tables (done hereabove)
}
else
{
// standard field, or external key
// add it to the output
foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr)
{
if (array_key_exists($sAttCode.$sColId, $aExpectedAtts))
{
$oFieldSQLExp = new FieldExpressionResolved($sSQLExpr, $sTableAlias);
foreach (MetaModel::EnumPlugins('iQueryModifier') as $sPluginClass => $oQueryModifier)
{
$oFieldSQLExp = $oQueryModifier->GetFieldExpression($oBuild, $sTargetClass, $sAttCode, $sColId, $oFieldSQLExp, $oSelectBase);
}
$aTranslation[$sTargetAlias][$sAttCode.$sColId] = $oFieldSQLExp;
}
}
}
}
// 4 - The external keys -> joins...
//
$aAllPointingTo = $this->GetCriteria_PointingTo();
if (array_key_exists($sTableClass, $aExtKeys))
{
foreach ($aExtKeys[$sTableClass] as $sKeyAttCode => $aExtFields)
{
$oKeyAttDef = MetaModel::GetAttributeDef($sTableClass, $sKeyAttCode);
$aPointingTo = $this->GetCriteria_PointingTo($sKeyAttCode);
if (!array_key_exists(TREE_OPERATOR_EQUALS, $aPointingTo))
{
// The join was not explicitely defined in the filter,
// we need to do it now
$sKeyClass = $oKeyAttDef->GetTargetClass();
$sKeyClassAlias = $oBuild->GenerateClassAlias($sKeyClass.'_'.$sKeyAttCode, $sKeyClass);
$oExtFilter = new DBObjectSearch($sKeyClass, $sKeyClassAlias);
$aAllPointingTo[$sKeyAttCode][TREE_OPERATOR_EQUALS][$sKeyClassAlias] = $oExtFilter;
}
}
}
foreach ($aAllPointingTo as $sKeyAttCode => $aPointingTo)
{
foreach($aPointingTo as $iOperatorCode => $aFilter)
{
foreach($aFilter as $oExtFilter)
{
if (!MetaModel::IsValidAttCode($sTableClass, $sKeyAttCode)) continue; // Not defined in the class, skip it
// The aliases should not conflict because normalization occured while building the filter
$oKeyAttDef = MetaModel::GetAttributeDef($sTableClass, $sKeyAttCode);
$sKeyClass = $oExtFilter->GetFirstJoinedClass();
$sKeyClassAlias = $oExtFilter->GetFirstJoinedClassAlias();
// Note: there is no search condition in $oExtFilter, because normalization did merge the condition onto the top of the filter tree
if ($iOperatorCode == TREE_OPERATOR_EQUALS)
{
if (array_key_exists($sTableClass, $aExtKeys) && array_key_exists($sKeyAttCode, $aExtKeys[$sTableClass]))
{
// Specify expected attributes for the target class query
// ... and use the current alias !
$aTranslateNow = array(); // Translation for external fields - must be performed before the join is done (recursion...)
foreach($aExtKeys[$sTableClass][$sKeyAttCode] as $sAttCode => $oAtt)
{
$oExtAttDef = $oAtt->GetExtAttDef();
if ($oExtAttDef->IsBasedOnOQLExpression())
{
$aTranslateNow[$sTargetAlias][$sAttCode] = new FieldExpression($oExtAttDef->GetCode(), $sKeyClassAlias);
}
else
{
$sExtAttCode = $oAtt->GetExtAttCode();
// Translate mainclass.extfield => remoteclassalias.remotefieldcode
$oRemoteAttDef = MetaModel::GetAttributeDef($sKeyClass, $sExtAttCode);
foreach ($oRemoteAttDef->GetSQLExpressions() as $sColId => $sRemoteAttExpr)
{
$aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias);
}
}
}
if ($oKeyAttDef instanceof AttributeObjectKey)
{
// Add the condition: `$sTargetAlias`.$sClassAttCode IN (subclasses of $sKeyClass')
$sClassAttCode = $oKeyAttDef->Get('class_attcode');
$oClassAttDef = MetaModel::GetAttributeDef($sTargetClass, $sClassAttCode);
foreach ($oClassAttDef->GetSQLExpressions() as $sColId => $sSQLExpr)
{
$aTranslateNow[$sTargetAlias][$sClassAttCode.$sColId] = new FieldExpressionResolved($sSQLExpr, $sTableAlias);
}
$oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sKeyClass, ENUM_CHILD_CLASSES_ALL));
$oClassExpr = new FieldExpression($sClassAttCode, $sTargetAlias);
$oClassRestriction = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
$oBuild->m_oQBExpressions->AddCondition($oClassRestriction);
}
// Translate prior to recursing
//
$oBuild->m_oQBExpressions->Translate($aTranslateNow, false);
self::DbgTrace("External key $sKeyAttCode (class: $sKeyClass), call MakeSQLObjectQuery()");
$oBuild->m_oQBExpressions->PushJoinField(new FieldExpression('id', $sKeyClassAlias));
$oSelectExtKey = $oExtFilter->MakeSQLObjectQuery($oBuild, $aAttToLoad);
$oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField();
$sExternalKeyTable = $oJoinExpr->GetParent();
$sExternalKeyField = $oJoinExpr->GetName();
$aCols = $oKeyAttDef->GetSQLExpressions(); // Workaround a PHP bug: sometimes issuing a Notice if invoking current(somefunc())
$sLocalKeyField = current($aCols); // get the first column for an external key
self::DbgTrace("External key $sKeyAttCode, Join on $sLocalKeyField = $sExternalKeyField");
if ($oKeyAttDef->IsNullAllowed())
{
$oSelectBase->AddLeftJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField);
}
else
{
$oSelectBase->AddInnerJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField, $sExternalKeyTable);
}
}
}
elseif(MetaModel::GetAttributeOrigin($sKeyClass, $sKeyAttCode) == $sTableClass)
{
$oBuild->m_oQBExpressions->PushJoinField(new FieldExpression($sKeyAttCode, $sKeyClassAlias));
$oSelectExtKey = $oExtFilter->MakeSQLObjectQuery($oBuild, $aAttToLoad);
$oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField();
$sExternalKeyTable = $oJoinExpr->GetParent();
$sExternalKeyField = $oJoinExpr->GetName();
$sLeftIndex = $sExternalKeyField.'_left'; // TODO use GetSQLLeft()
$sRightIndex = $sExternalKeyField.'_right'; // TODO use GetSQLRight()
$LocalKeyLeft = $oKeyAttDef->GetSQLLeft();
$LocalKeyRight = $oKeyAttDef->GetSQLRight();
$oSelectBase->AddInnerJoinTree($oSelectExtKey, $LocalKeyLeft, $LocalKeyRight, $sLeftIndex, $sRightIndex, $sExternalKeyTable, $iOperatorCode);
}
}
}
}
// Translate the selected columns
//
$oBuild->m_oQBExpressions->Translate($aTranslation, false);
// Filter out archived records
//
if (MetaModel::IsArchivable($sTableClass))
{
if (!$oBuild->GetRootFilter()->GetArchiveMode())
{
$bIsOnJoinedClass = array_key_exists($sTargetAlias, $oBuild->GetRootFilter()->GetJoinedClasses());
if ($bIsOnJoinedClass)
{
if (MetaModel::IsParentClass($sTableClass, $sTargetClass))
{
$oNotArchived = new BinaryExpression(new FieldExpressionResolved('archive_flag', $sTableAlias), '=', new ScalarExpression(0));
$oBuild->AddFilteredTable($sTableAlias, $oNotArchived);
}
}
}
}
return $oSelectBase;
}
/**
* Get the expression for the class and its subclasses (if finalclass = 'subclass' ...)
* Simplifies the final expression by grouping classes having the same expression
* Get the expression for the class and its subclasses (if finalclass = 'subclass' ...)
* Simplifies the final expression by grouping classes having the same expression
* @param $sClass
* @param $sAttCode
* @return \FunctionExpression|mixed|null

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -18,10 +18,21 @@
/**
* A union of DBObjectSearches
* A union of DBObjectSearches
*
* This search class represent an union over a collection of DBObjectSearch.
* For clarity purpose, since only the constructor vary between DBObjectSearch and DBUnionSearch, all the API is documented on the common ancestor: DBSearch
* Please refer to DBSearch's documentation
*
* @copyright Copyright (C) 2015-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*
*
* @package iTopORM
* @phpdoc-tuning-exclude-inherited this tag prevent PHPdoc from displaying inherited methods. This is done in order to force the API doc. location into DBSearch only.
* @api
* @see DBSearch
* @see DBObjectSearch
*/
class DBUnionSearch extends DBSearch
@@ -29,6 +40,15 @@ class DBUnionSearch extends DBSearch
protected $aSearches; // source queries
protected $aSelectedClasses; // alias => classes (lowest common ancestors) computed at construction
/**
* DBUnionSearch constructor.
*
* @api
*
* @param $aSearches
*
* @throws CoreException
*/
public function __construct($aSearches)
{
if (count ($aSearches) == 0)
@@ -435,8 +455,9 @@ class DBUnionSearch extends DBSearch
/**
* Returns a new DBUnionSearch object where duplicates queries have been removed based on their OQLs
*
*
* @return \DBUnionSearch
* @throws \CoreException
*/
public function RemoveDuplicateQueries()
{
@@ -478,7 +499,7 @@ class DBUnionSearch extends DBSearch
{
if (count($this->aSearches) == 1)
{
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, $aSelectExpr);
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
}
$aSQLQueries = array();

View File

@@ -111,7 +111,7 @@ class DisplayableNode extends GraphNode
return $aNode;
}
public function RenderAsPDF(TCPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
public function RenderAsPDF(iTopPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
{
$Alpha = 1.0;
$oPdf->SetFillColor(200, 200, 200);
@@ -161,8 +161,8 @@ class DisplayableNode extends GraphNode
$idx++;
}
}
$oPdf->SetFont('dejavusans', '', 24 * $fScale, '', true);
$oPdf->SetFontParams('', 24 * $fScale, '', true);
$width = $oPdf->GetStringWidth($this->GetProperty('label'));
$height = $oPdf->GetStringHeight(1000, $this->GetProperty('label'));
$oPdf->setAlpha(0.6 * $Alpha);
@@ -532,7 +532,7 @@ class DisplayableRedundancyNode extends DisplayableNode
return $aNode;
}
public function RenderAsPDF(TCPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
public function RenderAsPDF(iTopPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
{
$oPdf->SetAlpha(1);
if($this->GetProperty('is_reached_count') > $this->GetProperty('threshold'))
@@ -547,9 +547,9 @@ class DisplayableRedundancyNode extends DisplayableNode
$oPdf->Circle($this->x*$fScale, $this->y*$fScale, 16*$fScale, 0, 360, 'DF');
$oPdf->SetTextColor(255, 255, 255);
$oPdf->SetFont('dejavusans', '', 28 * $fScale, '', true);
$oPdf->SetFontParams('', 28 * $fScale, '', true);
$sLabel = (string)$this->GetProperty('label');
$width = $oPdf->GetStringWidth($sLabel, 'dejavusans', 'B', 24*$fScale);
$width = $oPdf->GetStringWidth($sLabel, iTopPDF::GetPdfFont(), 'B', 24 * $fScale);
$height = $oPdf->GetStringHeight(1000, $sLabel);
$xPos = (float)$this->x*$fScale - $width/2;
$yPos = (float)$this->y*$fScale - $height/2;
@@ -764,7 +764,7 @@ class DisplayableGroupNode extends DisplayableNode
return $aNode;
}
public function RenderAsPDF(TCPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
public function RenderAsPDF(iTopPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
{
$bReached = $this->GetProperty('is_reached');
$oPdf->SetFillColor(255, 255, 255);
@@ -794,7 +794,7 @@ class DisplayableGroupNode extends DisplayableNode
$oPdf->Image($sIconPath, ($this->x - 17)*$fScale, ($this->y - 17)*$fScale, 16*$fScale, 16*$fScale);
$oPdf->Image($sIconPath, ($this->x + 1)*$fScale, ($this->y - 17)*$fScale, 16*$fScale, 16*$fScale);
$oPdf->Image($sIconPath, ($this->x -8)*$fScale, ($this->y +1)*$fScale, 16*$fScale, 16*$fScale);
$oPdf->SetFont('dejavusans', '', 24 * $fScale, '', true);
$oPdf->SetFontParams('', 24 * $fScale, '', true);
$width = $oPdf->GetStringWidth($this->GetProperty('label'));
$oPdf->SetTextColor(0, 0, 0);
$oPdf->Text($this->x*$fScale - $width/2, ($this->y + 25)*$fScale, $this->GetProperty('label'));
@@ -1285,7 +1285,7 @@ class DisplayableGraph extends SimpleGraph
* @param hash $aContextDefs
* @return hash An array ('xmin' => , 'xmax' => ,'ymin' => , 'ymax' => ) of the remaining available area to paint the graph
*/
protected function RenderKey(TCPDF $oPdf, $sComments, $xMin, $yMin, $xMax, $yMax, $aContextDefs)
protected function RenderKey(iTopPDF $oPdf, $sComments, $xMin, $yMin, $xMax, $yMax, $aContextDefs)
{
$fFontSize = 7; // in mm
$fIconSize = 6; // in mm
@@ -1296,7 +1296,7 @@ class DisplayableGraph extends SimpleGraph
$aIcons = array();
$aContexts = array();
$aContextIcons = array();
$oPdf->SetFont('dejavusans', '', $fFontSize, '', true);
$oPdf->SetFontParams('', $fFontSize, '', true);
foreach($oIterator as $sId => $oNode)
{
if ($sClass = $oNode->GetObjectClass())
@@ -1446,7 +1446,7 @@ class DisplayableGraph extends SimpleGraph
<<<EOF
<div id="ds_flash" class="search_box">
<form id="dh_flash" class="search_form_handler closed">
<h2 class="sf_title"><span class="sft_long">$sSftShort</span><span class="sft_short">$sSftShort</span><span class="sft_toggler fa fa-caret-down pull-right" title="$sSearchToggle"></span></h2>
<h2 class="sf_title"><span class="sft_long">$sSftShort</span><span class="sft_short">$sSftShort</span><span class="sft_toggler fas fa-caret-down pull-right" title="$sSearchToggle"></span></h2>
<div id="dh_flash_criterion_outer" class="sf_criterion_area"><div class="sf_criterion_row">
EOF
);

View File

@@ -24,8 +24,6 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'/lib/swiftmailer/lib/swift_required.php');
Swift_Preferences::getInstance()->setCharset('UTF-8');
@@ -333,7 +331,6 @@ class EMail
{
if (($sMimeType === 'text/html') && ($sCustomStyles !== null))
{
require_once(APPROOT.'lib/emogrifier/Classes/Emogrifier.php');
$emogrifier = new \Pelago\Emogrifier($sBody, $sCustomStyles);
$sBody = $emogrifier->emogrify(); // Adds html/body tags if not already present
}

View File

@@ -1,88 +1,120 @@
<?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
*/
/**
* Class ExpressionCache
*/
class ExpressionCache
{
static private $aCache = array();
static public function GetCachedExpression($sClass, $sAttCode)
/**
* @param string $sClass
* @param string $sAttCode
*
* @return mixed|null
*/
public static function GetCachedExpression($sClass, $sAttCode)
{
if (!utils::GetConfig()->Get('expression_cache_enabled'))
{
return null;
}
// read current cache
@include_once (static::GetCacheFileName());
$oExpr = null;
$sKey = static::GetKey($sClass, $sAttCode);
if (array_key_exists($sKey, static::$aCache))
$sCacheClass = self::GetCacheClassName();
if (class_exists($sCacheClass))
{
$oExpr = static::$aCache[$sKey];
}
else
{
if (class_exists('ExpressionCacheData'))
/** @noinspection PhpUndefinedFieldInspection The property is dynamically generated */
if (array_key_exists($sKey, $sCacheClass::$aCache))
{
if (array_key_exists($sKey, ExpressionCacheData::$aCache))
{
$sVal = ExpressionCacheData::$aCache[$sKey];
$oExpr = unserialize($sVal);
static::$aCache[$sKey] = $oExpr;
}
$sVal = $sCacheClass::$aCache[$sKey];
$oExpr = unserialize($sVal);
}
}
return $oExpr;
}
static public function Warmup()
/**
* @throws \CoreException
* @throws \DictExceptionUnknownLanguage
*/
public static function Warmup()
{
$sFilePath = static::GetCacheFileName();
if (!is_file($sFilePath))
if (!utils::GetConfig()->Get('expression_cache_enabled'))
{
$content = <<<EOF
<?php
// Copyright (c) 2010-2017 Combodo SARL
// Generated Expression Cache file
return;
}
// Store current language
$sUserLang = Dict::GetUserLanguage();
$aLanguages = Dict::GetLanguages();
foreach($aLanguages as $sLang => $aLang)
{
Dict::SetUserLanguage($sLang);
$sFilePath = static::GetCacheFileName();
$sCacheClass = self::GetCacheClassName();
class ExpressionCacheData
if (!is_file($sFilePath))
{
$content = <<<EOF
<?php
// Copyright (c) 2010-2019 Combodo SARL
// Generated Expression Cache file for $sLang
class $sCacheClass
{
static \$aCache = array(
EOF;
foreach(MetaModel::GetClasses() as $sClass)
{
$content .= static::GetSerializedExpression($sClass, 'friendlyname');
if (MetaModel::IsObsoletable($sClass))
foreach (MetaModel::GetClasses() as $sClass)
{
$content .= static::GetSerializedExpression($sClass, 'obsolescence_flag');
$content .= static::GetSerializedExpression($sClass, 'friendlyname');
if (MetaModel::IsObsoletable($sClass))
{
$content .= static::GetSerializedExpression($sClass, 'obsolescence_flag');
}
}
}
$content .= <<<EOF
$content .= <<<EOF
);
}
EOF;
file_put_contents($sFilePath, $content);
SetupUtils::builddir(dirname($sFilePath));
file_put_contents($sFilePath, $content);
}
}
Dict::SetUserLanguage($sUserLang);
}
static private function GetSerializedExpression($sClass, $sAttCode)
/**
* @param string $sClass
* @param string $sAttCode
*
* @return string
* @throws \CoreException
*/
private static function GetSerializedExpression($sClass, $sAttCode)
{
$sKey = static::GetKey($sClass, $sAttCode);
$oExpr = DBObjectSearch::GetPolymorphicExpression($sClass, $sAttCode);
@@ -90,20 +122,44 @@ EOF;
}
/**
* @param $sClass
* @param $sAttCode
* @param string $sClass
* @param string $sAttCode
*
* @return string
*/
static private function GetKey($sClass, $sAttCode)
private static function GetKey($sClass, $sAttCode)
{
return $sClass.'::'.$sAttCode;
}
/**
* @return string
*/
public static function GetCacheFileName()
{
return utils::GetCachePath().'expressioncache.php';
$sLangName = self::GetLangName();
return utils::GetCachePath().'expressioncache/expressioncache-' . $sLangName . '.php';
}
/**
* @return string
*/
private static function GetCacheClassName()
{
$sLangName = self::GetLangName();
$sCacheClass = "ExpressionCacheData$sLangName";
return $sCacheClass;
}
/**
* @return mixed
*/
private static function GetLangName()
{
$sLang = Dict::GetUserLanguage();
$sLangName = str_replace(" ", "", $sLang);
return $sLangName;
}
}

View File

@@ -79,10 +79,13 @@ abstract class HTMLSanitizer
/**
* Dummy HTMLSanitizer which does nothing at all!
*
* Can be used if HTML Sanitization is not important
* (for example when importing "safe" data during an on-boarding)
* and performance is at stake
*
* **Warning** : this won't filter HTML inserted in iTop at all, so this is a great security issue !
* Also, the InlineImage objects processing won't be called.
*/
class HTMLNullSanitizer extends HTMLSanitizer
{
@@ -243,10 +246,16 @@ class HTMLDOMSanitizer extends HTMLSanitizer
{
// Regular urls
$sUrlPattern = utils::GetConfig()->Get('url_validation_pattern');
// Mailto urls
$sMailtoPattern = '(mailto:(' . utils::GetConfig()->Get('email_validation_pattern') . ')(?:\?(?:subject|body)=([a-zA-Z0-9+\$_.-]*)(?:&(?:subject|body)=([a-zA-Z0-9+\$_.-]*))?)?)';
$sPattern = $sUrlPattern . '|' . $sMailtoPattern;
// Notification placeholders
// eg. $this->caller_id$, $this->hyperlink()$, $this->hyperlink(portal)$, $APP_URL$, $MODULES_URL$, ...
// Note: Authorize both $xxx$ and %24xxx%24 as the latter one is encoded when used in HTML attributes (eg. a[href])
$sPlaceholderPattern = '(\$|%24)[\w-]*(->[\w]*(\([\w-]*?\))?)?(\$|%24)';
$sPattern = $sUrlPattern . '|' . $sMailtoPattern . '|' . $sPlaceholderPattern;
$sPattern = '/'.str_replace('/', '\/', $sPattern).'/i';
self::$aAttrsWhiteList['href'] = $sPattern;
}
@@ -261,7 +270,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
// We have to transform that into <p><br></p> (which is how Thunderbird implements empty lines)
// Unfortunately, DOMDocument::loadHTML does not take the tag namespaces into account (once loaded there is no way to know if the tag did have a namespace)
// therefore we have to do the transformation upfront
$sHTML = preg_replace('@<o:p>\s*</o:p>@', '<br>', $sHTML);
$sHTML = preg_replace('@<o:p>(\s|&nbsp;)*</o:p>@', '<br>', $sHTML);
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified

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