Compare commits

..

665 Commits

Author SHA1 Message Date
Bruno Da Silva
e508f645f3 advanced search: ShorthandExpansion TODOs list
SVN:b1312[5765]
2018-05-02 12:19:38 +00:00
Bruno Da Silva
81f7f9d70b advanced search: ShorthandExpansion tests
- tests (rely on generated SQL/OQL strings)
- removal of a BC break (added an optional parameter $bAllowExternalFields to FieldOqlExpression::RefreshAlias() )

SVN:b1312[5764]
2018-05-02 08:51:36 +00:00
Bruno Da Silva
59c9a98b87 advanced search: ShorthandExpansion on conversion To SQL
- plus tests
- plus implementation of ExternalFieldExpression->Render()

SVN:b1312[5760]
2018-04-27 16:15:08 +00:00
Eric Espié
d57fb3e24e Parser for Short Hand Syntax: unit tests
SVN:b1312[5753]
2018-04-27 13:26:56 +00:00
Bruno Da Silva
88ae239a56 advanced search: ExternalFieldOqlExpression Check method
- handling of special case for the attribute "id"
- clearer error message
- typo

SVN:b1312[5749]
2018-04-26 15:54:34 +00:00
Eric Espié
30494756fc Parser for Short Hand Syntax
SVN:b1312[5748]
2018-04-26 15:26:41 +00:00
Eric Espié
1900546440 Parser for Short Hand Syntax (->) Check method
SVN:b1312[5747]
2018-04-26 14:59:30 +00:00
Bruno Da Silva
d8dd0437b3 advanced search: ExternalFieldOqlExpression basic constructor
SVN:b1312[5746]
2018-04-26 13:38:19 +00:00
Eric Espié
d1d50e0448 Parser for Short Hand Syntax (->)
SVN:b1312[5745]
2018-04-26 13:12:42 +00:00
Eric Espié
1c87c15222 SVN:b1312[5740] 2018-04-26 08:56:13 +00:00
Guillaume Lajarige
95e56e7148 Portal: Fix regression introduced in revision 5698.
SVN:trunk[5739]
2018-04-25 09:07:28 +00:00
Vincent Dumas
ec471520f2 Datamodel: Person phones now supports click to call.
SVN:trunk[5738]
2018-04-24 12:48:12 +00:00
Stephen Abello
65b516d761 N°880 : Added a conf param (inline_image_garbage_collector_interval)
SVN:trunk[5737]
2018-04-24 12:36:03 +00:00
Denis Flaven
7d45d5e0ce Cosmetics on the TLS option in the setup (prevent flashing of the hidden content)
SVN:trunk[5736]
2018-04-24 12:35:13 +00:00
Guillaume Lajarige
d8e3966825 Portal: Small CSS enhancements on ManageBrick.
SVN:trunk[5735]
2018-04-24 12:25:54 +00:00
Denis Flaven
65409373eb Enhancement: automatic re-ordering of the background tasks at each execution of cron.php so that one single task cannot use all the CPU time.
SVN:trunk[5734]
2018-04-24 12:12:36 +00:00
Denis Flaven
50a1449a2a Bug fix: background tasks should not echo anything, but unless return the string to output.
SVN:trunk[5733]
2018-04-24 12:08:36 +00:00
Stephen Abello
8150faaa40 N°1226 : On globalsearching, the searched text is now placed as input value instead of placeholder value
SVN:trunk[5732]
2018-04-24 12:06:04 +00:00
Eric Espié
019542ff10 iTop 2.5.0 beta
SVN:trunk[5731]
2018-04-24 11:58:50 +00:00
Bruno Da Silva
20def0de02 bugfix: advanced search
- dates has to handle two format : the user's current and the system storage yyy-mm-dd. the system is now only handled on load ans=d converted to the user's one.
- if the date is not parsable, the fallback is now the current date.
- removal of a no more needed date setter (leaved in the comments since it may be nescessary to re-add this if the UI changes again and if the less panel has to display the dates without time 
- factorisatoin of the date/time formating into a function

SVN:trunk[5730]
2018-04-24 07:25:26 +00:00
Eric Espié
4a30a875f0 iTop 2.5.0 beta
SVN:trunk[5729]
2018-04-23 15:48:24 +00:00
Eric Espié
61905f111b iTop 2.5.0 beta
SVN:trunk[5728]
2018-04-23 15:27:10 +00:00
Eric Espié
1832d72e51 Version 1.5 of XML datamodel
SVN:trunk[5727]
2018-04-23 15:14:11 +00:00
Eric Espié
4d63b9e463 Fix initial setup error (Notice: Undefined index: CharMaxLength)
SVN:trunk[5726]
2018-04-23 14:59:31 +00:00
Pierre Goiffon
7b54f51d75 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

SVN:trunk[5724]
2018-04-23 14:48:40 +00:00
Pierre Goiffon
eaf94bc10a Audit : some more PHPDoc
SVN:trunk[5723]
2018-04-23 14:46:37 +00:00
Stephen Abello
9d6b5d347c Datamodel viewer: autocomplete validation goes to selected class, autofocus on autocomplete field, delete input text button, add_init_script for itopwebpage class, fixed cases where default value and default null value were array instead of strings, visual tweaks
SVN:trunk[5722]
2018-04-23 14:25:28 +00:00
Bruno Da Silva
e82a16146e typo
SVN:trunk[5721]
2018-04-23 13:09:44 +00:00
Bruno Da Silva
9797021511 bugfix: advanced search
- dates has to handle two format : the user's current and the system storage yyy-mm-dd. the system is now only handled on load ans=d converted to the user's one.
- if the date is not parsable, the fallback is now the current date.
- removal of a no more needed date setter (leaved in the comments since it may be nescessary to re-add this if the UI changes again and if the less panel has to display the dates without time 
- factorisatoin of the date/time formating into a function

SVN:trunk[5720]
2018-04-23 12:20:48 +00:00
Vincent Dumas
c27a9e8193 Fix email typo in English dictionnary
SVN:trunk[5719]
2018-04-23 10:09:52 +00:00
Eric Espié
c5506dab5d Internal version => 2.5.0 (beta)
SVN:trunk[5718]
2018-04-23 07:37:25 +00:00
Eric Espié
d120109a78 Setup: Display the XML errors on the screen (cleanup deprecated functions)
SVN:trunk[5717]
2018-04-20 16:02:41 +00:00
Eric Espié
b529f3d4cc Setup: Display the XML errors on the screen
SVN:trunk[5716]
2018-04-20 15:56:53 +00:00
Vincent Dumas
ea11b76461 Fix typo in dictionnary
SVN:trunk[5715]
2018-04-20 15:12:42 +00:00
Vincent Dumas
09c54d4fed Fix DataSynchro Group to allow management of DataSynchros through WebServices for non admin users
SVN:trunk[5714]
2018-04-20 15:10:33 +00:00
Bruno Da Silva
c5f00c5363 bugfix: advanced search
- dates "<=" operator handling

SVN:trunk[5713]
2018-04-20 15:06:28 +00:00
Eric Espié
256808c473 Advanced Search: Date transform from < and > to <= and >= for the search
SVN:trunk[5712]
2018-04-20 14:54:32 +00:00
Bruno Da Silva
487d89d970 bugfix: advanced search
- datepicker not displayed in dialogs : because the datepicker had a lower z-index. It is now forced using the `beforeShow` datepicker's option so we do not force it globaly has if we had done this using the css

SVN:trunk[5711]
2018-04-20 14:50:48 +00:00
Bruno Da Silva
ed3665b8c5 bugfix: advanced search
- date i18n : non standard date formating was totally wrong, full rewrite of date parsing (in getter and setter) relying on datepicker and datetimepicker parsers (they are awfull)
- date "now" button do no more close the criteria
- ie9 compat. : use history.replaceState only if available in order to prevent bugs with ie9

SVN:trunk[5710]
2018-04-20 14:32:32 +00:00
Bruno Da Silva
ef7a9ff02e bugfix: query serialization edge case
it did break when a "+" was present in the url, a rawurlencode was added, it is backward compatible because for pre-existing string, there is no % present so the unserialization's rawurldecode is without BC effect.

SVN:trunk[5709]
2018-04-20 13:54:53 +00:00
Eric Espié
aa4416ac4e Advanced Search: Display links also when the object is not visible
SVN:trunk[5708]
2018-04-20 13:45:02 +00:00
Stephen Abello
c734eec9e1 N°729 Form prefill : Minor fix in variables naming
SVN:trunk[5707]
2018-04-20 13:42:12 +00:00
Eric Espié
e1caf61a18 N°1248 - Fix API access (back to the same behavior as 2.4.1)
SVN:trunk[5706]
2018-04-20 12:32:01 +00:00
Eric Espié
f7879256c1 N°1248 - Fix API access (back to the same behavior as 2.4.1)
SVN:trunk[5705]
2018-04-20 12:30:20 +00:00
Eric Espié
1c86eba9d9 N° 1001 - utf8-mb4 removed innodb_large_prefix requirement
SVN:trunk[5704]
2018-04-20 12:14:32 +00:00
Stephen Abello
6a089aa1b7 N°729 Form prefill : Included Contract case in the datamodel.
SVN:trunk[5703]
2018-04-20 10:07:43 +00:00
Guillaume Lajarige
9a3749c1ed Advanced search: Fix IE9 bug when trying to add a criteria.
SVN:trunk[5702]
2018-04-20 09:50:13 +00:00
Eric Espié
d82b755557 N° 1001 - Database index size changed to support utf8-mb4
SVN:trunk[5701]
2018-04-20 09:44:12 +00:00
Eric Espié
5b83f2a554 Fix setup (support for distrib > 9.x.x)
SVN:trunk[5700]
2018-04-20 09:40:27 +00:00
Guillaume Lajarige
1f2b01937b Advanced search: Code cleanup.
SVN:trunk[5699]
2018-04-20 09:01:48 +00:00
Guillaume Lajarige
18bd0ae096 N°1405 Add support of AttributePhoneNumber which allows launch of phone application on click.
SVN:trunk[5698]
2018-04-19 15:56:11 +00:00
Denis Flaven
512f368cbf (regression) Do not block the execution of the page (based on the access rights on the menu) since the page is used for all exports. The export will be blocked anyway if the user does not have the BULK_READ rights on the target class.
SVN:trunk[5697]
2018-04-19 13:43:53 +00:00
Guillaume Lajarige
853d9ee87f Advanced search: Update french translations.
SVN:trunk[5696]
2018-04-19 07:43:28 +00:00
Guillaume Lajarige
e9440d0d4c Advanced search: Fix criteria closed after search modal.
SVN:trunk[5695]
2018-04-19 07:37:11 +00:00
Bruno Da Silva
381a988f43 bugfix: iTop Hub compatibility repaired
the new abstract classes used by admin menu management broke iTop hub installer

SVN:trunk[5694]
2018-04-18 15:27:22 +00:00
Pierre Goiffon
5d6ec4ce56 N°1370 ManageBrick charts tile : integrate CSS in portal.scss
SVN:trunk[5693]
2018-04-18 15:16:04 +00:00
Eric Espié
182e644a33 Fix setup
SVN:trunk[5692]
2018-04-18 14:30:33 +00:00
Denis Flaven
c719fbf7fc Bug fix (regression): use a different endpoint (ajax.document.render.php) for the output of the JS dictionary since we use the JS dictionary also when there is no user logged in (like in the login page).
SVN:trunk[5691]
2018-04-18 13:54:24 +00:00
Denis Flaven
9c3b053727 (Enhancement for developers) Use a timestamp defined at compile time to workaround client-side caching problems during development.
SVN:trunk[5690]
2018-04-18 13:45:08 +00:00
Eric Espié
d32db977eb Fix unnecessary warning about not empty directory
SVN:trunk[5689]
2018-04-18 13:44:45 +00:00
Denis Flaven
1eca74180c Let us install using a non-secure connexion !
SVN:trunk[5688]
2018-04-18 13:01:19 +00:00
Guillaume Lajarige
0210e090f2 Advanced search: Client side handles hierarchical keys correctly.
SVN:trunk[5687]
2018-04-18 08:34:45 +00:00
Stephen Abello
baf413ee55 N°729 Form prefill : switched argument type for Designer to reference
SVN:trunk[5686]
2018-04-18 07:43:04 +00:00
Eric Espié
8e6c001bf3 Advanced Search: External keys hierarchical type
SVN:trunk[5685]
2018-04-18 07:42:30 +00:00
Guillaume Lajarige
127e940ed4 Advanced search: Fix auto-submit option ignored when removing a criteria.
SVN:trunk[5684]
2018-04-18 07:29:12 +00:00
Pierre Goiffon
aa8072118d N°1260 remove db_tls.verify_server_cert : the server cert verification is now based on the TLS CA parameter value
SVN:trunk[5683]
2018-04-18 07:26:11 +00:00
Pierre Goiffon
f07bbfa174 N°1260 MySQL TLS connection : change parameters to only enable checkbox + CA (remove client key, client cert, cappath, cipher)
SVN:trunk[5682]
2018-04-18 06:57:38 +00:00
Eric Espié
e3a2c5b05b Advanced Search: External keys default criteria are read-only and organizations are not hierarchical
SVN:trunk[5681]
2018-04-17 14:15:38 +00:00
Stephen Abello
3ba5e30a96 datamodel viewer : update compiled scss
SVN:trunk[5680]
2018-04-17 14:05:26 +00:00
Eric Espié
7bf49011a3 Advanced Search: n:n links default criteria are not read-only and organizations are hierarchical
SVN:trunk[5679]
2018-04-17 13:38:59 +00:00
Stephen Abello
bd84dd9f2c datamodel viewer : fixed related class display, displaying linkset on related class graph, open/close all items on lifecycle and visual fixes
SVN:trunk[5678]
2018-04-17 12:13:39 +00:00
Eric Espié
c3fbdc907c N°1248 - User Management: Check organization related to the current user
SVN:trunk[5677]
2018-04-17 10:22:12 +00:00
Pierre Goiffon
f7817714a8 N°1001 change constant with concatenation to attribute (to avoid crash in setup for older PHP versions)
SVN:trunk[5676]
2018-04-17 08:14:45 +00:00
Eric Espié
c485286114 Advanced Search: External keys to hierarchical class selects sub-classes as in previous version
SVN:trunk[5675]
2018-04-16 16:35:20 +00:00
Guillaume Lajarige
bd8e44f835 Advanced search: Fixes for autocomplete on external keys.
SVN:trunk[5674]
2018-04-16 15:44:39 +00:00
Guillaume Lajarige
eddf4226b7 Advanced search: Fixs for autocomplete on external keys.
SVN:trunk[5673]
2018-04-16 10:07:19 +00:00
Pierre Goiffon
804578e38d N°1370 fix ManageBrick details export
SVN:trunk[5672]
2018-04-16 07:05:34 +00:00
Bruno Da Silva
d464fe5d67 bugfix: when canceling a modification of an object, the JS displayed two alerts.
SVN:trunk[5671]
2018-04-13 16:05:42 +00:00
Pierre Goiffon
885428627f N°1370 fix Portal ManageBrick regression : can search again in table details view
SVN:trunk[5670]
2018-04-13 16:02:00 +00:00
Bruno Da Silva
4c90a90131 advanced search: bugfix
- adapt the js to IE needs (do not reduce the search bar when the user click on a select's option)

SVN:trunk[5669]
2018-04-13 15:56:08 +00:00
Eric Espié
ec2aadb7cf Advanced Search: Fix ExternalFields allowed values
SVN:trunk[5668]
2018-04-13 15:27:00 +00:00
Pierre Goiffon
9d5ab75dbd Backup/restore : apply COmbodo formatting
SVN:trunk[5667]
2018-04-13 14:49:27 +00:00
Eric Espié
d5b145e052 Advanced Search: Fix ExternalFields allowed values
SVN:trunk[5666]
2018-04-13 14:43:11 +00:00
Stephen Abello
163f5dba8a N°729 Form prefill : Minor fix for prefillSeachForm
SVN:trunk[5665]
2018-04-13 14:17:03 +00:00
Eric Espié
e026ecf92f N°1161 - Fix Dashlet Group By edition of multiple dashlets
SVN:trunk[5664]
2018-04-13 13:58:12 +00:00
Bruno Da Silva
dcda5084d0 advanced search: bugfix
- adapt the css to IE needs
- a translation key had been renamed without renaming all the usages in the code

SVN:trunk[5663]
2018-04-13 13:42:19 +00:00
Guillaume Lajarige
496441cae6 Advanced search: Search button icon switch between "refresh" and "search" depending on the auto-submit state.
SVN:trunk[5662]
2018-04-13 12:27:32 +00:00
Guillaume Lajarige
cf75937b1d Advanced search: Fix multiple undefined values bug on enum criteria.
SVN:trunk[5661]
2018-04-13 12:26:43 +00:00
Pierre Goiffon
d7fc003216 backup.php : some little PHPDoc
SVN:trunk[5660]
2018-04-13 10:09:31 +00:00
Pierre Goiffon
de54575e04 N°1260 fix DB restore regression
* add comments to explain use of the token file
* only pass current env to the ajax call (it is enough to load the corresponding config file and get everything we need !)
* DBRestore : initialize user & pwd as needed
* DBRestore : do not throw Exception anymore but only BackupException

SVN:trunk[5659]
2018-04-13 09:43:03 +00:00
Bruno Da Silva
12093c311c advanced search: bugfix
- the modal window did update the history which resulted in several border effect like having request string too long and crashing on several pages

SVN:trunk[5658]
2018-04-13 08:58:32 +00:00
Eric Espié
5b9ca03fa6 Advanced Search: Fix missing OQL for FunctionExpression
SVN:trunk[5657]
2018-04-13 08:57:57 +00:00
Bruno Da Silva
bb820ab388 advanced search: bugfix
- the modal window did update the history which resulted in several border effect like having request string too long and crashing on several pages

SVN:trunk[5656]
2018-04-13 08:57:36 +00:00
Eric Espié
c68f56ecd4 Advanced Search: Enhance Date conversion
SVN:trunk[5655]
2018-04-13 08:47:29 +00:00
Eric Espié
910bae64e9 Advanced Search: Code cleanup
SVN:trunk[5654]
2018-04-13 08:11:21 +00:00
Bruno Da Silva
40258fb02a advanced search: bugfix
- the modal window did update the history wich resulted in several border effect like having request string too long and crashing on several pages

SVN:trunk[5653]
2018-04-13 08:11:03 +00:00
Guillaume Lajarige
011ed65ea1 Advanced search: WIP auto submit.
SVN:trunk[5652]
2018-04-13 07:39:27 +00:00
Bruno Da Silva
411d934e6a advanced search: if the field has an index and the equals operator is available : force the default to the equals operator
SVN:trunk[5651]
2018-04-12 15:49:15 +00:00
Vincent Dumas
582de40960 Display the search criterion when displaying the content of a shortcut.
SVN:trunk[5650]
2018-04-12 15:40:46 +00:00
Vincent Dumas
79d7ac7c8e Typos in German dictionaries
SVN:trunk[5649]
2018-04-12 15:39:45 +00:00
Vincent Dumas
6d86bd516b Set default search criteria for objects + index on ticket's ref.
SVN:trunk[5648]
2018-04-12 15:38:18 +00:00
Bruno Da Silva
f71bf1416c advanced search: add the refresh button on objects list menu
SVN:trunk[5647]
2018-04-12 15:29:42 +00:00
Guillaume Lajarige
8a1a27ee19 Advanced search: Fix title highlighting on enum widget.
SVN:trunk[5646]
2018-04-12 15:22:46 +00:00
Guillaume Lajarige
dc30cb2e4a Advanced search: Merge due to recent sourceforge crash.
SVN:trunk[5645]
2018-04-12 14:54:11 +00:00
Pierre Goiffon
24e669c65b N°1370 Portal AggregatePageBrick : integrate dashboard brick extension
SVN:trunk[5644]
2018-04-12 14:28:41 +00:00
Pierre Goiffon
80e6ba2d96 N°1370 Portal ManageBrick : add "display_modes" XML node to set the display modes in brick tile and details views
SVN:trunk[5643]
2018-04-12 14:13:28 +00:00
Pierre Goiffon
beef4b2738 add .idea in gitignore
SVN:trunk[5642]
2018-04-12 14:12:52 +00:00
Eric Espié
56f1369000 Advanced Search: Fix Date conversion
SVN:trunk[5641]
2018-04-12 13:19:11 +00:00
Eric Espié
7bcde47081 N°1161 - Fix Dashlet Group By edition
SVN:trunk[5640]
2018-04-12 13:07:00 +00:00
Eric Espié
35663281fa Advanced Search: Fix Date conversion
SVN:trunk[5639]
2018-04-12 12:32:09 +00:00
Bruno Da Silva
0b8f75f799 advanced search - merged commit (since sourceforge has lost our commit, this is a manual merge all all losts)
SVN:trunk[5638]
2018-04-12 12:29:32 +00:00
Bruno Da Silva
7309c046ae remove .idea dir
SVN:trunk[5637]
2018-04-12 10:38:13 +00:00
Eric Espié
6dfd44b731 Advanced Search: Small bug fixes and enhancements
SVN:trunk[5636]
2018-04-12 09:51:32 +00:00
Pierre Goiffon
d6e7309c34 N°1370 portal : add charts capacity to the ManageBrick (restore 2018-04-10 revisions : r5646)
SVN:trunk[5635]
2018-04-12 08:55:16 +00:00
Pierre Goiffon
e15bad7d3b Advanced search improvements (restore 2018-04-10 revisions : r5643..r5645)
* Support for empty dates
* UNION OQLs don't crash the search
* Better support for 'not empty' searches

SVN:trunk[5634]
2018-04-12 08:54:36 +00:00
Pierre Goiffon
4450d6af2f HTMLSanitizer : add wiki ref to white lists and split declarations one per line (to ease SCM annotation) (restore 2018-04-10 revisions : r5642)
SVN:trunk[5633]
2018-04-12 08:54:21 +00:00
Pierre Goiffon
efa7a4ee55 Advanced search improvements (restore 2018-04-10 revisions : r5635..r5641)
* Add 'search_manual_submit' config parameter to manage auto-submit
* bugfixes
** date i18n is now handled (using two new options `datepicker.dateFormat` and `datepicker.timeFormat` computed from `AttributeDateTime::GetFormat()->ToDatePicker()`
** handling of `empty` `not empty` operator titles
*** it led to tohers bugfixes and a redesign of the `_computeTitle` (from overwriting to extension using ie `_computeBetweenDaysOperatorTitle`
*** some bug still remain, because autocomplete needs to been finished before checking on them
* Promote 'friendlyname' in the 'most popular' list
* bugfixes
** filters (criterions, enum and FK without autocomplete) now ignore accent on matching with user input
** this is done by using a pre-existing tool used only by the portal, so it was moved to the core (latinize.min.js)
* Integration with manual submit parameter on client side.
* bugfixes : history/breadcrumb had several c[menu] appended (one for each refresh)
* Fix various sanity bugs

SVN:trunk[5632]
2018-04-12 08:54:05 +00:00
Pierre Goiffon
757130847f Fix: disable the connection to iTopHub when running in demo mode (restore 2018-04-10 revisions : r5634)
SVN:trunk[5631]
2018-04-12 08:53:20 +00:00
Pierre Goiffon
c562098ef7 N°993: restrict the access to the REST/JSON web services to users having the profile "REST Services User" (restore 2018-04-10 revisions : r5632..r5633)
SVN:trunk[5630]
2018-04-12 08:53:02 +00:00
Pierre Goiffon
42606873af Advanced search improvements (restore 2018-04-10 revisions : r5629..r5631)
* Add support for default search criteria
* replace friendlyname by the class name for consistency
* WIP Client

SVN:trunk[5629]
2018-04-12 08:52:33 +00:00
Bruno Da Silva
df8b73999f advanced search: update history and breadcrumb on search
SVN:trunk[5628]
2018-04-09 14:25:41 +00:00
Eric Espié
234b0e6825 Advanced search: Sort Id with the other fields in "more criteria"
SVN:trunk[5627]
2018-04-09 11:51:29 +00:00
Bruno Da Silva
6ca9f8ad31 advanced search: removal of legacy_search_drawer_open
associated to this change, those wiki pages are altered : 
 - latest:admin:itop_configuration_file (`legacy_search_drawer_open` removal)
 - latest:customization:xml_reference (`search_form_open` default value changed)

SVN:trunk[5626]
2018-04-06 12:06:20 +00:00
Guillaume Lajarige
dbc0971b99 Advanced search: Widget refactoring to use _computeTitle method.
SVN:trunk[5625]
2018-04-06 10:06:03 +00:00
Eric Espié
26127c8218 N°1161 - Dashlet Group By traduction
SVN:trunk[5624]
2018-04-06 09:00:15 +00:00
Eric Espié
f4b8b4cae3 N°1161 - Dashlet Group By supports sum, average, min and max.
SVN:trunk[5623]
2018-04-06 08:53:30 +00:00
Stephen Abello
d641ff3ab7 N°729 Form prefill : XML additions for Designer purpose
SVN:trunk[5622]
2018-04-06 08:29:53 +00:00
Bruno Da Silva
a08904a936 advanced search: Tooltip on values
in case they are larger than input, it leverage the possibility to read their value rapidly

SVN:trunk[5621]
2018-04-05 15:43:40 +00:00
Guillaume Lajarige
c13158cdb5 Advanced search: Enum/ExtKey criterion now supports min_autocomplete_chars conf parameter in autocomplete.
SVN:trunk[5620]
2018-04-05 13:32:27 +00:00
Bruno Da Silva
70a8c50d47 advanced search: handle auto opening of the form + open "add criteria" if no result list.
SVN:trunk[5619]
2018-04-05 13:15:28 +00:00
Guillaume Lajarige
991c87530f Advanced search: Fixes on enum criteria.
SVN:trunk[5618]
2018-04-05 10:18:18 +00:00
Bruno Da Silva
9d5156e0e0 advanced search: bugfix on search criterion titles
SVN:trunk[5617]
2018-04-05 10:02:52 +00:00
Bruno Da Silva
ec3ac05a1f advanced search: enum title optimisation
if the title is too long, display a count of checked itemps

SVN:trunk[5616]
2018-04-05 10:01:23 +00:00
Bruno Da Silva
a20fe37e98 advanced search: bugfix on search criterion titles
SVN:trunk[5615]
2018-04-05 10:00:08 +00:00
Bruno Da Silva
ee76eaedd6 advanced search: numeric between UI (displayed using 1 line instead of 2)
SVN:trunk[5614]
2018-04-05 09:58:29 +00:00
Bruno Da Silva
b90b200cd1 advanced search: numeric between UI (displayed using 1 line instead of 2)
SVN:trunk[5613]
2018-04-05 09:42:55 +00:00
Bruno Da Silva
873af8865c advanced search: numeric between UI (displayed using 1 line instead of 2)
SVN:trunk[5612]
2018-04-05 09:35:25 +00:00
Guillaume Lajarige
906ac14fa9 Advanced search: Fix copied values through criterion on initialization.
SVN:trunk[5611]
2018-04-05 09:17:23 +00:00
Bruno Da Silva
fcffe9d188 advanced search: bugfix
FK search with negative selection was failling if the exclusion list was empty ("not in" cannot be apployed on an empty array)

SVN:trunk[5610]
2018-04-05 08:35:03 +00:00
Stephen Abello
a84748a544 N°729 Form prefill : Allow to overload new methods in order to prefill search forms, creation forms and transition forms
SVN:trunk[5609]
2018-04-05 08:17:19 +00:00
Guillaume Lajarige
320c7646f0 Advanced search: Alpha version.
SVN:trunk[5608]
2018-04-05 07:05:58 +00:00
Guillaume Lajarige
f4f3c3bd37 Portal: Update table's filter hotkeys.
SVN:trunk[5607]
2018-04-04 15:01:36 +00:00
Guillaume Lajarige
2bb6acfa22 Advanced search: UI/UX, WIP.
SVN:b1162[5606]
2018-04-04 13:32:20 +00:00
Guillaume Lajarige
da5a8b0fd1 Advanced search: UI/UX, WIP.
SVN:b1162[5605]
2018-04-04 12:54:18 +00:00
Denis Flaven
aa22956f87 Added two new glyphs (binoculars and binoculars-alt) to the Combodo font.
SVN:b1162[5604]
2018-04-04 08:36:41 +00:00
Eric Espié
8b300358e9 Advanced Search: Fix direct links search
SVN:b1162[5603]
2018-04-04 08:16:48 +00:00
Eric Espié
54c5edc5da Advanced Search: Auto-complete search on foreign keys
SVN:b1162[5600]
2018-04-04 07:02:02 +00:00
Bruno Da Silva
5f08d98f66 search widget : widget numeric bugfix for between
when a date was empty the datetime plugin added the hours as a suffix

SVN:b1162[5599]
2018-04-03 16:27:28 +00:00
Pierre Goiffon
4d45f8d012 N°1328 Fix CSV import : check if user has rights on imported class
SVN:trunk[5597]
2018-04-03 13:36:27 +00:00
Eric Espié
b1c48929e4 Advanced Search: Auto-complete search on foreign keys
SVN:b1162[5596]
2018-04-03 13:29:10 +00:00
Eric Espié
612479b632 Advanced Search: Support of regexp for strings
SVN:b1162[5595]
2018-04-03 13:16:04 +00:00
Eric Espié
013dcdf93e Advanced Search: Auto-complete search on foreign keys
SVN:b1162[5594]
2018-04-03 13:12:49 +00:00
Eric Espié
d22d3945ee Advanced Search: Auto-complete search on foreign keys + refactoring of table_id2
SVN:b1162[5593]
2018-04-03 07:37:04 +00:00
Eric Espié
d4960080ea Advanced Search: Auto-complete search on foreign keys
SVN:b1162[5592]
2018-03-30 15:23:41 +00:00
Bruno Da Silva
4bc3d0ce2d add AGPL licence to the file
SVN:b1162[5591]
2018-03-30 14:24:54 +00:00
Bruno Da Silva
e1243532ba search widget : SearchFormForeignKeys (modal with search for foreign keys)
SVN:b1162[5590]
2018-03-30 14:24:33 +00:00
Guillaume Lajarige
f41a80a309 Portal: Fix table filter trigger on "tab" key hit.
SVN:b1162[5589]
2018-03-30 13:12:02 +00:00
Eric Espié
b447418f07 Advanced Search: debug mode
SVN:b1162[5588]
2018-03-30 12:31:13 +00:00
Guillaume Lajarige
95b523c2fa Advanced search: UI/UX, WIP.
SVN:b1162[5587]
2018-03-30 12:24:55 +00:00
Eric Espié
7dadd6e410 Advanced Search: Auto-complete search on foreign keys
SVN:b1162[5586]
2018-03-30 12:07:58 +00:00
Eric Espié
8ec75b9d45 Advanced Search: Auto-complete search on foreign keys
SVN:b1162[5585]
2018-03-30 10:30:08 +00:00
Guillaume Lajarige
e4b3086429 Advanced search: UI/UX, WIP.
SVN:b1162[5584]
2018-03-30 09:50:22 +00:00
Eric Espié
c56bda2f60 Advanced Search: Support of external fields (as strings)
SVN:b1162[5583]
2018-03-30 08:44:14 +00:00
Eric Espié
56566d83fd Advanced Search: Fix PHP syntax
SVN:b1162[5582]
2018-03-30 08:02:53 +00:00
Guillaume Lajarige
0e4dc43171 Advanced search: UI/UX, form submit throttling.
SVN:b1162[5581]
2018-03-29 16:07:11 +00:00
Guillaume Lajarige
7154aa05a6 Advanced search: UI/UX, disable hierarchical search on ext. key for now.
SVN:b1162[5580]
2018-03-29 15:52:01 +00:00
Guillaume Lajarige
1e80d76383 Advanced search: UI/UX, minor dict. updates.
SVN:b1162[5579]
2018-03-29 15:50:48 +00:00
Guillaume Lajarige
0ca2e33e7c Advanced search: UI/UX, ext. key autocomplete no UI.
SVN:b1162[5578]
2018-03-29 15:50:18 +00:00
Pierre Goiffon
38b10b6c10 N°1330 Header with statistics dashlet perf improvements
Now uses one count + group by query instead of one count query per grouping value

SVN:trunk[5576]
2018-03-29 15:47:42 +00:00
Guillaume Lajarige
e9444d3055 Advanced search: UI/UX, remove [not_]empty operators on fields that can't be null.
SVN:b1162[5575]
2018-03-29 15:15:43 +00:00
Bruno Da Silva
7e884dc69f search widget : bugfix
if the selected operator is not the default one, open in "advanced" mode in order to be able to see the operator

SVN:b1162[5574]
2018-03-29 12:27:30 +00:00
Bruno Da Silva
24c7ff4cfa search widget : numeric => between has a little margin on top and bottom
SVN:b1162[5573]
2018-03-29 10:14:33 +00:00
Bruno Da Silva
456f8be6e5 search widget : numeric => between after the basic operators
SVN:b1162[5572]
2018-03-29 10:13:07 +00:00
Bruno Da Silva
dfab460478 search widget : bugfix
if the selected operator is not the default one, open in "advanced" mode in order to be able to see the operator

SVN:b1162[5571]
2018-03-29 10:12:20 +00:00
Eric Espié
04154fa40c Advanced Search: add target_class for the external keys
SVN:b1162[5570]
2018-03-29 09:21:43 +00:00
Eric Espié
6e9fab849c Advanced Search: add target_class for the external keys
SVN:b1162[5569]
2018-03-29 09:18:40 +00:00
Pierre Goiffon
06555eb03e Run query : add shortcut in submit title
SVN:trunk[5568]
2018-03-29 09:17:44 +00:00
Pierre Goiffon
6b8d1b4b76 N°1041 add shortcut in submit button title
SVN:trunk[5567]
2018-03-29 09:17:12 +00:00
Bruno Da Silva
73d9ea42f0 search widget : widget numeric => default operator is now equals
because of the id field who will be almost the only numeric field, searching by id is the most common use case

SVN:b1162[5566]
2018-03-29 09:14:28 +00:00
Eric Espié
06f648b714 Advanced Search: back to max_combo_length for the external keys
SVN:b1162[5565]
2018-03-29 08:54:03 +00:00
Eric Espié
155034092f Advanced Search: remove the conversion IN <=> NOT IN for external keys
SVN:b1162[5564]
2018-03-29 08:45:46 +00:00
Eric Espié
11af11b3be Advanced Search: add class alias in criterion
SVN:b1162[5563]
2018-03-29 08:06:16 +00:00
Bruno Da Silva
3246c36984 search widget : widget search history
SVN:b1162[5562]
2018-03-29 08:03:47 +00:00
Eric Espié
c12a5cc98b Advanced Search: Fix missing label
SVN:b1162[5561]
2018-03-29 07:12:31 +00:00
Eric Espié
18ee7b194d Advanced Search: Display of raw titles enhanced
SVN:b1162[5560]
2018-03-29 07:02:03 +00:00
Guillaume Lajarige
14c0733949 Advanced search: UI/UX WIP.
SVN:b1162[5559]
2018-03-28 19:38:08 +00:00
Eric Espié
f3a2a24ee4 Advanced Search: read-only selection criteria to add an object
SVN:b1162[5558]
2018-03-28 15:03:53 +00:00
Eric Espié
26ec1269a5 Advanced Search: read-only selection criteria to add an object
SVN:b1162[5557]
2018-03-28 14:57:54 +00:00
Eric Espié
2811eb66c5 Advanced Search: Removed external fields from the attribute list
SVN:b1162[5556]
2018-03-28 13:38:21 +00:00
Eric Espié
9b0ccb8943 Advanced Search: Unit tests and some fixes
SVN:b1162[5555]
2018-03-28 12:53:46 +00:00
Bruno Da Silva
e33bdab4e9 search widget : widget search history handling
new parameter "class_name"

SVN:b1162[5554]
2018-03-28 11:57:10 +00:00
Denis Flaven
573b5fc879 Fallback to the default language, for missing entries in the current language, in the dictionary passed client-side.
SVN:b1162[5553]
2018-03-28 11:29:18 +00:00
Eric Espié
678821d54d Advanced Search: generic title for raw filters on joined classes
SVN:b1162[5552]
2018-03-28 07:47:20 +00:00
Eric Espié
5772042dd3 Advanced Search: generic title for raw filters on joined classes
SVN:b1162[5551]
2018-03-28 07:46:47 +00:00
Bruno Da Silva
7868a38137 search widget : widget search history draft
SVN:b1162[5550]
2018-03-27 16:09:21 +00:00
Bruno Da Silva
7bccfef3bd search widget : widget search history draft
SVN:b1162[5549]
2018-03-27 16:02:58 +00:00
Bruno Da Silva
736838474a search widget : widget date/datetime => i18n
SVN:b1162[5548]
2018-03-27 13:57:33 +00:00
Eric Espié
d553fad58d Advanced Search: Fix hidden filter on direct links
SVN:b1162[5547]
2018-03-27 13:43:22 +00:00
Bruno Da Silva
9e66ef5460 search widget : widget datetime and numeric => fine tuning the UI
SVN:b1162[5546]
2018-03-27 13:19:30 +00:00
Eric Espié
9550ec6efd Advanced Search: unit tests
SVN:b1162[5545]
2018-03-27 12:33:01 +00:00
Eric Espié
bc9e1b1d94 Advanced Search: code hardening and unit tests
SVN:b1162[5544]
2018-03-27 10:14:27 +00:00
Guillaume Lajarige
7672858d6b Advanced search: UI/UX WIP, integration with endpoint.
SVN:b1162[5543]
2018-03-27 09:36:21 +00:00
Eric Espié
2a2a9ab8b7 Advanced Search: translations
SVN:b1162[5542]
2018-03-27 08:27:49 +00:00
Denis Flaven
d8354c6666 IE compatibility: polyfill implementation of Array.from().
SVN:b1162[5541]
2018-03-27 08:19:14 +00:00
Guillaume Lajarige
ba04725ee3 Advanced search: UI/UX WIP.
SVN:b1162[5540]
2018-03-27 08:18:42 +00:00
Eric Espié
7664633f18 Advanced Search: Hierarchical keys & unit tests
SVN:b1162[5539]
2018-03-27 08:13:48 +00:00
Guillaume Lajarige
edcc211988 Advanced search: UI/UX WIP.
SVN:b1162[5538]
2018-03-26 17:58:06 +00:00
Bruno Da Silva
389e8f2de6 search widget : widget datetime the "advanced" (datetime) mode and the "less" (date only) modes are now less linked over each other
if you choose a date, you loose the time. Previously, the time was keeped hiddenly.

SVN:b1162[5537]
2018-03-26 15:59:44 +00:00
Guillaume Lajarige
070ac49d1b Advanced search: UI/UX, improve "Add criteria" cinematic.
SVN:b1162[5536]
2018-03-26 15:58:03 +00:00
Bruno Da Silva
40dbb2ce17 search widget : widget numeric various modifications
- bugfix: when the value is given by the backen and is typed as an integer some strin operations failed, a switch has to be broken in several sub cases to handle this
- enhancement: the input is now typed as numeric so the browser prevent some miss-typing (like space and alpha)

SVN:b1162[5535]
2018-03-26 15:28:33 +00:00
Bruno Da Silva
cd5dd04352 search widget : widget date bugfix when enter key is used to submit
SVN:b1162[5534]
2018-03-26 15:24:22 +00:00
Bruno Da Silva
592792dd7a search widget : widget datetime : open in advanced mode by default if a time is given
SVN:b1162[5533]
2018-03-26 15:23:49 +00:00
Guillaume Lajarige
cfe892d35e Advanced search: UI/UX, moving "Add criteria" to the left and separating criterion with "and"s for a better understanding.
SVN:b1162[5532]
2018-03-26 15:23:34 +00:00
Eric Espié
e01f48303b Advanced Search: Fix labels for starts with and contains
SVN:b1162[5531]
2018-03-26 15:13:19 +00:00
Eric Espié
f0c8b348c6 Unit tests
SVN:b1162[5530]
2018-03-26 15:02:24 +00:00
Eric Espié
ac5d24a848 Advanced Search: reorder criterion by label ('raw' in front)
Fix non-string labels

SVN:b1162[5529]
2018-03-26 14:33:31 +00:00
Bruno Da Silva
e8a37ff0af search widget : widget numeric bugfix for between
SVN:b1162[5528]
2018-03-26 11:55:10 +00:00
Bruno Da Silva
a60a8c0c4f search widget : I was cleaning my keyboard ...
SVN:b1162[5527]
2018-03-26 09:12:48 +00:00
Bruno Da Silva
e78f8c803e search widget : between operator bugfixes
SVN:b1162[5526]
2018-03-26 09:10:33 +00:00
Guillaume Lajarige
6ea0ba52d1 Advanced search: UI/UX WIP.
SVN:b1162[5525]
2018-03-26 06:52:43 +00:00
Guillaume Lajarige
8b0d9670f9 Advanced search: UI/UX WIP.
SVN:b1162[5524]
2018-03-25 12:21:26 +00:00
Eric Espié
440dd90316 Advanced Search: Merge enums and external keys
SVN:b1162[5523]
2018-03-23 16:57:10 +00:00
Eric Espié
5f86a60954 Advanced Search: Undefined for enums
SVN:b1162[5518]
2018-03-23 16:32:23 +00:00
Eric Espié
50e0ea5ec5 Advanced Search: Undefined for enums and unit tests
SVN:b1162[5517]
2018-03-23 16:22:10 +00:00
Eric Espié
b566bead31 Advanced Search: Undefined for enums and unit tests
SVN:b1162[5516]
2018-03-23 16:05:37 +00:00
Guillaume Lajarige
52731d7b0a Advanced search: Integration with endpoint.
SVN:b1162[5515]
2018-03-23 15:39:34 +00:00
Eric Espié
465532014b Advanced Search: Undefined and Id first
SVN:b1162[5514]
2018-03-23 15:02:23 +00:00
Bruno Da Silva
7153ae9614 search widget : date and datetime widget
remove = operator

SVN:b1162[5513]
2018-03-23 14:58:37 +00:00
Bruno Da Silva
0feb0fe972 search widget : date and datetime widget
SVN:b1162[5512]
2018-03-23 14:56:16 +00:00
Bruno Da Silva
b3cdbfc71b search widget : date and datetime widget
SVN:b1162[5511]
2018-03-23 14:44:13 +00:00
Guillaume Lajarige
3a32bd62ef Advanced search: More criteria UX WIP.
SVN:b1162[5510]
2018-03-23 14:23:32 +00:00
Eric Espié
c1adf880a4 Advanced Search: Dates between
SVN:b1162[5509]
2018-03-23 14:03:58 +00:00
Eric Espié
b0332b6ef5 Advanced Search: Dates between
SVN:b1162[5508]
2018-03-23 13:55:44 +00:00
Eric Espié
965e7b48df Advanced Search: Dates between
SVN:b1162[5507]
2018-03-23 13:33:09 +00:00
Bruno Da Silva
27f41baa9a search widget : date and datetime widget
SVN:b1162[5506]
2018-03-23 10:44:04 +00:00
Eric Espié
43615450ad Advanced Search: Dates between
SVN:b1162[5505]
2018-03-23 10:41:37 +00:00
Bruno Da Silva
956b8958fb search widget : date and datetime widget
SVN:b1162[5504]
2018-03-23 09:53:49 +00:00
Eric Espié
024459408a Advanced Search: open/closed search form
SVN:b1162[5503]
2018-03-23 09:41:49 +00:00
Eric Espié
78ccc44014 Advanced Search: open/closed search form
SVN:b1162[5502]
2018-03-23 09:40:41 +00:00
Eric Espié
85397c4e28 Advanced Search: Dates between
SVN:b1162[5501]
2018-03-23 09:34:21 +00:00
Eric Espié
0253f7d069 Advanced Search: Dates between
SVN:b1162[5500]
2018-03-23 09:28:47 +00:00
Eric Espié
ddcb709fd1 Advanced Search: Dates between
SVN:b1162[5499]
2018-03-23 09:21:44 +00:00
Eric Espié
a7d11c6670 Advanced Search: Dates between
SVN:b1162[5498]
2018-03-23 09:00:57 +00:00
Guillaume Lajarige
bbb4959f22 Advanced search: UX on enum widget.
SVN:b1162[5497]
2018-03-22 18:15:56 +00:00
Guillaume Lajarige
254b3fe9aa Advanced search: UX on enum widget.
SVN:b1162[5496]
2018-03-22 17:56:19 +00:00
Eric Espié
35c016482b Advanced Search: Support of undefined values for enum and external keys
SVN:b1162[5495]
2018-03-22 17:36:29 +00:00
Eric Espié
62895eedb7 Advanced Search: Add Id in search forms
SVN:b1162[5494]
2018-03-22 16:59:42 +00:00
Guillaume Lajarige
32809ae7d4 Advanced search: WIP POC, UI/UX.
SVN:b1162[5493]
2018-03-22 16:52:35 +00:00
Eric Espié
73e1e3422d Advanced Search: Numeric fields and dates
SVN:b1162[5492]
2018-03-22 16:28:54 +00:00
Guillaume Lajarige
2d9041c045 Advanced search: WIP POC, UI/UX.
SVN:b1162[5491]
2018-03-22 08:07:50 +00:00
Bruno Da Silva
60d6bb79b3 search widget : date widget UI tests
SVN:b1162[5490]
2018-03-21 16:38:30 +00:00
Guillaume Lajarige
6afb3a06ac Advanced search: WIP POC, UI/UX.
SVN:b1162[5489]
2018-03-21 15:34:00 +00:00
Eric Espié
3cdf22e9b2 Advanced Search: resolve variables for the search screen
SVN:b1162[5488]
2018-03-21 13:45:37 +00:00
Eric Espié
b05b41c7cc Advanced Search: 'between' for numeric criteria
SVN:b1162[5487]
2018-03-21 13:12:13 +00:00
Guillaume Lajarige
114a340527 Advanced search: WIP POC, UI/UX.
SVN:b1162[5486]
2018-03-21 13:10:48 +00:00
Bruno Da Silva
cfa5163590 search widget : todo added
SVN:b1162[5485]
2018-03-21 12:37:59 +00:00
Bruno Da Silva
53535dd82d search widget : console.debug removal (woops)
SVN:b1162[5484]
2018-03-21 10:02:52 +00:00
Bruno Da Silva
34f17074ca search widget : numeric widget
SVN:b1162[5483]
2018-03-21 09:51:36 +00:00
Bruno Da Silva
157d404019 search widget : reload interval moved from oDisplayBlock->Display() to oDisplayBlock->Render()
SVN:b1162[5482]
2018-03-21 08:31:56 +00:00
Bruno Da Silva
d1ef987dca search widget : numeric widget between operator
SVN:b1162[5481]
2018-03-20 16:10:53 +00:00
Eric Espié
fd8c7c99bd Advanced Search: IN with all values => 'true'
SVN:b1162[5480]
2018-03-20 15:35:01 +00:00
Guillaume Lajarige
4295437b3e Advanced search: WIP POC, UI/UX.
SVN:b1162[5479]
2018-03-20 15:34:27 +00:00
Guillaume Lajarige
92a08a1865 Advanced search: WIP POC, UI/UX.
SVN:b1162[5478]
2018-03-20 15:12:36 +00:00
Eric Espié
fbd7abf4e2 Advanced Search: Fix 'undefined index error' in 'empty' operator
SVN:b1162[5477]
2018-03-20 15:07:19 +00:00
Eric Espié
306ec09118 Advanced Search: Support '=', '!=', 'IN' and 'NOT IN' for enums and external keys (with empty/not empty and IN)
SVN:b1162[5476]
2018-03-20 15:04:33 +00:00
Eric Espié
a5e41b224f Advanced Search: Support '=' for external keys
SVN:b1162[5475]
2018-03-20 14:44:43 +00:00
Eric Espié
4abcf75b34 Advanced Search: Revert Sort on allowed values (done by JavaScript)
SVN:b1162[5474]
2018-03-20 14:36:52 +00:00
Eric Espié
7131a505be Advanced Search: Sort allowed values
SVN:b1162[5473]
2018-03-20 14:28:40 +00:00
Eric Espié
9b42af0149 Advanced Search: Sort allowed values
SVN:b1162[5472]
2018-03-20 14:20:07 +00:00
Guillaume Lajarige
27b9748f86 Advanced search: WIP POC, UI/UX.
SVN:b1162[5471]
2018-03-20 14:17:08 +00:00
Eric Espié
1301aa5c35 Advanced Search: Fix shortcut menu with sub-classes
SVN:b1162[5470]
2018-03-20 14:12:16 +00:00
Bruno Da Silva
1b80789288 search widget : numeric widget
SVN:b1162[5469]
2018-03-20 13:58:33 +00:00
Guillaume Lajarige
ca0232ae7b Advanced search: WIP POC, UI/UX.
SVN:b1162[5468]
2018-03-20 13:44:10 +00:00
Bruno Da Silva
2fb0ecc446 search widget : default width for operator name
SVN:b1162[5467]
2018-03-20 13:03:44 +00:00
Bruno Da Silva
0c41db76e2 search widget : numeric widget
SVN:b1162[5466]
2018-03-20 13:02:51 +00:00
Eric Espié
e120b149dc Advanced Search: Fix shortcut menu with sub-classes
SVN:b1162[5465]
2018-03-20 12:50:30 +00:00
Eric Espié
e33596960a Advanced Search: Better support of dates in expressions
SVN:b1162[5464]
2018-03-20 10:38:52 +00:00
Bruno Da Silva
d04fb645ec search widget : delete the "oql" parameter once the widget is modified
SVN:b1162[5463]
2018-03-20 10:04:33 +00:00
Guillaume Lajarige
87c5ee67c0 Advanced search: I would like to dedicate this commit to my beloved colleague, eespie <3
SVN:b1162[5462]
2018-03-20 09:27:42 +00:00
Guillaume Lajarige
a53a046351 Advanced search: WIP POC, UI/UX.
SVN:b1162[5461]
2018-03-20 08:19:33 +00:00
Eric Espié
afda182b4e Advanced Search: typo
SVN:b1162[5460]
2018-03-19 15:51:42 +00:00
Eric Espié
d883e3e661 Advanced Search: Dictionary compatible with the setup
SVN:b1162[5459]
2018-03-19 15:44:23 +00:00
Eric Espié
c9526130b7 Advanced Search: Cleaner Dictionary entries
SVN:b1162[5458]
2018-03-19 14:32:43 +00:00
Eric Espié
d6e3c7d1b7 Advanced Search: more info on fields and criterion
SVN:b1162[5457]
2018-03-19 14:24:41 +00:00
Eric Espié
0fdf6bfbb2 Advanced Search: Hidden criterion
SVN:b1162[5456]
2018-03-19 10:45:34 +00:00
Eric Espié
e628c68f09 Advanced Search: Search on Details pages
SVN:b1162[5455]
2018-03-19 09:58:46 +00:00
Eric Espié
8f858c2ddf Advanced Search: Labels on raw expressions
SVN:b1162[5454]
2018-03-19 09:57:31 +00:00
Denis Flaven
f8f6e201b9 Advanced Search WIP: new mechanism for passing the dictionary to the client side. Hopefully faster than before thanks to the browser's cache.
SVN:b1162[5453]
2018-03-16 17:44:55 +00:00
Guillaume Lajarige
52f56e1bb0 Advanced search: WIP POC, UI/UX.
SVN:b1162[5452]
2018-03-16 15:12:25 +00:00
Eric Espié
187f7e591e Advanced Search: Links n:n
SVN:b1162[5451]
2018-03-16 14:35:25 +00:00
Eric Espié
272e8eac4f Advanced Search: Links n:1
SVN:b1162[5450]
2018-03-16 14:21:40 +00:00
Eric Espié
d423d741b2 Advanced Search: Links n:n
SVN:b1162[5449]
2018-03-16 13:21:11 +00:00
Eric Espié
102b2d76f4 Advanced Search: Links n:n
SVN:b1162[5448]
2018-03-16 13:13:54 +00:00
Eric Espié
bb31cedcba Advanced Search: Links n:n
SVN:b1162[5447]
2018-03-16 11:24:58 +00:00
Eric Espié
bf02e04ae5 Advanced Search: Links n:n
SVN:b1162[5446]
2018-03-16 10:59:33 +00:00
Pierre Goiffon
c66884be0a N°1001 setup : log all modifications done on the DB in a SQL file (/log/setup-queries-YYYY-MM-DD_HH-mm.sql)
SVN:trunk[5445]
2018-03-16 10:00:04 +00:00
Pierre Goiffon
e7b94d3132 N°1001 setup : database/tables/columns charset and collation conversion
* check DB charset/collation and do conversion if needed
* same for existing tables
* add both info in fields signatures, so that conversions will be trigerred if needed

SVN:trunk[5444]
2018-03-16 09:59:25 +00:00
Pierre Goiffon
b219161011 N°1001 switch DB charset from utf8 to utf8mb4 to allow characters outside of the BMP
* use centralized constants instead of literal values in code
* remove config parameters 'db_character_set' and 'db_collation'
* always fix charset when creating/altering column
* backup : use utf8mb4 only for mysqldump >= 5.5.33 (was introduced in 5.5.3 but only available in 5.5.33 for programs)

SVN:trunk[5443]
2018-03-16 09:59:16 +00:00
Pierre Goiffon
fd7d30333f N°1001 setup add check for new MySQL requirement innodb_large_prefix
if disabled indexes will be limited to 767 bytes, that means 191 car in the new iTop charset utf8mb4 although the varchar iTop use are 255 car long. So we NEED this parameter to be set to true (its default value is true only since MySQL 5.7.7, see https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix)

SVN:trunk[5442]
2018-03-16 09:58:44 +00:00
Guillaume Lajarige
d734cdaf48 Advanced search: WIP POC, UI/UX.
SVN:b1162[5441]
2018-03-16 09:44:34 +00:00
Guillaume Lajarige
f60d0b10e0 Advanced search: WIP POC, UI/UX.
SVN:b1162[5440]
2018-03-16 08:59:29 +00:00
Guillaume Lajarige
a1c6e32e28 Advanced search: WIP POC, UI/UX.
SVN:b1162[5439]
2018-03-16 08:08:24 +00:00
Eric Espié
156cb03069 Advanced Search: Links n:n
SVN:b1162[5438]
2018-03-15 17:34:17 +00:00
Denis Flaven
cdb75729cb Enhancement: make the deletion of a Synchro Data Source a bit more resistant, in case of a missing or already deleted data table.
SVN:trunk[5437]
2018-03-15 16:49:58 +00:00
Guillaume Lajarige
d1a812f04c Advanced search: WIP POC, UI/UX.
SVN:b1162[5436]
2018-03-15 14:38:10 +00:00
Bruno Da Silva
b190ba1268 search widget
temps traces

SVN:b1162[5435]
2018-03-15 14:01:04 +00:00
Guillaume Lajarige
87ed90a825 Advanced search: WIP POC, UI/UX.
SVN:b1162[5434]
2018-03-15 13:58:42 +00:00
Eric Espié
a03a4af1e6 Advanced Search: warnings and default div removed
SVN:b1162[5433]
2018-03-15 13:48:38 +00:00
Eric Espié
e48e4e7139 Advanced Search: Dates between
SVN:b1162[5432]
2018-03-15 13:35:35 +00:00
Eric Espié
6ef31b7983 Fix code typos
SVN:b1162[5431]
2018-03-15 13:33:16 +00:00
Guillaume Lajarige
d378658a62 Advanced search: WIP POC, UI/UX.
SVN:b1162[5430]
2018-03-15 08:36:45 +00:00
Bruno Da Silva
ed02758940 search widget
- displayBlock "list" aExtraParams exposition in js

SVN:b1162[5429]
2018-03-14 16:35:35 +00:00
Denis Flaven
b28c45c84c N°1354: use only hashed server side information as the local storage identifier.
SVN:trunk[5428]
2018-03-14 16:25:00 +00:00
Eric Espié
42af530c63 Advanced Search: widget types
SVN:b1162[5427]
2018-03-14 16:06:34 +00:00
Eric Espié
7c3de1e976 Advanced Search: widget types
SVN:b1162[5426]
2018-03-14 15:46:30 +00:00
Guillaume Lajarige
afdd43c5e3 Advanced search: WIP POC, UI/UX.
SVN:b1162[5425]
2018-03-14 13:00:08 +00:00
Bruno Da Silva
5bc756716e search widget
- ajax endpoint parameter reading refactoring
- displayBlock "list" aExtraParams exposition in js

SVN:b1162[5424]
2018-03-14 11:14:45 +00:00
Guillaume Lajarige
6a6c069896 Advanced search: WIP POC, integration with endpoint.
SVN:b1162[5423]
2018-03-14 11:03:32 +00:00
Eric Espié
9499799f80 Advanced Search: IN/NOT IN
SVN:b1162[5422]
2018-03-14 08:50:54 +00:00
Guillaume Lajarige
5632f9786c Advanced search: WIP POC, UI/UX.
SVN:b1162[5421]
2018-03-14 08:33:17 +00:00
Eric Espié
6f79e07e90 Advanced Search: NOT IN
SVN:b1162[5420]
2018-03-13 16:16:33 +00:00
Guillaume Lajarige
601f18bbab Advanced search: WIP POC, integration with endpoint.
SVN:b1162[5419]
2018-03-13 16:10:17 +00:00
Eric Espié
6f750cf584 Advanced Search: Unit tests
SVN:b1162[5418]
2018-03-13 16:06:15 +00:00
Eric Espié
3b7c92d022 Advanced Search: Field List
SVN:b1162[5417]
2018-03-13 16:04:11 +00:00
Eric Espié
af12b47c90 Advanced Search: Field List
SVN:b1162[5416]
2018-03-13 13:50:04 +00:00
Bruno Da Silva
2d9140e56c Search form
Result list now expose their extra params using jQuery().data() function

SVN:b1162[5415]
2018-03-13 13:03:14 +00:00
Bruno Da Silva
0351ff30b4 Search form
Result list now expose their extra params using jQuery().data() function

SVN:b1162[5414]
2018-03-13 13:00:14 +00:00
Eric Espié
6d13dac2c3 Advanced Search: Smart conversion
SVN:b1162[5413]
2018-03-13 11:15:29 +00:00
Guillaume Lajarige
e2174b9ad4 Advanced search: WIP POC, integration with endpoint.
SVN:b1162[5412]
2018-03-13 10:47:54 +00:00
Eric Espié
2038785b09 SVN:b1162[5411] 2018-03-13 10:27:08 +00:00
Bruno Da Silva
0c8650d2e5 Search form
jquery expect html responses to begin with a <

SVN:b1162[5410]
2018-03-13 10:22:48 +00:00
Eric Espié
3e7edea7be SVN:b1162[5409] 2018-03-13 09:56:34 +00:00
Eric Espié
b3d625b9fc Advanced Search
SVN:b1162[5408]
2018-03-13 08:11:01 +00:00
Eric Espié
d02fb69ca7 Advanced Search
SVN:b1162[5407]
2018-03-09 17:07:15 +00:00
Guillaume Lajarige
8cf4bc58a7 Advanced search: WIP POC, better criteria widget instanciation.
SVN:b1162[5406]
2018-03-09 16:38:30 +00:00
Guillaume Lajarige
b8aed0f004 Advanced search: WIP POC, integration with endpoint, add "list_params" parameter.
SVN:b1162[5405]
2018-03-09 16:28:46 +00:00
Guillaume Lajarige
03a6473bd4 Update portal portugues (brazilian) translations thanks to Pedro Beck!
SVN:trunk[5404]
2018-03-09 13:51:14 +00:00
Eric Espié
7d95c02b57 Advanced Search
SVN:b1162[5403]
2018-03-09 13:42:19 +00:00
Guillaume Lajarige
767507d195 Advanced search: WIP POC, integration with endpoint.
SVN:b1162[5402]
2018-03-09 13:34:04 +00:00
Guillaume Lajarige
1d96cbeb07 Advanced search: WIP POC, client widgets.
SVN:b1162[5401]
2018-03-09 13:18:56 +00:00
Guillaume Lajarige
0bf33ec7e9 Advanced search: WIP POC, integration with endpoint.
SVN:b1162[5400]
2018-03-09 11:10:03 +00:00
Eric Espié
37196b42ed Advanced Search
SVN:b1162[5399]
2018-03-09 09:36:58 +00:00
Eric Espié
5f7453d031 Advanced Search
SVN:b1162[5398]
2018-03-09 09:23:36 +00:00
Eric Espié
88b7ef5345 Advanced Search
Conversion to search form

SVN:b1162[5397]
2018-03-09 09:16:00 +00:00
Eric Espié
213d591eb0 Advanced Search
SVN:b1162[5396]
2018-03-09 08:43:28 +00:00
Eric Espié
c04f73e86b Advanced Search
Convert from raw OQL to search form widget operator

SVN:b1162[5395]
2018-03-08 17:01:17 +00:00
Guillaume Lajarige
2033c171f0 Advanced search: Integration with endpoint POC & WIP.
SVN:b1162[5394]
2018-03-08 16:50:10 +00:00
Eric Espié
8ce708dade Advanced Search
generate id on div "result list outer" and use this id on search widget initialisation

SVN:b1162[5393]
2018-03-08 14:53:57 +00:00
Eric Espié
fa60322ff1 Advanced Search
SVN:b1162[5392]
2018-03-08 13:49:00 +00:00
Eric Espié
e9bcd170c0 Advanced Search
SVN:b1162[5391]
2018-03-07 17:07:40 +00:00
Guillaume Lajarige
118b5c8a7d Advanced search: Initializing javascript widgets.
SVN:b1162[5390]
2018-03-07 16:43:20 +00:00
Guillaume Lajarige
b5bcfa8d90 Advanced search: WIP...
SVN:b1162[5389]
2018-03-07 16:21:44 +00:00
Eric Espié
e496ab06b2 Advanced Search
SVN:b1162[5388]
2018-03-07 16:10:09 +00:00
Vincent Dumas
d7c960e150 Enabling search and access control by organization on User class. Reworking fields displayed in Details and List as well.
SVN:trunk[5387]
2018-03-07 14:00:10 +00:00
Eric Espié
9de7b4ba35 Advanced Search
SVN:b1162[5386]
2018-03-06 16:18:12 +00:00
Guillaume Lajarige
bb1a18f187 SVN:b1162[5385] 2018-03-06 14:47:09 +00:00
Guillaume Lajarige
68cdd6b8a9 N°1325 Dashboards: Unknown dashlets (eg. from an uninstalled extension) no longer raise an exception, a fallback is displayed and the XML configuration is still available in editor.
SVN:trunk[5384]
2018-03-06 14:07:33 +00:00
Guillaume Lajarige
34dab0c498 Update licences copyright
SVN:trunk[5383]
2018-03-06 14:06:19 +00:00
Pierre Goiffon
f9511aba17 N°1030 Collapsible Sections :
* stop event propagation (was causing tab switching in notifications)
* save section state in localStorage

SVN:trunk[5382]
2018-03-05 15:50:38 +00:00
Pierre Goiffon
d96015f2c1 N°1260 new db_tls.verify_server_cert option to force server certificates check
SVN:trunk[5381]
2018-03-05 15:50:18 +00:00
Eric Espié
e66d577f21 N°478 - Customizable access to the 'Admin Tools'
SVN:trunk[5380]
2018-03-05 08:09:10 +00:00
Pierre Goiffon
47653eeba7 N°1260 PHPUnit for the Mysql CLI TLS options generation
SVN:trunk[5379]
2018-02-28 16:47:00 +00:00
Pierre Goiffon
a0463c85a1 N°1260 MySQL TLS connection : use less rectrictive flag (no server cert check)
SVN:trunk[5376]
2018-02-28 15:13:11 +00:00
Pierre Goiffon
519093dceb N°1260 fix backup classes to correctly uses DB parameters including TLS
* backup : add missing PHPDoc
* backup : use a config object instead of each parameter attribute
* CMDBSource::InitServerAndPort : remove static attribute dependency, change visibility
* setup : generate a config instance to use in backup
* backup : add TLS params if needed to the mysqldump call

SVN:trunk[5375]
2018-02-28 15:12:57 +00:00
Pierre Goiffon
08c5d0e4c1 N°1342 cron : fix iScheduledProcess implementations that were rescheduled on every cron start and so never processed :( (was introduced in r5371)
SVN:trunk[5374]
2018-02-27 16:38:38 +00:00
Eric Espié
cae4526b3b Performance enhancement. Avoid multiple count requests.
SVN:trunk[5373]
2018-02-27 15:54:30 +00:00
Pierre Goiffon
79381d7fd2 N°1342 cron : previous commit was to handle BackgroundTask pointing to non existing class (can happen after an extension removal)
add comment reflecting the class_exists() test purpose

SVN:trunk[5372]
2018-02-27 15:49:03 +00:00
Pierre Goiffon
d8c141b1c9 N°1342 cron task validity checks :
* checks are now made one by one
* new class_exists() test

SVN:trunk[5371]
2018-02-27 15:20:55 +00:00
Pierre Goiffon
3b3f4044cb N°1260 Mutex : fix merge error in mutex name init
SVN:trunk[5369]
2018-02-26 15:16:33 +00:00
Pierre Goiffon
c0256428f9 setup : keep code compatibility with old PHP version
Whatever are the iTop requirements, we will be able to show warnings/errors on the setup first screen

SVN:trunk[5368]
2018-02-26 14:27:47 +00:00
Eric Espié
85a5ddb980 N°478 - Customizable access to the 'Admin Tools'
- Display additional rights (grant_by_profile) in the grant matrix

SVN:trunk[5367]
2018-02-26 10:38:09 +00:00
Stephen Abello
06bc58f383 datamodel viewer : display children in search tree, search configured to "contains", viewed class is now added to the search input by default and more information about attribute null values/default values
SVN:trunk[5366]
2018-02-23 15:37:00 +00:00
Pierre Goiffon
b739c00414 N°1260 wrong function name called to check TLS connection
SVN:trunk[5365]
2018-02-23 14:19:32 +00:00
Eric Espié
d65bd97956 N°478 - Customizable access to the 'Admin Tools'
SVN:trunk[5364]
2018-02-23 11:13:07 +00:00
Pierre Goiffon
b952f9da4a N°942 allow to have no new PHP/MySQL requirements for next release
SVN:trunk[5363]
2018-02-22 14:28:52 +00:00
Stephen Abello
388b3257fc jQuery modernization : visual fix for the top left pin
SVN:trunk[5362]
2018-02-22 12:53:06 +00:00
Stephen Abello
6318077278 jQuery modernization : removed unused minified jquery-ui css & unused non-minified jquery-migrate 1.2.1 library
SVN:trunk[5361]
2018-02-22 10:14:26 +00:00
Stephen Abello
9fee51bafb jQuery modernization : included console's jquery/jquery-ui files in portal instead of its own files
SVN:trunk[5360]
2018-02-22 09:44:42 +00:00
Stephen Abello
397ec9587b jQuery modernization : updated jquery to 1.12.4, jquery-ui to 1.11.4 and jquery-migrate to 1.4.1
SVN:trunk[5359]
2018-02-22 09:28:08 +00:00
Stephen Abello
862d08f57d jQuery modernization : removed and replaced calls to deprecated methods in jquery 1.12.4
SVN:trunk[5358]
2018-02-22 09:24:42 +00:00
Stephen Abello
ebb541e4f5 jQuery modernization : updated libraries to a version compatible with jquery 1.12.4
SVN:trunk[5357]
2018-02-22 09:21:05 +00:00
Stephen Abello
e05d780bec jQuery modernization : removed unused javascript libraries and jquery-ui stylesheets
SVN:trunk[5356]
2018-02-22 09:18:42 +00:00
Guillaume Lajarige
84b383154d Update spanish translations (thanks to Miguel Turrubiates!)
SVN:trunk[5353]
2018-02-21 16:39:44 +00:00
Guillaume Lajarige
f458826643 Internal: Rename core english dictionary files to match standard convention.
SVN:trunk[5352]
2018-02-21 16:36:08 +00:00
Pierre Goiffon
5d808992e6 N°942 use expression in SetupUtils constant as this is allowed since PHP 5.6.0 and it is the new required version for iTop 2.5 (see http://php.net/manual/en/language.oop5.constants.php)
SVN:trunk[5351]
2018-02-21 16:35:19 +00:00
Eric Espié
03f9a9fcac N°1161 - Add functions, order by and limits to DBSearch::MakeGroupByQuery()
SVN:trunk[5350]
2018-02-16 12:59:35 +00:00
Eric Espié
7ea9b5b2f3 Unit tests with debug()
SVN:trunk[5349]
2018-02-16 08:16:36 +00:00
Eric Espié
b8fc24f093 Unit tests with debug()
SVN:trunk[5348]
2018-02-16 08:14:29 +00:00
Denis Flaven
bddba15403 Cleanup target build directory before building into it...
SVN:trunk[5345]
2018-02-13 11:01:11 +00:00
Eric Espié
894f1c4f28 Magic trick for windows. Sometimes the folder is empty but rmdir fails
SVN:trunk[5344]
2018-02-13 10:59:56 +00:00
Eric Espié
19665d4ad9 Computations are not allowed in defining constants
SVN:trunk[5343]
2018-02-12 16:15:46 +00:00
Denis Flaven
18a8e86b2a 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:trunk[5341]
2018-02-12 12:31:20 +00:00
Guillaume Lajarige
3af724a941 Fix application being wrongly set to Archive Mode when it fails to retrieve an object from the database.
SVN:trunk[5340]
2018-02-12 12:28:10 +00:00
Eric Espié
5b378ee9dd N°1322 - Display of links now support both DBObjectSet and ormLinkSet
SVN:trunk[5339]
2018-02-09 15:32:15 +00:00
Denis Flaven
d5889a90d4 The Hub is alive ! Let's use the production URL.
SVN:trunk[5337]
2018-02-09 14:13:46 +00:00
Denis Flaven
81c8eb2830 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:trunk[5336]
2018-02-09 14:04:52 +00:00
Denis Flaven
507a073203 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:trunk[5333]
2018-02-09 13:44:48 +00:00
Denis Flaven
abe67d9e4e Added an extra safety check to detect inconsistencies between the added extensions and the choices made during the initial installation.
SVN:trunk[5318]
2018-02-09 08:43:42 +00:00
Vincent Dumas
741f44e8b7 dictionnary error 'criticity' replaced by 'criticality'
SVN:trunk[5317]
2018-02-08 15:57:57 +00:00
Pierre Goiffon
c715b9488a N°1260 MySQL connection : allow to use p: host prefix (persistent connection, see http://php.net/manual/en/mysqli.persistconns.php)
SVN:trunk[5316]
2018-02-08 14:22:27 +00:00
Pierre Goiffon
5107ef5119 N°1260 MySQL TLS connection : finalize setup warning message
SVN:trunk[5315]
2018-02-08 14:22:20 +00:00
Pierre Goiffon
ca28eeb596 N°1260 rename db_ssl* vars to db_tls (cause SSL is an old protocol and MySQL uses TLS)
Keep options label with SSL, to keep them aligned with the labels used in MySQL products and documentation

SVN:trunk[5314]
2018-02-08 14:22:14 +00:00
Pierre Goiffon
f51eb96c69 N°1260 MySQL TLS connection : do not use persistent connection in Mutex
SVN:trunk[5313]
2018-02-08 14:22:05 +00:00
Pierre Goiffon
b032299b05 N°1260 MySQL TLS connection : exception if the opened connection is not in TLS whereas TLS parameters were used to open it
SVN:trunk[5312]
2018-02-08 14:21:58 +00:00
Pierre Goiffon
5a2576bc29 N°1260 MySQL TLS connection : add options in setup
SVN:trunk[5311]
2018-02-08 14:21:51 +00:00
Pierre Goiffon
3375629d06 N°1260 MySQL TLS connection : add capath config for mysqli::ssl_set argument
SVN:trunk[5310]
2018-02-08 14:21:40 +00:00
Pierre Goiffon
37232bc222 N°1260 every classes creating a mysqli instance now use a dedicated method in CMDBSource
SVN:trunk[5309]
2018-02-08 14:21:33 +00:00
Pierre Goiffon
d2f0deec9c N°1260 Config : migrate DB* variables to the Get() model, create CMDBSource::InitFromConfig
SVN:trunk[5308]
2018-02-08 14:21:25 +00:00
Pierre Goiffon
5a25e44177 N°1260 MySQL TLS patch improvements :
* mysql connexion opening : simplify code
* rename DB_SSL_* variables to DB_SSL.*
* fix warnings when new param are not set
* persistent connection (host "p:" prefix) is used for every TLS connection
* add some missing @var

SVN:trunk[5307]
2018-02-08 14:21:06 +00:00
Pierre Goiffon
08d9d58894 N°1260 MySQL TLS connection : apply Hardis patch (many thanks !)
SVN:trunk[5306]
2018-02-08 14:20:58 +00:00
Guillaume Lajarige
0254a75c95 N°1280 Upgrade Silex library to 2.2 (Which is possible as iTop 2.5 requirements are now PHP 5.6+!)
SVN:trunk[5305]
2018-01-31 13:37:51 +00:00
Guillaume Lajarige
a7cfcefee2 Internal: PHPDoc update
SVN:trunk[5304]
2018-01-31 12:35:01 +00:00
Guillaume Lajarige
f78c057b45 Portal: Fix user profile edition due to recent user rights changes.
SVN:trunk[5303]
2018-01-31 12:31:45 +00:00
Guillaume Lajarige
4bd3084403 Fix regression introduced in r5298: Portal user could not change its preferences.
Removed the 'grant_by_profile' category check in UserRights::GetSelectFilter().

SVN:trunk[5302]
2018-01-31 12:29:20 +00:00
Pierre Goiffon
9d817f0c77 N°942 new requirements for iTop 2.5
SVN:trunk[5301]
2018-01-31 10:44:17 +00:00
Pierre Goiffon
68b8cc1d20 MetaModel : reduce code warnings and improve type hinting
SVN:trunk[5300]
2018-01-31 10:31:28 +00:00
Pierre Goiffon
fb8b0f4f65 PHPDoc for methods called by MetaModel
SVN:trunk[5299]
2018-01-31 10:31:23 +00:00
Eric Espié
94d45fc77f N°1248 - User Management Portal
* Added a new grant_by_profile category that allows to manage certain classes in addition to bizmodel with user profiles.
* The following classes have the new grant_by_profile category:
    User, UserInternal, UserLocal, UserLDAP, UserExternal, URP_UserProfile, URP_UserOrg
* For these classes, it is possible to manage access rights with user profiles for non-administrators.
* For these classes, the default behavior of SELECT requests changes from allowed to forbidden.
* For user profiles, the default behavior '*' is limited to the bizmodel category to keep the previous behavior of profiles, i. e. for classes in the grant_by_profile category, rights (including READ) must be given explicitly.
* New constraints have been added, so only an administrator can manage (attach or detach) the 'Administrator' profile.

SVN:trunk[5298]
2018-01-30 15:17:51 +00:00
Eric Espié
5144f62da9 UserRights Unit tests
SVN:trunk[5297]
2018-01-26 08:06:41 +00:00
Romain Quetiez
a58ba7fcc5 N°1287 Update the installation instructions (pointing to the wiki page), and fix the readme to point to the correct wiki pages
SVN:trunk[5295]
2018-01-25 11:05:12 +00:00
Romain Quetiez
17bec06c98 Cleanup - remove this index page not used anymore (and having broken links)
SVN:trunk[5294]
2018-01-25 10:50:50 +00:00
Guillaume Lajarige
d531ec846f Portal: CSS Fixes (lack of consistency with border-radius)
SVN:trunk[5293]
2018-01-24 14:21:36 +00:00
Guillaume Lajarige
07849922b8 Code cleanup
SVN:trunk[5292]
2018-01-23 14:53:14 +00:00
Denis Flaven
63ba267da0 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:trunk[5290]
2018-01-23 10:37:50 +00:00
Pierre Goiffon
93526c8a44 N°942 PHP version not yet validated was incorrectly set to 7.1.9, fix it back to 7.2.0
SVN:trunk[5288]
2018-01-18 11:11:53 +00:00
Denis Flaven
c7c2f4a482 Bug fix: do not (try to) launch the backup if the backup detection told us that the backup is not possible!
SVN:trunk[5285]
2018-01-17 16:31:16 +00:00
Denis Flaven
dc0e739667 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:trunk[5284]
2018-01-17 16:29:44 +00:00
Pierre Goiffon
e74b2e32c9 Remove file that do not belongs here
SVN:trunk[5282]
2018-01-17 14:49:54 +00:00
Romain Quetiez
a2dd9b1d48 Getting ready for the release of 2.4.1 in february
SVN:trunk[5279]
2018-01-17 13:10:57 +00:00
Denis Flaven
474fdc0473 Setup: special mapping for 2 extensions which code has changed...
SVN:trunk[5277]
2018-01-17 12:36:25 +00:00
Guillaume Lajarige
e6ab3ac9f9 N°1277 Portal: Improve error reporting when user with no associated contact logs in the portal.
SVN:trunk[5276]
2018-01-17 12:31:05 +00:00
Denis Flaven
ae59780297 The Hub Connects !!
Adding iTop Hub Connector.

SVN:trunk[5270]
2018-01-17 10:04:33 +00:00
Pierre Goiffon
cbdafbf264 Exclude for itop-hub-connector
SVN:trunk[5268]
2018-01-17 09:02:46 +00:00
Guillaume Lajarige
76fa4a3090 Portal: Fix CSS for selected rows in dataTables tables
SVN:trunk[5266]
2018-01-16 16:59:36 +00:00
Guillaume Lajarige
896c6b79db Internal: Portal code cleanup
SVN:trunk[5265]
2018-01-16 16:25:31 +00:00
Denis Flaven
bb052f30d6 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:trunk[5264]
2018-01-16 16:24:38 +00:00
Stephen Abello
ebbff7f615 datamodel viewer : fixed case where no class name were given, cleaned up code
SVN:trunk[5263]
2018-01-16 15:56:25 +00:00
Guillaume Lajarige
9d76ac96bc N°984 Portal: Fix autocomplete field reset when changing value of parent field in request templates.
SVN:trunk[5261]
2018-01-16 15:37:28 +00:00
Guillaume Lajarige
1926a40277 Internal: PHPDoc
SVN:trunk[5260]
2018-01-16 15:32:11 +00:00
Pierre Goiffon
6cd108badf RelationGraph : some PHPDoc
SVN:trunk[5258]
2018-01-16 15:04:08 +00:00
Eric Espié
5993d74eec N°1246 - Fix Obsolete data visible in dependency graph.
* Fix a wrong transient OQL expression cache.

SVN:trunk[5257]
2018-01-16 15:01:33 +00:00
Guillaume Lajarige
84b98a2265 N°1276 Portal: Aligned drop-down list to autocomplete threshold behavior to console's behavior.
SVN:trunk[5255]
2018-01-16 14:24:24 +00:00
Eric Espié
0df071b4db Default class is Organization
SVN:trunk[5254]
2018-01-16 11:07:13 +00:00
Eric Espié
bfd092b499 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

SVN:trunk[5252]
2018-01-16 10:38:17 +00:00
Eric Espié
23f2ea5031 N°1026 - Portal requests are too slow
* Counts on union requests are more optimized
* Requests for combo box values are more optimized

SVN:trunk[5249]
2018-01-15 15:16:20 +00:00
Stephen Abello
29165b8ef4 datamodel viewer : cleanup code, fixed links in tooltips and added a classname following while scrolling down the page
SVN:trunk[5247]
2018-01-12 16:32:10 +00:00
Pierre Goiffon
c862179465 N°942 set nex itop release MySQL requirement to 5.5.3 for utf8mb4
Update also the comments I forgot on the previous change (woooops), and the warning messages beginning with "error" (hahem)

SVN:trunk[5245]
2018-01-12 16:07:31 +00:00
Pierre Goiffon
7a371f8b26 N°942 next itop release PHP & MySQL requirements : use *.0 versions instead of the latests
SVN:trunk[5243]
2018-01-12 15:35:25 +00:00
Eric Espié
7a7b968c1b Cleanup code
SVN:trunk[5242]
2018-01-12 14:28:42 +00:00
Eric Espié
9571404907 Cleanup code
SVN:trunk[5240]
2018-01-12 13:37:40 +00:00
Pierre Goiffon
ae946f6821 N°942 change next itop release MySQL version requirement from 5.6 to 5.5, plus add some comments
SVN:trunk[5238]
2018-01-12 11:26:12 +00:00
Denis Flaven
6128625706 Fixed regression introduced by [r5235]: some directories (like data/production-modules) may not always exist... this should not stop the setup.
SVN:trunk[5237]
2018-01-11 17:23:01 +00:00
Denis Flaven
ea2a3c1980 Handle extensions with missing dependencies.
SVN:trunk[5235]
2018-01-11 10:49:18 +00:00
Denis Flaven
9b6814aee9 Typo
SVN:trunk[5234]
2018-01-11 10:38:30 +00:00
Denis Flaven
6544659251 Small setup refactoring for getting ready for the Hub.
SVN:trunk[5232]
2018-01-10 15:47:15 +00:00
Stephen Abello
dcff39da25 N°1147 Enable data synchronization for applications classes (Localized Data).
SVN:trunk[5230]
2018-01-10 14:10:29 +00:00
Pierre Goiffon
94ba32af57 Some PHPDoc
SVN:trunk[5229]
2018-01-10 14:08:36 +00:00
Pierre Goiffon
cc08613304 New method to test if a field is read only in the current DBObject state
SVN:trunk[5228]
2018-01-10 14:08:29 +00:00
Eric Espié
95941f4dc5 N°870 - Fix the display of archived objects in the dashlets when activating/deactivating the archive mode.
SVN:trunk[5226]
2018-01-10 13:39:41 +00:00
Guillaume Lajarige
3f2e20fe44 Portal: Change Ticket->public_log's flags in ev_reopen form. Now MUST_CHANGE instead of MUST_PROMPT.
SVN:trunk[5224]
2018-01-10 13:31:39 +00:00
Stephen Abello
67124a4104 datamode viewer : fix lifecycle image generation on Windows. (Error: "C:/Program does not exist")
SVN:trunk[5222]
2018-01-10 08:15:49 +00:00
Eric Espié
174bcf56d3 cleanup code
SVN:trunk[5221]
2018-01-10 07:58:51 +00:00
Romain Quetiez
d9fd3b47e1 Copyright updated to 2018
SVN:trunk[5220]
2018-01-09 16:41:30 +00:00
Eric Espié
89492f8904 N°870 - Avoid Obsolete data in audit results
SVN:trunk[5219]
2018-01-09 16:07:18 +00:00
Eric Espié
42dc73964c N°870 - Avoid Obsolete data export in CSV, Excel and PDF
SVN:trunk[5218]
2018-01-09 15:51:03 +00:00
Guillaume Lajarige
449316257a N°1247 Fix AttributeEnum display as vertical radio buttons in console UI.
SVN:trunk[5216]
2018-01-09 14:43:06 +00:00
Eric Espié
3b621adcb2 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'

SVN:trunk[5215]
2018-01-09 13:57:29 +00:00
Pierre Goiffon
8d9d4e67ca N°942 setup : max version for PHP
SVN:trunk[5213]
2018-01-08 15:34:00 +00:00
Guillaume Lajarige
eb43a02bce Fix regression introduced in r5183.
SVN:trunk[5211]
2018-01-08 12:40:05 +00:00
Guillaume Lajarige
7f034f60d6 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:trunk[5209]
2018-01-08 12:09:35 +00:00
Guillaume Lajarige
c4cf10b6e6 N°1172.3 Portal: Objects and external keys in linkedsets (forms) now open in a modal dialog.
SVN:trunk[5207]
2018-01-08 11:36:22 +00:00
Stephen Abello
b2a1404ce0 datamode viewer revamped: Class search, new panel for class list, graphical representation of each class and its related classes, granularity on data display and a fix on lifecycle graph.
SVN:trunk[5206]
2018-01-05 15:18:56 +00:00
Pierre Goiffon
52a97db259 N°1253 Configuration editor : save/restore editor state on saving
SVN:trunk[5205]
2018-01-05 15:16:45 +00:00
Pierre Goiffon
e5ccb4271e HTMLDOMSanitizer remove duplicate code declaration
SVN:trunk[5204]
2018-01-04 17:30:26 +00:00
Pierre Goiffon
27a2614b7d N°801 allow block quotes in HTML Fields
add BLOCKQUOTE tag in the HTMLDOMSanitizer white list

SVN:trunk[5202]
2018-01-04 17:18:02 +00:00
Guillaume Lajarige
5cc39848ff Typo
SVN:trunk[5201]
2018-01-04 10:55:14 +00:00
Guillaume Lajarige
b9d719d636 N°1194 Portal: Support for MUST_CHANGE flag on CaseLog attributes in transitions.
SVN:trunk[5199]
2018-01-04 10:38:47 +00:00
Guillaume Lajarige
3e6b3a2755 N°1245 Fix MUST_CHANGE flag behavior on CaseLog attributes in the console.
SVN:trunk[5197]
2018-01-03 14:44:59 +00:00
Guillaume Lajarige
88167fb3ae 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:trunk[5194]
2018-01-03 09:41:58 +00:00
Guillaume Lajarige
b5685a9d76 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:trunk[5193]
2018-01-03 09:07:26 +00:00
Guillaume Lajarige
4c652a87c0 N°1243 Fix MUST_CHANGE/MANDATORY checks on transition in the console on an HTML Attribute.
SVN:trunk[5192]
2018-01-02 16:18:18 +00:00
Pierre Goiffon
1f8bd69aef N°942 setup : add checks for next iTop release requirements on PHP and MySQL versions
* new constants in SetupUtils
* renamed existing methods
* warning if PHP and MySQL versions are lower than expected

SVN:trunk[5190]
2018-01-02 16:04:26 +00:00
Guillaume Lajarige
71d9bb18e5 N°1172.2 Fix regression introduced in r5161 (Email notification crash because of portal urls)
SVN:trunk[5189]
2018-01-02 15:59:01 +00:00
Pierre Goiffon
067b3364ee Add some PHPDoc, fix some syntax (thanks to SonarLint !)
SVN:trunk[5188]
2018-01-02 14:20:02 +00:00
Pierre Goiffon
b2494ebaf7 Create abstract DBSearch#GetSQLQueryStructure() to allow "call hierarchy" to work
Visibility to public to allow testing

SVN:trunk[5187]
2018-01-02 14:19:54 +00:00
Eric Espié
f73ca10b6c 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.

SVN:trunk[5185]
2018-01-02 13:08:14 +00:00
Guillaume Lajarige
fe23e099fe N°1227 New configuration parameter (disable_attachments_download_legacy_portal) to disable attachments download from the legacy portal. Default is "true"!
SVN:trunk[5183]
2017-12-29 13:54:20 +00:00
Guillaume Lajarige
333411535e 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:trunk[5181]
2017-12-29 09:55:36 +00:00
Guillaume Lajarige
cc6272e84a N°1143 Fix removed email links (mailto) in HTML attributes (CKEditor).
SVN:trunk[5179]
2017-12-28 15:34:24 +00:00
Guillaume Lajarige
c7857835c7 N°850 Show "delete" and "bulk delete" rights in user's grant matrix.
SVN:trunk[5177]
2017-12-28 10:37:04 +00:00
Guillaume Lajarige
9bfaf10468 N°624 Fix WYSIWYG feature in CaseLog / HTML attributes on transition.
SVN:trunk[5175]
2017-12-28 09:25:03 +00:00
Guillaume Lajarige
095d5c9442 Compiled CSS filed from previous commit (r5168)
SVN:trunk[5171]
2017-12-27 15:54:36 +00:00
Guillaume Lajarige
4fa6f85c2e 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:trunk[5170]
2017-12-27 15:51:50 +00:00
Pierre Goiffon
76a9978fc5 N°1182 fix overlapping table in console dashlets : now we have a scrolling bar if necessary
SVN:trunk[5168]
2017-12-27 14:50:44 +00:00
Guillaume Lajarige
4b46b2776a 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.

SVN:trunk[5166]
2017-12-27 09:59:21 +00:00
Eric Espié
907505ccf9 Fix and refactor based on unit tests results
SVN:trunk[5165]
2017-12-22 14:08:58 +00:00
Eric Espié
3e35dafefb Unit tests based on PHPUnit
These tests run with the sample datamodel and they don't commit any data (only some indexes are incremented).

To launch the test suite, run the following command from the test repository:
php.exe <PATH_TO>/phpunit.phar --configuration <PATH_TO>/test/PHPunit.xml

SVN:trunk[5164]
2017-12-22 13:37:26 +00:00
Eric Espié
11ee5126ef N°789 - Fix losing the additional links attributes values during impact analysis update
The issue was only visible when attributes were added to the links (FunctionalCIs and Contacts).
When a Ticket is modified, the impact analysis generate a new set of links for FunctionalCIs and Contacts.
If new attributes are added on links, the values were lost during the process.
Now existing links are kept along with the additional attributes values.

SVN:trunk[5162]
2017-12-22 13:09:21 +00:00
Guillaume Lajarige
37bdb1ba2f N°1172 Portal: Objects and external keys in linkedsets (forms) now have hyperlinks if access is authorized regarding the user's scopes.
SVN:trunk[5161]
2017-12-21 08:31:32 +00:00
Pierre Goiffon
a9fc1083c7 PHPDoc for AddModule()
SVN:trunk[5160]
2017-12-20 16:34:55 +00:00
Eric Espié
9be804fb35 N°1209 - Fix Organization selector width (missing button)
SVN:trunk[5157]
2017-12-15 13:17:23 +00:00
Eric Espié
38a466fc21 N°1209 - Fix Organization selector width (padding added)
SVN:trunk[5155]
2017-12-15 11:20:36 +00:00
Eric Espié
aa5ee45034 N°1209 - Fix Organization selector width
SVN:trunk[5154]
2017-12-15 09:48:13 +00:00
Denis Flaven
5b1e1d0d6a Enhancement: automatically recognize some well-know mutli-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:trunk[5152]
2017-12-14 12:11:51 +00:00
Romain Quetiez
a818a09469 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
SVN:trunk[5151]
2017-12-11 16:19:16 +00:00
Guillaume Lajarige
0d439a08fc Portal: Default brick icon classes were using a wrong constant and therefore not displaying correctly.
SVN:trunk[5150]
2017-12-05 10:24:02 +00:00
Pierre Goiffon
9032f25d64 CMDBSource : fix code errors and some warnings
SVN:trunk[5149]
2017-12-04 15:07:21 +00:00
Pierre Goiffon
28efea7ac1 N°1195 exception handling in cron.php
* cron.php : use exit(n°) instead of exit n°, and extract codes to constants
* CMDBSource : new MySQLHasGoneAwayException
* exits cron.php on MySQLHasGoneAwayException
* fix backgroundprocess PHPDoc
* new ProcessException and ProcessFatalException
* new cron.php catch blocks

SVN:trunk[5148]
2017-12-04 15:07:15 +00:00
Guillaume Lajarige
f2f0badc77 N°1199 Fixed "Notice: undefined index 0" in the portal. UserRequest/Incident::ComputePriority() was failing when attributes impact had no value.
SVN:trunk[5147]
2017-12-01 15:41:52 +00:00
Eric Espié
00e3d5c0d2 License reformat.
SVN:trunk[5146]
2017-11-30 09:23:26 +00:00
Eric Espié
c08edc207c N°1190 - Better error reporting and disk cleanup.
SVN:trunk[5145]
2017-11-30 08:58:58 +00:00
Eric Espié
6477e2e1bb N°1188 - Backup needs too much disk space
SVN:trunk[5144]
2017-11-30 08:52:44 +00:00
Eric Espié
694da178c4 N°1191 - Wrong file name for backup check.
SVN:trunk[5143]
2017-11-30 08:48:59 +00:00
Pierre Goiffon
d80b890cd0 Fix warnings and errors in SynchroReplica
SVN:trunk[5142]
2017-11-29 14:30:10 +00:00
Pierre Goiffon
6b9c038b31 Mutex : add some comments
SVN:trunk[5141]
2017-11-27 17:00:03 +00:00
Guillaume Lajarige
b071f920e9 Portal: Typo in SCSS variable.
SVN:trunk[5140]
2017-11-25 14:53:20 +00:00
Pierre Goiffon
3cd28d1559 Some PHPDoc and small reformat
SVN:trunk[5139]
2017-11-23 17:42:24 +00:00
Guillaume Lajarige
72563d8ef1 Internal: Typos in XML comments.
SVN:trunk[5138]
2017-11-23 09:34:26 +00:00
Eric Espié
375798a344 N°1070 - Enhance ergonomics of "Add To Dashboard..." popup window
SVN:trunk[5137]
2017-11-22 14:40:28 +00:00
Eric Espié
b401c65684 N°1163 - GET_LOCK 64 characters limitation in MySQL
SVN:trunk[5135]
2017-11-21 12:14:20 +00:00
Guillaume Lajarige
d7c78b3ce2 Portal: Updated SCSS to de-hardcode some values.
SVN:trunk[5134]
2017-11-20 14:29:40 +00:00
Eric Espié
4a4c03a225 N°1160 - Fix error when sending notification with list of links.
SVN:trunk[5132]
2017-11-17 14:43:01 +00:00
Eric Espié
85b31701f4 N°1156 - Manual backup can be very long
* The database is saved in last position to avoid overhead when generating the archive file

SVN:trunk[5130]
2017-11-16 14:00:41 +00:00
Guillaume Lajarige
28b3110895 N°1157 Portal: Exception raised in BrowseBrick when one of the levels had no scope.
SVN:trunk[5129]
2017-11-16 09:05:02 +00:00
Pierre Goiffon
011e6d895b N.1151 compiler : throw an exception if a module contains an unknown menuId reference
SVN:trunk[5128]
2017-11-10 13:42:55 +00:00
Vincent Dumas
0f7099acfa dictionnary typo on Notification header message
SVN:trunk[5126]
2017-11-03 09:03:14 +00:00
Eric Espié
96296fe211 Duration KPI added on sending email.
SVN:trunk[5125]
2017-11-02 16:43:00 +00:00
Pierre Goiffon
51a60e637c Some PHPDoc
SVN:trunk[5124]
2017-10-31 17:08:25 +00:00
Pierre Goiffon
078f13fdb1 applicationcontext CrLf to Lf conversion
SVN:trunk[5123]
2017-10-31 15:38:30 +00:00
Denis Flaven
c210afd086 (Regression) 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:trunk[5121]
2017-10-31 10:29:05 +00:00
Guillaume Lajarige
81d9071b01 N°634.3 Portal: Argh!! Secondary actions menu in BrowseBrick was broken due to previous CSS "fixes"...
SVN:trunk[5119]
2017-10-30 16:03:58 +00:00
Romain Quetiez
94ca9c4df9 Ready for releasing...
SVN:trunk[5117]
2017-10-30 14:26:22 +00:00
Eric Espié
bff2ae319f 2.4.0 - New readme format.
SVN:trunk[5115]
2017-10-30 14:14:51 +00:00
Eric Espié
5bd30381cf Core russian translation kindly provided by Vladimir Kunin.
SVN:trunk[5113]
2017-10-30 11:34:39 +00:00
Pierre Goiffon
121a615ce3 UI.php : remove unused variables and change todo comment
SVN:trunk[5112]
2017-10-27 14:45:53 +00:00
Pierre Goiffon
5877b66c83 PHP Code Style : allow one line if with no braces
SVN:trunk[5111]
2017-10-27 14:45:47 +00:00
Stephen Abello
1ba86a91f9 added german translation for obsolescence. Thanks to ITOMIG.
SVN:trunk[5109]
2017-10-27 13:49:07 +00:00
Denis Flaven
e56847ee8d Simplification of the obsolescence conditions (N° 890) due to the risk of reaching the limit of 61 tables (N°1049)
SVN:trunk[5107]
2017-10-27 13:07:54 +00:00
Pierre Goiffon
1fbbfd1063 Portal SCSS : restore @extend inside @media, because this gives no error and do the job with scssphp lib
SVN:trunk[5105]
2017-10-27 08:56:59 +00:00
Pierre Goiffon
1fed66fff3 N.1117 some PHPDoc modifications
SVN:trunk[5104]
2017-10-27 08:56:45 +00:00
Guillaume Lajarige
c607a7e35d Internal: Updated modules version to 2.4.0 (as well as some copyright dates)
SVN:trunk[5103]
2017-10-27 08:53:39 +00:00
Pierre Goiffon
06d6968951 Fix invalid CSS and SCSS
SVN:trunk[5102]
2017-10-27 08:20:46 +00:00
Guillaume Lajarige
7ed8a9f638 N°1138 Portal: Scrollbar appeared sometimes in navigation menu when on the last brick.
SVN:trunk[5097]
2017-10-26 17:37:13 +00:00
Guillaume Lajarige
52595138cd N°930.2 Console UI: Better object details layout step 2.
SVN:trunk[5095]
2017-10-26 17:03:32 +00:00
Pierre Goiffon
eca2b01307 Code style : line endings Lf
SVN:trunk[5094]
2017-10-26 16:02:52 +00:00
Pierre Goiffon
df758679cc Change version number for static resources calls
SVN:trunk[5093]
2017-10-26 15:43:57 +00:00
Pierre Goiffon
6b6300d117 PHPStorm shared Inspections update : inconsistent line endings as WARN
SVN:trunk[5092]
2017-10-26 15:28:06 +00:00
Pierre Goiffon
b535e11f5a Change modules XML version to the latest (1.4)
SVN:trunk[5091]
2017-10-26 15:10:06 +00:00
Pierre Goiffon
a4ad8d0a61 Change version number in CSS url() calls
SVN:trunk[5090]
2017-10-26 09:28:22 +00:00
Pierre Goiffon
e66eb537d8 datatable refresh prb when source is in CrLf instead of Lf :
* add a try/catch block in the JS code
* properly escape string returned
This can happen for example when checking out with git-svn on Windows with core.autocrlf=auto

SVN:trunk[5089]
2017-10-26 08:58:23 +00:00
Pierre Goiffon
b8ef2e68ba Firsts settings for JS code style
SVN:trunk[5088]
2017-10-26 08:58:11 +00:00
Pierre Goiffon
30b10d3b6b Dict::S() PHPDoc modification
SVN:trunk[5087]
2017-10-26 08:57:54 +00:00
Eric Espié
f09347841c Fix utils::GetCurrentModuleUrl() introduced in revision 4920
SVN:trunk[5085]
2017-10-25 15:22:40 +00:00
Guillaume Lajarige
f87e8ca522 Translations: Added icon attribute for ServiceFamily and Service classes. English and French done, feel free to contribute for others ! :)
SVN:trunk[5080]
2017-10-24 15:13:51 +00:00
Guillaume Lajarige
2871f64f68 Internal: Updated sample data with avatar of new Combodo members ! (Bis)
SVN:trunk[5079]
2017-10-24 14:54:06 +00:00
Guillaume Lajarige
cd1c5f5799 Internal: Updated sample of ServiceFamily and Service classes to add icons (used in the mosaic mode of the portal's services catalog)
SVN:trunk[5078]
2017-10-24 14:39:03 +00:00
Pierre Goiffon
890fcac73f Fix another regression introduced in r5071 : module url were generated with arguments values that were url-encoded twice (so this leads to some errors when using them)
SVN:trunk[5077]
2017-10-24 14:02:59 +00:00
Guillaume Lajarige
d7851ed090 Internal: Updated sample data with avatar of new Combodo members !
SVN:trunk[5076]
2017-10-24 11:46:16 +00:00
Pierre Goiffon
678df3cc46 Fix regression introduced in r5071 (wooops), plus some PHPDoc
SVN:trunk[5074]
2017-10-24 08:07:07 +00:00
Eric Espié
2f48b2e302 N°1134 - Bad version number when MTP
* The order of the installed versions is changed when doing MTP.

SVN:trunk[5073]
2017-10-24 08:03:26 +00:00
Pierre Goiffon
a816a6ff8d cursor:pointer for label
SVN:trunk[5072]
2017-10-23 15:59:44 +00:00
Pierre Goiffon
b189d2a39b 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)
SVN:trunk[5071]
2017-10-23 15:59:34 +00:00
Guillaume Lajarige
5424682fdb N°634.2 Portal: CSS fixes on mosaic mode of BroweseBrick
SVN:trunk[5070]
2017-10-23 15:43:18 +00:00
Guillaume Lajarige
ad3ce7c536 Internal: Updated module version (itop-portal, itop-portal-base)
SVN:trunk[5069]
2017-10-23 13:49:35 +00:00
Eric Espié
d4dd300e28 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

SVN:trunk[5067]
2017-10-23 11:37:13 +00:00
Pierre Goiffon
99fd6b97db JQuery hotkeys plugin license
SVN:trunk[5066]
2017-10-23 09:09:56 +00:00
Pierre Goiffon
684e9e3537 Run query screen : new Ctrl+Return shortcut to execute query
SVN:trunk[5065]
2017-10-23 07:43:05 +00:00
Guillaume Lajarige
1bde863124 N°1123.5 Typo
SVN:trunk[5061]
2017-10-20 13:25:28 +00:00
Guillaume Lajarige
5c34e3d988 N°1123.4 AttributeImage: Better UI when editing in console. Also, export was showing url for empty value.
SVN:trunk[5060]
2017-10-20 13:22:38 +00:00
Eric Espié
b7c4e084f3 N°1131 - Setup: Can't add options to ITIL Ticketing in update mode
* Fix the disable attribute of the options

SVN:trunk[5057]
2017-10-20 11:54:05 +00:00
Guillaume Lajarige
36395ae355 N°642.3 Portal: Lifecycle: Exception on UserRequest opening due to a bad variable initialization.
SVN:trunk[5056]
2017-10-20 08:30:27 +00:00
Romain Quetiez
f4881d11c7 N.1100 Regression introduced in [r4943], aka 2.4 RC3, and causing error during MTP (accessing the wrong expression cache)
SVN:trunk[5054]
2017-10-19 19:06:40 +00:00
Eric Espié
bbde89e0f9 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:trunk[5053]
2017-10-19 14:39:15 +00:00
Pierre Goiffon
fb22107be8 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:trunk[5051]
2017-10-19 13:43:06 +00:00
Pierre Goiffon
5ada93b46c N.689 throw Exception if querying without needed finalClass attribute, plus some PHPDoc
SVN:trunk[5050]
2017-10-19 13:42:56 +00:00
Guillaume Lajarige
b798b43733 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:trunk[5046]
2017-10-19 12:57:44 +00:00
Guillaume Lajarige
1669eb3759 N°1071 UI: Better rendering for external keys in linkedset (no more wrapping)
SVN:trunk[5045]
2017-10-19 09:28:20 +00:00
Guillaume Lajarige
071316c606 N°1129.1 Support of $this->hyperlink(itop-portal)$ in notifications (only "portal" was implemented for the default portal).
SVN:trunk[5044]
2017-10-19 08:57:05 +00:00
Guillaume Lajarige
d8b5dd7bd2 N°1094.3 Portal: Forgot compiled .css on last commit. Not necessary but improves first page load after setup / MTP.
SVN:trunk[5042]
2017-10-18 14:58:23 +00:00
Guillaume Lajarige
868c1cface N°1123.3 Internal: Removed deprecated comment.
SVN:trunk[5040]
2017-10-18 14:50:39 +00:00
Guillaume Lajarige
8e83baf72b N°1094.2 Portal: Showing "icon" attribute on ServiceFamily and Service levels in "mosaic" mode of "services" BrowseBrick.
SVN:trunk[5038]
2017-10-18 14:29:53 +00:00
Guillaume Lajarige
54858c63f5 N°1094.1 Adding icon (AttributeImage) to ServiceFamily and Service classes
SVN:trunk[5037]
2017-10-18 14:27:40 +00:00
Guillaume Lajarige
bc3d03c462 N°1123.2 AttributeImage: default_image is no longer mandatory.
SVN:trunk[5036]
2017-10-18 13:46:17 +00:00
Eric Espié
c94476b9a2 N.1065 Fix performance issues.
* Added the bGetCount flag into the cache to differentiate the cache entries for COUNT only.

SVN:trunk[5034]
2017-10-18 12:53:23 +00:00
Guillaume Lajarige
73812dc400 N°1123 AttributeImage: PHP notice when displaying an object without default_image on a AttributeImage attribute.
SVN:trunk[5031]
2017-10-18 09:34:50 +00:00
Guillaume Lajarige
cfdc7eb74a N°911.2 Portal: Updated typeahead repository url in files headers.
SVN:trunk[5030]
2017-10-17 14:03:10 +00:00
Romain Quetiez
a0ad331023 Automatic tests: improved the automatic benchmark of all queries
SVN:trunk[5029]
2017-10-17 10:26:02 +00:00
Guillaume Lajarige
2561358f9d N°1122 Portal: Clean-up of 2 redundants JS files regarding the autocomplete inputs in forms.
SVN:trunk[5028]
2017-10-17 09:57:44 +00:00
Pierre Goiffon
3fd7dae8f9 N.1108 return exception if $bMustBeFound and result is archived
SVN:trunk[5025]
2017-10-16 14:42:17 +00:00
Guillaume Lajarige
426a0933b1 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:trunk[5023]
2017-10-13 13:25:20 +00:00
Eric Espié
2f8062d296 N°454 - Check data validity during CSV import
* The controls are only done on database integrity for the different keys.
* If retrofit to branch 2.4 take also the revisions: 4999, 5000, 5005, 5006

SVN:trunk[5022]
2017-10-13 12:28:09 +00:00
Romain Quetiez
d18165ebe9 Continuation of [r5015] Typo in FR dictionary, for SoftwareInstance/patch (backtick used instead of single quote)
SVN:trunk[5021]
2017-10-13 09:00:42 +00:00
Eric Espié
38796f9d0c N.1065 Fix performance issues.
* Does not cache requests containing "id NOT IN ..." (too specific)

SVN:trunk[5019]
2017-10-12 15:31:14 +00:00
Eric Espié
79b887d189 N°1110 - DataSynchro: PHP Notice Undefined Index
* Fix access to REQUEST_URI when called by script

SVN:trunk[5017]
2017-10-12 13:54:28 +00:00
Vincent Dumas
8dc92e7ccf FR dictionnary typo for Ticket status Waiting for approval
SVN:trunk[5015]
2017-10-12 13:31:56 +00:00
Eric Espié
e04e5913de N.1065 Fix performance issues.
* Does not cache requests containing "id IN ..." (too specific)

SVN:trunk[5012]
2017-10-12 11:58:48 +00:00
Eric Espié
5c734cdabc Message when no data are available
SVN:trunk[5011]
2017-10-12 11:54:02 +00:00
Guillaume Lajarige
f924e99f70 N°642.2 Portal: Lifecycle transitions security is now a blacklist instead of a white list. Making migration transparent and portal configuration easier.
SVN:trunk[5008]
2017-10-12 08:33:41 +00:00
Eric Espié
e179825896 N°1110 - DataSynchro: PHP Notice Undefined Index
* Fix access to REQUEST_URI when called by script

SVN:trunk[5007]
2017-10-12 07:52:39 +00:00
Eric Espié
94a561f0e4 N°454 - Check data validity during CSV import
* Cleanup expression construction

SVN:trunk[5006]
2017-10-12 07:43:36 +00:00
Eric Espié
56e14fc107 N°454 - Check data validity during CSV import
* "simulate" phase is more permissive on new hierarchical entries
* Better check during "apply" phase

SVN:trunk[5005]
2017-10-11 15:31:57 +00:00
Pierre Goiffon
29f0b74824 N.1108 Add PHPDoc
SVN:trunk[5004]
2017-10-11 13:20:42 +00:00
Pierre Goiffon
de682d5530 MetaModel code format
SVN:trunk[5003]
2017-10-11 13:20:24 +00:00
Pierre Goiffon
571a3341da Code style settings : fix function argument on new line
SVN:trunk[5002]
2017-10-11 13:20:05 +00:00
Pierre Goiffon
8edf7f2d60 Code Style settings modification
SVN:trunk[5001]
2017-10-11 09:43:30 +00:00
Eric Espié
5408545c07 N°454 - Check data validity during CSV import
* "simulate" phase is more permissive on new hierarchical entries
* Better check during "apply" phase

SVN:trunk[5000]
2017-10-11 09:28:23 +00:00
Eric Espié
d504fb209f N°454 - Check data validity during CSV import
* Added additional checks for external keys (including hierarchical ones)

SVN:trunk[4999]
2017-10-10 10:00:05 +00:00
Guillaume Lajarige
ee53c3a71e N°1107.1 Portal: Fixed image display in mosaic mode of BrowseBrick for Chrome and Firefox. IE still pending !
SVN:trunk[4998]
2017-10-09 13:58:05 +00:00
Eric Espié
bcf88d24f3 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.

SVN:trunk[4997]
2017-10-09 11:58:10 +00:00
Eric Espié
b2935139b4 N.1065 Fix performance issues.
Cache display CSV format fix.

SVN:trunk[4996]
2017-10-06 15:09:01 +00:00
Romain Quetiez
635e7cfeec 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:trunk[4993]
2017-10-06 14:06:01 +00:00
Eric Espié
49b6c3bed7 N.1065 Fix performance issues.
Add statistics on query table join optimization.

SVN:trunk[4992]
2017-10-06 13:53:06 +00:00
Denis Flaven
3f7ab67506 Preparing the connexion to the Hub.
SVN:trunk[4991]
2017-10-06 13:32:53 +00:00
Pierre Goiffon
df26833eb1 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:trunk[4983]
2017-10-05 15:53:44 +00:00
Guillaume Lajarige
df1ebaebf9 N°1104 DBObject::__toString() was way too verbose and returned all objects from linksets as string as well, causing memory limits.
SVN:trunk[4971]
2017-10-05 15:38:27 +00:00
Eric Espié
26bd04857d N°1098 Fix CSV import by id.
Fix a regression introduced in rev 4885.

SVN:trunk[4969]
2017-10-05 13:30:43 +00:00
Eric Espié
4c4ed14af5 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:trunk[4968]
2017-10-05 10:13:14 +00:00
Romain Quetiez
f0c5a1b382 Automatic tests: improved the error reporting
SVN:trunk[4967]
2017-10-05 09:57:48 +00:00
Guillaume Lajarige
59ebc49d46 N°1025 Portal: Fixed regression introduced in r4863.
SVN:trunk[4966]
2017-10-05 09:48:29 +00:00
Eric Espié
bfde101f6b 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:trunk[4965]
2017-10-05 09:10:00 +00:00
Pierre Goiffon
2a0dce848c N.1041 little changes on trunk[4963] : uses NOWDOC instead of HEREDOC syntax, and some variable renaming
SVN:trunk[4964]
2017-10-04 09:45:13 +00:00
Pierre Goiffon
d759fed5e4 N.1041 configuration editor : add focus and ctrl+s shortcut
SVN:trunk[4963]
2017-10-04 09:32:59 +00:00
Pierre Goiffon
f731abe4e8 GitIgnore : .hacks/
SVN:trunk[4962]
2017-10-04 09:09:09 +00:00
Guillaume Lajarige
74111212a3 N°1065 Fixed a regression introduced in r4965.
SVN:trunk[4961]
2017-10-03 14:02:45 +00:00
Eric Espié
f86c1a87f9 N.1065 Fix performance issues.
Limit APC emulation cache entries to avoid disk saturation.
New configuration entry added: 'apc_cache_emulation.max_entries'.

SVN:trunk[4960]
2017-10-03 13:53:53 +00:00
Romain Quetiez
1f2493914f N.1065 and #372 Query build cache not efficient with global search (each search generates about 80 new entries in the APCu cache)
SVN:trunk[4959]
2017-10-03 11:35:21 +00:00
Guillaume Lajarige
e3efa7dc3d N°1065 Fixed a regression introduced in r4965.
SVN:trunk[4958]
2017-10-03 11:22:01 +00:00
Vincent Dumas
6612782021 FR dictionnary typo for obsolescence
SVN:trunk[4957]
2017-10-03 10:19:22 +00:00
Eric Espié
bdaabcea93 N.1065 Fix performance issues.
ormLinkSet creates the objects on demand.

SVN:trunk[4956]
2017-10-03 09:22:33 +00:00
Guillaume Lajarige
e6b6be2624 N°1034 New EventOnObject class to store explaination on object's updates
SVN:trunk[4955]
2017-10-03 08:19:05 +00:00
Eric Espié
b1f1c10878 APC emulation using files when APC or APCu is not installed.
SVN:trunk[4954]
2017-10-02 07:30:41 +00:00
Denis Flaven
d5b0bb021f N°1806, N°1069: CSV and Excel export and import of documents (files) and images as URLs.
SVN:trunk[4952]
2017-09-30 09:23:06 +00:00
Guillaume Lajarige
dd70275b41 N°653.2 Lifecycle: Fixed a regression introduced on r4767, transition buttons not working properly when editing an object.
SVN:trunk[4951]
2017-09-29 08:23:13 +00:00
Guillaume Lajarige
6aa782bd8b N°1082 Fixed a regression introduced with ormLinkSet rework: Modified links not updated.
SVN:trunk[4950]
2017-09-28 15:31:57 +00:00
Guillaume Lajarige
029545703f N°1067 Rework on ormLinkSet BC with DBObjectSet.
- PHP notice are not thrown anymore, see PHPDoc instead.
- GetColumnAsArray() introduced.

SVN:trunk[4949]
2017-09-28 11:33:19 +00:00
Guillaume Lajarige
8183674fc6 Internal: Typo in PHPDoc
SVN:trunk[4948]
2017-09-27 12:51:36 +00:00
Romain Quetiez
7391f64776 N.1072 Localization for magic attributes archive_date (completed the existing implementation) and obsolescence_date (full implementation)
SVN:trunk[4946]
2017-09-27 12:26:44 +00:00
Pierre Goiffon
776385cdc9 Fix regression introduced in trunk[4943]
SVN:trunk[4945]
2017-09-27 10:00:48 +00:00
Eric Espié
17bafc037c Code cleanup.
SVN:trunk[4944]
2017-09-27 09:44:19 +00:00
Eric Espié
e785352050 N.1065 Fix performance issues (caches added on query build)
SVN:trunk[4943]
2017-09-27 09:37:43 +00:00
Guillaume Lajarige
43e4408df1 N°1073 Reentrance issue on cmdbAbstractObject when coming from an extension implementing iApplicationObjectExtension.
SVN:trunk[4942]
2017-09-27 09:27:08 +00:00
Denis Flaven
78a68bb361 Internal: Make sure that UI dialogs are never bigger than the browser's window (not used in iTop though).
SVN:trunk[4940]
2017-09-26 09:58:20 +00:00
Guillaume Lajarige
ec2a2d3505 Internal: Changed way AttributeLinkedSet check if two ormLinkSet are equal.
SVN:trunk[4939]
2017-09-26 08:05:04 +00:00
Guillaume Lajarige
2625477d35 Internal: Typo in itop-tickets XML comments
SVN:trunk[4938]
2017-09-25 10:26:55 +00:00
Pierre Goiffon
a655dd639d Wooops fix some mistakes on .git* files
SVN:trunk[4937]
2017-09-22 16:05:00 +00:00
Romain Quetiez
3e61fd2452 N.707 Export of custom fields: improved the xlsx format and implemented the spreadsheet format (both are aligned)
SVN:trunk[4935]
2017-09-22 13:37:44 +00:00
Pierre Goiffon
c11753d91c Some JetBrains project configuration files
SVN:trunk[4934]
2017-09-21 15:02:46 +00:00
Pierre Goiffon
5884e6b3cf Add JetBrains files in .gitignore
SVN:trunk[4933]
2017-09-21 15:02:18 +00:00
Pierre Goiffon
46e4ba4518 Add a gitbugtraq file (https://github.com/mstrap/bugtraq)
SVN:trunk[4932]
2017-09-21 14:58:37 +00:00
Denis Flaven
a9c9e48cdb 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:trunk[4930]
2017-09-21 14:27:54 +00:00
Vincent Dumas
e32c1a4447 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:trunk[4929]
2017-09-21 13:52:01 +00:00
Romain Quetiez
fb99c25594 N°813 Enable bulk deletion of Data Synchro Replica
SVN:trunk[4927]
2017-09-21 10:09:45 +00:00
Eric Espié
6011aa2ac9 Configuration file editor:
- support syntax highlighting and checking (ace editor)
- better "apply" and "reset" buttons management
- limit code injection when checking the configuration
- better syntax checking for PHP7

SVN:trunk[4926]
2017-09-20 15:07:05 +00:00
Romain Quetiez
048c1ecf72 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:trunk[4923]
2017-09-20 14:45:45 +00:00
Romain Quetiez
40360da454 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:trunk[4922]
2017-09-20 14:41:45 +00:00
Eric Espié
0ce9ff4557 Allow modules to provide license file (license.<module_name>.xml) with same format as setup/licenses/community_license.xml
SVN:trunk[4921]
2017-09-20 09:21:31 +00:00
Eric Espié
625bfbb6fe Fix utils::GetCurrentModuleUrl() introduced in revision 4844
SVN:trunk[4920]
2017-09-19 08:12:39 +00:00
Guillaume Lajarige
4290d94841 N°1047.1 Internal: Modified some calls to the ITOP_XXX constants.
SVN:trunk[4915]
2017-09-15 15:46:03 +00:00
Guillaume Lajarige
8ff2151448 N°1006.2 Templates: $this->raw(attcode)$ can be used to display an date(time) attribute in the SQL format like before.
SVN:trunk[4914]
2017-09-15 14:26:15 +00:00
Guillaume Lajarige
610d69fb2e N°1006 Templates: Date & time format is now applied when using a date(time) attribute in a placeholder (eg. Notifications)
SVN:trunk[4913]
2017-09-15 13:49:25 +00:00
Guillaume Lajarige
822308b3a4 N°1019 Portal: OpenSans font embedded in iTop instead of fetching from google servers.
SVN:trunk[4912]
2017-09-15 09:18:51 +00:00
Guillaume Lajarige
c1d1e562ad N°762 Portal: Pre-filtering a browse brick in tree mode was making tree collapsing instead of showing results.
SVN:trunk[4911]
2017-09-14 15:10:57 +00:00
Guillaume Lajarige
379a0bd785 N°1038 Fatal error on transition with AttributeBlob or AttributeCaseLog
SVN:trunk[4907]
2017-09-13 15:59:06 +00:00
Denis Flaven
a477443c8d Adding E and F glyphs
SVN:trunk[4905]
2017-09-13 08:51:19 +00:00
Denis Flaven
ed693c03ab Combodo font enhancements.
SVN:trunk[4903]
2017-09-12 14:48:09 +00:00
1842 changed files with 140160 additions and 28786 deletions

View File

@@ -54,7 +54,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
{
$aParams = array
(
"category" => "addon/userrights",
"category" => "addon/userrights,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -137,11 +137,8 @@ class URP_Profiles extends UserRightsBaseClassGUI
$oUserRights = UserRights::GetModuleInstance();
$aDisplayData = array();
foreach (MetaModel::GetClasses('bizmodel') as $sClass)
foreach (MetaModel::GetClasses('bizmodel,grant_by_profile') as $sClass)
{
// Skip non instantiable classes
if (MetaModel::IsAbstract($sClass)) continue;
$aStimuli = array();
foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus)
{
@@ -183,7 +180,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
if (!$bEditMode)
{
$oPage->SetCurrentTab(Dict::S('UI:UserManagement:GrantMatrix'));
$this->DoShowGrantSumary($oPage);
$this->DoShowGrantSumary($oPage);
}
}
@@ -239,7 +236,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
{
$aParams = array
(
"category" => "addon/userrights",
"category" => "addon/userrights,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "userid",
"state_attcode" => "",
@@ -284,6 +281,34 @@ class URP_UserProfile extends UserRightsBaseClassGUI
}
return parent::CheckToDelete($oDeletionPlan);
}
protected function OnInsert()
{
$this->CheckIfProfileIsAllowed(UR_ACTION_CREATE);
}
protected function OnUpdate()
{
$this->CheckIfProfileIsAllowed(UR_ACTION_MODIFY);
}
protected function OnDelete()
{
$this->CheckIfProfileIsAllowed(UR_ACTION_DELETE);
}
protected function CheckIfProfileIsAllowed($iActionCode)
{
if (!UserRights::IsActionAllowed(get_class($this), $iActionCode, DBObjectSet::FromObject($this)))
{
throw new SecurityException(Dict::Format('UI:Error:ObjectCannotBeUpdated'));
}
if (UserRights::IsLoggedIn() && !UserRights::IsAdministrator() && ($this->Get('profile') === ADMIN_PROFILE_NAME))
{
throw new SecurityException(Dict::Format('UI:Login:Error:AccessAdmin'));
}
}
}
class URP_UserOrg extends UserRightsBaseClassGUI
@@ -292,7 +317,7 @@ class URP_UserOrg extends UserRightsBaseClassGUI
{
$aParams = array
(
"category" => "addon/userrights",
"category" => "addon/userrights,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "userid",
"state_attcode" => "",
@@ -412,11 +437,15 @@ class UserRightsProfile extends UserRightsAddOnAPI
/**
* Read and cache organizations allowed to the given user
*
* @param oUser
* @param sClass -not used here but can be used in overloads
*
* @param $oUser
* @param $sClass (not used here but can be used in overloads)
*
* @return array
* @throws \CoreException
* @throws \Exception
*/
protected function GetUserOrgs($oUser, $sClass)
public function GetUserOrgs($oUser, $sClass)
{
$iUser = $oUser->GetKey();
if (!array_key_exists($iUser, $this->m_aUserOrgs))
@@ -430,7 +459,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oUserOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sUserOrgQuery), array(), array('userid' => $iUser));
while ($aRow = $oUserOrgSet->FetchAssoc())
{
$oUserOrg = $aRow['UserOrg'];
$oOrg = $aRow['Org'];
$this->m_aUserOrgs[$iUser][] = $oOrg->GetKey();
}

View File

@@ -613,11 +613,15 @@ class UserRightsProfile extends UserRightsAddOnAPI
/**
* Read and cache organizations allowed to the given user
*
* @param oUser
* @param sClass -not used here but can be used in overloads
*
* @param $oUser
* @param $sClass (not used here but can be used in overloads)
*
* @return array
* @throws \CoreException
* @throws \Exception
*/
protected function GetUserOrgs($oUser, $sClass)
public function GetUserOrgs($oUser, $sClass)
{
$iUser = $oUser->GetKey();
if (!array_key_exists($iUser, $this->m_aUserOrgs))
@@ -631,7 +635,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oUserOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sUserOrgQuery), array(), array('userid' => $iUser));
while ($aRow = $oUserOrgSet->FetchAssoc())
{
$oUserOrg = $aRow['UserOrg'];
$oOrg = $aRow['Org'];
$this->m_aUserOrgs[$iUser][] = $oOrg->GetKey();
}

View File

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

View File

@@ -1,361 +1,362 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class ApplicationContext
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT."/application/utils.inc.php");
/**
* Interface for directing end-users to the relevant application
*/
interface iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId);
}
/**
* Direct end-users to the standard iTop application: UI.php
*/
class iTopStandardURLMaker implements iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId)
{
$sPage = DBObject::ComputeStandardUIPage($sClass);
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
$sUrl = "{$sAbsoluteUrl}pages/$sPage?operation=details&class=$sClass&id=$iId";
return $sUrl;
}
}
/**
* Direct end-users to the standard Portal application
*/
class PortalURLMaker implements iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId)
{
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
$sUrl = "{$sAbsoluteUrl}portal/index.php?operation=details&class=$sClass&id=$iId";
return $sUrl;
}
}
/**
* Helper class to store and manipulate the parameters that make the application's context
*
* Usage:
* 1) Build the application's context by constructing the object
* (the object will read some of the page's parameters)
*
* 2) Add these parameters to hyperlinks or to forms using the helper, functions
* GetForLink(), GetForForm() or GetAsHash()
*/
class ApplicationContext
{
protected $aNames;
protected $aValues;
protected static $aDefaultValues; // Cache shared among all instances
public function __construct($bReadContext = true)
{
$this->aNames = array(
'org_id', 'menu'
);
if ($bReadContext)
{
$this->ReadContext();
}
}
/**
* Read the context directly in the PHP parameters (either POST or GET)
* return nothing
*/
protected function ReadContext()
{
if (!isset(self::$aDefaultValues))
{
self::$aDefaultValues = array();
$aContext = utils::ReadParam('c', array(), false, 'context_param');
foreach($this->aNames as $sName)
{
$sValue = isset($aContext[$sName]) ? $aContext[$sName] : '';
// TO DO: check if some of the context parameters are mandatory (or have default values)
if (!empty($sValue))
{
self::$aDefaultValues[$sName] = $sValue;
}
// Hmm, there must be a better (more generic) way to handle the case below:
// When there is only one possible (allowed) organization, the context must be
// fixed to this org
if ($sName == 'org_id')
{
if (MetaModel::IsValidClass('Organization'))
{
$oSearchFilter = new DBObjectSearch('Organization');
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oSet = new CMDBObjectSet($oSearchFilter);
if ($oSet->Count(2) == 1)
{
// Only one possible value for org_id, set it in the context
$oOrg = $oSet->Fetch();
self::$aDefaultValues[$sName] = $oOrg->GetKey();
}
}
}
}
}
$this->aValues = self::$aDefaultValues;
}
/**
* Returns the current value for the given parameter
* @param string $sParamName Name of the parameter to read
* @return mixed The value for this parameter
*/
public function GetCurrentValue($sParamName, $defaultValue = '')
{
if (isset($this->aValues[$sParamName]))
{
return $this->aValues[$sParamName];
}
return $defaultValue;
}
/**
* Returns the context as string with the format name1=value1&name2=value2....
* return string The context as a string to be appended to an href property
*/
public function GetForLink()
{
$aParams = array();
foreach($this->aValues as $sName => $sValue)
{
$aParams[] = "c[$sName]".'='.urlencode($sValue);
}
return implode("&", $aParams);
}
/**
* Returns the context as sequence of input tags to be inserted inside a <form> tag
* return string The context as a sequence of <input type="hidden" /> tags
*/
public function GetForForm()
{
$sContext = "";
foreach($this->aValues as $sName => $sValue)
{
$sContext .= "<input type=\"hidden\" name=\"c[$sName]\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n";
}
return $sContext;
}
/**
* Returns the context as a hash array 'parameter_name' => value
* return array The context information
*/
public function GetAsHash()
{
$aReturn = array();
foreach($this->aValues as $sName => $sValue)
{
$aReturn["c[$sName]"] = $sValue;
}
return $aReturn;
}
/**
* Returns an array of the context parameters NAMEs
* @return array The list of context parameters
*/
public function GetNames()
{
return $this->aNames;
}
/**
* Removes the specified parameter from the context, for example when the same parameter
* is already a search parameter
* @param string $sParamName Name of the parameter to remove
* @return none
*/
public function Reset($sParamName)
{
if (isset($this->aValues[$sParamName]))
{
unset($this->aValues[$sParamName]);
}
}
/**
* Initializes the given object with the default values provided by the context
*/
public function InitObjectFromContext(DBObject &$oObj)
{
$sClass = get_class($oObj);
foreach($this->GetNames() as $key)
{
$aCallSpec = array($sClass, 'MapContextParam');
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
if (MetaModel::IsValidAttCode($sClass, $sAttCode))
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->IsWritable())
{
$value = $this->GetCurrentValue($key, null);
if (!is_null($value))
{
$oObj->Set($sAttCode, $value);
}
}
}
}
}
}
static $m_sUrlMakerClass = null;
/**
* Set the current application url provider
* @param sClass string Class implementing iDBObjectURLMaker
* @return void
*/
public static function SetUrlMakerClass($sClass = 'iTopStandardURLMaker')
{
$sPrevious = self::GetUrlMakerClass();
self::$m_sUrlMakerClass = $sClass;
$_SESSION['UrlMakerClass'] = $sClass;
return $sPrevious;
}
/**
* Get the current application url provider
* @return string the name of the class
*/
public static function GetUrlMakerClass()
{
if (is_null(self::$m_sUrlMakerClass))
{
if (isset($_SESSION['UrlMakerClass']))
{
self::$m_sUrlMakerClass = $_SESSION['UrlMakerClass'];
}
else
{
self::$m_sUrlMakerClass = 'iTopStandardURLMaker';
}
}
return self::$m_sUrlMakerClass;
}
/**
* Get the current application url provider
* @return string the name of the class
*/
public static function MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass = null, $bWithNavigationContext = true)
{
$oAppContext = new ApplicationContext();
if (is_null($sUrlMakerClass))
{
$sUrlMakerClass = self::GetUrlMakerClass();
}
$sUrl = call_user_func(array($sUrlMakerClass, 'MakeObjectUrl'), $sObjClass, $sObjKey);
if (strlen($sUrl) > 0)
{
if ($bWithNavigationContext)
{
return $sUrl."&".$oAppContext->GetForLink();
}
else
{
return $sUrl;
}
}
else
{
return '';
}
}
protected static $m_aPluginProperties = null;
/**
* Load plugin properties for the current session
* @return void
*/
protected static function LoadPluginProperties()
{
if (isset($_SESSION['PluginProperties']))
{
self::$m_aPluginProperties = $_SESSION['PluginProperties'];
}
else
{
self::$m_aPluginProperties = array();
}
}
/**
* Set plugin properties
* @param sPluginClass string Class implementing any plugin interface
* @param sProperty string Name of the property
* @param value scalar Value (numeric or string)
* @return void
*/
public static function SetPluginProperty($sPluginClass, $sProperty, $value)
{
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
self::$m_aPluginProperties[$sPluginClass][$sProperty] = $value;
$_SESSION['PluginProperties'][$sPluginClass][$sProperty] = $value;
}
/**
* Get plugin properties
* @param sPluginClass string Class implementing any plugin interface
* @return array of sProperty=>value pairs
*/
public static function GetPluginProperties($sPluginClass)
{
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
if (array_key_exists($sPluginClass, self::$m_aPluginProperties))
{
return self::$m_aPluginProperties[$sPluginClass];
}
else
{
return array();
}
}
}
?>
<?php
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class ApplicationContext
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT."/application/utils.inc.php");
/**
* Interface for directing end-users to the relevant application
*/
interface iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId);
}
/**
* Direct end-users to the standard iTop application: UI.php
*/
class iTopStandardURLMaker implements iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId)
{
$sPage = DBObject::ComputeStandardUIPage($sClass);
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
$sUrl = "{$sAbsoluteUrl}pages/$sPage?operation=details&class=$sClass&id=$iId";
return $sUrl;
}
}
/**
* Direct end-users to the standard Portal application
*/
class PortalURLMaker implements iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId)
{
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
$sUrl = "{$sAbsoluteUrl}portal/index.php?operation=details&class=$sClass&id=$iId";
return $sUrl;
}
}
/**
* Helper class to store and manipulate the parameters that make the application's context
*
* Usage:
* 1) Build the application's context by constructing the object
* (the object will read some of the page's parameters)
*
* 2) Add these parameters to hyperlinks or to forms using the helper, functions
* GetForLink(), GetForForm() or GetAsHash()
*/
class ApplicationContext
{
protected $aNames;
protected $aValues;
protected static $aDefaultValues; // Cache shared among all instances
public function __construct($bReadContext = true)
{
$this->aNames = array(
'org_id', 'menu'
);
if ($bReadContext)
{
$this->ReadContext();
}
}
/**
* Read the context directly in the PHP parameters (either POST or GET)
* return nothing
*/
protected function ReadContext()
{
if (!isset(self::$aDefaultValues))
{
self::$aDefaultValues = array();
$aContext = utils::ReadParam('c', array(), false, 'context_param');
foreach($this->aNames as $sName)
{
$sValue = isset($aContext[$sName]) ? $aContext[$sName] : '';
// TO DO: check if some of the context parameters are mandatory (or have default values)
if (!empty($sValue))
{
self::$aDefaultValues[$sName] = $sValue;
}
// Hmm, there must be a better (more generic) way to handle the case below:
// When there is only one possible (allowed) organization, the context must be
// fixed to this org
if ($sName == 'org_id')
{
if (MetaModel::IsValidClass('Organization'))
{
$oSearchFilter = new DBObjectSearch('Organization');
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oSet = new CMDBObjectSet($oSearchFilter);
$iCount = $oSet->Count();
if ($iCount == 1)
{
// Only one possible value for org_id, set it in the context
$oOrg = $oSet->Fetch();
self::$aDefaultValues[$sName] = $oOrg->GetKey();
}
}
}
}
}
$this->aValues = self::$aDefaultValues;
}
/**
* Returns the current value for the given parameter
* @param string $sParamName Name of the parameter to read
* @return mixed The value for this parameter
*/
public function GetCurrentValue($sParamName, $defaultValue = '')
{
if (isset($this->aValues[$sParamName]))
{
return $this->aValues[$sParamName];
}
return $defaultValue;
}
/**
* Returns the context as string with the format name1=value1&name2=value2....
* return string The context as a string to be appended to an href property
*/
public function GetForLink()
{
$aParams = array();
foreach($this->aValues as $sName => $sValue)
{
$aParams[] = "c[$sName]".'='.urlencode($sValue);
}
return implode("&", $aParams);
}
/**
* Returns the context as sequence of input tags to be inserted inside a <form> tag
* return string The context as a sequence of <input type="hidden" /> tags
*/
public function GetForForm()
{
$sContext = "";
foreach($this->aValues as $sName => $sValue)
{
$sContext .= "<input type=\"hidden\" name=\"c[$sName]\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n";
}
return $sContext;
}
/**
* Returns the context as a hash array 'parameter_name' => value
* return array The context information
*/
public function GetAsHash()
{
$aReturn = array();
foreach($this->aValues as $sName => $sValue)
{
$aReturn["c[$sName]"] = $sValue;
}
return $aReturn;
}
/**
* Returns an array of the context parameters NAMEs
* @return array The list of context parameters
*/
public function GetNames()
{
return $this->aNames;
}
/**
* Removes the specified parameter from the context, for example when the same parameter
* is already a search parameter
* @param string $sParamName Name of the parameter to remove
* @return none
*/
public function Reset($sParamName)
{
if (isset($this->aValues[$sParamName]))
{
unset($this->aValues[$sParamName]);
}
}
/**
* Initializes the given object with the default values provided by the context
*/
public function InitObjectFromContext(DBObject &$oObj)
{
$sClass = get_class($oObj);
foreach($this->GetNames() as $key)
{
$aCallSpec = array($sClass, 'MapContextParam');
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
if (MetaModel::IsValidAttCode($sClass, $sAttCode))
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->IsWritable())
{
$value = $this->GetCurrentValue($key, null);
if (!is_null($value))
{
$oObj->Set($sAttCode, $value);
}
}
}
}
}
}
static $m_sUrlMakerClass = null;
/**
* Set the current application url provider
* @param sClass string Class implementing iDBObjectURLMaker
* @return void
*/
public static function SetUrlMakerClass($sClass = 'iTopStandardURLMaker')
{
$sPrevious = self::GetUrlMakerClass();
self::$m_sUrlMakerClass = $sClass;
$_SESSION['UrlMakerClass'] = $sClass;
return $sPrevious;
}
/**
* Get the current application url provider
* @return string the name of the class
*/
public static function GetUrlMakerClass()
{
if (is_null(self::$m_sUrlMakerClass))
{
if (isset($_SESSION['UrlMakerClass']))
{
self::$m_sUrlMakerClass = $_SESSION['UrlMakerClass'];
}
else
{
self::$m_sUrlMakerClass = 'iTopStandardURLMaker';
}
}
return self::$m_sUrlMakerClass;
}
/**
* Get the current application url provider
* @return string the name of the class
*/
public static function MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass = null, $bWithNavigationContext = true)
{
$oAppContext = new ApplicationContext();
if (is_null($sUrlMakerClass))
{
$sUrlMakerClass = self::GetUrlMakerClass();
}
$sUrl = call_user_func(array($sUrlMakerClass, 'MakeObjectUrl'), $sObjClass, $sObjKey);
if (strlen($sUrl) > 0)
{
if ($bWithNavigationContext)
{
return $sUrl."&".$oAppContext->GetForLink();
}
else
{
return $sUrl;
}
}
else
{
return '';
}
}
protected static $m_aPluginProperties = null;
/**
* Load plugin properties for the current session
* @return void
*/
protected static function LoadPluginProperties()
{
if (isset($_SESSION['PluginProperties']))
{
self::$m_aPluginProperties = $_SESSION['PluginProperties'];
}
else
{
self::$m_aPluginProperties = array();
}
}
/**
* Set plugin properties
* @param sPluginClass string Class implementing any plugin interface
* @param sProperty string Name of the property
* @param value scalar Value (numeric or string)
* @return void
*/
public static function SetPluginProperty($sPluginClass, $sProperty, $value)
{
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
self::$m_aPluginProperties[$sPluginClass][$sProperty] = $value;
$_SESSION['PluginProperties'][$sPluginClass][$sProperty] = $value;
}
/**
* Get plugin properties
* @param sPluginClass string Class implementing any plugin interface
* @return array of sProperty=>value pairs
*/
public static function GetPluginProperties($sPluginClass)
{
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
if (array_key_exists($sPluginClass, self::$m_aPluginProperties))
{
return self::$m_aPluginProperties[$sPluginClass];
}
else
{
return array();
}
}
}
?>

View File

@@ -34,7 +34,7 @@ class AuditCategory extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application",
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",

View File

@@ -35,7 +35,7 @@ class AuditRule extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application",
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -57,8 +57,8 @@ class AuditRule extends cmdbAbstractObject
MetaModel::Init_SetZListItems('details', array('category_id', 'name', 'description', 'query', 'valid_flag')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('category_id', 'description', 'valid_flag')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('category_id', 'name', 'description', 'valid_flag')); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', array('category_id', 'name', 'description', 'valid_flag', 'query')); // Criteria of the advanced search form
MetaModel::Init_SetZListItems('standard_search', array('category_id', 'name', 'description', 'valid_flag', 'query')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'category_id')); // Criteria of the advanced search form
}
}
?>

View File

@@ -45,6 +45,11 @@ require_once(APPROOT.'/application/ui.extkeywidget.class.inc.php');
require_once(APPROOT.'/application/ui.htmleditorwidget.class.inc.php');
require_once(APPROOT.'/application/datatable.class.inc.php');
require_once(APPROOT.'/sources/renderer/console/consoleformrenderer.class.inc.php');
require_once(APPROOT.'/sources/application/search/searchform.class.inc.php');
require_once(APPROOT.'/sources/application/search/criterionparser.class.inc.php');
require_once(APPROOT.'/sources/application/search/criterionconversionabstract.class.inc.php');
require_once(APPROOT.'/sources/application/search/criterionconversion/criteriontooql.class.inc.php');
require_once(APPROOT.'/sources/application/search/criterionconversion/criteriontosearchform.class.inc.php');
abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
{
@@ -604,7 +609,7 @@ EOF
function GetBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix, $aExtraParams = array())
{
$sHtml = '';
$oAppContext = new ApplicationContext();
$oAppContext = new ApplicationContext();
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
$aDetails = array();
$sClass = get_class($this);
@@ -620,28 +625,28 @@ EOF
$aFieldsComments = (isset($aExtraParams['fieldsComments'])) ? $aExtraParams['fieldsComments'] : array();
$aExtraFlags = (isset($aExtraParams['fieldsFlags'])) ? $aExtraParams['fieldsFlags'] : array();
$bFieldComments = (count($aFieldsComments) > 0);
foreach($aDetailsStruct as $sTab => $aCols )
foreach($aDetailsStruct as $sTab => $aCols)
{
$aDetails[$sTab] = array();
$aTableStyles[] = 'vertical-align:top';
$aTableClasses = array();
$aTableStyles[] = 'vertical-align:top';
$aTableClasses = array();
$aColStyles[] = 'vertical-align:top';
$aColClasses = array();
ksort($aCols);
ksort($aCols);
$iColCount = count($aCols);
if($iColCount > 1)
{
$aTableClasses[] = 'n-cols-details';
$aTableClasses[] = $iColCount.'-cols-details';
if ($iColCount > 1)
{
$aTableClasses[] = 'n-cols-details';
$aTableClasses[] = $iColCount.'-cols-details';
$aColStyles[] = 'width:'.floor(100/$iColCount).'%';
}
else
{
$aTableClasses[] = 'one-col-details';
}
$aColStyles[] = 'width:'.floor(100 / $iColCount).'%';
}
else
{
$aTableClasses[] = 'one-col-details';
}
$oPage->SetCurrentTab(Dict::S($sTab));
$oPage->add('<table style="'.implode('; ', $aTableStyles).'" class="'.implode(' ', $aTableClasses).'" data-mode="'.$sEditMode.'"><tr>');
@@ -654,7 +659,7 @@ EOF
$aDetails[$sTab][$sColIndex] = array();
foreach($aFieldsets as $sFieldsetName => $aFields)
{
if (!empty($sFieldsetName) && ($sFieldsetName[0] != '_'))
if (!empty($sFieldsetName) && ($sFieldsetName[0] != '_'))
{
$sLabel = $sFieldsetName;
}
@@ -683,90 +688,93 @@ EOF
{
$sComments = isset($aFieldsComments[$sAttCode]) ? $aFieldsComments[$sAttCode] : '';
$sInfos = '';
$iFlags = $this->GetFormAttributeFlags($sAttCode);
if (array_key_exists($sAttCode, $aExtraFlags))
{
// the caller may override some flags if needed
$iFlags = $iFlags | $aExtraFlags[$sAttCode];
}
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ( (!$oAttDef->IsLinkSet()) && (($iFlags & OPT_ATT_HIDDEN) == 0))
{
$sInputId = $this->m_iFormId.'_'.$sAttCode;
if ($oAttDef->IsWritable())
$sComments = isset($aFieldsComments[$sAttCode]) ? $aFieldsComments[$sAttCode] : '';
$sInfos = '';
$iFlags = $this->GetFormAttributeFlags($sAttCode);
if (array_key_exists($sAttCode, $aExtraFlags))
{
if ($sStateAttCode == $sAttCode)
// the caller may override some flags if needed
$iFlags = $iFlags | $aExtraFlags[$sAttCode];
}
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ((!$oAttDef->IsLinkSet()) && (($iFlags & OPT_ATT_HIDDEN) == 0))
{
$sInputId = $this->m_iFormId.'_'.$sAttCode;
if ($oAttDef->IsWritable())
{
// State attribute is always read-only from the UI
$sHTMLValue = $this->GetStateLabel();
$val = array('label' => '<label>'.$oAttDef->GetLabel().'</label>', 'value' => $sHTMLValue, 'comments' => $sComments, 'infos' => $sInfos, 'attcode' => $sAttCode);
}
else
{
if ($iFlags & (OPT_ATT_READONLY|OPT_ATT_SLAVE))
if ($sStateAttCode == $sAttCode)
{
// Check if the attribute is not read-only because of a synchro...
if ($iFlags & OPT_ATT_SLAVE)
{
$aReasons = array();
$iSynchroFlags = $this->GetSynchroReplicaFlags($sAttCode, $aReasons);
$sSynchroIcon = "&nbsp;<img id=\"synchro_$sInputId\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>";
$sTip = '';
foreach($aReasons as $aRow)
{
$sDescription = htmlentities($aRow['description'], ENT_QUOTES, 'UTF-8');
$sDescription = str_replace(array("\r\n", "\n"), "<br/>", $sDescription);
$sTip .= "<div class='synchro-source'>";
$sTip .= "<div class='synchro-source-title'>Synchronized with {$aRow['name']}</div>";
$sTip .= "<div class='synchro-source-description'>$sDescription</div>";
}
$sTip = addslashes($sTip);
$oPage->add_ready_script("$('#synchro_$sInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
$sComments = $sSynchroIcon;
}
// Attribute is read-only
$sHTMLValue = "<span id=\"field_{$sInputId}\">".$this->GetAsHTML($sAttCode).'</span>';
// State attribute is always read-only from the UI
$sHTMLValue = $this->GetStateLabel();
$val = array('label' => '<label>'.$oAttDef->GetLabel().'</label>', 'value' => $sHTMLValue, 'comments' => $sComments, 'infos' => $sInfos, 'attcode' => $sAttCode);
}
else
{
$sValue = $this->Get($sAttCode);
$sDisplayValue = $this->GetEditValue($sAttCode);
$aArgs = array('this' => $this, 'formPrefix' => $sPrefix);
$sHTMLValue = "".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'';
if ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE))
{
// Check if the attribute is not read-only because of a synchro...
if ($iFlags & OPT_ATT_SLAVE)
{
$aReasons = array();
$iSynchroFlags = $this->GetSynchroReplicaFlags($sAttCode, $aReasons);
$sSynchroIcon = "&nbsp;<img id=\"synchro_$sInputId\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>";
$sTip = '';
foreach($aReasons as $aRow)
{
$sDescription = htmlentities($aRow['description'], ENT_QUOTES, 'UTF-8');
$sDescription = str_replace(array("\r\n", "\n"), "<br/>", $sDescription);
$sTip .= "<div class='synchro-source'>";
$sTip .= "<div class='synchro-source-title'>Synchronized with {$aRow['name']}</div>";
$sTip .= "<div class='synchro-source-description'>$sDescription</div>";
}
$sTip = addslashes($sTip);
$oPage->add_ready_script("$('#synchro_$sInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
$sComments = $sSynchroIcon;
}
// Attribute is read-only
$sHTMLValue = "<span id=\"field_{$sInputId}\">".$this->GetAsHTML($sAttCode).'</span>';
}
else
{
$sValue = $this->Get($sAttCode);
$sDisplayValue = $this->GetEditValue($sAttCode);
$aArgs = array('this' => $this, 'formPrefix' => $sPrefix);
$sHTMLValue = "".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'';
}
$aFieldsMap[$sAttCode] = $sInputId;
$val = array('label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().'</span>', 'value' => $sHTMLValue, 'comments' => $sComments, 'infos' => $sInfos, 'attcode' => $sAttCode);
}
}
else
{
$val = array(
'label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().'</span>',
'value' => "<span id=\"field_{$sInputId}\">".$this->GetAsHTML($sAttCode)."</span>",
'comments' => $sComments,
'infos' => $sInfos,
'attcode' => $sAttCode
);
$aFieldsMap[$sAttCode] = $sInputId;
$val = array('label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().'</span>', 'value' => $sHTMLValue, 'comments' => $sComments, 'infos' => $sInfos, 'attcode' => $sAttCode);
}
// Checking how the field should be rendered
// Note: For view mode, this is done in cmdbAbstractObject::GetFieldAsHtml()
// Note 2: Shouldn't this be a property of the AttDef instead an array that we have to maintain?
if (in_array($oAttDef->GetEditClass(), array('Text', 'HTML', 'CaseLog', 'CustomFields', 'OQLExpression')))
{
$val['layout'] = 'large';
}
else
{
$val['layout'] = 'small';
}
}
else
{
$val = array('label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().'</span>', 'value' => "<span id=\"field_{$sInputId}\">".$this->GetAsHTML($sAttCode)."</span>", 'comments' => $sComments, 'infos' => $sInfos, 'attcode' => $sAttCode);
$aFieldsMap[$sAttCode] = $sInputId;
$val = null; // Skip this field
}
// Checking how the field should be rendered
// Note: For view mode, this is done in cmdbAbstractObject::GetFieldAsHtml()
// Note 2: Shouldn't this be a property of the AttDef instead an array that we have to maintain?
if(in_array($oAttDef->GetEditClass(), array('Text', 'HTML', 'CaseLog', 'CustomFields', 'OQLExpression')))
{
$val['layout'] = 'large';
}
else
{
$val['layout'] = 'small';
}
}
else
{
$val = null; // Skip this field
}
}
else
{
@@ -776,10 +784,10 @@ EOF
if ($val != null)
{
// The field is visible, add it to the current column
// The field is visible, add it to the current column
$aDetails[$sTab][$sColIndex][] = $val;
$iInputId++;
}
}
}
}
if (!empty($sPreviousLabel))
@@ -796,6 +804,7 @@ EOF
}
$oPage->add('</tr></table>');
}
return $aFieldsMap;
}
@@ -814,6 +823,7 @@ EOF
{
// Object's details
// template not found display the object using the *old style*
$oPage->add('<div id="search-widget-results-outer">');
$this->DisplayBareHeader($oPage, $bEditMode);
$oPage->AddTabContainer(OBJECT_PROPERTIES_TAB);
$oPage->SetCurrentTabContainer(OBJECT_PROPERTIES_TAB);
@@ -823,6 +833,7 @@ EOF
//$oPage->SetCurrentTab(Dict::S('UI:HistoryTab'));
//$this->DisplayBareHistory($oPage, $bEditMode);
$oPage->AddAjaxTab(Dict::S('UI:HistoryTab'), utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=history&class='.get_class($this).'&id='.$this->GetKey());
$oPage->add('</div>');
}
}
@@ -1053,6 +1064,7 @@ EOF
$aExtraFieldsRaw = isset($aExtraParams['extra_fields']) ? explode(',', trim($aExtraParams['extra_fields'])) : array();
$aExtraFields = array();
$sAttCode = '';
foreach ($aExtraFieldsRaw as $sFieldName)
{
// Ignore attributes not of the main queried class
@@ -1077,7 +1089,7 @@ EOF
$aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if ( (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS)) &&
if ( (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO) &&
( (count($aDisplayAliases) == 0) || (in_array($sAlias, $aDisplayAliases))) )
{
$aAuthorizedClasses[$sAlias] = $sClassName;
@@ -1166,7 +1178,7 @@ EOF
$aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO)
{
$aAuthorizedClasses[$sAlias] = $sClassName;
}
@@ -1302,7 +1314,7 @@ EOF
$aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO)
{
$aAuthorizedClasses[$sAlias] = $sClassName;
}
@@ -1465,7 +1477,7 @@ EOF
$aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO)
{
$aAuthorizedClasses[$sAlias] = $sClassName;
}
@@ -1526,238 +1538,21 @@ EOF
$oPage->add(self::GetSearchForm($oPage, $oSet, $aExtraParams));
}
/**
* @param WebPage $oPage
* @param CMDBObjectSet $oSet
* @param array $aExtraParams
*
* @return string
* @throws CoreException
* @throws DictExceptionMissingString
*/
public static function GetSearchForm(WebPage $oPage, CMDBObjectSet $oSet, $aExtraParams = array())
{
static $iSearchFormId = 0;
$bMultiSelect = false;
$oAppContext = new ApplicationContext();
$sHtml = '';
$numCols=4;
$sClassName = $oSet->GetFilter()->GetClass();
$oSearchForm = new \Combodo\iTop\Application\Search\SearchForm();
// Romain: temporarily removed the tab "OQL query" because it was not finalized
// (especially when used to add a link)
/*
$sHtml .= "<div class=\"mini_tabs\" id=\"mini_tabs{$iSearchFormId}\"><ul>
<li><a href=\"#\" onClick=\"$('div.mini_tab{$iSearchFormId}').toggle();$('#mini_tabs{$iSearchFormId} ul li a').toggleClass('selected');\">".Dict::S('UI:OQLQueryTab')."</a></li>
<li><a class=\"selected\" href=\"#\" onClick=\"$('div.mini_tab{$iSearchFormId}').toggle();$('#mini_tabs{$iSearchFormId} ul li a').toggleClass('selected');\">".Dict::S('UI:SimpleSearchTab')."</a></li>
</ul></div>\n";
*/
// Simple search form
if (isset($aExtraParams['currentId']))
{
$sSearchFormId = $aExtraParams['currentId'];
}
else
{
$iSearchFormId = $oPage->GetUniqueId();
$sSearchFormId = 'SimpleSearchForm'.$iSearchFormId;
$sHtml .= "<div id=\"ds_$sSearchFormId\" class=\"mini_tab{$iSearchFormId}\">\n";
}
// Check if the current class has some sub-classes
if (isset($aExtraParams['baseClass']))
{
$sRootClass = $aExtraParams['baseClass'];
}
else
{
$sRootClass = $sClassName;
}
$aSubClasses = MetaModel::GetSubclasses($sRootClass);
if (count($aSubClasses) > 0)
{
$aOptions = array();
$aOptions[MetaModel::GetName($sRootClass)] = "<option value=\"$sRootClass\">".MetaModel::GetName($sRootClass)."</options>\n";
foreach($aSubClasses as $sSubclassName)
{
$aOptions[MetaModel::GetName($sSubclassName)] = "<option value=\"$sSubclassName\">".MetaModel::GetName($sSubclassName)."</options>\n";
}
$aOptions[MetaModel::GetName($sClassName)] = "<option selected value=\"$sClassName\">".MetaModel::GetName($sClassName)."</options>\n";
ksort($aOptions);
$sContext = $oAppContext->GetForLink();
$sClassesCombo = "<select name=\"class\" onChange=\"ReloadSearchForm('$sSearchFormId', this.value, '$sRootClass', '$sContext')\">\n".implode('', $aOptions)."</select>\n";
}
else
{
$sClassesCombo = MetaModel::GetName($sClassName);
}
$oUnlimitedFilter = new DBObjectSearch($sClassName);
$sAction = (isset($aExtraParams['action'])) ? $aExtraParams['action'] : utils::GetAbsoluteUrlAppRoot().'pages/UI.php';
$sHtml .= "<form id=\"fs_{$sSearchFormId}\" action=\"{$sAction}\">\n"; // Don't use $_SERVER['SCRIPT_NAME'] since the form may be called asynchronously (from ajax.php)
$sHtml .= "<h2>".Dict::Format('UI:SearchFor_Class_Objects', $sClassesCombo)."</h2>\n";
$index = 0;
$sHtml .= "<div>\n";
$aMapCriteria = array();
$aList = MetaModel::GetZListItems($sClassName, 'standard_search');
$aConsts = $oSet->ListConstantFields(); // Some fields are constants based on the query/context
$sClassAlias = $oSet->GetFilter()->GetClassAlias();
foreach($aList as $sFilterCode)
{
//$oAppContext->Reset($sFilterCode); // Make sure the same parameter will not be passed twice
$sHtml .= '<div class="SearchAttribute" style="white-space: nowrap;padding:5px;display:inline-block;">';
$sFilterValue = isset($aConsts[$sClassAlias][$sFilterCode]) ? $aConsts[$sClassAlias][$sFilterCode] : '';
$sFilterValue = utils::ReadParam($sFilterCode, $sFilterValue, false, 'raw_data');
$sFilterOpCode = null; // Use the default 'loose' OpCode
if (empty($sFilterValue))
{
if (isset($aMapCriteria[$sFilterCode]))
{
if (count($aMapCriteria[$sFilterCode]) > 1)
{
$sFilterValue = Dict::S('UI:SearchValue:Mixed');
}
else
{
$sFilterValue = $aMapCriteria[$sFilterCode][0]['value'];
$sFilterOpCode = $aMapCriteria[$sFilterCode][0]['opcode'];
}
// Todo: Investigate...
if ($sFilterCode != 'company')
{
$oUnlimitedFilter->AddCondition($sFilterCode, $sFilterValue, $sFilterOpCode);
}
}
}
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sFilterCode);
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
{
$oKeyAttDef = $oAttDef->GetFinalAttDef();
$sKeyAttClass = $oKeyAttDef->GetHostClass();
$sKeyAttCode = $oKeyAttDef->GetCode();
$sTargetClass = $oKeyAttDef->GetTargetClass();
$oSearch = new DBObjectSearch($sTargetClass);
$oSearch->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oAllowedValues = new DBObjectSet($oSearch);
$iFieldSize = $oKeyAttDef->GetMaxSize();
$iMaxComboLength = $oKeyAttDef->GetMaximumComboLength();
$sHtml .= "<label>".MetaModel::GetFilterLabel($sKeyAttClass, $sKeyAttCode).":</label>&nbsp;";
$aExtKeyParams = $aExtraParams;
$aExtKeyParams['iFieldSize'] = $oKeyAttDef->GetMaxSize();
$aExtKeyParams['iMinChars'] = $oKeyAttDef->GetMinAutoCompleteChars();
$sHtml .= UIExtKeyWidget::DisplayFromAttCode($oPage, $sKeyAttCode, $sKeyAttClass, $oAttDef->GetLabel(), $oAllowedValues, $sFilterValue, $sSearchFormId.'search_'.$sFilterCode, false, $sFilterCode, '', $aExtKeyParams, true);
}
else
{
$aAllowedValues = MetaModel::GetAllowedValues_flt($sClassName, $sFilterCode, $aExtraParams);
if (is_null($aAllowedValues))
{
// Any value is possible, display an input box
$sHtml .= "<label>".MetaModel::GetFilterLabel($sClassName, $sFilterCode).":</label>&nbsp;<input class=\"textSearch\" name=\"$sFilterCode\" value=\"".htmlentities($sFilterValue, ENT_QUOTES, 'utf-8')."\"/>\n";
}
else
{
//Enum field, display a multi-select combo
$sValue = "<select class=\"multiselect\" size=\"1\" name=\"{$sFilterCode}[]\" multiple>\n";
$bMultiSelect = true;
//$sValue .= "<option value=\"\">".Dict::S('UI:SearchValue:Any')."</option>\n";
asort($aAllowedValues);
foreach($aAllowedValues as $key => $value)
{
if (is_array($sFilterValue) && in_array($key, $sFilterValue))
{
$sSelected = ' selected';
}
else if ($sFilterValue == $key)
{
$sSelected = ' selected';
}
else
{
$sSelected = '';
}
$sValue .= "<option value=\"$key\"$sSelected>$value</option>\n";
}
$sValue .= "</select>\n";
$sHtml .= "<label>".MetaModel::GetFilterLabel($sClassName, $sFilterCode).":</label>&nbsp;$sValue\n";
}
}
unset($aExtraParams[$sFilterCode]);
// Finally, add a tooltip if one is defined for this attribute definition
$sTip = $oAttDef->GetHelpOnSmartSearch();
if (strlen($sTip) > 0)
{
$sTip = addslashes($sTip);
$sTip = str_replace(array("\n", "\r"), " ", $sTip);
// :input does represent in form visible input (INPUT, SELECT, TEXTAREA)
$oPage->add_ready_script("$('form#fs_$sSearchFormId :input[name={$sFilterCode}]').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
}
$index++;
$sHtml .= '</div> ';
}
$sHtml .= "</div>\n";
$sHtml .= "<p align=\"right\"><input type=\"submit\" value=\"".Dict::S('UI:Button:Search')."\"></p>\n";
if (isset($aExtraParams['table_id']))
{
// Rename to avoid collisions...
$aExtraParams['_table_id_'] = $aExtraParams['table_id'];
unset($aExtraParams['table_id']);
}
foreach($aExtraParams as $sName => $sValue)
{
if (is_scalar($sValue))
{
$sHtml .= "<input type=\"hidden\" name=\"$sName\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n";
}
}
$sHtml .= "<input type=\"hidden\" name=\"class\" value=\"$sClassName\" />\n";
$sHtml .= "<input type=\"hidden\" name=\"dosearch\" value=\"1\" />\n";
$sHtml .= "<input type=\"hidden\" name=\"operation\" value=\"search_form\" />\n";
$sHtml .= $oAppContext->GetForForm();
$sHtml .= "</form>\n";
if (!isset($aExtraParams['currentId']))
{
$sHtml .= "</div><!-- Simple search form -->\n";
}
if ($bMultiSelect)
{
$aOptions = array(
'header' => true,
'checkAllText' => Dict::S('UI:SearchValue:CheckAll'),
'uncheckAllText' => Dict::S('UI:SearchValue:UncheckAll'),
'noneSelectedText' => Dict::S('UI:SearchValue:Any'),
'selectedText' => Dict::S('UI:SearchValue:NbSelected'),
'selectedList' => 1,
);
$sJSOptions = json_encode($aOptions);
$oPage->add_ready_script("$('.multiselect').multiselect($sJSOptions);");
}
/*
// OQL query builder
$sHtml .= "<div id=\"OQLQuery{$iSearchFormId}\" style=\"display:none\" class=\"mini_tab{$iSearchFormId}\">\n";
$sHtml .= "<h1>".Dict::S('UI:OQLQueryBuilderTitle')."</h1>\n";
$sHtml .= "<form id=\"formOQL{$iSearchFormId}\"><table style=\"width:80%;\"><tr style=\"vertical-align:top\">\n";
$sHtml .= "<td style=\"text-align:right\"><label>SELECT&nbsp;</label><select name=\"oql_class\">";
$aClasses = MetaModel::EnumChildClasses($sClassName, ENUM_CHILD_CLASSES_ALL);
$sSelectedClass = utils::ReadParam('oql_class', $sClassName, false, 'class');
$sOQLClause = utils::ReadParam('oql_clause', '', false, 'raw_data');
asort($aClasses);
foreach($aClasses as $sChildClass)
{
$sSelected = ($sChildClass == $sSelectedClass) ? 'selected' : '';
$sHtml.= "<option value=\"$sChildClass\" $sSelected>".MetaModel::GetName($sChildClass)."</option>\n";
}
$sHtml .= "</select>&nbsp;</td><td>\n";
$sHtml .= "<textarea name=\"oql_clause\" style=\"width:100%\">$sOQLClause</textarea></td></tr>\n";
$sHtml .= "<tr><td colspan=\"2\" style=\"text-align:right\"><input type=\"submit\" value=\"".Dict::S('UI:Button:Query')."\"></td></tr>\n";
$sHtml .= "<input type=\"hidden\" name=\"dosearch\" value=\"1\" />\n";
foreach($aExtraParams as $sName => $sValue)
{
if (is_scalar($sValue))
{
$sHtml .= "<input type=\"hidden\" name=\"$sName\" value=\"$sValue\" />\n";
}
}
$sHtml .= "<input type=\"hidden\" name=\"operation\" value=\"search_oql\" />\n";
$sHtml .= $oAppContext->GetForForm();
$sHtml .= "</table></form>\n";
$sHtml .= "</div><!-- OQL query form -->\n";
*/
return $sHtml;
return $oSearchForm->GetSearchForm($oPage, $oSet, $aExtraParams);
}
/**
@@ -2403,7 +2198,7 @@ EOF
$sJSToken = json_encode($sOwnershipToken);
$oPage->add_ready_script(
<<<EOF
$(window).unload(function() { return OnUnload('$iTransactionId', '$sClass', $iKey, $sJSToken) } );
$(window).on('unload',function() { return OnUnload('$iTransactionId', '$sClass', $iKey, $sJSToken) } );
window.onbeforeunload = function() {
if (!window.bInSubmit && !window.bInCancel)
{
@@ -2526,12 +2321,7 @@ EOF
if ($oObjectToClone == null)
{
$oObj = MetaModel::NewObject($sClass);
if (!empty($sStateAttCode))
{
$sTargetState = MetaModel::GetDefaultState($sClass);
$oObj->Set($sStateAttCode, $sTargetState);
}
$oObj = DBObject::MakeDefaultInstance($sClass);
}
else
{
@@ -2568,7 +2358,7 @@ EOF
if ($oAttDef->IsExternalKey())
{
$oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs);
if ($oAllowedValues->Count(2) == 1)
if ($oAllowedValues->Count() == 1)
{
$oRemoteObj = $oAllowedValues->Fetch();
$oObj->Set($sAttCode, $oRemoteObj->GetKey());
@@ -2687,7 +2477,7 @@ EOF
if ($oAttDef->IsExternalKey())
{
$oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs, '', $this->Get($sAttCode));
if ($oAllowedValues->Count(2) == 1)
if ($oAllowedValues->Count() == 1)
{
$oRemoteObj = $oAllowedValues->Fetch();
$this->Set($sAttCode, $oRemoteObj->GetKey());
@@ -2755,7 +2545,7 @@ EOF
<<<EOF
// Starts the validation when the page is ready
CheckFields('apply_stimulus', false);
$(window).unload(function() { return OnUnload('$iTransactionId', '$sClass', $iKey, $sJSToken) } );
$(window).on('unload', function() { return OnUnload('$iTransactionId', '$sClass', $iKey, $sJSToken) } );
EOF
);

View File

@@ -115,15 +115,13 @@ abstract class Dashboard
$aDashletOrder = array();
foreach($oDashletList as $oDomNode)
{
$sDashletClass = $oDomNode->getAttribute('xsi:type');
$oRank = $oDomNode->getElementsByTagName('rank')->item(0);
if ($oRank)
{
$iRank = (float)$oRank->textContent;
}
$sId = $oDomNode->getAttribute('id');
$oNewDashlet = new $sDashletClass($this->oMetaModel, $sId);
$oNewDashlet->FromDOMNode($oDomNode);
$oNewDashlet = $this->InitDashletFromDOMNode($oDomNode);
$aDashletOrder[] = array('rank' => $iRank, 'dashlet' => $oNewDashlet);
}
usort($aDashletOrder, array(get_class($this), 'SortOnRank'));
@@ -147,6 +145,22 @@ abstract class Dashboard
}
}
protected function InitDashletFromDOMNode($oDomNode)
{
$sId = $oDomNode->getAttribute('id');
$sClass = $oDomNode->getAttribute('xsi:type');
// Test if dashlet can be instanciated, otherwise (uninstalled, broken, ...) we display a placeholder
if(!class_exists($sClass))
{
$sClass = 'DashletUnknown';
}
$oNewDashlet = new $sClass($this->oMetaModel, $sId);
$oNewDashlet->FromDOMNode($oDomNode);
return $oNewDashlet;
}
static function SortOnRank($aItem1, $aItem2)
{
return ($aItem1['rank'] > $aItem2['rank']) ? +1 : -1;
@@ -414,24 +428,11 @@ EOF
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:Dashlets').'</div>');
$sUrl = utils::GetAbsoluteUrlAppRoot();
$oPage->add('<div id="select_dashlet" style="text-align:center">');
foreach( get_declared_classes() as $sDashletClass)
$oPage->add('<div id="select_dashlet" style="text-align:center; max-height:120px; overflow-y:auto;">');
$aAvailableDashlets = $this->GetAvailableDashlets();
foreach($aAvailableDashlets as $sDashletClass => $aInfo)
{
if (is_subclass_of($sDashletClass, 'Dashlet'))
{
$oReflection = new ReflectionClass($sDashletClass);
if (!$oReflection->isAbstract())
{
$aCallSpec = array($sDashletClass, 'IsVisible');
$bVisible = call_user_func($aCallSpec);
if ($bVisible)
{
$aCallSpec = array($sDashletClass, 'GetInfo');
$aInfo = call_user_func($aCallSpec);
$oPage->add('<span dashlet_class="'.$sDashletClass.'" class="dashlet_icon ui-widget-content ui-corner-all" id="dashlet_'.$sDashletClass.'" title="'.$aInfo['label'].'" style="width:34px; height:34px; display:inline-block; margin:2px;"><img src="'.$sUrl.$aInfo['icon'].'" /></span>');
}
}
}
$oPage->add('<span dashlet_class="'.$sDashletClass.'" class="dashlet_icon ui-widget-content ui-corner-all" id="dashlet_'.$sDashletClass.'" title="'.$aInfo['label'].'" style="width:34px; height:34px; display:inline-block; margin:2px;"><img src="'.$sUrl.$aInfo['icon'].'" /></span>');
}
$oPage->add('</div>');
@@ -465,6 +466,38 @@ EOF
$oPage->add('</div>');
}
/**
* Return an array of dashlets available for selection.
*
* @return array
*/
protected function GetAvailableDashlets()
{
$aDashlets = array();
foreach( get_declared_classes() as $sDashletClass)
{
// DashletUnknown is not among the selection as it is just a fallback for dashlets that can't instanciated.
if ( is_subclass_of($sDashletClass, 'Dashlet') && !in_array($sDashletClass, array('DashletUnknown', 'DashletProxy')) )
{
$oReflection = new ReflectionClass($sDashletClass);
if (!$oReflection->isAbstract())
{
$aCallSpec = array($sDashletClass, 'IsVisible');
$bVisible = call_user_func($aCallSpec);
if ($bVisible)
{
$aCallSpec = array($sDashletClass, 'GetInfo');
$aInfo = call_user_func($aCallSpec);
$aDashlets[$sDashletClass] = $aInfo;
}
}
}
}
return $aDashlets;
}
protected function GetNewDashletId()
{

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.5">
<portals>
<portal id="legacy_portal" _delta="define">
<url>portal/index.php</url>
@@ -19,4 +19,9 @@
</deny>
</portal>
</portals>
<menus>
<menu id="AdminTools" xsi:type="MenuGroup" _delta="define">
<rank>80</rank>
</menu>
</menus>
</itop_design>

View File

@@ -174,6 +174,7 @@ class DataTable
}
$sJSOptions = json_encode($aOptions);
$oPage->add_ready_script("$('#datatable_{$this->iListId}').datatable($sJSOptions);");
return $sHtml;
}
@@ -297,7 +298,7 @@ EOF;
if (!$oPage->IsPrintableVersion())
{
$sMenuTitle = Dict::S('UI:ConfigureThisList');
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><img src="../images/toolkit_menu.png?itopversion='.ITOP_VERSION.'"><ul>';
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><img src="../images/toolkit_menu.png?t='.utils::GetCacheBusterTimestamp().'"><ul>';
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
$aActions = array(
@@ -571,33 +572,6 @@ EOF
{
$oPage->add_ready_script("oTable.trigger(\"fakesorton\", [$sFakeSortList]);");
}
//if ($iNbPages == 1)
if (false)
{
if (isset($aExtraParams['cssCount']))
{
$sCssCount = $aExtraParams['cssCount'];
if ($sSelectMode == 'single')
{
$sSelectSelector = ":radio[name^=selectObj]";
}
else if ($sSelectMode == 'multiple')
{
$sSelectSelector = ":checkbox[name^=selectObj]";
}
$oPage->add_ready_script(
<<<EOF
$('#{$this->iListId} table.listResults $sSelectSelector').change(function() {
var c = $('{$sCssCount}');
var v = $('#{$this->iListId} table.listResults $sSelectSelector:checked').length;
c.val(v);
$('#{$this->iListId} .selectedCount').text(v);
c.trigger('change');
});
EOF
);
}
}
return $sHtml;
}
@@ -606,7 +580,7 @@ EOF
$iPageSize = ($iDefaultPageSize < 1) ? 1 : $iDefaultPageSize;
$iPageIndex = 1 + floor($iStart / $iPageSize);
$sHtml = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex);
$oPage->add_ready_script("$('#pager{$this->iListId}').html('".str_replace("\n", ' ', addslashes($sHtml))."');");
$oPage->add_ready_script("$('#pager{$this->iListId}').html('".json_encode($sHtml)."');");
if ($iDefaultPageSize < 1)
{
$oPage->add_ready_script("$('#pager{$this->iListId}').parent().hide()");

View File

@@ -43,6 +43,7 @@ require_once(APPROOT.'/application/utils.inc.php');
class DisplayBlock
{
const TAG_BLOCK = 'itopblock';
/** @var \DBSearch */
protected $m_oFilter;
protected $m_aConditions; // Conditions added to the filter -> avoid duplicate conditions
protected $m_sStyle;
@@ -74,10 +75,17 @@ class DisplayBlock
{
return $this->m_oFilter;
}
/**
* Constructs a DisplayBlock object from a DBObjectSet already in memory
* @param $oSet DBObjectSet
*
* @param DBObjectSet $oSet
* @param string $sStyle
* @param array $aParams
*
* @return DisplayBlock The DisplayBlock object, or null if the creation failed
* @throws \CoreException
* @throws \Exception
*/
public static function FromObjectSet(DBObjectSet $oSet, $sStyle, $aParams = array())
{
@@ -212,47 +220,20 @@ class DisplayBlock
$aExtraParams['currentId'] = $sId;
$sExtraParams = addslashes(str_replace('"', "'", json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them
$bAutoReload = false;
if (isset($aExtraParams['auto_reload']))
{
if ($aExtraParams['auto_reload'] === true)
{
// Note: does not work in the switch (case true) because a positive number evaluates to true!!!
$aExtraParams['auto_reload'] = 'standard';
}
switch($aExtraParams['auto_reload'])
{
case 'fast':
$bAutoReload = true;
$iReloadInterval = MetaModel::GetConfig()->GetFastReloadInterval()*1000;
break;
case 'standard':
case 'true':
$bAutoReload = true;
$iReloadInterval = MetaModel::GetConfig()->GetStandardReloadInterval()*1000;
break;
default:
if (is_numeric($aExtraParams['auto_reload']) && ($aExtraParams['auto_reload'] > 0))
{
$bAutoReload = true;
$iReloadInterval = max(MetaModel::GetConfig()->Get('min_reload_interval'), $aExtraParams['auto_reload'])*1000;
}
else
{
// incorrect config, ignore it
$bAutoReload = false;
}
}
}
$sFilter = $this->m_oFilter->serialize(); // Used either for asynchronous or auto_reload
if (!$this->m_bAsynchronous)
{
// render now
$sHtml .= "<div id=\"$sId\" class=\"display_block\">\n";
$sHtml .= $this->GetRenderContent($oPage, $aExtraParams, $sId);
$sHtml .= "<div id=\"$sId\" class=\"display_block\" >\n";
try
{
$sHtml .= $this->GetRenderContent($oPage, $aExtraParams, $sId);
} catch (Exception $e)
{
}
$sHtml .= "</div>\n";
}
else
@@ -265,20 +246,49 @@ class DisplayBlock
$.post("ajax.render.php?style='.$this->m_sStyle.'",
{ operation: "ajax", filter: "'.$sFilter.'", extra_params: "'.$sExtraParams.'" },
function(data){
$("#'.$sId.'").empty();
$("#'.$sId.'").append(data);
$("#'.$sId.'").removeClass("loading");
$("#'.$sId.'")
.empty()
.append(data)
.removeClass("loading")
;
}
);
');
}
if (($bAutoReload) && ($this->m_sStyle != 'search')) // Search form do NOT auto-reload
{
$oPage->add_script('setInterval("ReloadBlock(\''.$sId.'\', \''.$this->m_sStyle.'\', \''.$sFilter.'\', \"'.$sExtraParams.'\")", '.$iReloadInterval.');');
}
if ($this->m_sStyle == 'list') // Search form need to extract result list extra data, the simplest way is to expose this configuration
{
$listJsonExtraParams = json_encode(json_encode($aExtraParams));
$oPage->add_ready_script("
$('#$sId').data('sExtraParams', ".$listJsonExtraParams.");
// console.debug($('#$sId').data());
// console.debug($('#$sId'));
// console.debug('#$sId');
");
// $oPage->add_ready_script("console.debug($('#Menu_UserRequest_OpenRequests').data());");
}
return $sHtml;
}
/**
* @param \WebPage $oPage
* @param array $aExtraParams
*
* @throws \ApplicationException
* @throws \CoreException
* @throws \CoreWarning
* @throws \DictExceptionMissingString
* @throws \MySQLException
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
if (!isset($aExtraParams['currentId']))
@@ -291,7 +301,19 @@ class DisplayBlock
}
$oPage->add($this->GetRenderContent($oPage, $aExtraParams, $sId));
}
/**
* @param WebPage $oPage
* @param array $aExtraParams
* @param $sId
* @return string
* @throws ApplicationException
* @throws CoreException
* @throws CoreWarning
* @throws DictExceptionMissingString
* @throws MySQLException
* @throws Exception
*/
public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId)
{
$sHtml = '';
@@ -406,6 +428,7 @@ class DisplayBlock
case 'count':
if (isset($aExtraParams['group_by']))
{
$sAlias = $this->m_oFilter->GetClassAlias();
if (isset($aExtraParams['group_by_label']))
{
$oGroupByExp = Expression::FromOQL($aExtraParams['group_by']);
@@ -414,7 +437,6 @@ class DisplayBlock
else
{
// Backward compatibility: group_by is simply a field id
$sAlias = $this->m_oFilter->GetClassAlias();
$oGroupByExp = new FieldExpression($aExtraParams['group_by'], $sAlias);
$sGroupByLabel = MetaModel::GetLabel($this->m_oFilter->GetClass(), $aExtraParams['group_by']);
}
@@ -436,7 +458,42 @@ class DisplayBlock
$aGroupBy = array();
$aGroupBy['grouped_by_1'] = $oGroupByExp;
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true);
$aQueryParams = array();
if (isset($aExtraParams['query_params']))
{
$aQueryParams = $aExtraParams['query_params'];
}
$aFunctions = array();
$aOrderBy = array();
$sAgregationFunction = 'count';
$sFctVar = '_itop_count_';
$sAgregationAttr = '';
if (isset($aExtraParams['agregation_function']) && !empty($aExtraParams['agregation_attribute']))
{
$sAgregationFunction = $aExtraParams['agregation_function'];
$sAgregationAttr = $aExtraParams['agregation_attribute'];
$oAttrExpr = Expression::FromOQL('`'.$sAlias.'`.`'.$sAgregationAttr.'`');
$oFctExpr = new FunctionExpression(strtoupper($sAgregationFunction), array($oAttrExpr));
$sFctVar = '_itop_'.$sAgregationFunction.'_';
$aFunctions = array($sFctVar => $oFctExpr);
}
if (!empty($sAgregationAttr))
{
$sClass = $this->m_oFilter->GetClass();
$sAgregationAttr = MetaModel::GetLabel($sClass, $sAgregationAttr);
}
$iLimit = 0;
if (isset($aExtraParams['limit']))
{
$iLimit = intval($aExtraParams['limit']);
}
if (isset($aExtraParams['order_direction']))
{
$aOrderBy = array($sFctVar => ($aExtraParams['order_direction'] === 'asc'));
}
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true, $aFunctions, $aOrderBy, $iLimit);
$aRes = CMDBSource::QueryToArray($sSql);
$aGroupBy = array();
@@ -449,7 +506,7 @@ class DisplayBlock
$aValues[$iRow] = $sValue;
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
$aLabels[$iRow] = $sHtmlValue;
$aGroupBy[$iRow] = (int) $aRow['_itop_count_'];
$aGroupBy[$iRow] = (int) $aRow[$sFctVar];
$iTotalCount += $aRow['_itop_count_'];
}
@@ -465,12 +522,12 @@ class DisplayBlock
$oSubsetSearch->AddConditionExpression($oCondition);
$sFilter = urlencode($oSubsetSearch->serialize());
$aData[] = array ( 'group' => $aLabels[$iRow],
$aData[] = array ('group' => $aLabels[$iRow],
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1&$sParams&filter=$sFilter\">$iCount</a>"); // TO DO: add the context information
}
$aAttribs =array(
'group' => array('label' => $sGroupByLabel, 'description' => ''),
'value' => array('label'=> Dict::S('UI:GroupBy:Count'), 'description' => Dict::S('UI:GroupBy:Count+'))
'value' => array('label'=> Dict::S('UI:GroupBy:'.$sAgregationFunction), 'description' => Dict::Format('UI:GroupBy:'.$sAgregationFunction.'+', $sAgregationAttr))
);
$sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection';
$sHtml .= $oPage->GetP(Dict::Format($sFormat, $iTotalCount));
@@ -594,7 +651,7 @@ class DisplayBlock
// Check the classes that can be read (i.e authorized) by this user...
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $this->m_oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $this->m_oSet) != UR_ALLOWED_NO)
{
$aAuthorizedClasses[$sAlias] = $sClassName;
}
@@ -655,13 +712,27 @@ class DisplayBlock
}
}
}
if (isset($aExtraParams['update_history']) && true == $aExtraParams['update_history'])
{
$seventAttachedData = json_encode(array(
'filter' => $this->m_oSet->GetFilter()->serialize(),
'breadcrumb_id' => "ui-search-".$this->m_oSet->GetClass(),
'breadcrumb_label' => MetaModel::GetName($this->m_oSet->GetClass()),
'breadcrumb_max_count' => utils::GetConfig()->Get('breadcrumb.max_count'),
'breadcrumb_instance_id'=> MetaModel::GetConfig()->GetItopInstanceid(),
'breadcrumb_icon' => utils::GetAbsoluteUrlAppRoot().'images/breadcrumb-search.png'
));
$oPage->add_ready_script("$('body').trigger('update_history.itop', [$seventAttachedData])");
}
}
break;
case 'links':
//$bDashboardMode = isset($aExtraParams['dashboard']) ? ($aExtraParams['dashboard'] == 'true') : false;
//$bSelectMode = isset($aExtraParams['select']) ? ($aExtraParams['select'] == 'true') : false;
if ( ($this->m_oSet->Count(1)> 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) )
if ( ($this->m_oSet->Count()> 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);
@@ -776,8 +847,7 @@ class DisplayBlock
$oAttDef = MetaModel::GetAttributeDef($sClass, $sStateAttrCode);
// Generate one count + group by query [#1330]
$sClassAlias = $this->m_oFilter->GetClassAlias();
$oGroupByExpr = Expression::FromOQL($sClassAlias.'.'.$sStateAttrCode);
$oGroupByExpr = Expression::FromOQL($sClass.'.'.$sStateAttrCode);
$aGroupBy = array('group1' => $oGroupByExpr);
$sCountGroupByQuery = $this->m_oFilter->MakeGroupByQuery(array(), $aGroupBy, false);
$aCountGroupByResults = CMDBSource::QueryToArray($sCountGroupByQuery);
@@ -912,21 +982,10 @@ class DisplayBlock
case 'search':
if (!$oPage->IsPrintableVersion())
{
$sStyle = (isset($aExtraParams['open']) && ($aExtraParams['open'] == 'true')) ? 'SearchDrawer' : 'SearchDrawer DrawerClosed';
$sHtml .= "<div id=\"ds_$sId\" class=\"$sStyle\">\n";
$oPage->add_ready_script(
<<<EOF
$("#dh_$sId").click( function() {
$("#ds_$sId").slideToggle('normal', function() { $("#ds_$sId").parent().resize(); FixSearchFormsDisposition(); $("#dh_$sId").trigger('toggle_complete'); } );
$("#dh_$sId").toggleClass('open');
});
EOF
);
$sHtml .= "<div id=\"ds_$sId\" class=\"search_box\">\n";
$aExtraParams['currentId'] = $sId;
$sHtml .= cmdbAbstractObject::GetSearchForm($oPage, $this->m_oSet, $aExtraParams);
$sHtml .= "</div>\n";
$sHtml .= "<div class=\"HRDrawer\"></div>\n";
$sHtml .= "<div id=\"dh_$sId\" class=\"DrawerHandle\">".Dict::S('UI:SearchToggle')."</div>\n";
}
break;
@@ -942,18 +1001,21 @@ EOF
$sFilter = $this->m_oFilter->serialize();
$oContext = new ApplicationContext();
$sContextParam = $oContext->GetForLink();
$sAgregationFunction = isset($aExtraParams['agregation_function']) ? $aExtraParams['agregation_function'] : '';
$sAgregationAttr = isset($aExtraParams['agregation_attribute']) ? $aExtraParams['agregation_attribute'] : '';
$sLimit = isset($aExtraParams['limit']) ? $aExtraParams['limit'] : '';
$sOrderBy = isset($aExtraParams['order_by']) ? $aExtraParams['order_by'] : '';
$sOrderDirection = isset($aExtraParams['order_direction']) ? $aExtraParams['order_direction'] : '';
if (isset($aExtraParams['group_by_label']))
{
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]=$sChartType&params[currentId]=$sId{$iChartCounter}&id=$sId{$iChartCounter}&filter=".urlencode($sFilter).'&'.$sContextParam);
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]=$sChartType&params[currentId]=$sId{$iChartCounter}&params[order_direction]=$sOrderDirection&params[order_by]=$sOrderBy&params[limit]=$sLimit&params[agregation_function]=$sAgregationFunction&params[agregation_attribute]=$sAgregationAttr&id=$sId{$iChartCounter}&filter=".urlencode($sFilter).'&'.$sContextParam);
}
else
{
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[chart_type]=$sChartType&params[currentId]=$sId{$iChartCounter}&id=$sId{$iChartCounter}&filter=".urlencode($sFilter).'&'.$sContextParam);
$sUrl = json_encode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[chart_type]=$sChartType&params[currentId]=$sId{$iChartCounter}&params[order_direction]=$sOrderDirection&params[order_by]=$sOrderBy&params[limit]=$sLimit&params[agregation_function]=$sAgregationFunction&params[agregation_attribute]=$sAgregationAttr&id=$sId{$iChartCounter}&filter=".urlencode($sFilter).'&'.$sContextParam);
}
$sType = ($sChartType == 'pie') ? 'pie' : 'bar';
$oPage->add_ready_script(
<<<EOF
$.post($sUrl, {}, function(data) {
@@ -970,22 +1032,47 @@ EOF
if (isset($aExtraParams['group_by']))
{
$sAlias = $this->m_oFilter->GetClassAlias();
if (isset($aExtraParams['group_by_label']))
{
$oGroupByExp = Expression::FromOQL($aExtraParams['group_by']);
$sGroupByLabel = $aExtraParams['group_by_label'];
}
else
{
// Backward compatibility: group_by is simply a field id
$sAlias = $this->m_oFilter->GetClassAlias();
$oGroupByExp = new FieldExpression($aExtraParams['group_by'], $sAlias);
$sGroupByLabel = MetaModel::GetLabel($this->m_oFilter->GetClass(), $aExtraParams['group_by']);
}
$aGroupBy = array();
$aGroupBy['grouped_by_1'] = $oGroupByExp;
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true);
$aQueryParams = array();
if (isset($aExtraParams['query_params']))
{
$aQueryParams = $aExtraParams['query_params'];
}
$aFunctions = array();
$aOrderBy = array();
$sFctVar = '_itop_count_';
if (isset($aExtraParams['agregation_function']) && !empty($aExtraParams['agregation_attribute']))
{
$sAgregationFunction = $aExtraParams['agregation_function'];
$sAgregationAttr = $aExtraParams['agregation_attribute'];
$oAttrExpr = Expression::FromOQL('`'.$sAlias.'`.`'.$sAgregationAttr.'`');
$oFctExpr = new FunctionExpression(strtoupper($sAgregationFunction), array($oAttrExpr));
$sFctVar = '_itop_'.$sAgregationFunction.'_';
$aFunctions = array($sFctVar => $oFctExpr);
}
$iLimit = 0;
if (isset($aExtraParams['limit']))
{
$iLimit = intval($aExtraParams['limit']);
}
if (isset($aExtraParams['order_direction']))
{
$aOrderBy = array($sFctVar => ($aExtraParams['order_direction'] === 'asc'));
}
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true, $aFunctions, $aOrderBy, $iLimit);
$aRes = CMDBSource::QueryToArray($sSql);
$oContext = new ApplicationContext();
$sContextParam = $oContext->GetForLink();
@@ -998,9 +1085,9 @@ EOF
{
$sValue = $aRow['grouped_by_1'];
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
$aGroupBy[(int)$iRow] = (int) $aRow['_itop_count_'];
$aGroupBy[(int)$iRow] = (int) $aRow[$sFctVar];
$iTotalCount += $aRow['_itop_count_'];
$aValues[] = array('label' => html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8'), 'label_html' => $sHtmlValue, 'value' => (int) $aRow['_itop_count_']);
$aValues[] = array('label' => html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8'), 'label_html' => $sHtmlValue, 'value' => (int) $aRow[$sFctVar]);
// Build the search for this subset
$oSubsetSearch = $this->m_oFilter->DeepClone();
@@ -1119,12 +1206,71 @@ EOF
// Unsupported style, do nothing.
$sHtml .= Dict::format('UI:Error:UnsupportedStyleOfBlock', $this->m_sStyle);
}
$bAutoReload = false;
if (isset($aExtraParams['auto_reload']))
{
if ($aExtraParams['auto_reload'] === true)
{
// Note: does not work in the switch (case true) because a positive number evaluates to true!!!
$aExtraParams['auto_reload'] = 'standard';
}
switch($aExtraParams['auto_reload'])
{
case 'fast':
$bAutoReload = true;
$iReloadInterval = MetaModel::GetConfig()->GetFastReloadInterval()*1000;
break;
case 'standard':
case 'true':
$bAutoReload = true;
$iReloadInterval = MetaModel::GetConfig()->GetStandardReloadInterval()*1000;
break;
default:
if (is_numeric($aExtraParams['auto_reload']) && ($aExtraParams['auto_reload'] > 0))
{
$bAutoReload = true;
$iReloadInterval = max(MetaModel::GetConfig()->Get('min_reload_interval'), $aExtraParams['auto_reload'])*1000;
}
else
{
// incorrect config, ignore it
$bAutoReload = false;
}
}
}
if (($bAutoReload) && ($this->m_sStyle != 'search')) // Search form do NOT auto-reload
{
$sFilter = $this->m_oFilter->serialize(); // Used either for asynchronous or auto_reload
$sExtraParams = addslashes(str_replace('"', "'", json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them
$oPage->add_script('if (typeof window.oAutoReloadBlock == "undefined") {
window.oAutoReloadBlock = {};
}
if (typeof window.oAutoReloadBlock[\''.$sId.'\'] != "undefined") {
clearInterval(window.oAutoReloadBlock[\''.$sId.'\']);
}
window.oAutoReloadBlock[\''.$sId.'\'] = setInterval("ReloadBlock(\''.$sId.'\', \''.$this->m_sStyle.'\', \''.$sFilter.'\', \"'.$sExtraParams.'\")", '.$iReloadInterval.');');
}
return $sHtml;
}
/**
* Add a condition (restriction) to the current DBSearch on which the display block is based
* taking into account the hierarchical keys for which the condition is based on the 'below' operator
*
* @param string $sFilterCode
* @param array $condition
* @param string $sOpCode
* @param bool $bParseSearchString
*
* @throws \CoreException
* @throws \CoreWarning
* @throws \Exception
*/
protected function AddCondition($sFilterCode, $condition, $sOpCode = null, $bParseSearchString = false)
{
@@ -1353,15 +1499,25 @@ EOF
* For backward compatibility 'popup' is equivalent to 'list'...
*/
class MenuBlock extends DisplayBlock
{
{
/**
* Renders the "Actions" popup menu for the given set of objects
*
*
* Note that the menu links containing (or ending) with a hash (#) will have their fragment
* part (whatever is after the hash) dynamically replaced (by javascript) when the menu is
* displayed, to correspond to the current hash/fragment in the page. This allows modifying
* an object in with the same tab active by default as the tab that was active when selecting
* the "Modify..." action.
*
* @param \WebPage $oPage
* @param array $aExtraParams
* @param string $sId
*
* @return string
* @throws \DictExceptionMissingString
* @throws \Exception
* @throws \MissingQueryArgument
* @throws \MySQLException
*/
public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId)
{
@@ -1584,7 +1740,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 + 1) < $iLimit)))
if ((count($aStates) > 0) && (($iLimit == 0) || ($oSet->Count() < $iLimit)))
{
// Life cycle actions may be available... if all objects are in the same state
//
@@ -1706,12 +1862,27 @@ class MenuBlock extends DisplayBlock
{
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>".Dict::S('UI:Menu:Actions')."\n<ul>\n";
}
$sHtml .= $oPage->RenderPopupMenuItems($aActions, $aFavoriteActions);
if ($this->m_sStyle == 'details')
{
$sSearchAction = "window.location=\"{$sRootUrl}pages/UI.php?operation=search_form&class=$sClass{$sContext}\"";
$sHtml .= "<div class=\"actions_button icon_actions_button\" title=\"".htmlentities(Dict::Format('UI:SearchFor_Class', MetaModel::GetName($sClass)), ENT_QUOTES, 'UTF-8')."\"><span class=\"search-button fa fa-search\" onclick='$sSearchAction'></span></div>";
}
if (empty($sRefreshAction) && $this->m_sStyle == 'list')
{
//for the detail page this var is defined way beyond this line
$sRefreshAction = "window.location.reload();";
}
if (!$oPage->IsPrintableVersion() && ($sRefreshAction!=''))
{
$sHtml .= "<div class=\"actions_button\" title=\"".htmlentities(Dict::S('UI:Button:Refresh'), ENT_QUOTES, 'UTF-8')."\"><span class=\"refresh-button\" onclick=\"$sRefreshAction\"></span></div>";
$sHtml .= "<div class=\"actions_button icon_actions_button\" title=\"".htmlentities(Dict::S('UI:Button:Refresh'), ENT_QUOTES, 'UTF-8')."\"><span class=\"refresh-button fa fa-refresh\" onclick=\"$sRefreshAction\"></span></div>";
}
}
static $bPopupScript = false;

View File

@@ -462,7 +462,7 @@ class ExcelExporter
$this->aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO)
{
$this->aAuthorizedClasses[$sAlias] = $sClassName;
}

View File

@@ -203,7 +203,7 @@ class DesignerForm
public function RenderAsPropertySheet($oP, $bReturnHTML = false, $sNotifyParentSelector = null)
{
$sReturn = '';
$sReturn = '';
$sActionUrl = addslashes($this->sSubmitTo);
$sJSSubmitParams = json_encode($this->aSubmitParams);
$sFormId = $this->GetFormId();
@@ -1525,7 +1525,6 @@ class DesignerFormSelectorField extends DesignerFormField
public function AddSubForm($oSubForm, $sLabel, $sValue)
{
$idx = count($this->aSubForms);
$this->aSubForms[] = array('form' => $oSubForm, 'label' => $sLabel, 'value' => $sValue);
if ($sValue == $this->defaultRealValue)
{
@@ -1539,7 +1538,7 @@ class DesignerFormSelectorField extends DesignerFormField
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
$this->aCSSClasses[] = 'formSelector';
$sCSSClasses = '';
@@ -1555,8 +1554,6 @@ class DesignerFormSelectorField extends DesignerFormField
if ($this->IsReadOnly())
{
$aSelected = array();
$aHiddenValues = array();
$sDisplayValue = '';
$sHiddenValue = '';
foreach($this->aSubForms as $iKey => $aFormData)
@@ -1572,8 +1569,6 @@ class DesignerFormSelectorField extends DesignerFormField
}
else
{
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
foreach($this->aSubForms as $iKey => $aFormData)
{
@@ -1589,7 +1584,6 @@ class DesignerFormSelectorField extends DesignerFormField
{
$sHtml .= '</td><td class="prop_icon prop_apply"><span title="Apply" class="ui-icon ui-icon-circle-check"/></td><td class="prop_icon prop_cancel"><span title="Revert" class="ui-icon ui-icon-circle-close"/></td></tr>';
}
foreach($this->aSubForms as $sKey => $aFormData)
{
$sId = $this->oForm->GetFieldId($this->sCode);
@@ -1615,25 +1609,7 @@ class DesignerFormSelectorField extends DesignerFormField
$oSubForm->SetHierarchyPath($sPath);
$oSubForm->SetDisplayed($sKey == $this->defaultValue);
$sState = ($sKey == $this->defaultValue) ? 'visible' : 'hidden';
//$sHtml .= "</tbody><tbody data-selector=\"$sSelector\" data-path=\"$sPath\" data-state=\"$sState\" $sStyle>";
$sHtml .= $oSubForm->RenderAsPropertySheet($oP, true);
$sState = $this->oForm->IsDisplayed() ? 'visible' : 'hidden';
$sParentStyle = '';
if ($oParent = $this->oForm->GetParentForm())
{
$sParentStyle = ($oParent->IsDisplayed()) ? '' : 'style="display:none"';
$sParentSelector = $oParent->GetHierarchyParent();
$sParentPath = $oParent->GetHierarchyPath();
}
else
{
$sParentSelector = '';
$sParentPath = '';
}
//$sHtml .= "</tbody><tbody data-selector=\"$sParentSelector\" data-path=\"$sParentPath\" data-state=\"$sState\" $sParentStyle>";
}
else
{
@@ -1681,7 +1657,6 @@ EOF
if ($selectedValue == $aFormData['value'])
{
$this->defaultValue =$iKey;
$aDefaultValues = $this->oForm->GetDefaultValues();
$oSubForm = $aFormData['form'];
$oSubForm->SetDefaultValues($aAllDefaultValues);
}

File diff suppressed because it is too large Load Diff

View File

@@ -42,6 +42,7 @@ class LoginWebPage extends NiceWebPage
const EXIT_CODE_WRONGCREDENTIALS = 3;
const EXIT_CODE_MUSTBEADMIN = 4;
const EXIT_CODE_PORTALUSERNOTAUTHORIZED = 5;
const EXIT_CODE_NOTAUTHORIZED = 6;
protected static $sHandlerClass = __class__;
public static function RegisterHandler($sClass)
@@ -97,10 +98,10 @@ class LoginWebPage extends NiceWebPage
}
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo.'?itopversion='.ITOP_VERSION;
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo.'?t='.utils::GetCacheBusterTimestamp();
if (file_exists(MODULESROOT.'branding/'.$sBrandingLogo))
{
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo.'?itopversion='.ITOP_VERSION;
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo.'?t='.utils::GetCacheBusterTimestamp();
}
$this->add("<div id=\"login-logo\"><a href=\"".htmlentities($sIconUrl, ENT_QUOTES, 'UTF-8')."\"><img title=\"$sVersionShort\" src=\"$sDisplayIcon\"></a></div>\n");
}

View File

@@ -61,11 +61,23 @@ require_once(APPROOT."/application/user.dashboard.class.inc.php");
class ApplicationMenu
{
/**
* @var bool
*/
static $bAdditionalMenusLoaded = false;
/**
* @var array
*/
static $aRootMenus = array();
/**
* @var array
*/
static $aMenusIndex = array();
/**
* @var string
*/
static $sFavoriteSiloQuery = 'SELECT Organization';
static public function LoadAdditionalMenus()
{
if (!self::$bAdditionalMenusLoaded)
@@ -96,7 +108,7 @@ class ApplicationMenu
/**
* Set the query used to limit the list of displayed organizations in the drop-down menu
* @param $sOQL string The OQL query returning a list of Organization objects
* @return none
* @return void
*/
static public function SetFavoriteSiloQuery($sOQL)
{
@@ -111,11 +123,34 @@ class ApplicationMenu
{
return self::$sFavoriteSiloQuery;
}
/**
* Check wether a menu Id is enabled or not
* @param $sMenuId
* @throws DictExceptionMissingString
*/
static public function CheckMenuIdEnabled($sMenuId)
{
self::LoadAdditionalMenus();
$oMenuNode = self::GetMenuNode(self::GetMenuIndexById($sMenuId));
if (is_null($oMenuNode) || !$oMenuNode->IsEnabled())
{
require_once(APPROOT.'/setup/setuppage.class.inc.php');
$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessRestricted')."</h1>\n");
$oP->p("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>");
$oP->output();
exit;
}
}
/**
* Main function to add a menu entry into the application, can be called during the definition
* of the data model objects
* @param MenuNode $oMenuNode
* @param $iParentIndex
* @param $fRank
* @return int
*/
static public function InsertMenu(MenuNode $oMenuNode, $iParentIndex, $fRank)
{
@@ -160,9 +195,12 @@ class ApplicationMenu
self::LoadAdditionalMenus();
return self::$aMenusIndex;
}
/**
* Entry point to display the whole menu into the web page, used by iTopWebPage
* @param $oPage
* @param $aExtraParams
* @throws DictExceptionMissingString
*/
static public function DisplayMenu($oPage, $aExtraParams)
{
@@ -173,34 +211,65 @@ class ApplicationMenu
$iActiveMenu = self::GetMenuIndexById(self::GetActiveNodeId());
foreach(self::$aRootMenus as $aMenu)
{
if (!self::CanDisplayMenu($aMenu)) { continue; }
$oMenuNode = self::GetMenuNode($aMenu['index']);
if (!$oMenuNode->IsEnabled()) continue; // Don't display a non-enabled menu
$oPage->AddToMenu('<h3 id="'.utils::GetSafeId('AccordionMenu_'.$oMenuNode->GetMenuID()).'">'.$oMenuNode->GetTitle().'</h3>');
$oPage->AddToMenu('<div>');
$oPage->AddToMenu('<ul>');
$aChildren = self::GetChildren($aMenu['index']);
if (count($aChildren) > 0)
$bActive = self::DisplaySubMenu($oPage, $aChildren, $aExtraParams, $iActiveMenu);
$oPage->AddToMenu('</ul>');
if ($bActive)
{
$oPage->AddToMenu('<ul>');
$bActive = self::DisplaySubMenu($oPage, $aChildren, $aExtraParams, $iActiveMenu);
$oPage->AddToMenu('</ul>');
if ($bActive)
{
$oPage->add_ready_script(
<<<EOF
// Accordion Menu
$("#accordion").css({display:'block'}).accordion({ header: "h3", navigation: true, heightStyle: "content", collapsible: true, active: $iAccordion, icons: false, animate:true }); // collapsible will be enabled once the item will be selected
// Accordion Menu
$("#accordion").css({display:'block'}).accordion({ header: "h3", navigation: true, heightStyle: "content", collapsible: true, active: $iAccordion, icons: false, animate:true }); // collapsible will be enabled once the item will be selected
EOF
);
}
);
}
$oPage->AddToMenu('</div>');
$iAccordion++;
}
}
/**
* Recursively check if the menu and at least one of his sub-menu is enabled
* @param array $aMenu menu entry
* @return bool true if at least one menu is enabled
*/
static private function CanDisplayMenu($aMenu)
{
$oMenuNode = self::GetMenuNode($aMenu['index']);
if ($oMenuNode->IsEnabled())
{
$aChildren = self::GetChildren($aMenu['index']);
if (count($aChildren) > 0)
{
foreach($aChildren as $aSubMenu)
{
if (self::CanDisplayMenu($aSubMenu))
{
return true;
}
}
}
else
{
return true;
}
}
return false;
}
/**
* Handles the display of the sub-menus (called recursively if necessary)
* @param WebPage $oPage
* @param array $aMenus
* @param array $aExtraParams
* @param int $iActiveMenu
* @return true if the currently selected menu is one of the submenus
* @throws DictExceptionMissingString
*/
static protected function DisplaySubMenu($oPage, $aMenus, $aExtraParams, $iActiveMenu = -1)
{
@@ -218,13 +287,12 @@ EOF
$sHyperlink = $oMenu->GetHyperlink($aExtraParams);
if ($sHyperlink != '')
{
$oPage->AddToMenu('<li id="'.utils::GetSafeId('AccordionMenu_'.$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="'.utils::GetSafeId('AccordionMenu_'.$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)
{
$bActive = true;
@@ -239,8 +307,12 @@ EOF
}
return $bActive;
}
/**
* Helper function to sort the menus based on their rank
* @param $a
* @param $b
* @return int
*/
static public function CompareOnRank($a, $b)
{
@@ -255,17 +327,21 @@ EOF
}
return $result;
}
/**
* Helper function to retrieve the MenuNodeObject based on its ID
* Helper function to retrieve the MenuNode Object based on its ID
* @param int $index
* @return MenuNode|null
*/
static public function GetMenuNode($index)
{
return isset(self::$aMenusIndex[$index]) ? self::$aMenusIndex[$index]['node'] : null;
}
/**
* Helper function to get the list of child(ren) of a menu
* @param int $index
* @return array
*/
static public function GetChildren($index)
{
@@ -304,8 +380,11 @@ EOF
$sMenuId = self::GetDefaultMenuId();
}
return $sMenuId;
}
}
/**
* @return null|string
*/
static public function GetDefaultMenuId()
{
static $sDefaultMenuId = null;
@@ -322,6 +401,10 @@ EOF
return $sDefaultMenuId;
}
/**
* @param $sMenuId
* @return string
*/
static public function GetRootMenuId($sMenuId)
{
$iMenuIndex = self::GetMenuIndexById($sMenuId);
@@ -366,8 +449,17 @@ EOF
*/
abstract class MenuNode
{
/**
* @var string
*/
protected $sMenuId;
/**
* @var int
*/
protected $index;
/**
* @var int
*/
protected $iParentIndex;
/**
@@ -394,7 +486,7 @@ abstract class MenuNode
* Stimulus to check: if the user can 'apply' this stimulus, then she/he can see this menu
*/
protected $m_aEnableStimuli;
/**
* Create a menu item, sets the condition to have it displayed and inserts it into the application's main menu
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
@@ -404,9 +496,8 @@ abstract class MenuNode
* @param mixed $iActionCode UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @param string $sEnableStimulus The user can see this menu if she/he has enough rights to apply this stimulus
* @return MenuNode
*/
public function __construct($sMenuId, $iParentIndex = -1, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
public function __construct($sMenuId, $iParentIndex = -1, $fRank = 0.0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
$this->sMenuId = $sMenuId;
$this->iParentIndex = $iParentIndex;
@@ -425,26 +516,43 @@ abstract class MenuNode
$this->index = ApplicationMenu::InsertMenu($this, $iParentIndex, $fRank);
}
/**
* @return array
*/
public function ReflectionProperties()
{
return $this->aReflectionProperties;
}
/**
* @return string
*/
public function GetMenuId()
{
return $this->sMenuId;
}
/**
* @return int
*/
public function GetParentIndex()
{
return $this->iParentIndex;
}
/**
* @return string
* @throws DictExceptionMissingString
*/
public function GetTitle()
{
return Dict::S("Menu:$this->sMenuId", str_replace('_', ' ', $this->sMenuId));
}
/**
* @return string
* @throws DictExceptionMissingString
*/
public function GetLabel()
{
$sRet = Dict::S("Menu:$this->sMenuId+", "");
@@ -463,7 +571,10 @@ abstract class MenuNode
}
return $sRet;
}
/**
* @return int
*/
public function GetIndex()
{
return $this->index;
@@ -479,6 +590,10 @@ abstract class MenuNode
}
}
/**
* @param $aExtraParams
* @return string
*/
public function GetHyperlink($aExtraParams)
{
$aExtraParams['c[menu]'] = $this->GetMenuId();
@@ -536,9 +651,19 @@ abstract class MenuNode
}
return true;
}
/**
* @param WebPage $oPage
* @param array $aExtraParams
* @return mixed
*/
public abstract function RenderContent(WebPage $oPage, $aExtraParams = array());
/**
* @param $sHyperlink
* @param $aExtraParams
* @return string
*/
protected function AddParams($sHyperlink, $aExtraParams)
{
if (count($aExtraParams) > 0)
@@ -572,13 +697,17 @@ class MenuGroup extends MenuNode
* @param string $sEnableClass Name of class of object
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @return MenuGroup
* @param string $sEnableStimulus
*/
public function __construct($sMenuId, $fRank, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
parent::__construct($sMenuId, -1 /* no parent, groups are at root level */, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
}
/**
* @param WebPage $oPage
* @param array $aExtraParams
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
assert(false); // Shall never be called, groups do not display any content
@@ -591,6 +720,9 @@ class MenuGroup extends MenuNode
*/
class TemplateMenuNode extends MenuNode
{
/**
* @var string
*/
protected $sTemplateFile;
/**
@@ -602,23 +734,34 @@ class TemplateMenuNode extends MenuNode
* @param string $sEnableClass Name of class of object
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @return MenuNode
* @param string $sEnableStimulus
*/
public function __construct($sMenuId, $sTemplateFile, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
public function __construct($sMenuId, $sTemplateFile, $iParentIndex, $fRank = 0.0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sTemplateFile = $sTemplateFile;
$this->aReflectionProperties['template_file'] = $sTemplateFile;
}
/**
* @param $aExtraParams
* @return string
*/
public function GetHyperlink($aExtraParams)
{
if ($this->sTemplateFile == '') return '';
return parent::GetHyperlink($aExtraParams);
}
/**
* @param WebPage $oPage
* @param array $aExtraParams
* @return mixed|void
* @throws DictExceptionMissingString
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
$sTemplate = @file_get_contents($this->sTemplateFile);
if ($sTemplate !== false)
{
@@ -639,17 +782,29 @@ class TemplateMenuNode extends MenuNode
*/
class OQLMenuNode extends MenuNode
{
/**
* @var string
*/
protected $sPageTitle;
/**
* @var string
*/
protected $sOQL;
/**
* @var bool
*/
protected $bSearch;
/**
* @var bool|null
*/
protected $bSearchFormOpen;
/**
* Extra parameters to be passed to the display block to fine tune its appearence
*/
protected $m_aParams;
/**
* Create a menu item based on an OQL query and inserts it into the application's main menu
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
@@ -660,22 +815,16 @@ class OQLMenuNode extends MenuNode
* @param string $sEnableClass Name of class of object
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @return MenuNode
* @param string $sEnableStimulus
* @param bool $bSearchFormOpen
*/
public function __construct($sMenuId, $sOQL, $iParentIndex, $fRank = 0, $bSearch = false, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null, $bSearchFormOpen = null)
public function __construct($sMenuId, $sOQL, $iParentIndex, $fRank = 0.0, $bSearch = false, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null, $bSearchFormOpen = null)
{
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sPageTitle = "Menu:$sMenuId+";
$this->sOQL = $sOQL;
$this->bSearch = $bSearch;
if ($bSearchFormOpen == null)
{
$this->bSearchFormOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open');
}
else
{
$this->bSearchFormOpen = $bSearchFormOpen;
}
$this->bSearchFormOpen = $bSearchFormOpen;
$this->m_aParams = array();
$this->aReflectionProperties['oql'] = $sOQL;
$this->aReflectionProperties['do_search'] = $bSearch;
@@ -695,9 +844,18 @@ class OQLMenuNode extends MenuNode
$this->aReflectionProperties[$sKey] = $value;
}
}
/**
* @param WebPage $oPage
* @param array $aExtraParams
* @return mixed|void
* @throws CoreException
* @throws DictExceptionMissingString
* @throws OQLException
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
OQLMenuNode::RenderOQLSearch
(
$this->sOQL,
@@ -711,6 +869,19 @@ class OQLMenuNode extends MenuNode
);
}
/**
* @param $sOql
* @param $sTitle
* @param $sUsageId
* @param $bSearchPane
* @param $bSearchOpen
* @param WebPage $oPage
* @param array $aExtraParams
* @param bool $bEnableBreadcrumb
* @throws CoreException
* @throws DictExceptionMissingString
* @throws OQLException
*/
public static function RenderOQLSearch($sOql, $sTitle, $sUsageId, $bSearchPane, $bSearchOpen, WebPage $oPage, $aExtraParams = array(), $bEnableBreadcrumb = false)
{
$sUsageId = utils::GetSafeId($sUsageId);
@@ -746,22 +917,28 @@ class OQLMenuNode extends MenuNode
*/
class SearchMenuNode extends MenuNode
{
/**
* @var string
*/
protected $sPageTitle;
/**
* @var string
*/
protected $sClass;
/**
* Create a menu item based on an OQL query and inserts it into the application's main menu
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
* @param string $sClass The class of objects to search for
* @param string $sPageTitle Title displayed into the page's content (will be looked-up in the dictionnary for translation)
* @param integer $iParentIndex ID of the parent menu
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
* @param bool $bSearch (not used)
* @param string $sEnableClass Name of class of object
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @return MenuNode
* @param string $sEnableStimulus
*/
public function __construct($sMenuId, $sClass, $iParentIndex, $fRank = 0, $bSearch = false, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
public function __construct($sMenuId, $sClass, $iParentIndex, $fRank = 0.0, $bSearch = false, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sPageTitle = "Menu:$sMenuId+";
@@ -769,12 +946,20 @@ class SearchMenuNode extends MenuNode
$this->aReflectionProperties['class'] = $sClass;
}
/**
* @param WebPage $oPage
* @param array $aExtraParams
* @return mixed|void
* @throws DictExceptionMissingString
* @throws Exception
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
$oPage->SetBreadCrumbEntry("menu-".$this->sMenuId, $this->GetTitle(), '', '', utils::GetAbsoluteUrlAppRoot().'images/search.png');
$oSearch = new DBObjectSearch($this->sClass);
$aParams = array_merge(array('open' => true, 'table_id' => 'Menu_'.utils::GetSafeId($this->GetMenuId())), $aExtraParams);
$aParams = array_merge(array('table_id' => 'Menu_'.utils::GetSafeId($this->GetMenuId())), $aExtraParams);
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
$oBlock->Display($oPage, 0);
}
@@ -789,8 +974,11 @@ class SearchMenuNode extends MenuNode
*/
class WebPageMenuNode extends MenuNode
{
/**
* @var string
*/
protected $sHyperlink;
/**
* Create a menu item that points to any web page (not only UI.php)
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
@@ -800,21 +988,29 @@ class WebPageMenuNode extends MenuNode
* @param string $sEnableClass Name of class of object
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @return MenuNode
* @param string $sEnableStimulus
*/
public function __construct($sMenuId, $sHyperlink, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
public function __construct($sMenuId, $sHyperlink, $iParentIndex, $fRank = 0.0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sHyperlink = $sHyperlink;
$this->aReflectionProperties['url'] = $sHyperlink;
}
/**
* @param array $aExtraParams
* @return string
*/
public function GetHyperlink($aExtraParams)
{
$aExtraParams['c[menu]'] = $this->GetMenuId();
return $this->AddParams( $this->sHyperlink, $aExtraParams);
}
/**
* @param WebPage $oPage
* @param array $aExtraParams
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
assert(false); // Shall never be called, the external web page will handle the display by itself
@@ -829,8 +1025,11 @@ class WebPageMenuNode extends MenuNode
*/
class NewObjectMenuNode extends MenuNode
{
/**
* @var string
*/
protected $sClass;
/**
* Create a menu item that points to the URL for creating a new object, the menu will be added only if the current user has enough
* rights to create such an object (or an object of a child class)
@@ -838,15 +1037,22 @@ class NewObjectMenuNode extends MenuNode
* @param string $sClass URL to the page to load. Use relative URL if you want to keep the application portable !
* @param integer $iParentIndex ID of the parent menu
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
* @return MenuNode
* @param string $sEnableClass
* @param int|null $iActionCode
* @param int $iAllowedResults
* @param string $sEnableStimulus
*/
public function __construct($sMenuId, $sClass, $iParentIndex, $fRank = 0)
public function __construct($sMenuId, $sClass, $iParentIndex, $fRank = 0.0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
parent::__construct($sMenuId, $iParentIndex, $fRank);
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sClass = $sClass;
$this->aReflectionProperties['class'] = $sClass;
}
/**
* @param string[] $aExtraParams
* @return string
*/
public function GetHyperlink($aExtraParams)
{
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class='.$this->sClass;
@@ -857,6 +1063,7 @@ class NewObjectMenuNode extends MenuNode
/**
* Overload the check of the "enable" state of this menu to take into account
* derived classes of objects
* @throws CoreException
*/
public function IsEnabled()
{
@@ -875,7 +1082,12 @@ class NewObjectMenuNode extends MenuNode
}
}
return $bActionIsAllowed;
}
}
/**
* @param WebPage $oPage
* @param string[] $aExtraParams
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
assert(false); // Shall never be called, the external web page will handle the display by itself
@@ -888,32 +1100,44 @@ require_once(APPROOT.'application/dashboard.class.inc.php');
*/
class DashboardMenuNode extends MenuNode
{
/**
* @var string
*/
protected $sDashboardFile;
/**
* Create a menu item based on a custom template and inserts it into the application's main menu
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
* @param string $sTemplateFile Path (or URL) to the file that will be used as a template for displaying the page's content
* @param string $sDashboardFile
* @param integer $iParentIndex ID of the parent menu
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
* @param string $sEnableClass Name of class of object
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @return MenuNode
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS
* @param string $sEnableStimulus
*/
public function __construct($sMenuId, $sDashboardFile, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
public function __construct($sMenuId, $sDashboardFile, $iParentIndex, $fRank = 0.0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sDashboardFile = $sDashboardFile;
$this->aReflectionProperties['definition_file'] = $sDashboardFile;
}
/**
* @param string[] $aExtraParams
* @return string
*/
public function GetHyperlink($aExtraParams)
{
if ($this->sDashboardFile == '') return '';
return parent::GetHyperlink($aExtraParams);
}
/**
* @return null|RuntimeDashboard
* @throws CoreException
* @throws Exception
*/
public function GetDashboard()
{
$sDashboardDefinition = @file_get_contents($this->sDashboardFile);
@@ -945,8 +1169,15 @@ class DashboardMenuNode extends MenuNode
return $oDashboard;
}
/**
* @param WebPage $oPage
* @param string[] $aExtraParams
* @throws CoreException
* @throws Exception
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
$oDashboard = $this->GetDashboard();
if ($oDashboard != null)
{
@@ -1020,7 +1251,12 @@ EOF
$oPage->p("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
}
}
/**
* @param WebPage $oPage
* @throws CoreException
* @throws Exception
*/
public function RenderEditor(WebPage $oPage)
{
$oDashboard = $this->GetDashboard();
@@ -1033,7 +1269,11 @@ EOF
$oPage->p("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
}
}
/**
* @param $oDashlet
* @throws Exception
*/
public function AddDashlet($oDashlet)
{
$oDashboard = $this->GetDashboard();
@@ -1055,15 +1295,28 @@ EOF
*/
class ShortcutContainerMenuNode extends MenuNode
{
/**
* @param string[] $aExtraParams
* @return string
*/
public function GetHyperlink($aExtraParams)
{
return '';
}
/**
* @param WebPage $oPage
* @param string[] $aExtraParams
* @return mixed|void
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
}
/**
* @throws CoreException
* @throws Exception
*/
public function PopulateChildMenus()
{
// Load user shortcuts in DB
@@ -1075,7 +1328,7 @@ class ShortcutContainerMenuNode extends MenuNode
while ($oShortcut = $oBMSet->Fetch())
{
$sName = $this->GetMenuId().'_'.$oShortcut->GetKey();
$oShortcutMenu = new ShortcutMenuNode($sName, $oShortcut, $this->GetIndex(), $fRank++);
new ShortcutMenuNode($sName, $oShortcut, $this->GetIndex(), $fRank++);
}
// Complete the tree
@@ -1091,8 +1344,11 @@ require_once(APPROOT.'application/shortcut.class.inc.php');
*/
class ShortcutMenuNode extends MenuNode
{
/**
* @var Shortcut
*/
protected $oShortcut;
/**
* Create a menu item based on a custom template and inserts it into the application's main menu
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
@@ -1102,15 +1358,20 @@ class ShortcutMenuNode extends MenuNode
* @param string $sEnableClass Name of class of object
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @return MenuNode
* @param string $sEnableStimulus
*/
public function __construct($sMenuId, $oShortcut, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
public function __construct($sMenuId, $oShortcut, $iParentIndex, $fRank = 0.0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->oShortcut = $oShortcut;
$this->aReflectionProperties['shortcut'] = $oShortcut->GetKey();
}
/**
* @param string[] $aExtraParams
* @return string
* @throws CoreException
*/
public function GetHyperlink($aExtraParams)
{
$sContext = $this->oShortcut->Get('context');
@@ -1126,16 +1387,31 @@ class ShortcutMenuNode extends MenuNode
return parent::GetHyperlink($aExtraParams);
}
/**
* @param WebPage $oPage
* @param string[] $aExtraParams
* @return mixed|void
* @throws DictExceptionMissingString
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
$this->oShortcut->RenderContent($oPage, $aExtraParams);
}
/**
* @return string
* @throws CoreException
*/
public function GetTitle()
{
return $this->oShortcut->Get('name');
}
/**
* @return string
* @throws CoreException
*/
public function GetLabel()
{
return $this->oShortcut->Get('name');

View File

@@ -37,10 +37,11 @@ class NiceWebPage extends WebPage
{
parent::__construct($s_title, $bPrintable);
$this->m_aReadyScripts = array();
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-1.10.0.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-1.2.1.min.js'); // Needed since many other plugins still rely on oldies like $.browser
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/ui-lightness/jquery-ui-1.10.3.custom.min.css');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-ui-1.10.3.custom.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-1.12.4.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-1.4.1.min.js'); // Needed since many other plugins still rely on oldies like $.browser
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/ui-lightness/jquery-ui-1.11.4.custom.css');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-ui-1.11.4.custom.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/utils.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/hovertip.js');
// table sorting
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.tablesorter.js');
@@ -50,7 +51,25 @@ class NiceWebPage extends WebPage
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/datatable.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.positionBy.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.popupmenu.js');
$this->add_ready_script(
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/searchformforeignkeys.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/latinise/latinise.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_handler.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_handler_history.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_raw.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_string.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_external_field.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_numeric.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_enum.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_external_key.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_hierarchical_key.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_abstract.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_time.js');
$this->add_dict_entries('UI:Combo');
$this->add_ready_script(
<<< EOF
//add new widget called TruncatedList to properly display truncated lists when they are sorted
$.tablesorter.addWidget({

View File

@@ -952,7 +952,7 @@ EOF
$sTransactionId = utils::GetNewTransactionId();
$this->SetTransactionId($sTransactionId);
$this->add("<input type=\"hidden\" id=\"transaction_id\" name=\"transaction_id\" value=\"$sTransactionId\">\n");
$this->add_ready_script("$(window).unload(function() { OnUnload('$sTransactionId') } );\n");
$this->add_ready_script("$(window).on('unload', function() { OnUnload('$sTransactionId') } );\n");
}
public function WizardFormButtons($iButtonFlags)

View File

@@ -32,7 +32,7 @@ abstract class Query extends cmdbAbstractObject
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui,application",
"category" => "core/cmdb,view_in_gui,application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -64,7 +64,7 @@ class QueryOQL extends Query
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui,application",
"category" => "core/cmdb,view_in_gui,application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",

View File

@@ -197,7 +197,7 @@ class ShortcutOQL extends Shortcut
}
$bSearchPane = true;
$bSearchOpen = false;
$bSearchOpen = true;
try
{
OQLMenuNode::RenderOQLSearch($this->Get('oql'), $this->Get('name'), 'shortcut_'.$this->GetKey(), $bSearchPane, $bSearchOpen, $oPage, $aExtraParams, true);

View File

@@ -19,15 +19,15 @@
* Class UIExtKeyWidget
* UI wdiget for displaying and editing external keys when
* A simple drop-down list is not enough...
*
*
* The layout is the following
*
*
* +-- #label_<id> (input)-------+ +-----------+
* | | | Browse... |
* +-----------------------------+ +-----------+
*
*
* And the popup dialog has the following layout:
*
*
* +------------------- ac_dlg_<id> (div)-----------+
* + +--- ds_<id> (div)---------------------------+ |
* | | +------------- fs_<id> (form)------------+ | |
@@ -61,13 +61,16 @@
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
class UIExtKeyWidget
class UIExtKeyWidget
{
const ENUM_OUTPUT_FORMAT_CSV = 'csv';
const ENUM_OUTPUT_FORMAT_JSON = 'json';
protected $iId;
protected $sTargetClass;
protected $sAttCode;
protected $bSearchMode;
//public function __construct($sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '')
static public function DisplayFromAttCode($oPage, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '', $aArgs, $bSearchMode = false)
{
@@ -94,7 +97,7 @@ class UIExtKeyWidget
$this->sAttCode = $sAttCode;
$this->bSearchMode = $bSearchMode;
}
/**
* Get the HTML fragment corresponding to the ext key editing widget
* @param WebPage $oP The web page used for all the output
@@ -107,10 +110,10 @@ class UIExtKeyWidget
{
$this->bSearchMode = $bSearchMode;
}
$sTitle = addslashes($sTitle);
$sTitle = addslashes($sTitle);
$oPage->add_linked_script('../js/extkeywidget.js');
$oPage->add_linked_script('../js/forms-json-utils.js');
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
$bExtensions = true;
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
@@ -123,7 +126,7 @@ class UIExtKeyWidget
$sWizHelper = 'null';
$sWizHelperJSON = "''";
$sJSSearchMode = 'true';
}
}
else
{
if (isset($aArgs['wizHelper']))
@@ -142,12 +145,7 @@ class UIExtKeyWidget
throw new Exception('Implementation: null value for allowed values definition');
}
$oAllowedValues->SetShowObsoleteData(utils::ShowObsoleteData());
// 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)
if ($oAllowedValues->Count() < $iMaxComboLength)
{
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
switch($sDisplayStyle)
@@ -164,7 +162,7 @@ class UIExtKeyWidget
while($oObj = $oAllowedValues->Fetch())
{
$aAllowedValues[$oObj->GetKey()] = $oObj->GetName();
}
}
$sHTMLValue .= $oPage->GetRadioButtons($aAllowedValues, $value, $this->iId, "{$sAttrFieldPrefix}{$sFieldName}", false /* $bMandatory will be placed manually */, $bVertical, $sValidationField);
$aEventsList[] ='change';
break;
@@ -173,7 +171,7 @@ class UIExtKeyWidget
case 'list':
default:
$sSelectMode = 'true';
$sHelpText = ''; //$this->oAttDef->GetHelpOnEdition();
$sHTMLValue .= "<div class=\"field_select_wrapper\">\n";
@@ -201,7 +199,7 @@ class UIExtKeyWidget
{
$key = $oObj->GetKey();
$display_value = $oObj->GetName();
if (($oAllowedValues->Count() == 1) && ($bMandatory == 'true') )
{
// When there is only once choice, select it by default
@@ -231,7 +229,7 @@ class UIExtKeyWidget
}
$oPage->add_ready_script(
<<<EOF
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
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') } );
@@ -244,7 +242,7 @@ EOF
{
// Too many choices, use an autocomplete
$sSelectMode = 'false';
// Check that the given value is allowed
$oSearch = $oAllowedValues->GetFilter();
$oSearch->AddCondition('id', $value);
@@ -264,19 +262,19 @@ EOF
}
$iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 3; //@@@ $this->oAttDef->GetMinAutoCompleteChars();
$iFieldSize = isset($aArgs['iFieldSize']) ? $aArgs['iFieldSize'] : 20; //@@@ $this->oAttDef->GetMaxSize();
// the input for the auto-complete
$sHTMLValue .= "<input class=\"field_autocomplete\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.Search();\"/></span>";
$sHTMLValue .= "<input class=\"field_autocomplete\" count=\"".$oAllowedValues->Count()."\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif?t=".utils::GetCacheBusterTimestamp()."\" onClick=\"oACWidget_{$this->iId}.Search();\"/></span>";
// another hidden input to store & pass the object's Id
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
// 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, $sJSDoSearch);
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
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 !
@@ -291,7 +289,7 @@ EOF
}
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false)
{
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/></span>";
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif?t=".utils::GetCacheBusterTimestamp()."\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/></span>";
$oPage->add_ready_script(
<<<EOF
if ($('#ac_tree_{$this->iId}').length == 0)
@@ -305,7 +303,7 @@ EOF
{
$sCallbackName = (MetaModel::IsAbstract($this->sTargetClass)) ? 'SelectObjectClass' : 'CreateObject';
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\"/></span>";
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif?t=".utils::GetCacheBusterTimestamp()."\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\"/></span>";
$oPage->add_ready_script(
<<<EOF
if ($('#ajax_{$this->iId}').length == 0)
@@ -325,7 +323,7 @@ EOF
return $sHTMLValue;
}
public function GetSearchDialog(WebPage $oPage, $sTitle, $oCurrObject = null)
{
$sHTML = '<div class="wizContainer" style="vertical-align:top;"><div id="dc_'.$this->iId.'">';
@@ -343,10 +341,18 @@ EOF
$aParams = array();
$oFilter = new DBObjectSearch($this->sTargetClass);
}
$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));
$sHTML .= $oBlock->GetDisplay($oPage, $this->iId,
array(
'menu' => false,
'currentId' => $this->iId,
'table_id' => "dr_{$this->iId}",
'table_inner_id' => "{$this->iId}_results",
'selection_mode' => true,
'selection_type' => 'single',
'cssCount' => '#count_'.$this->iId)
);
$sHTML .= "<form id=\"fr_{$this->iId}\" OnSubmit=\"return oACWidget_{$this->iId}.DoOk();\">\n";
$sHTML .= "<div id=\"dr_{$this->iId}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
$sHTML .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
@@ -380,7 +386,7 @@ EOF
{
throw new Exception('Implementation: null value for allowed values definition');
}
$oFilter = DBObjectSearch::FromOQL($sFilter);
if (strlen($sRemoteClass) > 0)
{
@@ -394,7 +400,7 @@ EOF
$oBlock = new DisplayBlock($oFilter, 'list', false, array('query_params' => array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId)));
$oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'table_id' => 'select_'.$this->sAttCode)); // Don't display the 'Actions' menu on the results
}
/**
* Search for objects to be selected
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
@@ -402,7 +408,7 @@ EOF
* @param DBObject $oObj The current object for the OQL context
* @param string $sContains The text of the autocomplete to filter the results
*/
public function AutoComplete(WebPage $oP, $sFilter, $oObj = null, $sContains)
public function AutoComplete(WebPage $oP, $sFilter, $oObj = null, $sContains, $sOutputFormat = self::ENUM_OUTPUT_FORMAT_CSV)
{
if (is_null($sFilter))
{
@@ -413,32 +419,26 @@ 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);
$aValuesEquals = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'equals_start_with');
//asort($aValuesEquals);
foreach($aValuesEquals as $sKey => $sFriendlyName)
switch($sOutputFormat)
{
$oP->add(trim($sFriendlyName)."\t".$sKey."\n");
$iMax--;
}
if ($iMax <= 0)
{
return;
}
case static::ENUM_OUTPUT_FORMAT_JSON:
// Array flip to preserve values order on the label, otherwise the JS will re-order regarding the keys.
$oP->SetContentType('application/json');
$oP->add(json_encode(array_flip($aValues)));
break;
$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");
}
case static::ENUM_OUTPUT_FORMAT_CSV:
foreach($aValues as $sKey => $sFriendlyName)
{
$oP->add(trim($sFriendlyName)."\t".$sKey."\n");
}
break;
default:
throw new Exception('Invalid output format, "'.$sOutputFormat.'" given.');
break;
}
}
@@ -506,7 +506,7 @@ EOF
/**
* Get the form to create a new object of the 'target' class
*/
public function GetObjectCreationForm(WebPage $oPage, $oCurrObject)
public function GetObjectCreationForm(WebPage $oPage, $oCurrObject, $aPrefillFormParam)
{
// Set all the default values in an object and clone this "default" object
$oNewObj = MetaModel::NewObject($this->sTargetClass);
@@ -514,7 +514,7 @@ EOF
// 1st - set context values
$oAppContext = new ApplicationContext();
$oAppContext->InitObjectFromContext($oNewObj);
$oNewObj->PrefillForm('creation_from_extkey', $aPrefillFormParam);
// 2nd set the default values from the constraint on the external key... if any
if ( ($oCurrObject != null) && ($this->sAttCode != ''))
{
@@ -531,7 +531,7 @@ EOF
}
}
}
// 3rd - set values from the page argument 'default'
$oNewObj->UpdateObjectFromArg('default');
@@ -548,7 +548,7 @@ EOF
$aFieldsComments[$sAttCode] = '&nbsp;<img src="../images/transp-lock.png" style="vertical-align:middle" title="'.htmlentities(Dict::S('UI:UploadNotSupportedInThisMode')).'"/>';
}
}
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), array('formPrefix' => $this->iId, 'noRelations' => true, 'fieldsFlags' => $aFieldsFlags, 'fieldsComments' => $aFieldsComments));
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), array('formPrefix' => $this->iId, 'noRelations' => true, 'fieldsFlags' => $aFieldsFlags, 'fieldsComments' => $aFieldsComments));
$oPage->add('</div></div></div>');
// $oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: $(window).width()*0.8, height: 'auto', autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
$oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
@@ -592,7 +592,7 @@ EOF
$oPage->add('</div>');
$oPage->add("<input type=\"button\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_tree_{$this->iId}').dialog('close');\">&nbsp;&nbsp;");
$oPage->add("<input type=\"button\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\" onClick=\"oACWidget_{$this->iId}.DoHKOk();\">");
$oPage->add('</div></div>');
$oPage->add_ready_script("\$('#tree_$this->iId ul').treeview();\n");
$oPage->add_ready_script("\$('#dlg_tree_$this->iId').dialog({ width: 'auto', height: 'auto', autoOpen: true, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.OnHKResize, close: oACWidget_{$this->iId}.OnHKClose });\n");
@@ -614,7 +614,7 @@ EOF
}
else
{
return array('error' => implode(' ', $aErrors), 'id' => 0);
return array('error' => implode(' ', $aErrors), 'id' => 0);
}
}
catch(Exception $e)
@@ -637,7 +637,7 @@ EOF
$aTree[$iParentId][$oObj->GetKey()] = $oObj->GetName();
$aNodes[$oObj->GetKey()] = $oObj;
}
$aParents = array_keys($aTree);
$aRoots = array();
foreach($aParents as $id)
@@ -652,7 +652,7 @@ EOF
$this->DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue);
}
}
function DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue)
{
$bSelect = true;

View File

@@ -179,7 +179,7 @@ class UILinksWidgetDirect
* @param WebPage $oPage
* @param string $sProposedRealClass
*/
public function GetObjectCreationDlg(WebPage $oPage, $sProposedRealClass = '')
public function GetObjectCreationDlg(WebPage $oPage, $sProposedRealClass = '', $oSourceObj = null)
{
// For security reasons: check that the "proposed" class is actually a subclass of the linked class
// and that the current user is allowed to create objects of this class
@@ -211,7 +211,10 @@ class UILinksWidgetDirect
$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));
$oObj = DBObject::MakeDefaultInstance($sRealClass);
$aPrefillParam = array('source_obj' => $oSourceObj);
$oObj->PrefillForm('creation_from_editinplace', $aPrefillParam);
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), array('formPrefix' => $this->sInputid, 'noRelations' => true, 'fieldsFlags' => $aFieldFlags));
}
else
{
@@ -272,23 +275,42 @@ class UILinksWidgetDirect
$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});");
$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 });");
}
/**
* @param WebPage $oPage
* @param DBObject $oCurrentObj
* @throws Exception
* @param $aAlreadyLinked
*
* @throws \CoreException
* @throws \Exception
* @throws \OQLException
*/
public function GetObjectsSelectionDlg($oPage, $oCurrentObj)
public function GetObjectsSelectionDlg($oPage, $oCurrentObj, $aAlreadyLinked)
{
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$valuesDef = $oLinksetDef->GetValuesDef();
$oHiddenFilter = new DBObjectSearch($this->sLinkedClass);
if (($oCurrentObj != null) && MetaModel::IsSameFamilyBranch($this->sLinkedClass, $this->sClass))
{
// Prevent linking to self if the linked object is of the same family
// and already present in the database
if (!$oCurrentObj->IsNew())
{
$oHiddenFilter->AddCondition('id', $oCurrentObj->GetKey(), '!=');
}
}
if (count($aAlreadyLinked) > 0)
{
$oHiddenFilter->AddCondition('id', $aAlreadyLinked, 'NOTIN');
}
$oHiddenCriteria = $oHiddenFilter->GetCriteria();
$aArgs = $oHiddenFilter->GetInternalParams();
$sHiddenCriteria = $oHiddenCriteria->Render($aArgs);
$oLinkSetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$valuesDef = $oLinkSetDef->GetValuesDef();
if ($valuesDef === null)
{
$oFilter = new DBObjectSearch($this->sLinkedClass);
@@ -301,13 +323,25 @@ class UILinksWidgetDirect
}
$oFilter = DBObjectSearch::FromOQL($valuesDef->GetFilterExpression());
}
if ($oCurrentObj != null)
{
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$aArgs = array_merge($oCurrentObj->ToArgs('this'), $oFilter->GetInternalParams());
$oFilter->SetInternalParams($aArgs);
}
$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 .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->sInputid}",
array(
'result_list_outer_selector' => "SearchResultsToAdd_{$this->sInputid}",
'table_id' => "add_{$this->sInputid}",
'table_inner_id' => "ResultsToAdd_{$this->sInputid}",
'cssCount' => "#count_{$this->sInputid}",
'query_params' => $oFilter->GetInternalParams(),
'hidden_criteria' => $sHiddenCriteria,
)
);
$sHtml .= "<form id=\"ObjectsAddForm_{$this->sInputid}\">\n";
$sHtml .= "<div id=\"SearchResultsToAdd_{$this->sInputid}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
$sHtml .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
@@ -333,8 +367,8 @@ class UILinksWidgetDirect
{
$sRemoteClass = $this->sLinkedClass;
}
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$valuesDef = $oLinksetDef->GetValuesDef();
$oLinkSetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$valuesDef = $oLinkSetDef->GetValuesDef();
if ($valuesDef === null)
{
$oFilter = new DBObjectSearch($sRemoteClass);
@@ -351,7 +385,7 @@ class UILinksWidgetDirect
if (($oCurrentObj != null) && MetaModel::IsSameFamilyBranch($sRemoteClass, $this->sClass))
{
// Prevent linking to self if the linked object is of the same family
// and laready present in the database
// and already present in the database
if (!$oCurrentObj->IsNew())
{
$oFilter->AddCondition('id', $oCurrentObj->GetKey(), '!=');
@@ -363,6 +397,8 @@ class UILinksWidgetDirect
}
if ($oCurrentObj != null)
{
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$aArgs = array_merge($oCurrentObj->ToArgs('this'), $oFilter->GetInternalParams());
$oFilter->SetInternalParams($aArgs);
}

View File

@@ -24,8 +24,8 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
require_once(APPROOT.'application/webpage.class.inc.php');
require_once(APPROOT.'application/displayblock.class.inc.php');
class UILinksWidget
{
@@ -39,14 +39,21 @@ class UILinksWidget
protected $m_sLinkedClass;
protected $m_sRemoteClass;
protected $m_bDuplicatesAllowed;
protected $m_aEditableFields;
protected $m_aTableConfig;
/**
* UILinksWidget constructor.
*
* @param string $sClass
* @param string $sAttCode
* @param int $iInputId
* @param string $sNameSuffix
* @param bool $bDuplicatesAllowed
*
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \Exception
*/
public function __construct($sClass, $sAttCode, $iInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
{
@@ -56,12 +63,15 @@ class UILinksWidget
$this->m_iInputId = $iInputId;
$this->m_bDuplicatesAllowed = $bDuplicatesAllowed;
$this->m_aEditableFields = array();
/** @var AttributeLinkedSetIndirect $oAttDef */
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode);
$this->m_sLinkedClass = $oAttDef->GetLinkedClass();
$this->m_sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
$this->m_sExtKeyToMe = $oAttDef->GetExtKeyToMe();
$oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote);
/** @var AttributeExternalKey $oLinkingAttDef */
$oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote);
$this->m_sRemoteClass = $oLinkingAttDef->GetTargetClass();
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
$sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass);
@@ -103,6 +113,7 @@ class UILinksWidget
/**
* A one-row form for editing a link record
*
* @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
@@ -110,7 +121,12 @@ class UILinksWidget
* @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
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \Exception
*/
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false)
{
@@ -301,12 +317,17 @@ EOF
/**
* Get the HTML fragment corresponding to the linkset editing widget
*
* @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
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \DictExceptionMissingString
*/
public function Display(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
{
@@ -344,12 +365,9 @@ 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}', $sJSDoSearch);
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}.Init();
EOF
);
@@ -364,16 +382,21 @@ EOF
/**
* @param string $sClass
* @param string $sAttCode
*
* @return string
* @throws \Exception
*/
protected static function GetTargetClass($sClass, $sAttCode)
{
/** @var AttributeLinkedSet $oAttDef */
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sLinkedClass = $oAttDef->GetLinkedClass();
$sTargetClass = '';
switch(get_class($oAttDef))
{
case 'AttributeLinkedSetIndirect':
/** @var AttributeExternalKey $oLinkingAttDef */
/** @var AttributeLinkedSetIndirect $oAttDef */
$oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote());
$sTargetClass = $oLinkingAttDef->GetTargetClass();
break;
@@ -389,21 +412,56 @@ EOF
/**
* @param WebPage $oPage
* @param DBObject $oCurrentObj
* @param $sJson
* @param array $aAlreadyLinkedIds
*
* @throws DictExceptionMissingString
* @throws Exception
*/
public function GetObjectPickerDialog($oPage, $oCurrentObj)
public function GetObjectPickerDialog($oPage, $oCurrentObj, $sJson, $aAlreadyLinkedIds = array(), $aPrefillFormParam = array())
{
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open') || utils::IsHighCardinality($this->m_sRemoteClass);
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
$oAlreadyLinkedFilter = new DBObjectSearch($this->m_sRemoteClass);
if (!$this->m_bDuplicatesAllowed && count($aAlreadyLinkedIds) > 0)
{
$oAlreadyLinkedFilter->AddCondition('id', $aAlreadyLinkedIds, 'NOTIN');
$oAlreadyLinkedExpression = $oAlreadyLinkedFilter->GetCriteria();
$sAlreadyLinkedExpression = $oAlreadyLinkedExpression->Render();
}
else
{
$sAlreadyLinkedExpression = '';
}
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
if(!empty($oCurrentObj))
{
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$aPrefillFormParam['filter'] = $oFilter;
$aPrefillFormParam['dest_class'] = $this->m_sRemoteClass;
$oCurrentObj->PrefillForm('search', $aPrefillFormParam);
}
$oBlock = new DisplayBlock($oFilter, 'search', false);
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}", array('open' => $bOpen));
$sHtml .= "<form id=\"ObjectsAddForm_{$this->m_sAttCode}{$this->m_sNameSuffix}\" OnSubmit=\"return oWidget{$this->m_iInputId}.DoAddObjects(this.id);\">\n";
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}",
array(
'menu' => false,
'result_list_outer_selector' => "SearchResultsToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}",
'table_id' => 'add_'.$this->m_sAttCode,
'table_inner_id' => "ResultsToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}",
'selection_mode' => true,
'json' => $sJson,
'cssCount' => '#count_'.$this->m_sAttCode.$this->m_sNameSuffix,
'query_params' => $oFilter->GetInternalParams(),
'hidden_criteria' => $sAlreadyLinkedExpression,
));
$sHtml .= "<form id=\"ObjectsAddForm_{$this->m_sAttCode}{$this->m_sNameSuffix}\">\n";
$sHtml .= "<div id=\"SearchResultsToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
$sHtml .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
$sHtml .= "</div>\n";
$sHtml .= "<input type=\"hidden\" id=\"count_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"0\"/>";
$sHtml .= "<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('close');\">&nbsp;&nbsp;<input id=\"btn_ok_{$this->m_sAttCode}{$this->m_sNameSuffix}\" disabled=\"disabled\" type=\"submit\" value=\"".Dict::S('UI:Button:Add')."\">";
$sHtml .= "<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('close');\">&nbsp;&nbsp;<input id=\"btn_ok_{$this->m_sAttCode}{$this->m_sNameSuffix}\" disabled=\"disabled\" type=\"button\" onclick=\"return oWidget{$this->m_iInputId}.DoAddObjects(this.id);\" value=\"".Dict::S('UI:Button:Add')."\">";
$sHtml .= "</div>\n";
$sHtml .= "</form>\n";
$oPage->add($sHtml);
@@ -415,11 +473,17 @@ EOF
/**
* 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 m_sRemoteClass
* @param Array $aAlreadyLinkedIds List of IDs of objects of "remote" class already linked, to be filtered out of the search
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of
* m_sRemoteClass
* @param array $aAlreadyLinkedIds List of IDs of objects of "remote" class already linked, to be filtered out of
* the search
*
* @throws \CoreException
* @throws \Exception
*/
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinkedIds = array())
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinkedIds = array(), $oCurrentObj = null)
{
if ($sRemoteClass != '')
{
@@ -435,6 +499,7 @@ EOF
{
$oFilter->AddCondition('id', $aAlreadyLinkedIds, 'NOTIN');
}
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$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
}
@@ -444,6 +509,9 @@ EOF
* @param int $iMaxAddedId
* @param $oFullSetFilter
* @param DBObject $oCurrentObj
*
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function DoAddObjects(WebPage $oP, $iMaxAddedId, $oFullSetFilter, $oCurrentObj)
{
@@ -465,11 +533,15 @@ 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|DBObjectSearch $oSearch
*
* @throws \CoreException
* @throws \Exception
*/
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
{
@@ -501,7 +573,31 @@ EOF
if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue))
{
$oSearch->AddCondition($sAttCode, $defaultValue);
// Add Hierarchical condition if hierarchical key
$oAttDef = MetaModel::GetAttributeDef($sDestClass, $sAttCode);
if (isset($oAttDef) && ($oAttDef->IsExternalKey()))
{
try
{
/** @var AttributeExternalKey $oAttDef */
$sTargetClass = $oAttDef->GetTargetClass();
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
if ($sHierarchicalKeyCode !== false)
{
$oFilter = new DBObjectSearch($sTargetClass);
$oFilter->AddCondition('id', $defaultValue);
$oHKFilter = new DBObjectSearch($sTargetClass);
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
}
} catch (Exception $e)
{
}
}
else
{
$oSearch->AddCondition($sAttCode, $defaultValue);
}
}
}
}

View File

@@ -0,0 +1,115 @@
<?php
/**
*
* Copyright (C) 2010-2018 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*
*/
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
class UISearchFormForeignKeys
{
public function __construct($sTargetClass, $iInputId = null)
{
$this->m_sRemoteClass = $sTargetClass;
$this->m_iInputId = $iInputId;
}
/**
* @param WebPage $oPage
*
* @param $sTitle
*
* @throws \Exception
*/
public function ShowModalSearchForeignKeys($oPage, $sTitle)
{
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
$oBlock = new DisplayBlock($oFilter, 'search', false);
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_iInputId}",
array(
'menu' => false,
'result_list_outer_selector' => "SearchResultsToAdd_{$this->m_iInputId}",
'table_id' => "add_{$this->m_iInputId}",
'table_inner_id' => "ResultsToAdd_{$this->m_iInputId}",
'selection_mode' => true,
'cssCount' => "#count_{$this->m_iInputId}",
'query_params' => $oFilter->GetInternalParams(),
));
$sHtml .= "<form id=\"ObjectsAddForm_{$this->m_iInputId}\">\n";
$sHtml .= "<div id=\"SearchResultsToAdd_{$this->m_iInputId}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
$sHtml .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
$sHtml .= "</div>\n";
$sHtml .= "<input type=\"hidden\" id=\"count_{$this->m_iInputId}\" value=\"0\"/>";
$sHtml .= "<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_{$this->m_iInputId}').dialog('close');\">&nbsp;&nbsp;<input id=\"btn_ok_{$this->m_iInputId}\" disabled=\"disabled\" type=\"button\" onclick=\"return oForeignKeysWidget{$this->m_iInputId}.DoAddObjects(this.id);\" value=\"".Dict::S('UI:Button:Add')."\">";
$sHtml .= "</div>\n";
$sHtml .= "</form>\n";
$oPage->add($sHtml);
$oPage->add_ready_script("$('#dlg_{$this->m_iInputId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, resizeStop: oForeignKeysWidget{$this->m_iInputId}.UpdateSizes });");
$oPage->add_ready_script("$('#dlg_{$this->m_iInputId}').dialog('option', {title:'$sTitle'});");
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_iInputId} form').bind('submit.uilinksWizard', oForeignKeysWidget{$this->m_iInputId}.SearchObjectsToAdd);");
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_iInputId}').resize(oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);");
}
public function GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter)
{
try
{
$aLinkedObjects = utils::ReadMultipleSelectionWithFriendlyname($oFullSetFilter);
$oPage->add(json_encode($aLinkedObjects));
}
catch (CoreException $e)
{
http_response_code(500);
$oPage->add(json_encode(array('error' => $e->GetMessage())));
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
}
}
/**
* 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 m_sRemoteClass
*
* @throws \Exception
*/
public function ListResultsSearchForeignKeys(WebPage $oP, $sRemoteClass = '')
{
if ($sRemoteClass != '')
{
// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
$oFilter = new DBObjectSearch($sRemoteClass);
}
else
{
// No remote class specified use the one defined in the linkedset
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
}
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, "ResultsToAdd_{$this->m_iInputId}",
array('menu' => false, 'cssCount' => "#count_{$this->m_iInputId}", 'selection_mode' => true, 'table_id' => "add_{$this->m_iInputId}"));
}
}

View File

@@ -413,8 +413,10 @@ class utils
/**
* Interprets the results posted by a normal or paginated list (in multiple selection mode)
*
* @param $oFullSetFilter DBSearch The criteria defining the whole sets of objects being selected
* @return Array An arry of object IDs corresponding to the objects selected in the set
*
* @return Array An array of object IDs corresponding to the objects selected in the set
*/
public static function ReadMultipleSelection($oFullSetFilter)
{
@@ -448,6 +450,51 @@ class utils
return $aSelectedObj;
}
/**
* Interprets the results posted by a normal or paginated list (in multiple selection mode)
*
* @param DBSearch $oFullSetFilter The criteria defining the whole sets of objects being selected
*
* @return Array An array of object IDs:friendlyname corresponding to the objects selected in the set
* @throws \CoreException
*/
public static function ReadMultipleSelectionWithFriendlyname($oFullSetFilter)
{
$sSelectionMode = utils::ReadParam('selectionMode', '');
if ($sSelectionMode === '')
{
throw new CoreException('selectionMode is mandatory');
}
// Paginated selection
$aSelectedIds = utils::ReadParam('storedSelection', array());
if (count($aSelectedIds) > 0 )
{
if ($sSelectionMode == 'positive')
{
// Only the explicitly listed items are selected
$oFullSetFilter->AddCondition('id', $aSelectedIds, 'IN');
}
else
{
// All items of the set are selected, except the one explicitly listed
$oFullSetFilter->AddCondition('id', $aSelectedIds, 'NOTIN');
}
}
$aSelectedObj = array();
$oFullSet = new DBObjectSet($oFullSetFilter);
$sClassAlias = $oFullSetFilter->GetClassAlias();
$oFullSet->OptimizeColumnLoad(array($sClassAlias => array('friendlyname'))); // We really need only the IDs but it does not work since id is not a real field
while ($oObj = $oFullSet->Fetch())
{
$aSelectedObj[$oObj->GetKey()] = $oObj->Get('friendlyname');
}
return $aSelectedObj;
}
public static function GetNewTransactionId()
{
return privUITransaction::GetNewTransactionId();
@@ -961,7 +1008,7 @@ class utils
new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'), "mailto:?body=".urlencode($sUrl).' '), // Add an extra space to make it work in Outlook
);
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_BULK_READ, $param) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_BULK_READ, $param) != UR_ALLOWED_NO)
{
// Bulk export actions
$aResult[] = new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '$sDataTableId', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")");
@@ -1840,17 +1887,19 @@ class utils
}
return $aCleanHeaders;
}
/**
* Check if the given class if configured as a high cardinality class.
*
* @param $sClass
*
* @return bool
* Return a string based on compilation time or (if not available because the datamodel has not been loaded)
* the version of iTop. This string is useful to prevent browser side caching of content that may vary at each
* (re)installation of iTop (especially during development).
* @return string
*/
public static function IsHighCardinality($sClass)
public static function GetCacheBusterTimestamp()
{
$aHugeClasses = MetaModel::GetConfig()->Get('high_cardinality_classes');
return in_array($sClass, $aHugeClasses);
if(!defined('COMPILATION_TIMESTAMP'))
{
return ITOP_VERSION;
}
return COMPILATION_TIMESTAMP;
}
}

View File

@@ -58,6 +58,7 @@ class WebPage implements Page
protected $s_deferred_content;
protected $a_scripts;
protected $a_dict_entries;
protected $a_dict_entries_prefixes;
protected $a_styles;
protected $a_include_scripts;
protected $a_include_stylesheets;
@@ -80,6 +81,7 @@ class WebPage implements Page
$this->s_deferred_content = '';
$this->a_scripts = array();
$this->a_dict_entries = array();
$this->a_dict_entries_prefixes = array();
$this->a_styles = array();
$this->a_linked_scripts = array();
$this->a_linked_stylesheets = array();
@@ -165,9 +167,11 @@ class WebPage implements Page
/**
* Adds a tabular content to the web page
* @param Hash $aConfig Configuration of the table: hash array of 'column_id' => 'Column Label'
* @param Hash $aData Hash array. Data to display in the table: each row is made of 'column_id' => Data. A column 'pkey' is expected for each row
* @param Hash $aParams Hash array. Extra parameters for the table.
*
* @param string[] $aConfig Configuration of the table: hash array of 'column_id' => 'Column Label'
* @param string[] $aData Hash array. Data to display in the table: each row is made of 'column_id' => Data. A column 'pkey' is expected for each row
* @param array $aParams Hash array. Extra parameters for the table.
*
* @return void
*/
public function table($aConfig, $aData, $aParams = array())
@@ -241,10 +245,39 @@ class WebPage implements Page
/**
* Add a dictionary entry for the Javascript side
*/
public function add_dict_entry($s_entryId)
{
$this->a_dict_entries[$s_entryId] = Dict::S($s_entryId);
}
public function add_dict_entry($s_entryId)
{
$this->a_dict_entries[] = $s_entryId;
}
/**
* Add a set of dictionary entries (based on the given prefix) for the Javascript side
*/
public function add_dict_entries($s_entriesPrefix)
{
$this->a_dict_entries_prefixes[] = $s_entriesPrefix;
}
protected function get_dict_signature()
{
return str_replace('_', '', Dict::GetUserLanguage()).'-'.md5(implode(',', $this->a_dict_entries).'|'.implode(',', $this->a_dict_entries_prefixes));
}
protected function get_dict_file_content()
{
$aEntries = array();
foreach($this->a_dict_entries as $sCode)
{
$aEntries[$sCode] = Dict::S($sCode);
}
foreach($this->a_dict_entries_prefixes as $sPrefix)
{
$aEntries = array_merge($aEntries, Dict::ExportEntries($sPrefix));
}
$sJSFile = 'var aDictEntries = '.json_encode($aEntries);
return $sJSFile;
}
/**
@@ -345,7 +378,7 @@ class WebPage implements Page
*/
public function GetDetails($aFields)
{
$sHtml = "<div class=\"details\">\n";
$sHtml = "<div class=\"details\" id='search-widget-results-outer'>\n";
foreach($aFields as $aAttrib)
{
$sDataAttCode = isset($aAttrib['attcode']) ? "data-attcode=\"{$aAttrib['attcode']}\"" : '';
@@ -499,17 +532,20 @@ class WebPage implements Page
echo "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, shrink-to-fit=no\" />";
echo "<title>".htmlentities($this->s_title, ENT_QUOTES, 'UTF-8')."</title>\n";
echo $this->get_base_tag();
$this->output_dict_entries();
foreach($this->a_linked_scripts as $s_script)
{
// Make sure that the URL to the script contains the application's version number
// so that the new script do NOT get reloaded from the cache when the application is upgraded
if (strpos($s_script, '?') === false)
{
$s_script .= "?itopversion=".ITOP_VERSION;
$s_script .= "?t=".utils::GetCacheBusterTimestamp();
}
else
{
$s_script .= "&itopversion=".ITOP_VERSION;
$s_script .= "&t=".utils::GetCacheBusterTimestamp();
}
echo "<script type=\"text/javascript\" src=\"$s_script\"></script>\n";
}
@@ -522,16 +558,15 @@ class WebPage implements Page
}
echo "</script>\n";
}
$this->output_dict_entries();
foreach($this->a_linked_stylesheets as $a_stylesheet)
{
if (strpos($a_stylesheet['link'], '?') === false)
{
$s_stylesheet = $a_stylesheet['link']."?itopversion=".ITOP_VERSION;
$s_stylesheet = $a_stylesheet['link']."?t=".utils::GetCacheBusterTimestamp();
}
else
{
$s_stylesheet = $a_stylesheet['link']."&itopversion=".ITOP_VERSION;
$s_stylesheet = $a_stylesheet['link']."&t=".utils::GetCacheBusterTimestamp();
}
if ($a_stylesheet['condition'] != "")
{
@@ -555,7 +590,7 @@ class WebPage implements Page
}
if (class_exists('MetaModel') && MetaModel::GetConfig())
{
echo "<link rel=\"shortcut icon\" href=\"".utils::GetAbsoluteUrlAppRoot()."images/favicon.ico?itopversion=".ITOP_VERSION."\" />\n";
echo "<link rel=\"shortcut icon\" href=\"".utils::GetAbsoluteUrlAppRoot()."images/favicon.ico?t=".utils::GetCacheBusterTimestamp()."\" />\n";
}
echo "</head>\n";
echo "<body>\n";
@@ -776,36 +811,21 @@ class WebPage implements Page
protected function output_dict_entries($bReturnOutput = false)
{
$sHtml = '';
if (count($this->a_dict_entries)>0)
if ((count($this->a_dict_entries) > 0) || (count($this->a_dict_entries_prefixes) > 0))
{
$sHtml .= "<script type=\"text/javascript\">\n";
$sHtml .= "var Dict = {};\n";
$sHtml .= "Dict._entries = {};\n";
$sHtml .= "Dict.S = function(sEntry) {\n";
$sHtml .= " if (sEntry in Dict._entries)\n";
$sHtml .= " {\n";
$sHtml .= " return Dict._entries[sEntry];\n";
$sHtml .= " }\n";
$sHtml .= " else\n";
$sHtml .= " {\n";
$sHtml .= " return sEntry;\n";
$sHtml .= " }\n";
$sHtml .= "};\n";
foreach($this->a_dict_entries as $s_entry => $s_value)
if (class_exists('Dict'))
{
$sHtml .= "Dict._entries['$s_entry'] = '".addslashes($s_value)."';\n";
// The dictionary may not be available for example during the setup...
// Create a specific dictionary file and load it as a JS script
$sSignature = $this->get_dict_signature();
$sJSFileName = utils::GetCachePath().$sSignature.'.js';
if (!file_exists($sJSFileName) && is_writable(utils::GetCachePath()))
{
file_put_contents($sJSFileName, $this->get_dict_file_content());
}
// Load the dictionary as the first javascript file, so that other JS file benefit from the translations
array_unshift($this->a_linked_scripts, utils::GetAbsoluteUrlAppRoot().'pages/ajax.document.php?operation=dict&s='.$sSignature);
}
$sHtml .= "</script>\n";
}
if ($bReturnOutput)
{
return $sHtml;
}
else
{
echo $sHtml;
}
}
}

View File

@@ -39,7 +39,7 @@ abstract class Action extends cmdbAbstractObject
{
$aParams = array
(
"category" => "core/cmdb",
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -103,7 +103,7 @@ abstract class ActionNotification extends Action
{
$aParams = array
(
"category" => "core/cmdb",
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -136,7 +136,7 @@ class ActionEmail extends ActionNotification
{
$aParams = array
(
"category" => "core/cmdb,application",
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",

View File

@@ -29,35 +29,11 @@
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);
$sRootCacheDir = apcFile::GetCacheFileName();
$aInfo['cache_list'] = apcFile::GetCacheEntries($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
@@ -71,40 +47,11 @@ function apc_store($key, $var = NULL, $ttl = 0)
$aResult = array();
foreach($key as $sKey => $value)
{
$aResult[] = apc_emul_store_unit($sKey, $value, $ttl);
$aResult[] = apcFile::StoreOneFile($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;
return apcFile::StoreOneFile($key, $var, $ttl);
}
/**
@@ -118,45 +65,11 @@ function apc_fetch($key)
$aResult = array();
foreach($key as $sKey)
{
$aResult[$sKey] = apc_emul_fetch_unit($sKey);
$aResult[$sKey] = apcFile::FetchOneFile($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;
return apcFile::FetchOneFile($key);
}
/**
@@ -165,36 +78,7 @@ function apc_emul_readcache_locked($sFilename)
*/
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;
}
}
apcFile::DeleteEntry(utils::GetCachePath());
return true;
}
@@ -204,98 +88,246 @@ function apc_emul_delete_entry($sCache)
*/
function apc_delete($key)
{
return apc_emul_delete_entry(apc_emul_get_cache_filename($key));
if (empty($key))
{
return false;
}
$bRet1 = apcFile::DeleteEntry(apcFile::GetCacheFileName($key));
$bRet2 = apcFile::DeleteEntry(apcFile::GetCacheFileName('-'.$key));
return $bRet1 || $bRet2;
}
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)
class apcFile
{
// 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;
}
}
static public $aFilesByTime = null;
static public $iFileCount = 0;
/** 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)
/** Get the file name corresponding to the cache entry.
* If an empty key is provided, the root of the cache is returned.
* @param $sKey
* @return string
*/
static public function GetCacheFileName($sKey = '')
{
$sSubFile = $sCheck.'/'.$sFile;
if (is_dir($sSubFile))
$sPath = str_replace(array(' ', '/', '\\', '.'), '-', $sKey);
return utils::GetCachePath().'apc-emul/'.$sPath;
}
/** Get the list of entries from a starting folder.
* @param $sEntry string starting folder.
* @return array list of entries stored into array of key 'info'
*/
static public function GetCacheEntries($sEntry)
{
$aResult = array();
if (is_dir($sEntry))
{
apc_emul_list_files_time($sSubFile, $aFilesByTime);
$aFiles = array_diff(scandir($sEntry), array('.', '..'));
foreach($aFiles as $sFile)
{
$sSubFile = $sEntry.'/'.$sFile;
$aResult = array_merge($aResult, self::GetCacheEntries($sSubFile));
}
}
else
{
$iTime = apc_emul_get_file_time($sSubFile);
if ($iTime !== false)
$sKey = basename($sEntry);
if (strpos($sKey, '-') === 0)
{
$aFilesByTime[$sSubFile] = $iTime;
$sKey = substr($sKey, 1);
}
$aResult[] = array('info' => $sKey);
}
return $aResult;
}
/** Delete one cache entry.
* @param $sCache
* @return bool true if the entry was deleted false if error occurs (like entry did not exist).
*/
static public function DeleteEntry($sCache)
{
if (is_dir($sCache))
{
$aFiles = array_diff(scandir($sCache), array('.', '..'));
foreach($aFiles as $sFile)
{
$sSubFile = $sCache.'/'.$sFile;
if (!self::DeleteEntry($sSubFile))
{
return false;
}
}
if (!@rmdir($sCache))
{
return false;
}
}
else
{
if (!@unlink($sCache))
{
return false;
}
}
self::ResetFileCount();
return true;
}
/** Get one cache entry content.
* @param $sKey
* @return bool|mixed
*/
static public function FetchOneFile($sKey)
{
// Try the 'TTLed' version
$sValue = self::ReadCacheLocked(self::GetCacheFileName('-'.$sKey));
if ($sValue === false)
{
$sValue = self::ReadCacheLocked(self::GetCacheFileName($sKey));
if ($sValue === false)
{
return false;
}
}
$oRes = @unserialize($sValue);
return $oRes;
}
/** Add one cache entry.
* @param string $sKey
* @param $value
* @param int $iTTL time to live
* @return bool
*/
static public function StoreOneFile($sKey, $value, $iTTL)
{
if (empty($sKey))
{
return false;
}
@unlink(self::GetCacheFileName($sKey));
@unlink(self::GetCacheFileName('-'.$sKey));
if ($iTTL > 0)
{
// hint for ttl management
$sKey = '-'.$sKey;
}
$sFilename = self::GetCacheFileName($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);
self::AddFile($sFilename);
return $bRes;
}
/** Manage the cache files when adding a new cache entry:
* remove older files if the mamximum is reached.
* @param $sNewFilename
*/
static protected function AddFile($sNewFilename)
{
if (strpos(basename($sNewFilename), '-') !== 0)
{
return;
}
$iMaxFiles = MetaModel::GetConfig()->Get('apc_cache_emulation.max_entries');
if ($iMaxFiles == 0)
{
return;
}
if (!self::$aFilesByTime)
{
self::ListFilesByTime();
self::$iFileCount = count(self::$aFilesByTime);
if ($iMaxFiles !== 0)
{
asort(self::$aFilesByTime);
}
}
else
{
self::$aFilesByTime[$sNewFilename] = time();
self::$iFileCount++;
}
if (self::$iFileCount > $iMaxFiles)
{
$iFileNbToRemove = self::$iFileCount - $iMaxFiles;
foreach(self::$aFilesByTime as $sFileToRemove => $iTime)
{
@unlink($sFileToRemove);
if (--$iFileNbToRemove === 0)
{
break;
}
}
self::$aFilesByTime = array_slice(self::$aFilesByTime, self::$iFileCount - $iMaxFiles, null, true);
self::$iFileCount = $iMaxFiles;
}
}
/** Get the list of files with their associated access time
* @param string $sCheck Directory to scan
*/
static protected function ListFilesByTime($sCheck = null)
{
if (empty($sCheck))
{
$sCheck = self::GetCacheFileName();
}
// Garbage collection
$aFiles = array_diff(@scandir($sCheck), array('.', '..'));
foreach($aFiles as $sFile)
{
$sSubFile = $sCheck.'/'.$sFile;
if (is_dir($sSubFile))
{
self::ListFilesByTime($sSubFile);
}
else
{
if (strpos(basename($sSubFile), '-') === 0)
{
self::$aFilesByTime[$sSubFile] = @fileatime($sSubFile);
}
}
}
}
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)
/** Read the content of one cache file under lock protection
* @param $sFilename
* @return bool|string the content of the cache entry or false if error
*/
static protected function ReadCacheLocked($sFilename)
{
return @fileatime($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;
}
return false;
static protected function ResetFileCount()
{
self::$aFilesByTime = null;
self::$iFileCount = 0;
}
}

View File

@@ -186,7 +186,7 @@ abstract class AsyncTask extends DBObject
* @return boolean True if the task record can be deleted
*/
public function Process()
{
{
// By default: consider that the task is not completed
$bRet = false;
@@ -196,16 +196,15 @@ abstract class AsyncTask extends DBObject
{
try
{
$sStatus = $this->DoProcess();
if ($this->Get('event_id') != 0)
{
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', $sStatus);
$oEventLog->DBUpdate();
$sStatus = $this->DoProcess();
if ($this->Get('event_id') != 0)
{
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', $sStatus);
$oEventLog->DBUpdate();
}
$bRet = true;
}
catch(Exception $e)
} catch (Exception $e)
{
$this->HandleError($e->getMessage(), $e->getCode());
}
@@ -215,6 +214,7 @@ abstract class AsyncTask extends DBObject
// Already done or being handled by another process... skip...
$bRet = false;
}
return $bRet;
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (C) 2010-2018 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
/**
* Typology for the attributes
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @copyright Copyright (C) 2010-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -111,6 +111,20 @@ define('LINKSET_EDITMODE_ADDREMOVE', 4); // The "linked" objects can be added/re
*/
abstract class AttributeDefinition
{
const SEARCH_WIDGET_TYPE_RAW = 'raw';
const SEARCH_WIDGET_TYPE_STRING = 'string';
const SEARCH_WIDGET_TYPE_NUMERIC = 'numeric';
const SEARCH_WIDGET_TYPE_ENUM = 'enum';
const SEARCH_WIDGET_TYPE_EXTERNAL_KEY = 'external_key';
const SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY = 'hierarchical_key';
const SEARCH_WIDGET_TYPE_EXTERNAL_FIELD = 'external_field';
const SEARCH_WIDGET_TYPE_DATE_TIME = 'date_time';
const SEARCH_WIDGET_TYPE_DATE = 'date';
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
const INDEX_LENGTH = 95;
public function GetType()
{
return Dict::S('Core:'.get_class($this));
@@ -122,11 +136,43 @@ abstract class AttributeDefinition
abstract public function GetEditClass();
/**
* Return the search widget type corresponding to this attribute
*
* @return string
*/
public function GetSearchType()
{
return static::SEARCH_WIDGET_TYPE;
}
/**
* @return bool
*/
public function IsSearchable()
{
return static::SEARCH_WIDGET_TYPE != static::SEARCH_WIDGET_TYPE_RAW;
}
protected $m_sCode;
private $m_aParams = array();
protected $m_sHostClass = '!undefined!';
public function Get($sParamName) {return $this->m_aParams[$sParamName];}
protected function IsParam($sParamName) {return (array_key_exists($sParamName, $this->m_aParams));}
public function GetIndexLength() {
$iMaxLength = $this->GetMaxSize();
if (is_null($iMaxLength))
{
return null;
}
if ($iMaxLength > static::INDEX_LENGTH)
{
return static::INDEX_LENGTH;
}
return $iMaxLength;
}
public function IsParam($sParamName) {return (array_key_exists($sParamName, $this->m_aParams));}
protected function GetOptional($sParamName, $default)
{
@@ -208,8 +254,11 @@ abstract class AttributeDefinition
/**
* Check the validity of the given value
*
* @param DBObject $oHostObject
* @param string An error if any, null otherwise
*
* @return bool
*/
public function CheckValue(DBObject $oHostObject, $value)
{
@@ -222,7 +271,8 @@ abstract class AttributeDefinition
{
return "";
// e.g: return array("Site", "infrid", "name");
}
}
public function GetFinalAttDef()
{
return $this;
@@ -270,8 +320,10 @@ abstract class AttributeDefinition
*/
static public function IsExternalField() {return false;}
/**
* Returns true if the attribute can be written (by essence)
* Returns true if the attribute can be written (by essence : metamodel field option)
*
* @return bool
* @see \DBObject::IsAttributeReadOnlyForCurrentState() for a specific object instance (depending on its workflow)
*/
public function IsWritable() {return false;}
/**
@@ -471,7 +523,18 @@ abstract class AttributeDefinition
public function GetSQLExpressions($sPrefix = '') {return array();} // returns suffix/expression pairs (1 in most of the cases), for READING (Select)
public function FromSQLToValue($aCols, $sPrefix = '') {return null;} // returns a value out of suffix/value pairs, for SELECT result interpretation
public function GetSQLColumns($bFullSpec = false) {return array();} // returns column/spec pairs (1 in most of the cases), for STRUCTURING (DB creation)
/**
* @param bool $bFullSpec
*
* @return array column/spec pairs (1 in most of the cases), for STRUCTURING (DB creation)
* @see \CMDBSource::GetFieldSpec()
*/
public function GetSQLColumns($bFullSpec = false)
{
return array();
}
public function GetSQLValues($value) {return array();} // returns column/value pairs (1 in most of the cases), for WRITING (Insert, Update)
public function RequiresIndex() {return false;}
public function CopyOnAllTables() {return false;}
@@ -1076,7 +1139,7 @@ class AttributeLinkedSet extends AttributeDefinition
return '<ul><li>'.implode("</li><li>", $aNames).'</li></ul>';
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));
}
}
@@ -1342,29 +1405,6 @@ 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
@@ -1503,7 +1543,9 @@ class AttributeDBFieldVoid extends AttributeDefinition
// To be overriden, used in GetSQLColumns
protected function GetSQLCol($bFullSpec = false)
{
return "VARCHAR(255)".($bFullSpec ? $this->GetSQLColSpec() : '');
return 'VARCHAR(255)'
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION
.($bFullSpec ? $this->GetSQLColSpec() : '');
}
protected function GetSQLColSpec()
{
@@ -1625,6 +1667,8 @@ class AttributeDBField extends AttributeDBFieldVoid
*/
class AttributeInteger extends AttributeDBField
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_NUMERIC;
static public function ListExpectedParams()
{
return parent::ListExpectedParams();
@@ -1718,6 +1762,8 @@ class AttributeInteger extends AttributeDBField
*/
class AttributeObjectKey extends AttributeDBFieldVoid
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_EXTERNAL_KEY;
static public function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array('class_attcode', 'is_null_allowed'));
@@ -1773,6 +1819,8 @@ class AttributeObjectKey extends AttributeDBFieldVoid
*/
class AttributePercentage extends AttributeInteger
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_NUMERIC;
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
$iWidth = 5; // Total width of the percentage bar graph, in em...
@@ -1812,6 +1860,8 @@ class AttributePercentage extends AttributeInteger
*/
class AttributeDecimal extends AttributeDBField
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_NUMERIC;
static public function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array('digits', 'decimals' /* including precision */));
@@ -1910,6 +1960,8 @@ class AttributeDecimal extends AttributeDBField
*/
class AttributeBoolean extends AttributeInteger
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
static public function ListExpectedParams()
{
return parent::ListExpectedParams();
@@ -2106,6 +2158,8 @@ class AttributeBoolean extends AttributeInteger
*/
class AttributeString extends AttributeDBField
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
static public function ListExpectedParams()
{
return parent::ListExpectedParams();
@@ -2113,7 +2167,13 @@ class AttributeString extends AttributeDBField
}
public function GetEditClass() {return "String";}
protected function GetSQLCol($bFullSpec = false) {return "VARCHAR(255)".($bFullSpec ? $this->GetSQLColSpec() : '');}
protected function GetSQLCol($bFullSpec = false)
{
return 'VARCHAR(255)'
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION
.($bFullSpec ? $this->GetSQLColSpec() : '');
}
public function GetValidationPattern()
{
@@ -2250,6 +2310,8 @@ class AttributeString extends AttributeDBField
*/
class AttributeClass extends AttributeString
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_ENUM;
static public function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("class_category", "more_values"));
@@ -2302,6 +2364,8 @@ class AttributeClass extends AttributeString
*/
class AttributeApplicationLanguage extends AttributeString
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
static public function ListExpectedParams()
{
return parent::ListExpectedParams();
@@ -2338,6 +2402,8 @@ class AttributeApplicationLanguage extends AttributeString
*/
class AttributeFinalClass extends AttributeString
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
public function __construct($sCode, $aParams)
{
$this->m_sCode = $sCode;
@@ -2492,6 +2558,8 @@ class AttributeFinalClass extends AttributeString
*/
class AttributePassword extends AttributeString
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
static public function ListExpectedParams()
{
return parent::ListExpectedParams();
@@ -2499,7 +2567,13 @@ class AttributePassword extends AttributeString
}
public function GetEditClass() {return "Password";}
protected function GetSQLCol($bFullSpec = false) {return "VARCHAR(64)".($bFullSpec ? $this->GetSQLColSpec() : '');}
protected function GetSQLCol($bFullSpec = false)
{
return "VARCHAR(64)"
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION
.($bFullSpec ? $this->GetSQLColSpec() : '');
}
public function GetMaxSize()
{
@@ -2538,6 +2612,8 @@ class AttributePassword extends AttributeString
*/
class AttributeEncryptedString extends AttributeString
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
static $sKey = null; // Encryption key used for all encrypted fields
public function __construct($sCode, $aParams)
@@ -2625,8 +2701,11 @@ define('WIKI_OBJECT_REGEXP', '/\[\[(.+):(.+)\]\]/U');
class AttributeText extends AttributeString
{
public function GetEditClass() {return ($this->GetFormat() == 'text') ? 'Text' : "HTML";}
protected function GetSQLCol($bFullSpec = false) {return "TEXT";}
protected function GetSQLCol($bFullSpec = false)
{
return "TEXT".CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
}
public function GetSQLColumns($bFullSpec = false)
{
@@ -2635,7 +2714,7 @@ class AttributeText extends AttributeString
if ($this->GetOptional('format', null) != null )
{
// Add the extra column only if the property 'format' is specified for the attribute
$aColumns[$this->Get('sql').'_format'] = "ENUM('text','html')";
$aColumns[$this->Get('sql').'_format'] = "ENUM('text','html')".CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
if ($bFullSpec)
{
$aColumns[$this->Get('sql').'_format'].= " DEFAULT 'text'"; // default 'text' is for migrating old records
@@ -2957,7 +3036,10 @@ class AttributeText extends AttributeString
*/
class AttributeLongText extends AttributeText
{
protected function GetSQLCol($bFullSpec = false) {return "LONGTEXT";}
protected function GetSQLCol($bFullSpec = false)
{
return "LONGTEXT".CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
}
public function GetMaxSize()
{
@@ -2974,6 +3056,8 @@ class AttributeLongText extends AttributeText
*/
class AttributeCaseLog extends AttributeLongText
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
public function GetNullValue()
{
return '';
@@ -3013,7 +3097,6 @@ class AttributeCaseLog extends AttributeLongText
*/
public function GetAsPlainText($value, $oHostObj = null)
{
$value = $oObj->Get($sAttCode);
if ($value instanceOf ormCaseLog)
{
@@ -3136,7 +3219,8 @@ class AttributeCaseLog extends AttributeLongText
public function GetSQLColumns($bFullSpec = false)
{
$aColumns = array();
$aColumns[$this->GetCode()] = 'LONGTEXT'; // 2^32 (4 Gb)
$aColumns[$this->GetCode()] = 'LONGTEXT' // 2^32 (4 Gb)
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
$aColumns[$this->GetCode().'_index'] = 'BLOB';
return $aColumns;
}
@@ -3362,7 +3446,10 @@ class AttributeEmailAddress extends AttributeString
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if (empty($sValue)) return '';
return '<a class="mailto" href="mailto:'.$sValue.'">'.parent::GetAsHTML($sValue).'</a>';
$sUrlDecorationClass = utils::GetConfig()->Get('email_decoration_class');
return '<a class="mailto" href="mailto:'.$sValue.'"><span class="text_decoration '.$sUrlDecorationClass.'"></span>'.parent::GetAsHTML($sValue).'</a>';
}
}
@@ -3386,6 +3473,35 @@ class AttributeIPAddress extends AttributeString
}
}
/**
* Specialization of a string: phone number
*
* @package iTopORM
*/
class AttributePhoneNumber extends AttributeString
{
public function GetValidationPattern()
{
return $this->GetOptional('validation_pattern', '^'.utils::GetConfig()->Get('phone_number_validation_pattern').'$');
}
static public function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\PhoneField';
}
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if (empty($sValue)) return '';
$sUrlDecorationClass = utils::GetConfig()->Get('phone_number_decoration_class');
$sUrlPattern = utils::GetConfig()->Get('phone_number_url_pattern');
$sUrl = sprintf($sUrlPattern, $sValue);
return '<a class="tel" href="'.$sUrl.'"><span class="text_decoration '.$sUrlDecorationClass.'"></span>'.parent::GetAsHTML($sValue).'</a>';
}
}
/**
* Specialization of a string: OQL expression
*
@@ -3393,6 +3509,8 @@ class AttributeIPAddress extends AttributeString
*/
class AttributeOQL extends AttributeText
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
public function GetEditClass() {return "OQLExpression";}
}
@@ -3403,6 +3521,7 @@ class AttributeOQL extends AttributeText
*/
class AttributeTemplateString extends AttributeString
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
}
/**
@@ -3412,6 +3531,7 @@ class AttributeTemplateString extends AttributeString
*/
class AttributeTemplateText extends AttributeText
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
}
/**
@@ -3421,6 +3541,8 @@ class AttributeTemplateText extends AttributeText
*/
class AttributeTemplateHTML extends AttributeText
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
public function GetSQLColumns($bFullSpec = false)
{
$aColumns = array();
@@ -3455,6 +3577,8 @@ class AttributeTemplateHTML extends AttributeText
*/
class AttributeEnum extends AttributeString
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_ENUM;
static public function ListExpectedParams()
{
return parent::ListExpectedParams();
@@ -3479,11 +3603,15 @@ class AttributeEnum extends AttributeString
// In particular, I had to remove unnecessary spaces to
// make sure that this string will match the field type returned by the DB
// (used to perform a comparison between the current DB format and the data model)
return "ENUM(".implode(",", $aValues).")".($bFullSpec ? $this->GetSQLColSpec() : '');
return "ENUM(".implode(",", $aValues).")"
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION
.($bFullSpec ? $this->GetSQLColSpec() : '');
}
else
{
return "VARCHAR(255)".($bFullSpec ? " DEFAULT ''" : ""); // ENUM() is not an allowed syntax!
return "VARCHAR(255)"
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION
.($bFullSpec ? " DEFAULT ''" : ""); // ENUM() is not an allowed syntax!
}
}
@@ -3688,7 +3816,12 @@ class AttributeEnum extends AttributeString
}
return $aLocalizedValues;
}
public function GetMaxSize()
{
return null;
}
/**
* An enum can be localized
*/
@@ -3878,8 +4011,14 @@ class AttributeMetaEnum extends AttributeEnum
*/
class AttributeDateTime extends AttributeDBField
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_DATE_TIME;
static $oFormat = null;
/**
*
* @return DateTimeFormat
*/
static public function GetFormat()
{
if (self::$oFormat == null)
@@ -4404,6 +4543,8 @@ class AttributeDuration extends AttributeInteger
*/
class AttributeDate extends AttributeDateTime
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_DATE;
static $oDateFormat = null;
static public function GetFormat()
@@ -4548,6 +4689,33 @@ class AttributeDeadline extends AttributeDateTime
*/
class AttributeExternalKey extends AttributeDBFieldVoid
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_EXTERNAL_KEY;
/**
* Return the search widget type corresponding to this attribute
*
* @return string
*/
public function GetSearchType()
{
try
{
$oRemoteAtt = $this->GetFinalAttDef();
$sTargetClass = $oRemoteAtt->GetTargetClass();
if (MetaModel::IsHierarchicalClass($sTargetClass))
{
return self::SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY;
}
return self::SEARCH_WIDGET_TYPE_EXTERNAL_KEY;
}
catch (CoreException $e)
{
}
return self::SEARCH_WIDGET_TYPE_RAW;
}
static public function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("targetclass", "is_null_allowed", "on_target_delete"));
@@ -4627,6 +4795,11 @@ class AttributeExternalKey extends AttributeDBFieldVoid
return $oSet;
}
public function GetAllowedValuesAsFilter($aArgs = array(), $sContains = '', $iAdditionalValue = null)
{
return DBObjectSearch::FromOQL($this->GetValuesDef()->GetFilterExpression());
}
public function GetDeletionPropagationOption()
{
return $this->Get("on_target_delete");
@@ -4748,6 +4921,8 @@ class AttributeExternalKey extends AttributeDBFieldVoid
*/
class AttributeHierarchicalKey extends AttributeExternalKey
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY;
protected $m_sTargetClass;
static public function ListExpectedParams()
@@ -4827,18 +5002,12 @@ class AttributeHierarchicalKey extends AttributeExternalKey
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
if (array_key_exists('this', $aArgs))
$oFilter = $this->GetHierachicalFilter($aArgs, $sContains);
if ($oFilter)
{
// Hierarchical keys have one more constraint: the "parent value" cannot be
// "under" themselves
$iRootId = $aArgs['this']->GetKey();
if ($iRootId > 0) // ignore objects that do no exist in the database...
{
$oValSetDef = $this->GetValuesDef();
$sClass = $this->m_sTargetClass;
$oFilter = DBObjectSearch::FromOQL("SELECT $sClass AS node JOIN $sClass AS root ON node.".$this->GetCode()." NOT BELOW root.id WHERE root.id = $iRootId");
$oValSetDef->AddCondition($oFilter);
}
$oValSetDef = $this->GetValuesDef();
$oValSetDef->AddCondition($oFilter);
return $oValSetDef->GetValues($aArgs, $sContains);
}
else
{
@@ -4849,6 +5018,27 @@ class AttributeHierarchicalKey extends AttributeExternalKey
public function GetAllowedValuesAsObjectSet($aArgs = array(), $sContains = '', $iAdditionalValue = null)
{
$oValSetDef = $this->GetValuesDef();
$oFilter = $this->GetHierachicalFilter($aArgs, $sContains, $iAdditionalValue);
if ($oFilter)
{
$oValSetDef->AddCondition($oFilter);
}
$oSet = $oValSetDef->ToObjectSet($aArgs, $sContains, $iAdditionalValue);
return $oSet;
}
public function GetAllowedValuesAsFilter($aArgs = array(), $sContains = '', $iAdditionalValue = null)
{
$oFilter = $this->GetHierachicalFilter($aArgs, $sContains, $iAdditionalValue);
if ($oFilter)
{
return $oFilter;
}
return parent::GetAllowedValuesAsFilter($aArgs, $sContains, $iAdditionalValue);
}
private function GetHierachicalFilter($aArgs = array(), $sContains = '', $iAdditionalValue = null)
{
if (array_key_exists('this', $aArgs))
{
// Hierarchical keys have one more constraint: the "parent value" cannot be
@@ -4856,14 +5046,11 @@ class AttributeHierarchicalKey extends AttributeExternalKey
$iRootId = $aArgs['this']->GetKey();
if ($iRootId > 0) // ignore objects that do no exist in the database...
{
$aValuesSetDef = $this->GetValuesDef();
$sClass = $this->m_sTargetClass;
$oFilter = DBObjectSearch::FromOQL("SELECT $sClass AS node JOIN $sClass AS root ON node.".$this->GetCode()." NOT BELOW root.id WHERE root.id = $iRootId");
$oValSetDef->AddCondition($oFilter);
return DBObjectSearch::FromOQL("SELECT $sClass AS node JOIN $sClass AS root ON node.".$this->GetCode()." NOT BELOW root.id WHERE root.id = $iRootId");
}
}
$oSet = $oValSetDef->ToObjectSet($aArgs, $sContains, $iAdditionalValue);
return $oSet;
return false;
}
/**
@@ -4883,6 +5070,38 @@ class AttributeHierarchicalKey extends AttributeExternalKey
*/
class AttributeExternalField extends AttributeDefinition
{
/**
* Return the search widget type corresponding to this attribute
*
* @return string
*/
public function GetSearchType()
{
// Not necessary the external key is already present
if ($this->IsFriendlyName())
{
return self::SEARCH_WIDGET_TYPE_RAW;
}
try
{
$oRemoteAtt = $this->GetFinalAttDef();
switch (true)
{
case ($oRemoteAtt instanceof AttributeString):
return self::SEARCH_WIDGET_TYPE_EXTERNAL_FIELD;
case ($oRemoteAtt instanceof AttributeExternalKey):
return self::SEARCH_WIDGET_TYPE_EXTERNAL_KEY;
}
}
catch (CoreException $e)
{
}
return self::SEARCH_WIDGET_TYPE_RAW;
}
static public function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("extkey_attcode", "target_attcode"));
@@ -4934,6 +5153,23 @@ class AttributeExternalField extends AttributeDefinition
}
return $sLabel;
}
public function GetLabelForSearchField()
{
$sLabel = parent::GetLabel('');
if (strlen($sLabel) == 0)
{
$sKeyAttCode = $this->Get("extkey_attcode");
$oExtKeyAttDef = MetaModel::GetAttributeDef($this->GetHostClass(), $sKeyAttCode);
$sLabel = $oExtKeyAttDef->GetLabel($this->m_sCode);
$oRemoteAtt = $this->GetExtAttDef();
$sLabel .= '->'.$oRemoteAtt->GetLabel($this->m_sCode);
}
return $sLabel;
}
public function GetDescription($sDefault = null)
{
$sLabel = parent::GetDescription('');
@@ -5195,7 +5431,12 @@ class AttributeURL extends AttributeString
return array_merge(parent::ListExpectedParams(), array("target"));
}
protected function GetSQLCol($bFullSpec = false) {return "VARCHAR(2048)".($bFullSpec ? $this->GetSQLColSpec() : '');}
protected function GetSQLCol($bFullSpec = false)
{
return "VARCHAR(2048)"
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION
.($bFullSpec ? $this->GetSQLColSpec() : '');
}
public function GetMaxSize()
{
@@ -5249,6 +5490,8 @@ class AttributeURL extends AttributeString
*/
class AttributeBlob extends AttributeDefinition
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
static public function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("depends_on"));
@@ -5368,8 +5611,8 @@ class AttributeBlob extends AttributeDefinition
{
$aColumns = array();
$aColumns[$this->GetCode().'_data'] = 'LONGBLOB'; // 2^32 (4 Gb)
$aColumns[$this->GetCode().'_mimetype'] = 'VARCHAR(255)';
$aColumns[$this->GetCode().'_filename'] = 'VARCHAR(255)';
$aColumns[$this->GetCode().'_mimetype'] = 'VARCHAR(255)'.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
$aColumns[$this->GetCode().'_filename'] = 'VARCHAR(255)'.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
return $aColumns;
}
@@ -5543,10 +5786,10 @@ class AttributeImage extends AttributeBlob
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
{
$iMaxWidthPx = $this->Get('display_max_width');
$iMaxHeightPx = $this->Get('display_max_height');
$iMaxWidthPx = $this->Get('display_max_width').'px';
$iMaxHeightPx = $this->Get('display_max_height').'px';
$sUrl = $this->Get('default_image');
$sRet = ($sUrl !== null) ? '<img src="'.$sUrl.'" style="max-width: '.$iMaxWidthPx.'px; max-height: '.$iMaxHeightPx.'px">' : '';
$sRet = ($sUrl !== null) ? '<img src="'.$sUrl.'" style="max-width: '.$iMaxWidthPx.'; max-height: '.$iMaxHeightPx.'">' : '';
if (is_object($value) && !$value->IsEmpty())
{
if ($oHostObject->IsNew() || ($oHostObject->IsModified() && (array_key_exists($this->GetCode(), $oHostObject->ListChanges()))))
@@ -5559,9 +5802,9 @@ class AttributeImage extends AttributeBlob
{
$sUrl = $value->GetDownloadURL(get_class($oHostObject), $oHostObject->GetKey(), $this->GetCode());
}
$sRet = '<img src="'.$sUrl.'" style="max-width: '.$iMaxWidthPx.'px; max-height: '.$iMaxHeightPx.'px">';
$sRet = '<img src="'.$sUrl.'" style="max-width: '.$iMaxWidthPx.'; max-height: '.$iMaxHeightPx.'">';
}
return '<div class="view-image" style="width: '.$iMaxWidthPx.'px; height: '.$iMaxHeightPx.'px;"><span class="helper-middle"></span>'.$sRet.'</div>';
return '<div class="view-image" style="width: '.$iMaxWidthPx.'; height: '.$iMaxHeightPx.';"><span class="helper-middle"></span>'.$sRet.'</div>';
}
static public function GetFormFieldClass()
@@ -5576,6 +5819,8 @@ class AttributeImage extends AttributeBlob
*/
class AttributeStopWatch extends AttributeDefinition
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
static public function ListExpectedParams()
{
// The list of thresholds must be an array of iPercent => array of 'option' => value
@@ -6244,6 +6489,8 @@ class AttributeStopWatch extends AttributeDefinition
*/
class AttributeSubItem extends AttributeDefinition
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
static public function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array('target_attcode', 'item_code'));
@@ -6410,6 +6657,8 @@ class AttributeSubItem extends AttributeDefinition
*/
class AttributeOneWayPassword extends AttributeDefinition
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
static public function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("depends_on"));
@@ -6506,7 +6755,7 @@ class AttributeOneWayPassword extends AttributeDefinition
public function GetImportColumns()
{
$aColumns = array();
$aColumns[$this->GetCode()] = 'TINYTEXT';
$aColumns[$this->GetCode()] = 'TINYTEXT'.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
return $aColumns;
}
@@ -6573,8 +6822,14 @@ class AttributeOneWayPassword extends AttributeDefinition
// Indexed array having two dimensions
class AttributeTable extends AttributeDBField
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
public function GetEditClass() {return "Table";}
protected function GetSQLCol($bFullSpec = false) {return "LONGTEXT";}
protected function GetSQLCol($bFullSpec = false)
{
return "LONGTEXT".CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION;
}
public function GetMaxSize()
{
@@ -6798,6 +7053,8 @@ class AttributePropertySet extends AttributeTable
*/
class AttributeFriendlyName extends AttributeDefinition
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_STRING;
public function __construct($sCode)
{
$this->m_sCode = $sCode;
@@ -6964,6 +7221,8 @@ class AttributeFriendlyName extends AttributeDefinition
*/
class AttributeRedundancySettings extends AttributeDBField
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
static public function ListExpectedParams()
{
return array('sql', 'relation_code', 'from_class', 'neighbour_id', 'enabled', 'enabled_mode', 'min_up', 'min_up_type', 'min_up_mode');
@@ -6975,7 +7234,9 @@ class AttributeRedundancySettings extends AttributeDBField
public function GetEditClass() {return "RedundancySetting";}
protected function GetSQLCol($bFullSpec = false)
{
return "VARCHAR(20)".($bFullSpec ? $this->GetSQLColSpec() : '');
return "VARCHAR(20)"
.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION
.($bFullSpec ? $this->GetSQLColSpec() : '');
}
@@ -7352,6 +7613,8 @@ class AttributeRedundancySettings extends AttributeDBField
*/
class AttributeCustomFields extends AttributeDefinition
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
static public function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("handler_class"));

View File

@@ -50,6 +50,6 @@ class ObsolescenceDateUpdater implements iBackgroundProcess
$oObsoletedToday->AddCondition('obsolescence_date', null, '!=');
$iCountReset += MetaModel::BulkUpdate($oObsoletedToday, array('obsolescence_date' => null));
}
echo "Obsolescence date updated (classes: $iClasses ; set: $iCountSet ; reset: $iCountReset)\n";
return "Obsolescence date updated (classes: $iClasses ; set: $iCountSet ; reset: $iCountReset)\n";
}
}

View File

@@ -24,9 +24,16 @@
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
interface iProcess
{
/**
* @param int $iUnixTimeLimit
*
* @return string status message
* @throws \ProcessException
* @throws \ProcessFatalException
* @throws MySQLHasGoneAwayException
*/
public function Process($iUnixTimeLimit);
}
@@ -37,13 +44,11 @@ interface iProcess
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
interface iBackgroundProcess extends iProcess
{
/*
Gives the repetition rate in seconds
@returns integer
*/
/**
* @return int repetition rate in seconds
*/
public function GetPeriodicity();
}
@@ -54,14 +59,30 @@ interface iBackgroundProcess extends iProcess
* @copyright Copyright (C) 2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
interface iScheduledProcess extends iProcess
{
/*
Gives the exact time at which the process must be run next time
@returns DateTime
*/
/**
* @return DateTime exact time at which the process must be run next time
*/
public function GetNextOccurrence();
}
?>
/**
* Class ProcessException
* Exception for iProcess implementations.<br>
* An error happened during the processing but we can go on with the next implementations.
*/
class ProcessException extends CoreException
{
}
/**
* Class ProcessFatalException
* Exception for iProcess implementations.<br>
* A big error occurred, we have to stop the iProcess processing.
*/
class ProcessFatalException extends CoreException
{
}

View File

@@ -320,7 +320,7 @@ class BulkChange
// Returns true if the CSV data specifies that the external key must be left undefined
protected function IsNullExternalKeySpec($aRowData, $sAttCode)
{
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
//$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
foreach ($this->m_aExtKeys[$sAttCode] as $sForeignAttCode => $iCol)
{
// The foreign attribute is one of our reconciliation key
@@ -367,6 +367,7 @@ class BulkChange
else
{
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
$aCacheKeys = array();
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
{
@@ -385,7 +386,6 @@ class BulkChange
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
}
$sCacheKey = implode('_|_', $aCacheKeys); // Unique key for this query...
$iCount = 0;
$iForeignKey = null;
$sOQL = '';
// TODO: check if *too long* keys can lead to collisions... and skip the cache in such a case...
@@ -476,7 +476,6 @@ class BulkChange
// skip reconciliation keys
if (!$oAttDef->IsWritable() && in_array($sAttCode, $this->m_aReconcilKeys)){ continue; }
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
$aReasons = array();
$iFlags = $oTargetObj->GetAttributeFlags($sAttCode, $aReasons);
if ( (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ( $oTargetObj->Get($sAttCode) != $aRowData[$iCol]) )
@@ -533,13 +532,11 @@ class BulkChange
{
$sCurValue = $oTargetObj->GetAsHTML($sAttCode, $this->m_bLocalizedValues);
$sOrigValue = $oTargetObj->GetOriginalAsHTML($sAttCode, $this->m_bLocalizedValues);
$sInput = htmlentities($aRowData[$iCol], ENT_QUOTES, 'UTF-8');
}
else
{
$sCurValue = $oTargetObj->GetAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter, $this->m_bLocalizedValues);
$sOrigValue = $oTargetObj->GetOriginalAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter, $this->m_bLocalizedValues);
$sInput = $aRowData[$iCol];
}
if (isset($aErrors[$sAttCode]))
{
@@ -610,10 +607,6 @@ class BulkChange
throw new BulkChangeException('Invalid attribute code', array('class' => get_class($oTargetObj), 'attcode' => $sAttCode));
}
$oTargetObj->Set($sAttCode, $value);
if (!array_key_exists($sAttCode, $this->m_aAttList))
{
// #@# will be out of the reporting... (counted anyway)
}
}
// Reporting on fields
@@ -651,6 +644,47 @@ class BulkChange
protected function CreateObject(&$aResult, $iRow, $aRowData, CMDBChange $oChange = null)
{
$oTargetObj = MetaModel::NewObject($this->m_sClass);
// Populate the cache for hierarchical keys (only if in verify mode)
if (is_null($oChange))
{
// 1. determine if a hierarchical key exists
foreach($this->m_aExtKeys as $sAttCode => $aKeyConfig)
{
$oExtKey = MetaModel::GetAttributeDef(get_class($oTargetObj), $sAttCode);
if (!$this->IsNullExternalKeySpec($aRowData, $sAttCode) && MetaModel::IsParentClass(get_class($oTargetObj), $this->m_sClass))
{
// 2. Populate the cache for further checks
$aCacheKeys = array();
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
{
// The foreign attribute is one of our reconciliation key
if ($sForeignAttCode == 'id')
{
$value = $aRowData[$iCol];
}
else
{
if (!isset($this->m_aAttList[$sForeignAttCode]) || !isset($aRowData[$this->m_aAttList[$sForeignAttCode]]))
{
// the key is not in the import
break 2;
}
$value = $aRowData[$this->m_aAttList[$sForeignAttCode]];
}
$aCacheKeys[] = $value;
}
$sCacheKey = implode('_|_', $aCacheKeys); // Unique key for this query...
$this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey] = array(
'c' => 1,
'k' => -1,
'oql' => '',
'h' => 0, // number of hits on this cache entry
);
}
}
}
$aResult[$iRow] = $this->PrepareObject($oTargetObj, $aRowData, $aErrors);
if (count($aErrors) > 0)
@@ -684,7 +718,7 @@ class BulkChange
if ($oChange)
{
$newID = $oTargetObj->DBInsertTrackedNoReload($oChange);
$aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj($this->m_sClass, $newID);
$aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj();
$aResult[$iRow]["finalclass"] = get_class($oTargetObj);
$aResult[$iRow]["id"] = new CellStatus_Void($newID);
}
@@ -1176,6 +1210,9 @@ EOF
/**
* Display the details of an import
* @param iTopWebPage $oPage
* @param $iChange
* @throws Exception
*/
static function DisplayImportHistoryDetails(iTopWebPage $oPage, $iChange)
{

View File

@@ -74,7 +74,11 @@ class BulkExportResult extends DBObject
MetaModel::Init_AddAttribute(new AttributeLongText("search", array("allowed_values"=>null, "sql"=>"search", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLongText("status_info", array("allowed_values"=>null, "sql"=>"status_info", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
}
/**
* @throws CoreUnexpectedValue
* @throws Exception
*/
public function ComputeValues()
{
$this->Set('user_id', UserRights::GetUserId());
@@ -150,9 +154,9 @@ abstract class BulkExport
/**
* Find the first class capable of exporting the data in the given format
* @param string $sFormat The lowercase format (e.g. html, csv, spreadsheet, xlsx, xml, json, pdf...)
* @param string $sFormatCode The lowercase format (e.g. html, csv, spreadsheet, xlsx, xml, json, pdf...)
* @param DBSearch $oSearch The search/filter defining the set of objects to export or null when listing the supported formats
* @return iBulkExport|NULL
* @return BulkExport|NULL
*/
static public function FindExporter($sFormatCode, $oSearch = null)
{
@@ -202,7 +206,11 @@ abstract class BulkExport
}
return $oBulkExporter;
}
/**
* @param $data
* @throws Exception
*/
public function AppendToTmpFile($data)
{
if ($this->sTmpFile == '')
@@ -221,10 +229,10 @@ abstract class BulkExport
{
return $this->sTmpFile;
}
/**
* Lists all possible export formats. The output is a hash array in the form: 'format_code' => 'localized format label'
* @return multitype:string
* @return array :string
*/
static public function FindSupportedFormats()
{
@@ -288,13 +296,21 @@ abstract class BulkExport
{
}
/**
* @return string
*/
public function GetHeader()
{
return '';
}
abstract public function GetNextChunk(&$aStatus);
/**
* @return string
*/
public function GetFooter()
{
return '';
}
public function SaveState()
@@ -355,13 +371,21 @@ abstract class BulkExport
{
}
/**
* @return string
*/
public function GetMimeType()
{
return '';
}
/**
* @return string
*/
public function GetFileExtension()
{
return '';
}
public function GetCharacterSet()
{
@@ -388,6 +412,11 @@ abstract class BulkExport
return $this->aStatusInfo;
}
/**
* @param $sExtension
* @return string
* @throws Exception
*/
protected function MakeTmpFile($sExtension)
{
if(!is_dir(APPROOT."data/bulk_export"))
@@ -401,7 +430,6 @@ abstract class BulkExport
}
$iNum = rand();
$sFileName = '';
do
{
$iNum++;

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2018 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,7 +20,7 @@
/**
* DB Server abstraction
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @copyright Copyright (C) 2010-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -29,7 +29,15 @@ require_once(APPROOT.'core/kpi.class.inc.php');
class MySQLException extends CoreException
{
public function __construct($sIssue, $aContext, $oException = null)
/**
* MySQLException constructor.
*
* @param string $sIssue
* @param array $aContext
* @param \Exception $oException
* @param \mysqli $oMysqli to use when working with a custom mysqli instance
*/
public function __construct($sIssue, $aContext, $oException = null, $oMysqli = null)
{
if ($oException != null)
{
@@ -37,6 +45,12 @@ class MySQLException extends CoreException
$this->code = $oException->getCode();
$aContext['mysql_error'] = $oException->getMessage();
}
else if ($oMysqli != null)
{
$aContext['mysql_errno'] = $oMysqli->errno;
$this->code = $oMysqli->errno;
$aContext['mysql_error'] = $oMysqli->error;
}
else
{
$aContext['mysql_errno'] = CMDBSource::GetErrNo();
@@ -47,6 +61,44 @@ class MySQLException extends CoreException
}
}
/**
* Class MySQLQueryHasNoResultException
*
* @since 2.5
*/
class MySQLQueryHasNoResultException extends MySQLException
{
}
/**
* Class MySQLHasGoneAwayException
*
* @since 2.5
* @see itop bug 1195
* @see https://dev.mysql.com/doc/refman/5.7/en/gone-away.html
*/
class MySQLHasGoneAwayException extends MySQLException
{
/**
* can not be a constant before PHP 5.6 (http://php.net/manual/fr/language.oop5.constants.php)
*
* @return int[]
*/
public static function getErrorCodes()
{
return array(
2006,
2013
);
}
public function __construct($sIssue, $aContext)
{
parent::__construct($sIssue, $aContext, null);
}
}
/**
* CMDBSource
@@ -56,41 +108,136 @@ class MySQLException extends CoreException
*/
class CMDBSource
{
/**
* SQL charset & collation declaration for text columns
*
* Using an attribute instead of a constant to avoid crash in the setup for older PHP versions
*
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-column.html
* @since 2.5 #1001 switch to utf8mb4
*/
public static $SQL_STRING_COLUMNS_CHARSET_DEFINITION = ' CHARACTER SET '.DEFAULT_CHARACTER_SET.' COLLATE '.DEFAULT_COLLATION;
protected static $m_sDBHost;
protected static $m_sDBUser;
protected static $m_sDBPwd;
protected static $m_sDBName;
/**
* @var boolean
* @since 2.5 #1260 MySQL TLS first implementation
*/
protected static $m_bDBTlsEnabled;
/**
* @var string
* @since 2.5 #1260 MySQL TLS first implementation
*/
protected static $m_sDBTlsCA;
/** @var mysqli $m_oMysqli */
protected static $m_oMysqli;
public static function Init($sServer, $sUser, $sPwd, $sSource = '')
/**
* @param Config $oConfig
*
* @throws \MySQLException
* @uses \CMDBSource::Init()
* @uses \CMDBSource::SetCharacterSet()
*/
public static function InitFromConfig($oConfig)
{
$sServer = $oConfig->Get('db_host');
$sUser = $oConfig->Get('db_user');
$sPwd = $oConfig->Get('db_pwd');
$sSource = $oConfig->Get('db_name');
$bTlsEnabled = $oConfig->Get('db_tls.enabled');
$sTlsCA = $oConfig->Get('db_tls.ca');
self::Init($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA);
$sCharacterSet = DEFAULT_CHARACTER_SET;
$sCollation = DEFAULT_COLLATION;
self::SetCharacterSet($sCharacterSet, $sCollation);
}
/**
* @param string $sServer
* @param string $sUser
* @param string $sPwd
* @param string $sSource database to use
* @param bool $bTlsEnabled
* @param string $sTlsCA
*
* @throws \MySQLException
*/
public static function Init(
$sServer, $sUser, $sPwd, $sSource = '', $bTlsEnabled = false, $sTlsCA = null
)
{
self::$m_sDBHost = $sServer;
self::$m_sDBUser = $sUser;
self::$m_sDBPwd = $sPwd;
self::$m_sDBName = $sSource;
self::$m_oMysqli = null;
self::$m_bDBTlsEnabled = empty($bTlsEnabled) ? false : $bTlsEnabled;
self::$m_sDBTlsCA = empty($sTlsCA) ? null : $sTlsCA;
self::$m_oMysqli = self::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, true);
}
/**
* @param string $sDbHost
* @param string $sUser
* @param string $sPwd
* @param string $sSource database to use
* @param bool $bTlsEnabled
* @param string $sTlsCa
* @param bool $bCheckTlsAfterConnection If true then verify after connection if it is encrypted
*
* @return \mysqli
* @throws \MySQLException
*/
public static function GetMysqliInstance(
$sDbHost, $sUser, $sPwd, $sSource = '', $bTlsEnabled = false, $sTlsCa = null, $bCheckTlsAfterConnection = false
) {
$oMysqli = null;
$sServer = null;
$iPort = null;
self::InitServerAndPort($sDbHost, $sServer, $iPort);
$iFlags = null;
// *some* errors (like connection errors) will throw mysqli_sql_exception instead of generating warnings printed to the output
// but some other errors will still cause the query() method to return false !!!
mysqli_report(MYSQLI_REPORT_STRICT);
mysqli_report(MYSQLI_REPORT_STRICT); // *some* errors (like connection errors) will throw mysqli_sql_exception instead
// of generating warnings printed to the output but some other errors will still
// cause the query() method to return false !!!
try
{
$aConnectInfo = explode(':', self::$m_sDBHost);
if (count($aConnectInfo) > 1)
$oMysqli = new mysqli();
$oMysqli->init();
if ($bTlsEnabled)
{
// Override the default port
$sServer = $aConnectInfo[0];
$iPort = (int)$aConnectInfo[1];
self::$m_oMysqli = new mysqli($sServer, self::$m_sDBUser, self::$m_sDBPwd, '', $iPort);
}
else
{
self::$m_oMysqli = new mysqli(self::$m_sDBHost, self::$m_sDBUser, self::$m_sDBPwd);
$iFlags = (empty($sTlsCa))
? MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT
: MYSQLI_CLIENT_SSL;
$sTlsCert = null; // not implemented
$sTlsCaPath = null; // not implemented
$sTlsCipher = null; // not implemented
$oMysqli->ssl_set($bTlsEnabled, $sTlsCert, $sTlsCa, $sTlsCaPath, $sTlsCipher);
}
$oMysqli->real_connect($sServer, $sUser, $sPwd, '', $iPort, ini_get("mysqli.default_socket"), $iFlags);
}
catch(mysqli_sql_exception $e)
{
throw new MySQLException('Could not connect to the DB server', array('host'=>self::$m_sDBHost, 'user'=>self::$m_sDBUser), $e);
throw new MySQLException('Could not connect to the DB server', array('host' => $sServer, 'user' => $sUser), $e);
}
if ($bTlsEnabled
&& $bCheckTlsAfterConnection
&& !self::IsOpenedDbConnectionUsingTls($oMysqli))
{
throw new MySQLException("Connection to the database is not encrypted whereas it was opened using TLS parameters",
null, null, $oMysqli);
}
if (!empty($sSource))
@@ -98,16 +245,104 @@ class CMDBSource
try
{
mysqli_report(MYSQLI_REPORT_STRICT); // Errors, in the next query, will throw mysqli_sql_exception
self::$m_oMysqli->query("USE `$sSource`");
$oMysqli->query("USE `$sSource`");
}
catch(mysqli_sql_exception $e)
{
throw new MySQLException('Could not select DB', array('host'=>self::$m_sDBHost, 'user'=>self::$m_sDBUser, 'db_name'=>self::$m_sDBName), $e);
throw new MySQLException('Could not select DB',
array('host' => $sServer, 'user' => $sUser, 'db_name' => $sSource), $e);
}
}
return $oMysqli;
}
/**
* @param string $sDbHost initial value ("p:domain:port" syntax)
* @param string $sServer server variable to update
* @param int $iPort port variable to update
*/
public static function InitServerAndPort($sDbHost, &$sServer, &$iPort)
{
$aConnectInfo = explode(':', $sDbHost);
$bUsePersistentConnection = false;
if (strcasecmp($aConnectInfo[0], 'p') == 0)
{
// we might have "p:" prefix to use persistent connections (see http://php.net/manual/en/mysqli.persistconns.php)
$bUsePersistentConnection = true;
$sServer = $aConnectInfo[0].':'.$aConnectInfo[1];
}
else
{
$sServer = $aConnectInfo[0];
}
$iConnectInfoCount = count($aConnectInfo);
if ($bUsePersistentConnection && ($iConnectInfoCount == 3))
{
$iPort = $aConnectInfo[2];
}
else if (!$bUsePersistentConnection && ($iConnectInfoCount == 2))
{
$iPort = $aConnectInfo[1];
}
else
{
$iPort = 3306;
}
}
public static function SetCharacterSet($sCharset = 'utf8', $sCollation = 'utf8_general_ci')
/**
* <p>A DB connection can be opened transparently (no errors thrown) without being encrypted, whereas the TLS
* parameters were used.<br>
* This method can be called to ensure that the DB connection really uses TLS.
*
* <p>We're using this object connection : {@link self::$m_oMysqli}
*
* @param \mysqli $oMysqli
*
* @return boolean true if the connection was really established using TLS
* @throws \MySQLException
*
* @uses IsMySqlVarNonEmpty
*/
private static function IsOpenedDbConnectionUsingTls($oMysqli)
{
if (self::$m_oMysqli == null)
{
self::$m_oMysqli = $oMysqli;
}
$bNonEmptySslVersionVar = self::IsMySqlVarNonEmpty('ssl_version');
$bNonEmptySslCipherVar = self::IsMySqlVarNonEmpty('ssl_cipher');
return ($bNonEmptySslVersionVar && $bNonEmptySslCipherVar);
}
/**
* @param string $sVarName
*
* @return bool
* @throws \MySQLException
*
* @uses SHOW STATUS queries
*/
private static function IsMySqlVarNonEmpty($sVarName)
{
try
{
$sResult = self::QueryToScalar("SHOW SESSION STATUS LIKE '$sVarName'", 1);
}
catch (MySQLQueryHasNoResultException $e)
{
$sResult = null;
}
return (!empty($sResult));
}
public static function SetCharacterSet($sCharset = DEFAULT_CHARACTER_SET, $sCollation = DEFAULT_COLLATION)
{
if (strlen($sCharset) > 0)
{
@@ -166,7 +401,12 @@ class CMDBSource
$aVersions = self::QueryToCol('SELECT Version() as version', 'version');
return $aVersions[0];
}
/**
* @param string $sSource
*
* @throws \MySQLException
*/
public static function SelectDB($sSource)
{
if (!((bool)self::$m_oMysqli->query("USE `$sSource`")))
@@ -176,9 +416,15 @@ class CMDBSource
self::$m_sDBName = $sSource;
}
/**
* @param string $sSource
*
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public static function CreateDB($sSource)
{
self::Query("CREATE DATABASE `$sSource` CHARACTER SET utf8 COLLATE utf8_unicode_ci");
self::Query("CREATE DATABASE `$sSource` CHARACTER SET ".DEFAULT_CHARACTER_SET." COLLATE ".DEFAULT_COLLATION);
self::SelectDB($sSource);
}
@@ -209,6 +455,14 @@ class CMDBSource
return $res;
}
/**
* @return \mysqli
*/
public static function GetMysqli()
{
return self::$m_oMysqli;
}
public static function GetErrNo()
{
if (self::$m_oMysqli->errno != 0)
@@ -275,6 +529,13 @@ class CMDBSource
return $value;
}
/**
* @param string $sSQLQuery
*
* @return \mysqli_result
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public static function Query($sSQLQuery)
{
$oKPI = new ExecutionKPI();
@@ -289,19 +550,35 @@ class CMDBSource
$oKPI->ComputeStats('Query exec (mySQL)', $sSQLQuery);
if ($oResult === false)
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSQLQuery));
$aContext = array('query' => $sSQLQuery);
$iMySqlErrorNo = self::$m_oMysqli->errno;
$aMySqlHasGoneAwayErrorCodes = MySQLHasGoneAwayException::getErrorCodes();
if (in_array($iMySqlErrorNo, $aMySqlHasGoneAwayErrorCodes))
{
throw new MySQLHasGoneAwayException(self::GetError(), $aContext);
}
throw new MySQLException('Failed to issue SQL query', $aContext);
}
return $oResult;
}
/**
* @param string $sTable
*
* @return int
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public static function GetNextInsertId($sTable)
{
$sSQL = "SHOW TABLE STATUS LIKE '$sTable'";
$oResult = self::Query($sSQL);
$aRow = $oResult->fetch_assoc();
$iNextInsertId = $aRow['Auto_increment'];
return $iNextInsertId;
return $aRow['Auto_increment'];
}
public static function GetInsertId()
@@ -328,7 +605,15 @@ class CMDBSource
self::Query($sSQLQuery);
}
public static function QueryToScalar($sSql)
/**
* @param string $sSql
* @param int $iCol beginning at 0
*
* @return string corresponding cell content on the first line
* @throws \MySQLException
* @throws \MySQLQueryHasNoResultException
*/
public static function QueryToScalar($sSql, $iCol = 0)
{
$oKPI = new ExecutionKPI();
try
@@ -345,20 +630,28 @@ class CMDBSource
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
}
if ($aRow = $oResult->fetch_array(MYSQLI_BOTH))
{
$res = $aRow[0];
$res = $aRow[$iCol];
}
else
{
$oResult->free();
throw new MySQLException('Found no result for query', array('query' => $sSql));
throw new MySQLQueryHasNoResultException('Found no result for query', array('query' => $sSql));
}
$oResult->free();
return $res;
}
/**
* @param string $sSql
*
* @return array
* @throws \MySQLException if query cannot be processed
*/
public static function QueryToArray($sSql)
{
$aData = array();
@@ -386,6 +679,13 @@ class CMDBSource
return $aData;
}
/**
* @param string $sSql
* @param int $col
*
* @return array
* @throws \MySQLException
*/
public static function QueryToCol($sSql, $col)
{
$aColumn = array();
@@ -397,6 +697,12 @@ class CMDBSource
return $aColumn;
}
/**
* @param string $sSql
*
* @return array
* @throws \MySQLException if query cannot be processed
*/
public static function ExplainQuery($sSql)
{
$aData = array();
@@ -412,8 +718,8 @@ class CMDBSource
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
}
$aNames = self::GetColumns($oResult);
$aNames = self::GetColumns($oResult, $sSql);
$aData[] = $aNames;
while ($aRow = $oResult->fetch_array(MYSQLI_ASSOC))
@@ -424,6 +730,12 @@ class CMDBSource
return $aData;
}
/**
* @param string $sSql
*
* @return string
* @throws \MySQLException if query cannot be processed
*/
public static function TestQuery($sSql)
{
try
@@ -461,7 +773,14 @@ class CMDBSource
return $oResult->fetch_array(MYSQLI_ASSOC);
}
public static function GetColumns($oResult)
/**
* @param mysqli_result $oResult
* @param string $sSql
*
* @return string[]
* @throws \MySQLException
*/
public static function GetColumns($oResult, $sSql)
{
$aNames = array();
for ($i = 0; $i < (($___mysqli_tmp = $oResult->field_count) ? $___mysqli_tmp : 0) ; $i++)
@@ -543,17 +862,35 @@ class CMDBSource
return ($aFieldData["Type"]);
}
/**
* @param string $sTable
* @param string $sField
*
* @return bool|string
* @see \AttributeDefinition::GetSQLColumns()
*/
public static function GetFieldSpec($sTable, $sField)
{
$aTableInfo = self::GetTableInfo($sTable);
if (empty($aTableInfo)) return false;
if (!array_key_exists($sField, $aTableInfo["Fields"])) return false;
$aFieldData = $aTableInfo["Fields"][$sField];
$sRet = $aFieldData["Type"];
$sColumnCharset = $aFieldData["Charset"];
$sColumnCollation = $aFieldData["Collation"];
if (!empty($sColumnCharset))
{
$sRet .= ' CHARACTER SET '.$sColumnCharset;
$sRet .= ' COLLATE '.$sColumnCollation;
}
if ($aFieldData["Null"] == 'NO')
{
$sRet .= ' NOT NULL';
}
if (is_numeric($aFieldData["Default"]))
{
if (strtolower(substr($aFieldData["Type"], 0, 5)) == 'enum(')
@@ -571,10 +908,11 @@ class CMDBSource
{
$sRet .= ' DEFAULT '.self::Quote($aFieldData["Default"]);
}
return $sRet;
}
public static function HasIndex($sTable, $sIndexId, $aFields = null)
public static function HasIndex($sTable, $sIndexId, $aFields = null, $aLength = null)
{
$aTableInfo = self::GetTableInfo($sTable);
if (empty($aTableInfo)) return false;
@@ -588,9 +926,24 @@ class CMDBSource
// Compare the columns
$sSearchedIndex = implode(',', $aFields);
$sExistingIndex = implode(',', $aTableInfo['Indexes'][$sIndexId]);
$aColumnNames = array();
$aSubParts = array();
foreach($aTableInfo['Indexes'][$sIndexId] as $aIndexDef)
{
$aColumnNames[] = $aIndexDef['Column_name'];
$aSubParts[] = $aIndexDef['Sub_part'];
}
$sExistingIndex = implode(',', $aColumnNames);
return ($sSearchedIndex == $sExistingIndex);
if (is_null($aLength))
{
return ($sSearchedIndex == $sExistingIndex);
}
$sSearchedLength = implode(',', $aLength);
$sExistingLength = implode(',', $aSubParts);
return ($sSearchedIndex == $sExistingIndex) && ($sSearchedLength == $sExistingLength);
}
// Returns an array of (fieldname => array of field info)
@@ -610,35 +963,49 @@ class CMDBSource
{
self::$m_aTablesInfo = array();
}
/**
* @param $sTableName
*
* @throws \MySQLException
*/
private static function _TableInfoCacheInit($sTableName)
{
if (isset(self::$m_aTablesInfo[strtolower($sTableName)])
&& (self::$m_aTablesInfo[strtolower($sTableName)] != null)) return;
try
&& (self::$m_aTablesInfo[strtolower($sTableName)] != null))
{
// Check if the table exists
$aFields = self::QueryToArray("SHOW COLUMNS FROM `$sTableName`");
// Note: without backticks, you get an error with some table names (e.g. "group")
foreach ($aFields as $aFieldData)
{
$sFieldName = $aFieldData["Field"];
self::$m_aTablesInfo[strtolower($sTableName)]["Fields"][$sFieldName] =
array
(
"Name"=>$aFieldData["Field"],
"Type"=>$aFieldData["Type"],
"Null"=>$aFieldData["Null"],
"Key"=>$aFieldData["Key"],
"Default"=>$aFieldData["Default"],
"Extra"=>$aFieldData["Extra"]
);
}
return;
}
catch(MySQLException $e)
// Create array entry, if table does not exist / has no columns
self::$m_aTablesInfo[strtolower($sTableName)] = null;
// Get table informations
// We were using SHOW COLUMNS FROM... but this don't return charset and collation info !
// so since 2.5 and #1001 (switch to utf8mb4) we're using INFORMATION_SCHEMA !
$aMapping = array(
"Name" => "COLUMN_NAME",
"Type" => "COLUMN_TYPE",
"Null" => "IS_NULLABLE",
"Key" => "COLUMN_KEY",
"Default" => "COLUMN_DEFAULT",
"Extra" => "EXTRA",
"Charset" => "CHARACTER_SET_NAME",
"Collation" => "COLLATION_NAME",
"CharMaxLength" => "CHARACTER_MAXIMUM_LENGTH",
);
$sColumns = implode(', ', $aMapping);
$sDBName = self::$m_sDBName;
$aFields = self::QueryToArray("SELECT $sColumns FROM information_schema.`COLUMNS` WHERE table_schema = '$sDBName' AND table_name = '$sTableName';");
foreach ($aFields as $aFieldData)
{
// Table does not exist
self::$m_aTablesInfo[strtolower($sTableName)] = null;
$aFields = array();
foreach($aMapping as $sKey => $sColumn)
{
$aFields[$sKey] = $aFieldData[$sColumn];
}
$sFieldName = $aFieldData["COLUMN_NAME"];
self::$m_aTablesInfo[strtolower($sTableName)]["Fields"][$sFieldName] = $aFields;
}
if (!is_null(self::$m_aTablesInfo[strtolower($sTableName)]))
@@ -647,32 +1014,57 @@ class CMDBSource
$aMyIndexes = array();
foreach ($aIndexes as $aIndexColumn)
{
$aMyIndexes[$aIndexColumn['Key_name']][$aIndexColumn['Seq_in_index']-1] = $aIndexColumn['Column_name'];
$aMyIndexes[$aIndexColumn['Key_name']][$aIndexColumn['Seq_in_index']-1] = $aIndexColumn;
}
self::$m_aTablesInfo[strtolower($sTableName)]["Indexes"] = $aMyIndexes;
}
}
//public static function EnumTables()
//{
// self::_TablesInfoCacheInit();
// return array_keys(self::$m_aTablesInfo);
//}
public static function GetTableInfo($sTable)
{
self::_TableInfoCacheInit($sTable);
// perform a case insensitive match because on Windows the table names become lowercase :-(
//foreach(self::$m_aTablesInfo as $sTableName => $aInfo)
//{
// if (strtolower($sTableName) == strtolower($sTable))
// {
// return $aInfo;
// }
//}
return self::$m_aTablesInfo[strtolower($sTable)];
//return null;
}
/**
* @param string $sTableName
*
* @return string query to upgrade table charset and collation if needed, null if not
* @throws \MySQLException
*
* @since 2.5 #1001 switch to utf8mb4
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-table.html
*/
public static function DBCheckTableCharsetAndCollation($sTableName)
{
$sDBName = self::DBName();
$sTableInfoQuery = "SELECT C.character_set_name, T.table_collation
FROM information_schema.`TABLES` T inner join information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` C
ON T.table_collation = C.collation_name
WHERE T.table_schema = '$sDBName'
AND T.table_name = '$sTableName';";
$aTableInfo = self::QueryToArray($sTableInfoQuery);
$sTableCharset = $aTableInfo[0]['character_set_name'];
$sTableCollation = $aTableInfo[0]['table_collation'];
if ((DEFAULT_CHARACTER_SET == $sTableCharset) && (DEFAULT_COLLATION == $sTableCollation))
{
return null;
}
return 'ALTER TABLE `'.$sTableName.'` '.self::$SQL_STRING_COLUMNS_CHARSET_DEFINITION.';';
}
/**
* @param string $sTable
*
* @return array
* @throws \MySQLException if query cannot be processed
*/
public static function DumpTable($sTable)
{
$sSql = "SELECT * FROM `$sTable`";
@@ -728,6 +1120,7 @@ class CMDBSource
}
catch(MySQLException $e)
{
$iCode = self::GetErrNo();
return "Current user not allowed to see his own privileges (could not access to the database 'mysql' - $iCode)";
}
@@ -786,4 +1179,28 @@ class CMDBSource
}
return false;
}
}
/**
* @return string query to upgrade database charset and collation if needed, null if not
* @throws \MySQLException
*
* @since 2.5 #1001 switch to utf8mb4
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-database.html
*/
public static function DBCheckCharsetAndCollation()
{
$sDBName = CMDBSource::DBName();
$sDBInfoQuery = "SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '$sDBName';";
$aDBInfo = CMDBSource::QueryToArray($sDBInfoQuery);
$sDBCharset = $aDBInfo[0]['DEFAULT_CHARACTER_SET_NAME'];
$sDBCollation = $aDBInfo[0]['DEFAULT_COLLATION_NAME'];
if ((DEFAULT_CHARACTER_SET == $sDBCharset) && (DEFAULT_COLLATION == $sDBCollation))
{
return null;
}
return 'ALTER DATABASE'.CMDBSource::$SQL_STRING_COLUMNS_CHARSET_DEFINITION.';';
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -69,6 +69,13 @@ class CoreException extends Exception
parent::__construct($sMessage, 0);
}
/**
* @return string code and message for log purposes
*/
public function getInfoLog()
{
return 'error_code='.$this->getCode().', message="'.$this->getMessage().'"';
}
public function getHtmlDesc($sHighlightHtmlBegin = '<b>', $sHighlightHtmlEnd = '</b>')
{
return $this->getMessage();

View File

@@ -1,3 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design>
<user_rights>
<profiles>
<profile id="1024" _delta="define">
<name>REST Services User</name>
<description>Only users having this profile are allowed to use the REST Web Services (unless 'secure_rest_services' is set to false in the configuration file).</description>
<groups />
</profile>
</profiles>
</user_rights>
</itop_design>

View File

@@ -74,8 +74,8 @@ abstract class DBObject implements iDisplay
private static $m_aBulkInsertCols = array(); // class => array of ('table' => array of <sql_column>)
private static $m_bBulkInsert = false;
private $m_bIsInDB = false; // true IIF the object is mapped to a DB record
private $m_iKey = null;
protected $m_bIsInDB = false; // true IIF the object is mapped to a DB record
protected $m_iKey = null;
private $m_aCurrValues = array();
protected $m_aOrigValues = array();
@@ -159,18 +159,13 @@ abstract class DBObject implements iDisplay
public function __toString()
{
$sRet = '';
$sClass = get_class($this);
$sRootClass = MetaModel::GetRootClass($sClass);
$iPKey = $this->GetKey();
$sRet .= "<b title=\"$sRootClass\">$sClass</b>::$iPKey<br/>\n";
$sRet .= "<ul class=\"treeview\">\n";
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
{
$sRet .= "<li>".$oAttDef->GetLabel()." = ".$this->GetAsHtml($sAttCode)."</li>\n";
}
$sRet .= "</ul>";
return $sRet;
$sRet = '';
$sClass = get_class($this);
$sRootClass = MetaModel::GetRootClass($sClass);
$iPKey = $this->GetKey();
$sFriendlyname = $this->Get('friendlyname');
$sRet .= "<b title=\"$sRootClass\">$sClass</b>::$iPKey ($sFriendlyname)<br/>\n";
return $sRet;
}
// Restore initial values... mmmm, to be discussed
@@ -1053,24 +1048,24 @@ abstract class DBObject implements iDisplay
}
/**
* Returns the set of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...)
* for the given attribute in the current state of the object
* @param $sAttCode string $sAttCode The code of the attribute
* @param $aReasons array To store the reasons why the attribute is read-only (info about the synchro replicas)
* @param $sTargetState string The target state in which to evalutate the flags, if empty the current state will be used
* @return integer Flags: the binary combination of the flags applicable to this attribute
*/
*
* @param string $sAttCode $sAttCode The code of the attribute
* @param array $aReasons To store the reasons why the attribute is read-only (info about the synchro replicas)
* @param string $sTargetState The target state in which to evalutate the flags, if empty the current state will be
* used
*
* @return integer the binary combination of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...) for the
* given attribute in the given state of the object
* @throws \CoreException
*/
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
{
$iFlags = 0; // By default (if no life cycle) no flag at all
$aReadOnlyAtts = $this->GetReadOnlyAttributes();
if ($aReadOnlyAtts != null)
if (($aReadOnlyAtts != null) && (in_array($sAttCode, $aReadOnlyAtts)))
{
if (in_array($sAttCode, $aReadOnlyAtts))
{
return OPT_ATT_READONLY;
}
return OPT_ATT_READONLY;
}
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
@@ -1094,6 +1089,19 @@ abstract class DBObject implements iDisplay
return $iFlags | $iSynchroFlags; // Combine both sets of flags
}
/**
* @param string $sAttCode
* @param array $aReasons To store the reasons why the attribute is read-only (info about the synchro replicas)
*
* @throws \CoreException
*/
public function IsAttributeReadOnlyForCurrentState($sAttCode, &$aReasons = array())
{
$iAttFlags = $this->GetAttributeFlags($sAttCode, $aReasons);
return ($iAttFlags & OPT_ATT_READONLY);
}
/**
* Returns the set of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...)
* for the given attribute in a transition
@@ -1216,9 +1224,10 @@ abstract class DBObject implements iDisplay
if ($oAtt->IsHierarchicalKey())
{
// This check cannot be deactivated since otherwise the user may break things by a CSV import of a bulk modify
if ($toCheck == $this->GetKey())
$aValues = $oAtt->GetAllowedValues(array('this' => $this));
if (!array_key_exists($toCheck, $aValues))
{
return "An object can not be its own parent in a hierarchy (".$oAtt->Getlabel()." = $toCheck)";
return "Value not allowed [$toCheck]";
}
}
}
@@ -2930,7 +2939,7 @@ abstract class DBObject implements iDisplay
$oSearch->AllowAllData();
}
$oSet = new CMDBObjectSet($oSearch);
if ($oSet->Count(1) > 0)
if ($oSet->Count() > 0)
{
$aDependentObjects[$sRemoteClass][$sExtKeyAttCode] = array(
'attribute' => $oExtKeyAttDef,
@@ -3648,5 +3657,76 @@ abstract class DBObject implements iDisplay
$this->m_aCurrValues['archive_date'] = null;
$this->m_aOrigValues['archive_date'] = null;
}
/**
* @param string $sClass Needs to be an instanciable class
* @returns $oObj
**/
public static function MakeDefaultInstance($sClass)
{
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
$oObj = MetaModel::NewObject($sClass);
if (!empty($sStateAttCode))
{
$sTargetState = MetaModel::GetDefaultState($sClass);
$oObj->Set($sStateAttCode, $sTargetState);
}
return $oObj;
}
/**
* Complete a new object with data from context
* @param array $aContextParam Context used for creation form prefilling
*
*/
public function PrefillCreationForm(&$aContextParam)
{
}
/**
* Complete an object after a state transition with data from context
* @param array $aContextParam Context used for creation form prefilling
*
*/
public function PrefillTransitionForm(&$aContextParam)
{
}
/**
* Complete a filter ($aContextParam['filter']) data from context
* (Called on source object)
* @param array $aContextParam Context used for creation form prefilling
*
*/
public function PrefillSearchForm(&$aContextParam)
{
}
/**
* Prefill a creation / stimulus change / search form according to context, current state of an object, stimulus.. $sOperation
* @param string $sOperation Operation identifier
* @param array $aContextParam Context used for creation form prefilling
*
*/
public function PrefillForm($sOperation, &$aContextParam)
{
switch($sOperation){
case 'creation_from_0':
case 'creation_from_extkey':
case 'creation_from_editinplace':
$this->PrefillCreationForm($aContextParam);
break;
case 'state_change':
$this->PrefillTransitionForm($aContextParam);
break;
case 'search':
$this->PrefillSearchForm($aContextParam);
break;
default:
break;
}
}
}

View File

@@ -501,9 +501,7 @@ class DBObjectSearch extends DBSearch
$oParamExpression = new VariableExpression($sInParamName);
$this->SetInternalParams(array($sInParamName => $aValues));
$oListExpression = new ListExpression(array($oParamExpression));
$oInCondition = new BinaryExpression($oFieldExpression, $sOperator, $oListExpression);
$oInCondition = new BinaryExpression($oFieldExpression, $sOperator, $oParamExpression);
$this->AddConditionExpression($oInCondition);
}
@@ -1054,7 +1052,7 @@ class DBObjectSearch extends DBSearch
public function GetCriteria() {return $this->m_oSearchCondition;}
public function GetCriteria_FullText() {throw new Exception("Removed GetCriteria_FullText");}
protected function GetCriteria_PointingTo($sKeyAttCode = "")
public function GetCriteria_PointingTo($sKeyAttCode = "")
{
if (empty($sKeyAttCode))
{
@@ -1135,7 +1133,108 @@ class DBObjectSearch extends DBSearch
{
return $this->m_oSearchCondition->ApplyParameters(array_merge($this->m_aParams, $aArgs));
}
/**
*
* @todo: check if the clone is mandatory or optional. (the performance would be better without cloning)
*
* @param bool $bClone
*
* @return DBObjectSearch|DBSearch
*/
public function ShorthandExpansion($bClone = false)
{
if ($bClone)
{
$oDbObject = $this->DeepClone();
}
else
{
$oDbObject = $this;
}
/**
* This callback add joins based on the classes found inside ExternalFieldExpression::$aFields
* to do so, it is applied on each Expression of $this->m_oSearchCondition.
*
* @param $oExpression
*/
$callback = function ($oExpression) use ($oDbObject)
{
if (!$oExpression instanceof ExternalFieldExpression) {
return;
}
/** @var FieldExpression[] $aFieldExpressionsPointingTo */
$aFields = $oExpression->GetFields();
$aRealiasingMap = array();
$aExpressionNewConditions = array();
foreach ($aFields as $aFieldExpressionPointingTo)
{
$oFilter = new DBObjectSearch($aFieldExpressionPointingTo['sClass']);
$aExpressionNewConditions[] = array(
'oFilter' => $oFilter,
'sExtKeyAttCode' => $aFieldExpressionPointingTo['sAttCode'],
);
$aRealiasingMap[$aFieldExpressionPointingTo['sClass']] = $aFieldExpressionPointingTo['sAlias'];
}
/**
* the iteration below is weird because wee need to
* - iterate in the reverse order
* - the iteration access the "index+1" so wee start at "length-1" (which is "count()-2")
* - whe stop at the index 1, because the index 0 is merged into the $oDbObject
*/
for ($i = count($aExpressionNewConditions) - 2; $i > 0; $i--)
{
$aExpressionNewConditions[$i]['oFilter']->AddCondition_PointingTo(
$aExpressionNewConditions[$i+1]['oFilter'],
$aExpressionNewConditions[$i]['sExtKeyAttCode'],
TREE_OPERATOR_EQUALS,
$aRealiasingMap
);
}
$oDbObject->AddCondition_PointingTo(
$aExpressionNewConditions[1]['oFilter'],
$aExpressionNewConditions[0]['sExtKeyAttCode'],
TREE_OPERATOR_EQUALS,
$aRealiasingMap
);
foreach ($aRealiasingMap as $sOldAlias => $sNewAlias)
{
if ($sOldAlias == $sNewAlias)
{
continue;
}
if ($sOldAlias != $aFields['sAlias'])
{
continue;
}
$aFields['sAlias'] = $sNewAlias;
}
$oExpression->SetFields($aFields);
}; //end of the callback definition
//Add the joins
$this->m_oSearchCondition->Browse($callback);
//replace the ExternalFieldExpression by a FieldExpression (based on last ExternalFieldExpression::$aFields)
$this->m_oSearchCondition->Translate(array(), false, false);//TODO: check if this call is correct
return $oDbObject;
}
public function ToOQL($bDevelopParams = false, $aContextParams = null, $bWithAllowAllFlag = false)
{
// Currently unused, but could be useful later
@@ -1264,6 +1363,23 @@ class DBObjectSearch extends DBSearch
$oRight = $this->OQLExpressionToCondition($sQuery, $oExpression->GetRightExpr(), $aClassAliases);
return new BinaryExpression($oLeft, $sOperator, $oRight);
}
elseif ($oExpression instanceof ExternalFieldOqlExpression)
{
//TODO : convert FieldOqlExpression[] to FieldExpression[]
$aOqlFieldExpression = $oExpression->GetExpressions();
$aFields = array();
foreach ($aOqlFieldExpression as $oOqlFieldExpression)
{
$aFields[] = array(
'sClass' => $oOqlFieldExpression->GetParent(),
'sAlias' => $oOqlFieldExpression->GetParent(),
'sAttCode' => $oOqlFieldExpression->GetName(),
);
}
return new ExternalFieldExpression($oExpression->GetName(), $aFields);
}
elseif ($oExpression instanceof FieldOqlExpression)
{
$sClassAlias = $oExpression->GetParent();
@@ -1483,7 +1599,7 @@ class DBObjectSearch extends DBSearch
return $sRet;
}
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null)
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
{
// Hide objects that are not visible to the current user
//
@@ -1532,7 +1648,7 @@ class DBObjectSearch extends DBSearch
$aContextData['sRequestUri'] = '';
}
// Need to identify the query
// Need to identify the querySELECT `Contact` FROM Contact AS `Contact` JOIN Organization AS `Organization` ON `Contact`.org_id = `Organization`.id JOIN DeliveryModel AS `DeliveryModel` ON `Organization`.deliverymodel_id = `DeliveryModel`.id WHERE ((((Contact.org_id->deliverymodel_id->name = 'Standard support') AND 1) AND 1) AND 1)
$sOqlQuery = $oSearch->ToOql(false, null, true);
if ((strpos($sOqlQuery, '`id` IN (') !== false) || (strpos($sOqlQuery, '`id` NOT IN (') !== false))
{
@@ -1566,7 +1682,15 @@ class DBObjectSearch extends DBSearch
$sRawId .= 'g:'.$sAlias.'!'.$oExpr->Render();
}
}
if (!is_null($aSelectExpr))
{
foreach($aSelectExpr as $sAlias => $oExpr)
{
$sRawId .= 'se:'.$sAlias.'!'.$oExpr->Render();
}
}
$aContextData['aGroupByExpr'] = $aGroupByExpr;
$aContextData['aSelectExpr'] = $aSelectExpr;
$sRawId .= $bGetCount;
$aContextData['bGetCount'] = $bGetCount;
if (is_array($aSelectedClasses))
@@ -1591,6 +1715,7 @@ class DBObjectSearch extends DBSearch
// Query caching
//
$sOqlAPCCacheId = null;
if (self::$m_bQueryCacheEnabled)
{
// Warning: using directly the query string as the key to the hash array can FAIL if the string
@@ -1629,8 +1754,9 @@ class DBObjectSearch extends DBSearch
if (!isset($oSQLQuery))
{
$oKPI = new ExecutionKPI();
$oSQLQuery = $oSearch->BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr, $aSelectedClasses);
$oSearch = $oSearch->ShorthandExpansion(true);//TODO : check if the clone is really needed (1st param of ShorthandExpansion)
$oKPI = new ExecutionKPI();
$oSQLQuery = $oSearch->BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
$oKPI->ComputeStats('BuildSQLQueryStruct', $sOqlQuery);
if (self::$m_bQueryCacheEnabled)
@@ -1650,16 +1776,17 @@ class DBObjectSearch extends DBSearch
}
/**
* @param $aAttToLoad
* @param $bGetCount
* @param $aModifierProperties
* @param null $aGroupByExpr
* @param null $aSelectedClasses
* @param array $aAttToLoad
* @param bool $bGetCount
* @param array $aModifierProperties
* @param array $aGroupByExpr
* @param array $aSelectedClasses
* @param array $aSelectExpr
* @return null|SQLObjectQuery
*/
protected function BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
protected function BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
{
$oBuild = new QueryBuilderContext($this, $aModifierProperties, $aGroupByExpr, $aSelectedClasses);
$oBuild = new QueryBuilderContext($this, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
$oSQLQuery = $this->MakeSQLObjectQuery($oBuild, $aAttToLoad, array());
$oSQLQuery->SetCondition($oBuild->m_oQBExpressions->GetCondition());
@@ -1673,6 +1800,17 @@ class DBObjectSearch extends DBSearch
{
$oSQLQuery->SetSelect($oBuild->m_oQBExpressions->GetSelect());
}
if ($aSelectExpr)
{
// Get the fields corresponding to the select expressions
foreach($oBuild->m_oQBExpressions->GetSelect() as $sAlias => $oExpr)
{
if (key_exists($sAlias, $aSelectExpr))
{
$oSQLQuery->AddSelect($sAlias, $oExpr);
}
}
}
$aMandatoryTables = null;
if (self::$m_bOptimizeQueries)
@@ -1871,86 +2009,46 @@ class DBObjectSearch extends DBSearch
}
}
$bRootFirst = MetaModel::GetConfig()->Get('optimize_requests_for_join_count');
if ($bRootFirst)
// First query built from the root, adding all tables including the leaf
// Before N.1065 we were joining from the leaf first, but this wasn't a good choice :
// most of the time (obsolescence, friendlyname, ...) we want to get a root attribute !
//
$oSelectBase = null;
$aClassHierarchy = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, true);
$bIsClassStandaloneClass = (count($aClassHierarchy) == 1);
foreach($aClassHierarchy as $sSomeClass)
{
// 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))
{
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));
}
continue;
}
}
else
{
// First query built upon on the leaf (ie current) class
//
self::DbgTrace("Main (=leaf) class, call MakeSQLObjectQuerySingleTable()");
if (MetaModel::HasTable($sClass))
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 = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sClass, $aExtKeys, $aValues);
$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 = 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));
}
$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sSomeClass));
}
}

View File

@@ -77,20 +77,18 @@ 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 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 $aOrderBy Array of '[<classalias>.]attcode' => bAscending
* @param hash $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, $bSort = true)
public function __construct(DBSearch $oFilter, $aOrderBy = array(), $aArgs = array(), $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0)
{
$this->m_oFilter = $oFilter->DeepClone();
$this->m_aAddedIds = array();
@@ -100,7 +98,6 @@ 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;
@@ -173,8 +170,8 @@ class DBObjectSet implements iDBObjectSetIterator
/**
* Specify the subset of attributes to load (for each class of objects) before performing the SQL query for retrieving the rows from the DB
*
* @param hash $aAttToLoad Format: alias => array of attribute_codes
*
* @param array $aAttToLoad Format: alias => array of attribute_codes
*
* @return void
*/
@@ -604,15 +601,10 @@ class DBObjectSet implements iDBObjectSetIterator
*
* Limitation: the sort order has no effect on objects added in-memory
*
* @return array Format: field_code => boolean (true = ascending, false = descending)
* @return hash 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))
@@ -642,7 +634,6 @@ class DBObjectSet implements iDBObjectSetIterator
$this->m_oSQLResult->free();
$this->m_oSQLResult = null;
}
$this->m_iNumTotalDBRows = null;
try
{
@@ -674,11 +665,12 @@ class DBObjectSet implements iDBObjectSetIterator
}
if ($this->m_oSQLResult === false) return;
if (($this->m_iLimitCount == 0) && ($this->m_iLimitStart == 0))
if ((($this->m_iLimitCount == 0) || ($this->m_iLimitCount > $this->m_oSQLResult->num_rows)) && ($this->m_iLimitStart == 0))
{
$this->m_iNumTotalDBRows = $this->m_oSQLResult->num_rows;
}
$this->m_iNumLoadedDBRows = $this->m_oSQLResult->num_rows;
}
@@ -710,25 +702,19 @@ class DBObjectSet implements iDBObjectSetIterator
* 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)
public function Count()
{
if (is_null($this->m_iNumTotalDBRows))
{
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit, 0, true);
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
$resQuery = CMDBSource::Query($sSQL);
if (!$resQuery) return 0;
$aRow = CMDBSource::FetchArray($resQuery);
CMDBSource::FreeResult($resQuery);
$this->m_iNumTotalDBRows = $aRow['COUNT'];
$this->m_iNumTotalDBRows = intval($aRow['COUNT']);
}
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ??

View File

@@ -61,6 +61,8 @@ abstract class DBSearch
/**
* Perform a deep clone (as opposed to "clone" which does copy a reference to the underlying objects)
*
* @return \DBSearch
**/
public function DeepClone()
{
@@ -243,7 +245,7 @@ abstract class DBSearch
public function serialize($bDevelopParams = false, $aContextParams = null)
{
$sOql = $this->ToOql($bDevelopParams, $aContextParams);
return base64_encode(serialize(array($sOql, $this->GetInternalParams(), $this->m_aModifierProperties)));
return rawurlencode(base64_encode(serialize(array($sOql, $this->GetInternalParams(), $this->m_aModifierProperties))));
}
/**
@@ -253,7 +255,7 @@ abstract class DBSearch
*/
static public function unserialize($sValue)
{
$aData = unserialize(base64_decode($sValue));
$aData = unserialize(base64_decode(rawurldecode($sValue)));
$sOql = $aData[0];
$aParams = $aData[1];
// We've tried to use gzcompress/gzuncompress, but for some specific queries
@@ -296,7 +298,7 @@ abstract class DBSearch
/**
* @param string $sQuery
* @param array $aParams
* @return DBSearch
* @return self
* @throws OQLException
*/
static public function FromOQL($sQuery, $aParams = null)
@@ -392,7 +394,7 @@ abstract class DBSearch
array_unshift($aColumns, 'id');
}
$aQueryCols = CMDBSource::GetColumns($resQuery);
$aQueryCols = CMDBSource::GetColumns($resQuery, $sSQL);
$sClassAlias = $this->GetClassAlias();
$aColMap = array();
@@ -427,8 +429,53 @@ abstract class DBSearch
protected static $m_aQueryStructCache = array();
public function MakeGroupByQuery($aArgs, $aGroupByExpr, $bExcludeNullValues = false)
/** Generate a Group By SQL request from a search
* @param array $aArgs
* @param array $aGroupByExpr array('alias' => Expression)
* @param bool $bExcludeNullValues
* @param array $aSelectExpr array('alias' => Expression) Additional expressions added to the request
* @param array $aOrderBy array('alias' => bool) true = ASC false = DESC
* @param int $iLimitCount
* @param int $iLimitStart
* @return string SQL query generated
* @throws Exception
*/
public function MakeGroupByQuery($aArgs, $aGroupByExpr, $bExcludeNullValues = false, $aSelectExpr = array(), $aOrderBy = array(), $iLimitCount = 0, $iLimitStart = 0)
{
// Sanity check
foreach($aGroupByExpr as $sAlias => $oExpr)
{
if (!($oExpr instanceof Expression))
{
throw new CoreException("Wrong parameter for 'Group By' for [$sAlias] (an array('alias' => Expression) is awaited)");
}
}
foreach($aSelectExpr as $sAlias => $oExpr)
{
if (array_key_exists($sAlias, $aGroupByExpr))
{
throw new CoreException("Alias collision between 'Group By' and 'Select Expressions' [$sAlias]");
}
if (!($oExpr instanceof Expression))
{
throw new CoreException("Wrong parameter for 'Select Expressions' for [$sAlias] (an array('alias' => Expression) is awaited)");
}
}
foreach($aOrderBy as $sAlias => $bAscending)
{
if (!array_key_exists($sAlias, $aGroupByExpr) && !array_key_exists($sAlias, $aSelectExpr) && ($sAlias != '_itop_count_'))
{
$aAllowedAliases = array_keys($aSelectExpr);
$aAllowedAliases = array_merge($aAllowedAliases, array_keys($aGroupByExpr));
$aAllowedAliases[] = '_itop_count_';
throw new CoreException("Wrong alias [$sAlias] for 'Order By'. Allowed values are: ", null, implode(", ", $aAllowedAliases));
}
if (!is_bool($bAscending))
{
throw new CoreException("Wrong direction in ORDER BY spec, found '$bAscending' and expecting a boolean value for '$sAlias''");
}
}
if ($bExcludeNullValues)
{
// Null values are not handled (though external keys set to 0 are allowed)
@@ -446,15 +493,15 @@ abstract class DBSearch
}
$aAttToLoad = array();
$oSQLQuery = $oQueryFilter->GetSQLQuery(array(), $aArgs, $aAttToLoad, null, 0, 0, false, $aGroupByExpr);
$oSQLQuery = $oQueryFilter->GetSQLQuery(array(), $aArgs, $aAttToLoad, null, 0, 0, false, $aGroupByExpr, $aSelectExpr);
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
try
{
$bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
$sRes = $oSQLQuery->RenderGroupBy($aScalarArgs, $bBeautifulSQL);
$sRes = $oSQLQuery->RenderGroupBy($aScalarArgs, $bBeautifulSQL, $aOrderBy, $iLimitCount, $iLimitStart);
}
catch (MissingQueryArgument $e)
catch (Exception $e)
{
// Add some information...
$e->addInfo('OQL', $this->ToOQL());
@@ -561,9 +608,9 @@ abstract class DBSearch
}
protected function GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $aGroupByExpr = null)
protected function GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $aGroupByExpr = null, $aSelectExpr = null)
{
$oSQLQuery = $this->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr);
$oSQLQuery = $this->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, null, $aSelectExpr);
$oSQLQuery->SetSourceOQL($this->ToOQL());
// Join to an additional table, if required...
@@ -585,7 +632,7 @@ abstract class DBSearch
}
public abstract function GetSQLQueryStructure(
$aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null
$aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null
);
////////////////////////////////////////////////////////////////////////////

View File

@@ -143,7 +143,7 @@ class DBUnionSearch extends DBSearch
/**
* Limited to the selected classes
*/
*/
public function GetClassName($sAlias)
{
if (array_key_exists($sAlias, $this->aSelectedClasses))
@@ -474,15 +474,17 @@ class DBUnionSearch extends DBSearch
throw new Exception('MakeUpdateQuery is not implemented for the unions!');
}
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null)
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
{
if (count($this->aSearches) == 1)
{
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr);
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, $aSelectExpr);
}
$aSQLQueries = array();
$aAliases = array_keys($this->aSelectedClasses);
$aQueryAttToLoad = null;
$aUnionQuerySelectExpr = array();
foreach ($this->aSearches as $iSearch => $oSearch)
{
$aSearchAliases = array_keys($oSearch->GetSelectedClasses());
@@ -544,7 +546,43 @@ class DBUnionSearch extends DBSearch
$aQueryGroupByExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
}
}
$oSubQuery = $oSearch->GetSQLQueryStructure($aQueryAttToLoad, false, $aQueryGroupByExpr, $aSearchSelectedClasses);
if (is_null($aSelectExpr))
{
$aQuerySelectExpr = null;
}
else
{
$aQuerySelectExpr = array();
$aTranslationData = array();
$aQueryColumns = array_keys($oSearch->GetSelectedClasses());
foreach($aAliases as $iColumn => $sAlias)
{
$sQueryAlias = $aQueryColumns[$iColumn];
$aTranslationData[$sAlias]['*'] = $sQueryAlias;
}
foreach($aSelectExpr as $sExpressionAlias => $oExpression)
{
$oExpression->Browse(function ($oNode) use (&$aQuerySelectExpr, &$aTranslationData)
{
if ($oNode instanceof FieldExpression)
{
$sAlias = $oNode->GetParent()."__".$oNode->GetName();
if (!key_exists($sAlias, $aQuerySelectExpr))
{
$aQuerySelectExpr[$sAlias] = $oNode->Translate($aTranslationData, false, false);
}
$aTranslationData[$oNode->GetParent()][$oNode->GetName()] = new FieldExpression($sAlias);
}
});
// Only done for the first select as aliases are named after the first query
if (!array_key_exists($sExpressionAlias, $aUnionQuerySelectExpr))
{
$aUnionQuerySelectExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
}
}
}
$oSubQuery = $oSearch->GetSQLQueryStructure($aQueryAttToLoad, false, $aQueryGroupByExpr, $aSearchSelectedClasses, $aQuerySelectExpr);
if (count($aSearchAliases) > 1)
{
// Necessary to make sure that selected columns will match throughout all the queries
@@ -554,7 +592,7 @@ class DBUnionSearch extends DBSearch
$aSQLQueries[] = $oSubQuery;
}
$oSQLQuery = new SQLUnionQuery($aSQLQueries, $aGroupByExpr);
$oSQLQuery = new SQLUnionQuery($aSQLQueries, $aGroupByExpr, $aUnionQuerySelectExpr);
//MyHelpers::var_dump_html($oSQLQuery, true);
//MyHelpers::var_dump_html($oSQLQuery->RenderSelect(), true);
if (self::$m_bDebugQuery) $oSQLQuery->DisplayHtml();

View File

@@ -20,7 +20,7 @@
* Class Dict
* Management of localizable strings
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @copyright Copyright (C) 2010-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -65,6 +65,11 @@ class Dict
protected static $m_aData = array();
protected static $m_sApplicationPrefix = null;
/**
* @param $sLanguageCode
*
* @throws \DictExceptionUnknownLanguage
*/
public static function SetDefaultLanguage($sLanguageCode)
{
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
@@ -74,6 +79,11 @@ class Dict
self::$m_sDefaultLanguage = $sLanguageCode;
}
/**
* @param $sLanguageCode
*
* @throws \DictExceptionUnknownLanguage
*/
public static function SetUserLanguage($sLanguageCode)
{
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
@@ -108,11 +118,12 @@ class Dict
/**
* Returns a localised string from the dictonary
*
* @param string $sStringCode The code identifying the dictionary entry
* @param string $sDefault Default value if there is no match in the dictionary
* @param bool $bUserLanguageOnly True to allow the use of the default language as a fallback, false otherwise
* @throws DictExceptionMissingString
* @return unknown|Ambigous <>|string
*
* @return string
*/
public static function S($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
{
@@ -122,7 +133,7 @@ class Dict
if (!array_key_exists(self::GetUserLanguage(), self::$m_aData))
{
// It may happen, when something happens before the dictionnaries get loaded
// It may happen, when something happens before the dictionaries get loaded
return $sStringCode;
}
$aCurrentDictionary = self::$m_aData[self::GetUserLanguage()];
@@ -153,25 +164,12 @@ class Dict
}
// Could not find the string...
//
switch (self::$m_iErrorMode)
if (is_null($sDefault))
{
case DICT_ERR_STRING:
if (is_null($sDefault))
{
return $sStringCode;
}
else
{
return $sDefault;
}
break;
case DICT_ERR_EXCEPTION:
default:
throw new DictExceptionMissingString(self::$m_sCurrentLanguage, $sStringCode);
break;
return $sStringCode;
}
return 'bug!';
return $sDefault;
}
@@ -283,6 +281,9 @@ class Dict
/**
* Clone a string in every language (if it exists in that language)
*
* @param $sSourceCode
* @param $sDestCode
*/
public static function CloneString($sSourceCode, $sDestCode)
{
@@ -355,5 +356,38 @@ class Dict
// No need to actually load the strings since it's only used to know the list of languages
// at setup time !!
}
/**
* Export all the dictionary entries - of the given language - whose code matches the given prefix
* missing entries in the current language will be replaced by entries in the default language
* @param string $sStartingWith
* @return string[]
*/
public static function ExportEntries($sStartingWith)
{
self::InitLangIfNeeded(self::GetUserLanguage());
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
$aEntries = array();
$iLength = strlen($sStartingWith);
// First prefill the array with entries from the default language
foreach(self::$m_aData[self::$m_sDefaultLanguage] as $sCode => $sEntry)
{
if (substr($sCode, 0, $iLength) == $sStartingWith)
{
$aEntries[$sCode] = $sEntry;
}
}
// Now put (overwrite) the entries for the user language
foreach(self::$m_aData[self::GetUserLanguage()] as $sCode => $sEntry)
{
if (substr($sCode, 0, $iLength) == $sStartingWith)
{
$aEntries[$sCode] = $sEntry;
}
}
return $aEntries;
}
}
?>

View File

@@ -1439,7 +1439,7 @@ class DisplayableGraph extends SimpleGraph
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
}
$oP->add("<div class=\"not-printable\">\n");
$oP->add("<div id=\"ds_flash\" class=\"SearchDrawer\" style=\"display:none;\">\n");
$oP->add("<div id=\"ds_flash\" class=\"search_box\" style=\"display:none;\">\n");
if (!$oP->IsPrintableVersion())
{
$oP->add_ready_script(

View File

@@ -195,18 +195,29 @@ class EMail
$aFailedRecipients = array();
$this->m_oMessage->setMaxLineLength(0);
$iSent = $oMailer->send($this->m_oMessage, $aFailedRecipients);
if ($iSent === 0)
$oKPI = new ExecutionKPI();
try
{
// Beware: it seems that $aFailedRecipients sometimes contains the recipients that actually received the message !!!
IssueLog::Warning('Email sending failed: Some recipients were invalid, aFailedRecipients contains: '.implode(', ', $aFailedRecipients));
$aIssues = array('Some recipients were invalid.');
return EMAIL_SEND_ERROR;
$iSent = $oMailer->send($this->m_oMessage, $aFailedRecipients);
if ($iSent === 0)
{
// Beware: it seems that $aFailedRecipients sometimes contains the recipients that actually received the message !!!
IssueLog::Warning('Email sending failed: Some recipients were invalid, aFailedRecipients contains: '.implode(', ', $aFailedRecipients));
$aIssues = array('Some recipients were invalid.');
$oKPI->ComputeStats('Email Sent', 'Error received');
return EMAIL_SEND_ERROR;
}
else
{
$aIssues = array();
$oKPI->ComputeStats('Email Sent', 'Succeded');
return EMAIL_SEND_OK;
}
}
else
catch (Exception $e)
{
$aIssues = array();
return EMAIL_SEND_OK;
$oKPI->ComputeStats('Email Sent', 'Error received');
throw $e;
}
}

View File

@@ -151,6 +151,11 @@ class HTMLPurifierSanitizer extends HTMLSanitizer
class HTMLDOMSanitizer extends HTMLSanitizer
{
protected $oDoc;
/**
* @var array
* @see https://www.itophub.io/wiki/page?id=2_5_0%3Aadmin%3Arich_text_limitations
*/
protected static $aTagsWhiteList = array(
'html' => array(),
'body' => array(),
@@ -188,7 +193,6 @@ class HTMLDOMSanitizer extends HTMLSanitizer
'big' => array(),
'small' => array(),
'tt' => array(),
'code' => array(),
'kbd' => array(),
'samp' => array(),
'var' => array(),
@@ -199,16 +203,34 @@ class HTMLDOMSanitizer extends HTMLSanitizer
'q' => array(),
'hr' => array('style'),
'pre' => array(),
'center' => array(),
'caption' => array(),
);
protected static $aAttrsWhiteList = array(
'src' => '/^(http:|https:|data:)/i',
);
/**
* @var array
* @see https://www.itophub.io/wiki/page?id=2_5_0%3Aadmin%3Arich_text_limitations
*/
protected static $aStylesWhiteList = array(
'background-color', 'color', 'float', 'font', 'font-style', 'font-size', 'font-family', 'padding', 'margin', 'border', 'cellpadding', 'cellspacing', 'bordercolor', 'border-collapse', 'width', 'height', 'text-align',
'background-color',
'border',
'border-collapse',
'bordercolor',
'cellpadding',
'cellspacing',
'color',
'float',
'font',
'font-family',
'font-size',
'font-style',
'height',
'margin',
'padding',
'text-align',
'width',
);
public function __construct()

View File

@@ -499,7 +499,7 @@ class InlineImageGC implements iBackgroundProcess
{
public function GetPeriodicity()
{
return 3600; // Runs every 3600 seconds
return MetaModel::GetConfig()->Get('inline_image_garbage_collector_interval'); // run every definied time
}
public function Process($iTimeLimit)

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2013-2017 Combodo SARL
// Copyright (C) 2013-2018 Combodo SARL
//
// This file is part of iTop.
//
@@ -24,17 +24,29 @@
* Relies on MySQL locks because the API sem_get is not always present in the
* installed PHP.
*
* @copyright Copyright (C) 2013-2017 Combodo SARL
* @copyright Copyright (C) 2013-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class iTopMutex
{
protected $sName;
protected $hDBLink;
/** @var bool */
protected $bLocked; // Whether or not this instance of the Mutex is locked
/** @var \mysqli */
protected $hDBLink;
protected $sDBHost;
protected $sDBUser;
protected $sDBPwd;
protected $sDBName;
protected $sDBSubname;
protected $bDBTlsEnabled;
protected $sDBTlsCA;
static protected $aAcquiredLocks = array(); // Number of instances of the Mutex, having the lock, in this page
public function __construct($sName, $sDBHost = null, $sDBUser = null, $sDBPwd = null)
public function __construct(
$sName, $sDBHost = null, $sDBUser = null, $sDBPwd = null, $bDBTlsEnabled = false, $sDBTlsCA = null
)
{
// Compute the name of a lock for mysql
// Note: names are server-wide!!! So let's make the name specific to this iTop instance
@@ -43,15 +55,22 @@ class iTopMutex
{
$oConfig = utils::GetConfig(); // Will return an empty config when called during the setup
}
$sDBName = $oConfig->GetDBName();
$sDBSubname = $oConfig->GetDBSubname();
$this->sDBHost = is_null($sDBHost) ? $oConfig->Get('db_host') : $sDBHost;
$this->sDBUser = is_null($sDBUser) ? $oConfig->Get('db_user') : $sDBUser;
$this->sDBPwd = is_null($sDBPwd) ? $oConfig->Get('db_pwd') : $sDBPwd;
$this->sDBName = $oConfig->Get('db_name');
$sDBSubname = $oConfig->Get('db_subname');
$this->bDBTlsEnabled = is_null($bDBTlsEnabled) ? $oConfig->Get('db_tls.enabled') : $bDBTlsEnabled;
$this->sDBTlsCA = is_null($sDBTlsCA) ? $oConfig->Get('db_tls.ca') : $sDBTlsCA;
$this->sName = $sName;
if (substr($sName, -strlen($sDBName.$sDBSubname)) != $sDBName.$sDBSubname)
if (substr($sName, -strlen($this->sDBName.$sDBSubname)) != $this->sDBName.$sDBSubname)
{
// If the name supplied already ends with the expected suffix
// don't add it twice, since the setup may try to detect an already
// running cron job by its mutex, without knowing if the config already exists or not
$this->sName .= $sDBName.$sDBSubname;
$this->sName .= $this->sDBName.$sDBSubname;
}
// Limit the length of the name for MySQL > 5.7.5
@@ -64,12 +83,9 @@ class iTopMutex
self::$aAcquiredLocks[$this->sName] = 0;
}
// It is a MUST to create a dedicated session each time a lock is required, because
// It is MANDATORY to create a dedicated session each time a lock is required, because
// using GET_LOCK anytime on the same session will RELEASE the current and unique session lock (known issue)
$sDBHost = is_null($sDBHost) ? $oConfig->GetDBHost() : $sDBHost;
$sDBUser = is_null($sDBUser) ? $oConfig->GetDBUser() : $sDBUser;
$sDBPwd = is_null($sDBPwd) ? $oConfig->GetDBPwd() : $sDBPwd;
$this->InitMySQLSession($sDBHost, $sDBUser, $sDBPwd);
$this->InitMySQLSession();
}
public function __destruct()
@@ -82,7 +98,9 @@ class iTopMutex
}
/**
* Acquire the mutex
* Acquire the mutex. Uses a MySQL lock. <b>Warn</b> : can have an abnormal behavior on MySQL clusters (see R-016204)
*
* @see https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_get-lock
*/
public function Lock()
{
@@ -200,26 +218,26 @@ class iTopMutex
self::$aAcquiredLocks[$this->sName]--;
}
public function InitMySQLSession($sHost, $sUser, $sPwd)
/**
* Initialize database connection. Mandatory attributes must be already set !
*
* @throws \Exception
* @throws \MySQLException
*/
public function InitMySQLSession()
{
$aConnectInfo = explode(':', $sHost);
if (count($aConnectInfo) > 1)
{
// Override the default port
$sServer = $aConnectInfo[0];
$iPort = $aConnectInfo[1];
$this->hDBLink = @mysqli_connect($sServer, $sUser, $sPwd, '', $iPort);
}
else
{
$this->hDBLink = @mysqli_connect($sHost, $sUser, $sPwd);
}
$sServer = $this->sDBHost;
$sUser = $this->sDBUser;
$sPwd = $this->sDBPwd;
$sSource = $this->sDBName;
$bTlsEnabled = $this->bDBTlsEnabled;
$sTlsCA = $this->sDBTlsCA;
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
if (!$this->hDBLink)
{
throw new Exception("Could not connect to the DB server (host=$sHost, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
}
}

View File

@@ -20,13 +20,13 @@
* echo "Ok, '$sOQL' is a valid query";
* }
*/
if (!class_exists('CoreException', false))
{
class CoreException extends Exception
{
}
}
//if (!class_exists('CoreException', false))
//{
// class CoreException extends Exception
// {
//
// }
//}
require_once(__DIR__.'/expression.class.inc.php');
require_once(__DIR__.'/oqlquery.class.inc.php');

File diff suppressed because it is too large Load Diff

View File

@@ -115,6 +115,8 @@ class OQLLexerRaw
'/\GWHERE/ ',
'/\GJOIN/ ',
'/\GON/ ',
'/\G->/ ',
'/\G:/ ',
'/\G\// ',
'/\G\\*/ ',
'/\G\\+/ ',
@@ -316,329 +318,339 @@ class OQLLexerRaw
function yy_r1_8($yy_subpatterns)
{
$this->token = OQLParser::MATH_DIV;
$this->token = OQLParser::ARROW;
}
function yy_r1_9($yy_subpatterns)
{
$this->token = OQLParser::MATH_MULT;
$this->token = OQLParser::COLON;
}
function yy_r1_10($yy_subpatterns)
{
$this->token = OQLParser::MATH_PLUS;
$this->token = OQLParser::MATH_DIV;
}
function yy_r1_11($yy_subpatterns)
{
$this->token = OQLParser::MATH_MINUS;
$this->token = OQLParser::MATH_MULT;
}
function yy_r1_12($yy_subpatterns)
{
$this->token = OQLParser::LOG_AND;
$this->token = OQLParser::MATH_PLUS;
}
function yy_r1_13($yy_subpatterns)
{
$this->token = OQLParser::LOG_OR;
$this->token = OQLParser::MATH_MINUS;
}
function yy_r1_14($yy_subpatterns)
{
$this->token = OQLParser::BITWISE_OR;
$this->token = OQLParser::LOG_AND;
}
function yy_r1_15($yy_subpatterns)
{
$this->token = OQLParser::BITWISE_AND;
$this->token = OQLParser::LOG_OR;
}
function yy_r1_16($yy_subpatterns)
{
$this->token = OQLParser::BITWISE_XOR;
$this->token = OQLParser::BITWISE_OR;
}
function yy_r1_17($yy_subpatterns)
{
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
$this->token = OQLParser::BITWISE_AND;
}
function yy_r1_18($yy_subpatterns)
{
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
$this->token = OQLParser::BITWISE_XOR;
}
function yy_r1_19($yy_subpatterns)
{
$this->token = OQLParser::COMA;
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
}
function yy_r1_20($yy_subpatterns)
{
$this->token = OQLParser::PAR_OPEN;
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
}
function yy_r1_21($yy_subpatterns)
{
$this->token = OQLParser::PAR_CLOSE;
$this->token = OQLParser::COMA;
}
function yy_r1_22($yy_subpatterns)
{
$this->token = OQLParser::REGEXP;
$this->token = OQLParser::PAR_OPEN;
}
function yy_r1_23($yy_subpatterns)
{
$this->token = OQLParser::EQ;
$this->token = OQLParser::PAR_CLOSE;
}
function yy_r1_24($yy_subpatterns)
{
$this->token = OQLParser::NOT_EQ;
$this->token = OQLParser::REGEXP;
}
function yy_r1_25($yy_subpatterns)
{
$this->token = OQLParser::GT;
$this->token = OQLParser::EQ;
}
function yy_r1_26($yy_subpatterns)
{
$this->token = OQLParser::LT;
$this->token = OQLParser::NOT_EQ;
}
function yy_r1_27($yy_subpatterns)
{
$this->token = OQLParser::GE;
$this->token = OQLParser::GT;
}
function yy_r1_28($yy_subpatterns)
{
$this->token = OQLParser::LE;
$this->token = OQLParser::LT;
}
function yy_r1_29($yy_subpatterns)
{
$this->token = OQLParser::LIKE;
$this->token = OQLParser::GE;
}
function yy_r1_30($yy_subpatterns)
{
$this->token = OQLParser::NOT_LIKE;
$this->token = OQLParser::LE;
}
function yy_r1_31($yy_subpatterns)
{
$this->token = OQLParser::IN;
$this->token = OQLParser::LIKE;
}
function yy_r1_32($yy_subpatterns)
{
$this->token = OQLParser::NOT_IN;
$this->token = OQLParser::NOT_LIKE;
}
function yy_r1_33($yy_subpatterns)
{
$this->token = OQLParser::INTERVAL;
$this->token = OQLParser::IN;
}
function yy_r1_34($yy_subpatterns)
{
$this->token = OQLParser::F_IF;
$this->token = OQLParser::NOT_IN;
}
function yy_r1_35($yy_subpatterns)
{
$this->token = OQLParser::F_ELT;
$this->token = OQLParser::INTERVAL;
}
function yy_r1_36($yy_subpatterns)
{
$this->token = OQLParser::F_COALESCE;
$this->token = OQLParser::F_IF;
}
function yy_r1_37($yy_subpatterns)
{
$this->token = OQLParser::F_ISNULL;
$this->token = OQLParser::F_ELT;
}
function yy_r1_38($yy_subpatterns)
{
$this->token = OQLParser::F_CONCAT;
$this->token = OQLParser::F_COALESCE;
}
function yy_r1_39($yy_subpatterns)
{
$this->token = OQLParser::F_SUBSTR;
$this->token = OQLParser::F_ISNULL;
}
function yy_r1_40($yy_subpatterns)
{
$this->token = OQLParser::F_TRIM;
$this->token = OQLParser::F_CONCAT;
}
function yy_r1_41($yy_subpatterns)
{
$this->token = OQLParser::F_DATE;
$this->token = OQLParser::F_SUBSTR;
}
function yy_r1_42($yy_subpatterns)
{
$this->token = OQLParser::F_DATE_FORMAT;
$this->token = OQLParser::F_TRIM;
}
function yy_r1_43($yy_subpatterns)
{
$this->token = OQLParser::F_CURRENT_DATE;
$this->token = OQLParser::F_DATE;
}
function yy_r1_44($yy_subpatterns)
{
$this->token = OQLParser::F_NOW;
$this->token = OQLParser::F_DATE_FORMAT;
}
function yy_r1_45($yy_subpatterns)
{
$this->token = OQLParser::F_TIME;
$this->token = OQLParser::F_CURRENT_DATE;
}
function yy_r1_46($yy_subpatterns)
{
$this->token = OQLParser::F_TO_DAYS;
$this->token = OQLParser::F_NOW;
}
function yy_r1_47($yy_subpatterns)
{
$this->token = OQLParser::F_FROM_DAYS;
$this->token = OQLParser::F_TIME;
}
function yy_r1_48($yy_subpatterns)
{
$this->token = OQLParser::F_YEAR;
$this->token = OQLParser::F_TO_DAYS;
}
function yy_r1_49($yy_subpatterns)
{
$this->token = OQLParser::F_MONTH;
$this->token = OQLParser::F_FROM_DAYS;
}
function yy_r1_50($yy_subpatterns)
{
$this->token = OQLParser::F_DAY;
$this->token = OQLParser::F_YEAR;
}
function yy_r1_51($yy_subpatterns)
{
$this->token = OQLParser::F_HOUR;
$this->token = OQLParser::F_MONTH;
}
function yy_r1_52($yy_subpatterns)
{
$this->token = OQLParser::F_MINUTE;
$this->token = OQLParser::F_DAY;
}
function yy_r1_53($yy_subpatterns)
{
$this->token = OQLParser::F_SECOND;
$this->token = OQLParser::F_HOUR;
}
function yy_r1_54($yy_subpatterns)
{
$this->token = OQLParser::F_DATE_ADD;
$this->token = OQLParser::F_MINUTE;
}
function yy_r1_55($yy_subpatterns)
{
$this->token = OQLParser::F_DATE_SUB;
$this->token = OQLParser::F_SECOND;
}
function yy_r1_56($yy_subpatterns)
{
$this->token = OQLParser::F_ROUND;
$this->token = OQLParser::F_DATE_ADD;
}
function yy_r1_57($yy_subpatterns)
{
$this->token = OQLParser::F_FLOOR;
$this->token = OQLParser::F_DATE_SUB;
}
function yy_r1_58($yy_subpatterns)
{
$this->token = OQLParser::F_INET_ATON;
$this->token = OQLParser::F_ROUND;
}
function yy_r1_59($yy_subpatterns)
{
$this->token = OQLParser::F_INET_NTOA;
$this->token = OQLParser::F_FLOOR;
}
function yy_r1_60($yy_subpatterns)
{
$this->token = OQLParser::BELOW;
$this->token = OQLParser::F_INET_ATON;
}
function yy_r1_61($yy_subpatterns)
{
$this->token = OQLParser::BELOW_STRICT;
$this->token = OQLParser::F_INET_NTOA;
}
function yy_r1_62($yy_subpatterns)
{
$this->token = OQLParser::NOT_BELOW;
$this->token = OQLParser::BELOW;
}
function yy_r1_63($yy_subpatterns)
{
$this->token = OQLParser::NOT_BELOW_STRICT;
$this->token = OQLParser::BELOW_STRICT;
}
function yy_r1_64($yy_subpatterns)
{
$this->token = OQLParser::ABOVE;
$this->token = OQLParser::NOT_BELOW;
}
function yy_r1_65($yy_subpatterns)
{
$this->token = OQLParser::ABOVE_STRICT;
$this->token = OQLParser::NOT_BELOW_STRICT;
}
function yy_r1_66($yy_subpatterns)
{
$this->token = OQLParser::NOT_ABOVE;
$this->token = OQLParser::ABOVE;
}
function yy_r1_67($yy_subpatterns)
{
$this->token = OQLParser::NOT_ABOVE_STRICT;
$this->token = OQLParser::ABOVE_STRICT;
}
function yy_r1_68($yy_subpatterns)
{
$this->token = OQLParser::HEXVAL;
$this->token = OQLParser::NOT_ABOVE;
}
function yy_r1_69($yy_subpatterns)
{
$this->token = OQLParser::NUMVAL;
$this->token = OQLParser::NOT_ABOVE_STRICT;
}
function yy_r1_70($yy_subpatterns)
{
$this->token = OQLParser::STRVAL;
$this->token = OQLParser::HEXVAL;
}
function yy_r1_71($yy_subpatterns)
{
$this->token = OQLParser::NAME;
$this->token = OQLParser::NUMVAL;
}
function yy_r1_72($yy_subpatterns)
{
$this->token = OQLParser::VARNAME;
$this->token = OQLParser::STRVAL;
}
function yy_r1_73($yy_subpatterns)
{
$this->token = OQLParser::NAME;
}
function yy_r1_74($yy_subpatterns)
{
$this->token = OQLParser::VARNAME;
}
function yy_r1_75($yy_subpatterns)
{
$this->token = OQLParser::DOT;

View File

@@ -88,6 +88,8 @@ where = "WHERE"
join = "JOIN"
on = "ON"
coma = ","
arrow = "->"
colon = ":"
par_open = "("
par_close = ")"
math_div = "/"
@@ -170,7 +172,7 @@ numval = /([0-9]+)/
strval = /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
name = /([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/
varname = /:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/
dot = "."
dot = "."
*/
/*!lex2php
@@ -198,6 +200,12 @@ join {
on {
$this->token = OQLParser::ON;
}
arrow {
$this->token = OQLParser::ARROW;
}
colon {
$this->token = OQLParser::COLON;
}
math_div {
$this->token = OQLParser::MATH_DIV;
}
@@ -396,6 +404,7 @@ varname {
dot {
$this->token = OQLParser::DOT;
}
*/
}

File diff suppressed because it is too large Load Diff

View File

@@ -154,8 +154,16 @@ scalar(A) ::= str_scalar(X). { A = X; }
num_scalar(A) ::= num_value(X). { A = new ScalarOqlExpression(X); }
str_scalar(A) ::= str_value(X). { A = new ScalarOqlExpression(X); }
field_id(A) ::= name(X). { A = new FieldOqlExpression(X); }
field_id(A) ::= class_name(X) DOT name(Y). { A = new FieldOqlExpression(Y, X); }
basic_field_id(A) ::= name(X). { A = new FieldOqlExpression(X); }
basic_field_id(A) ::= class_name(X) DOT name(Y). { A = new FieldOqlExpression(Y, X); }
field_id(A) ::= basic_field_id(X). { A = X; }
field_id(A) ::= field_id(X) ARROW name(Y).
{
$expr = new FieldOqlExpression(Y);
A = new ExternalFieldOqlExpression(X, $expr);
}
class_name(A) ::= name(X). { A=X; }

View File

@@ -85,6 +85,7 @@ class OqlInterpreter
/**
* @return OqlQuery
* @throws \OQLException
*/
public function ParseQuery()
{

View File

@@ -168,9 +168,108 @@ class ScalarOqlExpression extends ScalarExpression implements CheckableExpressio
}
}
class ExternalFieldOqlExpression extends ExternalFieldExpression implements CheckableExpression
{
protected $m_aExpression = array();
protected $m_sName = null;
function __construct($oExpr1, $oExpr2)
{
$this->m_sName = '';
if ($oExpr1 instanceof ExternalFieldOqlExpression)
{
$this->m_aExpression = $oExpr1->GetExpressions();
}
else
{
$this->m_aExpression[] = $oExpr1;
}
$this->m_aExpression[] = $oExpr2;
$this->m_sName = $oExpr1->GetValue().'->'.$oExpr2->GetValue();
parent::__construct($this->m_sName, $this->m_aExpression);
}
public function GetExpressions()
{
return $this->m_aExpression;
}
public function GetName()
{
return $this->m_sName;
}
/**
* Check the validity of the expression with regard to the data model
* and the query in which it is used
*
* @param ModelReflection $oModelReflection MetaModel to consider
* @param array $aAliases Aliases to class names (for the current query)
* @param string $sSourceQuery For the reporting
*
* @throws OqlNormalizeException
*/
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
{
$sParentAlias = null;
foreach($this->m_aExpression as $i => $oFieldOqlExpression)
{
if (is_null($sParentAlias))
{
$oFieldOqlExpression->RefreshAlias($oModelReflection, $aAliases, $sSourceQuery, FieldOqlExpression::ALLOW_EXTERNAL_FIELDS);
}
else
{
$oFieldOqlExpression->SetParent($sParentAlias);
}
$oFieldOqlExpression->Check($oModelReflection, $aAliases, $sSourceQuery);
$sClass = $aAliases[$oFieldOqlExpression->GetParent()];
$bLastIteration = ($i == (count($this->m_aExpression) - 1));
if (!$bLastIteration)
{
if ($oFieldOqlExpression->GetName() == 'id')
{
$sTargetClass = null;
}
else
{
$sTargetClass = $oModelReflection->GetAttributeProperty($sClass, $oFieldOqlExpression->GetName(), 'targetclass');
}
if (is_null($sTargetClass))
{
throw new OqlNormalizeException('Forbidden operation for attribute', $sSourceQuery, $oFieldOqlExpression->GetNameDetails(), $oModelReflection->GetFiltersList($sClass));
}
$aAliases[$sTargetClass] = $sTargetClass;
$sParentAlias = $sTargetClass;
}
else
{
if ($oFieldOqlExpression->GetName() != 'id') //on lastIteration, the id field can be used, but since it is not supporter by IsValidAttCode we must avoid it
{
if (!$oModelReflection->IsValidAttCode($sClass, $oFieldOqlExpression->GetName()))
{
throw new OqlNormalizeException('Invalid attribute', $sSourceQuery, $oFieldOqlExpression->GetNameDetails(), $oModelReflection->GetFiltersList($sClass));
}
}
}
}
}
}
class FieldOqlExpression extends FieldExpression implements CheckableExpression
{
protected $m_oParent;
const ALLOW_EXTERNAL_FIELDS = true;
const DISALLOW_EXTERNAL_FIELDS = false;
protected $m_oParent;
protected $m_oName;
public function __construct($oName, $oParent = null)
@@ -203,23 +302,7 @@ class FieldOqlExpression extends FieldExpression implements CheckableExpression
{
// Try to find an alias
// Build an array of field => array of aliases
$aFieldClasses = array();
foreach($aAliases as $sAlias => $sReal)
{
foreach($oModelReflection->GetFiltersList($sReal) as $sAnFltCode)
{
$aFieldClasses[$sAnFltCode][] = $sAlias;
}
}
if (!array_key_exists($sFltCode, $aFieldClasses))
{
throw new OqlNormalizeException('Unknown filter code', $sSourceQuery, $this->GetNameDetails(), array_keys($aFieldClasses));
}
if (count($aFieldClasses[$sFltCode]) > 1)
{
throw new OqlNormalizeException('Ambiguous filter code', $sSourceQuery, $this->GetNameDetails());
}
$sClassAlias = $aFieldClasses[$sFltCode][0];
$this->RefreshAlias($oModelReflection, $aAliases, $sSourceQuery);
}
else
{
@@ -234,6 +317,62 @@ class FieldOqlExpression extends FieldExpression implements CheckableExpression
}
}
}
/**
* Used by self::Check and ExternalFieldOqlExpression::Check => throws exception if error
*
* this method has a side effect : if not previously set $this->m_sParent if computed then set.
*
* @param \ModelReflection $oModelReflection
* @param $aAliases
* @param $sSourceQuery
* @param bool $bAllowExternalFields should external fields be authorised? used only by @see ExternalFieldOqlExpression
*
* @throws OqlNormalizeException
*/
public function RefreshAlias(ModelReflection $oModelReflection, $aAliases, $sSourceQuery, $bAllowExternalFields = self::DISALLOW_EXTERNAL_FIELDS)
{
$sClassAlias = $this->GetParent();
$sFltCode = $this->GetName();
if (empty($sClassAlias))
{
// Try to find an alias
// Build an array of field => array of aliases
$aFieldClasses = array();
foreach($aAliases as $sAlias => $sReal)
{
foreach($oModelReflection->GetFiltersList($sReal) as $sAnFltCode)
{
$aFieldClasses[$sAnFltCode][] = $sAlias;
}
}
if (!array_key_exists($sFltCode, $aFieldClasses))
{
if (count($aAliases) > 1)
{
throw new OqlNormalizeException('Ambiguous filter code', $sSourceQuery, $this->GetNameDetails(), array_keys($aFieldClasses));
}
$sClassAlias = reset($aAliases);
if (self::DISALLOW_EXTERNAL_FIELDS == $bAllowExternalFields || false == $oModelReflection->IsValidAttCode($sClassAlias, $sFltCode))
{
throw new OqlNormalizeException('Unknown filter code', $sSourceQuery, $this->GetNameDetails(), array_keys($aFieldClasses));
}
$this->SetParent($sClassAlias);
}
elseif (count($aFieldClasses[$sFltCode]) > 1)
{
throw new OqlNormalizeException('Ambiguous filter code', $sSourceQuery, $this->GetNameDetails());
}
else
{
$sClassAlias = $aFieldClasses[$sFltCode][0];
$this->SetParent($sClassAlias);
}
}
}
}
class VariableOqlExpression extends VariableExpression implements CheckableExpression
@@ -283,10 +422,10 @@ abstract class OqlQuery
/**
* Check the validity of the expression with regard to the data model
* and the query in which it is used
*
* @param ModelReflection $oModelReflection MetaModel to consider
* @throws OqlNormalizeException
*/
*
* @param ModelReflection $oModelReflection MetaModel to consider
* @param string $sSourceQuery
*/
abstract public function Check(ModelReflection $oModelReflection, $sSourceQuery);
/**

View File

@@ -1 +1 @@
2015-08-31
2018-04-26

View File

@@ -74,14 +74,6 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
*/
protected $iCursor = 0;
/**
* __toString magical function overload.
*/
public function __toString()
{
return '';
}
/**
* ormLinkSet constructor.
* @param $sHostClass

View File

@@ -34,10 +34,10 @@ class QueryBuilderContext
public $m_oQBExpressions;
public function __construct($oFilter, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
public function __construct($oFilter, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
{
$this->m_oRootFilter = $oFilter;
$this->m_oQBExpressions = new QueryBuilderExpressions($oFilter, $aGroupByExpr);
$this->m_oQBExpressions = new QueryBuilderExpressions($oFilter, $aGroupByExpr, $aSelectExpr);
$this->m_aClassAliases = $oFilter->GetJoinedClasses();
$this->m_aTableAliases = array();

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2015-2017 Combodo SARL
// Copyright (C) 2015-2018 Combodo SARL
//
// This file is part of iTop.
//
@@ -15,10 +15,12 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Data structures (i.e. PHP classes) to build and use relation graphs
*
* @copyright Copyright (C) 2015-2017 Combodo SARL
* @copyright Copyright (C) 2015-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*
*/
@@ -38,8 +40,12 @@ class RelationObjectNode extends GraphNode
}
/**
* Make a normalized ID to ensure the uniqueness of such a node
*/
* Make a normalized ID to ensure the uniqueness of such a node
*
* @param string $oObject
*
* @return string
*/
public static function MakeId($oObject)
{
return get_class($oObject).'::'.$oObject->GetKey();
@@ -47,7 +53,11 @@ class RelationObjectNode extends GraphNode
/**
* Formatting for GraphViz
*/
*
* @param bool $bNoLabel
*
* @return string
*/
public function GetDotAttributes($bNoLabel = false)
{
$sDot = parent::GetDotAttributes();
@@ -76,7 +86,10 @@ class RelationObjectNode extends GraphNode
/**
* Recursively mark the objects nodes as reached, unless we get stopped by a redundancy node or a 'not allowed' node
*/
*
* @param string $sProperty
* @param $value
*/
public function ReachDown($sProperty, $value)
{
if (is_null($this->GetProperty($sProperty)) && ($this->GetProperty($sProperty.'_allowed') !== false))
@@ -104,8 +117,15 @@ class RelationRedundancyNode extends GraphNode
}
/**
* Make a normalized ID to ensure the uniqueness of such a node
*/
* Make a normalized ID to ensure the uniqueness of such a node
*
* @param string $sRelCode
* @param string $sNeighbourId
* @param $oSourceObject
* @param \DBObject $oSinkObject
*
* @return string
*/
public static function MakeId($sRelCode, $sNeighbourId, $oSourceObject, $oSinkObject)
{
return 'redundancy-'.$sRelCode.'-'.$sNeighbourId.'-'.get_class($oSinkObject).'::'.$oSinkObject->GetKey();
@@ -113,7 +133,11 @@ class RelationRedundancyNode extends GraphNode
/**
* Formatting for GraphViz
*/
*
* @param bool $bNoLabel
*
* @return string
*/
public function GetDotAttributes($bNoLabel = false)
{
$sDisplayThreshold = sprintf('%.1f', $this->GetProperty('threshold'));
@@ -123,7 +147,10 @@ class RelationRedundancyNode extends GraphNode
/**
* Recursively mark the objects nodes as reached, unless we get stopped by a redundancy node
*/
*
* @param string $sProperty
* @param $value
*/
public function ReachDown($sProperty, $value)
{
$this->SetProperty($sProperty.'_count', $this->GetProperty($sProperty.'_count', 0) + 1);
@@ -145,6 +172,16 @@ class RelationRedundancyNode extends GraphNode
*/
class RelationEdge extends GraphEdge
{
/**
* RelationEdge constructor.
*
* @param \SimpleGraph $oGraph
* @param \GraphNode $oSourceNode
* @param \GraphNode $oSinkNode
* @param bool $bMustBeUnique
*
* @throws \SimpleGraphException
*/
public function __construct(SimpleGraph $oGraph, GraphNode $oSourceNode, GraphNode $oSinkNode, $bMustBeUnique = false)
{
$sId = $oSourceNode->GetId().'-to-'.$oSinkNode->GetId();
@@ -182,7 +219,9 @@ class RelationGraph extends SimpleGraph
/**
* Add an object that will be the starting point for building the relations downstream
*/
*
* @param \DBObject $oObject
*/
public function AddSourceObject(DBObject $oObject)
{
$oSourceNode = new RelationObjectNode($this, $oObject);
@@ -192,22 +231,28 @@ class RelationGraph extends SimpleGraph
/**
* Add an object that will be the starting point for building the relations uptream
*/
*
* @param \DBObject $oObject
*/
public function AddSinkObject(DBObject$oObject)
{
$oSinkNode = new RelationObjectNode($this, $oObject);
$oSinkNode->SetProperty('sink', true);
$this->aSinkNodes[$oSinkNode->GetId()] = $oSinkNode;
}
/**
* Add a 'context' OQL query, specifying extra objects to be marked as 'is_reached'
* even though they are not part of the sources.
* @param string $sOQL The OQL query defining the context objects
*
* @param string $key
* @param string $sOQL The OQL query defining the context objects
*
* @throws \Exception
*/
public function AddContextQuery($key, $sOQL)
{
if ($sOQL === '') return;
if ($sOQL === '') { return;}
$oSearch = static::MakeSearch($sOQL);
$aAliases = $oSearch->GetSelectedClasses();
@@ -217,7 +262,6 @@ class RelationGraph extends SimpleGraph
throw new Exception("Invalid context query '$sOQL'. A context query must contain at least two columns. Columns: ".implode(', ', $aAliases).'. ');
}
$aAliasNames = array_keys($aAliases);
$sClassAlias = $oSearch->GetClassAlias();
$oCondition = new BinaryExpression(new FieldExpression('id', $aAliasNames[0]), '=', new VariableExpression('id'));
$oSearch->AddConditionExpression($oCondition);
@@ -228,11 +272,14 @@ class RelationGraph extends SimpleGraph
}
$this->aContextSearches[$sClass][] = array('key' => $key, 'search' => $oSearch);
}
/**
* Determines if the given DBObject is part of a 'context'
*
* @param DBObject $oObj
*
* @return boolean
* @throws \CoreException
*/
public function IsPartOfContext(DBObject $oObj, &$aRootCauses)
{
@@ -271,7 +318,15 @@ class RelationGraph extends SimpleGraph
/**
* Build the graph downstream, and mark the nodes that can be reached from the source node
*/
*
* @param string $sRelCode
* @param int $iMaxDepth
* @param bool $bEnableRedundancy
* @param array $aUnreachableObjects
*
* @throws \CoreException
* @throws \Exception
*/
public function ComputeRelatedObjectsDown($sRelCode, $iMaxDepth, $bEnableRedundancy, $aUnreachableObjects = array())
{
//echo "<h5>Sources only...</h5>\n".$this->DumpAsHtmlImage()."<br/>\n";
@@ -316,7 +371,14 @@ class RelationGraph extends SimpleGraph
/**
* Build the graph upstream
*/
*
* @param string $sRelCode
* @param int $iMaxDepth
* @param bool $bEnableRedundancy
*
* @throws \CoreException
* @throws \Exception
*/
public function ComputeRelatedObjectsUp($sRelCode, $iMaxDepth, $bEnableRedundancy)
{
//echo "<h5>Sinks only...</h5>\n".$this->DumpAsHtmlImage()."<br/>\n";
@@ -344,14 +406,14 @@ class RelationGraph extends SimpleGraph
/**
* Recursively find related objects, and add them into the graph
*
*
* @param string $sRelCode The code of the relation to use for the computation
* @param boolean $bDown The direction: downstream or upstream
* @param array $oObjectNode The node from which to compute the neighbours
* @param \GraphElement $oObjectNode The node from which to compute the neighbours
* @param int $iMaxDepth
* @param boolean $bEnableReduncancy
*
* @return void
* @param boolean $bEnableRedundancy
*
* @throws \Exception
*/
protected function AddRelatedObjects($sRelCode, $bDown, $oObjectNode, $iMaxDepth, $bEnableRedundancy)
{
@@ -359,7 +421,7 @@ class RelationGraph extends SimpleGraph
{
if ($oObjectNode instanceof RelationRedundancyNode)
{
// Note: this happens when recursing on an existing part of the graph
// Note: this happens when recursing on an existing part of the graph
// Skip that redundancy node
$aRelatedEdges = $bDown ? $oObjectNode->GetOutgoingEdges() : $oObjectNode->GetIncomingEdges();
foreach ($aRelatedEdges as $oRelatedEdge)
@@ -441,8 +503,16 @@ class RelationGraph extends SimpleGraph
}
/**
* Determine if there is a redundancy (or use the existing one) and add the corresponding nodes/edges
*/
* Determine if there is a redundancy (or use the existing one) and add the corresponding nodes/edges
*
* @param string $sRelCode
* @param array $aQueryInfo
* @param GraphElement $oFromNode
* @param GraphElement $oToNode
*
* @return \GraphNode|NULL|\RelationRedundancyNode
* @throws \Exception
*/
protected function ComputeRedundancy($sRelCode, $aQueryInfo, $oFromNode, $oToNode)
{
$oRedundancyNode = null;
@@ -493,8 +563,14 @@ class RelationGraph extends SimpleGraph
}
/**
* Helper to determine the redundancy setting on a given relation
*/
* Helper to determine the redundancy setting on a given relation
*
* @param string $sRelCode
* @param array $aQueryInfo
* @param GraphElement $oToNode
*
* @return bool
*/
protected function IsRedundancyEnabled($sRelCode, $aQueryInfo, $oToNode)
{
$bRet = false;
@@ -509,8 +585,15 @@ class RelationGraph extends SimpleGraph
}
/**
* Helper to determine the redundancy threshold, given the count of objects upstream
*/
* Helper to determine the redundancy threshold, given the count of objects upstream
*
* @param string $sRelCode
* @param array $aQueryInfo
* @param GraphElement $oToNode
* @param int $iUpstreamObjects
*
* @return int
*/
protected function GetRedundancyMinUp($sRelCode, $aQueryInfo, $oToNode, $iUpstreamObjects)
{
$iMinUp = 0;
@@ -533,8 +616,14 @@ class RelationGraph extends SimpleGraph
}
/**
* Helper to search for the redundancy attribute
*/
* Helper to search for the redundancy attribute
*
* @param string $sRelCode
* @param array $aQueryInfo
* @param string $sClass
*
* @return \AttributeDefinition|\AttributeRedundancySettings|null
*/
protected function FindRedundancyAttribute($sRelCode, $aQueryInfo, $sClass)
{
$oRet = null;
@@ -560,7 +649,7 @@ class RelationGraph extends SimpleGraph
/**
* Get the objects referenced by the graph as a hash array: 'class' => array of objects
* @return Ambigous <multitype:multitype: , unknown>
* @return array Ambigous <multitype:multitype: , unknown>
*/
public function GetObjectsByClass()
{
@@ -580,8 +669,15 @@ class RelationGraph extends SimpleGraph
}
}
return $aResults;
}
}
/**
* @param string $sOQL
*
* @return \DBSearch
* @throws \CoreException
* @throws \OQLException
*/
protected static function MakeSearch($sOQL)
{
$oSearch = DBSearch::FromOQL($sOQL);

View File

@@ -245,6 +245,12 @@ class SQLObjectQuery extends SQLQuery
}
// Interface, build the SQL query
/**
* @param array $aArgs
* @return string
* @throws CoreException
*/
public function RenderDelete($aArgs = array())
{
$this->PrepareRendering();
@@ -270,6 +276,7 @@ class SQLObjectQuery extends SQLQuery
$sWhere = self::ClauseWhere($this->m_oConditionExpr, $aArgs);
return "DELETE $sDelete FROM $sFrom WHERE $sWhere";
}
return '';
}
/**
@@ -283,16 +290,25 @@ class SQLObjectQuery extends SQLQuery
}
/**
* Needed for the unions
* Needed for the unions
* @param $aOrderBy
* @return string
* @throws CoreException
*/
public function RenderOrderByClause($aOrderBy)
{
$this->PrepareRendering();
$sOrderBy = self::ClauseOrderBy($aOrderBy);
$sOrderBy = self::ClauseOrderBy($aOrderBy, $this->__aFields);
return $sOrderBy;
}
// Interface, build the SQL query
/**
* @param array $aArgs
* @return string
* @throws CoreException
*/
public function RenderUpdate($aArgs = array())
{
$this->PrepareRendering();
@@ -303,6 +319,17 @@ class SQLObjectQuery extends SQLQuery
}
// Interface, build the SQL query
/**
* @param array $aOrderBy
* @param array $aArgs
* @param int $iLimitCount
* @param int $iLimitStart
* @param bool $bGetCount
* @param bool $bBeautifulQuery
* @return string
* @throws CoreException
*/
public function RenderSelect($aOrderBy = array(), $aArgs = array(), $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false, $bBeautifulQuery = false)
{
$this->m_bBeautifulQuery = $bBeautifulQuery;
@@ -312,14 +339,6 @@ 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)
@@ -330,42 +349,75 @@ class SQLObjectQuery extends SQLQuery
$aCountFields[] = "COALESCE($sFieldExpr, 0)"; // Null values are excluded from the count
}
$sCountFields = implode(', ', $aCountFields);
// 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_";
$sSQL = "SELECT$sLineSep COUNT(DISTINCT $sCountFields) AS COUNT$sLineSep FROM $sFrom$sLineSep WHERE $sWhere";
}
else
{
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep 1 $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _tatooine_";
$sSQL = "SELECT$sLineSep COUNT(*) AS COUNT$sLineSep FROM $sFrom$sLineSep WHERE $sWhere";
}
}
else
{
$sSelect = self::ClauseSelect($this->__aFields);
$sOrderBy = self::ClauseOrderBy($aOrderBy);
$sSelect = self::ClauseSelect($this->__aFields, $sLineSep);
$sOrderBy = self::ClauseOrderBy($aOrderBy, $this->__aFields);
if (!empty($sOrderBy))
{
$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;
}
// Interface, build the SQL query
public function RenderGroupBy($aArgs = array(), $bBeautifulQuery = false)
/**
* @param array $aArgs
* @param bool $bBeautifulQuery
* @param array $aOrderBy
* @param int $iLimitCount
* @param int $iLimitStart
* @return string
* @throws CoreException
*/
public function RenderGroupBy($aArgs = array(), $bBeautifulQuery = false, $aOrderBy = array(), $iLimitCount = 0, $iLimitStart = 0)
{
$this->m_bBeautifulQuery = $bBeautifulQuery;
$sLineSep = $this->m_bBeautifulQuery ? "\n" : '';
$sIndent = $this->m_bBeautifulQuery ? " " : null;
$this->PrepareRendering();
$sSelect = self::ClauseSelect($this->__aFields);
$sFrom = self::ClauseFrom($this->__aFrom, $sIndent);
$sWhere = self::ClauseWhere($this->m_oConditionExpr, $aArgs);
$sGroupBy = self::ClauseGroupBy($this->__aGroupBy);
$sSQL = "SELECT $sSelect,$sLineSep COUNT(*) AS _itop_count_$sLineSep FROM $sFrom$sLineSep WHERE $sWhere$sLineSep GROUP BY $sGroupBy";
$sOrderBy = self::ClauseOrderBy($aOrderBy, $this->__aFields);
if (!empty($sGroupBy))
{
$sGroupBy = "GROUP BY $sGroupBy$sLineSep";
}
if (!empty($sOrderBy))
{
$sOrderBy = "ORDER BY $sOrderBy$sLineSep";
}
if ($iLimitCount > 0)
{
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
}
else
{
$sLimit = '';
}
$sSQL = "SELECT $sSelect,$sLineSep COUNT(*) AS _itop_count_$sLineSep FROM $sFrom$sLineSep WHERE $sWhere$sLineSep $sGroupBy $sOrderBy$sLineSep $sLimit";
return $sSQL;
}
@@ -388,6 +440,7 @@ class SQLObjectQuery extends SQLQuery
private function PrepareSingleTable(SQLObjectQuery $oRootQuery, &$aFrom, $sCallerAlias = '', $aJoinData)
{
$aTranslationTable[$this->m_sTable]['*'] = $this->m_sTableAlias;
$sJoinCond = '';
// Handle the various kinds of join (or first table in the list)
//
@@ -505,7 +558,7 @@ class SQLObjectQuery extends SQLQuery
{
$oRightSelect = $aJoinData["select"];
$sJoinTableAlias = $oRightSelect->PrepareSingleTable($oRootQuery, $aTempFrom, $this->m_sTableAlias, $aJoinData);
$oRightSelect->PrepareSingleTable($oRootQuery, $aTempFrom, $this->m_sTableAlias, $aJoinData);
}
$aFrom[$this->m_sTableAlias]['subfrom'] = $aTempFrom;
@@ -581,7 +634,7 @@ class SQLObjectQuery extends SQLQuery
}
if (isset($aJoinInfo["on_expression"]))
{
$sJoinCond = $aJoinInfo["on_expression"]->CollectUsedParents($aTables);
$aJoinInfo["on_expression"]->CollectUsedParents($aTables);
}
}
}
@@ -609,7 +662,7 @@ class SQLObjectQuery extends SQLQuery
}
if (isset($aJoinInfo["on_expression"]))
{
$sJoinCond = $aJoinInfo["on_expression"]->CollectUsedParents($aTables);
$aJoinInfo["on_expression"]->CollectUsedParents($aTables);
}
$bResult = true;
}

View File

@@ -69,18 +69,18 @@ abstract class SQLQuery
abstract public function RenderDelete($aArgs = array());
abstract public function RenderUpdate($aArgs = array());
abstract public function RenderSelect($aOrderBy = array(), $aArgs = array(), $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false, $bBeautifulQuery = false);
abstract public function RenderGroupBy($aArgs = array(), $bBeautifulQuery = false);
abstract public function RenderGroupBy($aArgs = array(), $bBeautifulQuery = false, $aOrderBy = array(), $iLimitCount = 0, $iLimitStart = 0);
abstract public function OptimizeJoins($aUsedTables, $bTopCall = true);
protected static function ClauseSelect($aFields)
protected static function ClauseSelect($aFields, $sLineSep = '')
{
$aSelect = array();
foreach ($aFields as $sFieldAlias => $sSQLExpr)
{
$aSelect[] = "$sSQLExpr AS $sFieldAlias";
}
$sSelect = implode(', ', $aSelect);
$sSelect = implode(",$sLineSep ", $aSelect);
return $sSelect;
}
@@ -101,6 +101,13 @@ abstract class SQLQuery
return $sDelTables;
}
/**
* @param $aFrom
* @param null $sIndent
* @param int $iIndentLevel
* @return string
* @throws CoreException
*/
protected static function ClauseFrom($aFrom, $sIndent = null, $iIndentLevel = 0)
{
$sLineBreakLong = $sIndent ? "\n".str_repeat($sIndent, $iIndentLevel + 1) : '';
@@ -174,7 +181,13 @@ abstract class SQLQuery
}
}
protected static function ClauseOrderBy($aOrderBy)
/**
* @param array $aOrderBy
* @param array $aExistingFields
* @return string
* @throws CoreException
*/
protected static function ClauseOrderBy($aOrderBy, $aExistingFields)
{
$aOrderBySpec = array();
foreach($aOrderBy as $sFieldAlias => $bAscending)

View File

@@ -38,8 +38,9 @@ class SQLUnionQuery extends SQLQuery
{
protected $aQueries;
protected $aGroupBy;
protected $aSelectExpr;
public function __construct($aQueries, $aGroupBy)
public function __construct($aQueries, $aGroupBy, $aSelectExpr = array())
{
parent::__construct();
@@ -49,6 +50,7 @@ class SQLUnionQuery extends SQLQuery
$this->aQueries[] = $oSQLQuery->DeepClone();
}
$this->aGroupBy = $aGroupBy;
$this->aSelectExpr = $aSelectExpr;
}
public function DisplayHtml()
@@ -69,12 +71,21 @@ class SQLUnionQuery extends SQLQuery
}
}
/**
* @param array $aArgs
* @throws Exception
*/
public function RenderDelete($aArgs = array())
{
throw new Exception(__class__.'::'.__function__.'Not implemented !');
}
// Interface, build the SQL query
/**
* @param array $aArgs
* @throws Exception
*/
public function RenderUpdate($aArgs = array())
{
throw new Exception(__class__.'::'.__function__.'Not implemented !');
@@ -92,43 +103,48 @@ class SQLUnionQuery extends SQLQuery
// Render SELECTS without orderby/limit/count
$aSelects[] = $oSQLQuery->RenderSelect(array(), $aArgs, 0, 0, false, $bBeautifulQuery);
}
if ($iLimitCount > 0)
{
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
}
else
{
$sLimit = '';
}
$sSelects = '('.implode(")$sLineSep UNION$sLineSep(", $aSelects).')';
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_";
$sSQL = "SELECT$sLineSep COUNT(*) AS COUNT$sLineSep FROM $sFrom$sLineSep";
}
else
{
$sOrderBy = $this->aQueries[0]->RenderOrderByClause($aOrderBy);
if (!empty($sOrderBy))
{
$sOrderBy = "ORDER BY $sOrderBy$sLineSep $sLimit";
$sSQL = '('.implode(")$sLineSep UNION$sLineSep (", $aSelects).')'.$sLineSep.$sOrderBy;
$sOrderBy = "ORDER BY $sOrderBy$sLineSep";
}
if ($iLimitCount > 0)
{
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
}
else
{
$sSQL = '('.implode(" $sLimit)$sLineSep UNION$sLineSep (", $aSelects)." $sLimit)";
$sLimit = '';
}
$sSQL = $sSelects.$sLineSep.$sOrderBy.' '.$sLimit;
}
return $sSQL;
}
// Interface, build the SQL query
public function RenderGroupBy($aArgs = array(), $bBeautifulQuery = false)
/**
* @param array $aArgs
* @param bool $bBeautifulQuery
* @param array $aOrderBy
* @param int $iLimitCount
* @param int $iLimitStart
* @return string
* @throws CoreException
*/
public function RenderGroupBy($aArgs = array(), $bBeautifulQuery = false, $aOrderBy = array(), $iLimitCount = 0, $iLimitStart = 0)
{
$this->m_bBeautifulQuery = $bBeautifulQuery;
$sLineSep = $this->m_bBeautifulQuery ? "\n" : '';
$sIndent = $this->m_bBeautifulQuery ? " " : null;
$aSelects = array();
foreach ($this->aQueries as $oSQLQuery)
@@ -139,15 +155,41 @@ class SQLUnionQuery extends SQLQuery
$sSelects = '('.implode(")$sLineSep UNION$sLineSep(", $aSelects).')';
$sFrom = "($sLineSep$sSelects$sLineSep) as __selects__";
$aAliases = array();
$aSelectAliases = array();
$aGroupAliases = array();
foreach ($this->aGroupBy as $sGroupAlias => $trash)
{
$aAliases[] = "`$sGroupAlias`";
$aSelectAliases[$sGroupAlias] = "`$sGroupAlias`";
$aGroupAliases[] = "`$sGroupAlias`";
}
foreach($this->aSelectExpr as $sSelectAlias => $oExpr)
{
$aSelectAliases[$sSelectAlias] = $oExpr->Render()." AS `$sSelectAlias`";
}
$sSelect = implode(', ', $aAliases);
$sGroupBy = implode(', ', $aAliases);
$sSQL = "SELECT $sSelect,$sLineSep COUNT(*) AS _itop_count_$sLineSep FROM $sFrom$sLineSep GROUP BY $sGroupBy";
$sSelect = implode(",$sLineSep ", $aSelectAliases);
$sGroupBy = implode(', ', $aGroupAliases);
$sOrderBy = self::ClauseOrderBy($aOrderBy, $aSelectAliases);
if (!empty($sGroupBy))
{
$sGroupBy = "GROUP BY $sGroupBy$sLineSep";
}
if (!empty($sOrderBy))
{
$sOrderBy = "ORDER BY $sOrderBy$sLineSep";
}
if ($iLimitCount > 0)
{
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
}
else
{
$sLimit = '';
}
$sSQL = "SELECT $sSelect,$sLineSep COUNT(*) AS _itop_count_$sLineSep FROM $sFrom$sLineSep $sGroupBy $sOrderBy$sLineSep $sLimit";
return $sSQL;
}

View File

@@ -188,7 +188,7 @@ abstract class TabularBulkExport extends BulkExport
$aAuthorizedClasses = array();
foreach($aSelectedClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_BULK_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_BULK_READ, $oSet) != UR_ALLOWED_NO)
{
$aAuthorizedClasses[$sAlias] = $sClassName;
}

View File

@@ -38,7 +38,7 @@ abstract class Trigger extends cmdbAbstractObject
{
$aParams = array
(
"category" => "core/cmdb",
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "description",
"state_attcode" => "",
@@ -97,7 +97,7 @@ abstract class TriggerOnObject extends Trigger
{
$aParams = array
(
"category" => "core/cmdb",
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "description",
"state_attcode" => "",
@@ -116,8 +116,8 @@ abstract class TriggerOnObject extends Trigger
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'filter', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'description')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
MetaModel::Init_SetZListItems('default_search', array('description', 'target_class')); // Default criteria of the search banner
// MetaModel::Init_SetZListItems('standard_search', array('name', 'target_class', 'description')); // Criteria of the search form
}
public function DoCheckToWrite()
@@ -194,7 +194,7 @@ class TriggerOnPortalUpdate extends TriggerOnObject
{
$aParams = array
(
"category" => "core/cmdb,application",
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"state_attcode" => "",
@@ -220,7 +220,7 @@ abstract class TriggerOnStateChange extends TriggerOnObject
{
$aParams = array
(
"category" => "core/cmdb",
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "description",
"state_attcode" => "",
@@ -249,7 +249,7 @@ class TriggerOnStateEnter extends TriggerOnStateChange
{
$aParams = array
(
"category" => "core/cmdb,application",
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"state_attcode" => "",
@@ -277,7 +277,7 @@ class TriggerOnStateLeave extends TriggerOnStateChange
{
$aParams = array
(
"category" => "core/cmdb,application",
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"state_attcode" => "",
@@ -305,7 +305,7 @@ class TriggerOnObjectCreate extends TriggerOnObject
{
$aParams = array
(
"category" => "core/cmdb,application",
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"state_attcode" => "",
@@ -333,7 +333,7 @@ class lnkTriggerAction extends cmdbAbstractObject
{
$aParams = array
(
"category" => "core/cmdb,application",
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -366,7 +366,7 @@ class TriggerOnThresholdReached extends TriggerOnObject
{
$aParams = array
(
"category" => "core/cmdb,application",
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "description",
"state_attcode" => "",

View File

@@ -168,7 +168,7 @@ abstract class User extends cmdbAbstractObject
{
$aParams = array
(
"category" => "core",
"category" => "core,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",
@@ -185,6 +185,7 @@ abstract class User extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeExternalField("last_name", array("allowed_values"=>null, "extkey_attcode"=> 'contactid', "target_attcode"=>"name")));
MetaModel::Init_AddAttribute(new AttributeExternalField("first_name", array("allowed_values"=>null, "extkey_attcode"=> 'contactid', "target_attcode"=>"first_name")));
MetaModel::Init_AddAttribute(new AttributeExternalField("email", array("allowed_values"=>null, "extkey_attcode"=> 'contactid', "target_attcode"=>"email")));
MetaModel::Init_AddAttribute(new AttributeExternalField("org_id", array("allowed_values"=>null, "extkey_attcode"=> 'contactid', "target_attcode"=>"org_id")));
MetaModel::Init_AddAttribute(new AttributeString("login", array("allowed_values"=>null, "sql"=>"login", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
@@ -195,11 +196,11 @@ abstract class User extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("allowed_org_list", array("linked_class"=>"URP_UserOrg", "ext_key_to_me"=>"userid", "ext_key_to_remote"=>"allowed_org_id", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'login', 'status')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list')); // Unused as it's an abstract class !
MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'status', 'org_id')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status')); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', array('login', 'contactid')); // Criteria of the advanced search form
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'email', 'language', 'status', 'org_id')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('login', 'contactid', 'org_id')); // Default criteria of the search banner
}
abstract public function CheckCredentials($sPassword);
@@ -240,7 +241,7 @@ abstract class User extends cmdbAbstractObject
protected $oContactObject;
/**
* Fetch and memoize the associated contact (if any)
* Fetch and memorize the associated contact (if any)
*/
public function GetContactObject()
{
@@ -254,9 +255,11 @@ abstract class User extends cmdbAbstractObject
return $this->oContactObject;
}
/*
* Overload the standard behavior
*/
/**
* Overload the standard behavior.
*
* @throws \CoreException
*/
public function DoCheckToWrite()
{
parent::DoCheckToWrite();
@@ -286,6 +289,40 @@ abstract class User extends cmdbAbstractObject
{
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:AtLeastOneProfileIsNeeded');
}
// Only administrators can manage administrators
if (UserRights::IsAdministrator($this) && !UserRights::IsAdministrator())
{
$this->m_aCheckIssues[] = Dict::Format('UI:Login:Error:AccessRestricted');
}
// Check users with restricted organizations
if (!UserRights::IsAdministrator())
{
$oUser = UserRights::GetUserObject();
$oAddon = UserRights::GetModuleInstance();
if (!is_null($oUser) && method_exists($oAddon, 'GetUserOrgs'))
{
$aOrgs = $oAddon->GetUserOrgs($oUser, '');
if (count($aOrgs) > 0)
{
/** @var ORMLinkset $oSet */
$oSet = $this->Get('allowed_org_list');
if ($oSet->Count() == 0)
{
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:AtLeastOneOrganizationIsNeeded');
}
else
{
while ($oUserOrg = $oSet->Fetch())
{
if (!in_array($oUserOrg->Get('allowed_org_id'), $aOrgs))
{
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:OrganizationNotAllowed');
}
}
}
}
}
}
}
function GetGrantAsHtml($sClass, $iAction)
@@ -364,8 +401,8 @@ abstract class User extends cmdbAbstractObject
if (!$bEditMode)
{
$oPage->SetCurrentTab(Dict::S('UI:UserManagement:GrantMatrix'));
$this->DoShowGrantSumary($oPage, 'bizmodel');
$this->DoShowGrantSumary($oPage, 'bizmodel,grant_by_profile');
// debug
if (false)
{
@@ -422,7 +459,7 @@ abstract class UserInternal extends User
{
$aParams = array
(
"category" => "core",
"category" => "core,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",
@@ -438,11 +475,10 @@ abstract class UserInternal extends User
MetaModel::Init_AddAttribute(new AttributeOneWayPassword("reset_pwd_token", array("allowed_values"=>null, "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'status', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'login', 'status')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'status', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'status', 'org_id')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status')); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', array('login', 'contactid')); // Criteria of the advanced search form
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status', 'org_id')); // Criteria of the std search form
}
/**
@@ -523,6 +559,7 @@ interface iSelfRegister
*/
class UserRights
{
/** @var UserRightsAddOnAPI $m_oAddOn */
protected static $m_oAddOn;
protected static $m_oUser;
protected static $m_oRealUser;
@@ -568,7 +605,7 @@ class UserRights
return $bRes;
}
protected static function IsLoggedIn()
public static function IsLoggedIn()
{
if (self::$m_oUser == null)
{
@@ -710,6 +747,7 @@ class UserRights
}
else
{
$oUser->AllowWrite(true);
return $oUser->ChangePassword($sOldPassword, $sNewPassword);
}
}
@@ -922,35 +960,55 @@ class UserRights
return true;
}
/**
* @param $sClass
* @param array $aSettings
*
* @return bool
*/
public static function GetSelectFilter($sClass, $aSettings = array())
{
// When initializing, we need to let everything pass trough
if (!self::CheckLogin()) return true;
if (!self::CheckLogin()) {return true;}
if (self::IsAdministrator()) return true;
if (self::IsAdministrator()) {return true;}
if (MetaModel::HasCategory($sClass, 'bizmodel'))
try
{
return self::$m_oAddOn->GetSelectFilter(self::$m_oUser, $sClass, $aSettings);
}
else
// Check only bizmodel to let API work like in 2.4.1
if (MetaModel::HasCategory($sClass, 'bizmodel'))
{
return self::$m_oAddOn->GetSelectFilter(self::$m_oUser, $sClass, $aSettings);
}
else
{
return true;
}
} catch (Exception $e)
{
return true;
return false;
}
}
public static function IsActionAllowed($sClass, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null, $oUser = null)
/**
* @param string $sClass
* @param int $iActionCode
* @param DBObjectSet $oInstanceSet
* @param User $oUser
* @return int (UR_ALLOWED_YES|UR_ALLOWED_NO|UR_ALLOWED_DEPENDS)
*/
public static function IsActionAllowed($sClass, $iActionCode, /*dbObjectSet*/$oInstanceSet = null, $oUser = null)
{
// When initializing, we need to let everything pass trough
if (!self::CheckLogin()) return true;
if (!self::CheckLogin()) return UR_ALLOWED_YES;
if (MetaModel::DBIsReadOnly())
{
if ($iActionCode == UR_ACTION_CREATE) return false;
if ($iActionCode == UR_ACTION_MODIFY) return false;
if ($iActionCode == UR_ACTION_BULK_MODIFY) return false;
if ($iActionCode == UR_ACTION_DELETE) return false;
if ($iActionCode == UR_ACTION_BULK_DELETE) return false;
if ($iActionCode == UR_ACTION_CREATE) return UR_ALLOWED_NO;
if ($iActionCode == UR_ACTION_MODIFY) return UR_ALLOWED_NO;
if ($iActionCode == UR_ACTION_BULK_MODIFY) return UR_ALLOWED_NO;
if ($iActionCode == UR_ACTION_DELETE) return UR_ALLOWED_NO;
if ($iActionCode == UR_ACTION_BULK_DELETE) return UR_ALLOWED_NO;
}
$aPredefinedObjects = call_user_func(array($sClass, 'GetPredefinedObjects'));
@@ -959,14 +1017,14 @@ class UserRights
// As opposed to the read-only DB, modifying an object is allowed
// (the constant columns will be marked as read-only)
//
if ($iActionCode == UR_ACTION_CREATE) return false;
if ($iActionCode == UR_ACTION_DELETE) return false;
if ($iActionCode == UR_ACTION_BULK_DELETE) return false;
if ($iActionCode == UR_ACTION_CREATE) return UR_ALLOWED_NO;
if ($iActionCode == UR_ACTION_DELETE) return UR_ALLOWED_NO;
if ($iActionCode == UR_ACTION_BULK_DELETE) return UR_ALLOWED_NO;
}
if (self::IsAdministrator($oUser)) return true;
if (self::IsAdministrator($oUser)) return UR_ALLOWED_YES;
if (MetaModel::HasCategory($sClass, 'bizmodel'))
if (MetaModel::HasCategory($sClass, 'bizmodel') || MetaModel::HasCategory($sClass, 'grant_by_profile'))
{
if (is_null($oUser))
{
@@ -982,12 +1040,12 @@ class UserRights
}
elseif(($iActionCode == UR_ACTION_READ) && MetaModel::HasCategory($sClass, 'view_in_gui'))
{
return true;
return UR_ALLOWED_YES;
}
else
{
// Other classes could be edited/listed by the administrators
return false;
return UR_ALLOWED_NO;
}
}
@@ -1018,32 +1076,45 @@ class UserRights
}
}
public static function IsActionAllowedOnAttribute($sClass, $sAttCode, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null, $oUser = null)
/**
* @param string $sClass
* @param string $sAttCode
* @param int $iActionCode
* @param DBObjectSet $oInstanceSet
* @param User $oUser
* @return int (UR_ALLOWED_YES|UR_ALLOWED_NO)
*/
public static function IsActionAllowedOnAttribute($sClass, $sAttCode, $iActionCode, /*dbObjectSet*/$oInstanceSet = null, $oUser = null)
{
// When initializing, we need to let everything pass trough
if (!self::CheckLogin()) return true;
if (!self::CheckLogin()) return UR_ALLOWED_YES;
if (MetaModel::DBIsReadOnly())
{
if ($iActionCode == UR_ACTION_MODIFY) return false;
if ($iActionCode == UR_ACTION_DELETE) return false;
if ($iActionCode == UR_ACTION_BULK_MODIFY) return false;
if ($iActionCode == UR_ACTION_BULK_DELETE) return false;
if ($iActionCode == UR_ACTION_MODIFY) return UR_ALLOWED_NO;
if ($iActionCode == UR_ACTION_DELETE) return UR_ALLOWED_NO;
if ($iActionCode == UR_ACTION_BULK_MODIFY) return falUR_ALLOWED_NOse;
if ($iActionCode == UR_ACTION_BULK_DELETE) return UR_ALLOWED_NO;
}
if (self::IsAdministrator($oUser)) return true;
if (self::IsAdministrator($oUser)) return UR_ALLOWED_YES;
if (MetaModel::HasCategory($sClass, 'bizmodel') || MetaModel::HasCategory($sClass, 'grant_by_profile'))
{
if (is_null($oUser))
{
$oUser = self::$m_oUser;
}
return self::$m_oAddOn->IsActionAllowedOnAttribute($oUser, $sClass, $sAttCode, $iActionCode, $oInstanceSet);
}
// this module is forbidden for non admins
if (MetaModel::HasCategory($sClass, 'addon/userrights')) return false;
if (MetaModel::HasCategory($sClass, 'addon/userrights')) return UR_ALLOWED_NO;
// the rest is allowed
return UR_ALLOWED_YES;
// the rest is allowed (#@# to be improved)
if (!MetaModel::HasCategory($sClass, 'bizmodel')) return true;
if (is_null($oUser))
{
$oUser = self::$m_oUser;
}
return self::$m_oAddOn->IsActionAllowedOnAttribute($oUser, $sClass, $sAttCode, $iActionCode, $oInstanceSet);
}
protected static $m_aAdmins = array();

View File

@@ -52,7 +52,7 @@ abstract class ValueSetDefinition
}
public function GetValues($aArgs, $sContains = '', $sOperation = 'contains')
public function GetValues($aArgs, $sContains = '')
{
if (!$this->m_bIsLoaded)
{
@@ -93,16 +93,12 @@ 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
@@ -110,15 +106,12 @@ 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)
@@ -170,11 +163,11 @@ class ValueSetObjects extends ValueSetDefinition
return new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs);
}
public function GetValues($aArgs, $sContains = '', $sOperation = 'contains')
public function GetValues($aArgs, $sContains = '')
{
if (!$this->m_bIsLoaded || ($sContains != $this->m_sContains) || ($sOperation != $this->m_sOperation))
if (!$this->m_bIsLoaded || ($sContains != $this->m_sContains))
{
$this->LoadValues($aArgs, $sContains, $sOperation);
$this->LoadValues($aArgs, $sContains);
$this->m_bIsLoaded = true;
}
// The results are already filtered and sorted (on friendly name)
@@ -182,10 +175,9 @@ class ValueSetObjects extends ValueSetDefinition
return $aRet;
}
protected function LoadValues($aArgs, $sContains = '', $sOperation = 'contains')
protected function LoadValues($aArgs, $sContains = '')
{
$this->m_sContains = $sContains;
$this->m_sOperation = $sOperation;
$this->m_aValues = array();
@@ -210,54 +202,12 @@ class ValueSetObjects extends ValueSetDefinition
}
}
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;
$oValueExpr = new ScalarExpression('%'.$sContains.'%');
$oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias());
$oNewCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr);
$oFilter->AddConditionExpression($oNewCondition);
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);
$oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs);
while ($oObject = $oObjects->Fetch())
{
if (empty($this->m_sValueAttCode))
@@ -281,22 +231,6 @@ 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

@@ -108,7 +108,7 @@ class XMLBulkExport extends BulkExport
$aClass2Attributes = array();
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_BULK_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_BULK_READ, $oSet) != UR_ALLOWED_NO)
{
$aAuthorizedClasses[$sAlias] = $sClassName;
$aAttributes = array();

View File

@@ -1,8 +1,53 @@
$highlight-color: #E87C1E;
// Base colors
$gray-base: #000 !default;
$gray-darker: lighten($gray-base, 13.5%) !default; // #222
$gray-dark: #444 !default;
$gray: #777 !default;
$gray-light: #808080 !default;
$gray-lighter: #ddd !default;
$gray-extra-light: #F1F1F1 !default;
$white: #FFFFFF !default;
$combodo-orange: #EA7D1E !default;
$combodo-dark-gray: #585653 !default;
$combodo-orange-dark: darken($combodo-orange, 13.8%) !default;
$combodo-orange-darker: darken($combodo-orange, 18%) !default;
$combodo-dark-gray-dark: darken($combodo-dark-gray, 13.5%) !default;
$combodo-dark-gray-darker: darken($combodo-dark-gray, 18%) !default;
// Vars
$highlight-color: $combodo-orange;
$grey-color: #555555;
$complement-color: #1c94c4;
$complement-light: #d6e8ef;
$frame-background-color: #F1F1F1;
$frame-background-color: $gray-extra-light;
$text-color: #000;
$box-radius: 0px;
$box-shadow-regular: 0 1px 1px rgba(0, 0, 0, 0.15);
// - Boxes
//$search-criteria-box-color: #2D2D2D;
//$search-criteria-box-bg-color: #f0f3f5;
//$search-criteria-box-border-color: #3f7294;
//$search-criteria-box-border: 1px solid $search-criteria-box-border-color;
//$search-criteria-box-radius: 1px;
//
$search-criteria-box-color: #2D2D2D;
$search-criteria-box-picto-color: #E87C1E;
$search-criteria-box-bg-color: #EEEEEE;
$search-criteria-box-hover-color: $white;
$search-criteria-box-border-color: #CCCCCC;
$search-criteria-box-border: 1px solid $search-criteria-box-border-color;
$search-criteria-box-radius: 1px;
//
$search-add-criteria-box-color: $search-criteria-box-color;
$search-add-criteria-box-bg-color: $white;
$search-add-criteria-box-hover-color: $gray-extra-light;
//
$search-button-box-color: $combodo-orange;
$search-button-box-bg-color: $white;
$search-button-box-bg-hover-color: $gray-extra-light;
// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
$version: "v2.4.0";
$version: "v2.5.0-beta";

Binary file not shown.

View File

@@ -21,7 +21,7 @@ OS2Version: 0
OS2_WeightWidthSlopeOnly: 0
OS2_UseTypoMetrics: 1
CreationTime: 1463745065
ModificationTime: 1506001058
ModificationTime: 1522325525
OS2TypoAscent: 0
OS2TypoAOffset: 1
OS2TypoDescent: 0
@@ -35,6 +35,7 @@ HheadAscent: 0
HheadAOffset: 1
HheadDescent: 0
HheadDOffset: 1
OS2Vendor: 'PfEd'
MarkAttachClasses: 1
DEI: 91125
Encoding: ISO8859-1
@@ -46,7 +47,7 @@ FitToEm: 0
WinInfo: 0 31 10
BeginPrivate: 0
EndPrivate
BeginChars: 256 11
BeginChars: 256 13
StartChar: zero
Encoding: 48 48 0
@@ -940,7 +941,135 @@ SplineSet
815.574 32.7998 814.774 33.5996 813.175 33.5996 c 2
810.774 33.5996 l 1
EndSplineSet
Validated: 524321
Validated: 33
EndChar
StartChar: B
Encoding: 66 66 11
Width: 1024
VWidth: 0
HStem: 0 59<210.859 353.111 670.997 813.141> 103 28<483.475 540.525> 326 280<245 398 626 779>
VStem: 87 62<121.402 265.716> 415 60<134.336 199.804> 549 60<134.336 199.804> 875 62<121.402 265.716>
LayerCount: 3
Fore
SplineSet
376 606 m 0
420 606 456 571 456 527 c 0
456 514 l 1
458 444 l 1
566 444 l 1
568 514 l 1
568 514 568 524 568 527 c 0
568 571 604 606 648 606 c 0
677 606 704 590 718 564 c 1
719 564 l 1
754 502 l 1
781 491 803 472 817 446 c 1
818 446 l 1
914 283 l 1
929 255 937 224 937 192 c 0
937 87 852 0 747 0 c 0
666 0 594 54 568 131 c 1
555 114 534 103 512 103 c 0
490 103 469 114 456 132 c 1
431 55 358 0 277 0 c 0
172 0 87 87 87 192 c 0
87 224 95 255 110 283 c 1
206 446 l 1
207 446 l 1
221 472 243 491 270 502 c 1
306 564 l 1
320 590 347 606 376 606 c 0
282 326 m 0
208 326 149 266 149 193 c 0
149 119 208 59 282 59 c 0
355 59 415 119 415 193 c 0
415 266 355 326 282 326 c 0
742 326 m 0
669 326 609 266 609 193 c 0
609 119 669 59 742 59 c 0
816 59 875 119 875 193 c 0
875 266 816 326 742 326 c 0
512 204 m 0
492 204 475 188 475 168 c 0
475 148 492 131 512 131 c 0
532 131 549 148 549 168 c 0
549 188 532 204 512 204 c 0
EndSplineSet
EndChar
StartChar: b
Encoding: 98 98 12
Width: 1024
VWidth: 0
Flags: H
HStem: -1 35<183.649 347.721 676.937 838.822> 198 25<478.803 548.794> 294 234<470.756 555.897>
VStem: 75 44<83.375 145> 414 42<83.6665 369.146> 432 24<255 473> 569 42<83.6665 369.146> 569 24<255 473> 906 43<84.3258 145>
LayerCount: 3
Fore
SplineSet
352 616 m 0xf080
390 616 423 593 420 563 c 1
422 488 l 1
452 468 454 441 454 441 c 1
455 305 456 159 456 12 c 1
453 -62 369 -121 266 -121 c 0
161 -121 74 -59 74 17 c 0
74 20 76 22 76 25 c 1
74 25 l 1
106 196 l 1
106 196 l 1
113 235 143 270 188 292 c 1
186 292 l 1
206 408 l 1
206 408 228 457 274 487 c 1
282 570 l 1
284 570 l 1
287 596 316 616 352 616 c 0xf080
266 114 m 0
185 114 118 69 118 14 c 0
118 -41 185 -86 266 -86 c 0
347 -86 414 -41 414 14 c 0
414 69 347 114 266 114 c 0
514 408 m 0
554 408 589 384 592 353 c 1
592 353 l 1
592 135 l 1
592 135 l 1
590 103 555 78 514 78 c 0
473 78 436 103 434 135 c 1
432 135 l 1
432 353 l 1
434 353 l 1
437 384 474 408 514 408 c 0
466 138 m 0
466 118 489 103 514 103 c 2
539 103 560 118 560 138 c 0
560 158 539 174 514 174 c 0
489 174 466 158 466 138 c 0
672 616 m 0
708 616 737 596 740 570 c 1
742 570 l 1
750 487 l 1
796 457 818 408 818 408 c 1
838 292 l 1
836 292 l 1
881 270 911 235 918 196 c 1
950 25 l 1
948 25 l 1
948 22 948 20 948 17 c 0
948 -59 863 -121 758 -121 c 0
655 -121 571 -62 568 12 c 1
568 159 569 305 570 441 c 1
570 441 572 468 602 488 c 1
604 563 l 1
601 593 634 616 672 616 c 0
758 114 m 0
677 114 610 69 610 14 c 0
610 -41 677 -86 758 -86 c 0
839 -86 906 -41 906 14 c 0
906 69 839 114 758 114 c 0
EndSplineSet
EndChar
EndChars
EndSplineFont

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: 'CombodoRegular';
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');
src: url('combodo-webfont.woff2?v=2.1') format('woff2'),
url('combodo-webfont.woff?v=2.1') format('woff'),
url('combodo-webfont.ttf?v=2.1') format('truetype');
font-weight: normal;
font-style: normal;
@@ -187,6 +187,12 @@
.fc-closed-request:before {
content: "3";
}
.fc-binoculars:before {
content: "B";
}
.fc-binoculars-alt:before {
content: "b";
}
.fc-combodo-icon-o:before {
content: "C";
}

35
css/font-combodo/glyphs/B.svg Executable file
View File

@@ -0,0 +1,35 @@
<?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"
id="svg8"
version="1.1"
viewBox="0 0 264.58333 264.58333"
height="1000"
width="1000">
<defs
id="defs2" />
<metadata
id="metadata5">
<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
id="layer2">
<path
id="path4516"
transform="scale(0.26458333)"
d="m 363.57227,214.44727 c -28.72645,0.0164 -55.09978,15.88172 -68.57422,41.25195 l -0.53907,-0.18164 -34.7832,60.49414 c -26.17161,10.74972 -47.91781,30.06712 -61.67773,54.78906 l -0.51367,-0.125 -93.94336,159.60352 0.33984,0.0918 c -14.914544,27.26255 -22.732489,57.83851 -22.734376,88.91407 -3.76e-4,102.32848 82.952766,185.2825 185.281246,185.2832 79.57685,-0.18509 150.14836,-51.16271 175.33008,-126.65039 12.75761,17.44216 33.06009,27.76494 54.66992,27.79687 21.38684,-0.0322 41.50695,-10.14508 54.29297,-27.28906 25.33697,75.22798 95.78021,125.95807 175.16016,126.14258 102.32848,-7e-4 185.28163,-82.95472 185.28125,-185.2832 -0.002,-31.07556 -7.81983,-61.65152 -22.73438,-88.91407 l 0.33985,-0.0918 -93.94336,-159.60352 -0.51367,0.125 c -13.75992,-24.72194 -35.50612,-44.03935 -61.67774,-54.78906 l -34.7832,-60.49414 -0.53906,0.18164 c -13.47444,-25.37023 -39.84777,-41.23555 -68.57422,-41.25195 -42.90975,4.5e-4 -77.69486,34.78556 -77.69531,77.69531 -0.7247,3.23852 -0.16993,12.87304 -0.16993,12.87304 l -2.25,67.8418 H 443.6875 l -2.25,-67.8418 c 0,0 -0.20029,-9.98923 -0.16992,-12.87304 -4.5e-4,-42.90975 -34.78556,-77.69486 -77.69531,-77.69531 z m -92.14454,274.125 c 71.79702,0 130,58.20298 130,130 0,71.79702 -58.20298,130 -130,130 -71.79702,0 -130,-58.20298 -130,-130 0,-71.79702 58.20298,-130 130,-130 z m 449.45313,0 c 71.79702,0 130,58.20298 130,130 0,71.79702 -58.20298,130 -130,130 -71.79702,0 -130,-58.20298 -130,-130 0,-71.79702 58.20298,-130 130,-130 z M 496.1543,607.14258 c 19.72489,-3.1e-4 35.71515,15.98995 35.71484,35.71484 3.1e-4,19.72489 -15.98995,35.71516 -35.71484,35.71485 -19.7249,3.1e-4 -35.71516,-15.98995 -35.71485,-35.71485 -3.1e-4,-19.72489 15.98996,-35.71515 35.71485,-35.71484 z"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:3.77952766;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,280 @@
<?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"
xmlns:xlink="http://www.w3.org/1999/xlink"
id="svg8"
version="1.1"
viewBox="0 0 264.58333 264.58334"
height="1000"
width="1000">
<defs
id="defs2" />
<metadata
id="metadata5">
<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="display:none"
transform="translate(0,-32.41665)"
id="layer1">
<image
width="262.41626"
height="211.46167"
preserveAspectRatio="none"
style="image-rendering:optimizeQuality"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAM4AAACmCAIAAAAd2iekAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA
B3RJTUUH4gMdCzQSNk2I5QAAIABJREFUeNrtfedzY1eW38sBORIgwZw656BuqZVG0monWJ71zrp2
vVV2+T+Zv8Df/Mku22Xv2ju7W5ODZqSeVufMJruZMwkip4eX8/OHR6JBACQB8IFBwlWXSkKTB+fe
+7vnnnPuCaBhGEB7tEfrB9RegvZoQ6092lBrj/ZofCCH+N3VaiIIgm1Sx4jU8YCaruvlMwdBsOkJ
t0kdCqljADVja+z/bLVJHQqp4wE1o2yUptrc2WqTOhRSxwNqhmHoul4x4ebEeJvUoZA6HlAzDEPT
tAoZDkEQBEFNrF2b1MGTOh7ODnPCFWdrP2vXJnXApPY/wAN4mNJ1Xdd182yVpDcMwzAMNzrhNqlD
IXU8LlBztqUJ7+dgtUkdCqnjATVd11VV1TRt250NQQiCQBDUJnX0SR0PqJUmXG74wDC8n7VrkzpI
UscDauUCvKQrmBNuSICbhnrZ2hmmkV6TlKppHMfn8gWaYSVJ0jQdhmGCwF0ORzDgI0kCAIA6SVnI
VfXK8LyQzeWLDCuKkqZpEASiKEoSuM/rtpGkqUsdMFc7EbTwtm0J1Mw5lwtwENzXXaCqKgAYO5Ey
DIPj+Ew2n87mkunMRiyRKxR4XlAVDUZgp8Pu93p6uyPhcDDg87jdLgSBwdZzVT1UVU1nc6l0NpFM
rUfj2VyB5TlVVWEYJgnc63F3hTtCHYGA3x8KBjwe98FwtdMOHgOpZhrYFQIcgiAMw/a/dgAAQhBc
ImUYhqppDMPNL67cffB4bOJNPJmuuUwIDPf3dd+8dumD926Egn4MQ0EQKidlIVc110SWlVQm++e7
Dx88fr64sqaqaukvAQAwoQ+BYDjUcfHC2Y/fvzk6POhyOuq3FpvgapcdNIe1Pl6LnR0lR47pyzE/
bFrnkGVZ07TytUMQBEXREilFVReXV3/1u6+evRxnGFZWlApduHygCGwjiUhn+D/+h78eHRp0u13l
pCzkqnqwHD+3sPjf/tf/i8YSHC9omrq1Nm9xtqm8wzCKoi6n88bVi1/84LPhwX4EQVrE1e44K8kI
q7AB//SnP7X23jS5tARnul65dgiCwDBs/r8gig+fvPjFr//w6vVUvkApirLzsTEAADB0XVYUhuM2
YomOYDAYDJAEYTlXtXDGjb+e+t//9PPF5TWOF0qxFRU4MzbXEFBVTRDFTC6fSKZQFA2HO3ZHW3Nc
7b6DrXi5QizEWfkwWTQVhaZ12+q1K0FW0/SnL8a/unN/bGKS44XdSZZ21DAMUZRm5pfuPnzqcjkv
XTgL1cdbnVzV/MX5heXbdx9Nzcxvl7g1cFb6QNf1XL4wNjEJgCAEQbduXqv5FU1ztcuN1AqDoFVQ
M0+DYRjWadwgBMHld4GqatFY/PY3D1+9nqofZ6Ud1TT9xavXvd1dgwO9XrfbKq5qjiLNjL+ZeT42
UT/Oyq/dVxNTCIz09kS6I51IlYhqmquaEzRHSZJZ/hhvzU1c8cq2n3tzT93WAABBEP54++7M/ALL
co3izPyAKtILSytLS6ut1rgXl1YWlpapIt0ozszBsOzM3MKf/nxXEITW2QElnJV5DEDA6lA2yBJ5
VkkUgmAYbk4/q1g7BEEq1k5V1VyhcO/h02wu3xzOzLEWjU3NLljF1U5jam5hbSPWHM7Mkcnl7z54
ms9TZUbrfrna3eVruUFgpVQrZ7Hpt+GddNuKOVNUcWxikirSqqo1jTMAAHIFKpZI7m6A18/VTocw
Fk/m8lTTODOPVqFIj72eKonGfXJVjbNyd4EJsla8k1oDtfJA9UYZNbVRRVG2H6wd145m2Zm5RVlR
9oMzAAAEQaSKNMvxNdHWKFc1KXC8QBVpQRCbxpk5ZFmemVukGXb/XO38tPBWWLQoZBKyVqQ1F4NQ
rdvCMIyiaE3pyPPiWjS+s/+s3h3VdV2SZJ4X9FpQa5SrGhQMg+d5SZZ1Xd8PzgAA0DRtbX2D4/j9
c1Vrgtu2r3WhH4hVIGvOZmlCtxUEIZlMaZq+H5yVLW4Nji3RuLf0anCfODNN5kQqxQuChXZACWel
vTM17NaFGFkGtSZYbMLHraqaIIgUzZSbS01rQjAMEQRewbmFnncCxyAI3CfOTJaoIkPTjCzLJsH9
cLV9ggeEM2su0P3grFHdlhf4Ik1bgjMAACAIJnDcQjugQhmCwE2xth+cbTGmUTTN8fz+7YCqCR5Q
aO4hBDDtR7flOH67j2o/OIMQ5G10jbUat6ZpqqqCIABDm06f/eDM/G2aZrb81c1ztX2CYNMeg0O7
QPdvB9Tp42Y5gSoyFWsIAgAEQQSBEziOoihi/oNs7rGm6aqmSZIkipIgiqIobc4chjEULS2xtZ73
kiaEoQgMw9qmGAYJAicJgiBwHMcQGIFhaOvnNVVVFVVTFEWSJF4Qq59KizTD84IVXL2dYHOi8XhA
bZ8aN8dzhWKxJPYRBEZgGEVggiAiXaGOYMDrcbtcLpfD4bDbMBwzDEAUJZ7nM5lcIpWOJVPxREpR
VFmRURTFMMxCO6AmKQzDMAwFIQjDMAxFO8Mdkc5QONTREfDZbDaSJEAQkCSZZXmG41iGo2g6nc5G
YwleEBRVVVVV2wrdpmmaFwRLA58ONAQcOWCc7VPj5li+SG1eoJFwcLC/d3Cgb6CvpzsSJkmSwAkc
w2AYBiEQAkFg62Y0DEPXdE3XJUnKF6jZ+cWnLydiiSSGodbaAdWkMAwNBQORSOc7Vy6dGh32et04
jsMQBMHQWx3XMHTDMHTz37qqabwgrq1vLCwtLyytLq+ubcQShmFQNCNKcnM4s2qCxwNqlmjcsqI6
7PbPPr51YmSwKxzyelx2m81ut5EkgWP4nmEzhsPudjn9Pu+pEyP5AkUQhLWe92pSn338wYe3bvp9
Xp/X63I6UHTvOBfDMBwOO46hnaHA5QtnChQdT6Rm5hZ0w9A13cJwrANONTiIPNCSY7r6zc6ccP06
6fLq+tr6ht1GdkfCDrttC1gggiCCKDEMy/G8KMmyLEub5xhEEJjACZfT7nG73S4njm9empIkq6qK
IPD+udplgqqqISiCY2+/tEgzVLFIM5woiaqqAYABwzCOYRiGEThmt9ucDgdJ4CVSqqZxrBBPpThO
6O3uGujvPfhlPzZQM82xao27ibtA0zRFVTVVBQBD0zSOFxiG5XhRkuVMNp/J5gpUkRMEQRAFQVRM
xRxD7TZbwOcNBQOhjmAw4PP7vF6vG0NRC7nahZRhGJIk5wtULl/IZPOpdCaVyWbzBY7nZVkBAABF
EJIkSJKwk6TH7Qr4vV6P22En7Xaby+l02O2mWtmoU8nCZT8eF6j1Grciq6omyXKRZlZW12fml5bX
NpZX11mW3fUBHgAAwGYje3siN69eun71YmdHEMVQBIastQMqSKmqxgt8dCP+5Pmrxy9erUdj/F4B
dggMOx32/t7uwYHe0ydGhocGgwEfBMMoghzKsh8PqVaasGHoVmncLMctr64/fvbyzfR8JpuXZVlV
NVXTavl1a3ibERjGMMzvdb9348rH79/s6+221g6oILW8uv6nP9+7++BpvkDJsqxuf9veyUFo+u4R
BMYwvCMYuHj+9AfvvTM80Oew24+RHXBwUCsFDpTem5uOeTfXLpfPT88uvHo9tbC0ks0XijSrKEpT
7Bsogvh93lOjQ7duXrt66bzD4WiaK12v7fIVJenJi1d37z+ZmpnP5PLlD9u746zcEQ2CIIZibrcr
GPCdGBm6dun82VOjXq+nOa4aneDxuEC314ww9uPj1nWd4/jpufnXkzPTswsra9Fcgdqn511V1VQ6
IwgCy/OSrNy4djkY8Ddh6OwU1J8vUM9ejn999+HM3CJNM3VyVf3gYRiAJMvpTDadyabSmVg8sbyy
dvHC2ZMjg6b21hBXTdhzxy7lGARBoDkftywr2VxuZnbh/uNnE5PT+UJxK+aneZyVfrPIsONvZnhB
8ng8NpJ0OOwNCeydnhZYlpuanf/l7/40v7isKGrTOKv4oWyukMtTyyvrG/Ekw7wzOjLk87jLk6ks
fPBoxV1nZXJeictaKcfNKKSyoiRT6YdPnv+fn/18em6B44X9vyRW7KiuG5lsDkVRv9/bEQiUxU00
pnHD8OYENU2fmVv4+s79x8/G6lEfG33A5QVhZS06PTvvdrn8Xi9BEuaq7s5VoztYnpF0RKFWXjvu
rSberMa9vLr2mz98/dsvb+cLBU3TLcdZ6YNEMk2SRF9vt3MvwbaTxl3a0VQm+8fb976680DZLU64
SZyVFpkXxPnFFVmWwx1Bj9u1J1dN48xCqEHW4qwcZJtYbip1SpaVuYWln//6ywePnxeoYktxZoqK
2fnF15PT+/e8T0xOz8wv8oLQIpyVfGb5AnXn/uN/+dXvl5ZXBUGwKvCpInXKwgFZiLOKlOOmy+kI
gri6Hv3dl18/fzmezmR1vbU4M790fSM+NTMvSXLTqQamq3ZqZj66Ea9D19l/aK6WTGWePBv7zZdf
r2/EJEneP85Ks2uFrmY91EoXfClwoCGoqaqaSKXu3n985/7jTC5fllzaKpyZgyoWV6Mb8UTtUPJ6
gvo1TY8nkmvrGwWq2GqcldY8ncl+/c39h09eZHN5M2G46VSDas3nW55yXKTp15Mzv/zdH1mWOzCc
mfZBLlcYe/2mWseq0/MuK8qL8clcvrCXSLAGZ+Zv64bOMNwvf/en6blFXpDaKccNeERfTUzd/uYh
y3L6AeLMHAzLzS0sK9t9rfUn96qqOrewyLDsgeHM/E1d1xmGvXPv8ez8UjvluF6cxROpqdn5xeXV
g8cZAACiKCYSqfK0v4YicDRNSyTTwlaU78HgbPOrdX1ucXl6diGTzTeBs+9iyvHU7Pz84jLLcQeP
M9PszVNF8x5pItVA1/UCRZvBGgeJM2NLJM8uLE3NzDVkbH4XU47NmPqpmbnoRvxQcGaKJUEQNU2v
Gem1u8ZtGIam6YIo7pAL3VqcmR+srkUnJmckWa7Hfjz4lGPIKpA1dxpKE9Z1LZ3NRTfihc2EqIPG
mfnzmq6bkqzRCBzdMBRF2cEjdRA4AwAgV6BW1qKZbH5Px1jpeFfcmy1NnYIOV56VNG5VVReXVhiO
OyyclaSTKV8bLvJTVgfvUHBmDpphZ+eXlF2jSKoNnYNJOUYswVlz/pFyjVtVtaWVKMsJh4kzADAA
wCyoC8OQKEmZTG51fSOZzpmFZCRZBgCAwDGnw+73eTvDocH+3nBHkCBwwwBM3+ch4gwAAJbl5peW
b1y7BOB1LXvJM3UAqaCH03q2OtZF0/SNRHKraM/h4GzTU6Vp+QIVjcWXVtY2YslUOlso0gzLCYKo
qGZ8NkqShMvp8Hk94VBHX09kZGigr6frcOWZOXhBWI/GNU2tb9m/eynHAABqupHO5MS3ryuHgzNV
VVfXN6hicfzN9PTcYr5AVZNSFJUXhFy+sLIWBQDA7/OePX3iyoWzPq+nLOL8EHAGAIAoSal0RlHU
6lCzdsrx5qqpmkZRxfqiIVooOURR+vWXX61H41Td1kkuX7j74MnE5Ex/Xw+/71Jq+5ygLCsFiuIF
0avr5TG3RyHlGDpgnNX0vGu6nspktIYDCqzfUVVVZ+eWaIZtlBRNM1PTc6WpHQrOzKGqWjKVEcs8
yRZWMz0eUNvF8y7LSiKZ3qFk2sHhzFRo5E2fRcPWtKIoZrbOIeLM1HqTyXQJakcn5fggLtA9Y95F
SUqk0pquHS7Ovh2kdF1LpFKCJFqeanAMoLZnzLsgSolU/VKtjbM9pFoilREE0cJUg+NxgdYTgSOK
YjKV0euSam2c7QU1XUumMoIgHrWU4wPrcrxb7qsoSvXpam2c1SXV4skUzTDVdsDhphxDB4Cz3SMj
ZFlmWJZhuL1e7to4q4+KrjMMw7BcWb39Q9PPDgJq9accc7xQoOi9kr/bOKufkKGoKlUs8puFc/eV
cnzUL9CGUo5phs3lCuWfmDXeUQSFERiGIBiGYRhCYEiUZEEQpM2AsO8iznAMs9lIAsdVbXOomqap
mqIqZsh9iVQ+TzEs5/W4j07K8eF3OaYZJlsoQBCEmhVsYRhFEYLAXU6nnSQJEicJgiQIG4mvrseW
VtbS2dx3Vp55Pa6R4YH+vh6eFwRe5AWB5wWW52mGEQTJDH/SVFVR1XyhyLD8kepyjLQCZxUBxLun
tDAMR9NMwOc9fWp0dHigrzsSDnX4fB4IBE2naCkQ7pe//dNGIvldvjdxHD8xNPiTv/pReR6kruuq
pqVS6UQyvR6LLy2vzs4vMRwriuI+U46PrlRrLuW4rzfy7/7N9yEIdLmcTodZaxRHUESRZV3HTGqa
piVSmeXVaC5HfZf1s0wuv7iyli9Qkc5Q6YnTfA/AkM6A3zs82Hf98nmaZiEE7u2ONJdy3Iq+s4CF
Ra9KJ6y8p3apy/EurMuyrOl6qbe1SURWFI7leIHnBYFhuWyu8Hpq7umL8Uw21zjD3x47AATBUDDw
7jtXL5473RH0O512kiAwDEVguGyBQQRBFFVDywqj1n8jlX+XtXlTlkHNRFg5zswYlTp51Q2D5wWa
YYpFhmYYlmUZlmMYlmbZfIGKJ9PziyuyrHyXcVZCAIZhp0aHIl1hn9fjdNgdNtJutxE4breTLqfT
7Xa5Xa4mUo4ruhybYdUWijdroFZKOS5d89DW2PMkyYoiSbIgiKvRjenZ+emZ+eXV9UKBUlXVaPs1
6iAFAQCCIF6vp7+3+9TJkXOnTvb39ZAkgeNYeQ+QenAGbE8TsdYPZwHUjKpRf6A6w3ATU9OPnr2Y
mp4vULQkS6qiamZmYhtndZMCzVcnBEERBMcxr8d95vToe+9cPX/mlNPh2BNnqqqWp1eWUqeOnK5W
Ic/qSWlRNY2m6RevXo+NTy6trqezOZpmzAm3/bT7JAWCAIIgLpezI+Af6u+9fPHs1UvnXS4XUqs6
aSnluFw5s/zqtBJq5W9Ke+Isnc3NLSxNvJmenV9aWYsyDKuX+R7bOLOKFASCTqdjoK/n5OjQhXOn
T44MlRdhNUXD1gv1W3lmVsA4ohZoCWp7poJqmhZPpl6OT95/9Gxs/E1Zues2OFpFyixqfuXiufff
vX7l4tnO8KaXZPOxQVXLW8+2NEXPsgsU2LVMkmEYiqrm8oVf/PaP9x4+3Ygl2uA4YFI9kc4P33vn
xz/63Of1wjBUSm03dTJo8wGwhalT1kAN2CsbVBDE5bW1//EP/zq/uEzTTJn/pg2OAyIFw7Db5Twx
Mvif/u4nka4QhqJml2Pzumyi6OKh+dV2GRzPv56c/edf/HZ2Ycnsdd8Gx6GQgmHIbrOdHBn84gef
nT45YreRpbypJoqUNTpaHvAtiuLEm+kvv747MTlt5ie2wXFYpDRNZ1h2YnKaJAld1y+cO00SxLck
5dgwjMXltW/uP3n8fGx7Nag2OA6HlGEAsqw8fzmBY5jb7Tp76sS3IeXYLKD89Tf3n41NbK+Q0AbH
IZMSRHFsYtLhcJwYGcIaeSfdz2ghnEVJun33wfibmXyBaoPjqJHKU8XXU7N/vvdElKTjDTVVVbO5
wld3HsSTqbYdcARJ6bqRSKa+/uZ+Lkft2dzySEONKtIvx98sr66XXZ1tcBwtUrwgLq+ujY2/2SpQ
cgyhZhhGKpO9+/AJLwjt94AjS8owDJ4XvnnwJJ3JHIDPqyVQkxUlmc5MvJne6hvXBscRJaUo6vjk
dDKdkRuuAXU0oJZKZ1dWo1tetPaOHl1ShmEoirK8Gk1lsscUapmV1fWyDNA2OI4uKcMwVtajqfTx
hFouX4gnkm2cHRdS8Xgqly/UROGRhpqu6xRFV7QFae/oUSaVzeWLRVovA1Yr8vOsh5okyRRNF5nq
huVtcBxRUlSRpoq0LMvlOKvOszxaUDPdHBRVrOKyvaNHl5RhGFSRyWTyQCtTjq3vcpzOZKpcgu0d
PeqkqCKdzmaPWZfjVCZH0XQbZ8eLVLFIp9LZlnY5RiyHWiabK9LMtwBnMAyRBOHxuB02G07gKAJD
EAQCgKZpiqKKksTxAlWkBVFssGD0UYQsVSymM5nylGPLk6asgVp5ynEZ1I4rzkAQxDHM43b19XRd
unB2oL83FPS7HA4MRwEAEEW5SNOpdG49Gnv1enItGqeKdJ396o6saCzSTDqTK1XraEWXY8QSeVb6
D0VR8oUix/LHWp457Lab1658+vGtUyeGUQTRdR0ADAgCARAEAMBhh11OR19Pz5VL53/4l5/MzC1+
def+42dj7GYvtmN5BbMsm8sXFEXBMKwVqe2AtaGRuq6nMlmO57Um9crD3wYIAp0Oxxc/+Oy9d672
9UTsdpssy7oObKuxiqJmMD4AAHYbee7MSafTEe4I/voPXzEsq+vGcVT1NF3neD6VyUY6OxGkJXmg
1kBtszSVrscT6aPQkqxpUhiKXb9y8d13ro4MDyAwbBbH273ov9NhPzE8AABGMp2+/+j5zpGGRxdn
5hBEMZ5Md4XDx6DLsa7rsURKEKRjijMAADAMvXHtUqQzhMBw/V2OMQzr7uq8cfUShqHHFGcAAAiC
FE+kWlRczTKpZjKn60YsFhcE4fiaYxAEuZzOapztWfQfRRCXy7nD3x4DnJlSLZZIaa1xqgHWdjnW
dT0aT/KNXaBHaxtkWXkx/jqVyTba/CtPUS9evd6eFXZsXHFbUk2MxZI79Go+GlAzr3bDMGRZTqUz
jaRFHLltkGTp7oMn9x4+iW7EdtHPKsZadOPugyd3Hz6VZOmY4gwAQFGUEqm0rCgtisi1zAJVFIUq
0hwv1H0sdls7GILMSogYhqIoiqIIiiKGDqiapphDVXlBEEVJUazsi6hpWjKVvv/4GcOw58+cGhzo
7QgGdsKZpmnZXH5xee3V66mxiTfJVMZycKAIQhA4SeAIgmAoUtIUVU1TVE1RFFlWJElit9UMaAZn
AAComsbxPFUselyunZXOIwA1UZJT6exenS52nDAMw06n02EjbTab3Uba7Ta/12O3kQRB4Dhm/jF0
QFYUSZJkSRZlmWZYiioWaUYQBEEQWY7jBXErcHlfx31peS2Xo1ajsYvnzwwN9HvcLhtBYBhaquEj
y4ogikWGWV2Pjo1Pzi+uFKiiJThDUdRuI83y0zYbabfbnA67027DcQzDMBzHMBSDYVhVNUmRRUmW
RJHnhWy+wPE8z4s8z7M8zzDsDg8Ye3Clqmo6nQ13dBxxqEnJdKa+s/V2wjAEISiK45jT6Tw1OjzY
39vX293b3dUV7kD3Kq6p6zrNMMl0ZmMjEYvHF1fW1qKxbI6SFUWWFWNXB9ee21AoFl+8ej02Mel0
Onq7Il2dHT6fmyQJU6fJ54vxRHo9HmOYmo60hnEGQRCGoRiKBvz+gf6eE8MDvZFId6TT43aiKFKu
NcJwpXViGIYsK/FkKhqLr65vLK+uT88ushwnS7KiKmWY25srXdeT6cwJSQKcjqMLNUmUUumGW5IF
A/6zp09evXz+3JlTLqcDRVEEhs0mLHviTJZlBIG7QsFQwHfh3ClV1Yo0s7oeffnqzcvxyQJVVOoV
sTtug64bDMPOLi4uLK+UCtxt9prRNU3TLMEZgiBej+vKxXPXLp0fGR70+7wogkAQuL1tErBTSzIQ
BDEM7enu6gqHrlw8ryhKschMTs++ePV6cmY+kUrXz5Wm6clUVhBbkoRspVRLpbJ7tY/dnDCB4+Fw
6N3rl0+fHOnqDPu8XrfLiSBw/Q48s3MvCABmdRMMABEE8Xo8HcHAyNDgp9/7YOLNzIuxicXl1Tpy
gXbbBl03dF1VAHU/kN3ZY4wOD/ZfvXzhwtmTAb/P53U77HYURc0Jbj9ru1knZr0+BIZxAAAA0mG3
ORy2E6NDiWR6Zm7x/uNniWRK2jLXduFK0/VUKitJRxxqopTMZPTdpJoBAACOYd1d4ZOjw6dOjJw7
c7Ir3EEQRP0+wz079xIE7vN6Bvp6QsFAb3fX5PTs1OzCylp0ZyXycIxEBEEG+nrOnBw5e/rEydHh
3u4uE0Nm84B9tiZGEMTv8/q8nq5wqDMUDAZ9M3MLs/OLsXhy9w5duqYlM2nxKEs1wzAEUUxncjs7
AA0IBEmCGBnqv3b5wtVLF4YG+5poS1Nn514Igvp7uyOdoVOjwz3Px+4/fra8us6wXJUZXwkOFEGG
BgcURWEYluG4eh7ZSJJw2G0Oux1F4ZXVTUzvgjMQBJ0O+2B/7/vvXr9x9VJXZwhF0UYnWOdawTDU
Ge4IhwLDg71d4Y7nY68Xl9d4QdzJnaHpejqTFUSxFW8G1kBN0zSeFwoUvYNZYEAQZCOI4cH+v/nx
D8+cHnXY7ZbgbHcnPoqifb2Rzs6OkaGBn/38N6+nZjiO13ct8Gaz2//z3/8Nw7JzC8vzi8sb8aSq
aYau67qhG/rbEuZmrUUIQmAo0hUeHugbGuhz2G3/5b/+d5pmdsEZBII2u+3sqRN/+9dfnD1zAkPR
/UywzrUCQXCwr7crFBrs7/+XX/1+YXGZF8SaO6XreoGieV7QNA1BkKMINZbl8hS1E84AAPC4nJcv
nP2bH/8wHO6w22zNrZ0sy3s2TK7pmjp75oTDYfvtH29/fecBzbC7XHYwBHWGOi6eO/3ejWuiKFJF
OpXOUDRNMyxDswzLAwDgdNicLofL6fC4XMFAwGYjMBQBQTCTycEQtPu96XDYPvnw1o/+8tOB/h50
+142PcE618put1+9dD4SCf/fn/3y+dhEvtI7U462IstyHo/7KEKNZrl8ntoJZ36v58qlc3/1o8+7
usI2kmyidlzJDmhCfQFBEEPR3p7I5598iCLoL377paIohrFj0X8YhnEcRxDYYbe53a5wR1BWFFVV
FVU1K/YgCIwiiBlHBG9V+FdVDXrbGKD2vYmiyPc/+973Pni3rydSLc+anmBDa9XT1fm3P/kCgqBn
L8dzhdq94XKFAsMdVagxLJuvwbcBAABJEGdPnfjo1s3B/r4m1m5PO6BOOgSOD/b3SZKcSCafvhyX
JXlP5d3EaAXRliWkAAARCklEQVQmKrgqXXYguAXVHeQZhqHvXr/y/s3rQwN9BIFbPsF6SJn9qYYH
+z/9+JYoSU+evxLEGspoPl80Zb+1GptFUGPYXKFQjTMAAIb6e29cv3zuzEkzAqdFdkA9A0ORvp7I
5598kEimo7G4JMlN25u1lSoY3okUjuN9PV0//PyTocFtOLPcDqiHFAiCF8+foYpMNld4Mz1bW6ox
3BFNOaZZLpcvVlt2KIJ89P7NyxfOkgRxAHbAnqQIHDt9cuTWzWt+nxewFGcwDGMouhOtoN/30fs3
z5056XTYD8AO2JMUjmHXLp3/9KNbKIJUi618nqJp+oimHLMsm6eoau/OyFD/YH+v3+dt2g5oNJhn
T1I4hr1/81ow4N/lsmuCKxTFwM1LtMZFHOoIfHzrXQLHDmCCdZLyeFxDg30jwwMIXHmz5QsFhuUA
q4cFUNM0jWG4YlXlQQzHbl6/0tkZanrtWqEmQxAUDPhPjg6HO4LN4WwHrnYEbqgjcGJ0MNQRKOf8
wOyAHTcegkIdgVs3ruN4ZdllimZohrU8cM0CqNEMW6SZsqjAzR11OhyXL5zze70NqZamu9wM6t+W
PNKUmlxNCgQhm812+uRod6TLUq52nGN3V+ep0REMQ0tPqC2dYP2kPG73pQtnHA57xU/KskKzrOWC
zQKo5QsUw7IV9zqOYR0Bf093l81GNtput/6g/qZJDQ/0NyTV9sNVuCM42Nd7wBOshxRJ4JHOUDDg
r+i7bRgGy3KFYtHaBwMLoMYwbPUDDkmSPZGuRj3OB6YmB4M+t9t5MFy53a6OoP9w7YAdHRAI0h3p
IkkCAIytPwAAADwvMgx35FKORVneHj0BAgCAoojD5YCgxs5oS93l5aQIHMcxzMyHaClXEASZcZ0H
PMF6JQ0IOh226pgaVVVr5UkcugVayyQGARAGoUbX7sDU5D1bl1rFVemLDt0OqE3KMGqaM0YLKsRY
INVQZDMSeptZquuiJNXD7UG6y8tPrapptUQaCMMQCDbMFQgCEFxj10wikiybdWUObIL1kDIMQ1UV
URTLogw3+UdgGEWP3nM7SRJ4VZ8iSZZzuUI98d8H7y4HAIBhOZ4XKg6umWJotsVslCsQgkqPP8b2
Sp8sx1FU0emwH+QE6yGlaZosy7l8ofquxHHMRpJH7gJ1Ou1m0H35EARxI54QJWl3OXxYanIylalK
PAEgCMQxzDzNjXIFmQ+mGFatnuZyhVg8eUTsgG2yVpIEQYwlUtUvoSRBOB2OIwe1oN/vcbsq9B5V
VfMFamlpdZf6PIfoLp9bXNqIJyp9zijq87oBAGiOKwiCfF43Wvk8b8STyYXllYN/D9iTFM8Ly2vR
AlXcClEGS8fG43YFAr4jBzUMw3xeT9mr4ubFIYrSvcfPM9nCUbADyhljWG5yZn57/3gAAACbzdbX
E9F1vTmuYBju6+2222zb1WsgnkjNzi2aMcCHbweUkcrl80+evtxSqd9KCr/P6/O6cavz8yyAGgSB
4Y5gf2+k4nNZUcYm3kRj8Qr5fLjucllR3kzNra3HWI6v+CuH3TY02AeBQHNcoShycmTI+TatbXMD
eV6IxhIzcwuGAR7we8AupARR3Ignx9/MKIpaYc309XaHOoKWB3xb89we6QqPDg9WMKdpWjyRGn89
FY3Gyu2DQ3SXK4qazuTu3H+USmcrlEgYhn1e9+jQQMnt3ChXKIKcPjHi93pgGC6P8jUraT54/KJA
Fc0KoAf2HrATKV3XN2LJ11Oziap8ShAER4cHI12dgNXDGqh1hjpGh4dsZI03qDv3Hj1+PkZvtTE4
RHe5YRh5ino+9vrhk+f5yug6wGG3RbrCA/09W/7MZjzvw4N9PZFOh8MGVPV5ffRs7MX463yNMvuH
YDPRDPfs5cS9h08r5BkIgnYbOTo82BkKHlGoEQTe0915/erF6u7Meap47+Gzr/583/QJHWLYDMcL
b6bm/vGff8HxfDUOB/t7Lp47DW0elSa5AgDgwtlTg3091dHkPC/8w89+8Xpqhq3x7QdqM+mGcef+
o0fPXlI0W+njwLB3rl7q7e6qiN+0ZMA//elP908FBEEUQWw2YmJyRhSlCscSLwg0w+m6Hgz4YQiq
1m2rPcBNq8k7kaKKzP3Hz3//x9ura9GqFHzD7XS8+86V925cs5HEPrmykUS+UFiPxiR5W5SvYRii
JFMUjSJoR9C/+142McF6SJkNQP9879Gd+0/Xo3FlezI2DMM+r+fvfvJvq0OFjxDUTE3FYbczHJfJ
5CrEhqqqLMfn8nlZkZ0Oh40kzaib8ph3q9zl1aQ0TU+lM3cfPLlz79HM/GK1uxKB4XeuXrp14+pA
Xw8IQvvkiiAwU4JGYwl9u2VnGEaxyBRpRtU0n9djI4maBnKjE6xzrVRVTaQy9x8/u3Pvyer6RnUZ
vI6A/8P3b3z8/rvuHWsSHg2oQRCEY1jA74un0rl8odSwaEsfl3P5QjyRQhCEwDGCwEmSxDDMKjug
Jild1zmeX4/GHj55+Yev7swtLFXhzEARZGig94d/8b3TJ0cJHN+JVP1cgSDocDhwAk+m0sUiU/Fe
oqpqOptLZbIwDBEEQeA4gmz7roYmWD9XDMutR+NPX7z68uu7q+sxafvuAADgdDounDv1ky9+0Bnu
sDwD1GKomWjz+7yGbuTyhVSm3MQzzFPGMOzy6jrNsC6X0+/3IWbTibqXb8eg/lrqi6woNMPOLy7/
9svbv/r9nzLZXNUrmQHDkN/n+duffHHh7CmX07kTqUa5wjDM63H7/b7puYUKdcIUNgWqODUzz/Oi
y2m32UgYgc1rsaEJ1sOVoiiqqnI8PzO3+OXtu7fvPszkCtV2CQzDZ0+d+Px7H1y9fKEV8sx6qG1Z
o0FRkqIbiZrJvbKixJPpqdmF1fVYwOd1OBw1s9920jmqr5Wa26Dr+sTkzM9//eW//Or3NffbJNIZ
Cn728fsf3rrhcjrRzWcla4J5HHZ70O/XNC2ZztQMZ9U0LRqLv5qYSqWzdpIMdQTMV4o6J1gnV7wg
LK6s/tO//uYPt+/OLa6IglTT+o10hj//9MNPPnwPRVGgZcN6qCEI4nY77XZbMpVmuc0cL3Brew0A
UDWdF8RMNj87v7S0vJovFEAIcjmdEATtJODqVJN5QViPxR8/ffmbL2/fufd4anY+mysoSu36tL2R
rls3r332vfd9Xg+O49ZaJwiCYCga7giqqmLqZ9W/rqoaL4iZXH5pZXVmbilXyIMAUHqEbdoOMIta
LC6vPn3+6utvHty+92h+caVA0YqiGrVeOHoinT/+0efvvnPF5/W0qLb35tK0ovCpqmmJZOrB4xd3
7j1cXd/gBQHcIbnX63F3hUM93V29ka5IJOz3eX0ej8fjcjrs5cXMqq8VcxsMwxBEqUDRxSKVzuZj
iWQ0Fo9uJDZiCZbl1B0SMSAIPDE8+M7Vi9cuXxge7N+Pxl2Tq3JS84vLj5+NPXr6cmZ+cac4FxiG
HXZbpCsU6Qx1d4Y7w6FgwOf1egN+n8Nuq0dz0nWdZTmqSOcKVDqTjW7ENmKJWDyZTGcKm+lFNaZm
t5EDfT3f++jWu9evdIaC1Zi2NuW4JVAzDENVVZph/nzv0f1HzxaXV7cu09p8QyBIEMRgf29XZ7gz
FAx1BAJ+n81GoptvL4Zh6DAMb7YQ1HTNMCAIUlWN54U8RaVS2VQmE92Ib8STVf0hK50yBI729/V8
8uF7Vy+d6wqHmvOImnWpqoN5apKKJ1JPX7z68va95dW1Wlf5Nh3D7XJ2hkM9ka7OcCgUCvq8bofN
hmKoWeAQhk3tFtA0XdU0TdM0VVMUheWFXD6fTmfjqfRGLL66tiFJUllTzxrL7nI5TwwPfHjrxqcf
vW+vyv8oy/o5wlAz39q0rcDDB4+ffXXn/sTkrChJdXaYg0AQx7HOcMjpcJAkThIYTuA2ktR1Q5Yk
UZRFWRYEIU/RuXyBrTuxB4Yhu43s7gr//b//q5OjQw67vWmN21S361feGZabnl34n//4s/WNBM/x
28XtblXZHA673+f1edwkSZIkaSMJgsBAEBREkedEXhB5gaeLTCKVkmTFMHSgjgJvMAwROHHpwpnv
f/rRh7du1NxBc1jbacpiqJk4M9uXmpQlWd6IJZ6+GP/D13cz2WydaANBMxoWhEAQBLfq1QOAYQCG
YQBGqUpo/RnYRm93183rlz/54L1QKIBjZjmOAwrqNwxDkuREKvXl7bsPHj9fW4/Vg7PNdYAg80Yu
i1EHN3teb7W+1nS9fpwFA4Hvf/bRrRtX+3t7qpNAy7scH12olXrklnf+NstRF6jiyvrGs5evnr4Y
T6Yy9aWzWlPPEYaggN97/cqFC+dOD/b3dAQCJjL2YwdUB1nsScpUKpLp7NLy6tjEm4fPxrLZnKZp
B1awEobhzlDw+tWL169cGujt9vu81U8CpY0rNWm0EGrWt54tyV7T02buK0mSwYA/4Pf0RLqmZxcW
l1fjyZQs79KNwQKc2UgyGPD193afOjF05uRopCtkt9kOK6gfBEEURXsinV63qyPoD3UEp2bn19Y2
srk8Lwitw5lZlLkrHBoe7D99cuTcmRODfX0oilbrYOaN1AqDwGKpViHMTF6rt0EQxKWVtfHJmfmF
pWQ6m8sXijRdVXy1eZyZO+r1uH1ed2eoY6Cv5/SJ4dGRAdN7t4vybqEdUA8pXhDmFpZmZheWV9cT
qUyhSBeooqKojW/HjmtFEoTb5fT5vOGOwOjI0MWzp4YG+qpD80vbZ47SJV26tY8W1Mq7HJsEoa2x
k5AQRWlyduH5y1evp2Zj8aSZwqSqmqapRplToM6i/zAMIwiMwDCOYz6v98rFc9cun+/riTjsNks8
743aAfWTohl2dW1j7PXUy/HJfKEgSfLWOmh1JAFtw5m5DjCCIDCMIEh3V+e5M6euXblw9uQIQeC7
gKaEM6Asm9Dy7rMWQM2oGuacdz8QhmEoiiorMk2ziVRmZW19dX1jdS26EUsUikVd04C6i/47nfbe
nu7+nu7+3shAX3dvd4TAcRAETHuifuXdQjugflIQBIMQqCiqIAjrG7GVtY3V9dhqdGM9usEw3K7d
bbbjDIZ9Xk9PpLOvt7uvp3ugr7czFHS5nBiKoiiyO85UVS3ZASWQWf5CZTHUSrpk/YJX0zRRkhmW
LRZpluM4jqNpNlegBEGUFVVWZFGURFESJAkCQQzDCBwnSILEMQInHA671+PyeT02G2m32Ww20k6S
BIEritKc8m6hHdAoKTPaiuMFnhc4nud5IV+gChTNspwoiYIki4IoSpIsy7quYyiK4xiBYziOkwRu
I8mOjqDP63HY7TYbabeRdpsNx7E9mTRxVi7PoDKD9yhCrVzUN4Szas+7YRiSLLMcr6m6phvmm7Es
K5KiQCBgvgmaPXJQFCUJwm632bb0j/qd+Ba+B7SOFC+IHMcLoqgoitnNSJJlWZZVRUEQ2OzyZmYE
2m02r8dNEET9omgr5dg0dN7Ksy0vsfUvVFZCrc7qBDUPlqIoW9uwebb2E87QJlUnKVVVy/0a9Wg+
h+zsKLHbHM7K8xBNu7W5tLM2qUZJVbjQWoczy6Qa0NRjWZmnapunsem1a5NqjpR5b5oVJICWDcQq
kdYczsqVPHPCpq7QJnXwpFqKMytfCxqdcMnZu08B3iZ1KKSOB9RKT6X7n3Cb1KGQOszXgoYmXO0H
b3rt2qQOntTxkGrlbpGSotCoy7dN6hBJHQ+oVbwolPt7m1i7NqmDJ3U8LtDyCVc8t+1n7dqkDozU
MZNq5dLbPFttUseI1DGAWrWPt+lT1SZ1KKSOpQXaHt/ZAbWXoD3aUGuPb9X4//0/Sa3hNTUCAAAA
AElFTkSuQmCC
"
id="image823"
x="2.9734159"
y="52.273003" />
</g>
<g
id="layer2">
<path
id="path827-9"
d="m 97.624888,39.796021 c -8.178314,0.006 -14.961537,4.568212 -15.54272,10.453627 l -0.226343,-0.0021 -1.86862,19.061368 c -10.48507,6.763065 -15.77113,18.088851 -15.77113,18.088851 l -4.276224,26.459893 0.258381,0.0661 C 50.006857,118.97418 43.274297,126.9138 41.68205,135.75964 l -0.183967,0.014 -7.216098,39.0214 0.316778,0.0109 c -0.07327,0.65029 -0.11842,1.30208 -0.135393,1.9544 -5e-6,17.33952 19.483317,31.39599 43.51724,31.39602 23.49434,-0.0182 42.72694,-13.48746 43.45006,-30.42967 0.0382,-33.56054 -0.12647,-67.01884 -0.28628,-98.079355 0,0 -0.58301,-6.16341 -7.34633,-10.666015 l -0.6258,-17.202566 c 0.0278,-0.242592 0.0447,-0.485771 0.0506,-0.729154 1.9e-4,-6.215145 -6.98334,-11.253585 -15.598012,-11.253579 z M 78.113934,154.49394 c 18.592694,-9e-5 33.665126,10.22596 33.665136,22.84046 0,12.61451 -15.072436,22.84056 -33.665136,22.84047 -18.592498,-1e-4 -33.664614,-10.2261 -33.664612,-22.84047 6e-6,-12.61437 15.07212,-22.84036 33.664612,-22.84046 z"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.36251795;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<path
id="path898"
d="m 134.63546,87.230775 a 17.907058,13.764381 0 0 0 -17.83715,12.695354 h -0.87127 v 49.712211 h 0.83251 a 17.907058,13.76438 0 0 0 17.87591,13.09635 17.907058,13.76438 0 0 0 17.86403,-13.09635 h 0.31057 V 99.926129 h -0.32711 A 17.907058,13.764381 0 0 0 134.63546,87.230775 Z m 0,53.624635 a 10.557147,8.1148223 0 0 1 10.55749,8.11475 10.557147,8.1148223 0 0 1 -10.55749,8.11475 10.557147,8.1148223 0 0 1 -10.55698,-8.11475 10.557147,8.1148223 0 0 1 10.55698,-8.11475 z"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.00007248;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<path
id="path827-9-6"
d="m 171.1122,39.796021 c 8.17832,0.006 14.96154,4.568212 15.54273,10.453627 l 0.22634,-0.0021 1.86862,19.061369 c 10.48507,6.763074 15.77113,18.08886 15.77113,18.08886 l 4.27623,26.459883 -0.25839,0.0661 c 10.19138,5.05042 16.92394,12.99004 18.51619,21.83588 l 0.18396,0.014 7.2161,39.0214 -0.31678,0.0109 c 0.0733,0.65029 0.11842,1.30208 0.1354,1.9544 0,17.33952 -19.48332,31.39599 -43.51725,31.39602 -23.49434,-0.0182 -42.72693,-13.48746 -43.45005,-30.42967 -0.0382,-33.56054 0.12647,-67.01884 0.28628,-98.079352 0,0 0.58301,-6.163411 7.34633,-10.666016 l 0.6258,-17.202565 c -0.0278,-0.242592 -0.0447,-0.485771 -0.0506,-0.729154 -1.9e-4,-6.215148 6.98334,-11.253588 15.598,-11.253582 z m 19.51096,114.697919 c -18.59269,-9e-5 -33.66512,10.22596 -33.66513,22.84046 0,12.61451 15.07243,22.84056 33.66513,22.84047 18.5925,-1e-4 33.66462,-10.2261 33.66461,-22.84047 0,-12.61437 -15.07212,-22.84036 -33.66461,-22.84046 z"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.36251807;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -1,6 +1,4 @@
.treeview ul { background-color: white; }
.treeview, .treeview ul {
.treeview, .treeview ul {
padding: 0;
margin: 0;
list-style: none;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,9 +1,13 @@
/*! jQuery UI - v1.10.4 - 2015-04-25
/*! jQuery UI - v1.12.1 - 2018-02-20
* http://jqueryui.com
* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=0&bgColorHeader=%23E87C1E&bgTextureHeader=flat&bgImgOpacityHeader=35&borderColorHeader=%23F26522&fcHeader=%23ffffff&iconColorHeader=%23ffffff&bgColorContent=%23eeeeee&bgTextureContent=flat&bgImgOpacityContent=100&borderColorContent=%23dddddd&fcContent=%23333333&iconColorContent=%23222222&bgColorDefault=%23f1f1f1&bgTextureDefault=flat&bgImgOpacityDefault=100&borderColorDefault=%23cccccc&fcDefault=%23555555&iconColorDefault=%23F26522&bgColorHover=%23fde17c&bgTextureHover=flat&bgImgOpacityHover=100&borderColorHover=%23E87C1E&fcHover=%23E87C1E&iconColorHover=%23E87C1E&bgColorActive=%23ffffff&bgTextureActive=flat&bgImgOpacityActive=65&borderColorActive=%23E87C1E&fcActive=%23E87C1E&iconColorActive=%23E87C1E&bgColorHighlight=%23ffe45c&bgTextureHighlight=flat&bgImgOpacityHighlight=75&borderColorHighlight=%23fed22f&fcHighlight=%23363636&iconColorHighlight=%231c94c4&bgColorError=%23b81900&bgTextureError=diagonals_thick&bgImgOpacityError=18&borderColorError=%23cd0a0a&fcError=%23ffffff&iconColorError=%23ffd27a&bgColorOverlay=%23666666&bgTextureOverlay=diagonals_thick&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=%23000000&bgTextureShadow=flat&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */
* Includes: draggable.css, core.css, resizable.css, selectable.css, sortable.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, progressbar.css, selectmenu.css, slider.css, spinner.css, tabs.css, tooltip.css, theme.css
* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=custom-theme&bgImgOpacityError=18&bgImgOpacityHighlight=75&bgImgOpacityActive=65&bgImgOpacityHover=100&bgImgOpacityDefault=100&bgImgOpacityContent=100&bgImgOpacityHeader=35&cornerRadiusShadow=5px&offsetLeftShadow=-5px&offsetTopShadow=-5px&thicknessShadow=5px&opacityShadow=20&bgImgOpacityShadow=10&bgTextureShadow=flat&bgColorShadow=%23000000&opacityOverlay=50&bgImgOpacityOverlay=20&bgTextureOverlay=diagonals_thick&bgColorOverlay=%23666666&iconColorError=%23ffd27a&fcError=%23ffffff&borderColorError=%23cd0a0a&bgTextureError=diagonals_thick&bgColorError=%23b81900&iconColorHighlight=%231c94c4&fcHighlight=%23363636&borderColorHighlight=%23fed22f&bgTextureHighlight=flat&bgColorHighlight=%23ffe45c&iconColorActive=%23E87C1E&fcActive=%23E87C1E&borderColorActive=%23E87C1E&bgTextureActive=flat&bgColorActive=%23ffffff&iconColorHover=%23E87C1E&fcHover=%23E87C1E&borderColorHover=%23E87C1E&bgTextureHover=flat&bgColorHover=%23fde17c&iconColorDefault=%23F26522&fcDefault=%23555555&borderColorDefault=%23cccccc&bgTextureDefault=flat&bgColorDefault=%23f1f1f1&iconColorContent=%23222222&fcContent=%23333333&borderColorContent=%23dddddd&bgTextureContent=flat&bgColorContent=%23eeeeee&iconColorHeader=%23ffffff&fcHeader=%23ffffff&borderColorHeader=%23F26522&bgTextureHeader=flat&bgColorHeader=%23E87C1E&cornerRadius=0&fwDefault=bold&fsDefault=1.1em&ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif
* Copyright jQuery Foundation and other contributors; Licensed MIT */
.ui-draggable-handle {
-ms-touch-action: none;
touch-action: none;
}
/* Layout helpers
----------------------------------*/
.ui-helper-hidden {
@@ -38,9 +42,6 @@
.ui-helper-clearfix:after {
clear: both;
}
.ui-helper-clearfix {
min-height: 0; /* support: IE7 */
}
.ui-helper-zfix {
width: 100%;
height: 100%;
@@ -48,7 +49,7 @@
left: 0;
position: absolute;
opacity: 0;
filter:Alpha(Opacity=0);
filter:Alpha(Opacity=0); /* support: IE8 */
}
.ui-front {
@@ -60,20 +61,27 @@
----------------------------------*/
.ui-state-disabled {
cursor: default !important;
pointer-events: none;
}
/* Icons
----------------------------------*/
/* states and images */
.ui-icon {
display: block;
display: inline-block;
vertical-align: middle;
margin-top: -.25em;
position: relative;
text-indent: -99999px;
overflow: hidden;
background-repeat: no-repeat;
}
.ui-widget-icon-block {
left: 50%;
margin-left: -8px;
display: block;
}
/* Misc visuals
----------------------------------*/
@@ -93,6 +101,8 @@
position: absolute;
font-size: 0.1px;
display: block;
-ms-touch-action: none;
touch-action: none;
}
.ui-resizable-disabled .ui-resizable-handle,
.ui-resizable-autohide .ui-resizable-handle {
@@ -154,33 +164,26 @@
right: -5px;
top: -5px;
}
.ui-selectable {
-ms-touch-action: none;
touch-action: none;
}
.ui-selectable-helper {
position: absolute;
z-index: 100;
border: 1px dotted black;
}
.ui-sortable-handle {
-ms-touch-action: none;
touch-action: none;
}
.ui-accordion .ui-accordion-header {
display: block;
cursor: pointer;
position: relative;
margin-top: 2px;
margin: 2px 0 0 0;
padding: .5em .5em .5em .7em;
min-height: 0; /* support: IE7 */
}
.ui-accordion .ui-accordion-icons {
padding-left: 2.2em;
}
.ui-accordion .ui-accordion-noicons {
padding-left: .7em;
}
.ui-accordion .ui-accordion-icons .ui-accordion-icons {
padding-left: 2.2em;
}
.ui-accordion .ui-accordion-header .ui-accordion-header-icon {
position: absolute;
left: .5em;
top: 50%;
margin-top: -8px;
font-size: 100%;
}
.ui-accordion .ui-accordion-content {
padding: 1em 2.2em;
@@ -193,17 +196,78 @@
left: 0;
cursor: default;
}
.ui-menu {
list-style: none;
padding: 0;
margin: 0;
display: block;
outline: 0;
}
.ui-menu .ui-menu {
position: absolute;
}
.ui-menu .ui-menu-item {
margin: 0;
cursor: pointer;
/* support: IE10, see #8844 */
list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
}
.ui-menu .ui-menu-item-wrapper {
position: relative;
padding: 3px 1em 3px .4em;
}
.ui-menu .ui-menu-divider {
margin: 5px 0;
height: 0;
font-size: 0;
line-height: 0;
border-width: 1px 0 0 0;
}
.ui-menu .ui-state-focus,
.ui-menu .ui-state-active {
margin: -1px;
}
/* icon support */
.ui-menu-icons {
position: relative;
}
.ui-menu-icons .ui-menu-item-wrapper {
padding-left: 2em;
}
/* left-aligned */
.ui-menu .ui-icon {
position: absolute;
top: 0;
bottom: 0;
left: .2em;
margin: auto 0;
}
/* right-aligned */
.ui-menu .ui-menu-icon {
left: auto;
right: 0;
}
.ui-button {
padding: .4em 1em;
display: inline-block;
position: relative;
padding: 0;
line-height: normal;
margin-right: .1em;
cursor: pointer;
vertical-align: middle;
text-align: center;
overflow: visible; /* removes extra width in IE */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
/* Support: IE <= 11 */
overflow: visible;
}
.ui-button,
.ui-button:link,
.ui-button:visited,
@@ -211,92 +275,130 @@
.ui-button:active {
text-decoration: none;
}
/* to make room for the icon, a width needs to be set here */
.ui-button-icon-only {
width: 2.2em;
}
/* button elements seem to need a little more width */
button.ui-button-icon-only {
width: 2.4em;
}
.ui-button-icons-only {
width: 3.4em;
}
button.ui-button-icons-only {
width: 3.7em;
width: 2em;
box-sizing: border-box;
text-indent: -9999px;
white-space: nowrap;
}
/* button text element */
.ui-button .ui-button-text {
display: block;
line-height: normal;
}
.ui-button-text-only .ui-button-text {
padding: .4em 1em;
}
.ui-button-icon-only .ui-button-text,
.ui-button-icons-only .ui-button-text {
padding: .4em;
text-indent: -9999999px;
}
.ui-button-text-icon-primary .ui-button-text,
.ui-button-text-icons .ui-button-text {
padding: .4em 1em .4em 2.1em;
}
.ui-button-text-icon-secondary .ui-button-text,
.ui-button-text-icons .ui-button-text {
padding: .4em 2.1em .4em 1em;
}
.ui-button-text-icons .ui-button-text {
padding-left: 2.1em;
padding-right: 2.1em;
}
/* no icon support for input elements, provide padding by default */
input.ui-button {
padding: .4em 1em;
/* no icon support for input elements */
input.ui-button.ui-button-icon-only {
text-indent: 0;
}
/* button icon element(s) */
.ui-button-icon-only .ui-icon,
.ui-button-text-icon-primary .ui-icon,
.ui-button-text-icon-secondary .ui-icon,
.ui-button-text-icons .ui-icon,
.ui-button-icons-only .ui-icon {
.ui-button-icon-only .ui-icon {
position: absolute;
top: 50%;
margin-top: -8px;
}
.ui-button-icon-only .ui-icon {
left: 50%;
margin-top: -8px;
margin-left: -8px;
}
.ui-button-text-icon-primary .ui-button-icon-primary,
.ui-button-text-icons .ui-button-icon-primary,
.ui-button-icons-only .ui-button-icon-primary {
left: .5em;
}
.ui-button-text-icon-secondary .ui-button-icon-secondary,
.ui-button-text-icons .ui-button-icon-secondary,
.ui-button-icons-only .ui-button-icon-secondary {
right: .5em;
.ui-button.ui-icon-notext .ui-icon {
padding: 0;
width: 2.1em;
height: 2.1em;
text-indent: -9999px;
white-space: nowrap;
}
/* button sets */
.ui-buttonset {
margin-right: 7px;
}
.ui-buttonset .ui-button {
margin-left: 0;
margin-right: -.3em;
input.ui-button.ui-icon-notext .ui-icon {
width: auto;
height: auto;
text-indent: 0;
white-space: normal;
padding: .4em 1em;
}
/* workarounds */
/* reset extra padding in Firefox, see h5bp.com/l */
/* Support: Firefox 5 - 40 */
input.ui-button::-moz-focus-inner,
button.ui-button::-moz-focus-inner {
border: 0;
padding: 0;
}
.ui-controlgroup {
vertical-align: middle;
display: inline-block;
}
.ui-controlgroup > .ui-controlgroup-item {
float: left;
margin-left: 0;
margin-right: 0;
}
.ui-controlgroup > .ui-controlgroup-item:focus,
.ui-controlgroup > .ui-controlgroup-item.ui-visual-focus {
z-index: 9999;
}
.ui-controlgroup-vertical > .ui-controlgroup-item {
display: block;
float: none;
width: 100%;
margin-top: 0;
margin-bottom: 0;
text-align: left;
}
.ui-controlgroup-vertical .ui-controlgroup-item {
box-sizing: border-box;
}
.ui-controlgroup .ui-controlgroup-label {
padding: .4em 1em;
}
.ui-controlgroup .ui-controlgroup-label span {
font-size: 80%;
}
.ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item {
border-left: none;
}
.ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item {
border-top: none;
}
.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content {
border-right: none;
}
.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content {
border-bottom: none;
}
/* Spinner specific style fixes */
.ui-controlgroup-vertical .ui-spinner-input {
/* Support: IE8 only, Android < 4.4 only */
width: 75%;
width: calc( 100% - 2.4em );
}
.ui-controlgroup-vertical .ui-spinner .ui-spinner-up {
border-top-style: solid;
}
.ui-checkboxradio-label .ui-icon-background {
box-shadow: inset 1px 1px 1px #ccc;
border-radius: .12em;
border: none;
}
.ui-checkboxradio-radio-label .ui-icon-background {
width: 16px;
height: 16px;
border-radius: 1em;
overflow: visible;
border: none;
}
.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,
.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon {
background-image: none;
width: 8px;
height: 8px;
border-width: 4px;
border-style: solid;
}
.ui-checkboxradio-disabled {
pointer-events: none;
}
.ui-datepicker {
width: 17em;
padding: .2em .2em 0;
@@ -349,7 +451,7 @@ button.ui-button::-moz-focus-inner {
}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year {
width: 49%;
width: 45%;
}
.ui-datepicker table {
width: 100%;
@@ -462,8 +564,17 @@ button.ui-button::-moz-focus-inner {
border-right-width: 0;
border-left-width: 1px;
}
.ui-dialog {
/* Icons */
.ui-datepicker .ui-icon {
display: block;
text-indent: -99999px;
overflow: hidden;
background-repeat: no-repeat;
left: .5em;
top: .3em;
}
.ui-dialog {
position: absolute;
top: 0;
left: 0;
@@ -512,85 +623,48 @@ button.ui-button::-moz-focus-inner {
margin: .5em .4em .5em 0;
cursor: pointer;
}
.ui-dialog .ui-resizable-n {
height: 2px;
top: 0;
}
.ui-dialog .ui-resizable-e {
width: 2px;
right: 0;
}
.ui-dialog .ui-resizable-s {
height: 2px;
bottom: 0;
}
.ui-dialog .ui-resizable-w {
width: 2px;
left: 0;
}
.ui-dialog .ui-resizable-se,
.ui-dialog .ui-resizable-sw,
.ui-dialog .ui-resizable-ne,
.ui-dialog .ui-resizable-nw {
width: 7px;
height: 7px;
}
.ui-dialog .ui-resizable-se {
width: 12px;
height: 12px;
right: -5px;
bottom: -5px;
background-position: 16px 16px;
right: 0;
bottom: 0;
}
.ui-dialog .ui-resizable-sw {
left: 0;
bottom: 0;
}
.ui-dialog .ui-resizable-ne {
right: 0;
top: 0;
}
.ui-dialog .ui-resizable-nw {
left: 0;
top: 0;
}
.ui-draggable .ui-dialog-titlebar {
cursor: move;
}
.ui-menu {
list-style: none;
padding: 2px;
margin: 0;
display: block;
outline: none;
}
.ui-menu .ui-menu {
margin-top: -3px;
position: absolute;
}
.ui-menu .ui-menu-item {
margin: 0;
padding: 0;
width: 100%;
/* support: IE10, see #8844 */
list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
}
.ui-menu .ui-menu-divider {
margin: 5px -2px 5px -2px;
height: 0;
font-size: 0;
line-height: 0;
border-width: 1px 0 0 0;
}
.ui-menu .ui-menu-item a {
text-decoration: none;
display: block;
padding: 2px .4em;
line-height: 1.5;
min-height: 0; /* support: IE7 */
font-weight: normal;
}
.ui-menu .ui-menu-item a.ui-state-focus,
.ui-menu .ui-menu-item a.ui-state-active {
font-weight: normal;
margin: -1px;
}
.ui-menu .ui-state-disabled {
font-weight: normal;
margin: .4em 0 .2em;
line-height: 1.5;
}
.ui-menu .ui-state-disabled a {
cursor: default;
}
/* icon support */
.ui-menu-icons {
position: relative;
}
.ui-menu-icons .ui-menu-item a {
position: relative;
padding-left: 2em;
}
/* left-aligned */
.ui-menu .ui-icon {
position: absolute;
top: .2em;
left: .2em;
}
/* right-aligned */
.ui-menu .ui-menu-icon {
position: static;
float: right;
}
.ui-progressbar {
height: 2em;
text-align: left;
@@ -601,14 +675,54 @@ button.ui-button::-moz-focus-inner {
height: 100%;
}
.ui-progressbar .ui-progressbar-overlay {
background: url("images/animated-overlay.gif");
background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");
height: 100%;
filter: alpha(opacity=25);
filter: alpha(opacity=25); /* support: IE8 */
opacity: 0.25;
}
.ui-progressbar-indeterminate .ui-progressbar-value {
background-image: none;
}
.ui-selectmenu-menu {
padding: 0;
margin: 0;
position: absolute;
top: 0;
left: 0;
display: none;
}
.ui-selectmenu-menu .ui-menu {
overflow: auto;
overflow-x: hidden;
padding-bottom: 1px;
}
.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup {
font-size: 1em;
font-weight: bold;
line-height: 1.5;
padding: 2px 0.4em;
margin: 0.5em 0 0 0;
height: auto;
border: 0;
}
.ui-selectmenu-open {
display: block;
}
.ui-selectmenu-text {
display: block;
margin-right: 20px;
overflow: hidden;
text-overflow: ellipsis;
}
.ui-selectmenu-button.ui-button {
text-align: left;
white-space: nowrap;
width: 14em;
}
.ui-selectmenu-icon.ui-icon {
float: right;
margin-top: 0;
}
.ui-slider {
position: relative;
text-align: left;
@@ -619,6 +733,8 @@ button.ui-button::-moz-focus-inner {
width: 1.2em;
height: 1.2em;
cursor: default;
-ms-touch-action: none;
touch-action: none;
}
.ui-slider .ui-slider-range {
position: absolute;
@@ -629,7 +745,7 @@ button.ui-button::-moz-focus-inner {
background-position: 0 0;
}
/* For IE8 - See #6727 */
/* support: IE8 - See #6727 */
.ui-slider.ui-state-disabled .ui-slider-handle,
.ui-slider.ui-state-disabled .ui-slider-range {
filter: inherit;
@@ -683,14 +799,14 @@ button.ui-button::-moz-focus-inner {
border: none;
background: none;
color: inherit;
padding: 0;
padding: .222em 0;
margin: .2em 0;
vertical-align: middle;
margin-left: .4em;
margin-right: 22px;
margin-right: 2em;
}
.ui-spinner-button {
width: 16px;
width: 1.6em;
height: 50%;
font-size: .5em;
padding: 0;
@@ -704,16 +820,9 @@ button.ui-button::-moz-focus-inner {
}
/* more specificity required here to override default borders */
.ui-spinner a.ui-spinner-button {
border-top: none;
border-bottom: none;
border-right: none;
}
/* vertically center icon */
.ui-spinner .ui-icon {
position: absolute;
margin-top: -8px;
top: 50%;
left: 0;
border-top-style: none;
border-bottom-style: none;
border-right-style: none;
}
.ui-spinner-up {
top: 0;
@@ -721,12 +830,6 @@ button.ui-button::-moz-focus-inner {
.ui-spinner-down {
bottom: 0;
}
/* TR overrides */
.ui-spinner .ui-icon-triangle-1-s {
/* need to fix icons sprite */
background-position: -65px -16px;
}
.ui-tabs {
position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
padding: .2em;
@@ -773,8 +876,6 @@ button.ui-button::-moz-focus-inner {
position: absolute;
z-index: 9999;
max-width: 300px;
-webkit-box-shadow: 0 0 5px #aaa;
box-shadow: 0 0 5px #aaa;
}
body .ui-tooltip {
border-width: 2px;
@@ -796,9 +897,12 @@ body .ui-tooltip {
font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;
font-size: 1em;
}
.ui-widget.ui-widget-content {
border: 1px solid #cccccc;
}
.ui-widget-content {
border: 1px solid #dddddd;
background: #eeeeee url("images/ui-bg_flat_100_eeeeee_40x100.png") 50% 50% repeat-x;
background: #eeeeee;
color: #333333;
}
.ui-widget-content a {
@@ -806,7 +910,7 @@ body .ui-tooltip {
}
.ui-widget-header {
border: 1px solid #F26522;
background: #E87C1E url("images/ui-bg_flat_35_E87C1E_40x100.png") 50% 50% repeat-x;
background: #E87C1E;
color: #ffffff;
font-weight: bold;
}
@@ -818,15 +922,25 @@ body .ui-tooltip {
----------------------------------*/
.ui-state-default,
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default {
.ui-widget-header .ui-state-default,
.ui-button,
/* We use html here because we need a greater specificity to make sure disabled
works properly when clicked or hovered */
html .ui-button.ui-state-disabled:hover,
html .ui-button.ui-state-disabled:active {
border: 1px solid #cccccc;
background: #f1f1f1 url("images/ui-bg_flat_100_f1f1f1_40x100.png") 50% 50% repeat-x;
background: #f1f1f1;
font-weight: bold;
color: #555555;
}
.ui-state-default a,
.ui-state-default a:link,
.ui-state-default a:visited {
.ui-state-default a:visited,
a.ui-button,
a:link.ui-button,
a:visited.ui-button,
.ui-button {
color: #555555;
text-decoration: none;
}
@@ -835,9 +949,11 @@ body .ui-tooltip {
.ui-widget-header .ui-state-hover,
.ui-state-focus,
.ui-widget-content .ui-state-focus,
.ui-widget-header .ui-state-focus {
.ui-widget-header .ui-state-focus,
.ui-button:hover,
.ui-button:focus {
border: 1px solid #E87C1E;
background: #fde17c url("images/ui-bg_flat_100_fde17c_40x100.png") 50% 50% repeat-x;
background: #fde17c;
font-weight: bold;
color: #E87C1E;
}
@@ -848,18 +964,32 @@ body .ui-tooltip {
.ui-state-focus a,
.ui-state-focus a:hover,
.ui-state-focus a:link,
.ui-state-focus a:visited {
.ui-state-focus a:visited,
a.ui-button:hover,
a.ui-button:focus {
color: #E87C1E;
text-decoration: none;
}
.ui-visual-focus {
box-shadow: 0 0 3px 1px rgb(94, 158, 214);
}
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active {
.ui-widget-header .ui-state-active,
a.ui-button:active,
.ui-button:active,
.ui-button.ui-state-active:hover {
border: 1px solid #E87C1E;
background: #ffffff url("images/ui-bg_flat_65_ffffff_40x100.png") 50% 50% repeat-x;
background: #ffffff;
font-weight: bold;
color: #E87C1E;
}
.ui-icon-background,
.ui-state-active .ui-icon-background {
border: #E87C1E;
background-color: #E87C1E;
}
.ui-state-active a,
.ui-state-active a:link,
.ui-state-active a:visited {
@@ -873,9 +1003,13 @@ body .ui-tooltip {
.ui-widget-content .ui-state-highlight,
.ui-widget-header .ui-state-highlight {
border: 1px solid #fed22f;
background: #ffe45c url("images/ui-bg_flat_75_ffe45c_40x100.png") 50% 50% repeat-x;
background: #ffe45c;
color: #363636;
}
.ui-state-checked {
border: 1px solid #fed22f;
background: #ffe45c;
}
.ui-state-highlight a,
.ui-widget-content .ui-state-highlight a,
.ui-widget-header .ui-state-highlight a {
@@ -907,18 +1041,18 @@ body .ui-tooltip {
.ui-widget-content .ui-priority-secondary,
.ui-widget-header .ui-priority-secondary {
opacity: .7;
filter:Alpha(Opacity=70);
filter:Alpha(Opacity=70); /* support: IE8 */
font-weight: normal;
}
.ui-state-disabled,
.ui-widget-content .ui-state-disabled,
.ui-widget-header .ui-state-disabled {
opacity: .35;
filter:Alpha(Opacity=35);
filter:Alpha(Opacity=35); /* support: IE8 */
background-image: none;
}
.ui-state-disabled .ui-icon {
filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */
}
/* Icons
@@ -936,41 +1070,48 @@ body .ui-tooltip {
.ui-widget-header .ui-icon {
background-image: url("images/ui-icons_ffffff_256x240.png");
}
.ui-state-default .ui-icon {
background-image: url("images/ui-icons_F26522_256x240.png");
}
.ui-state-hover .ui-icon,
.ui-state-focus .ui-icon {
.ui-state-focus .ui-icon,
.ui-button:hover .ui-icon,
.ui-button:focus .ui-icon {
background-image: url("images/ui-icons_E87C1E_256x240.png");
}
.ui-state-active .ui-icon {
.ui-state-active .ui-icon,
.ui-button:active .ui-icon {
background-image: url("images/ui-icons_E87C1E_256x240.png");
}
.ui-state-highlight .ui-icon {
.ui-state-highlight .ui-icon,
.ui-button .ui-state-highlight.ui-icon {
background-image: url("images/ui-icons_1c94c4_256x240.png");
}
.ui-state-error .ui-icon,
.ui-state-error-text .ui-icon {
background-image: url("images/ui-icons_ffd27a_256x240.png");
}
.ui-button .ui-icon {
background-image: url("images/ui-icons_F26522_256x240.png");
}
.ui-state-default .ui-icon{
background-image: url("images/ui-icons_F26522_256x240.png");
}
/* positioning */
.ui-icon-blank { background-position: 16px 16px; }
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-caret-1-n { background-position: 0 0; }
.ui-icon-caret-1-ne { background-position: -16px 0; }
.ui-icon-caret-1-e { background-position: -32px 0; }
.ui-icon-caret-1-se { background-position: -48px 0; }
.ui-icon-caret-1-s { background-position: -65px 0; }
.ui-icon-caret-1-sw { background-position: -80px 0; }
.ui-icon-caret-1-w { background-position: -96px 0; }
.ui-icon-caret-1-nw { background-position: -112px 0; }
.ui-icon-caret-2-n-s { background-position: -128px 0; }
.ui-icon-caret-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-s { background-position: -65px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
@@ -980,7 +1121,7 @@ body .ui-tooltip {
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-s { background-position: -65px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
@@ -992,7 +1133,7 @@ body .ui-tooltip {
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-n { background-position: 1px -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
@@ -1166,13 +1307,9 @@ body .ui-tooltip {
.ui-widget-overlay {
background: #666666 url("images/ui-bg_diagonals-thick_20_666666_40x40.png") 50% 50% repeat;
opacity: .5;
filter: Alpha(Opacity=50);
filter: Alpha(Opacity=50); /* support: IE8 */
}
.ui-widget-shadow {
margin: -5px 0 0 -5px;
padding: 5px;
background: #000000 url("images/ui-bg_flat_10_000000_40x100.png") 50% 50% repeat-x;
opacity: .2;
filter: Alpha(Opacity=20);
border-radius: 5px;
-webkit-box-shadow: -5px -5px 5px #000000;
box-shadow: -5px -5px 5px #000000;
}

View File

@@ -1,565 +0,0 @@
/*
* jQuery UI CSS Framework 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Theming/API
*/
/* Layout helpers
----------------------------------*/
.ui-helper-hidden { display: none; }
.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; }
.ui-helper-clearfix:after { clear: both; }
.ui-helper-clearfix { zoom: 1; }
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
/* Interaction Cues
----------------------------------*/
.ui-state-disabled { cursor: default !important; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
/*
* jQuery UI CSS Framework 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Theming/API
*
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
*/
/* Component containers
----------------------------------*/
.ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; }
.ui-widget .ui-widget { font-size: 1em; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; }
.ui-widget-content { border: 1px solid #dddddd; background: #eeeeee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x; color: #333333; }
.ui-widget-content a { color: #333333; }
.ui-widget-header { border: 1px solid #e78f08; background: #f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
.ui-widget-header a { color: #ffffff; }
/* Interaction states
----------------------------------*/
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1c94c4; }
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; }
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #c77405; }
.ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; }
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eb8f00; }
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; }
.ui-widget :active { outline: none; }
/* Interaction Cues
----------------------------------*/
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fed22f; background: #ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x; color: #363636; }
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat; color: #ffffff; }
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; }
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; }
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
.ui-state-default .ui-icon { background-image: url(images/ui-icons_ef8c08_256x240.png); }
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); }
.ui-state-active .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); }
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); }
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffd27a_256x240.png); }
/* positioning */
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-off { background-position: -96px -144px; }
.ui-icon-radio-on { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
/* Misc visuals
----------------------------------*/
/* Corner radius */
.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; }
.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; }
.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
/* Overlays */
.ui-widget-overlay { background: #666666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat; opacity: .50;filter:Alpha(Opacity=50); }
.ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -khtml-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/*
* jQuery UI Resizable 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Resizable#theming
*/
.ui-resizable { position: relative;}
.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; }
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
* jQuery UI Selectable 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Selectable#theming
*/
.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
/*
* jQuery UI Accordion 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Accordion#theming
*/
/* IE/Win - Fix animation bug - #4615 */
.ui-accordion { width: 100%; }
.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
.ui-accordion .ui-accordion-li-fix { display: inline; }
.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
.ui-accordion .ui-accordion-content-active { display: block; }
/*
* jQuery UI Autocomplete 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Autocomplete#theming
*/
.ui-autocomplete { position: absolute; cursor: default; }
/* workarounds */
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
/*
* jQuery UI Menu 1.8.17
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Menu#theming
*/
.ui-menu {
list-style:none;
padding: 2px;
margin: 0;
display:block;
float: left;
}
.ui-menu .ui-menu {
margin-top: -3px;
}
.ui-menu .ui-menu-item {
margin:0;
padding: 0;
zoom: 1;
float: left;
clear: left;
width: 100%;
}
.ui-menu .ui-menu-item a {
text-decoration:none;
display:block;
padding:.2em .4em;
line-height:1.5;
zoom:1;
}
.ui-menu .ui-menu-item a.ui-state-hover,
.ui-menu .ui-menu-item a.ui-state-active {
font-weight: normal;
margin: -1px;
}
/*
* jQuery UI Button 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Button#theming
*/
.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
.ui-button-icons-only { width: 3.4em; }
button.ui-button-icons-only { width: 3.7em; }
/*button text element */
.ui-button .ui-button-text { display: block; line-height: 1.4; }
.ui-button-text-only .ui-button-text { padding: .4em 1em; }
.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
/* no icon support for input elements, provide padding by default */
input.ui-button { padding: .4em 1em; }
/*button icon element(s) */
.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
/*button sets*/
.ui-buttonset { margin-right: 7px; }
.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
/* workarounds */
button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
/*
* jQuery UI Dialog 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Dialog#theming
*/
.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }
/*
* jQuery UI Slider 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Slider#theming
*/
.ui-slider { position: relative; text-align: left; }
.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
.ui-slider-horizontal { height: .8em; }
.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
.ui-slider-horizontal .ui-slider-range-min { left: 0; }
.ui-slider-horizontal .ui-slider-range-max { right: 0; }
.ui-slider-vertical { width: .8em; height: 100px; }
.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
.ui-slider-vertical .ui-slider-range-max { top: 0; }/*
* jQuery UI Tabs 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Tabs#theming
*/
.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
.ui-tabs .ui-tabs-hide { display: none !important; }
/*
* jQuery UI Datepicker 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Datepicker#theming
*/
.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
.ui-datepicker .ui-datepicker-prev { left:2px; }
.ui-datepicker .ui-datepicker-next { right:2px; }
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year { width: 49%;}
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
.ui-datepicker td { border: 0; padding: 1px; }
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi { width:auto; }
.ui-datepicker-multi .ui-datepicker-group { float:left; }
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
/* RTL support */
.ui-datepicker-rtl { direction: rtl; }
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
.ui-datepicker-cover {
display: none; /*sorry for IE5*/
display/**/: block; /*sorry for IE5*/
position: absolute; /*must have*/
z-index: -1; /*must have*/
filter: mask(); /*must have*/
top: -4px; /*must have*/
left: -4px; /*must have*/
width: 200px; /*must have*/
height: 200px; /*must have*/
}/*
* jQuery UI Progressbar 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Progressbar#theming
*/
.ui-progressbar { height:2em; text-align: left; overflow: hidden; }
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }

View File

@@ -1,489 +0,0 @@
/*
* jQuery UI CSS Framework
* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
*/
/* Layout helpers
----------------------------------*/
.ui-helper-hidden { display: none; }
.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
.ui-helper-clearfix { display: inline-block; }
/* required comment for clearfix to work in Opera \*/
* html .ui-helper-clearfix { height:1%; }
.ui-helper-clearfix { display:block; }
/* end clearfix */
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
/* Interaction Cues
----------------------------------*/
.ui-state-disabled { cursor: default !important; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
/*
* jQuery UI CSS Framework
* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
*/
/* Component containers
----------------------------------*/
.ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; }
.ui-widget .ui-widget { font-size: 1em; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; }
.ui-widget-content { border: 1px solid #dddddd; background: #eeeeee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x; color: #333333; }
.ui-widget-content a { color: #333333; }
.ui-widget-header { border: 1px solid #e78f08; background: #f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
.ui-widget-header a { color: #ffffff; }
/* Interaction states
----------------------------------*/
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1c94c4; }
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; }
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #c77405; }
.ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; }
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eb8f00; }
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; }
.ui-widget :active { outline: none; }
/* Interaction Cues
----------------------------------*/
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fed22f; background: #ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x; color: #363636; }
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat; color: #ffffff; }
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; }
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; }
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
.ui-state-default .ui-icon { background-image: url(images/ui-icons_ef8c08_256x240.png); }
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); }
.ui-state-active .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); }
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); }
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffd27a_256x240.png); }
/* positioning */
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-off { background-position: -96px -144px; }
.ui-icon-radio-on { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
/* Misc visuals
----------------------------------*/
/* Corner radius */
.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; }
.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; }
.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; }
.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
.ui-corner-right { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
.ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; }
/* Overlays */
.ui-widget-overlay { background: #666666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat; opacity: .50;filter:Alpha(Opacity=50); }
.ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/* Resizable
----------------------------------*/
.ui-resizable { position: relative;}
.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Selectable
----------------------------------*/
.ui-selectable-helper { border:1px dotted black }
/* Accordion
----------------------------------*/
.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
.ui-accordion .ui-accordion-li-fix { display: inline; }
.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
/* IE7-/Win - Fix extra vertical space in lists */
.ui-accordion a { zoom: 1; }
.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
.ui-accordion .ui-accordion-content-active { display: block; }/* Autocomplete
----------------------------------*/
.ui-autocomplete { position: absolute; cursor: default; }
.ui-autocomplete-loading { background: white url('images/ui-anim_basic_16x16.gif') right center no-repeat; }
/* workarounds */
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
/* Menu
----------------------------------*/
.ui-menu {
list-style:none;
padding: 2px;
margin: 0;
display:block;
}
.ui-menu .ui-menu {
margin-top: -3px;
}
.ui-menu .ui-menu-item {
margin:0;
padding: 0;
zoom: 1;
float: left;
clear: left;
width: 100%;
}
.ui-menu .ui-menu-item a {
text-decoration:none;
display:block;
padding:.2em .4em;
line-height:1.5;
zoom:1;
}
.ui-menu .ui-menu-item a.ui-state-hover,
.ui-menu .ui-menu-item a.ui-state-active {
font-weight: normal;
margin: -1px;
}
/* Button
----------------------------------*/
.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
.ui-button-icons-only { width: 3.4em; }
button.ui-button-icons-only { width: 3.7em; }
/*button text element */
.ui-button .ui-button-text { display: block; line-height: 1.4; }
.ui-button-text-only .ui-button-text { padding: .4em 1em; }
.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
.ui-button-text-icon .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
/* no icon support for input elements, provide padding by default */
input.ui-button { padding: .4em 1em; }
/*button icon element(s) */
.ui-button-icon-only .ui-icon, .ui-button-text-icon .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
.ui-button-text-icon .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
/*button sets*/
.ui-buttonset { margin-right: 7px; }
.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
/* workarounds */
button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
/* Dialog
----------------------------------*/
.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
.ui-dialog .ui-dialog-titlebar { padding: .5em 1em .3em; position: relative; }
.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; }
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
.ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }
/* Slider
----------------------------------*/
.ui-slider { position: relative; text-align: left; }
.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
.ui-slider-horizontal { height: .8em; }
.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
.ui-slider-horizontal .ui-slider-range-min { left: 0; }
.ui-slider-horizontal .ui-slider-range-max { right: 0; }
.ui-slider-vertical { width: .8em; height: 100px; }
.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
.ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs
----------------------------------*/
.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
.ui-tabs .ui-tabs-hide { display: none !important; }
/* Datepicker
----------------------------------*/
.ui-datepicker { width: 17em; padding: .2em .2em 0; }
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
.ui-datepicker .ui-datepicker-prev { left:2px; }
.ui-datepicker .ui-datepicker-next { right:2px; }
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year { width: 49%;}
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
.ui-datepicker td { border: 0; padding: 1px; }
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi { width:auto; }
.ui-datepicker-multi .ui-datepicker-group { float:left; }
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
.ui-datepicker-row-break { clear:both; width:100%; }
/* RTL support */
.ui-datepicker-rtl { direction: rtl; }
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
.ui-datepicker-cover {
display: none; /*sorry for IE5*/
display/**/: block; /*sorry for IE5*/
position: absolute; /*must have*/
z-index: -1; /*must have*/
filter: mask(); /*must have*/
top: -4px; /*must have*/
left: -4px; /*must have*/
width: 200px; /*must have*/
height: 200px; /*must have*/
}/* Progressbar
----------------------------------*/
.ui-progressbar { height:2em; text-align: left; }
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }

View File

@@ -37,7 +37,7 @@ class UserExternal extends User
{
$aParams = array
(
"category" => "addon/authentication",
"category" => "addon/authentication,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",

View File

@@ -27,7 +27,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-external/2.4.0',
'authent-external/2.5.0',
array(
// Identification
//

View File

@@ -42,10 +42,7 @@
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:UserLDAP' => 'Usuario LDAP',
'Class:UserLDAP+' => 'Usuario Autenticado vía LDAP',
'Class:UserLDAP/Attribute:password' => 'Contrase&ntilde;a',
'Class:UserLDAP/Attribute:password+' => 'Contrase&ntilde;a',
'Class:UserLDAP/Attribute:password' => 'Contraseña',
'Class:UserLDAP/Attribute:password+' => 'Contraseña',
));
?>

View File

@@ -32,7 +32,7 @@ class UserLDAP extends UserInternal
{
$aParams = array
(
"category" => "addon/authentication",
"category" => "addon/authentication,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",
@@ -46,11 +46,10 @@ class UserLDAP extends UserInternal
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login', 'status')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'status', 'org_id')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status')); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', array('login', 'contactid')); // Criteria of the advanced search form
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status', 'org_id')); // Criteria of the std search form
}
/**

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.4.0',
'authent-ldap/2.5.0',
array(
// Identification
//

View File

@@ -42,10 +42,6 @@
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:UserLocal' => 'Usuario de iTop',
'Class:UserLocal+' => 'Usuario Autenticado vía iTop',
'Class:UserLocal/Attribute:password' => 'Contrase&ntilde;a',
'Class:UserLocal/Attribute:password+' => 'Contrase&ntilde;a',
'Class:UserLocal/Attribute:password' => 'Contraseña',
'Class:UserLocal/Attribute:password+' => 'Contraseña',
));
?>

View File

@@ -32,7 +32,7 @@ class UserLocal extends UserInternal
{
$aParams = array
(
"category" => "addon/authentication",
"category" => "addon/authentication,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",
@@ -48,11 +48,10 @@ class UserLocal extends UserInternal
MetaModel::Init_AddAttribute(new AttributeOneWayPassword("password", array("allowed_values"=>null, "sql"=>"pwd", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'password', 'language', 'status', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'password', 'language', 'status', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login', 'org_id')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status')); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', array('login', 'contactid')); // Criteria of the advanced search form
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status', 'org_id')); // Criteria of the std search form
}
public function CheckCredentials($sPassword)

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.4.0',
'authent-local/2.5.0',
array(
// Identification
//

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.4">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.5">
<classes>
<class id="Attachment" _delta="define">
<parent>DBObject</parent>

View File

@@ -34,12 +34,11 @@ Dict::Add('ES CR', 'Spanish', 'Espa
'Attachments:History_File_Removed' => 'Anexo %1$s removido.',
'Attachments:AddAttachment' => 'Agregar Anexo: ',
'Attachments:UploadNotAllowedOnThisSystem' => 'La carga de archivos NO está permitida en este sistema.',
'Attachment:Max_Go' => '(Tama&ntilde;o M&aacute;ximo de Archivo: %1$s Gb)',
'Attachment:Max_Mo' => '(Tama&ntilde;o M&aacute;ximo de Archivo: %1$s Mb)',
'Attachment:Max_Ko' => '(Tama&ntilde;o M&aacute;ximo de Archivo: %1$s Kb)',
'Attachment:Max_Go' => '(Tamaño Máximo de Archivo: %1$s Gb)',
'Attachment:Max_Mo' => '(Tamaño Máximo de Archivo: %1$s Mb)',
'Attachment:Max_Ko' => '(Tamaño Máximo de Archivo: %1$s Kb)',
'Attachments:NoAttachment' => 'No hay Anexo. ',
'Class:Attachment' => 'Anexo',
'Class:Attachment+' => 'Anexo',
'Attachments:PreviewNotAvailable' => 'Preview not available for this type of attachment.~~',
'Attachments:PreviewNotAvailable' => 'Vista preliminar no disponible para este tipo de Anexo.',
));
?>

View File

@@ -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.4.0',
'itop-attachments/2.5.0',
array(
// Identification
//

View File

@@ -35,148 +35,157 @@ try
{
$sOperation = utils::ReadParam('operation', '');
switch($sOperation)
switch ($sOperation)
{
case 'backup':
require_once(APPROOT.'/application/startup.inc.php');
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin)
require_once(APPROOT.'/application/startup.inc.php');
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(); // Check user rights and prompt if needed
ApplicationMenu::CheckMenuIdEnabled('BackupStatus');
$oPage = new ajax_page("");
$oPage->no_cache();
$oPage->SetContentType('text/html');
$oPage = new ajax_page("");
$oPage->no_cache();
$oPage->SetContentType('text/html');
if (utils::GetConfig()->Get('demo_mode'))
{
$oPage->add("<div data-error-stimulus=\"Error\">Sorry, iTop is in <b>demonstration mode</b>: the feature is disabled.</div>");
}
else
{
try
if (utils::GetConfig()->Get('demo_mode'))
{
set_time_limit(0);
$oBB = new BackupExec(APPROOT.'data/backups/manual/', 0 /*iRetentionCount*/);
$sRes = $oBB->Process(time() + 36000); // 10 hours to complete should be sufficient!
$oPage->add("<div data-error-stimulus=\"Error\">Sorry, iTop is in <b>demonstration mode</b>: the feature is disabled.</div>");
}
catch (Exception $e)
else
{
$oPage->p('Error: '.$e->getMessage());
IssueLog::Error($sOperation.' - '.$e->getMessage());
try
{
set_time_limit(0);
$oBB = new BackupExec(APPROOT.'data/backups/manual/', 0 /*iRetentionCount*/);
$sRes = $oBB->Process(time() + 36000); // 10 hours to complete should be sufficient!
}
catch (Exception $e)
{
$oPage->p('Error: '.$e->getMessage());
IssueLog::Error($sOperation.' - '.$e->getMessage());
}
}
}
$oPage->output();
break;
$oPage->output();
break;
/*
* Fix a token :
* We can't load the MetaModel because in DBRestore, after restore is done we're launching a compile !
* So as \LoginWebPage::DoLogin needs a loaded DataModel, we can't use it
* As a result we're setting a token file to make sure the restore is called by an authenticated user with the correct rights !
*/
case 'restore_get_token':
require_once(APPROOT.'/application/startup.inc.php');
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin)
require_once(APPROOT.'/application/startup.inc.php');
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(); // Check user rights and prompt if needed
ApplicationMenu::CheckMenuIdEnabled('BackupStatus');
$oPage = new ajax_page("");
$oPage->no_cache();
$oPage->SetContentType('text/html');
$oPage = new ajax_page("");
$oPage->no_cache();
$oPage->SetContentType('text/html');
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
if (!$oRestoreMutex->IsLocked())
{
$sFile = utils::ReadParam('file', '', false, 'raw_data');
$sToken = str_replace(' ', '', (string)microtime());
$sTokenFile = APPROOT.'/data/restore.'.$sToken.'.tok';
file_put_contents($sTokenFile, $sFile);
$oPage->add_ready_script(
<<<EOF
$("#restore_token").val('$sToken');
EOF
);
}
else
{
$oPage->p(Dict::S('bkp-restore-running'));
}
$oPage->output();
break;
case 'restore_exec':
require_once(APPROOT."setup/runtimeenv.class.inc.php");
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/setup/backup.class.inc.php');
require_once(dirname(__FILE__).'/dbrestore.class.inc.php');
IssueLog::Enable(APPROOT.'log/error.log');
$oPage = new ajax_page("");
$oPage->no_cache();
$oPage->SetContentType('text/html');
if (utils::GetConfig()->Get('demo_mode'))
{
$oPage->add("<div data-error-stimulus=\"Error\">Sorry, iTop is in <b>demonstration mode</b>: the feature is disabled.</div>");
}
else
{
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
IssueLog::Info("Backup Restore - Acquiring the LOCK 'restore.$sEnvironment'");
$oRestoreMutex->Lock();
IssueLog::Info('Backup Restore - LOCK acquired, executing...');
try
if (!$oRestoreMutex->IsLocked())
{
set_time_limit(0);
// Get the file and destroy the token (single usage)
$sToken = utils::ReadParam('token', '', false, 'raw_data');
$sFile = utils::ReadParam('file', '', false, 'raw_data');
$sToken = str_replace(' ', '', (string)microtime());
$sTokenFile = APPROOT.'/data/restore.'.$sToken.'.tok';
if (!is_file($sTokenFile))
{
throw new Exception("Error: missing token file: '$sTokenFile'");
}
$sFile = file_get_contents($sTokenFile);
unlink($sTokenFile);
$sMySQLBinDir = utils::ReadParam('mysql_bindir', '', false, 'raw_data');
$sDBHost = utils::ReadParam('db_host', '', false, 'raw_data');
$sDBUser = utils::ReadParam('db_user', '', false, 'raw_data');
$sDBPwd = utils::ReadParam('db_pwd', '', false, 'raw_data');
$sDBName = utils::ReadParam('db_name', '', false, 'raw_data');
$sDBSubName = utils::ReadParam('db_subname', '', false, 'raw_data');
$oDBRS = new DBRestore($sDBHost, $sDBUser, $sDBPwd, $sDBName, $sDBSubName);
$oDBRS->SetMySQLBinDir($sMySQLBinDir);
$sBackupDir = APPROOT.'data/backups/';
$sBackupFile = $sBackupDir.$sFile;
$sRes = $oDBRS->RestoreFromCompressedBackup($sBackupFile, $sEnvironment);
IssueLog::Info('Backup Restore - Done, releasing the LOCK');
$oRestoreMutex->Unlock();
file_put_contents($sTokenFile, $sFile);
$oPage->add_ready_script(
<<<EOF
$("#restore_token").val('$sToken');
EOF
);
}
catch (Exception $e)
else
{
$oRestoreMutex->Unlock();
$oPage->p('Error: '.$e->getMessage());
IssueLog::Error($sOperation.' - '.$e->getMessage());
$oPage->p(Dict::S('bkp-restore-running'));
}
}
$oPage->output();
break;
$oPage->output();
break;
/*
* We can't call \LoginWebPage::DoLogin because DBRestore will do a compile after restoring the DB
* Authentication is checked with a token file (see $sOperation='restore_get_token')
*/
case 'restore_exec':
require_once(APPROOT."setup/runtimeenv.class.inc.php");
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/setup/backup.class.inc.php');
require_once(dirname(__FILE__).'/dbrestore.class.inc.php');
IssueLog::Enable(APPROOT.'log/error.log');
$oPage = new ajax_page("");
$oPage->no_cache();
$oPage->SetContentType('text/html');
if (utils::GetConfig()->Get('demo_mode'))
{
$oPage->add("<div data-error-stimulus=\"Error\">Sorry, iTop is in <b>demonstration mode</b>: the feature is disabled.</div>");
}
else
{
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
IssueLog::Info("Backup Restore - Acquiring the LOCK 'restore.$sEnvironment'");
$oRestoreMutex->Lock();
IssueLog::Info('Backup Restore - LOCK acquired, executing...');
try
{
set_time_limit(0);
// Get the file and destroy the token (single usage)
$sToken = utils::ReadParam('token', '', false, 'raw_data');
$sTokenFile = APPROOT.'/data/restore.'.$sToken.'.tok';
if (!is_file($sTokenFile))
{
throw new Exception("Error: missing token file: '$sTokenFile'");
}
$sFile = file_get_contents($sTokenFile);
unlink($sTokenFile);
// Loading config file : we don't have the MetaModel but we have the current env !
$sConfigFilePath = utils::GetConfigFilePath($sEnvironment);
$oItopConfig = new Config($sConfigFilePath, true);
$sMySQLBinDir = $oItopConfig->GetModuleSetting('itop-backup', 'mysql_bindir', '');
$oDBRS = new DBRestore($oItopConfig);
$oDBRS->SetMySQLBinDir($sMySQLBinDir);
$sBackupDir = APPROOT.'data/backups/';
$sBackupFile = $sBackupDir.$sFile;
$sRes = $oDBRS->RestoreFromCompressedBackup($sBackupFile, $sEnvironment);
IssueLog::Info('Backup Restore - Done, releasing the LOCK');
$oRestoreMutex->Unlock();
}
catch (Exception $e)
{
$oRestoreMutex->Unlock();
$oPage->p('Error: '.$e->getMessage());
IssueLog::Error($sOperation.' - '.$e->getMessage());
}
}
$oPage->output();
break;
case 'download':
require_once(APPROOT.'/application/startup.inc.php');
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin)
require_once(APPROOT.'/application/startup.inc.php');
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(); // Check user rights and prompt if needed
ApplicationMenu::CheckMenuIdEnabled('BackupStatus');
if (utils::GetConfig()->Get('demo_mode'))
{
throw new Exception('iTop is in demonstration mode: the feature is disabled');
}
$sFile = utils::ReadParam('file', '', false, 'raw_data');
$oBackup = new DBBackupScheduled();
$sBackupDir = APPROOT.'data/backups/';
$oBackup->DownloadBackup($sBackupDir.$sFile);
break;
if (utils::GetConfig()->Get('demo_mode'))
{
throw new Exception('iTop is in demonstration mode: the feature is disabled');
}
$sFile = utils::ReadParam('file', '', false, 'raw_data');
$oBackup = new DBBackupScheduled();
$sBackupDir = APPROOT.'data/backups/';
$oBackup->DownloadBackup($sBackupDir.$sFile);
break;
}
}
catch (Exception $e)

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2014-2017 Combodo SARL
// Copyright (C) 2014-2018 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -28,6 +28,9 @@ require_once(APPROOT.'application/startup.inc.php');
class MyDBBackup extends DBBackup
{
/** @var Page used to send log */
protected $oPage;
protected function LogInfo($sMsg)
{
$this->oPage->p($sMsg);
@@ -39,7 +42,6 @@ class MyDBBackup extends DBBackup
ToolsLog::Error($sMsg);
}
protected $oPage;
public function __construct($oPage)
{
$this->oPage = $oPage;

View File

@@ -86,10 +86,10 @@ function MakeArchiveFileName($iRefTime = null)
$sBackupFile = utils::ReadParam('backup_file', $sDefaultBackupFileName, true, 'raw_data');
$oConfig = new Config(APPCONF.'production/config-itop.php');
$sBackupFile = str_replace('__HOST__', $oConfig->GetDBHost(), $sBackupFile);
$sBackupFile = str_replace('__DB__', $oConfig->GetDBName(), $sBackupFile);
$sBackupFile = str_replace('__SUBNAME__', $oConfig->GetDBSubName(), $sBackupFile);
$sBackupFile = str_replace('__HOST__', $oConfig->Get('db_host'), $sBackupFile);
$sBackupFile = str_replace('__DB__', $oConfig->Get('db_name'), $sBackupFile);
$sBackupFile = str_replace('__SUBNAME__', $oConfig->Get('db_subname'), $sBackupFile);
if (is_null($iRefTime))
{

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