Compare commits

...

176 Commits

Author SHA1 Message Date
Guillaume Lajarige
7c78acbe25 Creating SVN tag for iTop 2.4.2 (Forgot to create it at the time)
SVN:2.4.2[5922]
2018-06-28 08:26:18 +00:00
Romain Quetiez
fe887f01c0 Reelasing 2.4.2
SVN:2.4[5867]
2018-06-14 09:33:29 +00:00
Eric Espié
9109138127 N°1420 - Fix index generation
SVN:2.4[5866]
2018-06-14 09:03:39 +00:00
Eric Espié
51b19c03b1 N°1420 - Fix index generation
SVN:2.4[5865]
2018-06-14 08:56:12 +00:00
Eric Espié
8cb584abdd N°1420 - Fix index generation
SVN:2.4[5842]
2018-06-08 13:01:45 +00:00
Eric Espié
3480d478b0 N°1420 - Limit count
SVN:2.4[5839]
2018-06-08 09:30:10 +00:00
Eric Espié
3a34417a7d N°1420 - Limit count
SVN:2.4[5838]
2018-06-08 09:24:25 +00:00
Eric Espié
6ca3ca108b N°1420 - Fix index generation
SVN:2.4[5837]
2018-06-08 09:23:30 +00:00
Eric Espié
037036ce6a N°1420 - don't launch the search automatically for n:n and n:1 links for huge tables
SVN:2.4[5832]
2018-06-07 12:20:22 +00:00
Eric Espié
9d2dab5eba N°1420 - Performance for auto-complete widgets
SVN:2.4[5829]
2018-06-06 07:51:07 +00:00
Eric Espié
3169b39952 N°1488 - restore failed on production-modules
SVN:2.4[5827]
2018-06-05 13:48:56 +00:00
Eric Espié
ba33b01d84 SVN:2.4[5820] 2018-05-30 10:24:07 +00:00
Eric Espié
7db69ed158 N°1420 - Autocomplete
Directory 'branch-2.4' created by PhpStorm

SVN:2.4[5819]
2018-05-30 10:20:10 +00:00
Guillaume Lajarige
a6f6d536e0 N°1472 Portal: OQL optimization in ManageBrick when several UNIONs are used.
SVN:2.4[5813]
2018-05-23 15:03:33 +00:00
Stephen Abello
880faf2021 (Retrofit from trunk) N°1444 : fixed regression introduced in [r5725] & [r5774]
SVN:2.4[5810]
2018-05-23 11:29:56 +00:00
Eric Espié
f96902c03e N°1330 - Fix broken sql requests due to the use of class instead of alias
SVN:2.4[5783]
2018-05-15 12:11:22 +00:00
Pierre Goiffon
6e810322f7 (Retrofit from trunk) N°1418 fix audit with valid_flag=true that were always failing (r5773)
SVN:2.4[5774]
2018-05-04 15:12:55 +00:00
Guillaume Lajarige
dd9984be7f (Retrofit from trunk) N°1425 Fix regression introduced in 2.4. Creation of an object in a specific state could result in a fatal error due to bad ormlinkSet initialization.
SVN:2.4[5756]
2018-04-27 14:20:44 +00:00
Stephen Abello
98f3f88ea2 N°1424 : (Retrofit from trunk r5339 & r5751) Sharing base compatibility fix
SVN:2.4[5752]
2018-04-27 12:32:28 +00:00
Pierre Goiffon
8be0c36859 (Retrofit from trunk) N°1418 Audits Perf optimization for AuditRule with valid_flag=true and lots of negative records
Use a new helper method that don't parse values anymore on SELECT IN / NOT IN queries (r5724)

SVN:2.4[5725]
2018-04-23 14:54:19 +00:00
Pierre Goiffon
8c5a65d836 (Retrofit from trunk) N°1328 Fix CSV import : check if user has rights on imported class (r5597)
SVN:2.4[5598]
2018-04-03 13:40:47 +00:00
Pierre Goiffon
db47973063 (Retrofit from trunk) N°1330 Header with statistics dashlet perf improvements
Now uses one count + group by query instead of one count query per grouping value (r5576)

SVN:2.4[5577]
2018-03-29 15:50:04 +00:00
Eric Espié
028e815bfa Retrofit from trunk
Magic trick for windows. Sometimes the folder is empty but rmdir fails [from revision 5344]

SVN:2.4[5347]
2018-02-13 11:03:44 +00:00
Denis Flaven
caf07affa6 (retrofit from trunk) Cleanup target build directory before building into it...
SVN:2.4[5346]
2018-02-13 11:02:52 +00:00
Denis Flaven
bdef9e59de (retrofit from trunk) Bug fixes:
- support an upgrade of a givne component (same directory in data/production-modules)
- deployment no longer blocked after a failed attempt (cleanup of the data/production-build-modules directory)
- load of the "structural data" of newly added extensions

Enhancements:
- All traces go to log/setup.log, and  traces have been added to clearly identify the different phases of the deployment.

SVN:2.4[5342]
2018-02-12 12:33:47 +00:00
Denis Flaven
4bf001d108 The Hub is alive ! Let's use the production URL.
SVN:2.4[5338]
2018-02-09 14:15:31 +00:00
Denis Flaven
a095208e53 N°1323: Bug fix for a crash with the error message: class 'cmdbAbstractObject' not found, in the last screen of the setup under very specific circumstances.
SVN:2.4[5335]
2018-02-09 14:03:40 +00:00
Denis Flaven
2429ec4eec N°1323: Bug fix for a crash with the error message: class 'cmdbAbstractObject' not found, in the last screen of the setup under very specific circumstances.
SVN:2.4[5334]
2018-02-09 13:46:23 +00:00
Denis Flaven
55655e3fca Added an extra safety check to detect inconsistencies between the added extensions and the choices made during the initial installation.
SVN:2.4[5319]
2018-02-09 08:53:26 +00:00
Romain Quetiez
7e98b04ed3 (retrofit from trunk) N°1287 Update the installation instructions (pointing to the wiki page), and fix the readme to point to the correct wiki pages
SVN:2.4[5296]
2018-01-25 13:58:48 +00:00
Denis Flaven
59c6c0250d (retrofit from trunk) Bug fixes:
- properly detect missing dependencies when deploying extensions from the Hub (and not only when deploying a 2nd time an extension, cf bug n°1284).
- setup hangs when upgrading to 2.4.1 with some "old" extensions in the "extensions" folder.

SVN:2.4[5291]
2018-01-23 10:39:47 +00:00
Pierre Goiffon
18a5afb8a6 (Retrofit from trunk) N°942 PHP version not yet validated was incorrectly set to 7.1.9, fix it back to 7.2.0 (r5288)
SVN:2.4[5289]
2018-01-18 11:12:41 +00:00
Denis Flaven
4b6258dfa1 (Retrofit from trunk) Bug fix: do not (try to) launch the backup if the backup detection told us that the backup is not possible!
SVN:2.4[5287]
2018-01-17 16:37:39 +00:00
Denis Flaven
44b9f23117 (Retrofit from trunk) Fix for a problem breaking the mysqldump detection (when it fails on windows). Root cause: do not return/display the output of the shell command used to test mysqldump since (on windows) it may contain non-UTF-8 characters of an unknown character set and this breaks "UTF-8 picky" functions like json_encode.
SVN:2.4[5286]
2018-01-17 16:34:36 +00:00
Pierre Goiffon
2ddefcd2fc (Retrofit from trunk) Remove file that do not belongs here (r5282)
SVN:2.4[5283]
2018-01-17 14:51:52 +00:00
Pierre Goiffon
39b15fec50 (Retrofit from trunk) Change GetSQLQueryStructure() method visibility (r5187)
SVN:2.4[5281]
2018-01-17 13:47:01 +00:00
Romain Quetiez
c180dd9863 (retrofit from trunk) Getting ready for the release of 2.4.1 in february
SVN:2.4[5280]
2018-01-17 13:12:49 +00:00
Denis Flaven
1172130d7c (Rtrofit from trunk) Setup: special mapping for 2 extensions which code has changed...
SVN:2.4[5278]
2018-01-17 12:38:42 +00:00
Denis Flaven
8172e14345 (Retrofit from trunk) The Hub Connects !!
Adding iTop Hub Connector.

SVN:2.4[5275]
2018-01-17 10:23:51 +00:00
Denis Flaven
6e4836f5e0 (Retrofit from trunk) Preparing for the Hub: better decouple the RunTimeEnvironment from the list of directories to scan/install in order to support installation from the Hub.
SVN:2.4[5274]
2018-01-17 10:22:14 +00:00
Denis Flaven
4ee6e6f915 (Retrofit from trunk) Fixed regression introduced by [r5235]: some directories (like data/production-modules) may not always exist... this should not stop the setup.
SVN:2.4[5273]
2018-01-17 10:16:42 +00:00
Denis Flaven
e5b318b94f (Retrofit from trunk) Handle extensions with missing dependencies.
SVN:2.4[5272]
2018-01-17 10:15:11 +00:00
Denis Flaven
c081b89f03 (Retrofit from trunk) Small setup refactoring for getting ready for the Hub.
SVN:2.4[5271]
2018-01-17 10:13:14 +00:00
Pierre Goiffon
f237a98c1d (Retrofit from trunk) Exclude for itop-hub-connector (r5268)
SVN:2.4[5269]
2018-01-17 09:03:59 +00:00
Guillaume Lajarige
fd2a41aee6 (Retrofit from trunk) Portal: Fix CSS for selected rows in dataTables tables
SVN:2.4[5267]
2018-01-16 17:00:47 +00:00
Guillaume Lajarige
25b1a0d8af (Retrofit from trunk) N°984 Portal: Fix autocomplete field reset when changing value of parent field in request templates.
SVN:2.4[5262]
2018-01-16 15:39:44 +00:00
Eric Espié
985b366cc7 Retrofit from trunk
N°1246 - Fix Obsolete data visible in dependency graph.
* Fix a wrong transient OQL expression cache introduced in [4943]. [from revision 5257]

SVN:2.4[5259]
2018-01-16 15:05:27 +00:00
Guillaume Lajarige
d1e2bc9b2b (Retrofit from trunk) N°1276 Portal: Aligned drop-down list to autocomplete threshold behavior to console's behavior.
SVN:2.4[5256]
2018-01-16 14:27:01 +00:00
Eric Espié
757b3d1cc3 Retrofit from trunk
N°1224 - The 2.4.x setup keep the selected choices from a 1.3.x version.
* The selection is kept even if the extension has a one more module than the 1.3.x [from revision 5252]

SVN:2.4[5253]
2018-01-16 10:41:47 +00:00
Pierre Goiffon
8c0bbadbfe (Retrofit from trunk) Split the method to get a module absolute URL into 3 different methods, to allow more flexibility (for example get the URL and the query string key/value array to construct a GET form) (r5071, r5074, r5077)
SVN:2.4[5251]
2018-01-16 09:11:23 +00:00
Eric Espié
cbd3d2c165 Retrofit from trunk
N°1026 - Portal requests are too slow
* Counts on union requests are more optimized
* Requests for combo box values are more optimized [from revision 5249]

SVN:2.4[5250]
2018-01-16 08:46:49 +00:00
Eric Espié
b286bd48c7 Retrofit from trunk
N°870 - Avoid Obsolete data export in CSV, Excel and PDF [from revision 5218]
N°870 - Avoid Obsolete data in audit results [from revision 5219]
N°870 - Fix the display of archived objects in the dashlets when activating/deactivating the archive mode. [from revision 5226]

SVN:2.4[5248]
2018-01-15 08:17:24 +00:00
Pierre Goiffon
c4efbe2891 (Retrofit from trunk) N°942 set nex itop release MySQL requirement to 5.5.3 for utf8mb4 (r5245)
SVN:2.4[5246]
2018-01-12 16:08:22 +00:00
Pierre Goiffon
58506441cf (Retrofit from trunk) N°942 next itop release PHP & MySQL requirements : use *.0 versions instead of the latests (r5243)
SVN:2.4[5244]
2018-01-12 15:36:25 +00:00
Guillaume Lajarige
d575ee50ee (Retrofit from trunk) N°1199 Fixed "Notice: undefined index 0" in the portal. UserRequest/Incident::ComputePriority() was failing when attributes impact had no value.
SVN:2.4[5241]
2018-01-12 13:57:21 +00:00
Pierre Goiffon
997fa3a856 (Retrofit from trunk) N°942 change next itop release MySQL version requirement from 5.6 to 5.5, plus add some comments (r5238)
SVN:2.4[5239]
2018-01-12 11:27:37 +00:00
Guillaume Lajarige
70abd8027e (Retrofit from trunk) Merged revision(s) 5192 & 5193.
r5192: N°1243 Fix MUST_CHANGE/MANDATORY checks on transition in the console on an HTML Attribute.
r5193: Rollback modifications from r5192 as it introduced a regression.

JS escaping and previous value comparison strategies are to be define before going further with this matter.

SVN:2.4[5236]
2018-01-11 14:57:41 +00:00
Eric Espié
4c6e6ffc9c Retrofit from trunk
N°1191 - Wrong file name for backup check. [from revision 5143]
N°1188 - Backup needs too much disk space [from revision 5144]
N°1190 - Better error reporting and disk cleanup. [from revision 5145]
License reformat. [from revision 5146]
N°1188 & N°1189 Too much disk space / memory used for backup / restore - completing the commit [r5144], because the PHP extension phar is no more required [from revision 5151]

SVN:2.4[5233]
2018-01-11 08:05:10 +00:00
Romain Quetiez
b268df7bb4 (Retrofit from trunk) N°1147 Enable data synchronization for applications classes (Localized Data)
SVN:2.4[5231]
2018-01-10 14:20:18 +00:00
Eric Espié
7aaa35f88e Retrofit from trunk
N°925 - Fix portal when request template field is in autocomplete mode with a wrong value
* No error is displayed, but the actual value is set to '0' [from revision 5215]

SVN:2.4[5227]
2018-01-10 13:45:04 +00:00
Guillaume Lajarige
4bb91b819c (Retrofit from trunk) Portal: Change Ticket->public_log's flags in ev_reopen form. Now MUST_CHANGE instead of MUST_PROMPT.
SVN:2.4[5225]
2018-01-10 13:32:43 +00:00
Guillaume Lajarige
656cae3e66 (Retrofit from trunk) N°1157 Portal: Exception raised in BrowseBrick when one of the levels had no scope.
SVN:2.4[5223]
2018-01-10 10:45:25 +00:00
Guillaume Lajarige
292735b4b2 (Retrofit from trunk) N°1247 Fix AttributeEnum display as vertical radio buttons in console UI.
SVN:2.4[5217]
2018-01-09 14:44:37 +00:00
Pierre Goiffon
dcc4061261 (Retrofit from trunk) N°942 setup : max version for PHP (r5213)
SVN:2.4[5214]
2018-01-08 15:41:25 +00:00
Guillaume Lajarige
ff7ac731df (Retrofit from trunk) Fix regression introduced in r5183.
SVN:2.4[5212]
2018-01-08 12:41:52 +00:00
Guillaume Lajarige
c784afdb06 (Retrofit from trunk) N°1254 Portal: Add CSS/JS hooks on object forms for the current state
- CSS class on <form> tag: form_object_state_<STATE_CODE>
- HTML attribute on <form> tag: data-object-state="<STATE_CODE>"

SVN:2.4[5210]
2018-01-08 12:12:26 +00:00
Guillaume Lajarige
5e60e8d725 (Retrofit from trunk) N°1172.3 Portal: Objects and external keys in linkedsets (forms) now open in a modal dialog.
SVN:2.4[5208]
2018-01-08 11:39:18 +00:00
Pierre Goiffon
564bf04647 (Retrofit from trunk) N°801 allow block quotes in HTML Fields
add BLOCKQUOTE tag in the HTMLDOMSanitizer white list (r5202)

SVN:2.4[5203]
2018-01-04 17:19:06 +00:00
Guillaume Lajarige
17d18eb75f (Retofit from trunk) N°1194 Portal: Support for MUST_CHANGE flag on CaseLog attributes in transitions.
SVN:2.4[5200]
2018-01-04 10:42:37 +00:00
Guillaume Lajarige
a9a6460747 (Retrofit from trunk) N°1245 Fix MUST_CHANGE flag behavior on CaseLog attributes in the console.
SVN:2.4[5198]
2018-01-03 14:53:11 +00:00
Guillaume Lajarige
1fa1a053e4 (Retrofit from trunk) N°1172.2 Fix regression introduced in r5161 (Email notification crash because of portal urls)
SVN:2.4[5196]
2018-01-03 09:46:49 +00:00
Guillaume Lajarige
01e151632f (Retrofit from trunk) N°1217.2 Console UI: Small enhancements on object properties display.
* HTML Attribute value not breaking on words anymore.
* Attribute label width bigger on single column display.

SVN:2.4[5195]
2018-01-03 09:44:19 +00:00
Pierre Goiffon
c17d0b1027 (Retrofit from trunk) N°942 setup : add checks for next iTop release requirements on PHP and MySQL versions (r5190)
SVN:2.4[5191]
2018-01-02 16:08:37 +00:00
Eric Espié
517d16e3d9 Retrofit from trunk
N°1070: Enhance ergonomics of "Add To Dashboard..." popup window
* Larger window to avoid the scrollbar.
* Check the dashboards root parent access rights to generate the dashboards list proposed in the popup.
* Select by default the first dashboard of the current menu.
[from revision 5137, 5185]

SVN:2.4[5186]
2018-01-02 13:33:20 +00:00
Guillaume Lajarige
696a5d1a48 (Retrofit from trunk) N°1227 New configuration parameter (disable_attachments_download_legacy_portal) to disable attachments download from the legacy portal. Default is "true"!
SVN:2.4[5184]
2017-12-29 13:59:41 +00:00
Guillaume Lajarige
96288db97b (Retrofit from trunk) N°1132 Add ContextTag on CRON background tasks (eg. "CRON:Task:<CLASS_NAME_OF_THE_CURRENT_TASK>").
Introduced for the "Mail to ticket automation" extension, so we know when a Ticket is created/updated from an email.

SVN:2.4[5182]
2017-12-29 09:59:37 +00:00
Guillaume Lajarige
e6072cd8b6 (Retrofit from trunk) N°1143 Fix removed email links (mailto) in HTML attributes (CKEditor).
SVN:2.4[5180]
2017-12-28 15:37:57 +00:00
Guillaume Lajarige
db8bb7d94a (Retrofit from trunk) N°850 Show "delete" and "bulk delete" rights in user's grant matrix.
SVN:2.4[5178]
2017-12-28 10:38:26 +00:00
Guillaume Lajarige
1b2dbf751c (Retrofit from trunk) N°624 Fix WYSIWYG feature in CaseLog / HTML attributes on transition.
SVN:2.4[5176]
2017-12-28 09:27:15 +00:00
Guillaume Lajarige
b6805af20e (Retrofit from trunk) N°1172 Portal: Objects and external keys in linkedsets (forms) now have hyperlinks if access is authorized regarding the user's scopes.
SVN:2.4[5174]
2017-12-27 16:12:15 +00:00
Guillaume Lajarige
4cc6290c88 (Retrofit from trunk) Compiled CSS filed from previous commit (r5168)
SVN:2.4[5173]
2017-12-27 16:10:18 +00:00
Guillaume Lajarige
c79d17516d (Retrofit from trunk) N°1217 Console UI improvements in object forms.
- Columns size optimization.
- Tooltip on (none empty) String attribute so long value can be seen without scrolling to the end of the input.
- OQL attribute displayed as Text/HTML attributes.

SVN:2.4[5172]
2017-12-27 16:09:25 +00:00
Pierre Goiffon
a2154d6418 (Retrofit from trunk) N°1182 fix overlapping table in console dashlets : now we have a scrolling bar if necessary [from revision 5168]
SVN:2.4[5169]
2017-12-27 14:53:54 +00:00
Guillaume Lajarige
f820d3bd81 (Retrofit from trunk) N°916 Fix impact analysis relation upstream description.
Description was unique for both directions. Now 2 separate entries are used 'Realtion:<RELATION_CODE>/<DIRECTION>Stream+'.
Translations for existing languages are already done.

NOTE: pages/UI.php a été repassé en fin de lignes Unix (LF) car il était pour une raison inconnue en Windows (CRLF). De fait, les annotations ne marcheront plus sur ce fichier dans la branche 2.4, mais elles sont toujours dispos sur trunk.

SVN:2.4[5167]
2017-12-27 10:13:27 +00:00
Eric Espié
0a914f527a Retrofit from trunk
N°789 - Fix losing the additional links attributes values during impact analysis update [from revision 5162]

SVN:2.4[5163]
2017-12-22 13:22:50 +00:00
Denis Flaven
a8c749a46a (retrofit from trunk) Preparing for integration with the Hub.
SVN:2.4[5159]
2017-12-15 14:55:25 +00:00
Eric Espié
e58c8afeab Retrofit from trunk
N°1209 - Fix Organization selector width (missing button) [from revision 5157]

SVN:2.4[5158]
2017-12-15 13:19:18 +00:00
Eric Espié
005b0eab1d Retrofit from trunk
N°1209 - Fix Organization selector width (padding added) [from revision 5154-5155]

SVN:2.4[5156]
2017-12-15 13:06:50 +00:00
Denis Flaven
55b34b5d11 (retrofit from trunk) Enhancement: automatically recognize some well-know multi-module extensions deployed using the old format (i.e. shipped without an extension.xml file) and emulate the new format for them in order to display a meaningful label and version in the setup and in the about box.
SVN:2.4[5153]
2017-12-14 12:14:08 +00:00
Eric Espié
7b4f00ee45 Retrofit from trunk
N°1163 - GET_LOCK 64 characters limitation in MySQL [from revision 5135]

SVN:2.4[5136]
2017-11-21 12:32:13 +00:00
Romain Quetiez
cc99cec250 (Retrofit from trunk) N°1160 - Fix error when sending notification with list of links.
SVN:2.4[5133]
2017-11-17 16:37:56 +00:00
Eric Espié
105dc46f46 Retrofit from trunk
N°1156 - Manual backup can be very long
* The database is saved in last position to avoid overhead when generating the archive file [from revision 5130]

SVN:2.4[5131]
2017-11-16 14:05:10 +00:00
Vincent Dumas
770fbe157c dictionnary typo on Notification header message
SVN:2.4[5127]
2017-11-03 09:30:15 +00:00
Denis Flaven
21d44c664f (Regression - retrofit from trunk) Fix display trouble for auto_reload menus. This was caused by a collision of HTML/DOM ids where the menu item of the left (accordion) menu had the same id as the div displaying the actual content in the right pane. This caused (when the id was a valid one !) the refresh of the content (list of objects) to occur INSIDE the accordion menu !
SVN:2.4[5122]
2017-10-31 10:46:09 +00:00
Guillaume Lajarige
b318cbe211 (Retrofit from trunk) N°634.3 Portal: Argh!! Secondary actions menu in BrowseBrick was broken due to previous CSS "fixes"...
SVN:2.4[5120]
2017-10-30 16:05:26 +00:00
Romain Quetiez
7106501812 (Retrofit from trunk) Ready for releasing...
SVN:2.4[5118]
2017-10-30 14:27:56 +00:00
Eric Espié
567dd39270 Retrofit from trunk
2.4.0 - New readme format. [from revision 5115]

SVN:2.4[5116]
2017-10-30 14:16:11 +00:00
Eric Espié
b292621f82 Retrofit from trunk
Core russian translation kindly provided by Vladimir Kunin. [from revision 5113]

SVN:2.4[5114]
2017-10-30 11:38:21 +00:00
Guillaume Lajarige
46f0b48add (Retrofit from trunk on behalf of sabello) added german translation for obsolescence. Thanks to ITOMIG.
SVN:2.4[5110]
2017-10-27 14:34:04 +00:00
Denis Flaven
9a5f16bd3e (retrofit from trunk) Simplification of the obsolescence condition (N°890) to avoid reaching the limit of 61 tables (N°1049)
SVN:2.4[5108]
2017-10-27 13:11:55 +00:00
Guillaume Lajarige
6224ec7a37 (Retrofit from trunk) Internal: Updated modules version to 2.4.0 (as well as some copyright dates)
SVN:2.4[5106]
2017-10-27 08:58:20 +00:00
Pierre Goiffon
c460edcd5e (Retrofit from trunk) r5093 Change version number for static resources calls
SVN:2.4[5101]
2017-10-27 07:55:07 +00:00
Pierre Goiffon
b839b42cd4 (Retrofit from trunk) r5091 Change modules XML version to the latest (1.4)
SVN:2.4[5100]
2017-10-27 07:54:58 +00:00
Pierre Goiffon
fabc8b91d7 (Retrofit from trunk) r5090 Change version number in CSS url() calls
SVN:2.4[5099]
2017-10-27 07:54:37 +00:00
Guillaume Lajarige
4c38cd570c (Retrofit from trunk) N°1138 Portal: Scrollbar appeared sometimes in navigation menu when on the last brick.
SVN:2.4[5098]
2017-10-26 17:41:28 +00:00
Guillaume Lajarige
c4fd15ae90 (Retrofit from trunk) N°930.2 Console UI: Better object details layout step 2.
SVN:2.4[5096]
2017-10-26 17:07:44 +00:00
Eric Espié
bbca491118 Retrofit from trunk
Fix utils::GetCurrentModuleUrl() bug introduced in revision 4920 [from revision 5085]

SVN:2.4[5086]
2017-10-25 16:01:16 +00:00
Guillaume Lajarige
0d04bd8b0e (Retrofit from trunk) Translations: Added icon attribute for ServiceFamily and Service classes. English and French done, feel free to contribute for others ! :)
SVN:2.4[5084]
2017-10-24 15:23:02 +00:00
Guillaume Lajarige
3bcb3a2154 (Retrofit from trunk) Internal: Updated sample data for Person, ServiceFamily and Service (use of AttributeImage)
SVN:2.4[5083]
2017-10-24 15:21:49 +00:00
Guillaume Lajarige
aa863c40d3 (Retrofit from trunk) N°634.2 Portal: CSS fixes on mosaic mode of BrowseBrick
SVN:2.4[5082]
2017-10-24 15:19:39 +00:00
Guillaume Lajarige
5d7ad9f6dd (Retrofit from trunk) Internal: Updated module version (itop-portal, itop-portal-base)
SVN:2.4[5081]
2017-10-24 15:18:05 +00:00
Eric Espié
72cb41dd7e Retrofit from trunk
N°1134 - Bad version number when MTP
* The order of the installed versions is changed when doing MTP. [from revision 5073]

SVN:2.4[5075]
2017-10-24 08:20:36 +00:00
Eric Espié
6ef39c9f5b Retrofit from trunk
N°1131 - Setup: Can't add options to ITIL Ticketing in update mode
* Fix the alternative selection when updating the setup
* Uncheck options when the alternatives are deselected [from revision 5067]

SVN:2.4[5068]
2017-10-23 11:40:46 +00:00
Eric Espié
614b948c82 Retrofit from trunk
N°1109 - itop-object-copier Create ticket from CI was not adding the CI in the CI list of the newly created ticket.
* Fix 'add_to_list' command.
* Fix Adding a n-n link at the creation time.

SVN:2.4[5064]
2017-10-20 14:16:54 +00:00
Eric Espié
7c0715ad59 Retrofit from trunk
N°1109 - itop-object-copier Create ticket from CI was not adding the CI in the CI list of the newly created ticket.
* Fix 'add_to_list' command.
* Fix Adding a n-n link at the creation time.

SVN:2.4[5063]
2017-10-20 14:13:12 +00:00
Guillaume Lajarige
31819497ae (Retrofit from trunk) N°1123.4 AttributeImage: Better UI when editing in console. Also, export was showing url for empty value.
SVN:2.4[5062]
2017-10-20 13:28:02 +00:00
Guillaume Lajarige
c86e2c6c79 (Retrofit from trunk) N°642.3 Portal: Lifecycle: Exception on UserRequest opening due to a bad variable initialization.
SVN:2.4[5059]
2017-10-20 12:43:21 +00:00
Eric Espié
1d54b66de9 Retrofit from trunk
N°1131 - Setup: Can't add options to ITIL Ticketing in update mode
* Fix the disable attribute of the options [from revision 5057]

SVN:2.4[5058]
2017-10-20 12:08:00 +00:00
Romain Quetiez
f932765e19 (Retrofit from trunk) N.1100 Regression introduced in [r4943], aka 2.4 RC3, and causing error during MTP (accessing the wrong expression cache)
SVN:2.4[5055]
2017-10-19 19:10:25 +00:00
Pierre Goiffon
11b4085566 (Retrofit from trunk) r5051 N.689 workaround on MySQL number of joins limit (61)
* change MySQLException to store initial exception code
* added a try/catch to launch query with full lazy load (no attr => only id)
* load finalClass field if needed (class is nor standalone nor a final leaf)

SVN:2.4[5052]
2017-10-19 13:58:39 +00:00
Guillaume Lajarige
12afcd7cce (Retrofit from trunk) N°1094 & N°1107 Portal: Mosaic mode in BrowseBrick displays icon nicely in IE9. Also, "name" on tiles doesn't break layout anymore when too long.
SVN:2.4[5049]
2017-10-19 13:02:24 +00:00
Guillaume Lajarige
666c95a656 (Retrofit from trunk) N°1071 UI: Better rendering for external keys in linkedset (no more wrapping)
SVN:2.4[5048]
2017-10-19 13:01:28 +00:00
Guillaume Lajarige
fde9c28263 (Retrofit from trunk) N°1129.1 Support of $this->hyperlink(itop-portal)$ in notifications (only "portal" was implemented for the default portal).
SVN:2.4[5047]
2017-10-19 13:00:36 +00:00
Guillaume Lajarige
52c3ff0406 (Retrofit from trunk) N°1094
- Adding "icon" attribute to ServiceFamily and Service classes
- Show icon on ServiceFamily and Service levels in BrowseBrick


