Compare commits

...

266 Commits

Author SHA1 Message Date
Molkobain
85f66f5e0c N°6618 - Router: Add protection against invalid routes cache 2023-08-02 11:44:20 +02:00
Molkobain
a5c980113b N°6618 - Router: Fix available routes cache being re-generated at each call 2023-08-02 11:44:20 +02:00
Eric Espie
df1cb0b6e3 N°6408 - CRUD : Fix log consuming too much memory 2023-07-12 11:21:32 +02:00
Molkobain
9b409b117f N°6043 - Booking: Move \TemporaryObjectDescriptor to /core to keep compatibility with legacy/custom packages
Cherry picked from 39305468
2023-07-10 13:16:53 +02:00
Pierre Goiffon
a2a0b2cd0b N°6494 Fix missing PHPUnit log in console log for postbuild tests 2023-07-06 15:28:32 +02:00
odain
4c9ea0c9d4 N°6494 - Some tests are run twice, some never 2023-07-05 14:37:02 +02:00
Benjamin Dalsass
e654daf4a5 N°6516 - Add @experimental PHP annotation on temporary objects features 2023-07-05 14:24:05 +02:00
Molkobain
8292b16387 N°6502 - Fix AttributeImage::GetAsHtml() using download URL instead of display URL which triggers download listeners 2023-07-05 10:35:36 +02:00
Molkobain
340b982d77 N°6502 - Avoid new history entry on AttributeBlob download 2023-07-05 10:35:35 +02:00
Stephen Abello
3e187282b0 N°6498 - Fix str_replace crashing with null value 2023-07-05 09:18:56 +02:00
Molkobain
722a4a2c4d Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	js/jquery.ba-bbq.js
2023-07-05 08:59:47 +02:00
Molkobain
8907525b78 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-07-05 08:56:17 +02:00
Molkobain
6d58adb6dd N°6359 - Fix JS crash due to new version trying to detect MSIE browser through a dependency that we don't have.
Cherry-picked from f889c53d71
2023-07-05 08:41:00 +02:00
Benjamin Dalsass
03e7e0e79f N°6503 - Logs purge system delete index.php 2023-07-05 08:39:48 +02:00
Molkobain
83529da319 N°6500 - Fix user request approval freezing in the portal due to "Cannot read properties of undefined (reading 'validation')" 2023-07-04 21:36:34 +02:00
bdalsass
9d38b4d1d6 N°6043 - Booking: Add prerequisites in iTop core - CRUD extensibility (#520) 2023-07-04 16:22:53 +02:00
Stephen Abello
40dc3deabb N°6495 - Fix strlen crashing with null value in AttributePassword 2023-07-04 13:56:31 +02:00
Stephen Abello
56688fbbff Merge branch 'support/3.0' into develop 2023-07-03 14:30:20 +02:00
Stephen Abello
764a170cd0 N°6483 - Security hardening 2023-07-03 14:29:45 +02:00
Stephen Abello
c30da57818 N°6488 - Fix object keyboard shortcut not working anymore due to summary cards stealing its shortcuts 2023-07-03 11:50:12 +02:00
Pierre Goiffon
e36b9eb664 N°6414 Make AbstractSimpleField::Validate final
We don't have any overrides remaining anymore
2023-07-03 11:30:04 +02:00
Pierre Goiffon
6cc2d49cd5 N°6414 Move existing AbstractSimpleField::Validate impl to custom validators
- LinkedSetField
- SelectObjectField
- MultipleChoicesField (warning this hierarchy contains non multiple value fields like SelectField !)

Also change AbstractValidator::Validate signature : now we are returning an array of error messages, so that we can return multiple ones
2023-07-03 11:30:04 +02:00
Pierre Goiffon
d085f15b6d N°6414 Separate SubFormField and Scalar fields
This will help to set methods as final, as SubFormField is often the sole one to have a real custom impl
2023-07-03 11:30:04 +02:00
Pierre Goiffon
6606af71ff N°6414 Validator refactoring
New AbstractValidator class, with new method Validate
All existing validators are now children of AbstractRegexpValidator
Handle validators JS counterparts in renderers : only regexp validators are implemented client side
2023-07-03 11:30:04 +02:00
Pierre Goiffon
52049b7837 N°6414 Disable Field validation for untouched attcodes 2023-07-03 11:30:04 +02:00
Pierre Goiffon
f40674ec85 N°6414 Replace SelectObjectField::VerifyCurrentValue by ResetCurrentValueIfNotAmongAllowedValues 2023-07-03 11:30:04 +02:00
bdalsass
b46d4db8ff N°6476 - Revert datamodel / compiler changes from N°5563 (#515) 2023-06-29 15:11:30 +02:00
Molkobain
12dbd0ed3d N°6482 - Fix URP_UserProfile classes not flagged as link classes so they can benefit of the same mechanisms (eg. events) 2023-06-29 09:59:09 +02:00
Lars Kaltefleiter
0d9f33ec4c 🌐 Updated german translations for 3.1 (#511) 2023-06-29 09:05:27 +02:00
Molkobain
71704404d0 N°5945 - Apply same explicit value comparison as previous commit 2023-06-28 10:19:07 +02:00
Molkobain
3c8bea8921 N°5945 - Fix "menu" option in dashlet list having no effect since relations refactoring 2023-06-28 10:11:51 +02:00
Molkobain
b755f65a8a Improve PHPDoc 2023-06-27 23:35:57 +02:00
Molkobain
3bd3081359 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	synchro/synchro_exec.php
2023-06-27 18:19:48 +02:00
Molkobain
5a33fb7a6a N°6474 - Fix regression introduced by 70e6f707 2023-06-27 18:16:55 +02:00
Stephen Abello
29a90fe244 N°6474 - Fix undefined variable due to failed merge + refactoring 2023-06-27 16:55:25 +02:00
Molkobain
011116029b N°6478 - Add "edit_mode" node for AttributeLinkedSetIndirect (n:n) for future usages 2023-06-27 16:41:01 +02:00
Eric Espie
f95cb50002 N°6442 - comment 2023-06-27 15:49:39 +02:00
Eric Espie
2c8db92504 N°6442 - Error in Database integrity due to "translate placeholder in notification" 2023-06-27 15:23:28 +02:00
Eric Espie
98d4fa4331 N°6384 - fix typo 2023-06-27 14:13:33 +02:00
Anne-Catherine
4a39a5e51d N°6342 - Fix empty list when configuring a list adding a log attribute (#498)
* N°6342 - Empty list when configuring a list adding a case log attribute

* Update js/dataTables.settings.js

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-06-27 13:45:04 +02:00
Eric Espie
8373a03d82 N°6408 - CRUD fix tests 2023-06-27 12:11:36 +02:00
Eric Espie
befbe2dfa9 N°6408 - CRUD fix tests 2023-06-27 11:52:23 +02:00
Eric Espie
86ca7fcb7a N°6408 - CRUD : rework on DBUpdate reentrancy 2023-06-27 11:37:10 +02:00
Stephen Abello
ccf1d7ecfb N°6446 Fix OAuth landing page missing jQuery library needed in ajax calls 2023-06-27 11:24:06 +02:00
Stephen Abello
178e82a039 N°6446 Replace jQuery with vanilla JS in ajax dictionaries 2023-06-27 11:24:06 +02:00
Stephen Abello
452cc77168 N°6446 Prevent loaded js file constant from being missing if an ajax call is made from a page that's not a Webpage 2023-06-27 11:24:05 +02:00
Molkobain
7fc46fbc50 N°6472 - Fix empty tables in "Progress" tab of a survey 2023-06-27 10:36:30 +02:00
Benjamin Dalsass
f338d3bdc8 N°6326 - Magnifier pop-up search from ExtKey in Link edit in pop-up, fails 2023-06-27 09:42:12 +02:00
Benjamin Dalsass
d41e54bf34 N°6473 - Do not track QueryOQL statistic values 2023-06-27 08:42:44 +02:00
Eric Espie
aa75456e6e N°6271 - Fix drop-down list not refreshed when adding an external key with a friendlyname built on an external key 2023-06-27 08:41:20 +02:00
Eric Espie
db6e4137b1 N°6271 - Fix drop-down list not refreshed when adding an external key with a friendlyname built on an external key 2023-06-26 18:00:04 +02:00
Benjamin Dalsass
a206af1813 N°6005 - CSV import : import an object and a linkset fails
- Restore old get as html for linked set with display style tab
2023-06-26 15:11:41 +02:00
Eric Espie
77868e356b N°6408 - CRUD : Issue on DBlinkchange event when delete et create 2023-06-26 11:34:00 +02:00
Eric Espie
60e302162c N°6408 - CRUD : Issue on DBlinkchange event when delete et create 2023-06-26 11:31:53 +02:00
Eric Espie
f36c8c5cdd N°6408 - CRUD : Issue on DBlinkchange event when delete et create 2023-06-26 11:17:20 +02:00
Molkobain
d062ba8196 N°6456 - Code convention 2023-06-24 11:49:43 +02:00
Eric Espie
c3469e43bc N°6408 - Add logs, changed modification test of current object and prevent calling OnDBUpdate() when object is not modified 2023-06-23 17:02:31 +02:00
acognet
68ee12bf5e N°6456 - Empty list when configuring a list and cancel 2023-06-23 09:35:42 +02:00
Purple Grape
47400d94ba N°6468 update zh-cn dict (#512) 2023-06-23 09:03:49 +02:00
Molkobain
b642dbe3d6 N°6389 - Fix SQL error when saving an object with an emptied blob attribute 2023-06-22 20:43:56 +02:00
Molkobain
83564849e0 N°6356 - Search, SELECT on class with a single field display the count and an empty list 2023-06-22 20:18:18 +02:00
Molkobain
3e73038709 N°6380 - Fix exception "Failed to parse time string" with check_backup.php script 2023-06-22 13:51:01 +02:00
vdumas
dcfefc1588 N°1350 - Audit domains: add uniqueness rule on n:n with Category 2023-06-22 11:19:53 +02:00
vdumas
4db092ea52 N°6370 - Replace Audit Category menu by a dashboard only in module 2023-06-22 11:05:01 +02:00
Molkobain
96f1bd3646 N°6462 - Sort \AttributeApplicationLanguage alphabetically (eg. "Language" in ActionEmail class) 2023-06-21 18:33:12 +02:00
Molkobain
f889c53d71 N°6359 - Fix JS crash due to new version trying to detect MSIE browser through a dependency that we don't have.
It's far from perfect but as mentioned in the previous commit, we don't have much choices for now
2023-06-21 17:21:40 +02:00
Molkobain
814916fb21 N°6362 - Fix export of linkset tab not working due to JS error 2023-06-21 17:04:55 +02:00
Eric Espie
9e667c968e N°6408 - CRUD : Issue on DBlinkchange event when delete et create 2023-06-21 16:41:54 +02:00
Molkobain
a8a3385969 N°6460 - Sort \AttributeClassAttCodeSet alphabetically (eg. "Target fields" in Trigger class) 2023-06-21 16:06:22 +02:00
Molkobain
87e04547bd N°6448 - Restore sort order for \AttributeClass and \AttributeSetEnumPadded (eg. "Target class" and "Context" in DataSynchronisation and Trigger classes) 2023-06-21 15:58:11 +02:00
Eric Espie
6fe41796d9 N°5909 - Fix iApplicationExtension not called when attachment is added 2023-06-21 15:15:59 +02:00
Eric Espie
99a4e5e861 N°5909 - Fix iApplicationExtension not called when attachment is added 2023-06-21 14:39:04 +02:00
Molkobain
3d3a751072 N°6200 - Delete old redundant turkish dict file 2023-06-21 13:13:57 +02:00
Molkobain
216a1b95b1 N°6405 - Fix XML displayed corrupted when using \DesignerLongTextField due to usage of \utils::EscapeHtml() without double encoding 2023-06-21 11:33:18 +02:00
Benjamin Dalsass
2074a0fa0d N°5305 - CSV import ergonomy for SaaS
- Fix ext key displayable value
2023-06-21 11:21:40 +02:00
Eric Espie
bfd078c2a3 N°6281 - Rest API core/create key value is no more between quote 2023-06-20 17:25:13 +02:00
Benjamin Dalsass
85a879b587 N°5305 - CSV import ergonomy for SaaS
- Change checkbox error ugly font
- Improve errors messages (ambiguous, mismatch)
2023-06-20 17:21:39 +02:00
Eric Espie
9b9ba3c440 N°6281 - Rest API core/create key value is no more between quote 2023-06-20 16:58:16 +02:00
bdalsass
4d43d83b95 N°6000 - CSV Import with quote no more handled (#508) 2023-06-20 16:39:56 +02:00
Benjamin Dalsass
63487226a9 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	core/csvbulkexport.class.inc.php
2023-06-20 10:24:03 +02:00
vdumas
7b5ef52865 N°6422 - Improve french dictionary 2023-06-20 08:37:17 +02:00
Molkobain
4550dfb1d4 N°5388 - PHP 8.2: Fix dynamic property declaration 2023-06-19 16:27:45 +02:00
Molkobain
1a5ffc23ce N°6251 - Fix dynamic property declaration 2023-06-19 14:47:06 +02:00
vdumas
3a9198bf1c N°6422 - Finalize french dictionaries 2023-06-16 16:39:05 +02:00
vdumas
2242a80808 N°6428 - Portal : Hide role attribute in linkset Contact/Ticket 2023-06-16 16:19:04 +02:00
Benjamin Dalsass
86148ecf57 N°6415 - Linked set shown as tag set in csv import is ugly 2023-06-16 16:06:35 +02:00
Benjamin Dalsass
2c4a7c5a77 # Conflicts:
#	core/csvbulkexport.class.inc.php
2023-06-16 15:57:58 +02:00
Benjamin Dalsass
2cca57c7fa N°6431 - CSV bulk export text delimiter option not initialized correctly 2023-06-16 15:50:28 +02:00
Timothee
e52fa28089 Merge remote-tracking branch 'origin/release/3.1.0-beta' into develop 2023-06-16 15:21:51 +02:00
Pierre Goiffon
42dfa2c066 Merge branch 'develop' into release/3.1.0-beta 2023-06-16 10:47:37 +02:00
odain-cbd
49d3f4c2b2 N°6047 - Make iTop scripts work by HTTP via token with specific scopes (#494)
* N°6047 - Make iTop scripts work by HTTP via token with specific scopes

* Update core/contexttag.class.inc.php

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-06-15 15:36:35 +02:00
odain
d91eda1343 ci: enhance AddProfileToUser to work with any User 2023-06-15 15:07:09 +02:00
Timothee
e075bf8e89 Licenses update 2023-06-15 10:47:16 +02:00
Molkobain
40355c9442 N°6426 - Fix RenderAllUiBlocks test page not displaying 2023-06-15 10:20:41 +02:00
Timothee
f548dc2898 Dictionaries update for 3.1 beta 2023-06-15 09:47:51 +02:00
Benjamin Dalsass
0ece69112a N°6416 - In user portal, empty ext key drop down doesn't display error message anymore
Fix wrong jquery element resulting in select all form field instead of links elements
2023-06-15 08:37:03 +02:00
Molkobain
587b6a7148 N°6179 - Add visual test 2023-06-14 19:25:02 +02:00
Pierre Goiffon
baa4641795 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-06-14 19:03:02 +02:00
Pierre Goiffon
52820925b1 N°4725 DeprecatedCallsLog::NotifyDeprecatedFile remove throw ConfigException
Those exceptions are handled silently since 3.0.1
2023-06-14 19:01:50 +02:00
Benjamin Dalsass
578f2dfa8e N°3190 - Edit n:n LinkedSetIndirect in object details using a tagset-like widget
Add security if add option button js code isn't provided
2023-06-14 14:50:17 +02:00
Anne-Catherine
5185f721bd N°5594 - Dashboard Bug with Group by (table) - iTop 3.0 (#466) 2023-06-14 11:06:25 +02:00
Pierre Goiffon
add038d649 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-06-14 10:28:46 +02:00
Pierre Goiffon
1824111de8 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-06-14 10:28:30 +02:00
Pierre Goiffon
5a0b5364d6 N°4698 setup/phpinfo.php : if no iTop installation then display a proper message instead of an exception (#265) 2023-06-14 10:18:38 +02:00
Pierre Goiffon
76eed2eba0 N°6098 updateLicenses script : check availability of the required JQ command (#458)
This packaging script requires both bash and the JQ command when running on Windows.
If the later isn't available, it will run without throwing an error...

With this change the script will now check directly at launch for the JQ command availability, and exit in error if it isn't.
2023-06-14 10:17:00 +02:00
Anne-Catherine
c2309876fb N°6239 - Portail: Fix error message when trying to display object with missing icon (#485) 2023-06-14 09:42:43 +02:00
Molkobain
1d12ebd733 N°6398 - Improve PHPDoc 2023-06-14 09:38:05 +02:00
Eric Espie
5578147002 N°5906 - CRUD Event - fire event EVENT_DB_LINKS_CHANGED when an n-n link is created/updated/deleted 2023-06-14 09:32:19 +02:00
Eric Espie
a152c5db66 N°6351 - code hardening 2023-06-14 09:11:36 +02:00
Eric Espie
5d5589dd64 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-06-14 09:09:12 +02:00
Eric Espie
1ec671ef61 N°6351 - code hardening 2023-06-14 09:08:42 +02:00
vdumas
04e0950efb N°1350 - Audit selection: use absolute url 2023-06-14 08:13:48 +02:00
Molkobain
79d6cadfde N°4106 - Fix typo in the PHPDoc thanks to @piRGoif 2023-06-13 22:34:50 +02:00
Tarjányi Csaba
a1daef70e2 N°6419 - Update hungarian translations thanks to @tacsaby (#472)
* Update hu.dictionary.itop.ui.php

Some corrections

* Revert formatting changes

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-06-13 18:00:21 +02:00
Molkobain
eb7afd53a1 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	datamodels/2.x/itop-config-mgmt/dictionaries/nl.dict.itop-config-mgmt.php
2023-06-13 17:50:57 +02:00
Thomas Casteleyn
b8199a6e2c N°6418 - Fix dutch translations on impact relation view (#499)
* Fix author and copyrights
* Correct NL dict for impact relation view
2023-06-13 17:48:36 +02:00
Purple Grape
68e1f359d3 N°6417 - Update chinese translations thanks to @purplegrape (#500) 2023-06-13 17:36:27 +02:00
Eric Espie
e574fe3dc5 N°5906 - CRUD Event - fire event EVENT_DB_LINKS_CHANGED when an n-n link is created/updated/deleted 2023-06-13 17:12:35 +02:00
Molkobain
0fbef99875 N°4010 - Remove unnecessary single quote escape 2023-06-13 15:59:16 +02:00
Denis
9a5ad6681d Typo in en.dictionary.itop.core.php 2023-06-13 13:44:20 +02:00
Pierre Goiffon
d0cad4d829 N°1150 Fix regression : cannot add caselog in the user portal
Was caused by field value check added in MultipleChoicesField and SelectObjectField in b71cd218

With this commit the checks are now only done if we have a ContextTag::TAG_REST
2023-06-13 12:34:46 +02:00
Pierre Goiffon
b3ef634d21 N°1150 SelectObjectField : factorize DBObjectSet retrieval between Validate and VerifyCurrentValue 2023-06-13 12:34:46 +02:00
Molkobain
5915b2cc01 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-06-13 11:43:11 +02:00
Molkobain
9f38eec40a N°4106 - Reword PHPDoc to avoid confusion as the @internal was not accurate 2023-06-13 11:42:28 +02:00
Benjamin Dalsass
b2c2d5eaea N°6398 - Portal: Allow linkset visible attributes to be limited to attributes defined in a zlist 2023-06-13 10:51:05 +02:00
Denis
fed149dc66 N°918 - Translate placeholders in notifications (#506)
- Localization of date & time formats
- Use of DataLocalizer (if present)
- All placeholders can be used in the uploaded HTML template as well as in the notification "message"
2023-06-13 09:51:32 +02:00
Molkobain
58a20e9a11 N°1646 - Fix crash in case no value return (real fix about to be discussed) 2023-06-13 09:36:30 +02:00
Benjamin Dalsass
0a45039c5c N°6005 - CSV import : import an object and a linkset fails 2023-06-13 08:14:34 +02:00
Benjamin Dalsass
465388b5e3 N°803 - Allow display & edition of attributes on n:n relations on Portal
- Issue when selecting links rows with checkbox
- Change linked set field validation to prevent checking host id -1
2023-06-12 17:26:34 +02:00
vdumas
ad1836d028 N°1350 - Audit: propose domains selection as default 2023-06-12 14:15:57 +02:00
Eric Espie
1655674543 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-06-12 11:39:39 +02:00
Eric Espie
5f5537b8b9 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	js/breadcrumb.js
2023-06-12 11:39:13 +02:00
Eric Espie
72716b7ec8 N°6396 - Protect URL display 2023-06-12 11:36:51 +02:00
Eric Espie
5084f7fccb N°6396 - Protect URL display 2023-06-12 11:33:33 +02:00
Molkobain
bd96247f97 N°1345 - Fix typo in conf. param. description 2023-06-12 09:56:57 +02:00
vdumas
01fb3466c8 N°1350 - Audit: propose domains selection as default 2023-06-12 09:22:34 +02:00
vdumas
b5a637fa11 N°1350 - Audit: Title, breadcrumb, back button and link to configure 2023-06-09 20:13:44 +02:00
Molkobain
4d6236cbea N°1345 - Restrict unit test iTop community only as it is based on the standard DM (thanks @piRGoif) 2023-06-09 17:55:54 +02:00
vdumas
2829068499 N°6381 - Add rank on Enums of default DataModel 2023-06-09 17:51:00 +02:00
Molkobain
8304e4df00 N°6394 - Force header statistics to sort [meta]enums pills according to the datamodel instead of the selection order 2023-06-09 17:49:20 +02:00
Pierre Goiffon
2824718189 N°6376 Fix french labels (requête => demande) 2023-06-09 16:35:21 +02:00
Pierre Goiffon
2d866b8f47 N°6320 Fix GetSelectFilterTest::testGetSelectFilter crashing when authent-token module installed 2023-06-09 15:47:19 +02:00
vdumas
36ad278b1f N°2199 - Request history tables without the Admin profile 2023-06-09 13:05:05 +02:00
Molkobain
74fec312d8 N°1345 - Add unit test 2023-06-09 13:01:59 +02:00
vdumas
05559a944b N°6381 - Add capital letter on enum values so rank by label works 2023-06-09 12:53:42 +02:00
Molkobain
508647fe0b N°1345 - Add possibility to sort transitions automatically 2023-06-09 12:49:42 +02:00
vdumas
c04c7e06ea N°6381 - Add capital letter on enum values so rank by label works 2023-06-09 11:48:14 +02:00
Benjamin Dalsass
2041a6ad3f N°803 - Allow display & edition of attributes on n:n relations on Portal
- Remove column mandatory information when object is in view mode
2023-06-09 10:40:24 +02:00
Benjamin Dalsass
c486aea299 N°803 - Allow display & edition of attributes on n:n relations on Portal
- Remove input when object is in view mode
- Improve form errors handling
- Prevent row selection when clicking input 
- Attach date time picker to table instead of input to prevent popup truncating (popup will be visible but not aligned on the input)
2023-06-09 10:34:19 +02:00
Eric Espie
1b7529fcb9 N°6384 - Flag LinkedSet to specify that it has CheckToWrite constraint (more specific parameter name) 2023-06-09 10:21:47 +02:00
Eric Espie
191742c2a9 N°6384 - Flag LinkedSet to specify that it has CheckToWrite constraint (more specific parameter name) 2023-06-09 10:17:22 +02:00
vdumas
d985ff860d N°2199 - Request history tables without the Admin profile 2023-06-09 09:20:41 +02:00
Molkobain
ae65856797 N°1646 - Fix unit tests to reflect new enum sort 2023-06-09 08:34:09 +02:00
vdumas
0fb770783d N°6381 - Add rank on Enums of default DataModel 2023-06-08 18:56:45 +02:00
Anne-Catherine
cc8c6ac027 N°3213 - Order transition attributes like in the "details" zlist instead of reordering them regarding the dependencies (#306)
N°3213 - Order transition attributes like in the "details" zlist instead of reordering them regarding the dependencies (#306)

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-06-08 17:29:53 +02:00
Eric Espie
99e84e86cd N°6384 - fix typo 2023-06-08 17:19:45 +02:00
Eric Espie
7ebf5a6dd4 N°6384 - Flag LinkedSet (Indirect) when CheckToWrite must be requested to Hosted Object 2023-06-08 17:07:01 +02:00
Vincent Dumas
55009b53e2 N°2199 - Request history tables without the Admin profile (#497) 2023-06-08 16:50:06 +02:00
vdumas
42304fb489 N°2199 - New History Group for access to other than Administrator 2023-06-08 16:38:09 +02:00
Eric Espie
f4fcfee03b N°6359 - Add comment 2023-06-08 15:52:08 +02:00
Eric Espie
a6e56e37c9 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	tests/php-unit-tests/ItopDataTestCase.php
2023-06-08 15:11:39 +02:00
Eric Espie
e288af4ddb Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	tests/setup_params/default-params.xml
2023-06-08 14:33:54 +02:00
Eric Espie
4f999de844 N°6359 - ⬆️ Update jQuery BBQ (from https://github.com/cee-chen/jquery-bbq) 2023-06-08 14:30:09 +02:00
denis.flaven@combodo.com
1809ea647f 💄 More explicit icon for the 'Add criterion' button 2023-06-08 11:55:08 +02:00
Anne-Catherine
f47133bc28 N°6125 - Issue with GetAttributeFlags and GetInitialStateAttributeFlags within iTop 3.0.2 (#474) 2023-06-08 11:12:22 +02:00
Pierre Goiffon
14d3eb6624 N°6238 Security hardening 2023-06-08 09:31:15 +02:00
odain
ea49c0a87c enable authent-cas in ci 2023-06-07 21:44:17 +02:00
odain
6cc971849b ci: enhance AddProfile 2023-06-07 21:44:00 +02:00
vdumas
68a1c0f0cb N°1350 - Audit result and audit configuration: add navigation from one to the other 2023-06-07 20:17:39 +02:00
Molkobain
edba3270a2 N°1646 - Update standard datamodel with enum sorted by rank 2023-06-07 19:53:12 +02:00
Molkobain
4d5d149079 N°1646 - Add unit tests 2023-06-07 19:53:12 +02:00
Molkobain
bac6cbc1d8 N°1646 - Add possibility to sort Attribute[Meta]Enum either by code (default), rank or label 2023-06-07 19:53:11 +02:00
Eric Espie
f886c78f24 Merge remote-tracking branch 'origin/support/3.0' into develop -> fix unit tests 2023-06-07 17:49:52 +02:00
Eric Espie
62ed5adbc5 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	application/loginwebpage.class.inc.php
#	composer.json
#	lib/composer/installed.json
#	lib/guzzlehttp/psr7/.github/workflows/ci.yml
#	lib/guzzlehttp/psr7/.github/workflows/integration.yml
#	lib/guzzlehttp/psr7/.github/workflows/static.yml
#	lib/guzzlehttp/psr7/CHANGELOG.md
#	lib/guzzlehttp/psr7/composer.json
#	lib/guzzlehttp/psr7/src/MessageTrait.php
#	tests/php-unit-tests/ItopDataTestCase.php
2023-06-07 17:45:50 +02:00
Eric Espie
0fbd41a884 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 -> fix syntax error 2023-06-07 17:30:39 +02:00
Eric Espie
70e6f707c4 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 -> composer autoloader 2023-06-07 17:23:47 +02:00
Eric Espie
e76ada641f Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	application/loginbasic.class.inc.php
#	application/loginexternal.class.inc.php
#	application/loginform.class.inc.php
#	application/loginurl.class.inc.php
#	application/loginwebpage.class.inc.php
#	composer.lock
#	datamodels/2.x/authent-cas/src/CASLoginExtension.php
#	lib/composer/autoload_real.php
#	lib/composer/installed.php
#	synchro/synchro_exec.php
#	synchro/synchro_import.php
#	tests/php-unit-tests/unitary-tests/application/utilsTest.php
2023-06-07 17:21:09 +02:00
Pierre Goiffon
2405810864 N°6238 Security hardening 2023-06-07 16:45:35 +02:00
Denis
120b8b55ed Typo 2023-06-07 15:53:52 +02:00
Eric Espie
fff46d99fc N°6358 - Login REST API - renamed test 2023-06-07 15:31:51 +02:00
Benjamin Dalsass
11c147574a N°803 - Allow display & edition of attributes on n:n relations on Portal 2023-06-07 15:30:08 +02:00
odain
3a891f707c ci: enhance AddProfile test method to work with any User (not only UserLocal) 2023-06-07 15:06:28 +02:00
odain
8b6ea43ebe N°6358 - Login REST API - fix cas + add tests 2023-06-07 15:05:32 +02:00
Eric Espie
90cf7502e8 N°6358 - Login REST API 2023-06-07 10:09:30 +02:00
Eric Espie
c596fa2967 N°6358 - Login API REST 2023-06-07 09:17:24 +02:00
Timothee
a45177410e N°6350 - Fixing phpunit test 2023-06-06 16:47:06 +02:00
vdumas
d778203f99 N°6370 - Replace Audit Category menu by a dashboard 2023-06-05 18:32:57 +02:00
vdumas
652a9f6e40 N°6370 - Replace Audit Category menu by a dashboard 2023-06-05 17:58:14 +02:00
Benjamin Dalsass
8eb1053daa N°803 - Allow display & edition of attributes on n:n relations on Portal 2023-06-05 16:19:06 +02:00
Eric Espie
519751faa1 N°6348 - Add unit test 2023-06-05 14:50:37 +02:00
Eric Espie
b99ff5e84a ✏️ typo 2023-06-05 08:22:29 +02:00
Benjamin Dalsass
4264123ef6 N°6219 - 1:n Read: tooltip, modal title and message on Add-Edit-Remove-Delete 2023-06-02 08:08:29 +02:00
vdumas
eb1ff95437 N°6347 - 1:n Add nice french dico entry on standard 1:n relationship 2023-06-01 17:12:47 +02:00
Eric Espie
cb7cbe9297 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	pages/UI.php
#	tests/php-unit-tests/unitary-tests/application/utilsTest.php
2023-06-01 16:59:59 +02:00
Eric Espie
b8f61362f5 N°6348 - Hardening code 2023-06-01 16:44:40 +02:00
Eric Espie
e3ba826e5d N°6349 - Hardening code 2023-06-01 16:36:56 +02:00
Eric Espie
17d22219d2 N°6350 - Fix unit tests 2023-06-01 16:30:09 +02:00
Eric Espie
a49025f371 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	application/utils.inc.php
2023-06-01 16:04:52 +02:00
Eric Espie
9e96ea2873 N°6350 - code hardening 2023-06-01 15:35:56 +02:00
Eric Espie
1172159745 N°6351 - code hardening 2023-06-01 15:12:50 +02:00
vdumas
c2fb0150b0 N°6347 - 1:n Add nice french dico entry on standard 1:n relationship 2023-06-01 11:56:34 +02:00
Molkobain
41bce359a0 N°6307 - Fix mentions not working in edition due to CRUD changes 2023-05-31 17:45:20 +02:00
Eric Espie
3e7c48d5c6 N°6357 - Prevent entering same password on change user password 2023-05-31 17:12:26 +02:00
vdumas
5e3b813da7 N°6339 - n:n Add nice french dico entry 2023-05-31 16:50:54 +02:00
Eric Espie
d680369733 N°5726 - loose data localizer language in console after impersonating a user with a different langage 2023-05-31 16:45:20 +02:00
vdumas
0af8f65a11 N°6339 - n:n Add nice french dico entry 2023-05-31 16:33:48 +02:00
Molkobain
f41879f0b1 N°6307 - Fix unappropriate confirm message when updating log on object edition
Proper fix as 345a8371 was temporary
2023-05-31 16:26:26 +02:00
Molkobain
eec9b027ac Revert "N°6307 Fix alert when caselog filled and submitting form"
This reverts commit 4b9b0a91a9.
2023-05-31 16:26:26 +02:00
Eric Espie
5f359b2b61 N°6151 - Allow to select classes on union outside the already selected classes 2023-05-31 14:40:13 +02:00
Benjamin Dalsass
31a96d24dd N°6219 - 1:n Read: tooltip, modal title and message on Add-Edit-Remove-Delete 2023-05-31 10:10:02 +02:00
Molkobain
d07bce3063 N°6352 - Portal: Fix autoloaders 2023-05-30 16:29:07 +02:00
Molkobain
c9beba0cea N°6352 - Portal: Fix "Combodo\iTop\Portal\Controller\AggregatePageBrickController" has no container set due to Symfony 5.4 migration 2023-05-30 16:09:58 +02:00
vdumas
4d9b054430 N°2639 - Add Tooltips EN/FR dico entries for Data Synchro 2023-05-30 15:42:42 +02:00
Eric Espie
2c4c69fcfc Fixed regression due to forcing camel case names in operation methods, now allow both camel case and snake case 2023-05-30 15:25:00 +02:00
Eric Espie
173960e717 N°6324 - CRUD Event for one time treatment before creation and before update 2023-05-30 11:54:15 +02:00
vdumas
79585c7128 N°6339 - n:n Add nice french dico entry on standard lnk 2023-05-30 11:34:33 +02:00
Eric Espie
c3bb995407 N°6324 - CRUD Event for one time treatment before creation and before update 2023-05-30 11:29:24 +02:00
vdumas
e3dd805f24 N°6338 - Add silo (org_id) and location on all final Interfaces, LogicalVolume & NASFileSystem 2023-05-26 13:49:49 +02:00
acognet
649457bfc2 N°6308 - issue on displaying a userrequest list with Service_details fields with PHP 8 2023-05-26 00:25:42 +02:00
vdumas
e933807b1c N°6203 - Complementary name on Ticket sub-classes 2023-05-24 18:41:02 +02:00
vdumas
52fd333922 N°6339 - n:n Add nice french dico entry on standard lnk 2023-05-24 18:17:58 +02:00
vdumas
91a9997480 N°6203 - Overcard & complementary on Ticket classes 2023-05-24 15:39:22 +02:00
vdumas
906ad70156 N°6331 - Add Service tab in Provider Contract and add fieldsets 2023-05-24 13:07:52 +02:00
Pierre Goiffon
0508608e78 N°6322 Delegate \AttributeCustomFields::CheckValue to the handler
This allows to move template_id checks from BuildForm (which is called in the UI to construct the form also) to a dedicated method only called for validation
2023-05-24 09:28:26 +02:00
vdumas
63fcf4e6f9 N°6203 - Add summary to CMDB classes 2023-05-23 20:00:57 +02:00
vdumas
f805c2f834 N°6203 - Add complementary name to CMDB classes 2023-05-22 18:30:33 +02:00
vdumas
976c9c1523 N°6311 - Add a Log on User Local, LDAP and External 2023-05-20 18:54:10 +02:00
vdumas
22a14a2de2 N°6323 - n:n Add filter on lnk ExtKey aligned to PrefillSearchForm 2023-05-20 17:21:10 +02:00
vdumas
bd04b2a24c N°6203 - Standard DM overcard, complementary name 2023-05-20 17:20:16 +02:00
Eric Espie
0188108a68 Add EVENT_DB_BEFORE_CREATE and EVENT_DB_BEFORE_UPDATE events in CRUD calls 2023-05-17 10:16:18 +02:00
Pierre Goiffon
4038d4d925 N°6314 N°1150 Fix cannot reopen or close a UR in the user portal
Since introduction of MultipleChoicesField and SelectObjectField value checks with b71cd218, when a portal user was trying to do an action on a resolved UR the agent_id field was rejected. The reason for the rejection is the corresponding SelectObjectField query is containing the portal user scopes (query generated in ObjectFormManager::Build), and of course the agent_id set isn't in this scope...

As a workaround we are disabling those checks for read only fields (values are set server side and possibly in a different context than the portal user)
2023-05-16 17:54:24 +02:00
acognet
ea88aa85df N°4517 - PHP 8.1 compatibility : Replace htmlentities(***, ENT_QUOTES,'UTF-8') with utils::EscapeHtml 2023-05-16 17:45:58 +02:00
Eric Espie
8f2814a590 Undo... Move event declaration where the object is defined 2023-05-16 15:59:50 +02:00
Eric Espie
ad25d8823f Add EVENT_DB_BEFORE_CREATE and EVENT_DB_BEFORE_UPDATE events in CRUD calls 2023-05-16 15:52:02 +02:00
Eric Espie
59cd489e72 N°6252 - Make designer itop 3.1 compatible - Fix deprecated calls of opendir() with null value 2023-05-16 14:06:01 +02:00
Pierre Goiffon
4b9b0a91a9 N°6307 Fix alert when caselog filled and submitting form
Regression was introduced in 8dc7d68b
Temporary fix, as the alert is caused by activity-panel.js which is disabled only if another handler is registered before 🤯 As a temporary fix I add an empty handler at the same place as the one that was there before, but we need a better way to disable activity-panel.js handler !
2023-05-16 12:14:26 +02:00
vdumas
a21def5329 N°4010 - Configuration file overwritten - fine tune messages 2023-05-16 12:06:26 +02:00
vdumas
55f8efe0db N°4010 - Configuration file overwritten - fine tune messages 2023-05-16 12:06:26 +02:00
Eric Espie
0cc28b42b2 Move EventService init to Metamodel::Startup() for unit tests 2023-05-16 10:23:01 +02:00
Anne-Catherine
a98fab8f66 N°6240 - Improve display of picture in read or edit mode (#484) 2023-05-15 16:12:26 +02:00
vdumas
bf21481bf6 N°6212 - Replace 'case logs' by 'logs' in naming of ActionEmail for TriggerOnObjectMention 2023-05-15 12:32:31 +02:00
Eric Espie
cda51c67c5 Fix warning 2023-05-15 11:46:25 +02:00
Eric Espie
64264eb7ed Move event declaration where the object is defined (Fix undefined constant) 2023-05-15 10:42:58 +02:00
Eric Espie
940118967c Move event declaration where the object is defined 2023-05-15 09:33:53 +02:00
vdumas
a647c235c3 N°1350 - Audit domains: add description as complementary name 2023-05-15 09:27:02 +02:00
vdumas
752ac5a469 N°5971 - Allow changing the Org of a Person having Portal User without allowed orgs 2023-05-15 08:54:19 +02:00
vdumas
c61faf453c N°5971 - Prevent changing the Org of a Person having Portal User without the new Org allowed 2023-05-12 18:24:46 +02:00
Pierre Goiffon
b71cd2182f N°1150 Add value checks in MultipleChoicesField and SelectObjectField
Now current value is checked against list of possible values
2023-05-12 10:59:33 +02:00
Pierre Goiffon
7c594db4b9 N°1150 AtributeCustomFields : FromJSONToValue is now delegated to the handler
Method was always returning null before
2023-05-12 10:58:43 +02:00
Pierre Goiffon
cca27674d0 ItopDataTestCase : extract UR params to a method to allow overrides 2023-05-12 10:58:43 +02:00
Eric Espie
f426626b9a N°6299 - DBUpdate regression in 3.1 - Fix unit tests 2023-05-12 09:09:39 +02:00
Eric Espie
5dc80f31f5 N°6299 - DBUpdate regression in 3.1 when setting a field with same value in the concerned object 2023-05-11 16:16:49 +02:00
denis.flaven@combodo.com
7fe565c4fb Merge branch 'feature/6133-add-extra-files-to-backup-and-restore' into develop 2023-05-11 14:41:02 +02:00
denis.flaven@combodo.com
08ebac1b5c Fix PR remarks 2023-05-10 17:42:50 +02:00
vdumas
380723056a N°5559 - Obsolete replica locks object edition - fix default param value 2023-05-10 10:24:12 +02:00
Stephen Abello
2f20cf52f3 N°6219 Fix issues in dictionary entries 2023-05-10 10:08:18 +02:00
Stephen Abello
5219541b74 N°6154 Fix some classes names hardcoded in dict entries and calls MetaModel GetName instead of hardcoded dict entry 2023-05-10 10:03:02 +02:00
Pierre Goiffon
4695511b46 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-05-09 15:37:05 +02:00
odain
0b5bf7bf6f N°6299 - DBUpdate regression in 3.1 when setting a field with same value in the concerned object - fix temporarly ci until fix comes 2023-05-09 14:51:26 +02:00
Pierre Goiffon
cdcc069099 Fix typo in exception message
Regression introduced in fe179079 in support/3.0 branch and upwards
2023-05-09 14:02:02 +02:00
denis.flaven@combodo.com
001194835f Config file may not exist during unattended setup/backup 2023-04-06 10:50:47 +02:00
denis.flaven@combodo.com
955aefc05b Ignore non-existing files. 2023-04-04 14:26:31 +02:00
denis.flaven@combodo.com
034ca26d01 Do NOT backup unsafe files. 2023-04-03 11:54:01 +02:00
denis.flaven@combodo.com
d03bd706e2 Support of extra files (configurable) in the backup. 2023-03-29 16:53:38 +02:00
denis.flaven@combodo.com
d3f8e1c472 Revert "N°5619 - Hide newsroom menu when no provider"
This reverts commit 647b669eb9.
2022-11-14 18:28:18 +01:00
denis.flaven@combodo.com
647b669eb9 N°5619 - Hide newsroom menu when no provider 2022-11-14 18:18:10 +01:00
929 changed files with 35615 additions and 25663 deletions

View File

@@ -19,17 +19,24 @@
* The target license file path is in `$xmlFilePath`
*/
$iTopFolder = __DIR__ . "/../../" ;
$xmlFilePath = $iTopFolder . "setup/licenses/community-licenses.xml";
$iTopFolder = __DIR__."/../../";
$xmlFilePath = $iTopFolder."setup/licenses/community-licenses.xml";
function get_scope($product_node)
{
$jqExec = shell_exec("jq -V"); // a param is mandatory otherwise the script will freeze
if ((null === $jqExec) || (false === $jqExec)) {
echo "/!\ JQ is required but cannot be launched :( \n";
echo "Check this script PHPDoc block for instructions\n";
die(-1);
}
function get_scope($product_node) {
$scope = $product_node->getAttribute("scope");
if ($scope === "")
{ //put iTop first
if ($scope === "") { //put iTop first
return "aaaaaaaaa";
}
return $scope;
}

View File

@@ -228,6 +228,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
"db_table" => "priv_urp_userprofile",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true, /** @since 3.1.0 N°6482 */
'uniqueness_rules' => array(
'no_duplicate' => array(
'attributes' => array(

View File

@@ -334,6 +334,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
"db_table" => "priv_urp_userprofile",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true, /** @since 3.1.0 N°6482 */
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();

View File

@@ -277,6 +277,7 @@ class URP_UserProfile extends UserRightsBaseClass
"db_table" => "priv_urp_userprofile",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true, /** @since 3.1.0 N°6482 */
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();

View File

@@ -238,7 +238,7 @@ class ApplicationContext
{
$aContextInputBlocks = [];
foreach ($this->aValues as $sName => $sValue) {
$aContextInputBlocks[] = InputUIBlockFactory::MakeForHidden("c[$sName]", utils::EscapeHtml($sValue));
$aContextInputBlocks[] = InputUIBlockFactory::MakeForHidden("c[$sName]", $sValue);
}
return $aContextInputBlocks;
}

View File

@@ -74,15 +74,23 @@ class AuditCategory extends cmdbAbstractObject
public function GetReportColor($iTotal, $iErrors)
{
$sResult = 'red';
if ( ($iTotal == 0) || ($iErrors / $iTotal) <= ($this->Get('ok_error_tolerance') / 100) )
{
if ( ($iTotal == 0) || ($iErrors / $iTotal) <= ($this->Get('ok_error_tolerance') / 100) ) {
$sResult = 'green';
}
else if ( ($iErrors / $iTotal) <= ($this->Get('warning_error_tolerance') / 100) )
{
} else if (($iErrors / $iTotal) <= ($this->Get('warning_error_tolerance') / 100)) {
$sResult = 'orange';
}
return $sResult;
}
public static function GetShortcutActions($sFinalClass)
{
$aShortcutActions = parent::GetShortcutActions($sFinalClass);
if (!in_array('UI:Menu:RunAudit', $aShortcutActions)) {
$aShortcutActions[] = 'UI:Menu:RunAudit';
}
return $aShortcutActions;
}
}
?>

View File

@@ -35,30 +35,42 @@ class AuditDomain extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_auditdomain",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-album.svg'),
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"complementary_name_attcode" => array('description'),
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_auditdomain",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-album.svg'),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeImage("icon", array("is_null_allowed"=>true, "depends_on"=>array(), "display_max_width"=>96, "display_max_height"=>96, "storage_max_width"=>256, "storage_max_height"=>256, "default_image"=>null, "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("categories_list", array("linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "domain_id", "ext_key_to_remote" => "category_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("name", array("description" => "Short name for this category", "allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeImage("icon", array("is_null_allowed" => true, "depends_on" => array(), "display_max_width" => 96, "display_max_height" => 96, "storage_max_width" => 256, "storage_max_height" => 256, "default_image" => null, "always_load_in_tables" => false)));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("categories_list",
array("linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "domain_id", "ext_key_to_remote" => "category_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'icon', 'categories_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description', )); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('description',)); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
}
public static function GetShortcutActions($sFinalClass)
{
$aShortcutActions = parent::GetShortcutActions($sFinalClass);
if (!in_array('UI:Menu:RunAudit', $aShortcutActions)) {
$aShortcutActions[] = 'UI:Menu:RunAudit';
}
return $aShortcutActions;
}
}
/**
@@ -74,15 +86,26 @@ class lnkAuditCategoryToAuditDomain extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array('category_id', 'domain_id'),
"db_table" => "priv_link_audit_category_domain",
"db_key_field" => "id",
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array('category_id', 'domain_id'),
"db_table" => "priv_link_audit_category_domain",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true,
"is_link" => true,
'uniqueness_rules' => array(
'no_duplicate' => array(
'attributes' => array(
0 => 'category_id',
1 => 'domain_id',
),
'filter' => '',
'disabled' => false,
'is_blocking' => true,
),
),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("targetclass" => "AuditCategory", "jointype" => '', "allowed_values" => null, "sql" => "category_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));

View File

@@ -35,23 +35,23 @@ class AuditRule extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_auditrule",
"db_key_field" => "id",
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_auditrule",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit.svg'),
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit.svg'),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeOQL("query", array("allowed_values"=>null, "sql"=>"query", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("valid_flag", array("allowed_values"=>new ValueSetEnum('true,false'), "sql"=>"valid_flag", "default_value"=>"true", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("allowed_values"=>null, "sql"=>"category_id", "targetclass"=>"AuditCategory", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", array("allowed_values"=>null, "extkey_attcode"=> 'category_id', "target_attcode"=>"name")));
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeOQL("query", array("allowed_values" => null, "sql" => "query", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("valid_flag", array("allowed_values" => new ValueSetEnum('true,false'), "sql" => "valid_flag", "default_value" => "true", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("allowed_values" => null, "sql" => "category_id", "targetclass" => "AuditCategory", "is_null_allowed" => false, "on_target_delete" => DEL_MANUAL, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", array("allowed_values" => null, "extkey_attcode" => 'category_id', "target_attcode" => "name")));
// Display lists
MetaModel::Init_SetZListItems('details', array('category_id', 'name', 'description', 'query', 'valid_flag')); // Attributes to be displayed for the complete details
@@ -60,5 +60,16 @@ class AuditRule extends cmdbAbstractObject
MetaModel::Init_SetZListItems('standard_search', array('category_id', 'name', 'description', 'valid_flag', 'query')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'category_id')); // Criteria of the advanced search form
}
public static function GetShortcutActions($sFinalClass)
{
$aShortcutActions = parent::GetShortcutActions($sFinalClass);
if (!in_array('UI:Menu:RunAudit', $aShortcutActions)) {
$aShortcutActions[] = 'UI:Menu:RunAudit';
}
return $aShortcutActions;
}
}
?>

View File

@@ -47,6 +47,7 @@ use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
use Combodo\iTop\Renderer\Console\ConsoleFormRenderer;
use Combodo\iTop\Service\Links\LinkSetDataTransformer;
use Combodo\iTop\Service\Links\LinkSetModel;
use Combodo\iTop\Service\TemporaryObjects\TemporaryObjectHelper;
define('OBJECT_PROPERTIES_TAB', 'ObjectProperties');
@@ -187,9 +188,6 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
/** @var array initial attributes flags cache [attcode]['flags'] */
protected $aInitialAttributesFlags;
protected $iUpdateLoopCount;
const MAX_UPDATE_LOOP_COUNT = 10;
/**
* @var array First level classname, second level id, value number of calls done
@@ -227,7 +225,6 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
$this->sDisplayMode = static::DEFAULT_DISPLAY_MODE;
$this->bAllowWrite = false;
$this->bAllowDelete = false;
$this->iUpdateLoopCount = 0;
}
/**
@@ -2822,33 +2819,33 @@ JS
}
}
// Custom operation for the form ?
if (isset($aExtraParams['custom_operation'])) {
$sOperation = $aExtraParams['custom_operation'];
} else {
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
$sOperation = 'apply_modify';
} else {
$sOperation = 'apply_new';
}
}
if (isset($aExtraParams['custom_operation'])) {
$sOperation = $aExtraParams['custom_operation'];
} else {
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
$sOperation = 'apply_modify';
} else {
$sOperation = 'apply_new';
}
}
$oContentBlock = new UIContentBlock();
$oPage->AddUiBlock($oContentBlock);
$oContentBlock = new UIContentBlock();
$oPage->AddUiBlock($oContentBlock);
$oForm = new Form("form_{$this->m_iFormId}");
$oForm->SetAction($sFormAction);
$sOnSubmitForm = "let bOnSubmitForm = OnSubmit('form_{$this->m_iFormId}');";
if (isset($aExtraParams['js_handlers']['form_on_submit'])) {
$oForm->SetOnSubmitJsCode($sOnSubmitForm.$aExtraParams['js_handlers']['form_on_submit']);
} else {
$oForm->SetOnSubmitJsCode($sOnSubmitForm."return bOnSubmitForm;");
}
$oContentBlock->AddSubBlock($oForm);
$oForm = new Form("form_{$this->m_iFormId}");
$oForm->SetAction($sFormAction);
$sOnSubmitForm = "let bOnSubmitForm = OnSubmit('form_{$this->m_iFormId}');";
if (isset($aExtraParams['js_handlers']['form_on_submit'])) {
$oForm->SetOnSubmitJsCode($sOnSubmitForm . $aExtraParams['js_handlers']['form_on_submit']);
} else {
$oForm->SetOnSubmitJsCode($sOnSubmitForm . "return bOnSubmitForm;");
}
$oContentBlock->AddSubBlock($oForm);
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
// The object already exists in the database, it's a modification
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('id', $iKey, "{$sPrefix}_id"));
}
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
// The object already exists in the database, it's a modification
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('id', $iKey, "{$sPrefix}_id"));
}
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', $sOperation));
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $sClass));
@@ -2857,6 +2854,11 @@ JS
$oPage->SetTransactionId($iTransactionId);
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', $iTransactionId));
// Add temporary object watchdog (only on root form)
if (!utils::IsXmlHttpRequest()) {
$oPage->add_ready_script(TemporaryObjectHelper::GetWatchDogJS($iTransactionId));
}
// TODO 3.0.0: Is this (the if condition, not the code inside) still necessary?
if (isset($aExtraParams['wizard_container']) && $aExtraParams['wizard_container']) {
$sClassLabel = MetaModel::GetName($sClass);
@@ -2867,34 +2869,34 @@ JS
}
}
$oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null);
$oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null);
$oCancelButton = ButtonUIBlockFactory::MakeForCancel();
$oCancelButton->AddCSSClasses(['action', 'cancel']);
$oToolbarButtons->AddSubBlock($oCancelButton);
$oApplyButton = ButtonUIBlockFactory::MakeForPrimaryAction($sApplyButton, null, null, true);
$oApplyButton->AddCSSClass('action');
$oToolbarButtons->AddSubBlock($oApplyButton);
$bAreTransitionsHidden = isset($aExtraParams['hide_transitions']) && $aExtraParams['hide_transitions'] === true;
$aTransitions = $this->EnumTransitions();
if (!isset($aExtraParams['custom_operation']) && !$bAreTransitionsHidden && count($aTransitions)) {
// Transitions are displayed only for the standard new/modify actions, not for modify_all or any other case...
$oSetToCheckRights = DBObjectSet::FromObject($this);
$oCancelButton = ButtonUIBlockFactory::MakeForCancel();
$oCancelButton->AddCSSClasses(['action', 'cancel']);
$oToolbarButtons->AddSubBlock($oCancelButton);
$oApplyButton = ButtonUIBlockFactory::MakeForPrimaryAction($sApplyButton, null, null, true);
$oApplyButton->AddCSSClass('action');
$oToolbarButtons->AddSubBlock($oApplyButton);
$bAreTransitionsHidden = isset($aExtraParams['hide_transitions']) && $aExtraParams['hide_transitions'] === true;
$aTransitions = $this->EnumTransitions();
if (!isset($aExtraParams['custom_operation']) && !$bAreTransitionsHidden && count($aTransitions)) {
// Transitions are displayed only for the standard new/modify actions, not for modify_all or any other case...
$oSetToCheckRights = DBObjectSet::FromObject($this);
$oTransitionPopoverMenu = new PopoverMenu();
$sTPMSectionId = 'transitions';
$oTransitionPopoverMenu->AddSection($sTPMSectionId);
$aStimuli = Metamodel::EnumStimuli($sClass);
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass,
$sStimulusCode, $oSetToCheckRights) : UR_ALLOWED_NO;
switch ($iActionAllowed) {
case UR_ALLOWED_YES:
// Button to be displayed on its own on large screens
$oButton = ButtonUIBlockFactory::MakeForPrimaryAction($aStimuli[$sStimulusCode]->GetLabel(), 'next_action', $sStimulusCode, true);
$oButton->AddCSSClass('action');
$oButton->SetColor(Button::ENUM_COLOR_SCHEME_NEUTRAL);
$oToolbarButtons->AddSubBlock($oButton);
$oTransitionPopoverMenu = new PopoverMenu();
$sTPMSectionId = 'transitions';
$oTransitionPopoverMenu->AddSection($sTPMSectionId);
$aStimuli = Metamodel::EnumStimuli($sClass);
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass,
$sStimulusCode, $oSetToCheckRights) : UR_ALLOWED_NO;
switch ($iActionAllowed) {
case UR_ALLOWED_YES:
// Button to be displayed on its own on large screens
$oButton = ButtonUIBlockFactory::MakeForPrimaryAction($aStimuli[$sStimulusCode]->GetLabel(), 'next_action', $sStimulusCode, true);
$oButton->AddCSSClass('action');
$oButton->SetColor(Button::ENUM_COLOR_SCHEME_NEUTRAL);
$oToolbarButtons->AddSubBlock($oButton);
// Button to be displayed in a grouped button on smaller screens
$oTPMPopupMenuItem = new JSPopupMenuItem('next_action--'.$oButton->GetId(), $oButton->GetLabel(), "$(`#{$oButton->GetId()}`).trigger(`click`);");
@@ -2949,8 +2951,7 @@ JS
}
// Prepare blocker protection to avoid loosing data
$sBlockerId = uniqid($sClass.':'.$iKey.':', true);
$sConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
$sBlockerId = $sClass.':'.$iKey; // Important: This must have the synthax format as in js/layouts/activity-panel/activity-panel.js
$sJSToken = json_encode($sOwnershipToken);
$oPage->add_ready_script(<<<JS
// Try to release concurrent lock when leaving the page
@@ -3007,7 +3008,7 @@ JS
$oToolbarButtons->AddCSSClass('ibo-toolbar-top');
$oObjectDetails->AddToolbarBlock($oToolbarButtons);
// Allow form title customization
if (array_key_exists('form_title', $aExtraParams)) {
if (array_key_exists('form_title', $aExtraParams) && $aExtraParams['form_title'] !== null) {
$oObjectDetails->SetTitle($aExtraParams['form_title']);
}
}
@@ -3050,16 +3051,21 @@ JS
$oPage->SetCurrentTab('');
// Static fields values for wizard helper serialization
$aWizardHelperStaticValues = [];
// Add as hidden inputs values that we want displayed if they're readonly
if(isset($aExtraParams['forceFieldsSubmission'])){
$aExtraFlags = $aExtraParams['fieldsFlags'] ?? [];
foreach ($aExtraParams['forceFieldsSubmission'] as $sAttCode) {
if(FormHelper::GetAttributeFlagsForObject($this, $sAttCode, $aExtraFlags) & OPT_ATT_READONLY) {
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('attr_'.$sPrefix.$sAttCode, $this->Get($sAttCode)));
$aWizardHelperStaticValues[$sAttCode] = $this->Get($sAttCode);
}
}
}
$sWizardHelperStaticValues = json_encode($aWizardHelperStaticValues);
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $sClass));
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', $iTransactionId));
foreach ($aExtraParams as $sName => $value) {
@@ -3086,7 +3092,10 @@ JS
$iFieldsCount = count($aFieldsMap);
$sJsonFieldsMap = json_encode($aFieldsMap);
$sState = $this->GetState();
$sLifecycleStateForWizardHelper = '';
if (MetaModel::HasLifecycle($sClass)) {
$sLifecycleStateForWizardHelper = $this->GetState();
}
$sSessionStorageKey = $sClass.'_'.$iKey;
$sTempId = utils::GetUploadTempId($iTransactionId);
$oPage->add_ready_script(InlineImage::EnableCKEditorImageUpload($this, $sTempId));
@@ -3096,9 +3105,10 @@ JS
sessionStorage.removeItem('$sSessionStorageKey');
// Create the object once at the beginning of the page...
var oWizardHelper$sPrefix = new WizardHelper('$sClass', '$sPrefix', '$sState');
var oWizardHelper$sPrefix = new WizardHelper('$sClass', '$sPrefix', '$sLifecycleStateForWizardHelper');
oWizardHelper$sPrefix.SetFieldsMap($sJsonFieldsMap);
oWizardHelper$sPrefix.SetFieldsCount($iFieldsCount);
oWizardHelper$sPrefix.SetStaticValues($sWizardHelperStaticValues);
EOF
);
$oPage->add_ready_script(
@@ -3390,22 +3400,15 @@ EOF
];
// The list of candidate fields is made of the ordered list of "details" attributes + other attributes
$aAttributes = array();
foreach ($this->FlattenZList(MetaModel::GetZListItems($sClass, 'details')) as $sAttCode) {
$aAttributes[$sAttCode] = true;
}
// First attributes from the "details" zlist as they were sorted...
$aList = $this->FlattenZList(MetaModel::GetZListItems($sClass, 'details'));
// ... then append forgotten attributes
foreach (MetaModel::GetAttributesList($sClass) as $sAttCode) {
if (!array_key_exists($sAttCode, $aAttributes)) {
$aAttributes[$sAttCode] = true;
if (!in_array($sAttCode, $aList)) {
$aList[] = $sAttCode;
}
}
// Order the fields based on their dependencies, set the fields for which there is only one possible value
// and perform this in the order of dependencies to avoid dead-ends
$aDeps = array();
foreach ($aAttributes as $sAttCode => $trash) {
$aDeps[$sAttCode] = MetaModel::GetPrerequisiteAttributes($sClass, $sAttCode);
}
$aList = $this->OrderDependentFields($aDeps);
$bExistFieldToDisplay = false;
foreach ($aList as $sAttCode) {
@@ -4518,16 +4521,12 @@ HTML;
*/
public function DBInsertNoReload()
{
$this->LogCRUDEnter(__METHOD__);
try {
$res = parent::DBInsertNoReload();
$this->SetWarningsAsSessionMessages('create');
// Invoke extensions after insertion (the object must exist, have an id, etc.)
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) {
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
}
} finally {
if (static::IsCrudStackEmpty()) {
// Avoid signaling the current object that links were modified
@@ -4535,9 +4534,23 @@ HTML;
static::FireEventDbLinksChangedForAllObjects();
}
}
$this->LogCRUDExit(__METHOD__);
return $res;
}
public function PostInsertActions(): void
{
parent::PostInsertActions();
// Invoke extensions after insertion (the object must exist, have an id, etc.)
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) {
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBInsert()");
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
}
}
/**
* @inheritdoc
* Attaches InlineImages to the current object
@@ -4563,57 +4576,39 @@ HTML;
public function DBUpdate()
{
$this->LogCRUDEnter(__METHOD__);
try {
if (count($this->ListChanges()) === 0) {
$this->LogCRUDExit(__METHOD__);
return $this->GetKey();
}
$res = parent::DBUpdate();
$this->SetWarningsAsSessionMessages('update');
// Protection against reentrance (e.g. cascading the update of ticket logs)
// Note: This is based on the fix made on r 3190 in DBObject::DBUpdate()
if (!MetaModel::StartReentranceProtection($this)) {
$sClass = get_class($this);
$sKey = $this->GetKey();
IssueLog::Debug("CRUD: DBUpdate $sClass::$sKey Rejected (reentrance)", LogChannels::DM_CRUD);
return $res;
}
try {
// Invoke extensions after the update (could be before)
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) {
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
}
}
finally {
MetaModel::StopReentranceProtection($this);
}
$aChanges = $this->ListChanges();
if (count($aChanges) != 0) {
$this->iUpdateLoopCount++;
if ($this->iUpdateLoopCount > self::MAX_UPDATE_LOOP_COUNT) {
$sClass = get_class($this);
$sKey = $this->GetKey();
$aPlugins = [];
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) {
$aPlugins[] = get_class($oExtensionInstance);
}
$sPlugins = implode(', ', $aPlugins);
IssueLog::Error("CRUD: DBUpdate $sClass::$sKey Update loop detected plugins: $sPlugins", LogChannels::DM_CRUD);
} else {
return $this->DBUpdate();
}
}
} finally {
if (static::IsCrudStackEmpty()) {
static::FireEventDbLinksChangedForAllObjects();
}
}
$this->LogCRUDExit(__METHOD__);
return $res;
}
public function PostUpdateActions(array $aChanges): void
{
parent::PostUpdateActions($aChanges);
// Invoke extensions after the update (could be before)
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) {
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBUpdate()");
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
}
}
/**
* @param string $sMessageIdPrefix
*
@@ -4633,6 +4628,7 @@ HTML;
public function DBDelete(&$oDeletionPlan = null)
{
$this->LogCRUDEnter(__METHOD__);
try {
parent::DBDelete($oDeletionPlan);
} finally {
@@ -4642,6 +4638,7 @@ HTML;
static::FireEventDbLinksChangedForAllObjects();
}
}
$this->LogCRUDExit(__METHOD__);
return $oDeletionPlan;
}
@@ -4670,9 +4667,12 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
if ($oExtensionInstance->OnIsModified($this))
{
$sExtensionClass = get_class($oExtensionInstance);
if ($oExtensionInstance->OnIsModified($this)) {
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified() -> true");
return true;
} else {
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified() -> false");
}
}
@@ -5871,39 +5871,28 @@ JS
*/
final protected function FireEventCheckToWrite(): void
{
$this->FireEvent(EVENT_DB_CHECK_TO_WRITE);
$this->FireEvent(EVENT_DB_CHECK_TO_WRITE, ['is_new' => $this->IsNew()]);
}
/**
* @return void
* @throws \CoreException
*
* @since 3.1.0
*/
final protected function FireEventCreateDone(): void
final protected function FireEventBeforeWrite()
{
$this->NotifyAttachedObjectsOnLinkClassModification();
$this->FireEventDbLinksChangedForCurrentObject();
$this->FireEvent(EVENT_DB_CREATE_DONE);
$this->FireEvent(EVENT_DB_BEFORE_WRITE, ['is_new' => $this->IsNew()]);
}
/////////////
/// UPDATE
///
/**
* @param array $aChanges
* @param bool $bIsNew
*
* @return void
* @throws \ArchivedObjectException
* @throws \CoreException
* @since 3.1.0
*/
final protected function FireEventUpdateDone(array $aChanges): void
final protected function FireEventAfterWrite(array $aChanges, bool $bIsNew): void
{
$this->NotifyAttachedObjectsOnLinkClassModification();
$this->FireEventDbLinksChangedForCurrentObject();
$this->FireEvent(EVENT_DB_UPDATE_DONE, ['changes' => $aChanges]);
$this->FireEvent(EVENT_DB_AFTER_WRITE, ['is_new' => $bIsNew, 'changes' => $aChanges]);
}
//////////////
@@ -5928,11 +5917,11 @@ JS
*
* @since 3.1.0
*/
final protected function FireEventDeleteDone(): void
final protected function FireEventAfterDelete(): void
{
$this->NotifyAttachedObjectsOnLinkClassModification();
$this->FireEventDbLinksChangedForCurrentObject();
$this->FireEvent(EVENT_DB_DELETE_DONE);
$this->FireEvent(EVENT_DB_AFTER_DELETE);
}
/**
@@ -6037,14 +6026,16 @@ JS
// - we have a EVENT_DB_LINKS_CHANGED listener on Ticket that will update impacted items, so it will create new lnkApplicationSolutionToFunctionalCI
// We want to avoid launching the listener twice, first here, and secondly after saving the Ticket in the listener
// By disabling the event to be fired, we can remove the current object from the attribute !
/** @noinspection PhpRedundantOptionalArgumentInspection */
$oObject = MetaModel::GetObject($sClass, $sId, true);
self::SetEventDBLinksChangedBlocked(true);
MetaModel::StartReentranceProtection($oObject);
$oObject->FireEvent(EVENT_DB_LINKS_CHANGED);
MetaModel::StopReentranceProtection($oObject);
if ($oObject->IsModified()) {
$oObject->DBUpdate();
$oObject = MetaModel::GetObject($sClass, $sId, false);
// N°6408 The object can have been deleted
if (!is_null($oObject)) {
self::SetEventDBLinksChangedBlocked(true);
MetaModel::StartReentranceProtection($oObject);
$oObject->FireEvent(EVENT_DB_LINKS_CHANGED);
MetaModel::StopReentranceProtection($oObject);
if (count($oObject->ListChanges()) !== 0) {
$oObject->DBUpdate();
}
}
self::RemoveObjectAwaitingEventDbLinksChanged($sClass, $sId);
cmdbAbstractObject::SetEventDBLinksChangedBlocked(false);

View File

@@ -667,7 +667,7 @@ class DashletUnknown extends Dashlet
*/
public function GetPropertiesFields(DesignerForm $oForm)
{
$oField = new DesignerLongTextField('xml', Dict::S('UI:DashletUnknown:Prop-XMLConfiguration'), $this->sOriginalDashletXML);
$oField = new DesignerXMLField('xml', Dict::S('UI:DashletUnknown:Prop-XMLConfiguration'), $this->sOriginalDashletXML);
$oForm->AddField($oField);
}
@@ -869,7 +869,7 @@ class DashletPlainText extends Dashlet
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
$sText = $this->aProperties['text'];
$sText = utils::EscapeHtml($sText);
$sText = utils::EscapeHtml(Dict::S($sText));
$sText = str_replace(array("\r\n", "\n", "\r"), "<br/>", $sText);
$sId = 'plaintext_'.($bEditMode ? 'edit_' : '').$this->sId;

View File

@@ -186,6 +186,27 @@
</menu>
</menus>
<events>
<event id="EVENT_DB_BEFORE_WRITE" _delta="define">
<description>An object is about to be written into the database. The object can be modified.</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::OnInsert</replaces>
<event_data>
<event_datum id="object">
<description>The object inserted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="is_new">
<description>Creation flag</description>
<type>boolean</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_CHECK_TO_WRITE" _delta="define">
<description>Check an object before it is written into the database (no change possible). Call DBObject::AddCheckIssue() to signal an issue</description>
<sources>
@@ -197,14 +218,18 @@
<description>The object to check</description>
<type>DBObject</type>
</event_datum>
<event_datum id="is_new">
<description>Creation flag</description>
<type>boolean</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_CREATE_DONE" _delta="define">
<description>An object has been created into the database. The modifications can be propagated to other objects.</description>
<event id="EVENT_DB_AFTER_WRITE" _delta="define">
<description>An object has been written into the database. The modifications can be propagated to other objects.</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
@@ -214,22 +239,13 @@
<description>The object inserted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
<event_datum id="is_new">
<description>Creation flag</description>
<type>boolean</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_UPDATE_DONE" _delta="define">
<description>An object has been updated into the database and reloaded.</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::AfterUpdate</replaces>
<event_data>
<event_datum id="object">
<description>The object updated</description>
<type>DBObject</type>
<event_datum id="changes">
<description>For updates, the list of changes done during this operation</description>
<type>array</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
@@ -254,7 +270,7 @@
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_DELETE_DONE" _delta="define">
<event id="EVENT_DB_AFTER_DELETE" _delta="define">
<description>An object has been deleted into the database</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>

View File

@@ -1083,6 +1083,7 @@ JS
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, array(), $aQueryParams);
$this->m_oSet->SetShowObsoleteData($this->m_bShowObsoleteData);
}
// Summary details
$aCounts = array();
$aStateLabels = array();
@@ -1129,6 +1130,18 @@ JS
}
$oBlock = new UIContentBlockWithJSRefreshCallback(null, ["ibo-dashlet-header-dynamic--container"]);
// N°6394 Make sure to sort elements as defined in the datamodel
if (utils::IsNotNullOrEmptyString($sStateAttrCode)) {
$oAttDef = MetaModel::GetAttributeDef($sClass, $sStateAttrCode);
$aAllowedValues = $oAttDef->GetAllowedValues() ?? [];
$AllowedValuesKeys = array_keys($aAllowedValues);
uksort($aStateLabels, function ($sKey1, $sKey2) use ($AllowedValuesKeys) {
return array_search($sKey1, $AllowedValuesKeys) > array_search($sKey2, $AllowedValuesKeys) ? 1 : -1;
});
}
foreach ($aStateLabels as $sStateValue => $sStateLabel) {
$aCount = $aCounts[$sStateValue];
$sHyperlink = $aCount['link'];
@@ -1858,7 +1871,13 @@ class MenuBlock extends DisplayBlock
/** @var array $aToolkitActions Any "legacy" toolkit menu item, which are now displayed in the same menu as the $aRegularActions, after them */
$aToolkitActions = [];
if (!isset($aExtraParams['selection_mode']) || ($aExtraParams['selection_mode'] == "")) {
// Display menu actions only if...
if (
// ... NOT in a selection mode
(!isset($aExtraParams['selection_mode']) || ($aExtraParams['selection_mode'] == ""))
// ... "menu" parameter is NOT EXPLICITLY disabled
&& (!isset($aExtraParams['menu']) || $aExtraParams['menu'] === "1" || $aExtraParams['menu'] === true)
) {
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
if (utils::IsNotNullOrEmptyString($sContext)) {
@@ -2321,10 +2340,15 @@ class MenuBlock extends DisplayBlock
false
);
// creation form title
if (array_key_exists('creation_in_modal_form_title', $aExtraParams) && $aExtraParams['creation_in_modal_form_title'] !== null) {
$oAddLinkActionButton->AddDataAttribute('modal-title', $aExtraParams['creation_in_modal_form_title']);
}
// - If we are used in a Datatable, 'datatable_' will be prefixed to our $sId, so we do the same here
$sRealId = $sId;
if(in_array($this->m_sStyle, [static::ENUM_STYLE_LIST, 'links', static::ENUM_STYLE_LIST_IN_OBJECT])){
$sRealId = 'datatable_' . $sId;
if (in_array($this->m_sStyle, [static::ENUM_STYLE_LIST, 'links', static::ENUM_STYLE_LIST_IN_OBJECT])) {
$sRealId = 'datatable_'.$sId;
}
$oAddLinkActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button'])
->SetOnClickJsCode("$('#$sRealId').trigger('open_creation_modal.object.itop');");

View File

@@ -1110,13 +1110,41 @@ $('#$sId').on('change keyup validate', function() { ValidateWithPattern('$sId',
}
EOF
);
$sValue = "<textarea $sCSSClasses id=\"$sId\" name=\"$sName\">".utils::EscapeHtml($this->defaultValue)."</textarea>";
$sValue = "<textarea $sCSSClasses id=\"$sId\" name=\"$sName\">".$this->PrepareValueForRendering()."</textarea>";
}
else {
$sValue = "<div $sCSSClasses id=\"$sId\">".utils::EscapeHtml($this->defaultValue)."</div>";
$sValue = "<div $sCSSClasses id=\"$sId\">".$this->PrepareValueForRendering()."</div>";
}
return array('label' => $this->sLabel, 'value' => $sValue);
}
/**
* @return string|null The value itself as expected for rendering. May it be encoded, escaped or else.
* @since 3.1.0 N°6405
*/
protected function PrepareValueForRendering(): ?string
{
return utils::EscapeHtml($this->defaultValue);
}
}
/**
* Class DesignerXMLField
*
* Field to display XML content
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @since 3.1.0 N°6405
*/
class DesignerXMLField extends DesignerLongTextField
{
/**
* @inheritDoc
*/
protected function PrepareValueForRendering(): ?string
{
return utils::EscapeHtml($this->defaultValue, true);
}
}
class DesignerIntegerField extends DesignerFormField

View File

@@ -80,6 +80,11 @@ class LoginBasic extends AbstractLoginFSMExtension
{
if (Session::Get('login_mode') == 'basic')
{
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN)
{
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
LoginWebPage::HTTP401Error();
}
return LoginWebPage::LOGIN_FSM_CONTINUE;

View File

@@ -79,7 +79,7 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
{
self::ResetLoginSession();
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit == LoginWebPage::EXIT_RETURN)
if ($iOnExit === LoginWebPage::EXIT_RETURN)
{
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
@@ -95,6 +95,12 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
{
if (!Session::IsSet('login_mode'))
{
// N°6358 - if EXIT_RETURN was asked, send an error
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
// If no plugin validated the user, exit
self::ResetLoginSession();
exit();

View File

@@ -73,6 +73,11 @@ class LoginExternal extends AbstractLoginFSMExtension
{
if (Session::Get('login_mode') == 'external')
{
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN)
{
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
LoginWebPage::HTTP401Error();
}
return LoginWebPage::LOGIN_FSM_CONTINUE;

View File

@@ -44,6 +44,10 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
exit;
}
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
// No credentials yet, display the form
$oPage = LoginWebPage::NewLoginWebPage();
$oPage->DisplayLoginForm($this->bForceFormOnError);

View File

@@ -391,6 +391,11 @@ class LoginWebPage extends NiceWebPage
Session::Unset('can_logoff');
Session::Unset('archive_mode');
Session::Unset('impersonate_user');
Session::Unset('PluginProperties');
Session::Unset('UrlMakerClass');
Session::Unset('itop_env');
Session::Unset('obj_messages');
Session::Unset('profile_list');
UserRights::_ResetSessionCache();
// If it's desired to kill the session, also delete the session cookie.
// Note: This will destroy the session, and not just the session data!

View File

@@ -74,6 +74,7 @@ abstract class Query extends cmdbAbstractObject
"default_value" => 0,
"is_null_allowed" => false,
"depends_on" => array(),
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
)));
MetaModel::Init_AddAttribute(new AttributeDateTime("export_last_date", array(
@@ -82,6 +83,7 @@ abstract class Query extends cmdbAbstractObject
"default_value" => null,
"is_null_allowed" => true,
"depends_on" => array(),
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
)));
MetaModel::Init_AddAttribute(new AttributeExternalKey("export_last_user_id",
@@ -93,14 +95,16 @@ abstract class Query extends cmdbAbstractObject
"depends_on"=>array(),
"display_style"=>'select',
"always_load_in_tables"=>false,
"on_target_delete"=>DEL_SILENT
"on_target_delete"=>DEL_SILENT,
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
)));
MetaModel::Init_AddAttribute(new AttributeExternalField("export_last_user_contact",
array(
"allowed_values"=>null,
"extkey_attcode"=> "export_last_user_id",
"target_attcode"=>"contactid"
"target_attcode"=>"contactid",
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
)));
// Display lists

View File

@@ -15,10 +15,7 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Application\EventRegister\ApplicationEvents;
use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Service\Events\EventData;
use Combodo\iTop\Service\Events\EventService;
require_once(APPROOT.'core/cmdbobject.class.inc.php');
require_once(APPROOT.'application/utils.inc.php');
@@ -103,6 +100,3 @@ else
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
// Event service must be initialized after the MetaModel startup, otherwise it cannot discover classes implementing the iEventServiceSetup interface
EventService::InitService();
EventService::FireEvent(new EventData(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED));

View File

@@ -168,8 +168,6 @@ class UIExtKeyWidget
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
$sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL());
if ($this->bSearchMode) {
$sWizHelper = 'null';
@@ -1070,18 +1068,27 @@ JS
{
$oObj = MetaModel::NewObject($this->sTargetClass);
$aErrors = $oObj->UpdateObjectFromPostedForm($this->iId);
if (count($aErrors) == 0)
{
$oObj->DBInsert();
if (count($aErrors) == 0) {
// Retrieve JSON data
$sJSON = utils::ReadParam('json', '{}', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
$oJSON = json_decode($sJSON);
$oObj->SetContextSection('temporary_objects', [
'create' => [
'transaction_id' => utils::ReadParam('root_transaction_id', '', false, utils::ENUM_SANITIZATION_FILTER_TRANSACTION_ID),
'host_class' => $oJSON->m_sClass,
'host_att_code' => $this->sAttCode,
],
]);
$oObj->DBInsertNoReload();
return array('name' => $oObj->GetName(), 'id' => $oObj->GetKey());
}
else
{
} else {
return array('error' => implode(' ', $aErrors), 'id' => 0);
}
}
catch(Exception $e)
{
catch (Exception $e) {
return array('error' => $e->getMessage(), 'id' => 0);
}
}

View File

@@ -480,7 +480,7 @@ class utils
// For URL
case static::ENUM_SANITIZATION_FILTER_URL:
$retValue = filter_var($value, FILTER_SANITIZE_URL);
$retValue = filter_var($value, FILTER_VALIDATE_URL);
break;
default:
@@ -1970,6 +1970,7 @@ SQL;
/**
* @param string $sValue
* @param bool $bDoubleEncode Whether to double encode the value or not
*
* @return string passed value with only characters having a special meaning in HTML escaped as entities
* Since 3.0.0 we were using for this {@link HtmlEntities} but it was overkill and leads to double escaping !
@@ -1977,14 +1978,15 @@ SQL;
* @uses \htmlspecialchars()
* @link https://www.php.net/manual/fr/function.htmlspecialchars.php
* @since 3.0.0 N°3623
* @since 3.1.0 N°6405 Add $bDoubleEncode parameter
*/
public static function EscapeHtml($sValue)
public static function EscapeHtml($sValue, bool $bDoubleEncode = false)
{
return htmlspecialchars(
$sValue ?? '',
ENT_QUOTES | ENT_DISALLOWED | ENT_HTML5,
WebPage::PAGES_CHARSET,
false
$bDoubleEncode
);
}
@@ -2045,6 +2047,9 @@ SQL;
*/
public static function TextToHtml($sText)
{
if (static::IsNullOrEmptyString($sText)){
return '';
}
$sText = str_replace("\r\n", "\n", $sText);
$sText = str_replace("\r", "\n", $sText);
@@ -2904,7 +2909,8 @@ HTML;
if ($sClassNameFilter !== '' && strpos($sPHPClass, $sClassNameFilter) === false) {
$bSkipped = true;
}
else {
// For some PHP classes we don't have their file path as they are already in memory, so we never filter on their paths
elseif (utils::IsNotNullOrEmptyString($sPHPFile)) {
$sPHPFile = self::LocalPath($sPHPFile);
if ($sPHPFile !== false) {
$sPHPFile = '/'.$sPHPFile; // for regex
@@ -3368,5 +3374,22 @@ HTML;
{
return in_array($sTrait, self::TraitsUsedByClass($sClass, true));
}
/**
* Get stack trace as string array.
*
* @return array
* @since 3.1.0
*/
public static function GetStackTraceAsArray(): array
{
$e = new Exception();
$aTrace = explode("\n", $e->getTraceAsString());
// Remove call to this method
array_shift($aTrace);
// Remove Main
array_pop($aTrace);
return $aTrace;
}
}

View File

@@ -12,10 +12,10 @@
"ext-json": "*",
"ext-mysqli": "*",
"ext-soap": "*",
"apereo/phpcas" : "~1.6.0",
"apereo/phpcas": "~1.6.0",
"combodo/tcpdf": "~6.4.4",
"firebase/php-jwt": "~6.4.0",
"guzzlehttp/guzzle": "^7.4.5",
"guzzlehttp/guzzle": "^7.5.1",
"laminas/laminas-mail": "^2.11",
"laminas/laminas-servicemanager": "^3.5",
"league/oauth2-google": "^3.0",

130
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "69db9dbdea61a588fa2058724d91a579",
"content-hash": "cb3883f141f50e1bfbc957880ab93be1",
"packages": [
{
"name": "apereo/phpcas",
@@ -217,22 +217,22 @@
},
{
"name": "guzzlehttp/guzzle",
"version": "7.4.5",
"version": "7.7.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "1dd98b0564cb3f6bd16ce683cb755f94c10fbd82"
"reference": "fb7566caccf22d74d1ab270de3551f72a58399f5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/1dd98b0564cb3f6bd16ce683cb755f94c10fbd82",
"reference": "1dd98b0564cb3f6bd16ce683cb755f94c10fbd82",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5",
"reference": "fb7566caccf22d74d1ab270de3551f72a58399f5",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/promises": "^1.5",
"guzzlehttp/psr7": "^1.9 || ^2.4",
"guzzlehttp/promises": "^1.5.3 || ^2.0",
"guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
"php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0"
@@ -241,10 +241,11 @@
"psr/http-client-implementation": "1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.4.1",
"bamarni/composer-bin-plugin": "^1.8.1",
"ext-curl": "*",
"php-http/client-integration-tests": "^3.0",
"phpunit/phpunit": "^8.5.5 || ^9.3.5",
"php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
"php-http/message-factory": "^1.1",
"phpunit/phpunit": "^8.5.29 || ^9.5.23",
"psr/log": "^1.1 || ^2.0 || ^3.0"
},
"suggest": {
@@ -254,8 +255,9 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "7.4-dev"
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
@@ -321,7 +323,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/7.4.5"
"source": "https://github.com/guzzle/guzzle/tree/7.7.0"
},
"funding": [
{
@@ -337,38 +339,37 @@
"type": "tidelift"
}
],
"time": "2022-06-20T22:16:13+00:00"
"time": "2023-05-21T14:04:53+00:00"
},
{
"name": "guzzlehttp/promises",
"version": "1.5.1",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da"
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
"reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
"url": "https://api.github.com/repos/guzzle/promises/zipball/3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
"shasum": ""
},
"require": {
"php": ">=5.5"
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^4.4 || ^5.1"
"bamarni/composer-bin-plugin": "^1.8.1",
"phpunit/phpunit": "^8.5.29 || ^9.5.23"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.5-dev"
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
}
@@ -405,7 +406,7 @@
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
"source": "https://github.com/guzzle/promises/tree/1.5.1"
"source": "https://github.com/guzzle/promises/tree/2.0.0"
},
"funding": [
{
@@ -421,26 +422,26 @@
"type": "tidelift"
}
],
"time": "2021-10-22T20:56:57+00:00"
"time": "2023-05-21T13:50:22+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "2.4.0",
"version": "2.5.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "13388f00956b1503577598873fffb5ae994b5737"
"reference": "b635f279edd83fc275f822a1188157ffea568ff6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/13388f00956b1503577598873fffb5ae994b5737",
"reference": "13388f00956b1503577598873fffb5ae994b5737",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6",
"reference": "b635f279edd83fc275f822a1188157ffea568ff6",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.0",
"psr/http-message": "^1.1 || ^2.0",
"ralouphie/getallheaders": "^3.0"
},
"provide": {
@@ -448,17 +449,18 @@
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.4.1",
"bamarni/composer-bin-plugin": "^1.8.1",
"http-interop/http-factory-tests": "^0.9",
"phpunit/phpunit": "^8.5.8 || ^9.3.10"
"phpunit/phpunit": "^8.5.29 || ^9.5.23"
},
"suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
@@ -520,7 +522,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/2.4.0"
"source": "https://github.com/guzzle/psr7/tree/2.5.0"
},
"funding": [
{
@@ -536,7 +538,7 @@
"type": "tidelift"
}
],
"time": "2022-06-20T21:43:11+00:00"
"time": "2023-04-17T16:11:26+00:00"
},
{
"name": "laminas/laminas-loader",
@@ -1668,21 +1670,21 @@
},
{
"name": "psr/http-client",
"version": "1.0.1",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-client.git",
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
"reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31",
"reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31",
"shasum": ""
},
"require": {
"php": "^7.0 || ^8.0",
"psr/http-message": "^1.0"
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
@@ -1702,7 +1704,7 @@
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP clients",
@@ -1714,27 +1716,27 @@
"psr-18"
],
"support": {
"source": "https://github.com/php-fig/http-client/tree/master"
"source": "https://github.com/php-fig/http-client/tree/1.0.2"
},
"time": "2020-06-29T06:28:15+00:00"
"time": "2023-04-10T20:12:12+00:00"
},
{
"name": "psr/http-factory",
"version": "1.0.1",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
"reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
"reference": "e616d01114759c4c489f93b099585439f795fe35"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
"reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
"reference": "e616d01114759c4c489f93b099585439f795fe35",
"shasum": ""
},
"require": {
"php": ">=7.0.0",
"psr/http-message": "^1.0"
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
@@ -1754,7 +1756,7 @@
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interfaces for PSR-7 HTTP message factories",
@@ -1769,31 +1771,31 @@
"response"
],
"support": {
"source": "https://github.com/php-fig/http-factory/tree/master"
"source": "https://github.com/php-fig/http-factory/tree/1.0.2"
},
"time": "2019-04-30T12:38:16+00:00"
"time": "2023-04-10T20:10:41+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"version": "2.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
"php": "^7.2 || ^8.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-master": "2.0.x-dev"
}
},
"autoload": {
@@ -1808,7 +1810,7 @@
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
@@ -1822,9 +1824,9 @@
"response"
],
"support": {
"source": "https://github.com/php-fig/http-message/tree/master"
"source": "https://github.com/php-fig/http-message/tree/2.0"
},
"time": "2016-08-06T14:39:51+00:00"
"time": "2023-04-04T09:54:51+00:00"
},
{
"name": "psr/log",
@@ -5274,5 +5276,5 @@
"platform-overrides": {
"php": "7.4.0"
},
"plugin-api-version": "2.1.0"
"plugin-api-version": "2.3.0"
}

View File

@@ -0,0 +1,101 @@
<?php
/*
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* Class TemporaryObjectDescriptor
*
* Descriptor to track a temporary object.
*
* @experimental do not use, this feature will be part of a future version
*
* @since 3.1
*/
class TemporaryObjectDescriptor extends DBObject
{
public static function Init()
{
$aParams = array(
'category' => 'core',
'key_type' => 'autoincrement',
'name_attcode' => array('item_class', 'temp_id'),
'image_attcode' => '',
'state_attcode' => '',
'reconc_keys' => array(''),
'db_table' => 'priv_temporary_object_descriptor',
'db_key_field' => 'id',
'db_finalclass_field' => '',
'style' => new ormStyle(null, null, null, null, null, null),
'indexes' => array(
1 =>
array(
0 => 'temp_id',
),
2 =>
array(
0 => 'item_class',
1 => 'item_id',
),
),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeDateTime('expiration_date', array('sql' => 'expiration_date', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeString('temp_id', array('sql' => 'temp_id', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeString('item_class', array('sql' => 'item_class', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeObjectKey('item_id', array('class_attcode' => 'item_class', 'sql' => 'item_id', 'is_null_allowed' => true, 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeDateTime('creation_date', array('sql' => 'creation_date', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeString('host_class', array('sql' => 'host_class', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeObjectKey('host_id', array('class_attcode' => 'host_class', 'sql' => 'host_id', 'is_null_allowed' => true, 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeString('host_att_code', array('sql' => 'host_att_code', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeEnum("operation", array("allowed_values" => new ValueSetEnum('create,delete'), "sql" => "operation", "default_value" => "create", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_SetZListItems('details', array(
0 => 'temp_id',
1 => 'item_class',
2 => 'item_id',
3 => 'creation_date',
4 => 'expiration_date',
5 => 'meta',
));
MetaModel::Init_SetZListItems('standard_search', array(
0 => 'temp_id',
1 => 'item_class',
2 => 'item_id',
));
MetaModel::Init_SetZListItems('list', array(
0 => 'temp_id',
1 => 'item_class',
2 => 'item_id',
3 => 'creation_date',
4 => 'expiration_date',
));;
}
public function DBInsertNoReload()
{
$this->SetCurrentDateIfNull('creation_date');
return parent::DBInsertNoReload();
}
/**
* Set/Update all of the '_item' fields
*
* @param object $oItem Container item
*
* @return void
*/
public function SetItem($oItem, $bUpdateOnChange = false)
{
$sClass = get_class($oItem);
$iItemId = $oItem->GetKey();
$this->Set('item_class', $sClass);
$this->Set('item_id', $iItemId);
}
}

View File

@@ -16,6 +16,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
/**
* Persistent classes (internal): user defined actions
@@ -225,7 +226,22 @@ class ActionEmail extends ActionNotification
* @since 3.0.1
*/
const ENUM_HEADER_NAME_REFERENCES = 'References';
/**
* @var string
* @since 3.1.0
*/
const TEMPLATE_BODY_CONTENT = '$content$';
/**
* Wraps the 'body' of the message for previewing inside an IFRAME -- i.e. without any of the iTop stylesheets being applied
* @var string
* @since 3.1.0
*/
const CONTENT_HIGHLIGHT = '<div style="border:2px dashed #6800ff;position:relative;padding:2px;margin-top:14px;"><div style="background-color:#6800ff;color:#fff;font-family:Courier New, sans-serif;font-size:14px;line-height:16px;padding:3px;display:block;position:absolute;top:-22px;right:0;">$content$</div>%s</div>';
/**
* Wraps a placeholder of the email's body for previewing inside an IFRAME -- i.e. without any of the iTop stylesheets being applied
* @var string
*/
const FIELD_HIGHLIGHT = '<span style="background-color:#6800ff;color:#fff;font-size:smaller;font-family:Courier New, sans-serif;padding:2px;">\\$$1\\$</span>';
/**
* @inheritDoc
*/
@@ -257,6 +273,10 @@ class ActionEmail extends ActionNotification
MetaModel::Init_AddAttribute(new AttributeTemplateString("subject", array("allowed_values" => null, "sql" => "subject", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeTemplateHTML("body", array("allowed_values" => null, "sql" => "body", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("importance", array("allowed_values" => new ValueSetEnum('low,normal,high'), "sql" => "importance", "default_value" => 'normal', "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", array("sql"=>"language", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeBlob("html_template", array("is_null_allowed"=>true, "depends_on"=>array(), "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeEnum("ignore_notify", array("allowed_values" => new ValueSetEnum('yes,no'), "sql" => "ignore_notify", "default_value" => 'yes', "is_null_allowed" => false, "depends_on" => array())));
// Display lists
// - Attributes to be displayed for the complete details
@@ -266,8 +286,10 @@ class ActionEmail extends ActionNotification
0 => 'name',
1 => 'description',
2 => 'status',
3 => 'subject',
4 => 'body',
3 => 'language',
4 => 'html_template',
5 => 'subject',
6 => 'body',
// 5 => 'importance', not handled when sending the mail, better hide it then
),
'fieldset:ActionEmail:trigger' => array(
@@ -281,20 +303,21 @@ class ActionEmail extends ActionNotification
2 => 'reply_to',
3 => 'reply_to_label',
4 => 'test_recipient',
5 => 'to',
6 => 'cc',
7 => 'bcc',
5 => 'ignore_notify',
6 => 'to',
7 => 'cc',
8 => 'bcc',
),
),
));
// - Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('status', 'to', 'subject'));
MetaModel::Init_SetZListItems('list', array('status', 'to', 'subject', 'language'));
// Search criteria
// - Standard criteria of the search
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'status', 'subject'));
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'status', 'subject', 'language'));
// - Default criteria for the search
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status', 'subject'));
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status', 'subject', 'language'));
}
// count the recipients found
@@ -324,6 +347,15 @@ class ActionEmail extends ActionNotification
try
{
$oSearch = DBObjectSearch::FromOQL($sOQL);
if ($this->Get('ignore_notify') === 'no') {
// In theory it is possible to notify *any* kind of object,
// as long as there is an email attribute in the class
// So let's not assume that the selected class is a Person
$sFirstSelectedClass = $oSearch->GetClass();
if (MetaModel::IsValidAttCode($sFirstSelectedClass, 'notify')) {
$oSearch->AddCondition('notify', 'yes');
}
}
$oSearch->AllowAllData();
}
catch (OQLException $e)
@@ -448,114 +480,27 @@ class ActionEmail extends ActionNotification
*/
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
{
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();
try
{
$this->m_iRecipients = 0;
$this->m_aMailErrors = array();
// Determine recipients
//
$sTo = $this->FindRecipients('to', $aContextArgs);
$sCC = $this->FindRecipients('cc', $aContextArgs);
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
$sFrom = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
$sFromLabel = MetaModel::ApplyParams($this->Get('from_label'), $aContextArgs);
$sReplyTo = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
$sReplyToLabel = MetaModel::ApplyParams($this->Get('reply_to_label'), $aContextArgs);
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
$oObj = $aContextArgs['this->object()'];
$sMessageId = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_MESSAGE_ID);
$sReference = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_REFERENCES);
}
catch (Exception $e) {
/** @noinspection PhpUnhandledExceptionInspection */
throw $e;
}
finally {
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
}
if (!is_null($oLog)) {
// Note: we have to secure this because those values are calculated
// inside the try statement, and we would like to keep track of as
// many data as we could while some variables may still be undefined
if (isset($sTo)) {
$oLog->Set('to', $sTo);
}
if (isset($sCC)) {
$oLog->Set('cc', $sCC);
}
if (isset($sBCC)) {
$oLog->Set('bcc', $sBCC);
}
if (isset($sFrom)) {
$oLog->Set('from', $sFrom);
}
if (isset($sSubject)) {
$oLog->Set('subject', $sSubject);
}
if (isset($sBody)) {
$oLog->Set('body', $sBody);
}
}
$sStyles = file_get_contents(APPROOT.'css/email.css');
$sStyles .= MetaModel::GetConfig()->Get('email_css');
$oEmail = new EMail();
if ($this->IsBeingTested()) {
$oEmail->SetSubject('TEST['.$sSubject.']');
$sTestBody = $sBody;
$sTestBody .= "<div style=\"border: dashed;\">\n";
$sTestBody .= "<h1>Testing email notification ".$this->GetHyperlink()."</h1>\n";
$sTestBody .= "<p>The email should be sent with the following properties\n";
$sTestBody .= "<ul>\n";
$sTestBody .= "<li>TO: $sTo</li>\n";
$sTestBody .= "<li>CC: $sCC</li>\n";
$sTestBody .= "<li>BCC: $sBCC</li>\n";
$sTestBody .= empty($sFromLabel) ? "<li>From: $sFrom</li>\n" : "<li>From: $sFromLabel &lt;$sFrom&gt;</li>\n";
$sTestBody .= empty($sReplyToLabel) ? "<li>Reply-To: $sReplyTo</li>\n" : "<li>Reply-To: $sReplyToLabel &lt;$sReplyTo&gt;</li>\n";
$sTestBody .= "<li>References: $sReference</li>\n";
$sTestBody .= "</ul>\n";
$sTestBody .= "</p>\n";
$sTestBody .= "</div>\n";
$oEmail->SetBody($sTestBody, 'text/html', $sStyles);
$oEmail->SetRecipientTO($this->Get('test_recipient'));
$oEmail->SetRecipientFrom($sFrom, $sFromLabel);
$oEmail->SetReferences($sReference);
$oEmail->SetMessageId($sMessageId);
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
$oEmail->SetInReplyTo($sReference);
} else {
$oEmail->SetSubject($sSubject);
$oEmail->SetBody($sBody, 'text/html', $sStyles);
$oEmail->SetRecipientTO($sTo);
$oEmail->SetRecipientCC($sCC);
$oEmail->SetRecipientBCC($sBCC);
$oEmail->SetRecipientFrom($sFrom, $sFromLabel);
$oEmail->SetRecipientReplyTo($sReplyTo, $sReplyToLabel);
$oEmail->SetReferences($sReference);
$oEmail->SetMessageId($sMessageId);
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
$oEmail->SetInReplyTo($sReference);
$aEmailContent = $this->PrepareMessageContent($aContextArgs, $oLog);
$oEmail->SetSubject($aEmailContent['subject']);
$oEmail->SetBody($aEmailContent['body'], 'text/html', $sStyles);
$oEmail->SetRecipientTO($aEmailContent['to']);
$oEmail->SetRecipientCC($aEmailContent['cc']);
$oEmail->SetRecipientBCC($aEmailContent['bcc']);
$oEmail->SetRecipientFrom($aEmailContent['from'], $aEmailContent['from_label']);
$oEmail->SetRecipientReplyTo($aEmailContent['reply_to'], $aEmailContent['reply_to_label']);
$oEmail->SetReferences($aEmailContent['references']);
$oEmail->SetMessageId($aEmailContent['message_id']);
$oEmail->SetInReplyTo($aEmailContent['in_reply_to']);
foreach($aEmailContent['attachments'] as $aAttachment) {
$oEmail->AddAttachment($aAttachment['data'], $aAttachment['filename'], $aAttachment['mime_type']);
}
if (isset($aContextArgs['attachments']))
{
$aAttachmentReport = array();
foreach($aContextArgs['attachments'] as $oDocument)
{
$oEmail->AddAttachment($oDocument->GetData(), $oDocument->GetFileName(), $oDocument->GetMimeType());
$aAttachmentReport[] = array($oDocument->GetFileName(), $oDocument->GetMimeType(), strlen($oDocument->GetData()));
}
$oLog->Set('attachments', $aAttachmentReport);
}
if (empty($this->m_aMailErrors))
{
if ($this->m_iRecipients == 0)
@@ -564,6 +509,7 @@ class ActionEmail extends ActionNotification
}
else
{
$aErrors = [];
$iRes = $oEmail->Send($aErrors, false, $oLog); // allow asynchronous mode
switch ($iRes)
{
@@ -588,13 +534,147 @@ class ActionEmail extends ActionNotification
}
}
/**
* @param array $aContextArgs
* @param \EventNotification $oLog
*
* @return array
* @throws \CoreException
* @throws \Exception
* @since 3.1.0 N°918
*/
protected function PrepareMessageContent($aContextArgs, &$oLog): array
{
$aMessageContent = [
'to' => '',
'cc' => '',
'bcc' => '',
'from' => '',
'from_label' => '',
'reply_to' => '',
'reply_to_label' => '',
'subject' => '',
'body' => '',
'references' => '',
'message_id' => '',
'in_reply_to' => '',
'attachments' => [],
];
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();
$sPreviousLanguage = Dict::GetUserLanguage();
$aPreviousPluginProperties = ApplicationContext::GetPluginProperties('QueryLocalizerPlugin');
if ($this->Get('language') !== '') {
// If a language is specified for this action, force this language
// when rendering all placeholders inside this message
Dict::SetUserLanguage($this->Get('language'));
AttributeDateTime::LoadFormatFromConfig();
ApplicationContext::SetPluginProperty('QueryLocalizerPlugin', 'language_code', $this->Get('language'));
}
try
{
$this->m_iRecipients = 0;
$this->m_aMailErrors = array();
// Determine recipients
//
$aMessageContent['to'] = $this->FindRecipients('to', $aContextArgs);
$aMessageContent['cc'] = $this->FindRecipients('cc', $aContextArgs);
$aMessageContent['bcc'] = $this->FindRecipients('bcc', $aContextArgs);
$aMessageContent['from'] = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
$aMessageContent['from_label'] = MetaModel::ApplyParams($this->Get('from_label'), $aContextArgs);
$aMessageContent['reply_to'] = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
$aMessageContent['reply_to_label'] = MetaModel::ApplyParams($this->Get('reply_to_label'), $aContextArgs);
$aMessageContent['subject'] = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
$sBody = $this->BuildMessageBody(false);
$aMessageContent['body'] = MetaModel::ApplyParams($sBody, $aContextArgs);
$oObj = $aContextArgs['this->object()'];
$aMessageContent['message_id'] = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_MESSAGE_ID);
$aMessageContent['references'] = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_REFERENCES);
}
catch (Exception $e) {
/** @noinspection PhpUnhandledExceptionInspection */
throw $e;
}
finally {
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
Dict::SetUserLanguage($sPreviousLanguage);
AttributeDateTime::LoadFormatFromConfig();
ApplicationContext::SetPluginProperty('QueryLocalizerPlugin', 'language_code', $aPreviousPluginProperties['language_code'] ?? null);
}
if (!is_null($oLog)) {
// Note: we have to secure this because those values are calculated
// inside the try statement, and we would like to keep track of as
// many data as we could while some variables may still be undefined
if (isset($aMessageContent['to'])) {
$oLog->Set('to', $aMessageContent['to']);
}
if (isset($aMessageContent['cc'])) {
$oLog->Set('cc', $aMessageContent['cc']);
}
if (isset($aMessageContent['bcc'])) {
$oLog->Set('bcc', $aMessageContent['bcc']);
}
if (isset($aMessageContent['from'])) {
$oLog->Set('from', $aMessageContent['from']);
}
if (isset($aMessageContent['subject'])) {
$oLog->Set('subject', $aMessageContent['subject']);
}
if (isset($aMessageContent['body'])) {
$oLog->Set('body', HTMLSanitizer::Sanitize($aMessageContent['body']));
}
}
$sStyles = file_get_contents(APPROOT.'css/email.css');
$sStyles .= MetaModel::GetConfig()->Get('email_css');
if ($this->IsBeingTested()) {
$sTestBody = $aMessageContent['body'];
$sTestBody .= "<div style=\"border: dashed;\">\n";
$sTestBody .= "<h1>Testing email notification ".$this->GetHyperlink()."</h1>\n";
$sTestBody .= "<p>The email should be sent with the following properties\n";
$sTestBody .= "<ul>\n";
$sTestBody .= "<li>TO: {$aMessageContent['to']}</li>\n";
$sTestBody .= "<li>CC: {$aMessageContent['cc']}</li>\n";
$sTestBody .= "<li>BCC: {$aMessageContent['bcc']}</li>\n";
$sTestBody .= empty($aMessageContent['from_label']) ? "<li>From: {$aMessageContent['from']}</li>\n" : "<li>From: {$aMessageContent['from_label']} &lt;{$aMessageContent['from']}&gt;</li>\n";
$sTestBody .= empty($aMessageContent['reply_to_label']) ? "<li>Reply-To: {$aMessageContent['reply_to']}</li>\n" : "<li>Reply-To: {$aMessageContent['reply_to_label']} &lt;{$aMessageContent['reply_to']}&gt;</li>\n";
$sTestBody .= "<li>References: {$aMessageContent['references']}</li>\n";
$sTestBody .= "</ul>\n";
$sTestBody .= "</p>\n";
$sTestBody .= "</div>\n";
$aMessageContent['subject'] = 'TEST['.$aMessageContent['subject'].']';
$aMessageContent['body'] = $sTestBody;
$aMessageContent['to'] = $this->Get('test_recipient');
}
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
$aMessageContent['in_reply_to'] = $aMessageContent['references'];
if (isset($aContextArgs['attachments']))
{
$aAttachmentReport = array();
foreach($aContextArgs['attachments'] as $oDocument)
{
$aMessageContent['attachments'][] = ['data' => $oDocument->GetData(), 'filename' => $oDocument->GetFileName(), 'mime_type' => $oDocument->GetMimeType()];
$aAttachmentReport[] = array($oDocument->GetFileName(), $oDocument->GetMimeType(), strlen($oDocument->GetData()));
}
$oLog->Set('attachments', $aAttachmentReport);
}
return $aMessageContent;
}
/**
* @param \DBObject $oObject
* @param string $sHeaderName {@see \ActionEmail::ENUM_HEADER_NAME_REFERENCES}, {@see \ActionEmail::ENUM_HEADER_NAME_MESSAGE_ID}
*
* @return string The formatted identifier for $sHeaderName based on $oObject
* @throws \Exception
* @since 3.0.1 N°4849
* @since 3.1.0 N°4849
*/
protected function GenerateIdentifierForHeaders(DBObject $oObject, string $sHeaderName): string
{
@@ -629,4 +709,65 @@ class ActionEmail extends ActionNotification
]);
throw new Exception($sErrorMessage);
}
/**
* Compose the body of the message from the 'body' attribute and the HTML template (if any)
* @since 3.1.0 N°4849
* @param bool $bHighlightPlaceholders If true add some extra HTML around placeholders to highlight them
* @return string
*/
protected function BuildMessageBody(bool $bHighlightPlaceholders = false): string
{
$sBody = $this->Get('body');
/** @var ormDocument $oHtmlTemplate */
$oHtmlTemplate = $this->Get('html_template');
if ($oHtmlTemplate && !$oHtmlTemplate->IsEmpty()) {
$sHtmlTemplate = $oHtmlTemplate->GetData();
if (false !== mb_strpos($sHtmlTemplate, static::TEMPLATE_BODY_CONTENT)) {
if ($bHighlightPlaceholders) {
// Highlight the $content$ block
$sBody = sprintf(static::CONTENT_HIGHLIGHT, $sBody);
}
$sBody = str_replace(static::TEMPLATE_BODY_CONTENT, $sBody, $oHtmlTemplate->GetData()); // str_replace is ok as long as both strings are well-formed UTF-8
} else {
$sBody = $oHtmlTemplate->GetData();
}
}
if($bHighlightPlaceholders) {
// Highlight all placeholders
$sBody = preg_replace('/\\$([^$]+)\\$/', static::FIELD_HIGHLIGHT, $sBody);
}
return $sBody;
}
/**
* @since 3.1.0 N°4849
* @inheritDoc
* @see cmdbAbstractObject::DisplayBareRelations()
*/
public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
{
parent::DisplayBareRelations($oPage, false);
if (!$bEditMode) {
$oPage->SetCurrentTab('action_email__preview', Dict::S('ActionEmail:preview_tab'), Dict::S('ActionEmail:preview_tab+'));
$sBody = $this->BuildMessageBody(true);
TwigHelper::RenderIntoPage($oPage, APPROOT.'/', 'templates/datamodel/ActionEmail/email-notification-preview', ['iframe_content' => $sBody]);
}
}
/**
* @since 3.1.0
* @inheritDoc
* @see cmdbAbstractObject::DoCheckToWrite()
*/
public function DoCheckToWrite()
{
parent::DoCheckToWrite();
$oHtmlTemplate = $this->Get('html_template');
if ($oHtmlTemplate && !$oHtmlTemplate->IsEmpty()) {
if (false === mb_strpos($oHtmlTemplate->GetData(), static::TEMPLATE_BODY_CONTENT)) {
$this->m_aCheckWarnings[] = Dict::Format('ActionEmail:content_placeholder_missing', static::TEMPLATE_BODY_CONTENT, Dict::S('Class:ActionEmail/Attribute:body'));
}
}
}
}

View File

@@ -9,8 +9,7 @@ use Combodo\iTop\Application\UI\Links\Set\BlockLinkSetDisplayAsProperty;
use Combodo\iTop\Form\Field\LabelField;
use Combodo\iTop\Form\Field\TextAreaField;
use Combodo\iTop\Form\Form;
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
use Combodo\iTop\Form\Validator\Validator;
use Combodo\iTop\Form\Validator\CustomRegexpValidator;
use Combodo\iTop\Renderer\BlockRenderer;
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
use Combodo\iTop\Service\Links\LinkSetModel;
@@ -92,9 +91,6 @@ define('LINKSET_EDITMODE_ACTIONS', 2); // Show the usual 'Actions' popup menu
define('LINKSET_EDITMODE_INPLACE', 3); // The "linked" objects can be created/modified/deleted in place
define('LINKSET_EDITMODE_ADDREMOVE', 4); // The "linked" objects can be added/removed in place
define('LINKSET_RELATIONTYPE_PROPERTY', 'property');
define('LINKSET_RELATIONTYPE_LINK', 'link');
define('LINKSET_DISPLAY_STYLE_PROPERTY', 'property');
define('LINKSET_DISPLAY_STYLE_TAB', 'tab');
@@ -801,8 +797,8 @@ abstract class AttributeDefinition
/**
* force an allowed value (type conversion and possibly forces a value as mySQL would do upon writing!
*
* @param $proposedValue
* @param $oHostObj
* @param mixed $proposedValue
* @param \DBObject $oHostObj
*
* @return mixed
*/
@@ -992,17 +988,21 @@ abstract class AttributeDefinition
}
/**
* Helper to form a value, given JSON decoded data
* Helper to form a value, given JSON decoded data. This way the attribute itself handles the transformation from the JSON structure to the expected data (the one that
* needs to be used in the {@see \DBObject::Set()} method).
*
* Note that for CSV and XML this isn't done yet (no delegation to the attribute but switch/case inside controllers) :/
*
* @see GetForJSON for the reverse operation
*
* @param string $json JSON encoded value
*
* @return mixed JSON decoded data
* @return mixed JSON decoded data, depending on the attribute type
*
* @see GetForJSON for the reverse operation
*/
public function FromJSONToValue($json)
{
// Passthrough in most of the cases
// Pass-through in most of the cases
return $json;
}
@@ -1121,7 +1121,7 @@ abstract class AttributeDefinition
// Validation pattern
if ($this->GetValidationPattern() !== '') {
$oFormField->AddValidator(new Validator($this->GetValidationPattern()));
$oFormField->AddValidator(new CustomRegexpValidator($this->GetValidationPattern()));
}
// Description
@@ -1150,6 +1150,13 @@ abstract class AttributeDefinition
$oFormField->AddMetadata('value-raw', (string)$oObject->Get($this->GetCode()));
}
// We don't want to invalidate field because of old untouched values that are no longer valid
$aModifiedAttCodes = $oObject->ListChanges();
$bAttributeHasBeenModified = array_key_exists($this->GetCode(), $aModifiedAttCodes);
if (false === $bAttributeHasBeenModified) {
$oFormField->SetValidationDisabled(true);
}
return $oFormField;
}
@@ -1692,22 +1699,12 @@ class AttributeLinkedSet extends AttributeDefinition
/**
* @return string see LINKSET_EDITMODE_* constants
* @since 3.1.0 N°5563 relations are edited using new attributes in details mode, but as nothing changed in edit mode we are still using edit_mode attribute
*/
public function GetEditMode()
{
return $this->GetOptional('edit_mode', LINKSET_EDITMODE_ACTIONS);
}
/**
* @return string see LINKSET_RELATIONTYPE_* constants
* @since 3.1.0 N°5563
*/
public function GetRelationType()
{
return $this->GetOptional('relation_type', LINKSET_RELATIONTYPE_LINK);
}
/**
* @return string see LINKSET_DISPLAY_STYLE_* constants
* @since 3.1.0 N°3190
@@ -1723,12 +1720,13 @@ class AttributeLinkedSet extends AttributeDefinition
}
/**
* @return boolean
* @since 3.1.0 N°5563
* Indicates if the current Attribute has constraints (php constraints or datamodel constraints)
* @return bool true if Attribute has constraints
* @since 3.1.0 N°6228
*/
public function GetReadOnly()
public function GetHasConstraint()
{
return $this->GetOptional('read_only', false);
return $this->GetOptional('with_php_constraint', false);
}
public function GetLinkedClass()
@@ -1757,12 +1755,58 @@ class AttributeLinkedSet extends AttributeDefinition
}
/** @inheritDoc * */
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true): string
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if($this->GetDisplayStyle() === LINKSET_DISPLAY_STYLE_TAB){
return $this->GetAsHTMLForTab($sValue, $oHostObject, $bLocalize);
}
else{
return $this->GetAsHTMLForProperty($sValue, $oHostObject, $bLocalize);
}
}
public function GetAsHTMLForTab($sValue, $oHostObject = null, $bLocalize = true)
{
if (is_object($sValue) && ($sValue instanceof ormLinkSet))
{
$sValue->Rewind();
$aItems = array();
while ($oObj = $sValue->Fetch())
{
// Show only relevant information (hide the external key to the current object)
$aAttributes = array();
foreach(MetaModel::ListAttributeDefs($this->GetLinkedClass()) as $sAttCode => $oAttDef)
{
if ($sAttCode == $this->GetExtKeyToMe())
{
continue;
}
if ($oAttDef->IsExternalField())
{
continue;
}
$sAttValue = $oObj->GetAsHTML($sAttCode);
if (strlen($sAttValue) > 0)
{
$aAttributes[] = $sAttValue;
}
}
$sAttributes = implode(', ', $aAttributes);
$aItems[] = $sAttributes;
}
return implode('<br/>', $aItems);
}
return null;
}
public function GetAsHTMLForProperty($sValue, $oHostObject = null, $bLocalize = true): string
{
try {
/** @var ormLinkSet $sValue */
if ($sValue->Count() === 0) {
if (is_null($sValue) || $sValue->Count() === 0) {
return '';
}
@@ -2381,43 +2425,61 @@ class AttributeLinkedSet extends AttributeDefinition
{
if ($oFormField === null)
{
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
// Setting target class
// Setting target class
if (!$this->IsIndirect()) {
$sTargetClass = $this->GetLinkedClass();
} else {
/** @var \AttributeExternalKey $oRemoteAttDef */
/** @var \AttributeLinkedSetIndirect $this */
$oRemoteAttDef = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToRemote());
$sTargetClass = $oRemoteAttDef->GetTargetClass();
$sTargetClass = $this->GetLinkedClass();
} else {
/** @var \AttributeExternalKey $oRemoteAttDef */
/** @var \AttributeLinkedSetIndirect $this */
$oRemoteAttDef = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToRemote());
$sTargetClass = $oRemoteAttDef->GetTargetClass();
/** @var \AttributeLinkedSetIndirect $this */
$oFormField->SetExtKeyToRemote($this->GetExtKeyToRemote());
}
$oFormField->SetTargetClass($sTargetClass);
$oFormField->SetIndirect($this->IsIndirect());
// Setting attcodes to display
$aAttCodesToDisplay = MetaModel::FlattenZList(MetaModel::GetZListItems($sTargetClass, 'list'));
// - Adding friendlyname attribute to the list is not already in it
$sTitleAttCode = MetaModel::GetFriendlyNameAttributeCode($sTargetClass);
if (($sTitleAttCode !== null) && !in_array($sTitleAttCode, $aAttCodesToDisplay)) {
$aAttCodesToDisplay = array_merge(array($sTitleAttCode), $aAttCodesToDisplay);
}
// - Adding attribute labels
$aAttributesToDisplay = array();
foreach ($aAttCodesToDisplay as $sAttCodeToDisplay) {
$oAttDefToDisplay = MetaModel::GetAttributeDef($sTargetClass, $sAttCodeToDisplay);
$aAttributesToDisplay[$sAttCodeToDisplay] = $oAttDefToDisplay->GetLabel();
}
$oFormField->SetAttributesToDisplay($aAttributesToDisplay);
/** @var \AttributeLinkedSetIndirect $this */
$oFormField->SetExtKeyToRemote($this->GetExtKeyToRemote());
}
$oFormField->SetTargetClass($sTargetClass);
$oFormField->SetLinkedClass($this->GetLinkedClass());
$oFormField->SetIndirect($this->IsIndirect());
// Setting attcodes to display
$aAttCodesToDisplay = MetaModel::FlattenZList(MetaModel::GetZListItems($sTargetClass, 'list'));
// - Adding friendlyname attribute to the list is not already in it
$sTitleAttCode = MetaModel::GetFriendlyNameAttributeCode($sTargetClass);
if (($sTitleAttCode !== null) && !in_array($sTitleAttCode, $aAttCodesToDisplay)) {
$aAttCodesToDisplay = array_merge(array($sTitleAttCode), $aAttCodesToDisplay);
}
// - Adding attribute properties
$aAttributesToDisplay = array();
foreach ($aAttCodesToDisplay as $sAttCodeToDisplay) {
$oAttDefToDisplay = MetaModel::GetAttributeDef($sTargetClass, $sAttCodeToDisplay);
$aAttributesToDisplay[$sAttCodeToDisplay] = [
'att_code' => $sAttCodeToDisplay,
'label' => $oAttDefToDisplay->GetLabel(),
];
}
$oFormField->SetAttributesToDisplay($aAttributesToDisplay);
parent::MakeFormField($oObject, $oFormField);
// Append lnk attributes (filtered from zlist)
if ($this->IsIndirect()) {
$aLnkAttDefToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectLinkClass($this->m_sHostClass, $this->m_sCode);
$aLnkAttributesToDisplay = array();
foreach ($aLnkAttDefToDisplay as $oLnkAttDefToDisplay) {
$aLnkAttributesToDisplay[$oLnkAttDefToDisplay->GetCode()] = [
'att_code' => $oLnkAttDefToDisplay->GetCode(),
'label' => $oLnkAttDefToDisplay->GetLabel(),
'mandatory' => !$oLnkAttDefToDisplay->IsNullAllowed(),
];
}
$oFormField->SetLnkAttributesToDisplay($aLnkAttributesToDisplay);
}
return $oFormField;
}
parent::MakeFormField($oObject, $oFormField);
return $oFormField;
}
public function IsPartOfFingerprint()
{
@@ -2523,15 +2585,6 @@ class AttributeLinkedSetIndirect extends AttributeLinkedSet
return $this->GetOptional("duplicates", false);
} // The same object may be linked several times... or not...
/**
* @return boolean
* @since 3.1.0 N°5563
*/
public function GetReadOnly()
{
return $this->GetOptional('read_only', false);
}
public function GetTrackingLevel()
{
return $this->GetOptional('tracking_level',
@@ -3116,7 +3169,7 @@ class AttributeDecimal extends AttributeDBField
$iPrecision = $this->Get('decimals');
$iNbIntegerDigits = $iNbDigits - $iPrecision - 1; // -1 because the first digit is treated separately in the pattern below
return "^[-+]?[0-9]\d{0,$iNbIntegerDigits}(\.\d{0,$iPrecision})?$";
return "^[\-\+]?[0-9]\d{0,$iNbIntegerDigits}(\.\d{0,$iPrecision})?$";
}
public function GetBasicFilterOperators()
@@ -3838,6 +3891,12 @@ class AttributeApplicationLanguage extends AttributeString
{
$aLanguageCodes[$sLangCode] = $aInfo['description'].' ('.$aInfo['localized_description'].')';
}
// N°6462 This should be sorted directly in \Dict during the compilation but we can't for 2 reasons:
// - Additional languages can be added on the fly even though it is not recommended
// - Formatting is done at run time (just above)
natcasesort($aLanguageCodes);
$aParams["allowed_values"] = new ValueSetEnum($aLanguageCodes);
parent::__construct($sCode, $aParams);
}
@@ -4128,7 +4187,7 @@ class AttributePassword extends AttributeString implements iAttributeNoGroupBy
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if (strlen($sValue) == 0)
if (utils::IsNullOrEmptyString($sValue))
{
return '';
}
@@ -5896,6 +5955,14 @@ class AttributeEnum extends AttributeString
$aLocalizedValues[$sKey] = $this->GetValueLabel($sKey);
}
// Sort by label only if necessary
// See N°1646 and {@see \MFCompiler::CompileAttributeEnumValues()} for complete information as for why sort on labels is done at runtime while other sorting are done at compile time
/** @var \ValueSetEnum $oValueSetDef */
$oValueSetDef = $this->GetValuesDef();
if ($oValueSetDef->IsSortedByValues()) {
asort($aLocalizedValues);
}
return $aLocalizedValues;
}
@@ -6171,7 +6238,7 @@ class AttributeDateTime extends AttributeDBField
/**
* Load the 3 settings: date format, time format and data_time format from the configuration
*/
protected static function LoadFormatFromConfig()
public static function LoadFormatFromConfig()
{
$aFormats = MetaModel::GetConfig()->Get('date_and_time_format');
$sLang = Dict::GetUserLanguage();
@@ -7153,14 +7220,27 @@ class AttributeExternalKey extends AttributeDBFieldVoid
{
return 0;
}
if (MetaModel::IsValidObject($proposedValue))
{
if (MetaModel::IsValidObject($proposedValue)) {
return $proposedValue->GetKey();
}
return (int)$proposedValue;
}
/** @inheritdoc @since 3.1 */
public function WriteExternalValues(DBObject $oHostObject): void
{
$sTargetKey = $oHostObject->Get($this->GetCode());
$oFilter = DBSearch::FromOQL('SELECT `'.TemporaryObjectDescriptor::class.'` WHERE item_class=:class AND item_id=:id');
$oSet = new DBObjectSet($oFilter, [], ['class' => $this->GetTargetClass(), 'id' => $sTargetKey]);
while ($oTemporaryObjectDescriptor = $oSet->Fetch()) {
$oTemporaryObjectDescriptor->Set('host_class', get_class($oHostObject));
$oTemporaryObjectDescriptor->Set('host_id', $oHostObject->GetKey());
$oTemporaryObjectDescriptor->Set('host_att_code', $this->GetCode());
$oTemporaryObjectDescriptor->DBUpdate();
}
}
public function GetMaximumComboLength()
{
return $this->GetOptional('max_combo_length', MetaModel::GetConfig()->Get('max_combo_length'));
@@ -7224,6 +7304,7 @@ class AttributeExternalKey extends AttributeDBFieldVoid
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
/** @var \Combodo\iTop\Form\Field\Field $oFormField */
if ($oFormField === null) {
// Later : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value
$sFormFieldClass = static::GetFormFieldClass();
@@ -7254,19 +7335,12 @@ class AttributeExternalKey extends AttributeDBFieldVoid
}
});
}
else
{
else {
$oSearch = DBSearch::FromOQL($this->GetValuesDef()->GetFilterExpression());
$oSearch->SetInternalParams(array('this' => $oObject));
$oFormField->SetSearch($oSearch);
}
// If ExtKey is mandatory, we add a validator to ensure that the value 0 is not selected
if ($oObject->GetAttributeFlags($this->GetCode()) & OPT_ATT_MANDATORY)
{
$oFormField->AddValidator(new NotEmptyExtKeyValidator());
}
parent::MakeFormField($oObject, $oFormField);
return $oFormField;
@@ -7892,6 +7966,17 @@ class AttributeExternalField extends AttributeDefinition
return $oExtAttDef->MakeRealValue($proposedValue, $oHostObj);
}
/**
* @inheritDoc
* @since 3.1.0 N°6271 Delegate to remote attribute to ensure cascading computed values
*/
public function GetSQLValues($value)
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetSQLValues($value);
}
public function ScalarToSQL($value)
{
// This one could be used in case of filtering only
@@ -8300,7 +8385,7 @@ class AttributeBlob extends AttributeDefinition
$aValues[$this->GetCode().'_data'] = '';
$aValues[$this->GetCode().'_mimetype'] = '';
$aValues[$this->GetCode().'_filename'] = '';
$aValues[$this->GetCode().'_downloads_count'] = ''; // Note: Should this be set to \ormDocument::DEFAULT_DOWNLOADS_COUNT ?
$aValues[$this->GetCode().'_downloads_count'] = ormDocument::DEFAULT_DOWNLOADS_COUNT;
}
return $aValues;
@@ -8490,6 +8575,22 @@ class AttributeBlob extends AttributeDefinition
return utils::IsNotNullOrEmptyString($proposedValue->GetData()) && utils::IsNotNullOrEmptyString($proposedValue->GetFileName());
}
/**
* @inheritDoc
* @param \ormDocument $original
* @param \ormDocument $value
* @since N°6502
*/
public function RecordAttChange(DBObject $oObject, $original, $value): void
{
// N°6502 Don't record history if only the download count has changed
if ($original->EqualsExceptDownloadsCount($value)) {
return;
}
parent::RecordAttChange($oObject, $original, $value);
}
protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void
{
if (is_null($original)) {
@@ -8607,7 +8708,7 @@ class AttributeImage extends AttributeBlob
$sCssClasses .= ' '.(($bIsCustomImage) ? 'attribute-image-custom' : 'attribute-image-default');
// Important: If you change this, mind updating edit_image.js as well
return '<div class="'.$sCssClasses.'" style="max-width: '.$sMaxWidthPx.'; max-height: '.$sMaxHeightPx.'; aspect-ratio: '.$iMaxWidth.' / '.$iMaxHeight.'">'.$sRet.'</div>';
return '<div class="'.$sCssClasses.'" style="max-width: min('.$sMaxWidthPx.',100%); max-height: min('.$sMaxHeightPx.',100%); aspect-ratio: '.$iMaxWidth.' / '.$iMaxHeight.'">'.$sRet.'</div>';
}
/**
@@ -8621,7 +8722,7 @@ class AttributeImage extends AttributeBlob
* @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.'">';
return '<img src="'.$sUrl.'" style="max-width: min('.$iMaxWidthPx.',100%); max-height: min('.$iMaxHeightPx.',100%)">';
}
/**
@@ -8649,7 +8750,7 @@ class AttributeImage extends AttributeBlob
return 'data:'.$value->GetMimeType().';base64,'.base64_encode($value->GetData());
}
return $value->GetDownloadURL(get_class($oHostObject), $oHostObject->GetKey(), $this->GetCode());
return $value->GetDisplayURL(get_class($oHostObject), $oHostObject->GetKey(), $this->GetCode());
}
public static function GetFormFieldClass()
@@ -8676,8 +8777,11 @@ class AttributeImage extends AttributeBlob
}
else
{
$oFormField->SetDownloadUrl($this->Get('default_image'));
$oFormField->SetDisplayUrl($this->Get('default_image'));
$oDefaultImage = $this->Get('default_image');
if (is_object($oDefaultImage) && !$oDefaultImage->IsEmpty()) {
$oFormField->SetDownloadUrl($oDefaultImage);
$oFormField->SetDisplayUrl($oDefaultImage);
}
}
return $oFormField;
@@ -11256,6 +11360,9 @@ class AttributeClassAttCodeSet extends AttributeSet
}
$aAllowedAttributes[$sAttCode] = $sLabel;
}
// N°6460 Always sort on the labels, not on the datamodel definition order
natcasesort($aAllowedAttributes);
return $aAllowedAttributes;
}
@@ -13048,6 +13155,7 @@ class AttributeCustomFields extends AttributeDefinition
public function GetHandler($aValues = null)
{
$sHandlerClass = $this->Get('handler_class');
/** @var \TemplateFieldsHandler $oHandler */
$oHandler = new $sHandlerClass($this->GetCode());
if (!is_null($aValues))
{
@@ -13072,36 +13180,50 @@ class AttributeCustomFields extends AttributeDefinition
/**
* Makes the string representation out of the values given by the form defined in GetDisplayForm
*/
public function ReadValueFromPostedForm($oHostObject, $sFormPrefix)
{
$aRawData = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->GetCode()}", '{}', 'raw_data'), true);
public function ReadValueFromPostedForm($oHostObject, $sFormPrefix) {
$aRawData = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->GetCode()}", '{}', 'raw_data'), true);
if ($aRawData != null) {
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aRawData);
} else{
}
else {
return null;
}
}
public function MakeRealValue($proposedValue, $oHostObject)
{
if (is_object($proposedValue) && ($proposedValue instanceof ormCustomFieldsValue))
{
public function MakeRealValue($proposedValue, $oHostObject) {
if (is_object($proposedValue) && ($proposedValue instanceof ormCustomFieldsValue)) {
if (false === $oHostObject->IsNew()) {
// In that case we need additional keys : see \TemplateFieldsHandler::DoBuildForm
$aRequestTemplateValues = $proposedValue->GetValues();
if (false === array_key_exists('current_template_id', $aRequestTemplateValues)) {
$aRequestTemplateValues['current_template_id'] = $aRequestTemplateValues['template_id'];
$aRequestTemplateValues['current_template_data'] = $aRequestTemplateValues['template_data'];
$proposedValue = new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aRequestTemplateValues);
}
}
if (is_null($proposedValue->GetHostObject())) {
// the object might not be set : for example in \AttributeCustomFields::FromJSONToValue we don't have the object available :(
$proposedValue->SetHostObject($oHostObject);
}
return $proposedValue;
}
elseif (is_string($proposedValue))
{
if (is_string($proposedValue)) {
$aValues = json_decode($proposedValue, true);
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aValues);
}
elseif (is_array($proposedValue))
{
if (is_array($proposedValue)) {
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $proposedValue);
}
elseif (is_null($proposedValue))
{
if (is_null($proposedValue)) {
return new ormCustomFieldsValue($oHostObject, $this->GetCode());
}
throw new Exception('Unexpected type for the value of a custom fields attribute: '.gettype($proposedValue));
}
@@ -13226,27 +13348,11 @@ class AttributeCustomFields extends AttributeDefinition
*/
public function CheckValue(DBObject $oHostObject, $value)
{
try
{
try {
$oHandler = $this->GetHandler($value->GetValues());
$oHandler->BuildForm($oHostObject, '');
$oForm = $oHandler->GetForm();
$oForm->Validate();
if ($oForm->GetValid())
{
$ret = true;
}
else
{
$aMessages = array();
foreach($oForm->GetErrorMessages() as $sFieldId => $aFieldMessages)
{
$aMessages[] = $sFieldId.': '.implode(', ', $aFieldMessages);
}
$ret = 'Invalid value: '.implode(', ', $aMessages);
}
} catch (Exception $e)
{
$ret = $oHandler->Validate($oHostObject);
} catch (Exception $e) {
$ret = $e->getMessage();
}
@@ -13388,11 +13494,13 @@ class AttributeCustomFields extends AttributeDefinition
/**
* @inheritDoc
*
* @return ?\ormCustomFieldsValue
* @return ?\ormCustomFieldsValue with empty host object as we don't have it here (most consumers don't have an object in their context, for example in \RestUtils::GetObjectSetFromKey)
* The host object will be set in {@see MakeRealValue}
* All the necessary checks will be done in {@see CheckValue}
*/
public function FromJSONToValue($json)
{
return null;
return ormCustomFieldsValue::FromJSONToValue($json, $this);
}
public function Equals($val1, $val2)

View File

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

View File

@@ -37,6 +37,14 @@ abstract class CellChangeSpec
return $this->m_proposedValue;
}
/**
* @since 3.1.0 N°5305
*/
public function SetDisplayableValue(string $sDisplayableValue)
{
$this->m_proposedValue = $sDisplayableValue;
}
public function GetOql()
{
return $this->m_sOql;
@@ -136,6 +144,12 @@ class CellStatus_SearchIssue extends CellStatus_Issue
/** @var string|null $m_sTargetClass */
private $m_sTargetClass;
/**
* @since 3.1.0 N°5305
* @var string $sAllowedValuesSearch
*/
private $sAllowedValuesSearch;
/**
* CellStatus_SearchIssue constructor.
* @since 3.1.0 N°5305
@@ -144,13 +158,15 @@ class CellStatus_SearchIssue extends CellStatus_Issue
* @param string $sReason : main message
* @param null $sClass : used for additional message that provides allowed values for current class $sClass
* @param null $sAllowedValues : used for additional message that provides allowed values $sAllowedValues for current class
* @param string|null $sAllowedValuesSearch : used to search all allowed values
*/
public function __construct($sSerializedSearch, $sReason, $sClass=null, $sAllowedValues=null)
public function __construct($sSerializedSearch, $sReason, $sClass=null, $sAllowedValues=null, string $sAllowedValuesSearch=null)
{
parent::__construct(null, null, $sReason);
$this->sSerializedSearch = $sSerializedSearch;
$this->m_sAllowedValues = $sAllowedValues;
$this->m_sTargetClass = $sClass;
$this->sAllowedValuesSearch = $sAllowedValuesSearch;
}
public function GetDisplayableValue()
@@ -182,6 +198,17 @@ class CellStatus_SearchIssue extends CellStatus_Issue
rawurlencode($this->sSerializedSearch)
);
}
/**
* @since 3.1.0 N°5305
* @return null|string
*/
public function GetAllowedValuesLinkUrl(): ?string
{
return sprintf("UI.php?operation=search&filter=%s",
rawurlencode($this->sAllowedValuesSearch)
);
}
}
class CellStatus_NullIssue extends CellStatus_Issue
@@ -745,6 +772,7 @@ class BulkChange
$oDbSearchWithoutAnyCondition->AllowAllData(false);
$oExtObjectSetWithCurrentUserPermissions = new CMDBObjectSet($oDbSearchWithoutAnyCondition);
$iCurrentUserRightsObjectCount = $oExtObjectSetWithCurrentUserPermissions->Count();
$sAllowedValuesOql = $oDbSearchWithoutAnyCondition->serialize();
if ($iCurrentUserRightsObjectCount === 0){
// No objects visible by current user
@@ -785,7 +813,7 @@ class BulkChange
if ($iAllowAllDataObjectCount != $iCurrentUserRightsObjectCount) {
// No match and some objects NOT visible by current user. including current search maybe...
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-SomeObjectNotVisibleForCurrentUser', $oDbSearchWithConditions->GetClass());
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues);
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues, $sAllowedValuesOql);
}
// No match. This is not linked to any right issue
@@ -796,7 +824,7 @@ class BulkChange
}
$value =implode(" ", $aCurrentValueFields);
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch', $value);
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues);
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues, $sAllowedValuesOql);
}
protected function PrepareMissingObject(&$oTargetObj, &$aErrors)

View File

@@ -19,17 +19,17 @@ class CMDBChange extends DBObject
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "date",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_change",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "date",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_change",
"db_key_field" => "id",
"db_finalclass_field" => "",
'indexes' => array(
'indexes' => array(
array('origin'),
)
),
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();

View File

@@ -52,17 +52,17 @@ class CMDBChangeOp extends DBObject implements iCMDBChangeOp
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop",
"db_key_field" => "id",
"db_finalclass_field" => "optype",
'indexes' => array(
'indexes' => array(
array('objclass', 'objkey'),
)
),
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
@@ -121,13 +121,13 @@ class CMDBChangeOpCreate extends CMDBChangeOp
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_create",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_create",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -157,13 +157,13 @@ class CMDBChangeOpDelete extends CMDBChangeOp
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_delete",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_delete",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -198,13 +198,13 @@ class CMDBChangeOpSetAttribute extends CMDBChangeOp
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -231,13 +231,13 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_scalar",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_scalar",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -290,14 +290,14 @@ class CMDBChangeOpSetAttributeTagSet extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_tagset",
"db_key_field" => "id",
"db_finalclass_field" => "",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_tagset",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
@@ -350,7 +350,7 @@ class CMDBChangeOpSetAttributeURL extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
@@ -419,13 +419,13 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_data",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_data",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -498,13 +498,13 @@ class CMDBChangeOpSetAttributeOneWayPassword extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_pwd",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_pwd",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -561,13 +561,13 @@ class CMDBChangeOpSetAttributeEncrypted extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_encrypted",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_encrypted",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -627,13 +627,13 @@ class CMDBChangeOpSetAttributeText extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_text",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_text",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -695,13 +695,13 @@ class CMDBChangeOpSetAttributeLongText extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_longtext",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_longtext",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -760,13 +760,13 @@ class CMDBChangeOpSetAttributeHTML extends CMDBChangeOpSetAttributeLongText
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_html",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_html",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -824,13 +824,13 @@ class CMDBChangeOpSetAttributeCaseLog extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_log",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_log",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -903,13 +903,13 @@ class CMDBChangeOpPlugin extends CMDBChangeOp
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_plugin",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_plugin",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -944,13 +944,13 @@ abstract class CMDBChangeOpSetAttributeLinks extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -977,13 +977,13 @@ class CMDBChangeOpSetAttributeLinksAddRemove extends CMDBChangeOpSetAttributeLin
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links_addremove",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links_addremove",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -1044,13 +1044,13 @@ class CMDBChangeOpSetAttributeLinksTune extends CMDBChangeOpSetAttributeLinks
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links_tune",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links_tune",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -1134,13 +1134,13 @@ class CMDBChangeOpSetAttributeCustomFields extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_custfields",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_custfields",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);

View File

@@ -469,7 +469,10 @@ abstract class CMDBObject extends DBObject
*/
public function DBDelete(&$oDeletionPlan = null)
{
return $this->DBDeleteTracked_Internal($oDeletionPlan);
$this->LogCRUDEnter(__METHOD__);
$oDeletionPlan = $this->DBDeleteTracked_Internal($oDeletionPlan);
$this->LogCRUDExit(__METHOD__);
return $oDeletionPlan;
}
/**

View File

@@ -137,7 +137,7 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'log_purge.max_keep_days' => [
'log_purge.max_keep_days' => [
'type' => 'integer',
'description' => 'Optional purge number of days to keep logs.',
'default' => 365,
@@ -145,7 +145,7 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'event_service.debug.filter_events' => [
'event_service.debug.filter_events' => [
'type' => 'array',
'description' => 'List of events name to filter Event Service debug messages',
'default' => [],
@@ -153,7 +153,7 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'event_service.debug.filter_sources' => [
'event_service.debug.filter_sources' => [
'type' => 'array',
'description' => 'List of event sources to filter Event Service debug messages',
'default' => '',
@@ -161,6 +161,38 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'temporary_object.force_creation' => [
'type' => 'bool',
'description' => 'If true, all the objects created by the external key are temporary',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'temporary_object.lifetime' => [
'type' => 'integer',
'description' => 'Seconds for temporary objects created',
'default' => 300,
'value' => 300,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'temporary_object.watchdog_interval' => [
'type' => 'integer',
'description' => 'Seconds between watchdog signals',
'default' => 60,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'temporary_object.garbage_interval' => [
'type' => 'integer',
'description' => 'Seconds between garbage collections',
'default' => 60,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'app_env_label' => [
'type' => 'string',
'description' => 'Label displayed to describe the current application environment, defaults to the environment name (e.g. "production")',
@@ -185,7 +217,7 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'db_host' => [
'db_host' => [
'type' => 'string',
'default' => null,
'value' => '',
@@ -483,7 +515,7 @@ class Config
'synchro_obsolete_replica_locks_object' => [
'type' => 'bool',
'description' => 'Obsolete synchro replicas prevent object modification by any mean (eg. anonymization)',
'default' => 'true',
'default' => true,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => true,
@@ -946,6 +978,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'lifecycle.transitions_sort_type' => [
'type' => 'string',
'description' => 'How transitions will be sorted in the GUI. Possible values are "xml", "alphabetical", "fixed" or "relative"',
'default' => DBObject::DEFAULT_TRANSITIONS_SORT_TYPE,
'value' => DBObject::DEFAULT_TRANSITIONS_SORT_TYPE,
'source_of_value' => '',
'show_in_conf_sample' => true,
],
'url_validation_pattern' => [
'type' => 'string',
'description' => 'Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)',
@@ -1630,8 +1670,8 @@ class Config
'audit.enable_selection_landing_page' => [
'type' => 'bool',
'description' => 'If true audit categories must be selected before results are computed (use this setting in case of a lot of audit categories)',
'default' => false,
'value' => false,
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
],

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.
@@ -58,6 +58,16 @@ class ContextTag
public const TAG_SETUP = 'Setup';
public const TAG_SYNCHRO = 'Synchro';
public const TAG_REST = 'REST/JSON';
/**
* @since 3.1.0 N°6047
*/
public const TAG_IMPORT = 'Import';
/**
* @since 3.1.0 N°6047
*/
public const TAG_EXPORT = 'Export';
/**
* @var string
* @since 3.1.0 N°3200
@@ -75,7 +85,7 @@ class ContextTag
{
static::$aStack[] = $sTag;
}
public static function AddContext($sTag)
{
static::$aStack[] = $sTag;

View File

@@ -137,7 +137,7 @@ class CSVBulkExport extends TabularBulkExport
$aSep['other'] = Dict::S('UI:CSVImport:SeparatorOther').' <input type="text" size="3" name="other-separator" value="'.utils::EscapeHtml($sOtherSeparator).'"/>';
foreach ($aSep as $sVal => $sLabel) {
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "separator", utils::EscapeHtml($sVal), $sLabel, "radio");
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "separator", $sVal, $sLabel, "radio");
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawSeparator));
$oRadio->SetBeforeInput(false);
$oRadio->GetInput()->AddCSSClass('ibo-input--label-right');
@@ -163,8 +163,8 @@ class CSVBulkExport extends TabularBulkExport
$aQualifiers['other'] = Dict::S('UI:CSVImport:QualifierOther').' <input type="text" size="3" name="other-text-qualifier" value="'.utils::EscapeHtml($sOtherQualifier).'"/>';
foreach ($aQualifiers as $sVal => $sLabel) {
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "text-qualifier", utils::EscapeHtml($sVal), $sLabel, "radio");
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawSeparator));
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "text-qualifier", $sVal, $sLabel, "radio");
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawQualifier));
$oRadio->SetBeforeInput(false);
$oRadio->GetInput()->AddCSSClass('ibo-input--label-right');
$oRadio->GetInput()->AddCSSClass('ibo-input-checkbox');

View File

@@ -23,10 +23,22 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class CustomFieldsHandler
{
abstract class CustomFieldsHandler {
/** @var string $sAttCode */
protected $sAttCode;
/** @var array{
* legacy: int,
* extradata_id: string,
* _template_name: string,
* template_id: string,
* template_data: string,
* user_data: array<string, mixed>,
* current_template_id: string,
* current_template_data: string,
* } $aValues same as {@see \ormCustomFieldsValue::$aCurrentValues}
*/
protected $aValues;
/** @var \Combodo\iTop\Form\Form $oForm */
protected $oForm;
/**
@@ -35,20 +47,47 @@ abstract class CustomFieldsHandler
*
* @param $sAttCode
*/
final public function __construct($sAttCode)
{
final public function __construct($sAttCode) {
$this->sAttCode = $sAttCode;
$this->aValues = null;
}
abstract public function BuildForm(DBObject $oHostObject, $sFormId);
/**
* @returns true|string true if no error found, error message otherwise
* @throws \ApplicationException if {@link static::$oForm} attribute not initialized yet
* @since 3.1.0 N°6322 N°1150 Add template_id checks
*/
public function Validate(DBObject $oHostObject) {
if (false === isset($this->oForm)) {
throw new ApplicationException('oForm attribute not init yet. You must call BuildForm before this method !');
}
try {
$this->oForm->Validate();
if ($this->oForm->GetValid()) {
$ret = true;
}
else {
$aMessages = array();
foreach ($this->oForm->GetErrorMessages() as $sFieldId => $aFieldMessages) {
$aMessages[] = $sFieldId.': '.implode(', ', $aFieldMessages);
}
$ret = 'Invalid value: '.implode(', ', $aMessages);
}
} catch (Exception $e) {
$ret = $e->getMessage();
}
return $ret;
}
/**
*
* @return \Combodo\iTop\Form\Form
*/
public function GetForm()
{
public function GetForm() {
return $this->oForm;
}
@@ -57,16 +96,14 @@ abstract class CustomFieldsHandler
$this->aValues = $aValues;
}
static public function GetPrerequisiteAttributes($sClass = null)
{
public static function GetPrerequisiteAttributes($sClass = null) {
return array();
}
/**
* List the available verbs for 'GetForTemplate'
*/
static public function EnumTemplateVerbs()
{
public static function EnumTemplateVerbs() {
return array();
}
@@ -118,6 +155,21 @@ abstract class CustomFieldsHandler
return null;
}
/**
* @param \stdClass|null $json
* @param string $sAttCode
*
* @return \ormCustomFieldsValue|null
*
* @since 3.1.0 N°1150 Method creation
*/
public function FromJSONToValue(?stdClass $json, string $sAttCode): ?ormCustomFieldsValue
{
// Default impl doing nothing, to avoid errors on children not having this method
return null;
}
/**
* @param DBObject $oHostObject
*

View File

@@ -58,6 +58,7 @@
<parent>cmdbAbstractObject</parent>
<properties>
<category>addon/userrights,grant_by_profile</category>
<is_link>1</is_link>
</properties>
<fields>
<field id="userid" xsi:type="AttributeExternalKey">
@@ -482,6 +483,18 @@
<type>boolean</type>
<default>true</default>
</property>
<property id="with_php_constraint">
<php_param>with_php_constraint</php_param>
<mandatory>false</mandatory>
<type>boolean</type>
<default>false</default>
</property>
<property id="create_temporary_object">
<php_param>create_temporary_object</php_param>
<mandatory>false</mandatory>
<type>boolean</type>
<default>false</default>
</property>
<property id="on_target_delete">
<php_param>on_target_delete</php_param>
<mandatory>false</mandatory>

File diff suppressed because it is too large Load Diff

View File

@@ -400,7 +400,7 @@ class DBObjectSearch extends DBSearch
}
/**
* Important: If you need to add a condition on the same $sFilterCode several times with different $value values; do not use this method as the previous $value occurences will be replaced by the last. Instead use:
* Important: If you need to add a condition on the same $sFilterCode several times with different $value values; do not use this method as the previous $value occurrences will be replaced by the last. Instead use:
* * {@see \DBObjectSearch::AddConditionExpression()} in loops to add conditions one by one
* * {@see \DBObjectSearch::AddConditionForInOperatorUsingParam()} for IN/NOT IN queries with lots of params at once
*

View File

@@ -224,6 +224,10 @@ class DBUnionSearch extends DBSearch
}
/**
* Set the selected classes for this query.
* The selected classes can be either in the selected classes of all the queries,
* or they should exist in all the sub-queries of the union.
*
* @param array $aSelectedClasses array of aliases
*
* @throws \Exception
@@ -236,22 +240,31 @@ class DBUnionSearch extends DBSearch
$aAliasesToColumn = array_flip(array_keys($oFirstSearch->GetSelectedClasses()));
foreach ($aSelectedClasses as $sSelectedAlias) {
if (!isset($aAliasesToColumn[$sSelectedAlias])) {
throw new CoreException("SetSelectedClasses: Invalid class alias $sSelectedAlias");
// The selected class is not in the selected classes of the union,
// try to delegate the feature to the sub-queries
$aSelectedColumns = [];
break;
}
$aSelectedColumns[] = $aAliasesToColumn[$sSelectedAlias];
}
// 1 - change for each search
foreach ($this->aSearches as $iPos => $oSearch)
{
foreach ($this->aSearches as $iPos => $oSearch) {
$aCurrentSelectedAliases = [];
foreach ($aSelectedColumns as $iColumn) {
$aCurrentSelectedAliases[] = $this->aColumnToAliases[$iColumn][$iPos];
if (count($aSelectedColumns) === 0) {
// Default to the list of aliases given
$aCurrentSelectedAliases = $aSelectedClasses;
} else {
// Map the aliases for each query
foreach ($aSelectedColumns as $iColumn) {
$aCurrentSelectedAliases[] = $this->aColumnToAliases[$iColumn][$iPos];
}
}
// Throws an exception if not valid
$oSearch->SetSelectedClasses($aCurrentSelectedAliases);
}
// 2 - update the lowest common ancestors
$this->ComputeSelectedClasses();
}

View File

@@ -106,10 +106,10 @@ class Dict
}
/**
* Returns a localised string from the dictonary
* Returns a localised string from the dictionary
*
* @param string $sStringCode The code identifying the dictionary entry
* @param string $sDefault Default value if there is no match in the dictionary
* @param string $sDefault Default value if there is no match in the dictionary, if no default provided, returns $sStringCode unchanged
* @param bool $bUserLanguageOnly False to allow the use of the default language as a fallback, true otherwise
*
* @return string

View File

@@ -583,6 +583,12 @@ class LogChannels
*/
public const DM_CRUD = 'DMCRUD';
/**
* @var string Everything related to the datamodel CRUD
* @since 3.1.0
*/
public const WEB_REQUEST = 'WebRequest';
/**
* @var string Everything related to the event service
* @since 3.1.0
@@ -606,6 +612,8 @@ class LogChannels
public const PORTAL = 'portal';
public const TEMPORARY_OBJECTS = 'TemporaryObjects';
/**
* @var string
* @since 3.1.0
@@ -1230,7 +1238,9 @@ class DeprecatedCallsLog extends LogAPI
}
/**
* @throws \ConfigException
* @since 3.0.1 3.1.0 N°4725 silently handles ConfigException
* @since 3.0.4 3.1.0 N°4725 remove forgotten throw PHPDoc annotation
*
* @link https://www.php.net/debug_backtrace
* @uses \debug_backtrace()
*/
@@ -1404,7 +1414,7 @@ class LogFileRotationProcess implements iScheduledProcess
$iMaxDays = MetaModel::GetConfig()->Get(LogAPI::ENUM_CONFIG_PARAM_PURGE_MAX_KEEP_DAYS);
// Files iterator (*.*)
$oIterator = new \GlobIterator(APPROOT.'log'.DIRECTORY_SEPARATOR.'/*.*');
$oIterator = new \GlobIterator(APPROOT.'log'.DIRECTORY_SEPARATOR.'*.*');
$aLogFiles = iterator_to_array($oIterator);
// Reference date
@@ -1416,6 +1426,11 @@ class LogFileRotationProcess implements iScheduledProcess
// File real path
$sFileRealPath = $oLogFile->getRealPath();
// Check file extension
if(!in_array($oLogFile->getExtension(), ['log','sql','xml'])){
continue;
}
// Compute number of days since last modification
$oDateFileLastModification = new DateTime();
$oDateFileLastModification->setTimestamp($oLogFile->getMTime());

View File

@@ -17,7 +17,10 @@
// along with iTop. If not, see <http://www.gnu.org/licenses/>
//
use Combodo\iTop\Application\EventRegister\ApplicationEvents;
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
use Combodo\iTop\Service\Events\EventData;
use Combodo\iTop\Service\Events\EventService;
require_once APPROOT.'core/modulehandler.class.inc.php';
require_once APPROOT.'core/querymodifier.class.inc.php';
@@ -2924,52 +2927,7 @@ abstract class MetaModel
}
self::$m_sTablePrefix = $sTablePrefix;
// Build the list of available extensions
//
$aInterfaces = [
'iApplicationUIExtension',
'iPreferencesExtension',
'iApplicationObjectExtension',
'iLoginFSMExtension',
'iLoginUIExtension',
'iLogoutExtension',
'iQueryModifier',
'iOnClassInitialization',
'iPopupMenuExtension',
'iPageUIExtension',
'iPageUIBlockExtension',
'iBackofficeLinkedScriptsExtension',
'iBackofficeEarlyScriptExtension',
'iBackofficeScriptExtension',
'iBackofficeInitScriptExtension',
'iBackofficeReadyScriptExtension',
'iBackofficeLinkedStylesheetsExtension',
'iBackofficeStyleExtension',
'iBackofficeDictEntriesExtension',
'iBackofficeDictEntriesPrefixesExtension',
'iPortalUIExtension',
'ModuleHandlerApiInterface',
'iNewsroomProvider',
'iModuleExtension',
];
foreach($aInterfaces as $sInterface)
{
self::$m_aExtensionClassNames[$sInterface] = array();
}
foreach(get_declared_classes() as $sPHPClass)
{
$oRefClass = new ReflectionClass($sPHPClass);
$oExtensionInstance = null;
foreach($aInterfaces as $sInterface)
{
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable())
{
self::$m_aExtensionClassNames[$sInterface][$sPHPClass] = $sPHPClass;
}
}
}
self::InitExtensions();
// Initialize the classes (declared attributes, etc.)
//
@@ -3618,8 +3576,7 @@ abstract class MetaModel
{
MyHelpers::CheckKeyInArray('list code', $sListCode, self::$m_aListInfos);
if (!$sTargetClass)
{
if (!$sTargetClass) {
$sTargetClass = self::GetCallersPHPClass("Init");
}
@@ -6343,36 +6300,37 @@ abstract class MetaModel
{
self::$m_sEnvironment = $sEnvironment;
if (!defined('MODULESROOT'))
{
define('MODULESROOT', APPROOT.'env-'.self::$m_sEnvironment.'/');
try {
if (!defined('MODULESROOT')) {
define('MODULESROOT', APPROOT.'env-'.self::$m_sEnvironment.'/');
self::$m_bTraceSourceFiles = $bTraceSourceFiles;
self::$m_bTraceSourceFiles = $bTraceSourceFiles;
// $config can be either a filename, or a Configuration object (volatile!)
if ($config instanceof Config)
{
self::LoadConfig($config, $bAllowCache);
}
else
{
self::LoadConfig(new Config($config), $bAllowCache);
// $config can be either a filename, or a Configuration object (volatile!)
if ($config instanceof Config) {
self::LoadConfig($config, $bAllowCache);
} else {
self::LoadConfig(new Config($config), $bAllowCache);
}
if ($bModelOnly) {
return;
}
}
if ($bModelOnly)
{
return;
CMDBSource::SelectDB(self::$m_sDBName);
foreach (MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass) {
$oPHPClass::OnMetaModelStarted();
}
ExpressionCache::Warmup();
}
finally {
// Event service must be initialized after the MetaModel startup, otherwise it cannot discover classes implementing the iEventServiceSetup interface
EventService::InitService();
EventService::FireEvent(new EventData(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED));
}
CMDBSource::SelectDB(self::$m_sDBName);
foreach(MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass)
{
$oPHPClass::OnMetaModelStarted();
}
ExpressionCache::Warmup();
}
/**
@@ -7639,6 +7597,56 @@ abstract class MetaModel
unset(self::$m_aReentranceProtection[get_class($oObject)][$oObject->GetKey()]);
}
}
/**
* For test purpose
* @throws \ReflectionException
* @since 3.1.0
*/
public static function InitExtensions()
{
// Build the list of available extensions
//
$aInterfaces = [
'iApplicationUIExtension',
'iPreferencesExtension',
'iApplicationObjectExtension',
'iLoginFSMExtension',
'iLoginUIExtension',
'iLogoutExtension',
'iQueryModifier',
'iOnClassInitialization',
'iPopupMenuExtension',
'iPageUIExtension',
'iPageUIBlockExtension',
'iBackofficeLinkedScriptsExtension',
'iBackofficeEarlyScriptExtension',
'iBackofficeScriptExtension',
'iBackofficeInitScriptExtension',
'iBackofficeReadyScriptExtension',
'iBackofficeLinkedStylesheetsExtension',
'iBackofficeStyleExtension',
'iBackofficeDictEntriesExtension',
'iBackofficeDictEntriesPrefixesExtension',
'iPortalUIExtension',
'ModuleHandlerApiInterface',
'iNewsroomProvider',
'iModuleExtension',
];
foreach ($aInterfaces as $sInterface) {
self::$m_aExtensionClassNames[$sInterface] = array();
}
foreach (get_declared_classes() as $sPHPClass) {
$oRefClass = new ReflectionClass($sPHPClass);
$oExtensionInstance = null;
foreach ($aInterfaces as $sInterface) {
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable()) {
self::$m_aExtensionClassNames[$sInterface][$sPHPClass] = $sPHPClass;
}
}
}
}
}

View File

@@ -3526,6 +3526,7 @@ class CharConcatWSExpression extends CharConcatExpression
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
// TODO: Seems weird, this should rather be $aRes[] = $oExpr->Evaluate($aArgs);
$aRes .= $oExpr->Evaluate($aArgs);
}
return implode($this->m_separator, $aRes);

View File

@@ -35,10 +35,11 @@ class ormCustomFieldsValue
* _template_name: string,
* template_id: string,
* template_data: string,
* user_data: string,
* user_data: array<string, mixed>,
* current_template_id: string,
* current_template_data: string,
* } $aCurrentValues Containing JSON encoded strings in template_data/current_template_data, user_data.
* } $aCurrentValues Containing JSON encoded strings in template_data/current_template_data.
* The user_data key contains an array with field code as key and field value as value
* Warning, current_* are mandatory for data to be saved in a DBUpdate() call !
*/
protected $aCurrentValues;
@@ -48,13 +49,31 @@ class ormCustomFieldsValue
* @param string $sAttCode
* @param array $aCurrentValues
*/
public function __construct(DBObject $oHostObject, $sAttCode, $aCurrentValues = null)
public function __construct(?DBObject $oHostObject, $sAttCode, $aCurrentValues = null)
{
$this->oHostObject = $oHostObject;
$this->sAttCode = $sAttCode;
$this->aCurrentValues = $aCurrentValues;
}
/**
* @return \DBObject|null
*/
public function GetHostObject(): ?DBObject
{
return $this->oHostObject;
}
/**
* @param \DBObject|null $oHostObject
*
* @return void
*/
public function SetHostObject(?DBObject $oHostObject): void
{
$this->oHostObject = $oHostObject;
}
public function GetValues()
{
return $this->aCurrentValues;
@@ -62,6 +81,7 @@ class ormCustomFieldsValue
/**
* Wrapper used when the only thing you have is the value...
*
* @return \Combodo\iTop\Form\Form
*/
public function GetForm($sFormPrefix = null)
@@ -96,6 +116,19 @@ class ormCustomFieldsValue
return $this->GetHandler()->GetAsJSON($this->aCurrentValues);
}
/**
* @param string|null $json
* @param \AttributeDefinition $oAttDef
*
* @return \ormCustomFieldsValue
*
* @since 3.1.0 N°1150 Method creation
*/
public static function FromJSONToValue(?stdClass $json, AttributeCustomFields $oAttDef)
{
return $oAttDef->GetHandler()->FromJSONToValue($json, $oAttDef->GetCode());
}
/**
* @return \CustomFieldsHandler
* @throws \Exception
@@ -103,6 +136,7 @@ class ormCustomFieldsValue
*/
final protected function GetHandler()
{
/** @var \AttributeCustomFields $oAttDef */
$oAttDef = MetaModel::GetAttributeDef(get_class($this->oHostObject), $this->sAttCode);
return $oAttDef->GetHandler($this->GetValues());

View File

@@ -86,6 +86,33 @@ class ormDocument
{
return ($this->m_data == null);
}
/**
* @param \ormDocument $oCompared
*
* @return bool True if the current ormDocument is equals to $oCompared EXCEPT for its download count. False if any other property is different OR if count is the same.
* @since 3.1.0 N°6502
*/
public function EqualsExceptDownloadsCount(ormDocument $oCompared): bool
{
// First checking equality on others properties
if ($oCompared->GetData() !== $this->GetData()) {
return false;
}
if ($oCompared->GetMimeType() !== $this->GetMimeType()) {
return false;
}
if ($oCompared->GetFileName() !== $this->GetFileName()) {
return false;
}
// Finally check equality of the download count
if ($oCompared->GetDownloadsCount() === $this->GetDownloadsCount()) {
return false;
} else {
return true;
}
}
public function GetMimeType()
{

View File

@@ -98,4 +98,14 @@ class PluginManager
return $oInstance;
}
/**
* For test purpose
* @return void
* @since 3.1.0
*/
protected static function ResetPlugins()
{
self::$m_aExtensionClasses = null;
}
}

View File

@@ -43,6 +43,12 @@
class SimpleCrypt
{
/**
* @var \SimpleCrypt
* @since 3.1.0 N°5388
*/
protected $oEngine;
public static function GetNewDefaultParams()
{
if(function_exists('sodium_crypto_secretbox_open') && function_exists('random_bytes')){
@@ -62,6 +68,7 @@ class SimpleCrypt
$sEngineName = 'SimpleCrypt' . $sEngineName . 'Engine';
return $sEngineName::GetNewDefaultParams();
}
/**
* Constructor
* @param string $sEngineName Engine for encryption. Values: Simple, Mcrypt, Sodium or OpenSSL

View File

@@ -218,8 +218,8 @@ class SQLObjectQueryBuilder
continue;
}
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
// Skip this attribute if not made of SQL columns
if (count($oAttDef->GetSQLExpressions()) == 0)
// Skip this attribute if not made of SQL columns nor in current table
if (count($oAttDef->GetSQLExpressions()) == 0 || $oAttDef->IsExternalField())
{
continue;
}

View File

@@ -50,7 +50,7 @@ abstract class Trigger extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("action_list",
array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "trigger_id", "ext_key_to_remote" => "action_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array())));
$aTags = ContextTag::GetTags();
MetaModel::Init_AddAttribute(new AttributeEnumSet("context", array("allowed_values" => null, "possible_values" => new ValueSetEnumPadded($aTags), "sql" => "context", "depends_on" => array(), "is_null_allowed" => true, "max_items" => 12)));
MetaModel::Init_AddAttribute(new AttributeEnumSet("context", array("allowed_values" => null, "possible_values" => new ValueSetEnumPadded($aTags, true), "sql" => "context", "depends_on" => array(), "is_null_allowed" => true, "max_items" => 12)));
// "complement" is a computed field, fed by Trigger sub-classes, in general in ComputeValues method, for eg. the TriggerOnObject fed it with target_class info
MetaModel::Init_AddAttribute(new AttributeString("complement", array("allowed_values" => null, "sql" => "complement", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));

View File

@@ -235,15 +235,25 @@ abstract class User extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeString("login", array("allowed_values" => null, "sql" => "login", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", array("sql"=>"language", "default_value"=>"EN US", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values" => new ValueSetEnum('enabled,disabled'), "styled_values"=>['enabled' => new ormStyle('ibo-dm-enum--User-status-enabled', 'ibo-dm-enum-alt--User-status-enabled', 'var(--ibo-dm-enum--User-status-enabled--main-color)', 'var(--ibo-dm-enum--User-status-enabled--complementary-color)', null, null),'disabled' => new ormStyle('ibo-dm-enum--User-status-disabled', 'ibo-dm-enum-alt--User-status-disabled', 'var(--ibo-dm-enum--User-status-disabled--main-color)', 'var(--ibo-dm-enum--User-status-disabled--complementary-color)', null, null)], "sql"=>"status", "default_value"=>"enabled", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", array("sql" => "language", "default_value" => "EN US", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array(
"allowed_values" => new ValueSetEnum('enabled,disabled'),
"styled_values" => [
'enabled' => new ormStyle('ibo-dm-enum--User-status-enabled', 'ibo-dm-enum-alt--User-status-enabled', 'var(--ibo-dm-enum--User-status-enabled--main-color)', 'var(--ibo-dm-enum--User-status-enabled--complementary-color)', null, null),
'disabled' => new ormStyle('ibo-dm-enum--User-status-disabled', 'ibo-dm-enum-alt--User-status-disabled', 'var(--ibo-dm-enum--User-status-disabled--main-color)', 'var(--ibo-dm-enum--User-status-disabled--complementary-color)', null, null),
],
"sql" => "status",
"default_value" => "enabled",
"is_null_allowed" => false,
"depends_on" => array(),
)));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("profile_list",
array("linked_class" => "URP_UserProfile", "ext_key_to_me" => "userid", "ext_key_to_remote" => "profileid", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array(), "display_style" => 'property')));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("allowed_org_list", array("linked_class" => "URP_UserOrg", "ext_key_to_me" => "userid", "ext_key_to_remote" => "allowed_org_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("profile_list",array("linked_class" => "URP_UserProfile", "ext_key_to_me" => "userid", "ext_key_to_remote" => "profileid", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array(), "display_style" => 'property', "with_php_constraint" => true)));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("allowed_org_list", array("linked_class" => "URP_UserOrg", "ext_key_to_me" => "userid", "ext_key_to_remote" => "allowed_org_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array(), 'with_php_constraint' => true)));
MetaModel::Init_AddAttribute(new AttributeCaseLog("log", array("sql" => 'log', "is_null_allowed" => true, "default_value" => '', "allowed_values" => null, "depends_on" => array(), "always_load_in_tables" => false)));
// Display lists
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list')); // Unused as it's an abstract class !
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list', 'log')); // Unused as it's an abstract class !
MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'status', 'org_id')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'email', 'language', 'status', 'org_id')); // Criteria of the std search form
@@ -640,23 +650,23 @@ abstract class UserInternal extends User
{
$aParams = array
(
"category" => "core,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",
"reconc_keys" => array('login'),
"db_table" => "priv_internaluser",
"db_key_field" => "id",
"category" => "core,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",
"reconc_keys" => array('login'),
"db_table" => "priv_internaluser",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// When set, this token allows for password reset
MetaModel::Init_AddAttribute(new AttributeOneWayPassword("reset_pwd_token", array("allowed_values"=>null, "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeOneWayPassword("reset_pwd_token", array("allowed_values" => null, "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'status', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'status', 'language', 'profile_list', 'allowed_org_list', 'log')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'status', 'org_id')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status', 'org_id')); // Criteria of the std search form

View File

@@ -53,7 +53,13 @@ abstract class ValueSetDefinition
return $sAllowedValues;
}
/**
* @param array $aArgs
* @param string $sContains
* @param string $sOperation for the values {@see static::LoadValues()}
*
* @return array hash array of keys => values
*/
public function GetValues($aArgs, $sContains = '', $sOperation = 'contains')
{
if (!$this->m_bIsLoaded)
@@ -78,11 +84,22 @@ abstract class ValueSetDefinition
}
}
}
// Sort on the display value
asort($aRet);
$this->SortValues($aRet);
return $aRet;
}
/**
* @param array $aValues Values to sort in the form keys => values
*
* @return void
* @since 3.1.0 N°1646 Create method
*/
public function SortValues(array &$aValues): void
{
// Sort alphabetically on values
natcasesort($aValues);
}
abstract protected function LoadValues($aArgs);
}
@@ -189,11 +206,7 @@ class ValueSetObjects extends ValueSetDefinition
}
/**
* @param $aArgs
* @param string $sContains
* @param string $sOperation for the values @see self::LoadValues()
*
* @return array
* @inheritDoc
* @throws CoreException
* @throws OQLException
*/
@@ -451,10 +464,33 @@ class ValueSetObjects extends ValueSetDefinition
class ValueSetEnum extends ValueSetDefinition
{
protected $m_values;
/**
* @var bool $bSortByValues If true, values will be sorted at runtime (on their values, not their keys), otherwise it is sorted at compile time in a predefined order.
* {@see \MFCompiler::CompileAttributeEnumValues()} for complete reasons.
* @since 3.1.0 N°1646
*/
protected bool $bSortByValues;
public function __construct($Values)
/**
* @param array|string $Values
* @param bool $bLocalizedSort
*
* @since 3.1.0 N°1646 Add $bLocalizedSort parameter
*/
public function __construct($Values, bool $bSortByValues = false)
{
$this->m_values = $Values;
$this->bSortByValues = $bSortByValues;
}
/**
* @see \ValueSetEnum::$bSortByValues
* @return bool
* @since 3.1.0 N°1646
*/
public function IsSortedByValues(): bool
{
return $this->bSortByValues;
}
// Helper to export the data model
@@ -464,6 +500,27 @@ class ValueSetEnum extends ValueSetDefinition
return $this->m_aValues;
}
/**
* @inheritDoc
* @since 3.1.0 N°1646 Overload method
*/
public function SortValues(array &$aValues): void
{
// Force sort by values only if necessary
if ($this->bSortByValues) {
natcasesort($aValues);
return;
}
// Don't sort values as we rely on the order defined during compilation
return;
}
/**
* @param array|string $aArgs
*
* @return true
*/
protected function LoadValues($aArgs)
{
if (is_array($this->m_values))
@@ -491,9 +548,13 @@ class ValueSetEnum extends ValueSetDefinition
class ValueSetEnumPadded extends ValueSetEnum
{
public function __construct($Values)
/**
* @inheritDoc
* @since 3.1.0 N°6448 Add $bSortByValues parameter
*/
public function __construct($Values, bool $bSortByValues = false)
{
parent::__construct($Values);
parent::__construct($Values, $bSortByValues);
if (is_string($Values))
{
$this->LoadValues(null);
@@ -505,6 +566,7 @@ class ValueSetEnumPadded extends ValueSetEnum
$aPaddedValues = array();
foreach ($this->m_aValues as $sKey => $sVal)
{
// Pad keys to the min. length required by the \AttributeSet
$sKey = str_pad($sKey, 3, '_', STR_PAD_LEFT);
$aPaddedValues[$sKey] = $sVal;
}
@@ -553,7 +615,7 @@ class ValueSetEnumClasses extends ValueSetEnum
public function __construct($sCategories = '', $sAdditionalValues = '')
{
$this->m_sCategories = $sCategories;
parent::__construct($sAdditionalValues);
parent::__construct($sAdditionalValues, true /* Classes are always sorted alphabetically */);
}
protected function LoadValues($aArgs)

View File

@@ -64,11 +64,22 @@ $ibo-field--enable-bulk--checkbox--margin-left: $ibo-spacing-300 !default;
word-break: break-word;
white-space: inherit;
}
& pre {
white-space: break-spaces;
}
}
}
/* We need the rule to keep picture inside the column */
&[data-attribute-type="AttributeImage"] {
> .ibo-field--value {
display: grid;
> span {
display: inherit;
}
}
}
}
/* Large field = Label on top, value below */

View File

@@ -1,5 +1,5 @@
<?php
/**
/**
* Spanish Localized data
*
* @copyright Copyright (C) 2010-2023 Combodo SARL
@@ -7,7 +7,6 @@
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
* @notas Utilizar codificación UTF-8 para mostrar acentos y otros caracteres especiales
*/
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'CAS:Error:UserNotAllowed' => 'Usuario no permitido',
'CAS:Login:SignIn' => 'Iniciar sesión con CAS',

View File

@@ -5,7 +5,6 @@
* @copyright Copyright (C) 2013 XXXXX
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('PL PL', 'Polish', 'Polski', array(
'CAS:Error:UserNotAllowed' => 'Użytkownik niedozwolony',
'CAS:Login:SignIn' => 'Zaloguj się za pomocą CAS',

View File

@@ -49,6 +49,11 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
protected function OnReadCredentials(&$iErrorCode)
{
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
// Not allowed if not already connected
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
if (empty(Session::Get('login_mode')) || Session::Get('login_mode') == static::LOGIN_MODE)
{
static::InitCASClient();
@@ -114,6 +119,10 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
if (Session::Get('login_mode') == static::LOGIN_MODE)
{
Session::Unset('phpCAS');
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
// don't display the login page
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
if ($iErrorCode != LoginWebPage::EXIT_CODE_MISSINGLOGIN)
{
$oLoginWebPage = new LoginWebPage();

View File

@@ -15,7 +15,7 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
/**
* Spanish Localized data
*
* @copyright Copyright (C) 2010-2023 Combodo SARL

View File

@@ -20,7 +20,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
@@ -30,11 +29,9 @@
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+
// Class:<class_name>/Stimulus:<stimulus_code>
// Class:<class_name>/Stimulus:<stimulus_code>+
//
// Class: UserExternal
//
Dict::Add('PL PL', 'Polish', 'Polski', array(
'Class:UserExternal' => 'Użytkownik zewnętrzny',
'Class:UserExternal+' => 'Użytkownik uwierzytelniony poza '.ITOP_APPLICATION_SHORT,

View File

@@ -61,6 +61,7 @@ class UserExternal extends User
'fieldset:User:profiles' => array('profile_list'),
),
'allowed_org_list',
'log',
)
); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login', 'status')); // Attributes to be displayed for a list

View File

@@ -309,6 +309,9 @@
<item id="allowed_org_list">
<rank>80</rank>
</item>
<item id="log">
<rank>90</rank>
</item>
</items>
</details>
<search>

View File

@@ -38,4 +38,5 @@
Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'Class:UserLDAP' => 'LDAP uživatel',
'Class:UserLDAP+' => 'Uživatel ověřen přes LDAP',
'UserLDAP:server' => 'LDAP specifics~~',
));

View File

@@ -23,4 +23,5 @@
Dict::Add('DA DA', 'Danish', 'Dansk', array(
'Class:UserLDAP' => 'LDAP-Bruger',
'Class:UserLDAP+' => 'Bruger der godkendes via LDAP',
'UserLDAP:server' => 'LDAP specifics~~',
));

View File

@@ -25,4 +25,7 @@
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:UserLDAP' => 'LDAP-Benutzer',
'Class:UserLDAP+' => 'Benutzer, der via LDAP authentifiziert wird',
'Class:UserLDAP/Attribute:ldap_server' => 'LDAP-Server',
'Class:UserLDAP/Attribute:ldap_server+' => 'Optional: LDAP-Server, der zur Authentifizierung verwendet werden soll, falls mehrere LDAP-Server konfiguriert sind.',
'UserLDAP:server' => 'LDAP-Einstellungen',
));

View File

@@ -15,7 +15,7 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
/**
* Spanish Localized data
*
* @copyright Copyright (C) 2010-2023 Combodo SARL
@@ -37,4 +37,5 @@
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:UserLDAP' => 'Usuario LDAP',
'Class:UserLDAP+' => 'Usuario autenticado vía LDAP',
'UserLDAP:server' => 'LDAP specifics~~',
));

View File

@@ -22,4 +22,5 @@
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'Class:UserLDAP' => 'LDAP felhasználó',
'Class:UserLDAP+' => '',
'UserLDAP:server' => 'LDAP specifics~~',
));

View File

@@ -36,4 +36,5 @@
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:UserLDAP' => 'Utente LDAP',
'Class:UserLDAP+' => 'Utente autenticato da LDAP',
'UserLDAP:server' => 'LDAP specifics~~',
));

View File

@@ -23,4 +23,5 @@
Dict::Add('JA JP', 'Japanese', '日本語', array(
'Class:UserLDAP' => 'LDAP ユーザー',
'Class:UserLDAP+' => 'LDAP認証ユーザー',
'UserLDAP:server' => 'LDAP specifics~~',
));

View File

@@ -37,4 +37,5 @@
Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'Class:UserLDAP' => 'LDAP-gebruiker',
'Class:UserLDAP+' => 'Gebruiker die aanmeldt via LDAP',
'UserLDAP:server' => 'LDAP specifics~~',
));

View File

@@ -20,7 +20,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
@@ -30,12 +29,11 @@
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+
// Class:<class_name>/Stimulus:<stimulus_code>
// Class:<class_name>/Stimulus:<stimulus_code>+
//
// Class: UserLDAP
//
Dict::Add('PL PL', 'Polish', 'Polski', array(
'Class:UserLDAP' => 'Użytkownik LDAP',
'Class:UserLDAP+' => 'Użytkownik uwierzytelniony przez LDAP',
'UserLDAP:server' => 'LDAP specifics~~',
));

View File

@@ -22,4 +22,5 @@
Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'Class:UserLDAP' => 'Usuário externo via LDAP',
'Class:UserLDAP+' => '',
'UserLDAP:server' => 'LDAP specifics~~',
));

View File

@@ -14,4 +14,5 @@
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:UserLDAP' => 'Пользователь LDAP',
'Class:UserLDAP+' => 'Пользователь, аутентифицируемый через LDAP',
'UserLDAP:server' => 'LDAP specifics~~',
));

View File

@@ -35,4 +35,5 @@
Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
'Class:UserLDAP' => 'LDAP užívateľ',
'Class:UserLDAP+' => '',
'UserLDAP:server' => 'LDAP specifics~~',
));

View File

@@ -37,4 +37,5 @@
Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
'Class:UserLDAP' => 'LDAP kullanıcısı',
'Class:UserLDAP+' => 'Yetki kontrolü LDAP tarafından yapılan',
'UserLDAP:server' => 'LDAP specifics~~',
));

View File

@@ -36,4 +36,5 @@
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:UserLDAP' => 'LDAP 用户',
'Class:UserLDAP+' => '用户身份由LDAP 认证',
'UserLDAP:server' => 'LDAP specifics~~',
));

View File

@@ -40,7 +40,6 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'Class:UserLocal+' => 'Uživatel ověřen interně v '.ITOP_APPLICATION_SHORT,
'Class:UserLocal/Attribute:password' => 'Heslo',
'Class:UserLocal/Attribute:password+' => '',
'Class:UserLocal/Attribute:expiration' => 'Password expiration~~',
'Class:UserLocal/Attribute:expiration+' => 'Password expiration status (requires an extension to have an effect)~~',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Can expire~~',
@@ -53,9 +52,7 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
'UserLocal:password:expiration' => 'The fields below require an extension~~',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
));

View File

@@ -25,7 +25,6 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
'Class:UserLocal+' => 'Bruger der godkendes af '.ITOP_APPLICATION_SHORT,
'Class:UserLocal/Attribute:password' => 'Password',
'Class:UserLocal/Attribute:password+' => 'Brugerens password',
'Class:UserLocal/Attribute:expiration' => 'Password expiration~~',
'Class:UserLocal/Attribute:expiration+' => 'Password expiration status (requires an extension to have an effect)~~',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Can expire~~',
@@ -38,9 +37,7 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
'UserLocal:password:expiration' => 'The fields below require an extension~~',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
));

View File

@@ -27,7 +27,6 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:UserLocal+' => 'Benutzer, der von '.ITOP_APPLICATION_SHORT.' authentifiziert wird',
'Class:UserLocal/Attribute:password' => 'Passwort',
'Class:UserLocal/Attribute:password+' => 'Benutzerpasswort',
'Class:UserLocal/Attribute:expiration' => 'Passwortablauf',
'Class:UserLocal/Attribute:expiration+' => 'Passwortablaufstatus (statusabhängige Effekte müssen per Extension implementiert werden)',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'kann ablaufen',
@@ -40,9 +39,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '',
'Class:UserLocal/Attribute:password_renewed_date' => 'Letzte Passworterneuerung',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Letztes Änderungsdatum',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Das Passwort entspricht nicht dem in den Konfigurationsregeln hinterlegten RegEx-Ausdruck',
'UserLocal:password:expiration' => 'Die folgenden Felder benötigen eine '.ITOP_APPLICATION_SHORT.' Erweiterung',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Das setzen des Passwortablaufs auf "Einmalpasswort" ist für den eigenen Benutzer nicht erlaubt.',
));

View File

@@ -39,7 +39,6 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:UserLocal+' => 'Usuario Autenticado vía '.ITOP_APPLICATION_SHORT,
'Class:UserLocal/Attribute:password' => 'Contraseña',
'Class:UserLocal/Attribute:password+' => 'Contraseña',
'Class:UserLocal/Attribute:expiration' => 'Expiración de contraseña',
'Class:UserLocal/Attribute:expiration+' => 'Estatus de expiración de contraseña (requiere de una extensión para que tenga efecto)',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Puede expirar',
@@ -52,9 +51,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'El usuario no puede cambiar la contraseña.',
'Class:UserLocal/Attribute:password_renewed_date' => 'Renovación de contraseña',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Cuando fue el último cambio de contraseña',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La contraseña debe ser de al menos 8 caracteres e incluír mayúsculas, minúsculas, números y caracteres especiales.',
'UserLocal:password:expiration' => 'El siguiente campo requiere una extensión',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Configurar expiración de contraseña para "ontraseña de un solo uso" no está permitido para su propio Usuario',
));

View File

@@ -24,7 +24,6 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:UserLocal+' => 'Utilisateur authentifié par '.ITOP_APPLICATION_SHORT,
'Class:UserLocal/Attribute:password' => 'Mot de passe',
'Class:UserLocal/Attribute:password+' => '',
'Class:UserLocal/Attribute:expiration' => 'Validité du mot de passe',
'Class:UserLocal/Attribute:expiration+' => 'Statut du mot de passe (nécessite une extension pour avoir un effet)',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Durée limitée',
@@ -37,7 +36,6 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '',
'Class:UserLocal/Attribute:password_renewed_date' => 'Mot de passe changé le',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Dernière date à laquelle le mot de passe a été changé',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Le mot de passe doit contenir au moins 8 caractères, avec minuscule, majuscule, nombre et caractère spécial.',
'UserLocal:password:expiration' => 'Les champs ci-dessous nécessitent une extension',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Impossible de mettre "Usage unique" comme validité du mot de passe pour son propre utilisateur.',

View File

@@ -24,7 +24,6 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'Class:UserLocal+' => '',
'Class:UserLocal/Attribute:password' => 'Jelszó',
'Class:UserLocal/Attribute:password+' => '',
'Class:UserLocal/Attribute:expiration' => 'Jelszó lejárati ideje',
'Class:UserLocal/Attribute:expiration+' => 'Jelszó lejárati státusz (bővítmény szükséges hozzá)',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Lejár',
@@ -35,11 +34,9 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '~~',
'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'Egyszeri jelszó',
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'A felhasználó nem változtathat jelszót.',
'Class:UserLocal/Attribute:password_renewed_date' => 'Jelszó megújítás ideje',
'Class:UserLocal/Attribute:password_renewed_date' => 'Jelszó megújítás ideje',
'Class:UserLocal/Attribute:password_renewed_date+' => 'A jelszó legutóbbi módosításának időpontja',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'A jelszónak legalább 8 karakterből kell állnia, és tartalmaznia kell nagybetűket, kisbetűket, numerikus és speciális karaktereket.',
'UserLocal:password:expiration' => 'Az alábbi mezőkhöz egy bővítmény szükséges',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'A jelszó lejárati idejének beállítása "Egyszeri jelszóra" nem engedélyezett a saját Felhasználó számára.',
));

View File

@@ -38,7 +38,6 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:UserLocal+' => 'Utente autenticato da '.ITOP_APPLICATION_SHORT,
'Class:UserLocal/Attribute:password' => 'Password',
'Class:UserLocal/Attribute:password+' => 'user authentication string',
'Class:UserLocal/Attribute:expiration' => 'Password expiration~~',
'Class:UserLocal/Attribute:expiration+' => 'Password expiration status (requires an extension to have an effect)~~',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Can expire~~',
@@ -51,9 +50,7 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
'UserLocal:password:expiration' => 'The fields below require an extension~~',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
));

View File

@@ -25,7 +25,6 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
'Class:UserLocal+' => ITOP_APPLICATION_SHORT.'ローカル認証ユーザー',
'Class:UserLocal/Attribute:password' => 'パスワード',
'Class:UserLocal/Attribute:password+' => '認証文字列',
'Class:UserLocal/Attribute:expiration' => 'Password expiration~~',
'Class:UserLocal/Attribute:expiration+' => 'Password expiration status (requires an extension to have an effect)~~',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Can expire~~',
@@ -38,9 +37,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
'UserLocal:password:expiration' => 'The fields below require an extension~~',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
));

View File

@@ -29,7 +29,6 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'Class:UserLocal+' => 'Gebruiker die aanmeldt met gegevens aangemaakt in het gebruikersbeheer van '.ITOP_APPLICATION_SHORT,
'Class:UserLocal/Attribute:password' => 'Wachtwoord',
'Class:UserLocal/Attribute:password+' => 'Het wachtwoord waarmee de gebruiker zich aanmeldt bij '.ITOP_APPLICATION_SHORT,
'Class:UserLocal/Attribute:expiration' => 'Wachtwoord verloopt',
'Class:UserLocal/Attribute:expiration+' => 'Of het wachtwoord al dan niet verlopen is (vereist een extensie vooraleer dit werkt)',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Kan verlopen',
@@ -42,9 +41,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'De gebruiker kan dit wachtwoord niet veranderen.',
'Class:UserLocal/Attribute:password_renewed_date' => 'Wachtwoord laatst aangepast',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Tijdstip waarop het wachtwoord het laatst aangepast werd.',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Het wachtwoord bestaat uit minstens 8 tekens en bestaat uit een mix van minstens 1 hoofdletter, kleine letter, cijfer en speciaal teken.',
'UserLocal:password:expiration' => 'De velden hieronder vereisen een extensie.',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Je kan geen eenmalig wachtwoord instellen voor je eigen gebruiker.',
));

View File

@@ -37,7 +37,6 @@ Dict::Add('PL PL', 'Polish', 'Polski', array(
'Class:UserLocal+' => 'Użytkownik uwierzytelniony przez '.ITOP_APPLICATION_SHORT,
'Class:UserLocal/Attribute:password' => 'Hasło',
'Class:UserLocal/Attribute:password+' => 'Ciąg uwierzytelniania użytkownika',
'Class:UserLocal/Attribute:expiration' => 'Wygaśnięcie hasła',
'Class:UserLocal/Attribute:expiration+' => 'Stan wygaśnięcia hasła (wymaga rozszerzenia, aby zadziałało)',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Może wygasnąć',
@@ -50,9 +49,7 @@ Dict::Add('PL PL', 'Polish', 'Polski', array(
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Hasło nie może być zmienione przez użytkownika.',
'Class:UserLocal/Attribute:password_renewed_date' => 'Odnowienie hasła',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Kiedy ostatnio zmieniano hasło',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Hasło musi mieć co najmniej 8 znaków i zawierać duże, małe litery, cyfry i znaki specjalne.',
'UserLocal:password:expiration' => 'Poniższe pola wymagają rozszerzenia',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Ustawienie wygaśnięcia hasła "Hasło jednorazowe" nie jest dozwolone dla własnego użytkownika',
));

View File

@@ -24,7 +24,6 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'Class:UserLocal+' => '',
'Class:UserLocal/Attribute:password' => 'Senha',
'Class:UserLocal/Attribute:password+' => '',
'Class:UserLocal/Attribute:expiration' => 'Expiração da senha',
'Class:UserLocal/Attribute:expiration+' => 'Status de expiração de senha (Requer uma extensão para fazer efeito)',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Senha expira',
@@ -37,9 +36,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Senha não pode ser alterada pelo usuário',
'Class:UserLocal/Attribute:password_renewed_date' => 'Data da última alteração de senha',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Quando a senha foi alterada anteriormente',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'A senha deve ter no mínimo 8 caracteres e incluir letras maiúsculas, minúsculas, números e símbolos',
'UserLocal:password:expiration' => 'O campo abaixo requer uma extensão',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Definir a expiração da senha para One-Time Password (OTP) não é permitido para o seu próprio usuário',
));

View File

@@ -16,7 +16,6 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:UserLocal+' => 'Пользователь, аутентифицируемый через '.ITOP_APPLICATION_SHORT,
'Class:UserLocal/Attribute:password' => 'Пароль',
'Class:UserLocal/Attribute:password+' => 'Строка аутентификации пользователя',
'Class:UserLocal/Attribute:expiration' => 'Срок действия пароля',
'Class:UserLocal/Attribute:expiration+' => 'Статус срока действия пароля (требуется расширение для эффекта)',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Органиченный',
@@ -29,9 +28,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
'Class:UserLocal/Attribute:password_renewed_date' => 'Дата изменения пароля',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Когда пароль был изменен в последний раз',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Пароль должен содержать не менее 8 символов и включать прописные, строчные, числовые и специальные символы.',
'UserLocal:password:expiration' => 'Поля требуют наличия доп. расширения',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
));

View File

@@ -37,7 +37,6 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
'Class:UserLocal+' => '',
'Class:UserLocal/Attribute:password' => 'Heslo',
'Class:UserLocal/Attribute:password+' => '',
'Class:UserLocal/Attribute:expiration' => 'Password expiration~~',
'Class:UserLocal/Attribute:expiration+' => 'Password expiration status (requires an extension to have an effect)~~',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Can expire~~',
@@ -50,9 +49,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
'UserLocal:password:expiration' => 'The fields below require an extension~~',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
));

View File

@@ -39,7 +39,6 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
'Class:UserLocal+' => 'Yetki kontorlünü '.ITOP_APPLICATION_SHORT.' tarafından yapılan kullanıcı',
'Class:UserLocal/Attribute:password' => 'Şifre',
'Class:UserLocal/Attribute:password+' => 'şifre',
'Class:UserLocal/Attribute:expiration' => 'Password expiration~~',
'Class:UserLocal/Attribute:expiration+' => 'Password expiration status (requires an extension to have an effect)~~',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Can expire~~',
@@ -52,9 +51,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
'UserLocal:password:expiration' => 'The fields below require an extension~~',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
));

View File

@@ -38,7 +38,6 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:UserLocal+' => '用户由 '.ITOP_APPLICATION_SHORT.' 验证身份',
'Class:UserLocal/Attribute:password' => '密码',
'Class:UserLocal/Attribute:password+' => '用于验证用户身份的字符串',
'Class:UserLocal/Attribute:expiration' => '密码过期',
'Class:UserLocal/Attribute:expiration+' => '密码过期状态(需要一个扩展才能生效)',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => '允许过期',
@@ -51,9 +50,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '用户不允许修改密码.',
'Class:UserLocal/Attribute:password_renewed_date' => '密码更新',
'Class:UserLocal/Attribute:password_renewed_date+' => '上次修改密码的时间',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => '密码必须至少8个字符,包含大小写,数字和特殊字符.',
'UserLocal:password:expiration' => '下面的区域需要插件扩展',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => '不允许用户为自己设置"一次性密码"的失效期限',
));

View File

@@ -108,6 +108,7 @@ class UserLocal extends UserInternal
'fieldset:UserLocal:password:expiration' => array('expiration', 'password_renewed_date',),
),
'allowed_org_list',
'log',
)
); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login', 'org_id')); // Attributes to be displayed for a list

View File

@@ -20,7 +20,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'theme:darkmoon' => 'Dark moon~~',
));

View File

@@ -20,7 +20,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
Dict::Add('DA DA', 'Danish', 'Dansk', array(
'theme:darkmoon' => 'Dark moon~~',
));

View File

@@ -20,7 +20,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
Dict::Add('DE DE', 'German', 'Deutsch', array(
'theme:darkmoon' => 'Dark moon',
));
));

View File

@@ -20,7 +20,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'theme:darkmoon' => 'Luna Obscura',
));

View File

@@ -20,7 +20,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
Dict::Add('FR FR', 'French', 'Français', array(
'theme:darkmoon' => 'Dark moon~~',
));

View File

@@ -20,7 +20,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'theme:darkmoon' => 'Dark moon',
));

View File

@@ -20,7 +20,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'theme:darkmoon' => 'Dark moon~~',
));

View File

@@ -20,7 +20,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
Dict::Add('JA JP', 'Japanese', '日本語', array(
'theme:darkmoon' => 'Dark moon~~',
));

View File

@@ -20,7 +20,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'theme:darkmoon' => 'Dark moon',
));

View File

@@ -20,7 +20,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'theme:darkmoon' => 'Dark moon',
));

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