Compare commits

...

171 Commits

Author SHA1 Message Date
Denis Flaven
62510e2b04 Fix for Trac #670: XSS vulnerability issue.
SVN:1.2[2588]
2013-01-22 17:38:01 +00:00
Denis Flaven
f33c821306 Merged, the implementation of Track #582: "stable name" for synchro_data_xxx tables
SVN:1.2[2404]
2012-10-29 15:36:55 +00:00
Denis Flaven
11d0e9f52b Do not perform time consuming computations for building the menus if there are too many objects in a list (limit is configurable).
SVN:1.2[2344]
2012-10-24 14:04:57 +00:00
Denis Flaven
3330fd79d0 Bug fix: regression introduced by revision 2278.
SVN:1.2[2340]
2012-10-24 13:20:26 +00:00
Denis Flaven
02e0a61dcf Optimization to speed-up the "group-by" tables. (The complete solution is implemented in 2.0)
SVN:1.2[2338]
2012-10-24 13:13:15 +00:00
Romain Quetiez
2bb8028d4d Fixed regression (CSV import - unknown method utils::GetConfig)
SVN:1.2[2322]
2012-10-22 08:07:46 +00:00
Romain Quetiez
891b22909a #583 Losing attachments when performing massive change
SVN:1.2[2286]
2012-10-18 09:39:37 +00:00
Denis Flaven
463748b6da Properly handle all types of fields when entering values via the state transition "wizard" screen
SVN:1.2[2279]
2012-10-17 14:05:10 +00:00
Denis Flaven
fc2878b6d1 Properly handle a change of an external key...
SVN:1.2[2276]
2012-10-17 13:25:32 +00:00
Romain Quetiez
17919f5389 Portal: enable adding dependent attributes in the request creation form -reintegrated from trunk
SVN:1.2[2274]
2012-10-17 12:54:19 +00:00
Romain Quetiez
e54b1d2ff4 Portal: enable adding dependent attributes in the request creation form -reintegrated from trunk
SVN:1.2[2271]
2012-10-17 12:17:28 +00:00
Romain Quetiez
523fb8bd25 Fixed issue in the portal: the list of opened requests and closed request where messed up when pagination was activated on both lists -reintegrated from trunk
SVN:1.2[2268]
2012-10-17 09:12:01 +00:00
Romain Quetiez
fad77fb9fa CSV import: added a flag to disable the history tab (too long to display, when the feature is heavily used), reintegrated from trunk
SVN:1.2[2265]
2012-10-16 14:47:22 +00:00
Erwan Taloc
8da92c9251 add iTop favicon
SVN:1.2[2171]
2012-09-10 09:39:31 +00:00
Erwan Taloc
977d616ef2 Rename translation of Friendly name attribute to Friendly Name
SVN:1.2[2148]
2012-08-09 12:59:44 +00:00
Romain Quetiez
29faa739c1 Reintegrated fixes from branch 1.2.1 (2116,2118,2119)
- HTML attributes > 64 Kb
- Log of notification displayed as HTML

SVN:1.2[2120]
2012-07-04 09:23:12 +00:00
Denis Flaven
b986f63a67 Bug fix: $this->label(attcode)$ used inside Email Notifications should not contain HTML entities since this not an HTML fragment.
SVN:1.2[2117]
2012-06-29 15:11:35 +00:00
Denis Flaven
8fa3a6d47d Localization of the title of the login page
SVN:1.2[2112]
2012-06-15 13:30:07 +00:00
Denis Flaven
0e6876b068 Localization of the title of the login page
SVN:1.2[2111]
2012-06-15 13:27:16 +00:00
Denis Flaven
8d962f4bdb Fix for Trac #559: ldap user can login with blank password
SVN:1.2[2109]
2012-06-14 16:28:14 +00:00
Denis Flaven
3b8f945c44 Fixed Trac #558: properly parse strings containing hexadecimal sequences (i.e. 'QWERTY0xCUIOP').
Note that for now hexadecimal numbers are parsed but not interpreted properly...

SVN:1.2[2104]
2012-06-14 09:22:01 +00:00
Romain Quetiez
9600c89a1f Fixed regression (See #556) due to the existence of an overload of the protected API GetUserOrgs, reintegrated from branch 1.2.1
SVN:1.2[2100]
2012-06-14 09:12:25 +00:00
Denis Flaven
d01202ba33 Reload the impact/depends on graph only on demand for better performance, via the new Refresh button
SVN:1.2[2088]
2012-06-07 13:57:17 +00:00
Romain Quetiez
1bc1a0a1b2 #556 Merged in branch 1.2
SVN:1.2[2083]
2012-06-05 15:46:24 +00:00
Romain Quetiez
37ea4cb5e3 Reintegrated changes from trunk, to uncompile legacy data models
SVN:1.2[2080]
2012-06-05 11:18:10 +00:00
Denis Flaven
c4a003f620 Make the 'filter' conditions on ExtKey applicable also when searching on derived classes.
SVN:1.2[1991]
2012-05-16 12:23:54 +00:00
Denis Flaven
7a8ee0353a Protects against too long strings when logging web services events
SVN:1.2[1961]
2012-04-19 11:17:20 +00:00
Romain Quetiez
6b526ba455 #541 Fixed bug in the export for spreadsheet (time format)
SVN:1.2[1946]
2012-04-06 09:41:43 +00:00
Denis Flaven
9d9b923b7e Properly log-off (and report the issue in the log) in case we fail to create a user during the CAS Synchro
SVN:1.2[1942]
2012-04-04 10:09:07 +00:00
Erwan Taloc
fcaad0cd07 Add web link to link class in schema.php for an attribute linkset
SVN:1.2[1937]
2012-04-03 11:18:23 +00:00
Romain Quetiez
da875dd945 #540 Data synchro: the option "write if empty" was not implemented
SVN:1.2[1933]
2012-03-29 13:32:35 +00:00
Denis Flaven
2a71bf5008 Bug fix: to do not try to access a DataSource while it's being deleted
SVN:1.2[1929]
2012-03-27 11:26:31 +00:00
Denis Flaven
9b36ebc106 Bug fix: support [+] button inside linkedsets.. with constraints
SVN:1.2[1926]
2012-03-22 17:19:06 +00:00
Denis Flaven
1a507b7aa4 CAS integration. Done.
SVN:1.2[1921]
2012-03-22 15:30:23 +00:00
Denis Flaven
178ee28596 CAS integration continuing
SVN:1.2[1919]
2012-03-22 15:16:22 +00:00
Denis Flaven
e44a5d2980 CAS integration:
- regression fix: support patterns for the MemberOf groups filtering
- activate/de-activate the profiles synchronization using the 'cas_update_profiles' configuration flag
- provide default profile(s) when creating a new user from CAS, only if no match is found for assigning profiles from the CAS MemberOf group(s).

SVN:1.2[1917]
2012-03-22 10:53:09 +00:00
Denis Flaven
828c02db0b Bug fix: support [+] button inside linkedsets.. with constraints
SVN:1.2[1914]
2012-03-22 09:38:03 +00:00
Denis Flaven
d1e4e2109f Bug fix: support [+] button inside linkedsets.. with constraints
SVN:1.2[1913]
2012-03-22 09:35:25 +00:00
Denis Flaven
b7a9b340b8 Bug fix: support [+] button inside linkedsets.. with constraints
SVN:1.2[1912]
2012-03-22 09:11:45 +00:00
Denis Flaven
3731cf6dc1 The date picker fills the "time" part of the field with 00:00:00 when picking a DateTime instead of just a Date.
SVN:1.2[1910]
2012-03-21 10:53:33 +00:00
Denis Flaven
0e7fc5e5c4 Bug fix: support [+] button inside linkedsets
SVN:1.2[1908]
2012-03-21 10:36:55 +00:00
Denis Flaven
bc62c06894 Rollback of the modification: For forward compatibility... It was already implemented !
SVN:1.2[1907]
2012-03-21 10:28:45 +00:00
Denis Flaven
4ed85c23de Allow to specify the name of the PDF to download
SVN:1.2[1905]
2012-03-20 16:55:20 +00:00
Denis Flaven
b6c1347f27 For forward compatibility...
SVN:1.2[1904]
2012-03-20 16:54:13 +00:00
Denis Flaven
237eefcbc6 LinkedSets can be read-only too...
SVN:1.2[1898]
2012-03-19 17:17:45 +00:00
Denis Flaven
5ee3c69898 Make the class "TriggerOnPortalUpdate" importable
SVN:1.2[1896]
2012-03-16 12:58:23 +00:00
Denis Flaven
53b3ae8016 Enhanced fix for Trac #503. Don't drop the column before re-creating it, in case the data can be converted by MySQL.
SVN:1.2[1892]
2012-03-14 16:15:53 +00:00
Denis Flaven
26b6bfaf7f Added detecting of missing columns in the synchro_data_xxx tables (in case of duplicate SQL column names in the orignal data model). See Trac #503.
SVN:1.2[1891]
2012-03-14 16:02:39 +00:00
Denis Flaven
1a659cc4d0 Experimental support of PDF output for iTop pages, provided that mPDF is installed in lib/MPDF
SVN:1.2[1876]
2012-03-07 16:40:22 +00:00
Denis Flaven
b000900d6c Stylesheet enhancements to support printing...
SVN:1.2[1875]
2012-03-07 14:42:28 +00:00
Denis Flaven
f68ec1cef1 Typo
SVN:1.2[1874]
2012-03-04 18:13:06 +00:00
Denis Flaven
3fb867d393 - Current block Id not passed to the chart ?
SVN:1.2[1873]
2012-03-04 17:53:28 +00:00
Denis Flaven
fe559eb492 Small fix for genericity
SVN:1.2[1872]
2012-03-04 17:51:53 +00:00
Denis Flaven
0041afd6d0 - Current block Id not passed to the chart ?
SVN:1.2[1871]
2012-03-04 17:48:03 +00:00
Denis Flaven
dc1b5b0d4c - Bug fix: the hierarchical key in Organizations is not always named 'parent_id' !
SVN:1.2[1868]
2012-02-27 16:15:08 +00:00
Denis Flaven
10a930b7b2 Typo!
SVN:1.2[1862]
2012-02-21 14:03:35 +00:00
Denis Flaven
72b6089db8 Added the ability to Find then Remove a tab inside a page
SVN:1.2[1860]
2012-02-20 17:13:53 +00:00
Denis Flaven
0cfb1c3a83 Fix in case there is only one non-shared organization.
SVN:1.2[1858]
2012-02-17 12:52:35 +00:00
Denis Flaven
36a73535a5 Delay the storage of the dictionary in the cache to allow for its alteration during the initialization of the classes
SVN:1.2[1856]
2012-02-17 12:47:58 +00:00
Denis Flaven
9382b89277 Distinguish between creation and modification rights
SVN:1.2[1854]
2012-02-17 12:43:55 +00:00
Romain Quetiez
6d14da15cf Implemented the capability to implemented a separate module for sharing objects between the silos:
+ possibility for a plugin to alter the definition of a class (add an attribute)
+ fixed a bug (low exposure) in the cache for MetaModel::GetObject()
+ possibility to have computed fields on links (list of fields in the form now based on the ZList 'list')

SVN:1.2[1853]
2012-02-17 09:35:06 +00:00
Denis Flaven
3113205f88 Allow to add some headers like content-type.
SVN:1.2[1852]
2012-02-10 12:42:34 +00:00
Denis Flaven
09bd8052d7 Allow more than 64K for the email body (including attachments)
SVN:1.2[1850]
2012-02-10 12:25:21 +00:00
Denis Flaven
85f0b79203 Update to the Italian translation
SVN:1.2[1848]
2012-02-08 14:39:30 +00:00
Romain Quetiez
81145d7b1c Improved the change tracking to simplify the development of plugins (1st step... still to be drastically simplified)
SVN:1.2[1847]
2012-02-08 14:24:45 +00:00
Denis Flaven
7e6d1c2ce4 Performance improvements of the autocomplete: don't trigger a search when there is no expression to search !
SVN:1.2[1846]
2012-02-07 13:34:40 +00:00
Denis Flaven
364259daa5 Restore the previous state of URLMaker after building a notification
SVN:1.2[1845]
2012-02-07 09:32:29 +00:00
Denis Flaven
7b270294f6 Fix for query modifiers plug-ins
SVN:1.2[1843]
2012-02-07 09:12:45 +00:00
Romain Quetiez
c7aa00e81a Implemented the capability to modify queries by the mean of a plugin (make it work with APC cache)
SVN:1.2[1842]
2012-02-06 10:54:35 +00:00
Romain Quetiez
f9e7446e7b Reverted (removed test code)
SVN:1.2[1841]
2012-02-03 17:22:54 +00:00
Romain Quetiez
493ab80965 Implemented the capability to modify queries by the mean of a plugin (beta)
SVN:1.2[1840]
2012-02-03 17:16:27 +00:00
Denis Flaven
e623467782 Readme updated for the 1.2.1 release.
SVN:1.2[1838]
2012-02-01 10:27:10 +00:00
Denis Flaven
32208fcbfc Readme updated for the 1.2.1 release.
SVN:1.2[1837]
2012-02-01 10:24:31 +00:00
Denis Flaven
dcbff406f7 Fix to the Brazilian translation: thanks to Google translate !
SVN:1.2[1836]
2012-01-31 17:51:15 +00:00
Denis Flaven
017dfe641c Update to the Brazilian translation, thanks to Marco Tulio
SVN:1.2[1834]
2012-01-31 17:48:02 +00:00
Denis Flaven
fad258cd2d (Tried to) improve the display of the Synchronization Tooltip that "sometimes" does not work on IE 8...
SVN:1.2[1832]
2012-01-31 17:30:59 +00:00
Denis Flaven
3470ce18e8 Removed a (useless) hardcoded reference to FunctionalCI
SVN:1.2[1830]
2012-01-31 15:39:35 +00:00
Denis Flaven
af710c549f SQL Block with parameters were always displayed as table, whatever their type...
SVN:1.2[1828]
2012-01-31 15:04:23 +00:00
Denis Flaven
ee938d674d Fixed typos during the copy/paste for automatic account creation
SVN:1.2[1826]
2012-01-31 12:36:05 +00:00
Denis Flaven
423de35cf5 Fixed the encoding of mail headers !
SVN:1.2[1825]
2012-01-31 12:35:34 +00:00
Denis Flaven
6a7af8ad73 Make the OQLQuery class import-able
SVN:1.2[1822]
2012-01-30 17:46:00 +00:00
Denis Flaven
89732d6e52 Put some default reconciliation keys to ease the use of CSV import
SVN:1.2[1820]
2012-01-30 17:42:54 +00:00
Denis Flaven
cddeab2c90 Protect against empty list of reconciliation keys
SVN:1.2[1818]
2012-01-30 17:38:45 +00:00
Denis Flaven
d9d84703ae Fix to have the proper use of GetEditValue... thanks to C. Naud
SVN:1.2[1817]
2012-01-30 12:49:04 +00:00
Romain Quetiez
c3de9ecf10 Export for spreadsheets: transform keys (id of the queried object or external keys) into the corresponding friendly name
SVN:1.2[1815]
2012-01-30 10:58:21 +00:00
Denis Flaven
cf37a50b3d Enhancements to the German localization
SVN:1.2[1814]
2012-01-30 10:45:27 +00:00
Romain Quetiez
4db30648c2 Object details: log always displayed AFTER plugin data
SVN:1.2[1812]
2012-01-27 13:51:17 +00:00
Denis Flaven
8de84d5ec7 Fixed Trac#518 : Properly pass the context (i.e. currently selected org) to the auto-refresh lists
SVN:1.2[1810]
2012-01-25 10:27:23 +00:00
Denis Flaven
459a271d11 Fixed Trac#522: issue with non-ASCII characters in notifications subject.
SVN:1.2[1809]
2012-01-24 18:24:34 +00:00
Denis Flaven
9da00b83b2 Added the ability to display a custom welcome/disclaimer message at the bottom of the login form.
SVN:1.2[1807]
2012-01-24 15:24:25 +00:00
Denis Flaven
0c8ef6a690 Why put "APPROOT/modules/" as the AbsoluteUrlAppRoot in the portal ??
SVN:1.2[1804]
2012-01-24 14:26:39 +00:00
Denis Flaven
cdefd7a4c6 Hmmm, also may cause troubles for bug #519...
SVN:1.2[1801]
2012-01-24 13:04:39 +00:00
Romain Quetiez
4f057ac29f Improved the check on data model consistency: detection of SQL columns used by two attributes
SVN:1.2[1800]
2012-01-24 13:01:55 +00:00
Denis Flaven
ab16588f87 Fix for Trac#519 - change password bug !
SVN:1.2[1799]
2012-01-24 13:00:15 +00:00
Denis Flaven
07d8da9d99 Added the "search" form on top of the list of users
SVN:1.2[1796]
2012-01-20 15:24:33 +00:00
Romain Quetiez
c539f19ce9 Optimized memory usage when auditing large volumes of CIs (10'000 items was requiring 200 Mb, it now runs with 32 Mb -including the 30Mb overhead!)
SVN:1.2[1795]
2012-01-20 15:03:07 +00:00
Denis Flaven
3635e60850 Typo fix
SVN:1.2[1794]
2012-01-20 14:19:56 +00:00
Denis Flaven
b278b84f46 Fixed #481: localized characters in Service / Service Category name and description were not properly displayed.
SVN:1.2[1793]
2012-01-20 14:16:20 +00:00
Romain Quetiez
04647970a8 Case log now largely bigger than 64 Kilobytes... (reintegrated change made in trunk)
SVN:1.2[1791]
2012-01-19 15:32:48 +00:00
Romain Quetiez
fe58f6bd19 Fixed regression in the tool to test queries: losing the query when there is a syntax error
SVN:1.2[1788]
2012-01-19 10:13:36 +00:00
Romain Quetiez
0d6cd529a1 #516 and #517 Improved the export (specify fields for multi-column queries) and web queries (default field list)
SVN:1.2[1786]
2012-01-18 16:59:09 +00:00
Denis Flaven
2a155fe8ee Added SetMessageId for forward compatibility with SwiftMailer
SVN:1.2[1785]
2012-01-18 10:05:12 +00:00
Romain Quetiez
3f381a3530 Partially de-hardcoded against the subdirectory "modules", so that 1.2.1 should be compatible with modules running under 2.0.
SVN:1.2[1778]
2012-01-17 15:26:02 +00:00
Denis Flaven
7fadb5e08b Added self-registering / user synchronization extensibility
SVN:1.2[1760]
2012-01-11 16:01:11 +00:00
Denis Flaven
5d4476f48b Added self-registering / user synchronization extensibility
SVN:1.2[1757]
2012-01-11 14:31:44 +00:00
Romain Quetiez
2fbb37cc2f Readme updated, ready for 1.2.1 beta
SVN:1.2[1753]
2011-12-22 10:34:00 +00:00
Denis Flaven
6801ecb266 Updated the readme file to prepare for the 1.2.1 beta release
SVN:1.2[1752]
2011-12-22 10:29:27 +00:00
Romain Quetiez
fa821d3a9b #512 Allow the CLI mode for export.php
SVN:1.2[1750]
2011-12-21 15:28:50 +00:00
Denis Flaven
00f9deeaa5 Updated the readme file to prepare for the 1.2.1 beta release
SVN:1.2[1747]
2011-12-21 15:14:41 +00:00
Romain Quetiez
58cfc1d51b CSV import (both std and related to the data synchro) can be used with a tab (keyword 'tab', case insensitive)
SVN:1.2[1746]
2011-12-21 14:46:03 +00:00
Denis Flaven
af8b3b972d Fixed Trac #480: properly take into account the 'min_autocomplete_chars' settings.
SVN:1.2[1745]
2011-12-21 14:23:20 +00:00
Denis Flaven
07671f40fd Allow a module to restrict the access to a given menu/group by redeclaring the menu with restricted rights. All rights are combined with the AND operator.
SVN:1.2[1743]
2011-12-21 14:17:03 +00:00
Denis Flaven
3da5c65fe4 Allow to filter which class(es) of objects are displayed in the graphical impact analysis view
SVN:1.2[1742]
2011-12-21 14:15:27 +00:00
Romain Quetiez
489be44b90 #489 Data synchro: reintegrated the latest improvements from trunk.
SVN:1.2[1740]
2011-12-21 13:45:07 +00:00
Denis Flaven
912088d017 Fixed Trac #486: SQL dashboards limitations
SVN:1.2[1730]
2011-12-20 15:17:14 +00:00
Denis Flaven
06620133b6 Bug fix: missing (but useless) parameter in ExpandArgs causes a notice...
SVN:1.2[1728]
2011-12-20 12:41:05 +00:00
Denis Flaven
95d7a24630 Keep the current value iin the search form when reloading the search form for a different (sub)class. For example Contact => Person.
SVN:1.2[1726]
2011-12-19 17:30:02 +00:00
Denis Flaven
cb0e1d8ef3 When searching objects to add to the current object (n:n relationship), set the default search params in order to stay in the current silo.
SVN:1.2[1724]
2011-12-19 16:50:39 +00:00
Denis Flaven
d321ebc8e4 Bug fix: apply the AllowedValues constraints(as default values) when selecting elements via the "magnifier" button or creating an new element via the "plus" button... also make sure that allowed values is enforced
SVN:1.2[1721]
2011-12-15 10:46:48 +00:00
Romain Quetiez
c149ec8e2c #485 Improved the end-user experience with Excel and the web queries (added a phrasebook) + link to test the OQL attributes (query phrasebook or email actions, etc.) including the handlink of query arguments) + fixed wrong prototypes for a few implementations of GetBareProperties()
SVN:1.2[1719]
2011-12-14 17:44:06 +00:00
Denis Flaven
281adfb043 Bug fix: apply the AllowedValues constraints(as default values) when selecting elements via the "magnifier" button or creating an new element via the "plus" button.
SVN:1.2[1715]
2011-12-13 14:43:33 +00:00
Romain Quetiez
4244029087 #485 Export for MS Excel web queries: format=spreadsheet
SVN:1.2[1713]
2011-12-13 14:30:36 +00:00
Denis Flaven
78e173d5fb Bug fix: paginated lists were broken in the Impact Analysis "List" tab
SVN:1.2[1708]
2011-12-07 10:41:13 +00:00
Romain Quetiez
6b8abce03a - commited merge information (done earlier) -
SVN:1.2[1705]
2011-12-06 13:52:44 +00:00
Denis Flaven
33a7005069 - Bug ! Incorrectly appending a parameter ?version= to linked scripts already containing a parameter in their URL !
- changed the parameter name to itopversion to avoid collisions

SVN:1.2[1702]
2011-12-01 17:18:47 +00:00
Denis Flaven
006a6037d1 Increased Suhosin minimum value for get_max_value to 2048 due to a bug seen on some installations
SVN:1.2[1700]
2011-11-30 13:23:32 +00:00
Denis Flaven
e2f8be1745 Use the default language when creating a new user from CAS
SVN:1.2[1698]
2011-11-29 15:56:24 +00:00
Denis Flaven
ebae45f6a5 Support patterns for casMemberof
SVN:1.2[1696]
2011-11-29 15:36:58 +00:00
Denis Flaven
0685835d49 Allow to log entries on behalf of another user
SVN:1.2[1694]
2011-11-24 17:22:19 +00:00
Romain Quetiez
1b1e88f9a4 In french, organiZation takes an S
SVN:1.2[1691]
2011-11-23 15:30:11 +00:00
Denis Flaven
b90f443e75 - Don't create warnings for empty ext keys (i.e. empty string)
- Properly record multiple warnings
- Don't record warnings in case of creation error (error has precedence)

SVN:1.2[1689]
2011-11-23 09:55:44 +00:00
Romain Quetiez
4da64a64b1 Cosmetic on the API of the dictionnary (internal)
SVN:1.2[1686]
2011-11-21 17:03:36 +00:00
Denis Flaven
6673e171dc Properly handle restrictions (e.g. AllowedValues) on objects that are used as n:n relationships managed via LinksWidget.
SVN:1.2[1685]
2011-11-21 13:08:10 +00:00
Denis Flaven
afee7297cc Properly handle restrictions (e.g. AllowedValues) on objects that are used as n:n relationships managed via LinksWidget.
SVN:1.2[1683]
2011-11-21 10:57:50 +00:00
Denis Flaven
3cc8b5b88a Enhancement: when an (optional) external key cannot be reconciled, log a warning on the replica. the replicas containing a warning are then processed everytime in case the ext key changes
Also improved the search/display of replicas

SVN:1.2[1678]
2011-11-18 11:42:29 +00:00
Romain Quetiez
b2e6981b24 Fixed bug in change tracking: it was impossible to factorize code creating objects in the background either in the context of an object update or in the context of the application of a stimulus
SVN:1.2[1674]
2011-11-18 10:46:42 +00:00
Denis Flaven
c0a79fa573 Prevent warnings when checking the available stimuli in the menu...
SVN:1.2[1672]
2011-11-16 17:26:27 +00:00
Denis Flaven
02ad6d19fe Prevent Javascript errors in case a name contains a quote.
SVN:1.2[1670]
2011-11-15 13:12:22 +00:00
Denis Flaven
d16308ab62 - Reload the object after applying a stimulus, in case an action has an effect on an external field...
SVN:1.2[1665]
2011-11-14 10:44:31 +00:00
Denis Flaven
4598959bc2 - Reload the object after applying a stimulus, in case an action has an effect on an external field...
SVN:1.2[1664]
2011-11-14 10:43:06 +00:00
Denis Flaven
65a3755f81 - Regression from previous fix: don't Reload an object before saving it !
SVN:1.2[1662]
2011-11-10 16:37:18 +00:00
Denis Flaven
dc46c65499 - Don't activate triggers if the transition fails
- Reload the object, in case some custom action changed an external object

SVN:1.2[1659]
2011-11-10 10:12:43 +00:00
Denis Flaven
9d691c8e56 Removed obsolete code which caused a warning in bulk_stimulus
SVN:1.2[1658]
2011-11-10 10:07:28 +00:00
Denis Flaven
99f897bff7 Better error handling in case of OQL error
SVN:1.2[1657]
2011-11-09 17:07:25 +00:00
Denis Flaven
8d83447222 Added the capability for plug-ins to "listen" to add/remove attachment events.
SVN:1.2[1652]
2011-10-28 12:46:22 +00:00
Romain Quetiez
dcc8ad08a4 New helper class: TemplateString - to allow extended syntaxes such as $this->location_id->org_id->parent_id->name$... to be progressively introduced and replace the heavy ToArgs()
SVN:1.2[1646]
2011-10-24 13:45:49 +00:00
Denis Flaven
c1b0b73b51 Bug fix: Trac #494. It seems that PHPSoap does not understand the <wsdl:documentation> tag.
SVN:1.2[1644]
2011-10-21 08:42:13 +00:00
Romain Quetiez
81173decca Fixed issue: nobody in the list of persons to notify for portal users (security takes precedence)
SVN:1.2[1641]
2011-10-21 08:05:07 +00:00
Denis Flaven
9aca062bf5 Enhancement to provide a forward compatible API for some external plugins: support adding attachments to an email in a "clean" way.
SVN:1.2[1640]
2011-10-20 16:33:46 +00:00
Denis Flaven
09aba95d0a Fixed Trac #493: incorrect display of Users' Grant Matrix
SVN:1.2[1635]
2011-10-17 09:27:23 +00:00
Denis Flaven
1683ca2dd6 Merged some enhancements fro the trunk to better keep track of sent emails
SVN:1.2[1634]
2011-10-13 15:42:59 +00:00
Denis Flaven
9e732d6045 Fixed Trac #487: resizable text areas disappeared when located on the second tab !
SVN:1.2[1628]
2011-10-04 10:52:03 +00:00
Denis Flaven
69df343bd2 Automatic synchro of CAS/LDAP users
SVN:1.2[1627]
2011-10-03 14:07:29 +00:00
Denis Flaven
eb8f49ebfe Initializes the admin contact's phone number, in case it is a mandatory field in the data model...
SVN:1.2[1623]
2011-09-30 08:03:45 +00:00
Denis Flaven
398e294604 Prevent crash when trying to load the favicon during the setup !
SVN:1.2[1621]
2011-09-29 15:27:25 +00:00
Denis Flaven
d04c6bccd5 Added a link to a favicon (icon in the browser's bar and tab)
SVN:1.2[1619]
2011-09-29 14:54:03 +00:00
Denis Flaven
f00c7c6bc2 GetValueLabel is used in some dashboards... make sure that it is available for any attribute
SVN:1.2[1617]
2011-09-29 09:46:58 +00:00
Denis Flaven
d30e8c359f Make sure that the organisation's drop-down list is not bigger than the left menu...
SVN:1.2[1616]
2011-09-29 09:44:38 +00:00
Romain Quetiez
2bd4a61c00 #485 Export.php improved for integration into Excel / web queries (bug with IIS/HTTPS, limitation on the size of the OQL)
SVN:1.2[1613]
2011-09-29 08:12:28 +00:00
Romain Quetiez
e35c8323df Merge most recent (few) bug fixes from trunk
SVN:1.2[1611]
2011-09-28 14:38:37 +00:00
Romain Quetiez
e4e814281d #484 Fixed issue with IIS ("Wrong password" at first prompt)
SVN:1.2[1610]
2011-09-28 12:59:40 +00:00
Denis Flaven
635cb424a2 Fixed Trac #482: OpenSearch broken.
SVN:1.2[1608]
2011-09-28 10:50:58 +00:00
Romain Quetiez
e95aa6cc69 Merged latest changes in module Attachments (bug fix on install and cosmetic improvements)
SVN:1.2[1604]
2011-09-23 13:49:26 +00:00
Romain Quetiez
c58fd17fc9 Fixed regression on attribute labels; introduced in [1582]
SVN:1.2[1603]
2011-09-23 13:36:57 +00:00
Romain Quetiez
efdec7a343 #478 Fixed issue in the audit: the results are wrong whenever an organization is selected
SVN:1.2[1599]
2011-09-22 12:08:11 +00:00
Romain Quetiez
2352c05d36 Fixed security issue: the attachments were visible by anybody (by forming URLs manually), whatever the allowed organizations. The change requires the execution of the setup/migration procedure.
SVN:1.2[1597]
2011-09-22 11:58:31 +00:00
Romain Quetiez
23634964a5 #477 Could not specify more than one reconciliation key (regression) + took the opportunity to enhance protection against XSS injection (using column names in the data)
SVN:1.2[1589]
2011-09-21 12:39:22 +00:00
Romain Quetiez
619252db99 Merge fix on previous change (fixed #473, but getting a warning)
SVN:1.2[1585]
2011-09-19 11:49:43 +00:00
Romain Quetiez
ca4dbd833c (record merge info)
SVN:1.2[1583]
2011-09-19 10:58:40 +00:00
Romain Quetiez
f62a3b22a3 #473 Could not load NW interfaces (reconciliation issue) - merged from trunk
SVN:1.2[1582]
2011-09-19 10:55:24 +00:00
Romain Quetiez
88416bca5d Created branch 1.2
SVN:1.2[1580]
2011-09-19 10:30:29 +00:00
106 changed files with 12357 additions and 8050 deletions

View File

@@ -274,7 +274,7 @@ class UserRightsMatrix extends UserRightsAddOnAPI
return true; return true;
} }
public function GetSelectFilter($oUser, $sClass) public function GetSelectFilter($oUser, $sClass, $aSettings = array())
{ {
$oNullFilter = new DBObjectSearch($sClass); $oNullFilter = new DBObjectSearch($sClass);
return $oNullFilter; return $oNullFilter;

View File

@@ -47,7 +47,7 @@ class UserRightsNull extends UserRightsAddOnAPI
return true; return true;
} }
public function GetSelectFilter($oUser, $sClass) public function GetSelectFilter($oUser, $sClass, $aSettings = array())
{ {
$oNullFilter = new DBObjectSearch($sClass); $oNullFilter = new DBObjectSearch($sClass);
return $oNullFilter; return $oNullFilter;

View File

@@ -557,6 +557,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
{ {
$oContact->Set('org_id', $iOrgId); $oContact->Set('org_id', $iOrgId);
} }
if (MetaModel::IsValidAttCode('Person', 'phone'))
{
$oContact->Set('phone', '+00 000 000 000');
}
$oContact->Set('email', 'my.email@foo.org'); $oContact->Set('email', 'my.email@foo.org');
$iContactId = $oContact->DBInsertTrackedNoReload($oChange, true /* skip security */); $iContactId = $oContact->DBInsertTrackedNoReload($oChange, true /* skip security */);
} }
@@ -593,12 +597,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
} }
protected $m_aAdmins; // id of users being linked to the well-known admin profile protected $m_aAdmins = array(); // id -> bool, true if the user has the well-known admin profile
protected $m_aPortalUsers; // id of users being linked to the well-known admin profile protected $m_aPortalUsers = array(); // id -> bool, true if the user has the well-known portal user profile
protected $m_aProfiles; // id -> object protected $m_aProfiles; // id -> object
protected $m_aUserProfiles; // userid,profileid -> object protected $m_aUserProfiles = array(); // userid,profileid -> object
protected $m_aUserOrgs; // userid -> orgid protected $m_aUserOrgs = array(); // userid -> array of orgid
// Those arrays could be completed on demand (inheriting parent permissions) // Those arrays could be completed on demand (inheriting parent permissions)
protected $m_aClassActionGrants = null; // profile, class, action -> actiongrantid (or false if NO, or null/missing if undefined) protected $m_aClassActionGrants = null; // profile, class, action -> actiongrantid (or false if NO, or null/missing if undefined)
@@ -607,20 +611,80 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Built on demand, could be optimized if necessary (doing a query for each attribute that needs to be read) // Built on demand, could be optimized if necessary (doing a query for each attribute that needs to be read)
protected $m_aObjectActionGrants = array(); protected $m_aObjectActionGrants = array();
/**
* Read and cache organizations allowed to the given user
*
* @param oUser
* @param sClass -not used here but can be used in overloads
*/
protected function GetUserOrgs($oUser, $sClass) protected function GetUserOrgs($oUser, $sClass)
{ {
return @$this->m_aUserOrgs[$oUser->GetKey()]; $iUser = $oUser->GetKey();
if (!array_key_exists($iUser, $this->m_aUserOrgs))
{
$this->m_aUserOrgs[$iUser] = array();
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass('Organization');
if ($sHierarchicalKeyCode !== false)
{
$sUserOrgQuery = 'SELECT UserOrg, Org FROM Organization AS Org JOIN Organization AS Root ON Org.'.$sHierarchicalKeyCode.' BELOW Root.id JOIN URP_UserOrg AS UserOrg ON UserOrg.allowed_org_id = Root.id WHERE UserOrg.userid = :userid';
$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();
}
}
else
{
$oSearch = new DBObjectSearch('URP_UserOrg');
$oSearch->AllowAllData();
$oCondition = new BinaryExpression(new FieldExpression('userid'), '=', new VariableExpression('userid'));
$oSearch->AddConditionExpression($oCondition);
$oUserOrgSet = new DBObjectSet($oSearch, array(), array('userid' => $iUser));
while ($oUserOrg = $oUserOrgSet->Fetch())
{
$this->m_aUserOrgs[$iUser][] = $oUserOrg->Get('allowed_org_id');
}
}
}
return $this->m_aUserOrgs[$iUser];
}
/**
* Read and cache profiles of the given user
*/
protected function GetUserProfiles($iUser)
{
if (!array_key_exists($iUser, $this->m_aUserProfiles))
{
$oSearch = new DBObjectSearch('URP_UserProfile');
$oSearch->AllowAllData();
$oCondition = new BinaryExpression(new FieldExpression('userid'), '=', new VariableExpression('userid'));
$oSearch->AddConditionExpression($oCondition);
$this->m_aUserProfiles[$iUser] = array();
$oUserProfileSet = new DBObjectSet($oSearch, array(), array('userid' => $iUser));
while ($oUserProfile = $oUserProfileSet->Fetch())
{
$this->m_aUserProfiles[$iUser][$oUserProfile->Get('profileid')] = $oUserProfile;
}
}
return $this->m_aUserProfiles[$iUser];
} }
public function ResetCache() public function ResetCache()
{ {
// Loaded by Load cache // Loaded by Load cache
$this->m_aProfiles = null; $this->m_aProfiles = null;
$this->m_aUserProfiles = null; $this->m_aUserProfiles = array();
$this->m_aUserOrgs = null; $this->m_aUserOrgs = array();
$this->m_aAdmins = null; $this->m_aAdmins = array();
$this->m_aPortalUsers = null; $this->m_aPortalUsers = array();
// Loaded on demand (time consuming as compared to the others) // Loaded on demand (time consuming as compared to the others)
$this->m_aClassActionGrants = null; $this->m_aClassActionGrants = null;
@@ -655,6 +719,11 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oKPI = new ExecutionKPI(); $oKPI = new ExecutionKPI();
if (self::HasSharing())
{
SharedObject::InitSharedClassProperties();
}
$oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_Profiles")); $oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_Profiles"));
$this->m_aProfiles = array(); $this->m_aProfiles = array();
while ($oProfile = $oProfileSet->Fetch()) while ($oProfile = $oProfileSet->Fetch())
@@ -662,30 +731,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
$this->m_aProfiles[$oProfile->GetKey()] = $oProfile; $this->m_aProfiles[$oProfile->GetKey()] = $oProfile;
} }
$oUserProfileSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_UserProfile"));
$this->m_aUserProfiles = array();
$this->m_aAdmins = array();
$this->m_aPortalUsers = array();
while ($oUserProfile = $oUserProfileSet->Fetch())
{
$this->m_aUserProfiles[$oUserProfile->Get('userid')][$oUserProfile->Get('profileid')] = $oUserProfile;
if ($oUserProfile->Get('profile') == ADMIN_PROFILE_NAME)
{
$this->m_aAdmins[] = $oUserProfile->Get('userid');
}
elseif ($oUserProfile->Get('profile') == PORTAL_PROFILE_NAME)
{
$this->m_aPortalUsers[] = $oUserProfile->Get('userid');
}
}
$oUserOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_UserOrg"));
$this->m_aUserOrgs = array();
while ($oUserOrg = $oUserOrgSet->Fetch())
{
$this->m_aUserOrgs[$oUserOrg->Get('userid')][] = $oUserOrg->Get('allowed_org_id');
}
$this->m_aClassStimulusGrants = array(); $this->m_aClassStimulusGrants = array();
$oStimGrantSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_StimulusGrant")); $oStimGrantSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_StimulusGrant"));
$this->m_aStimGrants = array(); $this->m_aStimGrants = array();
@@ -712,33 +757,45 @@ exit;
public function IsAdministrator($oUser) public function IsAdministrator($oUser)
{ {
$this->LoadCache(); //$this->LoadCache();
$iUser = $oUser->GetKey();
if (in_array($oUser->GetKey(), $this->m_aAdmins)) if (!array_key_exists($iUser, $this->m_aAdmins))
{ {
return true; $bIsAdmin = false;
} foreach($this->GetUserProfiles($iUser) as $oUserProfile)
else
{ {
return false; if ($oUserProfile->Get('profile') == ADMIN_PROFILE_NAME)
{
$bIsAdmin = true;
break;
} }
} }
$this->m_aAdmins[$iUser] = $bIsAdmin;
}
return $this->m_aAdmins[$iUser];
}
public function IsPortalUser($oUser) public function IsPortalUser($oUser)
{ {
$this->LoadCache(); //$this->LoadCache();
$iUser = $oUser->GetKey();
if (in_array($oUser->GetKey(), $this->m_aPortalUsers)) if (!array_key_exists($iUser, $this->m_aPortalUsers))
{ {
return true; $bIsPortalUser = false;
} foreach($this->GetUserProfiles($iUser) as $oUserProfile)
else
{ {
return false; if ($oUserProfile->Get('profile') == PORTAL_PROFILE_NAME)
{
$bIsPortalUser = true;
break;
} }
} }
$this->m_aPortalUsers[$iUser] = $bIsPortalUser;
}
return $this->m_aPortalUsers[$iUser];
}
public function GetSelectFilter($oUser, $sClass) public function GetSelectFilter($oUser, $sClass, $aSettings = array())
{ {
$this->LoadCache(); $this->LoadCache();
@@ -750,41 +807,18 @@ exit;
// Determine how to position the objects of this class // Determine how to position the objects of this class
// //
$aCallSpec = array($sClass, 'MapContextParam'); $sAttCode = self::GetOwnerOrganizationAttCode($sClass);
if (($sClass == 'Organization') || is_subclass_of($sClass, 'Organization')) if (is_null($sAttCode))
{ {
$sAttCode = 'id'; // No filtering for this object
}
elseif (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, 'org_id'); // Returns null when there is no mapping for this parameter
if ($sAttCode == null)
{
return true;
}
if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
{
// Skip silently. The data model checker will tell you something about this...
return true;
}
}
elseif(MetaModel::IsValidAttCode($sClass, 'org_id'))
{
$sAttCode = 'org_id';
}
else
{
// The objects of this class are not positioned in this dimension
// All of them are visible
return true; return true;
} }
// Position the user // Position the user
// //
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass); $aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
if (is_null($aUserOrgs) || count($aUserOrgs) == 0) if (count($aUserOrgs) == 0)
{ {
// No position means 'Everywhere' // No org means 'any org'
return true; return true;
} }
@@ -792,48 +826,65 @@ exit;
$oFilter = new DBObjectSearch($sClass); $oFilter = new DBObjectSearch($sClass);
$oListExpr = ListExpression::FromScalars($aUserOrgs); $oListExpr = ListExpression::FromScalars($aUserOrgs);
// Check if the condition points to a hierarchical key $oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr);
$bConditionAdded = false; $oFilter->AddConditionExpression($oCondition);
if ($sAttCode == 'id') if (self::HasSharing())
{ {
// Filtering on the objects themselves if (($sAttCode == 'id') && isset($aSettings['bSearchMode']) && $aSettings['bSearchMode'])
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sClass);
if ($sHierarchicalKeyCode !== false)
{ {
$oRootFilter = new DBObjectSearch($sClass); // Querying organizations (or derived)
$oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr); // and the expected list of organizations will be used as a search criteria
$oRootFilter->AddConditionExpression($oCondition); // Therefore the query can also return organization having objects shared with the allowed organizations
$oFilter->AddCondition_PointingTo($oRootFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW); // Use the 'below' operator by default //
$bConditionAdded = true; // 1) build the list of organizations sharing something with the allowed organizations
} // Organization <== sharing_org_id == SharedObject having org_id IN {user orgs}
} $oShareSearch = new DBObjectSearch('SharedObject');
else $oOrgField = new FieldExpression('org_id', 'SharedObject');
{ $oShareSearch->AddConditionExpression(new BinaryExpression($oOrgField, 'IN', $oListExpr));
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->IsExternalKey()) $oSearchSharers = new DBObjectSearch('Organization');
{ $oSearchSharers->AllowAllData();
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($oAttDef->GetTargetClass()); $oSearchSharers->AddCondition_ReferencedBy($oShareSearch, 'sharing_org_id');
$aSharers = array();
if ($sHierarchicalKeyCode !== false) foreach($oSearchSharers->ToDataArray(array('id')) as $aRow)
{ {
$oRootFilter = new DBObjectSearch($oAttDef->GetTargetClass()); $aSharers[] = $aRow['id'];
$oExpression = new FieldExpression('id', $oAttDef->GetTargetClass()); }
$oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr); // 2) Enlarge the overall results: ... OR id IN(id1, id2, id3)
$oRootFilter->AddConditionExpression($oCondition); if (count($aSharers) > 0)
$oHKFilter = new DBObjectSearch($oAttDef->GetTargetClass()); {
$oHKFilter->AddCondition_PointingTo($oRootFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW); // Use the 'below' operator by default $oSharersList = ListExpression::FromScalars($aSharers);
$oFilter->AddCondition_PointingTo($oHKFilter, $sAttCode); $oFilter->MergeConditionExpression(new BinaryExpression($oExpression, 'IN', $oSharersList));
$bConditionAdded = true;
} }
} }
}
if (!$bConditionAdded) $aShareProperties = SharedObject::GetSharedClassProperties($sClass);
{ if ($aShareProperties)
$oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr); {
$oFilter->AddConditionExpression($oCondition); $sShareClass = $aShareProperties['share_class'];
} $sShareAttCode = $aShareProperties['attcode'];
$oSearchShares = new DBObjectSearch($sShareClass);
$oSearchShares->AllowAllData();
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass('Organization');
$oOrgField = new FieldExpression('org_id', $sShareClass);
$oSearchShares->AddConditionExpression(new BinaryExpression($oOrgField, 'IN', $oListExpr));
$aShared = array();
foreach($oSearchShares->ToDataArray(array($sShareAttCode)) as $aRow)
{
$aShared[] = $aRow[$sShareAttCode];
}
if (count($aShared) > 0)
{
$oObjId = new FieldExpression('id', $sClass);
$oSharedIdList = ListExpression::FromScalars($aShared);
$oFilter->MergeConditionExpression(new BinaryExpression($oObjId, 'IN', $oSharedIdList));
}
}
} // if HasSharing
return $oFilter; return $oFilter;
} }
@@ -879,10 +930,8 @@ exit;
$iPermission = UR_ALLOWED_NO; $iPermission = UR_ALLOWED_NO;
$aAttributes = array(); $aAttributes = array();
if (isset($this->m_aUserProfiles[$iUser])) foreach($this->GetUserProfiles($iUser) as $iProfile => $oProfile)
{ {
foreach($this->m_aUserProfiles[$iUser] as $iProfile => $oProfile)
{
$iGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction); $iGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
if (is_null($iGrant) || !$iGrant) if (is_null($iGrant) || !$iGrant)
{ {
@@ -891,7 +940,7 @@ exit;
else else
{ {
$iPermission = UR_ALLOWED_YES; $iPermission = UR_ALLOWED_YES;
// update the list of attributes with those allowed for this profile // update the list of attributes with those allowed for this profile
// //
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT URP_AttributeGrant WHERE actiongrantid = :actiongrantid"); $oSearch = DBObjectSearch::FromOQL_AllData("SELECT URP_AttributeGrant WHERE actiongrantid = :actiongrantid");
@@ -908,7 +957,6 @@ exit;
} }
} }
} }
}
$aRes = array( $aRes = array(
'permission' => $iPermission, 'permission' => $iPermission,
@@ -922,10 +970,76 @@ exit;
{ {
$this->LoadCache(); $this->LoadCache();
// Note: The object set is ignored because it was interesting to optimize for huge data sets
// and acceptable to consider only the root class of the object set
$aObjectPermissions = $this->GetUserActionGrant($oUser, $sClass, $iActionCode); $aObjectPermissions = $this->GetUserActionGrant($oUser, $sClass, $iActionCode);
return $aObjectPermissions['permission']; $iPermission = $aObjectPermissions['permission'];
// Note: In most cases the object set is ignored because it was interesting to optimize for huge data sets
// and acceptable to consider only the root class of the object set
if ($iPermission != UR_ALLOWED_YES)
{
// It is already NO for everyone... that's the final word!
}
elseif ($iActionCode == UR_ACTION_READ)
{
// We are protected by GetSelectFilter: the object set contains objects allowed or shared for reading
}
elseif ($iActionCode == UR_ACTION_BULK_READ)
{
// We are protected by GetSelectFilter: the object set contains objects allowed or shared for reading
}
elseif ($oInstanceSet)
{
// We are protected by GetSelectFilter: the object set contains objects allowed or shared for reading
// We have to answer NO for objects shared for reading purposes
if (self::HasSharing())
{
$aClassProps = SharedObject::GetSharedClassProperties($sClass);
if ($aClassProps)
{
// This class is shared, GetSelectFilter may allow some objects for read only
// But currently we are checking wether the objects might be written...
// Let's exclude the objects based on the relevant criteria
$sOrgAttCode = self::GetOwnerOrganizationAttCode($sClass);
if (!is_null($sOrgAttCode))
{
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
if (!is_null($aUserOrgs) && count($aUserOrgs) > 0)
{
$iCountNO = 0;
$iCountYES = 0;
$oInstanceSet->Rewind();
while($oObject = $oInstanceSet->Fetch())
{
$iOrg = $oObject->Get($sOrgAttCode);
if (in_array($iOrg, $aUserOrgs))
{
$iCountYES++;
}
else
{
$iCountNO++;
}
}
if ($iCountNO == 0)
{
$iPermission = UR_ALLOWED_YES;
}
elseif ($iCountYES == 0)
{
$iPermission = UR_ALLOWED_NO;
}
else
{
$iPermission = UR_ALLOWED_DEPENDS;
}
}
}
}
}
}
return $iPermission;
} }
public function IsActionAllowedOnAttribute($oUser, $sClass, $sAttCode, $iActionCode, $oInstanceSet = null) public function IsActionAllowedOnAttribute($oUser, $sClass, $sAttCode, $iActionCode, $oInstanceSet = null)
@@ -970,10 +1084,8 @@ exit;
// Note: The object set is ignored because it was interesting to optimize for huge data sets // Note: The object set is ignored because it was interesting to optimize for huge data sets
// and acceptable to consider only the root class of the object set // and acceptable to consider only the root class of the object set
$iPermission = UR_ALLOWED_NO; $iPermission = UR_ALLOWED_NO;
if (isset($this->m_aUserProfiles[$iUser])) foreach($this->GetUserProfiles($iUser) as $iProfile => $oProfile)
{ {
foreach($this->m_aUserProfiles[$iUser] as $iProfile => $oProfile)
{
$oGrantRecord = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode); $oGrantRecord = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
if (!is_null($oGrantRecord)) if (!is_null($oGrantRecord))
{ {
@@ -981,7 +1093,6 @@ exit;
$iPermission = UR_ALLOWED_YES; $iPermission = UR_ALLOWED_YES;
} }
} }
}
return $iPermission; return $iPermission;
} }
@@ -989,6 +1100,49 @@ exit;
{ {
$this->ResetCache(); $this->ResetCache();
} }
/**
* Find out which attribute is corresponding the the dimension 'owner org'
* returns null if no such attribute has been found (no filtering should occur)
*/
public static function GetOwnerOrganizationAttCode($sClass)
{
$sAttCode = null;
$aCallSpec = array($sClass, 'MapContextParam');
if (($sClass == 'Organization') || is_subclass_of($sClass, 'Organization'))
{
$sAttCode = 'id';
}
elseif (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, 'org_id'); // Returns null when there is no mapping for this parameter
if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
{
// Skip silently. The data model checker will tell you something about this...
$sAttCode = null;
}
}
elseif(MetaModel::IsValidAttCode($sClass, 'org_id'))
{
$sAttCode = 'org_id';
}
return $sAttCode;
}
/**
* Determine wether the objects can be shared by the mean of a class SharedObject
**/
protected static function HasSharing()
{
static $bHasSharing;
if (!isset($bHasSharing))
{
$bHasSharing = class_exists('SharedObject');
}
return $bHasSharing;
}
} }

View File

@@ -734,7 +734,7 @@ exit;
return true; return true;
} }
public function GetSelectFilter($oUser, $sClass) public function GetSelectFilter($oUser, $sClass, $aSettings = array())
{ {
$aConditions = array(); $aConditions = array();
foreach ($this->m_aDimensions as $iDimension => $oDimension) foreach ($this->m_aDimensions as $iDimension => $oDimension)

View File

@@ -29,6 +29,7 @@ require_once(APPROOT.'/application/displayblock.class.inc.php');
require_once(APPROOT.'/application/sqlblock.class.inc.php'); require_once(APPROOT.'/application/sqlblock.class.inc.php');
require_once(APPROOT.'/application/audit.category.class.inc.php'); require_once(APPROOT.'/application/audit.category.class.inc.php');
require_once(APPROOT.'/application/audit.rule.class.inc.php'); require_once(APPROOT.'/application/audit.rule.class.inc.php');
require_once(APPROOT.'/application/query.class.inc.php');
//require_once(APPROOT.'/application/menunode.class.inc.php'); //require_once(APPROOT.'/application/menunode.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php'); require_once(APPROOT.'/application/utils.inc.php');

View File

@@ -77,12 +77,16 @@ class ApplicationContext
protected $aValues; protected $aValues;
protected static $aDefaultValues; // Cache shared among all instances protected static $aDefaultValues; // Cache shared among all instances
public function __construct() public function __construct($bReadContext = true)
{ {
$this->aNames = array( $this->aNames = array(
'org_id', 'menu' 'org_id', 'menu'
); );
$this->ReadContext(); if ($bReadContext)
{
$this->ReadContext();
}
} }
/** /**
@@ -111,6 +115,7 @@ class ApplicationContext
if (MetaModel::IsValidClass('Organization')) if (MetaModel::IsValidClass('Organization'))
{ {
$oSearchFilter = new DBObjectSearch('Organization'); $oSearchFilter = new DBObjectSearch('Organization');
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oSet = new CMDBObjectSet($oSearchFilter); $oSet = new CMDBObjectSet($oSearchFilter);
$iCount = $oSet->Count(); $iCount = $oSet->Count();
if ($iCount == 1) if ($iCount == 1)
@@ -299,5 +304,58 @@ class ApplicationContext
return ''; 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

@@ -185,7 +185,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
$sTip .= Dict::S('Core:Synchro:LastSynchro').'<br/>'.$aStruct['last_synchro']."</p>"; $sTip .= Dict::S('Core:Synchro:LastSynchro').'<br/>'.$aStruct['last_synchro']."</p>";
} }
$sSynchroIcon = '&nbsp;<img style="vertical-align:middle;" id="synchro_icon" src="../images/locked.png"/>'; $sSynchroIcon = '&nbsp;<img style="vertical-align:middle;" id="synchro_icon" src="../images/locked.png"/>';
$oPage->add_ready_script("$('#synchro_icon').qtip( { content: '$sTip', show: 'mouseover', hide: 'unfocus', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );"); $oPage->add_ready_script("$('#synchro_icon').qtip( { content: '$sTip', show: 'mouseover', hide: { fixed: true }, style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
} }
$oPage->add("<div class=\"page_header\"><h1>".$this->GetIcon()."&nbsp;\n"); $oPage->add("<div class=\"page_header\"><h1>".$this->GetIcon()."&nbsp;\n");
@@ -208,6 +208,11 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
{ {
$aFieldsMap = $this->GetBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams); $aFieldsMap = $this->GetBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$oExtensionInstance->OnDisplayProperties($this, $oPage, $bEditMode);
}
// Special case to display the case log, if any... // Special case to display the case log, if any...
// WARNING: if you modify the loop below, also check the corresponding code in UpdateObject and DisplayModifyForm // WARNING: if you modify the loop below, also check the corresponding code in UpdateObject and DisplayModifyForm
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef) foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
@@ -219,11 +224,6 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
} }
} }
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$oExtensionInstance->OnDisplayProperties($this, $oPage, $bEditMode);
}
return $aFieldsMap; return $aFieldsMap;
} }
@@ -252,16 +252,17 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
$sCount = " ($iCount)"; $sCount = " ($iCount)";
} }
$oPage->SetCurrentTab($oAttDef->GetLabel().$sCount); $oPage->SetCurrentTab($oAttDef->GetLabel().$sCount);
if ($bEditMode) if ($this->IsNew())
{
$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
}
else
{
$iFlags = $this->GetAttributeFlags($sAttCode);
}
$bReadOnly = ($iFlags & (OPT_ATT_READONLY|OPT_ATT_SLAVE));
if ($bEditMode && (!$bReadOnly))
{ {
if ($this->IsNew())
{
$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
}
else
{
$iFlags = $this->GetAttributeFlags($sAttCode);
}
$sInputId = $this->m_iFormId.'_'.$sAttCode; $sInputId = $this->m_iFormId.'_'.$sAttCode;
if (get_class($oAttDef) == 'AttributeLinkedSet') if (get_class($oAttDef) == 'AttributeLinkedSet')
{ {
@@ -655,8 +656,26 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
$bTruncated = isset($aExtraParams['truncated']) ? $aExtraParams['truncated'] == true : true; $bTruncated = isset($aExtraParams['truncated']) ? $aExtraParams['truncated'] == true : true;
$bSelectMode = isset($aExtraParams['selection_mode']) ? $aExtraParams['selection_mode'] == true : false; $bSelectMode = isset($aExtraParams['selection_mode']) ? $aExtraParams['selection_mode'] == true : false;
$bSingleSelectMode = isset($aExtraParams['selection_type']) ? ($aExtraParams['selection_type'] == 'single') : false; $bSingleSelectMode = isset($aExtraParams['selection_type']) ? ($aExtraParams['selection_type'] == 'single') : false;
$aExtraFields = isset($aExtraParams['extra_fields']) ? explode(',', trim($aExtraParams['extra_fields'])) : array();
$aExtraFieldsRaw = isset($aExtraParams['extra_fields']) ? explode(',', trim($aExtraParams['extra_fields'])) : array();
$aExtraFields = array();
foreach ($aExtraFieldsRaw as $sFieldName)
{
// Ignore attributes not of the main queried class
if (preg_match('/^(.*)\.(.*)$/', $sFieldName, $aMatches))
{
$sClassAlias = $aMatches[1];
$sAttCode = $aMatches[2];
if ($sClassAlias == $oSet->GetFilter()->GetClassAlias())
{
$aExtraFields[] = $sAttCode;
}
}
else
{
$aExtraFields[] = $sFieldName;
}
}
$sHtml = ''; $sHtml = '';
$oAppContext = new ApplicationContext(); $oAppContext = new ApplicationContext();
$sClassName = $oSet->GetFilter()->GetClass(); $sClassName = $oSet->GetFilter()->GetClass();
@@ -883,7 +902,9 @@ EOF
$sDisplayList = json_encode($aList); $sDisplayList = json_encode($aList);
$sCssCount = isset($aExtraParams['cssCount']) ? ", cssCount: '{$aExtraParams['cssCount']}'" : ''; $sCssCount = isset($aExtraParams['cssCount']) ? ", cssCount: '{$aExtraParams['cssCount']}'" : '';
$iPageSize = MetaModel::GetConfig()->GetMinDisplayLimit(); $iPageSize = MetaModel::GetConfig()->GetMinDisplayLimit();
$oPage->add_ready_script("$('#{$iListId} table.listResults').tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager{$iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sFilter', extra_params: '$sExtraParams', select_mode: '$sSelectMode', displayKey: $sDisplayKey, displayList: $sDisplayList $sCssCount});\n"); $oSet->ApplyParameters();
$sOQL = addslashes($oSet->GetFilter()->serialize());
$oPage->add_ready_script("$('#{$iListId} table.listResults').tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager{$iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectMode', displayKey: $sDisplayKey, displayList: $sDisplayList $sCssCount});\n");
} }
else else
{ {
@@ -940,7 +961,28 @@ EOF
$bDisplayMenu = isset($aExtraParams['menu']) ? $aExtraParams['menu'] == true : true; $bDisplayMenu = isset($aExtraParams['menu']) ? $aExtraParams['menu'] == true : true;
// Check if there is a list of aliases to limit the display to... // Check if there is a list of aliases to limit the display to...
$aDisplayAliases = isset($aExtraParams['display_aliases']) ? explode(',', $aExtraParams['display_aliases']) : array(); $aDisplayAliases = isset($aExtraParams['display_aliases']) ? explode(',', $aExtraParams['display_aliases']) : array();
$sZListName = isset($aExtraParams['zlist']) ? ($aExtraParams['zlist']) : 'list';
$aExtraFieldsRaw = isset($aExtraParams['extra_fields']) ? explode(',', trim($aExtraParams['extra_fields'])) : array();
$aExtraFields = array();
foreach ($aExtraFieldsRaw as $sFieldName)
{
// Ignore attributes not of the main queried class
if (preg_match('/^(.*)\.(.*)$/', $sFieldName, $aMatches))
{
$sClassAlias = $aMatches[1];
$sAttCode = $aMatches[2];
if (array_key_exists($sClassAlias, $oSet->GetSelectedClasses()))
{
$aExtraFields[$sClassAlias][] = $sAttCode;
}
}
else
{
$aExtraFields['*'] = $sAttCode;
}
}
$sHtml = ''; $sHtml = '';
$oAppContext = new ApplicationContext(); $oAppContext = new ApplicationContext();
$aClasses = $oSet->GetFilter()->GetSelectedClasses(); $aClasses = $oSet->GetFilter()->GetSelectedClasses();
@@ -956,12 +998,36 @@ EOF
$aAttribs = array(); $aAttribs = array();
foreach($aAuthorizedClasses as $sAlias => $sClassName) // TO DO: check if the user has enough rights to view the classes of the list... foreach($aAuthorizedClasses as $sAlias => $sClassName) // TO DO: check if the user has enough rights to view the classes of the list...
{ {
$aList[$sClassName] = MetaModel::GetZListItems($sClassName, 'list'); if (array_key_exists($sAlias, $aExtraFields))
{
$aList[$sAlias] = $aExtraFields[$sAlias];
}
else
{
$aList[$sAlias] = array();
}
if ($sZListName !== false)
{
$aDefaultList = self::FlattenZList(MetaModel::GetZListItems($sClassName, $sZListName));
$aList[$sAlias] = array_merge($aDefaultList, $aList[$sAlias]);
}
// Filter the list to removed linked set since we are not able to display them here
foreach($aList[$sAlias] as $index => $sAttCode)
{
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode);
if ($oAttDef instanceof AttributeLinkedSet)
{
// Removed from the display list
unset($aList[$sAlias][$index]);
}
}
if ($bViewLink) if ($bViewLink)
{ {
$aAttribs['key_'.$sAlias] = array('label' => MetaModel::GetName($sClassName), 'description' => ''); $aAttribs['key_'.$sAlias] = array('label' => MetaModel::GetName($sClassName), 'description' => '');
} }
foreach($aList[$sClassName] as $sAttCode) foreach($aList[$sAlias] as $sAttCode)
{ {
$aAttribs[$sAttCode.'_'.$sAlias] = array('label' => MetaModel::GetLabel($sClassName, $sAttCode), 'description' => MetaModel::GetDescription($sClassName, $sAttCode)); $aAttribs[$sAttCode.'_'.$sAlias] = array('label' => MetaModel::GetLabel($sClassName, $sAttCode), 'description' => MetaModel::GetDescription($sClassName, $sAttCode));
} }
@@ -970,7 +1036,7 @@ EOF
$aAttToLoad = array(); // attributes to load $aAttToLoad = array(); // attributes to load
foreach($aAuthorizedClasses as $sAlias => $sClassName) foreach($aAuthorizedClasses as $sAlias => $sClassName)
{ {
foreach($aList[$sClassName] as $sAttCode) foreach($aList[$sAlias] as $sAttCode)
{ {
$aAttToLoad[$sAlias][] = $sAttCode; $aAttToLoad[$sAlias][] = $sAttCode;
} }
@@ -1004,7 +1070,7 @@ EOF
$aRow['key_'.$sAlias] = $aObjects[$sAlias]->GetHyperLink(); $aRow['key_'.$sAlias] = $aObjects[$sAlias]->GetHyperLink();
} }
} }
foreach($aList[$sClassName] as $sAttCode) foreach($aList[$sAlias] as $sAttCode)
{ {
if (is_null($aObjects[$sAlias])) if (is_null($aObjects[$sAlias]))
{ {
@@ -1087,6 +1153,8 @@ EOF
$aHeader = array(); $aHeader = array();
foreach($aAuthorizedClasses as $sAlias => $sClassName) foreach($aAuthorizedClasses as $sAlias => $sClassName)
{ {
$aList[$sAlias] = array();
foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef) foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef)
{ {
if (is_null($aFields) || (count($aFields) == 0)) if (is_null($aFields) || (count($aFields) == 0))
@@ -1094,20 +1162,20 @@ EOF
// Standard list of attributes (no link sets) // Standard list of attributes (no link sets)
if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField())) if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField()))
{ {
$aList[$sClassName][$sAttCode] = $oAttDef; $aList[$sAlias][$sAttCode] = $oAttDef;
} }
} }
else else
{ {
// User defined list of attributes // User defined list of attributes
if (in_array($sAttCode, $aFields)) if (in_array($sAttCode, $aFields) || in_array($sAlias.'.'.$sAttCode, $aFields))
{ {
$aList[$sClassName][$sAttCode] = $oAttDef; $aList[$sAlias][$sAttCode] = $oAttDef;
} }
} }
} }
$aHeader[] = 'id'; $aHeader[] = 'id';
foreach($aList[$sClassName] as $sAttCode => $oAttDef) foreach($aList[$sAlias] as $sAttCode => $oAttDef)
{ {
$sStar = ''; $sStar = '';
if ($oAttDef->IsExternalField()) if ($oAttDef->IsExternalField())
@@ -1154,7 +1222,7 @@ EOF
{ {
$aRow[] = $oObj->GetKey(); $aRow[] = $oObj->GetKey();
} }
foreach($aList[$sClassName] as $sAttCode => $oAttDef) foreach($aList[$sAlias] as $sAttCode => $oAttDef)
{ {
if (is_null($oObj)) if (is_null($oObj))
{ {
@@ -1172,6 +1240,149 @@ EOF
return $sHtml; return $sHtml;
} }
static function DisplaySetAsHTMLSpreadsheet(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
{
$oPage->add(self::GetSetAsHTMLSpreadsheet($oSet, $aParams));
}
/**
* Spreadsheet output: designed for end users doing some reporting
* Then the ids are excluded and replaced by the corresponding friendlyname
*/
static function GetSetAsHTMLSpreadsheet(DBObjectSet $oSet, $aParams = array())
{
$aFields = null;
if (isset($aParams['fields']) && (strlen($aParams['fields']) > 0))
{
$aFields = explode(',', $aParams['fields']);
}
$aList = array();
$oAppContext = new ApplicationContext();
$aClasses = $oSet->GetFilter()->GetSelectedClasses();
$aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
{
$aAuthorizedClasses[$sAlias] = $sClassName;
}
}
$aAttribs = array();
$aHeader = array();
foreach($aAuthorizedClasses as $sAlias => $sClassName)
{
$aList[$sAlias] = array();
foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef)
{
if (is_null($aFields) || (count($aFields) == 0))
{
// Standard list of attributes (no link sets)
if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField()))
{
$aList[$sAlias][$sAttCode] = $oAttDef;
}
}
else
{
// User defined list of attributes
if (in_array($sAttCode, $aFields) || in_array($sAlias.'.'.$sAttCode, $aFields))
{
$aList[$sAlias][$sAttCode] = $oAttDef;
}
}
}
// Replace external key by the corresponding friendly name (if not already in the list)
foreach($aList[$sAlias] as $sAttCode => $oAttDef)
{
if ($oAttDef->IsExternalKey())
{
unset($aList[$sAlias][$sAttCode]);
$sFriendlyNameAttCode = $sAttCode.'_friendlyname';
if (!array_key_exists($sFriendlyNameAttCode, $aList[$sAlias]) && MetaModel::IsValidAttCode($sClassName, $sFriendlyNameAttCode))
{
$oFriendlyNameAtt = MetaModel::GetAttributeDef($sClassName, $sFriendlyNameAttCode);
$aList[$sAlias][$sFriendlyNameAttCode] = $oFriendlyNameAtt;
}
}
}
foreach($aList[$sAlias] as $sAttCode => $oAttDef)
{
if ($oAttDef->IsExternalField())
{
$sExtKeyLabel = MetaModel::GetLabel($sClassName, $oAttDef->GetKeyAttCode());
$oExtKeyAttDef = MetaModel::GetAttributeDef($sClassName, $oAttDef->GetKeyAttCode());
$sRemoteAttLabel = MetaModel::GetLabel($oAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
$oTargetAttDef = MetaModel::GetAttributeDef($oAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
$sSuffix = '';
if ($oTargetAttDef->IsExternalKey())
{
$sSuffix = '->id';
}
$sColLabel = $sExtKeyLabel.'->'.$sRemoteAttLabel.$sSuffix;
}
else
{
$sColLabel = MetaModel::GetLabel($sClassName, $sAttCode);
}
$oFinalAttDef = $oAttDef->GetFinalAttDef();
if (get_class($oFinalAttDef) == 'AttributeDateTime')
{
$aHeader[] = $sColLabel.' ('.Dict::S('UI:SplitDateTime-Date').')';
$aHeader[] = $sColLabel.' ('.Dict::S('UI:SplitDateTime-Time').')';
}
else
{
$aHeader[] = $sColLabel;
}
}
}
$sHtml = "<table border=\"1\">\n";
$sHtml .= "<tr>\n";
$sHtml .= "<td>".implode("</td><td>", $aHeader)."</td>\n";
$sHtml .= "</tr>\n";
$oSet->Seek(0);
while ($aObjects = $oSet->FetchAssoc())
{
$aRow = array();
foreach($aAuthorizedClasses as $sAlias => $sClassName)
{
$oObj = $aObjects[$sAlias];
foreach($aList[$sAlias] as $sAttCode => $oAttDef)
{
if (is_null($oObj))
{
$aRow[] = '<td></td>';
}
else
{
$oFinalAttDef = $oAttDef->GetFinalAttDef();
if (get_class($oFinalAttDef) == 'AttributeDateTime')
{
$iDate = AttributeDateTime::GetAsUnixSeconds($oObj->Get($sAttCode));
$aRow[] = '<td>'.date('Y-m-d', $iDate).'</td>';
$aRow[] = '<td>'.date('H:i:s', $iDate).'</td>';
}
else
{
$aRow[] = '<td>'.(string) $oObj->Get($sAttCode).'</td>';
}
}
}
}
$sHtml .= implode("\n", $aRow);
$sHtml .= "</tr>\n";
}
$sHtml .= "</table>\n";
return $sHtml;
}
static function DisplaySetAsXML(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array()) static function DisplaySetAsXML(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
{ {
$oAppContext = new ApplicationContext(); $oAppContext = new ApplicationContext();
@@ -1186,7 +1397,7 @@ EOF
} }
$aAttribs = array(); $aAttribs = array();
$aList = array(); $aList = array();
$aList[$sClassName] = MetaModel::GetZListItems($sClassName, 'details'); $aList[$sAlias] = MetaModel::GetZListItems($sClassName, 'details');
$oPage->add("<Set>\n"); $oPage->add("<Set>\n");
$oSet->Seek(0); $oSet->Seek(0);
while ($aObjects = $oSet->FetchAssoc()) while ($aObjects = $oSet->FetchAssoc())
@@ -1305,12 +1516,14 @@ EOF
$aMapCriteria[$aCriteria['filtercode']][] = array('value' => $aCriteria['value'], 'opcode' => $aCriteria['opcode']); $aMapCriteria[$aCriteria['filtercode']][] = array('value' => $aCriteria['value'], 'opcode' => $aCriteria['opcode']);
} }
$aList = MetaModel::GetZListItems($sClassName, 'standard_search'); $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) foreach($aList as $sFilterCode)
{ {
//$oAppContext->Reset($sFilterCode); // Make sure the same parameter will not be passed twice //$oAppContext->Reset($sFilterCode); // Make sure the same parameter will not be passed twice
$sHtml .= '<span style="white-space: nowrap;padding:5px;display:inline-block;">'; $sHtml .= '<span style="white-space: nowrap;padding:5px;display:inline-block;">';
$sFilterValue = ''; $sFilterValue = isset($aConsts[$sClassAlias][$sFilterCode]) ? $aConsts[$sClassAlias][$sFilterCode] : '';
$sFilterValue = utils::ReadParam($sFilterCode, '', false, 'raw_data'); $sFilterValue = utils::ReadParam($sFilterCode, $sFilterValue, false, 'raw_data');
$sFilterOpCode = null; // Use the default 'loose' OpCode $sFilterOpCode = null; // Use the default 'loose' OpCode
if (empty($sFilterValue)) if (empty($sFilterValue))
{ {
@@ -1336,7 +1549,9 @@ EOF
if ($oAttDef->IsExternalKey()) if ($oAttDef->IsExternalKey())
{ {
$sTargetClass = $oAttDef->GetTargetClass(); $sTargetClass = $oAttDef->GetTargetClass();
$oAllowedValues = new DBObjectSet(new DBObjectSearch($sTargetClass)); $oSearch = new DBObjectSearch($sTargetClass);
$oSearch->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oAllowedValues = new DBObjectSet($oSearch);
$iFieldSize = $oAttDef->GetMaxSize(); $iFieldSize = $oAttDef->GetMaxSize();
$iMaxComboLength = $oAttDef->GetMaximumComboLength(); $iMaxComboLength = $oAttDef->GetMaximumComboLength();
@@ -1480,7 +1695,7 @@ EOF
$aEventsList[] ='validate'; $aEventsList[] ='validate';
$aEventsList[] ='keyup'; $aEventsList[] ='keyup';
$aEventsList[] ='change'; $aEventsList[] ='change';
$sHTMLValue = "<input title=\"$sHelpText\" class=\"date-pick\" type=\"text\" size=\"20\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" id=\"$iId\"/>&nbsp;{$sValidationField}"; $sHTMLValue = "<input title=\"$sHelpText\" class=\"datetime-pick\" type=\"text\" size=\"20\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" id=\"$iId\"/>&nbsp;{$sValidationField}";
break; break;
case 'Duration': case 'Duration':
@@ -1507,6 +1722,7 @@ EOF
$sHTMLValue = "<input title=\"$sHelpText\" type=\"password\" size=\"30\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" id=\"$iId\"/>&nbsp;{$sValidationField}"; $sHTMLValue = "<input title=\"$sHelpText\" type=\"password\" size=\"30\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" id=\"$iId\"/>&nbsp;{$sValidationField}";
break; break;
case 'OQLExpression':
case 'Text': case 'Text':
$aEventsList[] ='validate'; $aEventsList[] ='validate';
$aEventsList[] ='keyup'; $aEventsList[] ='keyup';
@@ -1528,7 +1744,21 @@ EOF
{ {
$sStyle = 'style="'.implode('; ', $aStyles).'"'; $sStyle = 'style="'.implode('; ', $aStyles).'"';
} }
$sHTMLValue = "<table><tr><td><textarea class=\"resizable\" title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" rows=\"8\" cols=\"40\" id=\"$iId\" $sStyle>".htmlentities($sEditValue, ENT_QUOTES, 'UTF-8')."</textarea></td><td>{$sValidationField}</td></tr></table>"; if ($oAttDef->GetEditClass() == 'OQLExpression')
{
$sTestResId = 'query_res_'.$sFieldPrefix.$sAttCode.$sNameSuffix; //$oPage->GetUniqueId();
$sBaseUrl = utils::GetAbsoluteUrlAppRoot().'pages/run_query.php?expression=';
$sInitialUrl = $sBaseUrl.urlencode($sEditValue);
$sAdditionalStuff = "<a id=\"$sTestResId\" target=\"_blank\" href=\"$sInitialUrl\">".Dict::S('UI:Edit:TestQuery')."</a>";
$oPage->add_ready_script("$('#$iId').bind('change keyup', function(evt, sFormId) { $('#$sTestResId').attr('href', '$sBaseUrl'+encodeURIComponent($(this).val())); } );");
}
else
{
$sAdditionalStuff = "";
}
// Ok, the text area is drawn here
$sHTMLValue = "<table><tr><td><textarea class=\"resizable\" title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" rows=\"8\" cols=\"40\" id=\"$iId\" $sStyle>".htmlentities($sEditValue, ENT_QUOTES, 'UTF-8')."</textarea>$sAdditionalStuff</td><td>{$sValidationField}</td></tr></table>";
break; break;
case 'CaseLog': case 'CaseLog':
@@ -1565,8 +1795,9 @@ EOF
case 'LinkedSet': case 'LinkedSet':
$aEventsList[] ='validate'; $aEventsList[] ='validate';
$aEventsList[] ='change'; $aEventsList[] ='change';
$oWidget = new UILinksWidget($sClass, $sAttCode, $iId, $sNameSuffix, $oAttDef->DuplicatesAllowed()); $oWidget = new UILinksWidget($sClass, $sAttCode, $iId, $sNameSuffix, $oAttDef->DuplicatesAllowed(), $aArgs);
$sHTMLValue = $oWidget->Display($oPage, $value); $oObj = isset($aArgs['this']) ? $aArgs['this'] : null;
$sHTMLValue = $oWidget->Display($oPage, $value, array(), $sFormPrefix, $oObj);
break; break;
case 'Document': case 'Document':
@@ -1606,7 +1837,7 @@ EOF
$aExtKeyParams = $aArgs; $aExtKeyParams = $aArgs;
$aExtKeyParams['iFieldSize'] = $oAttDef->GetMaxSize(); $aExtKeyParams['iFieldSize'] = $oAttDef->GetMaxSize();
$aExtKeyParams['iMinChars'] = $oAttDef->GetMinAutoCompleteChars(); $aExtKeyParams['iMinChars'] = $oAttDef->GetMinAutoCompleteChars();
$sHTMLValue = UIExtKeyWidget::DisplayFromAttCode($oPage, $sAttCode, $sClass, $oAttDef->GetLabel(), $oAllowedValues, $value, $iId, $bMandatory, $sFieldName, $sFormPrefix, $aArgs); $sHTMLValue = UIExtKeyWidget::DisplayFromAttCode($oPage, $sAttCode, $sClass, $oAttDef->GetLabel(), $oAllowedValues, $value, $iId, $bMandatory, $sFieldName, $sFormPrefix, $aExtKeyParams);
$sHTMLValue .= "<!-- iFlags: $iFlags bMandatory: $bMandatory -->\n"; $sHTMLValue .= "<!-- iFlags: $iFlags bMandatory: $bMandatory -->\n";
break; break;
@@ -1733,7 +1964,7 @@ EOF
if ($iKey > 0) if ($iKey > 0)
{ {
// The object already exists in the database, it's a modification // The object already exists in the database, it's a modification
$sButtons = "<input type=\"hidden\" name=\"id\" value=\"$iKey\">\n"; $sButtons = "<input id=\"{$sPrefix}_id\" type=\"hidden\" name=\"id\" value=\"$iKey\">\n";
$sButtons .= "<input type=\"hidden\" name=\"operation\" value=\"{$sOperation}\">\n"; $sButtons .= "<input type=\"hidden\" name=\"operation\" value=\"{$sOperation}\">\n";
$sButtons .= "<button type=\"button\" class=\"action cancel\"><span>".Dict::S('UI:Button:Cancel')."</span></button>&nbsp;&nbsp;&nbsp;&nbsp;\n"; $sButtons .= "<button type=\"button\" class=\"action cancel\"><span>".Dict::S('UI:Button:Cancel')."</span></button>&nbsp;&nbsp;&nbsp;&nbsp;\n";
$sButtons .= "<button type=\"submit\" class=\"action\"><span>{$sApplyButton}</span></button>\n"; $sButtons .= "<button type=\"submit\" class=\"action\"><span>{$sApplyButton}</span></button>\n";
@@ -1819,6 +2050,10 @@ EOF
$oPage->SetCurrentTab(Dict::S('UI:PropertiesTab')); $oPage->SetCurrentTab(Dict::S('UI:PropertiesTab'));
$aFieldsMap = $this->DisplayBareProperties($oPage, true, $sPrefix, $aExtraParams); $aFieldsMap = $this->DisplayBareProperties($oPage, true, $sPrefix, $aExtraParams);
if ($iKey > 0)
{
$aFieldsMap['id'] = $sPrefix.'_id';
}
// Now display the relations, one tab per relation // Now display the relations, one tab per relation
if (!isset($aExtraParams['noRelations'])) if (!isset($aExtraParams['noRelations']))
{ {
@@ -2146,7 +2381,7 @@ EOF
$current = HILIGHT_CLASS_NONE; // Not hilighted by default $current = HILIGHT_CLASS_NONE; // Not hilighted by default
// Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information // Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{ {
$new = $oExtensionInstance->GetHilightClass($this); $new = $oExtensionInstance->GetHilightClass($this);
@$current = self::$m_highlightComparison[$current][$new]; @$current = self::$m_highlightComparison[$current][$new];
@@ -2431,7 +2666,7 @@ EOF
$this->UpdateObjectFromArray($aFinalValues); $this->UpdateObjectFromArray($aFinalValues);
// Invoke extensions after the update of the object from the form // Invoke extensions after the update of the object from the form
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{ {
$oExtensionInstance->OnFormSubmit($this, $sFormPrefix); $oExtensionInstance->OnFormSubmit($this, $sFormPrefix);
} }

View File

@@ -348,7 +348,11 @@ class DisplayBlock
$aGroupBy = array(); $aGroupBy = array();
$sLabels = array(); $sLabels = array();
$iTotalCount = $this->m_oSet->Count(); $iTotalCount = $this->m_oSet->Count();
while($oObj = $this->m_oSet->Fetch()) $oTmpSet = clone $this->m_oSet;
// Speed up the load, load only the needed field to group on
$sAlias = $oTmpSet->GetFilter()->GetClassAlias();
$oTmpSet->OptimizeColumnLoad(array($sAlias => array($sGroupByField)));
while($oObj = $oTmpSet->Fetch())
{ {
if (isset($aExtraParams['group_by_expr'])) if (isset($aExtraParams['group_by_expr']))
{ {
@@ -746,7 +750,7 @@ EOF
$sHtml .= "<div id=\"my_chart_{$iChartCounter}\">If the chart does not display, <a href=\"http://get.adobe.com/flash/\" target=\"_blank\">install Flash</a></div>\n"; $sHtml .= "<div id=\"my_chart_{$iChartCounter}\">If the chart does not display, <a href=\"http://get.adobe.com/flash/\" target=\"_blank\">install Flash</a></div>\n";
$oPage->add_script("function ofc_resize(left, width, top, height) { /* do nothing special */ }"); $oPage->add_script("function ofc_resize(left, width, top, height) { /* do nothing special */ }");
$oPage->add_ready_script("swfobject.embedSWF(\"../images/open-flash-chart.swf\", \"my_chart_{$iChartCounter}\", \"100%\", \"300\",\"9.0.0\", \"expressInstall.swf\", $oPage->add_ready_script("swfobject.embedSWF(\"../images/open-flash-chart.swf\", \"my_chart_{$iChartCounter}\", \"100%\", \"300\",\"9.0.0\", \"expressInstall.swf\",
{\"data-file\":\"".urlencode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=open_flash_chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[chart_type]=$sChartType&params[chart_title]=$sTitle&id=$sId&filter=".$sFilter)."\"}, {wmode: 'transparent'} );\n"); {\"data-file\":\"".urlencode(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=open_flash_chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[chart_type]=$sChartType&params[chart_title]=$sTitle&params[currentId]=$sId&id=$sId&filter=".$sFilter)."\"}, {wmode: 'transparent'} );\n");
$iChartCounter++; $iChartCounter++;
if (isset($aExtraParams['group_by'])) if (isset($aExtraParams['group_by']))
{ {
@@ -1081,12 +1085,12 @@ class MenuBlock extends DisplayBlock
$sDefault.= "&default[$sKey]=$sValue"; $sDefault.= "&default[$sKey]=$sValue";
} }
} }
$bIsCreationAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES);
switch($oSet->Count()) switch($oSet->Count())
{ {
case 0: case 0:
// No object in the set, the only possible action is "new" // No object in the set, the only possible action is "new"
$bIsModifyAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES); if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}"); }
if ($bIsModifyAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}"); }
break; break;
case 1: case 1:
@@ -1100,14 +1104,14 @@ class MenuBlock extends DisplayBlock
if (!isset($aExtraParams['link_attr'])) if (!isset($aExtraParams['link_attr']))
{ {
if ($bIsModifyAllowed) { $aActions['UI:Menu:Modify'] = array ('label' => Dict::S('UI:Menu:Modify'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=modify&class=$sClass&id=$id{$sContext}#"); } if ($bIsModifyAllowed) { $aActions['UI:Menu:Modify'] = array ('label' => Dict::S('UI:Menu:Modify'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=modify&class=$sClass&id=$id{$sContext}#"); }
if ($bIsModifyAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}"); } if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}"); }
if ($bIsDeleteAllowed) { $aActions['UI:Menu:Delete'] = array ('label' => Dict::S('UI:Menu:Delete'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$id{$sContext}"); } if ($bIsDeleteAllowed) { $aActions['UI:Menu:Delete'] = array ('label' => Dict::S('UI:Menu:Delete'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$id{$sContext}"); }
// Transitions / Stimuli // Transitions / Stimuli
$aTransitions = $oObj->EnumTransitions(); $aTransitions = $oObj->EnumTransitions();
if (count($aTransitions)) if (count($aTransitions))
{ {
$this->AddMenuSeparator($aActions); $this->AddMenuSeparator($aActions);
$aStimuli = Metamodel::EnumStimuli($sClass); $aStimuli = Metamodel::EnumStimuli(get_class($oObj));
foreach($aTransitions as $sStimulusCode => $aTransitionDef) foreach($aTransitions as $sStimulusCode => $aTransitionDef)
{ {
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet) : UR_ALLOWED_NO; $iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet) : UR_ALLOWED_NO;
@@ -1169,13 +1173,16 @@ class MenuBlock extends DisplayBlock
else else
{ {
// many objects in the set, possible actions are: new / modify all / delete all // many objects in the set, possible actions are: new / modify all / delete all
if ($bIsModifyAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}"); } if ($bIsCreationAllowed) { $aActions['UI:Menu:New'] = array ('label' => Dict::S('UI:Menu:New'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}"); }
if ($bIsBulkModifyAllowed) { $aActions['UI:Menu:ModifyAll'] = array ('label' => Dict::S('UI:Menu:ModifyAll'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=select_for_modify_all&class=$sClass&filter=$sFilter{$sContext}"); } if ($bIsBulkModifyAllowed) { $aActions['UI:Menu:ModifyAll'] = array ('label' => Dict::S('UI:Menu:ModifyAll'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=select_for_modify_all&class=$sClass&filter=$sFilter{$sContext}"); }
if ($bIsBulkDeleteAllowed) { $aActions['UI:Menu:BulkDelete'] = array ('label' => Dict::S('UI:Menu:BulkDelete'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=select_for_deletion&filter=$sFilter{$sContext}"); } if ($bIsBulkDeleteAllowed) { $aActions['UI:Menu:BulkDelete'] = array ('label' => Dict::S('UI:Menu:BulkDelete'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=select_for_deletion&filter=$sFilter{$sContext}"); }
// Stimuli // Stimuli
$aStates = MetaModel::EnumStates($sClass); $aStates = MetaModel::EnumStates($sClass);
if (count($aStates) > 0) // 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)))
{ {
// Life cycle actions may be available... if all objects are in the same state // Life cycle actions may be available... if all objects are in the same state
$oSet->Rewind(); $oSet->Rewind();

View File

@@ -32,13 +32,13 @@ require_once(APPROOT."/application/user.preferences.class.inc.php");
class iTopWebPage extends NiceWebPage class iTopWebPage extends NiceWebPage
{ {
private $m_sMenu; private $m_sMenu;
// private $m_currentOrganization; // private $m_currentOrganization;
private $m_aTabs; private $m_aTabs;
private $m_sCurrentTabContainer; private $m_sCurrentTabContainer;
private $m_sCurrentTab; private $m_sCurrentTab;
private $m_sMessage; private $m_sMessage;
private $m_sInitScript; private $m_sInitScript;
public function __construct($sTitle) public function __construct($sTitle)
{ {
parent::__construct($sTitle); parent::__construct($sTitle);
@@ -146,7 +146,12 @@ class iTopWebPage extends NiceWebPage
// that the tabs aren't changed on click, and any custom event name can be // that the tabs aren't changed on click, and any custom event name can be
// specified. Note that if you define a callback for the 'select' event, it // specified. Note that if you define a callback for the 'select' event, it
// will be executed for the selected tab whenever the hash changes. // will be executed for the selected tab whenever the hash changes.
tabs.tabs({ event: 'change'}); tabs.tabs({ event: 'change', 'show': function(event, ui) {
$('.resizable', ui.panel).resizable(); // Make resizable everything that claims to be resizable !
}
});
$('.resizable').filter(':visible').resizable();
} }
catch(err) catch(err)
{ {
@@ -154,7 +159,7 @@ class iTopWebPage extends NiceWebPage
alert(err); alert(err);
} }
EOF EOF
; ;
$this->add_ready_script( $this->add_ready_script(
<<< EOF <<< EOF
//add new widget called TruncatedList to properly display truncated lists when they are sorted //add new widget called TruncatedList to properly display truncated lists when they are sorted
@@ -198,7 +203,6 @@ EOF
} }
}); });
$('.resizable').resizable(); // Make resizable everything that claims to be resizable !
// Adjust initial size // Adjust initial size
$('.v-resizable').each( function() $('.v-resizable').each( function()
{ {
@@ -313,6 +317,15 @@ EOF
changeMonth: true, changeMonth: true,
changeYear: true changeYear: true
}); });
$(".datetime-pick").datepicker({
showOn: 'button',
buttonImage: '../images/calendar.png',
buttonImageOnly: true,
dateFormat: 'yy-mm-dd 00:00:00',
constrainInput: false,
changeMonth: true,
changeYear: true
});
// Restore the persisted sortable order, for all sortable lists... if any // Restore the persisted sortable order, for all sortable lists... if any
$('.sortable').each(function() $('.sortable').each(function()
{ {
@@ -350,7 +363,7 @@ EOF
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry').toggle(); }); $('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry').toggle(); });
EOF EOF
); );
$sUserPrefs = appUserPreferences::GetAsJSON(); $sUserPrefs = appUserPreferences::GetAsJSON();
$this->add_script( $this->add_script(
<<<EOF <<<EOF
@@ -403,6 +416,11 @@ EOF
return '$sAbsURLAppRoot'; return '$sAbsURLAppRoot';
} }
function GetAbsoluteUrlModulesRoot()
{
return '$sAbsURLAppRoot'+'modules/';
}
function AddAppContext(sURL) function AddAppContext(sURL)
{ {
var sContext = '$sAppContext'; var sContext = '$sAppContext';
@@ -434,10 +452,10 @@ EOF
}); });
EOF EOF
); );
// Build menus from module handlers // Build menus from module handlers
// //
foreach(get_declared_classes() as $sPHPClass) foreach(get_declared_classes() as $sPHPClass)
{ {
if (is_subclass_of($sPHPClass, 'ModuleHandlerAPI')) if (is_subclass_of($sPHPClass, 'ModuleHandlerAPI'))
@@ -447,7 +465,7 @@ EOF
} }
} }
} }
public function AddToMenu($sHtml) public function AddToMenu($sHtml)
{ {
$this->m_sMenu .= $sHtml; $this->m_sMenu .= $sHtml;
@@ -463,11 +481,13 @@ EOF
// Display the list of *favorite* organizations... but keeping in mind what is the real number of organizations // Display the list of *favorite* organizations... but keeping in mind what is the real number of organizations
$aFavoriteOrgs = appUserPreferences::GetPref('favorite_orgs', null); $aFavoriteOrgs = appUserPreferences::GetPref('favorite_orgs', null);
$oSearchFilter = new DBObjectSearch('Organization'); $oSearchFilter = new DBObjectSearch('Organization');
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oSet = new CMDBObjectSet($oSearchFilter); $oSet = new CMDBObjectSet($oSearchFilter);
$iCount = $oSet->Count(); // total number of existing Orgs $iCount = $oSet->Count(); // total number of existing Orgs
// Now get the list of Orgs to be displayed in the menu // Now get the list of Orgs to be displayed in the menu
$oSearchFilter = DBObjectSearch::FromOQL(ApplicationMenu::GetFavoriteSiloQuery()); $oSearchFilter = DBObjectSearch::FromOQL(ApplicationMenu::GetFavoriteSiloQuery());
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
if (!empty($aFavoriteOrgs)) if (!empty($aFavoriteOrgs))
{ {
$oSearchFilter->AddCondition('id', $aFavoriteOrgs, 'IN'); $oSearchFilter->AddCondition('id', $aFavoriteOrgs, 'IN');
@@ -477,76 +497,76 @@ EOF
switch($iCount) switch($iCount)
{ {
case 0: case 0:
// No such dimension/silo => nothing to select // No such dimension/silo => nothing to select
$sHtml = '<div id="SiloSelection"><!-- nothing to select --></div>'; $sHtml = '<div id="SiloSelection"><!-- nothing to select --></div>';
break; break;
case 1: case 1:
// Only one possible choice... no selection, but display the value // Only one possible choice... no selection, but display the value
$oOrg = $oSet->Fetch(); $oOrg = $oSet->Fetch();
$sHtml = '<div id="SiloSelection">'.$oOrg->GetName().'</div>'; $sHtml = '<div id="SiloSelection">'.$oOrg->GetName().'</div>';
$sHtml .= ''; $sHtml .= '';
break; break;
default: default:
$sHtml = ''; $sHtml = '';
$oAppContext = new ApplicationContext(); $oAppContext = new ApplicationContext();
$iCurrentOrganization = $oAppContext->GetCurrentValue('org_id'); $iCurrentOrganization = $oAppContext->GetCurrentValue('org_id');
$sHtml = '<div id="SiloSelection">'; $sHtml = '<div id="SiloSelection">';
$sHtml .= '<form style="display:inline" action="'.utils::GetAbsoluteUrlAppRoot().'pages/UI.php">'; //<select class="org_combo" name="c[org_id]" title="Pick an organization" onChange="this.form.submit();">'; $sHtml .= '<form style="display:inline" action="'.utils::GetAbsoluteUrlAppRoot().'pages/UI.php">'; //<select class="org_combo" name="c[org_id]" title="Pick an organization" onChange="this.form.submit();">';
/* /*
$sSelected = ($iCurrentOrganization == '') ? ' selected' : ''; $sSelected = ($iCurrentOrganization == '') ? ' selected' : '';
$sHtml .= '<option value=""'.$sSelected.'>'.Dict::S('UI:AllOrganizations').'</option>'; $sHtml .= '<option value=""'.$sSelected.'>'.Dict::S('UI:AllOrganizations').'</option>';
while($oOrg = $oSet->Fetch()) while($oOrg = $oSet->Fetch())
{ {
if ($iCurrentOrganization == $oOrg->GetKey()) if ($iCurrentOrganization == $oOrg->GetKey())
{ {
// $oCurrentOrganization = $oOrg; // $oCurrentOrganization = $oOrg;
$sSelected = " selected"; $sSelected = " selected";
} }
else else
{ {
$sSelected = ""; $sSelected = "";
} }
$sHtml .= '<option title="'.$oOrg->GetName().'" value="'.$oOrg->GetKey().'"'.$sSelected.'>'.$oOrg->GetName().'</option>'; $sHtml .= '<option title="'.$oOrg->GetName().'" value="'.$oOrg->GetKey().'"'.$sSelected.'>'.$oOrg->GetName().'</option>';
} }
$sHtml .= '</select>'; $sHtml .= '</select>';
*/ */
$sFavoriteOrgs = ''; $sFavoriteOrgs = '';
$oWidget = new UIExtKeyWidget('Organization', 'org_id'); $oWidget = new UIExtKeyWidget('Organization', 'org_id', '', true /* search mode */);
$sHtml .= $oWidget->Display($this, 50, false, '', $oSet, $iCurrentOrganization, 'org_id', false, 'c[org_id]', '', array('iFieldSize' => 20, 'sDefaultValue' => Dict::S('UI:AllOrganizations')), $bSearchMode = true); $sHtml .= $oWidget->Display($this, 50, false, '', $oSet, $iCurrentOrganization, 'org_id', false, 'c[org_id]', '', array('iFieldSize' => 20, 'iMinChars' => MetaModel::GetConfig()->Get('min_autocomplete_chars'), 'sDefaultValue' => Dict::S('UI:AllOrganizations')));
$this->add_ready_script('$("#org_id").bind("extkeychange", function() { $("#SiloSelection form").submit(); } )'); $this->add_ready_script('$("#org_id").bind("extkeychange", function() { $("#SiloSelection form").submit(); } )');
$this->add_ready_script("$('#label_org_id').click( function() { $(this).val(''); $('#org_id').val(''); return true; } );\n"); $this->add_ready_script("$('#label_org_id').click( function() { $(this).val(''); $('#org_id').val(''); return true; } );\n");
// Add other dimensions/context information to this form // Add other dimensions/context information to this form
$oAppContext->Reset('org_id'); // org_id is handled above and we want to be able to change it here ! $oAppContext->Reset('org_id'); // org_id is handled above and we want to be able to change it here !
$oAppContext->Reset('menu'); // don't pass the menu, since a menu may expect more parameters $oAppContext->Reset('menu'); // don't pass the menu, since a menu may expect more parameters
$sHtml .= $oAppContext->GetForForm(); // Pass what remains, if anything... $sHtml .= $oAppContext->GetForForm(); // Pass what remains, if anything...
$sHtml .= '</form>'; $sHtml .= '</form>';
$sHtml .= '</div>'; $sHtml .= '</div>';
} }
return $sHtml; return $sHtml;
} }
public function DisplayMenu() public function DisplayMenu()
{ {
// Display the menu // Display the menu
$oAppContext = new ApplicationContext(); $oAppContext = new ApplicationContext();
$iAccordionIndex = 0; $iAccordionIndex = 0;
ApplicationMenu::DisplayMenu($this, $oAppContext->GetAsHash()); ApplicationMenu::DisplayMenu($this, $oAppContext->GetAsHash());
} }
/** /**
* Outputs (via some echo) the complete HTML page by assembling all its elements * Outputs (via some echo) the complete HTML page by assembling all its elements
*/ */
public function output() public function output()
{ {
$sForm = $this->GetSiloSelectionForm(); $sForm = $this->GetSiloSelectionForm();
$this->DisplayMenu(); // Compute the menu $this->DisplayMenu(); // Compute the menu
// Put here the 'ready scripts' that must be executed after all others // Put here the 'ready scripts' that must be executed after all others
$this->add_ready_script( $this->add_ready_script(
<<<EOF <<<EOF
// Since the event is only triggered when the hash changes, we need to trigger // Since the event is only triggered when the hash changes, we need to trigger
// the event now, to handle the hash the page may have loaded with. // the event now, to handle the hash the page may have loaded with.
@@ -556,76 +576,87 @@ EOF
$('table.listResults').each( function() { FixTableSorter($(this)); } ); $('table.listResults').each( function() { FixTableSorter($(this)); } );
EOF EOF
); );
foreach($this->a_headers as $s_header) if ($this->GetOutputFormat() == 'html')
{ {
header($s_header); foreach($this->a_headers as $s_header)
}
$s_captured_output = ob_get_contents();
ob_end_clean();
echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
echo "<html>\n";
echo "<head>\n";
// Make sure that Internet Explorer renders the page using its latest/highest/greatest standards !
echo "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n";
echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
echo "<title>".htmlentities($this->s_title, ENT_QUOTES, 'UTF-8')."</title>\n";
echo $this->get_base_tag();
// Stylesheets MUST be loaded before any scripts otherwise
// jQuery scripts may face some spurious problems (like failing on a 'reload')
foreach($this->a_linked_stylesheets as $a_stylesheet)
{
if ($a_stylesheet['condition'] != "")
{ {
echo "<!--[if {$a_stylesheet['condition']}]>\n"; header($s_header);
} }
echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"{$a_stylesheet['link']}\" />\n";
if ($a_stylesheet['condition'] != "")
{
echo "<![endif]-->\n";
}
}
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 .= "?version=".ITOP_VERSION;
}
else
{
$s_script .= "&version=".ITOP_VERSION;
}
echo "<script type=\"text/javascript\" src=\"$s_script\"></script>\n";
}
$this->add_script("\$(document).ready(function() {\n{$this->m_sInitScript};\nwindow.setTimeout('onDelayedReady()',10)\n});");
if (count($this->m_aReadyScripts)>0)
{
$this->add_script("\nonDelayedReady = function() {\n".implode("\n", $this->m_aReadyScripts)."\n}\n");
} }
if (count($this->a_scripts)>0) $s_captured_output = ob_get_contents();
{ ob_end_clean();
echo "<script type=\"text/javascript\">\n"; $sHtml = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
foreach($this->a_scripts as $s_script) $sHtml .= "<html>\n";
{ $sHtml .= "<head>\n";
echo "$s_script\n"; // Make sure that Internet Explorer renders the page using its latest/highest/greatest standards !
} $sHtml .= "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n";
echo "</script>\n"; $sHtml .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
} $sHtml .= "<title>".htmlentities($this->s_title, ENT_QUOTES, 'UTF-8')."</title>\n";
$sHtml .= $this->get_base_tag();
if (count($this->a_styles)>0) // Stylesheets MUST be loaded before any scripts otherwise
{ // jQuery scripts may face some spurious problems (like failing on a 'reload')
echo "<style>\n"; foreach($this->a_linked_stylesheets as $a_stylesheet)
foreach($this->a_styles as $s_style) {
{ if ($a_stylesheet['condition'] != "")
echo "$s_style\n"; {
} $sHtml .= "<!--[if {$a_stylesheet['condition']}]>\n";
echo "</style>\n"; }
} $sHtml .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"{$a_stylesheet['link']}\" />\n";
echo "<link rel=\"search\" type=\"application/opensearchdescription+xml\" title=\"iTop\" href=\"".utils::GetAbsoluteUrlAppRoot()."pages/opensearch.xml.php\" />\n"; if ($a_stylesheet['condition'] != "")
echo "</head>\n"; {
echo "<body>\n"; $sHtml .= "<![endif]-->\n";
}
}
// special stylesheet for printing, hides the navigation gadgets
$sHtml .= "<link rel=\"stylesheet\" media=\"print\" type=\"text/css\" href=\"../css/print.css\" />\n";
if ($this->GetOutputFormat() == 'html')
{
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;
}
else
{
$s_script .= "&itopversion=".ITOP_VERSION;
}
$sHtml .= "<script type=\"text/javascript\" src=\"$s_script\"></script>\n";
}
$this->add_script("\$(document).ready(function() {\n{$this->m_sInitScript};\nwindow.setTimeout('onDelayedReady()',10)\n});");
if (count($this->m_aReadyScripts)>0)
{
$this->add_script("\nonDelayedReady = function() {\n".implode("\n", $this->m_aReadyScripts)."\n}\n");
}
if (count($this->a_scripts)>0)
{
$sHtml .= "<script type=\"text/javascript\">\n";
foreach($this->a_scripts as $s_script)
{
$sHtml .= "$s_script\n";
}
$sHtml .= "</script>\n";
}
}
if (count($this->a_styles)>0)
{
$sHtml .= "<style>\n";
foreach($this->a_styles as $s_style)
{
$sHtml .= "$s_style\n";
}
$sHtml .= "</style>\n";
}
$sHtml .= "<link rel=\"search\" type=\"application/opensearchdescription+xml\" title=\"iTop\" href=\"".utils::GetAbsoluteUrlAppRoot()."pages/opensearch.xml.php\" />\n";
$sHtml .= "<link rel=\"shortcut icon\" href=\"".utils::GetAbsoluteUrlAppRoot()."images/favicon.ico\" />\n";
$sHtml .= "</head>\n";
$sHtml .= "<body>\n";
@@ -655,7 +686,7 @@ EOF
// 2) clicking on it will erase it // 2) clicking on it will erase it
$sText = Dict::S("UI:YourSearch"); $sText = Dict::S("UI:YourSearch");
$sOnClick = " onclick=\"this.value='';this.onclick=null;\""; $sOnClick = " onclick=\"this.value='';this.onclick=null;\"";
} }
// Render the tabs in the page (if any) // Render the tabs in the page (if any)
foreach($this->m_aTabs as $sTabContainerName => $m_aTabs) foreach($this->m_aTabs as $sTabContainerName => $m_aTabs)
{ {
@@ -663,149 +694,185 @@ EOF
$container_index = 0; $container_index = 0;
if (count($m_aTabs) > 0) if (count($m_aTabs) > 0)
{ {
$sTabs = "<!-- tabs -->\n<div id=\"tabbedContent_{$container_index}\" class=\"light\">\n"; $sTabs = "<!-- tabs -->\n<div id=\"tabbedContent_{$container_index}\" class=\"light\">\n";
$sTabs .= "<ul>\n"; $sTabs .= "<ul>\n";
// Display the unordered list that will be rendered as the tabs // Display the unordered list that will be rendered as the tabs
$i = 0; $i = 0;
foreach($m_aTabs as $sTabName => $sTabContent) foreach($m_aTabs as $sTabName => $sTabContent)
{ {
$sTabs .= "<li><a href=\"#tab_$i\" class=\"tab\"><span>".htmlentities($sTabName, ENT_QUOTES, 'UTF-8')."</span></a></li>\n"; $sTabs .= "<li><a href=\"#tab_$i\" class=\"tab\"><span>".htmlentities($sTabName, ENT_QUOTES, 'UTF-8')."</span></a></li>\n";
$i++; $i++;
} }
$sTabs .= "</ul>\n"; $sTabs .= "</ul>\n";
// Now add the content of the tabs themselves // Now add the content of the tabs themselves
$i = 0; $i = 0;
foreach($m_aTabs as $sTabName => $sTabContent) foreach($m_aTabs as $sTabName => $sTabContent)
{ {
$sTabs .= "<div id=\"tab_$i\">".$sTabContent."</div>\n"; $sTabs .= "<div id=\"tab_$i\">".$sTabContent."</div>\n";
$i++; $i++;
} }
$sTabs .= "</div>\n<!-- end of tabs-->\n"; $sTabs .= "</div>\n<!-- end of tabs-->\n";
} }
$this->s_content = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $this->s_content); $this->s_content = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $this->s_content);
$container_index++; $container_index++;
} }
$sUserName = UserRights::GetUser();
$sIsAdmin = UserRights::IsAdministrator() ? '(Administrator)' : '';
if (UserRights::IsAdministrator())
{
$sLogonMessage = Dict::Format('UI:LoggedAsMessage+Admin', $sUserName);
}
else
{
$sLogonMessage = Dict::Format('UI:LoggedAsMessage', $sUserName);
}
$sLogOffMenu = "<span id=\"logOffBtn\"><ul><li><img src=\"../images/onOffBtn.png\"><ul>";
$sLogOffMenu .= "<li><span>$sLogonMessage</span></li>\n";
$sLogOffMenu .= "<li><a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/preferences.php\">".Dict::S('UI:Preferences')."</a></li>\n";
if (utils::CanLogOff())
{
//$sLogOffMenu .= "<li><a href=\"../pages/UI.php?loginop=logoff\">".Dict::S('UI:LogOffMenu')."</a></li>\n";
$sLogOffMenu .= "<li><a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a></li>\n";
}
if (UserRights::CanChangePassword())
{
$sLogOffMenu .= "<li><a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?loginop=change_pwd\">".Dict::S('UI:ChangePwdMenu')."</a></li>\n";
}
$sLogOffMenu .= "</ul>\n</li>\n</ul></span>\n";
$sRestrictions = ''; if ($this->GetOutputFormat() == 'html')
if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE))
{ {
$sUserName = UserRights::GetUser();
$sIsAdmin = UserRights::IsAdministrator() ? '(Administrator)' : '';
if (UserRights::IsAdministrator())
{
$sLogonMessage = Dict::Format('UI:LoggedAsMessage+Admin', $sUserName);
}
else
{
$sLogonMessage = Dict::Format('UI:LoggedAsMessage', $sUserName);
}
$sLogOffMenu = "<span id=\"logOffBtn\"><ul><li><img src=\"../images/onOffBtn.png\"><ul>";
$sLogOffMenu .= "<li><span>$sLogonMessage</span></li>\n";
$sLogOffMenu .= "<li><a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/preferences.php\">".Dict::S('UI:Preferences')."</a></li>\n";
if (utils::CanLogOff())
{
//$sLogOffMenu .= "<li><a href=\"../pages/UI.php?loginop=logoff\">".Dict::S('UI:LogOffMenu')."</a></li>\n";
$sLogOffMenu .= "<li><a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a></li>\n";
}
if (UserRights::CanChangePassword())
{
$sLogOffMenu .= "<li><a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?loginop=change_pwd\">".Dict::S('UI:ChangePwdMenu')."</a></li>\n";
}
$sLogOffMenu .= "</ul>\n</li>\n</ul></span>\n";
$sRestrictions = '';
if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE)) if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE))
{ {
$sRestrictions = Dict::S('UI:AccessRO-All'); if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE))
{
$sRestrictions = Dict::S('UI:AccessRO-All');
}
} }
} elseif (!MetaModel::DBHasAccess(ACCESS_USER_WRITE))
elseif (!MetaModel::DBHasAccess(ACCESS_USER_WRITE))
{
$sRestrictions = Dict::S('UI:AccessRO-Users');
}
if (strlen($sRestrictions) > 0)
{
$sAdminMessage = trim(MetaModel::GetConfig()->Get('access_message'));
$sApplicationBanner = '<div id="admin-banner">';
$sApplicationBanner .= '<img src="../images/locked.png" style="vertical-align:middle;">';
$sApplicationBanner .= '&nbsp;<b>'.$sRestrictions.'</b>';
if (strlen($sAdminMessage) > 0)
{ {
$sApplicationBanner .= '&nbsp;<b>'.$sAdminMessage.'</b>'; $sRestrictions = Dict::S('UI:AccessRO-Users');
} }
$sApplicationBanner .= '</div>';
} if (strlen($sRestrictions) > 0)
else if(strlen($this->m_sMessage)) {
{ $sAdminMessage = trim(MetaModel::GetConfig()->Get('access_message'));
$sApplicationBanner = '<div id="admin-banner"><span style="padding:5px;">'.$this->m_sMessage.'<span></div>'; $sApplicationBanner = '<div id="admin-banner">';
$sApplicationBanner .= '<img src="../images/locked.png" style="vertical-align:middle;">';
$sApplicationBanner .= '&nbsp;<b>'.$sRestrictions.'</b>';
if (strlen($sAdminMessage) > 0)
{
$sApplicationBanner .= '&nbsp;<b>'.$sAdminMessage.'</b>';
}
$sApplicationBanner .= '</div>';
}
else if(strlen($this->m_sMessage))
{
$sApplicationBanner = '<div id="admin-banner"><span style="padding:5px;">'.$this->m_sMessage.'<span></div>';
}
else
{
$sApplicationBanner = '';
}
$sOnlineHelpUrl = MetaModel::GetConfig()->Get('online_help');
//$sLogOffMenu = "<span id=\"logOffBtn\" style=\"height:55px;padding:0;margin:0;\"><img src=\"../images/onOffBtn.png\"></span>";
$sHtml .= '<div id="left-pane" class="ui-layout-west">';
$sHtml .= '<!-- Beginning of the left pane -->';
$sHtml .= ' <div id="header-logo">';
$sHtml .= ' <div id="top-left"></div><div id="logo"><a href="http://www.combodo.com/itop"><img src="../images/itop-logo.png" title="'.htmlentities($sVersionString, ENT_QUOTES, 'UTF-8').'" style="border:0; margin-top:16px; margin-right:40px;"/></a></div>';
$sHtml .= ' </div>';
$sHtml .= ' <div class="header-menu">';
$sHtml .= ' <div class="icon ui-state-default ui-corner-all"><span id="tPinMenu" class="ui-icon ui-icon-pin-w">pin</span></div>';
$sHtml .= ' <div style="text-align:center;">'.self::FilterXSS($sForm).'</div>';
$sHtml .= ' </div>';
$sHtml .= ' <div id="menu" class="ui-layout-content">';
$sHtml .= ' <div id="inner_menu">';
$sHtml .= ' <div id="accordion">';
$sHtml .= self::FilterXSS($this->m_sMenu);
$sHtml .= ' <!-- Beginning of the accordion menu -->';
$sHtml .= ' <!-- End of the accordion menu-->';
$sHtml .= ' </div>';
$sHtml .= ' </div> <!-- /inner menu -->';
$sHtml .= ' </div> <!-- /menu -->';
$sHtml .= ' <div class="footer"><a href="http://www.combodo.com" title="www.combodo.com" target="_blank"><img src="../images/logo-combodo.png"/></a></div>';
$sHtml .= '<!-- End of the left pane -->';
$sHtml .= '</div>';
$sHtml .= '<div class="ui-layout-center">';
$sHtml .= ' <div id="top-bar" style="width:100%">';
$sHtml .= self::FilterXSS($sApplicationBanner);
$sHtml .= ' <div id="global-search"><form action="'.utils::GetAbsoluteUrlAppRoot().'pages/UI.php"><table><tr><td></td><td id="g-search-input"><input type="text" name="text" value="'.$sText.'"'.$sOnClick.'/></td>';
$sHtml .= '<td><input type="image" src="../images/searchBtn.png"/></a></td>';
$sHtml .= '<td><a style="background:transparent;" href="'.$sOnlineHelpUrl.'" target="_blank"><img style="border:0;padding-left:20px;padding-right:10px;" title="'.Dict::S('UI:Help').'" src="../images/help.png"/></td>';
$sHtml .= '<td style="padding-right:20px;padding-left:10px;">'.self::FilterXSS($sLogOffMenu).'</td><td><input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
//echo '<td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
$sHtml .= ' </div>';
$sHtml .= ' <div class="ui-layout-content">';
$sHtml .= ' <!-- Beginning of page content -->';
$sHtml .= self::FilterXSS($this->s_content);
$sHtml .= ' <!-- End of page content -->';
$sHtml .= ' </div>';
$sHtml .= '</div>';
// Add the captured output
if (trim($s_captured_output) != "")
{
$sHtml .= "<div id=\"rawOutput\" title=\"Debug Output\"><div style=\"height:500px; overflow-y:auto;\">".self::FilterXSS($s_captured_output)."</div></div>\n";
}
$sHtml .= "<div id=\"at_the_end\">".self::FilterXSS($this->s_deferred_content)."</div>";
// echo $this->s_deferred_content;
$sHtml .= "<div style=\"display:none\" title=\"ex2\" id=\"ex2\">Please wait...</div>\n"; // jqModal Window
$sHtml .= "<div style=\"display:none\" title=\"dialog\" id=\"ModalDlg\"></div>";
$sHtml .= "<div style=\"display:none\" id=\"ajax_content\"></div>";
} }
else else
{ {
$sApplicationBanner = ''; $sHtml .= self::FilterXSS($this->s_content);
} }
$sOnlineHelpUrl = MetaModel::GetConfig()->Get('online_help'); $sHtml .= "</body>\n";
//$sLogOffMenu = "<span id=\"logOffBtn\" style=\"height:55px;padding:0;margin:0;\"><img src=\"../images/onOffBtn.png\"></span>"; $sHtml .= "</html>\n";
echo '<div id="left-pane" class="ui-layout-west">'; if ($this->GetOutputFormat() == 'html')
echo '<!-- Beginning of the left pane -->'; {
echo ' <div id="header-logo">'; echo $sHtml;
echo ' <div id="top-left"></div><div id="logo"><a href="http://www.combodo.com/itop"><img src="../images/itop-logo.png" title="'.htmlentities($sVersionString, ENT_QUOTES, 'UTF-8').'" style="border:0; margin-top:16px; margin-right:40px;"/></a></div>'; }
echo ' </div>'; else if ($this->GetOutputFormat() == 'pdf' && $this->IsOutputFormatAvailable('pdf') )
echo ' <div class="header-menu">'; {
echo ' <div class="icon ui-state-default ui-corner-all"><span id="tPinMenu" class="ui-icon ui-icon-pin-w">pin</span></div>'; require_once(APPROOT.'lib/MPDF/mpdf.php');
echo ' <div style="text-align:center;">'.self::FilterXSS($sForm).'</div>'; $oMPDF = new mPDF('c');
echo ' </div>'; $oMPDF->mirroMargins = false;
echo ' <div id="menu" class="ui-layout-content">'; if ($this->a_base['href'] != '')
echo ' <div id="inner_menu">'; {
echo ' <div id="accordion">'; $oMPDF->setBasePath($this->a_base['href']); // Seems that the <BASE> tag is not recognized by mPDF...
echo self::FilterXSS($this->m_sMenu); }
echo ' <!-- Beginning of the accordion menu -->'; $oMPDF->showWatermarkText = true;
echo ' <!-- End of the accordion menu-->'; if ($this->GetOutputOption('pdf', 'template_path'))
echo ' </div>'; {
echo ' </div> <!-- /inner menu -->'; $oMPDF->setImportUse(); // Allow templates
echo ' </div> <!-- /menu -->'; $oMPDF->SetDocTemplate ($this->GetOutputOption('pdf', 'template_path'), 1);
echo ' <div class="footer"><a href="http://www.combodo.com" title="www.combodo.com" target="_blank"><img src="../images/logo-combodo.png"/></a></div>'; }
echo '<!-- End of the left pane -->'; $oMPDF->WriteHTML($sHtml);
echo '</div>'; $sOutputName = $this->s_title.'.pdf';
if ($this->GetOutputOption('pdf', 'output_name'))
{
$sOutputName = $this->GetOutputOption('pdf', 'output_name');
}
$oMPDF->Output($sOutputName, 'I');
}
}
echo '<div class="ui-layout-center">';
echo ' <div id="top-bar" style="width:100%">';
echo self::FilterXSS($sApplicationBanner);
echo ' <div id="global-search"><form action="'.utils::GetAbsoluteUrlAppRoot().'pages/UI.php"><table><tr><td></td><td id="g-search-input"><input type="text" name="text" value="'.$sText.'"'.$sOnClick.'/></td>';
echo '<td><input type="image" src="../images/searchBtn.png"/></a></td>';
echo '<td><a style="background:transparent;" href="'.$sOnlineHelpUrl.'" target="_blank"><img style="border:0;padding-left:20px;padding-right:10px;" title="'.Dict::S('UI:Help').'" src="../images/help.png"/></td>';
echo '<td style="padding-right:20px;padding-left:10px;">'.self::FilterXSS($sLogOffMenu).'</td><td><input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
//echo '<td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
echo ' </div>';
echo ' <div class="ui-layout-content">';
echo ' <!-- Beginning of page content -->';
echo self::FilterXSS($this->s_content);
echo ' <!-- End of page content -->';
echo ' </div>';
echo '</div>';
// Add the captured output
if (trim($s_captured_output) != "")
{
echo "<div id=\"rawOutput\" title=\"Debug Output\"><div style=\"height:500px; overflow-y:auto;\">".self::FilterXSS($s_captured_output)."</div></div>\n";
}
echo "<div id=\"at_the_end\">".self::FilterXSS($this->s_deferred_content)."</div>";
// echo $this->s_deferred_content;
echo "<div style=\"display:none\" title=\"ex2\" id=\"ex2\">Please wait...</div>\n"; // jqModal Window
echo "<div style=\"display:none\" title=\"dialog\" id=\"ModalDlg\"></div>";
echo "<div style=\"display:none\" id=\"ajax_content\"></div>";
echo "</body>\n";
echo "</html>\n";
}
public function AddTabContainer($sTabContainer) public function AddTabContainer($sTabContainer)
{ {
$this->m_aTabs[$sTabContainer] = array(); $this->m_aTabs[$sTabContainer] = array();
$this->add("\$Tabs:$sTabContainer\$"); $this->add("\$Tabs:$sTabContainer\$");
} }
public function AddToTab($sTabContainer, $sTabLabel, $sHtml) public function AddToTab($sTabContainer, $sTabLabel, $sHtml)
{ {
if (!isset($this->m_aTabs[$sTabContainer][$sTabLabel])) if (!isset($this->m_aTabs[$sTabContainer][$sTabLabel]))
@@ -833,12 +900,53 @@ EOF
$this->m_sCurrentTab = $sTabLabel; $this->m_sCurrentTab = $sTabLabel;
return $sPreviousTab; return $sPreviousTab;
} }
public function GetCurrentTab() public function GetCurrentTab()
{ {
return $this->m_sCurrentTab; return $this->m_sCurrentTab;
} }
public function RemoveTab($sTabLabel, $sTabContainer = null)
{
if ($sTabContainer == null)
{
$sTabContainer = $this->m_sCurrentTabContainer;
}
if (isset($this->m_aTabs[$sTabContainer][$sTabLabel]))
{
// Delete the content of the tab
unset($this->m_aTabs[$sTabContainer][$sTabLabel]);
// If we just removed the active tab, let's reset the active tab
if (($this->m_sCurrentTabContainer == $sTabContainer) && ($this->m_sCurrentTab == $sTabLabel))
{
$this->m_sCurrentTab = '';
}
}
}
/**
* Finds the tab whose title matches a given pattern
* @return mixed The name of the tab as a string or false if not found
*/
public function FindTab($sPattern, $sTabContainer = null)
{
$return = false;
if ($sTabContainer == null)
{
$sTabContainer = $this->m_sCurrentTabContainer;
}
foreach($this->m_aTabs[$sTabContainer] as $sTabLabel => $void)
{
if (preg_match($sPattern, $sTabLabel))
{
$result = $sTabLabel;
break;
}
}
return $result;
}
/** /**
* Make the given tab the active one, as if it were clicked * Make the given tab the active one, as if it were clicked
* DOES NOT WORK: apparently in the *old* version of jquery * DOES NOT WORK: apparently in the *old* version of jquery
@@ -860,7 +968,7 @@ EOF
break; break;
} }
$tab_index++; $tab_index++;
} }
break; break;
} }
$container_index++; $container_index++;
@@ -868,7 +976,7 @@ EOF
$sSelector = '#tabbedContent_'.$container_index.' > ul'; $sSelector = '#tabbedContent_'.$container_index.' > ul';
$this->add_ready_script("$('$sSelector').tabs('select', $tab_index);"); $this->add_ready_script("$('$sSelector').tabs('select', $tab_index);");
} }
public function StartCollapsibleSection($sSectionLabel, $bOpen = false) public function StartCollapsibleSection($sSectionLabel, $bOpen = false)
{ {
$this->add($this->GetStartCollapsibleSection($sSectionLabel, $bOpen)); $this->add($this->GetStartCollapsibleSection($sSectionLabel, $bOpen));
@@ -898,91 +1006,69 @@ EOF
return "</div>"; return "</div>";
} }
public function add($sHtml) public function add($sHtml)
{ {
if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab)) if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab))
{ {
$this->AddToTab($this->m_sCurrentTabContainer, $this->m_sCurrentTab, $sHtml); $this->AddToTab($this->m_sCurrentTabContainer, $this->m_sCurrentTab, $sHtml);
} }
else else
{ {
parent::add($sHtml); parent::add($sHtml);
} }
} }
/** /**
* Records the current state of the 'html' part of the page output * Records the current state of the 'html' part of the page output
* @return mixed The current state of the 'html' output * @return mixed The current state of the 'html' output
*/ */
public function start_capture() public function start_capture()
{ {
if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab)) if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab))
{ {
$iOffset = isset($this->m_aTabs[$this->m_sCurrentTabContainer][$this->m_sCurrentTab]) ? strlen($this->m_aTabs[$this->m_sCurrentTabContainer][$this->m_sCurrentTab]): 0; $iOffset = isset($this->m_aTabs[$this->m_sCurrentTabContainer][$this->m_sCurrentTab]) ? strlen($this->m_aTabs[$this->m_sCurrentTabContainer][$this->m_sCurrentTab]): 0;
return array('tc' => $this->m_sCurrentTabContainer, 'tab' => $this->m_sCurrentTab, 'offset' => $iOffset); return array('tc' => $this->m_sCurrentTabContainer, 'tab' => $this->m_sCurrentTab, 'offset' => $iOffset);
} }
else else
{ {
return parent::start_capture(); return parent::start_capture();
} }
} }
/** /**
* Returns the part of the html output that occurred since the call to start_capture * Returns the part of the html output that occurred since the call to start_capture
* and removes this part from the current html output * and removes this part from the current html output
* @param $offset mixed The value returned by start_capture * @param $offset mixed The value returned by start_capture
* @return string The part of the html output that was added since the call to start_capture * @return string The part of the html output that was added since the call to start_capture
*/ */
public function end_capture($offset) public function end_capture($offset)
{ {
if (is_array($offset)) if (is_array($offset))
{ {
if (isset($this->m_aTabs[$offset['tc']][$offset['tab']])) if (isset($this->m_aTabs[$offset['tc']][$offset['tab']]))
{ {
$sCaptured = substr($this->m_aTabs[$offset['tc']][$offset['tab']], $offset['offset']); $sCaptured = substr($this->m_aTabs[$offset['tc']][$offset['tab']], $offset['offset']);
$this->m_aTabs[$offset['tc']][$offset['tab']] = substr($this->m_aTabs[$offset['tc']][$offset['tab']], 0, $offset['offset']); $this->m_aTabs[$offset['tc']][$offset['tab']] = substr($this->m_aTabs[$offset['tc']][$offset['tab']], 0, $offset['offset']);
} }
else else
{ {
$sCaptured = ''; $sCaptured = '';
} }
} }
else else
{ {
$sCaptured = parent::end_capture($offset); $sCaptured = parent::end_capture($offset);
} }
return $sCaptured; return $sCaptured;
} }
/** /**
* Set the message to be displayed in the 'admin-banner' section at the top of the page * Set the message to be displayed in the 'admin-banner' section at the top of the page
*/ */
public function SetMessage($sMessage) public function SetMessage($sMessage)
{ {
$this->m_sMessage = $sMessage; $this->m_sMessage = $sMessage;
}
/*
public function AddSearchForm($sClassName, $bOpen = false)
{
$iSearchSectionId = 0;
$sStyle = $bOpen ? 'SearchDrawer' : 'SearchDrawer DrawerClosed';
$this->add("<div id=\"Search_$iSearchSectionId\" class=\"$sStyle\">\n");
$this->add("<h1>Search form for ".Metamodel::GetName($sClassName)."</h1>\n");
$this->add_ready_script("\$(\"#LnkSearch_$iSearchSectionId\").click(function() {\$(\"#Search_$iSearchSectionId\").slideToggle('normal'); $(\"#LnkSearch_$iSearchSectionId\").toggleClass('open');});");
$oFilter = new DBObjectSearch($sClassName);
$sFilter = $oFilter->serialize();
$oSet = new CMDBObjectSet($oFilter);
cmdbAbstractObject::DisplaySearchForm($this, $oSet, array('operation' => 'search', 'filter' => $sFilter, 'search_form' => true));
$this->add("</div>\n");
$this->add("<div class=\"HRDrawer\"/></div>\n");
$this->add("<div id=\"LnkSearch_$iSearchSectionId\" class=\"DrawerHandle\">Search</div>\n");
$iSearchSectionId++;
} }
*/
} }
?> ?>

View File

@@ -29,10 +29,12 @@ require_once(APPROOT."/application/nicewebpage.class.inc.php");
*/ */
class LoginWebPage extends NiceWebPage class LoginWebPage extends NiceWebPage
{ {
protected static $m_sLoginFailedMessage = '';
public function __construct() public function __construct()
{ {
parent::__construct("iTop Login"); parent::__construct(Dict::S("UI:iTopLogin"));
$this->add_style(<<<EOF $this->add_style(<<<EOF
body { body {
background: #eee; background: #eee;
@@ -89,6 +91,11 @@ EOF
); );
} }
public static function SetLoginFailedMessage($sMessage)
{
self::$m_sLoginFailedMessage = $sMessage;
}
public function DisplayLoginForm($sLoginType, $bFailedLogin = false) public function DisplayLoginForm($sLoginType, $bFailedLogin = false)
{ {
switch($sLoginType) switch($sLoginType)
@@ -119,7 +126,14 @@ EOF
$this->add("<h1>".Dict::S('UI:Login:Welcome')."</h1>\n"); $this->add("<h1>".Dict::S('UI:Login:Welcome')."</h1>\n");
if ($bFailedLogin) if ($bFailedLogin)
{ {
$this->add("<p class=\"hilite\">".Dict::S('UI:Login:IncorrectLoginPassword')."</p>\n"); if (self::$m_sLoginFailedMessage != '')
{
$this->add("<p class=\"hilite\">".self::$m_sLoginFailedMessage."</p>\n");
}
else
{
$this->add("<p class=\"hilite\">".Dict::S('UI:Login:IncorrectLoginPassword')."</p>\n");
}
} }
else else
{ {
@@ -133,6 +147,7 @@ EOF
$this->add("</table>\n"); $this->add("</table>\n");
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"login\" />\n"); $this->add("<input type=\"hidden\" name=\"loginop\" value=\"login\" />\n");
$this->add("</form>\n"); $this->add("</form>\n");
$this->add(Dict::S('UI:Login:About'));
$this->add("</div>\n"); $this->add("</div>\n");
break; break;
} }
@@ -250,77 +265,17 @@ EOF
// check CAS authentication // check CAS authentication
if (phpCAS::isAuthenticated()) if (phpCAS::isAuthenticated())
{ {
// Check is a membership is required $sAuthUser = phpCAS::getUser();
$sCASMemberships = MetaModel::GetConfig()->Get('cas_memberof'); $sAuthPwd = '';
$bFound = false; $sLoginMode = 'cas';
if (!empty($sCASMemberships)) $sAuthentication = 'external';
{
if (phpCAS::hasAttribute('memberOf'))
{
// A list of groups is specified, the user must a be member of (at least) one of them to pass
$aCASMemberships = array();
$aTmp = explode(';', $sCASMemberships);
setlocale(LC_ALL, "en_US.utf8"); // !!! WARNING: this is needed to have the iconv //TRANSLIT working fine below !!!
foreach($aTmp as $sGroupName)
{
$aCASMemberships[] = trim(iconv('UTF-8', 'ASCII//TRANSLIT', $sGroupName)); // Just in case remove accents and spaces...
}
$aMemberOf = phpCAS::getAttribute('memberOf');
if (!is_array($aMemberOf)) $aMemberOf = array($aMemberOf); // Just one entry, turn it into an array
$aFilteredGroupNames = array();
foreach($aMemberOf as $sGroupName)
{
$sGroupName = trim(iconv('UTF-8', 'ASCII//TRANSLIT', $sGroupName)); // Remove accents and spaces as well
$aFilteredGroupNames[] = $sGroupName;
if (in_array($sGroupName, $aCASMemberships))
{
$bFound = true;
break;
}
}
if(!$bFound)
{
phpCAS :: log("User ".phpCAS::getUser().", none of his/her groups (".implode('; ', $aFilteredGroupNames).") match any of the required groups: ".implode('; ', $aCASMemberships));
}
}
else
{
// Too bad, the user is not part of any of the group => not allowed
phpCAS :: log("No 'memberOf' attribute found for user ".phpCAS::getUser().". Are you using the SAML protocol (S1) ?");
}
}
else
{
// No membership required, anybody will pass
$bFound = true;
}
if ($bFound)
{
$sAuthUser = phpCAS::getUser();
$sAuthPwd = '';
$sLoginMode = 'cas';
$sAuthentication = 'external';
}
else
{
// The user is not part of the allowed groups, => log out
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php';
$sCASLogoutUrl = MetaModel::GetConfig()->Get('cas_logout_redirect_service');
if (empty($sCASLogoutUrl))
{
$sCASLogoutUrl = $sUrl;
}
phpCAS::logoutWithRedirectService($sCASLogoutUrl); // Redirects to the CAS logout page
}
} }
break; break;
case 'form': case 'form':
// iTop standard mode: form based authentication // iTop standard mode: form based authentication
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data'); $sAuthUser = utils::ReadPostedParam('auth_user', '', false, 'raw_data');
$sAuthPwd = utils::ReadPostedParam('auth_pwd', '', 'raw_data'); $sAuthPwd = utils::ReadPostedParam('auth_pwd', '', false, 'raw_data');
if ($sAuthUser != '') if ($sAuthUser != '')
{ {
$sLoginMode = 'form'; $sLoginMode = 'form';
@@ -359,9 +314,9 @@ EOF
case 'url': case 'url':
// Credentials passed directly in the url // Credentials passed directly in the url
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data'); $sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
if ($sAuthUser != '') $sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
if (($sAuthUser != '') && ($sAuthPwd != null))
{ {
$sAuthPwd = utils::ReadParam('auth_pwd', '', false, 'raw_data');
$sLoginMode = 'url'; $sLoginMode = 'url';
} }
break; break;
@@ -388,7 +343,7 @@ EOF
} }
else else
{ {
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $sAuthentication)) if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $sLoginMode, $sAuthentication))
{ {
//echo "Check Credentials returned false for user $sAuthUser!"; //echo "Check Credentials returned false for user $sAuthUser!";
self::ResetSession(); self::ResetSession();
@@ -469,8 +424,8 @@ EOF
{ {
$sAuthUser = $_SESSION['auth_user']; $sAuthUser = $_SESSION['auth_user'];
UserRights::Login($sAuthUser); // Set the user's language UserRights::Login($sAuthUser); // Set the user's language
$sOldPwd = utils::ReadPostedParam('old_pwd', 'raw_data'); $sOldPwd = utils::ReadPostedParam('old_pwd', '', false, 'raw_data');
$sNewPwd = utils::ReadPostedParam('new_pwd', 'raw_data'); $sNewPwd = utils::ReadPostedParam('new_pwd', '', false, 'raw_data');
if (UserRights::CanChangePassword() && ((!UserRights::CheckCredentials($sAuthUser, $sOldPwd)) || (!UserRights::ChangePassword($sOldPwd, $sNewPwd)))) if (UserRights::CanChangePassword() && ((!UserRights::CheckCredentials($sAuthUser, $sOldPwd)) || (!UserRights::ChangePassword($sOldPwd, $sNewPwd))))
{ {
$oPage = new LoginWebPage(); $oPage = new LoginWebPage();
@@ -497,7 +452,6 @@ EOF
header('Location: '.utils::GetAbsoluteUrlAppRoot().'portal/index.php'); header('Location: '.utils::GetAbsoluteUrlAppRoot().'portal/index.php');
} }
return $sMessage; return $sMessage;
} }
} // End of class } // End of class
?> ?>

View File

@@ -87,25 +87,47 @@ class ApplicationMenu
* Main function to add a menu entry into the application, can be called during the definition * Main function to add a menu entry into the application, can be called during the definition
* of the data model objects * of the data model objects
*/ */
static public function InsertMenu(MenuNode $oMenuNode, $iParentIndex = -1, $fRank) static public function InsertMenu(MenuNode $oMenuNode, $iParentIndex, $fRank)
{ {
$index = self::GetMenuIndexById($oMenuNode->GetMenuId()); $index = self::GetMenuIndexById($oMenuNode->GetMenuId());
if ($index == -1) if ($index == -1)
{ {
// The menu does not already exist, insert it // The menu does not already exist, insert it
$index = count(self::$aMenusIndex); $index = count(self::$aMenusIndex);
self::$aMenusIndex[$index] = array( 'node' => $oMenuNode, 'children' => array());
if ($iParentIndex == -1) if ($iParentIndex == -1)
{ {
$sParentId = '';
self::$aRootMenus[] = array ('rank' => $fRank, 'index' => $index); self::$aRootMenus[] = array ('rank' => $fRank, 'index' => $index);
} }
else else
{ {
$sParentId = self::$aMenusIndex[$iParentIndex]['node']->GetMenuId();
self::$aMenusIndex[$iParentIndex]['children'][] = array ('rank' => $fRank, 'index' => $index); self::$aMenusIndex[$iParentIndex]['children'][] = array ('rank' => $fRank, 'index' => $index);
} }
// Note: At the time when 'parent', 'rank' and 'source_file' have been added for the reflection API,
// they were not used to display the menus (redundant or unused)
//
$aBacktrace = debug_backtrace();
$sFile = $aBacktrace[2]["file"];
self::$aMenusIndex[$index] = array('node' => $oMenuNode, 'children' => array(), 'parent' => $sParentId, 'rank' => $fRank, 'source_file' => $sFile);
}
else
{
// the menu already exists, let's combine the conditions that make it visible
self::$aMenusIndex[$index]['node']->AddCondition($oMenuNode);
} }
return $index; return $index;
} }
/**
* Reflection API - Get menu entries
*/
static public function ReflectionMenuNodes()
{
return self::$aMenusIndex;
}
/** /**
* Entry point to display the whole menu into the web page, used by iTopWebPage * Entry point to display the whole menu into the web page, used by iTopWebPage
@@ -284,25 +306,30 @@ abstract class MenuNode
protected $sMenuId; protected $sMenuId;
protected $index; protected $index;
/**
* Properties reflecting how the node has been declared
*/
protected $aReflectionProperties;
/** /**
* Class of objects to check if the menu is enabled, null if none * Class of objects to check if the menu is enabled, null if none
*/ */
protected $m_sEnableClass; protected $m_aEnableClasses;
/** /**
* User Rights Action code to check if the menu is enabled, null if none * User Rights Action code to check if the menu is enabled, null if none
*/ */
protected $m_iEnableAction; protected $m_aEnableActions;
/** /**
* User Rights allowed results (actually a bitmask) to check if the menu is enabled, null if none * User Rights allowed results (actually a bitmask) to check if the menu is enabled, null if none
*/ */
protected $m_iEnableActionResults; protected $m_aEnableActionResults;
/** /**
* Stimulus to check: if the user can 'apply' this stimulus, then she/he can see this menu * Stimulus to check: if the user can 'apply' this stimulus, then she/he can see this menu
*/ */
protected $m_sEnableStimulus; protected $m_aEnableStimuli;
/** /**
* Create a menu item, sets the condition to have it displayed and inserts it into the application's main menu * Create a menu item, sets the condition to have it displayed and inserts it into the application's main menu
@@ -318,12 +345,25 @@ abstract class 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, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{ {
$this->sMenuId = $sMenuId; $this->sMenuId = $sMenuId;
$this->m_sEnableClass = $sEnableClass; $this->aReflectionProperties = array();
$this->m_iEnableAction = $iActionCode; if (strlen($sEnableClass) > 0)
$this->m_iEnableActionResults = $iAllowedResults; {
$this->m_sEnableStimulus = $sEnableStimulus; $this->aReflectionProperties['enable_class'] = $sEnableClass;
$this->aReflectionProperties['enable_action'] = $iActionCode;
$this->aReflectionProperties['enable_permission'] = $iAllowedResults;
$this->aReflectionProperties['enable_stimulus'] = $sEnableStimulus;
}
$this->m_aEnableClasses = array($sEnableClass);
$this->m_aEnableActions = array($iActionCode);
$this->m_aEnableActionResults = array($iAllowedResults);
$this->m_aEnableStimuli = array($sEnableStimulus);
$this->index = ApplicationMenu::InsertMenu($this, $iParentIndex, $fRank); $this->index = ApplicationMenu::InsertMenu($this, $iParentIndex, $fRank);
} }
public function ReflectionProperties()
{
return $this->aReflectionProperties;
}
public function GetMenuId() public function GetMenuId()
{ {
@@ -351,38 +391,54 @@ abstract class MenuNode
return $this->AddParams(utils::GetAbsoluteUrlAppRoot().'pages/UI.php', $aExtraParams); return $this->AddParams(utils::GetAbsoluteUrlAppRoot().'pages/UI.php', $aExtraParams);
} }
/**
* Add a limiting display condition for the same menu node. The conditions will be combined with a AND
* @param $oMenuNode MenuNode Another definition of the same menu node, with potentially different access restriction
* @return void
*/
public function AddCondition(MenuNode $oMenuNode)
{
foreach($oMenuNode->m_aEnableClasses as $index => $sClass )
{
$this->m_aEnableClasses[] = $sClass;
$this->m_aEnableActions[] = $oMenuNode->m_aEnableActions[$index];
$this->m_aEnableActionResults[] = $oMenuNode->m_aEnableActionResults[$index];
$this->m_aEnableStimuli[] = $oMenuNode->m_aEnableStimuli[$index];
}
}
/** /**
* Tells whether the menu is enabled (i.e. displayed) for the current user * Tells whether the menu is enabled (i.e. displayed) for the current user
* @return bool True if enabled, false otherwise * @return bool True if enabled, false otherwise
*/ */
public function IsEnabled() public function IsEnabled()
{ {
if ($this->m_sEnableClass != null) foreach($this->m_aEnableClasses as $index => $sClass)
{ {
if (MetaModel::IsValidClass($this->m_sEnableClass)) if ($sClass != null)
{ {
if ($this->m_sEnableStimulus != null) if (MetaModel::IsValidClass($sClass))
{ {
if (!UserRights::IsStimulusAllowed($this->m_sEnableClass, $this->m_sEnableStimulus)) if ($this->m_aEnableStimuli[$index] != null)
{ {
return false; if (!UserRights::IsStimulusAllowed($sClass, $this->m_aEnableStimuli[$index]))
{
return false;
}
}
if ($this->m_aEnableActions[$index] != null)
{
$iResult = UserRights::IsActionAllowed($sClass, $this->m_aEnableActions[$index]);
if (!($iResult & $this->m_aEnableActionResults[$index]))
{
return false;
}
} }
} }
if ($this->m_iEnableAction != null) else
{ {
$iResult = UserRights::IsActionAllowed($this->m_sEnableClass, $this->m_iEnableAction); return false;
if (($iResult & $this->m_iEnableActionResults))
{
return true;
}
else
{
return false;
}
} }
return true;
} }
return false;
} }
return true; return true;
} }
@@ -458,6 +514,7 @@ class TemplateMenuNode extends MenuNode
{ {
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus); parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sTemplateFile = $sTemplateFile; $this->sTemplateFile = $sTemplateFile;
$this->aReflectionProperties['template_file'] = $sTemplateFile;
} }
public function GetHyperlink($aExtraParams) public function GetHyperlink($aExtraParams)
@@ -516,6 +573,8 @@ class OQLMenuNode extends MenuNode
$this->sOQL = $sOQL; $this->sOQL = $sOQL;
$this->bSearch = $bSearch; $this->bSearch = $bSearch;
$this->m_aParams = array(); $this->m_aParams = array();
$this->aReflectionProperties['oql'] = $sOQL;
$this->aReflectionProperties['do_search'] = $bSearch;
// Enhancement: we could set as the "enable" condition that the user has enough rights to "read" the objects // Enhancement: we could set as the "enable" condition that the user has enough rights to "read" the objects
// of the class specified by the OQL... // of the class specified by the OQL...
} }
@@ -527,6 +586,10 @@ class OQLMenuNode extends MenuNode
public function SetParameters($aParams) public function SetParameters($aParams)
{ {
$this->m_aParams = $aParams; $this->m_aParams = $aParams;
foreach($aParams as $sKey => $value)
{
$this->aReflectionProperties[$sKey] = $value;
}
} }
public function RenderContent(WebPage $oPage, $aExtraParams = array()) public function RenderContent(WebPage $oPage, $aExtraParams = array())
@@ -593,6 +656,7 @@ class SearchMenuNode extends MenuNode
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus); parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sPageTitle = "Menu:$sMenuId+"; $this->sPageTitle = "Menu:$sMenuId+";
$this->sClass = $sClass; $this->sClass = $sClass;
$this->aReflectionProperties['class'] = $sClass;
} }
public function RenderContent(WebPage $oPage, $aExtraParams = array()) public function RenderContent(WebPage $oPage, $aExtraParams = array())
@@ -632,6 +696,7 @@ class WebPageMenuNode extends MenuNode
{ {
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus); parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sHyperlink = $sHyperlink; $this->sHyperlink = $sHyperlink;
$this->aReflectionProperties['url'] = $sHyperlink;
} }
public function GetHyperlink($aExtraParams) public function GetHyperlink($aExtraParams)
@@ -669,6 +734,7 @@ class NewObjectMenuNode extends MenuNode
{ {
parent::__construct($sMenuId, $iParentIndex, $fRank); parent::__construct($sMenuId, $iParentIndex, $fRank);
$this->sClass = $sClass; $this->sClass = $sClass;
$this->aReflectionProperties['class'] = $sClass;
} }
public function GetHyperlink($aExtraParams) public function GetHyperlink($aExtraParams)

View File

@@ -212,8 +212,10 @@ EOF
} }
EOF EOF
); );
} // For Wizard helper to process the ajax replies
$this->add('<div id="ajax_content"></div>');
}
public function SetCurrentTab($sTabLabel = '') public function SetCurrentTab($sTabLabel = '')
{ {
@@ -273,13 +275,9 @@ EOF
{ {
// Home-made and very limited display of an object set // Home-made and very limited display of an object set
// $sUniqueId = $sClass.$this->GetUniqueId();
//$oSet->Seek(0);// juste pour que le warning soit moins crado $this->add("<div id=\"$sUniqueId\">\n"); // The id here MUST be the same as currentId, otherwise the pagination will be broken
//$oSet->Fetch();// juste pour que le warning soit moins crado cmdbAbstractObject::DisplaySet($this, $oSet, array('currentId' => $sUniqueId, 'menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList)));
//
$this->add("<div id=\"listOf$sClass\">\n");
cmdbAbstractObject::DisplaySet($this, $oSet, array('currentId' => "listOf$sClass", 'menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList)));
$this->add("</div>\n"); $this->add("</div>\n");
} }
else else

View File

@@ -0,0 +1,107 @@
<?php
// Copyright (C) 2010 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
// the Free Software Foundation; version 3 of the License.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Persistent class Event and derived
* Application internal events
* There is also a file log
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
abstract class Query extends cmdbAbstractObject
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui,application",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_query",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("fields", array("allowed_values"=>null, "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'fields')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('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
}
}
class QueryOQL extends Query
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui,application",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_query_oql",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeOQL("oql", array("allowed_values"=>null, "sql"=>"oql", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'oql', 'fields')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('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
}
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
if (!$bEditMode)
{
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
$sOql = $this->Get('oql');
$oSearch = DBObjectSearch::FromOQL($sOql);
$aParameters = $oSearch->GetQueryParams();
foreach($aParameters as $sParam => $val)
{
$sUrl .= '&arg_'.$sParam.'=["'.$sParam.'"]';
}
$oPage->p(Dict::S('UI:Query:UrlForExcel').':<br/><textarea cols="80" rows="3" READONLY>'.$sUrl.'</textarea>');
}
}
}
?>

View File

@@ -40,13 +40,15 @@ class SqlBlock
protected $m_aColumns; protected $m_aColumns;
protected $m_sTitle; protected $m_sTitle;
protected $m_sType; protected $m_sType;
protected $m_aParams;
public function __construct($sQuery, $aColumns, $sTitle, $sType) public function __construct($sQuery, $aColumns, $sTitle, $sType, $aParams = array())
{ {
$this->m_sQuery = $sQuery; $this->m_sQuery = $sQuery;
$this->m_aColumns = $aColumns; $this->m_aColumns = $aColumns;
$this->m_sTitle = $sTitle; $this->m_sTitle = $sTitle;
$this->m_sType = $sType; $this->m_sType = $sType;
$this->m_aParams = $aParams;
} }
/** /**
@@ -54,9 +56,14 @@ class SqlBlock
/* /*
* *
* <sqlblock> * <sqlblock>
* <sql>SELECT date_format(start_date, '%d') AS Date, count(*) AS Count FROM ticket WHERE DATE_SUB(NOW(), INTERVAL 15 DAY) &lt; start_date AND finalclass = 'UserIssue' GROUP BY date_format(start_date, '%d')</sql> * <sql>SELECT date_format(start_date, '%d') AS Date, count(*) AS Count FROM ticket WHERE DATE_SUB(NOW(), INTERVAL 15 DAY) &lt; start_date AND finalclass = 'UserIssue' GROUP BY date_format(start_date, '%d') AND $CONDITION(param1, ticket.org_id)$</sql>
* <type>table</type> * <type>table</type>
* <title>UserRequest:Overview-Title</title> * <title>UserRequest:Overview-Title</title>
* <parameter>
* <name>param1</name>
* <type>context</type>
* <mapping>org_id</mapping>
* </parameter>
* <column> * <column>
* <name>Date</name> * <name>Date</name>
* <label>UserRequest:Overview-Date</label> * <label>UserRequest:Overview-Date</label>
@@ -73,6 +80,11 @@ class SqlBlock
* - sql: a (My)SQL query. Do not forget to use html entities (e.g. &lt; for <) * - sql: a (My)SQL query. Do not forget to use html entities (e.g. &lt; for <)
* - type: table (default), bars or pie. If bars or pie is selected only the two first columns are taken into account. * - type: table (default), bars or pie. If bars or pie is selected only the two first columns are taken into account.
* - title: optional title, typed in clear or given as a dictionnary entry * - title: optional title, typed in clear or given as a dictionnary entry
* - parameter: specifies how to map the context parameters (namely org_id) to a given named parameter in the query.
* The expression $CONDITION(<param_name>, <sql_column_name>) will be automatically replaced by:
* either the string "1" if there is no restriction on the organisation in iTop
* or the string "(<sql_column_name>=<value_of_org_id>)" if there is a limitation to one organizations in iTop
* or the string "(<sql_column_name> IN (<values_of_org_id>))" if there is a limitation to a given set of organizations in iTop
* - column: specification of a column (not displayed if omitted) * - column: specification of a column (not displayed if omitted)
* - column / name: name of the column in the SQL query (use aliases) * - column / name: name of the column in the SQL query (use aliases)
* - column / label: label, typed in clear or given as a dictionnary entry * - column / label: label, typed in clear or given as a dictionnary entry
@@ -144,8 +156,96 @@ class SqlBlock
} }
} }
} }
$aParams = array();
return new SqlBlock($sQuery, $aColumns, $sTitle, $sType); if (isset($oXml->parameter))
{
foreach ($oXml->parameter AS $oParamData)
{
if (!isset($oParamData->name))
{
throw new Exception("Missing tag 'name' for parameter in sqlblock/column");
}
$sName = (string) $oParamData->name;
if (strlen($sName) == 0)
{
throw new Exception("Empty tag 'name' for parameter in sqlblock/column");
}
if (!isset($oParamData->mapping))
{
throw new Exception("Missing tag 'mapping' for parameter in sqlblock/column");
}
$sMapping = (string) $oParamData->mapping;
if (strlen($sMapping) == 0)
{
throw new Exception("Empty tag 'mapping' for parameter in sqlblock/column");
}
if (isset($oParamData->type))
{
$sParamType = $oParamData->type;
}
else
{
$sParamType = 'context';
}
$aParams[$sName] = array('mapping' => $sMapping, 'type' => $sParamType);
}
}
return new SqlBlock($sQuery, $aColumns, $sTitle, $sType, $aParams);
}
/**
* Applies the defined parameters into the SQL query
* @return string the SQL query to execute
*/
public function BuildQuery()
{
$oAppContext = new ApplicationContext();
$sQuery = $this->m_sQuery;
$sQuery = str_replace('$DB_PREFIX$', MetaModel::GetConfig()->GetDBSubname(), $sQuery); // put the tables DB prefix (if any)
foreach($this->m_aParams as $sName => $aParam)
{
if ($aParam['type'] == 'context')
{
$sSearchPattern = '/\$CONDITION\('.$sName.',([^\)]+)\)\$/';
$value = $oAppContext->GetCurrentValue($aParam['mapping']);
if (empty($value))
{
$sSQLExpr = '(1)';
}
else
{
// Special case for managing the hierarchy of organizations
if (($aParam['mapping'] == 'org_id') && ( MetaModel::IsValidClass('Organization')))
{
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass('Organization');
if ($sHierarchicalKeyCode != false)
{
// organizations are in hierarchy... gather all the orgs below the given one...
$sOQL = "SELECT Organization AS node JOIN Organization AS root ON node.$sHierarchicalKeyCode BELOW root.id WHERE root.id = :value";
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array(), array('value' => $value));
$aOrgIds = array();
while($oOrg = $oSet->Fetch())
{
$aOrgIds[]= $oOrg->GetKey();
}
$sSQLExpr = '($1 IN('.implode(',', $aOrgIds).'))';
}
else
{
$sSQLExpr = '($1 = '.CMDBSource::Quote($value).')';
}
}
else
{
$sSQLExpr = '($1 = '.CMDBSource::Quote($value).')';
}
}
$sQuery = preg_replace($sSearchPattern, $sSQLExpr, $sQuery);
}
}
return $sQuery;
} }
public function RenderContent(WebPage $oPage, $aExtraParams = array()) public function RenderContent(WebPage $oPage, $aExtraParams = array())
@@ -160,7 +260,8 @@ class SqlBlock
} }
// $oPage->add($this->GetRenderContent($oPage, $aExtraParams, $sId)); // $oPage->add($this->GetRenderContent($oPage, $aExtraParams, $sId));
$res = CMDBSource::Query($this->m_sQuery); $sQuery = $this->BuildQuery();
$res = CMDBSource::Query($sQuery);
$aQueryCols = CMDBSource::GetColumns($res); $aQueryCols = CMDBSource::GetColumns($res);
// Prepare column definitions (check + give default values) // Prepare column definitions (check + give default values)

View File

@@ -65,6 +65,8 @@ class UIExtKeyWidget
{ {
protected $iId; protected $iId;
protected $sTargetClass; protected $sTargetClass;
protected $sAttCode;
protected $bSearchMode;
//public function __construct($sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '') //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) static public function DisplayFromAttCode($oPage, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '', $aArgs, $bSearchMode = false)
@@ -81,14 +83,16 @@ class UIExtKeyWidget
{ {
$sDisplayStyle = 'select'; // In search mode, always use a drop-down list $sDisplayStyle = 'select'; // In search mode, always use a drop-down list
} }
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId); $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode);
return $oWidget->Display($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix, $aArgs, $bSearchMode, $sDisplayStyle); return $oWidget->Display($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix, $aArgs, null, $sDisplayStyle);
} }
public function __construct($sTargetClass, $iInputId) public function __construct($sTargetClass, $iInputId, $sAttCode = '', $bSearchMode = false)
{ {
$this->sTargetClass = $sTargetClass; $this->sTargetClass = $sTargetClass;
$this->iId = $iInputId; $this->iId = $iInputId;
$this->sAttCode = $sAttCode;
$this->bSearchMode = $bSearchMode;
} }
/** /**
@@ -97,28 +101,41 @@ class UIExtKeyWidget
* @param Hash $aArgs Extra context arguments * @param Hash $aArgs Extra context arguments
* @return string The HTML fragment to be inserted into the page * @return string The HTML fragment to be inserted into the page
*/ */
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = false, $sDisplayStyle = 'select') public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select')
{ {
if (!is_null($bSearchMode))
{
$this->bSearchMode = $bSearchMode;
}
$sTitle = addslashes($sTitle); $sTitle = addslashes($sTitle);
$oPage->add_linked_script('../js/extkeywidget.js'); $oPage->add_linked_script('../js/extkeywidget.js');
$oPage->add_linked_script('../js/forms-json-utils.js'); $oPage->add_linked_script('../js/forms-json-utils.js');
$bCreate = (!$bSearchMode) && (!MetaModel::IsAbstract($this->sTargetClass)) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation); $bCreate = (!$this->bSearchMode) && (!MetaModel::IsAbstract($this->sTargetClass)) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
$bExtensions = true; $bExtensions = true;
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm'); $sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sAttrFieldPrefix = ($bSearchMode) ? '' : 'attr_'; $sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
$sHTMLValue = "<span style=\"white-space:nowrap\">"; // no wrap $sHTMLValue = "<span style=\"white-space:nowrap\">"; // no wrap
$sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL()); $sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL());
if($bSearchMode) if($this->bSearchMode)
{ {
$sWizHelper = 'null'; $sWizHelper = 'null';
$sWizHelperJSON = "''"; $sWizHelperJSON = "''";
$sJSSearchMode = 'true';
} }
else else
{ {
$sWizHelper = 'oWizardHelper'.$sFormPrefix; if (isset($aArgs['wizHelper']))
$sWizHelperJSON = $sWizHelper.'.ToJSON()'; {
$sWizHelper = $aArgs['wizHelper'];
}
else
{
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
}
$sWizHelperJSON = $sWizHelper.'.UpdateWizardToJSON()';
$sJSSearchMode = 'false';
} }
if (is_null($oAllowedValues)) if (is_null($oAllowedValues))
{ {
@@ -153,7 +170,7 @@ class UIExtKeyWidget
$sHelpText = ''; //$this->oAttDef->GetHelpOnEdition(); $sHelpText = ''; //$this->oAttDef->GetHelpOnEdition();
$sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n"; $sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
if ($bSearchMode) if ($this->bSearchMode)
{ {
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : Dict::S('UI:SearchValue:Any'); $sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : Dict::S('UI:SearchValue:Any');
$sHTMLValue .= "<option value=\"\">$sDisplayValue</option>\n"; $sHTMLValue .= "<option value=\"\">$sDisplayValue</option>\n";
@@ -182,7 +199,7 @@ class UIExtKeyWidget
$sHTMLValue .= "</select>\n"; $sHTMLValue .= "</select>\n";
$oPage->add_ready_script( $oPage->add_ready_script(
<<<EOF <<<EOF
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper); 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>"; 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('update', function() { oACWidget_{$this->iId}.Update(); } );
$('#$this->iId').bind('change', function() { $(this).trigger('extkeychange') } ); $('#$this->iId').bind('change', function() { $(this).trigger('extkeychange') } );
@@ -213,13 +230,14 @@ EOF
// another hidden input to store & pass the object's Id // another hidden input to store & pass the object's Id
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"$value\" />\n"; $sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"$value\" />\n";
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
// Scripts to start the autocomplete and bind some events to it // Scripts to start the autocomplete and bind some events to it
$oPage->add_ready_script( $oPage->add_ready_script(
<<<EOF <<<EOF
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper); 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>"; 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', json: function() { return $sWizHelperJSON; } }}); $('#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 ! $('#label_$this->iId').keyup(function() { if ($(this).val() == '') { $('#$this->iId').val(''); } } ); // Useful for search forms: empty value in the "label", means no value, immediatly !
$('#label_$this->iId').result( function(event, data, formatted) { OnAutoComplete('{$this->iId}', event, data, formatted); } ); $('#label_$this->iId').result( function(event, data, formatted) { OnAutoComplete('{$this->iId}', event, data, formatted); } );
$('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } ); $('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
@@ -262,13 +280,24 @@ EOF
return $sHTMLValue; return $sHTMLValue;
} }
public function GetSearchDialog(WebPage $oPage, $sTitle) public function GetSearchDialog(WebPage $oPage, $sTitle, $oCurrObject = null)
{ {
$sHTML = '<div class="wizContainer" style="vertical-align:top;"><div id="dc_'.$this->iId.'">'; $sHTML = '<div class="wizContainer" style="vertical-align:top;"><div id="dc_'.$this->iId.'">';
$oFilter = new DBObjectSearch($this->sTargetClass); if ( ($oCurrObject != null) && ($this->sAttCode != ''))
$oSet = new CMDBObjectSet($oFilter); {
$oBlock = new DisplayBlock($oFilter, 'search', false); $oAttDef = MetaModel::GetAttributeDef(get_class($oCurrObject), $this->sAttCode);
$aParams = array('query_params' => array('this' => $oCurrObject));
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aParams);
$oFilter = $oSet->GetFilter();
}
else
{
$aParams = array();
$oFilter = new DBObjectSearch($this->sTargetClass);
}
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oBlock = new DisplayBlock($oFilter, 'search', false, $aParams);
$sHTML .= $oBlock->GetDisplay($oPage, $this->iId, array('open' => true, 'currentId' => $this->iId)); $sHTML .= $oBlock->GetDisplay($oPage, $this->iId, array('open' => true, 'currentId' => $this->iId));
$sHTML .= "<form id=\"fr_{$this->iId}\" OnSubmit=\"return oACWidget_{$this->iId}.DoOk();\">\n"; $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 id=\"dr_{$this->iId}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
@@ -294,7 +323,7 @@ EOF
/** /**
* Search for objects to be selected * Search for objects to be selected
* @param WebPage $oP The page used for the output (usually an AjaxWebPage) * @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 string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of sTargetClass
* @param Array $aAlreadyLinkedIds List of IDs of objects of "remote" class already linked, to be filtered out of the search * @param Array $aAlreadyLinkedIds List of IDs of objects of "remote" class already linked, to be filtered out of the search
*/ */
public function SearchObjectsToSelect(WebPage $oP, $sFilter, $sRemoteClass = '', $oObj = null) public function SearchObjectsToSelect(WebPage $oP, $sFilter, $sRemoteClass = '', $oObj = null)
@@ -305,8 +334,24 @@ EOF
} }
try try
{ {
$oFilter = DBObjectSearch::FromOQL($sFilter); if ($sRemoteClass != $this->sTargetClass)
$oBlock = new DisplayBlock($oFilter, 'list', false); {
//$oBaseClassFilter = DBObjectSearch::FromOQL($sFilter);
//$oFilter = new DBObjectSearch($sRemoteClass);
//$oFilter->AddConditionExpression($oBaseClassFilter->GetCriteria());
//$oFilter->TransferConditionExpression($oBaseClassFilter, array());
//$oFilter->TransferConditionExpression($oBaseClassFilter, array($this->sTargetClass => array('*' => $sRemoteClass.'_'.strtolower($this->sTargetClass))));
$sFilter = str_replace(" $this->sTargetClass ", " $sRemoteClass ", $sFilter);
$sFilter = str_replace("`$this->sTargetClass`", "`$sRemoteClass`", $sFilter);
$oFilter = DBObjectSearch::FromOQL($sFilter);
}
else
{
$oFilter = DBObjectSearch::FromOQL($sFilter);
}
//$oP->p($oFilter->ToOQL());
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oBlock = new DisplayBlock($oFilter, 'list', false, array('query_params' => array('this' => $oObj)));
$oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single')); // Don't display the 'Actions' menu on the results $oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single')); // Don't display the 'Actions' menu on the results
} }
catch(MissingQueryArgument $e) catch(MissingQueryArgument $e)
@@ -315,8 +360,9 @@ EOF
// TODO check if we can improve this behavior... // TODO check if we can improve this behavior...
$sOQL = 'SELECT '.$sRemoteClass; $sOQL = 'SELECT '.$sRemoteClass;
$oFilter = DBObjectSearch::FromOQL($sOQL); $oFilter = DBObjectSearch::FromOQL($sOQL);
$oBlock = new DisplayBlock($oFilter, 'list', false); $oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oBlock->Display($oP, $this->iId.'_results', array('cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single')); // Don't display the 'Actions' menu on the results //$oBlock = new DisplayBlock($oFilter, 'list', false);
//$oBlock->Display($oP, $this->iId.'_results', array('cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single')); // Don't display the 'Actions' menu on the results
} }
} }
@@ -334,6 +380,7 @@ EOF
throw new Exception('Implementation: null value for allowed values definition'); throw new Exception('Implementation: null value for allowed values definition');
} }
$oValuesSet = new ValueSetObjects($sFilter, 'friendlyname'); // Bypass GetName() to avoid the encoding by htmlentities $oValuesSet = new ValueSetObjects($sFilter, 'friendlyname'); // Bypass GetName() to avoid the encoding by htmlentities
$oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$aValues = $oValuesSet->GetValues(array('this' => $oObj), $sContains); $aValues = $oValuesSet->GetValues(array('this' => $oObj), $sContains);
foreach($aValues as $sKey => $sFriendlyName) foreach($aValues as $sKey => $sFriendlyName)
{ {
@@ -346,14 +393,24 @@ EOF
*/ */
public function GetObjectName($iObjId) public function GetObjectName($iObjId)
{ {
$oObj = MetaModel::GetObject($this->sTargetClass, $iObjId); $aModifierProps = array();
return $oObj->GetName(); $aModifierProps['UserRightsGetSelectFilter']['bSearchMode'] = $this->bSearchMode;
$oObj = MetaModel::GetObject($this->sTargetClass, $iObjId, false, false, $aModifierProps);
if ($oObj)
{
return $oObj->GetName();
}
else
{
return '';
}
} }
/** /**
* Get the form to create a new object of the 'target' class * Get the form to create a new object of the 'target' class
*/ */
public function GetObjectCreationForm(WebPage $oPage) public function GetObjectCreationForm(WebPage $oPage, $oCurrObject)
{ {
// Set all the default values in an object and clone this "default" object // Set all the default values in an object and clone this "default" object
$oNewObj = MetaModel::NewObject($this->sTargetClass); $oNewObj = MetaModel::NewObject($this->sTargetClass);
@@ -362,7 +419,24 @@ EOF
$oAppContext = new ApplicationContext(); $oAppContext = new ApplicationContext();
$oAppContext->InitObjectFromContext($oNewObj); $oAppContext->InitObjectFromContext($oNewObj);
// 2nd - set values from the page argument 'default' // 2nd set the default values from the constraint on the external key... if any
if ( ($oCurrObject != null) && ($this->sAttCode != ''))
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oCurrObject), $this->sAttCode);
$aParams = array('this' => $oCurrObject);
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aParams);
$aConsts = $oSet->ListConstantFields();
$sClassAlias = $oSet->GetFilter()->GetClassAlias();
if (isset($aConsts[$sClassAlias]))
{
foreach($aConsts[$sClassAlias] as $sAttCode => $value)
{
$oNewObj->Set($sAttCode, $value);
}
}
}
// 3rd - set values from the page argument 'default'
$oNewObj->UpdateObjectFromArg('default'); $oNewObj->UpdateObjectFromArg('default');
$sDialogTitle = addslashes($this->sTitle); $sDialogTitle = addslashes($this->sTitle);
@@ -391,6 +465,7 @@ EOF
try try
{ {
$oFilter = DBObjectSearch::FromOQL($sFilter); $oFilter = DBObjectSearch::FromOQL($sFilter);
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj)); $oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj));
} }
catch(MissingQueryArgument $e) catch(MissingQueryArgument $e)
@@ -399,6 +474,7 @@ EOF
// TODO check if we can improve this behavior... // TODO check if we can improve this behavior...
$sOQL = 'SELECT '.$this->m_sTargetClass; $sOQL = 'SELECT '.$this->m_sTargetClass;
$oFilter = DBObjectSearch::FromOQL($sOQL); $oFilter = DBObjectSearch::FromOQL($sOQL);
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oSet = new DBObjectSet($oFilter); $oSet = new DBObjectSet($oFilter);
} }

View File

@@ -34,6 +34,7 @@ class UILinksWidget
protected $m_iInputId; protected $m_iInputId;
protected $m_aAttributes; protected $m_aAttributes;
protected $m_sExtKeyToRemote; protected $m_sExtKeyToRemote;
protected $m_sExtKeyToMe;
protected $m_sLinkedClass; protected $m_sLinkedClass;
protected $m_sRemoteClass; protected $m_sRemoteClass;
protected $m_bDuplicatesAllowed; protected $m_bDuplicatesAllowed;
@@ -50,6 +51,7 @@ class UILinksWidget
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode); $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode);
$this->m_sLinkedClass = $oAttDef->GetLinkedClass(); $this->m_sLinkedClass = $oAttDef->GetLinkedClass();
$this->m_sExtKeyToRemote = $oAttDef->GetExtKeyToRemote(); $this->m_sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
$this->m_sExtKeyToMe = $oAttDef->GetExtKeyToMe();
$oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote); $oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote);
$this->m_sRemoteClass = $oLinkingAttDef->GetTargetClass(); $this->m_sRemoteClass = $oLinkingAttDef->GetTargetClass();
$sExtKeyToMe = $oAttDef->GetExtKeyToMe(); $sExtKeyToMe = $oAttDef->GetExtKeyToMe();
@@ -60,8 +62,9 @@ class UILinksWidget
$this->m_aTableConfig = array(); $this->m_aTableConfig = array();
$this->m_aTableConfig['form::checkbox'] = array( 'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_iInputId.".OnSelectChange();\">", 'description' => Dict::S('UI:SelectAllToggle+')); $this->m_aTableConfig['form::checkbox'] = array( 'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_iInputId.".OnSelectChange();\">", 'description' => Dict::S('UI:SelectAllToggle+'));
foreach(MetaModel::ListAttributeDefs($this->m_sLinkedClass) as $sAttCode=>$oAttDef) foreach(MetaModel::FlattenZList(MetaModel::GetZListItems($this->m_sLinkedClass, 'list')) as $sAttCode)
{ {
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sAttCode);
if ($sStateAttCode == $sAttCode) if ($sStateAttCode == $sAttCode)
{ {
// State attribute is always hidden from the UI // State attribute is always hidden from the UI
@@ -97,43 +100,98 @@ class UILinksWidget
* @param Hash $aArgs Extra context arguments * @param Hash $aArgs Extra context arguments
* @return string The HTML fragment of the one-row form * @return string The HTML fragment of the one-row form
*/ */
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId = null, $aArgs = array() ) protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId = null, $aArgs = array(), $oCurrentObj )
{ {
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}"; $sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
$aRow = array(); $aRow = array();
$aFieldsMap = array();
if(is_object($linkObjOrId)) if(is_object($linkObjOrId))
{ {
$key = $linkObjOrId->GetKey(); $key = $linkObjOrId->GetKey();
$iRemoteObjKey = $linkObjOrId->Get($this->m_sExtKeyToRemote);
$sPrefix .= "[$key]["; $sPrefix .= "[$key][";
$sNameSuffix = "]"; // To make a tabular form $sNameSuffix = "]"; // To make a tabular form
$aArgs['prefix'] = $sPrefix; $aArgs['prefix'] = $sPrefix;
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$key}";
$aArgs['this'] = $linkObjOrId;
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$key\">"; $aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$key\">";
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"$key\">"; $aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"$key\">";
foreach($this->m_aEditableFields as $sFieldCode) foreach($this->m_aEditableFields as $sFieldCode)
{ {
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId->GetKey().']'; $sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId->GetKey().']';
$sSafeId = str_replace(array('[',']','-'), '_', $sFieldId); $sSafeId = self::MakeID($sFieldId);
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode); $oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $linkObjOrId->Get($sFieldCode), '' /* DisplayValue */, $sSafeId, $sNameSuffix, 0, $aArgs); $aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $linkObjOrId->Get($sFieldCode), '' /* DisplayValue */, $sSafeId, $sNameSuffix, 0, $aArgs);
$aFieldsMap[$sFieldCode] = $sSafeId;
} }
$sState = $linkObjOrId->GetState();
} }
else else
{ {
// form for creating a new record // form for creating a new record
$sPrefix .= "[$linkObjOrId]["; $sPrefix .= "[$linkObjOrId][";
$iRemoteObjKey = -$linkObjOrId;
$oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass);
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, -$linkObjOrId);
$oNewLinkObj->Set($this->m_sExtKeyToRemote, $oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields
$oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object also fills the related external fields
$sNameSuffix = "]"; // To make a tabular form $sNameSuffix = "]"; // To make a tabular form
$aArgs['prefix'] = $sPrefix; $aArgs['prefix'] = $sPrefix;
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".(-$linkObjOrId);
$aArgs['this'] = $oNewLinkObj;
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$linkObjOrId\">"; $aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$linkObjOrId\">";
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"\">"; $aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"\">";
foreach($this->m_aEditableFields as $sFieldCode) foreach($this->m_aEditableFields as $sFieldCode)
{ {
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId.']'; $sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId.']';
$sSafeId = str_replace(array('[',']','-'), '_', $sFieldId); $sSafeId = self::MakeID($sFieldId);
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode); $oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, '' /* TO DO/ call GetDefaultValue($oObject->ToArgs()) */, '' /* DisplayValue */, $sSafeId /* id */, $sNameSuffix, 0, $aArgs); $aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, '' /* TO DO/ call GetDefaultValue($oObject->ToArgs()) */, '' /* DisplayValue */, $sSafeId /* id */, $sNameSuffix, 0, $aArgs);
$aFieldsMap[$sFieldCode] = $sSafeId;
} }
$sState = '';
$oP->add_script(
<<<EOF
$(".date-pick").datepicker({
showOn: 'button',
buttonImage: '../images/calendar.png',
buttonImageOnly: true,
dateFormat: 'yy-mm-dd',
constrainInput: false,
changeMonth: true,
changeYear: true
});
$(".datetime-pick").datepicker({
showOn: 'button',
buttonImage: '../images/calendar.png',
buttonImageOnly: true,
dateFormat: 'yy-mm-dd 00:00:00',
constrainInput: false,
changeMonth: true,
changeYear: true
});
EOF
);
} }
$sExtKeyToMeId = self::MakeID($sPrefix.$this->m_sExtKeyToMe);
$aFieldsMap[$this->m_sExtKeyToMe] = $sExtKeyToMeId;
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToMeId\" value=\"".$oCurrentObj->GetKey()."\">";
$sExtKeyToRemoteId = self::MakeID($sPrefix.$this->m_sExtKeyToRemote);
$aFieldsMap[$this->m_sExtKeyToRemote] = $sExtKeyToRemoteId;
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToRemoteId\" value=\"$iRemoteObjKey\">";
$iFieldsCount = count($aFieldsMap);
$sJsonFieldsMap = json_encode($aFieldsMap);
$oP->add_script(
<<<EOF
var {$aArgs['wizHelper']} = new WizardHelper('{$this->m_sLinkedClass}', '', '$sState');
{$aArgs['wizHelper']}.SetFieldsMap($sJsonFieldsMap);
{$aArgs['wizHelper']}.SetFieldsCount($iFieldsCount);
EOF
);
$aRow['static::key'] = $oLinkedObj->GetHyperLink(); $aRow['static::key'] = $oLinkedObj->GetHyperLink();
foreach(MetaModel::GetZListItems($this->m_sRemoteClass, 'list') as $sFieldCode) foreach(MetaModel::GetZListItems($this->m_sRemoteClass, 'list') as $sFieldCode)
{ {
@@ -141,6 +199,11 @@ class UILinksWidget
} }
return $aRow; return $aRow;
} }
protected function MakeID($sName)
{
return str_replace(array('[', ']'), '_', $sName);
}
/** /**
* Display one row of the whole form * Display one row of the whole form
@@ -207,9 +270,11 @@ class UILinksWidget
* @param WebPage $oP The web page used for all the output * @param WebPage $oP The web page used for all the output
* @param DBObjectSet The initial value of the linked set * @param DBObjectSet The initial value of the linked set
* @param Hash $aArgs Extra context arguments * @param Hash $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 * @return string The HTML fragment to be inserted into the page
*/ */
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array()) public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
{ {
$sHtmlValue = ''; $sHtmlValue = '';
$sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode); $sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode);
@@ -223,27 +288,28 @@ class UILinksWidget
if ($oCurrentLink->IsNew()) if ($oCurrentLink->IsNew())
{ {
$key = -$oLinkedObj->GetKey(); $key = -$oLinkedObj->GetKey();
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $key, $aArgs); $aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $key, $aArgs, $oCurrentObj);
} }
else else
{ {
$key = $oCurrentLink->GetKey(); $key = $oCurrentLink->GetKey();
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs); $aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj);
} }
} }
$sHtmlValue .= $this->DisplayFormTable($oPage, $this->m_aTableConfig, $aForm); $sHtmlValue .= $this->DisplayFormTable($oPage, $this->m_aTableConfig, $aForm);
$sDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false'; $sDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
$oPage->add_ready_script(<<<EOF $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); 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);
oWidget{$this->m_iInputId}.Init(); oWidget{$this->m_iInputId}.Init();
EOF EOF
); );
$sHtmlValue .= "<span style=\"float:left;\">&nbsp;&nbsp;&nbsp;<img src=\"../images/tv-item-last.gif\">&nbsp;&nbsp;<input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"oWidget{$this->m_iInputId}.RemoveSelected();\" >"; $sHtmlValue .= "<span style=\"float:left;\">&nbsp;&nbsp;&nbsp;<img src=\"../images/tv-item-last.gif\">&nbsp;&nbsp;<input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"oWidget{$this->m_iInputId}.RemoveSelected();\" >";
$sHtmlValue .= "&nbsp;&nbsp;&nbsp;<input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sRemoteClass))."\" onClick=\"oWidget{$this->m_iInputId}.AddObjects();\"></span>\n"; $sHtmlValue .= "&nbsp;&nbsp;&nbsp;<input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sRemoteClass))."\" onClick=\"oWidget{$this->m_iInputId}.AddObjects();\"><span id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_indicatorAdd\"></span></span>\n";
$sHtmlValue .= "<span style=\"clear:both;\"><p>&nbsp;</p></span>\n"; $sHtmlValue .= "<span style=\"clear:both;\"><p>&nbsp;</p></span>\n";
$sHtmlValue .= "</div>\n"; $sHtmlValue .= "</div>\n";
$oPage->add_at_the_end($this->GetObjectPickerDialog($oPage), "dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}"); // To prevent adding forms inside the main form $oPage->add_at_the_end("<div id=\"dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}\"></div>"); // To prevent adding forms inside the main form
return $sHtmlValue; return $sHtmlValue;
} }
@@ -266,12 +332,11 @@ EOF
return $sTargetClass; return $sTargetClass;
} }
protected function GetObjectPickerDialog($oPage) public function GetObjectPickerDialog($oPage, $oCurrentObj)
{ {
$sHtml = "<div id=\"dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}\">"; $sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
$sHtml .= "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
$oFilter = new DBObjectSearch($this->m_sRemoteClass); $oFilter = new DBObjectSearch($this->m_sRemoteClass);
$oSet = new CMDBObjectSet($oFilter); $this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$oBlock = new DisplayBlock($oFilter, 'search', false); $oBlock = new DisplayBlock($oFilter, 'search', false);
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}", array('open' => true)); $sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}", array('open' => true));
$sHtml .= "<form id=\"ObjectsAddForm_{$this->m_sAttCode}{$this->m_sNameSuffix}\" OnSubmit=\"return oWidget{$this->m_iInputId}.DoAddObjects(this.id);\">\n"; $sHtml .= "<form id=\"ObjectsAddForm_{$this->m_sAttCode}{$this->m_sNameSuffix}\" OnSubmit=\"return oWidget{$this->m_iInputId}.DoAddObjects(this.id);\">\n";
@@ -282,12 +347,11 @@ EOF
$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=\"submit\" value=\"".Dict::S('UI:Button:Add')."\">";
$sHtml .= "</div>\n"; $sHtml .= "</div>\n";
$sHtml .= "</form>\n"; $sHtml .= "</form>\n";
$sHtml .= "</div>\n"; $oPage->add($sHtml);
$oPage->add_ready_script("$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, resizeStop: oWidget{$this->m_iInputId}.UpdateSizes });"); $oPage->add_ready_script("$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, resizeStop: oWidget{$this->m_iInputId}.UpdateSizes });");
$oPage->add_ready_script("$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('option', {title:'".addslashes(Dict::Format('UI:AddObjectsOf_Class_LinkedWith_Class', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName($this->m_sClass)))."'});"); $oPage->add_ready_script("$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('option', {title:'".addslashes(Dict::Format('UI:AddObjectsOf_Class_LinkedWith_Class', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName($this->m_sClass)))."'});");
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix} form').bind('submit.uilinksWizard', oWidget{$this->m_iInputId}.SearchObjectsToAdd);"); $oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix} form').bind('submit.uilinksWizard', oWidget{$this->m_iInputId}.SearchObjectsToAdd);");
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}').resize(oWidget{$this->m_iInputId}.UpdateSizes);"); $oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}').resize(oWidget{$this->m_iInputId}.UpdateSizes);");
return $sHtml;
} }
/** /**
@@ -344,7 +408,7 @@ EOF
$oBlock->Display($oP, "ResultsToAdd_{$this->m_sAttCode}", array('menu' => false, 'cssCount'=> '#count_'.$this->m_sAttCode.$this->m_sNameSuffix , 'selection_mode' => true)); // Don't display the 'Actions' menu on the results $oBlock->Display($oP, "ResultsToAdd_{$this->m_sAttCode}", array('menu' => false, 'cssCount'=> '#count_'.$this->m_sAttCode.$this->m_sNameSuffix , 'selection_mode' => true)); // Don't display the 'Actions' menu on the results
} }
public function DoAddObjects(WebPage $oP, $oFullSetFilter) public function DoAddObjects(WebPage $oP, $oFullSetFilter, $oCurrentObj)
{ {
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter); $aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
@@ -353,7 +417,7 @@ EOF
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId); $oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId);
if (is_object($oLinkedObj)) if (is_object($oLinkedObj))
{ {
$aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId ); // Not yet created link get negative Ids $aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId, array(), $oCurrentObj ); // Not yet created link get negative Ids
$oP->add($this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iObjectId)); $oP->add($this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iObjectId));
} }
else else
@@ -362,5 +426,47 @@ EOF
} }
} }
} }
/**
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
* @param DBObject $oSourceObj
* @param DBObjectSearch $oSearch
*/
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
{
$oAppContext = new ApplicationContext();
$sSrcClass = get_class($oSourceObj);
$sDestClass = $oSearch->GetClass();
foreach($oAppContext->GetNames() as $key)
{
// Find the value of the object corresponding to each 'context' parameter
$aCallSpec = array($sSrcClass, 'MapContextParam');
$sAttCode = '';
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
{
$oAttDef = MetaModel::GetAttributeDef($sSrcClass, $sAttCode);
$defaultValue = $oSourceObj->Get($sAttCode);
// Find the attcode for the same 'context' parameter in the destination class
// and sets its value as the default value for the search condition
$aCallSpec = array($sDestClass, 'MapContextParam');
$sAttCode = '';
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue))
{
$oSearch->AddCondition($sAttCode, $defaultValue);
}
}
}
}
} }
?> ?>

View File

@@ -46,6 +46,7 @@ class utils
// Parameters loaded from a file, parameters of the page/command line still have precedence // Parameters loaded from a file, parameters of the page/command line still have precedence
private static $m_aParamsFromFile = null; private static $m_aParamsFromFile = null;
private static $m_aParamSource = array();
protected static function LoadParamFile($sParamFile) protected static function LoadParamFile($sParamFile)
{ {
@@ -82,6 +83,7 @@ class utils
$sParam = $aMatches[1]; $sParam = $aMatches[1];
$value = trim($aMatches[2]); $value = trim($aMatches[2]);
self::$m_aParamsFromFile[$sParam] = $value; self::$m_aParamsFromFile[$sParam] = $value;
self::$m_aParamSource[$sParam] = $sParamFile;
} }
} }
} }
@@ -99,6 +101,25 @@ class utils
} }
} }
/**
* Return the source file from which the parameter has been found,
* usefull when it comes to pass user credential to a process executed
* in the background
* @param $sName Parameter name
* @return The file name if any, or null
*/
public static function GetParamSourceFile($sName)
{
if (array_key_exists($sName, self::$m_aParamSource))
{
return self::$m_aParamSource[$sName];
}
else
{
return null;
}
}
public static function IsModeCLI() public static function IsModeCLI()
{ {
$sSAPIName = php_sapi_name(); $sSAPIName = php_sapi_name();
@@ -152,10 +173,18 @@ class utils
public static function Sanitize($value, $defaultValue, $sSanitizationFilter) public static function Sanitize($value, $defaultValue, $sSanitizationFilter)
{ {
$retValue = self::Sanitize_Internal($value, $sSanitizationFilter); if ($value === $defaultValue)
if ($retValue === false)
{ {
$retValue = $defaultValue; // Preserve the real default value (can be used to detect missing mandatory parameters)
$retValue = $value;
}
else
{
$retValue = self::Sanitize_Internal($value, $sSanitizationFilter);
if ($retValue === false)
{
$retValue = $defaultValue;
}
} }
return $retValue; return $retValue;
} }
@@ -431,8 +460,6 @@ class utils
/** /**
* Returns the absolute URL to the server's root path * Returns the absolute URL to the server's root path
* @param $sCurrentRelativePath string NO MORE USED, kept for backward compatibility only !
* @param $bForceHTTPS bool True to force HTTPS, false otherwise
* @return string The absolute URL to the server's root, without the first slash * @return string The absolute URL to the server's root, without the first slash
*/ */
static public function GetAbsoluteUrlAppRoot() static public function GetAbsoluteUrlAppRoot()
@@ -521,6 +548,17 @@ class utils
return $sAppRootUrl; return $sAppRootUrl;
} }
/**
* Returns the absolute URL to the modules root path
* Hardcoded here for compatibility with iTop 2.0 modules
* @return string The absolute URL to the modules
*/
static public function GetAbsoluteUrlModulesRoot()
{
$sUrl = self::GetAbsoluteUrlAppRoot().'modules/';
return $sUrl;
}
/** /**
* Tells whether or not log off operation is supported. * Tells whether or not log off operation is supported.
* Actually in only one case: * Actually in only one case:
@@ -601,5 +639,86 @@ class utils
} }
echo "<p><pre>".print_r($aLightTrace, true)."</pre></p>\n"; echo "<p><pre>".print_r($aLightTrace, true)."</pre></p>\n";
} }
/**
* Execute the given iTop PHP script, passing it the current credentials
* Only CLI mode is supported, because of the need to hand the credentials over to the next process
* Throws an exception if the execution fails or could not be attempted (config issue)
* @param string $sScript Name and relative path to the file (relative to the iTop root dir)
* @param hash $aArguments Associative array of 'arg' => 'value'
* @return array(iCode, array(output lines))
*/
/**
*/
static function ExecITopScript($sScriptName, $aArguments)
{
$aDisabled = explode(', ', ini_get('disable_functions'));
if (in_array('exec', $aDisabled))
{
throw new Exception("The PHP exec() function has been disabled on this server");
}
$sPHPExec = trim(MetaModel::GetConfig()->Get('php_path'));
if (strlen($sPHPExec) == 0)
{
throw new Exception("The path to php must not be empty. Please set a value for 'php_path' in your configuration file.");
}
$sAuthUser = self::ReadParam('auth_user', '', 'raw_data');
$sAuthPwd = self::ReadParam('auth_pwd', '', 'raw_data');
$sParamFile = self::GetParamSourceFile('auth_user');
if (is_null($sParamFile))
{
$aArguments['auth_user'] = $sAuthUser;
$aArguments['auth_pwd'] = $sAuthPwd;
}
else
{
$aArguments['param_file'] = $sParamFile;
}
$aArgs = array();
foreach($aArguments as $sName => $value)
{
// Note: See comment from the 23-Apr-2004 03:30 in the PHP documentation
// It suggests to rely on pctnl_* function instead of using escapeshellargs
$aArgs[] = "--$sName=".escapeshellarg($value);
}
$sArgs = implode(' ', $aArgs);
$sScript = realpath(APPROOT.$sScriptName);
if (!file_exists($sScript))
{
throw new Exception("Could not find the script file '$sScriptName' from the directory '".APPROOT."'");
}
$sCommand = '"'.$sPHPExec.'" '.escapeshellarg($sScript).' -- '.$sArgs;
if (version_compare(phpversion(), '5.3.0', '<'))
{
if (substr(PHP_OS,0,3) == 'WIN')
{
// Under Windows, and for PHP 5.2.x, the whole command has to be quoted
// Cf PHP doc: http://php.net/manual/fr/function.exec.php, comment from the 27-Dec-2010
$sCommand = '"'.$sCommand.'"';
}
}
$sLastLine = exec($sCommand, $aOutput, $iRes);
if ($iRes == 1)
{
throw new Exception(Dict::S('Core:ExecProcess:Code1')." - ".$sCommand);
}
elseif ($iRes == 255)
{
$sErrors = implode("\n", $aOutput);
throw new Exception(Dict::S('Core:ExecProcess:Code255')." - ".$sCommand.":\n".$sErrors);
}
//$aOutput[] = $sCommand;
return array($iRes, $aOutput);
}
} }
?> ?>

View File

@@ -50,6 +50,8 @@ class WebPage
protected $sContentType; protected $sContentType;
protected $sContentDisposition; protected $sContentDisposition;
protected $sContentFileName; protected $sContentFileName;
protected $s_sOutputFormat;
protected $a_OutputOptions;
public function __construct($s_title) public function __construct($s_title)
{ {
@@ -67,6 +69,8 @@ class WebPage
$this->sContentType = ''; $this->sContentType = '';
$this->sContentDisposition = ''; $this->sContentDisposition = '';
$this->sContentFileName = ''; $this->sContentFileName = '';
$this->s_OutputFormat = utils::ReadParam('output_format', 'html');
$this->a_OutputOptions = array();
ob_start(); // Start capturing the output ob_start(); // Start capturing the output
} }
@@ -372,13 +376,13 @@ class WebPage
{ {
// Make sure that the URL to the script contains the application's version number // 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 // so that the new script do NOT get reloaded from the cache when the application is upgraded
if (strpos('?', $s_script) === false) if (strpos($s_script, '?') === false)
{ {
$s_script .= "?version=".ITOP_VERSION; $s_script .= "?itopversion=".ITOP_VERSION;
} }
else else
{ {
$s_script .= "&version=".ITOP_VERSION; $s_script .= "&itopversion=".ITOP_VERSION;
} }
echo "<script type=\"text/javascript\" src=\"$s_script\"></script>\n"; echo "<script type=\"text/javascript\" src=\"$s_script\"></script>\n";
} }
@@ -413,6 +417,10 @@ class WebPage
} }
echo "</style>\n"; echo "</style>\n";
} }
if (class_exists('MetaModel') && MetaModel::GetConfig())
{
echo "<link rel=\"shortcut icon\" href=\"".utils::GetAbsoluteUrlAppRoot()."images/favicon.ico\" />\n";
}
echo "</head>\n"; echo "</head>\n";
echo "<body>\n"; echo "<body>\n";
echo self::FilterXSS($this->s_content); echo self::FilterXSS($this->s_content);
@@ -511,5 +519,67 @@ class WebPage
{ {
return str_ireplace('<script', '&lt;script', $sHTML); return str_ireplace('<script', '&lt;script', $sHTML);
} }
/**
* What is the currently selected output format
* @return string The selected output format: html, pdf...
*/
public function GetOutputFormat()
{
return $this->s_OutputFormat;
}
/**
* Check whether the desired output format is possible or not
* @param string $sOutputFormat The desired output format: html, pdf...
* @return bool True if the format is Ok, false otherwise
*/
function IsOutputFormatAvailable($sOutputFormat)
{
$bResult = false;
switch($sOutputFormat)
{
case 'html':
$bResult = true; // Always supported
break;
case 'pdf':
$bResult = @is_readable(APPROOT.'lib/MPDF/mpdf.php');
break;
}
return $bResult;
}
/**
* Retrieves the value of a named output option for the given format
* @param string $sFormat The format: html or pdf
* @param string $sOptionName The name of the option
* @return mixed false if the option was never set or the options's value
*/
public function GetOutputOption($sFormat, $sOptionName)
{
if (isset($this->a_OutputOptions[$sFormat][$sOptionName]))
{
return $this->a_OutputOptions[$sFormat][$sOptionName];
}
return false;
}
/**
* Sets a named output option for the given format
* @param string $sFormat The format for which to set the option: html or pdf
* @param string $sOptionName the name of the option
* @param mixed $sValue The value of the option
*/
public function SetOutputOption($sFormat, $sOptionName, $sValue)
{
if (!isset($this->a_OutputOptions[$sFormat]))
{
$this->a_OutputOptions[$sFormat] = array($sOptionName => $sValue);
}
else
{
$this->a_OutputOptions[$sFormat][$sOptionName] = $sValue;
}
}
} }
?> ?>

View File

@@ -1,5 +1,6 @@
<?php <?php
define('APPROOT', dirname(__FILE__).'/'); define('APPROOT', dirname(__FILE__).'/');
define('MODULESROOT', APPROOT.'modules/');
if (function_exists('microtime')) if (function_exists('microtime'))
{ {
$fItopStarted = microtime(true); $fItopStarted = microtime(true);

View File

@@ -42,7 +42,7 @@ abstract class Action extends cmdbAbstractObject
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "name", "name_attcode" => "name",
"state_attcode" => "", "state_attcode" => "",
"reconc_keys" => array(), "reconc_keys" => array('name'),
"db_table" => "priv_action", "db_table" => "priv_action",
"db_key_field" => "id", "db_key_field" => "id",
"db_finalclass_field" => "realclass", "db_finalclass_field" => "realclass",
@@ -106,7 +106,7 @@ abstract class ActionNotification extends Action
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "name", "name_attcode" => "name",
"state_attcode" => "", "state_attcode" => "",
"reconc_keys" => array(), "reconc_keys" => array('name'),
"db_table" => "priv_action_notification", "db_table" => "priv_action_notification",
"db_key_field" => "id", "db_key_field" => "id",
"db_finalclass_field" => "", "db_finalclass_field" => "",
@@ -139,7 +139,7 @@ class ActionEmail extends ActionNotification
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "name", "name_attcode" => "name",
"state_attcode" => "", "state_attcode" => "",
"reconc_keys" => array(), "reconc_keys" => array('name'),
"db_table" => "priv_action_email", "db_table" => "priv_action_email",
"db_key_field" => "id", "db_key_field" => "id",
"db_finalclass_field" => "", "db_finalclass_field" => "",
@@ -183,6 +183,7 @@ class ActionEmail extends ActionNotification
try try
{ {
$oSearch = DBObjectSearch::FromOQL($sOQL); $oSearch = DBObjectSearch::FromOQL($sOQL);
$oSearch->AllowAllData();
} }
catch (OQLException $e) catch (OQLException $e)
{ {
@@ -275,6 +276,7 @@ class ActionEmail extends ActionNotification
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog) protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
{ {
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass(); $sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();
$aHeaders = array();
try try
{ {
$this->m_iRecipients = 0; $this->m_iRecipients = 0;
@@ -294,14 +296,17 @@ class ActionEmail extends ActionNotification
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs); $sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
$oObj = $aContextArgs['this->object()']; $oObj = $aContextArgs['this->object()'];
$sReference = '<iTop/'.get_class($oObj).'/'.$oObj->GetKey().'>'; $sMessageId = sprintf('<iTop_%s_%d_%f@%s.openitop.org>', get_class($oObj), $oObj->GetKey(), microtime(true /* get as float*/), MetaModel::GetConfig()->Get('session_name'));
$sReference = $sMessageId;
$aHeaders['Message-ID'] = $sMessageId;
} }
catch(Exception $e) catch(Exception $e)
{ {
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker); ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
throw $e; throw $e;
} }
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
if (!is_null($oLog)) if (!is_null($oLog))
{ {
// Note: we have to secure this because those values are calculated // Note: we have to secure this because those values are calculated
@@ -315,7 +320,7 @@ class ActionEmail extends ActionNotification
if (isset($sBody)) $oLog->Set('body', $sBody); if (isset($sBody)) $oLog->Set('body', $sBody);
} }
$oEmail = new EMail(); $oEmail = new EMail('', '', '', $aHeaders);
if ($this->IsBeingTested()) if ($this->IsBeingTested())
{ {

View File

@@ -140,7 +140,7 @@ class AsyncSendEmail extends AsyncTask
MetaModel::Init_AddAttribute(new AttributeText("to", array("allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("to", array("allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("body", array("allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeLongText("body", array("allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("header", array("allowed_values"=>null, "sql"=>"header", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("header", array("allowed_values"=>null, "sql"=>"header", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists // Display lists

View File

@@ -116,6 +116,8 @@ abstract class AttributeDefinition
$this->m_aParams = $aParams; $this->m_aParams = $aParams;
$this->ConsistencyCheck(); $this->ConsistencyCheck();
} }
// Left here for backward compatibility, deprecated in 2.0
public function OverloadParams($aParams) public function OverloadParams($aParams)
{ {
foreach ($aParams as $sParam => $value) foreach ($aParams as $sParam => $value)
@@ -130,6 +132,12 @@ abstract class AttributeDefinition
} }
} }
} }
public function GetParams()
{
return $this->m_aParams;
}
public function SetHostClass($sHostClass) public function SetHostClass($sHostClass)
{ {
$this->m_sHostClass = $sHostClass; $this->m_sHostClass = $sHostClass;
@@ -141,7 +149,7 @@ abstract class AttributeDefinition
// Note: I could factorize this code with the parameter management made for the AttributeDef class // Note: I could factorize this code with the parameter management made for the AttributeDef class
// to be overloaded // to be overloaded
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return array(); return array();
} }
@@ -170,6 +178,10 @@ abstract class AttributeDefinition
return ""; return "";
// e.g: return array("Site", "infrid", "name"); // e.g: return array("Site", "infrid", "name");
} }
public function GetFinalAttDef()
{
return $this;
}
public function IsDirectField() {return false;} public function IsDirectField() {return false;}
public function IsScalar() {return false;} public function IsScalar() {return false;}
public function IsLinkSet() {return false;} public function IsLinkSet() {return false;}
@@ -206,6 +218,15 @@ abstract class AttributeDefinition
} }
return $sLabel; return $sLabel;
} }
/**
* Get the label corresponding to the given value
* To be overloaded for localized enums
*/
public function GetValueLabel($sValue)
{
return GetAsHTML($sValue);
}
public function GetLabel_Obsolete() public function GetLabel_Obsolete()
{ {
@@ -431,7 +452,7 @@ abstract class AttributeDefinition
*/ */
class AttributeLinkedSet extends AttributeDefinition class AttributeLinkedSet extends AttributeDefinition
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return array_merge(parent::ListExpectedParams(), array("allowed_values", "depends_on", "linked_class", "ext_key_to_me", "count_min", "count_max")); return array_merge(parent::ListExpectedParams(), array("allowed_values", "depends_on", "linked_class", "ext_key_to_me", "count_min", "count_max"));
} }
@@ -741,7 +762,7 @@ class AttributeLinkedSet extends AttributeDefinition
*/ */
class AttributeLinkedSetIndirect extends AttributeLinkedSet class AttributeLinkedSetIndirect extends AttributeLinkedSet
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return array_merge(parent::ListExpectedParams(), array("ext_key_to_remote")); return array_merge(parent::ListExpectedParams(), array("ext_key_to_remote"));
} }
@@ -758,7 +779,7 @@ class AttributeLinkedSetIndirect extends AttributeLinkedSet
*/ */
class AttributeDBFieldVoid extends AttributeDefinition class AttributeDBFieldVoid extends AttributeDefinition
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return array_merge(parent::ListExpectedParams(), array("allowed_values", "depends_on", "sql")); return array_merge(parent::ListExpectedParams(), array("allowed_values", "depends_on", "sql"));
} }
@@ -844,7 +865,7 @@ class AttributeDBFieldVoid extends AttributeDefinition
*/ */
class AttributeDBField extends AttributeDBFieldVoid class AttributeDBField extends AttributeDBFieldVoid
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return array_merge(parent::ListExpectedParams(), array("default_value", "is_null_allowed")); return array_merge(parent::ListExpectedParams(), array("default_value", "is_null_allowed"));
} }
@@ -859,7 +880,7 @@ class AttributeDBField extends AttributeDBFieldVoid
*/ */
class AttributeInteger extends AttributeDBField class AttributeInteger extends AttributeDBField
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return parent::ListExpectedParams(); return parent::ListExpectedParams();
//return array_merge(parent::ListExpectedParams(), array()); //return array_merge(parent::ListExpectedParams(), array());
@@ -955,7 +976,7 @@ class AttributeInteger extends AttributeDBField
*/ */
class AttributeDecimal extends AttributeDBField class AttributeDecimal extends AttributeDBField
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return array_merge(parent::ListExpectedParams(), array('digits', 'decimals' /* including precision */)); return array_merge(parent::ListExpectedParams(), array('digits', 'decimals' /* including precision */));
} }
@@ -1050,7 +1071,7 @@ class AttributeDecimal extends AttributeDBField
*/ */
class AttributeBoolean extends AttributeInteger class AttributeBoolean extends AttributeInteger
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return parent::ListExpectedParams(); return parent::ListExpectedParams();
//return array_merge(parent::ListExpectedParams(), array()); //return array_merge(parent::ListExpectedParams(), array());
@@ -1081,7 +1102,7 @@ class AttributeBoolean extends AttributeInteger
*/ */
class AttributeString extends AttributeDBField class AttributeString extends AttributeDBField
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return parent::ListExpectedParams(); return parent::ListExpectedParams();
//return array_merge(parent::ListExpectedParams(), array()); //return array_merge(parent::ListExpectedParams(), array());
@@ -1207,7 +1228,7 @@ class AttributeString extends AttributeDBField
*/ */
class AttributeClass extends AttributeString class AttributeClass extends AttributeString
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return array_merge(parent::ListExpectedParams(), array("class_category", "more_values")); return array_merge(parent::ListExpectedParams(), array("class_category", "more_values"));
} }
@@ -1259,7 +1280,7 @@ class AttributeClass extends AttributeString
*/ */
class AttributeApplicationLanguage extends AttributeString class AttributeApplicationLanguage extends AttributeString
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return parent::ListExpectedParams(); return parent::ListExpectedParams();
} }
@@ -1333,7 +1354,12 @@ class AttributeFinalClass extends AttributeString
{ {
return '='; return '=';
} }
public function GetValueLabel($sValue)
{
if (empty($sValue)) return '';
return MetaModel::GetName($sValue);
}
} }
@@ -1344,7 +1370,7 @@ class AttributeFinalClass extends AttributeString
*/ */
class AttributePassword extends AttributeString class AttributePassword extends AttributeString
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return parent::ListExpectedParams(); return parent::ListExpectedParams();
//return array_merge(parent::ListExpectedParams(), array()); //return array_merge(parent::ListExpectedParams(), array());
@@ -1625,7 +1651,7 @@ class AttributeLongText extends AttributeText
* *
* @package iTopORM * @package iTopORM
*/ */
class AttributeCaseLog extends AttributeText class AttributeCaseLog extends AttributeLongText
{ {
public function GetNullValue() public function GetNullValue()
{ {
@@ -1811,7 +1837,7 @@ class AttributeCaseLog extends AttributeText
* *
* @package iTopORM * @package iTopORM
*/ */
class AttributeHTML extends AttributeText class AttributeHTML extends AttributeLongText
{ {
public function GetEditClass() {return "HTML";} public function GetEditClass() {return "HTML";}
@@ -1862,6 +1888,7 @@ class AttributeIPAddress extends AttributeString
*/ */
class AttributeOQL extends AttributeText class AttributeOQL extends AttributeText
{ {
public function GetEditClass() {return "OQLExpression";}
} }
/** /**
@@ -1905,7 +1932,7 @@ class AttributeTemplateHTML extends AttributeText
*/ */
class AttributeEnum extends AttributeString class AttributeEnum extends AttributeString
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return parent::ListExpectedParams(); return parent::ListExpectedParams();
//return array_merge(parent::ListExpectedParams(), array()); //return array_merge(parent::ListExpectedParams(), array());
@@ -2075,7 +2102,7 @@ class AttributeDateTime extends AttributeDBField
return "Y-m-d H:i:s"; return "Y-m-d H:i:s";
} }
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return parent::ListExpectedParams(); return parent::ListExpectedParams();
//return array_merge(parent::ListExpectedParams(), array()); //return array_merge(parent::ListExpectedParams(), array());
@@ -2289,6 +2316,7 @@ class AttributeDateTime extends AttributeDBField
default: default:
$oNewCondition = parent::GetSmartConditionExpression($sSearchText, $oField, $aParams); $oNewCondition = parent::GetSmartConditionExpression($sSearchText, $oField, $aParams);
} }
return $oNewCondition; return $oNewCondition;
@@ -2386,7 +2414,7 @@ class AttributeDate extends AttributeDateTime
return "Y-m-d"; return "Y-m-d";
} }
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return parent::ListExpectedParams(); return parent::ListExpectedParams();
//return array_merge(parent::ListExpectedParams(), array()); //return array_merge(parent::ListExpectedParams(), array());
@@ -2472,7 +2500,7 @@ class AttributeDeadline extends AttributeDateTime
*/ */
class AttributeExternalKey extends AttributeDBFieldVoid class AttributeExternalKey extends AttributeDBFieldVoid
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return array_merge(parent::ListExpectedParams(), array("targetclass", "is_null_allowed", "on_target_delete")); return array_merge(parent::ListExpectedParams(), array("targetclass", "is_null_allowed", "on_target_delete"));
} }
@@ -2589,7 +2617,7 @@ class AttributeHierarchicalKey extends AttributeExternalKey
{ {
protected $m_sTargetClass; protected $m_sTargetClass;
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
$aParams = parent::ListExpectedParams(); $aParams = parent::ListExpectedParams();
$idx = array_search('targetclass', $aParams); $idx = array_search('targetclass', $aParams);
@@ -2713,12 +2741,19 @@ class AttributeHierarchicalKey extends AttributeExternalKey
*/ */
class AttributeExternalField extends AttributeDefinition class AttributeExternalField extends AttributeDefinition
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return array_merge(parent::ListExpectedParams(), array("extkey_attcode", "target_attcode")); return array_merge(parent::ListExpectedParams(), array("extkey_attcode", "target_attcode"));
} }
public function GetEditClass() {return "ExtField";} public function GetEditClass() {return "ExtField";}
public function GetFinalAttDef()
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetFinalAttDef();
}
protected function GetSQLCol() protected function GetSQLCol()
{ {
// throw new CoreException("external attribute: does it make any sense to request its type ?"); // throw new CoreException("external attribute: does it make any sense to request its type ?");
@@ -2933,7 +2968,7 @@ class AttributeExternalField extends AttributeDefinition
*/ */
class AttributeURL extends AttributeString class AttributeURL extends AttributeString
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
//return parent::ListExpectedParams(); //return parent::ListExpectedParams();
return array_merge(parent::ListExpectedParams(), array("target")); return array_merge(parent::ListExpectedParams(), array("target"));
@@ -2967,7 +3002,7 @@ class AttributeURL extends AttributeString
*/ */
class AttributeBlob extends AttributeDefinition class AttributeBlob extends AttributeDefinition
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return array_merge(parent::ListExpectedParams(), array("depends_on")); return array_merge(parent::ListExpectedParams(), array("depends_on"));
} }
@@ -3114,7 +3149,7 @@ class AttributeBlob extends AttributeDefinition
*/ */
class AttributeOneWayPassword extends AttributeDefinition class AttributeOneWayPassword extends AttributeDefinition
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return array_merge(parent::ListExpectedParams(), array("depends_on")); return array_merge(parent::ListExpectedParams(), array("depends_on"));
} }
@@ -3264,16 +3299,26 @@ class AttributeOneWayPassword extends AttributeDefinition
} }
// Indexed array having two dimensions // Indexed array having two dimensions
class AttributeTable extends AttributeText class AttributeTable extends AttributeDBField
{ {
public function GetEditClass() {return "Text";} public function GetEditClass() {return "Table";}
protected function GetSQLCol() {return "TEXT";} protected function GetSQLCol() {return "LONGTEXT";}
public function GetMaxSize() public function GetMaxSize()
{ {
return null; return null;
} }
public function GetNullValue()
{
return array();
}
public function IsNull($proposedValue)
{
return (count($proposedValue) == 0);
}
// Facilitate things: allow the user to Set the value from a string // Facilitate things: allow the user to Set the value from a string
public function MakeRealValue($proposedValue, $oHostObj) public function MakeRealValue($proposedValue, $oHostObj)
{ {
@@ -3336,13 +3381,39 @@ class AttributeTable extends AttributeText
$sRes .= "</TABLE>"; $sRes .= "</TABLE>";
return $sRes; return $sRes;
} }
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
{
// Not implemented
return '';
}
public function GetAsXML($value, $oHostObject = null)
{
if (count($value) == 0)
{
return "";
}
$sRes = "";
foreach($value as $iRow => $aRawData)
{
$sRes .= "<row>";
foreach ($aRawData as $iCol => $cell)
{
$sCell = Str::pure2xml((string)$cell);
$sRes .= "<cell icol=\"$iCol\">$sCell</cell>";
}
$sRes .= "</row>";
}
return $sRes;
}
} }
// The PHP value is a hash array, it is stored as a TEXT column // The PHP value is a hash array, it is stored as a TEXT column
class AttributePropertySet extends AttributeTable class AttributePropertySet extends AttributeTable
{ {
public function GetEditClass() {return "Text";} public function GetEditClass() {return "PropertySet";}
protected function GetSQLCol() {return "TEXT";}
// Facilitate things: allow the user to Set the value from a string // Facilitate things: allow the user to Set the value from a string
public function MakeRealValue($proposedValue, $oHostObj) public function MakeRealValue($proposedValue, $oHostObj)
@@ -3369,6 +3440,10 @@ class AttributePropertySet extends AttributeTable
$sRes .= "<TBODY>"; $sRes .= "<TBODY>";
foreach($value as $sProperty => $sValue) foreach($value as $sProperty => $sValue)
{ {
if ($sProperty == 'auth_pwd')
{
$sValue = '*****';
}
$sRes .= "<TR>"; $sRes .= "<TR>";
$sCell = str_replace("\n", "<br>\n", Str::pure2html((string)$sValue)); $sCell = str_replace("\n", "<br>\n", Str::pure2html((string)$sValue));
$sRes .= "<TD class=\"label\">$sProperty</TD><TD>$sCell</TD>"; $sRes .= "<TD class=\"label\">$sProperty</TD><TD>$sCell</TD>";
@@ -3378,6 +3453,53 @@ class AttributePropertySet extends AttributeTable
$sRes .= "</TABLE>"; $sRes .= "</TABLE>";
return $sRes; return $sRes;
} }
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
{
if (count($value) == 0)
{
return "";
}
$aRes = array();
foreach($value as $sProperty => $sValue)
{
if ($sProperty == 'auth_pwd')
{
$sValue = '*****';
}
$sFrom = array(',', '=');
$sTo = array('\,', '\=');
$aRes[] = $sProperty.'='.str_replace($sFrom, $sTo, (string)$sValue);
}
$sRaw = implode(',', $aRes);
$sFrom = array("\r\n", $sTextQualifier);
$sTo = array("\n", $sTextQualifier.$sTextQualifier);
$sEscaped = str_replace($sFrom, $sTo, $sRaw);
return $sTextQualifier.$sEscaped.$sTextQualifier;
}
public function GetAsXML($value, $oHostObject = null)
{
if (count($value) == 0)
{
return "";
}
$sRes = "";
foreach($value as $sProperty => $sValue)
{
if ($sProperty == 'auth_pwd')
{
$sValue = '*****';
}
$sRes .= "<property id=\"$sProperty\">";
$sRes .= Str::pure2xml((string)$sValue);
$sRes .= "</property>";
}
return $sRes;
}
} }
/** /**
@@ -3387,7 +3509,7 @@ class AttributePropertySet extends AttributeTable
*/ */
class AttributeComputedFieldVoid extends AttributeDefinition class AttributeComputedFieldVoid extends AttributeDefinition
{ {
static protected function ListExpectedParams() static public function ListExpectedParams()
{ {
return array_merge(parent::ListExpectedParams(), array()); return array_merge(parent::ListExpectedParams(), array());
} }
@@ -3480,6 +3602,43 @@ class AttributeFriendlyName extends AttributeComputedFieldVoid
public function GetKeyAttCode() {return $this->Get("extkey_attcode");} public function GetKeyAttCode() {return $this->Get("extkey_attcode");}
public function GetLabel($sDefault = null)
{
$sLabel = parent::GetLabel('');
if (strlen($sLabel) == 0)
{
$sKeyAttCode = $this->Get("extkey_attcode");
if ($sKeyAttCode == 'id')
{
return Dict::S('Core:FriendlyName-Label');
}
else
{
$oExtKeyAttDef = MetaModel::GetAttributeDef($this->GetHostClass(), $sKeyAttCode);
$sLabel = $oExtKeyAttDef->GetLabel($this->m_sCode);
}
}
return $sLabel;
}
public function GetDescription($sDefault = null)
{
$sLabel = parent::GetDescription('');
if (strlen($sLabel) == 0)
{
$sKeyAttCode = $this->Get("extkey_attcode");
if ($sKeyAttCode == 'id')
{
return Dict::S('Core:FriendlyName-Description');
}
else
{
$oExtKeyAttDef = MetaModel::GetAttributeDef($this->GetHostClass(), $sKeyAttCode);
$sLabel = $oExtKeyAttDef->GetDescription('');
}
}
return $sLabel;
}
// n/a, the friendly name is made of a complex expression (see GetNameSpec) // n/a, the friendly name is made of a complex expression (see GetNameSpec)
protected function GetSQLCol() {return "";} protected function GetSQLCol() {return "";}

View File

@@ -401,7 +401,13 @@ class BulkChange
if ($sAttCode == 'id') continue; if ($sAttCode == 'id') continue;
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect()) $aReasons = array();
$iFlags = $oTargetObj->GetAttributeFlags($sAttCode, $aReasons);
if ( (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ( $oTargetObj->Get($sAttCode) != $aRowData[$iCol]) )
{
$aErrors[$sAttCode] = "the attribute '$sAttCode' is read-only and cannot be modified (current value: ".$oTargetObj->Get($sAttCode).", proposed value: {$aRowData[$iCol]}).";
}
else if ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect())
{ {
try try
{ {

View File

@@ -76,6 +76,7 @@ require_once('cmdbchangeop.class.inc.php');
// Romain: temporary moved into application.inc.php (see explanations there) // Romain: temporary moved into application.inc.php (see explanations there)
//require_once('event.class.inc.php'); //require_once('event.class.inc.php');
require_once('templatestring.class.inc.php');
require_once('csvparser.class.inc.php'); require_once('csvparser.class.inc.php');
require_once('bulkchange.class.inc.php'); require_once('bulkchange.class.inc.php');
@@ -92,6 +93,23 @@ abstract class CMDBObject extends DBObject
protected static $m_oCurrChange = null; protected static $m_oCurrChange = null;
public static function SetCurrentChange(CMDBChange $oChange)
{
self::$m_oCurrChange = $oChange;
}
//
// Todo: simplify the APIs and do not pass the current change as an argument anymore
// SetCurrentChange to be invoked in very few cases (UI.php, CSV import, Data synchro)
// GetCurrentChange to be called ONCE (!) by CMDBChangeOp::OnInsert ($this->Set('change', ..GetCurrentChange())
// GetCurrentChange to create a default change if not already done in the current context
//
public static function GetCurrentChange()
{
return self::$m_oCurrChange;
}
private function RecordObjCreation(CMDBChange $oChange) private function RecordObjCreation(CMDBChange $oChange)
{ {
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpCreate"); $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpCreate");
@@ -279,9 +297,10 @@ abstract class CMDBObject extends DBObject
{ {
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY); $this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange; self::$m_oCurrChange = $oChange;
$ret = $this->DBInsertTracked_Internal(); $ret = $this->DBInsertTracked_Internal();
self::$m_oCurrChange = null; self::$m_oCurrChange = $oPreviousChange;
return $ret; return $ret;
} }
@@ -289,9 +308,10 @@ abstract class CMDBObject extends DBObject
{ {
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY); $this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange; self::$m_oCurrChange = $oChange;
$ret = $this->DBInsertTracked_Internal(true); $ret = $this->DBInsertTracked_Internal(true);
self::$m_oCurrChange = null; self::$m_oCurrChange = $oPreviousChange;
return $ret; return $ret;
} }
@@ -320,9 +340,10 @@ abstract class CMDBObject extends DBObject
public function DBCloneTracked(CMDBChange $oChange, $newKey = null) public function DBCloneTracked(CMDBChange $oChange, $newKey = null)
{ {
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange; self::$m_oCurrChange = $oChange;
$this->DBCloneTracked_Internal($newKey); $this->DBCloneTracked_Internal($newKey);
self::$m_oCurrChange = null; self::$m_oCurrChange = $oPreviousChange;
} }
protected function DBCloneTracked_Internal($newKey = null) protected function DBCloneTracked_Internal($newKey = null)
@@ -347,9 +368,10 @@ abstract class CMDBObject extends DBObject
{ {
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY); $this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange; self::$m_oCurrChange = $oChange;
$this->DBUpdateTracked_Internal(); $this->DBUpdateTracked_Internal();
self::$m_oCurrChange = null; self::$m_oCurrChange = $oPreviousChange;
} }
protected function DBUpdateTracked_Internal() protected function DBUpdateTracked_Internal()
@@ -382,9 +404,10 @@ abstract class CMDBObject extends DBObject
{ {
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_DELETE); $this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_DELETE);
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange; self::$m_oCurrChange = $oChange;
$this->DBDeleteTracked_Internal($oDeletionPlan); $this->DBDeleteTracked_Internal($oDeletionPlan);
self::$m_oCurrChange = null; self::$m_oCurrChange = $oPreviousChange;
} }
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null) protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
@@ -406,9 +429,10 @@ abstract class CMDBObject extends DBObject
public static function BulkDeleteTracked(CMDBChange $oChange, DBObjectSearch $oFilter) public static function BulkDeleteTracked(CMDBChange $oChange, DBObjectSearch $oFilter)
{ {
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange; self::$m_oCurrChange = $oChange;
$this->BulkDeleteTracked_Internal($oFilter); $this->BulkDeleteTracked_Internal($oFilter);
self::$m_oCurrChange = null; self::$m_oCurrChange = $oPreviousChange;
} }
protected static function BulkDeleteTracked_Internal(DBObjectSearch $oFilter) protected static function BulkDeleteTracked_Internal(DBObjectSearch $oFilter)
@@ -445,9 +469,10 @@ abstract class CMDBObject extends DBObject
public static function BulkUpdateTracked(CMDBChange $oChange, DBObjectSearch $oFilter, array $aValues) public static function BulkUpdateTracked(CMDBChange $oChange, DBObjectSearch $oFilter, array $aValues)
{ {
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange; self::$m_oCurrChange = $oChange;
$this->BulkUpdateTracked_Internal($oFilter, $aValues); $this->BulkUpdateTracked_Internal($oFilter, $aValues);
self::$m_oCurrChange = null; self::$m_oCurrChange = $oPreviousChange;
} }
protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues) protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues)

View File

@@ -124,6 +124,14 @@ class Config
'source_of_value' => '', 'source_of_value' => '',
'show_in_conf_sample' => false, 'show_in_conf_sample' => false,
), ),
'php_path' => array(
'type' => 'string',
'description' => 'Path to the php executable in CLI mode',
'default' => 'php',
'value' => 'php',
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'session_name' => array( 'session_name' => array(
'type' => 'string', 'type' => 'string',
'description' => 'The name of the cookie used to store the PHP session id', 'description' => 'The name of the cookie used to store the PHP session id',
@@ -189,6 +197,14 @@ class Config
'source_of_value' => '', 'source_of_value' => '',
'show_in_conf_sample' => false, 'show_in_conf_sample' => false,
), ),
'csv_import_history_display' => array(
'type' => 'bool',
'description' => 'Display the history tab in the import wizard',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'access_mode' => array( 'access_mode' => array(
'type' => 'integer', 'type' => 'integer',
'description' => 'Combination of flags (ACCESS_USER_WRITE | ACCESS_ADMIN_WRITE, or ACCESS_FULL)', 'description' => 'Combination of flags (ACCESS_USER_WRITE | ACCESS_ADMIN_WRITE, or ACCESS_FULL)',
@@ -383,6 +399,42 @@ class Config
'source_of_value' => '', 'source_of_value' => '',
'show_in_conf_sample' => true, 'show_in_conf_sample' => true,
), ),
'cas_user_synchro' => array(
'type' => 'bool',
'description' => 'Whether or not to synchronize users with CAS/LDAP',
// examples... not used (nor 'description')
'default' => 0,
'value' => 0,
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'cas_update_profiles' => array(
'type' => 'bool',
'description' => 'Whether or not to update the profiles of an existing user from the CAS information',
// examples... not used (nor 'description')
'default' => 0,
'value' => 0,
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'cas_profile_pattern' => array(
'type' => 'string',
'description' => 'A regular expression pattern to extract the name of the iTop profile from the name of an LDAP/CAS group',
// examples... not used (nor 'description')
'default' => '/^cn=([^,]+),/',
'value' => '/^cn=([^,]+),/',
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'cas_default_profiles' => array(
'type' => 'string',
'description' => 'A semi-colon separated list of iTop Profiles to use when creating a new user if no profile is retrieved from CAS',
// examples... not used (nor 'description')
'default' => 'Portal user',
'value' => 'Portal user',
'source_of_value' => '',
'show_in_conf_sample' => true,
),
'cas_debug' => array( 'cas_debug' => array(
'type' => 'bool', 'type' => 'bool',
'description' => 'Activate the CAS debug', 'description' => 'Activate the CAS debug',
@@ -419,6 +471,15 @@ class Config
'source_of_value' => '', 'source_of_value' => '',
'show_in_conf_sample' => true, 'show_in_conf_sample' => true,
), ),
'complex_actions_limit' => array(
'type' => 'integer',
'description' => 'Display the "actions" menu items that require long computation only if the list of objects is contains less objects than this number (0 means no limit)',
// examples... not used
'default' => 50,
'value' => 50,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
); );
public function IsProperty($sPropCode) public function IsProperty($sPropCode)
@@ -544,6 +605,7 @@ class Config
'application/menunode.class.inc.php', 'application/menunode.class.inc.php',
'application/user.preferences.class.inc.php', 'application/user.preferences.class.inc.php',
'application/audit.rule.class.inc.php', 'application/audit.rule.class.inc.php',
'application/query.class.inc.php',
// Romain - That's dirty, because those classes are in fact part of the core // Romain - That's dirty, because those classes are in fact part of the core
// but I needed those classes to be derived from cmdbAbstractObject // but I needed those classes to be derived from cmdbAbstractObject
// (to be managed via the GUI) and this class in not really known from // (to be managed via the GUI) and this class in not really known from

View File

@@ -154,7 +154,7 @@ abstract class DBObject
return true; return true;
} }
protected function Reload() public function Reload()
{ {
assert($this->m_bIsInDB); assert($this->m_bIsInDB);
$aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey, false/*, $this->m_bAllowAllData*/); $aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey, false/*, $this->m_bAllowAllData*/);
@@ -302,30 +302,53 @@ abstract class DBObject
$this->Reload(); $this->Reload();
} }
if ($oAttDef->IsExternalKey() && is_object($value)) if ($oAttDef->IsExternalKey())
{ {
// Setting an external key with a whole object (instead of just an ID) if (is_object($value))
// let's initialize also the external fields that depend on it
// (useful when building objects in memory and not from a query)
if ( (get_class($value) != $oAttDef->GetTargetClass()) && (!is_subclass_of($value, $oAttDef->GetTargetClass())))
{ {
throw new CoreUnexpectedValue("Trying to set the value of '$sAttCode', to an object of class '".get_class($value)."', whereas it's an ExtKey to '".$oAttDef->GetTargetClass()."'. Ignored"); // Setting an external key with a whole object (instead of just an ID)
// let's initialize also the external fields that depend on it
// (useful when building objects in memory and not from a query)
if ( (get_class($value) != $oAttDef->GetTargetClass()) && (!is_subclass_of($value, $oAttDef->GetTargetClass())))
{
throw new CoreUnexpectedValue("Trying to set the value of '$sAttCode', to an object of class '".get_class($value)."', whereas it's an ExtKey to '".$oAttDef->GetTargetClass()."'. Ignored");
}
else
{
$this->m_aCurrValues[$sAttCode] = $value->GetKey();
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef)
{
if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sAttCode))
{
$this->m_aCurrValues[$sCode] = $value->Get($oDef->GetExtAttCode());
}
}
$this->m_aCurrValues[$sAttCode.'_friendlyname'] = $value->GetName();
}
} }
else else if ($this->m_aCurrValues[$sAttCode] != $value)
{ {
// The object has changed, reset caches // If the external key changed, invalidate all the external fields (and friendly name) related to this external key
$this->m_bCheckStatus = null; $this->m_aCurrValues[$sAttCode] = $value;
$this->m_aAsArgs = null;
$this->m_aCurrValues[$sAttCode] = $value->GetKey();
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef) foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef)
{ {
if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sAttCode)) if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sAttCode))
{ {
$this->m_aCurrValues[$sCode] = $value->Get($oDef->GetExtAttCode()); unset($this->m_aLoadedAtt[$sCode]);
$this->m_aCurrValues[$sCode] = null;
} }
} }
$this->m_aCurrValues[$sAttCode.'_friendlyname'] = null;
unset($this->m_aLoadedAtt[$sAttCode.'_friendlyname']);
} }
// The object has changed, reset caches
$this->m_bCheckStatus = null;
$this->m_aAsArgs = null;
// Make sure we do not reload it anymore... before saving it
$this->RegisterAsDirty();
return; return;
} }
if(!$oAttDef->IsScalar() && !is_object($value)) if(!$oAttDef->IsScalar() && !is_object($value))
@@ -367,15 +390,93 @@ abstract class DBObject
} }
public function Get($sAttCode) public function Get($sAttCode)
{
if (($iPos = strpos($sAttCode, '->')) === false)
{
return $this->GetStrict($sAttCode);
}
else
{
$sExtKeyAttCode = substr($sAttCode, 0, $iPos);
$sRemoteAttCode = substr($sAttCode, $iPos + 2);
if (!MetaModel::IsValidAttCode(get_class($this), $sExtKeyAttCode))
{
throw new CoreException("Unknown external key '$sExtKeyAttCode' for the class ".get_class($this));
}
$oKeyAttDef = MetaModel::GetAttributeDef(get_class($this), $sExtKeyAttCode);
$sRemoteClass = $oKeyAttDef->GetTargetClass();
$oRemoteObj = MetaModel::GetObject($sRemoteClass, $this->GetStrict($sExtKeyAttCode), false);
if (is_null($oRemoteObj))
{
return '';
}
else
{
return $oRemoteObj->Get($sRemoteAttCode);
}
}
}
public function GetStrict($sAttCode)
{ {
if (!array_key_exists($sAttCode, MetaModel::ListAttributeDefs(get_class($this)))) if (!array_key_exists($sAttCode, MetaModel::ListAttributeDefs(get_class($this))))
{ {
throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this)); throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this));
} }
if ($this->m_bIsInDB && !isset($this->m_aLoadedAtt[$sAttCode]) && !$this->m_bDirty) if ($this->m_bIsInDB && !isset($this->m_aLoadedAtt[$sAttCode]))
{ {
// #@# non-scalar attributes.... handle that differently // #@# non-scalar attributes.... handle that differently
$this->Reload(); if (!$this->m_bDirty)
{
$this->Reload();
}
else
{
// If the missing attribute is an external fields (or a friendlyname), try to selectively reload it
// from the value of its external key... and reload the related external fields & friendlyname as well
$sTargetClass = '';
$iCurrKey = 0;
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
if ($oAttDef->IsExternalField())
{
$sKeyAttCode = $oAttDef->GetKeyAttCode();
$iCurrKey = $this->m_aCurrValues[$oAttDef->GetKeyAttCode()];
$sTargetClass= $oAttDef->GetTargetClass();
}
else if ($oAttDef instanceof AttributeFriendlyName)
{
$sKeyAttCode = $oAttDef->GetKeyAttCode();
$oKeyAttDef = MetaModel::GetAttributeDef(get_class($this), $sKeyAttCode);
$iCurrKey = $this->m_aCurrValues[$oAttDef->GetKeyAttCode()];
$sTargetClass = $oKeyAttDef->GetTargetClass();
}
if (($sTargetClass != '') && ($iCurrKey != 0))
{
$oTargetObj = MetaModel::GetObject($sTargetClass, $iCurrKey, false);
if (is_object($oTargetObj))
{
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef)
{
if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sKeyAttCode))
{
$this->m_aLoadedAtt[$sCode] = true;
$this->m_aCurrValues[$sCode] = $oTargetObj->Get($oDef->GetExtAttCode());
}
}
if ($oAttDef instanceof AttributeFriendlyName)
{
$this->m_aLoadedAtt[$sAttCode] = true;
$this->m_aCurrValues[$sAttCode] = $oTargetObj->GetName();
}
else
{
$this->m_aLoadedAtt[$sKeyAttCode.'_friendlyname'] = true;
$this->m_aCurrValues[$sKeyAttCode.'_friendlyname'] = $oTargetObj->GetName();
}
}
}
}
} }
$value = $this->m_aCurrValues[$sAttCode]; $value = $this->m_aCurrValues[$sAttCode];
if ($value instanceof DBObjectSet) if ($value instanceof DBObjectSet)
@@ -460,7 +561,7 @@ abstract class DBObject
} }
// That's a standard attribute (might be an ext field or a direct field, etc.) // That's a standard attribute (might be an ext field or a direct field, etc.)
return $oAtt->GetAsHTML($this->Get($sAttCode)); return $oAtt->GetAsHTML($this->Get($sAttCode), $this);
} }
public function GetEditValue($sAttCode) public function GetEditValue($sAttCode)
@@ -904,7 +1005,7 @@ abstract class DBObject
$oDeletionPlan->AddToDelete($oReplica, DEL_SILENT); $oDeletionPlan->AddToDelete($oReplica, DEL_SILENT);
if ($oDataSource->GetKey() == SynchroDataSource::GetCurrentTaskId()) if ($oDataSource->GetKey() == SynchroExecution::GetCurrentTaskId())
{ {
// The current task has the right to delete the object // The current task has the right to delete the object
continue; continue;
@@ -953,30 +1054,35 @@ abstract class DBObject
$aDelta = array(); $aDelta = array();
foreach ($aProposal as $sAtt => $proposedValue) foreach ($aProposal as $sAtt => $proposedValue)
{ {
if (!array_key_exists($sAtt, $this->m_aOrigValues)) $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAtt);
// Ignore external fields and friendly names that change only as a consequence of modifying another field
if ((!$oAttDef->IsExternalField() && !($oAttDef instanceof AttributeFriendlyName)))
{ {
// The value was not set if (!array_key_exists($sAtt, $this->m_aOrigValues))
$aDelta[$sAtt] = $proposedValue;
}
elseif(is_object($proposedValue))
{
$oLinkAttDef = MetaModel::GetAttributeDef(get_class($this), $sAtt);
// The value is an object, the comparison is not strict
if (!$oLinkAttDef->Equals($proposedValue, $this->m_aOrigValues[$sAtt]))
{ {
// The value was not set
$aDelta[$sAtt] = $proposedValue; $aDelta[$sAtt] = $proposedValue;
} }
} elseif(is_object($proposedValue))
else {
{ $oLinkAttDef = MetaModel::GetAttributeDef(get_class($this), $sAtt);
// The value is a scalar, the comparison must be 100% strict // The value is an object, the comparison is not strict
if($this->m_aOrigValues[$sAtt] !== $proposedValue) if (!$oLinkAttDef->Equals($proposedValue, $this->m_aOrigValues[$sAtt]))
{ {
//echo "$sAtt:<pre>\n"; $aDelta[$sAtt] = $proposedValue;
//var_dump($this->m_aOrigValues[$sAtt]); }
//var_dump($proposedValue); }
//echo "</pre>\n"; else
$aDelta[$sAtt] = $proposedValue; {
// The value is a scalar, the comparison must be 100% strict
if($this->m_aOrigValues[$sAtt] !== $proposedValue)
{
//echo "$sAtt:<pre>\n";
//var_dump($this->m_aOrigValues[$sAtt]);
//var_dump($proposedValue);
//echo "</pre>\n";
$aDelta[$sAtt] = $proposedValue;
}
} }
} }
} }
@@ -1275,6 +1381,11 @@ abstract class DBObject
return $this->DBInsert(); return $this->DBInsert();
} }
public function DBInsertTrackedNoReload(CMDBChange $oVoid)
{
return $this->DBInsertNoReload();
}
// Creates a copy of the current object into the database // Creates a copy of the current object into the database
// Returns the id of the newly created object // Returns the id of the newly created object
public function DBClone($iNewKey = null) public function DBClone($iNewKey = null)
@@ -1580,19 +1691,22 @@ abstract class DBObject
if (!$bRet) $bSuccess = false; if (!$bRet) $bSuccess = false;
} }
// Change state triggers... if ($bSuccess)
$sClass = get_class($this);
$sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class IN ('$sClassList') AND t.state='$sPreviousState'"));
while ($oTrigger = $oSet->Fetch())
{ {
$oTrigger->DoActivate($this->ToArgs('this')); // Change state triggers...
} $sClass = get_class($this);
$sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class IN ('$sClassList') AND t.state='$sNewState'")); $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class IN ('$sClassList') AND t.state='$sPreviousState'"));
while ($oTrigger = $oSet->Fetch()) while ($oTrigger = $oSet->Fetch())
{ {
$oTrigger->DoActivate($this->ToArgs('this')); $oTrigger->DoActivate($this->ToArgs('this'));
}
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class IN ('$sClassList') AND t.state='$sNewState'"));
while ($oTrigger = $oSet->Fetch())
{
$oTrigger->DoActivate($this->ToArgs('this'));
}
} }
return $bSuccess; return $bSuccess;
@@ -1625,7 +1739,7 @@ abstract class DBObject
// #@# Note: This has been proven to be quite slow, this can slow down bulk load // #@# Note: This has been proven to be quite slow, this can slow down bulk load
$sAsHtml = $this->GetAsHtml($sAttCode); $sAsHtml = $this->GetAsHtml($sAttCode);
$aScalarArgs[$sArgName.'->html('.$sAttCode.')'] = $sAsHtml; $aScalarArgs[$sArgName.'->html('.$sAttCode.')'] = $sAsHtml;
$aScalarArgs[$sArgName.'->label('.$sAttCode.')'] = strip_tags($sAsHtml); $aScalarArgs[$sArgName.'->label('.$sAttCode.')'] = $this->GetEditValue($sAttCode); // "Nice" display value, but without HTML tags and entities
} }
// Do something for case logs... quick N' dirty... // Do something for case logs... quick N' dirty...
if ($aScalarArgs[$sArgName.'->'.$sAttCode] instanceof ormCaseLog) if ($aScalarArgs[$sArgName.'->'.$sAttCode] instanceof ormCaseLog)
@@ -1831,7 +1945,7 @@ abstract class DBObject
$oSet = $this->GetMasterReplica(); $oSet = $this->GetMasterReplica();
while($aData = $oSet->FetchAssoc()) while($aData = $oSet->FetchAssoc())
{ {
if ($aData['datasource']->GetKey() == SynchroDataSource::GetCurrentTaskId()) if ($aData['datasource']->GetKey() == SynchroExecution::GetCurrentTaskId())
{ {
// Ignore the current task (check to write => ok) // Ignore the current task (check to write => ok)
continue; continue;

View File

@@ -66,6 +66,8 @@ class DBObjectSearch
$this->m_aRelatedTo = array(); $this->m_aRelatedTo = array();
$this->m_bDataFiltered = false; $this->m_bDataFiltered = false;
$this->m_aParentConditions = array(); $this->m_aParentConditions = array();
$this->m_aModifierProperties = array();
} }
public function AllowAllData() {$this->m_bAllowAllData = true;} public function AllowAllData() {$this->m_bAllowAllData = true;}
@@ -73,7 +75,18 @@ class DBObjectSearch
public function IsDataFiltered() {return $this->m_bDataFiltered; } public function IsDataFiltered() {return $this->m_bDataFiltered; }
public function SetDataFiltered() {$this->m_bDataFiltered = true;} public function SetDataFiltered() {$this->m_bDataFiltered = true;}
public function GetClassName($sAlias) {return $this->m_aClasses[$sAlias];} public function GetClassName($sAlias)
{
if (array_key_exists($sAlias, $this->m_aClasses))
{
return $this->m_aClasses[$sAlias];
}
else
{
throw new CoreException("Invalid class alias '$sAlias'");
}
}
public function GetJoinedClasses() {return $this->m_aClasses;} public function GetJoinedClasses() {return $this->m_aClasses;}
public function GetClass() public function GetClass()
@@ -115,6 +128,23 @@ class DBObjectSearch
} }
public function SetModifierProperty($sPluginClass, $sProperty, $value)
{
$this->m_aModifierProperties[$sPluginClass][$sProperty] = $value;
}
public function GetModifierProperties($sPluginClass)
{
if (array_key_exists($sPluginClass, $this->m_aModifierProperties))
{
return $this->m_aModifierProperties[$sPluginClass];
}
else
{
return array();
}
}
public function IsAny() public function IsAny()
{ {
// #@# todo - if (!$this->m_oSearchCondition->IsTrue()) return false; // #@# todo - if (!$this->m_oSearchCondition->IsTrue()) return false;
@@ -737,20 +767,47 @@ class DBObjectSearch
{ {
return $this->m_aRelatedTo; return $this->m_aRelatedTo;
} }
public function SetInternalParams($aParams)
{
return $this->m_aParams = $aParams;
}
public function GetInternalParams() public function GetInternalParams()
{ {
return $this->m_aParams; return $this->m_aParams;
} }
public function GetQueryParams()
{
$aParams = array();
$this->m_oSearchCondition->Render($aParams, true);
return $aParams;
}
public function ListConstantFields()
{
return $this->m_oSearchCondition->ListConstantFields();
}
public function RenderCondition() public function RenderCondition()
{ {
return $this->m_oSearchCondition->Render($this->m_aParams, false); return $this->m_oSearchCondition->Render($this->m_aParams, false);
} }
/**
* Turn the parameters (:xxx) into scalar values in order to easily
* serialize a search
*/
public function ApplyParameters($aArgs)
{
return $this->m_oSearchCondition->ApplyParameters(array_merge($this->m_aParams, $aArgs));
}
public function serialize($bDevelopParams = false, $aContextParams = null) public function serialize($bDevelopParams = false, $aContextParams = null)
{ {
$sOql = $this->ToOql($bDevelopParams, $aContextParams); $sOql = $this->ToOql($bDevelopParams, $aContextParams);
return base64_encode(serialize(array($sOql, $this->m_aParams))); return base64_encode(serialize(array($sOql, $this->m_aParams, $this->m_aModifierProperties)));
} }
static public function unserialize($sValue) static public function unserialize($sValue)
@@ -761,7 +818,9 @@ class DBObjectSearch
// We've tried to use gzcompress/gzuncompress, but for some specific queries // We've tried to use gzcompress/gzuncompress, but for some specific queries
// it was not working at all (See Trac #193) // it was not working at all (See Trac #193)
// gzuncompress was issuing a warning "data error" and the return object was null // gzuncompress was issuing a warning "data error" and the return object was null
return self::FromOQL($sOql, $aParams); $oRetFilter = self::FromOQL($sOql, $aParams);
$oRetFilter->m_aModifierProperties = $aData[2];
return $oRetFilter;
} }
// SImple BUt Structured Query Languag - SubuSQL // SImple BUt Structured Query Languag - SubuSQL

View File

@@ -687,6 +687,56 @@ class DBObjectSet
$this->Rewind(); $this->Rewind();
return $oCommonObj; return $oCommonObj;
} }
/**
* List the constant fields (and their value) in the given query
* @return Hash [Alias][AttCode] => value
*/
public function ListConstantFields()
{
$aScalarArgs = $this->ExpandArgs();
$aConst = $this->m_oFilter->ListConstantFields();
foreach($aConst as $sClassAlias => $aVals)
{
foreach($aVals as $sCode => $oExpr)
{
if ($oExpr instanceof ScalarExpression)
{
$aConst[$sClassAlias][$sCode] = $oExpr->GetValue();
}
else //Variable
{
$aConst[$sClassAlias][$sCode] = $aScalarArgs[$oExpr->GetName()];
}
}
}
return $aConst;
}
protected function ExpandArgs()
{
$aScalarArgs = $this->m_oFilter->GetInternalParams();
foreach($this->m_aArgs as $sArgName => $value)
{
if (MetaModel::IsValidObject($value))
{
$aScalarArgs = array_merge($aScalarArgs, $value->ToArgs($sArgName));
}
else
{
$aScalarArgs[$sArgName] = (string) $value;
}
}
$aScalarArgs['current_contact_id'] = UserRights::GetContactId();
return $aScalarArgs;
}
public function ApplyParameters()
{
$aScalarArgs = $this->ExpandArgs();
$this->m_oFilter->ApplyParameters($aScalarArgs);
}
} }
/** /**

View File

@@ -97,7 +97,7 @@ class Dict
} }
public static function GetCurrentLanguage() public static function GetUserLanguage()
{ {
if (self::$m_sCurrentLanguage == null) // May happen when no user is logged in (i.e login screen, non authentifed page) if (self::$m_sCurrentLanguage == null) // May happen when no user is logged in (i.e login screen, non authentifed page)
{ {
@@ -124,12 +124,12 @@ class Dict
{ {
// Attempt to find the string in the user language // Attempt to find the string in the user language
// //
if (!array_key_exists(self::GetCurrentLanguage(), self::$m_aData)) 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 dictionnaries get loaded
return $sStringCode; return $sStringCode;
} }
$aCurrentDictionary = self::$m_aData[self::GetCurrentLanguage()]; $aCurrentDictionary = self::$m_aData[self::GetUserLanguage()];
if (array_key_exists($sStringCode, $aCurrentDictionary)) if (array_key_exists($sStringCode, $aCurrentDictionary))
{ {
return $aCurrentDictionary[$sStringCode]; return $aCurrentDictionary[$sStringCode];
@@ -220,6 +220,20 @@ class Dict
} }
} }
/**
* Clone a string in every language (if it exists in that language)
*/
public static function CloneString($sSourceCode, $sDestCode)
{
foreach(self::$m_aLanguages as $sLanguageCode => $foo)
{
if (isset(self::$m_aData[$sLanguageCode][$sSourceCode]))
{
self::$m_aData[$sLanguageCode][$sDestCode] = self::$m_aData[$sLanguageCode][$sSourceCode];
}
}
}
public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US') public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US')
{ {
$aMissing = array(); // Strings missing for the target language $aMissing = array(); // Strings missing for the target language

View File

@@ -35,6 +35,7 @@ class EMail
protected $m_sSubject; protected $m_sSubject;
protected $m_sTo; protected $m_sTo;
protected $m_aHeaders; // array of key=>value protected $m_aHeaders; // array of key=>value
protected $m_aAttachments;
public function __construct($sTo = '', $sSubject = '', $sBody = '', $aHeaders = array()) public function __construct($sTo = '', $sSubject = '', $sBody = '', $aHeaders = array())
{ {
@@ -42,6 +43,7 @@ class EMail
$this->m_sSubject = $sSubject; $this->m_sSubject = $sSubject;
$this->m_sBody = $sBody; $this->m_sBody = $sBody;
$this->m_aHeaders = $aHeaders; $this->m_aHeaders = $aHeaders;
$this->m_aAttachments = array();
} }
// Errors management : not that simple because we need that function to be // Errors management : not that simple because we need that function to be
@@ -73,8 +75,14 @@ class EMail
{ {
$sHeaders = 'MIME-Version: 1.0' . "\r\n"; $sHeaders = 'MIME-Version: 1.0' . "\r\n";
// ! the case is important for MS-Outlook // ! the case is important for MS-Outlook
$sHeaders .= 'Content-Type: text/html; charset=UTF-8' . "\r\n"; if (!array_key_exists('Content-Type', $this->m_aHeaders))
$sHeaders .= 'Content-Transfer-Encoding: 8bit' . "\r\n"; {
$sHeaders .= 'Content-Type: text/html; charset=UTF-8' . "\r\n";
}
if (!array_key_exists('Content-Transfer-Encoding', $this->m_aHeaders))
{
$sHeaders .= 'Content-Transfer-Encoding: 8bit' . "\r\n";
}
foreach ($this->m_aHeaders as $sKey => $sValue) foreach ($this->m_aHeaders as $sKey => $sValue)
{ {
$sHeaders .= "$sKey: $sValue\r\n"; $sHeaders .= "$sKey: $sValue\r\n";
@@ -87,7 +95,7 @@ class EMail
$bRes = mail $bRes = mail
( (
str_replace(array("\n", "\r"), ' ', $this->m_sTo), // Prevent header injection str_replace(array("\n", "\r"), ' ', $this->m_sTo), // Prevent header injection
str_replace(array("\n", "\r"), ' ', $this->m_sSubject), // Prevent header injection $this->EncodeHeaderField($this->m_sSubject), // Prevent header injection & MIME Encode charsets
$this->m_sBody, $this->m_sBody,
$sHeaders $sHeaders
); );
@@ -110,6 +118,7 @@ class EMail
public function Send(&$aIssues, $bForceSynchronous = false, $oLog = null) public function Send(&$aIssues, $bForceSynchronous = false, $oLog = null)
{ {
$this->BuildMessage(); // assemble the attachments into the header/body structure
if ($bForceSynchronous) if ($bForceSynchronous)
{ {
return $this->SendSynchronous($aIssues, $oLog); return $this->SendSynchronous($aIssues, $oLog);
@@ -128,13 +137,18 @@ class EMail
} }
} }
protected function AddToHeader($sKey, $sValue) public function AddToHeader($sKey, $sValue)
{ {
if (strlen($sValue) > 0) if (strlen($sValue) > 0)
{ {
$this->m_aHeaders[$sKey] = $sValue; $this->m_aHeaders[$sKey] = $sValue;
} }
} }
public function SetMessageId($sId)
{
$this->AddToHeader('Message-ID', $sId);
}
public function SetReferences($sReferences) public function SetReferences($sReferences)
{ {
@@ -181,6 +195,48 @@ class EMail
$this->AddToHeader('Reply-To', $sAddress); $this->AddToHeader('Reply-To', $sAddress);
} }
public function AddAttachment($data, $sFileName, $sMimeType)
{
$this->m_aAttachments[] = array('data' => $data, 'filename' => $sFileName, 'mimeType' => $sMimeType);
}
/**
* Takes care of the attachments (if any) to build the header/body of the message before storing or sending it
*/
protected function BuildMessage()
{
if (count($this->m_aAttachments) == 0) return; // Nothing to do if there are no attachments
$sDelimiter = '== iTopEmailPart---'.md5(date('r', time()))." ==";
$sContentType = isset($this->m_aHeaders['Content-Type']) ? $this->m_aHeaders['Content-Type'] : 'text/html; charset="UTF-8"';
$sContentHeader = "Content-Type: $sContentType\r\n";
$this->m_aHeaders['Content-Type'] = "multipart/mixed; boundary=\"{$sDelimiter}\"";
$aAttachments = array();
foreach($this->m_aAttachments as $aAttach)
{
$sAttachmentHeader = "Content-Type: {$aAttach['mimeType']};\r\n Name=\"{$aAttach['filename']}\"\r\n";
$sAttachmentHeader .= "Content-Transfer-Encoding: base64\r\nContent-Disposition: attachment;\r\n filename=\"{$aAttach['filename']}\"\r\n";
$sAttachmentHeader .= "\r\n";
$sAttachment = chunk_split(base64_encode($aAttach['data']));
$aAttachments[] = $sAttachmentHeader.$sAttachment."\r\n";
}
$this->m_sBody = "This is a multi-part message in MIME format.\r\n--".$sDelimiter."\r\n".$sContentHeader."\r\n".$this->m_sBody."\r\n--".$sDelimiter."\r\n";
$this->m_sBody .= implode("--".$sDelimiter."\r\n", $aAttachments);
$this->m_sBody .= "--".$sDelimiter."--";
}
/**
* MIME encode the content of a header field according to RFC2047
* @param string $sFieldContent the content of the header to encode
* @return string The encoded string
*/
protected function EncodeHeaderField($sFieldContent)
{
$sTemp = str_replace(array("\n", "\r"), ' ', $sFieldContent);
$sTemp = iconv_mime_encode('Tagada', $sTemp, array('scheme' => 'Q', 'input-charset' => 'UTF-8', 'output-charset' => 'UTF-8'));
return preg_replace('/^Tagada: /', '', $sTemp);
}
} }
?> ?>

View File

@@ -103,7 +103,7 @@ class Event extends DBObject implements iDisplay
$this->DisplayBareProperties($oPage, $bEditMode); $this->DisplayBareProperties($oPage, $bEditMode);
} }
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $aExtraParams = array()) function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{ {
if ($bEditMode) return; // Not editable if ($bEditMode) return; // Not editable
@@ -174,7 +174,7 @@ class EventNotificationEmail extends EventNotification
MetaModel::Init_AddAttribute(new AttributeText("bcc", array("allowed_values"=>null, "sql"=>"bcc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("bcc", array("allowed_values"=>null, "sql"=>"bcc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("from", array("allowed_values"=>null, "sql"=>"from", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("from", array("allowed_values"=>null, "sql"=>"from", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("body", array("allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeHTML("body", array("allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists // Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_id', 'to', 'cc', 'bcc', 'from', 'subject', 'body')); // Attributes to be displayed for the complete details MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_id', 'to', 'cc', 'bcc', 'from', 'subject', 'body')); // Attributes to be displayed for the complete details
@@ -340,14 +340,22 @@ class EventLoginUsage extends Event
MetaModel::Init_InheritAttributes(); MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("contact_name", array("allowed_values"=>null, "extkey_attcode"=>"user_id", "target_attcode"=>"contactid", "is_null_allowed"=>true, "depends_on"=>array()))); $aZList = array('date', 'user_id');
MetaModel::Init_AddAttribute(new AttributeExternalField("contact_email", array("allowed_values"=>null, "extkey_attcode"=>"user_id", "target_attcode"=>"email", "is_null_allowed"=>true, "depends_on"=>array()))); if (MetaModel::IsValidAttCode('Contact', 'name'))
{
MetaModel::Init_AddAttribute(new AttributeExternalField("contact_name", array("allowed_values"=>null, "extkey_attcode"=>"user_id", "target_attcode"=>"contactid", "is_null_allowed"=>true, "depends_on"=>array())));
$aZList[] = 'contact_name';
}
if (MetaModel::IsValidAttCode('Contact', 'email'))
{
MetaModel::Init_AddAttribute(new AttributeExternalField("contact_email", array("allowed_values"=>null, "extkey_attcode"=>"user_id", "target_attcode"=>"email", "is_null_allowed"=>true, "depends_on"=>array())));
$aZList[] = 'contact_email';
}
// Display lists // Display lists
MetaModel::Init_SetZListItems('details', array('date', 'user_id', 'contact_name', 'contact_email', 'userinfo', 'message')); // Attributes to be displayed for the complete details MetaModel::Init_SetZListItems('details', array_merge($aZList, array('userinfo', 'message'))); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'user_id', 'contact_name', 'contact_email', 'userinfo')); // Attributes to be displayed for a list MetaModel::Init_SetZListItems('list', array_merge($aZList, array('userinfo'))); // Attributes to be displayed for a list
// Search criteria // Search criteria
MetaModel::Init_SetZListItems('standard_search', array('date', 'user_id', 'contact_name', 'contact_email')); // Criteria of the std search form MetaModel::Init_SetZListItems('standard_search', $aZList); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
} }
} }

View File

@@ -36,11 +36,16 @@ abstract class Expression
// recursive rendering (aArgs used as input by default, or used as output if bRetrofitParams set to True // recursive rendering (aArgs used as input by default, or used as output if bRetrofitParams set to True
abstract public function Render(&$aArgs = null, $bRetrofitParams = false); abstract public function Render(&$aArgs = null, $bRetrofitParams = false);
abstract public function ApplyParameters($aArgs);
// recursively builds an array of class => fieldname // recursively builds an array of class => fieldname
abstract public function ListRequiredFields(); abstract public function ListRequiredFields();
abstract public function IsTrue(); abstract public function IsTrue();
// recursively builds an array of [classAlias][fieldName] => value
abstract public function ListConstantFields();
public function RequiresField($sClass, $sFieldName) public function RequiresField($sClass, $sFieldName)
{ {
// #@# todo - optimize : this is called quite often when building a single query ! // #@# todo - optimize : this is called quite often when building a single query !
@@ -108,6 +113,10 @@ class SQLExpression extends Expression
return $this->m_sSQL; return $this->m_sSQL;
} }
public function ApplyParameters($aArgs)
{
}
public function GetUnresolvedFields($sAlias, &$aUnresolved) public function GetUnresolvedFields($sAlias, &$aUnresolved)
{ {
} }
@@ -122,6 +131,11 @@ class SQLExpression extends Expression
return array(); return array();
} }
public function ListConstantFields()
{
return array();
}
public function RenameParam($sOldName, $sNewName) public function RenameParam($sOldName, $sNewName)
{ {
// Do nothing, since there is nothing to rename // Do nothing, since there is nothing to rename
@@ -164,11 +178,11 @@ class BinaryExpression extends Expression
// return true if we are certain that it will be true // return true if we are certain that it will be true
if ($this->m_sOperator == 'AND') if ($this->m_sOperator == 'AND')
{ {
if ($this->m_oLeftExpr->IsTrue() && $this->m_oLeftExpr->IsTrue()) return true; if ($this->m_oLeftExpr->IsTrue() && $this->m_oRightExpr->IsTrue()) return true;
} }
return false; return false;
} }
public function GetLeftExpr() public function GetLeftExpr()
{ {
return $this->m_oLeftExpr; return $this->m_oLeftExpr;
@@ -192,7 +206,27 @@ class BinaryExpression extends Expression
$sRight = $this->GetRightExpr()->Render($aArgs, $bRetrofitParams); $sRight = $this->GetRightExpr()->Render($aArgs, $bRetrofitParams);
return "($sLeft $sOperator $sRight)"; return "($sLeft $sOperator $sRight)";
} }
public function ApplyParameters($aArgs)
{
if ($this->m_oLeftExpr instanceof VariableExpression)
{
$this->m_oLeftExpr = $this->m_oLeftExpr->GetAsScalar($aArgs);
}
else //if ($this->m_oLeftExpr instanceof Expression)
{
$this->m_oLeftExpr->ApplyParameters($aArgs);
}
if ($this->m_oRightExpr instanceof VariableExpression)
{
$this->m_oRightExpr = $this->m_oRightExpr->GetAsScalar($aArgs);
}
else //if ($this->m_oRightExpr instanceof Expression)
{
$this->m_oRightExpr->ApplyParameters($aArgs);
}
}
public function GetUnresolvedFields($sAlias, &$aUnresolved) public function GetUnresolvedFields($sAlias, &$aUnresolved)
{ {
$this->GetLeftExpr()->GetUnresolvedFields($sAlias, $aUnresolved); $this->GetLeftExpr()->GetUnresolvedFields($sAlias, $aUnresolved);
@@ -213,6 +247,44 @@ class BinaryExpression extends Expression
return array_merge($aLeft, $aRight); return array_merge($aLeft, $aRight);
} }
/**
* List all constant expression of the form <field> = <scalar> or <field> = :<variable>
* Could be extended to support <field> = <function><constant_expression>
*/
public function ListConstantFields()
{
$aResult = array();
if ($this->m_sOperator == '=')
{
if (($this->m_oLeftExpr instanceof FieldExpression) && ($this->m_oRightExpr instanceof ScalarExpression))
{
$aResult[$this->m_oLeftExpr->GetParent()][$this->m_oLeftExpr->GetName()] = $this->m_oRightExpr;
}
else if (($this->m_oRightExpr instanceof FieldExpression) && ($this->m_oLeftExpr instanceof ScalarExpression))
{
$aResult[$this->m_oRightExpr->GetParent()][$this->m_oRightExpr->GetName()] = $this->m_oLeftExpr;
}
else if (($this->m_oLeftExpr instanceof FieldExpression) && ($this->m_oRightExpr instanceof VariableExpression))
{
$aResult[$this->m_oLeftExpr->GetParent()][$this->m_oLeftExpr->GetName()] = $this->m_oRightExpr;
}
else if (($this->m_oRightExpr instanceof FieldExpression) && ($this->m_oLeftExpr instanceof VariableExpression))
{
$aResult[$this->m_oRightExpr->GetParent()][$this->m_oRightExpr->GetName()] = $this->m_oLeftExpr;
}
else
{
$aResult = array_merge($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields()) ;
}
}
else
{
$aResult = array_merge($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields()) ;
}
return $aResult;
}
public function RenameParam($sOldName, $sNewName) public function RenameParam($sOldName, $sNewName)
{ {
$this->GetLeftExpr()->RenameParam($sOldName, $sNewName); $this->GetLeftExpr()->RenameParam($sOldName, $sNewName);
@@ -244,18 +316,13 @@ class UnaryExpression extends Expression
// recursive rendering // recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false) public function Render(&$aArgs = null, $bRetrofitParams = false)
{ {
if ($bRetrofitParams) return CMDBSource::Quote($this->m_value);
{
$iParamIndex = count($aArgs) + 1; // 1-based indexation
$aArgs['param'.$iParamIndex] = $this->m_value;
return ':param'.$iParamIndex;
}
else
{
return CMDBSource::Quote($this->m_value);
}
} }
public function ApplyParameters($aArgs)
{
}
public function GetUnresolvedFields($sAlias, &$aUnresolved) public function GetUnresolvedFields($sAlias, &$aUnresolved)
{ {
} }
@@ -269,6 +336,11 @@ class UnaryExpression extends Expression
{ {
return array(); return array();
} }
public function ListConstantFields()
{
return array();
}
public function RenameParam($sOldName, $sNewName) public function RenameParam($sOldName, $sNewName)
{ {
@@ -440,7 +512,7 @@ class VariableExpression extends UnaryExpression
} }
elseif ($bRetrofitParams) elseif ($bRetrofitParams)
{ {
//$aArgs[$this->m_sName] = null; $aArgs[$this->m_sName] = null;
return ':'.$this->m_sName; return ':'.$this->m_sName;
} }
else else
@@ -456,6 +528,20 @@ class VariableExpression extends UnaryExpression
$this->m_sName = $sNewName; $this->m_sName = $sNewName;
} }
} }
public function GetAsScalar($aArgs)
{
$value = '';
if (array_key_exists($this->m_sName, $aArgs))
{
$value = $aArgs[$this->m_sName];
}
else
{
throw new MissingQueryArgument('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>array_keys($aArgs)));
}
return new ScalarExpression($value);
}
} }
// Temporary, until we implement functions and expression casting! // Temporary, until we implement functions and expression casting!
@@ -501,6 +587,22 @@ class ListExpression extends Expression
return '('.implode(', ', $aRes).')'; return '('.implode(', ', $aRes).')';
} }
public function ApplyParameters($aArgs)
{
$aRes = array();
foreach ($this->m_aExpressions as $idx => $oExpr)
{
if ($oExpr instanceof VariableExpression)
{
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar();
}
else
{
$oExpr->ApplyParameters($aArgs);
}
}
}
public function GetUnresolvedFields($sAlias, &$aUnresolved) public function GetUnresolvedFields($sAlias, &$aUnresolved)
{ {
foreach ($this->m_aExpressions as $oExpr) foreach ($this->m_aExpressions as $oExpr)
@@ -529,6 +631,16 @@ class ListExpression extends Expression
return $aRes; return $aRes;
} }
public function ListConstantFields()
{
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
$aRes = array_merge($aRes, $oExpr->ListConstantFields());
}
return $aRes;
}
public function RenameParam($sOldName, $sNewName) public function RenameParam($sOldName, $sNewName)
{ {
$aRes = array(); $aRes = array();
@@ -578,6 +690,22 @@ class FunctionExpression extends Expression
return $this->m_sVerb.'('.implode(', ', $aRes).')'; return $this->m_sVerb.'('.implode(', ', $aRes).')';
} }
public function ApplyParameters($aArgs)
{
$aRes = array();
foreach ($this->m_aArgs as $idx => $oExpr)
{
if ($oExpr instanceof VariableExpression)
{
$this->m_aArgs[$idx] = $oExpr->GetAsScalar($aArgs);
}
else
{
$oExpr->ApplyParameters($aArgs);
}
}
}
public function GetUnresolvedFields($sAlias, &$aUnresolved) public function GetUnresolvedFields($sAlias, &$aUnresolved)
{ {
foreach ($this->m_aArgs as $oExpr) foreach ($this->m_aArgs as $oExpr)
@@ -606,6 +734,16 @@ class FunctionExpression extends Expression
return $aRes; return $aRes;
} }
public function ListConstantFields()
{
$aRes = array();
foreach ($this->m_aArgs as $oExpr)
{
$aRes = array_merge($aRes, $oExpr->ListConstantFields());
}
return $aRes;
}
public function RenameParam($sOldName, $sNewName) public function RenameParam($sOldName, $sNewName)
{ {
foreach ($this->m_aArgs as $key => $oExpr) foreach ($this->m_aArgs as $key => $oExpr)
@@ -648,6 +786,18 @@ class IntervalExpression extends Expression
return 'INTERVAL '.$this->m_oValue->Render($aArgs, $bRetrofitParams).' '.$this->m_sUnit; return 'INTERVAL '.$this->m_oValue->Render($aArgs, $bRetrofitParams).' '.$this->m_sUnit;
} }
public function ApplyParameters($aArgs)
{
if ($this->m_oValue instanceof VariableExpression)
{
$this->m_oValue = $this->m_oValue->GetAsScalar($aArgs);
}
else
{
$this->m_oValue->ApplyParameters($aArgs);
}
}
public function GetUnresolvedFields($sAlias, &$aUnresolved) public function GetUnresolvedFields($sAlias, &$aUnresolved)
{ {
$this->m_oValue->GetUnresolvedFields($sAlias, $aUnresolved); $this->m_oValue->GetUnresolvedFields($sAlias, $aUnresolved);
@@ -662,6 +812,11 @@ class IntervalExpression extends Expression
{ {
return array(); return array();
} }
public function ListConstantFields()
{
return array();
}
public function RenameParam($sOldName, $sNewName) public function RenameParam($sOldName, $sNewName)
{ {
@@ -702,6 +857,22 @@ class CharConcatExpression extends Expression
return "CAST(CONCAT(".implode(', ', $aRes).") AS CHAR)"; return "CAST(CONCAT(".implode(', ', $aRes).") AS CHAR)";
} }
public function ApplyParameters($aArgs)
{
$aRes = array();
foreach ($this->m_aExpressions as $idx => $oExpr)
{
if ($oExpr instanceof VariableExpression)
{
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar();
}
else
{
$this->m_aExpressions->ApplyParameters($aArgs);
}
}
}
public function GetUnresolvedFields($sAlias, &$aUnresolved) public function GetUnresolvedFields($sAlias, &$aUnresolved)
{ {
foreach ($this->m_aExpressions as $oExpr) foreach ($this->m_aExpressions as $oExpr)
@@ -730,6 +901,16 @@ class CharConcatExpression extends Expression
return $aRes; return $aRes;
} }
public function ListConstantFields()
{
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
$aRes = array_merge($aRes, $oExpr->ListConstantFields());
}
return $aRes;
}
public function RenameParam($sOldName, $sNewName) public function RenameParam($sOldName, $sNewName)
{ {
foreach ($this->m_aExpressions as $key => $oExpr) foreach ($this->m_aExpressions as $key => $oExpr)
@@ -772,10 +953,10 @@ class QueryBuilderExpressions
protected $m_aSelectExpr; protected $m_aSelectExpr;
protected $m_aJoinFields; protected $m_aJoinFields;
public function __construct($aSelect, $oCondition) public function __construct($oCondition)
{ {
$this->m_oConditionExpr = $oCondition; $this->m_oConditionExpr = $oCondition;
$this->m_aSelectExpr = $aSelect; $this->m_aSelectExpr = array();
$this->m_aJoinFields = array(); $this->m_aJoinFields = array();
} }

View File

@@ -50,6 +50,7 @@ abstract class FilterDefinition
$this->ConsistencyCheck(); $this->ConsistencyCheck();
} }
// Left here for backward compatibility, deprecated in 2.0
public function OverloadParams($aParams) public function OverloadParams($aParams)
{ {
foreach ($aParams as $sParam => $value) foreach ($aParams as $sParam => $value)

View File

@@ -191,6 +191,16 @@ class ExecutionKPI
return $output[1] * 1024; return $output[1] * 1024;
} }
} }
static public function memory_get_peak_usage($bRealUsage = false)
{
if (function_exists('memory_get_peak_usage'))
{
return memory_get_peak_usage($bRealUsage);
}
// PHP > 5.2.1 - this verb depends on a compilation option
return 0;
}
} }
class ApplicationStartupKPI extends ExecutionKPI class ApplicationStartupKPI extends ExecutionKPI

View File

@@ -15,6 +15,9 @@
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
require_once(APPROOT.'core/modulehandler.class.inc.php'); require_once(APPROOT.'core/modulehandler.class.inc.php');
require_once(APPROOT.'core/querybuildercontext.class.inc.php');
require_once(APPROOT.'core/querymodifier.class.inc.php');
require_once(APPROOT.'core/metamodelmodifier.inc.php');
/** /**
* Metamodel * Metamodel
@@ -25,8 +28,6 @@ require_once(APPROOT.'core/modulehandler.class.inc.php');
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/ */
// #@# todo: change into class const (see Doctrine) // #@# todo: change into class const (see Doctrine)
// Doctrine example // Doctrine example
// class toto // class toto
@@ -676,10 +677,38 @@ abstract class MetaModel
if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass])) return false; if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass])) return false;
return (self::$m_aAttribDefs[$sClass][$sAttCode]->IsExternalKey()); return (self::$m_aAttribDefs[$sClass][$sAttCode]->IsExternalKey());
} }
final static public function IsValidAttCode($sClass, $sAttCode) final static public function IsValidAttCode($sClass, $sAttCode, $bExtended = false)
{ {
if (!array_key_exists($sClass, self::$m_aAttribDefs)) return false; if (!array_key_exists($sClass, self::$m_aAttribDefs)) return false;
return (array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]));
if ($bExtended)
{
if (($iPos = strpos($sAttCode, '->')) === false)
{
$bRes = array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]);
}
else
{
$sExtKeyAttCode = substr($sAttCode, 0, $iPos);
$sRemoteAttCode = substr($sAttCode, $iPos + 2);
if (MetaModel::IsValidAttCode($sClass, $sExtKeyAttCode))
{
$oKeyAttDef = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode);
$sRemoteClass = $oKeyAttDef->GetTargetClass();
$bRes = MetaModel::IsValidAttCode($sRemoteClass, $sRemoteAttCode, true);
}
else
{
$bRes = false;
}
}
}
else
{
$bRes = array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]);
}
return $bRes;
} }
final static public function IsAttributeOrigin($sClass, $sAttCode) final static public function IsAttributeOrigin($sClass, $sAttCode)
{ {
@@ -1144,6 +1173,33 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
self::$m_sTablePrefix = $sTablePrefix; self::$m_sTablePrefix = $sTablePrefix;
// Build the list of available extensions
//
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iQueryModifier', 'iOnClassInitialization');
foreach($aInterfaces as $sInterface)
{
self::$m_aExtensionClasses[$sInterface] = array();
}
foreach(get_declared_classes() as $sPHPClass)
{
$oRefClass = new ReflectionClass($sPHPClass);
$oExtensionInstance = null;
foreach($aInterfaces as $sInterface)
{
if ($oRefClass->implementsInterface($sInterface))
{
if (is_null($oExtensionInstance))
{
$oExtensionInstance = new $sPHPClass;
}
self::$m_aExtensionClasses[$sInterface][$sPHPClass] = $oExtensionInstance;
}
}
}
// Initialize the classes (declared attributes, etc.)
//
foreach(get_declared_classes() as $sPHPClass) { foreach(get_declared_classes() as $sPHPClass) {
if (is_subclass_of($sPHPClass, 'DBObject')) if (is_subclass_of($sPHPClass, 'DBObject'))
{ {
@@ -1156,6 +1212,10 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
if (method_exists($sPHPClass, 'Init')) if (method_exists($sPHPClass, 'Init'))
{ {
call_user_func(array($sPHPClass, 'Init')); call_user_func(array($sPHPClass, 'Init'));
foreach (MetaModel::EnumPlugins('iOnClassInitialization') as $sPluginClass => $oClassInit)
{
$oClassInit->OnAfterClassInitialization($sPHPClass);
}
} }
} }
} }
@@ -1373,31 +1433,6 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
// } // }
//} //}
} }
// Build the list of available extensions
//
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension');
foreach($aInterfaces as $sInterface)
{
self::$m_aExtensionClasses[$sInterface] = array();
}
foreach(get_declared_classes() as $sPHPClass)
{
$oRefClass = new ReflectionClass($sPHPClass);
$oExtensionInstance = null;
foreach($aInterfaces as $sInterface)
{
if ($oRefClass->implementsInterface($sInterface))
{
if (is_null($oExtensionInstance))
{
$oExtensionInstance = new $sPHPClass;
}
self::$m_aExtensionClasses[$sInterface][$sPHPClass] = $oExtensionInstance;
}
}
}
} }
// To be overriden, must be called for any object class (optimization) // To be overriden, must be called for any object class (optimization)
@@ -1514,6 +1549,8 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
self::$m_aChildClasses[$sAncestorClass][] = $sTargetClass; self::$m_aChildClasses[$sAncestorClass][] = $sTargetClass;
} }
} }
// Left here for backward compatibility, deprecated in 2.0
public static function Init_OverloadAttributeParams($sAttCode, $aParams) public static function Init_OverloadAttributeParams($sAttCode, $aParams)
{ {
$sTargetClass = self::GetCallersPHPClass("Init"); $sTargetClass = self::GetCallersPHPClass("Init");
@@ -1534,9 +1571,12 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
return true; return true;
} }
public static function Init_AddAttribute(AttributeDefinition $oAtt) public static function Init_AddAttribute(AttributeDefinition $oAtt, $sTargetClass = null)
{ {
$sTargetClass = self::GetCallersPHPClass("Init"); if (!$sTargetClass)
{
$sTargetClass = self::GetCallersPHPClass("Init");
}
$sAttCode = $oAtt->GetCode(); $sAttCode = $oAtt->GetCode();
if ($sAttCode == 'finalclass') if ($sAttCode == 'finalclass')
@@ -1597,11 +1637,14 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
// Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used // Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used
} }
public static function Init_SetZListItems($sListCode, $aItems) public static function Init_SetZListItems($sListCode, $aItems, $sTargetClass = null)
{ {
MyHelpers::CheckKeyInArray('list code', $sListCode, self::$m_aListInfos); MyHelpers::CheckKeyInArray('list code', $sListCode, self::$m_aListInfos);
$sTargetClass = self::GetCallersPHPClass("Init"); if (!$sTargetClass)
{
$sTargetClass = self::GetCallersPHPClass("Init");
}
// Discard attributes that do not make sense // Discard attributes that do not make sense
// (missing classes in the current module combination, resulting in irrelevant ext key or link set) // (missing classes in the current module combination, resulting in irrelevant ext key or link set)
@@ -1888,7 +1931,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
// //
if (!$oFilter->IsAllDataAllowed() && !$oFilter->IsDataFiltered()) if (!$oFilter->IsAllDataAllowed() && !$oFilter->IsDataFiltered())
{ {
$oVisibleObjects = UserRights::GetSelectFilter($oFilter->GetClass()); $oVisibleObjects = UserRights::GetSelectFilter($oFilter->GetClass(), $oFilter->GetModifierProperties('UserRightsGetSelectFilter'));
if ($oVisibleObjects === false) if ($oVisibleObjects === false)
{ {
// Make sure this is a valid search object, saying NO for all // Make sure this is a valid search object, saying NO for all
@@ -1906,12 +1949,26 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
} }
} }
// Compute query modifiers properties (can be set in the search itself, by the context, etc.)
//
$aModifierProperties = self::MakeModifierProperties($oFilter);
if (self::$m_bQueryCacheEnabled || self::$m_bTraceQueries) if (self::$m_bQueryCacheEnabled || self::$m_bTraceQueries)
{ {
// Need to identify the query // Need to identify the query
$sOqlQuery = $oFilter->ToOql(); $sOqlQuery = $oFilter->ToOql();
$sRawId = $sOqlQuery; if (count($aModifierProperties))
{
array_multisort($aModifierProperties);
$sModifierProperties = json_encode($aModifierProperties);
}
else
{
$sModifierProperties = '';
}
$sRawId = $sOqlQuery.$sModifierProperties;
if (!is_null($aAttToLoad)) if (!is_null($aAttToLoad))
{ {
foreach($aAttToLoad as $sAlias => $aAttributes) foreach($aAttToLoad as $sAlias => $aAttributes)
@@ -1971,7 +2028,10 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
$aOrderSpec = array(); $aOrderSpec = array();
foreach ($aOrderBy as $sFieldAlias => $bAscending) foreach ($aOrderBy as $sFieldAlias => $bAscending)
{ {
MyHelpers::CheckValueInArray('field name in ORDER BY spec', $sFieldAlias, self::GetAttributesList($oFilter->GetFirstJoinedClass())); if ($sFieldAlias != 'id')
{
MyHelpers::CheckValueInArray('field name in ORDER BY spec', $sFieldAlias, self::GetAttributesList($oFilter->GetFirstJoinedClass()));
}
if (!is_bool($bAscending)) if (!is_bool($bAscending))
{ {
throw new CoreException("Wrong direction in ORDER BY spec, found '$bAscending' and expecting a boolean value"); throw new CoreException("Wrong direction in ORDER BY spec, found '$bAscending' and expecting a boolean value");
@@ -2004,12 +2064,10 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
if (!isset($oSelect)) if (!isset($oSelect))
{ {
$aClassAliases = array(); $oBuild = new QueryBuilderContext($oFilter, $aModifierProperties);
$aTableAliases = array();
$oQBExpr = new QueryBuilderExpressions(array(), $oFilter->GetCriteria());
$oKPI = new ExecutionKPI(); $oKPI = new ExecutionKPI();
$oSelect = self::MakeQuery($oFilter->GetSelectedClasses(), $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, $aAttToLoad, array(), true /* main query */); $oSelect = self::MakeQuery($oBuild, $oFilter, $aAttToLoad, array(), true /* main query */);
$oSelect->SetSourceOQL($sOqlQuery); $oSelect->SetSourceOQL($sOqlQuery);
$oKPI->ComputeStats('MakeQuery (select)', $sOqlQuery); $oKPI->ComputeStats('MakeQuery (select)', $sOqlQuery);
@@ -2118,12 +2176,33 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
} }
} }
protected static function MakeModifierProperties($oFilter)
{
// Compute query modifiers properties (can be set in the search itself, by the context, etc.)
//
$aModifierProperties = array();
foreach (MetaModel::EnumPlugins('iQueryModifier') as $sPluginClass => $oQueryModifier)
{
// Lowest precedence: the application context
$aPluginProps = ApplicationContext::GetPluginProperties($sPluginClass);
// Highest precedence: programmatically specified (or OQL)
foreach($oFilter->GetModifierProperties($sPluginClass) as $sProp => $value)
{
$aPluginProps[$sProp] = $value;
}
if (count($aPluginProps) > 0)
{
$aModifierProperties[$sPluginClass] = $aPluginProps;
}
}
return $aModifierProperties;
}
public static function MakeDeleteQuery(DBObjectSearch $oFilter, $aArgs = array()) public static function MakeDeleteQuery(DBObjectSearch $oFilter, $aArgs = array())
{ {
$aClassAliases = array(); $aModifierProperties = self::MakeModifierProperties($oFilter);
$aTableAliases = array(); $oBuild = new QueryBuilderContext($oFilter, $aModifierProperties);
$oQBExpr = new QueryBuilderExpressions(array(), $oFilter->GetCriteria()); $oSelect = self::MakeQuery($oBuild, $oFilter, null, array(), true /* main query */);
$oSelect = self::MakeQuery($oFilter->GetSelectedClasses(), $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, null, array(), true /* main query */);
$aScalarArgs = array_merge(self::PrepareQueryArguments($aArgs), $oFilter->GetInternalParams()); $aScalarArgs = array_merge(self::PrepareQueryArguments($aArgs), $oFilter->GetInternalParams());
return $oSelect->RenderDelete($aScalarArgs); return $oSelect->RenderDelete($aScalarArgs);
} }
@@ -2131,26 +2210,21 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
public static function MakeUpdateQuery(DBObjectSearch $oFilter, $aValues, $aArgs = array()) public static function MakeUpdateQuery(DBObjectSearch $oFilter, $aValues, $aArgs = array())
{ {
// $aValues is an array of $sAttCode => $value // $aValues is an array of $sAttCode => $value
$aClassAliases = array(); $aModifierProperties = self::MakeModifierProperties($oFilter);
$aTableAliases = array(); $oBuild = new QueryBuilderContext($oFilter, $aModifierProperties);
$oQBExpr = new QueryBuilderExpressions(array(), $oFilter->GetCriteria()); $oSelect = self::MakeQuery($oBuild, $oFilter, null, $aValues, true /* main query */);
$oSelect = self::MakeQuery($oFilter->GetSelectedClasses(), $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, null, $aValues, true /* main query */);
$aScalarArgs = array_merge(self::PrepareQueryArguments($aArgs), $oFilter->GetInternalParams()); $aScalarArgs = array_merge(self::PrepareQueryArguments($aArgs), $oFilter->GetInternalParams());
return $oSelect->RenderUpdate($aScalarArgs); return $oSelect->RenderUpdate($aScalarArgs);
} }
private static function MakeQuery($aSelectedClasses, &$oQBExpr, &$aClassAliases, &$aTableAliases, DBObjectSearch $oFilter, $aAttToLoad = null, $aValues = array(), $bIsMainQuery = false) private static function MakeQuery(&$oBuild, DBObjectSearch $oFilter, $aAttToLoad = null, $aValues = array(), $bIsMainQuery = false)
{ {
// Note: query class might be different than the class of the filter // Note: query class might be different than the class of the filter
// -> this occurs when we are linking our class to an external class (referenced by, or pointing to) // -> this occurs when we are linking our class to an external class (referenced by, or pointing to)
$sClass = $oFilter->GetFirstJoinedClass(); $sClass = $oFilter->GetFirstJoinedClass();
$sClassAlias = $oFilter->GetFirstJoinedClassAlias(); $sClassAlias = $oFilter->GetFirstJoinedClassAlias();
$bIsOnQueriedClass = array_key_exists($sClassAlias, $aSelectedClasses); $bIsOnQueriedClass = array_key_exists($sClassAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
if ($bIsOnQueriedClass)
{
$aClassAliases = array_merge($aClassAliases, $oFilter->GetJoinedClasses());
}
self::DbgTrace("Entering: ".$oFilter->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY")); self::DbgTrace("Entering: ".$oFilter->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
@@ -2160,7 +2234,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
if ($bIsOnQueriedClass) if ($bIsOnQueriedClass)
{ {
// default to the whole list of attributes + the very std id/finalclass // default to the whole list of attributes + the very std id/finalclass
$oQBExpr->AddSelect($sClassAlias.'id', new FieldExpression('id', $sClassAlias)); $oBuild->m_oQBExpressions->AddSelect($sClassAlias.'id', new FieldExpression('id', $sClassAlias));
if (is_null($aAttToLoad) || !array_key_exists($sClassAlias, $aAttToLoad)) if (is_null($aAttToLoad) || !array_key_exists($sClassAlias, $aAttToLoad))
{ {
@@ -2176,7 +2250,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr) foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr)
{ {
$oQBExpr->AddSelect($sClassAlias.$sAttCode.$sColId, new FieldExpression($sAttCode.$sColId, $sClassAlias)); $oBuild->m_oQBExpressions->AddSelect($sClassAlias.$sAttCode.$sColId, new FieldExpression($sAttCode.$sColId, $sClassAlias));
} }
} }
@@ -2196,14 +2270,14 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
foreach($aFullText as $sFTNeedle) foreach($aFullText as $sFTNeedle)
{ {
$oNewCond = new BinaryExpression($oTextFields, 'LIKE', new ScalarExpression("%$sFTNeedle%")); $oNewCond = new BinaryExpression($oTextFields, 'LIKE', new ScalarExpression("%$sFTNeedle%"));
$oQBExpr->AddCondition($oNewCond); $oBuild->m_oQBExpressions->AddCondition($oNewCond);
} }
} }
} }
//echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oQBExpr, true)."</pre></p>\n"; //echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."</pre></p>\n";
$aExpectedAtts = array(); // array of (attcode => fieldexpression) $aExpectedAtts = array(); // array of (attcode => fieldexpression)
//echo "<p>".__LINE__.": GetUnresolvedFields($sClassAlias, ...)</p>\n"; //echo "<p>".__LINE__.": GetUnresolvedFields($sClassAlias, ...)</p>\n";
$oQBExpr->GetUnresolvedFields($sClassAlias, $aExpectedAtts); $oBuild->m_oQBExpressions->GetUnresolvedFields($sClassAlias, $aExpectedAtts);
// Compute a clear view of required joins (from the current class) // Compute a clear view of required joins (from the current class)
// Build the list of external keys: // Build the list of external keys:
@@ -2240,9 +2314,9 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
{ {
$aTranslateNow = array(); $aTranslateNow = array();
$aTranslateNow[$sClassAlias]['friendlyname'] = self::GetNameExpression($sClass, $sClassAlias); $aTranslateNow[$sClassAlias]['friendlyname'] = self::GetNameExpression($sClass, $sClassAlias);
//echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oQBExpr, true)."</pre></p>\n"; //echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."</pre></p>\n";
$oQBExpr->Translate($aTranslateNow, false); $oBuild->m_oQBExpressions->Translate($aTranslateNow, false);
//echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oQBExpr, true)."</pre></p>\n"; //echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."</pre></p>\n";
$aNameSpec = self::GetNameSpec($sClass); $aNameSpec = self::GetNameSpec($sClass);
foreach($aNameSpec[1] as $i => $sAttCode) foreach($aNameSpec[1] as $i => $sAttCode)
@@ -2284,7 +2358,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
self::DbgTrace("Main (=leaf) class, call MakeQuerySingleTable()"); self::DbgTrace("Main (=leaf) class, call MakeQuerySingleTable()");
if (self::HasTable($sClass)) if (self::HasTable($sClass))
{ {
$oSelectBase = self::MakeQuerySingleTable($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, $sClass, $aExtKeys, $aValues); $oSelectBase = self::MakeQuerySingleTable($oBuild, $oFilter, $sClass, $aExtKeys, $aValues);
} }
else else
{ {
@@ -2293,7 +2367,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
// As the join will not filter on the expected classes, we have to specify it explicitely // As the join will not filter on the expected classes, we have to specify it explicitely
$sExpectedClasses = implode("', '", self::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL)); $sExpectedClasses = implode("', '", self::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
$oFinalClassRestriction = Expression::FromOQL("`$sClassAlias`.finalclass IN ('$sExpectedClasses')"); $oFinalClassRestriction = Expression::FromOQL("`$sClassAlias`.finalclass IN ('$sExpectedClasses')");
$oQBExpr->AddCondition($oFinalClassRestriction); $oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
} }
// Then we join the queries of the eventual parent classes (compound model) // Then we join the queries of the eventual parent classes (compound model)
@@ -2302,7 +2376,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
if (!self::HasTable($sParentClass)) continue; if (!self::HasTable($sParentClass)) continue;
//echo "<p>Parent class: $sParentClass... let's call MakeQuerySingleTable()</p>"; //echo "<p>Parent class: $sParentClass... let's call MakeQuerySingleTable()</p>";
self::DbgTrace("Parent class: $sParentClass... let's call MakeQuerySingleTable()"); self::DbgTrace("Parent class: $sParentClass... let's call MakeQuerySingleTable()");
$oSelectParentTable = self::MakeQuerySingleTable($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, $sParentClass, $aExtKeys, $aValues); $oSelectParentTable = self::MakeQuerySingleTable($oBuild, $oFilter, $sParentClass, $aExtKeys, $aValues);
if (is_null($oSelectBase)) if (is_null($oSelectBase))
{ {
$oSelectBase = $oSelectParentTable; $oSelectBase = $oSelectParentTable;
@@ -2327,11 +2401,11 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
//self::DbgTrace($oSelectForeign->RenderSelect(array())); //self::DbgTrace($oSelectForeign->RenderSelect(array()));
$sForeignClassAlias = $oForeignFilter->GetFirstJoinedClassAlias(); $sForeignClassAlias = $oForeignFilter->GetFirstJoinedClassAlias();
$oQBExpr->PushJoinField(new FieldExpression($sForeignKeyAttCode, $sForeignClassAlias)); $oBuild->m_oQBExpressions->PushJoinField(new FieldExpression($sForeignKeyAttCode, $sForeignClassAlias));
$oSelectForeign = self::MakeQuery($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oForeignFilter, $aAttToLoad); $oSelectForeign = self::MakeQuery($oBuild, $oForeignFilter, $aAttToLoad);
$oJoinExpr = $oQBExpr->PopJoinField(); $oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField();
$sForeignKeyTable = $oJoinExpr->GetParent(); $sForeignKeyTable = $oJoinExpr->GetParent();
$sForeignKeyColumn = $oJoinExpr->GetName(); $sForeignKeyColumn = $oJoinExpr->GetName();
$oSelectBase->AddInnerJoin($oSelectForeign, $sKeyField, $sForeignKeyColumn, $sForeignKeyTable); $oSelectBase->AddInnerJoin($oSelectForeign, $sKeyField, $sForeignKeyColumn, $sForeignKeyTable);
@@ -2370,8 +2444,8 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
// //
if ($bIsMainQuery) if ($bIsMainQuery)
{ {
$oSelectBase->SetCondition($oQBExpr->GetCondition()); $oSelectBase->SetCondition($oBuild->m_oQBExpressions->GetCondition());
$oSelectBase->SetSelect($oQBExpr->GetSelect()); $oSelectBase->SetSelect($oBuild->m_oQBExpressions->GetSelect());
} }
// That's all... cross fingers and we'll get some working query // That's all... cross fingers and we'll get some working query
@@ -2382,7 +2456,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
return $oSelectBase; return $oSelectBase;
} }
protected static function MakeQuerySingleTable($aSelectedClasses, &$oQBExpr, &$aClassAliases, &$aTableAliases, $oFilter, $sTableClass, $aExtKeys, $aValues) protected static function MakeQuerySingleTable(&$oBuild, $oFilter, $sTableClass, $aExtKeys, $aValues)
{ {
// $aExtKeys is an array of sTableClass => array of (sAttCode (keys) => array of sAttCode (fields)) // $aExtKeys is an array of sTableClass => array of (sAttCode (keys) => array of sAttCode (fields))
//echo "MAKEQUERY($sTableClass)-liste des clefs externes($sTableClass): <pre>".print_r($aExtKeys, true)."</pre><br/>\n"; //echo "MAKEQUERY($sTableClass)-liste des clefs externes($sTableClass): <pre>".print_r($aExtKeys, true)."</pre><br/>\n";
@@ -2396,13 +2470,13 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
$sTargetClass = $oFilter->GetFirstJoinedClass(); $sTargetClass = $oFilter->GetFirstJoinedClass();
$sTargetAlias = $oFilter->GetFirstJoinedClassAlias(); $sTargetAlias = $oFilter->GetFirstJoinedClassAlias();
$sTable = self::DBGetTable($sTableClass); $sTable = self::DBGetTable($sTableClass);
$sTableAlias = self::GenerateUniqueAlias($aTableAliases, $sTargetAlias.'_'.$sTable, $sTable); $sTableAlias = $oBuild->GenerateTableAlias($sTargetAlias.'_'.$sTable, $sTable);
$aTranslation = array(); $aTranslation = array();
$aExpectedAtts = array(); $aExpectedAtts = array();
$oQBExpr->GetUnresolvedFields($sTargetAlias, $aExpectedAtts); $oBuild->m_oQBExpressions->GetUnresolvedFields($sTargetAlias, $aExpectedAtts);
$bIsOnQueriedClass = array_key_exists($sTargetAlias, $aSelectedClasses); $bIsOnQueriedClass = array_key_exists($sTargetAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
self::DbgTrace("Entering: tableclass=$sTableClass, filter=".$oFilter->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY")); self::DbgTrace("Entering: tableclass=$sTableClass, filter=".$oFilter->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
@@ -2451,6 +2525,18 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
$aUpdateValues[$sColumn] = $sValue; $aUpdateValues[$sColumn] = $sValue;
} }
} }
}
// 2 - The SQL query, for this table only
//
$oSelectBase = new SQLQuery($sTable, $sTableAlias, array(), $bIsOnQueriedClass, $aUpdateValues, $oSelectedIdField);
// 3 - Resolve expected expressions (translation table: alias.attcode => table.column)
//
foreach(self::ListAttributeDefs($sTableClass) as $sAttCode=>$oAttDef)
{
// Skip this attribute if not defined in this table
if (self::$m_aAttribOrigins[$sTargetClass][$sAttCode] != $sTableClass) continue;
// Select... // Select...
// //
@@ -2467,16 +2553,17 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
{ {
if (array_key_exists($sAttCode, $aExpectedAtts)) if (array_key_exists($sAttCode, $aExpectedAtts))
{ {
$aTranslation[$sTargetAlias][$sAttCode.$sColId] = new FieldExpressionResolved($sSQLExpr, $sTableAlias); $oFieldSQLExp = new FieldExpressionResolved($sSQLExpr, $sTableAlias);
foreach (MetaModel::EnumPlugins('iQueryModifier') as $sPluginClass => $oQueryModifier)
{
$oFieldSQLExp = $oQueryModifier->GetFieldExpression($oBuild, $sTargetClass, $sAttCode, $sColId, $oFieldSQLExp, $oSelectBase);
}
$aTranslation[$sTargetAlias][$sAttCode.$sColId] = $oFieldSQLExp;
} }
} }
} }
} }
// 3 - The whole stuff, for this table only
//
$oSelectBase = new SQLQuery($sTable, $sTableAlias, array(), $bIsOnQueriedClass, $aUpdateValues, $oSelectedIdField);
//echo "MAKEQUERY- Classe $sTableClass<br/>\n"; //echo "MAKEQUERY- Classe $sTableClass<br/>\n";
// 4 - The external keys -> joins... // 4 - The external keys -> joins...
// //
@@ -2496,7 +2583,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
// The join was not explicitely defined in the filter, // The join was not explicitely defined in the filter,
// we need to do it now // we need to do it now
$sKeyClass = $oKeyAttDef->GetTargetClass(); $sKeyClass = $oKeyAttDef->GetTargetClass();
$sKeyClassAlias = self::GenerateUniqueAlias($aClassAliases, $sKeyClass.'_'.$sKeyAttCode, $sKeyClass); $sKeyClassAlias = $oBuild->GenerateClassAlias($sKeyClass.'_'.$sKeyAttCode, $sKeyClass);
$oExtFilter = new DBObjectSearch($sKeyClass, $sKeyClassAlias); $oExtFilter = new DBObjectSearch($sKeyClass, $sKeyClassAlias);
$aAllPointingTo[$sKeyAttCode][TREE_OPERATOR_EQUALS][$sKeyClassAlias] = $oExtFilter; $aAllPointingTo[$sKeyAttCode][TREE_OPERATOR_EQUALS][$sKeyClassAlias] = $oExtFilter;
@@ -2553,18 +2640,18 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
} }
// Translate prior to recursing // Translate prior to recursing
// //
//echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oQBExpr, true)."\n".print_r($aTranslateNow, true)."</pre></p>\n"; //echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."\n".print_r($aTranslateNow, true)."</pre></p>\n";
$oQBExpr->Translate($aTranslateNow, false); $oBuild->m_oQBExpressions->Translate($aTranslateNow, false);
//echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oQBExpr, true)."</pre></p>\n"; //echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."</pre></p>\n";
//echo "<p>External key $sKeyAttCode (class: $sKeyClass), call MakeQuery()/p>\n"; //echo "<p>External key $sKeyAttCode (class: $sKeyClass), call MakeQuery()/p>\n";
self::DbgTrace("External key $sKeyAttCode (class: $sKeyClass), call MakeQuery()"); self::DbgTrace("External key $sKeyAttCode (class: $sKeyClass), call MakeQuery()");
$oQBExpr->PushJoinField(new FieldExpression('id', $sKeyClassAlias)); $oBuild->m_oQBExpressions->PushJoinField(new FieldExpression('id', $sKeyClassAlias));
//echo "<p>Recursive MakeQuery ".__LINE__.": <pre>\n".print_r($aSelectedClasses, true)."</pre></p>\n"; //echo "<p>Recursive MakeQuery ".__LINE__.": <pre>\n".print_r($oBuild->GetRootFilter()->GetSelectedClasses(), true)."</pre></p>\n";
$oSelectExtKey = self::MakeQuery($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oExtFilter); $oSelectExtKey = self::MakeQuery($oBuild, $oExtFilter);
$oJoinExpr = $oQBExpr->PopJoinField(); $oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField();
$sExternalKeyTable = $oJoinExpr->GetParent(); $sExternalKeyTable = $oJoinExpr->GetParent();
$sExternalKeyField = $oJoinExpr->GetName(); $sExternalKeyField = $oJoinExpr->GetName();
@@ -2584,9 +2671,9 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
} }
elseif(self::$m_aAttribOrigins[$sKeyClass][$sKeyAttCode] == $sTableClass) elseif(self::$m_aAttribOrigins[$sKeyClass][$sKeyAttCode] == $sTableClass)
{ {
$oQBExpr->PushJoinField(new FieldExpression($sKeyAttCode, $sKeyClassAlias)); $oBuild->m_oQBExpressions->PushJoinField(new FieldExpression($sKeyAttCode, $sKeyClassAlias));
$oSelectExtKey = self::MakeQuery($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oExtFilter); $oSelectExtKey = self::MakeQuery($oBuild, $oExtFilter);
$oJoinExpr = $oQBExpr->PopJoinField(); $oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField();
//echo "MAKEQUERY-PopJoinField pour $sKeyAttCode, $sKeyClassAlias: <pre>".print_r($oJoinExpr, true)."</pre><br/>\n"; //echo "MAKEQUERY-PopJoinField pour $sKeyAttCode, $sKeyClassAlias: <pre>".print_r($oJoinExpr, true)."</pre><br/>\n";
$sExternalKeyTable = $oJoinExpr->GetParent(); $sExternalKeyTable = $oJoinExpr->GetParent();
$sExternalKeyField = $oJoinExpr->GetName(); $sExternalKeyField = $oJoinExpr->GetName();
@@ -2605,9 +2692,9 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
// Translate the selected columns // Translate the selected columns
// //
//echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oQBExpr, true)."</pre></p>\n"; //echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."</pre></p>\n";
$oQBExpr->Translate($aTranslation, false); $oBuild->m_oQBExpressions->Translate($aTranslation, false);
//echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oQBExpr, true)."</pre></p>\n"; //echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."</pre></p>\n";
//MyHelpers::var_dump_html($oSelectBase->RenderSelect()); //MyHelpers::var_dump_html($oSelectBase->RenderSelect());
return $oSelectBase; return $oSelectBase;
@@ -3044,7 +3131,36 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
} }
} }
} }
}
// Check unicity of the SQL columns
//
if (self::HasTable($sClass))
{
$aTableColumns = array(); // array of column => attcode (the column is used by this attribute)
$aTableColumns[self::DBGetKey($sClass)] = 'id';
// Check that SQL columns are declared only once
//
foreach(self::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef)
{
// Skip this attribute if not originaly defined in this class
if (self::$m_aAttribOrigins[$sClass][$sAttCode] != $sClass) continue;
foreach($oAttDef->GetSQLColumns() as $sField => $sDBFieldType)
{
if (array_key_exists($sField, $aTableColumns))
{
$aErrors[$sClass][] = "Column '$sField' declared for attribute $sAttCode, but already used for attribute ".$aTableColumns[$sField];
$aSugFix[$sClass][] = "Please find another name for the SQL column";
}
else
{
$aTableColumns[$sField] = $sAttCode;
}
}
}
}
} // foreach class
if (count($aErrors) > 0) if (count($aErrors) > 0)
{ {
@@ -4085,17 +4201,15 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
// Note: load the dictionary as soon as possible, because it might be // Note: load the dictionary as soon as possible, because it might be
// needed when some error occur // needed when some error occur
$sAppIdentity = self::GetConfig()->Get('session_name'); $sAppIdentity = self::GetConfig()->Get('session_name');
$bDictInitializedFromData = false;
if (!self::$m_bUseAPCCache || !Dict::InCache($sAppIdentity)) if (!self::$m_bUseAPCCache || !Dict::InCache($sAppIdentity))
{ {
$bDictInitializedFromData = true;
foreach (self::$m_oConfig->GetDictionaries() as $sModule => $sToInclude) foreach (self::$m_oConfig->GetDictionaries() as $sModule => $sToInclude)
{ {
self::IncludeModule($sConfigFile, 'dictionaries', $sToInclude); self::IncludeModule($sConfigFile, 'dictionaries', $sToInclude);
} }
if (self::$m_bUseAPCCache) }
{
Dict::InitCache($sAppIdentity);
}
}
// Set the language... after the dictionaries have been loaded! // Set the language... after the dictionaries have been loaded!
Dict::SetDefaultLanguage(self::$m_oConfig->GetDefaultLanguage()); Dict::SetDefaultLanguage(self::$m_oConfig->GetDefaultLanguage());
@@ -4199,6 +4313,11 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
} }
} }
if (self::$m_bUseAPCCache && $bDictInitializedFromData)
{
Dict::InitCache($sAppIdentity);
}
self::$m_sDBName = $sSource; self::$m_sDBName = $sSource;
self::$m_sTablePrefix = $sTablePrefix; self::$m_sTablePrefix = $sTablePrefix;
@@ -4258,17 +4377,31 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
{ {
$aRes = array(); $aRes = array();
$iTotalHits = 0; $iTotalHits = 0;
foreach(self::$aQueryCacheGetObjectHits as $sClass => $iHits) foreach(self::$aQueryCacheGetObjectHits as $sClassSign => $iHits)
{ {
$aRes[] = "$sClass: $iHits"; $aRes[] = "$sClassSign: $iHits";
$iTotalHits += $iHits; $iTotalHits += $iHits;
} }
return $iTotalHits.' ('.implode(', ', $aRes).')'; return $iTotalHits.' ('.implode(', ', $aRes).')';
} }
public static function MakeSingleRow($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false) public static function MakeSingleRow($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
{ {
if (!array_key_exists($sClass, self::$aQueryCacheGetObject)) // Build the query cache signature
//
$sQuerySign = $sClass;
if($bAllowAllData)
{
$sQuerySign .= '_all_';
}
if (count($aModifierProperties))
{
array_multisort($aModifierProperties);
$sModifierProperties = json_encode($aModifierProperties);
$sQuerySign .= '_all_'.md5($sModifierProperties);
}
if (!array_key_exists($sQuerySign, self::$aQueryCacheGetObject))
{ {
// NOTE: Quick and VERY dirty caching mechanism which relies on // NOTE: Quick and VERY dirty caching mechanism which relies on
// the fact that the string '987654321' will never appear in the // the fact that the string '987654321' will never appear in the
@@ -4277,20 +4410,30 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
// but this would slow down -by how much time?- the application // but this would slow down -by how much time?- the application
$oFilter = new DBObjectSearch($sClass); $oFilter = new DBObjectSearch($sClass);
$oFilter->AddCondition('id', 987654321, '='); $oFilter->AddCondition('id', 987654321, '=');
if ($aModifierProperties)
{
foreach ($aModifierProperties as $sPluginClass => $aProperties)
{
foreach ($aProperties as $sProperty => $value)
{
$oFilter->SetModifierProperty($sPluginClass, $sProperty, $value);
}
}
}
if ($bAllowAllData) if ($bAllowAllData)
{ {
$oFilter->AllowAllData(); $oFilter->AllowAllData();
} }
$sSQL = self::MakeSelectQuery($oFilter); $sSQL = self::MakeSelectQuery($oFilter);
self::$aQueryCacheGetObject[$sClass] = $sSQL; self::$aQueryCacheGetObject[$sQuerySign] = $sSQL;
self::$aQueryCacheGetObjectHits[$sClass] = 0; self::$aQueryCacheGetObjectHits[$sQuerySign] = 0;
} }
else else
{ {
$sSQL = self::$aQueryCacheGetObject[$sClass]; $sSQL = self::$aQueryCacheGetObject[$sQuerySign];
self::$aQueryCacheGetObjectHits[$sClass] += 1; self::$aQueryCacheGetObjectHits[$sQuerySign] += 1;
// echo " -load $sClass/$iKey- ".self::$aQueryCacheGetObjectHits[$sClass]."<br/>\n"; // echo " -load $sClass/$iKey- ".self::$aQueryCacheGetObjectHits[$sQuerySign]."<br/>\n";
} }
$sSQL = str_replace(CMDBSource::Quote(987654321), CMDBSource::Quote($iKey), $sSQL); $sSQL = str_replace(CMDBSource::Quote(987654321), CMDBSource::Quote($iKey), $sSQL);
$res = CMDBSource::Query($sSQL); $res = CMDBSource::Query($sSQL);
@@ -4336,10 +4479,10 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
return new $sClass($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec); return new $sClass($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec);
} }
public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false) public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
{ {
self::_check_subclass($sClass); self::_check_subclass($sClass);
$aRow = self::MakeSingleRow($sClass, $iKey, $bMustBeFound, $bAllowAllData); $aRow = self::MakeSingleRow($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties);
if (empty($aRow)) if (empty($aRow))
{ {
return null; return null;
@@ -4569,6 +4712,9 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
*/ */
static public function ApplyParams($aInput, $aParams) static public function ApplyParams($aInput, $aParams)
{ {
$aParams['APP_URL'] = utils::GetAbsoluteUrlAppRoot();
$aParams['MODULES_URL'] = utils::GetAbsoluteUrlModulesRoot();
$aSearches = array(); $aSearches = array();
$aReplacements = array(); $aReplacements = array();
foreach($aParams as $sSearch => $replace) foreach($aParams as $sSearch => $replace)
@@ -4583,7 +4729,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass]))
} }
/** /**
* Returns an array of classes implementing the given interface * Returns an array of classes=>instance implementing the given interface
*/ */
public static function EnumPlugins($sInterface) public static function EnumPlugins($sInterface)
{ {
@@ -4668,5 +4814,4 @@ MetaModel::RegisterZList("preview", array("description"=>"All attributes visible
MetaModel::RegisterZList("standard_search", array("description"=>"List of criteria for the standard search", "type"=>"filters")); MetaModel::RegisterZList("standard_search", array("description"=>"List of criteria for the standard search", "type"=>"filters"));
MetaModel::RegisterZList("advanced_search", array("description"=>"List of criteria for the advanced search", "type"=>"filters")); MetaModel::RegisterZList("advanced_search", array("description"=>"List of criteria for the advanced search", "type"=>"filters"));
?> ?>

View File

@@ -0,0 +1,31 @@
<?php
// Copyright (C) 2010 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
// the Free Software Foundation; version 3 of the License.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Any extension to hook the initialization of the metamodel
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
interface iOnClassInitialization
{
public function OnAfterClassInitialization($sClass);
}
?>

View File

@@ -168,7 +168,7 @@ class OQLLexerRaw
'/\GABOVE STRICT/ ', '/\GABOVE STRICT/ ',
'/\GNOT ABOVE/ ', '/\GNOT ABOVE/ ',
'/\GNOT ABOVE STRICT/ ', '/\GNOT ABOVE STRICT/ ',
'/\G[0-9]+|0x[0-9a-fA-F]+/ ', '/\G(0x[0-9a-fA-F]+|[0-9]+)/ ',
'/\G\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/ ', '/\G\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/ ',
'/\G([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/ ', '/\G([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/ ',
'/\G:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/ ', '/\G:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/ ',

View File

@@ -140,7 +140,23 @@ above = "ABOVE"
above_strict = "ABOVE STRICT" above_strict = "ABOVE STRICT"
not_above = "NOT ABOVE" not_above = "NOT ABOVE"
not_above_strict = "NOT ABOVE STRICT" not_above_strict = "NOT ABOVE STRICT"
numval = /[0-9]+|0x[0-9a-fA-F]+/ //
// WARNING: there seems to be a bug in the Lexer about matching the longest pattern
// when there are alternates in the regexp.
//
// For instance:
// numval = /[0-9]+|0x[0-9a-fA-F]+/
// Does not work: SELECT Toto WHERE name = 'Text0xCTest' => Fails because 0xC is recongnized as a numval (inside the string) instead of a strval !!
//
// Inserting a ^ after the alternate (see comment at the top of this file) does not work either
// numval = /[0-9]+|'.chr(94).'0x[0-9a-fA-F]+/
// SELECT Toto WHERE name = 'Text0xCTest' => works but
// SELECT Toto WHERE id = 0xC => does not work, 'xC' is found as a name (apparently 0 is recognized as a numval and the remaining is a name !)
//
// numval = /([0-9]+|0x[0-9a-fA-F]+)/
// Does not work either, the hexadecimal numbers are not matched properly
// The following seems to work...
numval = /(0x[0-9a-fA-F]+|[0-9]+)/
strval = /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/ strval = /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
name = /([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/ 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]*)/ varname = /:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/

View File

@@ -153,16 +153,25 @@ class ormCaseLog {
* Add a new entry to the log and updates the internal index * Add a new entry to the log and updates the internal index
* @param $sText string The text of the new entry * @param $sText string The text of the new entry
*/ */
public function AddLogEntry($sText) public function AddLogEntry($sText, $sOnBehalfOf = '')
{ {
$sDate = date(Dict::S('UI:CaseLog:DateFormat')); $sDate = date(Dict::S('UI:CaseLog:DateFormat'));
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, UserRights::GetUserFriendlyName(), UserRights::GetUserId()); if ($sOnBehalfOf == '')
{
$sOnBehalfOf = UserRights::GetUserFriendlyName();
$iUserId = UserRights::GetUserId();
}
else
{
$iUserId = null;
}
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
$iSepLength = strlen($sSeparator); $iSepLength = strlen($sSeparator);
$iTextlength = strlen($sText); $iTextlength = strlen($sText);
$this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first $this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
$this->m_aIndex[] = array( $this->m_aIndex[] = array(
'user_name' => UserRights::GetUserFriendlyName(), 'user_name' => $sOnBehalfOf,
'user_id' => UserRights::GetUserId(), 'user_id' => $iUserId,
'date' => time(), 'date' => time(),
'text_length' => $iTextlength, 'text_length' => $iTextlength,
'separator_length' => $iSepLength, 'separator_length' => $iSepLength,

View File

@@ -0,0 +1,74 @@
<?php
// Copyright (C) 2010 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
// the Free Software Foundation; version 3 of the License.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Associated with the metamodel -> MakeQuery/MakeQuerySingleTable
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
class QueryBuilderContext
{
protected $m_oRootFilter;
protected $m_aClassAliases;
protected $m_aTableAliases;
protected $m_aModifierProperties;
public $m_oQBExpressions;
public function __construct($oFilter, $aModifierProperties)
{
$this->m_oRootFilter = $oFilter;
$this->m_oQBExpressions = new QueryBuilderExpressions($oFilter->GetCriteria());
$this->m_aClassAliases = $oFilter->GetJoinedClasses();
$this->m_aTableAliases = array();
$this->m_aModifierProperties = $aModifierProperties;
}
public function GetRootFilter()
{
return $this->m_oRootFilter;
}
public function GenerateTableAlias($sNewName, $sRealName)
{
return MetaModel::GenerateUniqueAlias($this->m_aTableAliases, $sNewName, $sRealName);
}
public function GenerateClassAlias($sNewName, $sRealName)
{
return MetaModel::GenerateUniqueAlias($this->m_aClassAliases, $sNewName, $sRealName);
}
public function GetModifierProperties($sPluginClass)
{
if (array_key_exists($sPluginClass, $this->m_aModifierProperties))
{
return $this->m_aModifierProperties[$sPluginClass];
}
else
{
return array();
}
}
}
?>

View File

@@ -0,0 +1,33 @@
<?php
// Copyright (C) 2010 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
// the Free Software Foundation; version 3 of the License.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Interface iQueryModifier
* Defines the API to tweak queries (e.g. translate data on the fly)
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
interface iQueryModifier
{
public function __construct();
public function GetFieldExpression(QueryBuilderContext &$oBuild, $sClass, $sAttCode, $sColId, Expression $oFieldSQLExp, SQLQuery &$oSelect);
}
?>

View File

@@ -68,6 +68,11 @@ class SQLQuery
$this->m_oSelectedIdField = $oSelectedIdField; $this->m_oSelectedIdField = $oSelectedIdField;
} }
public function GetTableAlias()
{
return $this->m_sTableAlias;
}
public function SetSourceOQL($sOQL) public function SetSourceOQL($sOQL)
{ {
$this->m_SourceOQL = $sOQL; $this->m_SourceOQL = $sOQL;
@@ -101,11 +106,20 @@ class SQLQuery
{ {
$sJoinType = $aJoinInfo["jointype"]; $sJoinType = $aJoinInfo["jointype"];
$oSQLQuery = $aJoinInfo["select"]; $oSQLQuery = $aJoinInfo["select"];
$sLeftField = $aJoinInfo["leftfield"]; if (isset($aJoinInfo["on_expression"]))
$sRightField = $aJoinInfo["rightfield"]; {
$sRightTableAlias = $aJoinInfo["righttablealias"]; $sOnCondition = $aJoinInfo["on_expression"]->Render();
echo "<li>Join '$sJoinType', $sLeftField, $sRightTableAlias.$sRightField".$oSQLQuery->DisplayHtml()."</li>\n"; echo "<li>Join '$sJoinType', ON ($sOnCondition)".$oSQLQuery->DisplayHtml()."</li>\n";
}
else
{
$sLeftField = $aJoinInfo["leftfield"];
$sRightField = $aJoinInfo["rightfield"];
$sRightTableAlias = $aJoinInfo["righttablealias"];
echo "<li>Join '$sJoinType', $sLeftField, $sRightTableAlias.$sRightField".$oSQLQuery->DisplayHtml()."</li>\n";
}
} }
echo "</ul>"; echo "</ul>";
} }
@@ -196,6 +210,24 @@ class SQLQuery
{ {
return $this->AddJoin("left", $oSQLQuery, $sLeftField, $sRightField); return $this->AddJoin("left", $oSQLQuery, $sLeftField, $sRightField);
} }
public function AddInnerJoinEx(SQLQuery $oSQLQuery, Expression $oOnExpression)
{
$this->m_aJoinSelects[] = array(
"jointype" => 'inner',
"select" => $oSQLQuery,
"on_expression" => $oOnExpression
);
}
public function AddLeftJoinEx(SQLQuery $oSQLQuery, Expression $oOnExpression)
{
$this->m_aJoinSelects[] = array(
"jointype" => 'left',
"select" => $oSQLQuery,
"on_expression" => $oOnExpression
);
}
// Interface, build the SQL query // Interface, build the SQL query
public function RenderDelete($aArgs = array()) public function RenderDelete($aArgs = array())
@@ -412,8 +444,14 @@ class SQLQuery
break; break;
case "inner": case "inner":
case "left": case "left":
// table or tablealias ??? if (isset($aJoinData["on_expression"]))
$sJoinCond = "`$sCallerAlias`.`{$aJoinData['leftfield']}` = `$sRightTableAlias`.`{$aJoinData['rightfield']}`"; {
$sJoinCond = $aJoinData["on_expression"]->Render();
}
else
{
$sJoinCond = "`$sCallerAlias`.`{$aJoinData['leftfield']}` = `$sRightTableAlias`.`{$aJoinData['rightfield']}`";
}
$aFrom[$this->m_sTableAlias] = array("jointype"=>$aJoinData['jointype'], "tablename"=>$this->m_sTable, "joincondition"=>"$sJoinCond"); $aFrom[$this->m_sTableAlias] = array("jointype"=>$aJoinData['jointype'], "tablename"=>$this->m_sTable, "joincondition"=>"$sJoinCond");
break; break;
case "inner_tree": case "inner_tree":
@@ -488,10 +526,6 @@ class SQLQuery
foreach ($this->m_aJoinSelects as $aJoinData) foreach ($this->m_aJoinSelects as $aJoinData)
{ {
$oRightSelect = $aJoinData["select"]; $oRightSelect = $aJoinData["select"];
// $sJoinType = $aJoinData["jointype"];
// $sLeftField = $aJoinData["leftfield"];
// $sRightField = $aJoinData["rightfield"];
// $sRightTableAlias = $aJoinData["righttablealias"];
$sJoinTableAlias = $oRightSelect->privRenderSingleTable($aTempFrom, $aFields, $aDelTables, $aSetValues, $aSelectedIdFields, $this->m_sTableAlias, $aJoinData); $sJoinTableAlias = $oRightSelect->privRenderSingleTable($aTempFrom, $aFields, $aDelTables, $aSetValues, $aSelectedIdFields, $this->m_sTableAlias, $aJoinData);
} }

View File

@@ -0,0 +1,177 @@
<?php
// Copyright (C) 2010 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
// the Free Software Foundation; version 3 of the License.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Simple helper class to interpret and transform a template string
*
* Usage:
* $oString = new TemplateString("Blah $this->friendlyname$ is in location $this->location_id->name$ ('$this->location_id->org_id->name$)");
* echo $oString->Render(array('this' => $oContact));
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
/**
* Helper class
*/
class TemplateStringPlaceholder
{
public $sToken;
public $sAttCode;
public $sFunction;
public $sParamName;
public $bIsValid;
public function __construct($sToken)
{
$this->sToken = $sToken;
$this->sAttcode = '';
$this->sFunction = '';
$this->sParamName = '';
$this->bIsValid = false; // Validity may be false in general, but it can work anyway (thanks to specialization) when rendering
}
}
/**
* Class TemplateString
*/
class TemplateString
{
protected $m_sRaw;
protected $m_aPlaceholders;
public function __construct($sRaw)
{
$this->m_sRaw = $sRaw;
$this->m_aPlaceholders = null;
}
/**
* Split the string into placholders
* @param Hash $aParamTypes Class of the expected parameters: hash array of '<param_id>' => '<class_name>'
* @return void
*/
protected function Analyze($aParamTypes = array())
{
if (!is_null($this->m_aPlaceholders)) return;
$this->m_aPlaceholders = array();
if (preg_match_all('/\\$([a-z0-9_]+(->[a-z0-9_]+)*)\\$/', $this->m_sRaw, $aMatches))
{
foreach($aMatches[1] as $sPlaceholder)
{
$oPlaceholder = new TemplateStringPlaceholder($sPlaceholder);
$oPlaceholder->bIsValid = false;
foreach ($aParamTypes as $sParamName => $sClass)
{
$sParamPrefix = $sParamName.'->';
if (substr($sPlaceholder, 0, strlen($sParamPrefix)) == $sParamPrefix)
{
// Todo - detect functions (label...)
$oPlaceholder->sFunction = '';
$oPlaceholder->sParamName = $sParamName;
$sAttcode = substr($sPlaceholder, strlen($sParamPrefix));
$oPlaceholder->sAttcode = $sAttcode;
$oPlaceholder->bIsValid = MetaModel::IsValidAttCode($sClass, $sAttcode, true /* extended */);
}
}
$this->m_aPlaceholders[] = $oPlaceholder;
}
}
}
/**
* Return the placeholders (for reporting purposes)
* @return void
*/
public function GetPlaceholders()
{
return $this->m_aPlaceholders;
}
/**
* Check the format when possible
* @param Hash $aParamTypes Class of the expected parameters: hash array of '<param_id>' => '<class_name>'
* @return void
*/
public function IsValid($aParamTypes = array())
{
$this->Analyze($aParamTypes);
foreach($this->m_aPlaceholders as $oPlaceholder)
{
if (!$oPlaceholder->bIsValid)
{
if (count($aParamTypes) == 0)
{
return false;
}
if (array_key_exists($oPlaceholder->sParamName, $aParamTypes))
{
return false;
}
}
}
return true;
}
/**
* Apply the given parameters to replace the placeholders
* @param Hash $aParamValues Value of the expected parameters: hash array of '<param_id>' => '<value>'
* @return void
*/
public function Render($aParamValues = array())
{
$aParamTypes = array();
foreach($aParamValues as $sParamName => $value)
{
$aParamTypes[$sParamName] = get_class($value);
}
$this->Analyze($aParamTypes);
$aSearch = array();
$aReplace = array();
foreach($this->m_aPlaceholders as $oPlaceholder)
{
if (array_key_exists($oPlaceholder->sParamName, $aParamValues))
{
$oRef = $aParamValues[$oPlaceholder->sParamName];
try
{
$value = $oRef->Get($oPlaceholder->sAttcode);
$aSearch[] = '$'.$oPlaceholder->sToken.'$';
$aReplace[] = $value;
$oPlaceholder->bIsValid = true;
}
catch(Exception $e)
{
$oPlaceholder->bIsValid = false;
}
}
else
{
$oPlaceholder->bIsValid = false;
}
}
return str_replace($aSearch, $aReplace, $this->m_sRaw);
}
}
?>

View File

@@ -41,7 +41,7 @@ abstract class Trigger extends cmdbAbstractObject
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "description", "name_attcode" => "description",
"state_attcode" => "", "state_attcode" => "",
"reconc_keys" => array(), "reconc_keys" => array('description'),
"db_table" => "priv_trigger", "db_table" => "priv_trigger",
"db_key_field" => "id", "db_key_field" => "id",
"db_finalclass_field" => "realclass", "db_finalclass_field" => "realclass",
@@ -87,7 +87,7 @@ abstract class TriggerOnObject extends Trigger
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "description", "name_attcode" => "description",
"state_attcode" => "", "state_attcode" => "",
"reconc_keys" => array(), "reconc_keys" => array('description'),
"db_table" => "priv_trigger_onobject", "db_table" => "priv_trigger_onobject",
"db_key_field" => "id", "db_key_field" => "id",
"db_finalclass_field" => "", "db_finalclass_field" => "",
@@ -114,11 +114,11 @@ class TriggerOnPortalUpdate extends TriggerOnObject
{ {
$aParams = array $aParams = array
( (
"category" => "core/cmdb", "category" => "core/cmdb,bizmodel",
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "description", "name_attcode" => "description",
"state_attcode" => "", "state_attcode" => "",
"reconc_keys" => array(), "reconc_keys" => array('description'),
"db_table" => "priv_trigger_onportalupdate", "db_table" => "priv_trigger_onportalupdate",
"db_key_field" => "id", "db_key_field" => "id",
"db_finalclass_field" => "", "db_finalclass_field" => "",
@@ -144,7 +144,7 @@ abstract class TriggerOnStateChange extends TriggerOnObject
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "description", "name_attcode" => "description",
"state_attcode" => "", "state_attcode" => "",
"reconc_keys" => array(), "reconc_keys" => array('description'),
"db_table" => "priv_trigger_onstatechange", "db_table" => "priv_trigger_onstatechange",
"db_key_field" => "id", "db_key_field" => "id",
"db_finalclass_field" => "", "db_finalclass_field" => "",
@@ -173,7 +173,7 @@ class TriggerOnStateEnter extends TriggerOnStateChange
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "description", "name_attcode" => "description",
"state_attcode" => "", "state_attcode" => "",
"reconc_keys" => array(), "reconc_keys" => array('description'),
"db_table" => "priv_trigger_onstateenter", "db_table" => "priv_trigger_onstateenter",
"db_key_field" => "id", "db_key_field" => "id",
"db_finalclass_field" => "", "db_finalclass_field" => "",
@@ -201,7 +201,7 @@ class TriggerOnStateLeave extends TriggerOnStateChange
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "description", "name_attcode" => "description",
"state_attcode" => "", "state_attcode" => "",
"reconc_keys" => array(), "reconc_keys" => array('description'),
"db_table" => "priv_trigger_onstateleave", "db_table" => "priv_trigger_onstateleave",
"db_key_field" => "id", "db_key_field" => "id",
"db_finalclass_field" => "", "db_finalclass_field" => "",
@@ -229,7 +229,7 @@ class TriggerOnObjectCreate extends TriggerOnObject
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "description", "name_attcode" => "description",
"state_attcode" => "", "state_attcode" => "",
"reconc_keys" => array(), "reconc_keys" => array('description'),
"db_table" => "priv_trigger_onobjcreate", "db_table" => "priv_trigger_onobjcreate",
"db_key_field" => "id", "db_key_field" => "id",
"db_finalclass_field" => "", "db_finalclass_field" => "",
@@ -257,7 +257,7 @@ class lnkTriggerAction extends cmdbAbstractObject
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "", "name_attcode" => "",
"state_attcode" => "", "state_attcode" => "",
"reconc_keys" => array(""), "reconc_keys" => array('action_id', 'trigger_id'),
"db_table" => "priv_link_action_trigger", "db_table" => "priv_link_action_trigger",
"db_key_field" => "link_id", "db_key_field" => "link_id",
"db_finalclass_field" => "", "db_finalclass_field" => "",

View File

@@ -55,7 +55,7 @@ abstract class UserRightsAddOnAPI
abstract public function Init(); // loads data (possible optimizations) abstract public function Init(); // loads data (possible optimizations)
// Used to build select queries showing only objects visible for the given user // Used to build select queries showing only objects visible for the given user
abstract public function GetSelectFilter($sLogin, $sClass); // returns a filter object abstract public function GetSelectFilter($sLogin, $sClass, $aSettings = array()); // returns a filter object
abstract public function IsActionAllowed($oUser, $sClass, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null); abstract public function IsActionAllowed($oUser, $sClass, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null);
abstract public function IsStimulusAllowed($oUser, $sClass, $sStimulusCode, /*dbObjectSet*/ $oInstanceSet = null); abstract public function IsStimulusAllowed($oUser, $sClass, $sStimulusCode, /*dbObjectSet*/ $oInstanceSet = null);
@@ -295,6 +295,35 @@ abstract class UserInternal extends User
} }
} }
/**
* Self register extension
*
* @package iTopORM
*/
interface iSelfRegister
{
/**
* Called when no user is found in iTop for the corresponding 'name'. This method
* can create/synchronize the User in iTop with an external source (such as AD/LDAP) on the fly
* @param string $sName The typed-in user name
* @param string $sPassword The typed-in password
* @param string $sLoginMode The login method used (cas|form|basic|url)
* @param string $sAuthentication The authentication method used (any|internal|external)
* @return bool true if the user is a valid one, false otherwise
*/
public static function CheckCredentialsAndCreateUser($sName, $sPassword, $sLoginMode, $sAuthentication);
/**
* Called after the user has been authenticated and found in iTop. This method can
* Update the user's definition on the fly (profiles...) to keep it in sync with an external source
* @param User $oUser The user to update/synchronize
* @param string $sLoginMode The login mode used (cas|form|basic|url)
* @param string $sAuthentication The authentication method used
* @return void
*/
public static function UpdateUser(User $oUser, $sLoginMode, $sAuthentication);
}
/** /**
* User management core API * User management core API
* *
@@ -305,6 +334,7 @@ class UserRights
protected static $m_oAddOn; protected static $m_oAddOn;
protected static $m_oUser; protected static $m_oUser;
protected static $m_oRealUser; protected static $m_oRealUser;
protected static $m_sSelfRegisterAddOn = null;
public static function SelectModule($sModuleName) public static function SelectModule($sModuleName)
{ {
@@ -324,6 +354,15 @@ class UserRights
self::$m_oRealUser = null; self::$m_oRealUser = null;
} }
public static function SelectSelfRegister($sModuleName)
{
if (!class_exists($sModuleName))
{
throw new CoreException("Could not select the class, '$sModuleName' for self register, is not a valid class name");
}
self::$m_sSelfRegisterAddOn = $sModuleName;
}
public static function GetModuleInstance() public static function GetModuleInstance()
{ {
return self::$m_oAddOn; return self::$m_oAddOn;
@@ -361,21 +400,38 @@ class UserRights
return true; return true;
} }
public static function CheckCredentials($sName, $sPassword, $sAuthentication = 'any') public static function CheckCredentials($sName, $sPassword, $sLoginMode = 'form', $sAuthentication = 'any')
{ {
$oUser = self::FindUser($sName, $sAuthentication); $oUser = self::FindUser($sName, $sAuthentication);
if (is_null($oUser)) if (is_null($oUser))
{ {
return false; return self::CheckCredentialsAndCreateUser($sName, $sPassword, $sLoginMode, $sAuthentication);
} }
if (!$oUser->CheckCredentials($sPassword)) if (!$oUser->CheckCredentials($sPassword))
{ {
return false; return false;
} }
self::UpdateUser($oUser, $sLoginMode, $sAuthentication);
return true; return true;
} }
public static function CheckCredentialsAndCreateUser($sName, $sPassword, $sLoginMode, $sAuthentication)
{
if (self::$m_sSelfRegisterAddOn != null)
{
return call_user_func(array(self::$m_sSelfRegisterAddOn, 'CheckCredentialsAndCreateUser'), $sName, $sPassword, $sLoginMode, $sAuthentication);
}
}
public static function UpdateUser($oUser, $sLoginMode, $sAuthentication)
{
if (self::$m_sSelfRegisterAddOn != null)
{
call_user_func(array(self::$m_sSelfRegisterAddOn, 'UpdateUser'), $oUser, $sLoginMode, $sAuthentication);
}
}
public static function TrustWebServerContext() public static function TrustWebServerContext()
{ {
if (!is_null(self::$m_oUser)) if (!is_null(self::$m_oUser))
@@ -591,7 +647,7 @@ class UserRights
return true; return true;
} }
public static function GetSelectFilter($sClass) public static function GetSelectFilter($sClass, $aSettings = array())
{ {
// When initializing, we need to let everything pass trough // When initializing, we need to let everything pass trough
if (!self::CheckLogin()) return true; if (!self::CheckLogin()) return true;
@@ -600,7 +656,7 @@ class UserRights
if (MetaModel::HasCategory($sClass, 'bizmodel')) if (MetaModel::HasCategory($sClass, 'bizmodel'))
{ {
return self::$m_oAddOn->GetSelectFilter(self::$m_oUser, $sClass); return self::$m_oAddOn->GetSelectFilter(self::$m_oUser, $sClass, $aSettings);
} }
else else
{ {
@@ -625,10 +681,6 @@ class UserRights
if (MetaModel::HasCategory($sClass, 'bizmodel')) if (MetaModel::HasCategory($sClass, 'bizmodel'))
{ {
// #@# Temporary?????
// The read access is controlled in MetaModel::MakeSelectQuery()
if ($iActionCode == UR_ACTION_READ) return true;
if (is_null($oUser)) if (is_null($oUser))
{ {
$oUser = self::$m_oUser; $oUser = self::$m_oUser;
@@ -969,4 +1021,339 @@ class StimulusChecker extends ActionChecker
return $this->iState; return $this->iState;
} }
} }
/**
* Self-register extension to allow the automatic creation & update of CAS users
*
* @package iTopORM
*
*/
class CAS_SelfRegister implements iSelfRegister
{
/**
* Called when no user is found in iTop for the corresponding 'name'. This method
* can create/synchronize the User in iTop with an external source (such as AD/LDAP) on the fly
* @param string $sName The CAS authenticated user name
* @param string $sPassword Ignored
* @param string $sLoginMode The login mode used (cas|form|basic|url)
* @param string $sAuthentication The authentication method used
* @return bool true if the user is a valid one, false otherwise
*/
public static function CheckCredentialsAndCreateUser($sName, $sPassword, $sLoginMode, $sAuthentication)
{
$bOk = true;
if ($sLoginMode != 'cas') return false; // Must be authenticated via CAS
$sCASMemberships = MetaModel::GetConfig()->Get('cas_memberof');
$bFound = false;
if (!empty($sCASMemberships))
{
if (phpCAS::hasAttribute('memberOf'))
{
// A list of groups is specified, the user must a be member of (at least) one of them to pass
$aCASMemberships = array();
$aTmp = explode(';', $sCASMemberships);
setlocale(LC_ALL, "en_US.utf8"); // !!! WARNING: this is needed to have the iconv //TRANSLIT working fine below !!!
foreach($aTmp as $sGroupName)
{
$aCASMemberships[] = trim(iconv('UTF-8', 'ASCII//TRANSLIT', $sGroupName)); // Just in case remove accents and spaces...
}
$aMemberOf = phpCAS::getAttribute('memberOf');
if (!is_array($aMemberOf)) $aMemberOf = array($aMemberOf); // Just one entry, turn it into an array
$aFilteredGroupNames = array();
foreach($aMemberOf as $sGroupName)
{
phpCAS::log("Info: user if a member of the group: ".$sGroupName);
$sGroupName = trim(iconv('UTF-8', 'ASCII//TRANSLIT', $sGroupName)); // Remove accents and spaces as well
$aFilteredGroupNames[] = $sGroupName;
$bIsMember = false;
foreach($aCASMemberships as $sCASPattern)
{
if (self::IsPattern($sCASPattern))
{
if (preg_match($sCASPattern, $sGroupName))
{
$bIsMember = true;
break;
}
}
else if ($sPattern == $sGroupName)
{
$bIsMember = true;
break;
}
}
if ($bIsMember)
{
$bCASUserSynchro = MetaModel::GetConfig()->Get('cas_user_synchro');
if ($bCASUserSynchro)
{
// If needed create a new user for this email/profile
phpCAS::log('Info: cas_user_synchro is ON');
$bOk = self::CreateCASUser(phpCAS::getUser(), $aMemberOf);
if($bOk)
{
$bFound = true;
}
else
{
phpCAS::log("User ".phpCAS::getUser()." cannot be created in iTop. Logging off...");
}
}
else
{
phpCAS::log('Info: cas_user_synchro is OFF');
$bFound = true;
}
break;
}
}
if($bOk && !$bFound)
{
phpCAS::log("User ".phpCAS::getUser().", none of his/her groups (".implode('; ', $aFilteredGroupNames).") match any of the required groups: ".implode('; ', $aCASMemberships));
}
}
else
{
// Too bad, the user is not part of any of the group => not allowed
phpCAS::log("No 'memberOf' attribute found for user ".phpCAS::getUser().". Are you using the SAML protocol (S1) ?");
}
}
else
{
// No membership required, anybody will pass
$bFound = true;
}
if (!$bFound)
{
// The user is not part of the allowed groups, => log out
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php';
$sCASLogoutUrl = MetaModel::GetConfig()->Get('cas_logout_redirect_service');
if (empty($sCASLogoutUrl))
{
$sCASLogoutUrl = $sUrl;
}
phpCAS::logoutWithRedirectService($sCASLogoutUrl); // Redirects to the CAS logout page
// Will never return !
}
return $bFound;
}
/**
* Called after the user has been authenticated and found in iTop. This method can
* Update the user's definition (profiles...) on the fly to keep it in sync with an external source
* @param User $oUser The user to update/synchronize
* @param string $sLoginMode The login mode used (cas|form|basic|url)
* @param string $sAuthentication The authentication method used
* @return void
*/
public static function UpdateUser(User $oUser, $sLoginMode, $sAuthentication)
{
$bCASUpdateProfiles = MetaModel::GetConfig()->Get('cas_update_profiles');
if (($sLoginMode == 'cas') && $bCASUpdateProfiles && (phpCAS::hasAttribute('memberOf')))
{
$aMemberOf = phpCAS::getAttribute('memberOf');
if (!is_array($aMemberOf)) $aMemberOf = array($aMemberOf); // Just one entry, turn it into an array
return self::SetProfilesFromCAS($oUser, $aMemberOf);
}
// No groups defined in CAS or not CAS at all: do nothing...
return true;
}
/**
* Helper method to create a CAS based user
* @param string $sEmail
* @param array $aGroups
* @return bool true on success, false otherwise
*/
protected static function CreateCASUser($sEmail, $aGroups)
{
if (!MetaModel::IsValidClass('URP_Profiles'))
{
phpCAS::log("URP_Profiles is not a valid class. Automatic creation of Users is not supported in this context, sorry.");
return false;
}
$oUser = MetaModel::GetObjectByName('UserExternal', $sEmail, false);
if ($oUser == null)
{
// Create the user, link it to a contact
phpCAS::log("Info: the user '$sEmail' does not exist. A new UserExternal will be created.");
$oSearch = new DBObjectSearch('Person');
$oSearch->AddCondition('email', $sEmail);
$oSet = new DBObjectSet($oSearch);
$iContactId = 0;
switch($oSet->Count())
{
case 0:
phpCAS::log("Error: found no contact with the email: '$sEmail'. Cannot create the user in iTop.");
return false;
case 1:
$oContact = $oSet->Fetch();
$iContactId = $oContact->GetKey();
phpCAS::log("Info: Found 1 contact '".$oContact->GetName()."' (id=$iContactId) corresponding to the email '$sEmail'.");
break;
default:
phpCAS::log("Error: ".$oSet->Count()." contacts have the same email: '$sEmail'. Cannot create a user for this email.");
return false;
}
$oUser = new UserExternal();
$oUser->Set('login', $sEmail);
$oUser->Set('contactid', $iContactId);
$oUser->Set('language', MetaModel::GetConfig()->GetDefaultLanguage());
}
else
{
phpCAS::log("Info: the user '$sEmail' already exists (id=".$oUser->GetKey().").");
}
// Now synchronize the profiles
if (!self::SetProfilesFromCAS($oUser, $aGroups))
{
return false;
}
else
{
if ($oUser->IsNew() || $oUser->IsModified())
{
$oMyChange = MetaModel::NewObject("CMDBChange");
$oMyChange->Set("date", time());
$oMyChange->Set("userinfo", 'CAS/LDAP Synchro');
$oMyChange->DBInsert();
if ($oUser->IsNew())
{
$oUser->DBInsertTracked($oMyChange);
}
else
{
$oUser->DBUpdateTracked($oMyChange);
}
}
return true;
}
}
protected static function SetProfilesFromCAS($oUser, $aGroups)
{
if (!MetaModel::IsValidClass('URP_Profiles'))
{
phpCAS::log("URP_Profiles is not a valid class. Automatic creation of Users is not supported in this context, sorry.");
return false;
}
// read all the existing profiles
$oProfilesSearch = new DBObjectSearch('URP_Profiles');
$oProfilesSet = new DBObjectSet($oProfilesSearch);
$aAllProfiles = array();
while($oProfile = $oProfilesSet->Fetch())
{
$aAllProfiles[strtolower($oProfile->GetName())] = $oProfile->GetKey();
}
// Translate the CAS/LDAP group names into iTop profile names
$aProfiles = array();
$sPattern = MetaModel::GetConfig()->Get('cas_profile_pattern');
foreach($aGroups as $sGroupName)
{
if (preg_match($sPattern, $sGroupName, $aMatches))
{
if (array_key_exists(strtolower($aMatches[1]), $aAllProfiles))
{
$aProfiles[] = $aAllProfiles[strtolower($aMatches[1])];
phpCAS::log("Info: Adding the profile '{$aMatches[1]}' from CAS.");
}
else
{
phpCAS::log("Warning: {$aMatches[1]} is not a valid iTop profile (extracted from group name: '$sGroupName'). Ignored.");
}
}
else
{
phpCAS::log("Info: The CAS group '$sGroupName' does not seem to match an iTop pattern. Ignored.");
}
}
if (count($aProfiles) == 0)
{
phpCAS::log("Info: The user '".$oUser->GetName()."' has no profiles retrieved from CAS. Default profile(s) will be used.");
// Second attempt: check if there is/are valid default profile(s)
$sCASDefaultProfiles = MetaModel::GetConfig()->Get('cas_default_profiles');
$aCASDefaultProfiles = explode(';', $sCASDefaultProfiles);
foreach($aCASDefaultProfiles as $sDefaultProfileName)
{
if (array_key_exists(strtolower($sDefaultProfileName), $aAllProfiles))
{
$aProfiles[] = $aAllProfiles[strtolower($sDefaultProfileName)];
phpCAS::log("Info: Adding the default profile '".$aAllProfiles[strtolower($sDefaultProfileName)]."' from CAS.");
}
else
{
phpCAS::log("Warning: the default profile {$sDefaultProfileName} is not a valid iTop profile. Ignored.");
}
}
if (count($aProfiles) == 0)
{
phpCAS::log("Error: The user '".$oUser->GetName()."' has no profiles in iTop, and therefore cannot be created.");
return false;
}
}
// Now synchronize the profiles
$oProfilesSet = DBObjectSet::FromScratch('URP_UserProfile');
foreach($aProfiles as $iProfileId)
{
$oLink = new URP_UserProfile();
$oLink->Set('profileid', $iProfileId);
$oLink->Set('reason', 'CAS/LDAP Synchro');
$oProfilesSet->AddObject($oLink);
}
$oUser->Set('profile_list', $oProfilesSet);
phpCAS::log("Info: the user '".$oUser->GetName()."' (id=".$oUser->GetKey().") now has the following profiles: '".implode("', '", $aProfiles)."'.");
if ($oUser->IsModified())
{
$oMyChange = MetaModel::NewObject("CMDBChange");
$oMyChange->Set("date", time());
$oMyChange->Set("userinfo", 'CAS/LDAP Synchro');
$oMyChange->DBInsert();
if ($oUser->IsNew())
{
$oUser->DBInsertTracked($oMyChange);
}
else
{
$oUser->DBUpdateTracked($oMyChange);
}
}
return true;
}
/**
* Helper function to check if the supplied string is a litteral string or a regular expression pattern
* @param string $sCASPattern
* @return bool True if it's a regular expression pattern, false otherwise
*/
protected static function IsPattern($sCASPattern)
{
if ((substr($sCASPattern, 0, 1) == '/') && (substr($sCASPattern, -1) == '/'))
{
// the string is enclosed by slashes, let's assume it's a pattern
return true;
}
else
{
return false;
}
}
}
// By default enable the 'CAS_SelfRegister' defined above
UserRights::SelectSelfRegister('CAS_SelfRegister');
?> ?>

View File

@@ -97,17 +97,24 @@ class ValueSetObjects extends ValueSetDefinition
protected $m_aOrderBy; protected $m_aOrderBy;
protected $m_aExtraConditions; protected $m_aExtraConditions;
private $m_bAllowAllData; private $m_bAllowAllData;
private $m_aModifierProperties;
public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array(), $bAllowAllData = false) public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array(), $bAllowAllData = false, $aModifierProperties = array())
{ {
$this->m_sContains = ''; $this->m_sContains = '';
$this->m_sFilterExpr = $sFilterExp; $this->m_sFilterExpr = $sFilterExp;
$this->m_sValueAttCode = $sValueAttCode; $this->m_sValueAttCode = $sValueAttCode;
$this->m_aOrderBy = $aOrderBy; $this->m_aOrderBy = $aOrderBy;
$this->m_bAllowAllData = $bAllowAllData; $this->m_bAllowAllData = $bAllowAllData;
$this->m_aModifierProperties = $aModifierProperties;
$this->m_aExtraConditions = array(); $this->m_aExtraConditions = array();
} }
public function SetModifierProperty($sPluginClass, $sProperty, $value)
{
$this->m_aModifierProperties[$sPluginClass][$sProperty] = $value;
}
public function AddCondition(DBObjectSearch $oFilter) public function AddCondition(DBObjectSearch $oFilter)
{ {
$this->m_aExtraConditions[] = $oFilter; $this->m_aExtraConditions[] = $oFilter;
@@ -127,6 +134,13 @@ class ValueSetObjects extends ValueSetDefinition
{ {
$oFilter->MergeWith($oExtraFilter); $oFilter->MergeWith($oExtraFilter);
} }
foreach($this->m_aModifierProperties as $sPluginClass => $aProperties)
{
foreach ($aProperties as $sProperty => $value)
{
$oFilter->SetModifierProperty($sPluginClass, $sProperty, $value);
}
}
return new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs); return new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs);
} }
@@ -162,6 +176,13 @@ class ValueSetObjects extends ValueSetDefinition
{ {
$oFilter->MergeWith($oExtraFilter); $oFilter->MergeWith($oExtraFilter);
} }
foreach($this->m_aModifierProperties as $sPluginClass => $aProperties)
{
foreach ($aProperties as $sProperty => $value)
{
$oFilter->SetModifierProperty($sPluginClass, $sProperty, $value);
}
}
$oValueExpr = new ScalarExpression('%'.$sContains.'%'); $oValueExpr = new ScalarExpression('%'.$sContains.'%');
$oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias()); $oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias());
@@ -187,6 +208,11 @@ class ValueSetObjects extends ValueSetDefinition
{ {
return 'Filter: '.$this->m_sFilterExpr; return 'Filter: '.$this->m_sFilterExpr;
} }
public function GetFilterExpression()
{
return $this->m_sFilterExpr;
}
} }
@@ -278,6 +304,13 @@ class ValueSetEnum extends ValueSetDefinition
$this->m_values = $Values; $this->m_values = $Values;
} }
// Helper to export the datat model
public function GetValueList()
{
$this->LoadValues($aArgs = array());
return $this->m_aValues;
}
protected function LoadValues($aArgs) protected function LoadValues($aArgs)
{ {
if (is_array($this->m_values)) if (is_array($this->m_values))

View File

@@ -2,7 +2,6 @@
body { body {
font-family: Tahoma, Verdana, Arial, Helvetica; font-family: Tahoma, Verdana, Arial, Helvetica;
font-size: 10pt; font-size: 10pt;
background-color: #fff;
color:#000000; color:#000000;
margin: 0; /* Remove body margin/padding */ margin: 0; /* Remove body margin/padding */
padding: 0; padding: 0;
@@ -60,6 +59,7 @@ table.listContainer {
padding: 0; padding: 0;
margin:0; margin:0;
width: 97%; width: 97%;
clear: both;
} }
tr.containerHeader, tr.containerHeader td { tr.containerHeader, tr.containerHeader td {
@@ -176,6 +176,10 @@ legend {
-webkit-border-radius: 6px; -webkit-border-radius: 6px;
border-radius: 6px; border-radius: 6px;
} }
.ui-widget-content td legend a, .ui-widget-content td legend a:hover, .ui-widget-content td legend a:visited {
color: #fff;
}
.ui-widget-content td a, p a, p a:visited, td a, td a:visited { .ui-widget-content td a, p a, p a:visited, td a, td a:visited {
text-decoration:none; text-decoration:none;
color: #1C94C4; color: #1C94C4;
@@ -1104,4 +1108,7 @@ div.actions_button a, .actions_button a:hover, .actions_button a:visited {
height:17px; height:17px;
line-height: 17px; line-height: 17px;
display: block; display: block;
}
select#org_id {
max-width: 90%;
} }

15
css/print.css Normal file
View File

@@ -0,0 +1,15 @@
@CHARSET "UTF-8";
#left-pane { display: none; }
span.ui-layout-resizer { display: none; }
#header-logo { display: none; }
#logo { display: none; }
div.header-menu { display:none; }
div.footer { display:none; }
#top-bar { display: none; }
#menu { display: none; }
div.actions_button { display:none; }
div.itop_popup { display:none; }
div.HRDrawer { display:none; }
div.DrawerHandle { display:none; }
a.tab { display:none; }
div.itop-tab { border: #ccc 1px solid; margin-top: 1em; padding-bottom:1em; }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -140,6 +140,12 @@ Operators:<br/>
'Core:AttributePropertySet' => 'Properties', 'Core:AttributePropertySet' => 'Properties',
'Core:AttributePropertySet+' => 'List of untyped properties (name and value)', 'Core:AttributePropertySet+' => 'List of untyped properties (name and value)',
'Core:AttributeFriendlyName' => 'Friendly name',
'Core:AttributeFriendlyName+' => 'Attribute created automatically ; the friendly name is computed after several attributes',
'Core:FriendlyName-Label' => 'Friendly name',
'Core:FriendlyName-Description' => 'Friendly name',
)); ));
@@ -564,6 +570,8 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:SynchroDataSource/Attribute:delete_policy_update+' => 'Syntax: field_name:value; ...', 'Class:SynchroDataSource/Attribute:delete_policy_update+' => 'Syntax: field_name:value; ...',
'Class:SynchroDataSource/Attribute:delete_policy_retention' => 'Retention Duration', 'Class:SynchroDataSource/Attribute:delete_policy_retention' => 'Retention Duration',
'Class:SynchroDataSource/Attribute:delete_policy_retention+' => 'How much time an obsolete object is kept before being deleted', 'Class:SynchroDataSource/Attribute:delete_policy_retention+' => 'How much time an obsolete object is kept before being deleted',
'Class:SynchroDataSource/Attribute:database_table_name' => 'Data table',
'Class:SynchroDataSource/Attribute:database_table_name+' => 'Name of the table to store the synchronization data. If left empty, a default name will be computed.',
'SynchroDataSource:Description' => 'Description', 'SynchroDataSource:Description' => 'Description',
'SynchroDataSource:Reconciliation' => 'Search &amp; reconciliation', 'SynchroDataSource:Reconciliation' => 'Search &amp; reconciliation',
'SynchroDataSource:Deletion' => 'Deletion rules', 'SynchroDataSource:Deletion' => 'Deletion rules',
@@ -587,7 +595,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Core:Synchro:History' => 'Synchronization History', 'Core:Synchro:History' => 'Synchronization History',
'Core:Synchro:NeverRun' => 'This synchro was never run. No log yet.', 'Core:Synchro:NeverRun' => 'This synchro was never run. No log yet.',
'Core:Synchro:SynchroEndedOn_Date' => 'The latest synchronization ended on %1$s.', 'Core:Synchro:SynchroEndedOn_Date' => 'The latest synchronization ended on %1$s.',
'Core:Synchro:SynchroRunningStartedOn_Date' => 'The synchronization started on $1$s is still running...', 'Core:Synchro:SynchroRunningStartedOn_Date' => 'The synchronization started on %1$s is still running...',
'Menu:DataSources' => 'Synchronization Data Sources', 'Menu:DataSources' => 'Synchronization Data Sources',
'Menu:DataSources+' => 'All Synchronization Data Sources', 'Menu:DataSources+' => 'All Synchronization Data Sources',
'Core:Synchro:label_repl_ignored' => 'Ignored (%1$s)', 'Core:Synchro:label_repl_ignored' => 'Ignored (%1$s)',
@@ -612,6 +620,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:SynchroDataSource/Error:AtLeastOneReconciliationKeyMustBeSpecified' => 'At Least one reconciliation key must be specified, or the reconciliation policy must be to use the primary key.', 'Class:SynchroDataSource/Error:AtLeastOneReconciliationKeyMustBeSpecified' => 'At Least one reconciliation key must be specified, or the reconciliation policy must be to use the primary key.',
'Class:SynchroDataSource/Error:DeleteRetentionDurationMustBeSpecified' => 'A delete retention period must be specified, since objects are to be deleted after being marked as obsolete', 'Class:SynchroDataSource/Error:DeleteRetentionDurationMustBeSpecified' => 'A delete retention period must be specified, since objects are to be deleted after being marked as obsolete',
'Class:SynchroDataSource/Error:DeletePolicyUpdateMustBeSpecified' => 'Obsolete objects are to be updated, but no update is specified.', 'Class:SynchroDataSource/Error:DeletePolicyUpdateMustBeSpecified' => 'Obsolete objects are to be updated, but no update is specified.',
'Class:SynchroDataSource/Error:DataTableAlreadyExists' => 'The table %1$s already exists in the database. Please use another name for the synchro data table.',
'Core:SynchroReplica:PublicData' => 'Public Data', 'Core:SynchroReplica:PublicData' => 'Public Data',
'Core:SynchroReplica:PrivateDetails' => 'Private Details', 'Core:SynchroReplica:PrivateDetails' => 'Private Details',
'Core:SynchroReplica:BackToDataSource' => 'Go Back to the Synchro Data Source: %1$s', 'Core:SynchroReplica:BackToDataSource' => 'Go Back to the Synchro Data Source: %1$s',
@@ -639,7 +648,9 @@ Dict::Add('EN US', 'English', 'English', array(
'Core:SyncDataSourceObsolete' => 'The data source is marked as obsolete. Operation cancelled.', 'Core:SyncDataSourceObsolete' => 'The data source is marked as obsolete. Operation cancelled.',
'Core:SyncDataSourceAccessRestriction' => 'Only adminstrators or the user specified in the data source can execute this operation. Operation cancelled.', 'Core:SyncDataSourceAccessRestriction' => 'Only adminstrators or the user specified in the data source can execute this operation. Operation cancelled.',
'Core:SyncTooManyMissingReplicas' => 'All records have been untouched for some time (all of the objects could be deleted). Please check that the process that writes into the synchronization table is still running. Operation cancelled.', 'Core:SyncTooManyMissingReplicas' => 'All records have been untouched for some time (all of the objects could be deleted). Please check that the process that writes into the synchronization table is still running. Operation cancelled.',
'Core:SyncSplitModeCLIOnly' => 'The synchronization can be executed in chunks only if run in mode CLI',
'Core:Synchro:ListReplicas_AllReplicas_Errors_Warnings' => '%1$s replicas, %2$s error(s), %3$s warning(s).',
'Core:SynchroReplica:TargetObject' => 'Synchronized Object: %1$s',
'Class:AsyncSendEmail' => 'Email (asynchronous)', 'Class:AsyncSendEmail' => 'Email (asynchronous)',
'Class:AsyncSendEmail/Attribute:to' => 'To', 'Class:AsyncSendEmail/Attribute:to' => 'To',
'Class:AsyncSendEmail/Attribute:subject' => 'Subject', 'Class:AsyncSendEmail/Attribute:subject' => 'Subject',
@@ -717,7 +728,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:SynchroReplica/Attribute:sync_source_id' => 'Synchro Data Source', 'Class:SynchroReplica/Attribute:sync_source_id' => 'Synchro Data Source',
'Class:SynchroReplica/Attribute:dest_id' => 'Destination object (ID)', 'Class:SynchroReplica/Attribute:dest_id' => 'Destination object (ID)',
'Class:SynchroReplica/Attribute:dest_class' => 'Destination type', 'Class:SynchroReplica/Attribute:dest_class' => 'Destination type',
'Class:SynchroReplica/Attribute:status_last_seen' => 'Lat seen', 'Class:SynchroReplica/Attribute:status_last_seen' => 'Last seen',
'Class:SynchroReplica/Attribute:status' => 'Status', 'Class:SynchroReplica/Attribute:status' => 'Status',
'Class:SynchroReplica/Attribute:status/Value:modified' => 'Modified', 'Class:SynchroReplica/Attribute:status/Value:modified' => 'Modified',
'Class:SynchroReplica/Attribute:status/Value:new' => 'New', 'Class:SynchroReplica/Attribute:status/Value:new' => 'New',
@@ -726,11 +737,14 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:SynchroReplica/Attribute:status/Value:synchronized' => 'Synchronized', 'Class:SynchroReplica/Attribute:status/Value:synchronized' => 'Synchronized',
'Class:SynchroReplica/Attribute:status_dest_creator' => 'Object Created ?', 'Class:SynchroReplica/Attribute:status_dest_creator' => 'Object Created ?',
'Class:SynchroReplica/Attribute:status_last_error' => 'Last Error', 'Class:SynchroReplica/Attribute:status_last_error' => 'Last Error',
'Class:SynchroReplica/Attribute:status_last_warning' => 'Warnings',
'Class:SynchroReplica/Attribute:info_creation_date' => 'Creation Date', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Creation Date',
'Class:SynchroReplica/Attribute:info_last_modified' => 'Last Modified Date', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Last Modified Date',
'Class:appUserPreferences' => 'User Preferences', 'Class:appUserPreferences' => 'User Preferences',
'Class:appUserPreferences/Attribute:userid' => 'User', 'Class:appUserPreferences/Attribute:userid' => 'User',
'Class:appUserPreferences/Attribute:preferences' => 'Prefs', 'Class:appUserPreferences/Attribute:preferences' => 'Prefs',
'Core:ExecProcess:Code1' => 'Wrong command or command finished with errors (e.g. wrong script name)',
'Core:ExecProcess:Code255' => 'PHP Error (parsing, or runtime)',
)); ));
// //

View File

@@ -76,6 +76,26 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:AuditRule/Attribute:category_name+' => 'Name of the category for this rule', 'Class:AuditRule/Attribute:category_name+' => 'Name of the category for this rule',
)); ));
//
// Class: QueryOQL
//
Dict::Add('EN US', 'English', 'English', array(
'Class:Query' => 'Query',
'Class:Query+' => 'A query is a data set defined in a dynamic way',
'Class:Query/Attribute:name' => 'Name',
'Class:Query/Attribute:name+' => 'Identifies the query',
'Class:Query/Attribute:description' => 'Description',
'Class:Query/Attribute:description+' => 'Long description for the query (purpose, usage, etc.)',
'Class:Query/Attribute:fields' => 'Fields',
'Class:Query/Attribute:fields+' => 'Coma separated list of attributes (or alias.attribute) to export',
'Class:QueryOQL' => 'OQL Query',
'Class:QueryOQL+' => 'A query based on the Object Query Language',
'Class:QueryOQL/Attribute:oql' => 'Expression',
'Class:QueryOQL/Attribute:oql+' => 'OQL Expression',
));
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Classes in 'addon/userrights' // Classes in 'addon/userrights'
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@@ -429,6 +449,8 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:UndefinedObject' => 'undefined', 'UI:UndefinedObject' => 'undefined',
'UI:Document:OpenInNewWindow:Download' => 'Open in new window: %1$s, Download: %2$s', 'UI:Document:OpenInNewWindow:Download' => 'Open in new window: %1$s, Download: %2$s',
'UI:SelectAllToggle+' => 'Select / Deselect All', 'UI:SelectAllToggle+' => 'Select / Deselect All',
'UI:SplitDateTime-Date' => 'date',
'UI:SplitDateTime-Time' => 'time',
'UI:TruncatedResults' => '%1$d objects displayed out of %2$d', 'UI:TruncatedResults' => '%1$d objects displayed out of %2$d',
'UI:DisplayAll' => 'Display All', 'UI:DisplayAll' => 'Display All',
'UI:CollapseList' => 'Collapse', 'UI:CollapseList' => 'Collapse',
@@ -448,6 +470,7 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:Login:IdentifyYourself' => 'Identify yourself before continuing', 'UI:Login:IdentifyYourself' => 'Identify yourself before continuing',
'UI:Login:UserNamePrompt' => 'User Name', 'UI:Login:UserNamePrompt' => 'User Name',
'UI:Login:PasswordPrompt' => 'Password', 'UI:Login:PasswordPrompt' => 'Password',
'UI:Login:About' => '',
'UI:Login:ChangeYourPassword' => 'Change Your Password', 'UI:Login:ChangeYourPassword' => 'Change Your Password',
'UI:Login:OldPasswordPrompt' => 'Old password', 'UI:Login:OldPasswordPrompt' => 'Old password',
'UI:Login:NewPasswordPrompt' => 'New password', 'UI:Login:NewPasswordPrompt' => 'New password',
@@ -532,7 +555,9 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:Audit:HeaderNbObjects' => '# Objects', 'UI:Audit:HeaderNbObjects' => '# Objects',
'UI:Audit:HeaderNbErrors' => '# Errors', 'UI:Audit:HeaderNbErrors' => '# Errors',
'UI:Audit:PercentageOk' => '% Ok', 'UI:Audit:PercentageOk' => '% Ok',
'UI:Audit:ErrorIn_Rule_Reason' => 'OQL Error in the Rule %1$s: %2$s.',
'UI:Audit:ErrorIn_Category_Reason' => 'OQL Error in the Category %1$s: %2$s.',
'UI:RunQuery:Title' => 'iTop - OQL Query Evaluation', 'UI:RunQuery:Title' => 'iTop - OQL Query Evaluation',
'UI:RunQuery:QueryExamples' => 'Query Examples', 'UI:RunQuery:QueryExamples' => 'Query Examples',
'UI:RunQuery:HeaderPurpose' => 'Purpose', 'UI:RunQuery:HeaderPurpose' => 'Purpose',
@@ -544,7 +569,7 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:RunQuery:DevelopedQuery' => 'Redevelopped query expression: ', 'UI:RunQuery:DevelopedQuery' => 'Redevelopped query expression: ',
'UI:RunQuery:SerializedFilter' => 'Serialized filter: ', 'UI:RunQuery:SerializedFilter' => 'Serialized filter: ',
'UI:RunQuery:Error' => 'An error occured while running the query: %1$s', 'UI:RunQuery:Error' => 'An error occured while running the query: %1$s',
'UI:Query:UrlForExcel' => 'URL to use for MS-Excel web queries',
'UI:Schema:Title' => 'iTop objects schema', 'UI:Schema:Title' => 'iTop objects schema',
'UI:Schema:CategoryMenuItem' => 'Category <b>%1$s</b>', 'UI:Schema:CategoryMenuItem' => 'Category <b>%1$s</b>',
'UI:Schema:Relationships' => 'Relationships', 'UI:Schema:Relationships' => 'Relationships',
@@ -604,8 +629,8 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:Schema:LifeCycleAttributeMustChange' => 'Must change', 'UI:Schema:LifeCycleAttributeMustChange' => 'Must change',
'UI:Schema:LifeCycleAttributeMustPrompt' => 'User will be prompted to change the value', 'UI:Schema:LifeCycleAttributeMustPrompt' => 'User will be prompted to change the value',
'UI:Schema:LifeCycleEmptyList' => 'empty list', 'UI:Schema:LifeCycleEmptyList' => 'empty list',
'UI:LinksWidget:Autocomplete+' => 'Type the first 3 characters...', 'UI:LinksWidget:Autocomplete+' => 'Type the first 3 characters...',
'UI:Edit:TestQuery' => 'Test query',
'UI:Combo:SelectValue' => '--- select a value ---', 'UI:Combo:SelectValue' => '--- select a value ---',
'UI:Label:SelectedObjects' => 'Selected objects: ', 'UI:Label:SelectedObjects' => 'Selected objects: ',
'UI:Label:AvailableObjects' => 'Available objects: ', 'UI:Label:AvailableObjects' => 'Available objects: ',
@@ -618,7 +643,6 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:RemoveLinkedObjectsOf_Class' => 'Remove selected objects', 'UI:RemoveLinkedObjectsOf_Class' => 'Remove selected objects',
'UI:Message:EmptyList:UseAdd' => 'The list is empty, use the "Add..." button to add elements.', 'UI:Message:EmptyList:UseAdd' => 'The list is empty, use the "Add..." button to add elements.',
'UI:Message:EmptyList:UseSearchForm' => 'Use the search form above to search for objects to be added.', 'UI:Message:EmptyList:UseSearchForm' => 'Use the search form above to search for objects to be added.',
'UI:Wizard:FinalStepTitle' => 'Final step: confirmation', 'UI:Wizard:FinalStepTitle' => 'Final step: confirmation',
'UI:Title:DeletionOf_Object' => 'Deletion of %1$s', 'UI:Title:DeletionOf_Object' => 'Deletion of %1$s',
'UI:Title:BulkDeletionOf_Count_ObjectsOf_Class' => 'Bulk deletion of %1$d objects of class %2$s', 'UI:Title:BulkDeletionOf_Count_ObjectsOf_Class' => 'Bulk deletion of %1$d objects of class %2$s',
@@ -821,6 +845,9 @@ When associated with a trigger, each action is given an "order" number, specifyi
'Menu:RunQueriesMenu' => 'Run Queries', 'Menu:RunQueriesMenu' => 'Run Queries',
'Menu:RunQueriesMenu+' => 'Run any query', 'Menu:RunQueriesMenu+' => 'Run any query',
'Menu:QueryMenu' => 'Query phrasebook',
'Menu:QueryMenu+' => 'Query phrasebook',
'Menu:DataAdministration' => 'Data administration', 'Menu:DataAdministration' => 'Data administration',
'Menu:DataAdministration+' => 'Data administration', 'Menu:DataAdministration+' => 'Data administration',
@@ -862,6 +889,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:RelationshipGraph' => 'Graphical view', 'UI:RelationshipGraph' => 'Graphical view',
'UI:RelationshipList' => 'List', 'UI:RelationshipList' => 'List',
'UI:OperationCancelled' => 'Operation Cancelled', 'UI:OperationCancelled' => 'Operation Cancelled',
'UI:ElementsDisplayed' => 'Filtering',
'Portal:Title' => 'iTop user portal', 'Portal:Title' => 'iTop user portal',
'Portal:Refresh' => 'Refresh', 'Portal:Refresh' => 'Refresh',
@@ -935,5 +963,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'Note that this is not a security setting, objects from any organization are still visible and can be accessed by selecting "All Organizations" in the drop-down list.', 'Note that this is not a security setting, objects from any organization are still visible and can be accessed by selecting "All Organizations" in the drop-down list.',
'UI:NavigateAwayConfirmationMessage' => 'Any modification will be discarded.', 'UI:NavigateAwayConfirmationMessage' => 'Any modification will be discarded.',
'UI:Create_Class_InState' => 'Create the %1$s in state: ', 'UI:Create_Class_InState' => 'Create the %1$s in state: ',
'UI:Button:Refresh' => 'Refresh',
'UI:iTopLogin' => 'iTop Login',
)); ));
?> ?>

View File

@@ -280,6 +280,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:SynchroDataSource/Attribute:url_icon+' => 'Hyperlien vers une icône représentant l\'application source des données', 'Class:SynchroDataSource/Attribute:url_icon+' => 'Hyperlien vers une icône représentant l\'application source des données',
'Class:SynchroDataSource/Attribute:url_application' => 'Application (hyperlien)', 'Class:SynchroDataSource/Attribute:url_application' => 'Application (hyperlien)',
'Class:SynchroDataSource/Attribute:url_application+' => 'Un hyperlien vers l\'application source des données. Paramètres possibles: $this->nom_de_champ$ et $replica->primary_key$', 'Class:SynchroDataSource/Attribute:url_application+' => 'Un hyperlien vers l\'application source des données. Paramètres possibles: $this->nom_de_champ$ et $replica->primary_key$',
'Class:SynchroDataSource/Attribute:database_table_name' => 'Table de données',
'Class:SynchroDataSource/Attribute:database_table_name+' => 'Nom de la table stockant les données de cette source. Un nom par défaut est calculé automatiquement si ce champ est laissé vide.',
'Class:SynchroAttribute' => 'Champs de synchronisation', 'Class:SynchroAttribute' => 'Champs de synchronisation',
'Class:SynchroAttribute+' => '', 'Class:SynchroAttribute+' => '',
'Class:SynchroAttribute/Attribute:sync_source_id' => 'Source de données', 'Class:SynchroAttribute/Attribute:sync_source_id' => 'Source de données',
@@ -386,6 +388,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:SynchroReplica/Attribute:status_dest_creator+' => '', 'Class:SynchroReplica/Attribute:status_dest_creator+' => '',
'Class:SynchroReplica/Attribute:status_last_error' => 'Dernière erreur', 'Class:SynchroReplica/Attribute:status_last_error' => 'Dernière erreur',
'Class:SynchroReplica/Attribute:status_last_error+' => '', 'Class:SynchroReplica/Attribute:status_last_error+' => '',
'Class:SynchroReplica/Attribute:status_last_warning' => 'Avertissements',
'Class:SynchroReplica/Attribute:status_last_warning+' => '',
'Class:SynchroReplica/Attribute:info_creation_date' => 'Date de création', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Date de création',
'Class:SynchroReplica/Attribute:info_creation_date+' => '', 'Class:SynchroReplica/Attribute:info_creation_date+' => '',
'Class:SynchroReplica/Attribute:info_last_modified' => 'Date de dernière modification', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Date de dernière modification',
@@ -483,6 +487,13 @@ Opérateurs :<br/>
'Core:AttributeTable+' => 'Tableau à deux dimensions', 'Core:AttributeTable+' => 'Tableau à deux dimensions',
'Core:AttributePropertySet' => 'Propriétés', 'Core:AttributePropertySet' => 'Propriétés',
'Core:AttributePropertySet+' => 'Liste de propriétés (nom et valeur) non typées', 'Core:AttributePropertySet+' => 'Liste de propriétés (nom et valeur) non typées',
'Core:AttributeFriendlyName' => 'Nom usuel (convivial)',
'Core:AttributeFriendlyName+' => 'Attribut créé automatiquement ; sa valeur est calculée d\'après d\'autres attributs',
'Core:FriendlyName-Label' => 'Nom',
'Core:FriendlyName-Description' => 'Nom usuel',
'Change:ObjectCreated' => 'Elément créé', 'Change:ObjectCreated' => 'Elément créé',
'Change:ObjectDeleted' => 'Elément effacé', 'Change:ObjectDeleted' => 'Elément effacé',
'Change:ObjectModified' => 'Elément modifié', 'Change:ObjectModified' => 'Elément modifié',
@@ -554,7 +565,7 @@ Opérateurs :<br/>
'Core:Synchro:History' => 'Historique de synchronisation', 'Core:Synchro:History' => 'Historique de synchronisation',
'Core:Synchro:NeverRun' => 'Aucun historique, la synchronisation n\'a pas encore fonctionné', 'Core:Synchro:NeverRun' => 'Aucun historique, la synchronisation n\'a pas encore fonctionné',
'Core:Synchro:SynchroEndedOn_Date' => 'La dernière synchronisation s\'est terminée à: %1$s.', 'Core:Synchro:SynchroEndedOn_Date' => 'La dernière synchronisation s\'est terminée à: %1$s.',
'Core:Synchro:SynchroRunningStartedOn_Date' => 'Synchronisation en cours (début à $1$s)', 'Core:Synchro:SynchroRunningStartedOn_Date' => 'Synchronisation en cours (début à %1$s)',
'Menu:DataSources' => 'Synchronisation', 'Menu:DataSources' => 'Synchronisation',
'Menu:DataSources+' => '', 'Menu:DataSources+' => '',
'Core:Synchro:label_repl_ignored' => 'Ignorés (%1$s)', 'Core:Synchro:label_repl_ignored' => 'Ignorés (%1$s)',
@@ -578,6 +589,7 @@ Opérateurs :<br/>
'Class:SynchroDataSource/Error:AtLeastOneReconciliationKeyMustBeSpecified' => 'Si la politique de réconciliation n\'est pas la clé primaire, au moins une clé de recherche doit être spécifiée', 'Class:SynchroDataSource/Error:AtLeastOneReconciliationKeyMustBeSpecified' => 'Si la politique de réconciliation n\'est pas la clé primaire, au moins une clé de recherche doit être spécifiée',
'Class:SynchroDataSource/Error:DeleteRetentionDurationMustBeSpecified' => 'Pour que les objets soient effacés après avoir été obsoletés, il faut spécifier une durée de rétention', 'Class:SynchroDataSource/Error:DeleteRetentionDurationMustBeSpecified' => 'Pour que les objets soient effacés après avoir été obsoletés, il faut spécifier une durée de rétention',
'Class:SynchroDataSource/Error:DeletePolicyUpdateMustBeSpecified' => 'Les objets obsolètes doivent être mis à jour, mais aucune information de mise à jour n\'est spécifiée', 'Class:SynchroDataSource/Error:DeletePolicyUpdateMustBeSpecified' => 'Les objets obsolètes doivent être mis à jour, mais aucune information de mise à jour n\'est spécifiée',
'Class:SynchroDataSource/Error:DataTableAlreadyExists' => 'La table %1$s existe déjà dans la base de données. Veuillez utiliser un autre nom pour la table des données de cette source.',
'Core:SynchroReplica:PublicData' => 'Données synchronisées', 'Core:SynchroReplica:PublicData' => 'Données synchronisées',
'Core:SynchroReplica:PrivateDetails' => 'Informations internes', 'Core:SynchroReplica:PrivateDetails' => 'Informations internes',
'Core:SynchroReplica:BackToDataSource' => 'Retourner aux détails de la source de données: %1$s', 'Core:SynchroReplica:BackToDataSource' => 'Retourner aux détails de la source de données: %1$s',
@@ -593,7 +605,7 @@ Opérateurs :<br/>
'Core:SynchroAtt:update_policy+' => '', 'Core:SynchroAtt:update_policy+' => '',
'Core:SynchroAtt:reconciliation_attcode' => 'Clé de recherche', 'Core:SynchroAtt:reconciliation_attcode' => 'Clé de recherche',
'Core:SynchroAtt:reconciliation_attcode+' => '', 'Core:SynchroAtt:reconciliation_attcode+' => '',
'Core:SyncDataExchangeComment' => '(Synhcronisation)', 'Core:SyncDataExchangeComment' => '(Synchronisation)',
'Core:Synchro:ListOfDataSources' => 'Sources de données:', 'Core:Synchro:ListOfDataSources' => 'Sources de données:',
'Core:Synchro:LastSynchro' => 'Dernière synchronisation:', 'Core:Synchro:LastSynchro' => 'Dernière synchronisation:',
'Core:Synchro:ThisObjectIsSynchronized' => 'Cet objet est synchronisé avec une source de données', 'Core:Synchro:ThisObjectIsSynchronized' => 'Cet objet est synchronisé avec une source de données',
@@ -605,6 +617,8 @@ Opérateurs :<br/>
'Core:SyncDataSourceObsolete' => 'Cette source de données est obsolète. Opération annulée.', 'Core:SyncDataSourceObsolete' => 'Cette source de données est obsolète. Opération annulée.',
'Core:SyncDataSourceAccessRestriction' => 'Seuls les administrateurs et l\'utilisateur spécifié dans la source de données peuvent exécuter cette synchronisation. Opération annulée.', 'Core:SyncDataSourceAccessRestriction' => 'Seuls les administrateurs et l\'utilisateur spécifié dans la source de données peuvent exécuter cette synchronisation. Opération annulée.',
'Core:SyncTooManyMissingReplicas' => 'Tous les réplicas sont absents de l\'import. L\'import a-t-il réellement tourné. Opération annulée.', 'Core:SyncTooManyMissingReplicas' => 'Tous les réplicas sont absents de l\'import. L\'import a-t-il réellement tourné. Opération annulée.',
'Core:Synchro:ListReplicas_AllReplicas_Errors_Warnings' => '%1$s replicas, %2$s erreur(s), %3$s avertissement(s).',
'Core:SynchroReplica:TargetObject' => 'Objet Synchronisé : %1$s',
'Core:Duration_Seconds' => '%1$ds', 'Core:Duration_Seconds' => '%1$ds',
'Core:Duration_Minutes_Seconds' => '%1$dmin %2$ds', 'Core:Duration_Minutes_Seconds' => '%1$dmin %2$ds',
'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds', 'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds',

View File

@@ -50,6 +50,18 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:AuditCategory/Attribute:definition_set+' => 'Expression OQL qui défini le périmètre d\'application de l\'audit', 'Class:AuditCategory/Attribute:definition_set+' => 'Expression OQL qui défini le périmètre d\'application de l\'audit',
'Class:AuditCategory/Attribute:rules_list' => 'Règles d\'audit', 'Class:AuditCategory/Attribute:rules_list' => 'Règles d\'audit',
'Class:AuditCategory/Attribute:rules_list+' => 'Règles d\'audit pour cette catégorie', 'Class:AuditCategory/Attribute:rules_list+' => 'Règles d\'audit pour cette catégorie',
'Class:Query' => 'Requête',
'Class:Query+' => 'Une requête définit un ensemble d\'information de manière dynamique',
'Class:Query/Attribute:name' => 'Nom',
'Class:Query/Attribute:name+' => 'Identification de la requête',
'Class:Query/Attribute:description' => 'Description',
'Class:Query/Attribute:description+' => 'Description complète (finalité, utilisations, public)',
'Class:Query/Attribute:fields' => 'Champs',
'Class:Query/Attribute:fields+' => 'Liste CSV des attributs (ou alias.attribut) à exporter',
'Class:QueryOQL' => 'Requête OQL',
'Class:QueryOQL+' => 'Une requête écrite dans le langage "Object Query Language"',
'Class:QueryOQL/Attribute:oql' => 'Expression',
'Class:QueryOQL/Attribute:oql+' => 'Expression OQL',
'Class:URP_Profiles' => 'Profil', 'Class:URP_Profiles' => 'Profil',
'Class:URP_Profiles+' => 'Profil utilisateur', 'Class:URP_Profiles+' => 'Profil utilisateur',
'Class:URP_Profiles/Attribute:name' => 'Nom', 'Class:URP_Profiles/Attribute:name' => 'Nom',
@@ -71,7 +83,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:URP_UserProfile/Attribute:reason' => 'Raison', 'Class:URP_UserProfile/Attribute:reason' => 'Raison',
'Class:URP_UserProfile/Attribute:reason+' => 'Justifie le rôle affecté à cet utilisateur', 'Class:URP_UserProfile/Attribute:reason+' => 'Justifie le rôle affecté à cet utilisateur',
'Class:URP_UserOrg' => 'Utilisateur/Organisation', 'Class:URP_UserOrg' => 'Utilisateur/Organisation',
'Class:URP_UserOrg+' => 'Organizations permises pour l\'utilisateur', 'Class:URP_UserOrg+' => 'Organisations permises pour l\'utilisateur',
'Class:URP_UserOrg/Attribute:userid' => 'Utilisateur', 'Class:URP_UserOrg/Attribute:userid' => 'Utilisateur',
'Class:URP_UserOrg/Attribute:userid+' => '', 'Class:URP_UserOrg/Attribute:userid+' => '',
'Class:URP_UserOrg/Attribute:userlogin' => 'Login', 'Class:URP_UserOrg/Attribute:userlogin' => 'Login',
@@ -215,7 +227,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'UI:WelcomeMenu:OpenIncidents' => 'Incidents en cours: %1$d', 'UI:WelcomeMenu:OpenIncidents' => 'Incidents en cours: %1$d',
'UI:WelcomeMenu:AllConfigItems' => 'Actifs: %1$d', 'UI:WelcomeMenu:AllConfigItems' => 'Actifs: %1$d',
'UI:WelcomeMenu:MyIncidents' => 'Mes Incidents', 'UI:WelcomeMenu:MyIncidents' => 'Mes Incidents',
'UI:AllOrganizations' => ' Toutes les Organizations ', 'UI:AllOrganizations' => ' Toutes les Organisations ',
'UI:YourSearch' => 'Votre recherche', 'UI:YourSearch' => 'Votre recherche',
'UI:LoggedAsMessage' => 'Connecté comme: %1$s', 'UI:LoggedAsMessage' => 'Connecté comme: %1$s',
'UI:LoggedAsMessage+Admin' => 'Connecté comme: %1$s (Administrateur)', 'UI:LoggedAsMessage+Admin' => 'Connecté comme: %1$s (Administrateur)',
@@ -316,6 +328,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
'UI:Document:OpenInNewWindow:Download' => 'Ouvrir dans un nouvelle fenêtre: %1$s, Télécharger: %2$s', 'UI:Document:OpenInNewWindow:Download' => 'Ouvrir dans un nouvelle fenêtre: %1$s, Télécharger: %2$s',
'UI:SelectAllToggle+' => 'Tout sélectionner / Tout déselectionner', 'UI:SelectAllToggle+' => 'Tout sélectionner / Tout déselectionner',
'UI:TruncatedResults' => '%1$d objets affichés sur %2$d', 'UI:TruncatedResults' => '%1$d objets affichés sur %2$d',
'UI:SplitDateTime-Date' => 'date',
'UI:SplitDateTime-Time' => 'heure',
'UI:DisplayAll' => 'Tout afficher', 'UI:DisplayAll' => 'Tout afficher',
'UI:CollapseList' => 'Refermer', 'UI:CollapseList' => 'Refermer',
'UI:CountOfResults' => '%1$d objet(s)', 'UI:CountOfResults' => '%1$d objet(s)',
@@ -416,6 +430,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
'UI:Audit:HeaderNbObjects' => 'Nb d\'Objets', 'UI:Audit:HeaderNbObjects' => 'Nb d\'Objets',
'UI:Audit:HeaderNbErrors' => 'Nb d\'Erreurs', 'UI:Audit:HeaderNbErrors' => 'Nb d\'Erreurs',
'UI:Audit:PercentageOk' => '% Ok', 'UI:Audit:PercentageOk' => '% Ok',
'UI:Audit:ErrorIn_Rule_Reason' => 'Erreur OQL dans la règle %1$s: %2$s.',
'UI:Audit:ErrorIn_Category_Reason' => 'Erreur OQL dans la catégorie %1$s: %2$s.',
'UI:RunQuery:Title' => 'iTop - Evaluation de requêtes OQL', 'UI:RunQuery:Title' => 'iTop - Evaluation de requêtes OQL',
'UI:RunQuery:QueryExamples' => 'Exemples de requêtes', 'UI:RunQuery:QueryExamples' => 'Exemples de requêtes',
'UI:RunQuery:HeaderPurpose' => 'Objectif', 'UI:RunQuery:HeaderPurpose' => 'Objectif',
@@ -427,6 +443,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'UI:RunQuery:DevelopedQuery' => 'Requête OQL décompilée : ', 'UI:RunQuery:DevelopedQuery' => 'Requête OQL décompilée : ',
'UI:RunQuery:SerializedFilter' => 'Version sérialisée : ', 'UI:RunQuery:SerializedFilter' => 'Version sérialisée : ',
'UI:RunQuery:Error' => 'Une erreur s\'est produite durant l\'exécution de la requête : %1$s', 'UI:RunQuery:Error' => 'Une erreur s\'est produite durant l\'exécution de la requête : %1$s',
'UI:Query:UrlForExcel' => 'Lien à copier-coller dans Excel, pour déclarer une source de données à partir du web',
'UI:Schema:Title' => 'Modèle de données iTop', 'UI:Schema:Title' => 'Modèle de données iTop',
'UI:Schema:CategoryMenuItem' => 'Catégorie <b>%1$s</b>', 'UI:Schema:CategoryMenuItem' => 'Catégorie <b>%1$s</b>',
'UI:Schema:Relationships' => 'Relations', 'UI:Schema:Relationships' => 'Relations',
@@ -486,6 +503,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'UI:Schema:LifeCycleAttributeMustPrompt' => 'L\'utilisateur se verra proposer de changer la valeur', 'UI:Schema:LifeCycleAttributeMustPrompt' => 'L\'utilisateur se verra proposer de changer la valeur',
'UI:Schema:LifeCycleEmptyList' => 'liste vide', 'UI:Schema:LifeCycleEmptyList' => 'liste vide',
'UI:LinksWidget:Autocomplete+' => 'Tapez les 3 premiers caractères...', 'UI:LinksWidget:Autocomplete+' => 'Tapez les 3 premiers caractères...',
'UI:Edit:TestQuery' => 'Tester la requête',
'UI:Combo:SelectValue' => '--- choisissez une valeur ---', 'UI:Combo:SelectValue' => '--- choisissez une valeur ---',
'UI:Label:SelectedObjects' => 'Objets sélectionnés: ', 'UI:Label:SelectedObjects' => 'Objets sélectionnés: ',
'UI:Label:AvailableObjects' => 'Objets disponibles: ', 'UI:Label:AvailableObjects' => 'Objets disponibles: ',
@@ -680,6 +698,8 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'Menu:Notifications:Title' => 'Catégories d\'audit', 'Menu:Notifications:Title' => 'Catégories d\'audit',
'Menu:RunQueriesMenu' => 'Requêtes OQL', 'Menu:RunQueriesMenu' => 'Requêtes OQL',
'Menu:RunQueriesMenu+' => 'Executer une requête OQL', 'Menu:RunQueriesMenu+' => 'Executer une requête OQL',
'Menu:QueryMenu' => 'Livre des requêtes',
'Menu:QueryMenu+' => 'Livre des requêtes',
'Menu:DataAdministration' => 'Administration des données', 'Menu:DataAdministration' => 'Administration des données',
'Menu:DataAdministration+' => 'Administration des données', 'Menu:DataAdministration+' => 'Administration des données',
'Menu:UniversalSearchMenu' => 'Recherche Universelle', 'Menu:UniversalSearchMenu' => 'Recherche Universelle',
@@ -712,6 +732,7 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:DisplayThisMessageAtStartup' => 'Afficher ce message au démarrage', 'UI:DisplayThisMessageAtStartup' => 'Afficher ce message au démarrage',
'UI:RelationshipGraph' => 'Vue graphique', 'UI:RelationshipGraph' => 'Vue graphique',
'UI:RelationshipList' => 'Liste', 'UI:RelationshipList' => 'Liste',
'UI:ElementsDisplayed' => 'Filtrage',
'UI:OperationCancelled' => 'Opération Annulée', 'UI:OperationCancelled' => 'Opération Annulée',
'Portal:Title' => 'Portail utilisateur iTop', 'Portal:Title' => 'Portail utilisateur iTop',
'Portal:Refresh' => 'Rafraîchir', 'Portal:Refresh' => 'Rafraîchir',
@@ -785,5 +806,6 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'Ceci n\'est pas un réglage de sécurité. Les objets de toutes les organisations sont toujours visibles en choisissant "Toutes les Organisations" dans le menu.', 'Ceci n\'est pas un réglage de sécurité. Les objets de toutes les organisations sont toujours visibles en choisissant "Toutes les Organisations" dans le menu.',
'UI:NavigateAwayConfirmationMessage' => 'Toute modification sera perdue.', 'UI:NavigateAwayConfirmationMessage' => 'Toute modification sera perdue.',
'UI:Create_Class_InState' => 'Créer l\'objet %1$s dans l\'état: ', 'UI:Create_Class_InState' => 'Créer l\'objet %1$s dans l\'état: ',
'UI:Button:Refresh' => 'Rafraîchir',
)); ));
?> ?>

View File

@@ -422,7 +422,7 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'Core:Synchro:History' => 'Szinkronizáció történet', 'Core:Synchro:History' => 'Szinkronizáció történet',
'Core:Synchro:NeverRun' => 'Ez a szinkronizáció még soha nem futott. Nincs még napló bejegyzés.', 'Core:Synchro:NeverRun' => 'Ez a szinkronizáció még soha nem futott. Nincs még napló bejegyzés.',
'Core:Synchro:SynchroEndedOn_Date' => 'Az utolsó szinkronizáció lefutásának időpontja: %1$s.', 'Core:Synchro:SynchroEndedOn_Date' => 'Az utolsó szinkronizáció lefutásának időpontja: %1$s.',
'Core:Synchro:SynchroRunningStartedOn_Date' => 'Az szinkronizáció elindut $1$s, de még fut.', 'Core:Synchro:SynchroRunningStartedOn_Date' => 'Az szinkronizáció elindut %1$s, de még fut.',
'Menu:DataSources' => 'Szinkronizált adatforrások', 'Menu:DataSources' => 'Szinkronizált adatforrások',
'Menu:DataSources+' => '', 'Menu:DataSources+' => '',
'Core:Synchro:label_repl_ignored' => 'Figyelmen kívül hagyott (%1$s)', 'Core:Synchro:label_repl_ignored' => 'Figyelmen kívül hagyott (%1$s)',

View File

@@ -772,5 +772,6 @@ Akció kiváltó okhoz rendelésekor kap egy sorszámot , amely meghatározza az
'UI:ActionNotAllowed' => 'Ennek a műveletnek a végrehajtása nem engedélyezett ezen az objektumon.', 'UI:ActionNotAllowed' => 'Ennek a műveletnek a végrehajtása nem engedélyezett ezen az objektumon.',
'UI:BulkAction:NoObjectSelected' => 'Válasszon ki legalább egy objketumot a művelet végrehajtásához', 'UI:BulkAction:NoObjectSelected' => 'Válasszon ki legalább egy objketumot a művelet végrehajtásához',
'UI:AttemptingToChangeASlaveAttribute_Name' => '%1$s mező nem írható, mert a szinkronizációnál használt kulcs. Érték változatlan maradt.', 'UI:AttemptingToChangeASlaveAttribute_Name' => '%1$s mező nem írható, mert a szinkronizációnál használt kulcs. Érték változatlan maradt.',
'UI:Button:Refresh' => 'Frissítés',
)); ));
?> ?>

File diff suppressed because it is too large Load Diff

View File

@@ -15,76 +15,265 @@
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/** /**
* @author Erwan Taloc <erwan.taloc@combodo.com> * Localized data
* @author Romain Quetiez <romain.quetiez@combodo.com> *
* @author Denis Flaven <denis.flaven@combodo.com> * @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @licence http://www.opensource.org/licenses/gpl-3.0.html LGPL * @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/ */
//////////////////////////////////////////////////////////////////////
// Classes in 'gui'
//////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////
// Classes in 'application'
//////////////////////////////////////////////////////////////////////
//
//
// Class: AuditCategory
//
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:AuditCategory' => 'Categoria di Audit',
'Class:AuditCategory+' => 'Una sezione all\'interno del controllo globale',
'Class:AuditCategory/Attribute:name' => 'Nome della categoria',
'Class:AuditCategory/Attribute:name+' => 'Abbreviazione per questa categoria',
'Class:AuditCategory/Attribute:description' => 'Descrizione della categoria di Audit',
'Class:AuditCategory/Attribute:description+' => 'Descrizione dettagliata della categoria di audit',
'Class:AuditCategory/Attribute:definition_set' => 'Insieme di definizione',
'Class:AuditCategory/Attribute:definition_set+' => 'Espressione OQLche definisce l\'insieme di oggetti da controllare',
'Class:AuditCategory/Attribute:rules_list' => 'Regole di Audit',
'Class:AuditCategory/Attribute:rules_list+' => 'Regolele di audit per queste categorie',
));
//
// Class: AuditRule
//
Dict::Add('IT IT', 'Italian', 'Italiano', array( Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:AuditRule' => 'Regola di Audit', 'Class:AuditRule' => 'Regola di Audit',
'Class:AuditRule+' => '', 'Class:AuditRule+' => '',
'Class:AuditRule/Attribute:name' => 'Nome della regola', 'Class:AuditRule/Attribute:name' => 'Nome della regola',
'Class:AuditRule/Attribute:name+' => '', 'Class:AuditRule/Attribute:name+' => '',
'Class:AuditRule/Attribute:description' => 'Descrizione della regola di Audit', 'Class:AuditRule/Attribute:description' => 'Descrizione della regola di Audit',
'Class:AuditRule/Attribute:description+' => '', 'Class:AuditRule/Attribute:description+' => 'Descrizione dettagliata per questa regola di audit ',
'Class:AuditRule/Attribute:query' => 'Query da eseguire', 'Class:AuditRule/Attribute:query' => 'Query da eseguire',
'Class:AuditRule/Attribute:query+' => '', 'Class:AuditRule/Attribute:query+' => 'Espressio OQL da eseguire',
'Class:AuditRule/Attribute:valid_flag' => 'Oggetti validi?', 'Class:AuditRule/Attribute:valid_flag' => 'Oggetti validi?',
'Class:AuditRule/Attribute:valid_flag+' => '', 'Class:AuditRule/Attribute:valid_flag+' => 'Vero se la regola ritorna oggetti validi, falso altrimenti ',
'Class:AuditRule/Attribute:valid_flag/Value:false' => 'falso',
'Class:AuditRule/Attribute:valid_flag/Value:false+' => '',
'Class:AuditRule/Attribute:valid_flag/Value:true' => 'vero', 'Class:AuditRule/Attribute:valid_flag/Value:true' => 'vero',
'Class:AuditRule/Attribute:valid_flag/Value:true+' => '', 'Class:AuditRule/Attribute:valid_flag/Value:true+' => 'vero',
'Class:AuditRule/Attribute:valid_flag/Value:false' => 'falso',
'Class:AuditRule/Attribute:valid_flag/Value:false+' => 'falso',
'Class:AuditRule/Attribute:category_id' => 'Categoria', 'Class:AuditRule/Attribute:category_id' => 'Categoria',
'Class:AuditRule/Attribute:category_id+' => '', 'Class:AuditRule/Attribute:category_id+' => 'Categoria per questa regola',
'Class:AuditCategory' => 'Categoria di Audit', 'Class:AuditRule/Attribute:category_name' => 'Categoria',
'Class:AuditCategory+' => '', 'Class:AuditRule/Attribute:category_name+' => 'Nome della categoria per questa regola',
'Class:AuditCategory/Attribute:name' => 'Nome della Categoria', ));
'Class:AuditCategory/Attribute:name+' => '',
'Class:AuditCategory/Attribute:description' => 'Descrizione della Categoria di Audit', //
'Class:AuditCategory/Attribute:description+' => '', // Class: QueryOQL
'Class:AuditCategory/Attribute:definition_set' => 'Insieme di definizione', //
'Class:AuditCategory/Attribute:definition_set+' => '',
'Class:AuditCategory/Attribute:rules_list' => 'Regole di Audit', Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:AuditCategory/Attribute:rules_list+' => '', 'Class:Query' => 'Query',
'Class:Query+' => 'Una query è un insieme di dati definito in modo dinamico',
'Class:Query/Attribute:name' => 'Nome',
'Class:Query/Attribute:name+' => 'Identificativi della query',
'Class:Query/Attribute:description' => 'Descrizione',
'Class:Query/Attribute:description+' => 'Descrizione dettagliata della query(scopo, usagoetc.)',
'Class:Query/Attribute:fields' => 'Campi',
'Class:Query/Attribute:fields+' => 'Lista di attributi separati da virgola (o alias.attributo) per l\'esportazione',
'Class:QueryOQL' => 'OQL Query',
'Class:QueryOQL+' => 'Una query basata su Object Query Language',
'Class:QueryOQL/Attribute:oql' => 'Espressione',
'Class:QueryOQL/Attribute:oql+' => 'Espressione OQL',
));
//////////////////////////////////////////////////////////////////////
// Classes in 'addon/userrights'
//////////////////////////////////////////////////////////////////////
//
//
// Class: User
//
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:User' => 'Utente',
'Class:User+' => 'Login Utente',
'Class:User/Attribute:finalclass' => 'Tipo di account',
'Class:User/Attribute:finalclass+' => '',
'Class:User/Attribute:contactid' => 'Contatto (persona)',
'Class:User/Attribute:contactid+' => 'Dettagli personali per dati aziendali',
'Class:User/Attribute:last_name' => 'Cognome',
'Class:User/Attribute:last_name+' => 'Cognome del contatto corrispondente',
'Class:User/Attribute:first_name' => 'Nome',
'Class:User/Attribute:first_name+' => 'Nome del contatto corrispondente',
'Class:User/Attribute:email' => 'Email',
'Class:User/Attribute:email+' => 'Email del contatto corrispondente',
'Class:User/Attribute:login' => 'Login',
'Class:User/Attribute:login+' => 'Stringa di identificazione dell\'utente',
'Class:User/Attribute:language' => 'Lingua',
'Class:User/Attribute:language+' => 'Lingua utente',
'Class:User/Attribute:language/Value:EN US' => 'English',
'Class:User/Attribute:language/Value:EN US+' => 'English (U.S.)',
'Class:User/Attribute:language/Value:IT IT' => 'Italiano',
'Class:User/Attribute:language/Value:IT IT+' => 'Italiano (IT)',
'Class:User/Attribute:language/Value:FR FR' => 'French',
'Class:User/Attribute:language/Value:FR FR+' => 'French (France)',
'Class:User/Attribute:profile_list' => 'Profili',
'Class:User/Attribute:profile_list+' => 'Regole per la concessione dei diritti per quella persona',
'Class:User/Attribute:allowed_org_list' => 'Organizzazione Consentite',
'Class:User/Attribute:allowed_org_list+' => 'L\'utente finale è autorizzato a vedere i dati appartenenti alle seguenti organizzazioni. Se non è specificato organizzazione, vi è alcuna restrizione.',
'Class:User/Error:LoginMustBeUnique' => 'Il Login deve essere unico - "%1s" già usato',
'Class:User/Error:AtLeastOneProfileIsNeeded' => 'Almeno un profilo deve essere assegnato all\'utente.',
));
//
// Class: URP_Profiles
//
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:URP_Profiles' => 'Profilo', 'Class:URP_Profiles' => 'Profilo',
'Class:URP_Profiles+' => '', 'Class:URP_Profiles+' => '',
'Class:URP_Profiles/Attribute:name' => 'Nome', 'Class:URP_Profiles/Attribute:name' => 'Nome',
'Class:URP_Profiles/Attribute:name+' => '', 'Class:URP_Profiles/Attribute:name+' => '',
'Class:URP_Profiles/Attribute:description' => 'Descrizione', 'Class:URP_Profiles/Attribute:description' => 'Descrizione',
'Class:URP_Profiles/Attribute:description+' => '', 'Class:URP_Profiles/Attribute:description+' => 'una linea di descrizione',
'Class:URP_Profiles/Attribute:user_list' => 'Utenti', 'Class:URP_Profiles/Attribute:user_list' => 'Utenti',
'Class:URP_Profiles/Attribute:user_list+' => '', 'Class:URP_Profiles/Attribute:user_list+' => 'Persone che hanno questo ruuolo',
));
//
// Class: URP_Dimensions
//
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:URP_Dimensions' => 'dimensione',
'Class:URP_Dimensions+' => 'dimensione dell\'applicazione (definizione di silos))',
'Class:URP_Dimensions/Attribute:name' => 'Nome',
'Class:URP_Dimensions/Attribute:name+' => 'etichetta',
'Class:URP_Dimensions/Attribute:description' => 'Descrizione',
'Class:URP_Dimensions/Attribute:description+' => 'una linea di descrizione',
'Class:URP_Dimensions/Attribute:type' => 'Tipo',
'Class:URP_Dimensions/Attribute:type+' => 'nome della classe o tipo di dato (proiezione dell\'unità)',
));
//
// Class: URP_UserProfile
//
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:URP_UserProfile' => 'Utente da Profilare', 'Class:URP_UserProfile' => 'Utente da Profilare',
'Class:URP_UserProfile+' => '', 'Class:URP_UserProfile+' => '',
'Class:URP_UserProfile/Attribute:userid' => 'Utente', 'Class:URP_UserProfile/Attribute:userid' => 'Utente',
'Class:URP_UserProfile/Attribute:userid+' => '', 'Class:URP_UserProfile/Attribute:userid+' => '',
'Class:URP_UserProfile/Attribute:userlogin' => 'Login',
'Class:URP_UserProfile/Attribute:userlogin+' => 'User\'s login',
'Class:URP_UserProfile/Attribute:profileid' => 'Profilo', 'Class:URP_UserProfile/Attribute:profileid' => 'Profilo',
'Class:URP_UserProfile/Attribute:profileid+' => '', 'Class:URP_UserProfile/Attribute:profileid+' => 'utilizzo del profilo',
'Class:URP_UserProfile/Attribute:profile' => 'Profilo',
'Class:URP_UserProfile/Attribute:profile+' => 'Nome del profilo',
'Class:URP_UserProfile/Attribute:reason' => 'Motivo', 'Class:URP_UserProfile/Attribute:reason' => 'Motivo',
'Class:URP_UserProfile/Attribute:reason+' => '', 'Class:URP_UserProfile/Attribute:reason+' => 'spiega perchè questo utente dovrebbe avere questo ruolo',
));
//
// Class: URP_UserOrg
//
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:URP_UserOrg' => 'Organizzazione dell\'utente', 'Class:URP_UserOrg' => 'Organizzazione dell\'utente',
'Class:URP_UserOrg+' => '', 'Class:URP_UserOrg+' => '',
'Class:URP_UserOrg/Attribute:userid' => 'Utente', 'Class:URP_UserOrg/Attribute:userid' => 'Utente',
'Class:URP_UserOrg/Attribute:userid+' => '', 'Class:URP_UserOrg/Attribute:userid+' => 'Account Utente',
'Class:URP_UserOrg/Attribute:userlogin' => 'Login',
'Class:URP_UserOrg/Attribute:userlogin+' => 'Login Utente',
'Class:URP_UserOrg/Attribute:allowed_org_id' => 'Organizazione', 'Class:URP_UserOrg/Attribute:allowed_org_id' => 'Organizazione',
'Class:URP_UserOrg/Attribute:allowed_org_id+' => '', 'Class:URP_UserOrg/Attribute:allowed_org_id+' => 'Organizzazione permesse',
'Class:URP_UserOrg/Attribute:allowed_org_name' => 'Organizzazione',
'Class:URP_UserOrg/Attribute:allowed_org_name+' => 'Organizzazione permesse',
'Class:URP_UserOrg/Attribute:reason' => 'Motivo', 'Class:URP_UserOrg/Attribute:reason' => 'Motivo',
'Class:URP_UserOrg/Attribute:reason+' => '', 'Class:URP_UserOrg/Attribute:reason+' => '',
));
//
// Class: URP_ProfileProjection
//
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:URP_ProfileProjection' => 'profile_projection',
'Class:URP_ProfileProjection+' => 'proiezioni di profilo',
'Class:URP_ProfileProjection/Attribute:dimensionid' => 'Dimensione',
'Class:URP_ProfileProjection/Attribute:dimensionid+' => 'dimensione applicazione',
'Class:URP_ProfileProjection/Attribute:dimension' => 'Dimensione',
'Class:URP_ProfileProjection/Attribute:dimension+' => 'dimensione applicazione',
'Class:URP_ProfileProjection/Attribute:profileid' => 'Profilo',
'Class:URP_ProfileProjection/Attribute:profileid+' => 'utilizzo di profilo',
'Class:URP_ProfileProjection/Attribute:profile' => 'Profilo',
'Class:URP_ProfileProjection/Attribute:profile+' => 'Nome del profilo',
'Class:URP_ProfileProjection/Attribute:value' => 'Valore dell\'espressione',
'Class:URP_ProfileProjection/Attribute:value+' => 'Espressione OQL (uso $user) | constante| | +codice attributo',
'Class:URP_ProfileProjection/Attribute:attribute' => 'Attributo',
'Class:URP_ProfileProjection/Attribute:attribute+' => 'Codice attributo bersaglio (opzionale)',
));
//
// Class: URP_ClassProjection
//
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:URP_ClassProjection' => 'class_projection',
'Class:URP_ClassProjection+' => 'proiezioni di classe',
'Class:URP_ClassProjection/Attribute:dimensionid' => 'Dimensione',
'Class:URP_ClassProjection/Attribute:dimensionid+' => 'dimensione dell\'applicazione',
'Class:URP_ClassProjection/Attribute:dimension' => 'Dimensione',
'Class:URP_ClassProjection/Attribute:dimension+' => 'dimensione applicazione',
'Class:URP_ClassProjection/Attribute:class' => 'Classe',
'Class:URP_ClassProjection/Attribute:class+' => 'Classe bersaglio',
'Class:URP_ClassProjection/Attribute:value' => 'Valore dell\'espressione',
'Class:URP_ClassProjection/Attribute:value+' => 'Espressione OQL (uso $this) | constante| | +codice attributo',
'Class:URP_ClassProjection/Attribute:attribute' => 'Attributo',
'Class:URP_ClassProjection/Attribute:attribute+' => 'Codice attributo bersaglio (opzionale)',
));
//
// Class: URP_ActionGrant
//
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:URP_ActionGrant' => 'azione_autorizzazione', 'Class:URP_ActionGrant' => 'azione_autorizzazione',
'Class:URP_ActionGrant+' => '', 'Class:URP_ActionGrant+' => 'permesso su classi',
'Class:URP_ActionGrant/Attribute:profileid' => 'Profilo', 'Class:URP_ActionGrant/Attribute:profileid' => 'Profilo',
'Class:URP_ActionGrant/Attribute:profileid+' => '', 'Class:URP_ActionGrant/Attribute:profileid+' => 'Utilizzo del profilo',
'Class:URP_ActionGrant/Attribute:profile' => 'Profilo',
'Class:URP_ActionGrant/Attribute:profile+' => 'Utilizzo del profilo',
'Class:URP_ActionGrant/Attribute:class' => 'Classe', 'Class:URP_ActionGrant/Attribute:class' => 'Classe',
'Class:URP_ActionGrant/Attribute:class+' => '', 'Class:URP_ActionGrant/Attribute:class+' => 'Classe bersaglio',
'Class:URP_ActionGrant/Attribute:permission' => 'Autorizzazione', 'Class:URP_ActionGrant/Attribute:permission' => 'Autorizzazione',
'Class:URP_ActionGrant/Attribute:permission+' => '', 'Class:URP_ActionGrant/Attribute:permission+' => 'permesso non permesso',
'Class:URP_ActionGrant/Attribute:permission/Value:yes' => 'si',
'Class:URP_ActionGrant/Attribute:permission/Value:yes+' => 'si',
'Class:URP_ActionGrant/Attribute:permission/Value:no' => 'no', 'Class:URP_ActionGrant/Attribute:permission/Value:no' => 'no',
'Class:URP_ActionGrant/Attribute:permission/Value:no+' => '', 'Class:URP_ActionGrant/Attribute:permission/Value:no+' => 'no',
'Class:URP_ActionGrant/Attribute:permission/Value:yes' => 'yes', 'Class:URP_ActionGrant/Attribute:action' => 'Azione',
'Class:URP_ActionGrant/Attribute:permission/Value:yes+' => '', 'Class:URP_ActionGrant/Attribute:action+' => 'operazioni da effettuare sulla data classe',
));
//
// Class: URP_StimulusGrant
//
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:URP_ActionGrant/Attribute:action' => 'Azione', 'Class:URP_ActionGrant/Attribute:action' => 'Azione',
'Class:URP_ActionGrant/Attribute:action+' => '', 'Class:URP_ActionGrant/Attribute:action+' => '',
'Class:URP_StimulusGrant' => 'stimulus_autorizzazione', 'Class:URP_StimulusGrant' => 'stimulus_autorizzazione',
@@ -95,92 +284,32 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:URP_StimulusGrant/Attribute:class+' => '', 'Class:URP_StimulusGrant/Attribute:class+' => '',
'Class:URP_StimulusGrant/Attribute:permission' => 'Autorizzazione', 'Class:URP_StimulusGrant/Attribute:permission' => 'Autorizzazione',
'Class:URP_StimulusGrant/Attribute:permission+' => '', 'Class:URP_StimulusGrant/Attribute:permission+' => '',
'Class:URP_StimulusGrant/Attribute:permission/Value:yes' => 'si',
'Class:URP_StimulusGrant/Attribute:permission/Value:yes+' => 'si',
'Class:URP_StimulusGrant/Attribute:permission/Value:no' => 'no', 'Class:URP_StimulusGrant/Attribute:permission/Value:no' => 'no',
'Class:URP_StimulusGrant/Attribute:permission/Value:no+' => '', 'Class:URP_StimulusGrant/Attribute:permission/Value:no+' => 'no',
'Class:URP_StimulusGrant/Attribute:permission/Value:yes' => 'yes',
'Class:URP_StimulusGrant/Attribute:permission/Value:yes+' => '',
'Class:URP_StimulusGrant/Attribute:stimulus' => 'Stimulus', 'Class:URP_StimulusGrant/Attribute:stimulus' => 'Stimulus',
'Class:URP_StimulusGrant/Attribute:stimulus+' => '', 'Class:URP_StimulusGrant/Attribute:stimulus+' => 'Codice per lo Stimolus',
));
//
// Class: URP_AttributeGrant
//
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:URP_AttributeGrant' => 'attributo_autorizzazione', 'Class:URP_AttributeGrant' => 'attributo_autorizzazione',
'Class:URP_AttributeGrant+' => '', 'Class:URP_AttributeGrant+' => 'autorizzazioni a livello di attributi',
'Class:URP_AttributeGrant/Attribute:actiongrantid' => 'Azione di sovvenzione', 'Class:URP_AttributeGrant/Attribute:actiongrantid' => 'Azione di sovvenzione',
'Class:URP_AttributeGrant/Attribute:actiongrantid+' => '', 'Class:URP_AttributeGrant/Attribute:actiongrantid+' => 'azione di sovvenzione',
'Class:URP_AttributeGrant/Attribute:attcode' => 'Attributo', 'Class:URP_AttributeGrant/Attribute:attcode' => 'Attributo',
'Class:URP_AttributeGrant/Attribute:attcode+' => '', 'Class:URP_AttributeGrant/Attribute:attcode+' => 'codice attributo',
'Class:AuditRule/Attribute:category_name' => 'Categoria', ));
'Class:AuditRule/Attribute:category_name+' => '',
'Class:User' => 'User~~', //
'Class:User+' => '', // String from the User Interface: menu, messages, buttons, etc...
'Class:User/Attribute:finalclass' => 'Tipo di account', //
'Class:User/Attribute:finalclass+' => '',
'Class:User/Attribute:contactid' => 'Contatto (persona)', Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:User/Attribute:contactid+' => '',
'Class:User/Attribute:last_name' => 'Cognome',
'Class:User/Attribute:last_name+' => '',
'Class:User/Attribute:first_name' => 'Nome',
'Class:User/Attribute:first_name+' => '',
'Class:User/Attribute:email' => 'Email',
'Class:User/Attribute:email+' => '',
'Class:User/Attribute:login' => 'Login',
'Class:User/Attribute:login+' => '',
'Class:User/Attribute:language' => 'Lingua',
'Class:User/Attribute:language+' => '',
'Class:User/Attribute:language/Value:EN US' => 'Inglese',
'Class:User/Attribute:language/Value:EN US+' => '',
'Class:User/Attribute:language/Value:FR FR' => 'Francese',
'Class:User/Attribute:language/Value:FR FR+' => '',
'Class:User/Attribute:profile_list' => 'Profili',
'Class:User/Attribute:profile_list+' => '',
'Class:User/Attribute:allowed_org_list' => 'Organizzazioni autorizzate',
'Class:User/Attribute:allowed_org_list+' => '',
'Class:User/Error:LoginMustBeUnique' => 'Login deve essere unico - "%1s" è già utilizzato.~~',
'Class:User/Error:AtLeastOneProfileIsNeeded' => 'Almeno un profilo deve essere assegnato a questo utente',
'Class:URP_Dimensions' => 'dimensione',
'Class:URP_Dimensions+' => '',
'Class:URP_Dimensions/Attribute:name' => 'Nome',
'Class:URP_Dimensions/Attribute:name+' => '',
'Class:URP_Dimensions/Attribute:description' => 'Descrizione',
'Class:URP_Dimensions/Attribute:description+' => '',
'Class:URP_Dimensions/Attribute:type' => 'Tipo',
'Class:URP_Dimensions/Attribute:type+' => '',
'Class:URP_UserProfile/Attribute:userlogin' => 'Login',
'Class:URP_UserProfile/Attribute:userlogin+' => '',
'Class:URP_UserProfile/Attribute:profile' => 'Profilo',
'Class:URP_UserProfile/Attribute:profile+' => '',
'Class:URP_UserOrg/Attribute:userlogin' => 'Login',
'Class:URP_UserOrg/Attribute:userlogin+' => '',
'Class:URP_UserOrg/Attribute:allowed_org_name' => 'Organizazione',
'Class:URP_UserOrg/Attribute:allowed_org_name+' => '',
'Class:URP_ProfileProjection' => 'profilo_proiezione',
'Class:URP_ProfileProjection+' => '',
'Class:URP_ProfileProjection/Attribute:dimensionid' => 'Dimensione',
'Class:URP_ProfileProjection/Attribute:dimensionid+' => '',
'Class:URP_ProfileProjection/Attribute:dimension' => 'Dimensione',
'Class:URP_ProfileProjection/Attribute:dimension+' => '',
'Class:URP_ProfileProjection/Attribute:profileid' => 'Profilo',
'Class:URP_ProfileProjection/Attribute:profileid+' => '',
'Class:URP_ProfileProjection/Attribute:profile' => 'Profilo',
'Class:URP_ProfileProjection/Attribute:profile+' => '',
'Class:URP_ProfileProjection/Attribute:value' => 'Valore dell\'espressione',
'Class:URP_ProfileProjection/Attribute:value+' => '',
'Class:URP_ProfileProjection/Attribute:attribute' => 'Attributo',
'Class:URP_ProfileProjection/Attribute:attribute+' => '',
'Class:URP_ClassProjection' => 'classe_proiezione',
'Class:URP_ClassProjection+' => '',
'Class:URP_ClassProjection/Attribute:dimensionid' => 'Dimensione',
'Class:URP_ClassProjection/Attribute:dimensionid+' => '',
'Class:URP_ClassProjection/Attribute:dimension' => 'Dimensione',
'Class:URP_ClassProjection/Attribute:dimension+' => '',
'Class:URP_ClassProjection/Attribute:class' => 'Classe',
'Class:URP_ClassProjection/Attribute:class+' => '',
'Class:URP_ClassProjection/Attribute:value' => 'Valore dell\'espressione',
'Class:URP_ClassProjection/Attribute:value+' => '',
'Class:URP_ClassProjection/Attribute:attribute' => 'Attributo',
'Class:URP_ClassProjection/Attribute:attribute+' => '',
'Class:URP_ActionGrant/Attribute:profile' => 'Profilo',
'Class:URP_ActionGrant/Attribute:profile+' => '',
'Class:URP_StimulusGrant/Attribute:profile' => 'Profilo',
'Class:URP_StimulusGrant/Attribute:profile+' => '',
'Menu:WelcomeMenu' => 'Benveuto', 'Menu:WelcomeMenu' => 'Benveuto',
'Menu:WelcomeMenu+' => '', 'Menu:WelcomeMenu+' => '',
'Menu:WelcomeMenuPage' => 'Benvenuto', 'Menu:WelcomeMenuPage' => 'Benvenuto',
@@ -280,6 +409,7 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
'UI:Error:ObjectAlreadyCreated' => 'Errore: l\'oggetto è già stato creato!', 'UI:Error:ObjectAlreadyCreated' => 'Errore: l\'oggetto è già stato creato!',
'UI:Error:Invalid_Stimulus_On_Object_In_State' => 'Errore: stimolo non valido "%1$s" su un oggetto %2$s nello stato "%3$s".', 'UI:Error:Invalid_Stimulus_On_Object_In_State' => 'Errore: stimolo non valido "%1$s" su un oggetto %2$s nello stato "%3$s".',
'UI:GroupBy:Count' => 'Conteggio', 'UI:GroupBy:Count' => 'Conteggio',
'UI:GroupBy:Count+' => '', 'UI:GroupBy:Count+' => '',
'UI:CountOfObjects' => '%1$d oggetti corrispondenti ai criteri.', 'UI:CountOfObjects' => '%1$d oggetti corrispondenti ai criteri.',
@@ -313,6 +443,7 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
'UI:Menu:CSVExport' => 'CSV Export', 'UI:Menu:CSVExport' => 'CSV Export',
'UI:Menu:Modify' => 'Modifica...', 'UI:Menu:Modify' => 'Modifica...',
'UI:Menu:Delete' => 'Cancella...', 'UI:Menu:Delete' => 'Cancella...',
'UI:Menu:Manage' => 'Gestisci...',
'UI:Menu:BulkDelete' => 'Cancella...', 'UI:Menu:BulkDelete' => 'Cancella...',
'UI:UndefinedObject' => 'non definito', 'UI:UndefinedObject' => 'non definito',
'UI:Document:OpenInNewWindow:Download' => 'Apri in una nuova finestra: %1$s, Scarica: %2$s', 'UI:Document:OpenInNewWindow:Download' => 'Apri in una nuova finestra: %1$s, Scarica: %2$s',
@@ -777,5 +908,6 @@ Quando è associata a un trigger, ad ogni azione è assegnato un numero "ordine"
'UI:ActionNotAllowed' => 'Non hai i permessi per eseguire questa azione su questi oggetti.', 'UI:ActionNotAllowed' => 'Non hai i permessi per eseguire questa azione su questi oggetti.',
'UI:BulkAction:NoObjectSelected' => 'Si prega di selezionare almeno un oggetto per eseguire questa operazione', 'UI:BulkAction:NoObjectSelected' => 'Si prega di selezionare almeno un oggetto per eseguire questa operazione',
'UI:AttemptingToChangeASlaveAttribute_Name' => 'Il campo %1$s on è scrivibile, perché è comandato dalla sincronizzazione dei dati. Valore rimane invariato.', 'UI:AttemptingToChangeASlaveAttribute_Name' => 'Il campo %1$s on è scrivibile, perché è comandato dalla sincronizzazione dei dati. Valore rimane invariato.',
'UI:Button:Refresh' => 'Ricarica',
)); ));
?> ?>

View File

@@ -588,7 +588,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
'UI:Schema:Triggers' => 'トリガ', //'Triggers', 'UI:Schema:Triggers' => 'トリガ', //'Triggers',
'UI:Schema:Relation_Code_Description' => 'リレーション <em>%1$s</em> (%2$s)', //'Relation <em>%1$s</em> (%2$s)', 'UI:Schema:Relation_Code_Description' => 'リレーション <em>%1$s</em> (%2$s)', //'Relation <em>%1$s</em> (%2$s)',
'UI:Schema:RelationDown_Description' => '下へ: %1$s', //'Down: %1$s', 'UI:Schema:RelationDown_Description' => '下へ: %1$s', //'Down: %1$s',
'UI:Schema:RelationUp_Description' => '上へ: $1$s', //'Up: %1$s', 'UI:Schema:RelationUp_Description' => '上へ: %1$s', //'Up: %1$s',
'UI:Schema:RelationPropagates' => '%1$s: %2$d レベルへ伝播、クエリ:%3$s', //'%1$s: propagate to %2$d levels, query: %3$s', 'UI:Schema:RelationPropagates' => '%1$s: %2$d レベルへ伝播、クエリ:%3$s', //'%1$s: propagate to %2$d levels, query: %3$s',
'UI:Schema:RelationDoesNotPropagate' => '%1$s: 伝播しない (%2$d レベル), クエリ: %3$s', //'%1$s: does not propagates (%2$d levels), query: %3$s', 'UI:Schema:RelationDoesNotPropagate' => '%1$s: 伝播しない (%2$d レベル), クエリ: %3$s', //'%1$s: does not propagates (%2$d levels), query: %3$s',
'UI:Schema:Class_ReferencingClasses_From_By' => '%1$s は%2$s クラスから %3$s フィールドにより参照されている', //'%1$s is referenced by the class %2$s via the field %3$s', 'UI:Schema:Class_ReferencingClasses_From_By' => '%1$s は%2$s クラスから %3$s フィールドにより参照されている', //'%1$s is referenced by the class %2$s via the field %3$s',
@@ -613,7 +613,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
'UI:Link_Class_Attributes' => '%1$s 属性', //'%1$s attributes', 'UI:Link_Class_Attributes' => '%1$s 属性', //'%1$s attributes',
'UI:SelectAllToggle+' => '全部を選択 / 全部を非選択', //'Select All / Deselect All', 'UI:SelectAllToggle+' => '全部を選択 / 全部を非選択', //'Select All / Deselect All',
'UI:AddObjectsOf_Class_LinkedWith_Class_Instance' => '%2$s にリンクされた%1$sオブジェクトを追加%3$s', //'Add %1$s objects linked with %2$s: %3$s', 'UI:AddObjectsOf_Class_LinkedWith_Class_Instance' => '%2$s にリンクされた%1$sオブジェクトを追加%3$s', //'Add %1$s objects linked with %2$s: %3$s',
'UI:AddObjectsOf_Class_LinkedWith_Class' => '$1$s オブジェクトを%2$sとのリンクに追加', //'Add %1$s objects to link with the %2$s', 'UI:AddObjectsOf_Class_LinkedWith_Class' => '%1$s オブジェクトを%2$sとのリンクに追加', //'Add %1$s objects to link with the %2$s',
'UI:ManageObjectsOf_Class_LinkedWith_Class_Instance' => '%2$s とりんくされた%1$sオブジェクトを管理する: %3$s', //'Manage %1$s objects linked with %2$s: %3$s', 'UI:ManageObjectsOf_Class_LinkedWith_Class_Instance' => '%2$s とりんくされた%1$sオブジェクトを管理する: %3$s', //'Manage %1$s objects linked with %2$s: %3$s',
'UI:AddLinkedObjectsOf_Class' => '%1$s を追加...', //'Add %1$ss...', 'UI:AddLinkedObjectsOf_Class' => '%1$s を追加...', //'Add %1$ss...',
'UI:RemoveLinkedObjectsOf_Class' => '選択したオブジェクトを除外', //'Remove selected objects', 'UI:RemoveLinkedObjectsOf_Class' => '選択したオブジェクトを除外', //'Remove selected objects',
@@ -860,8 +860,8 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
'UI:iTopVersion:Long' => 'iTopバージョン%1$s-%2$s, %3$sビルド', // 'iTop version %1$s-%2$s built on %3$s', 'UI:iTopVersion:Long' => 'iTopバージョン%1$s-%2$s, %3$sビルド', // 'iTop version %1$s-%2$s built on %3$s',
'UI:PropertiesTab' => 'プロパティ', // 'Properties', 'UI:PropertiesTab' => 'プロパティ', // 'Properties',
'UI:OpenDocumentInNewWindow_' => '新規ウィンドウで本ドキュメント: $1$sを開く', // 'Open this document in a new window: %1$s', 'UI:OpenDocumentInNewWindow_' => '新規ウィンドウで本ドキュメント: %1$sを開く', // 'Open this document in a new window: %1$s',
'UI:DownloadDocument_' => '本ドキュメント: $1$sをダウンロードする', // 'Download this document: %1$s', 'UI:DownloadDocument_' => '本ドキュメント: %1$sをダウンロードする', // 'Download this document: %1$s',
'UI:Document:NoPreview' => 'このタイプのドキュメントはプレビューできません。', // 'No preview is available for this type of document', 'UI:Document:NoPreview' => 'このタイプのドキュメントはプレビューできません。', // 'No preview is available for this type of document',
'UI:DeadlineMissedBy_duration' => '%1$s によって消去されました。', // 'Missed by %1$s', 'UI:DeadlineMissedBy_duration' => '%1$s によって消去されました。', // 'Missed by %1$s',
@@ -907,6 +907,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
'Portal:RemoveAttachment' => ' 添付を除去する ', // ' Remove Attachment ', 'Portal:RemoveAttachment' => ' 添付を除去する ', // ' Remove Attachment ',
'Portal:Attachment_No_To_Ticket_Name' => '#%1$d を$2$s ($3$s)に添付する', // 'Attachment #%1$d to %2$s (%3$s)', 'Portal:Attachment_No_To_Ticket_Name' => '#%1$d を$2$s ($3$s)に添付する', // 'Attachment #%1$d to %2$s (%3$s)',
'Enum:Undefined' => '定義されていません', // 'Undefined', 'Enum:Undefined' => '定義されていません', // 'Undefined',
'UI:Button:Refresh' => '更新', // 'Refresh',
)); ));

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -872,6 +872,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Portal:ErrorNoContactForThisUser' => 'Ошибка: текющий пользователь не ассоциирован с Контактом/Человеком. Пожалуйста свяжитесь с вашим администратором.', 'Portal:ErrorNoContactForThisUser' => 'Ошибка: текющий пользователь не ассоциирован с Контактом/Человеком. Пожалуйста свяжитесь с вашим администратором.',
'Enum:Undefined' => 'Неопределён', 'Enum:Undefined' => 'Неопределён',
'UI:Button:Refresh' => 'Обновить',
)); ));

View File

@@ -871,8 +871,6 @@ Tetikleme gerçekleştiriğinde işlemler tanımlanan sıra numarası ile gerçe
'Portal:ErrorNoContactForThisUser' => 'Hata: mevcut kullanıcının irtibat bilgisi yok. Sistem yöneticisi ile irtibata geçiniz.', 'Portal:ErrorNoContactForThisUser' => 'Hata: mevcut kullanıcının irtibat bilgisi yok. Sistem yöneticisi ile irtibata geçiniz.',
'Enum:Undefined' => 'Tanımsız', 'Enum:Undefined' => 'Tanımsız',
'UI:Button:Refresh' => 'Yenile',
)); ));
?> ?>

View File

@@ -869,6 +869,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Portal:ErrorNoContactForThisUser' => '错误: 当前用户没有和一个联系人或人员关联. 请联系您的系统管理员.', 'Portal:ErrorNoContactForThisUser' => '错误: 当前用户没有和一个联系人或人员关联. 请联系您的系统管理员.',
'Enum:Undefined' => '未定义', 'Enum:Undefined' => '未定义',
'UI:Button:Refresh' => '刷新',
)); ));

BIN
images/favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -13,17 +13,19 @@
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper) function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper, sAttCode, bSearchMode)
{ {
this.id = id; this.id = id;
this.sTargetClass = sTargetClass; this.sTargetClass = sTargetClass;
this.sFilter = sFilter; this.sFilter = sFilter;
this.sTitle = sTitle; this.sTitle = sTitle;
this.sAttCode = sAttCode;
this.emptyHtml = ''; // content to be displayed when the search results are empty (when opening the dialog) this.emptyHtml = ''; // content to be displayed when the search results are empty (when opening the dialog)
this.emptyOnClose = true; // Workaround for the JQuery dialog being very slow when opening and closing if the content contains many INPUT tags this.emptyOnClose = true; // Workaround for the JQuery dialog being very slow when opening and closing if the content contains many INPUT tags
this.oWizardHelper = oWizHelper; this.oWizardHelper = oWizHelper;
this.ajax_request = null; this.ajax_request = null;
this.bSelectMode = bSelectMode; // true if the edited field is a SELECT, false if it's an autocomplete this.bSelectMode = bSelectMode; // true if the edited field is a SELECT, false if it's an autocomplete
this.bSearchMode = bSearchMode; // true if selecting a value in the context of a search form
this.v_html = ''; this.v_html = '';
var me = this; var me = this;
@@ -61,10 +63,23 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
var theMap = { sAttCode: me.sAttCode, var theMap = { sAttCode: me.sAttCode,
iInputId: me.id, iInputId: me.id,
sTitle: me.sTitle, sTitle: me.sTitle,
sAttCode: me.sAttCode,
sTargetClass: me.sTargetClass, sTargetClass: me.sTargetClass,
bSearchMode: me.bSearchMode,
operation: 'objectSearchForm' operation: 'objectSearchForm'
} }
if (me.oWizardHelper == null)
{
theMap['json'] = '';
}
else
{
// Not inside a "search form", updating a real object
me.oWizardHelper.UpdateWizard();
theMap['json'] = me.oWizardHelper.ToJSON();
}
// Make sure that we cancel any pending request before issuing another // Make sure that we cancel any pending request before issuing another
// since responses may arrive in arbitrary order // since responses may arrive in arbitrary order
me.StopPendingRequest(); me.StopPendingRequest();
@@ -125,7 +140,8 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
{ {
var theMap = { sTargetClass: me.sTargetClass, var theMap = { sTargetClass: me.sTargetClass,
iInputId: me.id, iInputId: me.id,
sFilter: me.sFilter sFilter: me.sFilter,
bSearchMode: me.bSearchMode
} }
// Gather the parameters from the search form // Gather the parameters from the search form
@@ -152,6 +168,7 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
theMap['sRemoteClass'] = theMap['class']; // swap 'class' (defined in the form) and 'remoteClass' theMap['sRemoteClass'] = theMap['class']; // swap 'class' (defined in the form) and 'remoteClass'
theMap.operation = 'searchObjectsToSelect'; // Override what is defined in the form itself theMap.operation = 'searchObjectsToSelect'; // Override what is defined in the form itself
theMap.sAttCode = me.sAttCode,
sSearchAreaId = '#dr_'+me.id; sSearchAreaId = '#dr_'+me.id;
//$(sSearchAreaId).html('<div style="text-align:center;width:100%;height:24px;vertical-align:middle;"><img src="../images/indicator.gif" /></div>'); //$(sSearchAreaId).html('<div style="text-align:center;width:100%;height:24px;vertical-align:middle;"><img src="../images/indicator.gif" /></div>');
@@ -200,6 +217,8 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
var theMap = { sTargetClass: me.sTargetClass, var theMap = { sTargetClass: me.sTargetClass,
iInputId: me.id, iInputId: me.id,
iObjectId: iObjectId, iObjectId: iObjectId,
sAttCode: me.sAttCode,
bSearchMode: me.bSearchMode,
operation: 'getObjectName' operation: 'getObjectName'
} }
@@ -262,6 +281,7 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
me.oWizardHelper.UpdateWizard(); me.oWizardHelper.UpdateWizard();
var theMap = { sTargetClass: me.sTargetClass, var theMap = { sTargetClass: me.sTargetClass,
iInputId: me.id, iInputId: me.id,
sAttCode: me.sAttCode,
'json': me.oWizardHelper.ToJSON(), 'json': me.oWizardHelper.ToJSON(),
operation: 'objectCreationForm' operation: 'objectCreationForm'
} }
@@ -323,6 +343,7 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
$('#'+sFormId).block(); $('#'+sFormId).block();
var theMap = { sTargetClass: me.sTargetClass, var theMap = { sTargetClass: me.sTargetClass,
iInputId: me.id, iInputId: me.id,
sAttCode: me.sAttCode,
'json': me.oWizardHelper.ToJSON() 'json': me.oWizardHelper.ToJSON()
} }
@@ -403,6 +424,8 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
var theMap = { sTargetClass: me.sTargetClass, var theMap = { sTargetClass: me.sTargetClass,
sInputId: me.id, sInputId: me.id,
sFilter: me.sFilter, sFilter: me.sFilter,
bSearchMode: me.bSearchMode,
sAttCode: me.sAttCode,
value: $('#'+me.id).val() value: $('#'+me.id).val()
}; };
@@ -484,6 +507,8 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
var theMap = { sTargetClass: me.sTargetClass, var theMap = { sTargetClass: me.sTargetClass,
iInputId: me.id, iInputId: me.id,
iObjectId: iObjectId, iObjectId: iObjectId,
sAttCode: me.sAttCode,
bSearchMode: me.bSearchMode,
operation: 'getObjectName' operation: 'getObjectName'
} }

View File

@@ -1,5 +1,5 @@
// JavaScript Document // JavaScript Document
function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates) function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizHelper)
{ {
this.id = id; this.id = id;
this.iInputId = iInputId; this.iInputId = iInputId;
@@ -7,6 +7,7 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates)
this.sAttCode = sAttCode; this.sAttCode = sAttCode;
this.sSuffix = sSuffix; this.sSuffix = sSuffix;
this.bDuplicates = bDuplicates; this.bDuplicates = bDuplicates;
this.oWizardHelper = oWizHelper;
var me = this; var me = this;
this.Init = function() this.Init = function()
{ {
@@ -63,9 +64,28 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates)
this.AddObjects = function() this.AddObjects = function()
{ {
$('#dlg_'+me.id).dialog('open'); var me = this;
this.UpdateSizes(null, null); $('#'+me.id+'_indicatorAdd').html('&nbsp;<img src="../images/indicator.gif"/>');
this.SearchObjectsToAdd(); me.oWizardHelper.UpdateWizard();
var theMap = { sAttCode: me.sAttCode,
iInputId: me.iInputId,
sSuffix: me.sSuffix,
bDuplicates: me.bDuplicates,
'class' : me.sClass,
operation: 'addObjects',
json: me.oWizardHelper.ToJSON()
};
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,
function(data)
{
$('#dlg_'+me.id).html(data);
$('#dlg_'+me.id).dialog('open');
me.UpdateSizes(null, null);
me.SearchObjectsToAdd();
$('#'+me.id+'_indicatorAdd').html('');
},
'html'
);
} }
this.SearchObjectsToAdd = function() this.SearchObjectsToAdd = function()
@@ -202,6 +222,16 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates)
// } // }
theMap['operation'] = 'doAddObjects'; theMap['operation'] = 'doAddObjects';
if (me.oWizardHelper == null)
{
theMap['json'] = '';
}
else
{
// Not inside a "search form", updating a real object
me.oWizardHelper.UpdateWizard();
theMap['json'] = me.oWizardHelper.ToJSON();
}
$('#busy_'+me.iInputId).html('&nbsp;<img src="../images/indicator.gif"/>'); $('#busy_'+me.iInputId).html('&nbsp;<img src="../images/indicator.gif"/>');
// Run the query and display the results // Run the query and display the results
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap, $.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,

View File

@@ -119,8 +119,18 @@ function ReloadSearchForm(divId, sClassName, sBaseClass, sContext)
} }
sAction = $('#ds_'+divId+' form').attr('action'); sAction = $('#ds_'+divId+' form').attr('action');
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?'+sContext, // Save the current values in the form
{ operation: 'search_form', className: sClassName, baseClass: sBaseClass, currentId: divId, action: sAction }, var oMap = {};
$('#ds_'+divId+" form :input[name!='']").each(function() {
oMap[this.name] = this.value;
});
oMap.operation = 'search_form';
oMap.className = sClassName;
oMap.baseClass = sBaseClass;
oMap.currentId = divId;
oMap.action = sAction;
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?'+sContext, oMap,
function(data) { function(data) {
oDiv.empty(); oDiv.empty();
oDiv.append(data); oDiv.append(data);

View File

@@ -136,6 +136,12 @@ function WizardHelper(sClass, sFormPrefix, sState)
} }
} }
this.UpdateWizardToJSON = function ()
{
this.UpdateWizard();
return this.ToJSON()
}
this.AjaxQueryServer = function () this.AjaxQueryServer = function ()
{ {
//console.log('data sent:', this.ToJSON()); //console.log('data sent:', this.ToJSON());

View File

@@ -120,7 +120,7 @@ class UserLDAP extends UserInternal
$aEntry = ldap_get_entries($hDS, $hSearchResult); $aEntry = ldap_get_entries($hDS, $hSearchResult);
$sUserDN = $aEntry[0]['dn']; $sUserDN = $aEntry[0]['dn'];
$bUserBind = @ldap_bind($hDS, $sUserDN, $sPassword); $bUserBind = @ldap_bind($hDS, $sUserDN, $sPassword);
if ($bUserBind !== false) if (($bUserBind !== false) && !empty($sPassword))
{ {
ldap_unbind($hDS); ldap_unbind($hDS);
return true; // Password Ok return true; // Password Ok

View File

@@ -1,50 +1,32 @@
<?php <?php
// Copyright (C) 2010 Combodo SARL // Copyright (C) 2010 Combodo SARL
// //
// This program is free software; you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License. // the Free Software Foundation; version 3 of the License.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/** /**
* Localized data * @author Erwan Taloc <erwan.taloc@combodo.com>
* * @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Erwan Taloc <erwan.taloc@combodo.com> * @author Denis Flaven <denis.flaven@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com> * @author Stephan Rosenke <stephan.rosenke@itomig.de>
* @author Denis Flaven <denis.flaven@combodo.com>
* @author Stephan Rosenke <stephan.rosenke@itomig.de> * @licence http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL */
*/
Dict::Add('DE DE', 'German', 'Deutsch', array(
// Dictionnay conventions 'Class:UserLocal' => 'iTop-Benutzer',
// Class:<class_name> 'Class:UserLocal+' => 'Benutzer, der von iTop authentifiziert wird',
// Class:<class_name>+ 'Class:UserLocal/Attribute:password' => 'Passwort',
// Class:<class_name>/Attribute:<attribute_code> 'Class:UserLocal/Attribute:password+' => 'Benutzerpasswort',
// Class:<class_name>/Attribute:<attribute_code>+ ));
// Class:<class_name>/Attribute:<attribute_code>/Value:<value> ?>
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+
// Class:<class_name>/Stimulus:<stimulus_code>
// Class:<class_name>/Stimulus:<stimulus_code>+
//
// Class: UserLocal
//
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:UserLocal' => 'iTop-Benutzer',
'Class:UserLocal+' => 'Benutzer von iTop authentifiziert',
'Class:UserLocal/Attribute:password' => 'Passwort',
'Class:UserLocal/Attribute:password+' => 'Benutzerpasswort',
));
?>

View File

@@ -389,6 +389,7 @@ EOF
{ {
$('#attachment_'+att_id).attr('name', 'removed_attachments[]'); $('#attachment_'+att_id).attr('name', 'removed_attachments[]');
$('#display_attachment_'+att_id).hide(); $('#display_attachment_'+att_id).hide();
$('#attachment_plugin').trigger('remove_attachment', [att_id]);
return false; // Do not submit the form ! return false; // Do not submit the form !
} }
function ajaxFileUpload() function ajaxFileUpload()
@@ -433,6 +434,8 @@ EOF
{ {
$('#display_attachment_'+data.att_id).hover( function() { $(this).children(':button').toggleClass('btn_hidden'); } ); $('#display_attachment_'+data.att_id).hover( function() { $(this).children(':button').toggleClass('btn_hidden'); } );
} }
$('#attachment_plugin').trigger('add_attachment', [data.att_id, data.msg]);
//alert(data.msg); //alert(data.msg);
} }
} }
@@ -465,6 +468,7 @@ EOF
$oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file" onChange="ajaxFileUpload();"><span style="display:none;" id="attachment_loading">&nbsp;<img src="../images/indicator.gif"></span> '.$sMaxUpload); $oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file" onChange="ajaxFileUpload();"><span style="display:none;" id="attachment_loading">&nbsp;<img src="../images/indicator.gif"></span> '.$sMaxUpload);
//$oPage->p('<input type="button" onClick="ajaxFileUpload();" value=" Upload !">'); //$oPage->p('<input type="button" onClick="ajaxFileUpload();" value=" Upload !">');
$oPage->p('<span style="display:none;" id="attachment_loading">Loading, please wait...</span>'); $oPage->p('<span style="display:none;" id="attachment_loading">Loading, please wait...</span>');
$oPage->p('<input type="hidden" id="attachment_plugin" name="attachment_plugin"/>');
$oPage->add('</fieldset>'); $oPage->add('</fieldset>');
if ($this->m_bDeleteEnabled) if ($this->m_bDeleteEnabled)
{ {
@@ -495,6 +499,12 @@ EOF
protected static function UpdateAttachments($oObject, $oChange = null) protected static function UpdateAttachments($oObject, $oChange = null)
{ {
if (utils::ReadParam('attachment_plugin', 'not-in-form') == 'not-in-form')
{
// Workaround to an issue in iTop < 2.0
// Leave silently if there is no trace of the attachment form
return;
}
$iTransactionId = utils::ReadParam('transaction_id', null); $iTransactionId = utils::ReadParam('transaction_id', null);
if (!is_null($iTransactionId)) if (!is_null($iTransactionId))
{ {

View File

@@ -1,348 +1,304 @@
<?php <?php
// Copyright (C) 2010 Combodo SARL // Copyright (C) 2010 Combodo SARL
// //
// This program is free software; you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License. // the Free Software Foundation; version 3 of the License.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/** /**
* Localized data * @author Erwan Taloc <erwan.taloc@combodo.com>
* * @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Erwan Taloc <erwan.taloc@combodo.com> * @author Denis Flaven <denis.flaven@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com> * @author Stephan Rosenke <stephan.rosenke@itomig.de>
* @author Denis Flaven <denis.flaven@combodo.com>
* @author Stephan Rosenke <stephan.rosenke@itomig.de> * @licence http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL */
*/
Dict::Add('DE DE', 'German', 'Deutsch', array(
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:RoutineChange' => 'Routine Change',
'Menu:ChangeManagement' => 'Change Management', 'Class:RoutineChange+' => '',
'Menu:Change:Overview' => 'Übersicht', 'Class:RoutineChange/Stimulus:ev_validate' => 'Validieren',
'Menu:Change:Overview+' => '', 'Class:RoutineChange/Stimulus:ev_validate+' => '',
'Menu:NewChange' => 'Neuer Change', 'Class:RoutineChange/Stimulus:ev_reject' => 'Ablehnen',
'Menu:NewChange+' => 'Ein neues Change Ticket erstellen', 'Class:RoutineChange/Stimulus:ev_assign' => 'Zuweisen',
'Menu:SearchChanges' => 'Nach Changes suchen', 'Class:RoutineChange/Stimulus:ev_assign+' => '',
'Menu:SearchChanges+' => 'Nach Change Tickets suchen', 'Class:RoutineChange/Stimulus:ev_reopen' => 'Wiedereröffnen',
'Menu:Change:Shortcuts' => 'Shortcuts', 'Class:RoutineChange/Stimulus:ev_reopen+' => '',
'Menu:Change:Shortcuts+' => '', 'Class:RoutineChange/Stimulus:ev_plan' => 'Planen',
'Menu:WaitingAcceptance' => 'Changes, die auf Bestätigung warten', 'Class:RoutineChange/Stimulus:ev_plan+' => '',
'Menu:WaitingAcceptance+' => '', 'Class:RoutineChange/Stimulus:ev_approve' => 'Genehmigen',
'Menu:WaitingApproval' => 'Changes, die auf Genehmigung warten', 'Class:RoutineChange/Stimulus:ev_replan' => 'Umplanen',
'Menu:WaitingApproval+' => '', 'Class:RoutineChange/Stimulus:ev_replan+' => '',
'Menu:Changes' => 'Offene Changes', 'Class:RoutineChange/Stimulus:ev_notapprove' => 'Nicht genehmigen',
'Menu:Changes+' => 'Alle Offene Changes', 'Class:RoutineChange/Stimulus:ev_implement' => 'Implementieren',
'Menu:MyChanges' => 'An mich zugewiesene Changes', 'Class:RoutineChange/Stimulus:ev_implement+' => '',
'Menu:MyChanges+' => 'An mich zugewiesene Changes (als Bearbeiter)', 'Class:RoutineChange/Stimulus:ev_monitor' => 'Überwachen',
)); 'Class:RoutineChange/Stimulus:ev_monitor+' => '',
'Class:RoutineChange/Stimulus:ev_finish' => 'Abschließen',
// Dictionnay conventions 'Class:RoutineChange/Stimulus:ev_finish+' => '',
// Class:<class_name> 'Class:NormalChange' => 'Normaler Change',
// Class:<class_name>+ 'Class:NormalChange+' => '',
// Class:<class_name>/Attribute:<attribute_code> 'Class:NormalChange/Attribute:acceptance_date' => 'Datum der Bewilligung',
// Class:<class_name>/Attribute:<attribute_code>+ 'Class:NormalChange/Attribute:acceptance_date+' => '',
// Class:<class_name>/Attribute:<attribute_code>/Value:<value> 'Class:NormalChange/Attribute:acceptance_comment' => 'Kommentar zur Bewilligung',
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+ 'Class:NormalChange/Attribute:acceptance_comment+' => '',
// Class:<class_name>/Stimulus:<stimulus_code> 'Class:NormalChange/Stimulus:ev_validate' => 'Validieren',
// Class:<class_name>/Stimulus:<stimulus_code>+ 'Class:NormalChange/Stimulus:ev_validate+' => '',
'Class:NormalChange/Stimulus:ev_reject' => 'Ablehnen',
'Class:NormalChange/Stimulus:ev_reject+' => '',
// 'Class:NormalChange/Stimulus:ev_assign' => 'Zuweisen',
// Class: Change 'Class:NormalChange/Stimulus:ev_assign+' => '',
// 'Class:NormalChange/Stimulus:ev_reopen' => 'Wiedereröffnen',
'Class:NormalChange/Stimulus:ev_reopen+' => '',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:NormalChange/Stimulus:ev_plan' => 'Planen',
'Class:Change' => 'Change', 'Class:NormalChange/Stimulus:ev_plan+' => '',
'Class:Change+' => '', 'Class:NormalChange/Stimulus:ev_approve' => 'Bestätigen',
'Class:Change/Attribute:start_date' => 'Geplanter Start', 'Class:NormalChange/Stimulus:ev_approve+' => '',
'Class:Change/Attribute:start_date+' => '', 'Class:NormalChange/Stimulus:ev_replan' => 'Umplanen',
'Class:Change/Attribute:status' => 'Status', 'Class:NormalChange/Stimulus:ev_replan+' => '',
'Class:Change/Attribute:status+' => '', 'Class:NormalChange/Stimulus:ev_notapprove' => 'Bestätigen zurücknehmen',
'Class:Change/Attribute:status/Value:new' => 'Neu', 'Class:NormalChange/Stimulus:ev_notapprove+' => '',
'Class:Change/Attribute:status/Value:new+' => '', 'Class:NormalChange/Stimulus:ev_implement' => 'Implementieren',
'Class:Change/Attribute:status/Value:validated' => 'Validiert', 'Class:NormalChange/Stimulus:ev_implement+' => '',
'Class:Change/Attribute:status/Value:validated+' => '', 'Class:NormalChange/Stimulus:ev_monitor' => 'Überwachen',
'Class:Change/Attribute:status/Value:rejected' => 'Abgelehnt', 'Class:NormalChange/Stimulus:ev_monitor+' => '',
'Class:Change/Attribute:status/Value:rejected+' => '', 'Class:NormalChange/Stimulus:ev_finish' => 'Abschließen',
'Class:Change/Attribute:status/Value:assigned' => 'Zugewiesen', 'Class:NormalChange/Stimulus:ev_finish+' => '',
'Class:Change/Attribute:status/Value:assigned+' => '', 'Class:EmergencyChange' => 'Emergency Change',
'Class:Change/Attribute:status/Value:plannedscheduled' => 'Geplant und angesetzt', 'Class:EmergencyChange+' => '',
'Class:Change/Attribute:status/Value:plannedscheduled+' => '', 'Class:EmergencyChange/Stimulus:ev_validate' => 'Validieren',
'Class:Change/Attribute:status/Value:approved' => 'Genehmigt', 'Class:EmergencyChange/Stimulus:ev_validate+' => '',
'Class:Change/Attribute:status/Value:approved+' => '', 'Class:EmergencyChange/Stimulus:ev_reject' => 'Ablehnen',
'Class:Change/Attribute:status/Value:notapproved' => 'Nicht genehmigt', 'Class:EmergencyChange/Stimulus:ev_reject+' => '',
'Class:Change/Attribute:status/Value:notapproved+' => '', 'Class:EmergencyChange/Stimulus:ev_assign' => 'Zuweisen',
'Class:Change/Attribute:status/Value:implemented' => 'Implementiert', 'Class:EmergencyChange/Stimulus:ev_assign+' => '',
'Class:Change/Attribute:status/Value:implemented+' => '', 'Class:EmergencyChange/Stimulus:ev_reopen' => 'Wiedereröffnen',
'Class:Change/Attribute:status/Value:monitored' => 'Überwacht', 'Class:EmergencyChange/Stimulus:ev_reopen+' => '',
'Class:Change/Attribute:status/Value:monitored+' => '', 'Class:EmergencyChange/Stimulus:ev_plan' => 'Planen',
'Class:Change/Attribute:status/Value:closed' => 'Geschlossen', 'Class:EmergencyChange/Stimulus:ev_plan+' => '',
'Class:Change/Attribute:status/Value:closed+' => '', 'Class:EmergencyChange/Stimulus:ev_approve' => 'Bestätigen',
'Class:Change/Attribute:reason' => 'Ursache', 'Class:EmergencyChange/Stimulus:ev_approve+' => '',
'Class:Change/Attribute:reason+' => '', 'Class:EmergencyChange/Stimulus:ev_replan' => 'Umplanen',
'Class:Change/Attribute:requestor_id' => 'Antragsteller', 'Class:EmergencyChange/Stimulus:ev_replan+' => '',
'Class:Change/Attribute:requestor_id+' => '', 'Class:EmergencyChange/Stimulus:ev_notapprove' => 'Bestätigen zurücknehmen',
'Class:Change/Attribute:requestor_email' => 'Antragsteller', 'Class:EmergencyChange/Stimulus:ev_notapprove+' => '',
'Class:Change/Attribute:requestor_email+' => '', 'Class:EmergencyChange/Stimulus:ev_implement' => 'Implementieren',
'Class:Change/Attribute:org_id' => 'Kunde', 'Class:EmergencyChange/Stimulus:ev_implement+' => '',
'Class:Change/Attribute:org_id+' => '', 'Class:EmergencyChange/Stimulus:ev_monitor' => 'Überwachen',
'Class:Change/Attribute:org_name' => 'Kunde', 'Class:EmergencyChange/Stimulus:ev_monitor+' => '',
'Class:Change/Attribute:org_name+' => '', 'Class:EmergencyChange/Stimulus:ev_finish' => 'Abschließen',
'Class:Change/Attribute:workgroup_id' => 'Arbeitsgruppe', 'Class:EmergencyChange/Stimulus:ev_finish+' => '',
'Class:Change/Attribute:workgroup_id+' => '', 'Menu:ChangeManagement' => 'Change Management',
'Class:Change/Attribute:workgroup_name' => 'Arbeitsgruppe', 'Menu:Change:Overview' => 'Übersicht',
'Class:Change/Attribute:workgroup_name+' => '', 'Menu:Change:Overview+' => '',
'Class:Change/Attribute:creation_date' => 'Erstellt', 'Menu:NewChange' => 'Neuer Change',
'Class:Change/Attribute:creation_date+' => '', 'Menu:NewChange+' => 'Ein neues Change Ticket erstellen',
'Class:Change/Attribute:last_update' => 'Letzte Aktualisierung', 'Menu:SearchChanges' => 'Nach Changes suchen',
'Class:Change/Attribute:last_update+' => '', 'Menu:SearchChanges+' => 'Nach Change Tickets suchen',
'Class:Change/Attribute:end_date' => 'Enddatum', 'Menu:Change:Shortcuts' => 'Shortcuts',
'Class:Change/Attribute:end_date+' => '', 'Menu:Change:Shortcuts+' => '',
'Class:Change/Attribute:close_date' => 'Geschlossen', 'Menu:WaitingAcceptance' => 'Changes, die auf Bestätigung warten',
'Class:Change/Attribute:close_date+' => '', 'Menu:WaitingAcceptance+' => '',
'Class:Change/Attribute:impact' => 'Auswirkung', 'Menu:WaitingApproval' => 'Changes, die auf Genehmigung warten',
'Class:Change/Attribute:impact+' => '', 'Menu:WaitingApproval+' => '',
'Class:Change/Attribute:agent_id' => 'Bearbeiter', 'Menu:Changes' => 'Offene Changes',
'Class:Change/Attribute:agent_id+' => '', 'Menu:Changes+' => 'Alle Offene Changes',
'Class:Change/Attribute:agent_name' => 'Bearbeiter', 'Menu:MyChanges' => 'An mich zugewiesene Changes',
'Class:Change/Attribute:agent_name+' => '', 'Menu:MyChanges+' => 'An mich zugewiesene Changes (als Bearbeiter)',
'Class:Change/Attribute:agent_email' => 'Bearbeiter', 'Class:Change' => 'Change',
'Class:Change/Attribute:agent_email+' => '', 'Class:Change+' => '',
'Class:Change/Attribute:supervisor_group_id' => 'Aufsichts-Team', 'Class:Change/Attribute:start_date' => 'Geplanter Start',
'Class:Change/Attribute:supervisor_group_id+' => '', 'Class:Change/Attribute:start_date+' => '',
'Class:Change/Attribute:supervisor_group_name' => 'Aufsichts-Team', 'Class:Change/Attribute:status' => 'Status',
'Class:Change/Attribute:supervisor_group_name+' => '', 'Class:Change/Attribute:status+' => '',
'Class:Change/Attribute:supervisor_id' => 'Aufsicht', 'Class:Change/Attribute:status/Value:new' => 'Neu',
'Class:Change/Attribute:supervisor_id+' => '', 'Class:Change/Attribute:status/Value:new+' => '',
'Class:Change/Attribute:supervisor_email' => 'Aufsicht', 'Class:Change/Attribute:status/Value:validated' => 'Validiert',
'Class:Change/Attribute:supervisor_email+' => '', 'Class:Change/Attribute:status/Value:validated+' => '',
'Class:Change/Attribute:manager_group_id' => 'Manager-Team', 'Class:Change/Attribute:status/Value:rejected' => 'Abgelehnt',
'Class:Change/Attribute:manager_group_id+' => '', 'Class:Change/Attribute:status/Value:rejected+' => '',
'Class:Change/Attribute:manager_group_name' => 'Manager-Team', 'Class:Change/Attribute:status/Value:assigned' => 'Zugewiesen',
'Class:Change/Attribute:manager_group_name+' => '', 'Class:Change/Attribute:status/Value:assigned+' => '',
'Class:Change/Attribute:manager_id' => 'Manager', 'Class:Change/Attribute:status/Value:plannedscheduled' => 'Geplant und angesetzt',
'Class:Change/Attribute:manager_id+' => '', 'Class:Change/Attribute:status/Value:plannedscheduled+' => '',
'Class:Change/Attribute:manager_email' => 'Manager', 'Class:Change/Attribute:status/Value:approved' => 'Genehmigt',
'Class:Change/Attribute:manager_email+' => '', 'Class:Change/Attribute:status/Value:approved+' => '',
'Class:Change/Attribute:outage' => 'Ausfall', 'Class:Change/Attribute:status/Value:notapproved' => 'Nicht genehmigt',
'Class:Change/Attribute:outage+' => '', 'Class:Change/Attribute:status/Value:notapproved+' => '',
'Class:Change/Attribute:outage/Value:yes' => 'Ja', 'Class:Change/Attribute:status/Value:implemented' => 'Implementiert',
'Class:Change/Attribute:outage/Value:yes+' => '', 'Class:Change/Attribute:status/Value:implemented+' => '',
'Class:Change/Attribute:outage/Value:no' => 'Nein', 'Class:Change/Attribute:status/Value:monitored' => 'Überwacht',
'Class:Change/Attribute:outage/Value:no+' => '', 'Class:Change/Attribute:status/Value:monitored+' => '',
'Class:Change/Attribute:change_request' => 'Anfrage', 'Class:Change/Attribute:status/Value:closed' => 'Geschlossen',
'Class:Change/Attribute:change_request+' => '', 'Class:Change/Attribute:status/Value:closed+' => '',
'Class:Change/Attribute:fallback' => 'Fallback-Plan', 'Class:Change/Attribute:reason' => 'Ursache',
'Class:Change/Attribute:fallback+' => '', 'Class:Change/Attribute:reason+' => '',
'Class:Change/Stimulus:ev_validate' => 'Validieren', 'Class:Change/Attribute:requestor_id' => 'Antragsteller',
'Class:Change/Stimulus:ev_validate+' => '', 'Class:Change/Attribute:requestor_id+' => '',
'Class:Change/Stimulus:ev_reject' => 'Ablehnen', 'Class:Change/Attribute:requestor_email' => 'Antragsteller',
'Class:Change/Stimulus:ev_reject+' => '', 'Class:Change/Attribute:requestor_email+' => '',
'Class:Change/Stimulus:ev_assign' => 'Zuweisen', 'Class:Change/Attribute:org_id' => 'Kunde',
'Class:Change/Stimulus:ev_assign+' => '', 'Class:Change/Attribute:org_id+' => '',
'Class:Change/Stimulus:ev_reopen' => 'Wiedereröffnen', 'Class:Change/Attribute:org_name' => 'Kunde',
'Class:Change/Stimulus:ev_reopen+' => '', 'Class:Change/Attribute:org_name+' => '',
'Class:Change/Stimulus:ev_plan' => 'Planen', 'Class:Change/Attribute:workgroup_id' => 'Arbeitsgruppe',
'Class:Change/Stimulus:ev_plan+' => '', 'Class:Change/Attribute:workgroup_id+' => '',
'Class:Change/Stimulus:ev_approve' => 'Bestätigen', 'Class:Change/Attribute:workgroup_name' => 'Arbeitsgruppe',
'Class:Change/Stimulus:ev_approve+' => '', 'Class:Change/Attribute:workgroup_name+' => '',
'Class:Change/Stimulus:ev_replan' => 'Umplanen', 'Class:Change/Attribute:creation_date' => 'Erstellt',
'Class:Change/Stimulus:ev_replan+' => '', 'Class:Change/Attribute:creation_date+' => '',
'Class:Change/Stimulus:ev_notapprove' => 'Ablehnen', 'Class:Change/Attribute:last_update' => 'Letzte Aktualisierung',
'Class:Change/Stimulus:ev_notapprove+' => '', 'Class:Change/Attribute:last_update+' => '',
'Class:Change/Stimulus:ev_implement' => 'Implementieren', 'Class:Change/Attribute:end_date' => 'Enddatum',
'Class:Change/Stimulus:ev_implement+' => '', 'Class:Change/Attribute:end_date+' => '',
'Class:Change/Stimulus:ev_monitor' => 'Überwachen', 'Class:Change/Attribute:close_date' => 'Geschlossen',
'Class:Change/Stimulus:ev_monitor+' => '', 'Class:Change/Attribute:close_date+' => '',
'Class:Change/Stimulus:ev_finish' => 'Abschließen', 'Class:Change/Attribute:impact' => 'Auswirkung',
'Class:Change/Stimulus:ev_finish+' => '', 'Class:Change/Attribute:impact+' => '',
)); 'Class:Change/Attribute:agent_id' => 'Bearbeiter',
'Class:Change/Attribute:agent_id+' => '',
// 'Class:Change/Attribute:agent_name' => 'Bearbeiter',
// Class: RoutineChange 'Class:Change/Attribute:agent_name+' => '',
// 'Class:Change/Attribute:agent_email' => 'Bearbeiter',
'Class:Change/Attribute:agent_email+' => '',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:Change/Attribute:supervisor_group_id' => 'Aufsichts-Team',
'Class:RoutineChange' => 'Routine Change', 'Class:Change/Attribute:supervisor_group_id+' => '',
'Class:RoutineChange+' => '', 'Class:Change/Attribute:supervisor_group_name' => 'Aufsichts-Team',
'Class:RoutineChange/Attribute:status/Value:new' => 'Neu', 'Class:Change/Attribute:supervisor_group_name+' => '',
'Class:RoutineChange/Attribute:status/Value:new+' => '', 'Class:Change/Attribute:supervisor_id' => 'Aufsicht',
'Class:RoutineChange/Attribute:status/Value:assigned' => 'Zugewiesen', 'Class:Change/Attribute:supervisor_id+' => '',
'Class:RoutineChange/Attribute:status/Value:assigned+' => '', 'Class:Change/Attribute:supervisor_email' => 'Aufsicht',
'Class:RoutineChange/Attribute:status/Value:plannedscheduled' => 'Geplant und angesetzt', 'Class:Change/Attribute:supervisor_email+' => '',
'Class:RoutineChange/Attribute:status/Value:plannedscheduled+' => '', 'Class:Change/Attribute:manager_group_id' => 'Manager-Team',
'Class:RoutineChange/Attribute:status/Value:approved' => 'Bestätigt', 'Class:Change/Attribute:manager_group_id+' => '',
'Class:RoutineChange/Attribute:status/Value:approved+' => '', 'Class:Change/Attribute:manager_group_name' => 'Manager-Team',
'Class:RoutineChange/Attribute:status/Value:implemented' => 'Implementiert', 'Class:Change/Attribute:manager_group_name+' => '',
'Class:RoutineChange/Attribute:status/Value:implemented+' => '', 'Class:Change/Attribute:manager_id' => 'Manager',
'Class:RoutineChange/Attribute:status/Value:monitored' => 'Überwachen', 'Class:Change/Attribute:manager_id+' => '',
'Class:RoutineChange/Attribute:status/Value:monitored+' => '', 'Class:Change/Attribute:manager_email' => 'Manager',
'Class:RoutineChange/Attribute:status/Value:closed' => 'Geschlossen', 'Class:Change/Attribute:manager_email+' => '',
'Class:RoutineChange/Attribute:status/Value:closed+' => '', 'Class:Change/Attribute:outage' => 'Ausfall',
'Class:RoutineChange/Stimulus:ev_validate' => 'Validieren', 'Class:Change/Attribute:outage+' => '',
'Class:RoutineChange/Stimulus:ev_validate+' => '', 'Class:Change/Attribute:outage/Value:yes' => 'Ja',
'Class:RoutineChange/Stimulus:ev_assign' => 'Zuweisen', 'Class:Change/Attribute:outage/Value:yes+' => '',
'Class:RoutineChange/Stimulus:ev_assign+' => '', 'Class:Change/Attribute:outage/Value:no' => 'Nein',
'Class:RoutineChange/Stimulus:ev_reopen' => 'Wiedereröffnen', 'Class:Change/Attribute:outage/Value:no+' => '',
'Class:RoutineChange/Stimulus:ev_reopen+' => '', 'Class:Change/Attribute:change_request' => 'Anfrage',
'Class:RoutineChange/Stimulus:ev_plan' => 'Planen', 'Class:Change/Attribute:change_request+' => '',
'Class:RoutineChange/Stimulus:ev_plan+' => '', 'Class:Change/Attribute:fallback' => 'Fallback-Plan',
'Class:RoutineChange/Stimulus:ev_replan' => 'Umplanen', 'Class:Change/Attribute:fallback+' => '',
'Class:RoutineChange/Stimulus:ev_replan+' => '', 'Class:Change/Stimulus:ev_validate' => 'Validieren',
'Class:RoutineChange/Stimulus:ev_implement' => 'Implementieren', 'Class:Change/Stimulus:ev_validate+' => '',
'Class:RoutineChange/Stimulus:ev_implement+' => '', 'Class:Change/Stimulus:ev_reject' => 'Ablehnen',
'Class:RoutineChange/Stimulus:ev_monitor' => 'Überwachen', 'Class:Change/Stimulus:ev_reject+' => '',
'Class:RoutineChange/Stimulus:ev_monitor+' => '', 'Class:Change/Stimulus:ev_assign' => 'Zuweisen',
'Class:RoutineChange/Stimulus:ev_finish' => 'Abschließen', 'Class:Change/Stimulus:ev_assign+' => '',
'Class:RoutineChange/Stimulus:ev_finish+' => '', 'Class:Change/Stimulus:ev_reopen' => 'Wiedereröffnen',
)); 'Class:Change/Stimulus:ev_reopen+' => '',
'Class:Change/Stimulus:ev_plan' => 'Planen',
// 'Class:Change/Stimulus:ev_plan+' => '',
// Class: ApprovedChange 'Class:Change/Stimulus:ev_approve' => 'Bestätigen',
// 'Class:Change/Stimulus:ev_approve+' => '',
'Class:Change/Stimulus:ev_replan' => 'Umplanen',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:Change/Stimulus:ev_replan+' => '',
'Class:ApprovedChange' => 'Bewilligte Changes', 'Class:Change/Stimulus:ev_notapprove' => 'Ablehnen',
'Class:ApprovedChange+' => '', 'Class:Change/Stimulus:ev_notapprove+' => '',
'Class:ApprovedChange/Attribute:approval_date' => 'Datum der Bewilligung', 'Class:Change/Stimulus:ev_implement' => 'Implementieren',
'Class:ApprovedChange/Attribute:approval_date+' => '', 'Class:Change/Stimulus:ev_implement+' => '',
'Class:ApprovedChange/Attribute:approval_comment' => 'Kommentar zur Bewilligung', 'Class:Change/Stimulus:ev_monitor' => 'Überwachen',
'Class:ApprovedChange/Attribute:approval_comment+' => '', 'Class:Change/Stimulus:ev_monitor+' => '',
'Class:ApprovedChange/Stimulus:ev_validate' => 'Validieren', 'Class:Change/Stimulus:ev_finish' => 'Abschließen',
'Class:ApprovedChange/Stimulus:ev_validate+' => '', 'Class:Change/Stimulus:ev_finish+' => '',
'Class:ApprovedChange/Stimulus:ev_reject' => 'Ablehnen', 'Class:RoutineChange/Attribute:status/Value:new' => 'Neu',
'Class:ApprovedChange/Stimulus:ev_reject+' => '', 'Class:RoutineChange/Attribute:status/Value:new+' => '',
'Class:ApprovedChange/Stimulus:ev_assign' => 'Zuweisen', 'Class:RoutineChange/Attribute:status/Value:assigned' => 'Zugewiesen',
'Class:ApprovedChange/Stimulus:ev_assign+' => '', 'Class:RoutineChange/Attribute:status/Value:assigned+' => '',
'Class:ApprovedChange/Stimulus:ev_reopen' => 'Wiedereröffnen', 'Class:RoutineChange/Attribute:status/Value:plannedscheduled' => 'Geplant und angesetzt',
'Class:ApprovedChange/Stimulus:ev_reopen+' => '', 'Class:RoutineChange/Attribute:status/Value:plannedscheduled+' => '',
'Class:ApprovedChange/Stimulus:ev_plan' => 'Planen', 'Class:RoutineChange/Attribute:status/Value:approved' => 'Bestätigt',
'Class:ApprovedChange/Stimulus:ev_plan+' => '', 'Class:RoutineChange/Attribute:status/Value:approved+' => '',
'Class:ApprovedChange/Stimulus:ev_approve' => 'Bestätigen', 'Class:RoutineChange/Attribute:status/Value:implemented' => 'Implementiert',
'Class:ApprovedChange/Stimulus:ev_approve+' => '', 'Class:RoutineChange/Attribute:status/Value:implemented+' => '',
'Class:ApprovedChange/Stimulus:ev_replan' => 'Umplanen', 'Class:RoutineChange/Attribute:status/Value:monitored' => 'Überwachen',
'Class:ApprovedChange/Stimulus:ev_replan+' => '', 'Class:RoutineChange/Attribute:status/Value:monitored+' => '',
'Class:ApprovedChange/Stimulus:ev_notapprove' => 'Bestätigen zurücknehmen', 'Class:RoutineChange/Attribute:status/Value:closed' => 'Geschlossen',
'Class:ApprovedChange/Stimulus:ev_notapprove+' => '', 'Class:RoutineChange/Attribute:status/Value:closed+' => '',
'Class:ApprovedChange/Stimulus:ev_implement' => 'Implementieren', 'Class:ApprovedChange' => 'Bewilligte Changes',
'Class:ApprovedChange/Stimulus:ev_implement+' => '', 'Class:ApprovedChange+' => '',
'Class:ApprovedChange/Stimulus:ev_monitor' => 'Überwachen', 'Class:ApprovedChange/Attribute:approval_date' => 'Datum der Bewilligung',
'Class:ApprovedChange/Stimulus:ev_monitor+' => '', 'Class:ApprovedChange/Attribute:approval_date+' => '',
'Class:ApprovedChange/Stimulus:ev_finish' => 'Abschließen', 'Class:ApprovedChange/Attribute:approval_comment' => 'Kommentar zur Bewilligung',
'Class:ApprovedChange/Stimulus:ev_finish+' => '', 'Class:ApprovedChange/Attribute:approval_comment+' => '',
)); 'Class:ApprovedChange/Stimulus:ev_validate' => 'Validieren',
// 'Class:ApprovedChange/Stimulus:ev_validate+' => '',
// Class: NormalChange 'Class:ApprovedChange/Stimulus:ev_reject' => 'Ablehnen',
// 'Class:ApprovedChange/Stimulus:ev_reject+' => '',
'Class:ApprovedChange/Stimulus:ev_assign' => 'Zuweisen',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:ApprovedChange/Stimulus:ev_assign+' => '',
'Class:NormalChange' => 'Normaler Change', 'Class:ApprovedChange/Stimulus:ev_reopen' => 'Wiedereröffnen',
'Class:NormalChange+' => '', 'Class:ApprovedChange/Stimulus:ev_reopen+' => '',
'Class:NormalChange/Attribute:status/Value:new' => 'Neu', 'Class:ApprovedChange/Stimulus:ev_plan' => 'Planen',
'Class:NormalChange/Attribute:status/Value:new+' => '', 'Class:ApprovedChange/Stimulus:ev_plan+' => '',
'Class:NormalChange/Attribute:status/Value:validated' => 'Validiert', 'Class:ApprovedChange/Stimulus:ev_approve' => 'Bestätigen',
'Class:NormalChange/Attribute:status/Value:validated+' => '', 'Class:ApprovedChange/Stimulus:ev_approve+' => '',
'Class:NormalChange/Attribute:status/Value:rejected' => 'Abgelehnt', 'Class:ApprovedChange/Stimulus:ev_replan' => 'Umplanen',
'Class:NormalChange/Attribute:status/Value:rejected+' => '', 'Class:ApprovedChange/Stimulus:ev_replan+' => '',
'Class:NormalChange/Attribute:status/Value:assigned' => 'Zugewiesen', 'Class:ApprovedChange/Stimulus:ev_notapprove' => 'Bestätigen zurücknehmen',
'Class:NormalChange/Attribute:status/Value:assigned+' => '', 'Class:ApprovedChange/Stimulus:ev_notapprove+' => '',
'Class:NormalChange/Attribute:status/Value:plannedscheduled' => 'Geplant und angesetzt', 'Class:ApprovedChange/Stimulus:ev_implement' => 'Implementieren',
'Class:NormalChange/Attribute:status/Value:plannedscheduled+' => '', 'Class:ApprovedChange/Stimulus:ev_implement+' => '',
'Class:NormalChange/Attribute:status/Value:approved' => 'Bestätigt', 'Class:ApprovedChange/Stimulus:ev_monitor' => 'Überwachen',
'Class:NormalChange/Attribute:status/Value:approved+' => '', 'Class:ApprovedChange/Stimulus:ev_monitor+' => '',
'Class:NormalChange/Attribute:status/Value:notapproved' => 'Nicht bestätigt', 'Class:ApprovedChange/Stimulus:ev_finish' => 'Abschließen',
'Class:NormalChange/Attribute:status/Value:notapproved+' => '', 'Class:ApprovedChange/Stimulus:ev_finish+' => '',
'Class:NormalChange/Attribute:status/Value:implemented' => 'Implementiert', 'Class:NormalChange/Attribute:status/Value:new' => 'Neu',
'Class:NormalChange/Attribute:status/Value:implemented+' => '', 'Class:NormalChange/Attribute:status/Value:new+' => '',
'Class:NormalChange/Attribute:status/Value:monitored' => 'Überwachen', 'Class:NormalChange/Attribute:status/Value:validated' => 'Validiert',
'Class:NormalChange/Attribute:status/Value:monitored+' => '', 'Class:NormalChange/Attribute:status/Value:validated+' => '',
'Class:NormalChange/Attribute:status/Value:closed' => 'Geschlossen', 'Class:NormalChange/Attribute:status/Value:rejected' => 'Abgelehnt',
'Class:NormalChange/Attribute:status/Value:closed+' => '', 'Class:NormalChange/Attribute:status/Value:rejected+' => '',
'Class:NormalChange/Attribute:acceptance_date' => 'Datum der Bewilligung', 'Class:NormalChange/Attribute:status/Value:assigned' => 'Zugewiesen',
'Class:NormalChange/Attribute:acceptance_date+' => '', 'Class:NormalChange/Attribute:status/Value:assigned+' => '',
'Class:NormalChange/Attribute:acceptance_comment' => 'Kommentar zur Bewilligung', 'Class:NormalChange/Attribute:status/Value:plannedscheduled' => 'Geplant und angesetzt',
'Class:NormalChange/Attribute:acceptance_comment+' => '', 'Class:NormalChange/Attribute:status/Value:plannedscheduled+' => '',
'Class:NormalChange/Stimulus:ev_validate' => 'Validieren', 'Class:NormalChange/Attribute:status/Value:approved' => 'Bestätigt',
'Class:NormalChange/Stimulus:ev_validate+' => '', 'Class:NormalChange/Attribute:status/Value:approved+' => '',
'Class:NormalChange/Stimulus:ev_reject' => 'Ablehnen', 'Class:NormalChange/Attribute:status/Value:notapproved' => 'Nicht bestätigt',
'Class:NormalChange/Stimulus:ev_reject+' => '', 'Class:NormalChange/Attribute:status/Value:notapproved+' => '',
'Class:NormalChange/Stimulus:ev_assign' => 'Zuweisen', 'Class:NormalChange/Attribute:status/Value:implemented' => 'Implementiert',
'Class:NormalChange/Stimulus:ev_assign+' => '', 'Class:NormalChange/Attribute:status/Value:implemented+' => '',
'Class:NormalChange/Stimulus:ev_reopen' => 'Wiedereröffnen', 'Class:NormalChange/Attribute:status/Value:monitored' => 'Überwachen',
'Class:NormalChange/Stimulus:ev_reopen+' => '', 'Class:NormalChange/Attribute:status/Value:monitored+' => '',
'Class:NormalChange/Stimulus:ev_plan' => 'Planen', 'Class:NormalChange/Attribute:status/Value:closed' => 'Geschlossen',
'Class:NormalChange/Stimulus:ev_plan+' => '', 'Class:NormalChange/Attribute:status/Value:closed+' => '',
'Class:NormalChange/Stimulus:ev_approve' => 'Bestätigen', 'Class:EmergencyChange/Attribute:status/Value:new' => 'Neu',
'Class:NormalChange/Stimulus:ev_approve+' => '', 'Class:EmergencyChange/Attribute:status/Value:new+' => '',
'Class:NormalChange/Stimulus:ev_replan' => 'Umplanen', 'Class:EmergencyChange/Attribute:status/Value:validated' => 'Validiert',
'Class:NormalChange/Stimulus:ev_replan+' => '', 'Class:EmergencyChange/Attribute:status/Value:validated+' => '',
'Class:NormalChange/Stimulus:ev_notapprove' => 'Bestätigen zurücknehmen', 'Class:EmergencyChange/Attribute:status/Value:rejected' => 'Abgelehnt',
'Class:NormalChange/Stimulus:ev_notapprove+' => '', 'Class:EmergencyChange/Attribute:status/Value:rejected+' => '',
'Class:NormalChange/Stimulus:ev_implement' => 'Implementieren', 'Class:EmergencyChange/Attribute:status/Value:assigned' => 'Zugewiesen',
'Class:NormalChange/Stimulus:ev_implement+' => '', 'Class:EmergencyChange/Attribute:status/Value:assigned+' => '',
'Class:NormalChange/Stimulus:ev_monitor' => 'Überwachen', 'Class:EmergencyChange/Attribute:status/Value:plannedscheduled' => 'Geplant und angesetzt',
'Class:NormalChange/Stimulus:ev_monitor+' => '', 'Class:EmergencyChange/Attribute:status/Value:plannedscheduled+' => '',
'Class:NormalChange/Stimulus:ev_finish' => 'Abschließen', 'Class:EmergencyChange/Attribute:status/Value:approved' => 'Bestätigt',
'Class:NormalChange/Stimulus:ev_finish+' => '', 'Class:EmergencyChange/Attribute:status/Value:approved+' => '',
)); 'Class:EmergencyChange/Attribute:status/Value:notapproved' => 'Nicht bestätigt',
'Class:EmergencyChange/Attribute:status/Value:notapproved+' => '',
// 'Class:EmergencyChange/Attribute:status/Value:implemented' => 'Implementiert',
// Class: EmergencyChange 'Class:EmergencyChange/Attribute:status/Value:implemented+' => '',
// 'Class:EmergencyChange/Attribute:status/Value:monitored' => 'Überwachen',
'Class:EmergencyChange/Attribute:status/Value:monitored+' => '',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:EmergencyChange/Attribute:status/Value:closed' => 'Geschlossen',
'Class:EmergencyChange' => 'Emergency Change', 'Class:EmergencyChange/Attribute:status/Value:closed+' => '',
'Class:EmergencyChange+' => '', ));
'Class:EmergencyChange/Attribute:status/Value:new' => 'Neu', ?>
'Class:EmergencyChange/Attribute:status/Value:new+' => '',
'Class:EmergencyChange/Attribute:status/Value:validated' => 'Validiert',
'Class:EmergencyChange/Attribute:status/Value:validated+' => '',
'Class:EmergencyChange/Attribute:status/Value:rejected' => 'Abgelehnt',
'Class:EmergencyChange/Attribute:status/Value:rejected+' => '',
'Class:EmergencyChange/Attribute:status/Value:assigned' => 'Zugewiesen',
'Class:EmergencyChange/Attribute:status/Value:assigned+' => '',
'Class:EmergencyChange/Attribute:status/Value:plannedscheduled' => 'Geplant und angesetzt',
'Class:EmergencyChange/Attribute:status/Value:plannedscheduled+' => '',
'Class:EmergencyChange/Attribute:status/Value:approved' => 'Bestätigt',
'Class:EmergencyChange/Attribute:status/Value:approved+' => '',
'Class:EmergencyChange/Attribute:status/Value:notapproved' => 'Nicht bestätigt',
'Class:EmergencyChange/Attribute:status/Value:notapproved+' => '',
'Class:EmergencyChange/Attribute:status/Value:implemented' => 'Implementiert',
'Class:EmergencyChange/Attribute:status/Value:implemented+' => '',
'Class:EmergencyChange/Attribute:status/Value:monitored' => 'Überwachen',
'Class:EmergencyChange/Attribute:status/Value:monitored+' => '',
'Class:EmergencyChange/Attribute:status/Value:closed' => 'Geschlossen',
'Class:EmergencyChange/Attribute:status/Value:closed+' => '',
'Class:EmergencyChange/Stimulus:ev_validate' => 'Validieren',
'Class:EmergencyChange/Stimulus:ev_validate+' => '',
'Class:EmergencyChange/Stimulus:ev_reject' => 'Ablehnen',
'Class:EmergencyChange/Stimulus:ev_reject+' => '',
'Class:EmergencyChange/Stimulus:ev_assign' => 'Zuweisen',
'Class:EmergencyChange/Stimulus:ev_assign+' => '',
'Class:EmergencyChange/Stimulus:ev_reopen' => 'Wiedereröffnen',
'Class:EmergencyChange/Stimulus:ev_reopen+' => '',
'Class:EmergencyChange/Stimulus:ev_plan' => 'Planen',
'Class:EmergencyChange/Stimulus:ev_plan+' => '',
'Class:EmergencyChange/Stimulus:ev_approve' => 'Bestätigen',
'Class:EmergencyChange/Stimulus:ev_approve+' => '',
'Class:EmergencyChange/Stimulus:ev_replan' => 'Umplanen',
'Class:EmergencyChange/Stimulus:ev_replan+' => '',
'Class:EmergencyChange/Stimulus:ev_notapprove' => 'Bestätigen zurücknehmen',
'Class:EmergencyChange/Stimulus:ev_notapprove+' => '',
'Class:EmergencyChange/Stimulus:ev_implement' => 'Implementieren',
'Class:EmergencyChange/Stimulus:ev_implement+' => '',
'Class:EmergencyChange/Stimulus:ev_monitor' => 'Überwachen',
'Class:EmergencyChange/Stimulus:ev_monitor+' => '',
'Class:EmergencyChange/Stimulus:ev_finish' => 'Abschließen',
'Class:EmergencyChange/Stimulus:ev_finish+' => '',
));
?>

File diff suppressed because it is too large Load Diff

View File

@@ -257,7 +257,7 @@ abstract class Document extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("allowed_values"=>null, "extkey_attcode"=>"org_id", "target_attcode"=>"name", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("allowed_values"=>null, "extkey_attcode"=>"org_id", "target_attcode"=>"name", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("type", array("allowed_values"=>new ValueSetEnum('contract,networkmap,presentation,training,whitePaper,workinginstructions'), "sql"=>"type", "default_value"=>"presentation", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("type", array("allowed_values"=>new ValueSetEnum('contract,networkmap,presentation,training,whitePaper,workinginstructions'), "sql"=>"type", "default_value"=>"presentation", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('draft,published,obsolete'), "sql"=>"status", "default_value"=>"draft", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('draft,published,obsolete'), "sql"=>"status", "default_value"=>"draft", "is_null_allowed"=>false, "depends_on"=>array('org_id'))));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("contract_list", array("linked_class"=>"lnkContractToDoc", "ext_key_to_me"=>"document_id", "ext_key_to_remote"=>"contract_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("contract_list", array("linked_class"=>"lnkContractToDoc", "ext_key_to_me"=>"document_id", "ext_key_to_remote"=>"contract_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("service_list", array("linked_class"=>"lnkServiceToDoc", "ext_key_to_me"=>"document_id", "ext_key_to_remote"=>"service_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("service_list", array("linked_class"=>"lnkServiceToDoc", "ext_key_to_me"=>"document_id", "ext_key_to_remote"=>"service_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("ticket_list", array("linked_class"=>"lnkTicketToDoc", "ext_key_to_me"=>"document_id", "ext_key_to_remote"=>"ticket_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("ticket_list", array("linked_class"=>"lnkTicketToDoc", "ext_key_to_me"=>"document_id", "ext_key_to_remote"=>"ticket_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array())));
@@ -365,7 +365,7 @@ class FileDoc extends Document
$oPage->add($this->DisplayDocumentInline($oPage, 'contents')); $oPage->add($this->DisplayDocumentInline($oPage, 'contents'));
$oPage->SetCurrentTab(Dict::S('UI:PropertiesTab')); $oPage->SetCurrentTab(Dict::S('UI:PropertiesTab'));
} }
parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix); parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
} }
} }

View File

@@ -38,8 +38,8 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'Class:Organization/Attribute:status/Value:inactive+' => 'Inativo', 'Class:Organization/Attribute:status/Value:inactive+' => 'Inativo',
'Class:Organization/Attribute:parent_id' => 'Matriz', 'Class:Organization/Attribute:parent_id' => 'Matriz',
'Class:Organization/Attribute:parent_id+' => 'Organização matriz', 'Class:Organization/Attribute:parent_id+' => 'Organização matriz',
'Class:Location' => 'Localizacao', 'Class:Location' => 'Localização',
'Class:Location+' => 'Qualquer tipo localizacao: Região, Pais, Cidade, Site, Construção, Piso, Sala, Rack,...', 'Class:Location+' => 'Qualquer tipo localização: Região, Pais, Cidade, Site, Construção, Piso, Sala, Rack,...',
'Class:Location/Attribute:name' => 'Nome', 'Class:Location/Attribute:name' => 'Nome',
'Class:Location/Attribute:name+' => '', 'Class:Location/Attribute:name+' => '',
'Class:Location/Attribute:status' => 'Status', 'Class:Location/Attribute:status' => 'Status',
@@ -416,17 +416,17 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'Class:Contact/Attribute:status/Value:active+' => 'Ativo', 'Class:Contact/Attribute:status/Value:active+' => 'Ativo',
'Class:Contact/Attribute:status/Value:inactive' => 'Inativo', 'Class:Contact/Attribute:status/Value:inactive' => 'Inativo',
'Class:Contact/Attribute:status/Value:inactive+' => 'Inativo', 'Class:Contact/Attribute:status/Value:inactive+' => 'Inativo',
'Class:Contact/Attribute:org_id' => 'Organizacao', 'Class:Contact/Attribute:org_id' => 'Organização',
'Class:Contact/Attribute:org_id+' => '', 'Class:Contact/Attribute:org_id+' => '',
'Class:Contact/Attribute:org_name' => 'Organizacao', 'Class:Contact/Attribute:org_name' => 'Organização',
'Class:Contact/Attribute:org_name+' => '', 'Class:Contact/Attribute:org_name+' => '',
'Class:Contact/Attribute:email' => 'Email', 'Class:Contact/Attribute:email' => 'Email',
'Class:Contact/Attribute:email+' => '', 'Class:Contact/Attribute:email+' => '',
'Class:Contact/Attribute:phone' => 'Telefone', 'Class:Contact/Attribute:phone' => 'Telefone',
'Class:Contact/Attribute:phone+' => '', 'Class:Contact/Attribute:phone+' => '',
'Class:Contact/Attribute:location_id' => 'Localizacao', 'Class:Contact/Attribute:location_id' => 'Localização',
'Class:Contact/Attribute:location_id+' => '', 'Class:Contact/Attribute:location_id+' => '',
'Class:Contact/Attribute:location_name' => 'Localizacao', 'Class:Contact/Attribute:location_name' => 'Localização',
'Class:Contact/Attribute:location_name+' => '', 'Class:Contact/Attribute:location_name+' => '',
'Class:Contact/Attribute:ci_list' => 'CIs', 'Class:Contact/Attribute:ci_list' => 'CIs',
'Class:Contact/Attribute:ci_list+' => 'CIs relacionados para o contato', 'Class:Contact/Attribute:ci_list+' => 'CIs relacionados para o contato',

View File

@@ -1,75 +1,59 @@
<?php <?php
// Copyright (C) 2010 Combodo SARL // Copyright (C) 2010 Combodo SARL
// //
// This program is free software; you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License. // the Free Software Foundation; version 3 of the License.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/** /**
* Localized data * @author Erwan Taloc <erwan.taloc@combodo.com>
* * @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Erwan Taloc <erwan.taloc@combodo.com> * @author Denis Flaven <denis.flaven@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com> * @licence http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL */
*/
Dict::Add('DE DE', 'German', 'Deutsch', array(
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:Incident' => 'Incident',
'Menu:IncidentManagement' => 'Incident Management', 'Class:Incident+' => '',
'Menu:IncidentManagement+' => 'Incident Management', 'Class:Incident/Stimulus:ev_assign' => '"Zuweisen"',
'Menu:Incident:Overview' => 'Übersicht', 'Class:Incident/Stimulus:ev_assign+' => '',
'Menu:Incident:Overview+' => 'Übersicht', 'Class:Incident/Stimulus:ev_reassign' => 'Neu zuweisen',
'Menu:NewIncident' => 'Neuer Incident', 'Class:Incident/Stimulus:ev_reassign+' => '',
'Menu:NewIncident+' => 'Ein neues Incident-Ticket erstellen', 'Class:Incident/Stimulus:ev_timeout' => 'ev-Timeout',
'Menu:SearchIncidents' => 'Nach Incidents suchen', 'Class:Incident/Stimulus:ev_timeout+' => '',
'Menu:SearchIncidents+' => 'Nach Incidents suchen', 'Class:Incident/Stimulus:ev_resolve' => 'Als gelöst markieren',
'Menu:Incident:Shortcuts' => 'Shortcuts', 'Class:Incident/Stimulus:ev_resolve+' => '',
'Menu:Incident:Shortcuts+' => '', 'Class:Incident/Stimulus:ev_close' => 'Schließen',
'Menu:Incident:MyIncidents' => 'Incidents, die mich betreffen', 'Class:Incident/Stimulus:ev_close+' => '',
'Menu:Incident:MyIncidents+' => 'Incidents, die mich betreffen (als Bearbeiter)', 'Class:lnkTicketToIncident' => 'Ticket zu Incident',
'Menu:Incident:EscalatedIncidents' => 'Eskalierte Incidents', 'Class:lnkTicketToIncident/Attribute:ticket_id' => 'Ticket',
'Menu:Incident:EscalatedIncidents+' => 'Eskalierte Incidents', 'Class:lnkTicketToIncident/Attribute:incident_id' => 'Incident',
'Menu:Incident:OpenIncidents' => 'Alle offenen Incidents', 'Class:lnkTicketToIncident/Attribute:reason' => 'Grund',
'Menu:Incident:OpenIncidents+' => 'Alle offenen Incidents', 'Menu:IncidentManagement' => 'Incident Management',
'Menu:IncidentManagement+' => 'Incident Management',
)); 'Menu:Incident:Overview' => 'Übersicht',
'Menu:Incident:Overview+' => 'Übersicht',
// Dictionnay conventions 'Menu:NewIncident' => 'Neuer Incident',
// Class:<class_name> 'Menu:NewIncident+' => 'Ein neues Incident-Ticket erstellen',
// Class:<class_name>+ 'Menu:SearchIncidents' => 'Nach Incidents suchen',
// Class:<class_name>/Attribute:<attribute_code> 'Menu:SearchIncidents+' => 'Nach Incidents suchen',
// Class:<class_name>/Attribute:<attribute_code>+ 'Menu:Incident:Shortcuts' => 'Shortcuts',
// Class:<class_name>/Attribute:<attribute_code>/Value:<value> 'Menu:Incident:Shortcuts+' => '',
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+ 'Menu:Incident:MyIncidents' => 'Incidents, die mich betreffen',
// Class:<class_name>/Stimulus:<stimulus_code> 'Menu:Incident:MyIncidents+' => 'Incidents, die mich betreffen (als Bearbeiter)',
// Class:<class_name>/Stimulus:<stimulus_code>+ 'Menu:Incident:EscalatedIncidents' => 'Eskalierte Incidents',
'Menu:Incident:EscalatedIncidents+' => 'Eskalierte Incidents',
// 'Menu:Incident:OpenIncidents' => 'Alle offenen Incidents',
// Class: Incident 'Menu:Incident:OpenIncidents+' => 'Alle offenen Incidents',
// ));
?>
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:Incident' => 'Incident',
'Class:Incident+' => '',
'Class:Incident/Stimulus:ev_assign' => '"Zuweisen"',
'Class:Incident/Stimulus:ev_assign+' => '',
'Class:Incident/Stimulus:ev_reassign' => 'Neu zuweisen',
'Class:Incident/Stimulus:ev_reassign+' => '',
'Class:Incident/Stimulus:ev_timeout' => 'ev-Timeout',
'Class:Incident/Stimulus:ev_timeout+' => '',
'Class:Incident/Stimulus:ev_resolve' => 'Als gelöst markieren',
'Class:Incident/Stimulus:ev_resolve+' => '',
'Class:Incident/Stimulus:ev_close' => 'Schließen',
'Class:Incident/Stimulus:ev_close+' => '',
));
?>

View File

@@ -188,7 +188,7 @@ class Incident extends ResponseTicket
$sIcon = ''; $sIcon = '';
if ($sIconName != '') if ($sIconName != '')
{ {
$sPath = '../modules/itop-incident-mgmt-1.0.0/images/'.$sIconName; $sPath = utils::GetAbsoluteUrlModulesRoot().'itop-incident-mgmt-1.0.0/images/'.$sIconName;
if ($bImgTag) if ($bImgTag)
{ {
$sIcon = "<img src=\"$sPath\" style=\"vertical-align:middle;\"/>"; $sIcon = "<img src=\"$sPath\" style=\"vertical-align:middle;\"/>";

View File

@@ -1,456 +1,315 @@
<?php <?php
// Copyright (C) 2010 Combodo SARL // Copyright (C) 2010 Combodo SARL
// //
// This program is free software; you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License. // the Free Software Foundation; version 3 of the License.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/** /**
* Localized data * @author Erwan Taloc <erwan.taloc@combodo.com>
* * @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Erwan Taloc <erwan.taloc@combodo.com> * @author Denis Flaven <denis.flaven@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com> * @author Stephan Rosenke <stephan.rosenke@itomig.de>
* @author Denis Flaven <denis.flaven@combodo.com>
* @author Stephan Rosenke <stephan.rosenke@itomig.de> * @licence http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL */
*/
Dict::Add('DE DE', 'German', 'Deutsch', array(
// Dictionnay conventions 'Class:ProviderContract' => 'Provider-Vertrag',
// Class:<class_name> 'Class:ProviderContract+' => '',
// Class:<class_name>+ 'Class:ProviderContract/Attribute:provider_id' => 'Anbieter',
// Class:<class_name>/Attribute:<attribute_code> 'Class:ProviderContract/Attribute:provider_id+' => '',
// Class:<class_name>/Attribute:<attribute_code>+ 'Class:ProviderContract/Attribute:sla' => 'SLA',
// Class:<class_name>/Attribute:<attribute_code>/Value:<value> 'Class:ProviderContract/Attribute:sla+' => 'Service Level Agreement',
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+ 'Class:ProviderContract/Attribute:coverage' => 'Abdeckung',
// Class:<class_name>/Stimulus:<stimulus_code> 'Class:ProviderContract/Attribute:coverage+' => '',
// Class:<class_name>/Stimulus:<stimulus_code>+ 'Class:CustomerContract' => 'Kundenvertrag',
'Class:CustomerContract+' => '',
'Class:CustomerContract/Attribute:org_id' => 'Kunde',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:CustomerContract/Attribute:org_id+' => '',
'Menu:ServiceManagement' => 'Service Management', 'Class:CustomerContract/Attribute:provider_id' => 'Anbieter',
'Menu:ServiceManagement+' => 'Service Management-Übersicht', 'Class:CustomerContract/Attribute:provider_id+' => '',
'Menu:Service:Overview' => 'Übersicht', 'Class:CustomerContract/Attribute:support_team_id' => 'Support-Team',
'Menu:Service:Overview+' => '', 'Class:CustomerContract/Attribute:support_team_id+' => '',
'UI-ServiceManagementMenu-ContractsBySrvLevel' => 'Verträge nach Service Level', 'Class:CustomerContract/Attribute:sla_list' => 'SLAs',
'UI-ServiceManagementMenu-ContractsByStatus' => 'Verträge nach Status', 'Class:CustomerContract/Attribute:sla_list+' => '',
'UI-ServiceManagementMenu-ContractsEndingIn30Days' => 'Verträge, die in weniger als 30 Tagen enden', 'Class:CustomerContract/Attribute:provider_list' => 'Zugrunde liegende Verträge',
'Class:CustomerContract/Attribute:provider_list+' => '',
'Menu:ServiceType' => 'Service Typen', 'Class:lnkCustomerContractToProviderContract' => 'Verbindung Kundenvertratg zu Provider-Vertrag',
'Menu:ServiceType+' => 'Service Typen', 'Class:lnkCustomerContractToProviderContract+' => '',
'Menu:ProviderContract' => 'Provider-Verträge', 'Class:lnkCustomerContractToProviderContract/Attribute:customer_contract_id' => 'Kundenvertrag',
'Menu:ProviderContract+' => 'Provider-Verträge', 'Class:lnkCustomerContractToProviderContract/Attribute:customer_contract_id+' => '',
'Menu:CustomerContract' => 'Kundenverträge', 'Class:lnkCustomerContractToProviderContract/Attribute:provider_contract_id' => 'Provider Contract',
'Menu:CustomerContract+' => 'Kundenverträge', 'Class:lnkCustomerContractToProviderContract/Attribute:provider_contract_id+' => '',
'Menu:ServiceSubcategory' => 'Service-Unterkategorien', 'Class:lnkContractToSLA' => 'Contract/SLA',
'Menu:ServiceSubcategory+' => 'Service-Unterkategorien', 'Class:lnkContractToSLA+' => '',
'Menu:Service' => 'Services', 'Class:lnkContractToSLA/Attribute:contract_id' => 'Vertrag',
'Menu:Service+' => 'Services', 'Class:lnkContractToSLA/Attribute:contract_id+' => '',
'Menu:SLA' => 'SLAs', 'Class:lnkContractToSLA/Attribute:sla_id' => 'SLA',
'Menu:SLA+' => 'Service Level Agreements', 'Class:lnkContractToSLA/Attribute:sla_id+' => '',
'Menu:SLT' => 'SLTs', 'Class:lnkContractToSLA/Attribute:coverage' => 'Abdeckung',
'Menu:SLT+' => 'Service Level Targets', 'Class:lnkContractToSLA/Attribute:coverage+' => '',
'Class:lnkContractToDoc' => 'Vertrag/Dokument',
)); 'Class:lnkContractToDoc+' => '',
'Class:lnkContractToDoc/Attribute:contract_id' => 'Vertrag',
'Class:lnkContractToDoc/Attribute:contract_id+' => '',
/* 'Class:lnkContractToDoc/Attribute:document_id' => 'Dokument',
'UI:ServiceManagementMenu' => 'Gestion des Services', 'Class:lnkContractToDoc/Attribute:document_id+' => '',
'UI:ServiceManagementMenu+' => 'Gestion des Services', 'Class:lnkContractToContact' => 'Vertrag/Kontakt',
'UI:ServiceManagementMenu:Title' => 'Résumé des services & contrats', 'Class:lnkContractToContact+' => '',
'UI-ServiceManagementMenu-ContractsBySrvLevel' => 'Contrats par niveau de service', 'Class:lnkContractToContact/Attribute:contract_id' => 'Vertrag',
'UI-ServiceManagementMenu-ContractsByStatus' => 'Contrats par état', 'Class:lnkContractToContact/Attribute:contract_id+' => '',
'UI-ServiceManagementMenu-ContractsEndingIn30Days' => 'Contrats se terminant dans moins de 30 jours', 'Class:lnkContractToContact/Attribute:contact_id' => 'Kontakt',
*/ 'Class:lnkContractToContact/Attribute:contact_id+' => '',
'Class:lnkContractToContact/Attribute:role' => 'Rolle',
'Class:lnkContractToContact/Attribute:role+' => '',
// 'Class:lnkContractToCI' => 'Vertrag/CI',
// Class: Contract 'Class:lnkContractToCI+' => '',
// 'Class:lnkContractToCI/Attribute:contract_id' => 'Vertrag',
'Class:lnkContractToCI/Attribute:contract_id+' => '',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:lnkContractToCI/Attribute:ci_id' => 'CI',
'Class:Contract' => 'Vertrag', 'Class:lnkContractToCI/Attribute:ci_id+' => '',
'Class:Contract+' => '', 'Class:Service' => 'Service',
'Class:Contract/Attribute:name' => 'Name', 'Class:Service+' => '',
'Class:Contract/Attribute:name+' => '', 'Class:Service/Attribute:org_id' => 'Anbieter',
'Class:Contract/Attribute:description' => 'Beschreibung', 'Class:Service/Attribute:org_id+' => '',
'Class:Contract/Attribute:description+' => '', 'Class:Service/Attribute:name' => 'Name',
'Class:Contract/Attribute:start_date' => 'Anfangsdatum', 'Class:Service/Attribute:name+' => '',
'Class:Contract/Attribute:start_date+' => '', 'Class:Service/Attribute:description' => 'Beschreibung',
'Class:Contract/Attribute:end_date' => 'Ablaufdatum', 'Class:Service/Attribute:description+' => '',
'Class:Contract/Attribute:end_date+' => '', 'Class:Service/Attribute:type' => 'Typ',
'Class:Contract/Attribute:cost' => 'Kosten', 'Class:Service/Attribute:type+' => '',
'Class:Contract/Attribute:cost+' => '', 'Class:Service/Attribute:type/Value:IncidentManagement' => 'Incident Management',
'Class:Contract/Attribute:cost_currency' => 'Währung', 'Class:Service/Attribute:type/Value:IncidentManagement+' => 'Incident Management',
'Class:Contract/Attribute:cost_currency+' => '', 'Class:Service/Attribute:type/Value:RequestManagement' => 'Request Management',
'Class:Contract/Attribute:cost_currency/Value:dollars' => 'Dollar', 'Class:Service/Attribute:type/Value:RequestManagement+' => 'Request Management',
'Class:Contract/Attribute:cost_currency/Value:dollars+' => '', 'Class:Service/Attribute:status' => 'Status',
'Class:Contract/Attribute:cost_currency/Value:euros' => 'Euro', 'Class:Service/Attribute:status+' => '',
'Class:Contract/Attribute:cost_currency/Value:euros+' => '', 'Class:Service/Attribute:status/Value:design' => 'Design',
'Class:Contract/Attribute:cost_unit' => 'Kosteneinheit', 'Class:Service/Attribute:status/Value:design+' => '',
'Class:Contract/Attribute:cost_unit+' => '', 'Class:Service/Attribute:status/Value:obsolete' => 'Veraltet',
'Class:Contract/Attribute:billing_frequency' => 'Abrechnungshäufigkeit', 'Class:Service/Attribute:status/Value:obsolete+' => '',
'Class:Contract/Attribute:billing_frequency+' => '', 'Class:Service/Attribute:status/Value:production' => 'Produktion',
'Class:Contract/Attribute:contact_list' => 'Kontakte', 'Class:Service/Attribute:status/Value:production+' => '',
'Class:Contract/Attribute:contact_list+' => 'Kontakte zu diesem Vertrag', 'Class:Service/Attribute:subcategory_list' => 'Service-Unterkategorien',
'Class:Contract/Attribute:document_list' => 'Dokumente', 'Class:Service/Attribute:subcategory_list+' => '',
'Class:Contract/Attribute:document_list+' => 'Dokumente zu diesem Vertrag', 'Class:Service/Attribute:sla_list' => 'SLAs',
'Class:Contract/Attribute:ci_list' => 'CIs', 'Class:Service/Attribute:sla_list+' => '',
'Class:Contract/Attribute:ci_list+' => 'CI, die von dem Vertrag unterstützt werden', 'Class:Service/Attribute:document_list' => 'Dokumente',
'Class:Contract/Attribute:finalclass' => 'Typ', 'Class:Service/Attribute:document_list+' => 'Dokumente beigefügt zu dem Service',
'Class:Contract/Attribute:finalclass+' => '', 'Class:Service/Attribute:contact_list' => 'Kontakte',
)); 'Class:Service/Attribute:contact_list+' => 'Kontakte, die für diesen Service eine Rolle wahrnehmen',
'Class:ServiceSubcategory' => 'Service-Unterkategorien',
// 'Class:ServiceSubcategory+' => '',
// Class: ProviderContract 'Class:ServiceSubcategory/Attribute:name' => 'Name',
// 'Class:ServiceSubcategory/Attribute:name+' => '',
'Class:ServiceSubcategory/Attribute:description' => 'Beschreibung',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:ServiceSubcategory/Attribute:description+' => '',
'Class:ProviderContract' => 'Provider-Vertrag', 'Class:ServiceSubcategory/Attribute:service_id' => 'Service',
'Class:ProviderContract+' => '', 'Class:ServiceSubcategory/Attribute:service_id+' => '',
'Class:ProviderContract/Attribute:provider_id' => 'Anbieter', 'Class:SLA' => 'SLA',
'Class:ProviderContract/Attribute:provider_id+' => '', 'Class:SLA+' => '',
'Class:ProviderContract/Attribute:provider_name' => 'Providername', 'Class:SLA/Attribute:name' => 'Name',
'Class:ProviderContract/Attribute:provider_name+' => '', 'Class:SLA/Attribute:name+' => '',
'Class:ProviderContract/Attribute:sla' => 'SLA', 'Class:SLA/Attribute:service_id' => 'Service',
'Class:ProviderContract/Attribute:sla+' => 'Service Level Agreement', 'Class:SLA/Attribute:service_id+' => '',
'Class:ProviderContract/Attribute:coverage' => 'Abdeckung', 'Class:SLA/Attribute:slt_list' => 'SLTs',
'Class:ProviderContract/Attribute:coverage+' => '', 'Class:SLA/Attribute:slt_list+' => 'Auflistung der Service Level-Grenzbereiche',
)); 'Class:SLT' => 'SLT',
'Class:SLT+' => '',
// 'Class:SLT/Attribute:name' => 'Name',
// Class: CustomerContract 'Class:SLT/Attribute:name+' => '',
// 'Class:SLT/Attribute:metric' => 'Metrisch',
'Class:SLT/Attribute:metric+' => '',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:SLT/Attribute:metric/Value:TTO' => 'TTO',
'Class:CustomerContract' => 'Kundenvertrag', 'Class:SLT/Attribute:metric/Value:TTO+' => 'TTO',
'Class:CustomerContract+' => '', 'Class:SLT/Attribute:metric/Value:TTR' => 'TTR',
'Class:CustomerContract/Attribute:org_id' => 'Kunde', 'Class:SLT/Attribute:metric/Value:TTR+' => 'TTR',
'Class:CustomerContract/Attribute:org_id+' => '', 'Class:SLT/Attribute:ticket_priority' => 'Ticketpriorität',
'Class:CustomerContract/Attribute:org_name' => 'Kundenname', 'Class:SLT/Attribute:ticket_priority+' => '',
'Class:CustomerContract/Attribute:org_name+' => '', 'Class:SLT/Attribute:ticket_priority/Value:1' => '1',
'Class:CustomerContract/Attribute:provider_id' => 'Anbieter', 'Class:SLT/Attribute:ticket_priority/Value:1+' => '1',
'Class:CustomerContract/Attribute:provider_id+' => '', 'Class:SLT/Attribute:ticket_priority/Value:2' => '2',
'Class:CustomerContract/Attribute:provider_name' => 'Providername', 'Class:SLT/Attribute:ticket_priority/Value:2+' => '2',
'Class:CustomerContract/Attribute:provider_name+' => '', 'Class:SLT/Attribute:ticket_priority/Value:3' => '3',
'Class:CustomerContract/Attribute:support_team_id' => 'Support-Team', 'Class:SLT/Attribute:ticket_priority/Value:3+' => '3',
'Class:CustomerContract/Attribute:support_team_id+' => '', 'Class:SLT/Attribute:value' => 'Wert',
'Class:CustomerContract/Attribute:support_team_name' => 'Support-Team', 'Class:SLT/Attribute:value+' => '',
'Class:CustomerContract/Attribute:support_team_name+' => '', 'Class:SLT/Attribute:value_unit' => 'Einheit',
'Class:CustomerContract/Attribute:provider_list' => 'Anbieter', 'Class:SLT/Attribute:value_unit+' => '',
'Class:CustomerContract/Attribute:provider_list+' => '', 'Class:SLT/Attribute:value_unit/Value:days' => 'Tage',
'Class:CustomerContract/Attribute:sla_list' => 'SLAs', 'Class:SLT/Attribute:value_unit/Value:days+' => 'Tage',
'Class:CustomerContract/Attribute:sla_list+' => 'Liste der SLAs zu dem Vertrag', 'Class:SLT/Attribute:value_unit/Value:hours' => 'Stunden',
'Class:CustomerContract/Attribute:provider_list' => 'Zugrunde liegende Verträge', 'Class:SLT/Attribute:value_unit/Value:hours+' => 'Stunden',
'Class:CustomerContract/Attribute:sla_list+' => '', 'Class:SLT/Attribute:value_unit/Value:minutes' => 'Minuten',
)); 'Class:SLT/Attribute:value_unit/Value:minutes+' => 'Minuten',
'Class:SLT/Attribute:sla_list' => 'SLAs',
// Class: lnkCustomerContractToProviderContract 'Class:SLT/Attribute:sla_list+' => 'SLAs, die den SLT nutzen',
// 'Class:lnkSLTToSLA' => 'SLT/SLA',
'Class:lnkSLTToSLA+' => '',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:lnkSLTToSLA/Attribute:sla_id' => 'SLA',
'Class:lnkCustomerContractToProviderContract' => 'Verbindung Kundenvertratg zu Provider-Vertrag', 'Class:lnkSLTToSLA/Attribute:sla_id+' => '',
'Class:lnkCustomerContractToProviderContract+' => '', 'Class:lnkSLTToSLA/Attribute:slt_id' => 'SLT',
'Class:lnkCustomerContractToProviderContract/Attribute:customer_contract_id' => 'Kundenvertrag', 'Class:lnkSLTToSLA/Attribute:slt_id+' => '',
'Class:lnkCustomerContractToProviderContract/Attribute:customer_contract_id+' => '', 'Class:lnkServiceToDoc' => 'Service/Dokument',
'Class:lnkCustomerContractToProviderContract/Attribute:customer_contract_name' => 'Name', 'Class:lnkServiceToDoc+' => '',
'Class:lnkCustomerContractToProviderContract/Attribute:customer_contract_name+' => '', 'Class:lnkServiceToDoc/Attribute:service_id' => 'Service',
'Class:lnkCustomerContractToProviderContract/Attribute:provider_contract_id' => 'Provider Contract', 'Class:lnkServiceToDoc/Attribute:service_id+' => '',
'Class:lnkCustomerContractToProviderContract/Attribute:provider_contract_id+' => '', 'Class:lnkServiceToDoc/Attribute:document_id' => 'Dokument',
'Class:lnkCustomerContractToProviderContract/Attribute:provider_contract_name' => 'Name', 'Class:lnkServiceToDoc/Attribute:document_id+' => '',
'Class:lnkCustomerContractToProviderContract/Attribute:provider_contract_name+' => '', 'Class:lnkServiceToContact' => 'Service/Kontakt',
'Class:lnkCustomerContractToProviderContract/Attribute:provider_sla' => 'Provider-SLA', 'Class:lnkServiceToContact+' => '',
'Class:lnkCustomerContractToProviderContract/Attribute:provider_sla+' => 'Service Level Agreement', 'Class:lnkServiceToContact/Attribute:service_id' => 'Service',
'Class:lnkCustomerContractToProviderContract/Attribute:provider_coverage' => 'Service-Stunden', 'Class:lnkServiceToContact/Attribute:service_id+' => '',
'Class:lnkCustomerContractToProviderContract/Attribute:provider_coverage+' => '', 'Class:lnkServiceToContact/Attribute:contact_id' => 'Kontakt',
)); 'Class:lnkServiceToContact/Attribute:contact_id+' => '',
'Class:lnkServiceToContact/Attribute:role' => 'Rolle',
'Class:lnkServiceToContact/Attribute:role+' => '',
// 'Class:lnkServiceToCI' => 'Service/CI',
// Class: lnkContractToSLA 'Class:lnkServiceToCI+' => '',
// 'Class:lnkServiceToCI/Attribute:service_id' => 'Service',
'Class:lnkServiceToCI/Attribute:service_id+' => '',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:lnkServiceToCI/Attribute:ci_id' => 'CI',
'Class:lnkContractToSLA' => 'Contract/SLA', 'Class:lnkServiceToCI/Attribute:ci_id+' => '',
'Class:lnkContractToSLA+' => '', 'Menu:ServiceManagement' => 'Service Management',
'Class:lnkContractToSLA/Attribute:contract_id' => 'Vertrag', 'Menu:ServiceManagement+' => 'Service Management-Übersicht',
'Class:lnkContractToSLA/Attribute:contract_id+' => '', 'Menu:Service:Overview' => 'Übersicht',
'Class:lnkContractToSLA/Attribute:contract_name' => 'Vertrag', 'Menu:Service:Overview+' => '',
'Class:lnkContractToSLA/Attribute:contract_name+' => '', 'UI-ServiceManagementMenu-ContractsBySrvLevel' => 'Verträge nach Service Level',
'Class:lnkContractToSLA/Attribute:sla_id' => 'SLA', 'UI-ServiceManagementMenu-ContractsByStatus' => 'Verträge nach Status',
'Class:lnkContractToSLA/Attribute:sla_id+' => '', 'UI-ServiceManagementMenu-ContractsEndingIn30Days' => 'Verträge, die in weniger als 30 Tagen enden',
'Class:lnkContractToSLA/Attribute:sla_name' => 'SLA', 'Menu:ServiceType' => 'Service Typen',
'Class:lnkContractToSLA/Attribute:sla_name+' => '', 'Menu:ServiceType+' => 'Service Typen',
'Class:lnkContractToSLA/Attribute:coverage' => 'Abdeckung', 'Menu:ProviderContract' => 'Provider-Verträge',
'Class:lnkContractToSLA/Attribute:coverage+' => '', 'Menu:ProviderContract+' => 'Provider-Verträge',
)); 'Menu:CustomerContract' => 'Kundenverträge',
'Menu:CustomerContract+' => 'Kundenverträge',
// 'Menu:ServiceSubcategory' => 'Service-Unterkategorien',
// Class: lnkContractToDoc 'Menu:ServiceSubcategory+' => 'Service-Unterkategorien',
// 'Menu:Service' => 'Services',
'Menu:Service+' => 'Services',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Menu:SLA' => 'SLAs',
'Class:lnkContractToDoc' => 'Vertrag/Dokument', 'Menu:SLA+' => 'Service Level Agreements',
'Class:lnkContractToDoc+' => '', 'Menu:SLT' => 'SLTs',
'Class:lnkContractToDoc/Attribute:contract_id' => 'Vertrag', 'Menu:SLT+' => 'Service Level Targets',
'Class:lnkContractToDoc/Attribute:contract_id+' => '', 'Class:Contract' => 'Vertrag',
'Class:lnkContractToDoc/Attribute:contract_name' => 'Vertrag', 'Class:Contract+' => '',
'Class:lnkContractToDoc/Attribute:contract_name+' => '', 'Class:Contract/Attribute:name' => 'Name',
'Class:lnkContractToDoc/Attribute:document_id' => 'Dokument', 'Class:Contract/Attribute:name+' => '',
'Class:lnkContractToDoc/Attribute:document_id+' => '', 'Class:Contract/Attribute:description' => 'Beschreibung',
'Class:lnkContractToDoc/Attribute:document_name' => 'Dokument', 'Class:Contract/Attribute:description+' => '',
'Class:lnkContractToDoc/Attribute:document_name+' => '', 'Class:Contract/Attribute:start_date' => 'Anfangsdatum',
'Class:lnkContractToDoc/Attribute:document_type' => 'Dokumententyp', 'Class:Contract/Attribute:start_date+' => '',
'Class:lnkContractToDoc/Attribute:document_type+' => '', 'Class:Contract/Attribute:end_date' => 'Ablaufdatum',
'Class:lnkContractToDoc/Attribute:document_status' => 'Dokumentenstatus', 'Class:Contract/Attribute:end_date+' => '',
'Class:lnkContractToDoc/Attribute:document_status+' => '', 'Class:Contract/Attribute:cost' => 'Kosten',
)); 'Class:Contract/Attribute:cost+' => '',
'Class:Contract/Attribute:cost_currency' => 'Währung',
// 'Class:Contract/Attribute:cost_currency+' => '',
// Class: lnkContractToContact 'Class:Contract/Attribute:cost_currency/Value:dollars' => 'Dollar',
// 'Class:Contract/Attribute:cost_currency/Value:dollars+' => '',
'Class:Contract/Attribute:cost_currency/Value:euros' => 'Euro',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:Contract/Attribute:cost_currency/Value:euros+' => '',
'Class:lnkContractToContact' => 'Vertrag/Kontakt', 'Class:Contract/Attribute:cost_unit' => 'Kosteneinheit',
'Class:lnkContractToContact+' => '', 'Class:Contract/Attribute:cost_unit+' => '',
'Class:lnkContractToContact/Attribute:contract_id' => 'Vertrag', 'Class:Contract/Attribute:billing_frequency' => 'Abrechnungshäufigkeit',
'Class:lnkContractToContact/Attribute:contract_id+' => '', 'Class:Contract/Attribute:billing_frequency+' => '',
'Class:lnkContractToContact/Attribute:contract_name' => 'Vertrag', 'Class:Contract/Attribute:contact_list' => 'Kontakte',
'Class:lnkContractToContact/Attribute:contract_name+' => '', 'Class:Contract/Attribute:contact_list+' => 'Kontakte zu diesem Vertrag',
'Class:lnkContractToContact/Attribute:contact_id' => 'Kontakt', 'Class:Contract/Attribute:document_list' => 'Dokumente',
'Class:lnkContractToContact/Attribute:contact_id+' => '', 'Class:Contract/Attribute:document_list+' => 'Dokumente zu diesem Vertrag',
'Class:lnkContractToContact/Attribute:contact_name' => 'Kontakt', 'Class:Contract/Attribute:ci_list' => 'CIs',
'Class:lnkContractToContact/Attribute:contact_name+' => '', 'Class:Contract/Attribute:ci_list+' => 'CI, die von dem Vertrag unterstützt werden',
'Class:lnkContractToContact/Attribute:contact_email' => 'Kontaktemail', 'Class:Contract/Attribute:finalclass' => 'Typ',
'Class:lnkContractToContact/Attribute:contact_email+' => '', 'Class:Contract/Attribute:finalclass+' => '',
'Class:lnkContractToContact/Attribute:role' => 'Rolle', 'Class:ProviderContract/Attribute:provider_name' => 'Providername',
'Class:lnkContractToContact/Attribute:role+' => '', 'Class:ProviderContract/Attribute:provider_name+' => '',
)); 'Class:CustomerContract/Attribute:org_name' => 'Kundenname',
'Class:CustomerContract/Attribute:org_name+' => '',
// 'Class:CustomerContract/Attribute:provider_name' => 'Providername',
// Class: lnkContractToCI 'Class:CustomerContract/Attribute:provider_name+' => '',
// 'Class:CustomerContract/Attribute:support_team_name' => 'Support-Team',
'Class:CustomerContract/Attribute:support_team_name+' => '',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:lnkCustomerContractToProviderContract/Attribute:customer_contract_name' => 'Name',
'Class:lnkContractToCI' => 'Vertrag/CI', 'Class:lnkCustomerContractToProviderContract/Attribute:customer_contract_name+' => '',
'Class:lnkContractToCI+' => '', 'Class:lnkCustomerContractToProviderContract/Attribute:provider_contract_name' => 'Name',
'Class:lnkContractToCI/Attribute:contract_id' => 'Vertrag', 'Class:lnkCustomerContractToProviderContract/Attribute:provider_contract_name+' => '',
'Class:lnkContractToCI/Attribute:contract_id+' => '', 'Class:lnkCustomerContractToProviderContract/Attribute:provider_sla' => 'Provider-SLA',
'Class:lnkContractToCI/Attribute:contract_name' => 'Vertrag', 'Class:lnkCustomerContractToProviderContract/Attribute:provider_sla+' => 'Service Level Agreement',
'Class:lnkContractToCI/Attribute:contract_name+' => '', 'Class:lnkCustomerContractToProviderContract/Attribute:provider_coverage' => 'Service-Stunden',
'Class:lnkContractToCI/Attribute:ci_id' => 'CI', 'Class:lnkCustomerContractToProviderContract/Attribute:provider_coverage+' => '',
'Class:lnkContractToCI/Attribute:ci_id+' => '', 'Class:lnkContractToSLA/Attribute:contract_name' => 'Vertrag',
'Class:lnkContractToCI/Attribute:ci_name' => 'CI', 'Class:lnkContractToSLA/Attribute:contract_name+' => '',
'Class:lnkContractToCI/Attribute:ci_name+' => '', 'Class:lnkContractToSLA/Attribute:sla_name' => 'SLA',
'Class:lnkContractToCI/Attribute:ci_status' => 'CI-Status', 'Class:lnkContractToSLA/Attribute:sla_name+' => '',
'Class:lnkContractToCI/Attribute:ci_status+' => '', 'Class:lnkContractToDoc/Attribute:contract_name' => 'Vertrag',
)); 'Class:lnkContractToDoc/Attribute:contract_name+' => '',
'Class:lnkContractToDoc/Attribute:document_name' => 'Dokument',
// 'Class:lnkContractToDoc/Attribute:document_name+' => '',
// Class: Service 'Class:lnkContractToDoc/Attribute:document_type' => 'Dokumententyp',
// 'Class:lnkContractToDoc/Attribute:document_type+' => '',
'Class:lnkContractToDoc/Attribute:document_status' => 'Dokumentenstatus',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:lnkContractToDoc/Attribute:document_status+' => '',
'Class:Service' => 'Service', 'Class:lnkContractToContact/Attribute:contract_name' => 'Vertrag',
'Class:Service+' => '', 'Class:lnkContractToContact/Attribute:contract_name+' => '',
'Class:Service/Attribute:org_id' => 'Anbieter', 'Class:lnkContractToContact/Attribute:contact_name' => 'Kontakt',
'Class:Service/Attribute:org_id+' => '', 'Class:lnkContractToContact/Attribute:contact_name+' => '',
'Class:Service/Attribute:provider_name' => 'Anbieter', 'Class:lnkContractToContact/Attribute:contact_email' => 'Kontaktemail',
'Class:Service/Attribute:provider_name+' => '', 'Class:lnkContractToContact/Attribute:contact_email+' => '',
'Class:Service/Attribute:name' => 'Name', 'Class:lnkContractToCI/Attribute:contract_name' => 'Vertrag',
'Class:Service/Attribute:name+' => '', 'Class:lnkContractToCI/Attribute:contract_name+' => '',
'Class:Service/Attribute:description' => 'Beschreibung', 'Class:lnkContractToCI/Attribute:ci_name' => 'CI',
'Class:Service/Attribute:description+' => '', 'Class:lnkContractToCI/Attribute:ci_name+' => '',
'Class:Service/Attribute:type' => 'Typ', 'Class:lnkContractToCI/Attribute:ci_status' => 'CI-Status',
'Class:Service/Attribute:type+' => '', 'Class:lnkContractToCI/Attribute:ci_status+' => '',
'Class:Service/Attribute:type/Value:IncidentManagement' => 'Incident Management', 'Class:Service/Attribute:provider_name' => 'Anbieter',
'Class:Service/Attribute:type/Value:IncidentManagement+' => 'Incident Management', 'Class:Service/Attribute:provider_name+' => '',
'Class:Service/Attribute:type/Value:RequestManagement' => 'Request Management', 'Class:Service/Tab:Related_Contracts' => 'Zugehörige Verträge',
'Class:Service/Attribute:type/Value:RequestManagement+' => 'Request Management', 'Class:Service/Tab:Related_Contracts+' => 'Verträge, die für diesen Service unterschrieben wurden',
'Class:Service/Attribute:status' => 'Status', 'Class:ServiceSubcategory/Attribute:service_name' => 'Service',
'Class:Service/Attribute:status+' => '', 'Class:ServiceSubcategory/Attribute:service_name+' => '',
'Class:Service/Attribute:status/Value:design' => 'Design', 'Class:SLA/Attribute:service_name' => 'Service',
'Class:Service/Attribute:status/Value:design+' => '', 'Class:SLA/Attribute:service_name+' => '',
'Class:Service/Attribute:status/Value:obsolete' => 'Veraltet', 'Class:lnkSLTToSLA/Attribute:sla_name' => 'SLA',
'Class:Service/Attribute:status/Value:obsolete+' => '', 'Class:lnkSLTToSLA/Attribute:sla_name+' => '',
'Class:Service/Attribute:status/Value:production' => 'Produktion', 'Class:lnkSLTToSLA/Attribute:slt_name' => 'SLT',
'Class:Service/Attribute:status/Value:production+' => '', 'Class:lnkSLTToSLA/Attribute:slt_name+' => '',
'Class:Service/Attribute:subcategory_list' => 'Service-Unterkategorien', 'Class:lnkSLTToSLA/Attribute:slt_metric' => 'Metrik',
'Class:Service/Attribute:subcategory_list+' => '', 'Class:lnkSLTToSLA/Attribute:slt_metric+' => '',
'Class:Service/Attribute:sla_list' => 'SLAs', 'Class:lnkSLTToSLA/Attribute:slt_ticket_priority' => 'Ticketpriorität',
'Class:Service/Attribute:sla_list+' => '', 'Class:lnkSLTToSLA/Attribute:slt_ticket_priority+' => '',
'Class:Service/Attribute:document_list' => 'Dokumente', 'Class:lnkSLTToSLA/Attribute:slt_value' => 'Wert',
'Class:Service/Attribute:document_list+' => 'Dokumente beigefügt zu dem Service', 'Class:lnkSLTToSLA/Attribute:slt_value+' => '',
'Class:Service/Attribute:contact_list' => 'Kontakte', 'Class:lnkSLTToSLA/Attribute:slt_value_unit' => 'Einheit',
'Class:Service/Attribute:contact_list+' => 'Kontakte, die für diesen Service eine Rolle wahrnehmen', 'Class:lnkSLTToSLA/Attribute:slt_value_unit+' => '',
'Class:Service/Tab:Related_Contracts' => 'Zugehörige Verträge', 'Class:lnkServiceToDoc/Attribute:service_name' => 'Service',
'Class:Service/Tab:Related_Contracts+' => 'Verträge, die für diesen Vertrag unterschrieben wurden', 'Class:lnkServiceToDoc/Attribute:service_name+' => '',
)); 'Class:lnkServiceToDoc/Attribute:document_name' => 'Dokument',
'Class:lnkServiceToDoc/Attribute:document_name+' => '',
// 'Class:lnkServiceToDoc/Attribute:document_type' => 'Dokumententyp',
// Class: ServiceSubcategory 'Class:lnkServiceToDoc/Attribute:document_type+' => '',
// 'Class:lnkServiceToDoc/Attribute:document_status' => 'Dokumentenstatus',
'Class:lnkServiceToDoc/Attribute:document_status+' => '',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:lnkServiceToContact/Attribute:service_name' => 'Service',
'Class:ServiceSubcategory' => 'Service-Unterkategorien', 'Class:lnkServiceToContact/Attribute:service_name+' => '',
'Class:ServiceSubcategory+' => '', 'Class:lnkServiceToContact/Attribute:contact_name' => 'Kontakt',
'Class:ServiceSubcategory/Attribute:name' => 'Name', 'Class:lnkServiceToContact/Attribute:contact_name+' => '',
'Class:ServiceSubcategory/Attribute:name+' => '', 'Class:lnkServiceToContact/Attribute:contact_email' => 'Kontaktemail',
'Class:ServiceSubcategory/Attribute:description' => 'Beschreibung', 'Class:lnkServiceToContact/Attribute:contact_email+' => '',
'Class:ServiceSubcategory/Attribute:description+' => '', 'Class:lnkServiceToCI/Attribute:service_name' => 'Service',
'Class:ServiceSubcategory/Attribute:service_id' => 'Service', 'Class:lnkServiceToCI/Attribute:service_name+' => '',
'Class:ServiceSubcategory/Attribute:service_id+' => '', 'Class:lnkServiceToCI/Attribute:ci_name' => 'CI',
'Class:ServiceSubcategory/Attribute:service_name' => 'Service', 'Class:lnkServiceToCI/Attribute:ci_name+' => '',
'Class:ServiceSubcategory/Attribute:service_name+' => '', 'Class:lnkServiceToCI/Attribute:ci_status' => 'CI-Status',
)); 'Class:lnkServiceToCI/Attribute:ci_status+' => '',
));
// ?>
// Class: SLA
//
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:SLA' => 'SLA',
'Class:SLA+' => '',
'Class:SLA/Attribute:name' => 'Name',
'Class:SLA/Attribute:name+' => '',
'Class:SLA/Attribute:service_id' => 'Service',
'Class:SLA/Attribute:service_id+' => '',
'Class:SLA/Attribute:service_name' => 'Service',
'Class:SLA/Attribute:service_name+' => '',
'Class:SLA/Attribute:slt_list' => 'SLTs',
'Class:SLA/Attribute:slt_list+' => 'Auflistung der Service Level-Grenzbereiche',
));
//
// Class: SLT
//
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:SLT' => 'SLT',
'Class:SLT+' => '',
'Class:SLT/Attribute:name' => 'Name',
'Class:SLT/Attribute:name+' => '',
'Class:SLT/Attribute:metric' => 'Metrisch',
'Class:SLT/Attribute:metric+' => '',
'Class:SLT/Attribute:metric/Value:TTO' => 'TTO',
'Class:SLT/Attribute:metric/Value:TTO+' => 'TTO',
'Class:SLT/Attribute:metric/Value:TTR' => 'TTR',
'Class:SLT/Attribute:metric/Value:TTR+' => 'TTR',
'Class:SLT/Attribute:ticket_priority' => 'Ticketpriorität',
'Class:SLT/Attribute:ticket_priority+' => '',
'Class:SLT/Attribute:ticket_priority/Value:1' => '1',
'Class:SLT/Attribute:ticket_priority/Value:1+' => '1',
'Class:SLT/Attribute:ticket_priority/Value:2' => '2',
'Class:SLT/Attribute:ticket_priority/Value:2+' => '2',
'Class:SLT/Attribute:ticket_priority/Value:3' => '3',
'Class:SLT/Attribute:ticket_priority/Value:3+' => '3',
'Class:SLT/Attribute:value' => 'Wert',
'Class:SLT/Attribute:value+' => '',
'Class:SLT/Attribute:value_unit' => 'Einheit',
'Class:SLT/Attribute:value_unit+' => '',
'Class:SLT/Attribute:value_unit/Value:days' => 'Tage',
'Class:SLT/Attribute:value_unit/Value:days+' => 'Tage',
'Class:SLT/Attribute:value_unit/Value:hours' => 'Stunden',
'Class:SLT/Attribute:value_unit/Value:hours+' => 'Stunden',
'Class:SLT/Attribute:value_unit/Value:minutes' => 'Minuten',
'Class:SLT/Attribute:value_unit/Value:minutes+' => 'Minuten',
'Class:SLT/Attribute:sla_list' => 'SLAs',
'Class:SLT/Attribute:sla_list+' => 'SLAs, die den SLT nutzen',
));
//
// Class: lnkSLTToSLA
//
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:lnkSLTToSLA' => 'SLT/SLA',
'Class:lnkSLTToSLA+' => '',
'Class:lnkSLTToSLA/Attribute:sla_id' => 'SLA',
'Class:lnkSLTToSLA/Attribute:sla_id+' => '',
'Class:lnkSLTToSLA/Attribute:sla_name' => 'SLA',
'Class:lnkSLTToSLA/Attribute:sla_name+' => '',
'Class:lnkSLTToSLA/Attribute:slt_id' => 'SLT',
'Class:lnkSLTToSLA/Attribute:slt_id+' => '',
'Class:lnkSLTToSLA/Attribute:slt_name' => 'SLT',
'Class:lnkSLTToSLA/Attribute:slt_name+' => '',
'Class:lnkSLTToSLA/Attribute:slt_metric' => 'Metrisch',
'Class:lnkSLTToSLA/Attribute:slt_metric+' => '',
'Class:lnkSLTToSLA/Attribute:slt_ticket_priority' => 'Ticketpriorität',
'Class:lnkSLTToSLA/Attribute:slt_ticket_priority+' => '',
'Class:lnkSLTToSLA/Attribute:slt_value' => 'Wert',
'Class:lnkSLTToSLA/Attribute:slt_value+' => '',
'Class:lnkSLTToSLA/Attribute:slt_value_unit' => 'Einheit',
'Class:lnkSLTToSLA/Attribute:slt_value_unit+' => '',
));
//
// Class: lnkServiceToDoc
//
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:lnkServiceToDoc' => 'Service/Dokument',
'Class:lnkServiceToDoc+' => '',
'Class:lnkServiceToDoc/Attribute:service_id' => 'Service',
'Class:lnkServiceToDoc/Attribute:service_id+' => '',
'Class:lnkServiceToDoc/Attribute:service_name' => 'Service',
'Class:lnkServiceToDoc/Attribute:service_name+' => '',
'Class:lnkServiceToDoc/Attribute:document_id' => 'Dokument',
'Class:lnkServiceToDoc/Attribute:document_id+' => '',
'Class:lnkServiceToDoc/Attribute:document_name' => 'Dokument',
'Class:lnkServiceToDoc/Attribute:document_name+' => '',
'Class:lnkServiceToDoc/Attribute:document_type' => 'Dokumententyp',
'Class:lnkServiceToDoc/Attribute:document_type+' => '',
'Class:lnkServiceToDoc/Attribute:document_status' => 'Dokumentenstatus',
'Class:lnkServiceToDoc/Attribute:document_status+' => '',
));
//
// Class: lnkServiceToContact
//
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:lnkServiceToContact' => 'Service/Kontakt',
'Class:lnkServiceToContact+' => '',
'Class:lnkServiceToContact/Attribute:service_id' => 'Service',
'Class:lnkServiceToContact/Attribute:service_id+' => '',
'Class:lnkServiceToContact/Attribute:service_name' => 'Service',
'Class:lnkServiceToContact/Attribute:service_name+' => '',
'Class:lnkServiceToContact/Attribute:contact_id' => 'Kontakt',
'Class:lnkServiceToContact/Attribute:contact_id+' => '',
'Class:lnkServiceToContact/Attribute:contact_name' => 'Kontakt',
'Class:lnkServiceToContact/Attribute:contact_name+' => '',
'Class:lnkServiceToContact/Attribute:contact_email' => 'Kontaktemail',
'Class:lnkServiceToContact/Attribute:contact_email+' => '',
'Class:lnkServiceToContact/Attribute:role' => 'Rolle',
'Class:lnkServiceToContact/Attribute:role+' => '',
));
//
// Class: lnkServiceToCI
//
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:lnkServiceToCI' => 'Service/CI',
'Class:lnkServiceToCI+' => '',
'Class:lnkServiceToCI/Attribute:service_id' => 'Service',
'Class:lnkServiceToCI/Attribute:service_id+' => '',
'Class:lnkServiceToCI/Attribute:service_name' => 'Service',
'Class:lnkServiceToCI/Attribute:service_name+' => '',
'Class:lnkServiceToCI/Attribute:ci_id' => 'CI',
'Class:lnkServiceToCI/Attribute:ci_id+' => '',
'Class:lnkServiceToCI/Attribute:ci_name' => 'CI',
'Class:lnkServiceToCI/Attribute:ci_name+' => '',
'Class:lnkServiceToCI/Attribute:ci_status' => 'CI-Status',
'Class:lnkServiceToCI/Attribute:ci_status+' => '',
));
?>

View File

@@ -1,274 +1,220 @@
<?php <?php
// Copyright (C) 2010 Combodo SARL // Copyright (C) 2010 Combodo SARL
// //
// This program is free software; you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License. // the Free Software Foundation; version 3 of the License.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/** /**
* Localized data * @author Erwan Taloc <erwan.taloc@combodo.com>
* * @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Erwan Taloc <erwan.taloc@combodo.com> * @author Denis Flaven <denis.flaven@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com> * @author Stephan Rosenke <stephan.rosenke@itomig.de>
* @author Denis Flaven <denis.flaven@combodo.com>
* @author Stephan Rosenke <stephan.rosenke@itomig.de> * @licence http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL */
*/
Dict::Add('DE DE', 'German', 'Deutsch', array(
// Dictionnay conventions 'Class:lnkTicketToDoc' => 'Ticket/Document',
// Class:<class_name> 'Class:lnkTicketToDoc+' => '',
// Class:<class_name>+ 'Class:lnkTicketToDoc/Attribute:ticket_id' => 'Ticket',
// Class:<class_name>/Attribute:<attribute_code> 'Class:lnkTicketToDoc/Attribute:ticket_id+' => '',
// Class:<class_name>/Attribute:<attribute_code>+ 'Class:lnkTicketToDoc/Attribute:document_id' => 'Dokument',
// Class:<class_name>/Attribute:<attribute_code>/Value:<value> 'Class:lnkTicketToDoc/Attribute:document_id+' => '',
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+ 'Class:lnkTicketToContact' => 'Ticket/Kontakt',
// Class:<class_name>/Stimulus:<stimulus_code> 'Class:lnkTicketToContact+' => '',
// Class:<class_name>/Stimulus:<stimulus_code>+ 'Class:lnkTicketToContact/Attribute:ticket_id' => 'Ticket',
'Class:lnkTicketToContact/Attribute:ticket_id+' => '',
'Class:lnkTicketToContact/Attribute:contact_id' => 'Kontakt',
// 'Class:lnkTicketToContact/Attribute:contact_id+' => '',
// Class: Ticket 'Class:lnkTicketToContact/Attribute:role' => 'Rolle',
// 'Class:lnkTicketToContact/Attribute:role+' => '',
'Class:lnkTicketToCI' => 'Ticket/CI',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:lnkTicketToCI+' => '',
'Class:Ticket' => 'Ticket', 'Class:lnkTicketToCI/Attribute:ticket_id' => 'Ticket',
'Class:Ticket+' => '', 'Class:lnkTicketToCI/Attribute:ticket_id+' => '',
'Class:Ticket/Attribute:ref' => 'Referenz', 'Class:lnkTicketToCI/Attribute:ci_id' => 'CI',
'Class:Ticket/Attribute:ref+' => '', 'Class:lnkTicketToCI/Attribute:ci_id+' => '',
'Class:Ticket/Attribute:title' => 'Titel', 'Class:lnkTicketToCI/Attribute:impact' => 'Auswirkung',
'Class:Ticket/Attribute:title+' => '', 'Class:lnkTicketToCI/Attribute:impact+' => '',
'Class:Ticket/Attribute:description' => 'Beschreibung', 'Class:Ticket' => 'Ticket',
'Class:Ticket/Attribute:description+' => '', 'Class:Ticket+' => '',
'Class:Ticket/Attribute:ticket_log' => 'Protokoll', 'Class:Ticket/Attribute:ref' => 'Referenz',
'Class:Ticket/Attribute:ticket_log+' => '', 'Class:Ticket/Attribute:ref+' => '',
'Class:Ticket/Attribute:start_date' => 'Gestartet', 'Class:Ticket/Attribute:title' => 'Titel',
'Class:Ticket/Attribute:start_date+' => '', 'Class:Ticket/Attribute:title+' => '',
'Class:Ticket/Attribute:document_list' => 'Dokumente', 'Class:Ticket/Attribute:description' => 'Beschreibung',
'Class:Ticket/Attribute:document_list+' => 'Dokumente zu diesem Ticket', 'Class:Ticket/Attribute:description+' => '',
'Class:Ticket/Attribute:ci_list' => 'CIs', 'Class:Ticket/Attribute:ticket_log' => 'Protokoll',
'Class:Ticket/Attribute:ci_list+' => 'CIs, die diesen Incident betreffen', 'Class:Ticket/Attribute:ticket_log+' => '',
'Class:Ticket/Attribute:contact_list' => 'Kontakte', 'Class:Ticket/Attribute:start_date' => 'Gestartet',
'Class:Ticket/Attribute:contact_list+' => 'Beteiligtes Team und beteiligte Personen', 'Class:Ticket/Attribute:start_date+' => '',
'Class:Ticket/Attribute:incident_list' => 'Dazugehörige Incidents', 'Class:Ticket/Attribute:document_list' => 'Dokumente',
'Class:Ticket/Attribute:incident_list+' => '', 'Class:Ticket/Attribute:document_list+' => 'Dokumente zu diesem Ticket',
'Class:Ticket/Attribute:finalclass' => 'Typ', 'Class:Ticket/Attribute:ci_list' => 'CIs',
'Class:Ticket/Attribute:finalclass+' => '', 'Class:Ticket/Attribute:ci_list+' => 'CIs, die diesen Incident betreffen',
)); 'Class:Ticket/Attribute:contact_list' => 'Kontakte',
'Class:Ticket/Attribute:contact_list+' => 'Beteiligtes Team und beteiligte Personen',
// Fieldset translation 'Class:Ticket/Attribute:incident_list' => 'Dazugehörige Incidents',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:Ticket/Attribute:incident_list+' => '',
'Class:Ticket/Attribute:finalclass' => 'Typ',
'Ticket:baseinfo' => 'Allgemeine Informationen', 'Class:Ticket/Attribute:finalclass+' => '',
'Ticket:date' => 'Daten', 'Ticket:baseinfo' => 'Allgemeine Informationen',
'Ticket:contact' => 'Kontakte', 'Ticket:date' => 'Daten',
'Ticket:moreinfo' => 'weitergehende Informationen', 'Ticket:contact' => 'Kontakte',
'Ticket:relation' => 'Beziehungen', 'Ticket:moreinfo' => 'weitergehende Informationen',
'Ticket:log' => 'Kommunikation', 'Ticket:relation' => 'Beziehungen',
'Ticket:log' => 'Kommunikation',
)); 'Class:lnkTicketToDoc/Attribute:ticket_ref' => 'Ticket #',
'Class:lnkTicketToDoc/Attribute:ticket_ref+' => '',
// 'Class:lnkTicketToDoc/Attribute:document_name' => 'Dokument',
// Class: lnkTicketToDoc 'Class:lnkTicketToDoc/Attribute:document_name+' => '',
// 'Class:lnkTicketToContact/Attribute:ticket_ref' => 'Ticket #',
'Class:lnkTicketToContact/Attribute:ticket_ref+' => '',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:lnkTicketToContact/Attribute:contact_name' => 'Kontakt',
'Class:lnkTicketToDoc' => 'Ticket/Document', 'Class:lnkTicketToContact/Attribute:contact_name+' => '',
'Class:lnkTicketToDoc+' => '', 'Class:lnkTicketToContact/Attribute:contact_email' => 'Email',
'Class:lnkTicketToDoc/Attribute:ticket_id' => 'Ticket', 'Class:lnkTicketToContact/Attribute:contact_email+' => '',
'Class:lnkTicketToDoc/Attribute:ticket_id+' => '', 'Class:lnkTicketToCI/Attribute:ticket_ref' => 'Ticket #',
'Class:lnkTicketToDoc/Attribute:ticket_ref' => 'Ticket #', 'Class:lnkTicketToCI/Attribute:ticket_ref+' => '',
'Class:lnkTicketToDoc/Attribute:ticket_ref+' => '', 'Class:lnkTicketToCI/Attribute:ci_name' => 'CI',
'Class:lnkTicketToDoc/Attribute:document_id' => 'Dokument', 'Class:lnkTicketToCI/Attribute:ci_name+' => '',
'Class:lnkTicketToDoc/Attribute:document_id+' => '', 'Class:lnkTicketToCI/Attribute:ci_status' => 'CI-Status',
'Class:lnkTicketToDoc/Attribute:document_name' => 'Dokument', 'Class:lnkTicketToCI/Attribute:ci_status+' => '',
'Class:lnkTicketToDoc/Attribute:document_name+' => '', 'Class:ResponseTicket' => 'Antwortticket',
)); 'Class:ResponseTicket+' => '',
'Class:ResponseTicket/Attribute:status' => 'Status',
// 'Class:ResponseTicket/Attribute:status+' => '',
// Class: lnkTicketToContact 'Class:ResponseTicket/Attribute:status/Value:new' => 'Neu',
// 'Class:ResponseTicket/Attribute:status/Value:new+' => 'Neu eröffnet',
'Class:ResponseTicket/Attribute:status/Value:escalated_tto' => 'Eskalation/TTO',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:ResponseTicket/Attribute:status/Value:escalated_tto+' => '',
'Class:lnkTicketToContact' => 'Ticket/Kontakt', 'Class:ResponseTicket/Attribute:status/Value:assigned' => 'Zugeteilt',
'Class:lnkTicketToContact+' => '', 'Class:ResponseTicket/Attribute:status/Value:assigned+' => '',
'Class:lnkTicketToContact/Attribute:ticket_id' => 'Ticket', 'Class:ResponseTicket/Attribute:status/Value:escalated_ttr' => 'Eskalation/TTR',
'Class:lnkTicketToContact/Attribute:ticket_id+' => '', 'Class:ResponseTicket/Attribute:status/Value:escalated_ttr+' => '',
'Class:lnkTicketToContact/Attribute:ticket_ref' => 'Ticket #', 'Class:ResponseTicket/Attribute:status/Value:frozen' => 'Anstehend',
'Class:lnkTicketToContact/Attribute:ticket_ref+' => '', 'Class:ResponseTicket/Attribute:status/Value:frozen+' => '',
'Class:lnkTicketToContact/Attribute:contact_id' => 'Kontakt', 'Class:ResponseTicket/Attribute:status/Value:resolved' => 'Gelöst',
'Class:lnkTicketToContact/Attribute:contact_id+' => '', 'Class:ResponseTicket/Attribute:status/Value:resolved+' => '',
'Class:lnkTicketToContact/Attribute:contact_name' => 'Kontakt', 'Class:ResponseTicket/Attribute:status/Value:closed' => 'Geschlossen',
'Class:lnkTicketToContact/Attribute:contact_name+' => '', 'Class:ResponseTicket/Attribute:status/Value:closed+' => '',
'Class:lnkTicketToContact/Attribute:contact_email' => 'Email', 'Class:ResponseTicket/Attribute:caller_id' => 'Melder',
'Class:lnkTicketToContact/Attribute:contact_email+' => '', 'Class:ResponseTicket/Attribute:caller_id+' => '',
'Class:lnkTicketToContact/Attribute:role' => 'Rolle', 'Class:ResponseTicket/Attribute:caller_email' => 'Email',
'Class:lnkTicketToContact/Attribute:role+' => '', 'Class:ResponseTicket/Attribute:caller_email+' => '',
)); 'Class:ResponseTicket/Attribute:org_id' => 'Kunde',
'Class:ResponseTicket/Attribute:org_id+' => '',
// 'Class:ResponseTicket/Attribute:org_name' => 'Kunde',
// Class: lnkTicketToCI 'Class:ResponseTicket/Attribute:org_name+' => '',
// 'Class:ResponseTicket/Attribute:service_id' => 'Service',
'Class:ResponseTicket/Attribute:service_id+' => '',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:ResponseTicket/Attribute:service_name' => 'Name',
'Class:lnkTicketToCI' => 'Ticket/CI', 'Class:ResponseTicket/Attribute:service_name+' => '',
'Class:lnkTicketToCI+' => '', 'Class:ResponseTicket/Attribute:servicesubcategory_id' => 'Service-Element',
'Class:lnkTicketToCI/Attribute:ticket_id' => 'Ticket', 'Class:ResponseTicket/Attribute:servicesubcategory_id+' => '',
'Class:lnkTicketToCI/Attribute:ticket_id+' => '', 'Class:ResponseTicket/Attribute:servicesubcategory_name' => 'Name',
'Class:lnkTicketToCI/Attribute:ticket_ref' => 'Ticket #', 'Class:ResponseTicket/Attribute:servicesubcategory_name+' => '',
'Class:lnkTicketToCI/Attribute:ticket_ref+' => '', 'Class:ResponseTicket/Attribute:product' => 'Produkt',
'Class:lnkTicketToCI/Attribute:ci_id' => 'CI', 'Class:ResponseTicket/Attribute:product+' => '',
'Class:lnkTicketToCI/Attribute:ci_id+' => '', 'Class:ResponseTicket/Attribute:impact' => 'Auswirkung',
'Class:lnkTicketToCI/Attribute:ci_name' => 'CI', 'Class:ResponseTicket/Attribute:impact+' => '',
'Class:lnkTicketToCI/Attribute:ci_name+' => '', 'Class:ResponseTicket/Attribute:impact/Value:1' => 'Eine Person',
'Class:lnkTicketToCI/Attribute:ci_status' => 'CI-Status', 'Class:ResponseTicket/Attribute:impact/Value:1+' => '',
'Class:lnkTicketToCI/Attribute:ci_status+' => '', 'Class:ResponseTicket/Attribute:impact/Value:2' => 'Ein Service',
'Class:lnkTicketToCI/Attribute:impact' => 'Auswirkung', 'Class:ResponseTicket/Attribute:impact/Value:2+' => '',
'Class:lnkTicketToCI/Attribute:impact+' => '', 'Class:ResponseTicket/Attribute:impact/Value:3' => 'Eine Abteilung',
)); 'Class:ResponseTicket/Attribute:impact/Value:3+' => '',
'Class:ResponseTicket/Attribute:urgency' => 'Dringlichkeit',
// 'Class:ResponseTicket/Attribute:urgency+' => '',
// Class: ResponseTicket 'Class:ResponseTicket/Attribute:urgency/Value:1' => 'Niedrig',
// 'Class:ResponseTicket/Attribute:urgency/Value:1+' => '',
'Class:ResponseTicket/Attribute:urgency/Value:2' => 'Medium',
Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:ResponseTicket/Attribute:urgency/Value:2+' => '',
'Class:ResponseTicket' => 'Antwortticket', 'Class:ResponseTicket/Attribute:urgency/Value:3' => 'Hoch',
'Class:ResponseTicket+' => '', 'Class:ResponseTicket/Attribute:urgency/Value:3+' => '',
'Class:ResponseTicket/Attribute:status' => 'Status', 'Class:ResponseTicket/Attribute:priority' => 'Priorität',
'Class:ResponseTicket/Attribute:status+' => '', 'Class:ResponseTicket/Attribute:priority+' => '',
'Class:ResponseTicket/Attribute:status/Value:new' => 'Neu', 'Class:ResponseTicket/Attribute:priority/Value:1' => 'Niedrig',
'Class:ResponseTicket/Attribute:status/Value:new+' => 'Neu eröffnet', 'Class:ResponseTicket/Attribute:priority/Value:1+' => '',
'Class:ResponseTicket/Attribute:status/Value:frozen' => 'Unerledigt', 'Class:ResponseTicket/Attribute:priority/Value:2' => 'Medium',
'Class:ResponseTicket/Attribute:status/Value:frozen+' => '', 'Class:ResponseTicket/Attribute:priority/Value:2+' => '',
'Class:ResponseTicket/Attribute:status/Value:escalated_tto' => 'Eskalation/TTO', 'Class:ResponseTicket/Attribute:priority/Value:3' => 'Hoch',
'Class:ResponseTicket/Attribute:status/Value:escalated_tto+' => '', 'Class:ResponseTicket/Attribute:priority/Value:3+' => '',
'Class:ResponseTicket/Attribute:status/Value:assigned' => 'Zugeteilt', 'Class:ResponseTicket/Attribute:workgroup_id' => 'Arbeitsgruppe',
'Class:ResponseTicket/Attribute:status/Value:assigned+' => '', 'Class:ResponseTicket/Attribute:workgroup_id+' => '',
'Class:ResponseTicket/Attribute:status/Value:escalated_ttr' => 'Eskalation/TTR', 'Class:ResponseTicket/Attribute:workgroup_name' => 'Arbeitsgruppe',
'Class:ResponseTicket/Attribute:status/Value:escalated_ttr+' => '', 'Class:ResponseTicket/Attribute:workgroup_name+' => '',
'Class:ResponseTicket/Attribute:status/Value:frozen' => 'Anstehend', 'Class:ResponseTicket/Attribute:agent_id' => 'Bearbeiter',
'Class:ResponseTicket/Attribute:status/Value:frozen+' => '', 'Class:ResponseTicket/Attribute:agent_id+' => '',
'Class:ResponseTicket/Attribute:status/Value:resolved' => 'Gelöst', 'Class:ResponseTicket/Attribute:agent_name' => 'Bearbeiter',
'Class:ResponseTicket/Attribute:status/Value:resolved+' => '', 'Class:ResponseTicket/Attribute:agent_name+' => '',
'Class:ResponseTicket/Attribute:status/Value:closed' => 'Geschlossen', 'Class:ResponseTicket/Attribute:agent_email' => 'Bearbeiter schreiben (Email)',
'Class:ResponseTicket/Attribute:status/Value:closed+' => '', 'Class:ResponseTicket/Attribute:agent_email+' => '',
'Class:ResponseTicket/Attribute:caller_id' => 'Melder', 'Class:ResponseTicket/Attribute:related_problem_id' => 'Dazugehöriges Problem',
'Class:ResponseTicket/Attribute:caller_id+' => '', 'Class:ResponseTicket/Attribute:related_problem_id+' => '',
'Class:ResponseTicket/Attribute:caller_email' => 'Email', 'Class:ResponseTicket/Attribute:related_problem_ref' => 'Referenz',
'Class:ResponseTicket/Attribute:caller_email+' => '', 'Class:ResponseTicket/Attribute:related_problem_ref+' => '',
'Class:ResponseTicket/Attribute:org_id' => 'Kunde', 'Class:ResponseTicket/Attribute:related_change_id' => 'Verbundene Changes',
'Class:ResponseTicket/Attribute:org_id+' => '', 'Class:ResponseTicket/Attribute:related_change_id+' => '',
'Class:ResponseTicket/Attribute:org_name' => 'Kunde', 'Class:ResponseTicket/Attribute:related_change_ref' => 'Verbundene Changes',
'Class:ResponseTicket/Attribute:org_name+' => '', 'Class:ResponseTicket/Attribute:related_change_ref+' => '',
'Class:ResponseTicket/Attribute:service_id' => 'Service', 'Class:ResponseTicket/Attribute:close_date' => 'Geschlossen',
'Class:ResponseTicket/Attribute:service_id+' => '', 'Class:ResponseTicket/Attribute:close_date+' => '',
'Class:ResponseTicket/Attribute:service_name' => 'Name', 'Class:ResponseTicket/Attribute:last_update' => 'Letzte Aktualisierung',
'Class:ResponseTicket/Attribute:service_name+' => '', 'Class:ResponseTicket/Attribute:last_update+' => '',
'Class:ResponseTicket/Attribute:servicesubcategory_id' => 'Service-Element', 'Class:ResponseTicket/Attribute:assignment_date' => 'Zugeteilt',
'Class:ResponseTicket/Attribute:servicesubcategory_id+' => '', 'Class:ResponseTicket/Attribute:assignment_date+' => '',
'Class:ResponseTicket/Attribute:servicesubcategory_name' => 'Name', 'Class:ResponseTicket/Attribute:resolution_date' => 'Lösungsdatum',
'Class:ResponseTicket/Attribute:servicesubcategory_name+' => '', 'Class:ResponseTicket/Attribute:resolution_date+' => '',
'Class:ResponseTicket/Attribute:product' => 'Produkt', 'Class:ResponseTicket/Attribute:tto_escalation_deadline' => 'TTO Eskaltionsfrist',
'Class:ResponseTicket/Attribute:product+' => '', 'Class:ResponseTicket/Attribute:tto_escalation_deadline+' => '',
'Class:ResponseTicket/Attribute:impact' => 'Auswirkung', 'Class:ResponseTicket/Attribute:ttr_escalation_deadline' => 'TTR Eskaltionsfrist',
'Class:ResponseTicket/Attribute:impact+' => '', 'Class:ResponseTicket/Attribute:ttr_escalation_deadline+' => '',
'Class:ResponseTicket/Attribute:impact/Value:1' => 'Eine Person', 'Class:ResponseTicket/Attribute:closure_deadline' => 'Abschlussfrist',
'Class:ResponseTicket/Attribute:impact/Value:1+' => '', 'Class:ResponseTicket/Attribute:closure_deadline+' => '',
'Class:ResponseTicket/Attribute:impact/Value:2' => 'Ein Service', 'Class:ResponseTicket/Attribute:resolution_code' => 'Code für Lösung',
'Class:ResponseTicket/Attribute:impact/Value:2+' => '', 'Class:ResponseTicket/Attribute:resolution_code+' => '',
'Class:ResponseTicket/Attribute:impact/Value:3' => 'Eine Abteilung', 'Class:ResponseTicket/Attribute:resolution_code/Value:couldnotreproduce' => 'Konnte nicht reproduziert werden',
'Class:ResponseTicket/Attribute:impact/Value:3+' => '', 'Class:ResponseTicket/Attribute:resolution_code/Value:couldnotreproduce+' => '',
'Class:ResponseTicket/Attribute:urgency' => 'Dringlichkeit', 'Class:ResponseTicket/Attribute:resolution_code/Value:duplicate' => 'Duplikat eines bestehenden Tickets',
'Class:ResponseTicket/Attribute:urgency+' => '', 'Class:ResponseTicket/Attribute:resolution_code/Value:duplicate+' => '',
'Class:ResponseTicket/Attribute:urgency/Value:1' => 'Niedrig', 'Class:ResponseTicket/Attribute:resolution_code/Value:fixed' => 'Repariert',
'Class:ResponseTicket/Attribute:urgency/Value:1+' => '', 'Class:ResponseTicket/Attribute:resolution_code/Value:fixed+' => '',
'Class:ResponseTicket/Attribute:urgency/Value:2' => 'Medium', 'Class:ResponseTicket/Attribute:resolution_code/Value:irrelevant' => 'Irrelevant',
'Class:ResponseTicket/Attribute:urgency/Value:2+' => '', 'Class:ResponseTicket/Attribute:resolution_code/Value:irrelevant+' => '',
'Class:ResponseTicket/Attribute:urgency/Value:3' => 'Hoch', 'Class:ResponseTicket/Attribute:solution' => 'Lösung',
'Class:ResponseTicket/Attribute:urgency/Value:3+' => '', 'Class:ResponseTicket/Attribute:solution+' => '',
'Class:ResponseTicket/Attribute:priority' => 'Priorität', 'Class:ResponseTicket/Attribute:user_satisfaction' => 'Benutzerzufriedenheit',
'Class:ResponseTicket/Attribute:priority+' => '', 'Class:ResponseTicket/Attribute:user_satisfaction+' => '',
'Class:ResponseTicket/Attribute:priority/Value:1' => 'Niedrig', 'Class:ResponseTicket/Attribute:user_satisfaction/Value:1' => '1',
'Class:ResponseTicket/Attribute:priority/Value:1+' => '', 'Class:ResponseTicket/Attribute:user_satisfaction/Value:1+' => '1',
'Class:ResponseTicket/Attribute:priority/Value:2' => 'Medium', 'Class:ResponseTicket/Attribute:user_satisfaction/Value:2' => '2',
'Class:ResponseTicket/Attribute:priority/Value:2+' => '', 'Class:ResponseTicket/Attribute:user_satisfaction/Value:2+' => '2',
'Class:ResponseTicket/Attribute:priority/Value:3' => 'Hoch', 'Class:ResponseTicket/Attribute:user_satisfaction/Value:3' => '3',
'Class:ResponseTicket/Attribute:priority/Value:3+' => '', 'Class:ResponseTicket/Attribute:user_satisfaction/Value:3+' => '3',
'Class:ResponseTicket/Attribute:workgroup_id' => 'Arbeitsgruppe', 'Class:ResponseTicket/Attribute:user_satisfaction/Value:4' => '4',
'Class:ResponseTicket/Attribute:workgroup_id+' => '', 'Class:ResponseTicket/Attribute:user_satisfaction/Value:4+' => '4',
'Class:ResponseTicket/Attribute:workgroup_name' => 'Arbeitsgruppe', 'Class:ResponseTicket/Attribute:user_commment' => 'Benutzerkommentar',
'Class:ResponseTicket/Attribute:workgroup_name+' => '', 'Class:ResponseTicket/Attribute:user_commment+' => '',
'Class:ResponseTicket/Attribute:agent_id' => 'Bearbeiter', 'Class:ResponseTicket/Stimulus:ev_assign' => 'Zuweisen',
'Class:ResponseTicket/Attribute:agent_id+' => '', 'Class:ResponseTicket/Stimulus:ev_assign+' => '',
'Class:ResponseTicket/Attribute:agent_name' => 'Bearbeiter', 'Class:ResponseTicket/Stimulus:ev_reassign' => 'Neu zuweisen',
'Class:ResponseTicket/Attribute:agent_name+' => '', 'Class:ResponseTicket/Stimulus:ev_reassign+' => '',
'Class:ResponseTicket/Attribute:agent_email' => 'Bearbeiter schreiben (Email)', 'Class:ResponseTicket/Stimulus:ev_timeout' => 'ev-Timeout',
'Class:ResponseTicket/Attribute:agent_email+' => '', 'Class:ResponseTicket/Stimulus:ev_timeout+' => '',
'Class:ResponseTicket/Attribute:related_problem_id' => 'Dazugehöriges Problem', 'Class:ResponseTicket/Stimulus:ev_resolve' => 'Als gelöst markieren',
'Class:ResponseTicket/Attribute:related_problem_id+' => '', 'Class:ResponseTicket/Stimulus:ev_resolve+' => '',
'Class:ResponseTicket/Attribute:related_problem_ref' => 'Referenz', 'Class:ResponseTicket/Stimulus:ev_close' => 'Schließen',
'Class:ResponseTicket/Attribute:related_problem_ref+' => '', 'Class:ResponseTicket/Stimulus:ev_close+' => '',
'Class:ResponseTicket/Attribute:related_change_id' => 'Verbundene Änderungen', ));
'Class:ResponseTicket/Attribute:related_change_id+' => '', ?>
'Class:ResponseTicket/Attribute:related_change_ref' => 'Verbundene Änderungen',
'Class:ResponseTicket/Attribute:related_change_ref+' => '',
'Class:ResponseTicket/Attribute:close_date' => 'Geschlossen',
'Class:ResponseTicket/Attribute:close_date+' => '',
'Class:ResponseTicket/Attribute:last_update' => 'Letzte Aktualisierung',
'Class:ResponseTicket/Attribute:last_update+' => '',
'Class:ResponseTicket/Attribute:assignment_date' => 'Zugeteilt',
'Class:ResponseTicket/Attribute:assignment_date+' => '',
'Class:ResponseTicket/Attribute:resolution_date' => 'Lösungsdatum',
'Class:ResponseTicket/Attribute:resolution_date+' => '',
'Class:ResponseTicket/Attribute:tto_escalation_deadline' => 'TTO Eskaltionsfrist',
'Class:ResponseTicket/Attribute:tto_escalation_deadline+' => '',
'Class:ResponseTicket/Attribute:ttr_escalation_deadline' => 'TTR Eskaltionsfrist',
'Class:ResponseTicket/Attribute:ttr_escalation_deadline+' => '',
'Class:ResponseTicket/Attribute:closure_deadline' => 'Abschlussfrist',
'Class:ResponseTicket/Attribute:closure_deadline+' => '',
'Class:ResponseTicket/Attribute:resolution_code' => 'Code für Lösung',
'Class:ResponseTicket/Attribute:resolution_code+' => '',
'Class:ResponseTicket/Attribute:resolution_code/Value:couldnotreproduce' => 'Konnte nicht reproduziert werden',
'Class:ResponseTicket/Attribute:resolution_code/Value:couldnotreproduce+' => '',
'Class:ResponseTicket/Attribute:resolution_code/Value:duplicate' => 'Duplikat eines bestehenden Tickets',
'Class:ResponseTicket/Attribute:resolution_code/Value:duplicate+' => '',
'Class:ResponseTicket/Attribute:resolution_code/Value:fixed' => 'Repariert',
'Class:ResponseTicket/Attribute:resolution_code/Value:fixed+' => '',
'Class:ResponseTicket/Attribute:resolution_code/Value:irrelevant' => 'Irrelevant',
'Class:ResponseTicket/Attribute:resolution_code/Value:irrelevant+' => '',
'Class:ResponseTicket/Attribute:solution' => 'Lösung',
'Class:ResponseTicket/Attribute:solution+' => '',
'Class:ResponseTicket/Attribute:user_satisfaction' => 'Benutzerzufriedenheit',
'Class:ResponseTicket/Attribute:user_satisfaction+' => '',
'Class:ResponseTicket/Attribute:user_satisfaction/Value:1' => '1',
'Class:ResponseTicket/Attribute:user_satisfaction/Value:1+' => '1',
'Class:ResponseTicket/Attribute:user_satisfaction/Value:2' => '2',
'Class:ResponseTicket/Attribute:user_satisfaction/Value:2+' => '2',
'Class:ResponseTicket/Attribute:user_satisfaction/Value:3' => '3',
'Class:ResponseTicket/Attribute:user_satisfaction/Value:3+' => '3',
'Class:ResponseTicket/Attribute:user_satisfaction/Value:4' => '4',
'Class:ResponseTicket/Attribute:user_satisfaction/Value:4+' => '4',
'Class:ResponseTicket/Attribute:user_commment' => 'Benutzerkommentar',
'Class:ResponseTicket/Attribute:user_commment+' => '',
'Class:ResponseTicket/Stimulus:ev_assign' => 'Zuweisen',
'Class:ResponseTicket/Stimulus:ev_assign+' => '',
'Class:ResponseTicket/Stimulus:ev_reassign' => 'Neu zuweisen',
'Class:ResponseTicket/Stimulus:ev_reassign+' => '',
'Class:ResponseTicket/Stimulus:ev_timeout' => 'ev-Timeout',
'Class:ResponseTicket/Stimulus:ev_timeout+' => '',
'Class:ResponseTicket/Stimulus:ev_resolve' => 'Als gelöst markieren',
'Class:ResponseTicket/Stimulus:ev_resolve+' => '',
'Class:ResponseTicket/Stimulus:ev_close' => 'Schließen',
'Class:ResponseTicket/Stimulus:ev_close+' => '',
));
?>

View File

@@ -51,11 +51,12 @@ class ItopWelcome extends ModuleHandlerAPI
if (UserRights::IsAdministrator()) if (UserRights::IsAdministrator())
{ {
$oAdminMenu = new MenuGroup('AdminTools', 80 /* fRank */); $oAdminMenu = new MenuGroup('AdminTools', 80 /* fRank */);
new OQLMenuNode('UserAccountsMenu', 'SELECT User', $oAdminMenu->GetIndex(), 1 /* fRank */); new OQLMenuNode('UserAccountsMenu', 'SELECT User', $oAdminMenu->GetIndex(), 1 /* fRank */, true /* bSearch */);
new OQLMenuNode('ProfilesMenu', 'SELECT URP_Profiles', $oAdminMenu->GetIndex(), 2 /* fRank */); new OQLMenuNode('ProfilesMenu', 'SELECT URP_Profiles', $oAdminMenu->GetIndex(), 2 /* fRank */);
new TemplateMenuNode('NotificationsMenu', APPROOT.'application/templates/notifications_menu.html', $oAdminMenu->GetIndex(), 3 /* fRank */); new TemplateMenuNode('NotificationsMenu', APPROOT.'application/templates/notifications_menu.html', $oAdminMenu->GetIndex(), 3 /* fRank */);
new OQLMenuNode('AuditCategories', 'SELECT AuditCategory', $oAdminMenu->GetIndex(), 4 /* fRank */); new OQLMenuNode('AuditCategories', 'SELECT AuditCategory', $oAdminMenu->GetIndex(), 4 /* fRank */);
new WebPageMenuNode('RunQueriesMenu', utils::GetAbsoluteUrlAppRoot().'pages/run_query.php', $oAdminMenu->GetIndex(), 8 /* fRank */); new WebPageMenuNode('RunQueriesMenu', utils::GetAbsoluteUrlAppRoot().'pages/run_query.php', $oAdminMenu->GetIndex(), 8 /* fRank */);
new OQLMenuNode('QueryMenu', 'SELECT Query', $oAdminMenu->GetIndex(), 8.5 /* fRank */);
new WebPageMenuNode('ExportMenu', utils::GetAbsoluteUrlAppRoot().'webservices/export.php', $oAdminMenu->GetIndex(), 9 /* fRank */); new WebPageMenuNode('ExportMenu', utils::GetAbsoluteUrlAppRoot().'webservices/export.php', $oAdminMenu->GetIndex(), 9 /* fRank */);
new WebPageMenuNode('DataModelMenu', utils::GetAbsoluteUrlAppRoot().'pages/schema.php', $oAdminMenu->GetIndex(), 10 /* fRank */); new WebPageMenuNode('DataModelMenu', utils::GetAbsoluteUrlAppRoot().'pages/schema.php', $oAdminMenu->GetIndex(), 10 /* fRank */);
new WebPageMenuNode('UniversalSearchMenu', utils::GetAbsoluteUrlAppRoot().'pages/UniversalSearch.php', $oAdminMenu->GetIndex(), 11 /* fRank */); new WebPageMenuNode('UniversalSearchMenu', utils::GetAbsoluteUrlAppRoot().'pages/UniversalSearch.php', $oAdminMenu->GetIndex(), 11 /* fRank */);
@@ -63,24 +64,4 @@ class ItopWelcome extends ModuleHandlerAPI
} }
} }
/**
* Direct end-users to the standard Portal application
*/
class MyPortalURLMaker implements iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId)
{
switch($sClass)
{
case 'UserRequest':
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
$sUrl = "{$sAbsoluteUrl}portal/index.php?operation=details&class=$sClass&id=$iId";
return $sUrl;
default:
return '';
}
}
}
?> ?>

View File

@@ -8,6 +8,8 @@
import fl.controls.Slider; import fl.controls.Slider;
import fl.events.SliderEvent; import fl.events.SliderEvent;
import fl.controls.Label; import fl.controls.Label;
// For callbacks from Javascript
import flash.external.ExternalInterface;
// The main canvas // The main canvas
public class Navigator extends MovieClip public class Navigator extends MovieClip
@@ -26,6 +28,7 @@
protected var m_sRelation:String; protected var m_sRelation:String;
protected var m_sObjClass:String; protected var m_sObjClass:String;
protected var m_sObjId:String; protected var m_sObjId:String;
protected var m_sExclude:String;
// Constants // Constants
protected var m_RADIUS = 150; protected var m_RADIUS = 150;
@@ -43,14 +46,61 @@
{ {
m_aLinks = new Array(); m_aLinks = new Array();
m_aNodes = new Array(); m_aNodes = new Array();
m_sExclude = '';
m_fZoom = 1; m_fZoom = 1;
m_oLoader = null;
initParameters(); initParameters();
doLoadData(); var success = true;
if (ExternalInterface.available)
{
//Security.allowDomain(loader.contentLoaderInfo.url);
//the addCallback registers a function to be called from javascript, and this is what you wanted to do:
try
{
ExternalInterface.addCallback("Filter", DoFilter);
}
catch(err)
{
m_sTitle.text = 'E: '+err.description;
success = false
}
}
if (success)
{
//m_sTitle.text = 'Ok...';
doLoadData();
}
else
{
m_sTitle.text = 'Failed to addCallback';
}
addEventListener(Event.ENTER_FRAME, initGraphics); addEventListener(Event.ENTER_FRAME, initGraphics);
//Stop scaling the flash content //Stop scaling the flash content
stage.scaleMode = StageScaleMode.NO_SCALE; stage.scaleMode = StageScaleMode.NO_SCALE;
} }
function Reset()
{
m_aLinks = new Array();
for (var i:String in m_aNodes)
{
m_oCanvas.removeChild(m_aNodes[i]);
}
m_aNodes = new Array();
removeEventListener(Event.ENTER_FRAME, drawLines);
stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDown)
stage.removeEventListener(MouseEvent.MOUSE_UP, mouseReleased);
}
function DoFilter(sExcludeList:String):void
{
//do something
m_sExclude = sExcludeList;
doLoadData();
}
protected function initParameters():void protected function initParameters():void
{ {
@@ -70,6 +120,8 @@
m_oCanvas.scaleY = m_fZoom; m_oCanvas.scaleY = m_fZoom;
// Handle listeners... // Handle listeners...
removeEventListener(Event.ENTER_FRAME,initGraphics); removeEventListener(Event.ENTER_FRAME,initGraphics);
m_oZoomSlider.value = 100;
m_oZoomSlider.addEventListener(SliderEvent.CHANGE, onZoomChange);
} }
function mouseDown(event:MouseEvent):void function mouseDown(event:MouseEvent):void
{ {
@@ -99,21 +151,28 @@
m_oCanvas.scaleX = m_fZoom; m_oCanvas.scaleX = m_fZoom;
m_oCanvas.scaleY = m_fZoom; m_oCanvas.scaleY = m_fZoom;
} }
function GetZommLevel() function GetZoomLevel()
{ {
return m_fZoom; return m_fZoom;
} }
function doLoadData() function doLoadData()
{ {
m_sTitle.text = "Loading...";
m_oPreloader.visible = true;
m_oZoomSlider.enabled = true;
var sSeparator:String = '?'; var sSeparator:String = '?';
if (m_sDataUrl.indexOf(sSeparator) != -1) if (m_sDataUrl.indexOf(sSeparator) != -1)
{ {
sSeparator = '&'; sSeparator = '&';
} }
var myString:String = m_sDataUrl+sSeparator+'relation='+m_sRelation+'&class='+m_sObjClass+'&id='+m_sObjId; var myString:String = m_sDataUrl+sSeparator+'relation='+m_sRelation+'&class='+m_sObjClass+'&id='+m_sObjId+'&exclude='+m_sExclude;
trace("Requesting:"+myString); trace("Requesting:"+myString);
var myXMLURL:URLRequest = new URLRequest(myString); var myXMLURL:URLRequest = new URLRequest(myString);
if (m_oLoader != null)
{
m_oLoader.close();
}
m_oLoader = new URLLoader(); m_oLoader = new URLLoader();
m_oLoader.addEventListener(Event.COMPLETE, onXMLLoadComplete); m_oLoader.addEventListener(Event.COMPLETE, onXMLLoadComplete);
m_oLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onXMLLoadError); m_oLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onXMLLoadError);
@@ -125,6 +184,7 @@
{ {
try try
{ {
Reset();
var myXML:XML = XML(m_oLoader.data); var myXML:XML = XML(m_oLoader.data);
//trace("Data loaded." + myXML); //trace("Data loaded." + myXML);
//trace("==========================="); //trace("===========================");
@@ -132,8 +192,6 @@
m_sTitle.text = decodeEntities(myXML.attribute("title").toString()); m_sTitle.text = decodeEntities(myXML.attribute("title").toString());
m_oZoomSlider.enabled = true; m_oZoomSlider.enabled = true;
addEventListener(Event.ENTER_FRAME, drawLines); addEventListener(Event.ENTER_FRAME, drawLines);
m_oZoomSlider.value = 100;
m_oZoomSlider.addEventListener(SliderEvent.CHANGE, onZoomChange);
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown) stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown)
stage.addEventListener(MouseEvent.MOUSE_UP, mouseReleased); stage.addEventListener(MouseEvent.MOUSE_UP, mouseReleased);
//trace('======= Initial Posistions ========='); //trace('======= Initial Posistions =========');
@@ -155,8 +213,7 @@
{ {
if (m_oPreloader != null) if (m_oPreloader != null)
{ {
removeChild(m_oPreloader); m_oPreloader.visible = false;
m_oPreloader = null;
} }
} }
} }
@@ -165,8 +222,7 @@
{ {
if (m_oPreloader != null) if (m_oPreloader != null)
{ {
removeChild(m_oPreloader); m_oPreloader.visible = false;
m_oPreloader = null;
} }
m_sTitle.text = "I/O Error: unable to load the graph data ("+event+")"; m_sTitle.text = "I/O Error: unable to load the graph data ("+event+")";
} }

Binary file not shown.

Binary file not shown.

View File

@@ -393,6 +393,7 @@ function ApplyNextAction(Webpage $oP, CMDBObject $oObj, $sNextAction, $oMyChange
} }
$oObj->DBUpdateTracked($oMyChange); $oObj->DBUpdateTracked($oMyChange);
} }
$oObj->Reload();
$oObj->DisplayDetails($oP); $oObj->DisplayDetails($oP);
} }
else else
@@ -673,7 +674,7 @@ try
else else
{ {
$oP->set_title(Dict::S('UI:SearchResultsPageTitle')); $oP->set_title(Dict::S('UI:SearchResultsPageTitle'));
$oP->p("<h1>".Dict::Format('UI:FullTextSearchTitle_Text', $sFullText)."</h1>"); $oP->p("<h1>".Dict::Format('UI:FullTextSearchTitle_Text', htmlentities($sFullText, ENT_QUOTES, 'UTF-8'))."</h1>");
$iCount = 0; $iCount = 0;
$iBlock = 0; $iBlock = 0;
// Search in full text mode in all the classes // Search in full text mode in all the classes
@@ -842,7 +843,7 @@ try
$currValue = $oObj->Get($sAttCode); $currValue = $oObj->Get($sAttCode);
if ($oAttDef instanceof AttributeCaseLog) if ($oAttDef instanceof AttributeCaseLog)
{ {
$currValue = ''; $currValue = ' '; // Don't put an empty string, in case the field would be considered as mandatory...
} }
if (is_object($currValue)) continue; // Skip non scalar values... if (is_object($currValue)) continue; // Skip non scalar values...
if(!array_key_exists($currValue, $aValues[$sAttCode])) if(!array_key_exists($currValue, $aValues[$sAttCode]))
@@ -935,6 +936,7 @@ try
} }
} }
$sTip .= "</ul></p>"; $sTip .= "</ul></p>";
$sTip = addslashes($sTip);
$sReadyScript .= "$('#multi_values_$sAttCode').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );"; $sReadyScript .= "$('#multi_values_$sAttCode').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );";
$oDummyObj->Set($sAttCode, null); $oDummyObj->Set($sAttCode, null);
@@ -1578,12 +1580,7 @@ EOF
{ {
$sDisplayValue = empty($aVal['display']) ? '<i>'.Dict::S('Enum:Undefined').'</i>' : str_replace(array("\n", "\r"), " ", $aVal['display']); $sDisplayValue = empty($aVal['display']) ? '<i>'.Dict::S('Enum:Undefined').'</i>' : str_replace(array("\n", "\r"), " ", $aVal['display']);
$sTip .= "<li>".Dict::Format('UI:BulkModify:Value_Exists_N_Times', $sDisplayValue, $aVal['count'])."</li>"; $sTip .= "<li>".Dict::Format('UI:BulkModify:Value_Exists_N_Times', $sDisplayValue, $aVal['count'])."</li>";
$index++; $index++;
if ($iMaxCount == $index)
{
$sTip .= "<li>".(count($aMultiValues) - $iMaxCount)." more different values...</li>";
break;
}
if ($iMaxCount == $index) if ($iMaxCount == $index)
{ {
$sTip .= "<li>".Dict::Format('UI:BulkModify:N_MoreValues', count($aValues[$sAttCode]) - $iMaxCount)."</li>"; $sTip .= "<li>".Dict::Format('UI:BulkModify:N_MoreValues', count($aValues[$sAttCode]) - $iMaxCount)."</li>";
@@ -1591,6 +1588,7 @@ EOF
} }
} }
$sTip .= "</ul></p>"; $sTip .= "</ul></p>";
$sTip = addslashes($sTip);
$sReadyScript .= "$('#multi_values_$sAttCode').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );\n"; $sReadyScript .= "$('#multi_values_$sAttCode').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );\n";
$sComments .= '<div class="multi_values" id="multi_values_'.$sAttCode.'">'.count($aValues[$sAttCode]).'</div>'; $sComments .= '<div class="multi_values" id="multi_values_'.$sAttCode.'">'.count($aValues[$sAttCode]).'</div>';
} }
@@ -1739,13 +1737,13 @@ EOF
{ {
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$aErrors[] = Dict::Format('UI:AttemptingToSetASlaveAttribute_Name', $oAttDef->GetLabel()); $aErrors[] = Dict::Format('UI:AttemptingToSetASlaveAttribute_Name', $oAttDef->GetLabel());
} unset($aExpectedAttributes[$sAttCode]);
else
{
$oObj->Set($sAttCode, $paramValue);
} }
} }
} }
$oObj->UpdateObjectFromPostedForm('', array_keys($aExpectedAttributes), $sTargetState);
if (count($aErrors) == 0) if (count($aErrors) == 0)
{ {
if ($oObj->ApplyStimulus($sStimulus)) if ($oObj->ApplyStimulus($sStimulus))
@@ -1982,13 +1980,13 @@ EOF
{ {
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$aErrors[] = Dict::Format('UI:AttemptingToChangeASlaveAttribute_Name', $oAttDef->GetLabel()); $aErrors[] = Dict::Format('UI:AttemptingToChangeASlaveAttribute_Name', $oAttDef->GetLabel());
unset($aExpectedAttributes[$sAttCode]);
} }
else
{
$oObj->Set($sAttCode, $paramValue);
} }
} }
}
$oObj->UpdateObjectFromPostedForm('', array_keys($aExpectedAttributes), $sTargetState);
if (count($aErrors) == 0) if (count($aErrors) == 0)
{ {
if ($oObj->ApplyStimulus($sStimulus)) if ($oObj->ApplyStimulus($sStimulus))
@@ -2011,6 +2009,7 @@ EOF
$oP->p(implode('</p><p>', $aErrors)); $oP->p(implode('</p><p>', $aErrors));
} }
} }
$oObj->Reload();
$oObj->DisplayDetails($oP); $oObj->DisplayDetails($oP);
} }
else else
@@ -2030,24 +2029,102 @@ EOF
$oP->AddTabContainer('Navigator'); $oP->AddTabContainer('Navigator');
$oP->SetCurrentTabContainer('Navigator'); $oP->SetCurrentTabContainer('Navigator');
$oP->SetCurrentTab(Dict::S('UI:RelationshipGraph')); $oP->SetCurrentTab(Dict::S('UI:RelationshipGraph'));
$oP->add("<div id=\"ds_flash\" class=\"SearchDrawer\">\n");
$oP->add_ready_script(
<<<EOF
$("#dh_flash").click( function() {
$("#ds_flash").slideToggle('normal', function() { $("#ds_flash").parent().resize(); } );
$("#dh_flash").toggleClass('open');
});
EOF
);
$aResults = array();
$aSortedElements = array();
$oObj = MetaModel::GetObject($sClass, $id);
$oObj->GetRelatedObjects($sRelation, 20 /* iMaxDepth */, $aResults);
foreach($aResults as $sClassIdx => $aObjects)
{
foreach($aObjects as $oCurrObj)
{
$sSubClass = get_class($oCurrObj);
$aSortedElements[$sSubClass] = MetaModel::GetName($sSubClass);
}
}
asort($aSortedElements);
$idx = 0;
foreach($aSortedElements as $sSubClass => $sClassName)
{
$oP->add("<span style=\"padding-right:2em; white-space:nowrap;\"><input type=\"checkbox\" id=\"exclude_$idx\" name=\"excluded[]\" value=\"$sSubClass\" checked onChange=\"$('#ReloadMovieBtn').button('enable')\"><label for=\"exclude_$idx\">&nbsp;".MetaModel::GetClassIcon($sSubClass)."&nbsp;$sClassName</label></span> ");
$idx++;
}
$oP->add("<p style=\"text-align:right\"><button type=\"button\" id=\"ReloadMovieBtn\" onClick=\"DoReload()\">".Dict::S('UI:Button:Refresh')."</button></p>");
$oP->add("</div>\n");
$oP->add("<div class=\"HRDrawer\"></div>\n");
$oP->add("<div id=\"dh_flash\" class=\"DrawerHandle\">".Dict::S('UI:ElementsDisplayed')."</div>\n");
$width = 1000; $width = 1000;
$height = 700; $height = 700;
$sDrillUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&'.$oAppContext->GetForLink(); $sDrillUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&'.$oAppContext->GetForLink();
$sParams = "pWidth=$width&pHeight=$height&drillUrl=".urlencode($sDrillUrl)."&displayController=false&xmlUrl=".urlencode("./xml.navigator.php")."&obj_class=$sClass&obj_id=$id&relation=$sRelation"; $sParams = "pWidth=$width&pHeight=$height&drillUrl=".urlencode($sDrillUrl)."&displayController=false&xmlUrl=".urlencode("./xml.navigator.php")."&obj_class=$sClass&obj_id=$id&relation=$sRelation";
$oP->add("<div style=\"z-index:1;background:white\"><object style=\"z-index:2\" classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0\" width=\"$width\" height=\"$height\" id=\"navigator\" align=\"middle\"> $oP->add("<div style=\"z-index:1;background:white;width:100%;height:{$height}px\"><object style=\"z-index:2\" classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0\" width=\"100%\" height=\"$height\" id=\"navigator\" align=\"middle\">
<param name=\"allowScriptAccess\" value=\"sameDomain\" /> <param name=\"allowScriptAccess\" value=\"always\" />
<param name=\"allowFullScreen\" value=\"false\" /> <param name=\"allowFullScreen\" value=\"false\" />
<param name=\"FlashVars\" value=\"$sParams\" /> <param name=\"FlashVars\" value=\"$sParams\" />
<param name=\"wmode\" value=\"transparent\"> <param name=\"wmode\" value=\"transparent\">
<param name=\"movie\" value=\"../navigator/navigator.swf\" /><param name=\"quality\" value=\"high\" /><param name=\"bgcolor\" value=\"#ffffff\" /> <param name=\"movie\" value=\"../navigator/navigator.swf\" /><param name=\"quality\" value=\"high\" /><param name=\"bgcolor\" value=\"#ffffff\" />
<embed src=\"../navigator/navigator.swf\" wmode=\"transparent\" flashVars=\"$sParams\" quality=\"high\" bgcolor=\"#ffffff\" width=\"$width\" height=\"$height\" name=\"navigator\" align=\"middle\" allowScriptAccess=\"sameDomain\" allowFullScreen=\"false\" type=\"application/x-shockwave-flash\" pluginspage=\"http://www.adobe.com/go/getflashplayer\" /> <embed src=\"../navigator/navigator.swf\" wmode=\"transparent\" flashVars=\"$sParams\" quality=\"high\" bgcolor=\"#ffffff\" width=\"100%\" height=\"$height\" name=\"navigator\" align=\"middle\" swliveconnect=\"true\" allowScriptAccess=\"always\" allowFullScreen=\"false\" type=\"application/x-shockwave-flash\" pluginspage=\"http://www.adobe.com/go/getflashplayer\" />
</object></div>\n"); </object></div>\n");
$oP->SetCurrentTab(Dict::S('UI:RelationshipList')); $oP->SetCurrentTab(Dict::S('UI:RelationshipList'));
$oP->add("<div id=\"impacted_objects\" style=\"width:100%;background-color:#fff;padding:10px;\"><p style=\"height:150px;\">&nbsp;</p></div>"); $oP->add("<div id=\"impacted_objects\" style=\"width:100%;background-color:#fff;padding:10px;\"><p style=\"height:150px;\">&nbsp;</p></div>");
$oP->add_script(
<<<EOF
function getFlashMovieObject(movieName)
{
if (window.document[movieName])
{
return window.document[movieName];
}
if (navigator.appName.indexOf("Microsoft Internet")==-1)
{
if (document.embeds && document.embeds[movieName])
return document.embeds[movieName];
}
else // if (navigator.appName.indexOf("Microsoft Internet")!=-1)
{
return document.getElementById(movieName);
}
}
function DoReload()
{
var oMovie = getFlashMovieObject('navigator');
try
{
var aExcluded = [];
$('input[name^=excluded]').each( function() {
if (!$(this).attr('checked'))
{
aExcluded.push($(this).val());
}
} );
oMovie.Filter(aExcluded.join(','));
//oMovie.SetVariable("/:message", "foo");
}
catch(err)
{
alert(err);
}
}
EOF
);
$oP->add_ready_script( $oP->add_ready_script(
<<<EOF <<<EOF
var ajax_request = null; var ajax_request = null;
$('#ReloadMovieBtn').button().button('disable');
function UpdateImpactedObjects(sClass, iId, sRelation) function UpdateImpactedObjects(sClass, iId, sRelation)
{ {
@@ -2070,8 +2147,6 @@ EOF
$('#impacted_objects').empty(); $('#impacted_objects').empty();
$('#impacted_objects').append(data); $('#impacted_objects').append(data);
$('#impacted_objects').unblock(); $('#impacted_objects').unblock();
$('#impacted_objects .listResults').tablesorter( { widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
$('#impacted_objects table.listResults').tableHover(); // hover tables
} }
); );
} }
@@ -2188,4 +2263,4 @@ catch(Exception $e)
IssueLog::Error($e->getMessage()); IssueLog::Error($e->getMessage());
} }
} }
?> ?>

View File

@@ -347,6 +347,7 @@ try
$aMoreReconciliationKeys = array(); // Store: key => void to automatically remove duplicates $aMoreReconciliationKeys = array(); // Store: key => void to automatically remove duplicates
foreach($aReconciliationKeys as $sAttCode) foreach($aReconciliationKeys as $sAttCode)
{ {
if (!MetaModel::IsValidAttCode($sClassName, $sAttCode)) continue;
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode); $oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode);
if ($oAttDef->IsExternalKey()) if ($oAttDef->IsExternalKey())
{ {
@@ -403,7 +404,7 @@ EOF
} }
else else
{ {
$oPage = new ajax_page(""); $oPage = new ajax_page("Class $sClassName is not a valid class !");
} }
break; break;
} }

View File

@@ -73,7 +73,6 @@ try
$sSelectMode = utils::ReadParam('select_mode', ''); $sSelectMode = utils::ReadParam('select_mode', '');
$bDisplayKey = utils::ReadParam('display_key', 'true') == 'true'; $bDisplayKey = utils::ReadParam('display_key', 'true') == 'true';
$aList = utils::ReadParam('display_list', array()); $aList = utils::ReadParam('display_list', array());
$sClassName = $oFilter->GetClass(); $sClassName = $oFilter->GetClass();
//$aList = cmdbAbstractObject::FlattenZList(MetaModel::GetZListItems($sClassName, 'list')); //$aList = cmdbAbstractObject::FlattenZList(MetaModel::GetZListItems($sClassName, 'list'));
@@ -169,6 +168,28 @@ try
} }
break; break;
// ui.linkswidget
case 'addObjects':
$oPage->SetContentType('text/html');
$sAttCode = utils::ReadParam('sAttCode', '');
$iInputId = utils::ReadParam('iInputId', '');
$sSuffix = utils::ReadParam('sSuffix', '');
$bDuplicates = (utils::ReadParam('bDuplicates', 'false') == 'false') ? false : true;
$sJson = utils::ReadParam('json', '', false, 'raw_data');
if (!empty($sJson))
{
$oWizardHelper = WizardHelper::FromJSON($sJson);
$oObj = $oWizardHelper->GetTargetObject();
}
else
{
// Search form: no current object
$oObj = null;
}
$oWidget = new UILinksWidget($sClass, $sAttCode, $iInputId, $sSuffix, $bDuplicates);
$oWidget->GetObjectPickerDialog($oPage, $oObj);
break;
// ui.linkswidget // ui.linkswidget
case 'searchObjectsToAdd': case 'searchObjectsToAdd':
$oPage->SetContentType('text/html'); $oPage->SetContentType('text/html');
@@ -192,6 +213,8 @@ try
$sRemoteClass = utils::ReadParam('sRemoteClass', '', false, 'class'); $sRemoteClass = utils::ReadParam('sRemoteClass', '', false, 'class');
$sFilter = utils::ReadParam('sFilter', '', false, 'raw_data'); $sFilter = utils::ReadParam('sFilter', '', false, 'raw_data');
$sJson = utils::ReadParam('json', '', false, 'raw_data'); $sJson = utils::ReadParam('json', '', false, 'raw_data');
$sAttCode = utils::ReadParam('sAttCode', '');
$bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true');
if (!empty($sJson)) if (!empty($sJson))
{ {
$oWizardHelper = WizardHelper::FromJSON($sJson); $oWizardHelper = WizardHelper::FromJSON($sJson);
@@ -202,7 +225,7 @@ try
// Search form: no current object // Search form: no current object
$oObj = null; $oObj = null;
} }
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId); $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode);
$oWidget->SearchObjectsToSelect($oPage, $sFilter, $sRemoteClass, $oObj); $oWidget->SearchObjectsToSelect($oPage, $sFilter, $sRemoteClass, $oObj);
break; break;
@@ -213,6 +236,34 @@ try
$sFilter = utils::ReadParam('sFilter', '', false, 'raw_data'); $sFilter = utils::ReadParam('sFilter', '', false, 'raw_data');
$sJson = utils::ReadParam('json', '', false, 'raw_data'); $sJson = utils::ReadParam('json', '', false, 'raw_data');
$sContains = utils::ReadParam('q', '', false, 'raw_data'); $sContains = utils::ReadParam('q', '', false, 'raw_data');
$bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true');
if ($sContains !='')
{
if (!empty($sJson))
{
$oWizardHelper = WizardHelper::FromJSON($sJson);
$oObj = $oWizardHelper->GetTargetObject();
}
else
{
// Search form: no current object
$oObj = null;
}
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, '', $bSearchMode);
$oWidget->AutoComplete($oPage, $sFilter, $oObj, $sContains);
}
break;
// ui.extkeywidget
case 'objectSearchForm':
$oPage->SetContentType('text/html');
$sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class');
$iInputId = utils::ReadParam('iInputId', '');
$sTitle = utils::ReadParam('sTitle', '', false, 'raw_data');
$sAttCode = utils::ReadParam('sAttCode', '');
$bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true');
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode);
$sJson = utils::ReadParam('json', '', false, 'raw_data');
if (!empty($sJson)) if (!empty($sJson))
{ {
$oWizardHelper = WizardHelper::FromJSON($sJson); $oWizardHelper = WizardHelper::FromJSON($sJson);
@@ -223,26 +274,27 @@ try
// Search form: no current object // Search form: no current object
$oObj = null; $oObj = null;
} }
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId); $oWidget->GetSearchDialog($oPage, $sTitle, $oObj);
$oWidget->AutoComplete($oPage, $sFilter, $oObj, $sContains);
break;
// ui.extkeywidget
case 'objectSearchForm':
$oPage->SetContentType('text/html');
$sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class');
$iInputId = utils::ReadParam('iInputId', '');
$sTitle = utils::ReadParam('sTitle', '', false, 'raw_data');
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId);
$oWidget->GetSearchDialog($oPage, $sTitle);
break; break;
// ui.extkeywidget // ui.extkeywidget
case 'objectCreationForm': case 'objectCreationForm':
$sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class');
$iInputId = utils::ReadParam('iInputId', ''); $iInputId = utils::ReadParam('iInputId', '');
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId); $sAttCode = utils::ReadParam('sAttCode', '');
$oWidget->GetObjectCreationForm($oPage); $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, false);
$sJson = utils::ReadParam('json', '', false, 'raw_data');
if (!empty($sJson))
{
$oWizardHelper = WizardHelper::FromJSON($sJson);
$oObj = $oWizardHelper->GetTargetObject();
}
else
{
// Search form: no current object
$oObj = null;
}
$oWidget->GetObjectCreationForm($oPage, $oObj);
break; break;
// ui.extkeywidget // ui.extkeywidget
@@ -250,7 +302,8 @@ try
$sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class');
$iInputId = utils::ReadParam('iInputId', ''); $iInputId = utils::ReadParam('iInputId', '');
$sFormPrefix = utils::ReadParam('sFormPrefix', ''); $sFormPrefix = utils::ReadParam('sFormPrefix', '');
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId); $sAttCode = utils::ReadParam('sAttCode', '');
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, false);
$aResult = $oWidget->DoCreateObject($oPage); $aResult = $oWidget->DoCreateObject($oPage);
echo json_encode($aResult); echo json_encode($aResult);
break; break;
@@ -260,7 +313,8 @@ try
$sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class');
$iInputId = utils::ReadParam('iInputId', ''); $iInputId = utils::ReadParam('iInputId', '');
$iObjectId = utils::ReadParam('iObjectId', ''); $iObjectId = utils::ReadParam('iObjectId', '');
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId); $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true');
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, '', $bSearchMode);
$sName = $oWidget->GetObjectName($iObjectId); $sName = $oWidget->GetObjectName($iObjectId);
echo json_encode(array('name' => $sName)); echo json_encode(array('name' => $sName));
break; break;
@@ -273,6 +327,7 @@ try
$sFilter = utils::ReadParam('sFilter', '', false, 'raw_data'); $sFilter = utils::ReadParam('sFilter', '', false, 'raw_data');
$sJson = utils::ReadParam('json', '', false, 'raw_data'); $sJson = utils::ReadParam('json', '', false, 'raw_data');
$currValue = utils::ReadParam('value', ''); $currValue = utils::ReadParam('value', '');
$bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true');
if (!empty($sJson)) if (!empty($sJson))
{ {
$oWizardHelper = WizardHelper::FromJSON($sJson); $oWizardHelper = WizardHelper::FromJSON($sJson);
@@ -283,7 +338,7 @@ try
// Search form: no current object // Search form: no current object
$oObj = null; $oObj = null;
} }
$oWidget = new UIExtKeyWidget($sTargetClass, $sInputId); $oWidget = new UIExtKeyWidget($sTargetClass, $sInputId, '', $bSearchMode);
$oWidget->DisplayHierarchy($oPage, $sFilter, $currValue, $oObj); $oWidget->DisplayHierarchy($oPage, $sFilter, $currValue, $oObj);
break; break;
@@ -297,6 +352,9 @@ try
$sSuffix = utils::ReadParam('sSuffix', ''); $sSuffix = utils::ReadParam('sSuffix', '');
$sRemoteClass = utils::ReadParam('sRemoteClass', $sClass, false, 'class'); $sRemoteClass = utils::ReadParam('sRemoteClass', $sClass, false, 'class');
$bDuplicates = (utils::ReadParam('bDuplicates', 'false') == 'false') ? false : true; $bDuplicates = (utils::ReadParam('bDuplicates', 'false') == 'false') ? false : true;
$sJson = utils::ReadParam('json', '', false, 'raw_data');
$oWizardHelper = WizardHelper::FromJSON($sJson);
$oObj = $oWizardHelper->GetTargetObject();
$oWidget = new UILinksWidget($sClass, $sAttCode, $iInputId, $sSuffix, $bDuplicates); $oWidget = new UILinksWidget($sClass, $sAttCode, $iInputId, $sSuffix, $bDuplicates);
if ($sFilter != '') if ($sFilter != '')
{ {
@@ -306,7 +364,7 @@ try
{ {
$oFullSetFilter = new DBObjectSearch($sRemoteClass); $oFullSetFilter = new DBObjectSearch($sRemoteClass);
} }
$oWidget->DoAddObjects($oPage, $oFullSetFilter); $oWidget->DoAddObjects($oPage, $oFullSetFilter, $oObj);
break; break;
case 'wizard_helper_preview': case 'wizard_helper_preview':
@@ -392,6 +450,18 @@ try
{ {
$aExtraParams = json_decode(str_replace("'", '"', $sExtraParams), true /* associative array */); $aExtraParams = json_decode(str_replace("'", '"', $sExtraParams), true /* associative array */);
} }
// Restore the app context from the ExtraParams
$oAppContext = new ApplicationContext(false); // false => don't read the context yet !
$aContext = array();
foreach($oAppContext->GetNames() as $sName)
{
$sParamName = 'c['.$sName.']';
if (isset($aExtraParams[$sParamName]))
{
$aContext[$sName] = $aExtraParams[$sParamName];
}
}
$_REQUEST['c'] = $aContext;
if ($sEncoding == 'oql') if ($sEncoding == 'oql')
{ {
$oFilter = CMDBSearchFilter::FromOQL($sFilter); $oFilter = CMDBSearchFilter::FromOQL($sFilter);
@@ -457,7 +527,7 @@ try
$oPage->add_header("Pragma: public"); $oPage->add_header("Pragma: public");
$oPage->SetContentType('application/json'); $oPage->SetContentType('application/json');
$aParams = utils::ReadParam('params', array()); $aParams = utils::ReadParam('params', array(), false, 'raw_data');
if ($sFilter != '') if ($sFilter != '')
{ {
$oFilter = CMDBSearchFilter::unserialize($sFilter); $oFilter = CMDBSearchFilter::unserialize($sFilter);
@@ -557,7 +627,7 @@ try
// Let's take this opportunity to inform the plug-ins so that they can perform some cleanup // Let's take this opportunity to inform the plug-ins so that they can perform some cleanup
$iTransactionId = utils::ReadParam('transaction_id', 0); $iTransactionId = utils::ReadParam('transaction_id', 0);
$sTempId = session_id().'_'.$iTransactionId; $sTempId = session_id().'_'.$iTransactionId;
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{ {
$oExtensionInstance->OnFormCancel($sTempId); $oExtensionInstance->OnFormCancel($sTempId);
} }

View File

@@ -83,7 +83,7 @@ function FilterByContext(DBObjectSearch &$oFilter, ApplicationContext $oAppConte
} }
} }
function GetRuleResultSet($iRuleId, $oDefinitionFilter, $oAppContext) function GetRuleResultFilter($iRuleId, $oDefinitionFilter, $oAppContext)
{ {
$oRule = MetaModel::GetObject('AuditRule', $iRuleId); $oRule = MetaModel::GetObject('AuditRule', $iRuleId);
$sOql = $oRule->Get('query'); $sOql = $oRule->Get('query');
@@ -95,23 +95,36 @@ function GetRuleResultSet($iRuleId, $oDefinitionFilter, $oAppContext)
// The query returns directly the invalid elements // The query returns directly the invalid elements
$oFilter = $oRuleFilter; $oFilter = $oRuleFilter;
$oFilter->MergeWith($oDefinitionFilter); $oFilter->MergeWith($oDefinitionFilter);
$oErrorObjectSet = new CMDBObjectSet($oFilter);
} }
else else
{ {
// The query returns only the valid elements, all the others are invalid // The query returns only the valid elements, all the others are invalid
$oFilter = $oRuleFilter; $aValidRows = $oRuleFilter->ToDataArray(array('id'));
$oErrorObjectSet = new CMDBObjectSet($oFilter); $aValidIds = array();
$aValidIds = array(0); // Make sure that we have at least one value in the list foreach($aValidRows as $aRow)
while($oObj = $oErrorObjectSet->Fetch())
{ {
$aValidIds[] = $oObj->GetKey(); $aValidIds[] = $aRow['id'];
} }
$oFilter = clone $oDefinitionFilter; $oFilter = clone $oDefinitionFilter;
$oFilter->AddCondition('id', $aValidIds, 'NOTIN'); if (count($aValidIds) > 0)
$oErrorObjectSet = new CMDBObjectSet($oFilter); {
$aInDefSet = array();
foreach($oDefinitionFilter->ToDataArray(array('id')) as $aRow)
{
$aInDefSet[] = $aRow['id'];
}
$aInvalids = array_diff($aInDefSet, $aValidIds);
if (count($aInvalids) > 0)
{
$oFilter->AddCondition('id', $aInvalids, 'IN');
}
else
{
$oFilter->AddCondition('id', 0, '=');
}
}
} }
return $oErrorObjectSet; return $oFilter;
} }
function GetReportColor($iTotal, $iErrors) function GetReportColor($iTotal, $iErrors)
@@ -153,7 +166,8 @@ try
$oDefinitionFilter = DBObjectSearch::FromOQL($oAuditCategory->Get('definition_set')); $oDefinitionFilter = DBObjectSearch::FromOQL($oAuditCategory->Get('definition_set'));
FilterByContext($oDefinitionFilter, $oAppContext); FilterByContext($oDefinitionFilter, $oAppContext);
$oDefinitionSet = new CMDBObjectSet($oDefinitionFilter); $oDefinitionSet = new CMDBObjectSet($oDefinitionFilter);
$oErrorObjectSet = GetRuleResultSet($iRuleIndex, $oDefinitionFilter, $oAppContext); $oFilter = GetRuleResultFilter($iRuleIndex, $oDefinitionFilter, $oAppContext);
$oErrorObjectSet = new CMDBObjectSet($oFilter);
$oAuditRule = MetaModel::GetObject('AuditRule', $iRuleIndex); $oAuditRule = MetaModel::GetObject('AuditRule', $iRuleIndex);
$oP->add('<div class="page_header"><h1>Audit Errors: <span class="hilite">'.$oAuditRule->Get('description').'</span></h1><img style="margin-top: -20px; margin-right: 10px; float: right;" src="../images/stop.png"/></div>'); $oP->add('<div class="page_header"><h1>Audit Errors: <span class="hilite">'.$oAuditRule->Get('description').'</span></h1><img style="margin-top: -20px; margin-right: 10px; float: right;" src="../images/stop.png"/></div>');
$oP->p('<a href="./audit.php?'.$oAppContext->GetForLink().'">[Back to audit results]</a>'); $oP->p('<a href="./audit.php?'.$oAppContext->GetForLink().'">[Back to audit results]</a>');
@@ -177,52 +191,77 @@ try
$oP->add("</tr>\n"); $oP->add("</tr>\n");
while($oAuditCategory = $oCategoriesSet->fetch()) while($oAuditCategory = $oCategoriesSet->fetch())
{ {
$oDefinitionFilter = DBObjectSearch::FromOQL($oAuditCategory->Get('definition_set')); try
FilterByContext($oDefinitionFilter, $oAppContext);
$aObjectsWithErrors = array();
if (!empty($currentOrganization))
{ {
if (MetaModel::IsValidFilterCode($oDefinitionFilter->GetClass(), 'org_id')) $oDefinitionFilter = DBObjectSearch::FromOQL($oAuditCategory->Get('definition_set'));
FilterByContext($oDefinitionFilter, $oAppContext);
$aObjectsWithErrors = array();
if (!empty($currentOrganization))
{ {
$oDefinitionFilter->AddCondition('org_id', $currentOrganization, '='); if (MetaModel::IsValidFilterCode($oDefinitionFilter->GetClass(), 'org_id'))
{
$oDefinitionFilter->AddCondition('org_id', $currentOrganization, '=');
}
}
$aResults = array();
$oDefinitionSet = new CMDBObjectSet($oDefinitionFilter);
$iCount = $oDefinitionSet->Count();
$oRulesFilter = new CMDBSearchFilter('AuditRule');
$oRulesFilter->AddCondition('category_id', $oAuditCategory->GetKey(), '=');
$oRulesSet = new DBObjectSet($oRulesFilter);
while($oAuditRule = $oRulesSet->fetch() )
{
$aRow = array();
$aRow['description'] = $oAuditRule->GetName();
if ($iCount == 0)
{
// nothing to check, really !
$aRow['nb_errors'] = "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."\">0</a>";
$aRow['percent_ok'] = '100.00';
$aRow['class'] = GetReportColor($iCount, 0);
}
else
{
try
{
$oFilter = GetRuleResultFilter($oAuditRule->GetKey(), $oDefinitionFilter, $oAppContext);
$aErrors = $oFilter->ToDataArray(array('id'));
$iErrorsCount = count($aErrors);
foreach($aErrors as $aErrorRow)
{
$aObjectsWithErrors[$aErrorRow['id']] = true;
}
$aRow['nb_errors'] = ($iErrorsCount == 0) ? '0' : "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">$iErrorsCount</a>";
$aRow['percent_ok'] = sprintf('%.2f', 100.0 * (($iCount - $iErrorsCount) / $iCount));
$aRow['class'] = GetReportColor($iCount, $iErrorsCount);
}
catch(Exception $e)
{
$aRow['nb_errors'] = "OQL Error";
$aRow['percent_ok'] = 'n/a';
$aRow['class'] = 'red';
$sMessage = Dict::Format('UI:Audit:ErrorIn_Rule_Reason', $oAuditRule->GetHyperlink(), $e->getMessage());
$oP->p("<img style=\"vertical-align:middle\" src=\"../images/stop-mid.png\"/>&nbsp;".$sMessage);
}
}
$aResults[] = $aRow;
$iTotalErrors = count($aObjectsWithErrors);
$sOverallPercentOk = ($iCount == 0) ? '100.00' : sprintf('%.2f', 100.0 * (($iCount - $iTotalErrors) / $iCount));
$sClass = GetReportColor($iCount, $iTotalErrors);
} }
} }
$aResults = array(); catch(Exception $e)
$oDefinitionSet = new CMDBObjectSet($oDefinitionFilter);
$iCount = $oDefinitionSet->Count();
$oRulesFilter = new CMDBSearchFilter('AuditRule');
$oRulesFilter->AddCondition('category_id', $oAuditCategory->GetKey(), '=');
$oRulesSet = new DBObjectSet($oRulesFilter);
while($oAuditRule = $oRulesSet->fetch() )
{ {
$aRow = array(); $aRow = array();
$aRow['description'] = $oAuditRule->GetName(); $aRow['description'] = "OQL error";
if ($iCount == 0) $aRow['nb_errors'] = "n/a";
{ $aRow['percent_ok'] = '';
// nothing to check, really ! $aRow['class'] = 'red';
$aRow['nb_errors'] = "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."\">0</a>"; $sMessage = Dict::Format('UI:Audit:ErrorIn_Category_Reason', $oAuditCategory->GetHyperlink(), $e->getMessage());
$aRow['percent_ok'] = '100.00'; $oP->p("<img style=\"vertical-align:middle\" src=\"../images/stop-mid.png\"/>&nbsp;".$sMessage);
$aRow['class'] = GetReportColor($iCount, 0); $aResults[] = $aRow;
}
else
{
$oRuleFilter = DBObjectSearch::FromOQL($oAuditRule->Get('query'));
$oErrorObjectSet = GetRuleResultSet($oAuditRule->GetKey(), $oDefinitionFilter, $oAppContext);
$iErrorsCount = $oErrorObjectSet->Count();
while($oObj = $oErrorObjectSet->Fetch())
{
$aObjectsWithErrors[$oObj->GetKey()] = true;
}
$aRow['nb_errors'] = ($iErrorsCount == 0) ? '0' : "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">$iErrorsCount</a>";
$aRow['percent_ok'] = sprintf('%.2f', 100.0 * (($iCount - $iErrorsCount) / $iCount));
$aRow['class'] = GetReportColor($iCount, $iErrorsCount);
}
$aResults[] = $aRow;
$iTotalErrors = count($aObjectsWithErrors);
$sOverallPercentOk = ($iCount == 0) ? '100.00' : sprintf('%.2f', 100.0 * (($iCount - $iTotalErrors) / $iCount));
$sClass = GetReportColor($iCount, $iTotalErrors);
} }
$oP->add("<tr>\n"); $oP->add("<tr>\n");
$oP->add("<th><img src=\"../images/minus.gif\"></th><th class=\"alignLeft\">".$oAuditCategory->GetName()."</th><th class=\"alignRight\">$iCount</th><th class=\"alignRight\">$iTotalErrors</th><th class=\"alignRight $sClass\">$sOverallPercentOk %</th>\n"); $oP->add("<th><img src=\"../images/minus.gif\"></th><th class=\"alignLeft\">".$oAuditCategory->GetName()."</th><th class=\"alignRight\">$iCount</th><th class=\"alignRight\">$iTotalErrors</th><th class=\"alignRight $sClass\">$sOverallPercentOk %</th>\n");
@@ -238,7 +277,6 @@ try
$oP->add("</td></tr>\n"); $oP->add("</td></tr>\n");
$oP->add("</table>\n"); $oP->add("</table>\n");
} }
$oP->output(); $oP->output();
} }
catch(CoreException $e) catch(CoreException $e)

View File

@@ -304,6 +304,9 @@ try
$sUserString .= ' (CSV)'; $sUserString .= ' (CSV)';
$oMyChange->Set("userinfo", $sUserString); $oMyChange->Set("userinfo", $sUserString);
$iChangeId = $oMyChange->DBInsert(); $iChangeId = $oMyChange->DBInsert();
// Todo - simplify that when reworking the change tracking
CMDBObject::SetCurrentChange($oMyChange);
} }
$oBulk = new BulkChange( $oBulk = new BulkChange(
@@ -1402,14 +1405,18 @@ $('#select_template_class').change( function() {
EOF EOF
); );
if (MetaModel::GetConfig()->Get('csv_import_history_display'))
{
$oPage->SetCurrentTabContainer('tabs1'); $oPage->SetCurrentTabContainer('tabs1');
$oPage->SetCurrentTab(Dict::S('UI:History:BulkImports')); $oPage->SetCurrentTab(Dict::S('UI:History:BulkImports'));
BulkChange::DisplayImportHistory($oPage); BulkChange::DisplayImportHistory($oPage);
}
} }
switch($iStep) switch($iStep)
{ {
case 10: case 10:
// Case generated by BulkChange::DisplayImportHistory
$iChange = (int)utils::ReadParam('changeid', 0); $iChange = (int)utils::ReadParam('changeid', 0);
BulkChange::DisplayImportHistoryDetails($oPage, $iChange); BulkChange::DisplayImportHistoryDetails($oPage, $iChange);
break; break;

View File

@@ -120,34 +120,80 @@ try
// leave $sExpression as is // leave $sExpression as is
} }
$oFilter = null;
$aArgs = array();
$sSyntaxError = null;
if (!empty($sExpression))
{
try
{
$oFilter = DBObjectSearch::FromOQL($sExpression);
}
catch(OqlException $e)
{
$sSyntaxError = $e->getHtmlDesc();
}
if ($oFilter)
{
$aArgs = array();
foreach($oFilter->GetQueryParams() as $sParam => $foo)
{
$value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
if (!is_null($value))
{
$aArgs[$sParam] = $value;
}
else
{
$aArgs[$sParam] = '';
}
}
$oFilter->SetInternalParams($aArgs);
}
elseif ($sSyntaxError)
{
// Query arguments taken from the page args
}
}
$oP->add("<form method=\"get\">\n"); $oP->add("<form method=\"get\">\n");
$oP->add(Dict::S('UI:RunQuery:ExpressionToEvaluate')."<br/>\n"); $oP->add(Dict::S('UI:RunQuery:ExpressionToEvaluate')."<br/>\n");
$oP->add("<textarea cols=\"120\" rows=\"8\" name=\"expression\">$sExpression</textarea>\n"); $oP->add("<textarea cols=\"120\" rows=\"8\" name=\"expression\">".htmlentities($sExpression, ENT_QUOTES, 'UTF-8')."</textarea>\n");
if (count($aArgs) > 0)
{
$oP->add("<div class=\"wizContainer\">\n");
$oP->add("<h3>Query arguments</h3>\n");
foreach($aArgs as $sParam => $sValue)
{
$oP->p("$sParam: <input type=\"string\" name=\"arg_$sParam\" value=\"$sValue\">\n");
}
$oP->add("</div>\n");
}
$oP->add("<input type=\"submit\" value=\"".Dict::S('UI:Button:Evaluate')."\">\n"); $oP->add("<input type=\"submit\" value=\"".Dict::S('UI:Button:Evaluate')."\">\n");
$oP->add($oAppContext->GetForForm()); $oP->add($oAppContext->GetForForm());
$oP->add("</form>\n"); $oP->add("</form>\n");
if (!empty($sExpression)) if ($oFilter)
{ {
$oFilter = DBObjectSearch::FromOQL($sExpression); $oP->add("<h3>Query results</h3>\n");
if ($oFilter)
{ $oResultBlock = new DisplayBlock($oFilter, 'list', false);
$oP->add("<h3>Query results</h3>\n"); $oResultBlock->Display($oP, 'runquery');
$oResultBlock = new DisplayBlock($oFilter, 'list', false);
$oResultBlock->Display($oP, 'runquery');
$oP->p(''); $oP->p('');
$oP->StartCollapsibleSection(Dict::S('UI:RunQuery:MoreInfo'), false); $oP->StartCollapsibleSection(Dict::S('UI:RunQuery:MoreInfo'), false);
$oP->p(Dict::S('UI:RunQuery:DevelopedQuery').$oFilter->ToOQL()); $oP->p(Dict::S('UI:RunQuery:DevelopedQuery').htmlentities($oFilter->ToOQL(), ENT_QUOTES, 'UTF-8'));
$oP->p(Dict::S('UI:RunQuery:SerializedFilter').$oFilter->serialize()); $oP->p(Dict::S('UI:RunQuery:SerializedFilter').$oFilter->serialize());
$oP->EndCollapsibleSection(); $oP->EndCollapsibleSection();
} }
elseif ($sSyntaxError)
{
$oP->p('<b>'.Dict::Format('UI:RunQuery:Error', $e->getHtmlDesc()).'</b>');
} }
}
catch(CoreException $e)
{
$oP->p('<b>'.Dict::Format('UI:RunQuery:Error', $e->getHtmlDesc()).'</b>');
} }
catch(Exception $e) catch(Exception $e)
{ {

View File

@@ -385,6 +385,10 @@ function DisplayClassDetails($oPage, $sClass, $sContext)
{ {
$sValue = Dict::Format('UI:Schema:ExternalKey_To',MakeClassHLink($oAttDef->GetTargetClass(), $sContext)); $sValue = Dict::Format('UI:Schema:ExternalKey_To',MakeClassHLink($oAttDef->GetTargetClass(), $sContext));
} }
elseif ($oAttDef->IsLinkSet())
{
$sValue = MakeClassHLink($oAttDef->GetLinkedClass(), $sContext);
}
else else
{ {
$sValue = $oAttDef->GetDescription(); $sValue = $oAttDef->GetDescription();

View File

@@ -59,9 +59,10 @@ function AddNodeDetails(&$oNode, $oObj)
* @param DBObject $oObj The current object * @param DBObject $oObj The current object
* @param string $sRelation The name of the relation to search with * @param string $sRelation The name of the relation to search with
*/ */
function GetRelatedObjectsAsXml(DBObject $oObj, $sRelationName, &$oLinks, &$oXmlDoc, &$oXmlNode, $iDepth = 0) function GetRelatedObjectsAsXml(DBObject $oObj, $sRelationName, &$oLinks, &$oXmlDoc, &$oXmlNode, $iDepth = 0, $aExcludedClasses)
{ {
$aResults = array(); $aResults = array();
$bAddLinks = false;
$oObj->GetRelatedObjects($sRelationName, 1 /* iMaxDepth */, $aResults); $oObj->GetRelatedObjects($sRelationName, 1 /* iMaxDepth */, $aResults);
if ($iDepth > MAX_RECURSION_DEPTH) return; if ($iDepth > MAX_RECURSION_DEPTH) return;
@@ -71,25 +72,33 @@ function GetRelatedObjectsAsXml(DBObject $oObj, $sRelationName, &$oLinks, &$oXml
{ {
if (is_object($oTargetObj)) if (is_object($oTargetObj))
{ {
$oLinkingNode = $oXmlDoc->CreateElement('link'); if (in_array(get_class($oTargetObj), $aExcludedClasses))
$oLinkingNode->SetAttribute('relation', $sRelationName); {
$oLinkingNode->SetAttribute('arrow', 1); // Such relations have a direction, display an arrow GetRelatedObjectsAsXml($oTargetObj, $sRelationName, $oLinks, $oXmlDoc, $oXmlNode, $iDepth++, $aExcludedClasses);
$oLinkedNode = $oXmlDoc->CreateElement('node'); }
$oLinkedNode->SetAttribute('id', $oTargetObj->GetKey()); else
$oLinkedNode->SetAttribute('obj_class', get_class($oTargetObj)); {
$oLinkedNode->SetAttribute('obj_class_name', htmlspecialchars(MetaModel::GetName(get_class($oTargetObj)))); $oLinkingNode = $oXmlDoc->CreateElement('link');
$oLinkedNode->SetAttribute('name', htmlspecialchars($oTargetObj->GetRawName())); // htmlentities is too much for XML $oLinkingNode->SetAttribute('relation', $sRelationName);
$oLinkedNode->SetAttribute('icon', BuildIconPath($oTargetObj->GetIcon(false /* No IMG tag */))); $oLinkingNode->SetAttribute('arrow', 1); // Such relations have a direction, display an arrow
AddNodeDetails($oLinkedNode, $oTargetObj); $oLinkedNode = $oXmlDoc->CreateElement('node');
$oSubLinks = $oXmlDoc->CreateElement('links'); $oLinkedNode->SetAttribute('id', $oTargetObj->GetKey());
// Recurse $oLinkedNode->SetAttribute('obj_class', get_class($oTargetObj));
GetRelatedObjectsAsXml($oTargetObj, $sRelationName, $oSubLinks, $oXmlDoc, $oLinkedNode, $iDepth++); $oLinkedNode->SetAttribute('obj_class_name', htmlspecialchars(MetaModel::GetName(get_class($oTargetObj))));
$oLinkingNode->AppendChild($oLinkedNode); $oLinkedNode->SetAttribute('name', htmlspecialchars($oTargetObj->GetRawName())); // htmlentities is too much for XML
$oLinks->AppendChild($oLinkingNode); $oLinkedNode->SetAttribute('icon', BuildIconPath($oTargetObj->GetIcon(false /* No IMG tag */)));
AddNodeDetails($oLinkedNode, $oTargetObj);
$oSubLinks = $oXmlDoc->CreateElement('links');
// Recurse
GetRelatedObjectsAsXml($oTargetObj, $sRelationName, $oSubLinks, $oXmlDoc, $oLinkedNode, $iDepth++, $aExcludedClasses);
$oLinkingNode->AppendChild($oLinkedNode);
$oLinks->AppendChild($oLinkingNode);
$bAddLinks = true;
}
} }
} }
} }
if (count($aResults) > 0) if ($bAddLinks)
{ {
$oXmlNode->AppendChild($oLinks); $oXmlNode->AppendChild($oLinks);
} }
@@ -117,6 +126,9 @@ $id = utils::ReadParam('id', 1);
$sRelation = utils::ReadParam('relation', 'impacts'); $sRelation = utils::ReadParam('relation', 'impacts');
$aValidRelations = MetaModel::EnumRelations(); $aValidRelations = MetaModel::EnumRelations();
$sFormat = utils::ReadParam('format', 'xml'); $sFormat = utils::ReadParam('format', 'xml');
$sExcludedClasses = utils::ReadParam('exclude', '', false, 'raw_data');
$aExcludedClasses = explode(',', $sExcludedClasses);
if (!in_array($sRelation, $aValidRelations)) if (!in_array($sRelation, $aValidRelations))
{ {
@@ -167,7 +179,7 @@ try
$oXmlRoot->SetAttribute('position', 'left'); $oXmlRoot->SetAttribute('position', 'left');
$oXmlRoot->SetAttribute('title', MetaModel::GetRelationDescription($sRelation).' '. htmlspecialchars($oObj->GetRawName())); $oXmlRoot->SetAttribute('title', MetaModel::GetRelationDescription($sRelation).' '. htmlspecialchars($oObj->GetRawName()));
GetRelatedObjectsAsXml($oObj, $sRelation, $oLinks, $oXmlDoc, $oXmlNode); GetRelatedObjectsAsXml($oObj, $sRelation, $oLinks, $oXmlDoc, $oXmlNode, 0, $aExcludedClasses);
$oXmlRoot->AppendChild($oXmlNode); $oXmlRoot->AppendChild($oXmlNode);
$oXmlDoc->AppendChild($oXmlRoot); $oXmlDoc->AppendChild($oXmlRoot);

File diff suppressed because it is too large Load Diff

View File

@@ -7,98 +7,72 @@ html, body {
height: 100%; height: 100%;
} }
#content { #content {
margin: auto; margin: 10px;
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
text-align: center; text-align: center;
overflow-y: auto; overflow-y: auto;
no.max-width: 90%;
min-width: 960px;
position: relative;
display: block; display: block;
clear: both;
}
div#portal #welcome {
display: none;
background: url("./images/dockbar_bg.png") repeat-x scroll 0 0 #97A1AE;
border-bottom: 1px solid #636364;
font-size: 13px;
padding: 1px 5px;
position: relative;
z-index: 300;
text-align:right;
color: #2C2F34;
font-weight: bold;
text-shadow: 1px 1px #FFFFFF;
} }
div#portal #banner { div#portal #banner {
background-color: #F6F6F1;
display: block;
height: 60px;
vertical-align: middle;
width: 100%; width: 100%;
} height: 60px;
div#portal #logo { display: block;
background: url("../images/itop-logo.png") no-repeat scroll 0 0 transparent; vertical-align:middle;
border: 0 none; background-color: #f6f6f1;
display: inline-block;
height: 116px;
line-height: 48px;
margin-left: 20px;
margin-right: 20px;
padding-right: 50px;
text-align: center;
vertical-align: middle;
width: 240px;
} }
div#portal #logo {
width: 126px;
background: url(../images/itop-logo.png) 0 0 no-repeat;
margin-left:20px;
margin-right:20px;
height: 60px;
border: 0;
vertical-align: middle;
text-align: center;
display: inline-block;
line-height: 48px;
padding-right:50px;
}
div#menu { div#menu {
display: block; display: block;
height: 48px;
left: 200px;
line-height: 48px;
position: absolute;
right: 0;
top: 0;
width: auto; width: auto;
position: absolute;
top: 0;
left: 200px;
right: 0px;
line-height: 48px;
height: 48px;
} }
#portal_menu { #portal_menu {
height: 60px; height: 60px;
} }
#change_pwd {
background: url("../images/password.png") no-repeat scroll 0 0 transparent;
display: block;
float: right;
}
#logoff {
background: url("../images/logoff.png") no-repeat scroll 0 0 transparent;
display: block;
float: right;
}
#logoff span {
}
div.button { div.button {
font-size: 1.1em; margin-left:20px;
font-weight: bold; margin-right:20px;
text-decoration: none; height: 60px;
} border: 0;
a.button, a.button:visited { vertical-align: middle;
color: #1C94C4; text-align: center;
display: inline-block; display: inline-block;
height: 48px;
line-height: 48px; line-height: 48px;
}
a.button , a.button:visited {
color: #1C94C4;
text-decoration: none; text-decoration: none;
vertical-align: middle; vertical-align: middle;
height: 48px;
line-height: 48px;
display: inline-block;
} }
a.button span { a.button span {
margin-left: 50px; vertical-align:middle;
margin-right: 20px; margin-right: 20px;
vertical-align: middle; margin-left: 50px;
} }
#close_form_table { #close_form_table {
@@ -106,117 +80,66 @@ a.button span {
padding: 20px; padding: 20px;
} }
#request_details td { #logoff {
text-align:left; display: block;
float: right;
background: url(../images/logoff.png) right center no-repeat;
} }
#request_details td fieldset{ #logoff span {
xxxheight:100%; margin-right: 50px;
margin-left: 20px;
} }
#cancel {
background: url(../images/stop-mid.png) 0 0 no-repeat;
}
#create {
background: url(../modules/itop-request-mgmt-1.0.0/images/user-request.png) 0 0 no-repeat;
}
#user_info {
background: url(../images/clean-mid.png) 0 0 no-repeat;
}
#change_pwd {
background: url(../images/password.png) 0 0 no-repeat;
}
#back {
background: url(../images/back.png) 0 0 no-repeat;
}
#back span {
margin-left: 54px;
}
#refresh {
background: url(../images/refresh.png) 0 0 no-repeat;
margin-right: 40px;
}
#refresh span {
margin-left: 54px;
margin-right: 20px;
}
#request_details {
display: inline-block;
width:800px;
text-align: left;
}
#form_close_request {
display: inline-block;
width:800px;
text-align: left;
}
#request_details_log {
width:774px;
}
#request_details table {
border: #f1f1f6 2px solid;
text-align: left;
}
#form_details { #form_details {
display: inline-block; display: inline-block;
} }
.wizContainer table { .wizContainer table {
display: inline-block; display: inline-block;
text-align: left; text-align: left;
} }
#user_request_comment {
width: 30em;
height: 20em;
}
#buttons {
margin-top: 1em;
}
div#buttons #btn_cancel {
margin-right: 50px;
}
div#buttons #btn_back {
margin-left: 50px;
margin-right: 5px;
}
div#buttons #btn_next {
margin-left: 5px;
}
div#buttons #btn_finish {
margin-left: 5px;
}
table.listContainer {
clear: both;
width: 100%;
}
h1 {
font-weight: bold;
font-weight: bold;
padding: 5px;
margin-top: 5px;
}
div.DrawerHandle {
display:none;
}
div.HRDrawer {
background: transparent;
border: 0;
height: 0.5em;
}
.SearchDrawer {
background-color: #F9EDBF;
border: 0;
-moz-border-radius: 4px 4px 4px 4px;
}
.SearchDrawer label {
background: transparent;
}
#open_incidents, #open_requests, #open_changes, #request_details {
margin-bottom: 1em;
}
legend {
background: url("./images/header_bg.png") repeat-x scroll 0 0 #D4D4D4;
border-color: #C8C9CA #9E9E9E #9E9E9E #C8C9CA;
border-style: solid;
border-width: 1px;
font-size: 1.1em;
font-weight: bold;
color: #222222;
font-weight: bold;
text-shadow: 1px 1px #FFFFFF;
padding: 5px;
-moz-border-radius: 4px 4px 4px 4px;
margin-top:0;
}
table.details > tbody > tr > td {
padding-bottom: 5px;
padding-top: 3px;
padding-right: 5px;
border: 0;
}
.label {
font-weight: bold;
}
.caselog {
display:block;
width: 100%;
}
.caselog textarea {
resize: none;
}
.edit_item {
margin-bottom: 1em;
}
div.edit_item span div table {
width: 100%;
}
div.edit_item span div table tbody tr td textarea{
width: 99%;
}
div#ticket_shortcuts form {
display: inline-block;
}

View File

@@ -1,4 +1,4 @@
iTop - version 1.2.0 - 14-Sep-2011 iTop - version 1.2.1 - 01-Feb-2012
Readme file Readme file
1. ABOUT THIS RELEASE 1. ABOUT THIS RELEASE
@@ -7,14 +7,14 @@ Readme file
2.2. Install procedure 2.2. Install procedure
2.3. Migration from previous version 2.3. Migration from previous version
3. FEATURES 3. FEATURES
3.1. Changes since 1.1 3.1. Changes since 1.2
3.2. Known limitations 3.2. Known limitations
3.3. Known issues 3.3. Known issues
1. ABOUT THIS RELEASE 1. ABOUT THIS RELEASE
================== ==================
Thank you for downloading the tenth packaged release of iTop. Thank you for downloading the eleventh packaged release of iTop.
This version comes with a few new features and bug fixes. This version is mostly a maintenance release that fixes a few bugs of iTop 1.2.
A wiki is available: https://sourceforge.net/apps/mediawiki/itop/index.php?title=ITop_Documentation A wiki is available: https://sourceforge.net/apps/mediawiki/itop/index.php?title=ITop_Documentation
Additional documentation can be downloaded from there: Additional documentation can be downloaded from there:
@@ -30,21 +30,32 @@ The source code of iTop can be found on SourceForge: http://itop.sourceforge.net
1.1 What's new? 1.1 What's new?
--------------------------- ---------------------------
- Japanese localization is now part of iTop This version mostly consists in bug fixes for iTop 1.2, however there are a few new features:
- Paginated display: when a list contains lots of data it is displayed page per page
- Quite a few performance improvements to make iTop behave properly with huge data sets
- Hierarchical keys: parent/child relationships can now be described using a special type of key,
and then queried efficiently in the database (Used by Organization, Location and Group)
- CAS authentication: iTop now supports single-sign-on with JA-SIG CAS
1.2 Should I upgrade to 1.2.0? - Better display of the impact analysis view: Allows to filter the classes of objects displayed in the
graphical view. The view now resizes to fit the window of your browser.
- Query phrase book: create, test and store your OQL queries in the "Query Phrasebook" for an easy export
via the "Export" menu, without the limitation of the URL length in Excel web queries ! A new export format
"spreadsheet" has been added, specifically for running as Excel web queries (dates are split between date and time)
- Enhanced data synchronization:
- For performing huge synchronizations with little memory (despite PHP's memory leaks), the execution of the data
synchronization (in CLI mode only) can be run by chunk of x elements by specifying --max_chunk_size=x on the
command line to synchro_import.php or synchro_exec.php. Try to set this value to 1000 to run with 128 MB of memory.
As of now, there is no easy mean to check wether the execution has been split or not, and to evaluate the real benefit of this feature.
To do this, you will have to look into the database, in table priv_sync_log. The column memory_usage_peak gives you the maximum amount of memory used throughout the whole execution.
- When an (optional) external key cannot be reconciled, log a warning on the replica. the replicas containing a
warning are then processed everytime in case the ext key changes.
- Also improved the search/display of replicas for an easier troubleshooting of the synchronization.
1.2 Should I upgrade to 1.2.1?
--------------------------- ---------------------------
- If you are manipulating big sets of data (several thousands of objects in one go) Considering that there more than 30 bug fixes and very few new features, it's probably quite safe to upgrade to
- If you care about organizations or locations hierarchy this new version. If you are using the data synchronization with big sets of data, you can benefit from the new
- If you speak/read Japanese "chunk" mode by just adding a parameter to the command line !
- If you already use JA-SIG CAS (www.jasig.org/cas) for example with a Liferay portal The interactive audit is now faster and uses less memory when processing big numbers of elements.
then you'll benefit from iTop 1.2 and it's probably worth upgrading.
1.3 Special Thanks To: 1.3 Special Thanks To:
@@ -56,7 +67,7 @@ Christian Lempereur and Olivier Fouquet for their feedbacks.
Everaldo Coelho and the Oxygen Team for their wonderful icons. Everaldo Coelho and the Oxygen Team for their wonderful icons.
The JQuery team and the all the jQuery plugins authors for developing such a powerful library. The JQuery team and the all the jQuery plugins authors for developing such a powerful library.
Phil Eddies for the numerous feedbacks provided, and the first implementation of CKEdit Phil Eddies for the numerous feedbacks provided, and the first implementation of CKEdit
Marco Tulio and Bruno Cassaro for the Portuguese (Brazilian) translation Marco Túlio and Bruno Cassaro for the Portuguese (Brazilian) translation
Vladimir Shilov for the Russian translation Vladimir Shilov for the Russian translation
Izzet Sirin for the Turkish translation Izzet Sirin for the Turkish translation
Deng Lixin for the Chinese translation Deng Lixin for the Chinese translation
@@ -67,6 +78,7 @@ Tadashi Kaneda for the Japanese translation
Antoine Coetsier for the CAS support and tests Antoine Coetsier for the CAS support and tests
Vincenzo Todisco for his contribution to the enhancement of the webservices Vincenzo Todisco for his contribution to the enhancement of the webservices
Tobias Glemser and Sabri Saleh for their consulting about iTop security Tobias Glemser and Sabri Saleh for their consulting about iTop security
Claudio Cesar Sanchez Tejeda for his contribution to bug fixes on the export and data synchronization
2. INSTALLATION 2. INSTALLATION
============ ============
@@ -111,8 +123,8 @@ The following features will require the activation of CRON.PHP:
More information into the Wiki: https://sourceforge.net/apps/mediawiki/itop/index.php?title=Cron.php More information into the Wiki: https://sourceforge.net/apps/mediawiki/itop/index.php?title=Cron.php
2.4. Migrating from 1.0, 1.0.1, 1.0.2 or 1.1 2.4. Migrating from 1.0, 1.0.1, 1.0.2, 1.1 or 1.2
--------------------------------------- --------------------------------------------
You can simply overwrite the files from the previous version with the new ones but we recommend that you copy the files of the new version to new directory. You can simply overwrite the files from the previous version with the new ones but we recommend that you copy the files of the new version to new directory.
After installing the files, you MUST run the setup by After installing the files, you MUST run the setup by
1) Marking the file config-itop.php as read-write for the web server 1) Marking the file config-itop.php as read-write for the web server
@@ -140,135 +152,102 @@ Please refer to the migration guide available at http://www.combodo.com/itopdocu
3. FEATURES 3. FEATURES
======== ========
3.1. Changes since 1.1 3.1. Changes since 1.2
------------------- -------------------
Version 1.2.0 brings a few major changes. Version 1.2.1 brings a few major changes.
Major changes Major changes
------------- -------------
- Paginated display - Better display of the impact analysis view: Allows to filter the classes of objects displayed in the
- Management of hierarchy of objects: implemented for Organizations, Groups and Locations and taken into account by the profiles/user rights. graphical view. The view now resizes to fit your browser's window.
- CAS integration: added support of JA-SIG Central Authentication Service (CAS) with log-off support, using phpCAS API.
- Query phrase book: create, test and store your OQL queries in the "Query Phrasebook" for an easy export
via the "Export" menu, without the limitation of the URL length in Excel web queries ! A new export format
"spreadsheet" was aded, specifically for running as Excel web queries (dates are split between date and time)
- Enhanced data synchronization:
- For performing huge synchronizations with little memory (despite PHP's memory leaks), the execution of the data
synchronization (in CLI mode only) can be run by chunk of x elements by specifying --max_chunk_size=x on the
command line to synchro_import.php or synchro_exec.php. Try to set this value to 1000 to run with 128 MB of memory.
- When an (optional) external key cannot be reconciled, log a warning on the replica. the replicas containing a
warning are then processed everytime in case the ext key changes.
- Also improved the search/display of replicas for an easier troubleshooting of the synchronization.
Localization Localization
------------ ------------
The Japanese translation was added, thanks to Tadashi Kaneda. This version contains some enhancements to the German and Brazilian translations thanks to David Gümbel and Marco Túlio
The German translation was updated by Stephan Rosenke
More information on the localization (completion progress, how to contribute) here: More information on the localization (completion progress, how to contribute) here:
http://www.combodo.com/itop-localization/ http://www.combodo.com/itop-localization/
Minor changes Minor changes
------------- -------------
Improved import.php and synchro_import.php: added 'date_format' (example: %d/%m/%Y %H:%i:%s) Automatic synchro of CAS/LDAP users: it is possible to have iTop automatically create the user record when an authorized user connects through CAS
When needed the drop-down list of organizations is replaced by an autocomplete - Use the default language when creating a new user from CAS
Templates: new type of block = sqlblock, allows for displaying tables/charts based in SQL queries (much quicker for some 'Group By' operations) - Support patterns for casMemberof
Added support of 'drill-down' (i.e on_click) on bar and pie charts
Added SQL blocks Audit:
New feature: online help on search inputs (date format and operators) a tooltip appears when the user clicks a date/search field Better error handling in case of OQL error in the audit page, now the error is properly trapped and indicates which query is the cause of the error.
Better handling of object deletion issues during a data synchro... Optimized memory usage when auditing large volumes of CIs (10'000 items was requiring 200 Mb, it now runs with 32 Mb -including the 30Mb overhead!)
#130: keywords to narrow the scope of the global search (e.g. server:webserver searches "webserver" only in the "server" objects)
Added a new web service to create UserRequest tickets (similarly to Incidents tickets). Based on code from Vincenzo Todisco. Added a link to a favicon (icon in the browser's bar and tab)
Bug fix: when changing the currently selected organization, go back to the initial (Welcome) menu instead of trying to stay on the same menu... which caused troubles (e.g. "New Contact" => assertion failed)
Improved error handling when loading linkedset as attributes in one go in CSV import Allow a module to restrict the access to a given menu/group by redeclaring the menu with restricted rights.
Don't make the Ticket's case log hidden in the 'New' state, since it's not hidden in the portal ! All rights are combined with the AND operator.
Better error message if the configuration file exists but is not readable
In CLI mode, do not depend on the current directory for synchro/import.php and synchro/synchro.php: the scripts can now be run from anywhere. Added the "search form" on top of the list of user accounts, useful to find a user in a huge list !
New module to easily manage attachments in one click instead of creating a separate 'Document' object. If this module is installed, portal users will create attachments instead of linked documents when uploading files with their ticket
Added a new type of 'Trigger': TriggerOnPortalUpdate, called when the end-user updates a ticket via the portal. Added the ability to display a custom welcome/disclaimer message at the bottom of the login form.
The deadlines display format can be configured. Just put a non empty string (can contain HTML tags) in the dictionary entry 'UI:Login:About'
Lists can be displayed either as combo boxes (default and original behavior) or radio buttons, either horizontal or vertical.
In the templates or menus, the sort order can be specified. In the Toolkit: Improved the check on data model consistency: detection of SQL columns used by two attributes
Developers: DisplayBareProperties now called both in read and edition modes.
#446: XSS vulnerabilities Ticket's case log can now be bigger than 64 Kilobytes...
Security: protected bulk modify against HTTP/Post piracy
Added the display of the total count of objects in overviews.
Different display for 'Date' fields: shorter field than DateTime since there is no "time" part.
A mandatory case log field is now considered as 'filled' if it contains a previous entry
#148 Allow overloading attribute/enum labels in the dictionary
Warning (popup) message when navigating away from an edition form.
#452 Export.php - field list can be specified also for HTML output
German localization update, thanks to Stephan Rosenke.
Shortcut actions (parameter in the config file)
Notifications: case log in plain text (this->case_log) or the latest entry (this->head(case_log))
Allow creation of an ticket in a different initial state via the new 'initial_state_path' attribute.
Support update of CaseLog fields in bulk_modify mode.
Detection of the Suhosin extension during the installation and tell the user if the get_max_value is too small.
#284: Improved verification to the PHP file upload settings to avoid troubles later
Bugs fixed Bugs fixed
---------- ----------
The complete list of active tickets can be reviewed at http://sourceforge.net/apps/trac/itop/report/1 The complete list of active tickets can be reviewed at http://sourceforge.net/apps/trac/itop/report/1
#122 Optimized the load of data set (do not load unused columns, that can cause some tmp tables to get too big for memory) #522 issue with non-ASCII characters in notifications subject.
#403 Partial installation not working (error on ticket form) #519 Change password bug: it was not possible to for a user to change their own passord, if the (new) password contained non-alphanumeric characters !
#404: context lost when doing certain actions. What was fixed: #518 Properly pass the context (i.e. currently selected org) to the auto-refresh lists
- Run Query #516 and #517 Improved the export (specify fields for multi-column queries) and web queries (default field list)
- Display Data Model Schema #512 Command line mode (CLI) is now supported for the 'export' page. With either the --auth_user and --auth_pwd parameters or --param_file
- Drill-down in charts (OQL & SQL) #494 It seems that PHPSoap does not understand the <wsdl:documentation> tag, let's put them as comments
- Paginated lists (actually a regression) #493 Incorrect display of Users' Grant Matrix
What remains: #487 Resizable text areas disappeared when located on the second tab !
- Global search... #486 Fixed SQL dashboards limitations
- Drill-down in Flash "impacts / depends on" #485 Export.php improved for integration into Excel / web queries (bug with IIS/HTTPS, limitation on the size of the OQL)
#405 Could not install without the module 'User Request Management' Export for MS Excel web queries: format=spreadsheet Improved the end-user experience with Excel and the web queries (added a phrasebook)
#408 Case log not working with PHP < 5.3 - the fix preserves the compatibility with installed version (but the dates are lost) + link to test the OQL attributes (query phrasebook or email actions, etc.) including the handlink of query arguments)
#410 Added translation for ticket status (and other enum fields) when displaying the History tab. + fixed wrong prototypes for a few implementations of GetBareProperties()
#415 Could not limit user on some organization (symptom: wrong queries... org_id does not exist...) #484 Fixed issue with IIS ("Wrong password" at first prompt)
#420 Data synchro logs: increased the size of the attribute last_error #482 OpenSearch (integration with your browser's search bar) was broken.
#422 (detection of magic_quotes_runtime) #482 The setting 'min_autocomplete_chars' was not taken into account
#423 Fixed issues with application root URL = f(mode CLI, modules, web server techno, etc.) #481 localized characters in Service / Service Category name and description were not properly displayed.
#427 Unable to remove all items from a linkset when editing an object. #480 The 'min_autocomplete_chars' settings was not taken into account.
#424 Error when updating the Data Synchro statistics #478 Fixed issue in the audit: the results are wrong whenever an organization is selected
#429: web browser can crash when a text field contains several times the same URL !!! #477 Could not specify more than one reconciliation key (regression) + took the opportunity to enhance protection against XSS injection (using column names in the data)
#433: Database triggers creation was incorrect when iTop was installed with a 'prefix' for the DB tables. #473 Could not load NW interfaces (reconciliation issue) - merged from trunk
Dashboard templates: fixed issue with asynchronous mode (still some cosmetic issues) with itopblock and the table format
n:n wizard, context was lost when searching for objects of a derived class to be added.
'Apply stimulus multiple" was saying: "Please select at least one object"
Make sure that the flash object respects the z-order otherwise the hierarchy/organization picker appears behind the Flash in Chrome and IE.
Fixed issues when adding/removing modules during the setup:
- When adding modules: the data model was not refreshed in the cache before attempting to load "structure" (or "sample") data
- When removing a module: remaining (invalid) triggers were still used.
A title was missing for the menu 'All Opened Changes' at the top of the page
Fixed the parsing of OQL error messages: should be able to report the line number (usually 1) and the character where the error happened
Don't display an error (assertion failed) if the user selects nothing (i.e -- select one --) in the "CSV template" tab.
Display/download links on documents that were both doing exactly the same thing
Fixed the display of 'Used IP Addresses' (i.e. Network interfaces) in the details of a Subnet object.
Fixed the computation of IPs in a subnet that failed (returned negative numbers) on some versions of PHP compiled in 32-bit.
Enhanced interface for complex SLA computations...
#447: interfaces not showing up on the details of a server when an org is selected: there were collisions in the internal query parameters names ! This is now fixed.
The default value for Date fields is different than for DateTime fields: no 'time' part at the end (use the attribute's own format)
#458: back button was asking to fill the mandatory fields !
Support the selection (via an autocomplete) in a list that contains duplicates
#457: crash when deleting two organizations.
Accented characters not displayed within autocomplete selection controls
Email test: under IIS it was not detecting Windows correctly, and the help message was therefore completely wrong.
Dictionary: English was proposed twice in the list of available languages!
#465: incorrect logic when resetting the 'ConnectedToInterface'
Upgrade: fails to recreate a view when is has become invalid (missing attribute)
#363 Charts not displaying with IE8 + IIS + HTTPS
#373 Error when deleting two network devices connected to each other
#258 Context automatically set when specifying an organization in a search form
#444 Sort order not visible / lost on refresh
Crashing when displaying the details of a change in the CSV load history
Productivity Enhancements
-------------------------
Quicker path to create or modify objects: buttons directly accessible next to the Actions popup menu.
Limit the drop down menu to your favorite organizations: use the user preferences menu next to the Logoff button.
For tickets, create/modify and apply an action in one step; E.g. Create and assign in one click.
Form validation buttons (Ok/Cancel) showed on top and bottom. Can be tuned (top, bottom or both).
The paginated presentation allows a quicker navigation for really large data sets.
Performance Enhancements
------------------------
Do not load the full set of items when it comes to displaying an autocomplete!
Displaying 1000 object would take real long if many organizations are loaded into iTop (querying all the orgs for each object)
Cache the Count of items in an object set
Autocomplete = do not load every object when determining the list of matches
Other bugs not listed in Trac:
Security issue: the attachments were visible by anybody (by forming URLs manually), whatever the allowed organizations. The change requires the execution of the setup/migration procedure.
Apply the AllowedValues constraints(as default values) when selecting elements via the "magnifier" button or creating an new element via the "plus" button.
Paginated lists were broken in the Impact Analysis "List" tab
Incorrectly appending a parameter ?version= to linked scripts already containing a parameter in their URL, also changed the parameter name to 'itopversion' to avoid collisions
Always apply the AllowedValues constraints(as default values) when selecting elements via the "magnifier" button or creating an new element via the "plus" button... also make sure that allowed values is enforced
When searching objects to add to the current object (when managing n:n relationships), set the default search params in order to stay in the current silo.
Fixed issue: nobody in the list of persons to notify for portal users (security takes precedence)
In the setup: increased Suhosin minimum value for get_max_value to 2048 due to a bug seen on some installations
Fix to have the proper use of GetEditValue... thanks to C. Naud
SQL Block with parameters were always displayed as table, whatever their type...
Removed a (useless) hardcoded reference to FunctionalCI that may break inthe display of the Impact Analysis
(Tried to) improve the display of the Synchronization Tooltip that "sometimes" does not work on IE 8...
Put some default reconciliation keys on Actions and Triggers to ease the use of CSV import
Protect against an empty list of reconciliation keys in the interactive CSV Import
Export for spreadsheets: transform keys (id of the queried object or external keys) into the corresponding friendly name
3.2. Known limitations (https://sourceforge.net/apps/trac/itop/report/3) 3.2. Known limitations (https://sourceforge.net/apps/trac/itop/report/3)
----------------- -----------------
@@ -277,7 +256,7 @@ Autocomplete = do not load every object when determining the list of matches
Suhosin can interfere with iTop. More information can be found here: https://sourceforge.net/apps/mediawiki/itop/index.php?title=ITop_and_Suhosin Suhosin can interfere with iTop. More information can be found here: https://sourceforge.net/apps/mediawiki/itop/index.php?title=ITop_and_Suhosin
Internet Explorer 6 is not supported (neither IE7 nor IE8 in compatibility mode) Internet Explorer 6 is not supported (neither IE7 nor IE8 in compatibility mode)
Tested with IE8 and IE9. Be aware that there are certain limitations when using IE8 in "security mode" (when running IE on a Windows 2008 Server for example) Tested with IE8 and IE9, Firefox 3.6 up to Firefox 8 and Chrome. Be aware that there are certain limitations when using IE8 in "security mode" (when running IE on a Windows 2008 Server for example)
3.3. Known issues (https://sourceforge.net/apps/trac/itop/report/3) 3.3. Known issues (https://sourceforge.net/apps/trac/itop/report/3)

View File

@@ -37,7 +37,7 @@ define('FINAL_CONFIG_FILE', APPROOT.'/config-itop.php');
define('PHP_MIN_VERSION', '5.2.0'); define('PHP_MIN_VERSION', '5.2.0');
define('MYSQL_MIN_VERSION', '5.0.0'); define('MYSQL_MIN_VERSION', '5.0.0');
define('MIN_MEMORY_LIMIT', 32*1024*1024); define('MIN_MEMORY_LIMIT', 32*1024*1024);
define('SUHOSIN_GET_MAX_VALUE_LENGTH', 1024); define('SUHOSIN_GET_MAX_VALUE_LENGTH', 2048);
$sOperation = Utils::ReadParam('operation', 'step0'); $sOperation = Utils::ReadParam('operation', 'step0');
$oP = new SetupWebPage('iTop configuration wizard'); $oP = new SetupWebPage('iTop configuration wizard');

135
synchro/priv_sync_chunk.php Normal file
View File

@@ -0,0 +1,135 @@
<?php
// Copyright (C) 2010 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
// the Free Software Foundation; version 3 of the License.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Internal: synchronize part of the records - cannot be invoked separately
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
require_once(__DIR__.'/../approot.inc.php');
require_once(APPROOT.'/application/application.inc.php');
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/csvpage.class.inc.php');
require_once(APPROOT.'/application/clipage.class.inc.php');
require_once(APPROOT.'/application/startup.inc.php');
function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter = 'parameter')
{
$sValue = utils::ReadParam($sParam, null, true /* Allow CLI */, $sSanitizationFilter);
if (is_null($sValue))
{
$oP->p("ERROR: Missing argument '$sParam'\n");
exit(29);
}
return trim($sValue);
}
/////////////////////////////////
// Main program
if (!utils::IsModeCLI())
{
$oP = new WebPage(Dict::S("TitleSynchroExecution"));
$oP->p("This page is used internally by iTop");
$oP->output();
exit -2;
}
$oP = new CLIPage(Dict::S("TitleSynchroExecution"));
try
{
utils::UseParamFile();
}
catch(Exception $e)
{
$oP->p("Error: ".$e->GetMessage());
$oP->output();
exit -2;
}
// Next steps:
// specific arguments: 'csvfile'
//
$sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data');
$sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data');
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
{
UserRights::Login($sAuthUser); // Login & set the user's language
}
else
{
$oP->p("Access restricted or wrong credentials ('$sAuthUser')");
$oP->output();
exit -1;
}
$iStepCount = ReadMandatoryParam($oP, 'step_count');
$oP->p('Executing a partial synchro - step '.$iStepCount);
$iSource = ReadMandatoryParam($oP, 'source');
$iStatLog = ReadMandatoryParam($oP, 'log');
$iChange = ReadMandatoryParam($oP, 'change');
$sLastFullLoad = ReadMandatoryParam($oP, 'last_full_load', 'raw_data');
$iChunkSize = ReadMandatoryParam($oP, 'chunk');
$oP->p('Last full load: '.$sLastFullLoad);
$oP->p('Chunk size: '.$iChunkSize);
$oP->p('Source: '.$iSource);
try
{
$oSynchroDataSource = MetaModel::GetObject('SynchroDataSource', $iSource);
$oLog = MetaModel::GetObject('SynchroLog', $iStatLog);
$oChange = MetaModel::GetObject('CMDBChange', $iChange);
if (strlen($sLastFullLoad) > 0)
{
$oLastFullLoad = new DateTime($sLastFullLoad);
$oSynchroExec = new SynchroExecution($oSynchroDataSource, $oLastFullLoad);
}
else
{
$oSynchroExec = new SynchroExecution($oSynchroDataSource);
}
if ($oSynchroExec->DoSynchronizeChunk($oLog, $oChange, $iChunkSize))
{
// The last line MUST follow this convention
$oP->p("continue");
}
else
{
// The last line MUST follow this convention
$oP->p("finished");
}
$oP->output();
}
catch(Exception $e)
{
$oP->p("Error: ".$e->GetMessage());
$oP->add($e->getTraceAsString());
$oP->output();
exit(28);
}
?>

View File

@@ -53,6 +53,9 @@ try
{ {
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'oql')); throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'oql'));
} }
$oFilter = DBObjectSearch::FromOQL($sOQL);
$oBlock1 = new DisplayBlock($oFilter, 'search', false, array('menu'=>false));
$oBlock1->Display($oP, 0);
$oP->add('<p class="page-header">'.MetaModel::GetClassIcon('SynchroReplica').Dict::S('Core:SynchroReplica:ListOfReplicas').'</p>'); $oP->add('<p class="page-header">'.MetaModel::GetClassIcon('SynchroReplica').Dict::S('Core:SynchroReplica:ListOfReplicas').'</p>');
$iSourceId = utils::ReadParam('datasource', null); $iSourceId = utils::ReadParam('datasource', null);
if ($iSourceId != null) if ($iSourceId != null)
@@ -60,7 +63,6 @@ try
$oSource = MetaModel::GetObject('SynchroDataSource', $iSourceId); $oSource = MetaModel::GetObject('SynchroDataSource', $iSourceId);
$oP->p(Dict::Format('Core:SynchroReplica:BackToDataSource', $oSource->GetHyperlink()).'</a>'); $oP->p(Dict::Format('Core:SynchroReplica:BackToDataSource', $oSource->GetHyperlink()).'</a>');
} }
$oFilter = DBObjectSearch::FromOQL($sOQL);
$oBlock = new DisplayBlock($oFilter, 'list', false, array('menu'=>false)); $oBlock = new DisplayBlock($oFilter, 'list', false, array('menu'=>false));
$oBlock->Display($oP, 1); $oBlock->Display($oP, 1);
break; break;

View File

@@ -51,7 +51,7 @@ function UsageAndExit($oP)
if ($bModeCLI) if ($bModeCLI)
{ {
$oP->p("USAGE:\n"); $oP->p("USAGE:\n");
$oP->p("php -q synchro_exec.php --auth_user=<login> --auth_pwd=<password> --data_sources=<comma_separated_list_of_data_sources>\n"); $oP->p("php -q synchro_exec.php --auth_user=<login> --auth_pwd=<password> --data_sources=<comma_separated_list_of_data_sources> [max_chunk_size=<limit the count of replica loaded in a single pass>]\n");
} }
else else
{ {
@@ -147,7 +147,8 @@ foreach(explode(',', $sDataSourcesList) as $iSDS)
} }
try try
{ {
$oStatLog = $oSynchroDataSource->Synchronize(null); $oSynchroExec = new SynchroExecution($oSynchroDataSource);
$oStatLog = $oSynchroExec->Process();
if ($bSimulate) if ($bSimulate)
{ {
CMDBSource::Query('ROLLBACK'); CMDBSource::Query('ROLLBACK');
@@ -166,12 +167,12 @@ foreach(explode(',', $sDataSourcesList) as $iSDS)
$oP->p("Objects deletion errors: ".$oStatLog->Get('stats_nb_obj_deleted_errors')); $oP->p("Objects deletion errors: ".$oStatLog->Get('stats_nb_obj_deleted_errors'));
$oP->p("Objects obsoleted: ".$oStatLog->Get('stats_nb_obj_obsoleted')); $oP->p("Objects obsoleted: ".$oStatLog->Get('stats_nb_obj_obsoleted'));
$oP->p("Objects obsolescence errors: ".$oStatLog->Get('stats_nb_obj_obsoleted_errors')); $oP->p("Objects obsolescence errors: ".$oStatLog->Get('stats_nb_obj_obsoleted_errors'));
$oP->p("Objects created: ".$oStatLog->Get('stats_nb_obj_created')); $oP->p("Objects created: ".$oStatLog->Get('stats_nb_obj_created')." (".$oStatLog->Get('stats_nb_obj_created_warnings')." warnings)");
$oP->p("Objects creation errors: ".$oStatLog->Get('stats_nb_obj_created_errors')); $oP->p("Objects creation errors: ".$oStatLog->Get('stats_nb_obj_created_errors'));
$oP->p("Objects updated: ".$oStatLog->Get('stats_nb_obj_updated')); $oP->p("Objects updated: ".$oStatLog->Get('stats_nb_obj_updated')." (".$oStatLog->Get('stats_nb_obj_updated_warnings')." warnings)");
$oP->p("Objects update errors: ".$oStatLog->Get('stats_nb_obj_updated_errors')); $oP->p("Objects update errors: ".$oStatLog->Get('stats_nb_obj_updated_errors'));
$oP->p("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated')); $oP->p("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)");
$oP->p("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged')); $oP->p("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)");
$oP->p("Objects reconciliation errors: ".$oStatLog->Get('stats_nb_replica_reconciled_errors')); $oP->p("Objects reconciliation errors: ".$oStatLog->Get('stats_nb_replica_reconciled_errors'));
$oP->p("Replica disappeared, no action taken: ".$oStatLog->Get('stats_nb_replica_disappeared_no_action')); $oP->p("Replica disappeared, no action taken: ".$oStatLog->Get('stats_nb_replica_disappeared_no_action'));
} }

View File

@@ -104,7 +104,7 @@ $aPageParams = array
'mandatory' => false, 'mandatory' => false,
'modes' => 'http,cli', 'modes' => 'http,cli',
'default' => ';', 'default' => ';',
'description' => 'column separator in CSV data', 'description' => 'column separator in CSV data (1 char, or \'tab\')',
), ),
'qualifier' => array 'qualifier' => array
( (
@@ -120,6 +120,13 @@ $aPageParams = array
'default' => 'summary', 'default' => 'summary',
'description' => '[retcode] to return the count of lines in error, [summary] to return a concise report, [details] to get a detailed report (each line listed)', 'description' => '[retcode] to return the count of lines in error, [summary] to return a concise report, [details] to get a detailed report (each line listed)',
), ),
'max_chunk_size' => array
(
'mandatory' => false,
'modes' => 'cli',
'default' => '0',
'description' => 'Limit on the count of records that can be loaded at once while performing the synchronization',
),
/* /*
'reportlevel' => array 'reportlevel' => array
( (
@@ -302,6 +309,11 @@ try
$sComment = ReadParam($oP, 'comment', 'raw_data'); $sComment = ReadParam($oP, 'comment', 'raw_data');
$sNoStopOnImportError = ReadParam($oP, 'no_stop_on_import_error'); $sNoStopOnImportError = ReadParam($oP, 'no_stop_on_import_error');
if (strtolower(trim($sSep)) == 'tab')
{
$sSep = "\t";
}
$oLoadStartDate = new DateTime(); // Now $oLoadStartDate = new DateTime(); // Now
// Note about date formatting: These MySQL settings are read-only... and in fact unused :-( // Note about date formatting: These MySQL settings are read-only... and in fact unused :-(
@@ -613,7 +625,8 @@ try
// //
if ($bSynchronize) if ($bSynchronize)
{ {
$oStatLog = $oDataSource->Synchronize($oLoadStartDate); $oSynchroExec = new SynchroExecution($oDataSource, $oLoadStartDate);
$oStatLog = $oSynchroExec->Process();
$oP->add_comment('Synchronization---'); $oP->add_comment('Synchronization---');
$oP->add_comment('------------------'); $oP->add_comment('------------------');
if ($sOutput == 'details') if ($sOutput == 'details')
@@ -633,12 +646,12 @@ try
$oP->add_comment("Objects deletion errors: ".$oStatLog->Get('stats_nb_obj_deleted_errors')); $oP->add_comment("Objects deletion errors: ".$oStatLog->Get('stats_nb_obj_deleted_errors'));
$oP->add_comment("Objects obsoleted: ".$oStatLog->Get('stats_nb_obj_obsoleted')); $oP->add_comment("Objects obsoleted: ".$oStatLog->Get('stats_nb_obj_obsoleted'));
$oP->add_comment("Objects obsolescence errors: ".$oStatLog->Get('stats_nb_obj_obsoleted_errors')); $oP->add_comment("Objects obsolescence errors: ".$oStatLog->Get('stats_nb_obj_obsoleted_errors'));
$oP->add_comment("Objects created: ".$oStatLog->Get('stats_nb_obj_created')); $oP->add_comment("Objects created: ".$oStatLog->Get('stats_nb_obj_created')." (".$oStatLog->Get('stats_nb_obj_created_warnings')." warnings)");
$oP->add_comment("Objects creation errors: ".$oStatLog->Get('stats_nb_obj_created_errors')); $oP->add_comment("Objects creation errors: ".$oStatLog->Get('stats_nb_obj_created_errors'));
$oP->add_comment("Objects updated: ".$oStatLog->Get('stats_nb_obj_updated')); $oP->add_comment("Objects updated: ".$oStatLog->Get('stats_nb_obj_updated')." (".$oStatLog->Get('stats_nb_obj_updated_warnings')." warnings)");
$oP->add_comment("Objects update errors: ".$oStatLog->Get('stats_nb_obj_updated_errors')); $oP->add_comment("Objects update errors: ".$oStatLog->Get('stats_nb_obj_updated_errors'));
$oP->add_comment("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated')); $oP->add_comment("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)");
$oP->add_comment("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged')); $oP->add_comment("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)");
$oP->add_comment("Objects reconciliation errors: ".$oStatLog->Get('stats_nb_replica_reconciled_errors')); $oP->add_comment("Objects reconciliation errors: ".$oStatLog->Get('stats_nb_replica_reconciled_errors'));
$oP->add_comment("Replica disappeared, no action taken: ".$oStatLog->Get('stats_nb_replica_disappeared_no_action')); $oP->add_comment("Replica disappeared, no action taken: ".$oStatLog->Get('stats_nb_replica_disappeared_no_action'));
} }

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