SVN:2.4[5043]
2017-10-18 15:00:50 +00:00
Guillaume Lajarige
9f4d07aaa5 (Retrofit from trunk) N°1123.2 AttributeImage: default_image is no longer mandatory.
SVN:2.4[5041]
2017-10-18 14:53:36 +00:00
Guillaume Lajarige
70561d6331 (Retrofit from trunk) N°1123 AttributeImage: PHP notice when displaying an object without default_image on a AttributeImage attribute.
SVN:2.4[5039]
2017-10-18 14:44:19 +00:00
Eric Espié
ae8311e224 Retrofit from trunk
N.1065 Fix performance issues.
* Added the bGetCount flag into the cache to differentiate the cache entries for COUNT only. [from revision 5034]

SVN:2.4[5035]
2017-10-18 12:58:32 +00:00
Guillaume Lajarige
e09fa0ffc1 (Retrofit from trunk) N°911.2 Portal: Updated typeahead repository url in files headers.
SVN:2.4[5033]
2017-10-18 12:26:29 +00:00
Guillaume Lajarige
5b6a20048f (Retrofit from trunk) N°1122 Portal: Clean-up of 2 redundants JS files regarding the autocomplete inputs in forms.
SVN:2.4[5032]
2017-10-18 12:25:00 +00:00
Romain Quetiez
d86c211b73 (Retrofit from trunk) N.1108 return exception if $bMustBeFound and result is archived
SVN:2.4[5027]
2017-10-17 09:04:26 +00:00
Romain Quetiez
8499babcb3 (Retrofit from trunk) N°1092.1 Setup / MTP improvements regarding the environments folders:
- /env-production-build rights check before running setup
- /env-xxx-build is no longer deleted after MTT / MTP from the ITSM Designer. This prevents permissions issue when webserver user doesn't have suffisant rights on the root folder.


SVN:2.4[5026]
2017-10-17 08:02:30 +00:00
Romain Quetiez
2d44cbbb60 (Retrofit from trunk) Fixed dictionary typos (Chemin d'installation, En attente d'approbation)
SVN:2.4[5024]
2017-10-16 07:42:28 +00:00
Eric Espié
3d89ef9076 Merged from trunk
N.1065 Fix performance issues.
* Does not cache requests containing "id IN ..." and "id NOT IN ..." (too specific) [from revisions 5012, 5019]

SVN:2.4[5020]
2017-10-12 15:33:23 +00:00
Eric Espié
e896f8af3d (Retrofit from trunk)
N°1110 - DataSynchro: PHP Notice Undefined Index
* Fix access to REQUEST_URI when called by script [from revisions 5007, 5017]

SVN:2.4[5018]
2017-10-12 13:57:31 +00:00
Eric Espié
23268e8969 (Retrofit from trunk) Message when no data are available [from revision 5011]
SVN:2.4[5016]
2017-10-12 13:49:06 +00:00
Eric Espié
421e12debd (Retrofit from trunk)
N°870: Bulk operation and obsolescence flag
* Avoid the hidden selection (and update) of obsolete data when the user does not want to see the obsolete data. [from revision 4997]

SVN:2.4[5014]
2017-10-12 13:13:22 +00:00
Eric Espié
389848cef4 (Retrofit from trunk)
N.1065 Fix performance issues.
Cache display CSV format fix. [from revision 4996]

SVN:2.4[5013]
2017-10-12 13:10:50 +00:00
Guillaume Lajarige
7da4423346 (Retrofit from trunk) N°642.2 Portal: Lifecycle transitions security is now a blacklist instead of a white list. Making migration transparent and portal configuration easier.
SVN:2.4[5010]
2017-10-12 08:55:01 +00:00
Guillaume Lajarige
2a770b9dc4 (Retrofit from trunk) N°1107.1 Portal: Fixed image display in mosaic mode of BrowseBrick for Chrome and Firefox. IE still pending !
SVN:2.4[5009]
2017-10-12 08:54:10 +00:00
Pierre Goiffon
8df12e64f2 (Retrofit from trunk) r4492 N.1065 Fix performance issues.
Add statistics on query table join optimization.

SVN:2.4[4995]
2017-10-06 14:25:49 +00:00
Romain Quetiez
341261c14e (Retrofit from trunk) Fixed integration issue (possibly a regression): if an extension implements iApplicationExtension::OnDBInsert, and it calls DBWrite, then a fatal error occurs (call a member function on a null value). The error occurs for several types of attributes such as ormCustomField, ormCaseLog, ormLinkSet. The fix consists in aligning the internal values of a DBObject as soon as it has been written into the Database.
SVN:2.4[4994]
2017-10-06 14:07:33 +00:00
Guillaume Lajarige
bfc7c73e18 (Retrofit from trunk) Standard DataModel: Add org_id and location_id to 'Rack' reconciliation keys. Mandatory for CSV import of CI on a Rack, when Rack name is not a unique identifier.
SVN:2.4[4990]
2017-10-06 08:09:31 +00:00
Guillaume Lajarige
9a6c4ba7bb (Retrofit from trunk on behalf of cisou) FR dictionnary typo for obsolescence
SVN:2.4[4989]
2017-10-06 08:06:23 +00:00
Pierre Goiffon
dc47c34981 (Retrofit from trunk) r4983 N.1065 When joining, reverse leaf-root order : now it's root first !
* decrease the amount of joins on obsolescence use cases
* should also improve other uses cases as most of the time we believe the attribute linked is in the root class !
* the root table join is done using expressions instead of OQL for perf reasons
* a where clause on finalclass is also added to avoid problems if the leaf table join is not used (would be removed during query optimization phase)

SVN:2.4[4988]
2017-10-05 16:03:08 +00:00
Eric Espié
3ac5131a19 (Retrofit from trunk) r4969
N°1098 Fix CSV import by id. Fix a regression introduced in rev 4885.
---------------------
r4968
N°1100 - External field pointing to a magic attribute * A specific pass has been added in MetaModel::InitClasses() to generate the magic attributes before the external fields.
---------------------

SVN:2.4[4987]
2017-10-05 16:00:49 +00:00
Eric Espié
134a9aa684 (Retrofir from trunk) N.1065 Fix performance issues (unexpected objects reload).
* Fix regression in 2.4 into attribute optimization (archive_flag, obsolescence_flag).
* Fix attribute optimization (friendlyname for ExternalField pointing to ExternalKey)

SVN:2.4[4986]
2017-10-05 15:59:21 +00:00
Eric Espié
a98c0d32ae (Retrofit from trunk)
r4961
N°1065 Fixed a regression introduced in r4965.


SVN:2.4[4985]
2017-10-05 15:57:16 +00:00
Eric Espié
38af2b85c4 (Retrofit from trunk)
r4961
N°1065 Fixed a regression introduced in r4965.
---------------------
r4960
N.1065 Fix performance issues. Limit APC emulation cache entries to avoid disk saturation. New configuration entry added: 'apc_cache_emulation.max_entries'.
---------------------
r4956
N.1065 Fix performance issues. ormLinkSet creates the objects on demand.
---------------------
r4954
APC emulation using files when APC or APCu is not installed.

SVN:2.4[4984]
2017-10-05 15:55:15 +00:00
Guillaume Lajarige
c9c84735c4 (Retrofit from trunk) N°1025 Portal: Fixed regression introduced in r4863.
SVN:2.4[4982]
2017-10-05 15:52:12 +00:00
Guillaume Lajarige
646972838a (retrofit from trunk) N°1034 New EventOnObject class to store explaination on object's updates
SVN:2.4[4981]
2017-10-05 15:50:34 +00:00
Guillaume Lajarige
ce92241593 (Retrofit from trunk) N°653.2 Lifecycle: Fixed a regression introduced on r4767, transition buttons not working properly when editing an object.
SVN:2.4[4980]
2017-10-05 15:49:36 +00:00
Guillaume Lajarige
715eeff627 (Retrofit from trunk) N°1082 Fixed a regression introduced with ormLinkSet rework: Modified links not updated.
SVN:2.4[4979]
2017-10-05 15:48:13 +00:00
Romain Quetiez
651de821df (Retrofit from trunk) N.1065 and #372 Query build cache not efficient with global search (each search generates about 80 new entries in the APCu cache)
SVN:2.4[4978]
2017-10-05 15:46:53 +00:00
Guillaume Lajarige
2233ea5f54 (Retrofit from trunk) N°1067 Rework on ormLinkSet BC with DBObjectSet.
- PHP notice are not thrown anymore, see PHPDoc instead.
- GetColumnAsArray() introduced.

SVN:2.4[4977]
2017-10-05 15:46:21 +00:00
Eric Espié
39e6915e05 r4945
(Retrofit from trunk) Fix regression introduced in trunk[4943]
---------------------
r4944
Code cleanup.
---------------------
r4943
N.1065 Fix performance issues (caches added on query build)

SVN:2.4[4976]
2017-10-05 15:45:35 +00:00
Guillaume Lajarige
6279f4ac70 (Retrofit from trunk) Internal: Typo in PHPDoc
SVN:2.4[4975]
2017-10-05 15:45:01 +00:00
Guillaume Lajarige
badf10e74e (Retrofit from trunk) N°1073 Reentrance issue on cmdbAbstractObject when coming from an extension implementing iApplicationObjectExtension.
SVN:2.4[4974]
2017-10-05 15:43:50 +00:00
Guillaume Lajarige
2e9cd6a342 (Retrofit from trunk) Internal: Changed way AttributeLinkedSet check if two ormLinkSet are equal.
SVN:2.4[4973]
2017-10-05 15:42:51 +00:00
Guillaume Lajarige
cef70ee9a2 (Retrofit from trunk) Internal: Typo in itop-tickets XML comments
SVN:2.4[4972]
2017-10-05 15:41:52 +00:00
Eric Espié
37e15706b2 (Retrofit from trunk) Fix utils::GetCurrentModuleUrl() introduced in revision 4844
SVN:2.4[4970]
2017-10-05 15:34:09 +00:00
Denis Flaven
4b210273d2 (retrofit from trunk) N°1806, N°1069: CSV and Excel export and import of documents (files) and images as URLs.
SVN:2.4[4953]
2017-09-30 09:24:29 +00:00
Romain Quetiez
951e8e03b0 (Retrofit from trunk] N.1072 Localization for magic attributes archive_date (completed the existing implementation) and obsolescence_date (full implementation)
SVN:2.4[4947]
2017-09-27 12:38:01 +00:00
Denis Flaven
c016ca364d Internal: Make sure that UI dialogs are never bigger than the browser's window (not used in iTop though).
SVN:2.4[4941]
2017-09-26 10:00:24 +00:00
Romain Quetiez
4611442665 (Retrofit from trunk) N.707 Export of custom fields: improved the xlsx format and implemented the spreadsheet format (both are aligned)
SVN:2.4[4936]
2017-09-22 13:52:25 +00:00
Denis Flaven
c7e7976607 Added the open source logo as a character (uppercase letter O) to the font.
Increased the version number in the CSS to prevent caching/refresh issues.

SVN:2.4[4931]
2017-09-21 14:29:25 +00:00
Romain Quetiez
0ca9fac04b (Retrofit from trunk) N°813 Enable bulk deletion of Data Synchro Replica
SVN:2.4[4928]
2017-09-21 10:13:02 +00:00
Romain Quetiez
7edc79f398 (Retrofit from trunk) Code robustness: Though the commit [4922] solves the issue N.1052, it is safer to reset the cache as part of the "Commit" performed during the installation process.
SVN:2.4[4925]
2017-09-20 14:53:41 +00:00
Romain Quetiez
c110206264 (Retrofit from trunk) N.1052 After a setup or MTP, the datamodel is not taken into account... until the web server gets restarted or the APC cache (user data) gets reset.
SVN:2.4[4924]
2017-09-20 14:48:07 +00:00
Guillaume Lajarige
b3ad2030cb (Retrofit from trunk) N°1006.2 Templates: $this->raw(attcode)$ can be used to display an date(time) attribute in the SQL format like before.
SVN:2.4[4919]
2017-09-18 15:56:08 +00:00
Guillaume Lajarige
0938ba939c (Retrofit from trunk) N°1006 Templates: Date & time format is now applied when using a date(time) attribute in a placeholder (eg. Notifications)
SVN:2.4[4918]
2017-09-18 15:55:02 +00:00
Guillaume Lajarige
bca393def3 (Retrofit from trunk) N°1019 Portal: OpenSans font embedded in iTop instead of fetching from google servers.
SVN:2.4[4917]
2017-09-18 15:53:31 +00:00
Guillaume Lajarige
796ac55ff4 (Retrofit from trunk) N°762 Portal: Pre-filtering a browse brick in tree mode was making tree collapsing instead of showing results.
SVN:2.4[4916]
2017-09-18 15:43:19 +00:00
Guillaume Lajarige
66ae89e91d (Retrofit from trunk) N°1038 Fatal error on transition with AttributeBlob or AttributeCaseLog
SVN:2.4[4908]
2017-09-13 16:09:02 +00:00
Denis Flaven
361cca465a (retrofit from trunk) Added E and F glyphs.
SVN:2.4[4906]
2017-09-13 08:55:24 +00:00
Denis Flaven
7462d66643 Combodo font enhancements.
SVN:2.4[4904]
2017-09-12 14:49:46 +00:00
Guillaume Lajarige
a177b9b1d4 iTop 2.4: Code freeze
SVN:2.4[4902]
2017-09-08 14:25:46 +00:00
266 changed files with 20426 additions and 11745 deletions

View File

