Compare commits

...

136 Commits
2.3.1 ... 2.3.3

Author SHA1 Message Date
Denis Flaven
a0321d0c81 Releasing iTop 2.3.3
SVN:2.3.3[4544]
2016-12-22 16:42:45 +00:00
Denis Flaven
79a2ab8abd Getting ready for the release of iTop 2.3.3.
SVN:trunk[4542]
2016-12-22 15:14:04 +00:00
Guillaume Lajarige
af13b42eab Legacy portal: Since iTop 2.3, plain text caselog entries can no longer be toggled due to a bad jQuery selector. Only HTML entries were working.
SVN:trunk[4541]
2016-12-21 13:10:15 +00:00
Denis Flaven
75721091f2 Updated Russian dictionary, thanks to Vladimir Kunin.
Note: you can always get the latest version on Vladimir's github: https://github.com/itop-itsm-ru/itop-rus.

SVN:trunk[4540]
2016-12-19 16:34:23 +00:00
Romain Quetiez
6e327e245b N.497 Continuation of the fix done in [r4461], to correctly handle validation patterns containing a slash (AttributeURL in the enhanced customer portal). The initial fix has broken the validation of date (+time) fields because the slash was escaped twice, leading to an invalid regular expression. Requires testing of synchro, CSV import, console, customer portal...
SVN:trunk[4538]
2016-12-19 16:04:21 +00:00
Guillaume Lajarige
6bfead405d N.551: Correcting spanish translations in the new portal (When creating a new user request in full ITIL or changing password)
SVN:trunk[4536]
2016-12-19 10:15:46 +00:00
Denis Flaven
a4f5620076 N. 549: "Font" menu broken in case logs (console and legacy portal). The menu was showing <> instead of font names.
SVN:trunk[4534]
2016-12-19 10:09:48 +00:00
Guillaume Lajarige
134a427901 Portal: Added german dictionnary
SVN:trunk[4532]
2016-12-16 09:13:43 +00:00
Guillaume Lajarige
27c3facb2b Portal: Added an environment banner in the portal like in the console. It warns the user when he is browsing in an other mode than "production".
SVN:trunk[4531]
2016-12-15 15:02:26 +00:00
Denis Flaven
fa9848d1be CKEditor version 4.6.1 + color dialog (for choosing the color of a table cell)
SVN:trunk[4530]
2016-12-14 16:45:22 +00:00
Denis Flaven
1afd7d2ae4 Exclude non-needed folders from the build.
SVN:trunk[4529]
2016-12-14 15:53:20 +00:00
Guillaume Lajarige
c622efe95c Portal: Added itopversion to js/css file urls to prevent cache issues when upgrading.
SVN:trunk[4526]
2016-12-14 11:20:49 +00:00
Denis Flaven
c0949421ad Support of the "target" attribute for links.
SVN:trunk[4524]
2016-12-14 11:16:12 +00:00
Romain Quetiez
0e21edcc77 N.561 Could not add images into the description of a ticket in the legacy portal, and losing images added into the case log (update from the portal, issue occuring when the CRON is enabled)
SVN:trunk[4521]
2016-12-13 16:55:16 +00:00
Denis Flaven
df85186407 N. 481:
1) wiki text syntax was not displayed in the description or case logs of the tickets
2) when wiki text syntax was supported, the generated hyperlinks were pointing to the console (instead of the portal)

SVN:trunk[4519]
2016-12-13 16:16:13 +00:00
Romain Quetiez
403ecf7fba N.557 Date/date+time pickers in the legacy portal, not aligned with the configured date formats. Took the opportunity to implement time picking (thus aligned with any form of iTop)
SVN:trunk[4517]
2016-12-13 14:24:33 +00:00
Denis Flaven
d143f0880b N. 533: when reloading the ticket form (due to an alternate initial state path)
1) the value of some controls (non-text inputs in n:n links) was not preserved,
2) popup dialogs and CKEditor instances were not properly destroyed and re-created.

SVN:trunk[4515]
2016-12-13 10:54:45 +00:00
Guillaume Lajarige
c185e1fc4f Portal: LinkedSet label in forms can now be toggled by clicking on the field label (Was only working on the linkedset count and chevron)
SVN:trunk[4514]
2016-12-13 10:45:34 +00:00
Guillaume Lajarige
68c180f7eb N.474: Support for "file" attribute (AttributeBlob) in the portal. READONLY only for now.
SVN:trunk[4512]
2016-12-13 10:15:43 +00:00
Denis Flaven
c903fc2246 Support of text-align in the styles.
SVN:trunk[4511]
2016-12-08 13:58:01 +00:00
Denis Flaven
7d5898f302 Upgrade of CKEditor from 4.5.8 to 4.6.0 and addition of the formatting buttons:
- Font family
- Font Size
+ reordering of the toolbar buttons to have two lines of equivalent width.

SVN:trunk[4510]
2016-12-08 13:52:28 +00:00
Guillaume Lajarige
a4bdd3aaf4 N.551: Added spanish translations to the enhanced portal thanks to the community :)
SVN:trunk[4508]
2016-12-08 13:21:13 +00:00
Romain Quetiez
5bae9deecc N.545 HTML images not displayed when no login is required for the page.
SVN:trunk[4506]
2016-12-08 12:45:56 +00:00
Guillaume Lajarige
ffbd666aca N.481: Portal: Impossible to submit a form with a duration attribute. Also fixed the displayed value in tables (ManageBrick and BrowseBrick)
SVN:trunk[4504]
2016-12-08 11:36:54 +00:00
Romain Quetiez
4ec3aeec92 N.490 Losing carrier returns and rich text formatting when the latest comments are copied to child tickets
SVN:trunk[4501]
2016-12-08 10:24:06 +00:00
Guillaume Lajarige
c5a00a1bf1 N.546: Portal: Edit value in case log was kept after UserRequest update.
SVN:trunk[4500]
2016-12-08 10:18:59 +00:00
Romain Quetiez
1b1e91f0dc N.500 Changed the query to fetch tickets related to a CI, so as to make it unambiguous whatever customization is made to the datamodel.
SVN:trunk[4498]
2016-12-08 08:22:36 +00:00
Romain Quetiez
21fc272674 N.534 Cannot create a parent ticket from the ticket edition form. More generally, the object creation dialog box (opened by the mean of the PLUS button) fails as soon as any of the mandatory fields is an HTML field. Regression introduced in iTop 2.3.0, and due to HTML field edition widget (aka CKEditor)
SVN:trunk[4496]
2016-12-07 20:31:54 +00:00
Denis Flaven
5716c11450 N. 550 the OpCode cache may cause the upgrade of the datamodel to fail. Let's flush it after the compilation.
SVN:trunk[4494]
2016-12-07 13:13:36 +00:00
Romain Quetiez
f7e77e9fa6 N.539 Regression introduced in [r4451] on oct 7th. Some OQL were issuing a notice and some were generating a SQL query that would fail with error "Column 'functionalci_id' in where clause is ambiguous" (See CI details)
SVN:trunk[4492]
2016-12-05 12:52:08 +00:00
Romain Quetiez
ad8c3db6b4 Continuing [r4488] N.536 Regression introduced in [r4469] (N.505), itself fixing a regression introduced in [r4404]. REQUIRES TESTING
SVN:trunk[4490]
2016-12-05 09:59:19 +00:00
Romain Quetiez
d5c3b8d8e2 N.536 Regression introduced in [r4469] (N.505), itself fixing a regression introduced in [r4404]. REQUIRES TESTING
SVN:trunk[4488]
2016-12-02 20:37:13 +00:00
Romain Quetiez
6ac6aea29f N.480 Page broken (missing menu + ...) when bulk modifying Document Notes (having various values)
SVN:trunk[4486]
2016-12-01 14:06:02 +00:00
Romain Quetiez
7ce06c0797 Optimizations. Continuation of [r4423] and [r4471]. The optimization of the load of icons does not work depending on the itop installation directory and version of PHP (crc32 producing a negative value, not suitable for a class name).
SVN:trunk[4485]
2016-12-01 10:08:35 +00:00
Romain Quetiez
53bf1f424a N.502 Too many backups on sundays
SVN:trunk[4483]
2016-12-01 09:51:47 +00:00
Romain Quetiez
b793cded34 N.527 Enable the template placeholders for AttributeCustomFields
SVN:trunk[4481]
2016-11-25 16:37:31 +00:00
Romain Quetiez
47ec6d4917 N.523 UserRights::ListProfiles must return an empty array if nobody is currently logged in (instead of a FATAL ERROR).
SVN:trunk[4478]
2016-11-18 15:47:20 +00:00
Romain Quetiez
e586ba8d6e N.520 Setup: conflicts when a module in "extensions" is an upgrade of a module that already exists in datamodels/2.x. The most recent module must be installed and the older one must be ignored.
SVN:trunk[4477]
2016-11-17 15:45:34 +00:00
Romain Quetiez
8a913a18cf Cosmetics. Simplified the warning about editing the configuration file. No need to mention which sections can be edited, since ALL other sections have been moved out from the configuration file (includes and dictionnaries).
SVN:trunk[4475]
2016-11-08 16:36:53 +00:00
Denis Flaven
a09e579451 Support of non-case sensitive "forbidden values" in DesignerTextField
SVN:trunk[4473]
2016-11-04 15:58:59 +00:00
Denis Flaven
79c5dc2ce2 Support of "fileref" tags inside the definition of the class fields. Useful for the "default_image" tag of AttributeImage.
SVN:trunk[4472]
2016-11-04 13:34:17 +00:00
Romain Quetiez
95d7bc5319 Continuation of [r4423] Optimizing the scan of icons on disk. Added a LOCK_EX flag to prevent concurrent access related issues. Safe handing of collision on the cache key (used as a filename).
SVN:trunk[4471]
2016-10-28 14:03:17 +00:00
Romain Quetiez
d3f5d05063 N.505 Regression introduced in [r4404]. Security issue - Object visibility totally screwed the APC cache (user data) is enabled. This is a change in the way SQL queries are built and therefore requires testing.
SVN:trunk[4469]
2016-10-28 09:08:30 +00:00
Romain Quetiez
b30a35ceb5 N.504 Could not jump into the designer (APC, random)
SVN:trunk[4467]
2016-10-27 14:57:01 +00:00
Denis Flaven
fac22c9729 Bug fix: creating a new DOM Node containing the string "0" resulted in an empty node (no DOMText).
SVN:trunk[4466]
2016-10-27 08:36:26 +00:00
Romain Quetiez
44e329c38d Cleanup of unused and possibly confusing XML markup (parent node under class/presentation/details). Thanks to D. Gumble for reporting this.
SVN:trunk[4463]
2016-10-21 14:14:20 +00:00
Guillaume Lajarige
e48ad8cb61 #497 Portal : Could not update object due to "Warning: preg_match(): Unknown modifier '/'"
SVN:trunk[4461]
2016-10-21 08:29:36 +00:00
Guillaume Lajarige
359dc73526 #497 Portal : Could not update object due to "Warning: preg_match(): Unknown modifier '/'"
SVN:trunk[4460]
2016-10-20 15:45:17 +00:00
Romain Quetiez
8169e81e9a Automated tests: added tests following the latest optimizations of the query engine
SVN:trunk[4459]
2016-10-14 14:08:52 +00:00
Guillaume Lajarige
f62f087bcc #475 Portal : Could not upload attachments on IE9.
SVN:trunk[4457]
2016-10-14 09:56:45 +00:00
Romain Quetiez
4eb0b3086d N.466 HTML links with href="ftp://..." or "file://...". The filtering implemented by default (DOM Sanitization) now takes the configuration parameter url_validation_pattern into account. Thus aligning the behavior between HTML attributes and AttributeURL, and the automatic wiki formatting. By default, iTop allows the protocols http/https/ftp. To allow the 'file' protocol, edit the config file and change url_validation_pattern accordingly.
SVN:trunk[4455]
2016-10-10 16:01:46 +00:00
Guillaume Lajarige
1c81650572 Graph :
- Bar chart labels on x axis are now displayed vertically like in iTop 2.2. Also, when there are more than 24 labels, not all of them are displayed in order to keep the axis readable.
- Pie chart legend is now placed on the right side.

SVN:trunk[4453]
2016-10-10 13:14:13 +00:00
Romain Quetiez
59e3367da8 N.434 ... Continuation... Fixed regression introduced in [r4448]. OQL (parsed) queries are optimized too but the SQL query cannot be executed. See example herebelow:
SELECT
        UserRequest AS u
   JOIN Person AS p1 ON u.caller_id=p1.id
   JOIN Organization AS o1 ON p1.org_id=o1.id
   JOIN Person ON u.caller_id=Person.id
   JOIN Location AS l ON Person.location_id = l.id
WHERE Person.status='active' AND p1.status='inactive' AND l.country='France'


SVN:trunk[4451]
2016-10-07 13:15:30 +00:00
Guillaume Lajarige
2b4b0fed83 Portal : Final touch to AllowedOrganizations by settings ignore_silos to true for ServiceFamily/Service/ServiceSubcategory on the default portal configuration
SVN:trunk[4449]
2016-10-07 09:39:34 +00:00
Romain Quetiez
fe6ae6f2eb N.434 Optimized the DB queries. As an example, the query that shows the service catalog in the enhanced customer portal is now made of 5 nodes (at the class level) whereas it used to be made of 11 nodes... for the exact same results. This optimization impacts almost each queries built by iTop. The expected benefit can insignificant or not, depending on the cardinality of the data, the datamodel and the original OQL queries. We found one case where the query execution would apparently never end and it takes now less than a second. The risk with such a change is that is affects most of the queries built by iTop -requires testing!
SVN:trunk[4448]
2016-10-07 08:45:24 +00:00
Romain Quetiez
79d994acf7 N.444 ... fixing regression introduced in [r4438]
SVN:trunk[4441]
2016-10-04 13:15:33 +00:00
Romain Quetiez
9dbbb8e012 Fixed regression introduced in [r4415]: data model remains unchanged though the customizations have been successfully compiled (and taken into account in the DB). The fix consists in completing the implementation: the verb apc_cache_info was missing, preventing the cache from being reset by the setup. I took the opportunity to add other verbs of apc so as to make sure that the emulation be consistent with the emulation provided by older versions of apcu.
SVN:trunk[4440]
2016-10-03 12:47:13 +00:00
Romain Quetiez
17fafbf85b N.444 Two date picker icons (lifecycle shortcut to resolved state, or a datetime attribute on a link). Solved by a factorization of the widgets initialization so that the initialization be the same (must be idempotent)
SVN:trunk[4438]
2016-10-03 11:47:03 +00:00
Guillaume Lajarige
f3a6455ed8 Portal : Bug when adding item on the first LinkedSet of an edition form
SVN:trunk[4436]
2016-10-03 08:01:23 +00:00
Romain Quetiez
5336d9e965 Internal: improved the API robustness by throwing an exception as soon as DBObject::Set is called with a date badly formatted. This is in line with the assumption that internal DBObject values are always safe.
SVN:trunk[4435]
2016-09-30 14:22:13 +00:00
Romain Quetiez
97d11ba910 N.445 Specifying a date format (other than the default one) and allowing to create a user request in the resolved status results in an error when selecting the resolved status.
SVN:trunk[4433]
2016-09-30 14:16:35 +00:00
Guillaume Lajarige
2fa68a3abc Portal : Deadline attributes not displayed properly in ManageBrick
SVN:trunk[4431]
2016-09-30 12:50:32 +00:00
Guillaume Lajarige
a88365ca49 Resize on AttributeImage crashes when gd extension is not installed. Implemented a fallback so images are stored as is (original size) when gd extension is not available. A warning message is displayed during the setup.
SVN:trunk[4429]
2016-09-30 11:24:30 +00:00
Guillaume Lajarige
f3053c39c2 Portal : Allowed Organizations part for action rules.
SVN:trunk[4428]
2016-09-30 08:47:43 +00:00
Guillaume Lajarige
c7ac39b86a Portal : ManageBrick crashing when displaying an abstract class with child classes attributes
SVN:trunk[4426]
2016-09-30 07:12:16 +00:00
Guillaume Lajarige
88eece7188 Portal : Autocomplete bug with IE9 in forms
SVN:trunk[4424]
2016-09-29 10:16:30 +00:00
Romain Quetiez
b7d101cdfb Optimization: cache icons files (scan directories for the Icon selection edition widget) benefiting to each dashboard page (20% faster with PHP7 + OpCache + APCu)
SVN:trunk[4423]
2016-09-27 14:42:35 +00:00
Guillaume Lajarige
56ade2b44c Removed debug traces for AllowAllData
SVN:trunk[4422]
2016-09-27 13:04:16 +00:00
Romain Quetiez
2a38eb757d Code cleanup: remove unused code causing warnings with PHP7 (SQLBlock)
SVN:trunk[4421]
2016-09-27 12:41:52 +00:00
Guillaume Lajarige
954ba60611 Portal : Caching TWIG templates to improve performances
SVN:trunk[4420]
2016-09-27 12:29:25 +00:00
Guillaume Lajarige
f9a984f3e4 #1334 Portal : Sorting objects on BrowseBrick regarding the all classes' default order and not the first class' order only. (For example the services catalog might appear as sorted on the first column but not the second one)
SVN:trunk[4418]
2016-09-27 08:18:14 +00:00
Romain Quetiez
8fd9acb2ce N.440 Remote troubleshooting: when the REST/JSON API fails due to malformed utf8 characters, return a meaningful json error message (explain + debug information) instead of an empty response
SVN:trunk[4417]
2016-09-26 19:11:08 +00:00
Romain Quetiez
915a88afd4 N.441 Character "à" in a case log causing the REST/JSON API to fail if mbstring is not enabled
SVN:trunk[4416]
2016-09-26 18:45:32 +00:00
Romain Quetiez
c755b66d8c Optimization: make it less cumbersome to have the APC user cache enabled for PHP 5.5+. Prior to this enhancement, it used to require a compilation flag (for apcu) until PHP7... then it became mandatory to install a dedicated backward compatibility module named after apcu_bc. Now, having apcu installed and enabled is ENOUGH.
SVN:trunk[4415]
2016-09-26 15:54:41 +00:00
Romain Quetiez
2f8dc0fa0f Prerequisite for #1334. New API: DBObjectSet::SetOrderByClasses. Helper to sort on multicolumn queries (SELECT a, b FROM)
SVN:trunk[4413]
2016-09-23 15:05:06 +00:00
Guillaume Lajarige
a34747f893 Portal : Missing AllDataAllowed
SVN:trunk[4412]
2016-09-23 13:58:08 +00:00
Guillaume Lajarige
4dd6c813b1 Portal : Typo
SVN:trunk[4411]
2016-09-23 13:37:45 +00:00
Guillaume Lajarige
8e96094977 Portal : Allowed Organizations Part II. Made sur that the AllowAllData flag was passed everywhere it was necessary, only when it was necessary. This has been tested but needs MORE testing !
SVN:trunk[4409]
2016-09-22 09:30:12 +00:00
Guillaume Lajarige
dd41ebf861 Portal : Preserve debug parameter through urls
SVN:trunk[4408]
2016-09-22 09:24:07 +00:00
Guillaume Lajarige
f732df751c Portal : Renamed <ignore_allowed_organizations> to <ignore_silos> for a more generic aproch
SVN:trunk[4406]
2016-09-20 14:41:56 +00:00
Guillaume Lajarige
acfab8fb63 Portal : Allowed Organizations can now be applied on the portal scopes. Just set the <ignore_allowed_organizations> to true under the concerned <scope> tag.
SVN:trunk[4405]
2016-09-20 14:22:04 +00:00
Romain Quetiez
23193153c6 N°436 Core API: Correctly (mathematically!) handle the "allow all data" flag, with UNIONS and INTERSECTIONS. Requires testing
SVN:trunk[4404]
2016-09-19 13:08:32 +00:00
Romain Quetiez
a908dcd752 #1178 Internals: Object Update/Reload should never fail: as soon as a given object has been read in the current execution context, updating/reloading it is not an issue.
SVN:trunk[4402]
2016-09-15 10:01:32 +00:00
Romain Quetiez
b8a80cb267 #1325 Part III - Completing the fix by aligning the internal data structures of iTop... and possibly fix an issue (?) - Not recommended for a retrofit
SVN:trunk[4399]
2016-09-14 15:42:19 +00:00
Romain Quetiez
ad1412c7e2 #1325 Part II - Completing the fix by aligning the internal data structures of iTop... and possibly fix an issue (?) - Not recommended for a retrofit
SVN:trunk[4398]
2016-09-14 15:35:16 +00:00
Romain Quetiez
4bf51515be #1325 Could not declare an ext key to a subclass (view could not be created). This commit is minimalistic and aims at being retrofitted into the various branches of iTop. It will be followed by a second commit, which aims at completing the fix by aligning the internal data structures of iTop... and possibly fix an issue (?)
SVN:trunk[4397]
2016-09-14 15:24:03 +00:00
Guillaume Lajarige
05f97dd75f Portal : Optimized column load in ManageBrick and BrowseBrick to improve performances
SVN:trunk[4396]
2016-09-13 08:47:24 +00:00
Denis Flaven
90cab29a3c Enhancement:
- Add more debug traces (if 'synchro_trace' == 'save')
- Show debug traces (if any) at the bottom of the status page
- Protect against time differences between the MySQL server and the PHP server, when running 'synchro_import.php'

SVN:trunk[4394]
2016-09-12 12:47:40 +00:00
Romain Quetiez
445b488603 #1323 error.log polluted with the contents of each email sent (transport = PHPMail)
SVN:trunk[4392]
2016-09-07 12:40:12 +00:00
Romain Quetiez
7141e0f4b0 Fix for regression introduced in [r4384] and causing a blank screen when attempting to edit an object
SVN:trunk[4390]
2016-09-06 13:47:59 +00:00
Denis Flaven
ffc756e4a4 (regression from iTop 2.2.x) ExternalFields were not automatically reloaded when the corresponding external key changed.
SVN:trunk[4388]
2016-09-06 10:08:06 +00:00
Romain Quetiez
8bed267feb Fixed XSS vulnerability
SVN:trunk[4386]
2016-09-06 10:01:37 +00:00
Romain Quetiez
13933e488c Rich text editor: the Maximize button icon is missing if iTop is installed in a directory which name contains spaces
SVN:trunk[4384]
2016-09-06 09:35:28 +00:00
Guillaume Lajarige
d48f76e965 Portal : Added Location scope to standard portal configuration because of the implementation of r4380
SVN:trunk[4382]
2016-09-06 09:30:10 +00:00
Guillaume Lajarige
a025c95054 Portal : External keys OQL now intersect with scopes in forms!
SVN:trunk[4380]
2016-09-06 07:04:24 +00:00
Guillaume Lajarige
b2fa9c468f Portal : Added a new mode "apply_stimulus" for forms. This allows to add flags to attributes that are prompt during transitions.
SVN:trunk[4378]
2016-09-05 14:29:05 +00:00
Romain Quetiez
8feef7fd8a #1321 Losing table borders (notification templates and notes)
SVN:trunk[4376]
2016-09-05 13:01:08 +00:00
Guillaume Lajarige
8330f3d498 Portal : Request template OQL list fields marked as mandatory were not validated properly
SVN:trunk[4371]
2016-09-02 12:30:41 +00:00
Guillaume Lajarige
88e7bd225c Portal : Updated inline documentation of UserProfile brick's <fields /> tag for easiest comprehension.
SVN:trunk[4370]
2016-09-02 12:16:57 +00:00
Romain Quetiez
1b0f818c4b Forms (portal): fix the rendering of a TEXT AREA in read-only mode.
1) format=text -> several lines were displayed on a single line
2) format=html -> characters encoded twice

SVN:trunk[4369]
2016-09-02 12:11:16 +00:00
Guillaume Lajarige
32dc1225ca Portal : Fixed search on enum & finalclass as well as display value of enum and html images in lists. Also Fixed display of friendlyname in lists, which was not behaving well on abstract class when the it was composed of several fields in the child classes.
SVN:trunk[4364]
2016-09-01 10:29:04 +00:00
Guillaume Lajarige
2b35cf5047 Portal : IE9 fixes
- Remaining console.log() inthe field_set.js file
- Missing zoom-in / zoom-out mouse cursors on a object images (They are actually not available on IE9, so I put a pointing hand instead)
- Missing pointer cursors on CaseLog field collapsers

SVN:trunk[4362]
2016-08-31 15:59:09 +00:00
Denis Flaven
df66e28545 Bug fix: regression from 2.3.x: SOAP webservices were broken!
SVN:trunk[4360]
2016-08-31 14:56:21 +00:00
Romain Quetiez
0ab060c958 Improved the comments for access_mode in the config file
SVN:trunk[4359]
2016-08-31 08:22:56 +00:00
Denis Flaven
dfceef4ca6 Enhancement: protect RenameValueInDB from non-existent attributes.
SVN:trunk[4357]
2016-08-30 12:56:13 +00:00
Denis Flaven
49f82e6377 #1297: timezone configuration setting was inoperant.
SVN:trunk[4355]
2016-08-29 12:47:06 +00:00
Guillaume Lajarige
c5957f1146 Portal : Fixed a regression introduced by r4324 causing HTML entities on the browse brick.
SVN:trunk[4353]
2016-08-29 07:27:29 +00:00
Guillaume Lajarige
b357e7d7d6 Portal : Enhanced and refactored error feedback on ExternalKey / LinkedSet / CustomFields fields
SVN:trunk[4352]
2016-08-25 15:21:33 +00:00
Guillaume Lajarige
e7342b0eb8 Portal : Request template list fields now have the autocomplete option.
SVN:trunk[4351]
2016-08-25 13:52:41 +00:00
Guillaume Lajarige
f33f4e3406 Portal : Request template list fields now have the lookup/search option. (Autocomplete is still to be implemented!)
SVN:trunk[4350]
2016-08-25 13:11:34 +00:00
Romain Quetiez
3d57c720e0 Could not bulk import with the "final class" (interactive import).
SVN:trunk[4348]
2016-08-24 16:50:02 +00:00
Romain Quetiez
ef5d156b98 #1305 Issue with date/time inputs on Chrome: losing focus as soon as the date has been correctly typed, preventing the user from typing the time.
SVN:trunk[4346]
2016-08-23 18:43:41 +00:00
Guillaume Lajarige
8722447f2f Portal : Templates not working with OQL "list" fields. This only append when the field had too many items and was trying to render as an autocomplete.
SVN:trunk[4344]
2016-08-23 16:01:25 +00:00
Guillaume Lajarige
e6047dcbf5 Portal : Templates not working with OQL "list" fields. This only append when the field had too many items and was trying to render as an autocomplete.
SVN:trunk[4343]
2016-08-23 16:00:00 +00:00
Guillaume Lajarige
df1d10f1cb #1299 Portal : "Oops, could not load data" when creating request in Full ITIL instance when running PHP7. Cause was that PHP7 isn't able to understand the factory method invocation synthax, had to make it more simple with intermediate steps.
SVN:trunk[4336]
2016-08-23 11:23:12 +00:00
Guillaume Lajarige
d1dd60f928 #1281 Portal : Fixed a few hardcoded strings to dictionnaries
SVN:trunk[4335]
2016-08-23 09:25:13 +00:00
Guillaume Lajarige
7bea59fea1 Portal : Fixed a bug with external key as radio button in forms
SVN:trunk[4334]
2016-08-22 15:50:35 +00:00
Guillaume Lajarige
17703ce572 Portal : Fixed a bug on the default configuration that was displaying only UserRequest in the Closed requests brick instead of both UserRequest and Incident objects.
SVN:trunk[4333]
2016-08-22 14:55:57 +00:00
Guillaume Lajarige
6d556249aa #1284: Fixed portal issue when trying to re-open a ticket as a portal user. Cause was that the destination state had "must prompt" attributes that were all "read only" for the current user, making the entire form "read only" and therefore removing "submit" button. The user was the not able to complete the transition. Fix consists of skipping the form when all attributes are "read only" for the user.
Also :
- Refactored a portion of TWIG (Loader is now in an helper TWIG)
- Placed transition buttons to the right with the submit one as it was confusing

SVN:trunk[4332]
2016-08-22 14:51:10 +00:00
Guillaume Lajarige
16b5db448b #1281 : Service catalog brick had 2 hardcoded headers ("Service" and "Sous-Service")
SVN:trunk[4330]
2016-08-11 10:00:49 +00:00
Denis Flaven
8ef7f073b3 CSV export of audit results: pass the parameters as a POST since they may be too long to fit in the query string of the URL.
SVN:trunk[4328]
2016-08-11 09:44:31 +00:00
Denis Flaven
22c647e3f8 Cleanup a Notice message: align the prototype of DBDeleteSingleObject to the current one.
SVN:trunk[4326]
2016-08-11 09:38:14 +00:00
Denis Flaven
bf94dfa894 Bug fix: support the display of HTML fields in the lists in the new portal.
SVN:trunk[4324]
2016-08-11 09:28:23 +00:00
Guillaume Lajarige
43d3cfefb5 Portal : Removed console.log to prevent crashes on IE9
SVN:trunk[4323]
2016-08-11 08:55:38 +00:00
Denis Flaven
e0855093ea Cosmetics: Enlarge DateTime fields which were too narrow (the end of the time is not visible when editing).
SVN:trunk[4321]
2016-08-11 08:34:10 +00:00
Denis Flaven
c147062aaa Regression introduced after 2.3.0-beta [r4217]: broken links to donwload / display blobs.
SVN:trunk[4319]
2016-08-10 15:54:04 +00:00
Denis Flaven
480c2fab04 Performance optimization: do not load all the columns when it is not needed.
SVN:trunk[4317]
2016-08-10 14:56:59 +00:00
Denis Flaven
53fb619da1 Image upload inside CKEditor (via drag and drop) seems to be desactivated on IE9.
SVN:trunk[4315]
2016-08-05 10:18:22 +00:00
Denis Flaven
c0a7bbaa72 Remember that console.log breaks IE9 when the console is not open !!!
SVN:trunk[4313]
2016-08-05 10:05:08 +00:00
Denis Flaven
12b27778f5 Bug fix: properly disable the configuration editor in demo mode! (Regression)
SVN:trunk[4310]
2016-07-27 09:50:40 +00:00
Denis Flaven
babcd6a92a Increased version number of the module to 1.0.1 to reflect the change related to revision 4300 (support of custom controller in a brick).
SVN:trunk[4308]
2016-07-26 08:15:42 +00:00
Denis Flaven
d80d24c348 Disable PDF export if the PHP extension "GD" is not loaded.
SVN:trunk[4307]
2016-07-15 07:05:06 +00:00
Denis Flaven
2c78a91a00 Model factory: introduced a new variation of attribute _delta: delete_if_exists. Use this flag to delete a branch from the data model being hacked, without caring if it is already deleted (or non existing). This eases the burden of coping with different installation combinations.
SVN:trunk[4306]
2016-07-15 06:41:26 +00:00
Romain Quetiez
109e5dfe2c Releasing 2.3.1
SVN:trunk[4302]
2016-07-08 12:00:27 +00:00
Denis Flaven
93ff327b54 2.3.0 Regression: login_mode was broken !
SVN:trunk[4301]
2016-07-08 11:56:27 +00:00
Guillaume Lajarige
359c188089 Customer portal : Added possibility to give a controller action for a brick tile. This allows to use some logic in order to make a specific render relying for example on DB dataobjects
SVN:trunk[4300]
2016-07-08 09:32:43 +00:00
443 changed files with 7388 additions and 9364 deletions

View File

@@ -79,7 +79,7 @@ class Html2Text {
// replace &nbsp; with spaces
$html = str_replace("&nbsp;", " ", $html);
$html = mb_str_replace("\xa0", " ", $html); // DO NOT USE str_replace since it breaks the "à" character which is \xc3 \xa0 in UTF-8
$html = mb_str_replace("\xc2\xa0", " ", $html); // DO NOT USE str_replace since it breaks the "à" character which is \xc3 \xa0 in UTF-8
$html = static::fixNewlines($html);

View File

@@ -202,32 +202,16 @@ EOF
// Render the tabs in the page (if any)
$this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content, $this);
// Additional UI widgets to be activated inside the ajax fragment ??
if (($this->sContentType == 'text/html') && (preg_match('/class="date-pick"/', $this->s_content) || preg_match('/class="datetime-pick"/', $this->s_content)) )
// Additional UI widgets to be activated inside the ajax fragment
// Important: Testing the content type is not enough because some ajax handlers have not correctly positionned the flag (e.g json response corrupted by the script)
if (($this->sContentType == 'text/html') && (preg_match('/class="date-pick"/', $this->s_content) || preg_match('/class="datetime-pick"/', $this->s_content)) )
{
$this->add_ready_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
});
PrepareWidgets();
EOF
);
}
}
$s_captured_output = $this->ob_get_clean_safe();
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
{

View File

@@ -27,7 +27,6 @@
require_once(APPROOT.'/application/applicationcontext.class.inc.php');
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
require_once(APPROOT.'/application/displayblock.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.rule.class.inc.php');
require_once(APPROOT.'/application/query.class.inc.php');

View File

@@ -688,6 +688,7 @@ EOF
else
{
$val = array('label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().'</span>', 'value' => "<span id=\"field_{$sInputId}\">".$this->GetAsHTML($sAttCode)."</span>", 'comments' => $sComments, 'infos' => $sInfos);
$aFieldsMap[$sAttCode] = $sInputId;
}
}
else
@@ -1744,7 +1745,7 @@ EOF
$aEventsList[] ='change';
$sPlaceholderValue = 'placeholder="'.htmlentities(AttributeDateTime::GetFormat()->ToPlaceholder(), ENT_QUOTES, 'UTF-8').'"';
$sHTMLValue = "<input title=\"$sHelpText\" class=\"datetime-pick\" type=\"text\" size=\"15\" $sPlaceholderValue name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"".htmlentities($sDisplayValue, ENT_QUOTES, 'UTF-8')."\" id=\"$iId\"/>&nbsp;{$sValidationSpan}{$sReloadSpan}";
$sHTMLValue = "<input title=\"$sHelpText\" class=\"datetime-pick\" type=\"text\" size=\"19\" $sPlaceholderValue name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"".htmlentities($sDisplayValue, ENT_QUOTES, 'UTF-8')."\" id=\"$iId\"/>&nbsp;{$sValidationSpan}{$sReloadSpan}";
break;
case 'Duration':
@@ -3542,7 +3543,6 @@ EOF
// b) or override some of the configuration settings, using the second parameter of ckeditor()
$aConfig = array();
$sLanguage = strtolower(trim(UserRights::GetUserLanguage()));
$aConfig['font_style'] = $sLanguage;
$aConfig['language'] = $sLanguage;
$aConfig['contentsLanguage'] = $sLanguage;
$aConfig['extraPlugins'] = 'disabler';

View File

@@ -795,9 +795,17 @@ class DisplayBlock
$sCsvFile = strtolower($this->m_oFilter->GetClass()).'.csv';
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?expression='.urlencode($this->m_oFilter->ToOQL(true)).'&format=csv&filename='.urlencode($sCsvFile);
$sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.urlencode($this->m_oFilter->serialize()).'&format=csv';
// Pass the parameters via POST, since expression may be very long
$aParamsToPost = array(
'expression' => $this->m_oFilter->ToOQL(true),
'format' => 'csv',
'filename' => $sCsvFile,
'charset' => 'UTF-8',
);
if ($bAdvancedMode)
{
$sDownloadLink .= '&fields_advanced=1';
$aParamsToPost['fields_advance'] = 1;
$sChecked = 'CHECKED';
}
else
@@ -805,7 +813,7 @@ class DisplayBlock
$sLinkToToggle = $sLinkToToggle.'&advanced=1';
$sChecked = '';
}
$sAjaxLink = $sDownloadLink.'&charset=UTF-8'; // Includes &fields_advanced=1 if in advanced mode
$sAjaxLink = utils::GetAbsoluteUrlAppRoot().'webservices/export.php';
/*
$sCSVData = cmdbAbstractObject::GetSetAsCSV($this->m_oSet, array('fields_advanced' => $bAdvancedMode));
@@ -856,7 +864,8 @@ class DisplayBlock
$sHtml .= "<div id=\"csv_content_loading\"><div style=\"width: 250px; height: 20px; background: url(../setup/orange-progress.gif); border: 1px #999 solid; margin-left:auto; margin-right: auto; text-align: center;\">".Dict::S('UI:Loading')."</div></div><textarea id=\"csv_content\" style=\"display:none;\">\n";
//$sHtml .= htmlentities($sCSVData, ENT_QUOTES, 'UTF-8');
$sHtml .= "</textarea>\n";
$oPage->add_ready_script("$.post('$sAjaxLink', {}, function(data) { $('#csv_content').html(data); $('#csv_content_loading').hide(); $('#csv_content').show();} );");
$sJsonParams = json_encode($aParamsToPost);
$oPage->add_ready_script("$.post('$sAjaxLink', $sJsonParams, function(data) { $('#csv_content').html(data); $('#csv_content_loading').hide(); $('#csv_content').show();} );");
break;
case 'modify':
@@ -1006,6 +1015,12 @@ var chart = c3.generate({
},
axis: {
x: {
tick: {
culling: {max: 25}, // Maximum 24 labels on x axis (2 years).
centered: true,
rotate: 90,
multiline: false
},
type: 'category' // this needed to load string x value
}
},
@@ -1057,6 +1072,7 @@ var chart = c3.generate({
},
legend: {
show: true,
position: 'right',
},
tooltip: {
format: {

View File

@@ -881,7 +881,7 @@ class DesignerTextField extends DesignerFormField
$this->sValidationPattern = $sValidationPattern;
}
public function SetForbiddenValues($aValues, $sExplain)
public function SetForbiddenValues($aValues, $sExplain, $bCaseSensitive = true)
{
$aForbiddenValues = $aValues;
@@ -893,7 +893,7 @@ class DesignerTextField extends DesignerFormField
}
$this->aForbiddenValues[] = array('values' => $aForbiddenValues, 'message' => $sExplain);
$this->aForbiddenValues[] = array('values' => $aForbiddenValues, 'message' => $sExplain, 'case_sensitive' => $bCaseSensitive);
}
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
@@ -1367,6 +1367,36 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField
}
static protected function FindIconsOnDisk($sBaseDir, $sDir = '')
{
$aFiles = null;
$sKey = $sBaseDir.'/'.$sDir;
$sShortKey = abs(crc32($sKey));
$sCacheFile = utils::GetCachePath().'available-icons-'.$sShortKey.'.php';
$sCacheClass = 'AvailableIcons_'.$sShortKey;
if (file_exists($sCacheFile))
{
require_once($sCacheFile);
if ($sCacheClass::$sKey === $sKey) // crc32 collision detection
{
$aFiles = $sCacheClass::$aIconFiles;
}
}
if ($aFiles === null)
{
$aFiles = self::_FindIconsOnDisk($sBaseDir, $sDir);
$sAvailableIcons = '<?php'.PHP_EOL;
$sAvailableIcons .= '// Generated and used by '.__METHOD__.PHP_EOL;
$sAvailableIcons .= 'class '.$sCacheClass.PHP_EOL;
$sAvailableIcons .= '{'.PHP_EOL;
$sAvailableIcons .= ' static $sKey = '.var_export($sKey, true).';'.PHP_EOL;
$sAvailableIcons .= ' static $aIconFiles = '.var_export($aFiles, true).';'.PHP_EOL;
$sAvailableIcons .= '}'.PHP_EOL;
file_put_contents($sCacheFile, $sAvailableIcons, LOCK_EX);
}
return $aFiles;
}
static protected function _FindIconsOnDisk($sBaseDir, $sDir = '')
{
$aResult = array();
// Populate automatically the list of icon files
@@ -1378,7 +1408,7 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField
if (($sFile != '.') && ($sFile != '..') && ($sFile != 'lifecycle') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile))
{
$sDirSubPath = ($sDir == '') ? $sFile : $sDir.'/'.$sFile;
$aResult = array_merge($aResult, self::FindIconsOnDisk($sBaseDir, $sDirSubPath));
$aResult = array_merge($aResult, self::_FindIconsOnDisk($sBaseDir, $sDirSubPath));
}
if (preg_match("/\.(png|jpg|jpeg|gif)$/i", $sFile, $aMatches)) // png, jp(e)g and gif are considered valid
{

View File

@@ -191,7 +191,8 @@ EOF;
$sJSDatePickerOptions = json_encode($aPickerOptions);
// Time picker additional options
$aPickerOptions['showOn'] = '';
$aPickerOptions['buttonImage'] = null;
$aPickerOptions['timeFormat'] = $oTimeFormat->ToDatePicker();
$aPickerOptions['controlType'] = 'select';
$aPickerOptions['closeText'] = Dict::S('UI:Button:Ok');
@@ -208,7 +209,39 @@ EOF;
}";
$sJSDateTimePickerOptions = substr($sJSDateTimePickerOptions, 0, -1).$aMoreJSOptions;
}
$this->add_script(
<<< EOF
function PrepareWidgets()
{
// note: each action implemented here must be idempotent,
// because this helper function might be called several times on a given page
$(".date-pick").datepicker($sJSDatePickerOptions);
// Hack for the date and time picker addon issue on Chrome (see #1305)
// The workaround is to instantiate the widget on demand
// It relies on the same markup, thus reverting to the original implementation should be straightforward
$(".datetime-pick:not(.is-widget-ready)").each(function(){
var oInput = this;
$(oInput).addClass('is-widget-ready');
$('<img class="datetime-pick-button" src="../images/calendar.png">')
.insertAfter($(this))
.on('click', function(){
$(oInput)
.datetimepicker($sJSDateTimePickerOptions)
.datetimepicker('show')
.datetimepicker('option', 'onClose', function(dateText,inst){
$(oInput).datetimepicker('destroy');
})
.on('click keypress', function(){
$(oInput).datetimepicker('hide');
});
});
});
}
EOF
);
$this->m_sInitScript =
<<< EOF
try
@@ -444,8 +477,7 @@ EOF
// End of Tabs handling
$(".date-pick").datepicker($sJSDatePickerOptions);
$(".datetime-pick").datetimepicker($sJSDateTimePickerOptions);
PrepareWidgets();
// Make sortable, everything that claims to be sortable
$('.sortable').sortable( {axis: 'y', cursor: 'move', handle: '.drag_handle', stop: function()

View File

@@ -572,27 +572,59 @@ EOF
break;
}
$index++;
//echo "\nsLoginMode: $sLoginMode (user: $sAuthUser / pwd: $sAuthPwd\n)";
if ($sLoginMode == '')
}
//echo "\nsLoginMode: $sLoginMode (user: $sAuthUser / pwd: $sAuthPwd\n)";
if ($sLoginMode == '')
{
// First connection
$sDesiredLoginMode = utils::ReadParam('login_mode');
if (in_array($sDesiredLoginMode, $aAllowedLoginTypes))
{
// First connection
$sDesiredLoginMode = utils::ReadParam('login_mode');
if (in_array($sDesiredLoginMode, $aAllowedLoginTypes))
$sLoginMode = $sDesiredLoginMode;
}
else
{
$sLoginMode = $aAllowedLoginTypes[0]; // First in the list...
}
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER))
{
// X-Combodo-Ajax is a special header automatically added to all ajax requests
// Let's reply that we're currently logged-out
header('HTTP/1.0 401 Unauthorized');
exit;
}
if (($iOnExit == self::EXIT_HTTP_401) || ($sLoginMode == 'basic'))
{
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
header('HTTP/1.0 401 Unauthorized');
header('Content-type: text/html; charset=iso-8859-1');
exit;
}
else if($iOnExit == self::EXIT_RETURN)
{
if (($sAuthUser !== '') && ($sAuthPwd === null))
{
$sLoginMode = $sDesiredLoginMode;
return self::EXIT_CODE_MISSINGPASSWORD;
}
else
{
$sLoginMode = $aAllowedLoginTypes[0]; // First in the list...
}
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER))
{
// X-Combodo-Ajax is a special header automatically added to all ajax requests
// Let's reply that we're currently logged-out
header('HTTP/1.0 401 Unauthorized');
exit;
return self::EXIT_CODE_MISSINGLOGIN;
}
}
else
{
$oPage = self::NewLoginWebPage();
$oPage->DisplayLoginForm( $sLoginMode, false /* no previous failed attempt */);
$oPage->output();
exit;
}
}
else
{
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $sLoginMode, $sAuthentication))
{
//echo "Check Credentials returned false for user $sAuthUser!";
self::ResetSession();
if (($iOnExit == self::EXIT_HTTP_401) || ($sLoginMode == 'basic'))
{
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
@@ -602,66 +634,33 @@ EOF
}
else if($iOnExit == self::EXIT_RETURN)
{
if (($sAuthUser !== '') && ($sAuthPwd === null))
{
return self::EXIT_CODE_MISSINGPASSWORD;
}
else
{
return self::EXIT_CODE_MISSINGLOGIN;
}
return self::EXIT_CODE_WRONGCREDENTIALS;
}
else
{
$oPage = self::NewLoginWebPage();
$oPage->DisplayLoginForm( $sLoginMode, false /* no previous failed attempt */);
$oPage->DisplayLoginForm( $sLoginMode, true /* failed attempt */);
$oPage->output();
exit;
}
}
else
{
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $sLoginMode, $sAuthentication))
// User is Ok, let's save it in the session and proceed with normal login
UserRights::Login($sAuthUser, $sAuthentication); // Login & set the user's language
if (MetaModel::GetConfig()->Get('log_usage'))
{
//echo "Check Credentials returned false for user $sAuthUser!";
self::ResetSession();
if (($iOnExit == self::EXIT_HTTP_401) || ($sLoginMode == 'basic'))
{
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
header('HTTP/1.0 401 Unauthorized');
header('Content-type: text/html; charset=iso-8859-1');
exit;
}
else if($iOnExit == self::EXIT_RETURN)
{
return self::EXIT_CODE_WRONGCREDENTIALS;
}
else
{
$oPage = self::NewLoginWebPage();
$oPage->DisplayLoginForm( $sLoginMode, true /* failed attempt */);
$oPage->output();
exit;
}
}
else
{
// User is Ok, let's save it in the session and proceed with normal login
UserRights::Login($sAuthUser, $sAuthentication); // Login & set the user's language
if (MetaModel::GetConfig()->Get('log_usage'))
{
$oLog = new EventLoginUsage();
$oLog->Set('userinfo', UserRights::GetUser());
$oLog->Set('user_id', UserRights::GetUserObject()->GetKey());
$oLog->Set('message', 'Successful login');
$oLog->DBInsertNoReload();
}
$_SESSION['auth_user'] = $sAuthUser;
$_SESSION['login_mode'] = $sLoginMode;
UserRights::_InitSessionCache();
$oLog = new EventLoginUsage();
$oLog->Set('userinfo', UserRights::GetUser());
$oLog->Set('user_id', UserRights::GetUserObject()->GetKey());
$oLog->Set('message', 'Successful login');
$oLog->DBInsertNoReload();
}
$_SESSION['auth_user'] = $sAuthUser;
$_SESSION['login_mode'] = $sLoginMode;
UserRights::_InitSessionCache();
}
}
return self::EXIT_CODE_OK;

View File

@@ -19,7 +19,7 @@
/**
* Class PortalWebPage
*
* @copyright Copyright (C) 2010-2013 Combodo SARL
* @copyright Copyright (C) 2010-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -96,15 +96,88 @@ class PortalWebPage extends NiceWebPage
$this->add_linked_script("../js/ckeditor/ckeditor.js");
$this->add_linked_script("../js/ckeditor/adapters/jquery.js");
$this->add_linked_script("../js/jquery-ui-timepicker-addon.js");
$this->add_linked_script("../js/jquery-ui-timepicker-addon-i18n.min.js");
$this->add_linked_stylesheet("../css/jquery-ui-timepicker-addon.css");
$sJSDisconnectedMessage = json_encode(Dict::S('UI:DisconnectedDlgMessage'));
$sJSTitle = json_encode(Dict::S('UI:DisconnectedDlgTitle'));
$sJSLoginAgain = json_encode(Dict::S('UI:LoginAgain'));
$sJSStayOnThePage = json_encode(Dict::S('UI:StayOnThePage'));
$sJSDaysMin = json_encode(array(Dict::S('DayOfWeek-Sunday-Min'), Dict::S('DayOfWeek-Monday-Min'), Dict::S('DayOfWeek-Tuesday-Min'), Dict::S('DayOfWeek-Wednesday-Min'),
Dict::S('DayOfWeek-Thursday-Min'), Dict::S('DayOfWeek-Friday-Min'), Dict::S('DayOfWeek-Saturday-Min')));
$sJSMonthsShort = json_encode(array(Dict::S('Month-01-Short'), Dict::S('Month-02-Short'), Dict::S('Month-03-Short'), Dict::S('Month-04-Short'), Dict::S('Month-05-Short'), Dict::S('Month-06-Short'),
Dict::S('Month-07-Short'), Dict::S('Month-08-Short'), Dict::S('Month-09-Short'), Dict::S('Month-10-Short'), Dict::S('Month-11-Short'), Dict::S('Month-12-Short')));
$iFirstDayOfWeek = (int) Dict::S('Calendar-FirstDayOfWeek');
$aDaysMin = array(Dict::S('DayOfWeek-Sunday-Min'), Dict::S('DayOfWeek-Monday-Min'), Dict::S('DayOfWeek-Tuesday-Min'), Dict::S('DayOfWeek-Wednesday-Min'),
Dict::S('DayOfWeek-Thursday-Min'), Dict::S('DayOfWeek-Friday-Min'), Dict::S('DayOfWeek-Saturday-Min'));
$aMonthsShort = array(Dict::S('Month-01-Short'), Dict::S('Month-02-Short'), Dict::S('Month-03-Short'), Dict::S('Month-04-Short'), Dict::S('Month-05-Short'), Dict::S('Month-06-Short'),
Dict::S('Month-07-Short'), Dict::S('Month-08-Short'), Dict::S('Month-09-Short'), Dict::S('Month-10-Short'), Dict::S('Month-11-Short'), Dict::S('Month-12-Short'));
$sTimeFormat = AttributeDateTime::GetFormat()->ToTimeFormat();
$oTimeFormat = new DateTimeFormat($sTimeFormat);
$sJSLangShort = json_encode(strtolower(substr(Dict::GetUserLanguage(), 0, 2)));
// Date picker options
$aPickerOptions = array(
'showOn' => 'button',
'buttonImage' => '../images/calendar.png',
'buttonImageOnly' => true,
'dateFormat' => AttributeDate::GetFormat()->ToDatePicker(),
'constrainInput' => false,
'changeMonth' => true,
'changeYear' => true,
'dayNamesMin' => $aDaysMin,
'monthNamesShort' => $aMonthsShort,
'firstDay' => (int) Dict::S('Calendar-FirstDayOfWeek'),
);
$sJSDatePickerOptions = json_encode($aPickerOptions);
// Time picker additional options
$aPickerOptions['showOn'] = '';
$aPickerOptions['buttonImage'] = null;
$aPickerOptions['timeFormat'] = $oTimeFormat->ToDatePicker();
$aPickerOptions['controlType'] = 'select';
$aPickerOptions['closeText'] = Dict::S('UI:Button:Ok');
$sJSDateTimePickerOptions = json_encode($aPickerOptions);
if ($sJSLangShort != '"en"')
{
// More options that cannot be passed via json_encode since they must be evaluated client-side
$aMoreJSOptions = ",
'timeText': $.timepicker.regional[$sJSLangShort].timeText,
'hourText': $.timepicker.regional[$sJSLangShort].hourText,
'minuteText': $.timepicker.regional[$sJSLangShort].minuteText,
'secondText': $.timepicker.regional[$sJSLangShort].secondText,
'currentText': $.timepicker.regional[$sJSLangShort].currentText
}";
$sJSDateTimePickerOptions = substr($sJSDateTimePickerOptions, 0, -1).$aMoreJSOptions;
}
$this->add_script(
<<< EOF
function PrepareWidgets()
{
// note: each action implemented here must be idempotent,
// because this helper function might be called several times on a given page
$(".date-pick").datepicker($sJSDatePickerOptions);
// Hack for the date and time picker addon issue on Chrome (see #1305)
// The workaround is to instantiate the widget on demand
// It relies on the same markup, thus reverting to the original implementation should be straightforward
$(".datetime-pick:not(.is-widget-ready)").each(function(){
var oInput = this;
$(oInput).addClass('is-widget-ready');
$('<img class="datetime-pick-button" src="../images/calendar.png">')
.insertAfter($(this))
.on('click', function(){
$(oInput)
.datetimepicker($sJSDateTimePickerOptions)
.datetimepicker('show')
.datetimepicker('option', 'onClose', function(dateText,inst){
$(oInput).datetimepicker('destroy');
})
.on('click keypress', function(){
$(oInput).datetimepicker('hide');
});
});
});
}
EOF
);
$this->add_ready_script(
<<<EOF
@@ -146,34 +219,10 @@ try
}
});
$(".date-pick").datepicker({
showOn: 'button',
buttonImage: '../images/calendar.png',
buttonImageOnly: true,
dateFormat: 'yy-mm-dd',
constrainInput: false,
changeMonth: true,
changeYear: true,
dayNamesMin: $sJSDaysMin,
monthNamesShort: $sJSMonthsShort,
firstDay: $iFirstDayOfWeek
});
$(".datetime-pick").datepicker({
showOn: 'button',
buttonImage: '../images/calendar.png',
buttonImageOnly: true,
dateFormat: 'yy-mm-dd 00:00:00',
constrainInput: false,
changeMonth: true,
changeYear: true,
dayNamesMin: $sJSDaysMin,
monthNamesShort: $sJSMonthsShort,
firstDay: $iFirstDayOfWeek
});
PrepareWidgets();
//$('.resizable').resizable(); // Make resizable everything that claims to be resizable !
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry_html').toggle(); });
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry,.caselog_entry_html').toggle(); });
$(document).ajaxSend(function(event, jqxhr, options) {
jqxhr.setRequestHeader('X-Combodo-Ajax', 'true');

View File

@@ -1,531 +0,0 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* SqlBlock - display tables or charts, given an SQL query - use cautiously!
*
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/pages/php-ofc-library/open-flash-chart.php');
/**
* Helper class to design optimized dashboards, based on an SQL query
*
*/
class SqlBlock
{
protected $m_sQuery;
protected $m_aColumns;
protected $m_sTitle;
protected $m_sType;
protected $m_aParams;
public function __construct($sQuery, $aColumns, $sTitle, $sType, $aParams = array())
{
$this->m_sQuery = $sQuery;
$this->m_aColumns = $aColumns;
$this->m_sTitle = $sTitle;
$this->m_sType = $sType;
$this->m_aParams = $aParams;
}
/**
* Constructs a SqlBlock object from an XML template
/*
*
* <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') AND $CONDITION(param1, ticket.org_id)$</sql>
* <type>table</type>
* <title>UserRequest:Overview-Title</title>
* <parameter>
* <name>param1</name>
* <type>context</type>
* <mapping>org_id</mapping>
* </parameter>
* <column>
* <name>Date</name>
* <label>UserRequest:Overview-Date</label>
* <drilldown></drilldown>
* </column>
* <column>
* <name>Count</name>
* <label>UserRequest:Overview-Count</label>
* <drilldown>SELECT UserIssue WHERE date_format(start_date, '%d') = :Date</drilldown>
* </column>
* </sqlblock>
*
* Tags
* - 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.
* - 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 / name: name of the column in the SQL query (use aliases)
* - column / label: label, typed in clear or given as a dictionnary entry
* - column / drilldown: NOT IMPLEMENTED YET - OQL with parameters corresponding to column names (in the query)
*
* @param $sTemplate string The XML template
* @return DisplayBlock The DisplayBlock object, or null if the template is invalid
*/
public static function FromTemplate($sTemplate)
{
$oXml = simplexml_load_string('<root>'.$sTemplate.'</root>', 'SimpleXMLElement', LIBXML_NOCDATA);
if (false)
{
// Debug
echo "<pre>\n";
print_r($oXml);
echo "</pre>\n";
}
if (isset($oXml->title))
{
$sTitle = (string)$oXml->title;
}
if (isset($oXml->type))
{
$sType = (string)$oXml->type;
}
else
{
$sType = 'table';
}
if (!isset($oXml->sql))
{
throw new Exception('Missing tag "sql" in sqlblock');
}
$sQuery = (string)$oXml->sql;
$aColumns = array();
if (isset($oXml->column))
{
foreach ($oXml->column AS $oColumnData)
{
if (!isset($oColumnData->name))
{
throw new Exception("Missing tag 'name' in sqlblock/column");
}
$sName = (string) $oColumnData->name;
if (strlen($sName) == 0)
{
throw new Exception("Empty tag 'name' in sqlblock/column");
}
$aColumns[$sName] = array();
if (isset($oColumnData->label))
{
$sLabel = (string)$oColumnData->label;
if (strlen($sLabel) > 0)
{
$aColumns[$sName]['label'] = Dict::S($sLabel);
}
}
if (isset($oColumnData->drilldown))
{
$sDrillDown = (string)$oColumnData->drilldown;
if (strlen($sDrillDown) > 0)
{
$aColumns[$sName]['drilldown'] = $sDrillDown;
}
}
}
}
$aParams = array();
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())
{
if (empty($aExtraParams['currentId']))
{
$sId = 'sqlblock_'.$oPage->GetUniqueId(); // Works only if the page is not an Ajax one !
}
else
{
$sId = $aExtraParams['currentId'];
}
// $oPage->add($this->GetRenderContent($oPage, $aExtraParams, $sId));
$sQuery = $this->BuildQuery();
$res = CMDBSource::Query($sQuery);
$aQueryCols = CMDBSource::GetColumns($res);
// Prepare column definitions (check + give default values)
//
foreach($this->m_aColumns as $sName => $aColumnData)
{
if (!in_array($sName, $aQueryCols))
{
throw new Exception("Unknown column name '$sName' in sqlblock column");
}
if (!isset($aColumnData['label']))
{
$this->m_aColumns[$sName]['label'] = $sName;
}
if (isset($aColumnData['drilldown']) && !empty($aColumnData['drilldown']))
{
// Check if the OQL is valid
try
{
$this->m_aColumns[$sName]['filter'] = DBObjectSearch::FromOQL($aColumnData['drilldown']);
}
catch(OQLException $e)
{
unset($aColumnData['drilldown']);
}
}
}
if (strlen($this->m_sTitle) > 0)
{
$oPage->add("<h2>".Dict::S($this->m_sTitle)."</h2>\n");
}
switch ($this->m_sType)
{
case 'bars':
case 'pie':
$aColNames = array_keys($this->m_aColumns);
$sXColName = $aColNames[0];
$sYColName = $aColNames[1];
$aData = array();
$aRows = array();
while($aRow = CMDBSource::FetchArray($res))
{
$aData[$aRow[$sXColName]] = $aRow[$sYColName];
$aRows[$aRow[$sXColName]] = $aRow;
}
$this->RenderChart($oPage, $sId, $aData, $this->m_aColumns[$sYColName]['drilldown'], $aRows);
break;
default:
case 'table':
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
if (!empty($sContext))
{
$sContext = '&'.$sContext;
}
$aDisplayConfig = array();
foreach($this->m_aColumns as $sName => $aColumnData)
{
$aDisplayConfig[$sName] = array('label' => $aColumnData['label'], 'description' => '');
}
$aDisplayData = array();
while($aRow = CMDBSource::FetchArray($res))
{
$aSQLColNames = array_keys($aRow);
$aDisplayRow = array();
foreach($this->m_aColumns as $sName => $aColumnData)
{
if (isset($aColumnData['filter']))
{
$sFilter = $aColumnData['drilldown'];
$sClass = $aColumnData['filter']->GetClass();
$sFilter = str_replace('SELECT '.$sClass, '', $sFilter);
foreach($aSQLColNames as $sColName)
{
$sFilter = str_replace(':'.$sColName, "'".addslashes( $aRow[$sColName] )."'", $sFilter);
}
$sURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_oql&search_form=0&oql_class='.$sClass.'&oql_clause='.urlencode($sFilter).'&format=html'.$sContext;
$aDisplayRow[$sName] = '<a href="'.$sURL.'">'.$aRow[$sName]."</a>";
}
else
{
$aDisplayRow[$sName] = $aRow[$sName];
}
}
$aDisplayData[] = $aDisplayRow;
}
$oPage->table($aDisplayConfig, $aDisplayData);
break;
}
}
public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId)
{
$sHtml = '';
return $sHtml;
}
protected function RenderChart($oPage, $sId, $aValues, $sDrillDown = '', $aRows = array())
{
// 1- Compute Open Flash Chart data
//
$aValueKeys = array();
$index = 0;
if ((count($aValues) > 0) && ($sDrillDown != ''))
{
$oFilter = DBObjectSearch::FromOQL($sDrillDown);
$sClass = $oFilter->GetClass();
$sOQLClause = str_replace('SELECT '.$sClass, '', $sDrillDown);
$aSQLColNames = array_keys(current($aRows)); // Read the list of columns from the current (i.e. first) element of the array
$oAppContext = new ApplicationContext();
$sURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_oql&search_form=0&oql_class='.$sClass.'&format=html&'.$oAppContext->GetForLink().'&oql_clause=';
}
$aURLs = array();
foreach($aValues as $key => $value)
{
// Make sure that values are integers (so that max() will work....)
// and build an array of STRING with the keys (numeric keys are transformed into string by PHP :-(
$aValues[$key] = (int)$value;
$aValueKeys[] = (string)$key;
// Build the custom query for the 'drill down' on each element
if ($sDrillDown != '')
{
$sFilter = $sOQLClause;
foreach($aSQLColNames as $sColName)
{
$sFilter = str_replace(':'.$sColName, "'".addslashes( $aRows[$key][$sColName] )."'", $sFilter);
$aURLs[$index] = $sURL.urlencode($sFilter);
}
}
$index++;
}
$oChart = new open_flash_chart();
if ($this->m_sType == 'bars')
{
$oChartElement = new bar_glass();
if (count($aValues) > 0)
{
$maxValue = max($aValues);
}
else
{
$maxValue = 1;
}
$oYAxis = new y_axis();
$aMagicValues = array(1,2,5,10);
$iMultiplier = 1;
$index = 0;
$iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier;
while($maxValue > $iTop)
{
$index++;
$iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier;
if (($index % count($aMagicValues)) == 0)
{
$iMultiplier = $iMultiplier * 10;
}
}
//echo "oYAxis->set_range(0, $iTop, $iMultiplier);\n";
$oYAxis->set_range(0, $iTop, $iMultiplier);
$oChart->set_y_axis( $oYAxis );
$aBarValues = array();
foreach($aValues as $iValue)
{
$oBarValue = new bar_value($iValue);
$oBarValue->on_click("ofc_drilldown_{$sId}");
$aBarValues[] = $oBarValue;
}
$oChartElement->set_values($aBarValues);
//$oChartElement->set_values(array_values($aValues));
$oXAxis = new x_axis();
$oXLabels = new x_axis_labels();
// set them vertical
$oXLabels->set_vertical();
// set the label text
$oXLabels->set_labels($aValueKeys);
// Add the X Axis Labels to the X Axis
$oXAxis->set_labels( $oXLabels );
$oChart->set_x_axis( $oXAxis );
}
else
{
$oChartElement = new pie();
$oChartElement->set_start_angle( 35 );
$oChartElement->set_animate( true );
$oChartElement->set_tooltip( '#label# - #val# (#percent#)' );
$oChartElement->set_colours( array('#FF8A00', '#909980', '#2C2B33', '#CCC08D', '#596664') );
$aData = array();
foreach($aValues as $sValue => $iValue)
{
$oPieValue = new pie_value($iValue, $sValue); //@@ BUG: not passed via ajax !!!
$oPieValue->on_click("ofc_drilldown_{$sId}");
$aData[] = $oPieValue;
}
$oChartElement->set_values( $aData );
$oChart->x_axis = null;
}
// Title given in HTML
//$oTitle = new title($this->m_sTitle);
//$oChart->set_title($oTitle);
$oChart->set_bg_colour('#FFFFFF');
$oChart->add_element( $oChartElement );
$sData = $oChart->toPrettyString();
$sData = json_encode($sData);
// 2- Declare the Javascript function that will render the chart data\
//
$oPage->add_script(
<<< EOF
function ofc_get_data_{$sId}()
{
return $sData;
}
EOF
);
if (count($aURLs) > 0)
{
$sURLList = '';
foreach($aURLs as $index => $sURL)
{
$sURLList .= "\taURLs[$index] = '".addslashes($sURL)."';\n";
}
$oPage->add_script(
<<< EOF
function ofc_drilldown_{$sId}(index)
{
var aURLs = new Array();
{$sURLList}
var sURL = aURLs[index];
window.location.href = sURL; // Navigate !
}
EOF
);
}
// 3- Insert the Open Flash chart
//
$oPage->add("<div id=\"$sId\"><div>\n");
$oPage->add_ready_script(
<<<EOF
swfobject.embedSWF( "../images/open-flash-chart.swf",
"{$sId}",
"100%", "300","9.0.0",
"expressInstall.swf",
{"get-data":"ofc_get_data_{$sId}", "id":"{$sId}"},
{'wmode': 'transparent'}
);
EOF
);
}
}
?>

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010-2016 Combodo SARL
//
// This file is part of iTop.
//
@@ -20,9 +20,8 @@
* Class UIHTMLEditorWidget
* UI wdiget for displaying and editing one-way encrypted passwords
*
* @author Phil Eddies
* @author Romain Quetiez
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @copyright Copyright (C) 2010-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -99,9 +98,26 @@ class UIHTMLEditorWidget
// Could also be bound to 'instanceReady.ckeditor'
$oPage->add_ready_script("$('#$iId').bind('validate', function(evt, sFormId) { return ValidateCKEditField('$iId', '', {$this->m_sMandatory}, sFormId, '') } );\n");
$oPage->add_ready_script("$('#$iId').bind('update', function() { BlockField('cke_$iId', $('#$iId').attr('disabled')); $(this).data('ckeditorInstance').setReadOnly($(this).prop('disabled')); } );\n");
$oPage->add_ready_script(
<<<EOF
$('#$iId').bind('update', function(evt){
BlockField('cke_$iId', $('#$iId').attr('disabled'));
//Delayed execution - ckeditor must be properly initialized before setting readonly
var retryCount = 0;
var oMe = $('#$iId');
var delayedSetReadOnly = function () {
if (oMe.data('ckeditorInstance').editable() == undefined && retryCount++ < 10) {
setTimeout(delayedSetReadOnly, retryCount * 100); //Wait a while longer each iteration
}
else
{
oMe.data('ckeditorInstance').setReadOnly(oMe.prop('disabled'));
}
};
setTimeout(delayedSetReadOnly, 50);
});
EOF
);
return $sHtmlValue;
}
}
?>

View File

@@ -163,38 +163,10 @@ class UILinksWidget
$aFieldsMap[$sFieldCode] = $sSafeId;
}
$sState = '';
$sJSDaysMin = json_encode(array(Dict::S('DayOfWeek-Sunday-Min'), Dict::S('DayOfWeek-Monday-Min'), Dict::S('DayOfWeek-Tuesday-Min'), Dict::S('DayOfWeek-Wednesday-Min'),
Dict::S('DayOfWeek-Thursday-Min'), Dict::S('DayOfWeek-Friday-Min'), Dict::S('DayOfWeek-Saturday-Min')));
$sJSMonthsShort = json_encode(array(Dict::S('Month-01-Short'), Dict::S('Month-02-Short'), Dict::S('Month-03-Short'), Dict::S('Month-04-Short'), Dict::S('Month-05-Short'), Dict::S('Month-06-Short'),
Dict::S('Month-07-Short'), Dict::S('Month-08-Short'), Dict::S('Month-09-Short'), Dict::S('Month-10-Short'), Dict::S('Month-11-Short'), Dict::S('Month-12-Short')));
$iFirstDayOfWeek = (int) Dict::S('Calendar-FirstDayOfWeek');
$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,
dayNamesMin: $sJSDaysMin,
monthNamesShort: $sJSMonthsShort,
firstDay: $iFirstDayOfWeek
});
$(".datetime-pick").datepicker({
showOn: 'button',
buttonImage: '../images/calendar.png',
buttonImageOnly: true,
dateFormat: 'yy-mm-dd 00:00:00',
constrainInput: false,
changeMonth: true,
changeYear: true,
dayNamesMin: $sJSDaysMin,
monthNamesShort: $sJSMonthsShort,
firstDay: $iFirstDayOfWeek
});
PrepareWidgets();
EOF
);
}

View File

@@ -437,6 +437,45 @@ class utils
return $iReturn;
}
/**
* Format a value into a more friendly format (KB, MB, GB, TB) instead a juste a Bytes amount.
*
* @param type $value
* @return string
*/
public static function BytesToFriendlyFormat($value)
{
$sReturn = '';
// Kilobytes
if ($value >= 1024)
{
$sReturn = 'K';
$value = $value / 1024;
}
// Megabytes
if ($value >= 1024)
{
$sReturn = 'M';
$value = $value / 1024;
}
// Gigabytes
if ($value >= 1024)
{
$sReturn = 'G';
$value = $value / 1024;
}
// Terabytes
if ($value >= 1024)
{
$sReturn = 'T';
$value = $value / 1024;
}
$value = round($value, 1);
return $value . '' . $sReturn . 'B';
}
/**
* Helper function to convert a string to a date, given a format specification. It replaces strtotime which does not allow for specifying a date in a french format (for instance)
* Example: StringToTime('01/05/11 12:03:45', '%d/%m/%y %H:%i:%s')
@@ -848,7 +887,11 @@ class utils
// Bulk export actions
$aResult[] = new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '$sDataTableId', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")");
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '$sDataTableId', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")");
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportPDF', Dict::S('UI:Menu:ExportPDF'), "ExportListDlg('$sOQL', '$sDataTableId', 'pdf', ".json_encode(Dict::S('UI:Menu:ExportPDF')).")");
if (extension_loaded('gd'))
{
// PDF export requires GD
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportPDF', Dict::S('UI:Menu:ExportPDF'), "ExportListDlg('$sOQL', '$sDataTableId', 'pdf', ".json_encode(Dict::S('UI:Menu:ExportPDF')).")");
}
}
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL')");
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
@@ -1275,10 +1318,21 @@ class utils
*/
public static function ResizeImageToFit(ormDocument $oImage, $iWidth, $iHeight, $iMaxImageWidth, $iMaxImageHeight)
{
// If image size smaller than maximums, we do nothing
if (($iWidth <= $iMaxImageWidth) && ($iHeight <= $iMaxImageHeight))
{
return $oImage;
}
// If gd extension is not loaded, we put a warning in the log and return the image as is
if (extension_loaded('gd') === false)
{
IssueLog::Warning('Image could not be resized as the "gd" extension does not seem to be loaded. It will remain as ' . $iWidth . 'x' . $iHeight . ' instead of ' . $iMaxImageWidth . 'x' . $iMaxImageHeight);
return $oImage;
}
switch($oImage->GetMimeType())
{
case 'image/gif':

View File

@@ -138,6 +138,22 @@ class WizardHelper
$oObj->Set($sAttCode, $value);
}
}
else if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
{
if ($value != null)
{
$oDate = $oAttDef->GetFormat()->Parse($value);
if ($oDate instanceof DateTime)
{
$value = $oDate->format($oAttDef->GetInternalFormat());
}
else
{
$value = null;
}
}
$oObj->Set($sAttCode, $value);
}
else
{
$oObj->Set($sAttCode, $value);

69
core/apc-compat.php Normal file
View File

@@ -0,0 +1,69 @@
<?php
// Copyright (C) 2016 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// Emulate the API of APC, over APCU
// Note: for PHP < 7, this compatibility used to be provided by APCU itself (if compiled with some options)
// for PHP 7+, it can be provided by the mean of apcu_bc, which is not so simple to install
// The current emulation aims at skipping this complexity
if (!function_exists('apc_store') && function_exists('apcu_store'))
{
function apc_add($key, $var, $ttl = 0)
{
return apcu_add($key, $var, $ttl);
}
function apc_cache_info($cache_type = '', $limited = false)
{
return apcu_cache_info($limited);
}
function apc_cas($key, $old, $new)
{
return apcu_cas($key, $old, $new);
}
function apc_clear_cache($cache_type = '')
{
return apcu_clear_cache();
}
function apc_dec($key, $step = 1, &$success = null)
{
apcu_dec($key, $step, $success);
}
function apc_delete($key)
{
return apcu_delete($key);
}
function apc_exists($keys)
{
return apcu_exists($keys);
}
function apc_fetch($key)
{
return apcu_fetch($key);
}
function apc_inc($key, $step = 1, &$success = null)
{
apcu_inc($key, $step, $success);
}
function apc_sma_info($limited = false)
{
return apcu_sma_info($limited);
}
function apc_store($key, $var, $ttl = 0)
{
return apcu_store($key, $var, $ttl);
}
}

View File

@@ -2116,6 +2116,41 @@ class AttributeFinalClass extends AttributeString
}
}
/**
* An enum can be localized
*/
public function MakeValueFromString($sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null, $sAttributeQualifier = null)
{
if ($bLocalizedValue)
{
// Lookup for the value matching the input
//
$sFoundValue = null;
$aRawValues = self::GetAllowedValues();
if (!is_null($aRawValues))
{
foreach ($aRawValues as $sKey => $sValue)
{
if ($sProposedValue == $sValue)
{
$sFoundValue = $sKey;
break;
}
}
}
if (is_null($sFoundValue))
{
return null;
}
return $this->MakeRealValue($sFoundValue, null);
}
else
{
return parent::MakeValueFromString($sProposedValue, $bLocalizedValue, $sSepItem, $sSepAttribute, $sSepValue, $sAttributeQualifier);
}
}
// Because this is sometimes used to get a localized/string version of an attribute...
public function GetEditValue($sValue, $oHostObj = null)
{
@@ -3776,6 +3811,17 @@ class AttributeDateTime extends AttributeDBField
}
if (!is_numeric($proposedValue))
{
// Check the format
try
{
$oFormat = new DateTimeFormat($this->GetInternalFormat());
$oTrash = $oFormat->Parse($proposedValue);
}
catch (Exception $e)
{
throw new Exception('Wrong format for date attribute '.$this->GetCode().', expecting "'.$this->GetInternalFormat().'" and got "'.$proposedValue.'"');
}
return $proposedValue;
}
@@ -4003,7 +4049,7 @@ class AttributeDuration extends AttributeInteger
static public function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\LabelField';
return '\\Combodo\\iTop\\Form\\Field\\DurationField';
}
public function MakeFormField(DBObject $oObject, $oFormField = null)
@@ -4017,7 +4063,7 @@ class AttributeDuration extends AttributeInteger
// Note : As of today, this attribute is -by nature- only supported in readonly mode, not edition
$sAttCode = $this->GetCode();
$oFormField->SetCurrentValue(html_entity_decode($oObject->GetAsHTML($sAttCode), ENT_QUOTES, 'UTF-8'));
$oFormField->SetCurrentValue($oObject->Get($sAttCode));
$oFormField->SetReadOnly(true);
return $oFormField;
@@ -4330,9 +4376,13 @@ class AttributeExternalKey extends AttributeDBFieldVoid
$oTmpField = $oFormField;
$oFormField->SetOnFinalizeCallback(function() use ($oTmpField, $oTmpAttDef, $oObject)
{
$oSearch = DBSearch::FromOQL($oTmpAttDef->GetValuesDef()->GetFilterExpression());
$oSearch->SetInternalParams(array('this' => $oObject));
$oTmpField->SetSearch($oSearch);
// We set search object only if it has not already been set (overrided)
if ($oTmpField->GetSearch() === null)
{
$oSearch = DBSearch::FromOQL($oTmpAttDef->GetValuesDef()->GetFilterExpression());
$oSearch->SetInternalParams(array('this' => $oObject));
$oTmpField->SetSearch($oSearch);
}
});
}
else
@@ -5063,6 +5113,33 @@ class AttributeBlob extends AttributeDefinition
}
return $sFingerprint;
}
static public function GetFormFieldClass()
{
return '\\Combodo\\iTop\\Form\\Field\\BlobField';
}
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
if ($oFormField === null)
{
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
// Note: As of today we want this field to always be read-only
$oFormField->SetReadOnly(true);
// Generating urls
$value = $oObject->Get($this->GetCode());
$oFormField->SetDownloadUrl($value->GetDownloadURL(get_class($oObject), $oObject->GetKey(), $this->GetCode()));
$oFormField->SetDisplayUrl($value->GetDisplayURL(get_class($oObject), $oObject->GetKey(), $this->GetCode()));
parent::MakeFormField($oObject, $oFormField);
return $oFormField;
}
}
/**

View File

@@ -22,6 +22,7 @@ MetaModel::IncludeModule('application/user.preferences.class.inc.php');
MetaModel::IncludeModule('application/user.dashboard.class.inc.php');
MetaModel::IncludeModule('application/audit.rule.class.inc.php');
MetaModel::IncludeModule('application/query.class.inc.php');
MetaModel::IncludeModule('setup/moduleinstallation.class.inc.php');
MetaModel::IncludeModule('core/event.class.inc.php');
MetaModel::IncludeModule('core/action.class.inc.php');

View File

@@ -828,8 +828,8 @@ class BulkChange
$sFormat = $sDateFormat;
}
$oFormat = new DateTimeFormat($sFormat);
$sRegExp = $oFormat->ToRegExpr();
if (!preg_match('/'.$sRegExp.'/', $this->m_aData[$iRow][$iCol]))
$sRegExp = $oFormat->ToRegExpr('/');
if (!preg_match($sRegExp, $this->m_aData[$iRow][$iCol]))
{
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
}

View File

@@ -412,7 +412,11 @@ abstract class BulkExport
// The built-in exports
require_once(APPROOT.'core/tabularbulkexport.class.inc.php');
require_once(APPROOT.'core/htmlbulkexport.class.inc.php');
require_once(APPROOT.'core/pdfbulkexport.class.inc.php');
if (extension_loaded('gd'))
{
// PDF export - via TCPDF - requires GD
require_once(APPROOT.'core/pdfbulkexport.class.inc.php');
}
require_once(APPROOT.'core/csvbulkexport.class.inc.php');
require_once(APPROOT.'core/excelbulkexport.class.inc.php');
require_once(APPROOT.'core/spreadsheetbulkexport.class.inc.php');

View File

@@ -246,7 +246,7 @@ class Config
),
'access_mode' => array(
'type' => 'integer',
'description' => 'Combination of flags (ACCESS_USER_WRITE | ACCESS_ADMIN_WRITE, or ACCESS_FULL)',
'description' => 'Access mode: ACCESS_READONLY = 0, ACCESS_ADMIN_WRITE = 2, ACCESS_FULL = 3',
'default' => ACCESS_FULL,
'value' => ACCESS_FULL,
'source_of_value' => '',

View File

@@ -413,10 +413,16 @@ EOF
/**
* Get the regular expression to (approximately) validate a date/time for the current format
* The validation does not take into account the number of days in a month (i.e. June 31st will pass, as well as Feb 30th!)
* @param string $sDelimiter Surround the regexp (and escape) if needed
* @return string The regular expression in PCRE syntax
*/
public function ToRegExpr()
public function ToRegExpr($sDelimiter = null)
{
return '^'.$this->Transform('regexpr', "\\%s", false /* escape all */, '.?*$^()[]/:').'$';
$sRet = '^'.$this->Transform('regexpr', "\\%s", false /* escape all */, '.?*$^()[]:').'$';
if ($sDelimiter !== null)
{
$sRet = $sDelimiter.str_replace($sDelimiter, '\\'.$sDelimiter, $sRet).$sDelimiter;
}
return $sRet;
}
}

View File

@@ -192,10 +192,14 @@ abstract class DBObject implements iDisplay
return true;
}
/**
* @param bool $bAllowAllData DEPRECATED: the reload must never fail!
* @throws CoreException
*/
public function Reload($bAllowAllData = false)
{
assert($this->m_bIsInDB);
$aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey, false /* must be found */, $bAllowAllData/* in the future $this->m_bAllowAllData ??*/);
$aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey, false /* must be found */, true /* AllowAllData */);
if (empty($aRow))
{
throw new CoreException("Failed to reload object of class '".get_class($this)."', id = ".$this->m_iKey);
@@ -1931,6 +1935,7 @@ abstract class DBObject implements iDisplay
{
$oFilter = new DBObjectSearch(get_class($this));
$oFilter->AddCondition('id', $this->m_iKey, '=');
$oFilter->AllowAllData();
$sSQL = $oFilter->MakeUpdateQuery($aDBChanges);
CMDBSource::Query($sSQL);

View File

@@ -23,7 +23,9 @@
* @copyright Copyright (C) 2010-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
// Dev hack for disabling the some query build optimizations (Folding/Merging)
define('ENABLE_OPT', true);
class DBObjectSearch extends DBSearch
{
@@ -35,6 +37,11 @@ class DBObjectSearch extends DBSearch
private $m_aPointingTo;
private $m_aReferencedBy;
// By default, some information may be hidden to the current user
// But it may happen that we need to disable that feature
protected $m_bAllowAllData = false;
protected $m_bDataFiltered = false;
public function __construct($sClass, $sClassAlias = null)
{
parent::__construct();
@@ -52,6 +59,11 @@ class DBObjectSearch extends DBSearch
$this->m_aReferencedBy = array();
}
public function AllowAllData($bAllowAllData = true) {$this->m_bAllowAllData = $bAllowAllData;}
public function IsAllDataAllowed() {return $this->m_bAllowAllData;}
protected function IsDataFiltered() {return $this->m_bDataFiltered; }
protected function SetDataFiltered() {$this->m_bDataFiltered = true;}
// Create a search definition that leads to 0 result, still a valid search object
static public function FromEmptySet($sClass)
{
@@ -172,12 +184,87 @@ class DBObjectSearch extends DBSearch
{
if (!array_key_exists($sAlias, $this->m_aClasses))
{
throw new CoreException("Invalid class alias $sAlias");
throw new CoreException("SetSelectedClasses: Invalid class alias $sAlias");
}
$this->m_aSelectedClasses[$sAlias] = $this->m_aClasses[$sAlias];
}
}
/**
* Change any alias of the query tree
*
* @param $sOldName
* @param $sNewName
* @return bool True if the alias has been found and changed
*/
public function RenameAlias($sOldName, $sNewName)
{
$bFound = false;
if (array_key_exists($sOldName, $this->m_aClasses))
{
$bFound = true;
}
if (array_key_exists($sNewName, $this->m_aClasses))
{
throw new Exception("RenameAlias: alias '$sNewName' already used");
}
$aClasses = array();
foreach ($this->m_aClasses as $sAlias => $sClass)
{
if ($sAlias === $sOldName)
{
$aClasses[$sNewName] = $sClass;
}
else
{
$aClasses[$sAlias] = $sClass;
}
}
$this->m_aClasses = $aClasses;
$aSelectedClasses = array();
foreach ($this->m_aSelectedClasses as $sAlias => $sClass)
{
if ($sAlias === $sOldName)
{
$aSelectedClasses[$sNewName] = $sClass;
}
else
{
$aSelectedClasses[$sAlias] = $sClass;
}
}
$this->m_aSelectedClasses = $aSelectedClasses;
$this->m_oSearchCondition->RenameAlias($sOldName, $sNewName);
foreach($this->m_aPointingTo as $sExtKeyAttCode=>$aPointingTo)
{
foreach($aPointingTo as $iOperatorCode => $aFilter)
{
foreach($aFilter as $oExtFilter)
{
$bFound = $oExtFilter->RenameAlias($sOldName, $sNewName) || $bFound;
}
}
}
foreach($this->m_aReferencedBy as $sForeignClass => $aReferences)
{
foreach($aReferences as $sForeignExtKeyAttCode => $aFiltersByOperator)
{
foreach ($aFiltersByOperator as $iOperatorCode => $aFilters)
{
foreach ($aFilters as $oForeignFilter)
{
$bFound = $oForeignFilter->RenameAlias($sOldName, $sNewName) || $bFound;
}
}
}
}
return $bFound;
}
public function SetModifierProperty($sPluginClass, $sProperty, $value)
{
$this->m_aModifierProperties[$sPluginClass][$sProperty] = $value;
@@ -554,8 +641,85 @@ class DBObjectSearch extends DBSearch
return null;
}
/**
* Helper to
* - convert a translation table (format optimized for the translation in an expression tree) into simple hash
* - compile over an eventually existing map
*
* @param $aRealiasingMap Map to update
* @param $aAliasTranslation Translation table resulting from calls to MergeWith_InNamespace
* @return array of <old-alias> => <new-alias>
*/
protected function UpdateRealiasingMap(&$aRealiasingMap, $aAliasTranslation)
{
if ($aRealiasingMap !== null)
{
foreach ($aAliasTranslation as $sPrevAlias => $aRules)
{
if (isset($aRules['*']))
{
$sNewAlias = $aRules['*'];
$sOriginalAlias = array_search($sPrevAlias, $aRealiasingMap);
if ($sOriginalAlias !== false)
{
$aRealiasingMap[$sOriginalAlias] = $sNewAlias;
}
else
{
$aRealiasingMap[$sPrevAlias] = $sNewAlias;
}
}
}
}
}
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
/**
* Completes the list of alias=>class by browsing the whole structure recursively
* This a workaround to handle some cases in which the list of classes is not correctly updated.
* This code should disappear as soon as DBObjectSearch get split between a container search class and a Node class
*
* @param $aClasses List to be completed
*/
protected function RecomputeClassList(&$aClasses)
{
$aClasses[$this->GetFirstJoinedClassAlias()] = $this->GetFirstJoinedClass();
// Recurse in the query tree
foreach($this->m_aPointingTo as $sExtKeyAttCode=>$aPointingTo)
{
foreach($aPointingTo as $iOperatorCode => $aFilter)
{
foreach($aFilter as $oFilter)
{
$oFilter->RecomputeClassList($aClasses);
}
}
}
foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences)
{
foreach($aReferences as $sForeignExtKeyAttCode => $aFiltersByOperator)
{
foreach ($aFiltersByOperator as $iOperatorCode => $aFilters)
{
foreach ($aFilters as $oForeignFilter)
{
$oForeignFilter->RecomputeClassList($aClasses);
}
}
}
}
}
/**
* @param DBObjectSearch $oFilter
* @param $sExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
* @throws CoreException
* @throws CoreWarning
*/
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
{
if (!MetaModel::IsValidKeyAttCode($this->GetClass(), $sExtKeyAttCode))
{
@@ -579,6 +743,22 @@ class DBObjectSearch extends DBSearch
$aAliasTranslation = array();
$res = $this->AddCondition_PointingTo_InNameSpace($oFilter, $sExtKeyAttCode, $this->m_aClasses, $aAliasTranslation, $iOperatorCode);
$this->TransferConditionExpression($oFilter, $aAliasTranslation);
$this->UpdateRealiasingMap($aRealiasingMap, $aAliasTranslation);
if (ENABLE_OPT && ($oFilter->GetClass() == $oFilter->GetFirstJoinedClass()))
{
if (isset($oFilter->m_aReferencedBy[$this->GetClass()][$sExtKeyAttCode][$iOperatorCode]))
{
// Optimization - fold sibling query
$oRemoteFilter = $oFilter->m_aReferencedBy[$this->GetClass()][$sExtKeyAttCode][$iOperatorCode][0];
$aAliasTranslation = array();
$this->MergeWith_InNamespace($oRemoteFilter, $this->m_aClasses, $aAliasTranslation);
unset($oFilter->m_aReferencedBy[$this->GetClass()][$sExtKeyAttCode][$iOperatorCode]);
$this->m_oSearchCondition = $this->m_oSearchCondition->Translate($aAliasTranslation, false, false);
$this->UpdateRealiasingMap($aRealiasingMap, $aAliasTranslation);
}
}
$this->RecomputeClassList($this->m_aClasses);
return $res;
}
@@ -587,11 +767,25 @@ class DBObjectSearch extends DBSearch
// Find the node on which the new tree must be attached (most of the time it is "this")
$oReceivingFilter = $this->GetNode($this->GetClassAlias());
$oFilter->AddToNamespace($aClassAliases, $aAliasTranslation);
$oReceivingFilter->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode][] = $oFilter;
if (ENABLE_OPT && isset($oReceivingFilter->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode]))
{
$oExisting = $oReceivingFilter->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode][0];
$oExisting->MergeWith_InNamespace($oFilter, $oExisting->m_aClasses, $aAliasTranslation);
}
else
{
$oFilter->AddToNamespace($aClassAliases, $aAliasTranslation);
$oReceivingFilter->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode][] = $oFilter;
}
}
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
/**
* @param DBObjectSearch $oFilter
* @param $sForeignExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
*/
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
{
$sForeignClass = $oFilter->GetClass();
if (!MetaModel::IsValidKeyAttCode($sForeignClass, $sForeignExtKeyAttCode))
@@ -604,6 +798,7 @@ class DBObjectSearch extends DBSearch
// à refaire en spécifique dans FromOQL
throw new CoreException("The specified filter (objects referencing an object of class {$this->GetClass()}) is not compatible with the key '{$sForeignClass}::$sForeignExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}");
}
// Note: though it seems to be a good practice to clone the given source filter
// (as it was done and fixed an issue in Intersect())
// this was not implemented here because it was causing a regression (login as admin, select an org, click on any badge)
@@ -613,6 +808,22 @@ class DBObjectSearch extends DBSearch
$aAliasTranslation = array();
$res = $this->AddCondition_ReferencedBy_InNameSpace($oFilter, $sForeignExtKeyAttCode, $this->m_aClasses, $aAliasTranslation, $iOperatorCode);
$this->TransferConditionExpression($oFilter, $aAliasTranslation);
$this->UpdateRealiasingMap($aRealiasingMap, $aAliasTranslation);
if (ENABLE_OPT && ($oFilter->GetClass() == $oFilter->GetFirstJoinedClass()))
{
if (isset($oFilter->m_aPointingTo[$sForeignExtKeyAttCode][$iOperatorCode]))
{
// Optimization - fold sibling query
$oRemoteFilter = $oFilter->m_aPointingTo[$sForeignExtKeyAttCode][$iOperatorCode][0];
$aAliasTranslation = array();
$this->MergeWith_InNamespace($oRemoteFilter, $this->m_aClasses, $aAliasTranslation);
unset($oFilter->m_aPointingTo[$sForeignExtKeyAttCode][$iOperatorCode]);
$this->m_oSearchCondition = $this->m_oSearchCondition->Translate($aAliasTranslation, false, false);
$this->UpdateRealiasingMap($aRealiasingMap, $aAliasTranslation);
}
}
$this->RecomputeClassList($this->m_aClasses);
return $res;
}
@@ -623,8 +834,16 @@ class DBObjectSearch extends DBSearch
// Find the node on which the new tree must be attached (most of the time it is "this")
$oReceivingFilter = $this->GetNode($this->GetClassAlias());
$oFilter->AddToNamespace($aClassAliases, $aAliasTranslation);
$oReceivingFilter->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode][$iOperatorCode][] = $oFilter;
if (ENABLE_OPT && isset($oReceivingFilter->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode][$iOperatorCode]))
{
$oExisting = $oReceivingFilter->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode][$iOperatorCode][0];
$oExisting->MergeWith_InNamespace($oFilter, $oExisting->m_aClasses, $aAliasTranslation);
}
else
{
$oFilter->AddToNamespace($aClassAliases, $aAliasTranslation);
$oReceivingFilter->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode][$iOperatorCode][] = $oFilter;
}
}
public function Intersect(DBSearch $oFilter)
@@ -654,7 +873,10 @@ class DBObjectSearch extends DBSearch
$oLeftFilter = $this->DeepClone();
$oRightFilter = $oRightFilter->DeepClone();
$bAllowAllData = ($oLeftFilter->IsAllDataAllowed() && $oRightFilter->IsAllDataAllowed());
$oLeftFilter->AllowAllData($bAllowAllData);
if ($oLeftFilter->GetClass() != $oRightFilter->GetClass())
{
if (MetaModel::IsParentClass($oLeftFilter->GetClass(), $oRightFilter->GetClass()))
@@ -810,7 +1032,7 @@ class DBObjectSearch extends DBSearch
return $this->m_oSearchCondition->ApplyParameters(array_merge($this->m_aParams, $aArgs));
}
public function ToOQL($bDevelopParams = false, $aContextParams = null)
public function ToOQL($bDevelopParams = false, $aContextParams = null, $bWithAllowAllFlag = false)
{
// Currently unused, but could be useful later
$bRetrofitParams = false;
@@ -850,6 +1072,10 @@ class DBObjectSearch extends DBSearch
{
$sRes .= " AND MATCHES '$sFullText'";
}
if ($bWithAllowAllFlag && $this->m_bAllowAllData)
{
$sRes .= " ALLOW ALL DATA";
}
return $sRes;
}
@@ -1007,6 +1233,22 @@ class DBObjectSearch extends DBSearch
$aAliases = array($sClassAlias => $sClass);
// Note: the condition must be built here, it may be altered later on when optimizing some joins
$oConditionTree = $oOqlQuery->GetCondition();
if ($oConditionTree instanceof Expression)
{
$aRawAliases = array($sClassAlias => $sClass);
$aJoinSpecs = $oOqlQuery->GetJoins();
if (is_array($aJoinSpecs))
{
foreach ($aJoinSpecs as $oJoinSpec)
{
$aRawAliases[$oJoinSpec->GetClassAlias()] = $oJoinSpec->GetClass();
}
}
$this->m_oSearchCondition = $this->OQLExpressionToCondition($sQuery, $oConditionTree, $aRawAliases);
}
// Maintain an array of filters, because the flat list is in fact referring to a tree
// And this will be an easy way to dispatch the conditions
// $this will be referenced by the other filters, or the other way around...
@@ -1015,19 +1257,32 @@ class DBObjectSearch extends DBSearch
$aJoinSpecs = $oOqlQuery->GetJoins();
if (is_array($aJoinSpecs))
{
$aAliasTranslation = array();
foreach ($aJoinSpecs as $oJoinSpec)
{
$sJoinClass = $oJoinSpec->GetClass();
$sJoinClassAlias = $oJoinSpec->GetClassAlias();
if (isset($aAliasTranslation[$sJoinClassAlias]['*']))
{
$sJoinClassAlias = $aAliasTranslation[$sJoinClassAlias]['*'];
}
// Assumption: ext key on the left only !!!
// normalization should take care of this
$oLeftField = $oJoinSpec->GetLeftField();
$sFromClass = $oLeftField->GetParent();
if (isset($aAliasTranslation[$sFromClass]['*']))
{
$sFromClass = $aAliasTranslation[$sFromClass]['*'];
}
$sExtKeyAttCode = $oLeftField->GetName();
$oRightField = $oJoinSpec->GetRightField();
$sToClass = $oRightField->GetParent();
if (isset($aAliasTranslation[$sToClass]['*']))
{
$sToClass = $aAliasTranslation[$sToClass]['*'];
}
$aAliases[$sJoinClassAlias] = $sJoinClass;
$aJoinItems[$sJoinClassAlias] = new DBObjectSearch($sJoinClass, $sJoinClassAlias);
@@ -1069,19 +1324,16 @@ class DBObjectSearch extends DBSearch
{
$oReceiver = $aJoinItems[$sToClass];
$oNewComer = $aJoinItems[$sFromClass];
$aAliasTranslation = array();
$oReceiver->AddCondition_ReferencedBy_InNameSpace($oNewComer, $sExtKeyAttCode, $oReceiver->m_aClasses, $aAliasTranslation, $iOperatorCode);
}
else
{
$oReceiver = $aJoinItems[$sFromClass];
$oNewComer = $aJoinItems[$sToClass];
$aAliasTranslation = array();
$oReceiver->AddCondition_PointingTo_InNameSpace($oNewComer, $sExtKeyAttCode, $oReceiver->m_aClasses, $aAliasTranslation, $iOperatorCode);
}
}
$this->m_oSearchCondition = $this->m_oSearchCondition->Translate($aAliasTranslation, false, false /* leave unresolved fields */);
}
// Check and prepare the select information
@@ -1092,12 +1344,6 @@ class DBObjectSearch extends DBSearch
$this->m_aSelectedClasses[$sClassToSelect] = $aAliases[$sClassToSelect];
}
$this->m_aClasses = $aAliases;
$oConditionTree = $oOqlQuery->GetCondition();
if ($oConditionTree instanceof Expression)
{
$this->m_oSearchCondition = $this->OQLExpressionToCondition($sQuery, $oConditionTree, $aAliases);
}
}
////////////////////////////////////////////////////////////////////////////
@@ -1129,7 +1375,139 @@ class DBObjectSearch extends DBSearch
return $oSQLQuery->RenderUpdate($aScalarArgs);
}
public function MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null)
{
// Hide objects that are not visible to the current user
//
$oSearch = $this;
if (!$this->IsAllDataAllowed() && !$this->IsDataFiltered())
{
$oVisibleObjects = UserRights::GetSelectFilter($this->GetClass(), $this->GetModifierProperties('UserRightsGetSelectFilter'));
if ($oVisibleObjects === false)
{
// Make sure this is a valid search object, saying NO for all
$oVisibleObjects = DBObjectSearch::FromEmptySet($this->GetClass());
}
if (is_object($oVisibleObjects))
{
$oVisibleObjects->AllowAllData();
$oSearch = $this->Intersect($oVisibleObjects);
$oSearch->SetDataFiltered();
}
else
{
// should be true at this point, meaning that no additional filtering
// is required
}
}
// Compute query modifiers properties (can be set in the search itself, by the context, etc.)
//
$aModifierProperties = MetaModel::MakeModifierProperties($oSearch);
// Create a unique cache id
//
if (self::$m_bQueryCacheEnabled || self::$m_bTraceQueries)
{
// Need to identify the query
$sOqlQuery = $oSearch->ToOql(false, null, true);
if (count($aModifierProperties))
{
array_multisort($aModifierProperties);
$sModifierProperties = json_encode($aModifierProperties);
}
else
{
$sModifierProperties = '';
}
$sRawId = $sOqlQuery.$sModifierProperties;
if (!is_null($aAttToLoad))
{
$sRawId .= json_encode($aAttToLoad);
}
if (!is_null($aGroupByExpr))
{
foreach($aGroupByExpr as $sAlias => $oExpr)
{
$sRawId .= 'g:'.$sAlias.'!'.$oExpr->Render();
}
}
$sRawId .= $bGetCount;
if (is_array($aSelectedClasses))
{
$sRawId .= implode(',', $aSelectedClasses); // Unions may alter the list of selected columns
}
$sOqlId = md5($sRawId);
}
else
{
$sOqlQuery = "SELECTING... ".$oSearch->GetClass();
$sOqlId = "query id ? n/a";
}
// Query caching
//
if (self::$m_bQueryCacheEnabled)
{
// Warning: using directly the query string as the key to the hash array can FAIL if the string
// is long and the differences are only near the end... so it's safer (but not bullet proof?)
// to use a hash (like md5) of the string as the key !
//
// Example of two queries that were found as similar by the hash array:
// SELECT SLT JOIN lnkSLTToSLA AS L1 ON L1.slt_id=SLT.id JOIN SLA ON L1.sla_id = SLA.id JOIN lnkContractToSLA AS L2 ON L2.sla_id = SLA.id JOIN CustomerContract ON L2.contract_id = CustomerContract.id WHERE SLT.ticket_priority = 1 AND SLA.service_id = 3 AND SLT.metric = 'TTO' AND CustomerContract.customer_id = 2
// and
// SELECT SLT JOIN lnkSLTToSLA AS L1 ON L1.slt_id=SLT.id JOIN SLA ON L1.sla_id = SLA.id JOIN lnkContractToSLA AS L2 ON L2.sla_id = SLA.id JOIN CustomerContract ON L2.contract_id = CustomerContract.id WHERE SLT.ticket_priority = 1 AND SLA.service_id = 3 AND SLT.metric = 'TTR' AND CustomerContract.customer_id = 2
// the only difference is R instead or O at position 285 (TTR instead of TTO)...
//
if (array_key_exists($sOqlId, self::$m_aQueryStructCache))
{
// hit!
$oSQLQuery = unserialize(serialize(self::$m_aQueryStructCache[$sOqlId]));
// Note: cloning is not enough because the subtree is made of objects
}
elseif (self::$m_bUseAPCCache)
{
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
//
$sOqlAPCCacheId = 'itop-'.MetaModel::GetEnvironmentId().'-query-cache-'.$sOqlId;
$oKPI = new ExecutionKPI();
$result = apc_fetch($sOqlAPCCacheId);
$oKPI->ComputeStats('Query APC (fetch)', $sOqlQuery);
if (is_object($result))
{
$oSQLQuery = $result;
self::$m_aQueryStructCache[$sOqlId] = $oSQLQuery;
}
}
}
if (!isset($oSQLQuery))
{
$oKPI = new ExecutionKPI();
$oSQLQuery = $oSearch->BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr, $aSelectedClasses);
$oKPI->ComputeStats('BuildSQLQueryStruct', $sOqlQuery);
if (self::$m_bQueryCacheEnabled)
{
if (self::$m_bUseAPCCache)
{
$oKPI = new ExecutionKPI();
apc_store($sOqlAPCCacheId, $oSQLQuery, self::$m_iQueryCacheTTL);
$oKPI->ComputeStats('Query APC (store)', $sOqlQuery);
}
self::$m_aQueryStructCache[$sOqlId] = $oSQLQuery->DeepClone();
}
}
return $oSQLQuery;
}
protected function BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
{
$oBuild = new QueryBuilderContext($this, $aModifierProperties, $aGroupByExpr, $aSelectedClasses);

View File

@@ -445,8 +445,8 @@ class DBObjectSet
/**
* Sets the sort order for loading the rows from the DB. Changing the order by causes a Reload.
*
* @param hash $aOrderBy Format: field_code => boolean (true = ascending, false = descending)
*
* @param hash $aOrderBy Format: [alias.]attcode => boolean (true = ascending, false = descending)
*/
public function SetOrderBy($aOrderBy)
{
@@ -461,6 +461,34 @@ class DBObjectSet
}
}
/**
* Sets the sort order for loading the rows from the DB. Changing the order by causes a Reload.
*
* @param hash $aAliases Format: alias => boolean (true = ascending, false = descending). If omitted, then it defaults to all the selected classes
*/
public function SetOrderByClasses($aAliases = null)
{
if ($aAliases === null)
{
$aAliases = array();
foreach ($this->GetSelectedClasses() as $sAlias => $sClass)
{
$aAliases[$sAlias] = true;
}
}
$aAttributes = array();
foreach ($aAliases as $sAlias => $bClassDirection)
{
foreach (MetaModel::GetOrderByDefault($this->m_oFilter->GetClass($sAlias)) as $sAttCode => $bAttributeDirection)
{
$bDirection = $bClassDirection ? $bAttributeDirection : !$bAttributeDirection;
$aAttributes[$sAlias.'.'.$sAttCode] = $bDirection;
}
}
$this->SetOrderBy($aAttributes);
}
/**
* Returns the 'count' limit for loading the rows from the DB
*

View File

@@ -42,14 +42,9 @@ abstract class DBSearch
const JOIN_POINTING_TO = 0;
const JOIN_REFERENCED_BY = 1;
protected $m_bDataFiltered = false;
protected $m_bNoContextParameters = false;
protected $m_aModifierProperties = array();
// By default, some information may be hidden to the current user
// But it may happen that we need to disable that feature
protected $m_bAllowAllData = false;
public function __construct()
{
}
@@ -62,12 +57,11 @@ abstract class DBSearch
return unserialize(serialize($this)); // Beware this serializes/unserializes the search and its parameters as well
}
public function AllowAllData() {$this->m_bAllowAllData = true;}
public function IsAllDataAllowed() {return $this->m_bAllowAllData;}
abstract public function AllowAllData();
abstract public function IsAllDataAllowed();
public function NoContextParameters() {$this->m_bNoContextParameters = true;}
public function HasContextParameters() {return $this->m_bNoContextParameters;}
public function IsDataFiltered() {return $this->m_bDataFiltered; }
public function SetDataFiltered() {$this->m_bDataFiltered = true;}
public function SetModifierProperty($sPluginClass, $sProperty, $value)
{
@@ -103,6 +97,15 @@ abstract class DBSearch
*/
abstract public function SetSelectedClasses($aSelectedClasses);
/**
* Change any alias of the query tree
*
* @param $sOldName
* @param $sNewName
* @return bool True if the alias has been found and changed
*/
abstract public function RenameAlias($sOldName, $sNewName);
abstract public function IsAny();
public function Describe(){return 'deprecated - use ToOQL() instead';}
@@ -127,19 +130,35 @@ abstract class DBSearch
abstract public function AddConditionAdvanced($sAttSpec, $value);
abstract public function AddCondition_FullText($sFullText);
abstract public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS);
abstract public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS);
/**
* @param DBObjectSearch $oFilter
* @param $sExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
* @throws CoreException
* @throws CoreWarning
*/
abstract public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null);
/**
* @param DBObjectSearch $oFilter
* @param $sForeignExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
*/
abstract public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null);
abstract public function Intersect(DBSearch $oFilter);
/**
*
* @param DBSearch $oFilter
* @param integer $iDirection
* @param string $sExtKeyAttCode
* @param integer $iOperatorCode
* @param array &$RealisasingMap Map of aliases from the attached query, that could have been renamed by the optimization process
* @return DBSearch
*/
public function Join(DBSearch $oFilter, $iDirection, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
public function Join(DBSearch $oFilter, $iDirection, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
{
$oSourceFilter = $this->DeepClone();
$oRet = null;
@@ -149,7 +168,7 @@ abstract class DBSearch
$aSearches = array();
foreach ($oFilter->GetSearches() as $oSearch)
{
$aSearches[] = $oSourceFilter->Join($oSearch, $iDirection, $sExtKeyAttCode, $iOperatorCode);
$aSearches[] = $oSourceFilter->Join($oSearch, $iDirection, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
}
$oRet = new DBUnionSearch($aSearches);
}
@@ -157,7 +176,7 @@ abstract class DBSearch
{
if ($iDirection === static::JOIN_POINTING_TO)
{
$oSourceFilter->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode);
$oSourceFilter->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
}
else
{
@@ -165,7 +184,7 @@ abstract class DBSearch
{
throw new Exception('Only TREE_OPERATOR_EQUALS operator code is supported yet for AddCondition_ReferencedBy.');
}
$oSourceFilter->AddCondition_ReferencedBy($oFilter, $sExtKeyAttCode);
$oSourceFilter->AddCondition_ReferencedBy($oFilter, $sExtKeyAttCode, TREE_OPERATOR_EQUALS, $aRealiasingMap);
}
$oRet = $oSourceFilter;
}
@@ -219,7 +238,7 @@ abstract class DBSearch
return $oSearchWithAlias;
}
abstract public function ToOQL($bDevelopParams = false, $aContextParams = null);
abstract public function ToOQL($bDevelopParams = false, $aContextParams = null, $bWithAllowAllFlag = false);
static protected $m_aOQLQueries = array();
@@ -492,129 +511,8 @@ abstract class DBSearch
protected function GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $aGroupByExpr = null)
{
// Hide objects that are not visible to the current user
//
$oSearch = $this;
if (!$this->IsAllDataAllowed() && !$this->IsDataFiltered())
{
$oVisibleObjects = UserRights::GetSelectFilter($this->GetClass(), $this->GetModifierProperties('UserRightsGetSelectFilter'));
if ($oVisibleObjects === false)
{
// Make sure this is a valid search object, saying NO for all
$oVisibleObjects = DBObjectSearch::FromEmptySet($this->GetClass());
}
if (is_object($oVisibleObjects))
{
$oSearch = $this->Intersect($oVisibleObjects);
$oSearch->SetDataFiltered();
}
else
{
// should be true at this point, meaning that no additional filtering
// is required
}
}
// Compute query modifiers properties (can be set in the search itself, by the context, etc.)
//
$aModifierProperties = MetaModel::MakeModifierProperties($oSearch);
// Create a unique cache id
//
if (self::$m_bQueryCacheEnabled || self::$m_bTraceQueries)
{
// Need to identify the query
$sOqlQuery = $oSearch->ToOql();
if (count($aModifierProperties))
{
array_multisort($aModifierProperties);
$sModifierProperties = json_encode($aModifierProperties);
}
else
{
$sModifierProperties = '';
}
$sRawId = $sOqlQuery.$sModifierProperties;
if (!is_null($aAttToLoad))
{
$sRawId .= json_encode($aAttToLoad);
}
if (!is_null($aGroupByExpr))
{
foreach($aGroupByExpr as $sAlias => $oExpr)
{
$sRawId .= 'g:'.$sAlias.'!'.$oExpr->Render();
}
}
$sRawId .= $bGetCount;
$sOqlId = md5($sRawId);
}
else
{
$sOqlQuery = "SELECTING... ".$oSearch->GetClass();
$sOqlId = "query id ? n/a";
}
// Query caching
//
if (self::$m_bQueryCacheEnabled)
{
// Warning: using directly the query string as the key to the hash array can FAIL if the string
// is long and the differences are only near the end... so it's safer (but not bullet proof?)
// to use a hash (like md5) of the string as the key !
//
// Example of two queries that were found as similar by the hash array:
// SELECT SLT JOIN lnkSLTToSLA AS L1 ON L1.slt_id=SLT.id JOIN SLA ON L1.sla_id = SLA.id JOIN lnkContractToSLA AS L2 ON L2.sla_id = SLA.id JOIN CustomerContract ON L2.contract_id = CustomerContract.id WHERE SLT.ticket_priority = 1 AND SLA.service_id = 3 AND SLT.metric = 'TTO' AND CustomerContract.customer_id = 2
// and
// SELECT SLT JOIN lnkSLTToSLA AS L1 ON L1.slt_id=SLT.id JOIN SLA ON L1.sla_id = SLA.id JOIN lnkContractToSLA AS L2 ON L2.sla_id = SLA.id JOIN CustomerContract ON L2.contract_id = CustomerContract.id WHERE SLT.ticket_priority = 1 AND SLA.service_id = 3 AND SLT.metric = 'TTR' AND CustomerContract.customer_id = 2
// the only difference is R instead or O at position 285 (TTR instead of TTO)...
//
if (array_key_exists($sOqlId, self::$m_aQueryStructCache))
{
// hit!
$oSQLQuery = unserialize(serialize(self::$m_aQueryStructCache[$sOqlId]));
// Note: cloning is not enough because the subtree is made of objects
}
elseif (self::$m_bUseAPCCache)
{
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
//
$sOqlAPCCacheId = 'itop-'.MetaModel::GetEnvironmentId().'-query-cache-'.$sOqlId;
$oKPI = new ExecutionKPI();
$result = apc_fetch($sOqlAPCCacheId);
$oKPI->ComputeStats('Query APC (fetch)', $sOqlQuery);
if (is_object($result))
{
$oSQLQuery = $result;
self::$m_aQueryStructCache[$sOqlId] = $oSQLQuery;
}
}
}
if (!isset($oSQLQuery))
{
$oKPI = new ExecutionKPI();
$oSQLQuery = $oSearch->MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr);
$oSQLQuery->SetSourceOQL($sOqlQuery);
$oKPI->ComputeStats('MakeSQLQuery', $sOqlQuery);
if (self::$m_bQueryCacheEnabled)
{
if (self::$m_bUseAPCCache)
{
$oKPI = new ExecutionKPI();
apc_store($sOqlAPCCacheId, $oSQLQuery, self::$m_iQueryCacheTTL);
$oKPI->ComputeStats('Query APC (store)', $sOqlQuery);
}
self::$m_aQueryStructCache[$sOqlId] = $oSQLQuery->DeepClone();
}
}
$oSQLQuery = $this->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr);
$oSQLQuery->SetSourceOQL($this->ToOQL());
// Join to an additional table, if required...
//
@@ -624,7 +522,7 @@ abstract class DBSearch
$aExtendedFields = array();
foreach($aExtendedDataSpec['fields'] as $sColumn)
{
$sColRef = $oSearch->GetClassAlias().'_extdata_'.$sColumn;
$sColRef = $this->GetClassAlias().'_extdata_'.$sColumn;
$aExtendedFields[$sColRef] = new FieldExpressionResolved($sColumn, $sTableAlias);
}
$oSQLQueryExt = new SQLObjectQuery($aExtendedDataSpec['table'], $sTableAlias, $aExtendedFields);

View File

@@ -55,6 +55,22 @@ class DBUnionSearch extends DBSearch
$this->ComputeSelectedClasses();
}
public function AllowAllData()
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AllowAllData();
}
}
public function IsAllDataAllowed()
{
foreach ($this->aSearches as $oSearch)
{
if ($oSearch->IsAllDataAllowed() === false) return false;
}
return true;
}
/**
* Find the lowest common ancestor for each of the selected class
*/
@@ -187,6 +203,23 @@ class DBUnionSearch extends DBSearch
$this->ComputeSelectedClasses();
}
/**
* Change any alias of the query tree
*
* @param $sOldName
* @param $sNewName
* @return bool True if the alias has been found and changed
*/
public function RenameAlias($sOldName, $sNewName)
{
$bRet = false;
foreach ($this->aSearches as $oSearch)
{
$bRet = $oSearch->RenameAlias($sOldName, $sNewName) || $bRet;
}
return $bRet;
}
public function IsAny()
{
$bIsAny = true;
@@ -282,19 +315,33 @@ class DBUnionSearch extends DBSearch
}
}
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
/**
* @param DBObjectSearch $oFilter
* @param $sExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
* @throws CoreException
* @throws CoreWarning
*/
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode);
$oSearch->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
}
}
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
/**
* @param DBObjectSearch $oFilter
* @param $sForeignExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
*/
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddCondition_ReferencedBy($oFilter, $sForeignExtKeyAttCode, $iOperatorCode);
$oSearch->AddCondition_ReferencedBy($oFilter, $sForeignExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
}
}
@@ -357,12 +404,12 @@ class DBUnionSearch extends DBSearch
/**
* Overloads for query building
*/
public function ToOQL($bDevelopParams = false, $aContextParams = null)
public function ToOQL($bDevelopParams = false, $aContextParams = null, $bWithAllowAllFlag = false)
{
$aSubQueries = array();
foreach ($this->aSearches as $oSearch)
{
$aSubQueries[] = $oSearch->ToOQL($bDevelopParams, $aContextParams);
$aSubQueries[] = $oSearch->ToOQL($bDevelopParams, $aContextParams, $bWithAllowAllFlag);
}
$sRet = implode(' UNION ', $aSubQueries);
return $sRet;
@@ -409,11 +456,11 @@ class DBUnionSearch extends DBSearch
throw new Exception('MakeUpdateQuery is not implemented for the unions!');
}
protected function MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
protected function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null)
{
if (count($this->aSearches) == 1)
{
return $this->aSearches[0]->MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr);
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr);
}
$aSQLQueries = array();
@@ -468,7 +515,7 @@ class DBUnionSearch extends DBSearch
$aQueryGroupByExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
}
}
$oSubQuery = $oSearch->MakeSQLQuery($aQueryAttToLoad, false, $aModifierProperties, $aQueryGroupByExpr, $aSearchSelectedClasses);
$oSubQuery = $oSearch->GetSQLQueryStructure($aQueryAttToLoad, false, $aQueryGroupByExpr, $aSearchSelectedClasses);
$aSQLQueries[] = $oSubQuery;
}

View File

@@ -195,8 +195,6 @@ class EMail
$aFailedRecipients = array();
$this->m_oMessage->setMaxLineLength(0);
IssueLog::Info(__METHOD__.' '.$this->m_oMessage->getMaxLineLength());
IssueLog::Info(__METHOD__.' '.$this->m_oMessage->toString());
$iSent = $oMailer->send($this->m_oMessage, $aFailedRecipients);
if ($iSent === 0)
{

View File

@@ -138,7 +138,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
protected static $aTagsWhiteList = array(
'html' => array(),
'body' => array(),
'a' => array('href', 'name', 'style'),
'a' => array('href', 'name', 'style', 'target'),
'p' => array('style'),
'br' => array(),
'span' => array('style'),
@@ -159,7 +159,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
'nav' => array('style'),
'section' => array('style'),
'code' => array('style'),
'table' => array('style', 'width'),
'table' => array('style', 'width', 'summary', 'align', 'border', 'cellpadding', 'cellspacing'),
'thead' => array('style'),
'tbody' => array('style'),
'tr' => array('style'),
@@ -183,17 +183,26 @@ class HTMLDOMSanitizer extends HTMLSanitizer
'hr' => array('style'),
'pre' => array(),
'center' => array(),
'caption' => array(),
);
protected static $aAttrsWhiteList = array(
'href' => '/^(http:|https:)/i',
'src' => '/^(http:|https:|data:)/i',
);
protected static $aStylesWhiteList = array(
'background-color', 'color', 'float', 'font', 'font-style', 'font-size', 'font-family', 'padding', 'margin', 'border', 'cellpadding', 'cellspacing', 'bordercolor', 'border-collapse', 'width', 'height',
'background-color', 'color', 'float', 'font', 'font-style', 'font-size', 'font-family', 'padding', 'margin', 'border', 'cellpadding', 'cellspacing', 'bordercolor', 'border-collapse', 'width', 'height', 'text-align',
);
public function __construct()
{
if (!array_key_exists('href', self::$aAttrsWhiteList))
{
$sPattern = '/'.str_replace('/', '\/', utils::GetConfig()->Get('url_validation_pattern')).'/i';
self::$aAttrsWhiteList['href'] = $sPattern;
}
}
public function DoSanitize($sHTML)
{
$this->oDoc = new DOMDocument();

View File

@@ -464,7 +464,7 @@ EOF
oEditor.on( 'instanceReady', function() {
if(!CKEDITOR.env.iOS && $('#'+oEditor.id+'_toolbox .editor_magnifier').length == 0)
{
$('#'+oEditor.id+'_toolbox').append('<span class="editor_magnifier" title="$sToggleFullScreen" style="display:block;width:12px;height:11px;border:1px #A6A6A6 solid;cursor:pointer; background-image:url($sAppRootUrl/images/full-screen.png)">&nbsp;</span>');
$('#'+oEditor.id+'_toolbox').append('<span class="editor_magnifier" title="$sToggleFullScreen" style="display:block;width:12px;height:11px;border:1px #A6A6A6 solid;cursor:pointer; background-image:url(\\'$sAppRootUrl/images/full-screen.png\\')">&nbsp;</span>');
$('#'+oEditor.id+'_toolbox .editor_magnifier').on('click', function() {
oEditor.execCommand('maximize');
if ($(this).closest('.cke_maximized').length != 0)
@@ -473,12 +473,15 @@ EOF
}
});
}
oEditor.widgets.registered.uploadimage.onUploaded = function( upload ) {
var oData = JSON.parse(upload.xhr.responseText);
this.replaceWith( '<img src="' + upload.url + '" ' +
'width="' + oData.width + '" ' +
'height="' + oData.height + '">' );
}
if (oEditor.widgets.registered.uploadimage)
{
oEditor.widgets.registered.uploadimage.onUploaded = function( upload ) {
var oData = JSON.parse(upload.xhr.responseText);
this.replaceWith( '<img src="' + upload.url + '" ' +
'width="' + oData.width + '" ' +
'height="' + oData.height + '">' );
}
}
});
});
EOF

View File

@@ -22,6 +22,7 @@ require_once(APPROOT.'core/querymodifier.class.inc.php');
require_once(APPROOT.'core/metamodelmodifier.inc.php');
require_once(APPROOT.'core/computing.inc.php');
require_once(APPROOT.'core/relationgraph.class.inc.php');
require_once(APPROOT.'core/apc-compat.php');
/**
* Metamodel
@@ -1767,10 +1768,10 @@ abstract class MetaModel
$oFriendlyName = new AttributeExternalField($sFriendlyNameAttCode, array("allowed_values"=>null, "extkey_attcode"=>$sKeyAttCode, "target_attcode"=>$sRemoteAttCode, "depends_on"=>array()));
$oFriendlyName->SetHostClass($sClass);
self::$m_aAttribDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyName;
self::$m_aAttribOrigins[$sClass][$sFriendlyNameAttCode] = $sRemoteClass;
self::$m_aAttribOrigins[$sClass][$sFriendlyNameAttCode] = self::$m_aAttribOrigins[$sClass][$sKeyAttCode];
$oFriendlyNameFlt = new FilterFromAttribute($oFriendlyName);
self::$m_aFilterDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyNameFlt;
self::$m_aFilterOrigins[$sClass][$sFriendlyNameAttCode] = $sRemoteClass;
self::$m_aFilterOrigins[$sClass][$sFriendlyNameAttCode] = self::$m_aFilterOrigins[$sClass][$sKeyAttCode];
}
else
{
@@ -1779,10 +1780,10 @@ abstract class MetaModel
$oFriendlyName = new AttributeFriendlyName($sFriendlyNameAttCode, $sAttCode);
$oFriendlyName->SetHostClass($sClass);
self::$m_aAttribDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyName;
self::$m_aAttribOrigins[$sClass][$sFriendlyNameAttCode] = $sRemoteClass;
self::$m_aAttribOrigins[$sClass][$sFriendlyNameAttCode] = self::$m_aAttribOrigins[$sClass][$sAttCode];
$oFriendlyNameFlt = new FilterFromAttribute($oFriendlyName);
self::$m_aFilterDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyNameFlt;
self::$m_aFilterOrigins[$sClass][$sFriendlyNameAttCode] = $sRemoteClass;
self::$m_aFilterOrigins[$sClass][$sFriendlyNameAttCode] = self::$m_aFilterOrigins[$sClass][$sAttCode];
if (self::HasChildrenClasses($sRemoteClass))
{
@@ -1797,11 +1798,11 @@ abstract class MetaModel
));
$oClassRecall->SetHostClass($sClass);
self::$m_aAttribDefs[$sClass][$sClassRecallAttCode] = $oClassRecall;
self::$m_aAttribOrigins[$sClass][$sClassRecallAttCode] = $sRemoteClass;
self::$m_aAttribOrigins[$sClass][$sClassRecallAttCode] = self::$m_aAttribOrigins[$sClass][$sAttCode];
$oClassFlt = new FilterFromAttribute($oClassRecall);
self::$m_aFilterDefs[$sClass][$sClassRecallAttCode] = $oClassFlt;
self::$m_aFilterOrigins[$sClass][$sClassRecallAttCode] = $sRemoteClass;
self::$m_aFilterOrigins[$sClass][$sClassRecallAttCode] = self::$m_aFilterOrigins[$sClass][$sAttCode];
// Add it to the ZLists where the external key is present
//foreach(self::$m_aListData[$sClass] as $sListCode => $aAttributes)

View File

@@ -639,12 +639,12 @@ class ormCaseLog {
}
public function GetModifiedEntry()
public function GetModifiedEntry($sFormat = 'text')
{
$sModifiedEntry = '';
if ($this->m_bModified)
{
$sModifiedEntry = $this->GetLatestEntry();
$sModifiedEntry = $this->GetLatestEntry($sFormat);
}
return $sModifiedEntry;
}

View File

@@ -87,7 +87,7 @@ class ormCustomFieldsValue
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this->oHostObject), $this->sAttCode);
$oHandler = $oAttDef->GetHandler($this->GetValues());
return 'template...verb='.$sVerb.' sur "'.json_encode($this->aCurrentValues).'"';
return $oHandler->GetForTemplate($this->aCurrentValues, $sVerb, $bLocalize);
}
/**

View File

@@ -115,21 +115,29 @@ class ormDocument
*/
public function GetDownloadLink($sClass, $Id, $sAttCode)
{
return "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode\">".htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8')."</a>\n";
return "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode\">".htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8')."</a>\n";
}
/**
* Returns an URL to display a document like an image
* @return string
*/
public function GetDisplayURL($sClass, $Id, $sAttCode)
{
return utils::GetAbsoluteUrlAppRoot() . "pages/ajax.render.php?operation=display_document&class=$sClass&id=$Id&field=$sAttCode";
}
/**
* Returns an URL to download a document like an image (uses HTTP caching)
* @return string
*/
*/
public function GetDownloadURL($sClass, $Id, $sAttCode)
{
// Compute a signature to reset the cache anytime the data changes (this is acceptable if used only with icon files)
$sSignature = md5($this->GetData());
return utils::GetAbsoluteUrlAppRoot()."pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode&s=$sSignature&cache=86400";
return utils::GetAbsoluteUrlAppRoot() . "pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode&s=$sSignature&cache=86400";
}
public function IsPreviewAvailable()
{
$bRet = false;
@@ -176,7 +184,7 @@ class ormDocument
{
$oPage->TrashUnexpectedOutput();
$oPage->SetContentType($oDocument->GetMimeType());
//$oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName());
$oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName());
$oPage->add($oDocument->GetData());
}
}

View File

@@ -494,6 +494,7 @@ class CheckStopWatchThresholds implements iBackgroundProcess
$sExpression = "SELECT $sClass WHERE {$sAttCode}_laststart AND {$sAttCode}_{$iThreshold}_triggered = 0 AND {$sAttCode}_{$iThreshold}_deadline < '$sNow'";
$oFilter = DBObjectSearch::FromOQL($sExpression);
$oSet = new DBObjectSet($oFilter);
$oSet->OptimizeColumnLoad(array($sAttCode));
while ((time() < $iTimeLimit) && ($oObj = $oSet->Fetch()))
{
$sClass = get_class($oObj);

View File

@@ -1045,7 +1045,12 @@ class UserRights
{
$oUser = self::$m_oUser;
}
if ($oUser->GetKey() == self::$m_oUser->GetKey())
if ($oUser === null)
{
// Not logged in: no profile at all
$aProfiles = array();
}
elseif ((self::$m_oUser !== null) && ($oUser->GetKey() == self::$m_oUser->GetKey()))
{
// Data about the current user can be found into the session data
if (array_key_exists('profile_list', $_SESSION))

View File

@@ -2,42 +2,19 @@
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @license http://opensource.org/licenses/AGPL-3.0
*
*
* Инструкция по установке
*
* Процесс установки заключается в замене имеющихся локализационных файлов полученными и последующем запуске процедуры обновления iTop для перекомпиляции кода.
* 1. Скопируйте с заменой два полученных файла из "itop-rus/dictionaries" в "путь/до/вашего/itop/dictionaries".
* 2. Скопируйте с заменой полученные файлы "itop-rus/datamodels/2.x/название-модуля/ru.dict.название-модуля.php" в "путь/до/вашего/itop/datamodels/2.x/название-модуля".
* 3. Перейдите по адресу "http://адрес/вашего/itop/setup", при этом файл "путь/до/вашего/itop/conf/production/config-itop.php" должен быть доступен для записи.
* 4. На второй странице установщика выберите "Upgrade an existing iTop instance" и следуйте дальнейшим инструкциям установщика.
*
* Ответы на вопросы по установке и использованию переводов, а также на любые другие вопросы по iTop всегда можно получить на сайте сообщества iTop по-русски http://community.itop-itsm.ru.
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// 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: UserExternal
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:UserExternal' => 'Внешний пользователь',
'Class:UserExternal+' => 'Пользователь, аутентифицированный вне iTop',
'Class:UserExternal+' => 'Пользователь, аутентифицируемый вне iTop',
));
?>

View File

@@ -2,44 +2,21 @@
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @license http://opensource.org/licenses/AGPL-3.0
*
*
* Инструкция по установке
*
* Процесс установки заключается в замене имеющихся локализационных файлов полученными и последующем запуске процедуры обновления iTop для перекомпиляции кода.
* 1. Скопируйте с заменой два полученных файла из "itop-rus/dictionaries" в "путь/до/вашего/itop/dictionaries".
* 2. Скопируйте с заменой полученные файлы "itop-rus/datamodels/2.x/название-модуля/ru.dict.название-модуля.php" в "путь/до/вашего/itop/datamodels/2.x/название-модуля".
* 3. Перейдите по адресу "http://адрес/вашего/itop/setup", при этом файл "путь/до/вашего/itop/conf/production/config-itop.php" должен быть доступен для записи.
* 4. На второй странице установщика выберите "Upgrade an existing iTop instance" и следуйте дальнейшим инструкциям установщика.
*
* Ответы на вопросы по установке и использованию переводов, а также на любые другие вопросы по iTop всегда можно получить на сайте сообщества iTop по-русски http://community.itop-itsm.ru.
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// 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: UserLDAP
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:UserLDAP' => 'Пользователь LDAP',
'Class:UserLDAP+' => 'Пользователь, аутентифицированный в LDAP',
'Class:UserLDAP+' => 'Пользователь, аутентифицируемый через LDAP',
'Class:UserLDAP/Attribute:password' => 'Пароль',
'Class:UserLDAP/Attribute:password+' => 'Строка аутентификации пользователя',
));
?>
));

View File

@@ -2,44 +2,21 @@
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @license http://opensource.org/licenses/AGPL-3.0
*
*
* Инструкция по установке
*
* Процесс установки заключается в замене имеющихся локализационных файлов полученными и последующем запуске процедуры обновления iTop для перекомпиляции кода.
* 1. Скопируйте с заменой два полученных файла из "itop-rus/dictionaries" в "путь/до/вашего/itop/dictionaries".
* 2. Скопируйте с заменой полученные файлы "itop-rus/datamodels/2.x/название-модуля/ru.dict.название-модуля.php" в "путь/до/вашего/itop/datamodels/2.x/название-модуля".
* 3. Перейдите по адресу "http://адрес/вашего/itop/setup", при этом файл "путь/до/вашего/itop/conf/production/config-itop.php" должен быть доступен для записи.
* 4. На второй странице установщика выберите "Upgrade an existing iTop instance" и следуйте дальнейшим инструкциям установщика.
*
* Ответы на вопросы по установке и использованию переводов, а также на любые другие вопросы по iTop всегда можно получить на сайте сообщества iTop по-русски http://community.itop-itsm.ru.
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// 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('RU RU', 'Russian', 'Русский', array(
'Class:UserLocal' => 'Пользователь iTop',
'Class:UserLocal+' => 'Пользователь, аутентифицированный в iTop',
'Class:UserLocal+' => 'Пользователь, аутентифицируемый через iTop',
'Class:UserLocal/Attribute:password' => 'Пароль',
'Class:UserLocal/Attribute:password+' => 'Строка аутентификации пользователя',
));
?>
));

View File

@@ -0,0 +1,27 @@
<?php
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Attachments:TabTitle_Count' => 'Вложения (%1$d)',
'Attachments:EmptyTabTitle' => 'Вложения',
'Attachments:FieldsetTitle' => 'Вложения',
'Attachments:DeleteBtn' => 'Удалить',
'Attachments:History_File_Added' => 'Вложение %1$s добавлено.',
'Attachments:History_File_Removed' => 'Вложение %1$s удалено.',
'Attachments:AddAttachment' => 'Добавить вложение:',
'Attachments:UploadNotAllowedOnThisSystem' => 'Загрузка файлов НЕ разрешена в этой системе. За подробностями обратитесь к администратору вашего iTop',
'Attachment:Max_Go' => '(Максимальный размер файла: %1$s ГБ)',
'Attachment:Max_Mo' => '(Максимальный размер файла: %1$s МБ)',
'Attachment:Max_Ko' => '(Максимальный размер файла: %1$s кБ)',
'Attachments:NoAttachment' => 'Нет вложений.',
'Attachments:PreviewNotAvailable' => 'Предварительный просмотр не доступен для этого типа вложений.',
));

View File

@@ -244,6 +244,7 @@ class BackupExec implements iScheduledProcess
{
break;
}
$iNextPos = false; // necessary on sundays
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
Dict::Add('RU RU', 'Russian', 'Русский', array(
'bkp-backup-running' => 'Выполняется резервное копирование. Пожалуйста, подождите...',
'bkp-restore-running' => 'Выполняется восстановление из резервной копии. Пожалуйста, подождите...',
'Menu:BackupStatus' => 'Резервное копирование',
'bkp-status-title' => 'Резервное копирование по расписанию',
'bkp-status-checks' => 'Настройки и проверки',
'bkp-mysqldump-ok' => 'Утилита mysqldump найдена: %1$s',
'bkp-mysqldump-notfound' => 'Утилиту mysqldump найти не удалось: %1$s - пожалуйста, убедитесь в том, что она установлена, и путь до директории с бинарными файлами добавлен в PATH, либо измените параметр mysql_bindir в файле конфигурации.',
'bkp-mysqldump-issue' => 'Утилита mysqldump на может быть запущена (retcode=%1$d) Пожалуйста, убедитесь в том, что она установлена, и путь до директории с бинарными файлами добавлен в PATH, либо измените параметр mysql_bindir в файле конфигурации.',
'bkp-missing-dir' => 'The target directory %1$s count not be found',
'bkp-free-disk-space' => '<b>%1$s свободно</b> в %2$s',
'bkp-dir-not-writeable' => '%1$s недоступен для записи',
'bkp-wrong-format-spec' => 'Неправильный формат шаблона названия файлов резервных копий (%1$s). Будет использован шаблон по умолчанию: %2$s',
'bkp-name-sample' => 'Название файлов резервных копий зависит от идентификатора БД, даты и времени. Пример: %1$s',
'bkp-week-days' => 'Резервное копирование будет выполняться <b>каждый %1$s в %2$s</b>',
'bkp-retention' => 'Не более <b>%1$d файлов резервных копий будут храниться</b> в целевом каталоге.',
'bkp-next-to-delete' => 'Будет удалена при следующем запуске резервного копирования (см. параметр \"retention_count\")',
'bkp-table-file' => 'Файл',
'bkp-table-file+' => 'Только файлы с расширением .zip считаются файлами резервных копий.',
'bkp-table-size' => 'Размер',
'bkp-table-size+' => '',
'bkp-table-actions' => 'Действия',
'bkp-table-actions+' => '',
'bkp-status-backups-auto' => 'Резервное копирование по расписанию',
'bkp-status-backups-manual' => 'Резервное копирование вручную',
'bkp-status-backups-none' => 'Резервных копий ещё нет',
'bkp-next-backup' => 'Следующее резервное копирование будет выполняться в <b>%1$s</b> (%2$s) в %3$s',
'bkp-button-backup-now' => 'Запустить сейчас!',
'bkp-button-restore-now' => 'Восстановить!',
'bkp-confirm-backup' => 'Пожалуйста, подтвердите, что вы хотите выполнить резервное копирование прямо сейчас.',
'bkp-confirm-restore' => 'Пожалуйста, подтвердите, что вы хотите выполнить восстановление из резервной копии %1$s.',
'bkp-wait-backup' => 'Пожалуйста, дождитесь завершения резервного копирования...',
'bkp-wait-restore' => 'Пожалуйста, дождитесь завершения восстановления...',
'bkp-success-restore' => 'Восстановление успешно завершено.',
));
?>

View File

@@ -2,45 +2,39 @@
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @license http://opensource.org/licenses/AGPL-3.0
*
*
* Инструкция по установке
*
* Процесс установки заключается в замене имеющихся локализационных файлов полученными и последующем запуске процедуры обновления iTop для перекомпиляции кода.
* 1. Скопируйте с заменой два полученных файла из "itop-rus/dictionaries" в "путь/до/вашего/itop/dictionaries".
* 2. Скопируйте с заменой полученные файлы "itop-rus/datamodels/2.x/название-модуля/ru.dict.название-модуля.php" в "путь/до/вашего/itop/datamodels/2.x/название-модуля".
* 3. Перейдите по адресу "http://адрес/вашего/itop/setup", при этом файл "путь/до/вашего/itop/conf/production/config-itop.php" должен быть доступен для записи.
* 4. На второй странице установщика выберите "Upgrade an existing iTop instance" и следуйте дальнейшим инструкциям установщика.
*
* Ответы на вопросы по установке и использованию переводов, а также на любые другие вопросы по iTop всегда можно получить на сайте сообщества iTop по-русски http://community.itop-itsm.ru.
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Menu:ChangeManagement' => 'Управление изменениями',
'Menu:Change:Overview' => 'Обзор',
'Menu:Change:Overview+' => '',
'Menu:Change:Overview+' => 'Управление изменениями - Обзор',
'Menu:NewChange' => 'Новый запрос на изменение',
'Menu:NewChange+' => 'Создание нового запроса на изменение',
'Menu:SearchChanges' => 'Поиск изменений',
'Menu:SearchChanges+' => 'Поиск запросов на изменение',
'Menu:Change:Shortcuts' => 'Ярлыки',
'Menu:Change:Shortcuts+' => '',
'Menu:WaitingAcceptance' => 'Изменения, ожидающие принятия',
'Menu:WaitingAcceptance+' => '',
'Menu:WaitingApproval' => 'Изменения, ожидающие утверждения',
'Menu:WaitingApproval+' => '',
'Menu:Changes' => 'Открытые изменения',
'Menu:Change:Shortcuts+' => 'Ярлыки',
'Menu:WaitingAcceptance' => 'Ожидающие принятия',
'Menu:WaitingAcceptance+' => 'Изменения, ожидающие принятия',
'Menu:WaitingApproval' => 'Ожидающие утверждения',
'Menu:WaitingApproval+' => 'Изменения, ожидающие утверждения',
'Menu:Changes' => 'Открытые',
'Menu:Changes+' => 'Все открытые изменения',
'Menu:MyChanges' => 'Изменения, назначенные на меня',
'Menu:MyChanges+' => 'Изменения, назначенные на меня (как агента)',
'Menu:MyChanges' => 'Назначенные мне',
'Menu:MyChanges+' => 'Изменения, назначенные мне',
'UI-ChangeManagementOverview-ChangeByCategory-last-7-days' => 'Изменения по категориям за 7 дней',
'UI-ChangeManagementOverview-Last-7-days' => 'Количество изменений за 7 дней',
'UI-ChangeManagementOverview-ChangeByDomain-last-7-days' => 'Изменения по домену за 7 дней',
'UI-ChangeManagementOverview-ChangeByStatus-last-7-days' => 'Изменения по статусу за 7 дней',
'Tickets:Related:OpenChanges' => 'Открытые изменения',
'Tickets:Related:RecentChanges' => 'Недавние изменения (72ч)',
));
// Dictionnay conventions
@@ -89,7 +83,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:Change/Attribute:requestor_id+' => '',
'Class:Change/Attribute:requestor_email' => 'Email инициатора запроса',
'Class:Change/Attribute:requestor_email+' => '',
'Class:Change/Attribute:creation_date' => 'Создан',
'Class:Change/Attribute:creation_date' => 'Дата создания',
'Class:Change/Attribute:creation_date+' => '',
'Class:Change/Attribute:impact' => 'Критичность',
'Class:Change/Attribute:impact+' => '',
@@ -119,7 +113,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:Change/Attribute:fallback+' => '',
'Class:Change/Attribute:parent_id' => 'Родительское изменение',
'Class:Change/Attribute:parent_id+' => '',
'Class:Change/Attribute:parent_name' => 'Имя родительского изменения',
'Class:Change/Attribute:parent_name' => 'Родительское изменение',
'Class:Change/Attribute:parent_name+' => '',
'Class:Change/Attribute:related_request_list' => 'Связанные запросы',
'Class:Change/Attribute:related_request_list+' => 'Связанные запросы',
@@ -197,7 +191,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:ApprovedChange+' => '',
'Class:ApprovedChange/Attribute:approval_date' => 'Дата утверждения',
'Class:ApprovedChange/Attribute:approval_date+' => '',
'Class:ApprovedChange/Attribute:approval_comment' => 'Коментарий утверждения',
'Class:ApprovedChange/Attribute:approval_comment' => 'Комментарий утверждения',
'Class:ApprovedChange/Attribute:approval_comment+' => '',
'Class:ApprovedChange/Stimulus:ev_validate' => 'Подтвердить',
'Class:ApprovedChange/Stimulus:ev_validate+' => '',
@@ -213,7 +207,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:ApprovedChange/Stimulus:ev_approve+' => '',
'Class:ApprovedChange/Stimulus:ev_replan' => 'Перепланировать',
'Class:ApprovedChange/Stimulus:ev_replan+' => '',
'Class:ApprovedChange/Stimulus:ev_notapprove' => 'Отклонить утверждение',
'Class:ApprovedChange/Stimulus:ev_notapprove' => 'Отклонить',
'Class:ApprovedChange/Stimulus:ev_notapprove+' => '',
'Class:ApprovedChange/Stimulus:ev_implement' => 'Реализовать',
'Class:ApprovedChange/Stimulus:ev_implement+' => '',
@@ -287,8 +281,4 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:EmergencyChange/Stimulus:ev_monitor+' => '',
'Class:EmergencyChange/Stimulus:ev_finish' => 'Закончить',
'Class:EmergencyChange/Stimulus:ev_finish+' => '',
'Tickets:Related:OpenChanges' => 'Open changes~~',
'Tickets:Related:RecentChanges' => 'Recent changes (72h)~~',
));
?>
));

View File

@@ -0,0 +1,127 @@
<?php
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Menu:ChangeManagement' => 'Управление изменениями',
'Menu:Change:Overview' => 'Обзор',
'Menu:Change:Overview+' => 'Управление изменениями - Обзор',
'Menu:NewChange' => 'Новый запрос на изменение',
'Menu:NewChange+' => 'Создание нового запроса на изменение',
'Menu:SearchChanges' => 'Поиск изменений',
'Menu:SearchChanges+' => 'Поиск запросов на изменения',
'Menu:Change:Shortcuts' => 'Ярлыки',
'Menu:Change:Shortcuts+' => 'Ярлыки',
'Menu:WaitingAcceptance' => 'Ожидающие принятия',
'Menu:WaitingAcceptance+' => 'Изменения, ожидающие принятия',
'Menu:WaitingApproval' => 'Ожидающие утверждения',
'Menu:WaitingApproval+' => 'Изменения, ожидающие утверждения',
'Menu:Changes' => 'Открытые',
'Menu:Changes+' => 'Открытые изменения',
'Menu:MyChanges' => 'Назначенные мне',
'Menu:MyChanges+' => 'Изменения, назначенные мне',
'UI-ChangeManagementOverview-ChangeByCategory-last-7-days' => 'Изменения по категориям за 7 дней',
'UI-ChangeManagementOverview-Last-7-days' => 'Количество изменений за 7 дней',
'UI-ChangeManagementOverview-ChangeByDomain-last-7-days' => 'Изменения по домену за 7 дней',
'UI-ChangeManagementOverview-ChangeByStatus-last-7-days' => 'Изменения по статусу за 7 дней',
));
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// 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: Change
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:Change' => 'Изменение',
'Class:Change+' => '',
'Class:Change/Attribute:status' => 'Статус',
'Class:Change/Attribute:status+' => '',
'Class:Change/Attribute:status/Value:new' => 'Новый',
'Class:Change/Attribute:status/Value:new+' => '',
'Class:Change/Attribute:status/Value:assigned' => 'Назначен',
'Class:Change/Attribute:status/Value:assigned+' => '',
'Class:Change/Attribute:status/Value:planned' => 'Запланировано',
'Class:Change/Attribute:status/Value:planned+' => '',
'Class:Change/Attribute:status/Value:rejected' => 'Отклонён',
'Class:Change/Attribute:status/Value:rejected+' => '',
'Class:Change/Attribute:status/Value:approved' => 'Утверждён',
'Class:Change/Attribute:status/Value:approved+' => '',
'Class:Change/Attribute:status/Value:closed' => 'Закрыт',
'Class:Change/Attribute:status/Value:closed+' => '',
'Class:Change/Attribute:category' => 'Категория',
'Class:Change/Attribute:category+' => '',
'Class:Change/Attribute:category/Value:application' => 'Приложение',
'Class:Change/Attribute:category/Value:application+' => 'Приложение',
'Class:Change/Attribute:category/Value:hardware' => 'Оборудование',
'Class:Change/Attribute:category/Value:hardware+' => 'Оборудование',
'Class:Change/Attribute:category/Value:network' => 'Сеть',
'Class:Change/Attribute:category/Value:network+' => 'Сеть',
'Class:Change/Attribute:category/Value:other' => 'Другое',
'Class:Change/Attribute:category/Value:other+' => 'Другое',
'Class:Change/Attribute:category/Value:software' => 'Программное обеспечение',
'Class:Change/Attribute:category/Value:software+' => 'Программное обеспечение',
'Class:Change/Attribute:category/Value:system' => 'Система',
'Class:Change/Attribute:category/Value:system+' => 'Система',
'Class:Change/Attribute:reject_reason' => 'Причина отклонения',
'Class:Change/Attribute:reject_reason+' => '',
'Class:Change/Attribute:changemanager_id' => 'Менеджер изменения',
'Class:Change/Attribute:changemanager_id+' => '',
'Class:Change/Attribute:changemanager_email' => 'Email менеджера',
'Class:Change/Attribute:changemanager_email+' => '',
'Class:Change/Attribute:parent_id' => 'Родительское изменение',
'Class:Change/Attribute:parent_id+' => '',
'Class:Change/Attribute:parent_name' => 'Родительское изменение',
'Class:Change/Attribute:parent_name+' => '',
'Class:Change/Attribute:creation_date' => 'Дата создания',
'Class:Change/Attribute:creation_date+' => '',
'Class:Change/Attribute:approval_date' => 'Дата утверждения',
'Class:Change/Attribute:approval_date+' => '',
'Class:Change/Attribute:fallback_plan' => 'План отката',
'Class:Change/Attribute:fallback_plan+' => '',
'Class:Change/Attribute:related_request_list' => 'Связанные запросы',
'Class:Change/Attribute:related_request_list+' => 'Связанные запросы',
'Class:Change/Attribute:related_incident_list' => 'Связанные инциденты',
'Class:Change/Attribute:related_incident_list+' => 'Связанные инциденты',
'Class:Change/Attribute:related_problems_list' => 'Связанные проблемы',
'Class:Change/Attribute:related_problems_list+' => 'Связанные проблемы',
'Class:Change/Attribute:child_changes_list' => 'Дочерние изменения',
'Class:Change/Attribute:child_changes_list+' => 'Дочерние изменения',
'Class:Change/Attribute:parent_id_friendlyname' => 'Родительское изменение',
'Class:Change/Attribute:parent_id_friendlyname+' => '',
'Class:Change/Stimulus:ev_assign' => 'Назначить',
'Class:Change/Stimulus:ev_assign+' => '',
'Class:Change/Stimulus:ev_plan' => 'Планировать',
'Class:Change/Stimulus:ev_plan+' => '',
'Class:Change/Stimulus:ev_reject' => 'Отклонить',
'Class:Change/Stimulus:ev_reject+' => '',
'Class:Change/Stimulus:ev_reopen' => 'Вновь открыть',
'Class:Change/Stimulus:ev_reopen+' => '',
'Class:Change/Stimulus:ev_approve' => 'Утвердить',
'Class:Change/Stimulus:ev_approve+' => '',
'Class:Change/Stimulus:ev_finish' => 'Закрыть',
'Class:Change/Stimulus:ev_finish+' => '',
'Class:Change/Attribute:outage' => 'Простой услуги',
'Class:Change/Attribute:outage+' => '',
'Class:Change/Attribute:outage/Value:no' => 'Нет',
'Class:Change/Attribute:outage/Value:no+' => '',
'Class:Change/Attribute:outage/Value:yes' => 'Да',
'Class:Change/Attribute:outage/Value:yes+' => '',
));

View File

@@ -552,7 +552,7 @@
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[ public function DBDeleteSingleObject(&$oDeletionPlan)
<code><![CDATA[ public function DBDeleteSingleObject()
{
if (MetaModel::GetConfig()->Get('demo_mode'))
{
@@ -1582,7 +1582,8 @@
$sStateAttCode = MetaModel::GetStateAttributeCode($sSubClass);
if ($sStateAttCode != '')
{
$oSearch = DBSearch::FromOQL("SELECT $sSubClass AS t JOIN $sLnkClass AS lnk ON lnk.$sExtKeyToRemote = t.id WHERE $sExtKeyToMe = :myself AND $sStateAttCode NOT IN ('rejected', 'resolved', 'closed')", array('myself' => $this->GetKey()));
// Todo: base the search condition on operational_status = 'ongoing' for a more flexible behavior
$oSearch = DBSearch::FromOQL("SELECT $sSubClass AS t JOIN $sLnkClass AS lnk ON lnk.$sExtKeyToRemote = t.id WHERE lnk.$sExtKeyToMe = :myself AND t.$sStateAttCode NOT IN ('rejected', 'resolved', 'closed')", array('myself' => $this->GetKey()));
$aSearches[$sSubClass] = $oSearch;
$oSet = new DBObjectSet($oSearch);

File diff suppressed because it is too large Load Diff

View File

@@ -105,7 +105,7 @@ try
{
$oP->add("<div class=\"header_message message_info\">Sorry, iTop is in <b>demonstration mode</b>: the configuration file cannot be edited.</div>");
}
if (MetaModel::GetModuleSetting('itop-config', 'config_editor', '') == 'disabled')
else if (MetaModel::GetModuleSetting('itop-config', 'config_editor', '') == 'disabled')
{
$oP->add("<div class=\"header_message message_info\">iTop interactive edition of the configuration as been disabled. See <tt>'config_editor' => 'disabled'</tt> in the configuration file.</div>");
}

View File

@@ -11,7 +11,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Menu:ConfigEditor' => 'Konfiguration',
'config-edit-title' => 'Konfigurations-Editor',
'config-edit-intro' => 'Seien sie bei der Bearbeitung der Konfigurationsdatei sehr vorsichtig. Normalerweise sollten es ausreichen die Einträge im oberen Teil der Konfiguration zu bearbeiten (Bsp.: die globalen Konfigurations- und Moduleinstellungen).',
'config-edit-intro' => 'Seien sie bei der Bearbeitung der Konfigurationsdatei sehr vorsichtig.',
'config-apply' => 'Anwenden',
'config-cancel' => 'Zurücksetzen',
'config-confirm-cancel' => 'Ihre Änderungen werden nicht gespeichert.',

View File

@@ -10,7 +10,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Menu:ConfigEditor' => 'Configuration',
'config-edit-title' => 'Configuration File Editor',
'config-edit-intro' => 'Be very cautious when editing the configuration file. In particular, only the upper items (i.e. the global configuration and modules settings) should be edited.',
'config-edit-intro' => 'Be very cautious when editing the configuration file.',
'config-apply' => 'Apply',
'config-cancel' => 'Reset',
'config-confirm-cancel' => 'Your changes will be lost.',

View File

@@ -10,7 +10,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Menu:ConfigEditor' => 'Configuration',
'config-edit-title' => 'Editeur du Fichier de Configuration',
'config-edit-intro' => 'Attention: une configuration incorrecte peut rendre iTop indisponible. En particulier, vous ne devriez éditer QUE les deux premiers éléments, à savoir la configuration globale et la configuration des modules.',
'config-edit-intro' => 'Attention: une configuration incorrecte peut rendre iTop inopérant pour tous les utilisateurs!',
'config-save' => 'Appliquer',
'config-restore' => 'Réinitialiser',
'config-confirm-cancel' => 'Vos modifications seront perdues.',

View File

@@ -0,0 +1,25 @@
<?php
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Menu:ConfigEditor' => 'Конфигурация',
'config-edit-title' => 'Редактор файла конфигурации',
'config-edit-intro' => 'Будьте очень осторожны при редактировании файла конфигурации. В частности, могут быть отредактированы только верхние элементы (т.е. глобальная конфигурация и настройки модулей).',
'config-apply' => 'Применить',
'config-cancel' => 'Сбросить',
'config-confirm-cancel' => 'Ваши изменения будут утеряны.',
'config-no-change' => 'Изменений нет: файл не был изменён.',
'config-parse-error' => 'Строка %2$d: %1$s.<br/>Файл не был обновлен.',
'config-current-line' => 'Редактируемая строка: %1$s',
));
?>

View File

@@ -1229,7 +1229,7 @@
if (!MetaModel::IsValidClass('UserRequest')) return true; // Do nothing
$oLog = $this->Get('public_log');
$sLogPublic = $oLog->GetModifiedEntry();
$sLogPublic = $oLog->GetModifiedEntry('html');
if ($sLogPublic != '')
{
$sOQL = "SELECT UserRequest WHERE parent_incident_id=:ticket";
@@ -1247,7 +1247,7 @@
}
$oLog = $this->Get('private_log');
$sLogPrivate = $oLog->GetModifiedEntry();
$sLogPrivate = $oLog->GetModifiedEntry('html');
if ($sLogPrivate != '')
{
$sOQL = "SELECT UserRequest WHERE parent_incident_id=:ticket";
@@ -1274,7 +1274,7 @@
<code><![CDATA[ public function UpdateChildIncidentLog()
{
$oLog = $this->Get('public_log');
$sLogPublic = $oLog->GetModifiedEntry();
$sLogPublic = $oLog->GetModifiedEntry('html');
if ($sLogPublic != '')
{
$sOQL = "SELECT Incident WHERE parent_incident_id=:ticket";
@@ -1292,7 +1292,7 @@
}
$oLog = $this->Get('private_log');
$sLogPrivate = $oLog->GetModifiedEntry();
$sLogPrivate = $oLog->GetModifiedEntry('html');
if ($sLogPrivate != '')
{
$sOQL = "SELECT Incident WHERE parent_incident_id=:ticket";

View File

@@ -0,0 +1,217 @@
<?php
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Menu:IncidentManagement' => 'Управление инцидентами',
'Menu:IncidentManagement+' => 'Управление инцидентами',
'Menu:Incident:Overview' => 'Обзор',
'Menu:Incident:Overview+' => 'Обзор',
'Menu:NewIncident' => 'Новый инцидент',
'Menu:NewIncident+' => 'Создать новый инцидент',
'Menu:SearchIncidents' => 'Поиск инцидентов',
'Menu:SearchIncidents+' => 'Поиск инцидентов',
'Menu:Incident:Shortcuts' => 'Ярлыки',
'Menu:Incident:Shortcuts+' => 'Ярлыки',
'Menu:Incident:MyIncidents' => 'Назначенные мне',
'Menu:Incident:MyIncidents+' => 'Инциденты, назначенные мне (в качестве агента)',
'Menu:Incident:EscalatedIncidents' => 'Эскалированные',
'Menu:Incident:EscalatedIncidents+' => 'Эскалированные инциденты',
'Menu:Incident:OpenIncidents' => 'Открытые',
'Menu:Incident:OpenIncidents+' => 'Открытые инциденты',
'UI-IncidentManagementOverview-IncidentByPriority-last-14-days' => 'Инциденты по приоритету за 14 дней',
'UI-IncidentManagementOverview-Last-14-days' => 'Количество инцидентов за 14 дней',
'UI-IncidentManagementOverview-OpenIncidentByStatus' => 'Открытые инциденты по статусу',
'UI-IncidentManagementOverview-OpenIncidentByAgent' => 'Открытые инциденты по агенту',
'UI-IncidentManagementOverview-OpenIncidentByCustomer' => 'Открытые инциденты по заказчику',
));
//
// Class: Incident
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:Incident' => 'Инцидент',
'Class:Incident+' => '',
'Class:Incident/Attribute:status' => 'Статус',
'Class:Incident/Attribute:status+' => '',
'Class:Incident/Attribute:status/Value:new' => 'Новый',
'Class:Incident/Attribute:status/Value:new+' => '',
'Class:Incident/Attribute:status/Value:escalated_tto' => 'Эскалация TTO',
'Class:Incident/Attribute:status/Value:escalated_tto+' => '',
'Class:Incident/Attribute:status/Value:assigned' => 'Назначен',
'Class:Incident/Attribute:status/Value:assigned+' => '',
'Class:Incident/Attribute:status/Value:escalated_ttr' => 'Эскалация TTR',
'Class:Incident/Attribute:status/Value:escalated_ttr+' => '',
'Class:Incident/Attribute:status/Value:waiting_for_approval' => 'Ожидание утверждения',
'Class:Incident/Attribute:status/Value:waiting_for_approval+' => '',
'Class:Incident/Attribute:status/Value:pending' => 'В ожидании',
'Class:Incident/Attribute:status/Value:pending+' => '',
'Class:Incident/Attribute:status/Value:resolved' => 'Решенный',
'Class:Incident/Attribute:status/Value:resolved+' => '',
'Class:Incident/Attribute:status/Value:closed' => 'Закрыт',
'Class:Incident/Attribute:status/Value:closed+' => '',
'Class:Incident/Attribute:impact' => 'Влияние',
'Class:Incident/Attribute:impact+' => '',
'Class:Incident/Attribute:impact/Value:1' => 'Департамент',
'Class:Incident/Attribute:impact/Value:1+' => '',
'Class:Incident/Attribute:impact/Value:2' => 'Служба',
'Class:Incident/Attribute:impact/Value:2+' => '',
'Class:Incident/Attribute:impact/Value:3' => 'Персона',
'Class:Incident/Attribute:impact/Value:3+' => '',
'Class:Incident/Attribute:priority' => 'Приоритет',
'Class:Incident/Attribute:priority+' => '',
'Class:Incident/Attribute:priority/Value:1' => 'Критический',
'Class:Incident/Attribute:priority/Value:1+' => 'Критический',
'Class:Incident/Attribute:priority/Value:2' => 'Высокий',
'Class:Incident/Attribute:priority/Value:2+' => 'Высокий',
'Class:Incident/Attribute:priority/Value:3' => 'Средний',
'Class:Incident/Attribute:priority/Value:3+' => 'Средний',
'Class:Incident/Attribute:priority/Value:4' => 'Низкий',
'Class:Incident/Attribute:priority/Value:4+' => 'Низкий',
'Class:Incident/Attribute:urgency' => 'Срочность',
'Class:Incident/Attribute:urgency+' => '',
'Class:Incident/Attribute:urgency/Value:1' => 'Критическая',
'Class:Incident/Attribute:urgency/Value:1+' => 'Критическая',
'Class:Incident/Attribute:urgency/Value:2' => 'Высокая',
'Class:Incident/Attribute:urgency/Value:2+' => 'Высокая',
'Class:Incident/Attribute:urgency/Value:3' => 'Средняя',
'Class:Incident/Attribute:urgency/Value:3+' => 'Средняя',
'Class:Incident/Attribute:urgency/Value:4' => 'Низкая',
'Class:Incident/Attribute:urgency/Value:4+' => 'Низкая',
'Class:Incident/Attribute:origin' => 'Источник',
'Class:Incident/Attribute:origin+' => '',
'Class:Incident/Attribute:origin/Value:mail' => 'Почта',
'Class:Incident/Attribute:origin/Value:mail+' => 'Почта',
'Class:Incident/Attribute:origin/Value:monitoring' => 'Мониторинг',
'Class:Incident/Attribute:origin/Value:monitoring+' => 'Мониторинг',
'Class:Incident/Attribute:origin/Value:phone' => 'Телефон',
'Class:Incident/Attribute:origin/Value:phone+' => 'Телефон',
'Class:Incident/Attribute:origin/Value:portal' => 'Портал',
'Class:Incident/Attribute:origin/Value:portal+' => 'Портал',
'Class:Incident/Attribute:service_id' => 'Услуга',
'Class:Incident/Attribute:service_id+' => '',
'Class:Incident/Attribute:service_name' => 'Услуга',
'Class:Incident/Attribute:service_name+' => '',
'Class:Incident/Attribute:servicesubcategory_id' => 'Подкатегория',
'Class:Incident/Attribute:servicesubcategory_id+' => 'Подкатегория услуги',
'Class:Incident/Attribute:servicesubcategory_name' => 'Подкатегория услуги',
'Class:Incident/Attribute:servicesubcategory_name+' => '',
'Class:Incident/Attribute:escalation_flag' => '«Флаг эскалации',
'Class:Incident/Attribute:escalation_flag+' => 'Флаг повышенного приоритета',
'Class:Incident/Attribute:escalation_flag/Value:no' => 'Нет',
'Class:Incident/Attribute:escalation_flag/Value:no+' => 'Нет',
'Class:Incident/Attribute:escalation_flag/Value:yes' => 'Да',
'Class:Incident/Attribute:escalation_flag/Value:yes+' => 'Да',
'Class:Incident/Attribute:escalation_reason' => 'Причина эскалации',
'Class:Incident/Attribute:escalation_reason+' => '',
'Class:Incident/Attribute:assignment_date' => 'Дата назначения',
'Class:Incident/Attribute:assignment_date+' => '',
'Class:Incident/Attribute:resolution_date' => 'Дата решения',
'Class:Incident/Attribute:resolution_date+' => '',
'Class:Incident/Attribute:last_pending_date' => 'Дата последнего ожидания',
'Class:Incident/Attribute:last_pending_date+' => '',
'Class:Incident/Attribute:cumulatedpending' => 'Накопленное ожидание',
'Class:Incident/Attribute:cumulatedpending+' => '',
'Class:Incident/Attribute:tto' => 'TTO',
'Class:Incident/Attribute:tto+' => '',
'Class:Incident/Attribute:ttr' => 'TTR',
'Class:Incident/Attribute:ttr+' => '',
'Class:Incident/Attribute:tto_escalation_deadline' => 'Срок TTO',
'Class:Incident/Attribute:tto_escalation_deadline+' => 'Крайний срок назаначения агента (принятия в работу) по текущему SLA',
'Class:Incident/Attribute:sla_tto_passed' => 'SLA TTO пропущено',
'Class:Incident/Attribute:sla_tto_passed+' => '',
'Class:Incident/Attribute:sla_tto_over' => 'SLA TTO превышено',
'Class:Incident/Attribute:sla_tto_over+' => '',
'Class:Incident/Attribute:ttr_escalation_deadline' => 'Срок TTR',
'Class:Incident/Attribute:ttr_escalation_deadline+' => 'Крайний срок решения по текущему SLA',
'Class:Incident/Attribute:sla_ttr_passed' => 'SLA TTR пропущено',
'Class:Incident/Attribute:sla_ttr_passed+' => '',
'Class:Incident/Attribute:sla_ttr_over' => 'SLA TTR превышено',
'Class:Incident/Attribute:sla_ttr_over+' => '',
'Class:Incident/Attribute:time_spent' => 'Время на решение',
'Class:Incident/Attribute:time_spent+' => '',
'Class:Incident/Attribute:resolution_code' => 'Код решения',
'Class:Incident/Attribute:resolution_code+' => '',
'Class:Incident/Attribute:resolution_code/Value:assistance' => 'Помощь',
'Class:Incident/Attribute:resolution_code/Value:assistance+' => 'Помощь',
'Class:Incident/Attribute:resolution_code/Value:bug fixed' => 'Исправление ошибки',
'Class:Incident/Attribute:resolution_code/Value:bug fixed+' => 'Исправление ошибки',
'Class:Incident/Attribute:resolution_code/Value:hardware repair' => 'Ремонт оборудования',
'Class:Incident/Attribute:resolution_code/Value:hardware repair+' => 'Ремонт оборудования',
'Class:Incident/Attribute:resolution_code/Value:other' => 'Другое',
'Class:Incident/Attribute:resolution_code/Value:other+' => 'Другое',
'Class:Incident/Attribute:resolution_code/Value:software patch' => 'Патч ПО',
'Class:Incident/Attribute:resolution_code/Value:software patch+' => 'Патч ПО',
'Class:Incident/Attribute:resolution_code/Value:system update' => 'Обновление системы',
'Class:Incident/Attribute:resolution_code/Value:system update+' => 'Обновление системы',
'Class:Incident/Attribute:resolution_code/Value:training' => 'Обучение',
'Class:Incident/Attribute:resolution_code/Value:training+' => 'Обучение',
'Class:Incident/Attribute:solution' => 'Описание решения',
'Class:Incident/Attribute:solution+' => '',
'Class:Incident/Attribute:pending_reason' => 'Причина ожидания',
'Class:Incident/Attribute:pending_reason+' => '',
'Class:Incident/Attribute:parent_incident_id' => 'Родительский инцидент',
'Class:Incident/Attribute:parent_incident_id+' => '',
'Class:Incident/Attribute:parent_problem_id' => 'Родительская проблема',
'Class:Incident/Attribute:parent_problem_id+' => '',
'Class:Incident/Attribute:parent_problem_ref' => 'Родительская проблема',
'Class:Incident/Attribute:parent_problem_ref+' => '',
'Class:Incident/Attribute:parent_incident_ref' => 'Родительский инцидент',
'Class:Incident/Attribute:parent_incident_ref+' => '',
'Class:Incident/Attribute:parent_change_id' => 'Родительское изменение',
'Class:Incident/Attribute:parent_change_id+' => '',
'Class:Incident/Attribute:parent_change_ref' => 'Родительское изменение',
'Class:Incident/Attribute:parent_change_ref+' => '',
'Class:Incident/Attribute:child_incidents_list' => 'Дочерние инциденты',
'Class:Incident/Attribute:child_incidents_list+' => 'Все инциденты, связанные с этим инцидентом',
'Class:Incident/Attribute:related_request_list' => 'Запросы',
'Class:Incident/Attribute:related_request_list+' => 'Все пользовательские запросы, связанные с этим инцидентом',
'Class:Incident/Attribute:public_log' => 'Общий журнал',
'Class:Incident/Attribute:public_log+' => 'Информация в общем журнале доступна для пользователей портала',
'Class:Incident/Attribute:user_satisfaction' => 'Удовлетворенность пользователя',
'Class:Incident/Attribute:user_satisfaction+' => '',
'Class:Incident/Attribute:user_satisfaction/Value:1' => 'Очень доволен',
'Class:Incident/Attribute:user_satisfaction/Value:1+' => 'Очень доволен',
'Class:Incident/Attribute:user_satisfaction/Value:2' => 'Вполне доволен',
'Class:Incident/Attribute:user_satisfaction/Value:2+' => 'Вполне доволен',
'Class:Incident/Attribute:user_satisfaction/Value:3' => 'Скорее недоволен',
'Class:Incident/Attribute:user_satisfaction/Value:3+' => 'Скорее недоволен',
'Class:Incident/Attribute:user_satisfaction/Value:4' => 'Очень недоволен',
'Class:Incident/Attribute:user_satisfaction/Value:4+' => 'Очень недоволен',
'Class:Incident/Attribute:user_comment' => 'Комментарий пользователя',
'Class:Incident/Attribute:user_comment+' => '',
'Class:Incident/Attribute:parent_incident_id_friendlyname' => 'Родительский инцидент',
'Class:Incident/Attribute:parent_incident_id_friendlyname+' => '',
'Class:Incident/Stimulus:ev_assign' => 'Назначить',
'Class:Incident/Stimulus:ev_assign+' => '',
'Class:Incident/Stimulus:ev_reassign' => 'Переназначить',
'Class:Incident/Stimulus:ev_reassign+' => '',
'Class:Incident/Stimulus:ev_pending' => 'В ожидание',
'Class:Incident/Stimulus:ev_pending+' => '',
'Class:Incident/Stimulus:ev_timeout' => 'Таймаут',
'Class:Incident/Stimulus:ev_timeout+' => '',
'Class:Incident/Stimulus:ev_autoresolve' => 'Автоматическое решение',
'Class:Incident/Stimulus:ev_autoresolve+' => '',
'Class:Incident/Stimulus:ev_autoclose' => 'Автоматическое закрытие',
'Class:Incident/Stimulus:ev_autoclose+' => '',
'Class:Incident/Stimulus:ev_resolve' => 'Отметить как решенный',
'Class:Incident/Stimulus:ev_resolve+' => '',
'Class:Incident/Stimulus:ev_close' => 'Закрыть',
'Class:Incident/Stimulus:ev_close+' => '',
'Class:Incident/Stimulus:ev_reopen' => 'Вновь открыть',
'Class:Incident/Stimulus:ev_reopen+' => '',
'Class:Incident/Error:CannotAssignParentIncidentIdToSelf' => 'Невозможно назначить этот же инцидент в качестве родительского',
'Class:Incident/Method:ResolveChildTickets' => 'ResolveChildTickets',
'Class:Incident/Method:ResolveChildTickets+' => 'Cascade the resolution to child ticket (ev_autoresolve), and align the following characteristics: service, team, agent, resolution info',
'Tickets:Related:OpenIncidents' => 'Открытые инциденты',
));

View File

@@ -622,7 +622,7 @@
<parent_att/>
<name_att/>
<tooltip_att/>
<title>Catégories</title>
<title>Class:FAQCategory</title>
<actions/>
<levels>
<level id="1">
@@ -630,7 +630,7 @@
<parent_att>category_id</parent_att>
<name_att>title</name_att>
<tooltip_att>summary</tooltip_att>
<title>FAQs</title>
<title>Class:FAQ</title>
<fields>
<field id="error_code"/>
<field id="key_words">

View File

@@ -125,5 +125,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:lnkDocumentToError/Attribute:error_name+' => '',
'Class:FAQ/Attribute:category_name' => 'Kategoriename',
'Class:FAQ/Attribute:category_name+' => '',
'Brick:Portal:FAQ:Menu' => 'FAQ',
'Brick:Portal:FAQ:Title' => 'Oft gestellte Fragen (FAQs)',
'Brick:Portal:FAQ:Title+' => '<p>In Eile?</p><p>Sehen Sie sich die meistgestellten Fragen an (FAQs) und finden Sie (eventuell) die Antwort direkt dort.</p>',
));
?>

View File

@@ -187,5 +187,8 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Menu:FAQ' => 'Preguntas y Respuestas Frecuentes',
'Menu:FAQ+' => 'Preguntas y Respuestas Frecuentes',
'Brick:Portal:FAQ:Menu' => 'FAQ',
'Brick:Portal:FAQ:Title' => 'Preguntas y Respuestas Frecuentes',
'Brick:Portal:FAQ:Title+' => '<p>¿En una prisa?</p><p>Vea la lista de las preguntas más comunes y encontrará (tal vez) la respuesta inmediata a sus necesidades.</p>',
));
?>

View File

@@ -2,48 +2,14 @@
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @license http://opensource.org/licenses/AGPL-3.0
*
*
* Инструкция по установке
*
* Процесс установки заключается в замене имеющихся локализационных файлов полученными и последующем запуске процедуры обновления iTop для перекомпиляции кода.
* 1. Скопируйте с заменой два полученных файла из "itop-rus/dictionaries" в "путь/до/вашего/itop/dictionaries".
* 2. Скопируйте с заменой полученные файлы "itop-rus/datamodels/2.x/название-модуля/ru.dict.название-модуля.php" в "путь/до/вашего/itop/datamodels/2.x/название-модуля".
* 3. Перейдите по адресу "http://адрес/вашего/itop/setup", при этом файл "путь/до/вашего/itop/conf/production/config-itop.php" должен быть доступен для записи.
* 4. На второй странице установщика выберите "Upgrade an existing iTop instance" и следуйте дальнейшим инструкциям установщика.
*
* Ответы на вопросы по установке и использованию переводов, а также на любые другие вопросы по iTop всегда можно получить на сайте сообщества iTop по-русски http://community.itop-itsm.ru.
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// 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>+
//////////////////////////////////////////////////////////////////////
// Classes in 'bizmodel'
//////////////////////////////////////////////////////////////////////
//
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// 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: KnownError
//
@@ -55,11 +21,11 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:KnownError/Attribute:name+' => '',
'Class:KnownError/Attribute:org_id' => 'Организация',
'Class:KnownError/Attribute:org_id+' => '',
'Class:KnownError/Attribute:cust_name' => 'Имя клиента',
'Class:KnownError/Attribute:cust_name' => 'Организация',
'Class:KnownError/Attribute:cust_name+' => '',
'Class:KnownError/Attribute:problem_id' => 'Проблема',
'Class:KnownError/Attribute:problem_id+' => '',
'Class:KnownError/Attribute:problem_ref' => 'Ссылка на проблему',
'Class:KnownError/Attribute:problem_ref' => 'Проблема',
'Class:KnownError/Attribute:problem_ref+' => '',
'Class:KnownError/Attribute:symptom' => 'Проявление',
'Class:KnownError/Attribute:symptom+' => '',
@@ -102,11 +68,11 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkErrorToFunctionalCI+' => 'Infra related to a known error',
'Class:lnkErrorToFunctionalCI/Attribute:functionalci_id' => 'КЕ',
'Class:lnkErrorToFunctionalCI/Attribute:functionalci_id+' => '',
'Class:lnkErrorToFunctionalCI/Attribute:functionalci_name' => 'Имя КЕ',
'Class:lnkErrorToFunctionalCI/Attribute:functionalci_name' => 'КЕ',
'Class:lnkErrorToFunctionalCI/Attribute:functionalci_name+' => '',
'Class:lnkErrorToFunctionalCI/Attribute:error_id' => 'Известная ошибка',
'Class:lnkErrorToFunctionalCI/Attribute:error_id+' => '',
'Class:lnkErrorToFunctionalCI/Attribute:error_name' => мя известной ошибки',
'Class:lnkErrorToFunctionalCI/Attribute:error_name' => 'Известная ошибка',
'Class:lnkErrorToFunctionalCI/Attribute:error_name+' => '',
'Class:lnkErrorToFunctionalCI/Attribute:reason' => 'Причина',
'Class:lnkErrorToFunctionalCI/Attribute:reason+' => '',
@@ -121,11 +87,11 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkDocumentToError+' => 'A link between a document and a known error',
'Class:lnkDocumentToError/Attribute:document_id' => 'Документ',
'Class:lnkDocumentToError/Attribute:document_id+' => '',
'Class:lnkDocumentToError/Attribute:document_name' => 'Имя документа',
'Class:lnkDocumentToError/Attribute:document_name' => 'Документ',
'Class:lnkDocumentToError/Attribute:document_name+' => '',
'Class:lnkDocumentToError/Attribute:error_id' => 'Известная ошибка',
'Class:lnkDocumentToError/Attribute:error_id+' => '',
'Class:lnkDocumentToError/Attribute:error_name' => 'Часто задаваемые вопросы',
'Class:lnkDocumentToError/Attribute:error_name' => 'Известная ошибка',
'Class:lnkDocumentToError/Attribute:error_name+' => '',
'Class:lnkDocumentToError/Attribute:link_type' => 'Тип связи',
'Class:lnkDocumentToError/Attribute:link_type+' => '',
@@ -146,7 +112,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:FAQ/Attribute:description+' => '',
'Class:FAQ/Attribute:category_id' => 'Категория',
'Class:FAQ/Attribute:category_id+' => '',
'Class:FAQ/Attribute:category_name' => 'Имя категории',
'Class:FAQ/Attribute:category_name' => 'Категория',
'Class:FAQ/Attribute:category_name+' => '',
'Class:FAQ/Attribute:error_code' => 'Код ошибки',
'Class:FAQ/Attribute:error_code+' => '',
@@ -159,27 +125,31 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:FAQCategory' => 'Категории ЧаВо',
'Class:FAQCategory+' => 'Category for FAQ',
'Class:FAQCategory/Attribute:name' => 'Имя',
'Class:FAQCategory' => 'Категории FAQ',
'Class:FAQCategory+' => 'Категории FAQ',
'Class:FAQCategory/Attribute:name' => 'Название',
'Class:FAQCategory/Attribute:name+' => '',
'Class:FAQCategory/Attribute:faq_list' => 'ЧаВо',
'Class:FAQCategory/Attribute:faq_list+' => 'All the frequently asked questions related to this category',
'Class:FAQCategory/Attribute:faq_list' => 'FAQ',
'Class:FAQCategory/Attribute:faq_list+' => 'Связанные FAQ',
));
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Menu:ProblemManagement' => 'Problem Management',
'Menu:ProblemManagement+' => 'Problem Management',
'Menu:Problem:Shortcuts' => 'Shortcuts',
'Menu:NewError' => 'Создать известную ошибку',
'Menu:NewError+' => 'Creation of a new known error',
'Menu:SearchError' => 'Найти известную ошибку',
'Menu:SearchError+' => 'Search for known errors',
'Menu:Problem:KnownErrors' => 'Известные ошибки',
'Menu:Problem:KnownErrors+' => 'База известных ошибок',
'Menu:ProblemManagement' => 'Управление проблемами',
'Menu:ProblemManagement+' => 'Управление проблемами',
'Menu:Problem:Shortcuts' => 'Ярлыки',
'Menu:NewError' => 'Новая известная ошибка',
'Menu:NewError+' => 'Создать новую известную ошибку',
'Menu:SearchError' => 'Поиск известных ошибок',
'Menu:SearchError+' => 'Поиск известных ошибок',
'Menu:Problem:KnownErrors' => 'Известные ошибки',
'Menu:Problem:KnownErrors+' => 'База известных ошибок',
'Menu:FAQCategory' => 'Категории FAQ',
'Menu:FAQCategory+' => 'Категории FAQ',
'Menu:FAQ' => 'FAQ',
'Menu:FAQ+' => 'Часто задаваемые вопросы',
));
?>
'Brick:Portal:FAQ:Menu' => 'FAQ',
'Brick:Portal:FAQ:Title' => 'Часто задаваемые вопросы',
'Brick:Portal:FAQ:Title+' => '<p>Торопитесь?</p><p>Проверьте список часто задаваемых вопросов, возможно, ответ уже есть.</p>',
));

View File

@@ -40,13 +40,15 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'Portal:Button:Add' => 'Přidat',
'Portal:Button:Remove' => 'Odebrat',
'Portal:Button:Delete' => 'Smazat',
'Error:HTTP:404' => 'Stránka nenalezena',
'Portal:EnvironmentBanner:Title' => 'You are currently in <strong>%1$s</strong> mode~~',
'Portal:EnvironmentBanner:GoToProduction' => 'Go back to PRODUCTION mode~~',
'Error:HTTP:404' => 'Stránka nenalezena',
'Error:HTTP:500' => 'Jejda! Nastal problém',
'Error:HTTP:GetHelp' => 'Kontaktujte prosím administrátora, pokud problém přetrvá.',
'Error:XHR:Fail' => 'Data se nepodařilo načíst, kontaktujte prosím administrátora.',
'Portal:Datatables:Language:Processing' => 'Počkejte prosím',
'Portal:Datatables:Language:Search' => 'filtr :',
'Portal:Datatables:Language:LengthMenu' => 'Zobrazit _MENU_ položek na stránku',
'Portal:Datatables:Language:Search' => 'Filtr :',
'Portal:Datatables:Language:LengthMenu' => 'Zobrazit _MENU_ položek na stránku',
'Portal:Datatables:Language:ZeroRecords' => 'Žádný výsledek',
'Portal:Datatables:Language:Info' => 'Stránka _PAGE_ z _PAGES_',
'Portal:Datatables:Language:InfoEmpty' => 'Žádná informace',
@@ -61,6 +63,9 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'Portal:Datatables:Language:Sort:Descending' => 'řadit sestupně',
'Portal:Autocomplete:NoResult' => 'Žádná data',
'Portal:Attachments:DropZone:Message' => 'Přesuňte soubory myší pro vložení',
'Portal:File:None' => 'No file',
'Portal:File:DisplayInfo' => '<a href="%2$s" class="file_download_link">%1$s</a>',
'Portal:File:DisplayInfo+' => '%1$s (%2$s) <a href="%3$s" class="file_open_link" target="_blank">Open</a> / <a href="%4$s" class="file_download_link">Download</a>',
));
// UserProfile brick

View File

@@ -0,0 +1,117 @@
<?php
// Copyright (C) 2010-2016 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @copyright Copyright (C) 2016 ITOMIG GmbH
* @license http://opensource.org/licenses/AGPL-3.0
*/
// Portal
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Page:DefaultTitle' => 'iTop - Benutzer Portal',
'Page:PleaseWait' => 'Bitte warten...',
'Page:Home' => 'Start',
'Page:GoPortalHome' => 'Startseite',
'Page:GoPreviousPage' => 'vorherige Seite',
'Portal:Button:Submit' => 'Abschicken',
'Portal:Button:Cancel' => 'Zurück',
'Portal:Button:Close' => 'Schließen',
'Portal:Button:Add' => 'Hinzu',
'Portal:Button:Remove' => 'Wegnehmen',
'Portal:Button:Delete' => 'Löschen',
'Portal:EnvironmentBanner:Title' => 'Sie sind im Moment im <strong>%1$s</strong> Modus',
'Portal:EnvironmentBanner:GoToProduction' => 'Zurück zum PRODUCTION Modus',
'Error:HTTP:404' => 'Seite nicht gefunden.',
'Error:HTTP:500' => 'Oops! Es ist ein Fehler aufgetreten.',
'Error:HTTP:GetHelp' => 'Bitte kontaktieren Sie Ihren iTop administrator falls das Problem öfter auftaucht.',
'Error:XHR:Fail' => 'Konnte Daten nicht laden, bitte kontaktieren Sie Ihren iTop administrator',
'Portal:Datatables:Language:Processing' => 'Bitte warten...',
'Portal:Datatables:Language:Search' => 'Filter :',
'Portal:Datatables:Language:LengthMenu' => 'Anzahl _MENU_ Einträge pro Seite',
'Portal:Datatables:Language:ZeroRecords' => 'Keine Resultate',
'Portal:Datatables:Language:Info' => 'Seite _PAGE_ von _PAGES_',
'Portal:Datatables:Language:InfoEmpty' => 'Keine Information',
'Portal:Datatables:Language:InfoFiltered' => 'gefiltert aus _MAX_ Resultaten',
'Portal:Datatables:Language:EmptyTable' => 'Keine Daten in dieser Tabelle verfügbar',
'Portal:Datatables:Language:DisplayLength:All' => 'Alle',
'Portal:Datatables:Language:Paginate:First' => '1.Seite',
'Portal:Datatables:Language:Paginate:Previous' => 'vorherige',
'Portal:Datatables:Language:Paginate:Next' => 'Nächste',
'Portal:Datatables:Language:Paginate:Last' => 'Letzte',
'Portal:Datatables:Language:Sort:Ascending' => 'wähle aufsteigende Sortierung',
'Portal:Datatables:Language:Sort:Descending' => 'wähle abfallende Sortierung',
'Portal:Autocomplete:NoResult' => 'keine Daten',
'Portal:Attachments:DropZone:Message' => 'Legen Sie hier Ihre Files ab, um sie als Anhang dem Ticket hinzuzufügen',
'Portal:File:None' => 'Kein File vorhanden',
'Portal:File:DisplayInfo' => '<a href="%2$s" class="file_download_link">%1$s</a>',
'Portal:File:DisplayInfo+' => '%1$s (%2$s) <a href="%3$s" class="file_open_link" target="_blank">Öffnen</a> / <a href="%4$s" class="file_download_link">Download</a>',
));
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Brick:Portal:UserProfile:Name' => 'Benutzer Profil',
'Brick:Portal:UserProfile:Navigation:Dropdown:MyProfil' => 'Mein Profil',
'Brick:Portal:UserProfile:Navigation:Dropdown:Logout' => 'Abmelden',
'Brick:Portal:UserProfile:Password:Title' => 'Passwort',
'Brick:Portal:UserProfile:Password:ChoosePassword' => 'Passwort wählen',
'Brick:Portal:UserProfile:Password:ConfirmPassword' => 'Passwort bestätigen',
'Brick:Portal:UserProfile:Password:CantChangeContactAdministrator' => 'Um das Password zu ändern, kontaktieren Sie bitte Ihren iTop Administrator',
'Brick:Portal:UserProfile:Password:CantChangeForUnknownReason' => 'Kann das Passwort nicht ändern - bitte kontaktieren Sie Ihren iTop Administrator',
'Brick:Portal:UserProfile:PersonalInformations:Title' => 'Persönliche Informationen',
'Brick:Portal:UserProfile:Photo:Title' => 'Foto',
));
// BrowseBrick brick
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Brick:Portal:Browse:Name' => 'List durchgehen',
'Brick:Portal:Browse:Mode:List' => 'Liste',
'Brick:Portal:Browse:Mode:Tree' => 'Baum',
'Brick:Portal:Browse:Action:Drilldown' => 'Drilldown',
'Brick:Portal:Browse:Action:View' => 'Details',
'Brick:Portal:Browse:Action:Edit' => 'Editieren',
'Brick:Portal:Browse:Action:Create' => 'Erstellen',
'Brick:Portal:Browse:Action:CreateObjectFromThis' => 'Neue %1$s',
'Brick:Portal:Browse:Tree:ExpandAll' => 'Alle expandieren',
'Brick:Portal:Browse:Tree:CollapseAll' => 'Alle kollabieren',
'Brick:Portal:Browse:Filter:NoData' => 'Kein Eintrag',
));
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Brick:Portal:Manage:Name' => 'Einträge managen',
'Brick:Portal:Manage:Table:NoData' => 'Kein Eintrag.',
));
// ObjectBrick brick
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Brick:Portal:Object:Name' => 'Object',
'Brick:Portal:Object:Form:Create:Title' => 'Neue %1$s',
'Brick:Portal:Object:Form:Edit:Title' => 'Wird aktualisiert %2$s (%1$s)',
'Brick:Portal:Object:Form:View:Title' => '%1$s : %2$s',
'Brick:Portal:Object:Form:Stimulus:Title' => 'Bitte die folgendenen Informationen ausfüllen:',
'Brick:Portal:Object:Form:Message:Saved' => 'Saved',
'Brick:Portal:Object:Search:Regular:Title' => 'Select %1$s (%2$s)',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Select %1$s (%2$s)',
));
// CreateBrick brick
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Brick:Portal:Create:Name' => 'Schnelles Erstellen',
));
?>

View File

@@ -36,12 +36,14 @@ Dict::Add('EN US', 'English', 'English', array(
'Portal:Button:Add' => 'Add',
'Portal:Button:Remove' => 'Remove',
'Portal:Button:Delete' => 'Delete',
'Portal:EnvironmentBanner:Title' => 'You are currently in <strong>%1$s</strong> mode',
'Portal:EnvironmentBanner:GoToProduction' => 'Go back to PRODUCTION mode',
'Error:HTTP:404' => 'Page not found',
'Error:HTTP:500' => 'Oops! An error has occured.',
'Error:HTTP:GetHelp' => 'Please contact your iTop administrator if the problem keeps happening.',
'Error:XHR:Fail' => 'Could not load data, please contact your iTop administrator',
'Portal:Datatables:Language:Processing' => 'Please wait...',
'Portal:Datatables:Language:Search' => 'filter :',
'Portal:Datatables:Language:Search' => 'Filter:',
'Portal:Datatables:Language:LengthMenu' => 'Display _MENU_ items per page',
'Portal:Datatables:Language:ZeroRecords' => 'No result',
'Portal:Datatables:Language:Info' => 'Page _PAGE_ of _PAGES_',
@@ -57,6 +59,9 @@ Dict::Add('EN US', 'English', 'English', array(
'Portal:Datatables:Language:Sort:Descending' => 'enable for a descending sort',
'Portal:Autocomplete:NoResult' => 'No data',
'Portal:Attachments:DropZone:Message' => 'Drop your files to add them as attachments',
'Portal:File:None' => 'No file',
'Portal:File:DisplayInfo' => '<a href="%2$s" class="file_download_link">%1$s</a>',
'Portal:File:DisplayInfo+' => '%1$s (%2$s) <a href="%3$s" class="file_open_link" target="_blank">Open</a> / <a href="%4$s" class="file_download_link">Download</a>',
));
// UserProfile brick

View File

@@ -0,0 +1,118 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
// Portal
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Page:DefaultTitle' => 'iTop User portal',
'Page:PleaseWait' => 'Please wait...',
'Page:Home' => 'Bienvenido',
'Page:GoPortalHome' => 'Regresar a bienvenida',
'Page:GoPreviousPage' => 'página anterior',
'Portal:Button:Submit' => 'Enviar',
'Portal:Button:Cancel' => 'Cancelar',
'Portal:Button:Close' => 'Cerrar',
'Portal:Button:Add' => 'Añadir',
'Portal:Button:Remove' => 'Eliminar',
'Portal:Button:Delete' => 'Borrar',
'Portal:EnvironmentBanner:Title' => 'You are currently in <strong>%1$s</strong> mode~~',
'Portal:EnvironmentBanner:GoToProduction' => 'Go back to PRODUCTION mode~~',
'Error:HTTP:404' => 'Página no encontrada',
'Error:HTTP:500' => '¡Vaya! Ha ocurrido un error.',
'Error:HTTP:GetHelp' => 'Póngase en contacto con el administrador de iTop si el problema persiste.',
'Error:XHR:Fail' => 'No se pudieron cargar datos, póngase en contacto con su administrador de iTop',
'Portal:Datatables:Language:Processing' => 'Por favor esperar...',
'Portal:Datatables:Language:Search' => 'Filtrar:',
'Portal:Datatables:Language:LengthMenu' => 'Mostrar _MENU_ elementos por página',
'Portal:Datatables:Language:ZeroRecords' => 'Sin resultados',
'Portal:Datatables:Language:Info' => 'Página _PAGE_ de _PAGES_',
'Portal:Datatables:Language:InfoEmpty' => 'Sin información',
'Portal:Datatables:Language:InfoFiltered' => 'Filtrada de _MAX_ elementos',
'Portal:Datatables:Language:EmptyTable' => 'No hay datos disponibles en esta tabla',
'Portal:Datatables:Language:DisplayLength:All' => 'Todas',
'Portal:Datatables:Language:Paginate:First' => 'primero',
'Portal:Datatables:Language:Paginate:Previous' => 'Anterior',
'Portal:Datatables:Language:Paginate:Next' => 'Siguiente',
'Portal:Datatables:Language:Paginate:Last' => 'Último',
'Portal:Datatables:Language:Sort:Ascending' => 'Habilitar para un orden ascendente',
'Portal:Datatables:Language:Sort:Descending' => 'Habilitar para un tipo descendente',
'Portal:Autocomplete:NoResult' => 'Sin datos',
'Portal:Attachments:DropZone:Message' => 'Agrega tus archivos para agregarlos como documentos adjuntos',
'Portal:File:None' => 'No file',
'Portal:File:DisplayInfo' => '<a href="%2$s" class="file_download_link">%1$s</a>',
'Portal:File:DisplayInfo+' => '%1$s (%2$s) <a href="%3$s" class="file_open_link" target="_blank">Open</a> / <a href="%4$s" class="file_download_link">Download</a>',
));
// UserProfile brick
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Brick:Portal:UserProfile:Name' => 'Perfil del usuario',
'Brick:Portal:UserProfile:Navigation:Dropdown:MyProfil' => 'Mi perfil',
'Brick:Portal:UserProfile:Navigation:Dropdown:Logout' => 'Desconectarse',
'Brick:Portal:UserProfile:Password:Title' => 'Contraseña',
'Brick:Portal:UserProfile:Password:ChoosePassword' => 'Elegir una contraseña',
'Brick:Portal:UserProfile:Password:ConfirmPassword' => 'Confirmar contraseña',
'Brick:Portal:UserProfile:Password:CantChangeContactAdministrator' => 'Para cambiar su contraseña, póngase en contacto con su administrador de iTop',
'Brick:Portal:UserProfile:Password:CantChangeForUnknownReason' => 'No se puede cambiar la contraseña, póngase en contacto con el administrador de iTop',
'Brick:Portal:UserProfile:PersonalInformations:Title' => 'Informaciones personales',
'Brick:Portal:UserProfile:Photo:Title' => 'Foto',
));
// BrowseBrick brick
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Brick:Portal:Browse:Name' => 'Buscar en todos los elementos',
'Brick:Portal:Browse:Mode:List' => 'Lista',
'Brick:Portal:Browse:Mode:Tree' => 'Árbol',
'Brick:Portal:Browse:Action:Drilldown' => 'Desglose',
'Brick:Portal:Browse:Action:View' => 'Detalles',
'Brick:Portal:Browse:Action:Edit' => 'Editar',
'Brick:Portal:Browse:Action:Create' => 'Crear',
'Brick:Portal:Browse:Action:CreateObjectFromThis' => 'Nuevo %1$s',
'Brick:Portal:Browse:Tree:ExpandAll' => 'Expandir todo',
'Brick:Portal:Browse:Tree:CollapseAll' => 'Desplegar todo',
'Brick:Portal:Browse:Filter:NoData' => 'Sin objeto',
));
// ManageBrick brick
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Brick:Portal:Manage:Name' => 'Administrar elementos',
'Brick:Portal:Manage:Table:NoData' => 'Sin objeto.',
));
// ObjectBrick brick
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Brick:Portal:Object:Name' => 'Object',
'Brick:Portal:Object:Form:Create:Title' => 'New %1$s',
'Brick:Portal:Object:Form:Edit:Title' => 'Updating %2$s (%1$s)',
'Brick:Portal:Object:Form:View:Title' => '%1$s : %2$s',
'Brick:Portal:Object:Form:Stimulus:Title' => 'Please, fill the following informations:',
'Brick:Portal:Object:Form:Message:Saved' => 'Saved',
'Brick:Portal:Object:Search:Regular:Title' => 'Select %1$s (%2$s)',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Select %1$s (%2$s)',
));
// CreateBrick brick
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Brick:Portal:Create:Name' => 'Creación rápida',
));
?>

View File

@@ -36,6 +36,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Portal:Button:Add' => 'Ajouter',
'Portal:Button:Remove' => 'Enlever',
'Portal:Button:Delete' => 'Supprimer',
'Portal:EnvironmentBanner:Title' => 'Vous êtes dans le mode <strong>%1$s</strong>',
'Portal:EnvironmentBanner:GoToProduction' => 'Retourner au mode PRODUCTION',
'Error:HTTP:404' => 'Page non trouvée',
'Error:HTTP:500' => 'Oups ! Une erreur est survenue.',
'Error:HTTP:GetHelp' => 'Si le problème persiste, veuillez contacter votre administrateur iTop.',
@@ -57,6 +59,9 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Portal:Datatables:Language:Sort:Descending' => 'activer pour trier la colonne par ordre décroissant',
'Portal:Autocomplete:NoResult' => 'Aucun résultat',
'Portal:Attachments:DropZone:Message' => 'Déposez vos fichiers pour les ajouter en pièces jointes',
'Portal:File:None' => 'Aucun fichier',
'Portal:File:DisplayInfo' => '<a href="%2$s" class="file_download_link">%1$s</a>',
'Portal:File:DisplayInfo+' => '%1$s (%2$s) <a href="%3$s" class="file_open_link" target="_blank">Ouvrir</a> / <a href="%4$s" class="file_download_link">Télécharger</a>',
));
// UserProfile brick

View File

@@ -2,7 +2,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-portal-base/1.0.0', array(
'itop-portal-base/1.0.1', array(
// Identification
'label' => 'Portal Development Library',
'category' => 'Portal',

View File

@@ -93,11 +93,19 @@ class BrowseBrickController extends BrickController
{
// Retrieving class alias for all depth
array_unshift($aLevelsClasses, $aLevelsProperties[$aLevelsPropertiesKeys[$i]]['search']->GetClassAlias());
// Joining queries from bottom-up
if ($i < $iLoopMax)
{
$aLevelsProperties[$aLevelsPropertiesKeys[$i]]['search'] = $aLevelsProperties[$aLevelsPropertiesKeys[$i]]['search']->Join($aLevelsProperties[$aLevelsPropertiesKeys[$i + 1]]['search'], DBSearch::JOIN_REFERENCED_BY, $aLevelsProperties[$aLevelsPropertiesKeys[$i + 1]]['parent_att']);
$aRealiasingMap = array();
$aLevelsProperties[$aLevelsPropertiesKeys[$i]]['search'] = $aLevelsProperties[$aLevelsPropertiesKeys[$i]]['search']->Join($aLevelsProperties[$aLevelsPropertiesKeys[$i + 1]]['search'], DBSearch::JOIN_REFERENCED_BY, $aLevelsProperties[$aLevelsPropertiesKeys[$i + 1]]['parent_att'], TREE_OPERATOR_EQUALS, $aRealiasingMap);
foreach ($aLevelsPropertiesKeys as $sLevelAlias)
{
if (array_key_exists($sLevelAlias, $aRealiasingMap))
{
$aLevelsProperties[$aLevelsPropertiesKeys[$i]]['search']->RenameAlias($aRealiasingMap[$sLevelAlias], $sLevelAlias);
}
}
}
// Adding search clause
@@ -168,7 +176,7 @@ class BrowseBrickController extends BrickController
}
}
$oQuery = $aLevelsProperties[$aLevelsPropertiesKeys[0]]['search'];
// Testing appropriate data loading mode if we are in auto
if ($sDataLoading === AbstractBrick::ENUM_DATA_LOADING_AUTO)
{
@@ -259,7 +267,32 @@ class BrowseBrickController extends BrickController
{
$oSet = new DBObjectSet($oQuery);
}
// Optimizing the ObjectSet to retrieve only necessary columns
$aColumnAttrs = array();
foreach ($oSet->GetFilter()->GetSelectedClasses() as $sTmpClassAlias => $sTmpClassName)
{
if (isset($aLevelsProperties[$sTmpClassAlias]))
{
$aTmpLevelProperties = $aLevelsProperties[$sTmpClassAlias];
// Mandatory main attribute
$aTmpColumnAttrs = array($aTmpLevelProperties['name_att']);
// Optionnal attributes, only if in list mode
if ($sBrowseMode === BrowseBrick::ENUM_BROWSE_MODE_LIST)
{
foreach ($aTmpLevelProperties['fields'] as $aTmpField)
{
$aTmpColumnAttrs[] = $aTmpField['code'];
}
}
$aColumnAttrs[$sTmpClassAlias] = $aTmpColumnAttrs;
}
}
$oSet->OptimizeColumnLoad($aColumnAttrs);
// Sorting objects through defined order (in DM)
$oSet->SetOrderByClasses();
// Retrieving results and organizing them for templating
$aItems = array();
while ($aCurrentRow = $oSet->FetchAssoc())
@@ -338,10 +371,16 @@ class BrowseBrickController extends BrickController
{
$sCurrentLevelAlias = $sLevelAliasPrefix . static::LEVEL_SEPARATOR . $aLevel['id'];
$oSearch = DBSearch::CloneWithAlias(DBSearch::FromOQL($aLevel['oql']), $sCurrentLevelAlias);
// Restricting to the allowed scope
$oScopeSearch = $oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $oSearch->GetClass(), UR_ACTION_READ);
$oSearch = ($oScopeSearch !== null) ? $oSearch->Intersect($oScopeSearch) : null;
// - Allowing all data if necessary
if ($oScopeSearch->IsAllDataAllowed())
{
$oSearch->AllowAllData();
}
if ($oSearch !== null)
{
$aLevelsProperties[$sCurrentLevelAlias] = array(
@@ -561,7 +600,15 @@ class BrowseBrickController extends BrickController
$aRow[$key]['fields'] = array();
foreach ($aLevelsProperties[$key]['fields'] as $aField)
{
$aRow[$key]['fields'][$aField['code']] = $value->Get($aField['code']);
$oAttDef = MetaModel::GetAttributeDef(get_class($value), $aField['code']);
if ($oAttDef->GetEditClass() === 'Duration')
{
$aRow[$key]['fields'][$aField['code']] = $oAttDef->GetAsHTML($value->Get($aField['code']));
}
else
{
$aRow[$key]['fields'][$aField['code']] = $oAttDef->GetValueLabel($value->Get($aField['code']));
}
}
}
}
@@ -630,5 +677,3 @@ class BrowseBrickController extends BrickController
}
}
?>

View File

@@ -37,8 +37,17 @@ class DefaultController
// Doing it only for tile visible on home page to avoid unnecessary rendering
if (($oBrick->GetVisibleHome() === true) && ($oBrick->GetTileControllerAction() !== null))
{
$oController = new \Combodo\iTop\Portal\Controller\ManageBrickController($oRequest, $oApp);
$aData['aTilesRendering'][$oBrick->GetId()] = $oController->HomeAction($oRequest, $oApp);
$aControllerActionParts = explode('::', $oBrick->GetTileControllerAction());
if (count($aControllerActionParts) !== 2)
{
$oApp->abort(500, 'Tile controller action must be of form "\Namespace\ControllerClass::FunctionName" for brick "' . $oBrick->GetId() . '"');
}
$sControllerName = $aControllerActionParts[0];
$sControllerAction = $aControllerActionParts[1];
$oController = new $sControllerName($oRequest, $oApp, $oBrick->GetId());
$aData['aTilesRendering'][$oBrick->GetId()] = $oController->$sControllerAction($oRequest, $oApp, $oBrick->GetId());
}
}

View File

@@ -28,6 +28,8 @@ use \MetaModel;
use \AttributeDefinition;
use \AttributeDate;
use \AttributeDateTime;
use \AttributeDuration;
use \AttributeSubItem;
use \DBSearch;
use \DBObjectSearch;
use \DBObjectSet;
@@ -59,6 +61,15 @@ class ManageBrickController extends BrickController
// Getting search value
$sSearchValue = $oRequest->get('sSearchValue', null);
// Getting area columns properties
$aColumnsAttrs = $oBrick->GetFields();
// Adding friendlyname attribute to the list is not already in it
$sTitleAttrCode = 'friendlyname';
if (($sTitleAttrCode !== null) && !in_array($sTitleAttrCode, $aColumnsAttrs))
{
$aColumnsAttrs = array_merge(array($sTitleAttrCode), $aColumnsAttrs);
}
// Starting to build query
$oQuery = DBSearch::FromOQL($oBrick->GetOql());
@@ -198,6 +209,11 @@ class ManageBrickController extends BrickController
if ($oDistinctScopeQuery != null)
{
$oDistinctQuery = $oDistinctQuery->Intersect($oDistinctScopeQuery);
// - Allowing all data if necessary
if ($oDistinctScopeQuery->IsAllDataAllowed())
{
$oDistinctQuery->AllowAllData();
}
}
// Adding grouping conditions
$oFieldExp = new FieldExpression($sGroupingAreaAttCode, $sParentAlias);
@@ -252,7 +268,19 @@ class ManageBrickController extends BrickController
// Note : Will need to moved the scope restriction on queries elsewhere when we consider grouping on something else than finalclass
// Note : We now get view scope instead of edit scope as we allowed users to view/edit objects in the brick regarding their rights
$oScopeQuery = $oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $aGroupingAreasValue['value'], UR_ACTION_READ);
$oAreaQuery = ($oScopeQuery !== null) ? $oAreaQuery->Intersect($oScopeQuery) : null;
if ($oScopeQuery !== null)
{
$oAreaQuery = $oAreaQuery->Intersect($oScopeQuery);
// - Allowing all data if necessary
if ($oScopeQuery->IsAllDataAllowed())
{
$oAreaQuery->AllowAllData();
}
}
else
{
$oAreaQuery = null;
}
$aQueries[$sKey] = $oAreaQuery;
}
@@ -264,6 +292,7 @@ class ManageBrickController extends BrickController
// - Check how many records there is.
// - Update $sDataLoading with its new value regarding the number of record and the threshold
$oCountSet = new DBObjectSet($oQuery);
$oCountSet->OptimizeColumnLoad(array());
$fThreshold = (float) MetaModel::GetModuleSetting($oApp['combodo.portal.instance.id'], 'lazy_loading_threshold');
$sDataLoading = ($oCountSet->Count() > $fThreshold) ? AbstractBrick::ENUM_DATA_LOADING_LAZY : AbstractBrick::ENUM_DATA_LOADING_FULL;
unset($oCountSet);
@@ -285,6 +314,7 @@ class ManageBrickController extends BrickController
// Getting total records number
$oCountSet = new DBObjectSet($oQuery);
$oCountSet->OptimizeColumnLoad(array($oQuery->GetClassAlias() => $aColumnsAttrs));
$aData['recordsTotal'] = $oCountSet->Count();
$aData['recordsFiltered'] = $oCountSet->Count();
unset($oCountSet);
@@ -296,6 +326,8 @@ class ManageBrickController extends BrickController
{
$oSet = new DBObjectSet($oQuery);
}
$oSet->OptimizeColumnLoad(array($oQuery->GetClassAlias() => $aColumnsAttrs));
$oSet->SetOrderByClasses();
$aSets[$sKey] = $oSet;
}
}
@@ -306,15 +338,7 @@ class ManageBrickController extends BrickController
{
// Set properties
$sCurrentClass = $sKey;
$sTitleAttrCode = MetaModel::GetFriendlyNameAttributeCode($sCurrentClass);
// Getting area columns properties
$aColumnsAttrs = $oBrick->GetFields();
// Adding friendlyname attribute to the list is not already in it
if (($sTitleAttrCode !== null) && !in_array($sTitleAttrCode, $aColumnsAttrs))
{
$aColumnsAttrs = array_merge(array($sTitleAttrCode), $aColumnsAttrs);
}
// Defining which attribute will open the edition form)
$sMainActionAttrCode = $aColumnsAttrs[0];
@@ -367,7 +391,7 @@ class ManageBrickController extends BrickController
);
}
}
$oAttDef = MetaModel::GetAttributeDef($sCurrentClass, $sItemAttr);
if ($oAttDef->IsExternalKey())
{
@@ -387,6 +411,10 @@ class ManageBrickController extends BrickController
}
}
}
elseif ($oAttDef instanceof AttributeSubItem || $oAttDef instanceof AttributeDuration)
{
$sValue = $oAttDef->GetAsHTML($oCurrentRow->Get($sItemAttr));
}
else
{
$sValue = $oAttDef->GetValueLabel($oCurrentRow->Get($sItemAttr));
@@ -446,5 +474,3 @@ class ManageBrickController extends BrickController
}
}
?>

View File

@@ -32,6 +32,7 @@ use \IssueLog;
use \MetaModel;
use \DBSearch;
use \DBObjectSearch;
use \FalseExpression;
use \BinaryExpression;
use \FieldExpression;
use \VariableExpression;
@@ -39,6 +40,8 @@ use \ListExpression;
use \ScalarExpression;
use \DBObjectSet;
use \cmdbAbstractObject;
use \AttributeEnum;
use \AttributeFinalClass;
use \UserRights;
use \Combodo\iTop\Portal\Helper\ApplicationHelper;
use \Combodo\iTop\Portal\Helper\SecurityHelper;
@@ -83,7 +86,7 @@ class ObjectController extends AbstractController
}
// Retrieving object
$oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */);
$oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */, $oApp['scope_validator']->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass));
if ($oObject === null)
{
// We should never be there as the secuirty helper makes sure that the object exists, but just in case.
@@ -155,7 +158,7 @@ class ObjectController extends AbstractController
}
// Retrieving object
$oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */);
$oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */, $oApp['scope_validator']->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass));
if ($oObject === null)
{
// We should never be there as the secuirty helper makes sure that the object exists, but just in case.
@@ -275,23 +278,26 @@ class ObjectController extends AbstractController
}
// Retrieving origin object
$oOriginObject = MetaModel::GetObject($sObjectClass, $sObjectId);
// Note : AllowAllData set to true here instead of checking scope's flag because we are displaying a value that has been set and validated
$oOriginObject = MetaModel::GetObject($sObjectClass, $sObjectId, true, true);
// Retrieving target object (We check if the method is a simple function or if it's part of a class in which case only static function are supported)
if (!strpos($sMethodName, '::'))
{
$sTargetObject = $sMethodName($oOriginObject);
$oTargetObject = $sMethodName($oOriginObject);
}
else
{
$aMethodNameParts = explode('::', $sMethodName);
$sTargetObject = $aMethodNameParts[0]::$aMethodNameParts[1]($oOriginObject);
$sMethodClass = $aMethodNameParts[0];
$sMethodName = $aMethodNameParts[1];
$oTargetObject = $sMethodClass::$sMethodName($oOriginObject);
}
// Preparing redirection
// - Route
$aRouteParams = array(
'sObjectClass' => get_class($sTargetObject)
'sObjectClass' => get_class($oTargetObject)
);
$sRedirectRoute = $oApp['url_generator']->generate('p_object_create', $aRouteParams);
// - Request
@@ -327,7 +333,7 @@ class ObjectController extends AbstractController
// }
// Retrieving object
$oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */);
$oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */, $oApp['scope_validator']->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass));
if ($oObject === null)
{
// We should never be there as the secuirty helper makes sure that the object exists, but just in case.
@@ -335,6 +341,9 @@ class ObjectController extends AbstractController
$oApp->abort(404, Dict::S('UI:ObjectDoesNotExist'));
}
// Retrieving request parameters
$sOperation = $oRequest->request->get('operation');
// Preparing a dedicated form for the stimulus application
$aFormProperties = array(
'id' => 'apply-stimulus',
@@ -372,14 +381,39 @@ class ObjectController extends AbstractController
'url' => $oApp['url_generator']->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $sObjectId))
);
// TODO : This is a ugly patch to avoid showing a modal with a readonly form to the user as it would prevent user from finishing the transition.
// Instead, we apply the stimulus directly here and then go to the edited object.
if ($sOperation === null)
{
if (isset($aData['form']['editable_fields_count']) && $aData['form']['editable_fields_count'] === 0)
{
$sOperation = 'redirect';
$oSubRequest = $oRequest;
$oSubRequest->request->set('operation', 'submit');
$oSubRequest->request->set('stimulus_code', null);
$aData = array('sMode' => 'apply_stimulus');
$aData['form'] = $this->HandleForm($oSubRequest, $oApp, $aData['sMode'], $sObjectClass, $sObjectId, $aFormProperties);
// Redefining the array to be as simple as possible :
$aData = array('redirection' =>
array('url' => $oApp['url_generator']->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $sObjectId)))
);
}
}
// Preparing response
if ($oRequest->isXmlHttpRequest())
{
// We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form.
if ($oRequest->request->get('operation') === null)
if ($sOperation === null)
{
$oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData);
}
elseif ($sOperation === 'redirect')
{
$oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/modal/mode_loader.html.twig', $aData);
}
else
{
$oResponse = $oApp->json($aData);
@@ -428,7 +462,7 @@ class ObjectController extends AbstractController
}
else
{
$oObject = MetaModel::GetObject($sObjectClass, $sObjectId);
$oObject = MetaModel::GetObject($sObjectClass, $sObjectId, true, $oApp['scope_validator']->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass));
}
// Preparing transitions only if we are currently going through one
@@ -473,9 +507,16 @@ class ObjectController extends AbstractController
->SetMode($sMode)
->SetActionRulesToken($sActionRulesToken)
->SetRenderer($oFormRenderer)
->SetFormProperties($aFormProperties)
->Build();
->SetFormProperties($aFormProperties);
if ($sMode === 'apply_stimulus')
{
$aEditFormProperties = ApplicationHelper::GetLoadedFormFromClass($oApp, $sObjectClass, ObjectFormManager::ENUM_MODE_APPLY_STIMULUS);
$oFormManager->MergeFormProperties($aEditFormProperties);
}
$oFormManager->Build();
// Check the number of editable fields
$aFormData['editable_fields_count'] = $oFormManager->GetForm()->GetEditableFieldCount();
}
@@ -613,6 +654,8 @@ class ObjectController extends AbstractController
// Retrieving parameters
$sQuery = $aRequestContent['sQuery'];
$sFormPath = $aRequestContent['sFormPath'];
$sFieldId = $aRequestContent['sFieldId'];
// Checking security layers
if (!SecurityHelper::IsActionAllowed($oApp, UR_ACTION_READ, $sHostObjectClass, $sHostObjectId))
@@ -624,7 +667,8 @@ class ObjectController extends AbstractController
// Retrieving host object for future DBSearch parameters
if ($sHostObjectId !== null)
{
$oHostObject = MetaModel::GetObject($sHostObjectClass, $sHostObjectId);
// Note : AllowAllData set to true here instead of checking scope's flag because we are displaying a value that has been set and validated
$oHostObject = MetaModel::GetObject($sHostObjectClass, $sHostObjectId, true, true);
}
else
{
@@ -665,20 +709,58 @@ class ObjectController extends AbstractController
// Building search query
// - Retrieving target object class from attcode
$oTargetAttDef = MetaModel::GetAttributeDef($sHostObjectClass, $sTargetAttCode);
$sTargetObjectClass = $oTargetAttDef->GetTargetClass();
if ($oTargetAttDef->GetEditClass() === 'CustomFields')
{
$oRequestTemplate = $oHostObject->Get($sTargetAttCode);
$oTemplateFieldSearch = $oRequestTemplate->GetForm()->GetField('user_data')->GetForm()->GetField($sFieldId)->GetSearch();
$sTargetObjectClass = $oTemplateFieldSearch->GetClass();
}
elseif ($oTargetAttDef->IsLinkSet())
{
throw new Exception('Search autocomplete cannot apply on AttributeLinkedSet objects, ' . get_class($oTargetAttDef) . ' (' . $sHostObjectClass . '->' . $sTargetAttCode . ') given.');
}
else
{
$sTargetObjectClass = $oTargetAttDef->GetTargetClass();
}
// - Base query from meta model
$oSearch = DBSearch::FromOQL($oTargetAttDef->GetValuesDef()->GetFilterExpression());
if ($oTargetAttDef->GetEditClass() === 'CustomFields')
{
$oSearch = $oTemplateFieldSearch;
}
else
{
$oSearch = DBSearch::FromOQL($oTargetAttDef->GetValuesDef()->GetFilterExpression());
}
// - Adding query condition
$oSearch->AddConditionExpression(new BinaryExpression(new FieldExpression('friendlyname', $oSearch->GetClassAlias()), 'LIKE', new VariableExpression('ac_query')));
// - Intersecting with scope constraints
$oSearch = $oSearch->Intersect($oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sTargetObjectClass, UR_ACTION_READ));
// Note : This do NOT apply to custom fields as the portal administrator is not supposed to know which objects will be put in the templates.
// It is the responsability of the template designer to write the right query so the user see only what he should.
if ($oTargetAttDef->GetEditClass() !== 'CustomFields')
{
$oScopeSearch = $oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sTargetObjectClass, UR_ACTION_READ);
$oSearch = $oSearch->Intersect($oScopeSearch);
// - Allowing all data if necessary
if ($oScopeSearch->IsAllDataAllowed())
{
$oSearch->AllowAllData();
}
}
// Retrieving results
// - Preparing object set
$oSet = new DBObjectSet($oSearch, array(), array('this' => $oHostObject, 'ac_query' => '%' . $sQuery . '%'));
$oSet->OptimizeColumnLoad(array($oSearch->GetClassAlias() => array('friendlyname')));
// Note : This limit is also used in the field renderer by typeahead to determine how many suggestions to display
$oSet->SetLimit($oTargetAttDef->GetMaximumComboLength()); // TODO : Is this the right limit value ? We might want to use another parameter
if ($oTargetAttDef->GetEditClass() === 'CustomFields')
{
$oSet->SetLimit(static::DEFAULT_COUNT_PER_PAGE_LIST);
}
else
{
$oSet->SetLimit($oTargetAttDef->GetMaximumComboLength()); // TODO : Is this the right limit value ? We might want to use another parameter
}
// - Retrieving objects
while ($oItem = $oSet->Fetch())
{
@@ -729,7 +811,8 @@ class ObjectController extends AbstractController
// Retrieving host object for future DBSearch parameters
if ($sHostObjectId !== null)
{
$oHostObject = MetaModel::GetObject($sHostObjectClass, $sHostObjectId);
// Note : AllowAllData set to true here instead of checking scope's flag because we are displaying a value that has been set and validated
$oHostObject = MetaModel::GetObject($sHostObjectClass, $sHostObjectId, true, true);
}
else
{
@@ -795,6 +878,12 @@ class ObjectController extends AbstractController
$sTargetObjectClass = $oRemoteAttDef->GetTargetClass();
}
}
elseif ($oTargetAttDef->GetEditClass() === 'CustomFields')
{
$oRequestTemplate = $oHostObject->Get($sTargetAttCode);
$oTemplateFieldSearch = $oRequestTemplate->GetForm()->GetField('user_data')->GetForm()->GetField($sFieldId)->GetSearch();
$sTargetObjectClass = $oTemplateFieldSearch->GetClass();
}
else
{
throw new Exception('Search from attribute can only apply on AttributeExternalKey or AttributeLinkedSet objects, ' . get_class($oTargetAttDef) . ' given.');
@@ -803,16 +892,18 @@ class ObjectController extends AbstractController
// - Retrieving class attribute list
$aAttCodes = ApplicationHelper::GetLoadedListFromClass($oApp, $sTargetObjectClass, 'list');
// - Adding friendlyname attribute to the list is not already in it
$sTitleAttCode = MetaModel::GetFriendlyNameAttributeCode($sTargetObjectClass);
$sTitleAttCode = 'friendlyname';
if (($sTitleAttCode !== null) && !in_array($sTitleAttCode, $aAttCodes))
{
$aAttCodes = array_merge(array($sTitleAttCode), $aAttCodes);
}
// - Retrieving scope search
// Note : This do NOT apply to custom fields as the portal administrator is not supposed to know which objects will be put in the templates.
// It is the responsability of the template designer to write the right query so the user see only what he should.
$oScopeSearch = $oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sTargetObjectClass, UR_ACTION_READ);
$aInternalParams = array();
if ($oScopeSearch === null)
if (($oScopeSearch === null) && ($oTargetAttDef->GetEditClass() !== 'CustomFields'))
{
IssueLog::Info(__METHOD__ . ' at line ' . __LINE__ . ' : User #' . UserRights::GetUserId() . ' has no scope query for ' . $sTargetObjectClass . ' class.');
$oApp->abort(404, Dict::S('UI:ObjectDoesNotExist'));
@@ -827,6 +918,11 @@ class ObjectController extends AbstractController
{
$oSearch = $oScopeSearch;
}
elseif ($oTargetAttDef->GetEditClass() === 'CustomFields')
{
// Note : $oTemplateFieldSearch has been defined in the "Retrieving target object class from attcode" part, it is not available otherwise
$oSearch = $oTemplateFieldSearch;
}
// - Filtering objects to ignore
if (($aObjectIdsToIgnore !== null) && (is_array($aObjectIdsToIgnore)))
@@ -839,7 +935,7 @@ class ObjectController extends AbstractController
}
$oSearch->AddConditionExpression(new BinaryExpression(new FieldExpression('id', $oSearch->GetClassAlias()), 'NOT IN', new ListExpression($aExpressions)));
}
// - Adding query condition
$aInternalParams['this'] = $oHostObject;
if ($sQuery !== null)
@@ -851,7 +947,35 @@ class ObjectController extends AbstractController
$oAttDef = MetaModel::GetAttributeDef($sTargetObjectClass, $aAttCodes[$i]);
$sAttCode = (!$oAttDef->IsExternalKey()) ? $aAttCodes[$i] : $aAttCodes[$i] . '_friendlyname';
// Building expression for the current attcode
$oBinExpr = new BinaryExpression(new FieldExpression($sAttCode, $oSearch->GetClassAlias()), 'LIKE', new VariableExpression('re_query'));
// - For attributes that need conversion from their display value to storage value
// Note : This is dirty hack that will need to be refactored in the OQL core in order to be nicer and to be extended to other types such as dates etc...
if (($oAttDef instanceof AttributeEnum) || ($oAttDef instanceof AttributeFinalClass))
{
// Looking up storage value
$aMatchedCodes = array();
foreach ($oAttDef->GetAllowedValues() as $sValueCode => $sValueLabel)
{
if (stripos($sValueLabel, $sQuery) !== false)
{
$aMatchedCodes[] = $sValueCode;
}
}
// Building expression
if (!empty($aMatchedCodes))
{
$oEnumeratedListExpr = ListExpression::FromScalars($aMatchedCodes);
$oBinExpr = new BinaryExpression(new FieldExpression($sAttCode, $oSearch->GetClassAlias()), 'IN', $oEnumeratedListExpr);
}
else
{
$oBinExpr = new FalseExpression();
}
}
// - For regular attributs
else
{
$oBinExpr = new BinaryExpression(new FieldExpression($sAttCode, $oSearch->GetClassAlias()), 'LIKE', new VariableExpression('re_query'));
}
// Adding expression to the full expression (all attcodes)
if ($i === 0)
{
@@ -868,7 +992,17 @@ class ObjectController extends AbstractController
}
// - Intersecting with scope constraints
$oSearch = $oSearch->Intersect($oScopeSearch);
// Note : This do NOT apply to custom fields as the portal administrator is not supposed to know which objects will be put in the templates.
// It is the responsability of the template designer to write the right query so the user see only what he should.
if (($oScopeSearch !== null) && ($oTargetAttDef->GetEditClass() !== 'CustomFields'))
{
$oSearch = $oSearch->Intersect($oScopeSearch);
// - Allowing all data if necessary
if ($oScopeSearch->IsAllDataAllowed())
{
$oSearch->AllowAllData();
}
}
// Retrieving results
// - Preparing object set
@@ -946,7 +1080,7 @@ class ObjectController extends AbstractController
'sFormManagerData' => $sFormManagerData
)
);
if ($oRequest->isXmlHttpRequest())
{
$oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData);
@@ -1001,7 +1135,8 @@ class ObjectController extends AbstractController
// Retrieving host object for future DBSearch parameters
if ($sHostObjectId !== null)
{
$oHostObject = MetaModel::GetObject($sHostObjectClass, $sHostObjectId);
// Note : AllowAllData set to true here instead of checking scope's flag because we are displaying a value that has been set and validated
$oHostObject = MetaModel::GetObject($sHostObjectClass, $sHostObjectId, true, true);
}
else
{
@@ -1041,7 +1176,7 @@ class ObjectController extends AbstractController
// // - Retrieving class attribute list
// $aAttCodes = MetaModel::FlattenZList(MetaModel::GetZListItems($sTargetObjectClass, 'list'));
// // - Adding friendlyname attribute to the list is not already in it
// $sTitleAttrCode = MetaModel::GetFriendlyNameAttributeCode($sTargetObjectClass);
// $sTitleAttrCode = 'friendlyname';
// if (($sTitleAttrCode !== null) && !in_array($sTitleAttrCode, $aAttCodes))
// {
// $aAttCodes = array_merge(array($sTitleAttrCode), $aAttCodes);
@@ -1092,6 +1227,11 @@ class ObjectController extends AbstractController
// }
// - Intersecting with scope constraints
$oSearch = $oSearch->Intersect($oScopeSearch);
// - Allowing all data if necessary
if ($oScopeSearch->IsAllDataAllowed())
{
$oSearch->AllowAllData();
}
// Retrieving results
// - Preparing object set
@@ -1246,7 +1386,8 @@ class ObjectController extends AbstractController
}
}
$oResponse = $oApp->json($aData);
// Note : The Content-Type header is set to 'text/plain' in order to be IE9 compatible. Otherwise ('application/json') IE9 will download the response as a JSON file to the user computer...
$oResponse = $oApp->json($aData, 200, array('Content-Type' => 'text/plain'));
break;
case 'download':
@@ -1307,7 +1448,12 @@ class ObjectController extends AbstractController
}
// Building the search
$bIgnoreSilos = $oApp['scope_validator']->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass);
$oSearch = DBObjectSearch::FromOQL("SELECT " . $sObjectClass . " WHERE id IN ('" . implode("','", $aObjectIds) . "')");
if ($bIgnoreSilos === true)
{
$oSearch->AllowAllData();
}
$oSet = new DBObjectSet($oSearch);
$oSet->OptimizeColumnLoad($aObjectAttCodes);

View File

@@ -493,6 +493,9 @@ abstract class PortalBrick extends AbstractBrick
$this->SetDecorationClassNavigationMenu($optionalNodeValue);
}
break;
case 'tile_controller_action':
$this->SetTileControllerAction($oBrickSubNode->GetText(static::DEFAULT_TILE_CONTROLLER_ACTION));
break;
}
}

View File

@@ -29,6 +29,7 @@ use \MetaModel;
use \CMDBSource;
use \DBObject;
use \DBObjectSet;
use \DBSearch;
use \DBObjectSearch;
use \DBObjectSetComparator;
use \InlineImage;
@@ -49,6 +50,7 @@ class ObjectFormManager extends FormManager
const ENUM_MODE_VIEW = 'view';
const ENUM_MODE_EDIT = 'edit';
const ENUM_MODE_CREATE = 'create';
const ENUM_MODE_APPLY_STIMULUS = 'apply_stimulus';
protected $oApp;
protected $oObject;
@@ -92,7 +94,8 @@ class ObjectFormManager extends FormManager
}
else
{
$oObject = MetaModel::GetObject($sObjectClass, $aJson['formobject_id'], true);
// Note : AllowAllData set to true here instead of checking scope's flag because we are displaying a value that has been set and validated
$oObject = MetaModel::GetObject($sObjectClass, $aJson['formobject_id'], true, true);
}
$oFormManager->SetObject($oObject);
@@ -482,6 +485,18 @@ class ObjectFormManager extends FormManager
{
$oField->SetReadOnly(true);
}
// - Else if it's must change, we force it as not readonly and not hidden
elseif (($iFieldFlags & OPT_ATT_MUSTCHANGE) === OPT_ATT_MUSTCHANGE)
{
$oField->SetReadOnly(false);
$oField->SetHidden(false);
}
// - Else if it's must prompt, we force it as not readonly and not hidden
elseif (($iFieldFlags & OPT_ATT_MUSTPROMPT) === OPT_ATT_MUSTPROMPT)
{
$oField->SetReadOnly(false);
$oField->SetHidden(false);
}
else
{
// Normal field
@@ -516,6 +531,61 @@ class ObjectFormManager extends FormManager
$oField->SetInformationEndpoint($this->oApp['url_generator']->generate('p_object_get_informations_json'));
}
}
// - Field that require to apply scope on its DM OQL
if (in_array(get_class($oField), array('Combodo\\iTop\\Form\\Field\\SelectObjectField')))
{
if ($this->oApp !== null)
{
$oScopeOriginal = ($oField->GetSearch() !== null) ? $oField->GetSearch() : DBSearch::FromOQL($oAttDef->GetValuesDef()->GetFilterExpression());
$oScopeSearch = $this->oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $oScopeOriginal->GetClass(), UR_ACTION_READ);
if ($oScopeSearch === null)
{
IssueLog::Info(__METHOD__ . ' at line ' . __LINE__ . ' : User #' . UserRights::GetUserId() . ' has no scope query for ' . $oScopeOriginal->GetClass() . ' class.');
$this->oApp->abort(404, Dict::S('UI:ObjectDoesNotExist'));
}
$oScopeOriginal = $oScopeOriginal->Intersect($oScopeSearch);
// Note : This is to skip the silo restriction on the final query
if ($oScopeSearch->IsAllDataAllowed())
{
$oScopeOriginal->AllowAllData();
}
$oScopeOriginal->SetInternalParams(array('this' => $this->oObject));
$oField->SetSearch($oScopeOriginal);
}
}
// - Field that require processing on their subfields
if (in_array(get_class($oField), array('Combodo\\iTop\\Form\\Field\\SubFormField')))
{
$oSubForm = $oField->GetForm();
if ($oAttDef->GetEditClass() === 'CustomFields')
{
// Retrieving only user data fields (not the metadata fields of the template)
if ($oSubForm->HasField('user_data'))
{
$oUserDataField = $oSubForm->GetField('user_data');
$oUserDataForm = $oUserDataField->GetForm();
foreach ($oUserDataForm->GetFields() as $oCustomField)
{
// - Field that require a search endpoint (OQL based dropdown list fields)
if (in_array(get_class($oCustomField), array('Combodo\\iTop\\Form\\Field\\SelectObjectField')))
{
if ($this->oApp !== null)
{
$sSearchEndpoint = $this->oApp['url_generator']->generate('p_object_search_generic', array(
'sTargetAttCode' => $oAttDef->GetCode(),
'sHostObjectClass' => get_class($this->oObject),
'sHostObjectId' => ($this->oObject->IsNew()) ? null : $this->oObject->GetKey(),
'ar_token' => $this->GetActionRulesToken(),
));
$oCustomField->SetSearchEndpoint($sSearchEndpoint);
}
}
}
}
}
}
}
else
{
@@ -539,7 +609,7 @@ class ObjectFormManager extends FormManager
// Note : This snippet is inspired from AttributeLinkedSet::MakeFormField()
$aAttCodesToDisplay = ApplicationHelper::GetLoadedListFromClass($this->oApp, $oField->GetTargetClass(), 'list');
// - Adding friendlyname attribute to the list is not already in it
$sTitleAttCode = MetaModel::GetFriendlyNameAttributeCode($oField->GetTargetClass());
$sTitleAttCode = 'friendlyname';
if (($sTitleAttCode !== null) && !in_array($sTitleAttCode, $aAttCodesToDisplay))
{
$aAttCodesToDisplay = array_merge(array($sTitleAttCode), $aAttCodesToDisplay);
@@ -625,6 +695,87 @@ class ObjectFormManager extends FormManager
$this->oRenderer->SetForm($this->oForm);
}
/**
* Merging $this->aFormProperties with $aFormPropertiesToMerge. Merge only layout for now
*
* @param array $aFormPropertiesToMerge
* @throws Exception
*/
public function MergeFormProperties($aFormPropertiesToMerge)
{
if ($aFormPropertiesToMerge['layout'] !== null)
{
// Checking if we need to render the template from twig to html in order to parse the fields
if ($aFormPropertiesToMerge['layout']['type'] === 'twig')
{
// Creating sandbox twig env. to load and test the custom form template
$oTwig = new \Twig_Environment(new \Twig_Loader_String());
$sRendered = $oTwig->render($aFormPropertiesToMerge['layout']['content'], array('oRenderer' => $this->oRenderer, 'oObject' => $this->oObject));
}
else
{
$sRendered = $aFormPropertiesToMerge['layout']['content'];
}
// Parsing rendered template to find the fields
$oHtmlDocument = new \DOMDocument();
$oHtmlDocument->loadHTML('<root>' . $sRendered . '</root>');
// Adding fields to the list
$oXPath = new \DOMXPath($oHtmlDocument);
foreach ($oXPath->query('//div[@class="form_field"][@data-field-id]') as $oFieldNode)
{
$sFieldId = $oFieldNode->getAttribute('data-field-id');
$sFieldFlags = $oFieldNode->getAttribute('data-field-flags');
// $iFieldFlags = OPT_ATT_NORMAL;
// // Checking if field has form_path, if not, we add it
// if (!$oFieldNode->hasAttribute('data-form-path'))
// {
// $oFieldNode->setAttribute('data-form-path', $oForm->GetId());
// }
// Merging only fields that are already in the form
if (array_key_exists($sFieldId, $this->aFormProperties['fields']))
{
// Settings field flags from the data-field-flags attribute
foreach (explode(' ', $sFieldFlags) as $sFieldFlag)
{
if ($sFieldFlag !== '')
{
$sConst = 'OPT_ATT_' . strtoupper(str_replace('_', '', $sFieldFlag));
if (defined($sConst))
{
switch ($sConst)
{
case 'OPT_ATT_SLAVE':
case 'OPT_ATT_HIDDEN':
if (!array_key_exists($sFieldId, $this->aFormProperties['fields']))
{
$this->aFormProperties['fields'][$sFieldId] = array();
}
$this->aFormProperties['fields'][$sFieldId]['hidden'] = true;
break;
case 'OPT_ATT_READONLY':
if (!array_key_exists($sFieldId, $this->aFormProperties['fields']))
{
$this->aFormProperties['fields'][$sFieldId] = array();
}
$this->aFormProperties['fields'][$sFieldId]['read_only'] = true;
break;
}
}
else
{
IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Flag "' . $sFieldFlag . '" is not valid for field [@data-field-id="' . $sFieldId . '"] in form[@id="' . $aFormPropertiesToMerge['id'] . '"]');
throw new Exception('Flag "' . $sFieldFlag . '" is not valid for field [@data-field-id="' . $sFieldId . '"] in form[@id="' . $aFormPropertiesToMerge['id'] . '"]');
}
}
}
}
}
}
}
/**
* Calls all form fields OnCancel method in order to delegate them the cleanup;
*
@@ -791,7 +942,8 @@ class ObjectFormManager extends FormManager
// LinkedSet
if (!$oAttDef->IsIndirect())
{
$oLinkedObject = MetaModel::GetObject($sTargetClass, abs($iTargetId));
// Note : AllowAllData set to true here instead of checking scope's flag because we are displaying a value that has been set and validated
$oLinkedObject = MetaModel::GetObject($sTargetClass, abs($iTargetId), true, true);
$oValueSet->AddObject($oLinkedObject);
}
// LinkedSetIndirect
@@ -807,7 +959,8 @@ class ObjectFormManager extends FormManager
// Existing relation
else
{
$oLink = MetaModel::GetObject($sTargetClass, $iTargetId);
// Note : AllowAllData set to true here instead of checking scope's flag because we are displaying a value that has been set and validated
$oLink = MetaModel::GetObject($sTargetClass, $iTargetId, true, true);
}
$oValueSet->AddObject($oLink);
}
@@ -860,7 +1013,7 @@ class ObjectFormManager extends FormManager
else
{
$this->oObject->Set($sAttCode, $value);
}
}
}
}
$this->oObject->DoComputeValues();

View File

@@ -202,6 +202,7 @@ class ApplicationHelper
return Dict::S($sStringCode, $sDefault, $bUserLanguageOnly);
})
);
// A filter to format a string via the Dict::Format function
// Usage in twig : {{ 'String:ToTranslate'|dict_format() }}
$oApp['twig']->addFilter(new Twig_SimpleFilter('dict_format', function($sStringCode, $sParam01 = null, $sParam02 = null, $sParam03 = null, $sParam04 = null)
@@ -209,10 +210,12 @@ class ApplicationHelper
return Dict::Format($sStringCode, $sParam01, $sParam02, $sParam03, $sParam04);
})
);
// Filters to enable base64 encode/decode
// Usage in twig : {{ 'String to encode'|base64_encode }}
$oApp['twig']->addFilter(new Twig_SimpleFilter('base64_encode', 'base64_encode'));
$oApp['twig']->addFilter(new Twig_SimpleFilter('base64_decode', 'base64_decode'));
// Filters to enable json decode (encode already exists)
// Usage in twig : {{ aSomeArray|json_decode }}
$oApp['twig']->addFilter(new Twig_SimpleFilter('json_decode', function($sJsonString, $bAssoc = false)
@@ -220,6 +223,21 @@ class ApplicationHelper
return json_decode($sJsonString, $bAssoc);
})
);
// Filter to add itopversion to an url
$oApp['twig']->addFilter(new Twig_SimpleFilter('add_itop_version', function($sUrl)
{
if (strpos($sUrl, '?') === false)
{
$sUrl = $sUrl . "?itopversion=" . ITOP_VERSION;
}
else
{
$sUrl = $sUrl . "&itopversion=" . ITOP_VERSION;
}
return $sUrl;
}));
}
/**

View File

@@ -4,7 +4,7 @@
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -23,6 +23,7 @@ use \Exception;
use \Silex\Application;
use \DOMNodeList;
use \DOMFormatException;
use \UserRights;
use \DBObject;
use \DBSearch;
use \DBObjectSet;
@@ -42,6 +43,7 @@ class ContextManipulatorHelper
const ENUM_RULE_CALLBACK_OPEN_EDIT = 'edit';
const DEFAULT_RULE_CALLBACK_OPEN = self::ENUM_RULE_CALLBACK_OPEN_VIEW;
protected $oApp;
protected $aRules;
public function __construct()
@@ -59,7 +61,7 @@ class ContextManipulatorHelper
public function Init(DOMNodeList $oNodes)
{
$this->aRules = array();
// Iterating over the scope nodes
foreach ($oNodes as $oRuleNode)
{
@@ -181,6 +183,11 @@ class ContextManipulatorHelper
}
}
public function SetApp($oApp)
{
$this->oApp = $oApp;
}
/**
* Returns a hash array of rules
*
@@ -222,7 +229,7 @@ class ContextManipulatorHelper
* ...
* )
* )
*
*
* @param array $aData
* @param DBObject $oObject
*/
@@ -290,6 +297,13 @@ class ContextManipulatorHelper
}
}
// Checking for silos
$oScopeSearch = $this->oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sSearchClass, UR_ACTION_READ);
if ($oScopeSearch->IsAllDataAllowed())
{
$oSearch->AllowAllData();
}
// Retrieving source object(s) and applying rules
$oSet = new DBObjectSet($oSearch, array(), $aSearchParams);
while ($oSourceObject = $oSet->Fetch())

View File

@@ -36,6 +36,7 @@ class ScopeValidatorHelper
const ENUM_TYPE_ALLOW = 'allow';
const ENUM_TYPE_RESTRICT = 'restrict';
const DEFAULT_GENERATED_CLASS = 'PortalScopesValues';
const DEFAULT_IGNORE_SILOS = false;
protected $sCachePath;
protected $sFilename;
@@ -179,6 +180,9 @@ class ScopeValidatorHelper
// Retrieving the edit query
$oOqlEditNode = $oScopeNode->GetOptionalElement('oql_edit');
$sOqlEdit = ( ($oOqlEditNode !== null) && ($oOqlEditNode->GetText() !== null) ) ? $oOqlEditNode->GetText() : null;
// Retrieving ignore allowed org flag
$oIgnoreSilosNode = $oScopeNode->GetOptionalElement('ignore_silos');
$bIgnoreSilos = ( ($oIgnoreSilosNode !== null) && ($oIgnoreSilosNode->GetText() === 'true') ) ? true : static::DEFAULT_IGNORE_SILOS;
// Retrieving profiles for the scope
$oProfilesNode = $oScopeNode->GetOptionalElement('allowed_profiles');
@@ -221,13 +225,20 @@ class ScopeValidatorHelper
$oExistingFilter = DBSearch::FromOQL($aProfiles[$sMatrixPrefix . static::ENUM_MODE_READ][$sOqlViewType]);
$aFilters = array($oExistingFilter, $oViewFilter);
$oResFilter = new DBUnionSearch($aFilters);
// Applying ignore_silos flag on result filter if necessary (As the union will remove it if it is not on all sub-queries)
if ($aProfiles[$sMatrixPrefix . static::ENUM_MODE_READ]['ignore_silos'] === true)
{
$bIgnoreSilos = true;
}
}
else
{
$oResFilter = $oViewFilter;
}
$aProfiles[$sMatrixPrefix . static::ENUM_MODE_READ] = array(
$sOqlViewType => $oResFilter->ToOQL()
$sOqlViewType => $oResFilter->ToOQL(),
'ignore_silos' => $bIgnoreSilos
);
// - Edit query
if ($sOqlEdit !== null)
@@ -264,7 +275,8 @@ class ScopeValidatorHelper
$oResFilter = $oEditFilter;
}
$aProfiles[$sMatrixPrefix . static::ENUM_MODE_WRITE] = array(
$sOqlViewType => $oResFilter->ToOQL()
$sOqlViewType => $oResFilter->ToOQL(),
'ignore_silos' => $bIgnoreSilos
);
}
}
@@ -273,7 +285,7 @@ class ScopeValidatorHelper
$aProfileClasses[] = $sClass;
}
}
// Filling the array with missing classes from MetaModel, so we can have an inheritance principle on the scope
// For each class explicitly given in the scopes, we check if its child classes were also in the scope :
// If not, we add them with the same OQL
@@ -295,10 +307,14 @@ class ScopeValidatorHelper
$aTmpProfile = $aProfiles[$iProfileId . '_' . $sProfileClass . '_' . $sAction];
foreach ($aTmpProfile as $sType => $sOql)
{
$oTmpFilter = DBSearch::FromOQL($sOql);
$oTmpFilter->ChangeClass($sChildClass);
// IF condition is just to skip the 'ignore_silos' flag
if (in_array($sType, array(static::ENUM_TYPE_ALLOW, static::ENUM_TYPE_RESTRICT)))
{
$oTmpFilter = DBSearch::FromOQL($sOql);
$oTmpFilter->ChangeClass($sChildClass);
$aTmpProfile[$sType] = $oTmpFilter->ToOQL();
$aTmpProfile[$sType] = $oTmpFilter->ToOQL();
}
}
$aProfiles[$iProfileId . '_' . $sChildClass . '_' . $sAction] = $aTmpProfile;
@@ -471,6 +487,7 @@ class ScopeValidatorHelper
$oSearch = null;
$aAllowSearches = array();
$aRestrictSearches = array();
$bIgnoreSilos = static::DEFAULT_IGNORE_SILOS;
// Checking the default mode
if ($iAction === null)
@@ -498,6 +515,11 @@ class ScopeValidatorHelper
{
$aRestrictSearches[] = DBSearch::FromOQL($aProfileMatrix['restrict']);
}
// If a profile should ignore allowed org, we set it for all its queries no matter the profile
if (isset($aProfileMatrix['ignore_silos']) && $aProfileMatrix['ignore_silos'] === true)
{
$bIgnoreSilos = true;
}
}
}
@@ -514,10 +536,47 @@ class ScopeValidatorHelper
$oSearch = new DBUnionSearch($aAllowSearches);
$oSearch = $oSearch->RemoveDuplicateQueries();
}
if ($bIgnoreSilos === true)
{
$oSearch->AllowAllData();
}
return $oSearch;
}
/**
* Returns true if at least one of the $aProfiles has the ignore_silos flag set to true for the $sClass.
*
* @param array $aProfiles
* @param string $sClass
* @return boolean
*/
public function IsAllDataAllowedForScope($aProfiles, $sClass)
{
$bIgnoreSilos = false;
// Iterating on profiles to retrieving the different OQLs parts
foreach ($aProfiles as $sProfile)
{
// Retrieving matrix informtions
$iProfileId = $this->GetProfileIdFromProfileName($sProfile);
// Retrieving profile OQLs
$sScopeValuesClass = $this->sGeneratedClass;
$aProfileMatrix = $sScopeValuesClass::GetProfileScope($iProfileId, $sClass, static::ENUM_MODE_READ);
if ($aProfileMatrix !== null)
{
// If a profile should ignore allowed org, we set it for all its queries no matter the profile
if (isset($aProfileMatrix['ignore_silos']) && $aProfileMatrix['ignore_silos'] === true)
{
$bIgnoreSilos = true;
}
}
}
return $bIgnoreSilos;
}
/**
* Returns the profile id from a string being either a constant or its name.
*

View File

@@ -112,7 +112,7 @@ class SecurityHelper
// Checking if the cmdbAbstractObject exists if id is specified
if ($sObjectId !== null)
{
$oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */);
$oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */, $oApp['scope_validator']->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass));
if ($oObject === null)
{
if ($oApp['debug'])

View File

@@ -44,6 +44,7 @@ class UrlGenerator extends SymfonyUrlGenerator
*/
public function generate($name, $parameters = array(), $referenceType = SymfonyUrlGenerator::ABSOLUTE_PATH)
{
// Mandatory parameters
$sExecModule = utils::ReadParam('exec_module', '', false, 'string');
$sExecPage = utils::ReadParam('exec_page', '', false, 'string');
if ($sExecModule !== '' && $sExecPage !== '')
@@ -52,6 +53,18 @@ class UrlGenerator extends SymfonyUrlGenerator
$parameters['exec_page'] = $sExecPage;
}
// Optional parameters
$sEnvSwitch = utils::ReadParam('env_switch', '', false, 'string');
if ($sEnvSwitch !== '')
{
$parameters['env_switch'] = $sEnvSwitch;
}
$sDebug = utils::ReadParam('debug', '', false, 'string');
if ($sDebug !== '')
{
$parameters['debug'] = $sDebug;
}
return parent::generate($name, $parameters, $referenceType);
}

View File

@@ -38,6 +38,7 @@ class ContextManipulatorServiceProvider implements ServiceProviderInterface
$oApp->flush();
$oContextManipulatorHelper = new ContextManipulatorHelper();
$oContextManipulatorHelper->SetApp($oApp);
return $oContextManipulatorHelper;
});

View File

@@ -73,11 +73,11 @@
// For the same reason, tooltip widget is created in "drawCallback" instead of here.
if( (data.tooltip !== undefined) && (data.tooltip !== ''))
{
cellElem.html( $('<span></span>').attr('title', data.tooltip).attr('data-toggle', 'tooltip').text(data.name).prop('outerHTML') );
cellElem.html( $('<span></span>').attr('title', data.tooltip).attr('data-toggle', 'tooltip').html(data.name).prop('outerHTML') );
}
else
{
cellElem.text(data.name);
cellElem.html(data.name);
}
// Building actions
@@ -97,11 +97,11 @@
break;
case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_CREATE_FROM_THIS') }}':
url = levelPrimaryAction.url.replace(/-objectClass-/, data.class).replace(/-objectId-/, data.id);
url = addParameterToUrl(url, 'ar_token', data.action_rules_token[levelPrimaryAction.type]);
url = AddParameterToUrl(url, 'ar_token', data.action_rules_token[levelPrimaryAction.type]);
cellElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
break;
default:
console.log('Action "'+levelPrimaryAction.type+'" not implemented');
//console.log('Action "'+levelPrimaryAction.type+'" not implemented');
break;
}
@@ -150,11 +150,11 @@
break;
case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_CREATE_FROM_THIS') }}':
url = action.url.replace(/-objectClass-/, data.class).replace(/-objectId-/, data.id);
url = addParameterToUrl(url, 'ar_token', data.action_rules_token[action.type]);
url = AddParameterToUrl(url, 'ar_token', data.action_rules_token[action.type]);
actionElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
break;
default:
console.log('Action "'+action.type+'" not implemented for secondary action');
//console.log('Action "'+action.type+'" not implemented for secondary action');
break;
}
@@ -201,7 +201,6 @@
"type": "html",
"data": oLevelsProperties[sKey].alias+".fields."+oLevelsProperties[sKey].fields[i].code
});
console.log(oLevelsProperties[sKey].fields[i].visible);
}
}
}
@@ -240,6 +239,7 @@
"displayLength": {{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::DEFAULT_COUNT_PER_PAGE_LIST') }},
"dom": '<"row"<"col-sm-6"l><"col-sm-6"<f><"visible-xs"p>>>t<"row"<"col-sm-6"i><"col-sm-6"p>>',
"columns": getColumnsDefinition(),
"order": [],
"drawCallback": function(settings){
// Tooltip has to been created here, as the render callback only returns a string, not an object.
$(this).find('[data-toggle="tooltip"]').tooltip({container: 'body', html: true, trigger: 'hover', placement: 'right'}); // container option is necessary when in a table

View File

@@ -25,7 +25,7 @@
</div>
</div>
<div class="col-xs-8 col-sm-10 col-lg-11 text-right">
<label>Filtrer :<input type="search" class="form-control input-sm" id="brick_search_field" placeholder="" aria-controls="brick_main_table" value="{{ sSearchValue }}"></label>
<label>{{ 'Portal:Datatables:Language:Search'|dict_s }}<input type="search" class="form-control input-sm" id="brick_search_field" placeholder="" aria-controls="brick_main_table" value="{{ sSearchValue }}"></label>
</div>
</div>
<ul class="list-group" id="brick_content_tree" data-level-id="L">
@@ -223,11 +223,11 @@
break;
case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_CREATE_FROM_THIS') }}':
url = levelPrimaryAction.url.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);
url = addParameterToUrl(url, 'ar_token', item.action_rules_token[levelPrimaryAction.type]);
url = AddParameterToUrl(url, 'ar_token', item.action_rules_token[levelPrimaryAction.type]);
aElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
break;
default:
console.log('Action "'+levelPrimaryAction.type+'" not implemented for primary action');
//console.log('Action "'+levelPrimaryAction.type+'" not implemented for primary action');
break;
}
@@ -275,11 +275,11 @@
break;
case '{{ constant('Combodo\\iTop\\Portal\\Brick\\BrowseBrick::ENUM_ACTION_CREATE_FROM_THIS') }}':
url = action.url.replace(/-objectClass-/, item.class).replace(/-objectId-/, item.id);
url = addParameterToUrl(url, 'ar_token', item.action_rules_token[action.type]);
url = AddParameterToUrl(url, 'ar_token', item.action_rules_token[action.type]);
actionElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
break;
default:
console.log('Action "'+action.type+'" not implemented for secondary action');
//console.log('Action "'+action.type+'" not implemented for secondary action');
break;
}

View File

@@ -17,24 +17,35 @@
{% endblock %}
{% block pMainContentHolder%}
{% set iTableCount = 0 %}
{% if aGroupingAreasData|length > 0 %}
{% for aAreaData in aGroupingAreasData %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{ aAreaData.sTitle }}</h3>
{% if aAreaData.iItemsCount > 0 %}
{% set iTableCount = iTableCount + 1 %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{ aAreaData.sTitle }}</h3>
</div>
<div class="panel-body">
{# We decided not to show empty tables anymore #}
{#
{% if aAreaData.iItemsCount > 0 %}
#}
<table id="table-{{ aAreaData.sId }}" class="table table-striped table-bordered responsive" width="100%"></table>
{#
{% else %}
<div class="text-center">
{{ 'Brick:Portal:Manage:Table:NoData'|dict_s }}
</div>
{% endif %}
#}
</div>
</div>
<div class="panel-body">
{% if aAreaData.iItemsCount > 0 %}
<table id="table-{{ aAreaData.sId }}" class="table table-striped table-bordered responsive" width="100%"></table>
{% else %}
<div class="text-center">
{{ 'Brick:Portal:Manage:Table:NoData'|dict_s }}
</div>
{% endif %}
</div>
</div>
{% endif %}
{% endfor %}
{% else %}
{% endif %}
{% if iTableCount == 0 %}
<div class="panel panel-default">
<div class="panel-body">
<h3 class="text-center">{{ 'Brick:Portal:Manage:Table:NoData'|dict_s }}</h3>
@@ -69,12 +80,12 @@
{
var tableProperties = columnsProperties[tableName];
if(tableProperties === undefined)
if(tableProperties === undefined && window.console)
{
console.log('Could not retrieve columns properties for table "'+tableName+'"');
return false;
}
if(rawData[tableName] === undefined)
if(rawData[tableName] === undefined && window.console)
{
console.log('Could not retrieve data for table "'+tableName+'"');
return false;
@@ -103,7 +114,7 @@
// Preparing the cell data
cellElem = (itemActions.length > 0) ? $('<a></a>') : $('<span></span>');
cellElem.text(row.attributes[att_code].value);
cellElem.html(row.attributes[att_code].value);
// Building actions
if(itemActions.length > 0)
{
@@ -120,7 +131,7 @@
cellElem.attr('data-toggle', 'modal').attr('data-target', '#modal-for-all').attr('href', url);
break;
default:
console.log('Action "'+itemPrimaryAction+'" not implemented');
//console.log('Action "'+itemPrimaryAction+'" not implemented');
break;
}
@@ -227,7 +238,7 @@
// Note : The '.off()' call is to unbind event from DataTables that where triggered before we could intercept anything
$('#table-{{ sAreaId }}_filter input').off().on('keyup', function(){
var me = this;
console.log('here');
clearTimeout(oKeyTimeout);
oKeyTimeout = setTimeout(function() {
oTable{{ sAreaId }}.search(me.value.latinise()).draw();

View File

@@ -24,7 +24,7 @@
{% if form.buttons is defined and form.buttons.transitions is defined and form.buttons.transitions|length > 0 %}
<div class="form_btn_transitions">
{% for sStimulusCode, sStimulusLabel in form.buttons.transitions %}
<button class="btn btn-default form_btn_transition" type="submit" name="stimulus_code" value="{{ sStimulusCode }}">{{ sStimulusLabel }}</button>
<button class="btn btn-primary form_btn_transition" type="submit" name="stimulus_code" value="{{ sStimulusCode }}">{{ sStimulusLabel }}</button>
{% endfor %}
</div>
{% endif %}
@@ -67,60 +67,63 @@
// Sticky buttons handler
{% if sMode != 'view' %}
// Note : This pattern if to prevent performance issues
// - Cloning buttons
var oNormalRegularButtons_{{ sFormIdSanitized }} = $('#{{ sFormId }} .form_btn_regular');
var oStickyRegularButtons_{{ sFormIdSanitized }} = oNormalRegularButtons_{{ sFormIdSanitized }}.clone(true, true);
oStickyRegularButtons_{{ sFormIdSanitized }}.addClass('sticky');
if(oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_submit span.glyphicon').length > 0)
if( $('#{{ sFormId }} .form_btn_regular button').length > 0 )
{
oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_submit').html( oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_submit span.glyphicon')[0].outerHTML );
}
if(oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_cancel span.glyphicon').length > 0)
{
oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_cancel').html( oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_cancel span.glyphicon')[0].outerHTML );
}
$('#{{ sFormId }}').closest({% if tIsModal == true %}'.modal'{% else %}'#main-content'{% endif %}).append(oStickyRegularButtons_{{ sFormIdSanitized }});
// - Global timeout for any
var oScrollTimeout;
// - Scroll handler
scrollHandler_{{ sFormIdSanitized }} = function () {
if($('#{{ sFormId }} .form_buttons').visible())
// Note : This pattern if to prevent performance issues
// - Cloning buttons
var oNormalRegularButtons_{{ sFormIdSanitized }} = $('#{{ sFormId }} .form_btn_regular');
var oStickyRegularButtons_{{ sFormIdSanitized }} = oNormalRegularButtons_{{ sFormIdSanitized }}.clone(true, true);
oStickyRegularButtons_{{ sFormIdSanitized }}.addClass('sticky');
if(oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_submit span.glyphicon').length > 0)
{
oStickyRegularButtons_{{ sFormIdSanitized }}.addClass('closed');
oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_submit').html( oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_submit span.glyphicon')[0].outerHTML );
}
else
if(oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_cancel span.glyphicon').length > 0)
{
oStickyRegularButtons_{{ sFormIdSanitized }}.removeClass('closed');
oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_cancel').html( oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_cancel span.glyphicon')[0].outerHTML );
}
};
// - Event binding for scroll
$({% if tIsModal == true %}'.modal.in'{% else %}window{% endif %}).off('scroll').on('scroll', function () {
if (oScrollTimeout) {
// Clear the timeout, if one is pending
clearTimeout(oScrollTimeout);
oScrollTimeout = null;
}
oScrollTimeout = setTimeout(scrollHandler_{{ sFormIdSanitized }}, 50);
});
// - Event binding for linkedset collapse
$({% if tIsModal == true %}'.modal.in'{% else %}window{% endif %}).off('shown.bs.collapse hidden.bs.collapse').on('shown.bs.collapse hidden.bs.collapse', function () {
scrollHandler_{{ sFormIdSanitized }}();
});
// - Event binding for form building / updating
// Note : We do not want to 'off' the event or it will remove listeners from the widget
oFieldSet_{{ sFormIdSanitized }}.on('form_built', function(oEvent){
scrollHandler_{{ sFormIdSanitized }}();
});
// - Initial test
setTimeout(function(){ scrollHandler_{{ sFormIdSanitized }}(); }, 400);
// Remove sticky button when closing modal
$('#{{ sFormId }}').closest('.modal').on('hide.bs.modal', function () {
oStickyRegularButtons_{{ sFormIdSanitized }}.remove();
});
$('#{{ sFormId }}').closest({% if tIsModal == true %}'.modal'{% else %}'#main-content'{% endif %}).append(oStickyRegularButtons_{{ sFormIdSanitized }});
// - Global timeout for any
var oScrollTimeout;
// - Scroll handler
scrollHandler_{{ sFormIdSanitized }} = function () {
if($('#{{ sFormId }} .form_buttons').visible())
{
oStickyRegularButtons_{{ sFormIdSanitized }}.addClass('closed');
}
else
{
oStickyRegularButtons_{{ sFormIdSanitized }}.removeClass('closed');
}
};
// - Event binding for scroll
$({% if tIsModal == true %}'.modal.in'{% else %}window{% endif %}).off('scroll').on('scroll', function () {
if (oScrollTimeout) {
// Clear the timeout, if one is pending
clearTimeout(oScrollTimeout);
oScrollTimeout = null;
}
oScrollTimeout = setTimeout(scrollHandler_{{ sFormIdSanitized }}, 50);
});
// - Event binding for linkedset collapse
$({% if tIsModal == true %}'.modal.in'{% else %}window{% endif %}).off('shown.bs.collapse hidden.bs.collapse').on('shown.bs.collapse hidden.bs.collapse', function () {
scrollHandler_{{ sFormIdSanitized }}();
});
// - Event binding for form building / updating
// Note : We do not want to 'off' the event or it will remove listeners from the widget
oFieldSet_{{ sFormIdSanitized }}.on('form_built', function(oEvent){
scrollHandler_{{ sFormIdSanitized }}();
});
// - Initial test
setTimeout(function(){ scrollHandler_{{ sFormIdSanitized }}(); }, 400);
// Remove sticky button when closing modal
$('#{{ sFormId }}').closest('.modal').on('hide.bs.modal', function () {
oStickyRegularButtons_{{ sFormIdSanitized }}.remove();
});
}
{% endif %}
{% if tIsModal == true %}

View File

@@ -92,7 +92,7 @@
$(document).ready(function(){
showTableLoader();
// Note : Those options should be externalized in an library so we can use them on any DataTables for the portal.
// We would just have to override / complete the necessary elements
oTable = $('#{{ sTableId }}').DataTable({
@@ -152,6 +152,8 @@
"url": "{{ app.url_generator.generate('p_object_search_from_attribute', {'sTargetAttCode': sTargetAttCode, 'sHostObjectClass': sHostObjectClass, 'sHostObjectId': sHostObjectId, 'ar_token': sActionRulesToken})|raw }}",
"type": "POST",
"data": function(d){
d.sFormPath = '{{ aSource.sFormPath }}';
d.sFieldId = '{{ aSource.sFieldId }}';
d.aObjectIdsToIgnore = {{ aSource.aObjectIdsToIgnore|json_encode()|raw }};
d.iPageNumber = Math.floor(d.start/d.length) + 1;
d.iCountPerPage = d.length;
@@ -168,6 +170,24 @@
// Retrieving values from source form
d.current_values = $('[data-form-path="{{aSource.sFormPath}}"][data-field-id="{{aSource.sFieldId}}"]').closest('.portal_form_handler').portal_form_handler('getCurrentValues');
{% endif %}
},
"error": function(oData, sError, sThrow){
if(oData.responseJSON !== undefined && oData.responseJSON !== null)
{
var oResponse = oData.responseJSON;
// If we encounter an error
if(oResponse.exception !== undefined)
{
// Note : This could be refactored for a global use
$('#{{ sTableId }}').closest('.modal').html( $('#modal-for-alert').html() );
var oModalElem = $('#{{ sTableId }}').closest('.modal');
oModalElem.find('.modal-title').html(oResponse.error_title);
oModalElem.find('.modal-body .alert').html(oResponse.error_message)
.removeClass('alert-success alert-info alert-warning alert-danger')
.addClass('alert-danger');
oModalElem.modal('show');
}
}
}
}
});

View File

@@ -8,7 +8,7 @@
{% if form.buttons is defined and form.buttons.links is defined %}
<div class="form_btn_transitions">
{% for aLink in form.buttons.links %}
<a class="btn btn-default" href="{{ aLink.url }}">{{ aLink.label }}</a>
<a class="btn btn-primary" href="{{ aLink.url }}">{{ aLink.label }}</a>
{% endfor %}
</div>
{% endif %}

View File

@@ -43,7 +43,7 @@
<div class="col-sm-6">
<div class="panel panel-default user_profile_picture">
<div class="panel-heading">
<h3 class="panel-title">Photo</h3>
<h3 class="panel-title">{{ 'Brick:Portal:UserProfile:Photo:Title'|dict_s }}</h3>
</div>
<div class="panel-body" style="position: relative;">
<div class="form_alerts">
@@ -189,11 +189,11 @@
});
// - Undo button
/*$('#user-profile-wrapper .actions .btn_undo').on('click', function(oEvent){
console.log('Picture undo trigger');
//console.log('Picture undo trigger');
});*/
// - Reset button
$('#user-profile-wrapper .actions .btn_reset').on('click', function(oEvent){
console.log('Picture reset trigger');
//console.log('Picture reset trigger');
});
// Submit button

View File

@@ -0,0 +1,6 @@
<div class="content_loader">
<div class="icon glyphicon glyphicon-refresh"></div>
<div class="message">
{{ 'Page:PleaseWait'|dict_s }}
</div>
</div>

View File

@@ -22,87 +22,99 @@
{# This block can be used to add your own meta tags by extending the default template #}
{% block pPageExtraMetas %}
{% endblock %}
<title>{% block pPageTitle %}{% if sPageTitle is defined and sPageTitle is not null %}{{ sPageTitle }} - iTop{% else %}{{ 'Page:DefaultTitle'|dict_s }}{% endif %}{% endblock %}</title>
<link rel="shortcut icon" href="{{ app['combodo.absolute_url'] }}images/favicon.ico?itopversion=$ITOP_VERSION$" />
<title>{% block pPageTitle %}{% if sPageTitle is defined and sPageTitle is not null %}{{ sPageTitle }} - {{ constant('ITOP_APPLICATION') }}{% else %}{{ 'Page:DefaultTitle'|dict_s }}{% endif %}{% endblock %}</title>
<link rel="shortcut icon" href="{{ app['combodo.absolute_url'] ~ 'images/favicon.ico'|add_itop_version }}" />
{% block pPageStylesheets %}
{# First bootstrap core, lib themes, then bootstrap theme, portal adjustements #}
<link href="{{ app['combodo.portal.base.absolute_url'] }}lib/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/bootstrap/css/bootstrap.min.css'|add_itop_version }}" rel="stylesheet">
{# - Bootstrap Datetime picker #}
<link href="{{ app['combodo.portal.base.absolute_url'] }}lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css'|add_itop_version }}" rel="stylesheet">
{# - Datatables #}
<link href="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/css/dataTables.bootstrap.min.css" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/css/fixedHeader.bootstrap.min.css" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/css/responsive.bootstrap.min.css" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/css/scroller.bootstrap.min.css" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/css/select.bootstrap.min.css" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/css/select.dataTables.min.css" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/css/dataTables.bootstrap.min.css'|add_itop_version }}" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/css/fixedHeader.bootstrap.min.css'|add_itop_version }}" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/css/responsive.bootstrap.min.css'|add_itop_version }}" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/css/scroller.bootstrap.min.css'|add_itop_version }}" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/css/select.bootstrap.min.css'|add_itop_version }}" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/css/select.dataTables.min.css'|add_itop_version }}" rel="stylesheet">
{# - Font Combodo #}
<link href="{{ app['combodo.absolute_url'] }}css/font-combodo/font-combodo.css" rel="stylesheet">
<link href="{{ app['combodo.absolute_url'] ~ 'css/font-combodo/font-combodo.css'|add_itop_version }}" rel="stylesheet">
{# - Font awesome #}
<link href="{{ app['combodo.portal.base.absolute_url'] }}lib/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/font-awesome/css/font-awesome.min.css'|add_itop_version }}" rel="stylesheet">
{# - Misc libs #}
<link href="{{ app['combodo.portal.base.absolute_url'] }}lib/typeahead/css/typeaheadjs.bootstrap.css" rel="stylesheet">
<link href="{{ app['combodo.absolute_url'] }}css/magnific-popup.css" rel="stylesheet">
<link href="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/typeahead/css/typeaheadjs.bootstrap.css'|add_itop_version }}" rel="stylesheet">
<link href="{{ app['combodo.absolute_url'] ~ 'css/magnific-popup.css'|add_itop_version }}" rel="stylesheet">
{# - Bootstrap theme #}
<link href="{{ app['combodo.portal.instance.conf'].properties.themes.bootstrap }}" rel="stylesheet" id="css_bootstrap_theme">
<link href="{{ app['combodo.portal.instance.conf'].properties.themes.bootstrap|add_itop_version }}" rel="stylesheet" id="css_bootstrap_theme">
{# - Portal adjustments for BS theme #}
<link href="{{ app['combodo.portal.instance.conf'].properties.themes.portal }}" rel="stylesheet" id="css_portal">
<link href="{{ app['combodo.portal.instance.conf'].properties.themes.portal|add_itop_version }}" rel="stylesheet" id="css_portal">
{# Custom CSS that is supposed to do adjustments to the portal #}
{% if app['combodo.portal.instance.conf'].properties.themes.custom is defined %}
<link href="{{ app['combodo.portal.instance.conf'].properties.themes.custom }}" rel="stylesheet">
<link href="{{ app['combodo.portal.instance.conf'].properties.themes.custom|add_itop_version }}" rel="stylesheet">
{% endif %}
{# Others CSS that will come after the theme/portal/custom, in an undefined order #}
{% if app['combodo.portal.instance.conf'].properties.themes.others is defined %}
{% for theme in app['combodo.portal.instance.conf'].properties.themes.others %}
<link href="{{ theme }}" rel="stylesheet">
<link href="{{ theme|add_itop_version }}" rel="stylesheet">
{% endfor %}
{% endif %}
{% endblock %}
{% block pPageScripts %}
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/jquery/jquery-1.11.3.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/jquery-ui-1.10.3.custom.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/jquery.magnific-popup.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/jquery.fileupload.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/latinise/latinise.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/jquery/jquery-1.11.3.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/jquery-ui-1.10.3.custom.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/jquery.magnific-popup.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/jquery.iframe-transport.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/jquery.fileupload.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/bootstrap/js/bootstrap.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/latinise/latinise.min.js'|add_itop_version }}"></script>
{# Visible.js to check if an element is visible on screen #}
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/jquery-visible/js/jquery.visible.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/jquery-visible/js/jquery.visible.min.js'|add_itop_version }}"></script>
{# Base64.js #}
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/jquery-base64/js/jquery.base64.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/jquery-base64/js/jquery.base64.min.js'|add_itop_version }}"></script>
{# Moment.js #}
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/moment/js/moment.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/moment/js/moment.min.js'|add_itop_version }}"></script>
{# Datatables #}
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/js/dataTables.bootstrap.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/js/dataTables.fixedHeader.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/js/dataTables.responsive.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/js/dataTables.scroller.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/js/dataTables.select.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/js/datetime-moment.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}js/dataTables.accentNeutraliseForFilter.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/js/jquery.dataTables.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/js/dataTables.bootstrap.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/js/dataTables.fixedHeader.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/js/dataTables.responsive.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/js/dataTables.scroller.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/js/dataTables.select.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/datatables/js/datetime-moment.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/dataTables.accentNeutraliseForFilter.js'|add_itop_version }}"></script>
{# CKEditor files for HTML WYSIWYG #}
<script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/ckeditor/ckeditor.js"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/ckeditor/adapters/jquery.js"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/ckeditor/ckeditor.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/ckeditor/adapters/jquery.js'|add_itop_version }}"></script>
{# Date-time picker for Bootstrap #}
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js'|add_itop_version }}"></script>
{# Typeahead files for autocomplete #}
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/typeahead/js/bloodhound.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/typeahead/js/typeahead.bundle.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/typeahead/js/typeahead.jquery.min.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/handlebars/js/handlebars.min-768ddbd.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/typeahead/js/bloodhound.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/typeahead/js/typeahead.bundle.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/typeahead/js/typeahead.jquery.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/handlebars/js/handlebars.min-768ddbd.js'|add_itop_version }}"></script>
{# Form files #}
<script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/form_handler.js"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/form_field.js"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/subform_field.js"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/field_set.js"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/form_handler.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/form_field.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/subform_field.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/field_set.js'|add_itop_version }}"></script>
{# Form files for portal #}
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}js/portal_form_handler.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}js/portal_form_field.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}js/portal_form_field_html.js"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/portal_form_handler.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/portal_form_field.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'js/portal_form_field_html.js'|add_itop_version }}"></script>
{% endblock %}
</head>
<body class="{% block pPageBodyClass %}{% endblock %}">
{% block pPageBodyWrapper %}
{% block pEnvBannerWrapper %}
{% if app['combodo.current_environment'] != 'production' %}
<div id="envbanner" class="alert alert-danger" role="alert">
{{ 'Portal:EnvironmentBanner:Title'|dict_format( app['combodo.current_environment']|upper )|raw }}
<button type="button" onclick="window;location.href='{{ app['url_generator'].generate('p_home', {'switch_env': 'production'}) }}'">
{{ 'Portal:EnvironmentBanner:GoToProduction'|dict_s|raw }}
</button>
</div>
{% endif %}
{% endblock %}
{% block pNavigationWrapper %}
{# Topbar navigation menu for mobile screens #}
<nav class="navbar navbar-fixed-top navbar-default visible-xs" id="topbar" role="navigation">
@@ -260,12 +272,7 @@
<div class="modal fade" id="modal-for-all" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="content_loader">
<div class="icon glyphicon glyphicon-refresh"></div>
<div class="message">
{{ 'Page:PleaseWait'|dict_s }}
</div>
</div>
{% include 'itop-portal-base/portal/src/views/helpers/loader.html.twig' %}
</div>
</div>
</div>
@@ -289,12 +296,7 @@
<div id="page_overlay" class="global_overlay">
<div class="overlay_content">
<div class="content_loader">
<div class="icon glyphicon glyphicon-refresh"></div>
<div class="message">
{{ 'Page:PleaseWait'|dict_s }}
</div>
</div>
{% include 'itop-portal-base/portal/src/views/helpers/loader.html.twig' %}
</div>
</div>
{% endblock %}
@@ -305,19 +307,22 @@
{
return '{{ app['combodo.absolute_url'] }}';
};
var addParameterToUrl = function(sUrl, sParamName, sParamValue)
var AddParameterToUrl = function(sUrl, sParamName, sParamValue)
{
sUrl += (sUrl.split('?')[1] ? '&':'?') + sParamName + '=' + sParamValue;
return sUrl;
};
var contentLoaderTemplate = '<div class="content_loader"><div class="icon glyphicon glyphicon-refresh"></div><div class="message">{{ 'Page:PleaseWait'|dict_s }}</div></div>';
var GetContentLoaderTemplate = function()
{
return '<div class="content_loader"><div class="icon glyphicon glyphicon-refresh"></div><div class="message">{{ 'Page:PleaseWait'|dict_s }}</div></div>';
}
$(document).ready(function(){
{% block pPageReadyScripts %}
// Hack to enable a same modal to load content from different urls
$('body').on('hidden.bs.modal', '.modal#modal-for-all', function () {
$(this).removeData('bs.modal');
$(this).find('.modal-content').html(contentLoaderTemplate);
$(this).find('.modal-content').html(GetContentLoaderTemplate());
});
// Hack to enable multiple modals by making sure the .modal-open class is set to the <body> when there is at least one modal open left
$('body').on('hidden.bs.modal', function () {
@@ -335,7 +340,7 @@
$('body').on('loaded.bs.modal', function (oEvent) {
var sModalContent = $(oEvent.target).find('.modal-content').html();
if( (sModalContent === '') || (sModalContent.replace(/[\n\r\t]+/g, '') === contentLoaderTemplate) )
if( (sModalContent === '') || (sModalContent.replace(/[\n\r\t]+/g, '') === GetContentLoaderTemplate()) )
{
$(oEvent.target).find('.modal-content').html($('#modal-for-alert .modal-content').html());
$(oEvent.target).find('.modal-content .modal-header .modal-title').text('{{ 'Error:HTTP:500'|dict_s }}');

View File

@@ -1,12 +1,14 @@
{# modal/layout.html.twig #}
{# Base modal layout, used to fill Bootstrap .modal-content #}
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">{% block pModalTitle %}{% endblock %}</h4>
</div>
<div class="modal-body">{% block pModalBody %}{% endblock %}</div>
<div class="modal-footer">
{% block pModalFooter %}
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'Portal:ButtonClose'|dict_s }}</button>
{% endblock %}
</div>
{% block pModalContent %}
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">{% block pModalTitle %}{% endblock %}</h4>
</div>
<div class="modal-body">{% block pModalBody %}{% endblock %}</div>
<div class="modal-footer">
{% block pModalFooter %}
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'Portal:ButtonClose'|dict_s }}</button>
{% endblock %}
</div>
{% endblock %}

View File

@@ -0,0 +1,15 @@
{# itop-portal-base/portal/src/views/bricks/object/mode_loader.html.twig #}
{# Modal loader layout #}
{% extends 'itop-portal-base/portal/src/views/modal/layout.html.twig' %}
{% block pModalContent %}
{% include 'itop-portal-base/portal/src/views/helpers/loader.html.twig' %}
{% if redirection is defined and redirection.url is defined %}
<script type="text/javascript">
$(document).ready( function(){
window.location = '{{ redirection.url|raw }}';
});
</script>
{% endif %}
{% endblock %}

View File

@@ -15,6 +15,17 @@
footer {
margin: 5em 1em;
}
/* Environment banner */
#envbanner {
position: relative;
z-index: 10;
padding: 5px 15px;
text-align: center;
}
#envbanner > button {
margin-left: 5px;
color: #000;
}
/* Navigation menu */
.navbar-nav .dropdown-menu a .glyphicon, .user_infos .dropdown-menu a .glyphicon {
margin-right: 15px;
@@ -267,6 +278,10 @@ footer {
.mfp-wrap {
z-index: 1210;
}
.mfp-img {
cursor: pointer;
cursor: zoom-out;
}
/********************/
/* Typeahed setting */
/********************/
@@ -438,6 +453,10 @@ footer {
.dataTables_wrapper {
padding: 10px 10px;
}
.dataTable.table td img {
max-width: 100%;
height: initial !important;
}
#brick_content_toolbar {
/* margin: 10px 0px 6px 0px; */
padding: 10px;
@@ -602,6 +621,11 @@ table .group-actions {
color: #ea7d1e;
font-size: 0.9em;
}
/* InlineImage */
.inline-image {
cursor: pointer;
cursor: zoom-in;
}
/* CaseLog field */
.caselog_field_entry {
border: 1px solid #ddd;
@@ -622,6 +646,7 @@ table .group-actions {
font-size: 16px;
border: 1px solid #a6a6a6;
border-bottom-color: #979797;
cursor: pointer;
}
.caselog_field_entry_button:hover {
background-color: #ccc;
@@ -638,10 +663,12 @@ table .group-actions {
}
/* LinkedSet*/
.form_linkedset_toggler, .form_linkedset_toggler:hover, .form_linkedset_toggler:focus {
margin-left: 0.4em;
text-decoration: none;
color: inherit;
}
.form_linkedset_toggler > .text {
margin-left: 0.4em;
}
.form_linkedset_toggler > .text:before {
content: "(";
}
@@ -796,6 +823,10 @@ table .group-actions {
}
}
}
/* BlobField */
.form_fields .file_open_link {
margin-left: 10px;
}
.form_field .form-control-static img {
max-width: 100% !important;
height: initial !important;
@@ -816,7 +847,8 @@ table .group-actions {
@media (min-width: 768px) {
/* Making regular button sticky */
.form_buttons .form_btn_transitions {
float: left !important;
float: right !important;
margin-left: 3px;
}
.form_buttons .form_btn_regular {
text-align: right;
@@ -885,9 +917,17 @@ table .group-actions {
border-color: #fbeed5;
color: #c09853;
}
/* CKEditor : Misc */
.cke_toolbox_collapser, .cke_toolbox_collapser .cke_arrow {
cursor: pointer !important;
}
/* DataTables : Selection inputs */
.dataTable.table th span.row_input, .dataTable.table td span.row_input {
display: inline-block;
width: 100%;
text-align: center;
}
/* Wiki text (hyperlinks) */
.wiki_broken_link {
text-decoration: line-through;
}

View File

@@ -19,6 +19,18 @@ footer{
margin: 5em 1em;
}
/* Environment banner */
#envbanner{
position: relative;
z-index: 10;
padding: 5px 15px;
text-align: center;
}
#envbanner > button{
margin-left: 5px;
color: #000;
}
/* Navigation menu */
.navbar-nav .dropdown-menu a .glyphicon,
.user_infos .dropdown-menu a .glyphicon{
@@ -282,6 +294,10 @@ footer{
.mfp-wrap{
z-index: 1210;
}
.mfp-img{
cursor: pointer;
cursor: zoom-out;
}
/********************/
/* Typeahed setting */
@@ -461,6 +477,10 @@ footer{
.dataTables_wrapper{
padding: 10px 10px;
}
.dataTable.table td img{
max-width: 100%;
height: initial !important;
}
#brick_content_toolbar{
/* margin: 10px 0px 6px 0px; */
padding: 10px;
@@ -640,6 +660,11 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
color: $brand-primary;
font-size: 0.9em;
}
/* InlineImage */
.inline-image{
cursor: pointer;
cursor: zoom-in;
}
/* CaseLog field */
.caselog_field_entry{
border: 1px solid $gray-lighter;
@@ -660,6 +685,7 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
font-size: 16px;
border: 1px solid #a6a6a6;
border-bottom-color: #979797;
cursor: pointer;
}
.caselog_field_entry_button:hover{
background-color: #cccccc;
@@ -678,10 +704,12 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
.form_linkedset_toggler,
.form_linkedset_toggler:hover,
.form_linkedset_toggler:focus{
margin-left: 0.4em;
text-decoration: none;
color: inherit;
}
.form_linkedset_toggler > .text{
margin-left: 0.4em;
}
.form_linkedset_toggler > .text:before{
content: "(";
}
@@ -836,6 +864,10 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
}
}
}
/* BlobField */
.form_fields .file_open_link{
margin-left: 10px;
}
.form_field .form-control-static img{
max-width: 100% !important;
@@ -857,7 +889,8 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
}
@media (min-width: 768px){
.form_buttons .form_btn_transitions{
float: left !important;
float: right !important;
margin-left: 3px;
}
.form_buttons .form_btn_regular{
text-align: right;
@@ -927,6 +960,11 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
border-color: $alert-warning-border;
color: $alert-warning-text;
}
/* CKEditor : Misc */
.cke_toolbox_collapser,
.cke_toolbox_collapser .cke_arrow{
cursor: pointer !important;
}
/* DataTables : Selection inputs */
.dataTable.table th span.row_input,
@@ -934,4 +972,8 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
display: inline-block;
width: 100%;
text-align: center;
}
/* Wiki text (hyperlinks) */
.wiki_broken_link {
text-decoration: line-through;
}

View File

@@ -73,12 +73,16 @@ $oApp->register(new Combodo\iTop\Portal\Provider\ScopeValidatorServiceProvider()
'scope_validator.instance_name' => PORTAL_ID
));
$oApp->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => MODULESROOT
'twig.path' => MODULESROOT,
'twig.options' => array(
'cache' => utils::GetCachePath() . 'twig/'
)
));
$oApp->register(new Silex\Provider\HttpFragmentServiceProvider());
// Configuring Silex application
$oApp['debug'] = $bDebug;
$oApp['combodo.current_environment'] = utils::GetCurrentEnvironment();
$oApp['combodo.absolute_url'] = utils::GetAbsoluteUrlAppRoot();
$oApp['combodo.portal.base.absolute_url'] = utils::GetAbsoluteUrlAppRoot() . 'env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/web/';
$oApp['combodo.portal.base.absolute_path'] = MODULESROOT . '/itop-portal-base/portal/web/';

View File

@@ -135,6 +135,16 @@ $(function()
// If everything is okay, we close the form and reload it.
if(oValidation.valid)
{
// Resetting textarea fields (delay is necessary has we have to wait for the form to be built.
setTimeout(function(){
me.options.field_set.find('.portal_form_field_html').each(function(iIndex, oElem){
if($(oElem).find('.cke').length > 0)
{
$(oElem).triggerHandler('set_current_value', '');
}
});
}, 200);
if(me.options.is_modal)
{
me.element.closest('.modal').modal('hide');
@@ -171,7 +181,13 @@ $(function()
oModalElem.attr('id', '').appendTo('body');
// Loading content
oModalElem.find('.modal-content').html($('#page_overlay .overlay_content').html());
oModalElem.find('.modal-content').load(sUrl);
oModalElem.find('.modal-content').load(sUrl, {
// Passing formmanager data to the next page, just in case it needs it (eg. when applying stimulus)
formmanager_class: me.options.formmanager_class,
formmanager_data: JSON.stringify(me.options.formmanager_data)
}
);
oModalElem.modal('show');
}
else

View File

@@ -0,0 +1,99 @@
<?php
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
// Portal
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Page:DefaultTitle' => 'Пользовательский портал iTop',
'Page:PleaseWait' => 'Пожалуйста, подождите...',
'Page:Home' => 'Домашняя страница',
'Page:GoPortalHome' => 'Домашняя страница',
'Page:GoPreviousPage' => 'Предыдущяя страница',
'Portal:Button:Submit' => 'Применить',
'Portal:Button:Cancel' => 'Отменить',
'Portal:Button:Close' => 'Закрыть',
'Portal:Button:Add' => 'Добавить',
'Portal:Button:Remove' => 'Удалить',
'Portal:Button:Delete' => 'Удалить',
'Error:HTTP:404' => 'Страница не найдена',
'Error:HTTP:500' => 'Упс! Произошла ошибка.',
'Error:XHR:Fail' => 'Не удалось загрузить данные. Пожалуйста, свяжитесь с вашим администратором iTop.',
'Error:HTTP:GetHelp' => 'Пожалуйста, свяжитесь с вашим администратором iTop, если проблема сохраняется.',
'Portal:Datatables:Language:Processing' => 'Пожалуйста, подождите...',
'Portal:Datatables:Language:Search' => 'Фильтр :',
'Portal:Datatables:Language:LengthMenu' => 'Показывать _MENU_ элементов на странице',
'Portal:Datatables:Language:ZeroRecords' => 'Нет записей',
'Portal:Datatables:Language:Info' => 'Страница _PAGE_ из _PAGES_',
'Portal:Datatables:Language:InfoEmpty' => 'Нет информации',
'Portal:Datatables:Language:InfoFiltered' => 'Отфильтровано из _MAX_ элементов',
'Portal:Datatables:Language:EmptyTable' => 'Нет данных в этой таблице',
'Portal:Datatables:Language:DisplayLength:All' => 'Все',
'Portal:Datatables:Language:Paginate:First' => 'Первая',
'Portal:Datatables:Language:Paginate:Previous' => 'Предыдущая',
'Portal:Datatables:Language:Paginate:Next' => 'Следующая',
'Portal:Datatables:Language:Paginate:Last' => 'Последняя',
'Portal:Datatables:Language:Sort:Ascending' => 'Включить сортировку по возрастанию',
'Portal:Datatables:Language:Sort:Descending' => 'Включить сортировку по убыванию',
'Portal:Autocomplete:NoResult' => 'Нет данных',
'Portal:Attachments:DropZone:Message' => 'Перетащите файл для добавления вложения',
));
// UserProfile brick
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Brick:Portal:UserProfile:Name' => 'Профиль пользователя',
'Brick:Portal:UserProfile:Navigation:Dropdown:MyProfil' => 'Мой профиль',
'Brick:Portal:UserProfile:Navigation:Dropdown:Logout' => 'Выйти',
'Brick:Portal:UserProfile:Password:Title' => 'Пароль',
'Brick:Portal:UserProfile:Password:ChoosePassword' => 'Введите новый пароль',
'Brick:Portal:UserProfile:Password:ConfirmPassword' => 'Подтвердите новый пароль',
'Brick:Portal:UserProfile:Password:CantChangeContactAdministrator' => 'Пожалуйста, свяжитесь с вашим администратором iTop для изменения пароля.',
'Brick:Portal:UserProfile:Password:CantChangeForUnknownReason' => 'Не удалось изменить пароль, пожалуйста, свяжитесь с вашим администратором iTop.',
'Brick:Portal:UserProfile:PersonalInformations:Title' => 'Персональная информация',
'Brick:Portal:UserProfile:Photo:Title' => 'Фотография',
));
// BrowseBrick brick
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Brick:Portal:Browse:Name' => 'Просмотр элементов',
'Brick:Portal:Browse:Mode:List' => 'Список',
'Brick:Portal:Browse:Mode:Tree' => 'Дерево',
'Brick:Portal:Browse:Action:Drilldown' => 'Детализация',
'Brick:Portal:Browse:Action:View' => 'Подробно',
'Brick:Portal:Browse:Action:Edit' => 'Изменить',
'Brick:Portal:Browse:Action:Create' => 'Создать',
'Brick:Portal:Browse:Action:CreateObjectFromThis' => 'Новый %1$s',
'Brick:Portal:Browse:Tree:ExpandAll' => 'Развернуть все',
'Brick:Portal:Browse:Tree:CollapseAll' => 'Свернуть все',
'Brick:Portal:Browse:Filter:NoData' => 'Нет элементов',
));
// ManageBrick brick
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Brick:Portal:Manage:Name' => 'Управление элементами',
'Brick:Portal:Manage:Table:NoData' => 'Нет элементов',
));
// ObjectBrick brick
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Brick:Portal:Object:Name' => 'Object',
'Brick:Portal:Object:Form:Create:Title' => 'Создать %1$s',
'Brick:Portal:Object:Form:Edit:Title' => 'Обновление %2$s (%1$s)',
'Brick:Portal:Object:Form:View:Title' => '%1$s : %2$s',
'Brick:Portal:Object:Form:Stimulus:Title' => 'Пожалуйста, укажите следующую информацию:',
'Brick:Portal:Object:Form:Message:Saved' => 'Сохранено',
'Brick:Portal:Object:Search:Regular:Title' => 'Выбрать %1$s (%2$s)',
'Brick:Portal:Object:Search:Hierarchy:Title' => 'Выбрать %1$s (%2$s)',
));
// CreateBrick brick
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Brick:Portal:Create:Name' => 'Быстрое создание',
));

View File

@@ -37,4 +37,5 @@ $sDir = basename(__DIR__);
define('PORTAL_MODULE_ID', $sDir);
define('PORTAL_ID', $sDir);
ApplicationContext::SetUrlMakerClass('iTopPortalViewUrlMaker');
require_once APPROOT . '/env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/web/index.php';

View File

@@ -22,20 +22,26 @@
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
*/
class iTopPortalUrlMaker implements iDBObjectURLMaker
class iTopPortalEditUrlMaker implements iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId)
/**
* Generate an (absolute) URL to an object, either in view or edit mode
* @param string $sClass The class of the object
* @param int $iId The identifier of the object
* @param string $sMode edit|view
* @return string
*/
public static function PrepareObjectURL($sClass, $iId, $sMode)
{
require_once APPROOT . '/lib/silex/vendor/autoload.php';
require_once APPROOT . '/env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/src/providers/urlgeneratorserviceprovider.class.inc.php';
require_once APPROOT . '/env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/src/helpers/urlgeneratorhelper.class.inc.php';
require_once APPROOT . '/env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/src/helpers/applicationhelper.class.inc.php';
// Using a static var allows to preserve the object through function calls
static $oApp = null;
static $sPortalId = null;
// Initializing Silex app
if ($oApp === null)
{
@@ -49,15 +55,51 @@ class iTopPortalUrlMaker implements iDBObjectURLMaker
// Retrieving portal id
$sPortalId = basename(__DIR__);
}
$sObjectQueryString = $oApp['url_generator']->generate('p_object_edit', array('sObjectClass' => $sClass, 'sObjectId' => $iId));
$sPortalAbsoluteUrl = utils::GetAbsoluteUrlModulePage($sPortalId, 'index.php');
$sUrl = str_replace('?', $sObjectQueryString . '?', $sPortalAbsoluteUrl);
// The object is reachable in the specified mode (edit/view)
switch($sMode)
{
case 'view':
$sObjectQueryString = $oApp['url_generator']->generate('p_object_view', array('sObjectClass' => $sClass, 'sObjectId' => $iId));
break;
case 'edit':
default:
$sObjectQueryString = $oApp['url_generator']->generate('p_object_edit', array('sObjectClass' => $sClass, 'sObjectId' => $iId));
}
$sPortalAbsoluteUrl = utils::GetAbsoluteUrlModulePage($sPortalId, 'index.php');
if (strpos($sPortalAbsoluteUrl, '?') !== false)
{
$sUrl = substr($sPortalAbsoluteUrl, 0, strpos($sPortalAbsoluteUrl, '?')).$sObjectQueryString;
}
else
{
$sUrl = $sPortalAbsoluteUrl.$sObjectQueryString;
}
return $sUrl;
}
public static function MakeObjectURL($sClass, $iId)
{
return static::PrepareObjectURL($sClass, $iId, 'edit');
}
}
DBObject::RegisterURLMakerClass('portal', 'iTopPortalUrlMaker');
/**
* Hyperlinks to the "view" of the object (vs edition)
* @author denis
*
*/
class iTopPortalViewUrlMaker extends iTopPortalEditUrlMaker
{
public static function MakeObjectURL($sClass, $iId)
{
return static::PrepareObjectURL($sClass, $iId, 'view');
}
}
// Default portal hyperlink (for notifications) is the edit hyperlink
DBObject::RegisterURLMakerClass('portal', 'iTopPortalEditUrlMaker');

View File

@@ -2,67 +2,30 @@
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @license http://opensource.org/licenses/AGPL-3.0
*
*
* Инструкция по установке
*
* Процесс установки заключается в замене имеющихся локализационных файлов полученными и последующем запуске процедуры обновления iTop для перекомпиляции кода.
* 1. Скопируйте с заменой два полученных файла из "itop-rus/dictionaries" в "путь/до/вашего/itop/dictionaries".
* 2. Скопируйте с заменой полученные файлы "itop-rus/datamodels/2.x/название-модуля/ru.dict.название-модуля.php" в "путь/до/вашего/itop/datamodels/2.x/название-модуля".
* 3. Перейдите по адресу "http://адрес/вашего/itop/setup", при этом файл "путь/до/вашего/itop/conf/production/config-itop.php" должен быть доступен для записи.
* 4. На второй странице установщика выберите "Upgrade an existing iTop instance" и следуйте дальнейшим инструкциям установщика.
*
* Ответы на вопросы по установке и использованию переводов, а также на любые другие вопросы по iTop всегда можно получить на сайте сообщества iTop по-русски http://community.itop-itsm.ru.
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// 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>+
//////////////////////////////////////////////////////////////////////
// Classes in 'bizmodel'
//////////////////////////////////////////////////////////////////////
//
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// 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>+
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Menu:ProblemManagement' => 'Управление проблемами',
'Menu:ProblemManagement+' => 'Управление проблемами',
'Menu:Problem:Overview' => 'Обзор',
'Menu:Problem:Overview+' => 'Overview',
'Menu:NewProblem' => 'Создать проблему',
'Menu:NewProblem+' => 'Новая проблема',
'Menu:SearchProblems' => 'Найти проблему',
'Menu:SearchProblems+' => 'Search for problems',
'Menu:Problem:Shortcuts' => 'Ярлыки',
'Menu:Problem:MyProblems' => 'Назначенные мне проблемы',
'Menu:Problem:MyProblems+' => 'Мои проблемы',
'Menu:Problem:OpenProblems' => 'Открытые проблемы',
'Menu:Problem:OpenProblems+' => 'Все открытые проблемы',
'UI-ProblemManagementOverview-ProblemByService' => 'Проблемы по сервису',
'UI-ProblemManagementOverview-ProblemByService+' => 'Проблемы по сервису',
'Menu:ProblemManagement' => 'Управление проблемами',
'Menu:ProblemManagement+' => 'Управление проблемами',
'Menu:Problem:Overview' => 'Обзор',
'Menu:Problem:Overview+' => 'Управление проблемами - Обзор',
'Menu:NewProblem' => 'Новая проблема',
'Menu:NewProblem+' => 'Создать новую проблему',
'Menu:SearchProblems' => 'Поиск проблем',
'Menu:SearchProblems+' => 'Поиск проблем',
'Menu:Problem:Shortcuts' => 'Ярлыки',
'Menu:Problem:MyProblems' => 'Назначенные мне',
'Menu:Problem:MyProblems+' => 'Назначенные мне проблемы',
'Menu:Problem:OpenProblems' => 'Открытые',
'Menu:Problem:OpenProblems+' => 'Все открытые проблемы',
'UI-ProblemManagementOverview-ProblemByService' => 'Проблемы по услугам',
'UI-ProblemManagementOverview-ProblemByService+' => 'Проблемы по услугам',
'UI-ProblemManagementOverview-ProblemByPriority' => 'Проблемы по приоритету',
'UI-ProblemManagementOverview-ProblemByPriority+' => 'Проблемы по приоритету',
'UI-ProblemManagementOverview-ProblemUnassigned' => 'Неназначенные проблемы',
@@ -90,54 +53,56 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:Problem/Attribute:status/Value:closed+' => '',
'Class:Problem/Attribute:service_id' => 'Услуга',
'Class:Problem/Attribute:service_id+' => '',
'Class:Problem/Attribute:service_name' => 'Имя услуги',
'Class:Problem/Attribute:service_name' => 'Услуга',
'Class:Problem/Attribute:service_name+' => '',
'Class:Problem/Attribute:servicesubcategory_id' => 'Тип запроса',
'Class:Problem/Attribute:servicesubcategory_id+' => '',
'Class:Problem/Attribute:servicesubcategory_name' => 'Тип запроса',
'Class:Problem/Attribute:servicesubcategory_id' => 'Подкатегория',
'Class:Problem/Attribute:servicesubcategory_id+' => 'Подкатегория услуги',
'Class:Problem/Attribute:servicesubcategory_name' => 'Подкатегория услуги',
'Class:Problem/Attribute:servicesubcategory_name+' => '',
'Class:Problem/Attribute:product' => 'Продукт',
'Class:Problem/Attribute:product+' => '',
'Class:Problem/Attribute:impact' => 'Влияние',
'Class:Problem/Attribute:impact+' => '',
'Class:Problem/Attribute:impact/Value:1' => 'Услуга',
'Class:Problem/Attribute:impact/Value:1' => 'Департамент',
'Class:Problem/Attribute:impact/Value:1+' => '',
'Class:Problem/Attribute:impact/Value:2' => 'Отдел',
'Class:Problem/Attribute:impact/Value:2' => 'Служба',
'Class:Problem/Attribute:impact/Value:2+' => '',
'Class:Problem/Attribute:impact/Value:3' => 'Персона',
'Class:Problem/Attribute:impact/Value:3+' => '',
'Class:Problem/Attribute:urgency' => 'Срочность',
'Class:Problem/Attribute:urgency+' => '',
'Class:Problem/Attribute:urgency/Value:1' => 'Критическая',
'Class:Problem/Attribute:urgency/Value:1+' => 'critical',
'Class:Problem/Attribute:urgency/Value:1+' => 'Критическая',
'Class:Problem/Attribute:urgency/Value:2' => 'Высокая',
'Class:Problem/Attribute:urgency/Value:2+' => 'high',
'Class:Problem/Attribute:urgency/Value:2+' => 'Высокая',
'Class:Problem/Attribute:urgency/Value:3' => 'Средняя',
'Class:Problem/Attribute:urgency/Value:3+' => 'medium',
'Class:Problem/Attribute:urgency/Value:3+' => 'Средняя',
'Class:Problem/Attribute:urgency/Value:4' => 'Низкая',
'Class:Problem/Attribute:urgency/Value:4+' => 'low',
'Class:Problem/Attribute:urgency/Value:4+' => 'Низкая',
'Class:Problem/Attribute:priority' => 'Приоритет',
'Class:Problem/Attribute:priority+' => '',
'Class:Problem/Attribute:priority/Value:1' => 'Критический',
'Class:Problem/Attribute:priority/Value:1+' => 'Critical',
'Class:Problem/Attribute:priority/Value:1+' => 'Критический',
'Class:Problem/Attribute:priority/Value:2' => 'Высокий',
'Class:Problem/Attribute:priority/Value:2+' => 'High',
'Class:Problem/Attribute:priority/Value:2+' => 'Высокий',
'Class:Problem/Attribute:priority/Value:3' => 'Средний',
'Class:Problem/Attribute:priority/Value:3+' => 'Medium',
'Class:Problem/Attribute:priority/Value:3+' => 'Средний',
'Class:Problem/Attribute:priority/Value:4' => 'Низкий',
'Class:Problem/Attribute:priority/Value:4+' => 'Low',
'Class:Problem/Attribute:priority/Value:4+' => 'Низкий',
'Class:Problem/Attribute:related_change_id' => 'Связанное изменение',
'Class:Problem/Attribute:related_change_id+' => '',
'Class:Problem/Attribute:related_change_ref' => 'Ссылка на изменение',
'Class:Problem/Attribute:related_change_ref' => 'Связанное изменение',
'Class:Problem/Attribute:related_change_ref+' => '',
'Class:Problem/Attribute:assignment_date' => 'Назначение',
'Class:Problem/Attribute:assignment_date' => 'Дата назначения',
'Class:Problem/Attribute:assignment_date+' => '',
'Class:Problem/Attribute:resolution_date' => 'Решение',
'Class:Problem/Attribute:resolution_date' => 'Дата решения',
'Class:Problem/Attribute:resolution_date+' => '',
'Class:Problem/Attribute:knownerrors_list' => 'Известные ошибки',
'Class:Problem/Attribute:knownerrors_list+' => 'Связанные известные ошибки',
'Class:Problem/Attribute:related_request_list' => 'Запросы',
'Class:Problem/Attribute:related_request_list+' => 'Связанные запросы',
'Class:Problem/Attribute:related_incident_list' => 'Инциденты',
'Class:Problem/Attribute:related_incident_list+' => 'Связанные инциденты',
'Class:Problem/Stimulus:ev_assign' => 'Назначить',
'Class:Problem/Stimulus:ev_assign+' => '',
'Class:Problem/Stimulus:ev_reassign' => 'Переназначить',
@@ -146,8 +111,4 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:Problem/Stimulus:ev_resolve+' => '',
'Class:Problem/Stimulus:ev_close' => 'Закрыть',
'Class:Problem/Stimulus:ev_close+' => '',
'Class:Problem/Attribute:related_incident_list' => 'Related incidents~~',
'Class:Problem/Attribute:related_incident_list+' => 'All the incidents that are related to this problem~~',
));
?>
));

View File

@@ -1325,7 +1325,7 @@
<code><![CDATA[ public function UpdateChildRequestLog()
{
$oLog = $this->Get('public_log');
$sLogPublic = $oLog->GetModifiedEntry();
$sLogPublic = $oLog->GetModifiedEntry('html');
if ($sLogPublic != '')
{
$sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket";
@@ -1343,7 +1343,7 @@
}
$oLog = $this->Get('private_log');
$sLogPrivate = $oLog->GetModifiedEntry();
$sLogPrivate = $oLog->GetModifiedEntry('html');
if ($sLogPrivate != '')
{
$sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket";

View File

@@ -0,0 +1,255 @@
<?php
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Menu:RequestManagement' => 'Управление запросами',
'Menu:RequestManagement+' => 'Управление запросами',
'Menu:RequestManagementProvider' => 'Поставщик техподдержки',
'Menu:RequestManagementProvider+' => 'Поставщик техподдержки',
'Menu:UserRequest:Provider' => 'Открытые запросы, отправленные поставщику',
'Menu:UserRequest:Provider+' => 'Открытые запросы, отправленные поставщику',
'Menu:UserRequest:Overview' => 'Обзор',
'Menu:UserRequest:Overview+' => 'Обзор',
'Menu:NewUserRequest' => 'Новый запрос',
'Menu:NewUserRequest+' => 'Создать новый запрос на обслуживание',
'Menu:SearchUserRequests' => 'Поиск запросов',
'Menu:SearchUserRequests+' => 'Поиск запросов на обслуживание',
'Menu:UserRequest:Shortcuts' => 'Ярлыки',
'Menu:UserRequest:Shortcuts+' => 'Ярлыки',
'Menu:UserRequest:MyRequests' => 'Назначенные мне',
'Menu:UserRequest:MyRequests+' => 'Назначенные мне запросы',
'Menu:UserRequest:MySupportRequests' => "Созданные мной",
'Menu:UserRequest:MySupportRequests+' => "Созданные мной запросы",
'Menu:UserRequest:EscalatedRequests' => 'Эскалированные',
'Menu:UserRequest:EscalatedRequests+' => 'Эскалированные запросы',
'Menu:UserRequest:OpenRequests' => 'Открытые',
'Menu:UserRequest:OpenRequests+' => 'Открытые запросы',
'UI:WelcomeMenu:MyAssignedCalls' => 'Назначенные мне запросы',
'UI-RequestManagementOverview-RequestByType-last-14-days' => 'Запросы по типу за 14 дней',
'UI-RequestManagementOverview-Last-14-days' => 'Количество запросов за 14 дней',
'UI-RequestManagementOverview-OpenRequestByStatus' => 'Открытые запросы по статусу',
'UI-RequestManagementOverview-OpenRequestByAgent' => 'Открытые запросы по агенту',
'UI-RequestManagementOverview-OpenRequestByType' => 'Открытые запросы по типу',
'UI-RequestManagementOverview-OpenRequestByCustomer' => 'Открытые запросы по заказчику',
'Class:UserRequest:KnownErrorList' => 'Известные ошибки',
));
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// 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: UserRequest
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:UserRequest' => 'Запрос',
'Class:UserRequest+' => '',
'Class:UserRequest/Attribute:status' => 'Статус',
'Class:UserRequest/Attribute:status+' => '',
'Class:UserRequest/Attribute:status/Value:new' => 'Новый',
'Class:UserRequest/Attribute:status/Value:new+' => '',
'Class:UserRequest/Attribute:status/Value:escalated_tto' => 'Эскалация TTO',
'Class:UserRequest/Attribute:status/Value:escalated_tto+' => '',
'Class:UserRequest/Attribute:status/Value:assigned' => 'Назначен',
'Class:UserRequest/Attribute:status/Value:assigned+' => '',
'Class:UserRequest/Attribute:status/Value:escalated_ttr' => 'Эскалация TTR',
'Class:UserRequest/Attribute:status/Value:escalated_ttr+' => '',
'Class:UserRequest/Attribute:status/Value:waiting_for_approval' => 'Ожидание утверждения',
'Class:UserRequest/Attribute:status/Value:waiting_for_approval+' => '',
'Class:UserRequest/Attribute:status/Value:approved' => 'Утвержден',
'Class:UserRequest/Attribute:status/Value:approved+' => '',
'Class:UserRequest/Attribute:status/Value:rejected' => 'Отклонен',
'Class:UserRequest/Attribute:status/Value:rejected+' => '',
'Class:UserRequest/Attribute:status/Value:pending' => 'В ожидании',
'Class:UserRequest/Attribute:status/Value:pending+' => '',
'Class:UserRequest/Attribute:status/Value:resolved' => 'Решенный',
'Class:UserRequest/Attribute:status/Value:resolved+' => '',
'Class:UserRequest/Attribute:status/Value:closed' => 'Закрыт',
'Class:UserRequest/Attribute:status/Value:closed+' => '',
'Class:UserRequest/Attribute:request_type' => 'Тип запроса',
'Class:UserRequest/Attribute:request_type+' => '',
'Class:UserRequest/Attribute:request_type/Value:service_request' => 'Запрос на обслуживание',
'Class:UserRequest/Attribute:request_type/Value:service_request+' => 'Запрос на обслуживание',
'Class:UserRequest/Attribute:impact' => 'Влияние',
'Class:UserRequest/Attribute:impact+' => '',
'Class:UserRequest/Attribute:impact/Value:1' => 'Департамент',
'Class:UserRequest/Attribute:impact/Value:1+' => '',
'Class:UserRequest/Attribute:impact/Value:2' => 'Служба',
'Class:UserRequest/Attribute:impact/Value:2+' => '',
'Class:UserRequest/Attribute:impact/Value:3' => 'Персона',
'Class:UserRequest/Attribute:impact/Value:3+' => '',
'Class:UserRequest/Attribute:priority' => 'Приоритет',
'Class:UserRequest/Attribute:priority+' => '',
'Class:UserRequest/Attribute:priority/Value:1' => 'Критический',
'Class:UserRequest/Attribute:priority/Value:1+' => 'Критический',
'Class:UserRequest/Attribute:priority/Value:2' => 'Высокий',
'Class:UserRequest/Attribute:priority/Value:2+' => 'Высокий',
'Class:UserRequest/Attribute:priority/Value:3' => 'Средний',
'Class:UserRequest/Attribute:priority/Value:3+' => 'Средний',
'Class:UserRequest/Attribute:priority/Value:4' => 'Низкий',
'Class:UserRequest/Attribute:priority/Value:4+' => 'Низкий',
'Class:UserRequest/Attribute:urgency' => 'Срочность',
'Class:UserRequest/Attribute:urgency+' => '',
'Class:UserRequest/Attribute:urgency/Value:1' => 'Критическая',
'Class:UserRequest/Attribute:urgency/Value:1+' => 'Критическая',
'Class:UserRequest/Attribute:urgency/Value:2' => 'Высокая',
'Class:UserRequest/Attribute:urgency/Value:2+' => 'Высокая',
'Class:UserRequest/Attribute:urgency/Value:3' => 'Средняя',
'Class:UserRequest/Attribute:urgency/Value:3+' => 'Средняя',
'Class:UserRequest/Attribute:urgency/Value:4' => 'Низкая',
'Class:UserRequest/Attribute:urgency/Value:4+' => 'Низкая',
'Class:UserRequest/Attribute:origin' => 'Источник',
'Class:UserRequest/Attribute:origin+' => '',
'Class:UserRequest/Attribute:origin/Value:mail' => 'Почта',
'Class:UserRequest/Attribute:origin/Value:mail+' => 'Почта',
'Class:UserRequest/Attribute:origin/Value:monitoring' => 'Мониторинг',
'Class:UserRequest/Attribute:origin/Value:monitoring+' => 'Мониторинг',
'Class:UserRequest/Attribute:origin/Value:phone' => 'Телефон',
'Class:UserRequest/Attribute:origin/Value:phone+' => 'Телефон',
'Class:UserRequest/Attribute:origin/Value:portal' => 'Портал',
'Class:UserRequest/Attribute:origin/Value:portal+' => 'Портал',
'Class:UserRequest/Attribute:approver_id' => 'Утверждающий',
'Class:UserRequest/Attribute:approver_id+' => '',
'Class:UserRequest/Attribute:approver_email' => 'Email утверждающего',
'Class:UserRequest/Attribute:approver_email+' => '',
'Class:UserRequest/Attribute:service_id' => 'Услуга',
'Class:UserRequest/Attribute:service_id+' => '',
'Class:UserRequest/Attribute:service_name' => 'Услуга',
'Class:UserRequest/Attribute:service_name+' => '',
'Class:UserRequest/Attribute:servicesubcategory_id' => 'Подкатегория',
'Class:UserRequest/Attribute:servicesubcategory_id+' => 'Подкатегория услуги',
'Class:UserRequest/Attribute:servicesubcategory_name' => 'Подкатегория услуги',
'Class:UserRequest/Attribute:servicesubcategory_name+' => '',
'Class:UserRequest/Attribute:escalation_flag' => 'Флаг эскалации',
'Class:UserRequest/Attribute:escalation_flag+' => '',
'Class:UserRequest/Attribute:escalation_flag/Value:no' => 'Нет',
'Class:UserRequest/Attribute:escalation_flag/Value:no+' => 'Нет',
'Class:UserRequest/Attribute:escalation_flag/Value:yes' => 'Да',
'Class:UserRequest/Attribute:escalation_flag/Value:yes+' => 'Да',
'Class:UserRequest/Attribute:escalation_reason' => 'Причина эскалации',
'Class:UserRequest/Attribute:escalation_reason+' => '',
'Class:UserRequest/Attribute:assignment_date' => 'Дата назначения',
'Class:UserRequest/Attribute:assignment_date+' => '',
'Class:UserRequest/Attribute:resolution_date' => 'Дата решения',
'Class:UserRequest/Attribute:resolution_date+' => '',
'Class:UserRequest/Attribute:last_pending_date' => 'Дата последнего ожидания',
'Class:UserRequest/Attribute:last_pending_date+' => '',
'Class:UserRequest/Attribute:cumulatedpending' => 'Накопленное ожидание',
'Class:UserRequest/Attribute:cumulatedpending+' => '',
'Class:UserRequest/Attribute:tto' => 'TTO',
'Class:UserRequest/Attribute:tto+' => '',
'Class:UserRequest/Attribute:ttr' => 'TTR',
'Class:UserRequest/Attribute:ttr+' => '',
'Class:UserRequest/Attribute:tto_escalation_deadline' => 'Срок TTO',
'Class:UserRequest/Attribute:tto_escalation_deadline+' => 'Крайний срок назаначения агента (принятия в работу) по текущему SLA',
'Class:UserRequest/Attribute:sla_tto_passed' => 'SLA TTO пропущено',
'Class:UserRequest/Attribute:sla_tto_passed+' => '',
'Class:UserRequest/Attribute:sla_tto_over' => 'SLA TTO превышено',
'Class:UserRequest/Attribute:sla_tto_over+' => '',
'Class:UserRequest/Attribute:ttr_escalation_deadline' => 'Срок TTR',
'Class:UserRequest/Attribute:ttr_escalation_deadline+' => 'Крайний срок решения по текущему SLA',
'Class:UserRequest/Attribute:sla_ttr_passed' => 'SLA TTR пропущено',
'Class:UserRequest/Attribute:sla_ttr_passed+' => '',
'Class:UserRequest/Attribute:sla_ttr_over' => 'SLA TTR превышено',
'Class:UserRequest/Attribute:sla_ttr_over+' => '',
'Class:UserRequest/Attribute:time_spent' => 'Время на решение',
'Class:UserRequest/Attribute:time_spent+' => '',
'Class:UserRequest/Attribute:resolution_code' => 'Код решения',
'Class:UserRequest/Attribute:resolution_code+' => '',
'Class:UserRequest/Attribute:resolution_code/Value:assistance' => 'Помощь',
'Class:UserRequest/Attribute:resolution_code/Value:assistance+' => 'Помощь',
'Class:UserRequest/Attribute:resolution_code/Value:bug fixed' => 'Исправление ошибки',
'Class:UserRequest/Attribute:resolution_code/Value:bug fixed+' => 'Исправление ошибки',
'Class:UserRequest/Attribute:resolution_code/Value:hardware repair' => 'Ремонт оборудования',
'Class:UserRequest/Attribute:resolution_code/Value:hardware repair+' => 'Ремонт оборудования',
'Class:UserRequest/Attribute:resolution_code/Value:other' => 'Другое',
'Class:UserRequest/Attribute:resolution_code/Value:other+' => 'Другое',
'Class:UserRequest/Attribute:resolution_code/Value:software patch' => 'Патч ПО',
'Class:UserRequest/Attribute:resolution_code/Value:software patch+' => 'Патч ПО',
'Class:UserRequest/Attribute:resolution_code/Value:system update' => 'Обновление системы',
'Class:UserRequest/Attribute:resolution_code/Value:system update+' => 'Обновление системы',
'Class:UserRequest/Attribute:resolution_code/Value:training' => 'Обучение',
'Class:UserRequest/Attribute:resolution_code/Value:training+' => 'Обучение',
'Class:UserRequest/Attribute:solution' => 'Описание решения',
'Class:UserRequest/Attribute:solution+' => '',
'Class:UserRequest/Attribute:pending_reason' => 'Причина ожидания',
'Class:UserRequest/Attribute:pending_reason+' => '',
'Class:UserRequest/Attribute:parent_request_id' => 'Родительский запрос',
'Class:UserRequest/Attribute:parent_request_id+' => '',
'Class:UserRequest/Attribute:parent_incident_id' => 'Родительский инцидент',
'Class:UserRequest/Attribute:parent_incident_id+' => '',
'Class:UserRequest/Attribute:parent_incident_ref' => 'Родительский инцидент',
'Class:UserRequest/Attribute:parent_incident_ref+' => '',
'Class:UserRequest/Attribute:parent_request_ref' => 'Родительский запрос',
'Class:UserRequest/Attribute:parent_request_ref+' => '',
'Class:UserRequest/Attribute:parent_problem_id' => 'Родительская проблема',
'Class:UserRequest/Attribute:parent_problem_id+' => '',
'Class:UserRequest/Attribute:parent_problem_ref' => 'Родительская проблема',
'Class:UserRequest/Attribute:parent_problem_ref+' => '',
'Class:UserRequest/Attribute:parent_change_id' => 'Родительское изменение',
'Class:UserRequest/Attribute:parent_change_id+' => '',
'Class:UserRequest/Attribute:parent_change_ref' => 'Родительское изменение',
'Class:UserRequest/Attribute:parent_change_ref+' => '',
'Class:UserRequest/Attribute:related_request_list' => 'Дочерние запросы',
'Class:UserRequest/Attribute:related_request_list+' => 'Дочерние запросы',
'Class:UserRequest/Attribute:public_log' => 'Общий журнал',
'Class:UserRequest/Attribute:public_log+' => 'Информация в общем журнале доступна для пользователей портала',
'Class:UserRequest/Attribute:user_satisfaction' => 'Удовлетворенность пользователя',
'Class:UserRequest/Attribute:user_satisfaction+' => '',
'Class:UserRequest/Attribute:user_satisfaction/Value:1' => 'Очень доволен',
'Class:UserRequest/Attribute:user_satisfaction/Value:1+' => 'Очень доволен',
'Class:UserRequest/Attribute:user_satisfaction/Value:2' => 'Вполне доволен',
'Class:UserRequest/Attribute:user_satisfaction/Value:2+' => 'Вполне доволен',
'Class:UserRequest/Attribute:user_satisfaction/Value:3' => 'Скорее недоволен',
'Class:UserRequest/Attribute:user_satisfaction/Value:3+' => 'Скорее недоволен',
'Class:UserRequest/Attribute:user_satisfaction/Value:4' => 'Очень недоволен',
'Class:UserRequest/Attribute:user_satisfaction/Value:4+' => 'Очень недоволен',
'Class:UserRequest/Attribute:user_comment' => 'Комментарий пользователя',
'Class:UserRequest/Attribute:user_comment+' => '',
'Class:UserRequest/Attribute:parent_request_id_friendlyname' => 'Родительский запрос',
'Class:UserRequest/Attribute:parent_request_id_friendlyname+' => '',
'Class:UserRequest/Stimulus:ev_assign' => 'Назначить',
'Class:UserRequest/Stimulus:ev_assign+' => '',
'Class:UserRequest/Stimulus:ev_reassign' => 'Переназначить',
'Class:UserRequest/Stimulus:ev_reassign+' => '',
'Class:UserRequest/Stimulus:ev_approve' => 'Утвердить',
'Class:UserRequest/Stimulus:ev_approve+' => '',
'Class:UserRequest/Stimulus:ev_reject' => 'Отклонить',
'Class:UserRequest/Stimulus:ev_reject+' => '',
'Class:UserRequest/Stimulus:ev_pending' => 'В ожидание',
'Class:UserRequest/Stimulus:ev_pending+' => '',
'Class:UserRequest/Stimulus:ev_timeout' => 'Таймаут',
'Class:UserRequest/Stimulus:ev_timeout+' => '',
'Class:UserRequest/Stimulus:ev_autoresolve' => 'Автоматическое решение',
'Class:UserRequest/Stimulus:ev_autoresolve+' => '',
'Class:UserRequest/Stimulus:ev_autoclose' => 'Автоматическое закрытие',
'Class:UserRequest/Stimulus:ev_autoclose+' => '',
'Class:UserRequest/Stimulus:ev_resolve' => 'Отметить как решенный',
'Class:UserRequest/Stimulus:ev_resolve+' => '',
'Class:UserRequest/Stimulus:ev_close' => 'Закрыть',
'Class:UserRequest/Stimulus:ev_close+' => '',
'Class:UserRequest/Stimulus:ev_reopen' => 'Вновь открыть',
'Class:UserRequest/Stimulus:ev_reopen+' => '',
'Class:UserRequest/Stimulus:ev_wait_for_approval' => 'Ждать утверждения',
'Class:UserRequest/Stimulus:ev_wait_for_approval+' => '',
'Class:UserRequest/Error:CannotAssignParentRequestIdToSelf' => 'Невозможно назначить этот же запрос в качестве родительского',
'Class:UserRequest/Method:ResolveChildTickets' => 'ResolveChildTickets',
'Class:UserRequest/Method:ResolveChildTickets+' => 'Cascade the resolution to child requests (ev_autoresolve), and align the following characteristics of the request: service, team, agent, resolution info',
));

View File

@@ -1338,7 +1338,7 @@
<code><![CDATA[ public function UpdateChildRequestLog()
{
$oLog = $this->Get('public_log');
$sLogPublic = $oLog->GetModifiedEntry();
$sLogPublic = $oLog->GetModifiedEntry('html');
if ($sLogPublic != '')
{
$sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket";
@@ -1356,7 +1356,7 @@
}
$oLog = $this->Get('private_log');
$sLogPrivate = $oLog->GetModifiedEntry();
$sLogPrivate = $oLog->GetModifiedEntry('html');
if ($sLogPrivate != '')
{
$sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket";

View File

@@ -0,0 +1,284 @@
<?php
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Menu:RequestManagement' => 'Helpdesk',
'Menu:RequestManagement+' => 'Helpdesk',
'Menu:RequestManagementProvider' => 'Поставщик техподдержки',
'Menu:RequestManagementProvider+' => 'Поставщик техподдержки',
'Menu:UserRequest:Provider' => 'Открытые запросы, отправленные поставщику',
'Menu:UserRequest:Provider+' => 'Открытые запросы, отправленные поставщику',
'Menu:UserRequest:Overview' => 'Обзор',
'Menu:UserRequest:Overview+' => 'Обзор',
'Menu:NewUserRequest' => 'Новый запрос',
'Menu:NewUserRequest+' => 'Создать новый запрос на обслуживание',
'Menu:SearchUserRequests' => 'Поиск запросов',
'Menu:SearchUserRequests+' => 'Поиск запросов на обслуживание',
'Menu:UserRequest:Shortcuts' => 'Ярлыки',
'Menu:UserRequest:Shortcuts+' => 'Ярлыки',
'Menu:UserRequest:MyRequests' => 'Назначенные мне',
'Menu:UserRequest:MyRequests+' => 'Назначенные мне (в качестве агента)',
'Menu:UserRequest:MySupportRequests' => "Созданные мной",
'Menu:UserRequest:MySupportRequests+' => "Созданные мной запросы",
'Menu:UserRequest:EscalatedRequests' => 'Эскалированные',
'Menu:UserRequest:EscalatedRequests+' => 'Эскалированные запросы',
'Menu:UserRequest:OpenRequests' => 'Открытые',
'Menu:UserRequest:OpenRequests+' => 'Открытые запросы',
'UI:WelcomeMenu:MyAssignedCalls' => 'Назначенные мне запросы',
'UI-RequestManagementOverview-RequestByType-last-14-days' => 'Запросы по типу за 14 дней',
'UI-RequestManagementOverview-Last-14-days' => 'Количество запросов за 14 дней',
'UI-RequestManagementOverview-OpenRequestByStatus' => 'Открытые запросы по статусу',
'UI-RequestManagementOverview-OpenRequestByAgent' => 'Открытые запросы по агенту',
'UI-RequestManagementOverview-OpenRequestByType' => 'Открытые запросы по типу',
'UI-RequestManagementOverview-OpenRequestByCustomer' => 'Открытые запросы по заказчику',
'Class:UserRequest:KnownErrorList' => 'Известные ошибки',
'Menu:UserRequest:MyWorkOrders' => 'Назначенные мне наряды на работу',
'Menu:UserRequest:MyWorkOrders+' => 'Назначенные мне наряды на работу',
'Class:Problem:KnownProblemList' => 'Известные проблемы',
'Tickets:Related:OpenIncidents' => 'Открытые инциденты',
));
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// 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: UserRequest
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:UserRequest' => 'Запрос',
'Class:UserRequest+' => '',
'Class:UserRequest/Attribute:status' => 'Статус',
'Class:UserRequest/Attribute:status+' => '',
'Class:UserRequest/Attribute:status/Value:new' => 'Новый',
'Class:UserRequest/Attribute:status/Value:new+' => '',
'Class:UserRequest/Attribute:status/Value:escalated_tto' => 'Эскалация TTO',
'Class:UserRequest/Attribute:status/Value:escalated_tto+' => '',
'Class:UserRequest/Attribute:status/Value:assigned' => 'Назначен',
'Class:UserRequest/Attribute:status/Value:assigned+' => '',
'Class:UserRequest/Attribute:status/Value:escalated_ttr' => 'Эскалация TTR',
'Class:UserRequest/Attribute:status/Value:escalated_ttr+' => '',
'Class:UserRequest/Attribute:status/Value:waiting_for_approval' => 'Ожидание утверждения',
'Class:UserRequest/Attribute:status/Value:waiting_for_approval+' => '',
'Class:UserRequest/Attribute:status/Value:approved' => 'Утвержден',
'Class:UserRequest/Attribute:status/Value:approved+' => '',
'Class:UserRequest/Attribute:status/Value:rejected' => 'Отклонен',
'Class:UserRequest/Attribute:status/Value:rejected+' => '',
'Class:UserRequest/Attribute:status/Value:pending' => 'В ожидании',
'Class:UserRequest/Attribute:status/Value:pending+' => '',
'Class:UserRequest/Attribute:status/Value:resolved' => 'Решенный',
'Class:UserRequest/Attribute:status/Value:resolved+' => '',
'Class:UserRequest/Attribute:status/Value:closed' => 'Закрыт',
'Class:UserRequest/Attribute:status/Value:closed+' => '',
'Class:UserRequest/Attribute:request_type' => 'Тип запроса',
'Class:UserRequest/Attribute:request_type+' => '',
'Class:UserRequest/Attribute:request_type/Value:incident' => 'Инцидент',
'Class:UserRequest/Attribute:request_type/Value:incident+' => 'Инцидент',
'Class:UserRequest/Attribute:request_type/Value:service_request' => 'Запрос на обслуживание',
'Class:UserRequest/Attribute:request_type/Value:service_request+' => 'Запрос на обслуживание',
'Class:UserRequest/Attribute:impact' => 'Влияние',
'Class:UserRequest/Attribute:impact+' => '',
'Class:UserRequest/Attribute:impact/Value:1' => 'Департамент',
'Class:UserRequest/Attribute:impact/Value:1+' => '',
'Class:UserRequest/Attribute:impact/Value:2' => 'Служба',
'Class:UserRequest/Attribute:impact/Value:2+' => '',
'Class:UserRequest/Attribute:impact/Value:3' => 'Персона',
'Class:UserRequest/Attribute:impact/Value:3+' => '',
'Class:UserRequest/Attribute:priority' => 'Приоритет',
'Class:UserRequest/Attribute:priority+' => '',
'Class:UserRequest/Attribute:priority/Value:1' => 'Критический',
'Class:UserRequest/Attribute:priority/Value:1+' => 'Критический',
'Class:UserRequest/Attribute:priority/Value:2' => 'Высокий',
'Class:UserRequest/Attribute:priority/Value:2+' => 'Высокий',
'Class:UserRequest/Attribute:priority/Value:3' => 'Средний',
'Class:UserRequest/Attribute:priority/Value:3+' => 'Средний',
'Class:UserRequest/Attribute:priority/Value:4' => 'Низкий',
'Class:UserRequest/Attribute:priority/Value:4+' => 'Низкий',
'Class:UserRequest/Attribute:urgency' => 'Срочность',
'Class:UserRequest/Attribute:urgency+' => '',
'Class:UserRequest/Attribute:urgency/Value:1' => 'Критическая',
'Class:UserRequest/Attribute:urgency/Value:1+' => 'Критическая',
'Class:UserRequest/Attribute:urgency/Value:2' => 'Высокая',
'Class:UserRequest/Attribute:urgency/Value:2+' => 'Высокая',
'Class:UserRequest/Attribute:urgency/Value:3' => 'Средняя',
'Class:UserRequest/Attribute:urgency/Value:3+' => 'Средняя',
'Class:UserRequest/Attribute:urgency/Value:4' => 'Низкая',
'Class:UserRequest/Attribute:urgency/Value:4+' => 'Низкая',
'Class:UserRequest/Attribute:origin' => 'Источник',
'Class:UserRequest/Attribute:origin+' => '',
'Class:UserRequest/Attribute:origin/Value:mail' => 'Почта',
'Class:UserRequest/Attribute:origin/Value:mail+' => 'Почта',
'Class:UserRequest/Attribute:origin/Value:monitoring' => 'Мониторинг',
'Class:UserRequest/Attribute:origin/Value:monitoring+' => 'Мониторинг',
'Class:UserRequest/Attribute:origin/Value:phone' => 'Телефон',
'Class:UserRequest/Attribute:origin/Value:phone+' => 'Телефон',
'Class:UserRequest/Attribute:origin/Value:portal' => 'Портал',
'Class:UserRequest/Attribute:origin/Value:portal+' => 'Портал',
'Class:UserRequest/Attribute:approver_id' => 'Утверждающий',
'Class:UserRequest/Attribute:approver_id+' => '',
'Class:UserRequest/Attribute:approver_email' => 'Email утверждающего',
'Class:UserRequest/Attribute:approver_email+' => '',
'Class:UserRequest/Attribute:service_id' => 'Услуга',
'Class:UserRequest/Attribute:service_id+' => '',
'Class:UserRequest/Attribute:service_name' => 'Услуга',
'Class:UserRequest/Attribute:service_name+' => '',
'Class:UserRequest/Attribute:servicesubcategory_id' => 'Подкатегория',
'Class:UserRequest/Attribute:servicesubcategory_id+' => 'Подкатегория услуги',
'Class:UserRequest/Attribute:servicesubcategory_name' => 'Подкатегория услуги',
'Class:UserRequest/Attribute:servicesubcategory_name+' => '',
'Class:UserRequest/Attribute:escalation_flag' => 'Флаг эскалации',
'Class:UserRequest/Attribute:escalation_flag+' => '',
'Class:UserRequest/Attribute:escalation_flag/Value:no' => 'Нет',
'Class:UserRequest/Attribute:escalation_flag/Value:no+' => 'Нет',
'Class:UserRequest/Attribute:escalation_flag/Value:yes' => 'Да',
'Class:UserRequest/Attribute:escalation_flag/Value:yes+' => 'Да',
'Class:UserRequest/Attribute:escalation_reason' => 'Причина эскалации',
'Class:UserRequest/Attribute:escalation_reason+' => '',
'Class:UserRequest/Attribute:assignment_date' => 'Дата назначения',
'Class:UserRequest/Attribute:assignment_date+' => '',
'Class:UserRequest/Attribute:resolution_date' => 'Дата решения',
'Class:UserRequest/Attribute:resolution_date+' => '',
'Class:UserRequest/Attribute:last_pending_date' => 'Дата последнего ожидания',
'Class:UserRequest/Attribute:last_pending_date+' => '',
'Class:UserRequest/Attribute:cumulatedpending' => 'Накопленное ожидание',
'Class:UserRequest/Attribute:cumulatedpending+' => '',
'Class:UserRequest/Attribute:tto' => 'TTO',
'Class:UserRequest/Attribute:tto+' => '',
'Class:UserRequest/Attribute:ttr' => 'TTR',
'Class:UserRequest/Attribute:ttr+' => '',
'Class:UserRequest/Attribute:tto_escalation_deadline' => 'Срок TTO',
'Class:UserRequest/Attribute:tto_escalation_deadline+' => 'Крайний срок назаначения агента (принятия в работу) по текущему SLA',
'Class:UserRequest/Attribute:sla_tto_passed' => 'SLA TTO пропущено',
'Class:UserRequest/Attribute:sla_tto_passed+' => '',
'Class:UserRequest/Attribute:sla_tto_over' => 'SLA TTO превышено',
'Class:UserRequest/Attribute:sla_tto_over+' => '',
'Class:UserRequest/Attribute:ttr_escalation_deadline' => 'Срок TTR',
'Class:UserRequest/Attribute:ttr_escalation_deadline+' => 'Крайний срок решения по текущему SLA',
'Class:UserRequest/Attribute:sla_ttr_passed' => 'SLA TTR пропущено',
'Class:UserRequest/Attribute:sla_ttr_passed+' => '',
'Class:UserRequest/Attribute:sla_ttr_over' => 'SLA TTR превышено',
'Class:UserRequest/Attribute:sla_ttr_over+' => '',
'Class:UserRequest/Attribute:time_spent' => 'Время на решение',
'Class:UserRequest/Attribute:time_spent+' => '',
'Class:UserRequest/Attribute:resolution_code' => 'Код решения',
'Class:UserRequest/Attribute:resolution_code+' => '',
'Class:UserRequest/Attribute:resolution_code/Value:assistance' => 'Помощь',
'Class:UserRequest/Attribute:resolution_code/Value:assistance+' => 'Помощь',
'Class:UserRequest/Attribute:resolution_code/Value:bug fixed' => 'Исправление ошибки',
'Class:UserRequest/Attribute:resolution_code/Value:bug fixed+' => 'Исправление ошибки',
'Class:UserRequest/Attribute:resolution_code/Value:hardware repair' => 'Ремонт оборудования',
'Class:UserRequest/Attribute:resolution_code/Value:hardware repair+' => 'Ремонт оборудования',
'Class:UserRequest/Attribute:resolution_code/Value:other' => 'Другое',
'Class:UserRequest/Attribute:resolution_code/Value:other+' => 'Другое',
'Class:UserRequest/Attribute:resolution_code/Value:software patch' => 'Патч ПО',
'Class:UserRequest/Attribute:resolution_code/Value:software patch+' => 'Патч ПО',
'Class:UserRequest/Attribute:resolution_code/Value:system update' => 'Обновление системы',
'Class:UserRequest/Attribute:resolution_code/Value:system update+' => 'Обновление системы',
'Class:UserRequest/Attribute:resolution_code/Value:training' => 'Обучение',
'Class:UserRequest/Attribute:resolution_code/Value:training+' => 'Обучение',
'Class:UserRequest/Attribute:solution' => 'Описание решения',
'Class:UserRequest/Attribute:solution+' => '',
'Class:UserRequest/Attribute:pending_reason' => 'Причина ожидания',
'Class:UserRequest/Attribute:pending_reason+' => '',
'Class:UserRequest/Attribute:parent_request_id' => 'Родительский запрос',
'Class:UserRequest/Attribute:parent_request_id+' => '',
'Class:UserRequest/Attribute:parent_request_ref' => 'Родительский запрос',
'Class:UserRequest/Attribute:parent_request_ref+' => '',
'Class:UserRequest/Attribute:parent_problem_id' => 'Родительская проблема',
'Class:UserRequest/Attribute:parent_problem_id+' => '',
'Class:UserRequest/Attribute:parent_problem_ref' => 'Родительская проблема',
'Class:UserRequest/Attribute:parent_problem_ref+' => '',
'Class:UserRequest/Attribute:parent_change_id' => 'Родительское изменение',
'Class:UserRequest/Attribute:parent_change_id+' => '',
'Class:UserRequest/Attribute:parent_change_ref' => 'Родительское изменение',
'Class:UserRequest/Attribute:parent_change_ref+' => '',
'Class:UserRequest/Attribute:related_request_list' => 'Дочерние запросы',
'Class:UserRequest/Attribute:related_request_list+' => 'Дочерние запросы',
'Class:UserRequest/Attribute:public_log' => 'Общий журнал',
'Class:UserRequest/Attribute:public_log+' => 'Информация в общем журнале доступна для пользователей портала',
'Class:UserRequest/Attribute:user_satisfaction' => 'Удовлетворенность пользователя',
'Class:UserRequest/Attribute:user_satisfaction+' => '',
'Class:UserRequest/Attribute:user_satisfaction/Value:1' => 'Очень доволен',
'Class:UserRequest/Attribute:user_satisfaction/Value:1+' => 'Очень доволен',
'Class:UserRequest/Attribute:user_satisfaction/Value:2' => 'Вполне доволен',
'Class:UserRequest/Attribute:user_satisfaction/Value:2+' => 'Вполне доволен',
'Class:UserRequest/Attribute:user_satisfaction/Value:3' => 'Скорее недоволен',
'Class:UserRequest/Attribute:user_satisfaction/Value:3+' => 'Скорее недоволен',
'Class:UserRequest/Attribute:user_satisfaction/Value:4' => 'Очень недоволен',
'Class:UserRequest/Attribute:user_satisfaction/Value:4+' => 'Очень недоволен',
'Class:UserRequest/Attribute:user_comment' => 'Комментарий пользователя',
'Class:UserRequest/Attribute:user_comment+' => '',
'Class:UserRequest/Attribute:parent_request_id_friendlyname' => 'Родительский запрос',
'Class:UserRequest/Attribute:parent_request_id_friendlyname+' => '',
'Class:UserRequest/Stimulus:ev_assign' => 'Назначить',
'Class:UserRequest/Stimulus:ev_assign+' => '',
'Class:UserRequest/Stimulus:ev_reassign' => 'Переназначить',
'Class:UserRequest/Stimulus:ev_reassign+' => '',
'Class:UserRequest/Stimulus:ev_approve' => 'Утвердить',
'Class:UserRequest/Stimulus:ev_approve+' => '',
'Class:UserRequest/Stimulus:ev_reject' => 'Отклонить',
'Class:UserRequest/Stimulus:ev_reject+' => '',
'Class:UserRequest/Stimulus:ev_pending' => 'В ожидание',
'Class:UserRequest/Stimulus:ev_pending+' => '',
'Class:UserRequest/Stimulus:ev_timeout' => 'Таймаут',
'Class:UserRequest/Stimulus:ev_timeout+' => '',
'Class:UserRequest/Stimulus:ev_autoresolve' => 'Автоматическое решение',
'Class:UserRequest/Stimulus:ev_autoresolve+' => '',
'Class:UserRequest/Stimulus:ev_autoclose' => 'Автоматическое закрытие',
'Class:UserRequest/Stimulus:ev_autoclose+' => '',
'Class:UserRequest/Stimulus:ev_resolve' => 'Отметить как решенный',
'Class:UserRequest/Stimulus:ev_resolve+' => '',
'Class:UserRequest/Stimulus:ev_close' => 'Закрыть',
'Class:UserRequest/Stimulus:ev_close+' => '',
'Class:UserRequest/Stimulus:ev_reopen' => 'Вновь открыть',
'Class:UserRequest/Stimulus:ev_reopen+' => '',
'Class:UserRequest/Stimulus:ev_wait_for_approval' => 'Ждать утверждения',
'Class:UserRequest/Stimulus:ev_wait_for_approval+' => '',
'Class:UserRequest/Error:CannotAssignParentRequestIdToSelf' => 'Невозможно назначить этот же запрос в качестве родительского',
));
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Portal:TitleDetailsFor_Request' => 'Детали запроса',
'Portal:ButtonUpdate' => 'Обновить',
'Portal:ButtonClose' => 'Закрыть',
'Portal:ButtonReopen' => 'Открыть вновь',
'Portal:ShowServices' => 'Каталог услуг',
'Portal:SelectRequestType' => 'Выберите тип запроса',
'Portal:SelectServiceElementFrom_Service' => 'Выберите элемент услуги для %1$s',
'Portal:SelectRequestTemplate' => 'Выберите шаблон для %1$s',
'Portal:ListServices' => 'Список услуг',
'Portal:TitleDetailsFor_Service' => 'Детали услуги',
'Portal:Button:CreateRequestFromService' => 'Создать запрос по услуге',
'Portal:ListOpenRequests' => 'Открытые запросы',
'Portal:UserRequest:MoreInfo' => 'Дополнительная информация',
'Portal:Details-Service-Element' => 'Элементы услуги',
'Portal:NoClosedTicket' => 'Нет закрытых запросов',
'Portal:NoService' => '',
'Portal:ListOpenProblems' => 'Открытые проблемы',
'Portal:ShowProblem' => 'Проблемы',
'Portal:ShowFaqs' => 'FAQ',
'Portal:NoOpenProblem' => 'Нет открытых проблем',
'Portal:SelectLanguage' => "Изменить язык",
'Portal:LanguageChangedTo_Lang' => 'Язык изменен на',
'Portal:ChooseYourFavoriteLanguage' => 'Выберите язык',
'Class:UserRequest/Method:ResolveChildTickets' => 'ResolveChildTickets',
'Class:UserRequest/Method:ResolveChildTickets+' => 'Cascade the resolution to child requests (ev_autoresolve), and align the following characteristics of the request: service, team, agent, resolution info',
));

View File

@@ -0,0 +1,494 @@
<?php
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Menu:ServiceManagement' => 'Управление услугами',
'Menu:ServiceManagement+' => 'Управление услугами',
'Menu:Service:Overview' => 'Обзор',
'Menu:Service:Overview+' => 'Управление услугами - Обзор',
'UI-ServiceManagementMenu-ContractsBySrvLevel' => 'Договоры по уровню услуги',
'UI-ServiceManagementMenu-ContractsByStatus' => 'Договоры по статусу',
'UI-ServiceManagementMenu-ContractsEndingIn30Days' => 'Договоры, оканчивающиеся в течение 30-ти дней',
'Menu:ProviderContract' => 'Договоры с поставщиками',
'Menu:ProviderContract+' => 'Договоры с поставщиками',
'Menu:CustomerContract' => 'Договоры с заказчиками',
'Menu:CustomerContract+' => 'Договоры с заказчиками',
'Menu:ServiceSubcategory' => 'Подкатегории услуг',
'Menu:ServiceSubcategory+' => 'Подкатегории услуг',
'Menu:Service' => 'Услуги',
'Menu:Service+' => 'Услуги',
'Menu:ServiceElement' => 'Элементы услуг',
'Menu:ServiceElement+' => 'Элементы услуг',
'Menu:SLA' => 'SLA',
'Menu:SLA+' => 'Соглашения об уровне услуг',
'Menu:SLT' => 'SLT',
'Menu:SLT+' => 'Целевые показатели уровня услуг',
'Menu:DeliveryModel' => 'Модели предоставления услуг',
'Menu:DeliveryModel+' => 'Модели предоставления услуг (Delivery Models)',
));
//
// Class: Organization
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:Organization/Attribute:deliverymodel_id' => 'Модель предоставления услуг',
'Class:Organization/Attribute:deliverymodel_id+' => 'Модель предоставления услуг (Delivery Model)',
'Class:Organization/Attribute:deliverymodel_name' => 'Модель предоставления услуг',
'Class:Organization/Attribute:deliverymodel_name+' => '',
));
//
// Class: ContractType
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:ContractType' => 'Тип договора',
'Class:ContractType+' => '',
));
//
// Class: Contract
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:Contract' => 'Договор',
'Class:Contract+' => '',
'Class:Contract/Attribute:name' => 'Название',
'Class:Contract/Attribute:name+' => '',
'Class:Contract/Attribute:org_id' => 'Заказчик',
'Class:Contract/Attribute:org_id+' => '',
'Class:Contract/Attribute:organization_name' => 'Заказчик',
'Class:Contract/Attribute:organization_name+' => '',
'Class:Contract/Attribute:contacts_list' => 'Контакты',
'Class:Contract/Attribute:contacts_list+' => 'Связанные контакты',
'Class:Contract/Attribute:documents_list' => 'Документы',
'Class:Contract/Attribute:documents_list+' => 'Связанные документы',
'Class:Contract/Attribute:description' => 'Описание',
'Class:Contract/Attribute:description+' => '',
'Class:Contract/Attribute:start_date' => 'Дата начала',
'Class:Contract/Attribute:start_date+' => '',
'Class:Contract/Attribute:end_date' => 'Дата окончания',
'Class:Contract/Attribute:end_date+' => '',
'Class:Contract/Attribute:cost' => 'Стоимость',
'Class:Contract/Attribute:cost+' => '',
'Class:Contract/Attribute:cost_currency' => 'Валюта стоимости',
'Class:Contract/Attribute:cost_currency+' => '',
'Class:Contract/Attribute:cost_currency/Value:dollars' => 'Доллары',
'Class:Contract/Attribute:cost_currency/Value:dollars+' => '',
'Class:Contract/Attribute:cost_currency/Value:euros' => 'Евро',
'Class:Contract/Attribute:cost_currency/Value:euros+' => '',
'Class:Contract/Attribute:contracttype_id' => 'Тип договора',
'Class:Contract/Attribute:contracttype_id+' => '',
'Class:Contract/Attribute:contracttype_name' => 'Тип договора',
'Class:Contract/Attribute:contracttype_name+' => '',
'Class:Contract/Attribute:billing_frequency' => 'Периодичность платежей',
'Class:Contract/Attribute:billing_frequency+' => '',
'Class:Contract/Attribute:cost_unit' => 'Единица стоимости',
'Class:Contract/Attribute:cost_unit+' => '',
'Class:Contract/Attribute:provider_id' => 'Поставщик',
'Class:Contract/Attribute:provider_id+' => '',
'Class:Contract/Attribute:provider_name' => 'Поставщик',
'Class:Contract/Attribute:provider_name+' => '',
'Class:Contract/Attribute:status' => 'Статус',
'Class:Contract/Attribute:status+' => '',
'Class:Contract/Attribute:status/Value:implementation' => 'Внедрение',
'Class:Contract/Attribute:status/Value:implementation+' => 'Внедрение',
'Class:Contract/Attribute:status/Value:obsolete' => 'Устаревшее',
'Class:Contract/Attribute:status/Value:obsolete+' => 'Устаревшее',
'Class:Contract/Attribute:status/Value:production' => 'Эксплуатация',
'Class:Contract/Attribute:status/Value:production+' => 'Эксплуатация',
'Class:Contract/Attribute:finalclass' => 'Тип',
'Class:Contract/Attribute:finalclass+' => '',
));
//
// Class: CustomerContract
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:CustomerContract' => 'Договор с заказчиком',
'Class:CustomerContract+' => '',
'Class:CustomerContract/Attribute:services_list' => 'Услуги',
'Class:CustomerContract/Attribute:services_list+' => 'Все услуги, предоставляемые по договору',
'Class:CustomerContract/Attribute:functionalcis_list' => 'КЕ',
'Class:CustomerContract/Attribute:functionalcis_list+' => 'Конфигурационные единицы, охватываемые договором',
'Class:CustomerContract/Attribute:providercontracts_list' => 'Договоры с поставщиками',
'Class:CustomerContract/Attribute:providercontracts_list+' => 'Договоры с поставщиками, используемые для поддержки услуг данного договора (Underpinning Contracts)',
));
//
// Class: ProviderContract
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:ProviderContract' => 'Договор с поставщиком',
'Class:ProviderContract+' => '',
'Class:ProviderContract/Attribute:functionalcis_list' => 'КЕ',
'Class:ProviderContract/Attribute:functionalcis_list+' => 'Конфигурационные единицы, охватываемые договором',
'Class:ProviderContract/Attribute:sla' => 'SLA',
'Class:ProviderContract/Attribute:sla+' => 'Соглашение об уровне услуги (Service Level Agreement)',
'Class:ProviderContract/Attribute:coverage' => 'Время обслуживания',
'Class:ProviderContract/Attribute:coverage+' => '',
));
//
// Class: lnkContactToContract
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkContactToContract' => 'Связь Контакт/Договор',
'Class:lnkContactToContract+' => '',
'Class:lnkContactToContract/Attribute:contract_id' => 'Договор',
'Class:lnkContactToContract/Attribute:contract_id+' => '',
'Class:lnkContactToContract/Attribute:contract_name' => 'Договор',
'Class:lnkContactToContract/Attribute:contract_name+' => '',
'Class:lnkContactToContract/Attribute:contact_id' => 'Контакт',
'Class:lnkContactToContract/Attribute:contact_id+' => '',
'Class:lnkContactToContract/Attribute:contact_name' => 'Контакт',
'Class:lnkContactToContract/Attribute:contact_name+' => '',
));
//
// Class: lnkContractToDocument
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkContractToDocument' => 'Связь Договор/Документ',
'Class:lnkContractToDocument+' => '',
'Class:lnkContractToDocument/Attribute:contract_id' => 'Договор',
'Class:lnkContractToDocument/Attribute:contract_id+' => '',
'Class:lnkContractToDocument/Attribute:contract_name' => 'Договор',
'Class:lnkContractToDocument/Attribute:contract_name+' => '',
'Class:lnkContractToDocument/Attribute:document_id' => 'Документ',
'Class:lnkContractToDocument/Attribute:document_id+' => '',
'Class:lnkContractToDocument/Attribute:document_name' => 'Документ',
'Class:lnkContractToDocument/Attribute:document_name+' => '',
));
//
// Class: lnkFunctionalCIToProviderContract
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkFunctionalCIToProviderContract' => 'Связь Функциональная КЕ/Договор с поставщиком',
'Class:lnkFunctionalCIToProviderContract+' => '',
'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_id' => 'Договор с поставщиком',
'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_id+' => '',
'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_name' => 'Договор с поставщиком',
'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_name+' => '',
'Class:lnkFunctionalCIToProviderContract/Attribute:functionalci_id' => 'КЕ',
'Class:lnkFunctionalCIToProviderContract/Attribute:functionalci_id+' => '',
'Class:lnkFunctionalCIToProviderContract/Attribute:functionalci_name' => 'КЕ',
'Class:lnkFunctionalCIToProviderContract/Attribute:functionalci_name+' => '',
));
//
// Class: ServiceFamily
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:ServiceFamily' => 'Пакет услуг',
'Class:ServiceFamily+' => '',
'Class:ServiceFamily/Attribute:name' => 'Название',
'Class:ServiceFamily/Attribute:name+' => '',
'Class:ServiceFamily/Attribute:services_list' => 'Услуги',
'Class:ServiceFamily/Attribute:services_list+' => 'Связанные услуги',
));
//
// Class: Service
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:Service' => 'Услуга',
'Class:Service+' => '',
'Class:Service/Attribute:name' => 'Название',
'Class:Service/Attribute:name+' => '',
'Class:Service/Attribute:org_id' => 'Поставщик',
'Class:Service/Attribute:org_id+' => '',
'Class:Service/Attribute:organization_name' => 'Поставщик',
'Class:Service/Attribute:organization_name+' => '',
'Class:Service/Attribute:description' => 'Описание',
'Class:Service/Attribute:description+' => '',
'Class:Service/Attribute:servicefamily_id' => 'Пакет услуг',
'Class:Service/Attribute:servicefamily_id+' => '',
'Class:Service/Attribute:servicefamily_name' => 'Пакет услуг',
'Class:Service/Attribute:servicefamily_name+' => '',
'Class:Service/Attribute:documents_list' => 'Документы',
'Class:Service/Attribute:documents_list+' => 'Связанные документы',
'Class:Service/Attribute:contacts_list' => 'Контакты',
'Class:Service/Attribute:contacts_list+' => 'Связанные контакты',
'Class:Service/Attribute:status' => 'Статус',
'Class:Service/Attribute:status+' => '',
'Class:Service/Attribute:status/Value:implementation' => 'Внедрение',
'Class:Service/Attribute:status/Value:implementation+' => 'Внедрение',
'Class:Service/Attribute:status/Value:obsolete' => 'Устаревшее',
'Class:Service/Attribute:status/Value:obsolete+' => 'Устаревшее',
'Class:Service/Attribute:status/Value:production' => 'Эксплуатация',
'Class:Service/Attribute:status/Value:production+' => 'Эксплуатация',
'Class:Service/Attribute:customercontracts_list' => 'Договоры с заказчиками',
'Class:Service/Attribute:customercontracts_list+' => 'Договоры с заказчиками, по которым предоставляется услуга',
'Class:Service/Attribute:servicesubcategories_list' => 'Подкатегории услуги',
'Class:Service/Attribute:servicesubcategories_list+' => 'Подкатегории услуги',
));
//
// Class: lnkDocumentToService
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkDocumentToService' => 'Связь Документ/Услуга',
'Class:lnkDocumentToService+' => '',
'Class:lnkDocumentToService/Attribute:service_id' => 'Услуга',
'Class:lnkDocumentToService/Attribute:service_id+' => '',
'Class:lnkDocumentToService/Attribute:service_name' => 'Услуга',
'Class:lnkDocumentToService/Attribute:service_name+' => '',
'Class:lnkDocumentToService/Attribute:document_id' => 'Документ',
'Class:lnkDocumentToService/Attribute:document_id+' => '',
'Class:lnkDocumentToService/Attribute:document_name' => 'Документ',
'Class:lnkDocumentToService/Attribute:document_name+' => '',
));
//
// Class: lnkContactToService
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkContactToService' => 'Связь Контакт/Услуга',
'Class:lnkContactToService+' => '',
'Class:lnkContactToService/Attribute:service_id' => 'Услуга',
'Class:lnkContactToService/Attribute:service_id+' => '',
'Class:lnkContactToService/Attribute:service_name' => 'Услуга',
'Class:lnkContactToService/Attribute:service_name+' => '',
'Class:lnkContactToService/Attribute:contact_id' => 'Контакт',
'Class:lnkContactToService/Attribute:contact_id+' => '',
'Class:lnkContactToService/Attribute:contact_name' => 'Контакт',
'Class:lnkContactToService/Attribute:contact_name+' => '',
));
//
// Class: ServiceSubcategory
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:ServiceSubcategory' => 'Подкатегория услуги',
'Class:ServiceSubcategory+' => '',
'Class:ServiceSubcategory/Attribute:name' => 'Название',
'Class:ServiceSubcategory/Attribute:name+' => '',
'Class:ServiceSubcategory/Attribute:description' => 'Описание',
'Class:ServiceSubcategory/Attribute:description+' => '',
'Class:ServiceSubcategory/Attribute:service_id' => 'Услуга',
'Class:ServiceSubcategory/Attribute:service_id+' => '',
'Class:ServiceSubcategory/Attribute:service_name' => 'Услуга',
'Class:ServiceSubcategory/Attribute:service_name+' => '',
'Class:ServiceSubcategory/Attribute:status' => 'Статус',
'Class:ServiceSubcategory/Attribute:status+' => '',
'Class:ServiceSubcategory/Attribute:status/Value:implementation' => 'Внедрение',
'Class:ServiceSubcategory/Attribute:status/Value:implementation+' => 'Внедрение',
'Class:ServiceSubcategory/Attribute:status/Value:obsolete' => 'Устаревшее',
'Class:ServiceSubcategory/Attribute:status/Value:obsolete+' => 'Устаревшее',
'Class:ServiceSubcategory/Attribute:status/Value:production' => 'Эксплуатация',
'Class:ServiceSubcategory/Attribute:status/Value:production+' => 'Эксплуатация',
'Class:ServiceSubcategory/Attribute:request_type' => 'Тип запроса',
'Class:ServiceSubcategory/Attribute:request_type+' => '',
'Class:ServiceSubcategory/Attribute:request_type/Value:incident' => 'Инцидент',
'Class:ServiceSubcategory/Attribute:request_type/Value:incident+' => 'Инцидент',
'Class:ServiceSubcategory/Attribute:request_type/Value:service_request' => 'Запрос на обслуживание',
'Class:ServiceSubcategory/Attribute:request_type/Value:service_request+' => 'Запрос на обслуживание',
'Class:ServiceSubcategory/Attribute:service_provider' => 'Поставщик',
'Class:ServiceSubcategory/Attribute:service_org_id' => 'Поставщик',
));
//
// Class: SLA
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:SLA' => 'SLA',
'Class:SLA+' => '',
'Class:SLA/Attribute:name' => 'Название',
'Class:SLA/Attribute:name+' => '',
'Class:SLA/Attribute:description' => 'Описание',
'Class:SLA/Attribute:description+' => '',
'Class:SLA/Attribute:org_id' => 'Организация',
'Class:SLA/Attribute:org_id+' => '',
'Class:SLA/Attribute:organization_name' => 'Организация',
'Class:SLA/Attribute:organization_name+' => '',
'Class:SLA/Attribute:slts_list' => 'SLT',
'Class:SLA/Attribute:slts_list+' => 'Целевой показатель уровня услуги (Service Level Target)',
'Class:SLA/Attribute:customercontracts_list' => 'Договоры с заказчиками',
'Class:SLA/Attribute:customercontracts_list+' => 'Договоры с заказчиками, в которых используется SLA',
));
//
// Class: SLT
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:SLT' => 'SLT',
'Class:SLT+' => '',
'Class:SLT/Attribute:name' => 'Название',
'Class:SLT/Attribute:name+' => '',
'Class:SLT/Attribute:priority' => 'Приоритет',
'Class:SLT/Attribute:priority+' => '',
'Class:SLT/Attribute:priority/Value:1' => 'Критический',
'Class:SLT/Attribute:priority/Value:1+' => 'Критический',
'Class:SLT/Attribute:priority/Value:2' => 'Высокий',
'Class:SLT/Attribute:priority/Value:2+' => 'Высокий',
'Class:SLT/Attribute:priority/Value:3' => 'Средний',
'Class:SLT/Attribute:priority/Value:3+' => 'Средний',
'Class:SLT/Attribute:priority/Value:4' => 'Низкий',
'Class:SLT/Attribute:priority/Value:4+' => 'Низкий',
'Class:SLT/Attribute:request_type' => 'Тип запроса',
'Class:SLT/Attribute:request_type+' => '',
'Class:SLT/Attribute:request_type/Value:incident' => 'Инцидент',
'Class:SLT/Attribute:request_type/Value:incident+' => 'Инцидент',
'Class:SLT/Attribute:request_type/Value:service_request' => 'Запрос на обслуживание',
'Class:SLT/Attribute:request_type/Value:service_request+' => 'Запрос на обслуживание',
'Class:SLT/Attribute:metric' => 'Метрика',
'Class:SLT/Attribute:metric+' => '',
'Class:SLT/Attribute:metric/Value:tto' => 'TTO',
'Class:SLT/Attribute:metric/Value:tto+' => 'Time-To-Own - время до назначения агента (принятия в работу)',
'Class:SLT/Attribute:metric/Value:ttr' => 'TTR',
'Class:SLT/Attribute:metric/Value:ttr+' => 'Time-To-Resolve - время до решения',
'Class:SLT/Attribute:value' => 'Значение',
'Class:SLT/Attribute:value+' => '',
'Class:SLT/Attribute:unit' => 'Единицы',
'Class:SLT/Attribute:unit+' => '',
'Class:SLT/Attribute:unit/Value:hours' => 'Часы',
'Class:SLT/Attribute:unit/Value:hours+' => 'Часы',
'Class:SLT/Attribute:unit/Value:minutes' => 'Минуты',
'Class:SLT/Attribute:unit/Value:minutes+' => 'Минуты',
));
//
// Class: lnkSLAToSLT
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkSLAToSLT' => 'Связь SLA/SLT',
'Class:lnkSLAToSLT+' => '',
'Class:lnkSLAToSLT/Attribute:sla_id' => 'SLA',
'Class:lnkSLAToSLT/Attribute:sla_id+' => '',
'Class:lnkSLAToSLT/Attribute:sla_name' => 'SLA',
'Class:lnkSLAToSLT/Attribute:sla_name+' => '',
'Class:lnkSLAToSLT/Attribute:slt_id' => 'SLT',
'Class:lnkSLAToSLT/Attribute:slt_id+' => '',
'Class:lnkSLAToSLT/Attribute:slt_name' => 'SLT',
'Class:lnkSLAToSLT/Attribute:slt_name+' => '',
));
//
// Class: lnkCustomerContractToService
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkCustomerContractToService' => 'Связь Договор с заказчиком/Услуга',
'Class:lnkCustomerContractToService+' => '',
'Class:lnkCustomerContractToService/Attribute:customercontract_id' => 'Договор с заказчиком',
'Class:lnkCustomerContractToService/Attribute:customercontract_id+' => '',
'Class:lnkCustomerContractToService/Attribute:customercontract_name' => 'Договор с заказчиком',
'Class:lnkCustomerContractToService/Attribute:customercontract_name+' => '',
'Class:lnkCustomerContractToService/Attribute:service_id' => 'Услуга',
'Class:lnkCustomerContractToService/Attribute:service_id+' => '',
'Class:lnkCustomerContractToService/Attribute:service_name' => 'Услуга',
'Class:lnkCustomerContractToService/Attribute:service_name+' => '',
'Class:lnkCustomerContractToService/Attribute:sla_id' => 'SLA',
'Class:lnkCustomerContractToService/Attribute:sla_id+' => '',
'Class:lnkCustomerContractToService/Attribute:sla_name' => 'SLA',
'Class:lnkCustomerContractToService/Attribute:sla_name+' => '',
));
//
// Class: lnkCustomerContractToProviderContract
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkCustomerContractToProviderContract' => 'Связь Договор с заказчиком/Договор с поставщиком',
'Class:lnkCustomerContractToProviderContract+' => '',
'Class:lnkCustomerContractToProviderContract/Attribute:customercontract_id' => 'Договор с заказчиком',
'Class:lnkCustomerContractToProviderContract/Attribute:customercontract_id+' => '',
'Class:lnkCustomerContractToProviderContract/Attribute:customercontract_name' => 'Договор с заказчиком',
'Class:lnkCustomerContractToProviderContract/Attribute:customercontract_name+' => '',
'Class:lnkCustomerContractToProviderContract/Attribute:providercontract_id' => 'Договор с поставщиком',
'Class:lnkCustomerContractToProviderContract/Attribute:providercontract_id+' => '',
'Class:lnkCustomerContractToProviderContract/Attribute:providercontract_name' => 'Договор с поставщиком',
'Class:lnkCustomerContractToProviderContract/Attribute:providercontract_name+' => '',
));
//
// Class: lnkCustomerContractToFunctionalCI
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkCustomerContractToFunctionalCI' => 'Связь Договор с заказчиком/Функциональная КЕ',
'Class:lnkCustomerContractToFunctionalCI+' => '',
'Class:lnkCustomerContractToFunctionalCI/Attribute:customercontract_id' => 'Договор с заказчиком',
'Class:lnkCustomerContractToFunctionalCI/Attribute:customercontract_id+' => '',
'Class:lnkCustomerContractToFunctionalCI/Attribute:customercontract_name' => 'Договор с заказчиком',
'Class:lnkCustomerContractToFunctionalCI/Attribute:customercontract_name+' => '',
'Class:lnkCustomerContractToFunctionalCI/Attribute:functionalci_id' => 'КЕ',
'Class:lnkCustomerContractToFunctionalCI/Attribute:functionalci_id+' => '',
'Class:lnkCustomerContractToFunctionalCI/Attribute:functionalci_name' => 'КЕ',
'Class:lnkCustomerContractToFunctionalCI/Attribute:functionalci_name+' => '',
));
//
// Class: DeliveryModel
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:DeliveryModel' => 'Модель предоставления услуг',
'Class:DeliveryModel+' => '',
'Class:DeliveryModel/Attribute:name' => 'Название',
'Class:DeliveryModel/Attribute:name+' => '',
'Class:DeliveryModel/Attribute:org_id' => 'Организация',
'Class:DeliveryModel/Attribute:org_id+' => '',
'Class:DeliveryModel/Attribute:organization_name' => 'Организация',
'Class:DeliveryModel/Attribute:organization_name+' => '',
'Class:DeliveryModel/Attribute:description' => 'Описание',
'Class:DeliveryModel/Attribute:description+' => '',
'Class:DeliveryModel/Attribute:contacts_list' => 'Контакты',
'Class:DeliveryModel/Attribute:contacts_list+' => 'Контакты (команды и персоны), которые участвуют в предоставлении услуг по этой модели',
'Class:DeliveryModel/Attribute:customers_list' => 'Заказчики',
'Class:DeliveryModel/Attribute:customers_list+' => 'Заказчики, которым предоставляются услуги по этой модели',
));
//
// Class: lnkDeliveryModelToContact
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkDeliveryModelToContact' => 'Связь Модель предоставления услуг/Контакт',
'Class:lnkDeliveryModelToContact+' => '',
'Class:lnkDeliveryModelToContact/Attribute:deliverymodel_id' => 'Модель предоставления услуг',
'Class:lnkDeliveryModelToContact/Attribute:deliverymodel_id+' => '',
'Class:lnkDeliveryModelToContact/Attribute:deliverymodel_name' => 'Модель предоставления услуг',
'Class:lnkDeliveryModelToContact/Attribute:deliverymodel_name+' => '',
'Class:lnkDeliveryModelToContact/Attribute:contact_id' => 'Контакт',
'Class:lnkDeliveryModelToContact/Attribute:contact_id+' => '',
'Class:lnkDeliveryModelToContact/Attribute:contact_name' => 'Контакт',
'Class:lnkDeliveryModelToContact/Attribute:contact_name+' => '',
'Class:lnkDeliveryModelToContact/Attribute:role_id' => 'Роль',
'Class:lnkDeliveryModelToContact/Attribute:role_id+' => '',
'Class:lnkDeliveryModelToContact/Attribute:role_name' => 'Роль',
'Class:lnkDeliveryModelToContact/Attribute:role_name+' => '',
));

View File

@@ -174,7 +174,6 @@
</fields>
<methods/>
<presentation>
<parent>Typology</parent>
<details>
<items>
<item id="name">

View File

@@ -2,39 +2,20 @@
/**
* Локализация интерфейса Combodo iTop подготовлена сообществом iTop по-русски http://community.itop-itsm.ru.
*
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @license http://opensource.org/licenses/AGPL-3.0
*
*
* Инструкция по установке
*
* Процесс установки заключается в замене имеющихся локализационных файлов полученными и последующем запуске процедуры обновления iTop для перекомпиляции кода.
* 1. Скопируйте с заменой два полученных файла из "itop-rus/dictionaries" в "путь/до/вашего/itop/dictionaries".
* 2. Скопируйте с заменой полученные файлы "itop-rus/datamodels/2.x/название-модуля/ru.dict.название-модуля.php" в "путь/до/вашего/itop/datamodels/2.x/название-модуля".
* 3. Перейдите по адресу "http://адрес/вашего/itop/setup", при этом файл "путь/до/вашего/itop/conf/production/config-itop.php" должен быть доступен для записи.
* 4. На второй странице установщика выберите "Upgrade an existing iTop instance" и следуйте дальнейшим инструкциям установщика.
*
* Ответы на вопросы по установке и использованию переводов, а также на любые другие вопросы по iTop всегда можно получить на сайте сообщества iTop по-русски http://community.itop-itsm.ru.
* @author Vladimir Kunin <v.b.kunin@gmail.com>
* @link http://community.itop-itsm.ru iTop Russian Community
* @link https://github.com/itop-itsm-ru/itop-rus
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// 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>+
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Menu:ServiceManagement' => 'Управление услугами',
'Menu:ServiceManagement+' => 'Управление услугами',
'Menu:Service:Overview' => 'Обзор',
'Menu:Service:Overview+' => '',
'Menu:Service:Overview+' => 'Управление услугами - Обзор',
'UI-ServiceManagementMenu-ContractsBySrvLevel' => 'Договоры по уровню услуг',
'UI-ServiceManagementMenu-ContractsByStatus' => 'Договоры по статусу',
'UI-ServiceManagementMenu-ContractsEndingIn30Days' => 'Договоры, оканчивающиеся в течение 30-ти дней',
@@ -54,7 +35,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Menu:SLT' => 'SLT',
'Menu:SLT+' => 'Целевые показатели уровня услуг',
'Menu:DeliveryModel' => 'Модели предоставления услуг',
'Menu:DeliveryModel+' => 'Модели предоставления услуг (Delivery models)',
'Menu:DeliveryModel+' => 'Модели предоставления услуг (Delivery Models)',
'Menu:ServiceFamily' => 'Пакеты услуг',
'Menu:ServiceFamily+' => 'Пакеты услуг',
'Menu:Procedure' => 'Каталог процедур',
@@ -69,9 +50,9 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
//
Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:Organization/Attribute:deliverymodel_id' => 'Delivery model',
'Class:Organization/Attribute:deliverymodel_id+' => '',
'Class:Organization/Attribute:deliverymodel_name' => 'Delivery model name',
'Class:Organization/Attribute:deliverymodel_id' => 'Модель предоставления услуг',
'Class:Organization/Attribute:deliverymodel_id+' => 'Модель предоставления услуг (Delivery Model)',
'Class:Organization/Attribute:deliverymodel_name' => 'Модель предоставления услуг',
));
@@ -96,8 +77,8 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:Contract/Attribute:name+' => '',
'Class:Contract/Attribute:org_id' => 'Заказчик',
'Class:Contract/Attribute:org_id+' => '',
'Class:Contract/Attribute:organization_name' => 'Имя заказчика',
'Class:Contract/Attribute:organization_name+' => 'Common name',
'Class:Contract/Attribute:organization_name' => 'Заказчик',
'Class:Contract/Attribute:organization_name+' => '',
'Class:Contract/Attribute:contacts_list' => 'Контакты',
'Class:Contract/Attribute:contacts_list+' => 'Связанные контакты',
'Class:Contract/Attribute:documents_list' => 'Документы',
@@ -118,25 +99,25 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:Contract/Attribute:cost_currency/Value:euros+' => '',
'Class:Contract/Attribute:contracttype_id' => 'Тип договора',
'Class:Contract/Attribute:contracttype_id+' => '',
'Class:Contract/Attribute:contracttype_name' => 'Имя типа договора',
'Class:Contract/Attribute:contracttype_name' => 'Тип договора',
'Class:Contract/Attribute:contracttype_name+' => '',
'Class:Contract/Attribute:billing_frequency' => 'Частота платежей',
'Class:Contract/Attribute:billing_frequency' => 'Периодичность платежей',
'Class:Contract/Attribute:billing_frequency+' => '',
'Class:Contract/Attribute:cost_unit' => 'Единица стоимости',
'Class:Contract/Attribute:cost_unit+' => '',
'Class:Contract/Attribute:provider_id' => 'Поставщик',
'Class:Contract/Attribute:provider_id+' => '',
'Class:Contract/Attribute:provider_name' => 'Имя поставщика',
'Class:Contract/Attribute:provider_name+' => 'Common name',
'Class:Contract/Attribute:provider_name' => 'Поставщик',
'Class:Contract/Attribute:provider_name+' => '',
'Class:Contract/Attribute:status' => 'Статус',
'Class:Contract/Attribute:status+' => '',
'Class:Contract/Attribute:status/Value:implementation' => 'Внедрение',
'Class:Contract/Attribute:status/Value:implementation+' => 'implementation',
'Class:Contract/Attribute:status/Value:implementation+' => 'Внедрение',
'Class:Contract/Attribute:status/Value:obsolete' => 'Устаревшее',
'Class:Contract/Attribute:status/Value:obsolete+' => 'obsolete',
'Class:Contract/Attribute:status/Value:production' => 'Производство',
'Class:Contract/Attribute:status/Value:production+' => 'production',
'Class:Contract/Attribute:finalclass' => 'Тип договора',
'Class:Contract/Attribute:status/Value:obsolete+' => 'Устаревшее',
'Class:Contract/Attribute:status/Value:production' => 'Эксплуатация',
'Class:Contract/Attribute:status/Value:production+' => 'Эксплуатация',
'Class:Contract/Attribute:finalclass' => 'Тип',
'Class:Contract/Attribute:finalclass+' => '',
));
//
@@ -147,7 +128,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:CustomerContract' => 'Договор с заказчиком',
'Class:CustomerContract+' => '',
'Class:CustomerContract/Attribute:services_list' => 'Услуги',
'Class:CustomerContract/Attribute:services_list+' => 'Связанные услуги',
'Class:CustomerContract/Attribute:services_list+' => 'Все услуги, предоставляемые по договору',
));
//
@@ -158,14 +139,14 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:ProviderContract' => 'Договор с поставщиком',
'Class:ProviderContract+' => '',
'Class:ProviderContract/Attribute:functionalcis_list' => 'КЕ',
'Class:ProviderContract/Attribute:functionalcis_list+' => 'Связанные конфигурационные единицы',
'Class:ProviderContract/Attribute:functionalcis_list+' => 'Конфигурационные единицы, охватываемые договором',
'Class:ProviderContract/Attribute:sla' => 'SLA',
'Class:ProviderContract/Attribute:sla+' => 'Соглашение об уровне услуги (Service Level Agreement)',
'Class:ProviderContract/Attribute:coverage' => 'Время работы',
'Class:ProviderContract/Attribute:coverage' => 'Время обслуживания',
'Class:ProviderContract/Attribute:coverage+' => '',
'Class:ProviderContract/Attribute:contracttype_id' => 'Тип договора',
'Class:ProviderContract/Attribute:contracttype_id+' => '',
'Class:ProviderContract/Attribute:contracttype_name' => 'Имя типа договора',
'Class:ProviderContract/Attribute:contracttype_name' => 'Тип договора',
'Class:ProviderContract/Attribute:contracttype_name+' => '',
));
@@ -178,11 +159,11 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkContactToContract+' => '',
'Class:lnkContactToContract/Attribute:contract_id' => 'Договор',
'Class:lnkContactToContract/Attribute:contract_id+' => '',
'Class:lnkContactToContract/Attribute:contract_name' => 'Имя договора',
'Class:lnkContactToContract/Attribute:contract_name' => 'Договор',
'Class:lnkContactToContract/Attribute:contract_name+' => '',
'Class:lnkContactToContract/Attribute:contact_id' => 'Контакт',
'Class:lnkContactToContract/Attribute:contact_id+' => '',
'Class:lnkContactToContract/Attribute:contact_name' => 'Контактное лицо',
'Class:lnkContactToContract/Attribute:contact_name' => 'Контакт',
'Class:lnkContactToContract/Attribute:contact_name+' => '',
));
@@ -195,11 +176,11 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkContractToDocument+' => '',
'Class:lnkContractToDocument/Attribute:contract_id' => 'Договор',
'Class:lnkContractToDocument/Attribute:contract_id+' => '',
'Class:lnkContractToDocument/Attribute:contract_name' => 'Имя договора',
'Class:lnkContractToDocument/Attribute:contract_name' => 'Договор',
'Class:lnkContractToDocument/Attribute:contract_name+' => '',
'Class:lnkContractToDocument/Attribute:document_id' => 'Документ',
'Class:lnkContractToDocument/Attribute:document_id+' => '',
'Class:lnkContractToDocument/Attribute:document_name' => 'Имя документа',
'Class:lnkContractToDocument/Attribute:document_name' => 'Документ',
'Class:lnkContractToDocument/Attribute:document_name+' => '',
));
@@ -212,11 +193,11 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkFunctionalCIToProviderContract+' => '',
'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_id' => 'Договор с поставщиком',
'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_id+' => '',
'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_name' => 'Имя договора поставщика',
'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_name' => 'Договор с поставщиком',
'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_name+' => '',
'Class:lnkFunctionalCIToProviderContract/Attribute:functionalci_id' => 'КЕ',
'Class:lnkFunctionalCIToProviderContract/Attribute:functionalci_id+' => '',
'Class:lnkFunctionalCIToProviderContract/Attribute:functionalci_name' => 'Имя КЕ',
'Class:lnkFunctionalCIToProviderContract/Attribute:functionalci_name' => 'КЕ',
'Class:lnkFunctionalCIToProviderContract/Attribute:functionalci_name+' => '',
));
@@ -244,11 +225,11 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:Service/Attribute:name+' => '',
'Class:Service/Attribute:org_id' => 'Поставщик',
'Class:Service/Attribute:org_id+' => '',
'Class:Service/Attribute:organization_name' => 'Имя поставщика',
'Class:Service/Attribute:organization_name' => 'Поставщик',
'Class:Service/Attribute:organization_name+' => '',
'Class:Service/Attribute:servicefamily_id' => 'Пакет услуг',
'Class:Service/Attribute:servicefamily_id+' => '',
'Class:Service/Attribute:servicefamily_name' => 'Имя пакета услуг',
'Class:Service/Attribute:servicefamily_name' => 'Пакет услуг',
'Class:Service/Attribute:servicefamily_name+' => '',
'Class:Service/Attribute:description' => 'Описание',
'Class:Service/Attribute:description+' => '',
@@ -259,17 +240,17 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:Service/Attribute:status' => 'Статус',
'Class:Service/Attribute:status+' => '',
'Class:Service/Attribute:status/Value:implementation' => 'Внедрение',
'Class:Service/Attribute:status/Value:implementation+' => '',
'Class:Service/Attribute:status/Value:implementation+' => 'Внедрение',
'Class:Service/Attribute:status/Value:obsolete' => 'Устаревшее',
'Class:Service/Attribute:status/Value:obsolete+' => '',
'Class:Service/Attribute:status/Value:production' => 'Производство',
'Class:Service/Attribute:status/Value:production+' => '',
'Class:Service/Attribute:status/Value:obsolete+' => 'Устаревшее',
'Class:Service/Attribute:status/Value:production' => 'Эксплуатация',
'Class:Service/Attribute:status/Value:production+' => 'Эксплуатация',
'Class:Service/Attribute:customercontracts_list' => 'Договоры с заказчиками',
'Class:Service/Attribute:customercontracts_list+' => 'Договоры с заказчиками',
'Class:Service/Attribute:customercontracts_list+' => 'Договоры с заказчиками, по которым предоставляется услуга',
'Class:Service/Attribute:providercontracts_list' => 'Договоры с поставщиками',
'Class:Service/Attribute:providercontracts_list+' => 'Договоры с поставщиками',
'Class:Service/Attribute:providercontracts_list+' => 'Договоры с поставщиками, по которым поддерживается услуга',
'Class:Service/Attribute:functionalcis_list' => 'Зависимость от КЕ',
'Class:Service/Attribute:functionalcis_list+' => 'Зависимость услуги от конфигурационных единиц',
'Class:Service/Attribute:functionalcis_list+' => 'Конфигурационные единицы, которые используются для предоставления услуги',
'Class:Service/Attribute:servicesubcategories_list' => 'Подкатегории услуги',
'Class:Service/Attribute:servicesubcategories_list+' => 'Подкатегории услуги',
));
@@ -283,11 +264,11 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkDocumentToService+' => '',
'Class:lnkDocumentToService/Attribute:service_id' => 'Услуга',
'Class:lnkDocumentToService/Attribute:service_id+' => '',
'Class:lnkDocumentToService/Attribute:service_name' => 'Имя услуги',
'Class:lnkDocumentToService/Attribute:service_name' => 'Услуга',
'Class:lnkDocumentToService/Attribute:service_name+' => '',
'Class:lnkDocumentToService/Attribute:document_id' => 'Документ',
'Class:lnkDocumentToService/Attribute:document_id+' => '',
'Class:lnkDocumentToService/Attribute:document_name' => 'Имя документа',
'Class:lnkDocumentToService/Attribute:document_name' => 'Документ',
'Class:lnkDocumentToService/Attribute:document_name+' => '',
));
@@ -300,11 +281,11 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkContactToService+' => '',
'Class:lnkContactToService/Attribute:service_id' => 'Услуга',
'Class:lnkContactToService/Attribute:service_id+' => '',
'Class:lnkContactToService/Attribute:service_name' => 'Имя услуги',
'Class:lnkContactToService/Attribute:service_name' => 'Услуга',
'Class:lnkContactToService/Attribute:service_name+' => '',
'Class:lnkContactToService/Attribute:contact_id' => 'Контакт',
'Class:lnkContactToService/Attribute:contact_id+' => '',
'Class:lnkContactToService/Attribute:contact_name' => 'Контактное лицо',
'Class:lnkContactToService/Attribute:contact_name' => 'Контакт',
'Class:lnkContactToService/Attribute:contact_name+' => '',
));
@@ -326,17 +307,19 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:ServiceSubcategory/Attribute:request_type' => 'Тип запроса',
'Class:ServiceSubcategory/Attribute:request_type+' => '',
'Class:ServiceSubcategory/Attribute:request_type/Value:incident' => 'Инцидент',
'Class:ServiceSubcategory/Attribute:request_type/Value:incident+' => 'incident',
'Class:ServiceSubcategory/Attribute:request_type/Value:incident+' => 'Инцидент',
'Class:ServiceSubcategory/Attribute:request_type/Value:service_request' => 'Запрос на обслуживание',
'Class:ServiceSubcategory/Attribute:request_type/Value:service_request+' => 'service request',
'Class:ServiceSubcategory/Attribute:request_type/Value:service_request+' => 'Запрос на обслуживание',
'Class:ServiceSubcategory/Attribute:status' => 'Статус',
'Class:ServiceSubcategory/Attribute:status+' => '',
'Class:ServiceSubcategory/Attribute:status/Value:implementation' => 'Внедрение',
'Class:ServiceSubcategory/Attribute:status/Value:implementation+' => 'implementation',
'Class:ServiceSubcategory/Attribute:status/Value:implementation+' => 'Внедрение',
'Class:ServiceSubcategory/Attribute:status/Value:obsolete' => 'Устаревшее',
'Class:ServiceSubcategory/Attribute:status/Value:obsolete+' => 'obsolete',
'Class:ServiceSubcategory/Attribute:status/Value:production' => 'Производство',
'Class:ServiceSubcategory/Attribute:status/Value:production+' => 'production',
'Class:ServiceSubcategory/Attribute:status/Value:obsolete+' => 'Устаревшее',
'Class:ServiceSubcategory/Attribute:status/Value:production' => 'Эксплуатация',
'Class:ServiceSubcategory/Attribute:status/Value:production+' => 'Эксплуатация',
'Class:ServiceSubcategory/Attribute:service_provider' => 'Поставщик',
'Class:ServiceSubcategory/Attribute:service_org_id' => 'Поставщик',
));
//
@@ -352,12 +335,12 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:SLA/Attribute:description+' => '',
'Class:SLA/Attribute:org_id' => 'Поставщик',
'Class:SLA/Attribute:org_id+' => '',
'Class:SLA/Attribute:organization_name' => 'Имя поставщика',
'Class:SLA/Attribute:organization_name+' => 'Common name',
'Class:SLA/Attribute:organization_name' => 'Поставщик',
'Class:SLA/Attribute:organization_name+' => '',
'Class:SLA/Attribute:slts_list' => 'SLT',
'Class:SLA/Attribute:slts_list+' => 'Целевой показатель уровня услуги (Service Level Target)',
'Class:SLA/Attribute:slts_list+' => 'Целевые показатели уровня услуги (Service Level Target)',
'Class:SLA/Attribute:customercontracts_list' => 'Договоры с заказчиками',
'Class:SLA/Attribute:customercontracts_list+' => 'Договоры с заказчиками',
'Class:SLA/Attribute:customercontracts_list+' => 'Договоры с заказчиками, в которых используется SLA',
));
//
@@ -372,33 +355,33 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:SLT/Attribute:priority' => 'Приоритет',
'Class:SLT/Attribute:priority+' => '',
'Class:SLT/Attribute:priority/Value:1' => 'Критический',
'Class:SLT/Attribute:priority/Value:1+' => 'critical',
'Class:SLT/Attribute:priority/Value:1+' => 'Критический',
'Class:SLT/Attribute:priority/Value:2' => 'Высокий',
'Class:SLT/Attribute:priority/Value:2+' => 'high',
'Class:SLT/Attribute:priority/Value:2+' => 'Высокий',
'Class:SLT/Attribute:priority/Value:3' => 'Средний',
'Class:SLT/Attribute:priority/Value:3+' => 'medium',
'Class:SLT/Attribute:priority/Value:3+' => 'Средний',
'Class:SLT/Attribute:priority/Value:4' => 'Низкий',
'Class:SLT/Attribute:priority/Value:4+' => 'low',
'Class:SLT/Attribute:priority/Value:4+' => 'Низкий',
'Class:SLT/Attribute:request_type' => 'Тип запроса',
'Class:SLT/Attribute:request_type+' => '',
'Class:SLT/Attribute:request_type/Value:incident' => 'Инцидент',
'Class:SLT/Attribute:request_type/Value:incident+' => 'incident',
'Class:SLT/Attribute:request_type/Value:service_request' => 'service request',
'Class:SLT/Attribute:request_type/Value:service_request+' => 'service request',
'Class:SLT/Attribute:request_type/Value:incident+' => 'Инцидент',
'Class:SLT/Attribute:request_type/Value:service_request' => 'Запрос на обслуживание',
'Class:SLT/Attribute:request_type/Value:service_request+' => 'Запрос на обслуживание',
'Class:SLT/Attribute:metric' => 'Метрика',
'Class:SLT/Attribute:metric+' => '',
'Class:SLT/Attribute:metric/Value:tto' => 'TTO',
'Class:SLT/Attribute:metric/Value:tto+' => 'TTO',
'Class:SLT/Attribute:metric/Value:tto+' => 'Time-To-Own - время до назначения агента (принятия в работу)',
'Class:SLT/Attribute:metric/Value:ttr' => 'TTR',
'Class:SLT/Attribute:metric/Value:ttr+' => 'TTR',
'Class:SLT/Attribute:metric/Value:ttr+' => 'Time-To-Resolve - время до решения',
'Class:SLT/Attribute:value' => 'Значение',
'Class:SLT/Attribute:value+' => '',
'Class:SLT/Attribute:unit' => 'Единицы',
'Class:SLT/Attribute:unit+' => '',
'Class:SLT/Attribute:unit/Value:hours' => 'Часы',
'Class:SLT/Attribute:unit/Value:hours+' => 'часов',
'Class:SLT/Attribute:unit/Value:hours+' => 'Часы',
'Class:SLT/Attribute:unit/Value:minutes' => 'Минуты',
'Class:SLT/Attribute:unit/Value:minutes+' => 'минут',
'Class:SLT/Attribute:unit/Value:minutes+' => 'Минуты',
));
//
@@ -410,11 +393,11 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkSLAToSLT+' => '',
'Class:lnkSLAToSLT/Attribute:sla_id' => 'SLA',
'Class:lnkSLAToSLT/Attribute:sla_id+' => '',
'Class:lnkSLAToSLT/Attribute:sla_name' => 'Название SLA',
'Class:lnkSLAToSLT/Attribute:sla_name' => 'SLA',
'Class:lnkSLAToSLT/Attribute:sla_name+' => '',
'Class:lnkSLAToSLT/Attribute:slt_id' => 'SLT',
'Class:lnkSLAToSLT/Attribute:slt_id+' => '',
'Class:lnkSLAToSLT/Attribute:slt_name' => 'Название SLT',
'Class:lnkSLAToSLT/Attribute:slt_name' => 'SLT',
'Class:lnkSLAToSLT/Attribute:slt_name+' => '',
));
@@ -427,15 +410,15 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkCustomerContractToService+' => '',
'Class:lnkCustomerContractToService/Attribute:customercontract_id' => 'Договор с заказчиком',
'Class:lnkCustomerContractToService/Attribute:customercontract_id+' => '',
'Class:lnkCustomerContractToService/Attribute:customercontract_name' => 'Контактное лицо клиента',
'Class:lnkCustomerContractToService/Attribute:customercontract_name' => 'Договор с заказчиком',
'Class:lnkCustomerContractToService/Attribute:customercontract_name+' => '',
'Class:lnkCustomerContractToService/Attribute:service_id' => 'Услуга',
'Class:lnkCustomerContractToService/Attribute:service_id+' => '',
'Class:lnkCustomerContractToService/Attribute:service_name' => 'Имя услуги',
'Class:lnkCustomerContractToService/Attribute:service_name' => 'Услуга',
'Class:lnkCustomerContractToService/Attribute:service_name+' => '',
'Class:lnkCustomerContractToService/Attribute:sla_id' => 'SLA',
'Class:lnkCustomerContractToService/Attribute:sla_id+' => '',
'Class:lnkCustomerContractToService/Attribute:sla_name' => 'Название SLA',
'Class:lnkCustomerContractToService/Attribute:sla_name' => 'SLA',
'Class:lnkCustomerContractToService/Attribute:sla_name+' => '',
));
@@ -448,11 +431,11 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkProviderContractToService+' => '',
'Class:lnkProviderContractToService/Attribute:service_id' => 'Услуга',
'Class:lnkProviderContractToService/Attribute:service_id+' => '',
'Class:lnkProviderContractToService/Attribute:service_name' => 'Имя услуги',
'Class:lnkProviderContractToService/Attribute:service_name' => 'Услуга',
'Class:lnkProviderContractToService/Attribute:service_name+' => '',
'Class:lnkProviderContractToService/Attribute:providercontract_id' => 'Договор с поставщиком',
'Class:lnkProviderContractToService/Attribute:providercontract_id+' => '',
'Class:lnkProviderContractToService/Attribute:providercontract_name' => 'Имя договора поставщика',
'Class:lnkProviderContractToService/Attribute:providercontract_name' => 'Договор с поставщиком',
'Class:lnkProviderContractToService/Attribute:providercontract_name+' => '',
));
@@ -465,11 +448,11 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkFunctionalCIToService+' => '',
'Class:lnkFunctionalCIToService/Attribute:service_id' => 'Услуга',
'Class:lnkFunctionalCIToService/Attribute:service_id+' => '',
'Class:lnkFunctionalCIToService/Attribute:service_name' => 'Имя услуги',
'Class:lnkFunctionalCIToService/Attribute:service_name' => 'Услуга',
'Class:lnkFunctionalCIToService/Attribute:service_name+' => '',
'Class:lnkFunctionalCIToService/Attribute:functionalci_id' => 'КЕ',
'Class:lnkFunctionalCIToService/Attribute:functionalci_id+' => '',
'Class:lnkFunctionalCIToService/Attribute:functionalci_name' => 'Имя КЕ',
'Class:lnkFunctionalCIToService/Attribute:functionalci_name' => 'КЕ',
'Class:lnkFunctionalCIToService/Attribute:functionalci_name+' => '',
));
@@ -484,14 +467,14 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:DeliveryModel/Attribute:name+' => '',
'Class:DeliveryModel/Attribute:org_id' => 'Организация',
'Class:DeliveryModel/Attribute:org_id+' => '',
'Class:DeliveryModel/Attribute:organization_name' => 'Название организации',
'Class:DeliveryModel/Attribute:organization_name+' => 'Common name',
'Class:DeliveryModel/Attribute:organization_name' => 'Организация',
'Class:DeliveryModel/Attribute:organization_name+' => '',
'Class:DeliveryModel/Attribute:description' => 'Описание',
'Class:DeliveryModel/Attribute:description+' => '',
'Class:DeliveryModel/Attribute:contacts_list' => 'Контакты',
'Class:DeliveryModel/Attribute:contacts_list+' => 'Связанные контакты',
'Class:DeliveryModel/Attribute:contacts_list+' => 'Контакты (команды и персоны), которые участвуют в предоставлении услуг по этой модели',
'Class:DeliveryModel/Attribute:customers_list' => 'Заказчики',
'Class:DeliveryModel/Attribute:customers_list+' => 'Связанные заказчик',
'Class:DeliveryModel/Attribute:customers_list+' => 'Заказчики, которым предоставляются услуги по этой модели',
));
//
@@ -503,17 +486,14 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:lnkDeliveryModelToContact+' => '',
'Class:lnkDeliveryModelToContact/Attribute:deliverymodel_id' => 'Модель предоставления услуг',
'Class:lnkDeliveryModelToContact/Attribute:deliverymodel_id+' => '',
'Class:lnkDeliveryModelToContact/Attribute:deliverymodel_name' => 'Название модели предоставления услуг',
'Class:lnkDeliveryModelToContact/Attribute:deliverymodel_name' => 'Модель предоставления услуг',
'Class:lnkDeliveryModelToContact/Attribute:deliverymodel_name+' => '',
'Class:lnkDeliveryModelToContact/Attribute:contact_id' => 'Контакт',
'Class:lnkDeliveryModelToContact/Attribute:contact_id+' => '',
'Class:lnkDeliveryModelToContact/Attribute:contact_name' => 'Контактное лицо',
'Class:lnkDeliveryModelToContact/Attribute:contact_name' => 'Контакт',
'Class:lnkDeliveryModelToContact/Attribute:contact_name+' => '',
'Class:lnkDeliveryModelToContact/Attribute:role_id' => 'Роль',
'Class:lnkDeliveryModelToContact/Attribute:role_id+' => '',
'Class:lnkDeliveryModelToContact/Attribute:role_name' => 'Должность',
'Class:lnkDeliveryModelToContact/Attribute:role_name' => 'Роль',
'Class:lnkDeliveryModelToContact/Attribute:role_name+' => '',
));
?>
));

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