@@ -118,8 +118,7 @@ class ApplicationContext
$oSearchFilter = new DBObjectSearch('Organization');
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oSet = new CMDBObjectSet($oSearchFilter);
$iCount = $oSet->Count();
if ($iCount == 1)
if ($oSet->Count(2) == 1)
{
// Only one possible value for org_id, set it in the context
$oOrg = $oSet->Fetch();

File diff suppressed because it is too large Load Diff

View File

@@ -755,33 +755,65 @@ EOF
public static function GetDashletCreationForm($sOQL = null)
{
$oAppContext = new ApplicationContext();
$sContextMenuId = $oAppContext->GetCurrentValue('menu', null);
$oForm = new DesignerForm();
// Get the list of all 'dashboard' menus in which we can insert a dashlet
$aAllMenus = ApplicationMenu::ReflectionMenuNodes();
$sRootMenuId = ApplicationMenu::GetRootMenuId($sContextMenuId);
$aAllowedDashboards = array();
foreach($aAllMenus as $idx => $aMenu)
$sDefaultDashboard = null;
// Store the parent menus for acces check
$aParentMenus = array();
foreach($aAllMenus as $idx => $aMenu)
{
/** @var MenuNode $oMenu */
$oMenu = $aMenu['node'];
if (count(ApplicationMenu::GetChildren($oMenu->GetIndex())) > 0)
{
$aParentMenus[$oMenu->GetMenuId()] = $aMenu;
}
}
foreach($aAllMenus as $idx => $aMenu)
{
$oMenu = $aMenu['node'];
$sParentId = $aMenu['parent'];
if ($oMenu instanceof DashboardMenuNode)
{
$sMenuLabel = $oMenu->GetTitle();
$sParentLabel = Dict::S('Menu:'.$sParentId);
if ($sParentLabel != $sMenuLabel)
{
$aAllowedDashboards[$oMenu->GetMenuId()] = $sParentLabel.' - '.$sMenuLabel;
}
else
{
$aAllowedDashboards[$oMenu->GetMenuId()] = $sMenuLabel;
}
}
if ($oMenu instanceof DashboardMenuNode)
{
// Get the root parent for access check
$sParentId = $aMenu['parent'];
$aParentMenu = $aParentMenus[$sParentId];
while (isset($aParentMenus[$aParentMenu['parent']]))
{
// grand parent exists
$sParentId = $aParentMenu['parent'];
$aParentMenu = $aParentMenus[$sParentId];
}
$oParentMenu = $aParentMenu['node'];
if ($oMenu->IsEnabled() && $oParentMenu->IsEnabled())
{
$sMenuLabel = $oMenu->GetTitle();
$sParentLabel = Dict::S('Menu:'.$sParentId);
if ($sParentLabel != $sMenuLabel)
{
$aAllowedDashboards[$oMenu->GetMenuId()] = $sParentLabel.' - '.$sMenuLabel;
}
else
{
$aAllowedDashboards[$oMenu->GetMenuId()] = $sMenuLabel;
}
if (empty($sDefaultDashboard) && ($sRootMenuId == ApplicationMenu::GetRootMenuId($oMenu->GetMenuId())))
{
$sDefaultDashboard = $oMenu->GetMenuId();
}
}
}
}
asort($aAllowedDashboards);
$aKeys = array_keys($aAllowedDashboards); // Select the first one by default
$sDefaultDashboard = $aKeys[0];
$oField = new DesignerComboField('menu_id', Dict::S('UI:DashletCreation:Dashboard'), $sDefaultDashboard);
$oField->SetAllowedValues($aAllowedDashboards);
$oField->SetMandatory(true);
@@ -842,7 +874,7 @@ EOF
$oPage->add_ready_script(
<<<EOF
$('#dashlet_creation_dlg').dialog({
width: 400,
width: 600,
modal: true,
title: '$sDialogTitle',
buttons: [

View File

@@ -661,7 +661,7 @@ class DisplayBlock
case 'links':
//$bDashboardMode = isset($aExtraParams['dashboard']) ? ($aExtraParams['dashboard'] == 'true') : false;
//$bSelectMode = isset($aExtraParams['select']) ? ($aExtraParams['select'] == 'true') : false;
if ( ($this->m_oSet->Count()> 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) )
if ( ($this->m_oSet->Count(1)> 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) )
{
//$sLinkage = isset($aExtraParams['linkage']) ? $aExtraParams['linkage'] : '';
$sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $this->m_oSet, $aExtraParams);
@@ -774,21 +774,38 @@ class DisplayBlock
{
$aStates = explode(',', $sStatesList);
$oAttDef = MetaModel::GetAttributeDef($sClass, $sStateAttrCode);
// Generate one count + group by query [#1330]
$sClassAlias = $this->m_oFilter->GetClassAlias();
$oGroupByExpr = Expression::FromOQL($sClassAlias.'.'.$sStateAttrCode);
$aGroupBy = array('group1' => $oGroupByExpr);
$sCountGroupByQuery = $this->m_oFilter->MakeGroupByQuery(array(), $aGroupBy, false);
$aCountGroupByResults = CMDBSource::QueryToArray($sCountGroupByQuery);
$aCountsQueryResults = array();
foreach ($aCountGroupByResults as $aCountGroupBySingleResult)
{
$aCountsQueryResults[$aCountGroupBySingleResult[0]] = $aCountGroupBySingleResult[1];
}
foreach($aStates as $sStateValue)
{
$oFilter = $this->m_oFilter->DeepClone();
$oFilter->AddCondition($sStateAttrCode, $sStateValue, '=');
$oSet = new DBObjectSet($oFilter);
$oSet->SetShowObsoleteData($this->m_bShowObsoleteData);
$aCounts[$sStateValue] = $oSet->Count();
$aStateLabels[$sStateValue] = htmlentities($oAttDef->GetValueLabel($sStateValue), ENT_QUOTES, 'UTF-8');
$aCounts[$sStateValue] = (array_key_exists($sStateValue, $aCountsQueryResults))
? $aCountsQueryResults[$sStateValue]
: 0;
if ($aCounts[$sStateValue] == 0)
{
$aCounts[$sStateValue] = '-';
}
else
{
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.urlencode($oFilter->serialize());
$oSingleGroupByValueFilter = $this->m_oFilter->DeepClone();
$oSingleGroupByValueFilter->AddCondition($sStateAttrCode, $sStateValue, '=');
$sHyperlink = utils::GetAbsoluteUrlAppRoot()
.'pages/UI.php?operation=search&'.$oAppContext->GetForLink()
.'&filter='.urlencode($oSingleGroupByValueFilter->serialize());
$aCounts[$sStateValue] = "<a href=\"$sHyperlink\">{$aCounts[$sStateValue]}</a>";
}
}
@@ -1567,7 +1584,7 @@ class MenuBlock extends DisplayBlock
// Do not perform time consuming computations if there are too may objects in the list
$iLimit = MetaModel::GetConfig()->Get('complex_actions_limit');
if ((count($aStates) > 0) && (($iLimit == 0) || ($oSet->Count() < $iLimit)))
if ((count($aStates) > 0) && (($iLimit == 0) || ($oSet->Count($iLimit + 1) < $iLimit)))
{
// Life cycle actions may be available... if all objects are in the same state
//

View File

@@ -360,6 +360,7 @@ EOF
<<<EOF
$('#$sDialogId').dialog({
height: 'auto',
maxHeight: $(window).height() - 8,
width: $iDialogWidth,
modal: true,
autoOpen: $sAutoOpen,

View File

@@ -331,31 +331,6 @@ EOF
// This selector will be reused when selecting actual tab widget A elements.
var tab_a_selector = 'ul.ui-tabs-nav a';
// This helper will be used to resize tab width
var resizeTab = function(oElem){
var iTableWidth = (oElem.children('table:first').length > 0) ? oElem.children('table:first').outerWidth() : 0;
var oLayoutContentElem = oElem.closest('.ui-layout-content');
var bEditMode = (oLayoutContentElem.find('.wizContainer').length > 0);
var oContainerElem = (bEditMode) ? oLayoutContentElem.find('.wizContainer:first') : oLayoutContentElem.find('.ui-tabs:first');
// Resizing wizard container
oContainerElem.css('min-width',
parseInt(iTableWidth) +
parseInt(oElem.css('margin-left'))*2 +
parseInt(oElem.css('padding-left'))*2 +
parseInt(tabs.css('margin-left'))*2 +
parseInt(tabs.css('padding-left'))*2
)
// Resizing header according to content container
var iLayoutContentWidth = parseInt(oContainerElem.width());
if(bEditMode)
{
iLayoutContentWidth += parseInt(oContainerElem.css('margin-left'))*2 + parseInt(oContainerElem.css('padding-left'))*2
}
oLayoutContentElem.find('.page_header').css('min-width', iLayoutContentWidth);
};
// Ugly patch for a change in the behavior of jQuery UI:
// Before jQuery UI 1.9, tabs were always considered as "local" (opposed to Ajax)
// when their href was beginning by #. Starting with 1.9, a <base> tag in the page
@@ -379,9 +354,6 @@ EOF
event: 'change', 'show': function(event, ui) {
$('.resizable', ui.panel).resizable(); // Make resizable everything that claims to be resizable !
},
create: function( event, ui ) {
resizeTab(ui.panel);
},
beforeLoad: function( event, ui ) {
if ( ui.tab.data('loaded') && (ui.tab.attr('data-cache') == 'true')) {
event.preventDefault();
@@ -391,9 +363,6 @@ EOF
ui.jqXHR.success(function() {
ui.tab.data( "loaded", true );
});
},
activate: function( event, ui ) {
resizeTab(ui.newPanel);
}
});

View File

@@ -148,6 +148,7 @@ class ApplicationMenu
// the menu already exists, let's combine the conditions that make it visible
self::$aMenusIndex[$index]['node']->AddCondition($oMenuNode);
}
return $index;
}
@@ -174,7 +175,7 @@ class ApplicationMenu
{
$oMenuNode = self::GetMenuNode($aMenu['index']);
if (!$oMenuNode->IsEnabled()) continue; // Don't display a non-enabled menu
$oPage->AddToMenu('<h3 id="Menu_'.$oMenuNode->GetMenuID().'">'.$oMenuNode->GetTitle().'</h3>');
$oPage->AddToMenu('<h3 id="'.utils::GetSafeId('AccordionMenu_'.$oMenuNode->GetMenuID()).'">'.$oMenuNode->GetTitle().'</h3>');
$oPage->AddToMenu('<div>');
$aChildren = self::GetChildren($aMenu['index']);
if (count($aChildren) > 0)
@@ -217,11 +218,11 @@ EOF
$sHyperlink = $oMenu->GetHyperlink($aExtraParams);
if ($sHyperlink != '')
{
$oPage->AddToMenu('<li id="Menu_'.$oMenu->GetMenuID().'"'.$sCSSClass.'><a href="'.$oMenu->GetHyperlink($aExtraParams).'">'.$oMenu->GetTitle().'</a></li>');
$oPage->AddToMenu('<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'"'.$sCSSClass.'><a href="'.$oMenu->GetHyperlink($aExtraParams).'">'.$oMenu->GetTitle().'</a></li>');
}
else
{
$oPage->AddToMenu('<li id="Menu_'.$oMenu->GetMenuID().'"'.$sCSSClass.'>'.$oMenu->GetTitle().'</li>');
$oPage->AddToMenu('<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'"'.$sCSSClass.'>'.$oMenu->GetTitle().'</li>');
}
$aCurrentMenu = self::$aMenusIndex[$index];
if ($iActiveMenu == $index)
@@ -320,6 +321,21 @@ EOF
}
return $sDefaultMenuId;
}
static public function GetRootMenuId($sMenuId)
{
$iMenuIndex = self::GetMenuIndexById($sMenuId);
if ($iMenuIndex == -1)
{
return '';
}
$oMenu = ApplicationMenu::GetMenuNode($iMenuIndex);
while ($oMenu->GetParentIndex() != -1)
{
$oMenu = ApplicationMenu::GetMenuNode($oMenu->GetParentIndex());
}
return $oMenu->GetMenuId();
}
}
/**
@@ -418,7 +434,12 @@ abstract class MenuNode
{
return $this->sMenuId;
}
public function GetParentIndex()
{
return $this->iParentIndex;
}
public function GetTitle()
{
return Dict::S("Menu:$this->sMenuId", str_replace('_', ' ', $this->sMenuId));

View File

@@ -142,7 +142,12 @@ class UIExtKeyWidget
throw new Exception('Implementation: null value for allowed values definition');
}
$oAllowedValues->SetShowObsoleteData(utils::ShowObsoleteData());
if ($oAllowedValues->Count() < $iMaxComboLength)
// Don't automatically launch the search if the table is huge
$bDoSearch = !utils::IsHighCardinality($this->sTargetClass);
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
if ($oAllowedValues->Count($iMaxComboLength * 2) < $iMaxComboLength)
{
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
switch($sDisplayStyle)
@@ -226,7 +231,7 @@ class UIExtKeyWidget
}
$oPage->add_ready_script(
<<<EOF
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
$('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
$('#$this->iId').bind('change', function() { $(this).trigger('extkeychange') } );
@@ -261,7 +266,7 @@ EOF
$iFieldSize = isset($aArgs['iFieldSize']) ? $aArgs['iFieldSize'] : 20; //@@@ $this->oAttDef->GetMaxSize();
// the input for the auto-complete
$sHTMLValue .= "<input class=\"field_autocomplete\" count=\"".$oAllowedValues->Count()."\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
$sHTMLValue .= "<input class=\"field_autocomplete\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.Search();\"/></span>";
// another hidden input to store & pass the object's Id
@@ -271,7 +276,7 @@ EOF
// Scripts to start the autocomplete and bind some events to it
$oPage->add_ready_script(
<<<EOF
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
$('#label_$this->iId').autocomplete(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, mustMatch: true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'$sFilter',bSearchMode:$JSSearchMode, json: function() { return $sWizHelperJSON; } }});
$('#label_$this->iId').keyup(function() { if ($(this).val() == '') { $('#$this->iId').val(''); } } ); // Useful for search forms: empty value in the "label", means no value, immediatly !
@@ -338,7 +343,7 @@ EOF
$aParams = array();
$oFilter = new DBObjectSearch($this->sTargetClass);
}
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open');
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open') || utils::IsHighCardinality($this->sTargetClass);
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oBlock = new DisplayBlock($oFilter, 'search', false, $aParams);
$sHTML .= $oBlock->GetDisplay($oPage, $this->iId, array('open' => $bOpen, 'currentId' => $this->iId));
@@ -408,14 +413,35 @@ EOF
$iCurrentExtKeyId = (is_null($oObj) || $this->sAttCode === '') ? 0 : $oObj->Get($this->sAttCode);
$oValuesSet = new ValueSetObjects($sFilter, 'friendlyname'); // Bypass GetName() to avoid the encoding by htmlentities
$iMax = 150;
$oValuesSet->SetLimit($iMax);
$oValuesSet->SetSort(false);
$oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$aValues = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains);
foreach($aValues as $sKey => $sFriendlyName)
$aValuesEquals = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'equals_start_with');
//asort($aValuesEquals);
foreach($aValuesEquals as $sKey => $sFriendlyName)
{
$oP->add(trim($sFriendlyName)."\t".$sKey."\n");
$iMax--;
}
if ($iMax <= 0)
{
return;
}
$oValuesSet->SetLimit($iMax);
$aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains');
asort($aValuesContains);
foreach($aValuesContains as $sKey => $sFriendlyName)
{
if (!isset($aValuesEquals[$sKey]))
{
$oP->add(trim($sFriendlyName)."\t".$sKey."\n");
}
}
}
/**
* Get the display name of the selected object, to fill back the autocomplete
*/

View File

@@ -30,7 +30,14 @@ class UILinksWidgetDirect
protected $sInputid;
protected $sNameSuffix;
protected $sLinkedClass;
/**
* UILinksWidgetDirect constructor.
* @param string $sClass
* @param string $sAttCode
* @param string $sInputId
* @param string $sNameSuffix
*/
public function __construct($sClass, $sAttCode, $sInputId, $sNameSuffix = '')
{
$this->sClass = $sClass;
@@ -74,12 +81,12 @@ class UILinksWidgetDirect
/**
* @param WebPage $oPage
* @param DBObjectSet $oValue
* @param DBObjectSet|ormLinkSet $oValue
* @param array $aArgs
* @param $sFormPrefix
* @param $oCurrentObj
* @param string $sFormPrefix
* @param DBObject $oCurrentObj
*/
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
public function Display(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
{
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
switch($oLinksetDef->GetEditMode())
@@ -127,11 +134,11 @@ class UILinksWidgetDirect
* @param WebPage $oPage
* @param DBObjectSet $oValue
* @param array $aArgs
* @param $sFormPrefix
* @param $oCurrentObj
* @param $bDisplayMenu
* @param string $sFormPrefix
* @param DBObject $oCurrentObj
* @param bool $bDisplayMenu
*/
protected function DisplayAsBlock(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $bDisplayMenu)
protected function DisplayAsBlock(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $bDisplayMenu)
{
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$sTargetClass = $oLinksetDef->GetLinkedClass();
@@ -170,50 +177,8 @@ class UILinksWidgetDirect
/**
* @param WebPage $oPage
* @param DBObjectSet $oValue
* @param array $aArgs
* @param $sFormPrefix
* @param $oCurrentObj
* @param array $aButtons
* @param string $sProposedRealClass
*/
protected function DisplayEditInPlace(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
{
$aAttribs = $this->GetTableConfig();
$oValue->Rewind();
$oPage->add('<table class="listContainer" id="'.$this->sInputid.'"><tr><td>');
$aData = array();
while($oLinkObj = $oValue->Fetch())
{
$aRow = array();
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.$oLinkObj->GetKey().'"/>';
foreach($this->aZlist as $sLinkedAttCode)
{
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
}
$aData[] = $aRow;
}
$oPage->table($aAttribs, $aData);
$oPage->add('</td></tr></table>'); //listcontainer
$sInputName = $sFormPrefix.'attr_'.$this->sAttCode;
$aLabels = array(
'delete' => Dict::S('UI:Button:Delete'),
// 'modify' => 'Modify...' ,
'creation_title' => Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sLinkedClass)),
'create' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($this->sLinkedClass)),
'remove' => Dict::S('UI:Button:Remove'),
'add' => Dict::Format('UI:AddAnExisting_Class', MetaModel::GetName($this->sLinkedClass)),
'selection_title' => Dict::Format('UI:SelectionOf_Class', MetaModel::GetName($this->sLinkedClass)),
);
$oContext = new ApplicationContext();
$sSubmitUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?'.$oContext->GetForLink();
$sJSONLabels = json_encode($aLabels);
$sJSONButtons = json_encode($aButtons);
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
$oPage->add_ready_script("$('#{$this->sInputid}').directlinks({class_name: '$this->sClass', att_code: '$this->sAttCode', input_name:'$sInputName', labels: $sJSONLabels, submit_to: '$sSubmitUrl', buttons: $sJSONButtons, oWizardHelper: $sWizHelper });");
}
public function GetObjectCreationDlg(WebPage $oPage, $sProposedRealClass = '')
{
// For security reasons: check that the "proposed" class is actually a subclass of the linked class
@@ -239,14 +204,14 @@ class UILinksWidgetDirect
$aKeys = array_keys($aPossibleClasses);
$sRealClass = $aKeys[0];
}
if ($sRealClass != '')
{
$oPage->add("<h1>".MetaModel::GetClassIcon($sRealClass)."&nbsp;".Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($sRealClass))."</h1>\n");
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
$aFieldFlags = array( $sExtKeyToMe => OPT_ATT_HIDDEN);
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, null, array(), array('formPrefix' => $this->sInputid, 'noRelations' => true, 'fieldsFlags' => $aFieldFlags));
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, null, array(), array('formPrefix' => $this->sInputid, 'noRelations' => true, 'fieldsFlags' => $aFieldFlags));
}
else
{
@@ -263,7 +228,61 @@ class UILinksWidgetDirect
}
$oPage->add('</div></div>');
}
/**
* @param WebPage $oPage
* @param DBObjectSet $oValue
* @param array $aArgs
* @param string $sFormPrefix
* @param DBObject $oCurrentObj
* @param array $aButtons
*/
protected function DisplayEditInPlace(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
{
$aAttribs = $this->GetTableConfig();
$oValue->Rewind();
$oPage->add('<table class="listContainer" id="'.$this->sInputid.'"><tr><td>');
$aData = array();
while($oLinkObj = $oValue->Fetch())
{
$aRow = array();
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.$oLinkObj->GetKey().'"/>';
foreach($this->aZlist as $sLinkedAttCode)
{
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
}
$aData[] = $aRow;
}
$oPage->table($aAttribs, $aData);
$oPage->add('</td></tr></table>'); //listcontainer
$sInputName = $sFormPrefix.'attr_'.$this->sAttCode;
$aLabels = array(
'delete' => Dict::S('UI:Button:Delete'),
// 'modify' => 'Modify...' ,
'creation_title' => Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sLinkedClass)),
'create' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($this->sLinkedClass)),
'remove' => Dict::S('UI:Button:Remove'),
'add' => Dict::Format('UI:AddAnExisting_Class', MetaModel::GetName($this->sLinkedClass)),
'selection_title' => Dict::Format('UI:SelectionOf_Class', MetaModel::GetName($this->sLinkedClass)),
);
$oContext = new ApplicationContext();
$sSubmitUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?'.$oContext->GetForLink();
$sJSONLabels = json_encode($aLabels);
$sJSONButtons = json_encode($aButtons);
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
// Don't automatically launch the search if the table is huge
$bDoSearch = !utils::IsHighCardinality($this->sLinkedClass);
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
$oPage->add_ready_script("$('#{$this->sInputid}').directlinks({class_name: '$this->sClass', att_code: '$this->sAttCode', input_name:'$sInputName', labels: $sJSONLabels, submit_to: '$sSubmitUrl', buttons: $sJSONButtons, oWizardHelper: $sWizHelper, do_search: $sJSDoSearch});");
}
/**
* @param WebPage $oPage
* @param DBObject $oCurrentObj
* @throws Exception
*/
public function GetObjectsSelectionDlg($oPage, $oCurrentObj)
{
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
@@ -286,7 +305,7 @@ class UILinksWidgetDirect
{
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
}
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open');
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open') || utils::IsHighCardinality($this->sLinkedClass);
$oBlock = new DisplayBlock($oFilter, 'search', false);
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->sInputid}", array('open' => $bOpen));
$sHtml .= "<form id=\"ObjectsAddForm_{$this->sInputid}\">\n";
@@ -299,13 +318,14 @@ class UILinksWidgetDirect
$sHtml .= "</form>\n";
$oPage->add($sHtml);
}
/**
* Search for objects to be linked to the current object (i.e "remote" objects)
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of $this->sLinkedClass
* @param array $aAlreadyLinked Array of indentifiers of objects which are already linke to the current object (or about to be linked)
* @param DBObject $oCurrentObj The object currently being edited... if known...
* @throws Exception
*/
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinked = array(), $oCurrentObj = null)
{
@@ -350,6 +370,10 @@ class UILinksWidgetDirect
$oBlock->Display($oP, "ResultsToAdd_{$this->sInputid}", array('menu' => false, 'cssCount'=> '#count_'.$this->sInputid , 'selection_mode' => true, 'table_id' => 'add_'.$this->sInputid)); // Don't display the 'Actions' menu on the results
}
/**
* @param WebPage $oP
* @param $oFullSetFilter
*/
public function DoAddObjects(WebPage $oP, $oFullSetFilter)
{
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
@@ -377,7 +401,14 @@ class UILinksWidgetDirect
}
return $aAttribs;
}
/**
* @param WebPage $oPage
* @param string $sRealClass
* @param array $aValues
* @param int $iTempId
* @return mixed
*/
public function GetRow($oPage, $sRealClass, $aValues, $iTempId)
{
if ($sRealClass == '')
@@ -389,7 +420,13 @@ class UILinksWidgetDirect
return $this->GetObjectRow($oPage, $oLinkObj, $iTempId);
}
/**
* @param WebPage $oPage
* @param $oLinkObj
* @param int $iTempId
* @return mixed
*/
protected function GetObjectRow($oPage, $oLinkObj, $iTempId)
{
$aAttribs = $this->GetTableConfig();
@@ -405,7 +442,7 @@ class UILinksWidgetDirect
/**
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
* @param DBObject $oSourceObj
* @param DBSearch $oSearch
* @param DBSearch|DBObjectSearch $oSearch
*/
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
{
@@ -424,7 +461,6 @@ class UILinksWidgetDirect
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
{
$oAttDef = MetaModel::GetAttributeDef($sSrcClass, $sAttCode);
$defaultValue = $oSourceObj->Get($sAttCode);
// Find the attcode for the same 'context' parameter in the destination class

View File

@@ -39,7 +39,15 @@ class UILinksWidget
protected $m_sLinkedClass;
protected $m_sRemoteClass;
protected $m_bDuplicatesAllowed;
/**
* UILinksWidget constructor.
* @param string $sClass
* @param string $sAttCode
* @param int $iInputId
* @param string $sNameSuffix
* @param bool $bDuplicatesAllowed
*/
public function __construct($sClass, $sAttCode, $iInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
{
$this->m_sClass = $sClass;
@@ -98,11 +106,11 @@ class UILinksWidget
* @param WebPage $oP Web page used for the ouput
* @param DBObject $oLinkedObj Remote object
* @param mixed $linkObjOrId Either the object linked or a unique number for new link records to add
* @param array|Hash $aArgs Extra context arguments
* @param $oCurrentObj The object to which all the elements of the linked set refer to
* @param $iUniqueId A unique identifier of new links
* @param boolean $bReadOnly Display link as editable or read-only. Default is false (editable)
* @return string The HTML fragment of the one-row form
* @param array $aArgs Extra context arguments
* @param DBObject $oCurrentObj The object to which all the elements of the linked set refer to
* @param int $iUniqueId A unique identifier of new links
* @param boolean $bReadOnly Display link as editable or read-only. Default is false (editable)
* @return array The HTML fragment of the one-row form
*/
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false)
{
@@ -138,7 +146,9 @@ class UILinksWidget
$sValue = $linkObjOrId->Get($sFieldCode);
$sDisplayValue = $linkObjOrId->GetEditValue($sFieldCode);
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $sValue, $sDisplayValue, $sSafeId, $sNameSuffix, 0, $aArgs);
$aRow[$sFieldCode] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'.
cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $sValue, $sDisplayValue, $sSafeId, $sNameSuffix, 0, $aArgs).
'</div></div></div>';
$aFieldsMap[$sFieldCode] = $sSafeId;
}
}
@@ -166,7 +176,7 @@ class UILinksWidget
$sPrefix .= "[-$iUniqueId][";
$sNameSuffix = "]"; // To make a tabular form
$aArgs['prefix'] = $sPrefix;
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".$iUniqueId;
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".($iUniqueId < 0 ? -$iUniqueId : $iUniqueId);
$aArgs['this'] = $oNewLinkObj;
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"-$iUniqueId\">";
foreach($this->m_aEditableFields as $sFieldCode)
@@ -176,17 +186,24 @@ class UILinksWidget
$sValue = $oNewLinkObj->Get($sFieldCode);
$sDisplayValue = $oNewLinkObj->GetEditValue($sFieldCode);
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $sValue, $sDisplayValue, $sSafeId /* id */, $sNameSuffix, 0, $aArgs);
$aRow[$sFieldCode] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'.
cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $sValue, $sDisplayValue, $sSafeId /* id */, $sNameSuffix, 0, $aArgs).
'</div></div></div>';
$aFieldsMap[$sFieldCode] = $sSafeId;
}
$sState = '';
$oP->add_script(
<<<EOF
// Rows created with ajax call need OnLinkAdded call.
// Rows added before loading the form cannot call OnLinkAdded.
if ($iUniqueId > 0)
{
$oP->add_script(
<<<EOF
PrepareWidgets();
oWidget{$this->m_iInputId}.OnLinkAdded($iUniqueId, $iRemoteObjKey);
EOF
);
);
}
}
if(!$bReadOnly)
@@ -220,7 +237,11 @@ EOF
/**
* Display one row of the whole form
* @return none
* @param WebPage $oP
* @param array $aConfig
* @param array $aRow
* @param int $iRowId
* @return string
*/
protected function DisplayFormRow(WebPage $oP, $aConfig, $aRow, $iRowId)
{
@@ -238,8 +259,8 @@ EOF
/**
* Display the table with the form for editing all the links at once
* @param WebPage $oP The web page used for the output
* @param Hash $aConfig The table's header configuration
* @param Hash $aData The tabular data to be displayed
* @param array $aConfig The table's header configuration
* @param array $aData The tabular data to be displayed
* @return string Html fragment representing the form table
*/
protected function DisplayFormTable(WebPage $oP, $aConfig, $aData)
@@ -276,21 +297,20 @@ EOF
return $sHtml;
}
/**
* Get the HTML fragment corresponding to the linkset editing widget
* @param WebPage $oP The web page used for all the output
* @param DBObjectSet The initial value of the linked set
* @param Hash $aArgs Extra context arguments
* @param WebPage $oPage
* @param DBObject|ormLinkSet $oValue
* @param array $aArgs Extra context arguments
* @param string $sFormPrefix prefix of the fields in the current form
* @param DBObject $oCurrentObj the current object to which the linkset is related
* @return string The HTML fragment to be inserted into the page
*/
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
public function Display(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
{
$sHtmlValue = '';
$sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode);
$sHtmlValue .= "<div id=\"linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix}\">\n";
$sHtmlValue .= "<input type=\"hidden\" id=\"{$sFormPrefix}{$this->m_iInputId}\">\n";
$oValue->Rewind();
@@ -324,9 +344,12 @@ EOF
}
$sHtmlValue .= $this->DisplayFormTable($oPage, $this->m_aTableConfig, $aForm);
$sDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
// Don't automatically launch the search if the table is huge
$bDoSearch = !utils::IsHighCardinality($this->m_sRemoteClass);
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
$oPage->add_ready_script(<<<EOF
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates, $sWizHelper, '{$this->m_sExtKeyToRemote}');
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates, $sWizHelper, '{$this->m_sExtKeyToRemote}', $sJSDoSearch);
oWidget{$this->m_iInputId}.Init();
EOF
);
@@ -337,11 +360,17 @@ EOF
$oPage->add_at_the_end("<div id=\"dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}\"></div>"); // To prevent adding forms inside the main form
return $sHtmlValue;
}
/**
* @param string $sClass
* @param string $sAttCode
* @return string
*/
protected static function GetTargetClass($sClass, $sAttCode)
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sLinkedClass = $oAttDef->GetLinkedClass();
$sTargetClass = '';
switch(get_class($oAttDef))
{
case 'AttributeLinkedSetIndirect':
@@ -356,10 +385,14 @@ EOF
return $sTargetClass;
}
/**
* @param WebPage $oPage
* @param DBObject $oCurrentObj
*/
public function GetObjectPickerDialog($oPage, $oCurrentObj)
{
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open');
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open') || utils::IsHighCardinality($this->m_sRemoteClass);
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
@@ -405,7 +438,13 @@ EOF
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, "ResultsToAdd_{$this->m_sAttCode}", array('menu' => false, 'cssCount'=> '#count_'.$this->m_sAttCode.$this->m_sNameSuffix , 'selection_mode' => true, 'table_id' => 'add_'.$this->m_sAttCode)); // Don't display the 'Actions' menu on the results
}
/**
* @param WebPage $oP
* @param int $iMaxAddedId
* @param $oFullSetFilter
* @param DBObject $oCurrentObj
*/
public function DoAddObjects(WebPage $oP, $iMaxAddedId, $oFullSetFilter, $oCurrentObj)
{
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
@@ -430,7 +469,7 @@ EOF
/**
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
* @param DBObject $oSourceObj
* @param DBSearch $oSearch
* @param DBSearch|DBObjectSearch $oSearch
*/
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
{
@@ -449,7 +488,6 @@ EOF
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
{
$oAttDef = MetaModel::GetAttributeDef($sSrcClass, $sAttCode);
$defaultValue = $oSourceObj->Get($sAttCode);
// Find the attcode for the same 'context' parameter in the destination class

View File

@@ -141,6 +141,9 @@ class utils
}
protected static $bPageMode = null;
/**
* @var boolean[]
*/
protected static $aModes = array();
public static function InitArchiveMode()
@@ -164,6 +167,10 @@ class utils
self::$bPageMode = ($iCurrent == 1);
}
/**
* @param boolean $bMode if true then activate archive mode (archived objects are visible), otherwise archived objects are
* hidden (archive = "soft deletion")
*/
public static function PushArchiveMode($bMode)
{
array_push(self::$aModes, $bMode);
@@ -174,6 +181,9 @@ class utils
array_pop(self::$aModes);
}
/**
* @return boolean true if archive mode is enabled
*/
public static function IsArchiveMode()
{
if (count(self::$aModes) > 0)
@@ -605,16 +615,20 @@ class utils
{
if (self::$oConfig == null)
{
$sConfigFile = self::GetConfigFilePath();
if (file_exists($sConfigFile))
{
self::$oConfig = new Config($sConfigFile);
}
else
{
// When executing the setup, the config file may be still missing
self::$oConfig = new Config();
}
self::$oConfig = MetaModel::GetConfig();
if (self::$oConfig == null)
{
$sConfigFile = self::GetConfigFilePath();
if (file_exists($sConfigFile))
{
self::$oConfig = new Config($sConfigFile);
}
else
{
// When executing the setup, the config file may be still missing
self::$oConfig = new Config();
}
}
}
return self::$oConfig;
}
@@ -917,7 +931,7 @@ class utils
*/
public static function GetCachePath()
{
return APPROOT.'data/cache-'.self::GetCurrentEnvironment().'/';
return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/';
}
/**
* Merge standard menu items with plugin provided menus items
@@ -957,8 +971,8 @@ class utils
// PDF export requires GD
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportPDF', Dict::S('UI:Menu:ExportPDF'), "ExportListDlg('$sOQL', '$sDataTableId', 'pdf', ".json_encode(Dict::S('UI:Menu:ExportPDF')).")");
}
}
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL')");
}
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL', '$sContext')");
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
break;
@@ -1054,8 +1068,7 @@ class utils
}
/**
* Returns the absolute URL to the modules root path
* @return string ...
* @return string the absolute URL to the modules root path
*/
static public function GetAbsoluteUrlModulesRoot()
{
@@ -1063,33 +1076,67 @@ class utils
return $sUrl;
}
/**
* Returns the URL to a page that will execute the requested module page
*
* To be compatible with this mechanism, the called page must include approot
* with an absolute path OR not include it at all (losing the direct access to the page)
* if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
* require_once(__DIR__.'/../../approot.inc.php');
*
* @return string ...
*/
/**
* To be compatible with this mechanism, the called page must include approot with an absolute path OR not include
* it at all (losing the direct access to the page) :
*
* ```php
* if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
* require_once(__DIR__.'/../../approot.inc.php');
* ```
*
* @param string $sModule
* @param string $sPage
* @param string[] $aArguments
* @param string $sEnvironment
*
* @return string the URL to a page that will execute the requested module page, with query string values url encoded
*
* @see GetExecPageArguments can be used to submit using the GET method (see bug in N.1108)
* @see GetAbsoluteUrlExecPage
*/
static public function GetAbsoluteUrlModulePage($sModule, $sPage, $aArguments = array(), $sEnvironment = null)
{
$aArgs = self::GetExecPageArguments($sModule, $sPage, $aArguments, $sEnvironment);
$sArgs = http_build_query($aArgs);
return self::GetAbsoluteUrlExecPage()."?".$sArgs;
}
/**
* @param string $sModule
* @param string $sPage
* @param string[] $aArguments
* @param string $sEnvironment
*
* @return string[] key/value pair for the exec page query string. <b>Warning</b> : values are not url encoded !
* @throws \Exception if one of the argument has a reserved name
*/
static public function GetExecPageArguments($sModule, $sPage, $aArguments = array(), $sEnvironment = null)
{
$sEnvironment = is_null($sEnvironment) ? self::GetCurrentEnvironment() : $sEnvironment;
$aArgs = array();
$aArgs[] = 'exec_module='.$sModule;
$aArgs[] = 'exec_page='.$sPage;
$aArgs[] = 'exec_env='.$sEnvironment;
$aArgs['exec_module'] = $sModule;
$aArgs['exec_page'] = $sPage;
$aArgs['exec_env'] = $sEnvironment;
foreach($aArguments as $sName => $sValue)
{
if (($sName == 'exec_module')||($sName == 'exec_page')||($sName == 'exec_env'))
if (($sName == 'exec_module') || ($sName == 'exec_page') || ($sName == 'exec_env'))
{
throw new Exception("Module page: $sName is a reserved page argument name");
}
$aArgs[] = $sName.'='.urlencode($sValue);
$aArgs[$sName] = $sValue;
}
$sArgs = implode('&', $aArgs);
return self::GetAbsoluteUrlAppRoot().'pages/exec.php?'.$sArgs;
return $aArgs;
}
/**
* @return string
*/
static public function GetAbsoluteUrlExecPage()
{
return self::GetAbsoluteUrlAppRoot().'pages/exec.php';
}
/**
@@ -1590,4 +1637,220 @@ class utils
}
return null;
}
/**
* Check if the given path/url is an http(s) URL
* @param string $sPath
* @return boolean
*/
public static function IsURL($sPath)
{
$bRet = false;
if ((substr($sPath, 0, 7) == 'http://') || (substr($sPath, 0, 8) == 'https://') || (substr($sPath, 0, 8) == 'ftp://'))
{
$bRet = true;
}
return $bRet;
}
/**
* Check if the given URL is a link to download a document/image on the CURRENT iTop
* In such a case we can read the content of the file directly in the database (if the users rights allow) and return the ormDocument
* @param string $sPath
* @return false|ormDocument
* @throws Exception
*/
public static function IsSelfURL($sPath)
{
$result = false;
$sPageUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.document.php';
if (substr($sPath, 0, strlen($sPageUrl)) == $sPageUrl)
{
// If the URL is an URL pointing to this instance of iTop, then
// extract the "query" part of the URL and analyze it
$sQuery = parse_url($sPath, PHP_URL_QUERY);
if ($sQuery !== null)
{
$aParams = array();
foreach(explode('&', $sQuery) as $sChunk)
{
$aParts = explode('=', $sChunk);
if (count($aParts) != 2) continue;
$aParams[$aParts[0]] = urldecode($aParts[1]);
}
$result = array_key_exists('operation', $aParams) && array_key_exists('class', $aParams) && array_key_exists('id', $aParams) && array_key_exists('field', $aParams) && ($aParams['operation'] == 'download_document');
if ($result)
{
// This is a 'download_document' operation, let's retrieve the document directly from the database
$sClass = $aParams['class'];
$iKey = $aParams['id'];
$sAttCode = $aParams['field'];
$oObj = MetaModel::GetObject($sClass, $iKey, false /* must exist */); // Users rights apply here !!
if ($oObj)
{
/**
* @var ormDocument $result
*/
$result = clone $oObj->Get($sAttCode);
return $result;
}
}
}
throw new Exception('Invalid URL. This iTop URL is not pointing to a valid Document/Image.');
}
return $result;
}
/**
* Read the content of a file (and retrieve its MIME type) from either:
* - an URL pointing to a blob (image/document) on the current iTop server
* - an http(s) URL
* - the local file system (but only if you are an administrator)
* @param string $sPath
* @return ormDocument|null
* @throws Exception
*/
public static function FileGetContentsAndMIMEType($sPath)
{
$oUploadedDoc = null;
$aKnownExtensions = array(
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'png' => 'image/png',
'pdf' => 'application/pdf',
'doc' => 'application/msword',
'dot' => 'application/msword',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'vsd' => 'application/x-visio',
'vdx' => 'application/visio.drawing',
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'zip' => 'application/zip',
'txt' => 'text/plain',
'htm' => 'text/html',
'html' => 'text/html',
'exe' => 'application/octet-stream'
);
$sData = null;
$sMimeType = 'text/plain'; // Default MIME Type: treat the file as a bunch a characters...
$sFileName = 'uploaded-file'; // Default name for downloaded-files
$sExtension = '.txt'; // Default file extension in case we don't know the MIME Type
if(empty($sPath))
{
// Empty path (NULL or '') means that there is no input, making an empty document.
$oUploadedDoc = new ormDocument('', '', '');
}
elseif (static::IsURL($sPath))
{
if ($oUploadedDoc = static::IsSelfURL($sPath))
{
// Nothing more to do, we've got it !!
}
else
{
// Remote file, let's use the HTTP headers to find the MIME Type
$sData = @file_get_contents($sPath);
if ($sData === false)
{
throw new Exception("Failed to load the file from the URL '$sPath'.");
}
else
{
if (isset($http_response_header))
{
$aHeaders = static::ParseHeaders($http_response_header);
$sMimeType = array_key_exists('Content-Type', $aHeaders) ? strtolower($aHeaders['Content-Type']) : 'application/x-octet-stream';
// Compute the file extension from the MIME Type
foreach($aKnownExtensions as $sExtValue => $sMime)
{
if ($sMime === $sMimeType)
{
$sExtension = '.'.$sExtValue;
break;
}
}
}
$sFileName .= $sExtension;
}
$oUploadedDoc = new ormDocument($sData, $sMimeType, $sFileName);
}
}
else if (UserRights::IsAdministrator())
{
// Only administrators are allowed to read local files
$sData = @file_get_contents($sPath);
if ($sData === false)
{
throw new Exception("Failed to load the file '$sPath'. The file does not exist or the current process is not allowed to access it.");
}
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
$sFileName = basename($sPath);
if (array_key_exists($sExtension, $aKnownExtensions))
{
$sMimeType = $aKnownExtensions[$sExtension];
}
else if (extension_loaded('fileinfo'))
{
$finfo = new finfo(FILEINFO_MIME);
$sMimeType = $finfo->file($sPath);
}
$oUploadedDoc = new ormDocument($sData, $sMimeType, $sFileName);
}
return $oUploadedDoc;
}
protected static function ParseHeaders($aHeaders)
{
$aCleanHeaders = array();
foreach( $aHeaders as $sKey => $sValue )
{
$aTokens = explode(':', $sValue, 2);
if(isset($aTokens[1]))
{
$aCleanHeaders[trim($aTokens[0])] = trim($aTokens[1]);
}
else
{
// The header is not in the form Header-Code: Value
$aCleanHeaders[] = $sValue; // Store the value as-is
$aMatches = array();
// Check if it's not the HTTP response code
if( preg_match("|HTTP/[0-9\.]+\s+([0-9]+)|", $sValue, $aMatches) )
{
$aCleanHeaders['reponse_code'] = intval($aMatches[1]);
}
}
}
return $aCleanHeaders;
}
/**
* Check if the given class if configured as a high cardinality class.
*
* @param $sClass
*
* @return bool
*/
public static function IsHighCardinality($sClass)
{
$aHugeClasses = MetaModel::GetConfig()->Get('high_cardinality_classes');
return in_array($sClass, $aHugeClasses);
}
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2016 Combodo SARL
// Copyright (C) 2016-2017 Combodo SARL
//
// This file is part of iTop.
//
@@ -67,3 +67,32 @@ if (!function_exists('apc_store') && function_exists('apcu_store'))
return apcu_store($key, $var, $ttl);
}
}
/**
* Returns user cache info... beware of the format of the returned structure that may vary (See usages)
* @return array
*/
function apc_cache_info_compat()
{
if (!function_exists('apc_cache_info')) return array();
$oFunction = new ReflectionFunction('apc_cache_info');
if ($oFunction->getNumberOfParameters() != 2)
{
// Beware: APCu behaves slightly differently from APC !!
// Worse: the compatibility layer integrated into APC differs from apcu-bc (testing the number of parameters is a must)
// In CLI mode (PHP > 7) apc_cache_info returns null and outputs an error message.
$aCacheUserData = @apc_cache_info();
}
else
{
$aCacheUserData = @apc_cache_info('user');
}
return $aCacheUserData;
}
// Cache emulation
if (!function_exists('apc_store'))
{
require_once(APPROOT.'core/apc-emulation.php');
}

301
core/apc-emulation.php Normal file
View File

@@ -0,0 +1,301 @@
<?php
// Copyright (c) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
//
/**
* Date: 27/09/2017
*/
/**
* @param string $cache_type
* @param bool $limited
* @return array|bool
*/
function apc_cache_info($cache_type = '', $limited = false)
{
$aInfo = array();
$sRootCacheDir = apc_emul_get_cache_filename('');
$aInfo['cache_list'] = apc_emul_get_cache_entries($sRootCacheDir);
return $aInfo;
}
function apc_emul_get_cache_entries($sEntry)
{
$aResult = array();
if (is_dir($sEntry))
{
$aFiles = array_diff(scandir($sEntry), array('.', '..'));
foreach($aFiles as $sFile)
{
$sSubFile = $sEntry.'/'.$sFile;
$aResult = array_merge($aResult, apc_emul_get_cache_entries($sSubFile));
}
}
else
{
$sKey = basename($sEntry);
if (strpos($sKey, '-') === 0)
{
$sKey = substr($sKey, 1);
}
$aResult[] = array('info' => $sKey);
}
return $aResult;
}
/**
* @param array|string $key
* @param $var
* @param int $ttl
* @return array|bool
*/
function apc_store($key, $var = NULL, $ttl = 0)
{
if (is_array($key))
{
$aResult = array();
foreach($key as $sKey => $value)
{
$aResult[] = apc_emul_store_unit($sKey, $value, $ttl);
}
return $aResult;
}
return apc_emul_store_unit($key, $var, $ttl);
}
/**
* @param string $sKey
* @param $value
* @param int $iTTL time to live
* @return bool
*/
function apc_emul_store_unit($sKey, $value, $iTTL)
{
if ($iTTL > 0)
{
// hint for ttl management
$sKey = '-'.$sKey;
}
$sFilename = apc_emul_get_cache_filename($sKey);
// try to create the folder
$sDirname = dirname($sFilename);
if (!file_exists($sDirname))
{
if (!@mkdir($sDirname, 0755, true))
{
return false;
}
}
$bRes = !(@file_put_contents($sFilename, serialize($value), LOCK_EX) === false);
apc_emul_manage_new_entry($sFilename);
return $bRes;
}
/**
* @param $key string|array
* @return mixed
*/
function apc_fetch($key)
{
if (is_array($key))
{
$aResult = array();
foreach($key as $sKey)
{
$aResult[$sKey] = apc_emul_fetch_unit($sKey);
}
return $aResult;
}
return apc_emul_fetch_unit($key);
}
/**
* @param $sKey
* @return bool|mixed
*/
function apc_emul_fetch_unit($sKey)
{
// Try the 'TTLed' version
$sValue = apc_emul_readcache_locked(apc_emul_get_cache_filename('-'.$sKey));
if ($sValue === false)
{
$sValue = apc_emul_readcache_locked(apc_emul_get_cache_filename($sKey));
if ($sValue === false)
{
return false;
}
}
$oRes = @unserialize($sValue);
return $oRes;
}
function apc_emul_readcache_locked($sFilename)
{
$file = @fopen($sFilename, 'r');
if ($file === false)
{
return false;
}
flock($file, LOCK_SH);
$sContent = @fread($file, @filesize($sFilename));
flock($file, LOCK_UN);
fclose($file);
return $sContent;
}
/**
* @param string $cache_type
* @return bool
*/
function apc_clear_cache($cache_type = '')
{
$sRootCacheDir = apc_emul_get_cache_filename('');
apc_emul_delete_entry($sRootCacheDir);
return true;
}
function apc_emul_delete_entry($sCache)
{
if (is_dir($sCache))
{
$aFiles = array_diff(scandir($sCache), array('.', '..'));
foreach($aFiles as $sFile)
{
$sSubFile = $sCache.'/'.$sFile;
if (!apc_emul_delete_entry($sSubFile))
{
return false;
}
}
if (!@rmdir($sCache))
{
return false;
}
}
else
{
if (!@unlink($sCache))
{
return false;
}
}
return true;
}
/**
* @param $key
* @return bool|string[]
*/
function apc_delete($key)
{
return apc_emul_delete_entry(apc_emul_get_cache_filename($key));
}
function apc_emul_get_cache_filename($sKey)
{
$sPath = str_replace(array(' ', '/', '\\', '.'), '-', $sKey);
return utils::GetCachePath().'apc-emul/'.$sPath;
}
/** Manage the cache files when a new cache entry is added
* @param string $sNewFilename new cache file added
*/
function apc_emul_manage_new_entry($sNewFilename)
{
// Check only once per request
static $aFilesByTime = null;
static $iFileCount = 0;
$iMaxFiles = MetaModel::GetConfig()->Get('apc_cache_emulation.max_entries');
if ($iMaxFiles == 0)
{
return;
}
if (!$aFilesByTime)
{
$sRootCacheDir = apc_emul_get_cache_filename('');
$aFilesByTime = apc_emul_list_files_time($sRootCacheDir);
$iFileCount = count($aFilesByTime);
if ($iMaxFiles !== 0)
{
asort($aFilesByTime);
}
}
else
{
$aFilesByTime[$sNewFilename] = time();
$iFileCount++;
}
if ($iFileCount > $iMaxFiles)
{
$iFileNbToRemove = $iFileCount - $iMaxFiles;
foreach($aFilesByTime as $sFileToRemove => $iTime)
{
@unlink($sFileToRemove);
if ($iFileNbToRemove-- === 0)
{
break;
}
}
$aFilesByTime = array_slice($aFilesByTime, $iFileCount - $iMaxFiles, null, true);
$iFileCount = $iMaxFiles;
}
}
/** Get the list of files with their associated access time
* @param string $sCheck Directory to scan
* @param array $aFilesByTime used by recursion
* @return array
*/
function apc_emul_list_files_time($sCheck, &$aFilesByTime = array())
{
// Garbage collection
$aFiles = array_diff(@scandir($sCheck), array('.', '..'));
foreach($aFiles as $sFile)
{
$sSubFile = $sCheck.'/'.$sFile;
if (is_dir($sSubFile))
{
apc_emul_list_files_time($sSubFile, $aFilesByTime);
}
else
{
$iTime = apc_emul_get_file_time($sSubFile);
if ($iTime !== false)
{
$aFilesByTime[$sSubFile] = $iTime;
}
}
}
return $aFilesByTime;
}
/** Get the file access time if TTL is managed
* @param string $sFilename
* @return bool|int returns the file atime or false if not relevant
*/
function apc_emul_get_file_time($sFilename)
{
if (strpos(basename($sFilename), '-') === 0)
{
return @fileatime($sFilename);
}
return false;
}

View File

@@ -702,7 +702,7 @@ abstract class AttributeDefinition
break;
default:
throw new Exception("Unknown verb '$sVerb' for attribute ".$this->GetCode().' in class '.get_class($oHostObj));
throw new Exception("Unknown verb '$sVerb' for attribute ".$this->GetCode().' in class '.get_class($oHostObject));
}
}
return null;
@@ -1053,20 +1053,20 @@ class AttributeLinkedSet extends AttributeDefinition
$oLinkSet = clone $value; // Workaround/Safety net for Trac #887
$iLimit = MetaModel::GetConfig()->Get('max_linkset_output');
if ($iLimit > 0)
$iCount = 0;
$aNames = array();
foreach($oLinkSet as $oItem)
{
$oLinkSet->SetLimit($iLimit);
}
$aNames = $oLinkSet->GetColumnAsArray($sRemoteName);
if ($iLimit > 0)
{
$iTotal = $oLinkSet->Count();
if ($iTotal > count($aNames))
if (($iLimit > 0) && ($iCount == $iLimit))
{
$aNames[] = '... '.Dict::Format('UI:TruncatedResults', count($aNames), $iTotal);
$iTotal = $oLinkSet->Count();
$aNames[] = '... '.Dict::Format('UI:TruncatedResults', $iCount, $iTotal);
break;
}
$aNames[] = $oItem->Get($sRemoteName);
$iCount++;
}
switch($sVerb)
{
case '':
@@ -1342,6 +1342,29 @@ class AttributeLinkedSet extends AttributeDefinition
return $oSet;
}
/**
* @param $proposedValue
* @param $oHostObj
*
* @return mixed
*/
public function MakeRealValue($proposedValue, $oHostObj){
if($proposedValue === null)
{
$sLinkedClass = $this->GetLinkedClass();
$aLinkedObjectsArray = array();
$oSet = DBObjectSet::FromArray($sLinkedClass, $aLinkedObjectsArray);
return new ormLinkSet(
get_class($oHostObj),
$this->GetCode(),
$oSet
);
}
return $proposedValue;
}
/**
* @param ormLinkSet $val1
* @param ormLinkSet $val2
@@ -1355,7 +1378,7 @@ class AttributeLinkedSet extends AttributeDefinition
}
else
{
$bAreEquivalent = $val1->Equals($val2);
$bAreEquivalent = ($val2->HasDelta() === false);
}
return $bAreEquivalent;
}
@@ -3963,6 +3986,42 @@ class AttributeDateTime extends AttributeDBField
return $oFormField;
}
/**
* @inheritdoc
*/
public function EnumTemplateVerbs()
{
return array(
'' => 'Formatted representation',
'raw' => 'Not formatted representation',
);
}
/**
* @inheritdoc
*/
public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
{
switch ($sVerb)
{
case '':
case 'text':
return static::GetFormat()->format($value);
break;
case 'html':
// Note: Not passing formatted value as the method will format it.
return $this->GetAsHTML($value);
break;
case 'raw':
return $value;
break;
default:
return parent::GetForTemplate($value, $sVerb, $oHostObject, $bLocalize);
break;
}
return null;
}
static public function ListExpectedParams()
{
return parent::ListExpectedParams();
@@ -5207,75 +5266,34 @@ class AttributeBlob extends AttributeDefinition
{
return '';
}
// Facilitate things: allow administrators to upload a document
// from a CSV by specifying its path/URL
/**
* Users can provide the document from an URL (including an URL on iTop itself)
* for CSV import. Administrators can even provide the path to a local file
* {@inheritDoc}
* @see AttributeDefinition::MakeRealValue()
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
if ($proposedValue === null) return null;
if (is_object($proposedValue))
{
$proposedValue = clone $proposedValue;
}
else
{
if (file_exists($proposedValue) && UserRights::IsAdministrator())
try
{
$sContent = file_get_contents($proposedValue);
$sExtension = strtolower(pathinfo($proposedValue, PATHINFO_EXTENSION));
$sMimeType = "application/x-octet-stream";
$aKnownExtensions = array(
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12.xlsx',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'png' => 'image/png',
'pdf' => 'application/pdf',
'doc' => 'application/msword',
'dot' => 'application/msword',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'vsd' => 'application/x-visio',
'vdx' => 'application/visio.drawing',
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'zip' => 'application/zip',
'txt' => 'text/plain',
'htm' => 'text/html',
'html' => 'text/html',
'exe' => 'application/octet-stream'
);
if (!array_key_exists($sExtension, $aKnownExtensions) && extension_loaded('fileinfo'))
{
$finfo = new finfo(FILEINFO_MIME);
$sMimeType = $finfo->file($proposedValue);
}
return new ormDocument($sContent, $sMimeType);
// Read the file from iTop, an URL (or the local file system - for admins only)
$proposedValue = Utils::FileGetContentsAndMIMEType($proposedValue);
}
else
catch(Exception $e)
{
return new ormDocument($proposedValue, 'text/plain');
}
IssueLog::Warning(get_class($this)."::MakeRealValue - ".$e->getMessage());
// Not a real document !! store is as text !!! (This was the default behavior before)
$proposedValue = new ormDocument($e->getMessage()." \n".$proposedValue, 'text/plain');
}
}
return $proposedValue;
}
@@ -5390,6 +5408,11 @@ class AttributeBlob extends AttributeDefinition
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
{
$sAttCode = $this->GetCode();
if ($sValue instanceof ormDocument && !$sValue->IsEmpty())
{
return $sValue->GetDownloadURL(get_class($oHostObject), $oHostObject->GetKey(), $sAttCode);
}
return ''; // Not exportable in CSV !
}
@@ -5493,48 +5516,49 @@ class AttributeImage extends AttributeBlob
{
public function GetEditClass() {return "Image";}
// Facilitate things: allow administrators to upload a document
// from a CSV by specifying its path/URL
/**
* {@inheritDoc}
* @see AttributeBlob::MakeRealValue()
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_object($proposedValue))
{
$proposedValue = clone $proposedValue;
}
else
{
if (file_exists($proposedValue) && UserRights::IsAdministrator())
{
$sContent = file_get_contents($proposedValue);
$sExtension = strtolower(pathinfo($proposedValue, PATHINFO_EXTENSION));
$sMimeType = "application/x-octet-stream";
$aKnownExtensions = array(
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'png' => 'image/png'
);
if (!array_key_exists($sExtension, $aKnownExtensions) && extension_loaded('fileinfo'))
{
$finfo = new finfo(FILEINFO_MIME);
$sMimeType = $finfo->file($proposedValue);
}
return new ormDocument($sContent, $sMimeType);
}
}
return $proposedValue;
$oDoc = parent::MakeRealValue($proposedValue, $oHostObj);
// The validation of the MIME Type is done by CheckFormat below
return $oDoc;
}
/**
* Check that the supplied ormDocument actually contains an image
* {@inheritDoc}
* @see AttributeDefinition::CheckFormat()
*/
public function CheckFormat($value)
{
if ($value instanceof ormDocument && !$value->IsEmpty())
{
return ($value->GetMainMimeType() == 'image');
}
return true;
}
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
{
$iMaxWidthPx = $this->Get('display_max_width');
$iMaxHeightPx = $this->Get('display_max_height');
$sUrl = $this->Get('default_image');
$sRet = '<img src="'.$sUrl.'" style="max-width: '.$iMaxWidthPx.'px; max-height: '.$iMaxHeightPx.'px">';
$sRet = ($sUrl !== null) ? '<img src="'.$sUrl.'" style="max-width: '.$iMaxWidthPx.'px; max-height: '.$iMaxHeightPx.'px">' : '';
if (is_object($value) && !$value->IsEmpty())
{
$sUrl = $value->GetDownloadURL(get_class($oHostObject), $oHostObject->GetKey(), $this->GetCode());
if ($oHostObject->IsNew() || ($oHostObject->IsModified() && (array_key_exists($this->GetCode(), $oHostObject->ListChanges()))))
{
// If the object is modified (or not yet stored in the database) we must serve the content of the image directly inline
// otherwise (if we just give an URL) the browser will be given the wrong content... and may cache it
$sUrl = 'data:'.$value->GetMimeType().';base64,'.base64_encode($value->GetData());
}
else
{
$sUrl = $value->GetDownloadURL(get_class($oHostObject), $oHostObject->GetKey(), $this->GetCode());
}
$sRet = '<img src="'.$sUrl.'" style="max-width: '.$iMaxWidthPx.'px; max-height: '.$iMaxHeightPx.'px">';
}
return '<div class="view-image" style="width: '.$iMaxWidthPx.'px; height: '.$iMaxHeightPx.'px;"><span class="helper-middle"></span>'.$sRet.'</div>';
@@ -7370,7 +7394,7 @@ class AttributeCustomFields extends AttributeDefinition
public function GetEditValue($sValue, $oHostObj = null)
{
return 'GetEditValueNotImplemented for '.get_class($this);
return $this->GetForTemplate($sValue, '', $oHostObj, true);
}
/**
@@ -7773,3 +7797,16 @@ class AttributeObsolescenceFlag extends AttributeBoolean
}
}
class AttributeObsolescenceDate extends AttributeDate
{
public function GetLabel($sDefault = null)
{
$sDefault = Dict::S('Core:AttributeObsolescenceDate/Label', $sDefault);
return parent::GetLabel($sDefault);
}
public function GetDescription($sDefault = null)
{
$sDefault = Dict::S('Core:AttributeObsolescenceDate/Label+', $sDefault);
return parent::GetDescription($sDefault);
}
}

View File

@@ -468,10 +468,11 @@ class BulkChange
//
foreach ($this->m_aAttList as $sAttCode => $iCol)
{
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
// skip the private key, if any
if ($sAttCode == 'id') continue;
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
// skip reconciliation keys
if (!$oAttDef->IsWritable() && in_array($sAttCode, $this->m_aReconcilKeys)){ continue; }

View File

@@ -33,13 +33,15 @@ class MySQLException extends CoreException
{
if ($oException != null)
{
$aContext['mysql_error'] = $oException->getCode();
$aContext['mysql_errno'] = $oException->getMessage();
$aContext['mysql_errno'] = $oException->getCode();
$this->code = $oException->getCode();
$aContext['mysql_error'] = $oException->getMessage();
}
else
{
$aContext['mysql_error'] = CMDBSource::GetError();
$aContext['mysql_errno'] = CMDBSource::GetErrNo();
$this->code = CMDBSource::GetErrNo();
$aContext['mysql_error'] = CMDBSource::GetError();
}
parent::__construct($sIssue, $aContext);
}

View File

@@ -18,7 +18,8 @@
define('ITOP_APPLICATION', 'iTop');
define('ITOP_VERSION', '2.4.x');
define('ITOP_APPLICATION_SHORT', 'iTop');
define('ITOP_VERSION', '2.4.0');
define('ITOP_REVISION', 'svn');
define('ITOP_BUILD_DATE', '$WCNOW$');
@@ -435,6 +436,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'apc_cache_emulation.max_entries' => array(
'type' => 'integer',
'description' => 'Maximum number of cache entries (0 means no limit)',
'default' => 1000,
'value' => 1000,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'timezone' => array(
'type' => 'string',
'description' => 'Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitely configured in PHP',
@@ -953,6 +962,30 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'disable_attachments_download_legacy_portal' => array(
'type' => 'bool',
'description' => 'Disable attachments download from legacy portal',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'optimize_requests_for_join_count' => array(
'type' => 'bool',
'description' => 'Optimize request joins to minimize the count (default is true, try to set it to false in case of performance issues)',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'high_cardinality_classes' => array(
'type' => 'array',
'description' => 'List of classes with high cardinality (force auto-complete mode)',
'default' => array(),
'value' => array(),
'source_of_value' => '',
'show_in_conf_sample' => true,
),
);
public function IsProperty($sPropCode)

View File

@@ -112,4 +112,13 @@ class SecurityException extends CoreException
{
}
/**
* Throwned when querying on an object that exists in the database but is archived
*
* @see N.1108
*/
class ArchivedObjectException extends CoreException
{
}
?>

View File

@@ -1731,6 +1731,14 @@ abstract class DBObject implements iDisplay
$this->m_bIsInDB = true;
$this->m_bDirty = false;
foreach ($this->m_aCurrValues as $sAttCode => $value)
{
if (is_object($value))
{
$value = clone $value;
}
$this->m_aOrigValues[$sAttCode] = $value;
}
$this->AfterInsert();
@@ -2922,7 +2930,7 @@ abstract class DBObject implements iDisplay
$oSearch->AllowAllData();
}
$oSet = new CMDBObjectSet($oSearch);
if ($oSet->Count() > 0)
if ($oSet->Count(1) > 0)
{
$aDependentObjects[$sRemoteClass][$sExtKeyAttCode] = array(
'attribute' => $oExtKeyAttDef,

View File

@@ -1,9 +1,9 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (c) 2010-2018 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -15,14 +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/>
/**
* Define filters for a given class of objects (formerly named "filter")
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
//
// Dev hack for disabling the some query build optimizations (Folding/Merging)
define('ENABLE_OPT', true);
@@ -33,7 +26,6 @@ class DBObjectSearch extends DBSearch
private $m_aSelectedClasses; // selected for the output (alias => class name)
private $m_oSearchCondition;
private $m_aParams;
private $m_aFullText;
private $m_aPointingTo;
private $m_aReferencedBy;
@@ -54,7 +46,6 @@ class DBObjectSearch extends DBSearch
$this->m_aClasses = array($sClassAlias => $sClass);
$this->m_oSearchCondition = new TrueExpression;
$this->m_aParams = array();
$this->m_aFullText = array();
$this->m_aPointingTo = array();
$this->m_aReferencedBy = array();
}
@@ -285,7 +276,6 @@ class DBObjectSearch extends DBSearch
public function IsAny()
{
if (!$this->m_oSearchCondition->IsTrue()) return false;
if (count($this->m_aFullText) > 0) return false;
if (count($this->m_aPointingTo) > 0) return false;
if (count($this->m_aReferencedBy) > 0) return false;
return true;
@@ -367,10 +357,19 @@ class DBObjectSearch extends DBSearch
$this->AddConditionExpression($oNewCondition);
}
public function AddCondition($sFilterCode, $value, $sOpCode = null, $bParseSeachString = false)
/**
* @param string $sFilterCode
* @param mixed $value
* @param string $sOpCode operator to use : 'IN', 'NOT IN', 'Contains',' Begins with', 'Finishes with', ...
* @param bool $bParseSearchString
*
* @throws \CoreException
*
* @see AddConditionForInOperatorUsingParam for IN/NOT IN queries with lots of params
*/
public function AddCondition($sFilterCode, $value, $sOpCode = null, $bParseSearchString = false)
{
MyHelpers::CheckKeyInArray('filter code in class: '.$this->GetClass(), $sFilterCode, MetaModel::GetClassFilterDefs($this->GetClass()));
$oFilterDef = MetaModel::GetClassFilterDef($this->GetClass(), $sFilterCode);
$oField = new FieldExpression($sFilterCode, $this->GetClassAlias());
if (empty($sOpCode))
@@ -382,15 +381,15 @@ class DBObjectSearch extends DBSearch
else
{
$oAttDef = MetaModel::GetAttributeDef($this->GetClass(), $sFilterCode);
$oNewCondition = $oAttDef->GetSmartConditionExpression($value, $oField, $this->m_aParams, $bParseSeachString);
$oNewCondition = $oAttDef->GetSmartConditionExpression($value, $oField, $this->m_aParams, $bParseSearchString);
$this->AddConditionExpression($oNewCondition);
return;
}
}
// Parse search strings if needed and if the filter code corresponds to a valid attcode
if($bParseSeachString && MetaModel::IsValidAttCode($this->GetClass(), $sFilterCode))
if($bParseSearchString && MetaModel::IsValidAttCode($this->GetClass(), $sFilterCode))
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sFilterCode);
$oAttDef = MetaModel::GetAttributeDef($this->GetClass(), $sFilterCode);
$value = $oAttDef->ParseSearchString($value);
}
@@ -408,14 +407,14 @@ class DBObjectSearch extends DBSearch
throw new CoreException('Deprecated operator, please consider using OQL (SQL) expressions like "(TO_DAYS(NOW()) - TO_DAYS(x)) AS AgeDays"', array('operator' => $sOpCode));
break;
case "IN":
case 'IN':
if (!is_array($value)) $value = array($value);
if (count($value) === 0) throw new Exception('AddCondition '.$sOpCode.': Value cannot be an empty array.');
$sListExpr = '('.implode(', ', CMDBSource::Quote($value)).')';
$sOQLCondition = $oField->Render()." IN $sListExpr";
break;
case "NOTIN":
case 'NOTIN':
if (!is_array($value)) $value = array($value);
if (count($value) === 0) throw new Exception('AddCondition '.$sOpCode.': Value cannot be an empty array.');
$sListExpr = '('.implode(', ', CMDBSource::Quote($value)).')';
@@ -469,6 +468,8 @@ class DBObjectSearch extends DBSearch
break;
case "IN":
case "NOTIN":
// this will parse all of the values... Can take forever if there are lots of them !
// In this case using a parameter is far better : WHERE ... IN (:my_param)
$oNewCondition = Expression::FromOQL($sOQLCondition);
break;
@@ -483,6 +484,44 @@ class DBObjectSearch extends DBSearch
$this->AddConditionExpression($oNewCondition);
}
/**
* @param string $sFilterCode attribute code to use
* @param array $aValues
* @param bool $bPositiveMatch if true will add a IN filter, else a NOT IN
*
* @throws \CoreException
*/
public function AddConditionForInOperatorUsingParam($sFilterCode, $aValues, $bPositiveMatch = true)
{
$oFieldExpression = new FieldExpression($sFilterCode, $this->GetClassAlias());
$sOperator = $bPositiveMatch ? 'IN' : 'NOT IN';
$sInParamName = $this->GenerateUniqueParamName();
$oParamExpression = new VariableExpression($sInParamName);
$this->SetInternalParams(array($sInParamName => $aValues));
$oListExpression = new ListExpression(array($oParamExpression));
$oInCondition = new BinaryExpression($oFieldExpression, $sOperator, $oListExpression);
$this->AddConditionExpression($oInCondition);
}
/**
* @return string a unique param name
*/
private function GenerateUniqueParamName() {
$iExistingParamsNb = count($this->m_aParams);
$iCurrentArrayParamNb = $iExistingParamsNb + 1;
$sParamName = 'param'.$iCurrentArrayParamNb;
if (isset($this->m_aParams[$sParamName])) {
$sParamName .= '_'.microtime(true) . '_' .rand(0,100);
}
return $sParamName;
}
/**
* Specify a condition on external keys or link sets
* @param sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
@@ -549,9 +588,24 @@ class DBObjectSearch extends DBSearch
}
}
public function AddCondition_FullText($sFullText)
public function AddCondition_FullText($sNeedle)
{
$this->m_aFullText[] = $sFullText;
// Transform the full text condition into additional condition expression
$aFullTextFields = array();
foreach (MetaModel::ListAttributeDefs($this->GetClass()) as $sAttCode => $oAttDef)
{
if (!$oAttDef->IsScalar()) continue;
if ($oAttDef->IsExternalKey()) continue;
$aFullTextFields[] = new FieldExpression($sAttCode, $this->GetClassAlias());
}
$oTextFields = new CharConcatWSExpression(' ', $aFullTextFields);
$sQueryParam = 'needle';
$oFlexNeedle = new CharConcatExpression(array(new ScalarExpression('%'), new VariableExpression($sQueryParam), new ScalarExpression('%')));
$oNewCond = new BinaryExpression($oTextFields, 'LIKE', $oFlexNeedle);
$this->AddConditionExpression($oNewCond);
$this->m_aParams[$sQueryParam] = $sNeedle;
}
protected function AddToNameSpace(&$aClassAliases, &$aAliasTranslation, $bTranslateMainAlias = true)
@@ -973,8 +1027,6 @@ class DBObjectSearch extends DBSearch
// Translate search condition into our aliasing scheme
$aAliasTranslation[$oFilter->GetClassAlias()]['*'] = $this->GetClassAlias();
$this->m_aFullText = array_merge($this->m_aFullText, $oFilter->m_aFullText);
foreach($oFilter->m_aPointingTo as $sExtKeyAttCode=>$aPointingTo)
{
foreach($aPointingTo as $iOperatorCode => $aFilter)
@@ -1001,7 +1053,7 @@ class DBObjectSearch extends DBSearch
}
public function GetCriteria() {return $this->m_oSearchCondition;}
public function GetCriteria_FullText() {return $this->m_aFullText;}
public function GetCriteria_FullText() {throw new Exception("Removed GetCriteria_FullText");}
protected function GetCriteria_PointingTo($sKeyAttCode = "")
{
if (empty($sKeyAttCode))
@@ -1119,11 +1171,6 @@ class DBObjectSearch extends DBSearch
$sRes .= $this->ToOQL_Joins();
$sRes .= " WHERE ".$this->m_oSearchCondition->Render($aParams, $bRetrofitParams);
// Temporary: add more info about other conditions, necessary to avoid strange behaviors with the cache
foreach($this->m_aFullText as $sFullText)
{
$sRes .= " AND MATCHES '$sFullText'";
}
if ($bWithAllowAllFlag && $this->m_bAllowAllData)
{
$sRes .= " ALLOW ALL DATA";
@@ -1468,10 +1515,32 @@ class DBObjectSearch extends DBSearch
// Create a unique cache id
//
$aContextData = array();
$bCanCache = true;
if (self::$m_bQueryCacheEnabled || self::$m_bTraceQueries)
{
if (isset($_SERVER['REQUEST_URI']))
{
$aContextData['sRequestUri'] = $_SERVER['REQUEST_URI'];
}
else if (isset($_SERVER['SCRIPT_NAME']))
{
$aContextData['sRequestUri'] = $_SERVER['SCRIPT_NAME'];
}
else
{
$aContextData['sRequestUri'] = '';
}
// Need to identify the query
$sOqlQuery = $oSearch->ToOql(false, null, true);
if ((strpos($sOqlQuery, '`id` IN (') !== false) || (strpos($sOqlQuery, '`id` NOT IN (') !== false))
{
// Requests containing "id IN" are not worth caching
$bCanCache = false;
}
$aContextData['sOqlQuery'] = $sOqlQuery;
if (count($aModifierProperties))
{
@@ -1482,12 +1551,14 @@ class DBObjectSearch extends DBSearch
{
$sModifierProperties = '';
}
$aContextData['sModifierProperties'] = $sModifierProperties;
$sRawId = $sOqlQuery.$sModifierProperties;
if (!is_null($aAttToLoad))
{
$sRawId .= json_encode($aAttToLoad);
}
$aContextData['aAttToLoad'] = $aAttToLoad;
if (!is_null($aGroupByExpr))
{
foreach($aGroupByExpr as $sAlias => $oExpr)
@@ -1495,13 +1566,20 @@ class DBObjectSearch extends DBSearch
$sRawId .= 'g:'.$sAlias.'!'.$oExpr->Render();
}
}
$aContextData['aGroupByExpr'] = $aGroupByExpr;
$sRawId .= $bGetCount;
$aContextData['bGetCount'] = $bGetCount;
if (is_array($aSelectedClasses))
{
$sRawId .= implode(',', $aSelectedClasses); // Unions may alter the list of selected columns
}
$sRawId .= $oSearch->GetArchiveMode() ? '--arch' : '';
$sRawId .= $oSearch->GetShowObsoleteData() ? '--obso' : '';
$aContextData['aSelectedClasses'] = $aSelectedClasses;
$bIsArchiveMode = $oSearch->GetArchiveMode();
$sRawId .= $bIsArchiveMode ? '--arch' : '';
$bShowObsoleteData = $oSearch->GetShowObsoleteData();
$sRawId .= $bShowObsoleteData ? '--obso' : '';
$aContextData['bIsArchiveMode'] = $bIsArchiveMode;
$aContextData['bShowObsoleteData'] = $bShowObsoleteData;
$sOqlId = md5($sRawId);
}
else
@@ -1557,8 +1635,9 @@ class DBObjectSearch extends DBSearch
if (self::$m_bQueryCacheEnabled)
{
if (self::$m_bUseAPCCache)
if ($bCanCache && self::$m_bUseAPCCache)
{
$oSQLQuery->m_aContextData = $aContextData;
$oKPI = new ExecutionKPI();
apc_store($sOqlAPCCacheId, $oSQLQuery, self::$m_iQueryCacheTTL);
$oKPI->ComputeStats('Query APC (store)', $sOqlQuery);
@@ -1570,6 +1649,14 @@ class DBObjectSearch extends DBSearch
return $oSQLQuery;
}
/**
* @param $aAttToLoad
* @param $bGetCount
* @param $aModifierProperties
* @param null $aGroupByExpr
* @param null $aSelectedClasses
* @return null|SQLObjectQuery
*/
protected function BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
{
$oBuild = new QueryBuilderContext($this, $aModifierProperties, $aGroupByExpr, $aSelectedClasses);
@@ -1614,6 +1701,12 @@ class DBObjectSearch extends DBSearch
}
/**
* @param $oBuild
* @param null $aAttToLoad
* @param array $aValues
* @return null|SQLObjectQuery
*/
protected function MakeSQLObjectQuery(&$oBuild, $aAttToLoad = null, $aValues = array())
{
// Note: query class might be different than the class of the filter
@@ -1623,9 +1716,9 @@ class DBObjectSearch extends DBSearch
$bIsOnQueriedClass = array_key_exists($sClassAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
self::DbgTrace("Entering: ".$this->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
//self::DbgTrace("Entering: ".$this->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
$sRootClass = MetaModel::GetRootClass($sClass);
//$sRootClass = MetaModel::GetRootClass($sClass);
$sKeyField = MetaModel::DBGetKey($sClass);
if ($bIsOnQueriedClass)
@@ -1658,30 +1751,10 @@ class DBObjectSearch extends DBSearch
}
}
}
// Transform the full text condition into additional condition expression
$aFullText = $this->GetCriteria_FullText();
if (count($aFullText) > 0)
{
$aFullTextFields = array();
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
if (!$oAttDef->IsScalar()) continue;
if ($oAttDef->IsExternalKey()) continue;
$aFullTextFields[] = new FieldExpression($sAttCode, $sClassAlias);
}
$oTextFields = new CharConcatWSExpression(' ', $aFullTextFields);
foreach($aFullText as $sFTNeedle)
{
$oNewCond = new BinaryExpression($oTextFields, 'LIKE', new ScalarExpression("%$sFTNeedle%"));
$oBuild->m_oQBExpressions->AddCondition($oNewCond);
}
}
}
//echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."</pre></p>\n";
//echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."</pre></p>\n";
$aExpectedAtts = array(); // array of (attcode => fieldexpression)
//echo "<p>".__LINE__.": GetUnresolvedFields($sClassAlias, ...)</p>\n";
//echo "<p>".__LINE__.": GetUnresolvedFields($sClassAlias, ...)</p>\n";
$oBuild->m_oQBExpressions->GetUnresolvedFields($sClassAlias, $aExpectedAtts);
// Compute a clear view of required joins (from the current class)
@@ -1798,37 +1871,86 @@ class DBObjectSearch extends DBSearch
}
}
// First query built upon on the leaf (ie current) class
//
self::DbgTrace("Main (=leaf) class, call MakeSQLObjectQuerySingleTable()");
if (MetaModel::HasTable($sClass))
$bRootFirst = MetaModel::GetConfig()->Get('optimize_requests_for_join_count');
if ($bRootFirst)
{
$oSelectBase = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sClass, $aExtKeys, $aValues);
// First query built from the root, adding all tables including the leaf
// Before N.1065 we were joining from the leaf first, but this wasn't a good choice :
// most of the time (obsolescence, friendlyname, ...) we want to get a root attribute !
//
$oSelectBase = null;
$aClassHierarchy = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, true);
$bIsClassStandaloneClass = (count($aClassHierarchy) == 1);
foreach($aClassHierarchy as $sSomeClass)
{
if (!MetaModel::HasTable($sSomeClass))
{
continue;
}
self::DbgTrace("Adding join from root to leaf: $sSomeClass... let's call MakeSQLObjectQuerySingleTable()");
$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sSomeClass, $aExtKeys, $aValues);
if (is_null($oSelectBase))
{
$oSelectBase = $oSelectParentTable;
if (!$bIsClassStandaloneClass && (MetaModel::IsRootClass($sSomeClass)))
{
// As we're linking to root class first, we're adding a where clause on the finalClass attribute :
// COALESCE($sRootClassFinalClass IN ('$sExpectedClasses'), 1)
// If we don't, the child classes can be removed in the query optimisation phase, including the leaf that was queried
// So we still need to filter records to only those corresponding to the child classes !
// The coalesce is mandatory if we have a polymorphic query (left join)
$oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
$sFinalClassSqlColumnName = MetaModel::DBGetClassField($sSomeClass);
$oClassExpr = new FieldExpression($sFinalClassSqlColumnName, $oSelectBase->GetTableAlias());
$oInExpression = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
$oTrueExpression = new TrueExpression();
$aCoalesceAttr = array($oInExpression, $oTrueExpression);
$oFinalClassRestriction = new FunctionExpression("COALESCE", $aCoalesceAttr);
$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
}
}
else
{
$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sSomeClass));
}
}
}
else
{
$oSelectBase = null;
// As the join will not filter on the expected classes, we have to specify it explicitely
$sExpectedClasses = implode("', '", MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
$oFinalClassRestriction = Expression::FromOQL("`$sClassAlias`.finalclass IN ('$sExpectedClasses')");
$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
}
// Then we join the queries of the eventual parent classes (compound model)
foreach(MetaModel::EnumParentClasses($sClass) as $sParentClass)
{
if (!MetaModel::HasTable($sParentClass)) continue;
self::DbgTrace("Parent class: $sParentClass... let's call MakeSQLObjectQuerySingleTable()");
$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sParentClass, $aExtKeys, $aValues);
if (is_null($oSelectBase))
// First query built upon on the leaf (ie current) class
//
self::DbgTrace("Main (=leaf) class, call MakeSQLObjectQuerySingleTable()");
if (MetaModel::HasTable($sClass))
{
$oSelectBase = $oSelectParentTable;
$oSelectBase = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sClass, $aExtKeys, $aValues);
}
else
{
$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sParentClass));
$oSelectBase = null;
// As the join will not filter on the expected classes, we have to specify it explicitely
$sExpectedClasses = implode("', '", MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
$oFinalClassRestriction = Expression::FromOQL("`$sClassAlias`.finalclass IN ('$sExpectedClasses')");
$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
}
// Then we join the queries of the eventual parent classes (compound model)
foreach(MetaModel::EnumParentClasses($sClass) as $sParentClass)
{
if (!MetaModel::HasTable($sParentClass)) continue;
self::DbgTrace("Parent class: $sParentClass... let's call MakeSQLObjectQuerySingleTable()");
$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sParentClass, $aExtKeys, $aValues);
if (is_null($oSelectBase))
{
$oSelectBase = $oSelectParentTable;
}
else
{
$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sParentClass));
}
}
}
@@ -2159,8 +2281,14 @@ class DBObjectSearch extends DBSearch
* Get the expression for the class and its subclasses (if finalclass = 'subclass' ...)
* Simplifies the final expression by grouping classes having the same expression
*/
static protected function GetPolymorphicExpression($sClass, $sAttCode)
static public function GetPolymorphicExpression($sClass, $sAttCode)
{
$oExpression = ExpressionCache::GetCachedExpression($sClass, $sAttCode);
if (!empty($oExpression))
{
return $oExpression;
}
// 1st step - get all of the required expressions (instantiable classes)
// and group them using their OQL representation
//

View File

@@ -69,7 +69,7 @@ class DBObjectSet implements iDBObjectSetIterator
* @var int
*/
protected $m_iCurrRow;
/*
/**
* @var DBSearch
*/
protected $m_oFilter;
@@ -77,18 +77,20 @@ class DBObjectSet implements iDBObjectSetIterator
* @var mysqli_result
*/
protected $m_oSQLResult;
protected $m_bSort;
/**
* Create a new set based on a Search definition.
*
*
* @param DBSearch $oFilter The search filter defining the objects which are part of the set (multiple columns/objects per row are supported)
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
* @param hash $aArgs Values to substitute for the search/query parameters (if any). Format: param_name => value
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
* @param array $aArgs Values to substitute for the search/query parameters (if any). Format: param_name => value
* @param hash $aExtendedDataSpec
* @param int $iLimitCount Maximum number of rows to load (i.e. equivalent to MySQL's LIMIT start, count)
* @param int $iLimitStart Index of the first row to load (i.e. equivalent to MySQL's LIMIT start, count)
* @param bool $bSort if false no order by is done
*/
public function __construct(DBSearch $oFilter, $aOrderBy = array(), $aArgs = array(), $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0)
public function __construct(DBSearch $oFilter, $aOrderBy = array(), $aArgs = array(), $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bSort = true)
{
$this->m_oFilter = $oFilter->DeepClone();
$this->m_aAddedIds = array();
@@ -98,6 +100,7 @@ class DBObjectSet implements iDBObjectSetIterator
$this->m_aExtendedDataSpec = $aExtendedDataSpec;
$this->m_iLimitCount = $iLimitCount;
$this->m_iLimitStart = $iLimitStart;
$this->m_bSort = $bSort;
$this->m_iNumTotalDBRows = null;
$this->m_iNumLoadedDBRows = 0;
@@ -195,11 +198,25 @@ class DBObjectSet implements iDBObjectSetIterator
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad);
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad] = $oAttDef;
if ($oAttDef->IsExternalKey())
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
{
// Add the external key friendly name anytime
$oFriendlyNameAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_friendlyname');
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_friendlyname'] = $oFriendlyNameAttDef;
if (MetaModel::IsArchivable($oAttDef->GetTargetClass(EXTKEY_ABSOLUTE)))
{
// Add the archive flag if necessary
$oArchiveFlagAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_archive_flag');
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_archive_flag'] = $oArchiveFlagAttDef;
}
if (MetaModel::IsObsoletable($oAttDef->GetTargetClass(EXTKEY_ABSOLUTE)))
{
// Add the obsolescence flag if necessary
$oObsoleteFlagAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_obsolescence_flag');
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_obsolescence_flag'] = $oObsoleteFlagAttDef;
}
}
}
}
@@ -207,6 +224,20 @@ class DBObjectSet implements iDBObjectSetIterator
$oFriendlyNameAttDef = MetaModel::GetAttributeDef($sClass, 'friendlyname');
$aAttToLoadWithAttDef[$sClassAlias]['friendlyname'] = $oFriendlyNameAttDef;
if (MetaModel::IsArchivable($sClass))
{
// Add the archive flag if necessary
$oArchiveFlagAttDef = MetaModel::GetAttributeDef($sClass, 'archive_flag');
$aAttToLoadWithAttDef[$sClassAlias]['archive_flag'] = $oArchiveFlagAttDef;
}
if (MetaModel::IsObsoletable($sClass))
{
// Add the obsolescence flag if necessary
$oObsoleteFlagAttDef = MetaModel::GetAttributeDef($sClass, 'obsolescence_flag');
$aAttToLoadWithAttDef[$sClassAlias]['obsolescence_flag'] = $oObsoleteFlagAttDef;
}
// Make sure that the final class is requested anytime, whatever the specification (needed for object construction!)
if (!MetaModel::IsStandaloneClass($sClass) && !array_key_exists('finalclass', $aAttToLoadWithAttDef[$sClassAlias]))
{
@@ -312,7 +343,7 @@ class DBObjectSet implements iDBObjectSetIterator
}
/**
* Note: After calling this method, the set cursor will be at the end of the set. You might to rewind it.
* Note: After calling this method, the set cursor will be at the end of the set. You might want to rewind it.
*
* @param bool $bWithId
* @return array
@@ -386,7 +417,7 @@ class DBObjectSet implements iDBObjectSetIterator
}
/**
* Note: After calling this method, the set cursor will be at the end of the set. You might to rewind it.
* Note: After calling this method, the set cursor will be at the end of the set. You might want to rewind it.
*
* @param string $sAttCode
* @param bool $bWithId
@@ -573,10 +604,15 @@ class DBObjectSet implements iDBObjectSetIterator
*
* Limitation: the sort order has no effect on objects added in-memory
*
* @return hash Format: field_code => boolean (true = ascending, false = descending)
* @return array Format: field_code => boolean (true = ascending, false = descending)
*/
public function GetRealSortOrder()
{
if (!$this->m_bSort)
{
// No order by
return array();
}
// Get the class default sort order if not specified with the API
//
if (empty($this->m_aOrderBy))
@@ -598,14 +634,7 @@ class DBObjectSet implements iDBObjectSetIterator
// Note: it is mandatory to set this value now, to protect against reentrance
$this->m_bLoaded = true;
if ($this->m_iLimitCount > 0)
{
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $this->m_aAttToLoad, $this->m_aExtendedDataSpec, $this->m_iLimitCount, $this->m_iLimitStart);
}
else
{
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
}
$sSQL = $this->_makeSelectQuery($this->m_aAttToLoad);
if (is_object($this->m_oSQLResult))
{
@@ -614,8 +643,36 @@ class DBObjectSet implements iDBObjectSetIterator
$this->m_oSQLResult = null;
}
$this->m_iNumTotalDBRows = null;
$this->m_oSQLResult = CMDBSource::Query($sSQL);
try
{
$this->m_oSQLResult = CMDBSource::Query($sSQL);
} catch (MySQLException $e)
{
// 1116 = ER_TOO_MANY_TABLES
// https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html#error_er_too_many_tables
if ($e->getCode() != 1116)
{
throw $e;
}
// N.689 Workaround for the 61 max joins in MySQL : full lazy load !
$aAttToLoad = array();
foreach($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass)
{
$aAttToLoad[$sClassAlias] = array();
$bIsAbstractClass = MetaModel::IsAbstract($sClass);
$bIsClassWithChildren = MetaModel::HasChildrenClasses($sClass);
if ($bIsAbstractClass || $bIsClassWithChildren)
{
// we need finalClass field at least to be able to instantiate the real corresponding object !
$aAttToLoad[$sClassAlias]['finalclass'] = MetaModel::GetAttributeDef($sClass, 'finalclass');
}
}
$sSQL = $this->_makeSelectQuery($aAttToLoad);
$this->m_oSQLResult = CMDBSource::Query($sSQL); // may fail again
}
if ($this->m_oSQLResult === false) return;
if (($this->m_iLimitCount == 0) && ($this->m_iLimitStart == 0))
@@ -626,24 +683,54 @@ class DBObjectSet implements iDBObjectSetIterator
}
/**
* The total number of rows in this set. Independently of the SetLimit used for loading the set and taking into account the rows added in-memory.
*
* May actually perform the SQL query SELECT COUNT... if the set was not previously loaded, or loaded with a SetLimit
*
* @return int The total number of rows for this set.
* @param string[] $aAttToLoad
*
* @return string SQL query
*/
public function Count()
private function _makeSelectQuery($aAttToLoad)
{
if ($this->m_iLimitCount > 0)
{
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $aAttToLoad,
$this->m_aExtendedDataSpec, $this->m_iLimitCount, $this->m_iLimitStart);
}
else
{
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $aAttToLoad,
$this->m_aExtendedDataSpec);
}
return $sSQL;
}
/**
* The total number of rows in this set. Independently of the SetLimit used for loading the set and taking into
* account the rows added in-memory.
*
* May actually perform the SQL query SELECT COUNT... if the set was not previously loaded, or loaded with a
* SetLimit
*
* @param int $iLimit used for autocomplete: the count is only used to know if the number of entries exceed
* a certain amount or not
*
* @return int The total number of rows for this set.
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
*/
public function Count($iLimit = 0)
{
if (is_null($this->m_iNumTotalDBRows))
{
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit, 0, true);
$resQuery = CMDBSource::Query($sSQL);
if (!$resQuery) return 0;
$aRow = CMDBSource::FetchArray($resQuery);
CMDBSource::FreeResult($resQuery);
$this->m_iNumTotalDBRows = $aRow['COUNT'];
}
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ??
}

File diff suppressed because it is too large Load Diff

View File

@@ -474,7 +474,7 @@ class DBUnionSearch extends DBSearch
throw new Exception('MakeUpdateQuery is not implemented for the unions!');
}
protected function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null)
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null)
{
if (count($this->aSearches) == 1)
{
@@ -496,19 +496,30 @@ class DBUnionSearch extends DBSearch
$aSearchSelectedClasses[$sSearchAlias] = $this->aSelectedClasses[$sAlias];
}
if (is_null($aAttToLoad))
if ($bGetCount)
{
$aQueryAttToLoad = null;
// Select only ids for the count to allow optimization of joins
foreach($aSearchAliases as $sSearchAlias)
{
$aQueryAttToLoad[$sSearchAlias] = array();
}
}
else
{
// (Eventually) Transform the aliases
$aQueryAttToLoad = array();
foreach ($aAttToLoad as $sAlias => $aAttributes)
if (is_null($aAttToLoad))
{
$iColumn = array_search($sAlias, $aAliases);
$sQueryAlias = ($iColumn === false) ? $sAlias : $aSearchAliases[$iColumn];
$aQueryAttToLoad[$sQueryAlias] = $aAttributes;
$aQueryAttToLoad = null;
}
else
{
// (Eventually) Transform the aliases
$aQueryAttToLoad = array();
foreach($aAttToLoad as $sAlias => $aAttributes)
{
$iColumn = array_search($sAlias, $aAliases);
$sQueryAlias = ($iColumn === false) ? $sAlias : $aSearchAliases[$iColumn];
$aQueryAttToLoad[$sQueryAlias] = $aAttributes;
}
}
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2013 Combodo SARL
// Copyright (C) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//
@@ -408,4 +408,30 @@ class EventLoginUsage extends Event
}
}
?>
class EventOnObject extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_onobject",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("obj_class", array("allowed_values"=>null, "sql"=>"obj_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("obj_key", array("allowed_values"=>null, "sql"=>"obj_key", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'obj_class', 'obj_key', 'message')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'obj_class', 'obj_key', 'message')); // Attributes to be displayed for a list
}
}

View File

@@ -188,10 +188,10 @@ EOF
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
}
else if ($value instanceOf ormCustomFieldsValue)
else if ($value instanceOf ormDocument)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, "\n", '', $oObj);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
}
else
{

View File

@@ -0,0 +1,110 @@
<?php
// Copyright (c) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
//
class ExpressionCache
{
static private $aCache = array();
static public function GetCachedExpression($sClass, $sAttCode)
{
// read current cache
@include_once (static::GetCacheFileName());
$oExpr = null;
$sKey = static::GetKey($sClass, $sAttCode);
if (array_key_exists($sKey, static::$aCache))
{
$oExpr = static::$aCache[$sKey];
}
else
{
if (class_exists('ExpressionCacheData'))
{
if (array_key_exists($sKey, ExpressionCacheData::$aCache))
{
$sVal = ExpressionCacheData::$aCache[$sKey];
$oExpr = unserialize($sVal);
static::$aCache[$sKey] = $oExpr;
}
}
}
return $oExpr;
}
static public function Warmup()
{
$sFilePath = static::GetCacheFileName();
if (!is_file($sFilePath))
{
$content = <<<EOF
<?php
// Copyright (c) 2010-2017 Combodo SARL
// Generated Expression Cache file
class ExpressionCacheData
{
static \$aCache = array(
EOF;
foreach(MetaModel::GetClasses() as $sClass)
{
$content .= static::GetSerializedExpression($sClass, 'friendlyname');
if (MetaModel::IsObsoletable($sClass))
{
$content .= static::GetSerializedExpression($sClass, 'obsolescence_flag');
}
}
$content .= <<<EOF
);
}
EOF;
file_put_contents($sFilePath, $content);
}
}
static private function GetSerializedExpression($sClass, $sAttCode)
{
$sKey = static::GetKey($sClass, $sAttCode);
$oExpr = DBObjectSearch::GetPolymorphicExpression($sClass, $sAttCode);
return "'".$sKey."' => '".serialize($oExpr)."',\n";
}
/**
* @param $sClass
* @param $sAttCode
* @return string
*/
static private function GetKey($sClass, $sAttCode)
{
return $sClass.'::'.$sAttCode;
}
public static function GetCacheFileName()
{
return utils::GetCachePath().'expressioncache.php';
}
}

View File

@@ -156,6 +156,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
'body' => array(),
'a' => array('href', 'name', 'style', 'target', 'title'),
'p' => array('style'),
'blockquote' => array('style'),
'br' => array(),
'span' => array('style'),
'div' => array('style'),
@@ -212,9 +213,17 @@ class HTMLDOMSanitizer extends HTMLSanitizer
public function __construct()
{
// Building href validation pattern from url and email validation patterns as the patterns are not used the same way in HTML content than in standard attributes value.
// eg. "foo@bar.com" vs "mailto:foo@bar.com?subject=Title&body=Hello%20world"
if (!array_key_exists('href', self::$aAttrsWhiteList))
{
$sPattern = '/'.str_replace('/', '\/', utils::GetConfig()->Get('url_validation_pattern')).'/i';
// Regular urls
$sUrlPattern = utils::GetConfig()->Get('url_validation_pattern');
// Mailto urls
$sMailtoPattern = '(mailto:(' . utils::GetConfig()->Get('email_validation_pattern') . ')(?:\?(?:subject|body)=([a-zA-Z0-9+\$_.-]*)(?:&(?:subject|body)=([a-zA-Z0-9+\$_.-]*))?)?)';
$sPattern = $sUrlPattern . '|' . $sMailtoPattern;
$sPattern = '/'.str_replace('/', '\/', $sPattern).'/i';
self::$aAttrsWhiteList['href'] = $sPattern;
}
}

View File

@@ -1,9 +1,9 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (c) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -15,6 +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/>
//
require_once(APPROOT.'core/modulehandler.class.inc.php');
require_once(APPROOT.'core/querybuildercontext.class.inc.php');
@@ -23,6 +24,7 @@ require_once(APPROOT.'core/metamodelmodifier.inc.php');
require_once(APPROOT.'core/computing.inc.php');
require_once(APPROOT.'core/relationgraph.class.inc.php');
require_once(APPROOT.'core/apc-compat.php');
require_once(APPROOT.'core/expressioncache.class.inc.php');
/**
* Metamodel
@@ -331,9 +333,16 @@ abstract class MetaModel
}
final static public function GetObsolescenceExpression($sClass)
{
self::_check_subclass($sClass);
$sOql = self::$m_aClassParams[$sClass]['obsolescence_expression'];
$oRet = Expression::FromOQL("COALESCE($sOql, 0)");
if (self::IsObsoletable($sClass))
{
self::_check_subclass($sClass);
$sOql = self::$m_aClassParams[$sClass]['obsolescence_expression'];
$oRet = Expression::FromOQL("COALESCE($sOql, 0)");
}
else
{
$oRet = Expression::FromOQL("0");
}
return $oRet;
}
final static public function GetNameSpec($sClass)
@@ -413,13 +422,13 @@ abstract class MetaModel
}
/**
* Returns the friendly name IIF it is equivalent to a single attribute
*/
* Returns the friendly name IIF it is equivalent to a single attribute
*/
final static public function GetFriendlyNameAttributeCode($sClass)
{
$aNameSpec = self::GetNameSpec($sClass);
$sFormat = trim($aNameSpec[0]);
$aAttributes = $aNameSpec[1];
$aAttributes = $aNameSpec[1];
if (($sFormat != '') && ($sFormat != '%1$s'))
{
return null;
@@ -431,6 +440,20 @@ abstract class MetaModel
return reset($aAttributes);
}
/**
* Returns the list of attributes composing the friendlyname
*
* @param $sClass
*
* @return array
*/
final static public function GetFriendlyNameAttributeCodeList($sClass)
{
$aNameSpec = self::GetNameSpec($sClass);
$aAttributes = $aNameSpec[1];
return $aAttributes;
}
final static public function GetStateAttributeCode($sClass)
{
self::_check_subclass($sClass);
@@ -1082,11 +1105,37 @@ abstract class MetaModel
return $aClassRelations;
}
final static public function GetRelationDescription($sRelCode)
/**
* @param string $sRelCode Relation code
* @param bool $bDown Relation direction, is it downstream (true) or upstream (false). Default is true.
*
* @return string
*/
final static public function GetRelationDescription($sRelCode, $bDown = true)
{
return Dict::S("Relation:$sRelCode/Description");
// Legacy convention had only one description describing the relation.
// Now, as the relation is bidirectional, we have a description for each directions.
$sLegacy = Dict::S("Relation:$sRelCode/Description");
if($bDown)
{
$sKey = "Relation:$sRelCode/DownStream+";
}
else
{
$sKey = "Relation:$sRelCode/UpStream+";
}
$sRet = Dict::S($sKey, $sLegacy);
return $sRet;
}
/**
* @param string $sRelCode Relation code
* @param bool $bDown Relation direction, is it downstream (true) or upstream (false). Default is true.
*
* @return string
*/
final static public function GetRelationLabel($sRelCode, $bDown = true)
{
if ($bDown)
@@ -1857,15 +1906,13 @@ abstract class MetaModel
}
}
// Prepare external fields and filters
// Add final class to external keys
//
// Add magic attributes to the classes
foreach (self::GetClasses() as $sClass)
{
$sRootClass = self::$m_aRootClasses[$sClass];
// Create the friendly name attribute
$sFriendlyNameAttCode = 'friendlyname';
$sFriendlyNameAttCode = 'friendlyname';
$oFriendlyName = new AttributeFriendlyName($sFriendlyNameAttCode);
self::AddMagicAttribute($oFriendlyName, $sClass);
@@ -1875,7 +1922,7 @@ abstract class MetaModel
$oArchiveFlag = new AttributeArchiveFlag('archive_flag');
self::AddMagicAttribute($oArchiveFlag, $sClass);
$oArchiveDate = new AttributeDate('archive_date', array('magic' => true, "allowed_values"=>null, "sql"=>'archive_date', "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array()));
$oArchiveDate = new AttributeArchiveDate('archive_date', array('magic' => true, "allowed_values" => null, "sql" => 'archive_date', "default_value" => '', "is_null_allowed" => true, "depends_on" => array()));
self::AddMagicAttribute($oArchiveDate, $sClass);
}
elseif (self::$m_aClassParams[$sClass]["archive"])
@@ -1898,7 +1945,7 @@ abstract class MetaModel
if (self::$m_aRootClasses[$sClass] == $sClass)
{
$oObsolescenceDate = new AttributeDate('obsolescence_date', array('magic' => true, "allowed_values" => null, "sql" => 'obsolescence_date', "default_value" => '', "is_null_allowed" => true, "depends_on" => array()));
$oObsolescenceDate = new AttributeObsolescenceDate('obsolescence_date', array('magic' => true, "allowed_values" => null, "sql" => 'obsolescence_date', "default_value" => '', "is_null_allowed" => true, "depends_on" => array()));
self::AddMagicAttribute($oObsolescenceDate, $sClass);
}
else
@@ -1909,6 +1956,13 @@ abstract class MetaModel
self::$m_aAttribOrigins[$sClass]['obsolescence_date'] = $sRootClass;
}
}
}
// Prepare external fields and filters
// Add final class to external keys
// Add magic attributes to external keys (finalclass, friendlyname, archive_flag, obsolescence_flag)
foreach (self::GetClasses() as $sClass)
{
foreach (self::$m_aAttribDefs[$sClass] as $sAttCode => $oAttDef)
{
// Compute the filter codes
@@ -3766,6 +3820,7 @@ abstract class MetaModel
//
$aTableInfo = CMDBSource::GetTableInfo($sTable);
$aTableInfo['Fields'][$sKeyField]['used'] = true;
$aFriendlynameAttcodes = self::GetFriendlyNameAttributeCodeList($sClass);
foreach(self::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef)
{
if (!$oAttDef->CopyOnAllTables())
@@ -3779,6 +3834,14 @@ abstract class MetaModel
$aTableInfo['Fields'][$sField]['used'] = true;
$bIndexNeeded = $oAttDef->RequiresIndex();
if (!$bIndexNeeded)
{
// Add an index on the columns of the friendlyname
if (in_array($sField, $aFriendlynameAttcodes))
{
$bIndexNeeded = true;
}
}
$sFieldDefinition = "`$sField` $sDBFieldSpec";
if (!CMDBSource::IsField($sTable, $sField))
@@ -3787,6 +3850,7 @@ abstract class MetaModel
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` ADD $sFieldDefinition";
if ($bIndexNeeded)
{
$aTableInfo['Indexes'][$sField]['used'] = true;
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` ADD INDEX (`$sField`)";
}
if (array_key_exists($sTable, $aCreateTable))
@@ -3794,6 +3858,7 @@ abstract class MetaModel
$aCreateTableItems[$sTable][$sField] = $sFieldDefinition;
if ($bIndexNeeded)
{
$aTableInfo['Indexes'][$sField]['used'] = true;
$aCreateTableItems[$sTable][] = "INDEX (`$sField`)";
}
}
@@ -3802,6 +3867,7 @@ abstract class MetaModel
$aAlterTableItems[$sTable][$sField] = "ADD $sFieldDefinition";
if ($bIndexNeeded)
{
$aTableInfo['Indexes'][$sField]['used'] = true;
$aAlterTableItems[$sTable][] = "ADD INDEX (`$sField`)";
}
}
@@ -3827,6 +3893,7 @@ abstract class MetaModel
//
if ($bIndexNeeded && !CMDBSource::HasIndex($sTable, $sField, array($sField)))
{
$aTableInfo['Indexes'][$sField]['used'] = true;
$aErrors[$sClass][$sAttCode][] = "Foreign key '$sField' in table '$sTable' should have an index";
if (CMDBSource::HasIndex($sTable, $sField))
{
@@ -3848,7 +3915,10 @@ abstract class MetaModel
foreach (self::DBGetIndexes($sClass) as $aColumns)
{
$sIndexId = implode('_', $aColumns);
if (isset($aTableInfo['Indexes'][$sIndexId]['used']) && $aTableInfo['Indexes'][$sIndexId]['used'])
{
continue;
}
if(!CMDBSource::HasIndex($sTable, $sIndexId, $aColumns))
{
$sColumns = "`".implode("`, `", $aColumns)."`";
@@ -4394,6 +4464,8 @@ abstract class MetaModel
echo "Debug<br/>\n";
self::static_var_dump();
}
ExpressionCache::Warmup();
}
public static function LoadConfig($oConfiguration, $bAllowCache = false)
@@ -4590,6 +4662,14 @@ abstract class MetaModel
return self::$m_oConfig;
}
/**
* @return string The environment in which the model has been loaded (e.g. 'production')
*/
public static function GetEnvironment()
{
return self::$m_sEnvironment;
}
public static function GetEnvironmentId()
{
return md5(APPROOT).'-'.self::$m_sEnvironment;
@@ -4675,6 +4755,18 @@ abstract class MetaModel
return $iTotalHits.' ('.implode(', ', $aRes).')';
}
/**
* @param string $sClass
* @param int $iKey
* @param bool $bMustBeFound
* @param bool $bAllowAllData if true then no rights filtering
* @param array $aModifierProperties
*
* @return string[] column name / value array
* @throws CoreException if no result found and $bMustBeFound=true
*
* @see utils::PushArchiveMode() to enable search on archived objects
*/
public static function MakeSingleRow($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
{
// Build the query cache signature
@@ -4731,13 +4823,27 @@ abstract class MetaModel
$aRow = CMDBSource::FetchArray($res);
CMDBSource::FreeResult($res);
if ($bMustBeFound && empty($aRow))
{
throw new CoreException("No result for the single row query: '$sSQL'");
}
return $aRow;
}
/**
* Converts a column name / value array to a {@link DBObject}
*
* @param string $sClass
* @param string[] $aRow column name / value array
* @param string $sClassAlias
* @param string[] $aAttToLoad
* @param array $aExtendedDataSpec
*
* @return DBObject
* @throws CoreException if finalClass cannot be found
*/
public static function GetObjectByRow($sClass, $aRow, $sClassAlias = '', $aAttToLoad = null, $aExtendedDataSpec = null)
{
self::_check_subclass($sClass);
@@ -4771,30 +4877,75 @@ abstract class MetaModel
}
/**
* @param $sClass
* Search for the specified class and id.
*
* @param string $sClass
* @param $iKey
* @param bool $bMustBeFound
* @param bool $bAllowAllData
* @param null $aModifierProperties
* @return DBObject|null
* @return DBObject|null null if : (the object is not found) or (archive mode disabled and object is archived and $bMustBeFound=false)
* @throws CoreException if no result found and $bMustBeFound=true
* @throws ArchivedObjectException if archive mode disabled and result is archived and $bMustBeFound=true
*
* @see MetaModel::GetObjectWithArchive to get object even if it's archived
* @see utils::PushArchiveMode() to enable search on archived objects
*/
public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
{
self::_check_subclass($sClass);
$oObject = self::GetObjectWithArchive($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties);
if (empty($oObject))
{
return null;
}
if (!utils::IsArchiveMode() && $oObject->IsArchived())
{
if ($bMustBeFound)
{
throw new ArchivedObjectException("The object $sClass::$iKey is archived");
}
else
{
return null;
}
}
return $oObject;
}
/**
* Search for the specified class and id. If the object is archived it will be returned anyway (this is for pre-2.4
* module compatibility, see N.1108)
*
* @param string $sClass
* @param int $iKey
* @param bool $bMustBeFound
* @param bool $bAllowAllData
* @param array $aModifierProperties
*
* @return DBObject|null
* @throws CoreException if no result found and $bMustBeFound=true
*
* @since 2.4 introduction of the archive functionalities
*
* @see MetaModel::GetObject() same but returns null or ArchivedObjectFoundException if object exists but is archived
*/
public static function GetObjectWithArchive($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
{
self::_check_subclass($sClass);
utils::PushArchiveMode(true);
$aRow = self::MakeSingleRow($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties);
utils::PopArchiveMode();
if (empty($aRow))
{
return null;
}
return self::GetObjectByRow($sClass, $aRow);
}
public static function GetObjectWithArchive($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
{
utils::PushArchiveMode(true);
$oObject = static::GetObject($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties);
utils::PopArchiveMode();
return $oObject;
return self::GetObjectByRow($sClass, $aRow); // null should not be returned, this is handled in the callee
}
public static function GetObjectByName($sClass, $sName, $bMustBeFound = true)
@@ -5226,21 +5377,12 @@ abstract class MetaModel
public static function GetCacheEntries($sEnvironment = null)
{
if (!function_exists('apc_cache_info')) return array();
if (is_null($sEnvironment))
{
$sEnvironment = MetaModel::GetEnvironmentId();
}
$aEntries = array();
if (extension_loaded('apcu'))
{
// Beware: APCu behaves slightly differently from APC !!
$aCacheUserData = @apc_cache_info();
}
else
{
$aCacheUserData = @apc_cache_info('user');
}
$aCacheUserData = apc_cache_info_compat();
if (is_array($aCacheUserData) && isset($aCacheUserData['cache_list']))
{
$sPrefix = 'itop-'.$sEnvironment.'-';

View File

@@ -45,7 +45,7 @@ class iTopMutex
}
$sDBName = $oConfig->GetDBName();
$sDBSubname = $oConfig->GetDBSubname();
$this->sName = 'itop.'.$sName;
$this->sName = $sName;
if (substr($sName, -strlen($sDBName.$sDBSubname)) != $sDBName.$sDBSubname)
{
// If the name supplied already ends with the expected suffix
@@ -54,6 +54,9 @@ class iTopMutex
$this->sName .= $sDBName.$sDBSubname;
}
// Limit the length of the name for MySQL > 5.7.5
$this->sName = 'itop.'.md5($this->sName);
$this->bLocked = false; // Not yet locked
if (!array_key_exists($this->sName, self::$aAcquiredLocks))

View File

@@ -1,9 +1,9 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (c) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -15,14 +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/>
/**
* General definition of an expression tree (could be OQL, SQL or whatever)
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
//
class MissingQueryArgument extends CoreException
{
@@ -89,9 +82,15 @@ abstract class Expression
*/
static public function FromOQL($sConditionExpr)
{
static $aCache = array();
if (array_key_exists($sConditionExpr, $aCache))
{
return unserialize($aCache[$sConditionExpr]);
}
$oOql = new OqlInterpreter($sConditionExpr);
$oExpression = $oOql->ParseExpression();
$aCache[$sConditionExpr] = serialize($oExpression);
return $oExpression;
}
@@ -227,6 +226,10 @@ class BinaryExpression extends Expression
{
throw new CoreException('Expecting an Expression object on the right hand', array('found_class' => get_class($oRightExpr)));
}
if ( (($sOperator == "IN") || ($sOperator == "NOT IN")) && !$oRightExpr instanceof ListExpression)
{
throw new CoreException("Expecting a List Expression object on the right hand for operator $sOperator", array('found_class' => get_class($oRightExpr)));
}
$this->m_oLeftExpr = $oLeftExpr;
$this->m_oRightExpr = $oRightExpr;
$this->m_sOperator = $sOperator;

View File

@@ -191,8 +191,15 @@ class ormCaseLog {
public function __toString()
{
return $this->m_sLog;
if($this->IsEmpty()) return '';
return $this->m_sLog;
}
public function IsEmpty()
{
return ($this->m_sLog === null);
}
public function ClearModifiedFlag()
{

View File

@@ -51,6 +51,8 @@ class ormDocument
public function __toString()
{
if($this->IsEmpty()) return '';
return MyHelpers::beautifulstr($this->m_data, 100, true);
}

View File

@@ -52,7 +52,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
* Object from the original set, minus the removed objects
* @var DBObject[] array of iObjectId => DBObject
*/
protected $aPreserved;
protected $aPreserved = array();
/**
* @var DBObject[] New items
@@ -74,6 +74,14 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
*/
protected $iCursor = 0;
/**
* __toString magical function overload.
*/
public function __toString()
{
return '';
}
/**
* ormLinkSet constructor.
* @param $sHostClass
@@ -123,19 +131,18 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
{
assert($oLink instanceof $this->sClass);
// No impact on the iteration algorithm
$this->aAdded[] = $oLink;
$iObjectId = $oLink->GetKey();
$this->aAdded[$iObjectId] = $oLink;
$this->bHasDelta = true;
}
/**
* @param DBObject $oObject
* @param string $sClassAlias
* @deprecated
* @deprecated Since iTop 2.4, use ormLinkset->AddItem() instead.
*/
public function AddObject(DBObject $oObject, $sClassAlias = '')
{
trigger_error('iTop: ormLinkSet::AddObject() is deprecated use ormLinkSet::AddItem() instead.', E_USER_DEPRECATED);
$this->AddItem($oObject);
}
@@ -150,6 +157,13 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
$this->aRemoved[$iObjectId] = $iObjectId;
$this->bHasDelta = true;
}
else
{
if (array_key_exists($iObjectId, $this->aAdded))
{
unset($this->aAdded[$iObjectId]);
}
}
}
/**
@@ -158,9 +172,14 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
public function ModifyItem(DBObject $oLink)
{
assert($oLink instanceof $this->sClass);
$iObjectId = $oLink->GetKey();
$this->aModified[$iObjectId] = $oLink;
$this->bHasDelta = true;
if (array_key_exists($iObjectId, $this->aPreserved))
{
unset($this->aPreserved[$iObjectId]);
$this->aModified[$iObjectId] = $oLink;
$this->bHasDelta = true;
}
}
protected function LoadOriginalIds()
@@ -169,15 +188,22 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
{
if ($this->oOriginalSet)
{
$this->aOriginalObjects = $this->oOriginalSet->ToArray();
$this->aOriginalObjects = $this->GetArrayOfIndex();
$this->aPreserved = $this->aOriginalObjects; // Copy (not effective until aPreserved gets modified)
foreach ($this->aRemoved as $iObjectId)
{
if (array_key_exists($iObjectId, $this->aPreserved))
{
unset($this->aPreserved[$iObjectId]);
}
}
foreach ($this->aRemoved as $iObjectId)
{
if (array_key_exists($iObjectId, $this->aPreserved))
{
unset($this->aPreserved[$iObjectId]);
}
}
foreach ($this->aModified as $iObjectId => $oLink)
{
if (array_key_exists($iObjectId, $this->aPreserved))
{
unset($this->aPreserved[$iObjectId]);
}
}
}
else
{
@@ -189,17 +215,29 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
}
}
/**
* Note: After calling this method, the set cursor will be at the end of the set. You might want to rewind it.
* @return array
*/
protected function GetArrayOfIndex()
{
$aRet = array();
$this->oOriginalSet->Rewind();
$iRow = 0;
while ($oObject = $this->oOriginalSet->Fetch())
{
$aRet[$oObject->GetKey()] = $iRow++;
}
return $aRet;
}
/**
* Note: After calling this method, the set cursor will be at the end of the set. You might to rewind it.
*
* @param bool $bWithId
* @return array
* @deprecated
* @deprecated Since iTop 2.4, use foreach($this as $oItem){} instead
*/
public function ToArray($bWithId = true)
{
trigger_error('iTop: ormLinkSet::ToArray() is deprecated use foreach instead.', E_USER_DEPRECATED);
$aRet = array();
foreach($this as $oItem)
{
@@ -215,6 +253,28 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
return $aRet;
}
/**
* @param string $sAttCode
* @param bool $bWithId
* @return array
*/
public function GetColumnAsArray($sAttCode, $bWithId = true)
{
$aRet = array();
foreach($this as $oItem)
{
if ($bWithId)
{
$aRet[$oItem->GetKey()] = $oItem->Get($sAttCode);
}
else
{
$aRet[] = $oItem->Get($sAttCode);
}
}
return $aRet;
}
/**
* The class of the objects of the collection (at least a common ancestor)
*
@@ -233,7 +293,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
public function Count()
{
$this->LoadOriginalIds();
$iRet = count($this->aPreserved) + count($this->aAdded);
$iRet = count($this->aPreserved) + count($this->aAdded) + count($this->aModified);
return $iRet;
}
@@ -290,11 +350,21 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
$iPreservedCount = count($this->aPreserved);
if ($this->iCursor < $iPreservedCount)
{
$oRet = current($this->aPreserved);
$iRet = current($this->aPreserved);
$this->oOriginalSet->Seek($iRet);
$oRet = $this->oOriginalSet->Fetch();
}
else
{
$oRet = current($this->aAdded);
$iModifiedCount = count($this->aModified);
if($this->iCursor < $iPreservedCount + $iModifiedCount)
{
$oRet = current($this->aModified);
}
else
{
$oRet = current($this->aAdded);
}
}
return $oRet;
}
@@ -315,9 +385,17 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
}
else
{
next($this->aAdded);
$iModifiedCount = count($this->aModified);
if($this->iCursor < $iPreservedCount + $iModifiedCount)
{
next($this->aModified);
}
else
{
next($this->aAdded);
}
}
// Increment AFTER moving the internal cursors because when starting aAdded, we must leave it intact
// Increment AFTER moving the internal cursors because when starting aModified / aAdded, we must leave it intact
$this->iCursor++;
}
@@ -353,11 +431,12 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
*/
public function rewind()
{
$this->LoadOriginalIds();
$this->LoadOriginalIds();
$this->iCursor = 0;
$this->iCursor = 0;
reset($this->aPreserved);
reset($this->aAdded);
reset($this->aAdded);
reset($this->aModified);
}
public function HasDelta()
@@ -429,7 +508,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
$this->aAdded = array();
$this->aRemoved = array();
$this->aModified = array();
$this->aPreserved = $this->aOriginalObjects;
$this->aPreserved = ($this->aOriginalObjects === null) ? array() : $this->aOriginalObjects;
$this->bHasDelta = false;
/** @var AttributeLinkedSet $oAttDef */
@@ -504,12 +583,15 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
// Check for the existing links
//
/** @var DBObject[] $aExistingLinks */
$aExistingLinks = array();
/** @var Int[] $aExistingRemote */
$aExistingRemote = array();
if (count($aCheckLinks) > 0)
{
$oSearch = new DBObjectSearch($this->sClass);
$oSearch->AddCondition('id', $aCheckLinks, 'IN');
$oSet = new DBObjectSet($oSearch);
/** @var DBObject[] $aExistingLinks */
$aExistingLinks = $oSet->ToArray();
}
@@ -521,8 +603,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
$oSearch->AddCondition($sExtKeyToMe, $oHostObject->GetKey(), '=');
$oSearch->AddCondition($sExtKeyToRemote, $aCheckRemote, 'IN');
$oSet = new DBObjectSet($oSearch);
/** @var Int[] $aExistingRemote */
$aExistingRemote = $oSet->GetColumnAsArray($sExtKeyToRemote);
$aExistingRemote = $oSet->GetColumnAsArray($sExtKeyToRemote, true);
}
// Write the links according to the existing links
@@ -536,11 +617,28 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
{
if (count($aCheckRemote) > 0)
{
if (in_array($oLink->Get($sExtKeyToRemote), $aExistingRemote))
{
// Do not create a duplicate
continue;
}
$bIsDuplicate = false;
foreach($aExistingRemote as $sLinkKey => $sExtKey)
{
if ($sExtKey == $oLink->Get($sExtKeyToRemote))
{
// Do not create a duplicate
// + In the case of a remove action followed by an add action
// of an existing link,
// the final state to consider is add action,
// so suppress the entry in the removed list.
if (array_key_exists($sLinkKey, $this->aRemoved))
{
unset($this->aRemoved[$sLinkKey]);
}
$bIsDuplicate = true;
break;
}
}
if ($bIsDuplicate)
{
continue;
}
}
}
@@ -598,4 +696,34 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
//
$oMtx->Unlock();
}
public function ToDBObjectSet($bShowObsolete = true)
{
$oAttDef = MetaModel::GetAttributeDef($this->sHostClass, $this->sAttCode);
$oLinkSearch = $this->GetFilter();
if ($oAttDef->IsIndirect())
{
$sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
$oLinkingAttDef = MetaModel::GetAttributeDef($this->sClass, $sExtKeyToRemote);
$sTargetClass = $oLinkingAttDef->GetTargetClass();
if (!$bShowObsolete && MetaModel::IsObsoletable($sTargetClass))
{
$oNotObsolete = new BinaryExpression(
new FieldExpression('obsolescence_flag', $sTargetClass),
'=',
new ScalarExpression(0)
);
$oNotObsoleteRemote = new DBObjectSearch($sTargetClass);
$oNotObsoleteRemote->AddConditionExpression($oNotObsolete);
$oLinkSearch->AddCondition_PointingTo($oNotObsoleteRemote, $sExtKeyToRemote);
}
}
$oLinkSet = new DBObjectSet($oLinkSearch);
$oLinkSet->SetShowObsoleteData($bShowObsolete);
if ($this->HasDelta())
{
$oLinkSet->AddObjectArray($this->aAdded);
}
return $oLinkSet;
}
}

View File

@@ -226,9 +226,9 @@ EOF
$iNewWidth = $iWidth * $fScale;
$iNewHeight = $iHeight * $fScale;
$sUrl = 'data:' . $value->GetMimeType() . ';base64,' . base64_encode($value->GetData());
$sUrl = 'data:'.$value->GetMimeType().';base64,'.base64_encode($value->GetData());
}
$sRet = '<img src="' . $sUrl . '" style="width: ' . $iNewWidth . 'px; height: ' . $iNewHeight . 'px">';
$sRet = ($sUrl !== null) ? '<img src="'.$sUrl.'" style="width: '.$iNewWidth.'px; height: '.$iNewHeight.'px">' : '';
$sRet = '<div class="view-image">'.$sRet.'</div>';
}
else

View File

@@ -175,7 +175,8 @@ EOF
}
elseif ($oAttDef instanceof AttributeCustomFields)
{
$sRet = $oObj->GetAsHTML($sAttCode);
// Stick to the weird implementation made in GetNextChunk
$sRet = utils::TextToHtml($oObj->GetEditValue($sAttCode));
}
else
{
@@ -326,6 +327,12 @@ EOF
}
$sData .= "<td x:str>$sField</td>";
}
elseif ($oAttDef instanceof AttributeCustomFields)
{
// GetAsHTML returns a table that would not fit
$sField = utils::TextToHtml($oObj->GetEditValue($sAttCode));
$sData .= "<td x:str>$sField</td>";
}
else if($oAttDef instanceof AttributeString)
{
$sField = $oObj->GetAsHTML($sAttCode, $this->bLocalizeOutput);

View File

@@ -36,7 +36,8 @@
class SQLObjectQuery extends SQLQuery
{
private $m_SourceOQL = '';
public $m_aContextData = null;
public $m_iOriginalTableCount = 0;
private $m_sTable = '';
private $m_sTableAlias = '';
private $m_aFields = array();
@@ -46,7 +47,7 @@ class SQLObjectQuery extends SQLQuery
private $m_aValues = array(); // Values to set in case of an update query
private $m_oSelectedIdField = null;
private $m_aJoinSelects = array();
private $m_bBeautifulQuery = false;
protected $m_bBeautifulQuery = false;
// Data set by PrepareRendering()
private $__aFrom;
@@ -311,6 +312,14 @@ class SQLObjectQuery extends SQLQuery
$this->PrepareRendering();
$sFrom = self::ClauseFrom($this->__aFrom, $sIndent);
$sWhere = self::ClauseWhere($this->m_oConditionExpr, $aArgs);
if ($iLimitCount > 0)
{
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
}
else
{
$sLimit = '';
}
if ($bGetCount)
{
if (count($this->__aSelectedIdFields) > 0)
@@ -321,11 +330,13 @@ class SQLObjectQuery extends SQLQuery
$aCountFields[] = "COALESCE($sFieldExpr, 0)"; // Null values are excluded from the count
}
$sCountFields = implode(', ', $aCountFields);
$sSQL = "SELECT$sLineSep COUNT(DISTINCT $sCountFields) AS COUNT$sLineSep FROM $sFrom$sLineSep WHERE $sWhere";
// Count can be limited for performance reason, in this case the total amount is not important,
// we only need to know if the number of entries is greater than a certain amount.
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep DISTINCT $sCountFields $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _tatooine_";
}
else
{
$sSQL = "SELECT$sLineSep COUNT(*) AS COUNT$sLineSep FROM $sFrom$sLineSep WHERE $sWhere";
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep 1 $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _tatooine_";
}
}
else
@@ -336,14 +347,7 @@ class SQLObjectQuery extends SQLQuery
{
$sOrderBy = "ORDER BY $sOrderBy$sLineSep";
}
if ($iLimitCount > 0)
{
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
}
else
{
$sLimit = '';
}
$sSQL = "SELECT$sLineSep DISTINCT $sSelect$sLineSep FROM $sFrom$sLineSep WHERE $sWhere$sLineSep $sOrderBy $sLimit";
}
return $sSQL;
@@ -510,6 +514,7 @@ class SQLObjectQuery extends SQLQuery
public function OptimizeJoins($aUsedTables, $bTopCall = true)
{
$this->m_iOriginalTableCount = $this->CountTables();
if ($bTopCall)
{
// Top call: complete the list of tables absolutely required to perform the right query
@@ -545,7 +550,7 @@ class SQLObjectQuery extends SQLQuery
return $iRet;
}
protected function CollectUsedTables(&$aTables)
public function CollectUsedTables(&$aTables)
{
$this->m_oConditionExpr->CollectUsedParents($aTables);
foreach($this->m_aFields as $sFieldAlias => $oField)
@@ -612,4 +617,5 @@ class SQLObjectQuery extends SQLQuery
// None of the tables is in the list of required tables
return $bResult;
}
}

View File

@@ -39,7 +39,7 @@ require_once('cmdbsource.class.inc.php');
abstract class SQLQuery
{
private $m_SourceOQL = '';
private $m_bBeautifulQuery = false;
protected $m_bBeautifulQuery = false;
public function __construct()
{

View File

@@ -58,7 +58,7 @@ class SQLUnionQuery extends SQLQuery
{
$aQueriesHtml[] = '<p>'.$oSQLQuery->DisplayHtml().'</p>';
}
echo implode('UNION', $aQueries);
echo implode('UNION', $aQueriesHtml);
}
public function AddInnerJoin($oSQLQuery, $sLeftField, $sRightField, $sRightTable = '')
@@ -85,7 +85,6 @@ class SQLUnionQuery extends SQLQuery
{
$this->m_bBeautifulQuery = $bBeautifulQuery;
$sLineSep = $this->m_bBeautifulQuery ? "\n" : '';
$sIndent = $this->m_bBeautifulQuery ? " " : null;
$aSelects = array();
foreach ($this->aQueries as $oSQLQuery)
@@ -93,36 +92,33 @@ class SQLUnionQuery extends SQLQuery
// Render SELECTS without orderby/limit/count
$aSelects[] = $oSQLQuery->RenderSelect(array(), $aArgs, 0, 0, false, $bBeautifulQuery);
}
$sSelects = '('.implode(")$sLineSep UNION$sLineSep(", $aSelects).')';
if ($bGetCount)
if ($iLimitCount > 0)
{
$sFrom = "($sLineSep$sSelects$sLineSep) as __selects__";
$sSQL = "SELECT$sLineSep COUNT(*) AS COUNT$sLineSep FROM $sFrom$sLineSep";
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
}
else
{
$sLimit = '';
}
if ($bGetCount)
{
$sSelects = '('.implode(" $sLimit)$sLineSep UNION$sLineSep(", $aSelects)." $sLimit)";
$sFrom = "($sLineSep$sSelects$sLineSep) as __selects__";
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep 1 $sLineSep FROM $sFrom$sLineSep) AS _union_tatooine_";
}
else
{
$aSelects = array();
foreach ($this->aQueries as $oSQLQuery)
{
// Render SELECT without orderby/limit/count
$aSelects[] = $oSQLQuery->RenderSelect(array(), $aArgs, 0, 0, false, $bBeautifulQuery);
}
$sSelect = $this->aQueries[0]->RenderSelectClause();
$sOrderBy = $this->aQueries[0]->RenderOrderByClause($aOrderBy);
if (!empty($sOrderBy))
{
$sOrderBy = "ORDER BY $sOrderBy$sLineSep";
}
if ($iLimitCount > 0)
{
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
$sOrderBy = "ORDER BY $sOrderBy$sLineSep $sLimit";
$sSQL = '('.implode(")$sLineSep UNION$sLineSep (", $aSelects).')'.$sLineSep.$sOrderBy;
}
else
{
$sLimit = '';
$sSQL = '('.implode(" $sLimit)$sLineSep UNION$sLineSep (", $aSelects)." $sLimit)";
}
$sSQL = $sSelects.$sLineSep.$sOrderBy.' '.$sLimit;
}
return $sSQL;
}

View File

@@ -338,6 +338,8 @@ abstract class User extends cmdbAbstractObject
'bulkread' => $this->GetGrantAsHtml($sClass, UR_ACTION_BULK_READ),
'write' => $this->GetGrantAsHtml($sClass, UR_ACTION_MODIFY),
'bulkwrite' => $this->GetGrantAsHtml($sClass, UR_ACTION_BULK_MODIFY),
'delete' => $this->GetGrantAsHtml($sClass, UR_ACTION_DELETE),
'bulkdelete' => $this->GetGrantAsHtml($sClass, UR_ACTION_BULK_DELETE),
'stimuli' => $sStimuli,
);
}
@@ -350,6 +352,8 @@ abstract class User extends cmdbAbstractObject
$aDisplayConfig['bulkread'] = array('label' => Dict::S('UI:UserManagement:Action:BulkRead'), 'description' => Dict::S('UI:UserManagement:Action:BulkRead+'));
$aDisplayConfig['write'] = array('label' => Dict::S('UI:UserManagement:Action:Modify'), 'description' => Dict::S('UI:UserManagement:Action:Modify+'));
$aDisplayConfig['bulkwrite'] = array('label' => Dict::S('UI:UserManagement:Action:BulkModify'), 'description' => Dict::S('UI:UserManagement:Action:BulkModify+'));
$aDisplayConfig['delete'] = array('label' => Dict::S('UI:UserManagement:Action:Delete'), 'description' => Dict::S('UI:UserManagement:Action:Delete+'));
$aDisplayConfig['bulkdelete'] = array('label' => Dict::S('UI:UserManagement:Action:BulkDelete'), 'description' => Dict::S('UI:UserManagement:Action:BulkDelete+'));
$aDisplayConfig['stimuli'] = array('label' => Dict::S('UI:UserManagement:Action:Stimuli'), 'description' => Dict::S('UI:UserManagement:Action:Stimuli+'));
$oPage->table($aDisplayConfig, $aDisplayData);
}
@@ -1153,7 +1157,7 @@ class UserRights
self::$m_aAdmins = array();
self::$m_aPortalUsers = array();
}
if (!isset($_SESSION))
if (!isset($_SESSION) && !utils::IsModeCLI())
{
session_name('itop-'.md5(APPROOT));
session_start();

View File

@@ -52,7 +52,7 @@ abstract class ValueSetDefinition
}
public function GetValues($aArgs, $sContains = '')
public function GetValues($aArgs, $sContains = '', $sOperation = 'contains')
{
if (!$this->m_bIsLoaded)
{
@@ -93,12 +93,16 @@ abstract class ValueSetDefinition
class ValueSetObjects extends ValueSetDefinition
{
protected $m_sContains;
protected $m_sOperation;
protected $m_sFilterExpr; // in OQL
protected $m_sValueAttCode;
protected $m_aOrderBy;
protected $m_aExtraConditions;
private $m_bAllowAllData;
private $m_aModifierProperties;
private $m_bSort;
private $m_iLimit;
/**
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
@@ -106,12 +110,15 @@ class ValueSetObjects extends ValueSetDefinition
public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array(), $bAllowAllData = false, $aModifierProperties = array())
{
$this->m_sContains = '';
$this->m_sOperation = '';
$this->m_sFilterExpr = $sFilterExp;
$this->m_sValueAttCode = $sValueAttCode;
$this->m_aOrderBy = $aOrderBy;
$this->m_bAllowAllData = $bAllowAllData;
$this->m_aModifierProperties = $aModifierProperties;
$this->m_aExtraConditions = array();
$this->m_bSort = true;
$this->m_iLimit = 0;
}
public function SetModifierProperty($sPluginClass, $sProperty, $value)
@@ -163,11 +170,11 @@ class ValueSetObjects extends ValueSetDefinition
return new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs);
}
public function GetValues($aArgs, $sContains = '')
public function GetValues($aArgs, $sContains = '', $sOperation = 'contains')
{
if (!$this->m_bIsLoaded || ($sContains != $this->m_sContains))
if (!$this->m_bIsLoaded || ($sContains != $this->m_sContains) || ($sOperation != $this->m_sOperation))
{
$this->LoadValues($aArgs, $sContains);
$this->LoadValues($aArgs, $sContains, $sOperation);
$this->m_bIsLoaded = true;
}
// The results are already filtered and sorted (on friendly name)
@@ -175,9 +182,10 @@ class ValueSetObjects extends ValueSetDefinition
return $aRet;
}
protected function LoadValues($aArgs, $sContains = '')
protected function LoadValues($aArgs, $sContains = '', $sOperation = 'contains')
{
$this->m_sContains = $sContains;
$this->m_sOperation = $sOperation;
$this->m_aValues = array();
@@ -202,12 +210,54 @@ class ValueSetObjects extends ValueSetDefinition
}
}
$oValueExpr = new ScalarExpression('%'.$sContains.'%');
$oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias());
$oNewCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
$oFilter->AddConditionExpression($oNewCondition);
switch ($sOperation)
{
case 'equals_start_with':
$aAttributes = MetaModel::GetFriendlyNameAttributeCodeList($oFilter->GetClass());
$sClassAlias = $oFilter->GetClassAlias();
$aFilters = array();
// Equals first
$oValueExpr = new ScalarExpression($sContains);
foreach($aAttributes as $sAttribute)
{
$oNewFilter = $oFilter->DeepClone();
$oNameExpr = new FieldExpression($sAttribute, $sClassAlias);
$oCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
$oNewFilter->AddConditionExpression($oCondition);
$aFilters[] = $oNewFilter;
}
// start with next
$oValueExpr = new ScalarExpression($sContains.'%');
foreach($aAttributes as $sAttribute)
{
$oNewFilter = $oFilter->DeepClone();
$oNameExpr = new FieldExpression($sAttribute, $sClassAlias);
$oCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
$oNewFilter->AddConditionExpression($oCondition);
$aFilters[] = $oNewFilter;
}
// Unions are much faster than OR conditions
$oFilter = new DBUnionSearch($aFilters);
break;
$oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs);
default:
$oValueExpr = new ScalarExpression('%'.$sContains.'%');
$oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias());
$oNewCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
$oFilter->AddConditionExpression($oNewCondition);
break;
}
$oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs, null, $this->m_iLimit, 0, $this->m_bSort);
if (empty($this->m_sValueAttCode))
{
$aAttToLoad = array($oFilter->GetClassAlias() => array('friendlyname'));
}
else
{
$aAttToLoad = array($oFilter->GetClassAlias() => array($this->m_sValueAttCode));
}
$oObjects->OptimizeColumnLoad($aAttToLoad);
while ($oObject = $oObjects->Fetch())
{
if (empty($this->m_sValueAttCode))
@@ -231,6 +281,22 @@ class ValueSetObjects extends ValueSetDefinition
{
return $this->m_sFilterExpr;
}
/**
* @param $iLimit
*/
public function SetLimit($iLimit)
{
$this->m_iLimit = $iLimit;
}
/**
* @param $bSort
*/
public function SetSort($bSort)
{
$this->m_bSort = $bSort;
}
}

View File

@@ -5,4 +5,4 @@ $complement-light: #d6e8ef;
$frame-background-color: #F1F1F1;
$text-color: #000;
// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
$version: "v2.4.0-beta";
$version: "v2.4.0";

Binary file not shown.

View File

@@ -21,7 +21,7 @@ OS2Version: 0
OS2_WeightWidthSlopeOnly: 0
OS2_UseTypoMetrics: 1
CreationTime: 1463745065
ModificationTime: 1464178421
ModificationTime: 1506001058
OS2TypoAscent: 0
OS2TypoAOffset: 1
OS2TypoDescent: 0
@@ -35,7 +35,6 @@ HheadAscent: 0
HheadAOffset: 1
HheadDescent: 0
HheadDOffset: 1
OS2Vendor: 'PfEd'
MarkAttachClasses: 1
DEI: 91125
Encoding: ISO8859-1
@@ -47,7 +46,7 @@ FitToEm: 0
WinInfo: 0 31 10
BeginPrivate: 0
EndPrivate
BeginChars: 256 8
BeginChars: 256 11
StartChar: zero
Encoding: 48 48 0
@@ -210,7 +209,7 @@ StartChar: three
Encoding: 51 51 3
Width: 1022
VWidth: 0
Flags: MO
Flags: M
LayerCount: 3
Fore
SplineSet
@@ -261,11 +260,13 @@ StartChar: C
Encoding: 67 67 4
Width: 1080
VWidth: 0
Flags: HW
Flags: W
HStem: -112 36<398.67 444.211> 97 36<463.993 575.071> 116 37<411.524 459.906> 250 37<334.123 402.464> 335 37<749.246 821.773> 387 37<836.543 929.295> 396 37<873.093 933.545> 442 37<475 482 739.647 795.664> 621 37<286.042 389.13> 650 37<510.192 579.789>
VStem: 53 37<216.048 298> 218 37<457.867 579.651> 272 36<60.6299 128.446> 439 37<479.452 577.67> 451 37<-73.2171 5.85426> 472 37<568.829 649.107> 553 37<359.872 417.574> 651 38<-34.3438 62.6665> 718 38<8.82031 164.606> 934 38<341.569 396>
LayerCount: 3
Fore
SplineSet
641 -116 m 4x9b20
641 -116 m 4x9b39f0
637 -116 633 -115 630 -113 c 4
624 -110 616 -102 616 -88 c 4
616 -84 617 -80 618 -75 c 4
@@ -275,14 +276,14 @@ SplineSet
651 31 641 52 624 75 c 4
609 94 595 108 586 115 c 5
562 107 523 97 500 97 c 6
499 97 l 6xdb20
499 97 l 6xdb39f0
479 97 468 105 460 111 c 4
457 114 455 115 453 116 c 4
450 116 438 112 423 104 c 4
412 98 405 94 401 90 c 5
411 80 436 61 448 52 c 4
460 43 468 37 473 32 c 4
485 20 488 -4 488 -20 c 4
485 20 488 -4 488 -20 c 4xb93af0
488 -27 487 -33 487 -36 c 4
485 -55 477 -90 452 -105 c 4
444 -110 435 -112 426 -112 c 4
@@ -321,10 +322,10 @@ SplineSet
219 484 218 493 218 504 c 4
218 526 222 550 231 575 c 4
237 593 255 636 284 651 c 4
293 656 305 658 320 658 c 4xb9a0
293 656 305 658 320 658 c 4
342 658 367 653 389 644 c 4
416 633 437 617 452 597 c 4
472 571 476 537 476 508 c 4
472 571 476 537 476 508 c 4xb9bcf0
476 497 476 487 475 479 c 5
482 479 l 5
505 499 l 5
@@ -352,7 +353,7 @@ SplineSet
766 473 767 473 768 473 c 4
787 473 808 460 831 446 c 4
845 437 865 424 873 424 c 4
874 424 l 6x9d60
874 424 l 6x9d79f0
875 424 880 425 884 426 c 4
897 429 915 433 930 433 c 4
956 433 965 421 969 412 c 4
@@ -362,7 +363,7 @@ SplineSet
916 289 911 288 905 288 c 4
888 288 863 299 835 311 c 4
810 322 780 335 766 335 c 4
765 335 764 335 764 335 c 4
764 335 l 4
763 335 757 331 748 315 c 4
741 301 734 283 727 264 c 4
721 248 715 231 708 216 c 5
@@ -370,7 +371,7 @@ SplineSet
756 69 755 58 753 47 c 4
748 19 727 -22 708 -51 c 4
697 -68 687 -82 677 -93 c 4
663 -109 652 -116 641 -116 c 4x9b20
663 -109 652 -116 641 -116 c 4x9b39f0
308 85 m 5
308 83 312 72 340 46 c 4
357 30 374 18 374 18 c 6
@@ -387,14 +388,14 @@ SplineSet
383 55 362 72 362 90 c 4
362 92 362 93 362 95 c 4
363 100 366 113 402 134 c 4
410 138 435 153 453 153 c 4xb9a0
410 138 435 153 453 153 c 4xb9baf0
455 153 458 152 460 152 c 4
470 150 476 145 482 141 c 4
488 136 491 133 499 133 c 6
500 133 l 6xd920
500 133 l 6xd93af0
521 133 562 145 581 153 c 6
589 156 l 5
596 153 l 6xb920
596 153 l 6xb93af0
612 146 637 119 654 97 c 4
670 75 689 44 689 16 c 4
689 13 689 10 688 7 c 4
@@ -413,7 +414,7 @@ SplineSet
868 337 893 326 903 325 c 5
907 329 916 340 924 359 c 4
932 377 934 390 934 396 c 4
933 396 932 396 930 396 c 4xdb20
933 396 932 396 930 396 c 4xdb3af0
919 396 902 392 892 390 c 4
886 389 881 388 878 388 c 4
876 388 875 387 873 387 c 4
@@ -433,9 +434,9 @@ SplineSet
629 559 619 566 619 566 c 6
615 569 l 5
580 633 l 5
566 639 539 650 520 650 c 4x9d60
566 639 539 650 520 650 c 4
514 650 512 650 511 649 c 4
510 648 509 644 509 637 c 4
510 648 509 644 509 637 c 4x9d79f0
509 629 510 618 514 602 c 4
517 589 520 577 523 569 c 5
562 561 l 5
@@ -446,7 +447,7 @@ SplineSet
435 465 l 6
437 474 439 491 439 510 c 4
439 533 436 558 423 575 c 4
400 605 354 621 320 621 c 4x99a0
400 605 354 621 320 621 c 4x99bcf0
310 621 304 619 301 618 c 4
281 608 255 548 255 504 c 4
255 497 255 490 257 484 c 4
@@ -475,22 +476,25 @@ SplineSet
424 204 390 196 377 186 c 4
330 149 310 105 308 85 c 5
EndSplineSet
Validated: 1
EndChar
StartChar: I
Encoding: 73 73 5
Width: 1024
VWidth: 0
Flags: HW
Flags: W
HStem: -154 166<226 365 659 798> 126 26<498.267 525.733> 151 131<288 343 681 735> 313 132<288 343 681 735> 330 32<424.389 599.754> 443 26<498.267 525.733> 584 184<226 365 659 798>
VStem: 51 175<12 151 445 584> 365 132<74 126 469 521> 366 31<282 313> 527 132<74 126 469 521> 627 31<282 313> 798 175<12 151 445 584>
LayerCount: 3
Fore
SplineSet
51 -154 m 1
51 -154 m 1x8308
51 768 l 1
973 768 l 1
973 -154 l 1
51 -154 l 1
497 469 m 2
51 -154 l 1x8308
497 469 m 1x87a8
502 469 507 470 512 470 c 0
517 470 522 469 527 469 c 1
527 521 l 1
@@ -498,35 +502,34 @@ SplineSet
512 702 l 1
414 604 l 1
497 521 l 1
497 469 l 1
497 469 l 2
497 469 l 1x87a8
653 417 m 1
681 445 l 1
798 445 l 1
798 445 l 1x9328
798 584 l 1
659 584 l 1
659 467 l 1
633 440 l 1
643 433 649 425 653 417 c 1
366 282 m 1
366 282 m 1xb348
366 313 l 1
288 313 l 1
206 396 l 1
108 298 l 1
206 200 l 1
288 282 l 1
366 282 l 1
366 282 l 1xb348
343 445 m 1
371 417 l 1
375 425 381 433 391 440 c 1
365 467 l 1
365 584 l 1
365 584 l 1x9388
226 584 l 1
226 445 l 1
343 445 l 1
371 178 m 1
343 151 l 1
226 151 l 1
226 151 l 1xa388
226 12 l 1
365 12 l 1
365 129 l 1
@@ -535,7 +538,7 @@ SplineSet
818 396 m 1
735 313 l 1
658 313 l 1
658 282 l 1
658 282 l 1xb318
735 282 l 1
818 200 l 1
916 298 l 1
@@ -543,12 +546,12 @@ SplineSet
653 178 m 1
649 170 643 162 633 155 c 1
659 129 l 1
659 12 l 1
659 12 l 1xa328
798 12 l 1
798 151 l 1
681 151 l 1
653 178 l 1
527 126 m 1
527 126 m 1xc3a8
522 126 517 126 512 126 c 0
507 126 502 126 497 126 c 1
497 74 l 1
@@ -556,8 +559,7 @@ SplineSet
512 -106 l 1
610 -8 l 1
527 74 l 1
527 126 l 1
527 126 l 1
527 126 l 1xc3a8
610 348 m 0
584 337 549 330 512 330 c 0
475 330 441 337 414 348 c 0
@@ -567,9 +569,9 @@ SplineSet
449 157 479 152 512 152 c 0
545 152 575 157 598 167 c 0
616 174 627 184 627 192 c 2
627 356 l 1
627 356 l 1xcb58
622 353 616 351 610 348 c 0
512 443 m 1
512 443 m 1x8f58
479 443 449 438 426 428 c 0
408 421 397 410 397 402 c 0
397 394 408 384 426 377 c 0
@@ -577,8 +579,7 @@ SplineSet
545 362 575 367 598 377 c 0
616 384 627 394 627 402 c 0
627 410 616 421 598 428 c 0
575 438 545 443 512 443 c 1
512 443 l 1
575 438 545 443 512 443 c 1x8f58
EndSplineSet
Validated: 5
EndChar
@@ -587,7 +588,8 @@ StartChar: four
Encoding: 52 52 6
Width: 1024
VWidth: 0
Flags: H
HStem: -2 41<389.544 635.489> 292 109<316 441 550 675> 639 41<389.544 636.396>
VStem: 117 41<241.556 436.857> 441 109<167 292 401 525> 868 41<249.643 435.223>
LayerCount: 3
Fore
SplineSet
@@ -607,7 +609,6 @@ SplineSet
909 288 892 238 861 189 c 0
835 149 801 111 757 77 c 1
801 -67 l 1
801 -67 l 1
649 63 m 1
733 17 l 1
710 93 l 1
@@ -618,7 +619,6 @@ SplineSet
158 174 317 39 513 39 c 0
552 39 592 49 634 59 c 2
649 63 l 1
649 63 l 1
675 306 m 1
675 298 667 292 657 292 c 2
550 292 l 1
@@ -640,7 +640,6 @@ SplineSet
657 401 l 2
667 401 675 394 675 386 c 2
675 306 l 1
675 306 l 1
EndSplineSet
Validated: 5
EndChar
@@ -649,15 +648,17 @@ StartChar: D
Encoding: 68 68 7
Width: 1080
VWidth: 0
Flags: HW
Flags: W
HStem: 198 131<79.9424 131.341> 306 98<835.102 921.871> 353 100<731.982 810.871>
VStem: 238 219<460.138 583.719> 292 88<67.6511 114.863> 369 100<-72.2559 6.9753> 667 66<3.74069 76.8123>
LayerCount: 3
Fore
SplineSet
469 -14 m 4
469 -14 m 4x26
469 -45 459 -89 425 -89 c 4
397 -89 372 -53 369 -41 c 4
397 -89 372 -53 369 -41 c 4x26
365 -27 363 7 363 7 c 5
363 7 292 63 292 88 c 4
363 7 292 63 292 88 c 4x2a
292 113 317 164 366 202 c 4
388 219 439 225 439 225 c 5
439 225 414 269 373 269 c 4
@@ -669,7 +670,7 @@ SplineSet
119 206 108 198 97 198 c 4
78 198 76 234 76 261 c 4
76 268 76 274 76 279 c 4
76 288 76 295 76 295 c 6
76 295 l 6
76 295 82 320 96 328 c 4
97 328 98 329 99 329 c 4
121 329 217 268 217 268 c 6
@@ -696,12 +697,12 @@ SplineSet
570 401 569 394 569 388 c 4
569 355 614 330 614 330 c 5
614 330 646 364 662 384 c 4
678 404 734 453 763 453 c 4
678 404 734 453 763 453 c 4xb2
790 453 841 404 867 404 c 4
878 404 902 413 924 413 c 4
938 413 945 409 945 396 c 4
945 371 922 318 905 307 c 4
903 306 902 306 899 306 c 4
903 306 902 306 899 306 c 4x52
872 306 795 353 761 353 c 4
758 353 756 353 754 352 c 4
723 342 708 263 682 213 c 5
@@ -714,11 +715,232 @@ SplineSet
667 64 605 129 586 138 c 5
565 130 522 118 499 118 c 4
475 118 470 137 453 137 c 4
437 137 380 108 380 93 c 4
437 137 380 108 380 93 c 4x2a
380 77 446 36 460 23 c 4
465 18 469 3 469 -14 c 4
465 18 469 3 469 -14 c 4x26
EndSplineSet
Validated: 1
EndChar
StartChar: E
Encoding: 69 69 8
Width: 1024
VWidth: 0
HStem: -65 248<767.992 903.557> 62.9598 56.9516<226.56 349.224 705.189 710> 143 26.9554<256.859 324.845> 192.776 27.2245<289.957 314.952> 295.071 27.9295<269.575 354.789> 344 66<376.038 439.962> 433 25<407.015 431.988> 469 248<767.951 904.873>
VStem: 66.5097 115.469<193.203 298.14> 204 32.9583<187.285 267.499> 257.966 57.0345<194.835 224.243> 335.831 30.8205<177.895 239.59> 388.713 44.4108<157.773 270.37> 407 25<433.012 457.985> 442 55.4336<365.791 447.874> 711 249<-8.06335 62 583 662.557>
LayerCount: 3
Fore
SplineSet
279 573 m 0x7ffb
292 573 313 570 343 563 c 0
462 535 489 420 496 389 c 0
496.963 384.737 497.434 380.795 497.434 377.167 c 0
497.434 354.414 478.912 344 447 344 c 0
422.333 344 394.111 344.889 359.074 344.889 c 0
341.556 344.889 322.333 344.667 301 344 c 0
237 342 192 300 183 241 c 0
182.31 236.477 181.979 231.948 181.979 227.44 c 0
181.979 173.142 230.063 121.847 279 120 c 0
280.57 119.941 282.134 119.911 283.69 119.911 c 0
334.636 119.911 377.296 151.452 387 199 c 0
388.159 204.678 388.713 210.303 388.713 215.813 c 0
388.713 257.852 356.437 293.232 314 295 c 0
312.865 295.047 311.742 295.071 310.63 295.071 c 0
264.267 295.071 236.958 255.02 236.958 229.128 c 0
236.958 206.914 253.918 170.972 286 170 c 0
287.107 169.97 288.186 169.955 289.238 169.955 c 0
323.358 169.955 329.15 185.419 334 199 c 0
335.206 202.378 335.831 206.221 335.831 210.207 c 0
335.831 222.743 329.657 236.689 316 242 c 0
298 249 291 238 290 234 c 0
289 228 294 220 299 220 c 0
306 220 316 220 315 206 c 0
314.143 195.714 302.265 192.776 292.592 192.776 c 0
290.98 192.776 289.429 192.857 288 193 c 0
278.231 193.977 257.966 202.588 257.966 228.155 c 0
257.966 248.46 275.443 271.256 306.289 271.256 c 0
308.143 271.256 310.047 271.172 312 271 c 0
351.28 267.429 366.651 246.325 366.651 219.783 c 0
366.651 216.594 366.429 213.326 366 210 c 0
362 179 343 143 284 143 c 0
225 143 204 192 204 229 c 0
204 266 232 322 318 323 c 0
318.502 323.006 319.002 323.009 319.501 323.009 c 0
394.354 323.009 433.124 257.543 433.124 205.254 c 0
433.124 198.198 432.418 191.383 431 185 c 0
420.148 134.686 398.59 62.9598 273.187 62.9598 c 0
271.478 62.9598 269.749 62.9731 268 63 c 0
134.539 65.8599 66.5097 196.855 66.5097 295.718 c 0
66.5097 300.562 66.673 305.329 67 310 c 0
74 410 112 447 146 480 c 0
204.987 537.114 264.85 531.109 264.85 543.245 c 0
264.85 544.071 264.573 544.981 264 546 c 0
260.913 551.732 257.827 557.659 257.827 562.495 c 0
257.827 568.625 262.788 573 279 573 c 0x7ffb
408 478 m 0
389 478 374 463 374 444 c 0
374 425 389 410 408 410 c 0
427 410 442 425 442 444 c 0
442 463 427 478 408 478 c 0
835 717 m 0
904 717 960 662 960 593 c 0
960 524 904 469 835 469 c 0
798 469 751 495 731 526 c 1
515 456 l 1
503 483 491 498 481 511 c 1
712 583 l 2
712 586 711 590 711 593 c 0
711 662 766 717 835 717 c 0
462 207 m 1
727 121 l 1
747 155 795 183 834 183 c 0
903 183 958 128 958 59 c 0
958 -10 903 -65 834 -65 c 0x9ff3
765 -65 710 -10 710 59 c 0
710 60 710 61 710 62 c 2
456 143 l 1
458.679 159.077 462.157 171.165 462.157 197.076 c 0
462.157 200.176 462.107 203.474 462 207 c 1
407 445 m 0x1ff7
407 453 412 458 420 458 c 0
428 458 432 453 432 445 c 0
432 437 428 433 420 433 c 0
412 433 407 437 407 445 c 0x1ff7
EndSplineSet
Validated: 1
EndChar
StartChar: F
Encoding: 70 70 9
Width: 1024
VWidth: 0
HStem: -36 87<438.038 591.036> 87 39<470.027 570.5> 163 41<517.914 555.989> 241 41<519.089 577.135> 317 43<482.813 612.995> 394 100<654.807 741.193> 528 39<697.004 734.996>
VStem: 177 177<195.689 296.938> 387 50<156.758 272.234> 469 87<171.273 203.985> 588 47<139.283 231.811> 669 67<122.953 266.087> 697 38<528.004 566.995> 750 85<426.5 559.5>
LayerCount: 3
Fore
SplineSet
502 742 m 0xfff4
522 742 553 738 599 727 c 0
780 684 822 509 833 462 c 0
835 456 835 450 835 444 c 0
835 409 806 392 758 392 c 0
721 392 678 394 624 394 c 0
597 394 568 393 535 392 c 0
437 388 368 325 355 235 c 0
354 228 354 222 354 215 c 0
354 132 426 55 501 51 c 0
504 51 507 51 510 51 c 0
587 51 652 99 666 171 c 0
668 180 669 188 669 196 c 0
669 260 620 313 555 317 c 0
553 317 550 317 548 317 c 0
478 317 437 256 437 218 c 0
437 184 463 127 512 126 c 0
514 126 516 126 518 126 c 0
569 126 578 150 586 171 c 0
588 176 588 181 588 187 c 0
588 207 579 228 557 237 c 0
550 240 545 241 540 241 c 0
525 241 520 230 518 225 c 0
516 216 524 204 532 204 c 0
543 204 556 202 556 183 c 0
556 166 541 163 525 163 c 0
522 163 518 163 515 163 c 0
499 164 469 177 469 217 c 0
469 247 497 282 543 282 c 0
546 282 549 282 552 282 c 0
611 277 635 243 635 202 c 0
635 197 635 193 634 188 c 0
628 141 598 87 508 87 c 0
418 87 387 162 387 218 c 0
387 274 430 358 561 360 c 0
562 360 563 360 564 360 c 0
677 360 736 262 736 182 c 0
736 171 735 160 733 150 c 0
716 74 683 -36 494 -36 c 0
491 -36 487 -36 484 -36 c 0
280 -32 177 170 177 321 c 0
177 328 178 335 178 342 c 0
188 495 246 549 298 600 c 0
388 687 479 677 479 696 c 0
479 697 479 698 478 700 c 0
473 709 469 718 469 725 c 0
469 735 477 742 502 742 c 0xfff4
698 597 m 0
669 597 646 574 646 545 c 0
646 516 669 494 698 494 c 0
727 494 750 516 750 545 c 0
750 574 727 597 698 597 c 0
697 547 m 0xffec
697 560 703 567 716 567 c 0
729 567 735 560 735 547 c 0
735 534 729 528 716 528 c 0
703 528 697 534 697 547 c 0xffec
EndSplineSet
Validated: 1
EndChar
StartChar: O
Encoding: 79 79 10
Width: 1024
VWidth: 0
HStem: 1.59961 8<801.97 829.007> 20 35.2002<801.975 810.774 823.574 824.837> 33.5996 5.60059<810.774 817.806> 48.7998 6.40039<810.774 818.736> 61.5996 8.7998<801.043 829.91> 434.399 245.601<441.712 567.228>
VStem: 780.375 8.7998<21.8033 49.6942> 801.975 8.7998<20 33.5996 39.2002 48.7998> 819.574 8.80078<40.0451 47.9513> 841.175 8.7998<21.5374 50.3356>
LayerCount: 3
Fore
SplineSet
504.375 680 m 0x8fc0
672.375 680 816.375 560 846.774 395.2 c 0
877.175 230.399 785.975 67.2002 629.175 7.2002 c 0
621.975 4.7998 613.975 8 611.574 15.2002 c 2
533.175 218.399 l 2
530.774 225.6 533.975 234.399 541.175 236.8 c 0
587.574 254.399 613.975 301.6 605.175 350.399 c 0
596.375 399.2 553.975 434.399 504.375 434.399 c 0
454.774 434.399 413.175 399.2 404.375 350.399 c 0
395.574 301.6 421.175 254.399 467.574 236.8 c 0
474.774 234.399 477.975 225.6 475.574 218.399 c 2
397.975 15.2002 l 2
395.574 8 386.774 4.7998 379.574 7.2002 c 0
222.774 67.2002 131.574 230.399 161.975 395.2 c 0
192.375 560 336.375 680 504.375 680 c 0x8fc0
815.574 70.3994 m 0
834.774 70.3994 849.975 55.2002 849.975 36 c 0
849.975 16.7998 834.774 1.59961 815.574 1.59961 c 0
796.375 1.59961 780.375 16.7998 780.375 36 c 0
780.375 55.2002 796.375 70.3994 815.574 70.3994 c 0
815.574 61.5996 m 0
801.175 61.5996 789.175 50.3994 789.175 36 c 0
789.175 21.5996 801.175 9.59961 815.574 9.59961 c 0
829.975 9.59961 841.175 21.5996 841.175 36 c 0
841.175 50.3994 829.975 61.5996 815.574 61.5996 c 0
814.774 39.2002 m 2xbfc0
816.375 39.2002 817.975 40 818.774 40.7998 c 0
819.574 41.5996 819.574 42.3994 819.574 44 c 0
819.574 45.5996 819.574 46.3994 818.774 47.2002 c 0
817.975 48 816.375 48.7998 814.774 48.7998 c 2
810.774 48.7998 l 1
810.774 39.2002 l 1
814.774 39.2002 l 2xbfc0
810.774 33.5996 m 1
810.774 20 l 1
801.975 20 l 1
801.975 55.2002 l 1xcfc0
815.574 55.2002 l 2x9fc0
820.375 55.2002 822.774 54.3994 825.175 52.7998 c 0
827.574 51.2002 828.375 48.7998 828.375 45.5996 c 0
828.375 43.2002 827.574 41.5996 826.774 40 c 0
825.975 38.3994 824.375 36.7998 821.975 36 c 1
823.574 36 824.375 35.2002 825.175 34.3994 c 0
825.975 33.5996 827.574 32 828.375 29.5996 c 2
833.175 20 l 1
823.574 20 l 1xcfc0
819.574 28.7998 l 2
818.774 30.3994 817.175 31.2002 816.375 32 c 0
815.574 32.7998 814.774 33.5996 813.175 33.5996 c 2
810.774 33.5996 l 1
EndSplineSet
Validated: 524321
EndChar
EndChars
EndSplineFont

Binary file not shown.

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: 'CombodoRegular';
src: url('combodo-webfont.woff2?v=1.0') format('woff2'),
url('combodo-webfont.woff?v=1.0') format('woff'),
url('combodo-webfont.ttf?v=1.0') format('truetype');
src: url('combodo-webfont.woff2?v=2.0') format('woff2'),
url('combodo-webfont.woff?v=2.0') format('woff'),
url('combodo-webfont.ttf?v=2.0') format('truetype');
font-weight: normal;
font-style: normal;
@@ -193,7 +193,16 @@
.fc-combodo-icon:before {
content: "D";
}
.fc-itophub-icon:before {
content: "E";
}
.fc-chameleon-icon:before {
content: "F";
}
.fc-itop-icon:before {
content: "I";
}
.fc-opensource-icon:before {
content: "O";
}

50
css/font-combodo/glyphs/E.svg Executable file
View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="1000"
height="1000"
viewBox="0 0 264.58333 264.58334"
version="1.1"
id="svg909">
<defs
id="defs903" />
<metadata
id="metadata906">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
style="fill:#000000;fill-opacity:1"
transform="matrix(2.6189878,0,0,2.6189878,-138.11438,-66.566639)"
id="layer1">
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 77.776825,48.628779 c -3.105953,0.0165 -2.465143,1.56905 -1.672249,3.00189 0.969595,1.75214 -6.046384,0.53457 -12.856064,7.17372 -3.69523,3.60269 -7.8302,7.46233 -8.56485,18.39112 -0.73465,10.9288 6.57401,26.601051 21.834325,26.890351 14.098468,0.26727 16.523001,-7.762201 17.760671,-13.268961 1.323287,-5.88768 -2.939907,-15.10068 -12.294338,-14.96704 -9.354428,0.13364 -12.428178,6.14692 -12.428178,10.15597 0,4.00904 2.27182,9.35447 8.686288,9.35447 6.414468,0 8.552552,-3.87524 8.953457,-7.2161 0.400905,-3.34087 -1.12866,-6.23284 -5.863206,-6.68176 -3.664545,-0.34747 -5.823431,2.30622 -5.896281,4.44314 -0.100228,2.93996 2.194808,3.98392 3.307291,4.04264 1.112483,0.0587 2.766575,-0.003 2.856155,-1.33635 0.10023,-1.49182 -0.935023,-1.63676 -1.686717,-1.62005 -0.534409,0.0119 -1.147985,-0.88438 -0.985986,-1.50379 0.121989,-0.46643 0.78549,-1.62036 2.790009,-0.81855 2.004523,0.8018 2.544982,3.13823 2.021065,4.67723 -0.534538,1.57021 -1.18617,3.2909 -5.228621,3.17397 -3.536694,-0.1023 -5.411874,-4.25964 -5.328354,-6.68177 0.0939,-2.724 3.190312,-7.1826 8.402068,-6.91534 5.211754,0.26728 8.953352,5.07775 7.884274,10.42314 -1.069079,5.34539 -6.013196,8.82024 -11.759488,8.55297 -5.746294,-0.26727 -11.359094,-6.68189 -10.423654,-13.09636 0.93544,-6.41447 5.87967,-10.95789 12.828675,-11.22516 6.949005,-0.26727 11.885592,0.016 15.894636,0.016 4.009047,0 6.155487,-1.61955 5.353677,-4.96042 -0.8018,-3.34086 -3.786135,-15.85543 -16.689419,-18.91977 -3.29736,-0.78307 -5.483391,-1.09268 -6.895186,-1.0852 z m 13.978992,10.35854 a 3.685268,3.685268 0 0 1 3.685563,3.68505 3.685268,3.685268 0 0 1 -3.685563,3.68556 3.685268,3.685268 0 0 1 -3.685048,-3.68556 3.685268,3.685268 0 0 1 3.685048,-3.68505 z"
id="path882" />
<path
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 138.24445,32.953249 a 13.49711,13.49711 0 0 0 -13.49685,13.49685 13.49711,13.49711 0 0 0 0.0465,1.06195 l -25.043431,7.84087 c 1.104251,1.4075 2.257151,3.10658 3.611151,6.01255 l 23.55566,-7.5799 a 13.49711,13.49711 0 0 0 11.32696,6.1619 13.49711,13.49711 0 0 0 13.49737,-13.49737 13.49711,13.49711 0 0 0 -13.49737,-13.49685 z"
id="rect827" />
<path
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 97.624193,88.491979 c 0.07421,3.57897 -0.303877,5.04652 -0.587044,6.95772 l 27.570411,8.739001 a 13.49711,13.49711 0 0 0 -0.0134,0.37258 13.49711,13.49711 0 0 0 13.49737,13.49737 13.49711,13.49711 0 0 0 13.49685,-13.49737 13.49711,13.49711 0 0 0 -13.49685,-13.496851 13.49711,13.49711 0 0 0 -11.65201,6.71794 z"
id="rect827-1" />
<circle
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path888"
cx="93.031555"
cy="62.530666"
r="1.3701637" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

41
css/font-combodo/glyphs/F.svg Executable file
View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="1000"
height="1000"
viewBox="0 0 264.58333 264.58334"
version="1.1"
id="svg909">
<defs
id="defs903" />
<metadata
id="metadata906">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-32.41665)"
id="layer1">
<path
style="fill:#1b1b1b;fill-opacity:1;stroke:#000000;stroke-width:1.71210456;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 128.74663,48.188011 c -13.29429,0.07062 -10.55146,6.715944 -7.15766,12.848874 4.15012,7.499617 -25.880108,2.288099 -55.027316,30.705397 -15.81655,15.420458 -33.515303,31.940728 -36.659797,78.718798 -3.144494,46.77813 28.138482,113.85946 93.456613,115.09774 60.34513,1.14398 70.72277,-33.22425 76.02032,-56.79463 5.66401,-25.20081 -12.58357,-64.63485 -52.62298,-64.06284 -40.0394,0.57201 -53.195848,26.31042 -53.195848,43.47021 0,17.15974 9.723978,40.03957 37.179578,40.03957 27.4556,0 36.60716,-16.58704 38.32314,-30.88679 1.71598,-14.2998 -4.83096,-26.67818 -25.09606,-28.59968 -15.68521,-1.48726 -24.9258,9.87122 -25.23762,19.0178 -0.429,12.5838 9.39435,17.05222 14.15607,17.30356 4.76172,0.25125 11.84166,-0.0128 12.22509,-5.71993 0.42901,-6.38538 -4.00214,-7.00576 -7.21959,-6.93424 -2.28741,0.0509 -4.91368,-3.78538 -4.22028,-6.43661 0.52215,-1.99644 3.3621,-6.93557 11.94197,-3.50361 8.57988,3.43191 10.89319,13.43244 8.65069,20.01977 -2.28797,6.72091 -5.07712,14.08591 -22.37987,13.58542 -15.13797,-0.43787 -23.16423,-18.23237 -22.80675,-28.59972 0.40192,-11.65944 13.65537,-30.74341 35.96305,-29.59947 22.30767,1.14403 38.32269,21.7341 33.74675,44.61377 -4.57593,22.87966 -25.73805,37.75293 -50.33368,36.60894 -24.59564,-1.14398 -48.619888,-28.60023 -44.615958,-56.05584 4.00393,-27.45561 25.166518,-46.90263 54.910078,-48.04662 29.74356,-1.14398 50.87344,0.0685 68.0332,0.0685 17.15977,0 26.34709,-6.93209 22.91514,-21.23189 -3.43192,-14.29975 -16.20565,-67.865386 -71.43508,-80.981563 -14.11356,-3.351744 -23.47035,-4.676956 -29.5132,-4.644939 z m 59.83374,44.337259 a 15.77391,15.77391 0 0 1 15.77517,15.77298 15.77391,15.77391 0 0 1 -15.77517,15.77516 15.77391,15.77391 0 0 1 -15.77297,-15.77516 15.77391,15.77391 0 0 1 15.77297,-15.77298 z"
id="path882" />
<circle
style="opacity:1;fill:#101010;fill-opacity:1;stroke:#000000;stroke-width:1.71210456;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path888"
cx="194.04086"
cy="107.69173"
r="5.8646588" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

87
css/font-combodo/glyphs/O.svg Executable file
View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1000"
height="1000"
viewBox="0 0 264.58333 264.58334"
version="1.1"
id="svg876"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
sodipodi:docname="O.svg">
<defs
id="defs870" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="-272.85714"
inkscape:cy="560"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1005"
inkscape:window-x="1911"
inkscape:window-y="-9"
inkscape:window-maximized="1" />
<metadata
id="metadata873">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-32.41664)">
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8.99672318;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 131.53572,50.179132 c -54.174274,0 -100.702568,38.6765 -110.575582,91.938598 -9.873159,53.26282 19.698102,106.06258 70.277674,125.4761 2.319548,0.89024 4.921621,-0.26836 5.811987,-2.58786 L 122.30183,199.2106 c 0.89023,-2.31957 -0.26838,-4.92164 -2.58789,-5.812 -14.87072,-5.70891 -23.515673,-21.14784 -20.612431,-36.80689 2.903191,-15.6588 16.504681,-26.96534 32.434211,-26.96534 15.92952,0 29.53338,11.30654 32.43657,26.96534 2.90326,15.65905 -5.74171,31.09798 -20.61242,36.80689 -2.31951,0.89036 -3.47812,3.49245 -2.58788,5.812 l 25.25202,65.79537 c 0.89038,2.31821 3.49074,3.47652 5.8096,2.58786 50.57958,-19.41352 80.15083,-72.21328 70.27768,-125.4761 -9.87301,-53.262098 -56.4013,-91.938598 -110.57557,-91.938598 z"
id="path3773"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ssccscsssccccss" />
<g
style="display:inline"
id="g3797"
transform="matrix(1.2200654,0,0,1.2200654,455.86197,1099.5367)"
inkscape:export-filename="/home/rafael/workspace/logo-osi/3/png/logo396x412.png"
inkscape:export-xdpi="48.18"
inkscape:export-ydpi="48.18">
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.25831962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -183.5617,-698.57555 c -5.02556,0 -9.12392,4.09113 -9.12392,9.10782 0,5.01668 4.09836,9.10781 9.12392,9.10781 5.02556,0 9.12392,-4.09113 9.12392,-9.10781 0,-5.01669 -4.09836,-9.10782 -9.12392,-9.10782 z m 0,2.25596 c 3.80399,0 6.8633,3.05458 6.8633,6.85186 0,3.79727 -3.05931,6.85119 -6.8633,6.85119 -3.80399,0 -6.86397,-3.05392 -6.86397,-6.85119 0,-3.79728 3.05998,-6.85186 6.86397,-6.85186 z"
id="path3015"
inkscape:connector-curvature="0" />
<g
aria-label="R"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:OpenSymbol;-inkscape-font-specification:'OpenSymbol Bold';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
id="text3793">
<path
inkscape:connector-curvature="0"
d="m -183.7552,-690.46513 q 0.74769,0 1.06902,-0.27807 0.3275,-0.27807 0.3275,-0.91454 0,-0.63028 -0.3275,-0.90217 -0.32133,-0.27189 -1.06902,-0.27189 h -1.00105 v 2.36667 z m -1.00105,1.64369 v 3.4913 h -2.37902 v -9.22569 h 3.63342 q 1.82289,0 2.66946,0.61175 0.85274,0.61175 0.85274,1.93412 0,0.91454 -0.44491,1.50157 -0.43873,0.58703 -1.32855,0.8651 0.48817,0.11123 0.87128,0.50671 0.3893,0.38929 0.78477,1.18642 l 1.29147,2.62002 h -2.53351 l -1.12463,-2.29252 q -0.33986,-0.69208 -0.69208,-0.94543 -0.34604,-0.25335 -0.92689,-0.25335 z"
style="font-size:12.65519524px;line-height:1.25"
id="path863" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -18,6 +18,9 @@ aIcons = {
'combodo-icon': 'Combodo icon',
'combodo-icon-o': 'Combodo icon (outline)',
'itop-icon': 'iTop icon',
'itophub-icon': 'iTop Hub icon',
'chameleon-icon': 'Hub\'s Chameleon icon',
'opensource-icon': 'Open Source Logo',
}
function GenerateTable() {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,112 @@
/* cyrillic-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: local('Open Sans Regular'), local('OpenSans-Regular'), url('./OpenSans-Regular-cyrillic-ext.woff2') format('woff2');
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
}
/* cyrillic */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: local('Open Sans Regular'), local('OpenSans-Regular'), url('./OpenSans-Regular-cyrillic.woff2') format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: local('Open Sans Regular'), local('OpenSans-Regular'), url('./OpenSans-Regular-greek-ext.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: local('Open Sans Regular'), local('OpenSans-Regular'), url('./OpenSans-Regular-greek.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: local('Open Sans Regular'), local('OpenSans-Regular'), url('./OpenSans-Regular-vietnamese.woff2') format('woff2');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: local('Open Sans Regular'), local('OpenSans-Regular'), url('./OpenSans-Regular-latin-ext.woff2') format('woff2');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: local('Open Sans Regular'), local('OpenSans-Regular'), url('./OpenSans-Regular-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
/* cyrillic-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
src: local('Open Sans Bold'), local('OpenSans-Bold'), url('./OpenSans-Bold-cyrillic-ext.woff2') format('woff2');
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
}
/* cyrillic */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
src: local('Open Sans Bold'), local('OpenSans-Bold'), url('./OpenSans-Bold-cyrillic.woff2') format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
src: local('Open Sans Bold'), local('OpenSans-Bold'), url('./OpenSans-Bold-greek-ext.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
src: local('Open Sans Bold'), local('OpenSans-Bold'), url('./OpenSans-Bold-greek.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
src: local('Open Sans Bold'), local('OpenSans-Bold'), url('./OpenSans-Bold-vietnamese.woff2') format('woff2');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
src: local('Open Sans Bold'), local('OpenSans-Bold'), url('./OpenSans-Bold-latin-ext.woff2') format('woff2');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
src: local('Open Sans Bold'), local('OpenSans-Bold'), url('./OpenSans-Bold-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}

File diff suppressed because it is too large Load Diff

View File

@@ -35,6 +35,14 @@ body.printable-version {
.ui-layout-content {
padding-left: 10px;
}
.ui-layout-content .ui-tabs-nav li {
/* Overriding jQuery UI theme to see active tab better */
margin-bottom: 2px;
&.ui-tabs-active{
padding-bottom: 3px;
}
}
.raw_output {
font-family: Courier-New, Courier, Arial, Helvetica;
@@ -107,6 +115,12 @@ table.listResults td .view-image {
.view-image {
display: inline-block;
img[src=""],
img[src="null"] {
// Hiding "broken" image when src is not set
visibility: hidden;
}
&.dirty {
// The image will be modified when saving the changes
@@ -835,7 +849,7 @@ table.listResults tr.even td.truncated, .wizContainer table.listResults tr.even
}
/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */
table.listResults tr.even td.hover.truncated, , .wizContainer table.listResults tr.even td.hover.truncated {
table.listResults tr.even td.hover.truncated, .wizContainer table.listResults tr.even td.hover.truncated {
background: #fdf5d0 url(../images/truncated.png?v=#{$version}) bottom repeat-x;
}
@@ -1245,6 +1259,18 @@ span.form_validation {
margin-bottom: 0.25em;
}
/* Helper classes for object details display. */
.one-col-details {
min-width: 400px;
max-width: 600px;
}
.n-cols-details {
width: 100%;
> tbody > tr > td {
min-width: 240px;
}
}
.details {
border-collapse: collapse;
noborder-bottom: 2px #fff solid;
@@ -1269,18 +1295,23 @@ fieldset .details>.field_container {
margin-bottom: 0px;
}
&.field_large{
&.field_large {
display: inherit;
/* .field_label, .field_data */
> div {
display: inherit;
&.field_label{
&.field_label {
width: inherit;
margin-bottom: 5px;
}
&.field_data {
margin-top: 8px;
margin-bottom: 10px;
}
/* .field_value, .field_comments, .field_infos */
> div {
}
@@ -1496,6 +1527,13 @@ fieldset .details>.field_container {
}
}
}
.one-col-details .details .field_container.field_small {
div.field_label {
/* On a single column, field labels can take more width but they are limited so it doesn't feel weird when all labels are short */
width: 175px;
max-width: inherit;
}
}
/* This is extracted from the ".details > .field_container ..." because of the fullscreen option (element is moved at the end of the body */
.field_input_text{
border: none;
@@ -1563,6 +1601,10 @@ fieldset .details>.field_container {
vertical-align: middle;
}
.ac_input {
width: 100%;
}
.field_input_extkey{
display: table;
width: 100%;
@@ -1579,8 +1621,8 @@ fieldset .details>.field_container {
.field_input_btn{
display: table-cell;
width: 20px;
margin-left: 0.4em;
width: 25px;
padding-left: 0.4em;
}
}
}
@@ -1674,6 +1716,7 @@ td.layout_cell {
.dashlet-content {
background: #fff;
margin:0;
overflow: auto;
}
table.prop_table {
border-bottom: 2px solid #F9F9F1;

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//
@@ -21,13 +21,13 @@
* Authent External
* Module definition file for the "External Authentication" module
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-external/2.2.0',
'authent-external/2.4.0',
array(
// Identification
//
@@ -63,4 +63,3 @@ SetupWebPage::AddModule(
'settings' => array(),
)
);
?>

View File

@@ -9,7 +9,7 @@ if (function_exists('ldap_connect'))
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-ldap/2.2.0',
'authent-ldap/2.4.0',
array(
// Identification
//
@@ -63,5 +63,3 @@ SetupWebPage::AddModule(
);
} // if (function_exists('ldap_connect'))
?>

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-local/2.2.0',
'authent-local/2.4.0',
array(
// Identification
//
@@ -40,5 +40,3 @@ SetupWebPage::AddModule(
),
)
);
?>

View File

@@ -16,6 +16,7 @@
<module>itop-profiles-itil</module>
<module>itop-welcome-itil</module>
<module>itop-tickets</module>
<module>itop-hub-connector</module>
</modules>
<mandatory>true</mandatory>
</choice>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.4">
<classes>
<class id="Attachment" _delta="define">
<parent>DBObject</parent>

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2014 Combodo SARL
// Copyright (C) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//
@@ -39,4 +39,3 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Attachments:NoAttachment' => 'Kein Attachment. ',
'Attachments:PreviewNotAvailable' => 'Vorschau für diesen Attachment-Typ nicht verfügbar.',
));
?>

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2016 Combodo SARL
// Copyright (C) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//
@@ -19,7 +19,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-attachments/2.3.0',
'itop-attachments/2.4.0',
array(
// Identification
//
@@ -145,5 +145,3 @@ if (!class_exists('AttachmentInstaller'))
}
}
}
?>

View File

@@ -24,4 +24,4 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Attachment:Max_Ko' => '(Максимальный размер файла: %1$s кБ)',
'Attachments:NoAttachment' => 'Нет вложений.',
'Attachments:PreviewNotAvailable' => 'Предварительный просмотр не доступен для этого типа вложений.',
));
));

View File

@@ -61,6 +61,7 @@ try
catch (Exception $e)
{
$oPage->p('Error: '.$e->getMessage());
IssueLog::Error($sOperation.' - '.$e->getMessage());
}
}
$oPage->output();
@@ -156,6 +157,7 @@ EOF
{
$oRestoreMutex->Unlock();
$oPage->p('Error: '.$e->getMessage());
IssueLog::Error($sOperation.' - '.$e->getMessage());
}
}
$oPage->output();
@@ -179,6 +181,6 @@ EOF
}
catch (Exception $e)
{
IssueLog::Error($e->getMessage());
IssueLog::Error($sOperation.' - '.$e->getMessage());
}

View File

@@ -211,7 +211,7 @@ catch(Exception $e)
exit;
}
$sZipArchiveFile = MakeArchiveFileName();
$sZipArchiveFile = MakeArchiveFileName().'.tar.gz';
echo date('Y-m-d H:i:s')." - Checking file: $sZipArchiveFile\n";
if (file_exists($sZipArchiveFile))
@@ -223,7 +223,7 @@ if (file_exists($sZipArchiveFile))
if ($iSize > $iMIN)
{
echo "Found the archive\n";
$sOldArchiveFile = MakeArchiveFileName(time() - 86400); // yesterday's archive
$sOldArchiveFile = MakeArchiveFileName(time() - 86400).'.tar.gz'; // yesterday's archive
if (file_exists($sOldArchiveFile))
{
if ($aOldStat = stat($sOldArchiveFile))

View File

@@ -99,8 +99,10 @@ class DBRestore extends DBBackup
}
/**
* @param $sFile A file with the extension .zip or .tar.gz
* @param string $sFile A file with the extension .zip or .tar.gz
* @param string $sEnvironment Target environment
*
* @throws \Exception
*/
public function RestoreFromCompressedBackup($sFile, $sEnvironment = 'production')
{
@@ -111,7 +113,7 @@ class DBRestore extends DBBackup
{
$this->LogInfo('zip file detected');
$oArchive = new ZipArchiveEx();
$res = $oArchive->open($sFile);
$oArchive->open($sFile);
}
elseif (substr($sNormalizedFile, -7) == '.tar.gz')
{
@@ -125,21 +127,22 @@ class DBRestore extends DBBackup
// Load the database
//
$sDataDir = tempnam(SetupUtils::GetTmpDir(), 'itop-');
unlink($sDataDir); // I need a directory, not a file...
$sDataDir = APPROOT.'data/tmp-backup-'.rand(10000, getrandmax());
SetupUtils::builddir($sDataDir); // Here is the directory
$oArchive->extractFileTo($sDataDir, 'itop-dump.sql');
$oArchive->extractTo($sDataDir);
$sDataFile = $sDataDir.'/itop-dump.sql';
$this->LoadDatabase($sDataFile);
unlink($sDataFile);
// Update the code
//
$sDeltaFile = APPROOT.'data/'.$sEnvironment.'.delta.xml';
if ($oArchive->hasFile('delta.xml') !== false)
if (is_file($sDataDir.'/delta.xml'))
{
// Extract and rename delta.xml => <env>.delta.xml;
file_put_contents($sDeltaFile, $oArchive->getFromName('delta.xml'));
rename($sDataDir.'/delta.xml', $sDeltaFile);
}
else
{
@@ -149,16 +152,18 @@ class DBRestore extends DBBackup
{
SetupUtils::rrmdir(APPROOT.'data/production-modules/');
}
if ($oArchive->hasDir('production-modules/') !== false)
if (is_dir($sDataDir.'/production-modules'))
{
$oArchive->extractDirTo(APPROOT.'data/', 'production-modules/');
rename($sDataDir.'/production-modules', APPROOT.'data/production-modules/');
}
$sConfigFile = APPROOT.'conf/'.$sEnvironment.'/config-itop.php';
@chmod($sConfigFile, 0770); // Allow overwriting the file
$oArchive->extractFileTo(APPROOT.'conf/'.$sEnvironment, 'config-itop.php');
rename($sDataDir.'/config-itop.php', $sConfigFile);
@chmod($sConfigFile, 0444); // Read-only
SetupUtils::rrmdir($sDataDir);
$oEnvironment = new RunTimeEnvironment($sEnvironment);
$oEnvironment->CompileFrom($sEnvironment);
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2014 Combodo SARL
// Copyright (C) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//

View File

@@ -291,4 +291,3 @@ class ItopBackup extends ModuleHandlerAPI
}
}
}

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-backup/2.2.0',
'itop-backup/2.4.0',
array(
// Identification
//
@@ -56,5 +56,3 @@ SetupWebPage::AddModule(
),
)
);
?>

View File

@@ -47,4 +47,3 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'bkp-wait-restore' => 'Пожалуйста, дождитесь завершения восстановления...',
'bkp-success-restore' => 'Восстановление успешно завершено.',
));
?>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.4">
<classes>
<class id="lnkVirtualDeviceToVolume" _delta="define">
<parent>cmdbAbstractObject</parent>

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-virtualization-storage/2.3.0',
'itop-bridge-virtualization-storage/2.4.0',
array(
// Identification
//
@@ -43,6 +43,3 @@ SetupWebPage::AddModule(
),
)
);
?>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.4">
<classes>
<class id="Change" _delta="define">
<parent>Ticket</parent>
@@ -7,7 +7,7 @@
<comment><![CDATA[/**
* Persistent classes for a CMDB
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/]]></comment>
<category>bizmodel,searchable,changemgmt</category>

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2014 Combodo SARL
// Copyright (C) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-change-mgmt-itil/2.3.0',
'itop-change-mgmt-itil/2.4.0',
array(
// Identification
//
@@ -42,5 +42,3 @@ SetupWebPage::AddModule(
),
)
);
?>

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2014 Combodo SARL
// Copyright (C) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-change-mgmt/2.3.0',
'itop-change-mgmt/2.4.0',
array(
// Identification
//
@@ -91,5 +91,3 @@ class ChangeManagementInstaller extends ModuleInstallerAPI
}
}
}
?>

File diff suppressed because one or more lines are too long

View File

@@ -1663,6 +1663,8 @@
<attribute id="name"/>
<attribute id="org_id"/>
<attribute id="organization_name"/>
<attribute id="location_id"/>
<attribute id="location_name"/>
<attribute id="finalclass"/>
</attributes>
</reconciliation>

View File

@@ -395,8 +395,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:Group/Attribute:status+' => '',
'Class:Group/Attribute:status/Value:implementation' => 'Implementation',
'Class:Group/Attribute:status/Value:implementation+' => 'Implementation',
'Class:Group/Attribute:status/Value:obsolete' => 'Veraltet',
'Class:Group/Attribute:status/Value:obsolete+' => 'Veraltet',
'Class:Group/Attribute:status/Value:obsolete' => 'Obsolet (Veraltet)',
'Class:Group/Attribute:status/Value:obsolete+' => 'Obsolet (Veraltet)',
'Class:Group/Attribute:status/Value:production' => 'Produktion',
'Class:Group/Attribute:status/Value:production+' => 'Produktion',
'Class:Group/Attribute:org_id' => 'Organisation',
@@ -489,7 +489,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:Document/Attribute:status+' => '',
'Class:Document/Attribute:status/Value:draft' => 'Entwurf',
'Class:Document/Attribute:status/Value:draft+' => '',
'Class:Document/Attribute:status/Value:obsolete' => 'Veraltet',
'Class:Document/Attribute:status/Value:obsolete' => 'Obsolet (Veraltet)',
'Class:Document/Attribute:status/Value:obsolete+' => '',
'Class:Document/Attribute:status/Value:published' => 'Veröffentlicht',
'Class:Document/Attribute:status/Value:published+' => '',
@@ -1184,4 +1184,4 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:Subnet/Tab:FreeIPs-count' => 'Freie IPs: %1$s',
'Class:Subnet/Tab:FreeIPs-explain' => 'Hier ist eine Aufstellung von 10 freien IP Adressen',
));
?>
?>

View File

@@ -32,7 +32,10 @@
Dict::Add('EN US', 'English', 'English', array(
'Relation:impacts/Description' => 'Elements impacted by',
'Relation:impacts/DownStream' => 'Impacts...',
'Relation:impacts/DownStream+' => 'Elements impacted by',
'Relation:impacts/UpStream' => 'Depends on......',
'Relation:impacts/UpStream+' => 'Elements impacting',
// Legacy entries
'Relation:depends on/Description' => 'Elements impacting',
'Relation:depends on/DownStream' => 'Depends on...',
'Relation:depends on/UpStream' => 'Impacts...',

View File

@@ -33,7 +33,10 @@
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Relation:impacts/Description' => 'Elementos Impactados por',
'Relation:impacts/DownStream' => 'Impacto...',
'Relation:impacts/DownStream+' => 'Elementos Impactados por',
'Relation:impacts/UpStream' => 'Depende de...',
'Relation:impacts/UpStream+' => 'Elementos de los cuales depende',
// Legacy entries
'Relation:depends on/Description' => 'Elementos de los cuales depende',
'Relation:depends on/DownStream' => 'Depende de...',
'Relation:depends on/UpStream' => 'Impactos...',

View File

@@ -685,7 +685,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:SoftwareInstance/Attribute:softwarelicence_id+' => '',
'Class:SoftwareInstance/Attribute:softwarelicence_name' => 'Nom Licence logiciel',
'Class:SoftwareInstance/Attribute:softwarelicence_name+' => '',
'Class:SoftwareInstance/Attribute:path' => 'Chemin d`installation',
'Class:SoftwareInstance/Attribute:path' => 'Chemin d\'installation',
'Class:SoftwareInstance/Attribute:path+' => '',
'Class:SoftwareInstance/Attribute:status' => 'Statut',
'Class:SoftwareInstance/Attribute:status+' => '',
@@ -1836,7 +1836,10 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Menu:UI_WelcomeMenu_AllConfigItems' => 'Résumé',
'Relation:impacts/Description' => 'Eléments impactés par',
'Relation:impacts/DownStream' => 'Impacte...',
'Relation:impacts/DownStream+' => 'Eléments impactés par',
'Relation:impacts/UpStream' => 'Dépend de...',
'Relation:impacts/UpStream+' => 'Eléments dont dépend',
// Legacy entries
'Relation:depends on/Description' => 'Eléments dont dépend',
'Relation:depends on/DownStream' => 'Dépend de...',
'Relation:depends on/UpStream' => 'Impacte...',

View File

@@ -384,7 +384,10 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'Class:lnkProcessToSolution/Attribute:reason+' => '',
'Relation:impacts/Description' => 'Konfigurációs elem működését befolyásolják',
'Relation:impacts/DownStream' => 'Hatás',
'Relation:impacts/DownStream+' => 'Konfigurációs elem működését befolyásolják',
'Relation:impacts/UpStream' => 'Függőségek',
'Relation:impacts/UpStream+' => 'Konfigurációs elemtől függnek',
// Legacy entries
'Relation:depends on/Description' => 'Konfigurációs elemtől függnek',
'Relation:depends on/DownStream' => 'Függőségek',
'Relation:depends on/UpStream' => 'Hatások',

View File

@@ -384,7 +384,10 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:lnkProcessToSolution/Attribute:reason+' => 'Più informazioni tra il processo di business e la soluzione applicativa',
'Relation:impacts/Description' => 'Elementi impattati da...',
'Relation:impacts/DownStream' => 'Impatto...',
'Relation:impacts/DownStream+' => 'Elementi impattati da...',
'Relation:impacts/UpStream' => 'Dipende da...',
'Relation:impacts/UpStream+' => 'Elementi di questo elemento dipende da',
// Legacy entries
'Relation:depends on/Description' => 'Elementi di questo elemento dipende da',
'Relation:depends on/DownStream' => 'Dipende da...',
'Relation:depends on/UpStream' => 'Impatto...',

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