Compare commits

...

2479 Commits
1.0 ... 2.2.1

Author SHA1 Message Date
Romain Quetiez
f3cabe4cf1 Releasing iTop 2.2.1
SVN:2.2.1[3900]
2016-02-08 11:16:37 +00:00
Romain Quetiez
1ca3d46889 Preparing release 2.2.1
SVN:2.2.0[3899]
2016-02-03 16:12:30 +00:00
Romain Quetiez
cc74af4343 #1196 Only administrators can add attachments by the mean of the REST/JSON API -retrofit from trunk
SVN:2.2.0[3897]
2016-02-03 13:05:15 +00:00
Denis Flaven
8041f1a8f3 #1193: When creating new object from tab with <edit_mode>add_only</edit_mode> id of the parent object was not transfered to the form. Fix provided by Vladimir Kunin. Thank you Vladimir.
SVN:2.2.0[3895]
2016-02-02 13:34:27 +00:00
Denis Flaven
c32290e1fc internal: new autoOpen flag.
SVN:2.2.0[3888]
2016-01-27 13:22:05 +00:00
Denis Flaven
6449891f07 #1174: support HTML fields in the bulk modify forms (capability to enable/disable the field live)
SVN:2.2.0[3884]
2016-01-26 14:34:42 +00:00
Denis Flaven
55b166c076 #1183: more refactoring and some robustness enhancements after tests on big datasets.
SVN:2.2.0[3882]
2016-01-26 13:25:07 +00:00
Denis Flaven
c5793ab3b2 #1153: preserve leading zeroes (in "numeric" fields) in the Excel export.
SVN:2.2.0[3880]
2016-01-26 09:53:50 +00:00
Denis Flaven
2b315801f1 #1183: grouping threshold is now taken int account for "Depends on..." graphs (i.e. grouping backwards)
SVN:2.2.0[3876]
2016-01-25 14:34:23 +00:00
Denis Flaven
d5ae7722b7 #1176: empty placeholders are represented by an empty string as in previous version.
SVN:2.2.0[3874]
2016-01-25 13:00:41 +00:00
Denis Flaven
f37b9a47d1 IconSelectorField (Design time !) can be read-only.
SVN:2.2.0[3872]
2016-01-21 16:05:56 +00:00
Romain Quetiez
49c5fda280 #1165 backup with errors fills up tmp-directories with lots of backup-files
SVN:2.2.0[3869]
2016-01-21 15:07:58 +00:00
Denis Flaven
4097a7c74d #1150: Spurious message "A restore is running..." - FIXED !
SVN:2.2.0[3865]
2016-01-20 15:59:39 +00:00
Denis Flaven
4808ef74f3 (retrofit) Properly read radio button values inside a form.
SVN:2.2.0[3851]
2015-12-14 13:14:21 +00:00
Denis Flaven
beaaa163bb (retrofit) (internal) Remove _altered_in when exporting the delta.
SVN:2.2.0[3850]
2015-12-14 13:11:58 +00:00
Denis Flaven
81b7fc25b3 (retrofit) Keep track of which module altered which node in the XML.
SVN:2.2.0[3846]
2015-12-09 14:58:59 +00:00
Denis Flaven
f7772570cd (retrofit from trunk) Fixed the computation of the lowest common ancestor.
SVN:2.2.0[3841]
2015-12-02 10:40:29 +00:00
Denis Flaven
ef9deb89c3 (retrofit from trunk) Internal: dehardcoded OqlUnionQuery::GetClass against the metamodel reflection API
SVN:2.2.0[3840]
2015-12-02 10:39:35 +00:00
Denis Flaven
5cdf1765a4 (retrofit from trunk) Added AttributeDef::EnumTemplateVerbs, to generate the documentation about the available attribute formatting placeholders
SVN:2.2.0[3838]
2015-12-02 10:38:18 +00:00
Denis Flaven
00b3b9e1cc (retrofit from trunk) Internal - MFFactory: fixed GetDelta when there is no change at all
SVN:2.2.0[3834]
2015-11-30 14:59:24 +00:00
Denis Flaven
ff3eafc1d2 Added structured error reporting in case of missing dependencies for the modules to install.
SVN:2.2.0[3832]
2015-11-25 16:57:59 +00:00
Denis Flaven
c3b3ee85a2 Properly create DOMNodes with a text content (beware of XML entities inside the text)
SVN:2.2.0[3830]
2015-11-25 16:54:22 +00:00
Denis Flaven
424f08d650 Support validation patterns containing a forward slash
SVN:2.2.0[3828]
2015-11-25 16:50:57 +00:00
Denis Flaven
97809190de Support of derived classes in "add_remove" edition mode for AttributeLinkSet fields (the search form was not refreshing / loading properly when toggling the class to search for).
SVN:2.2.0[3823]
2015-11-20 14:21:42 +00:00
Denis Flaven
e33930b10c Make ReloadSearchForm work properly when the "submit" event handler is declared either with or without a "namespace" portion (e.g. 'submit.itop' vs 'submit')
SVN:2.2.0[3817]
2015-11-09 10:48:01 +00:00
Denis Flaven
41ca454c5d #1164: typo in German localization.
SVN:2.2.0[3812]
2015-10-26 10:59:35 +00:00
Denis Flaven
d17abfafe3 Support the download of "bigger-than-memory" backup files.
SVN:2.2.0[3810]
2015-10-26 10:41:00 +00:00
Romain Quetiez
8025dcbc37 Fixed regressions due to the recent code refactoring [3803]
SVN:2.2.0[3808]
2015-10-12 15:40:44 +00:00
Denis Flaven
cbe8745f3a Do NOT localize finalclass values in REST/JSON.
SVN:2.2.0[3806]
2015-10-12 12:40:58 +00:00
Denis Flaven
87206225d2 Code refactoring to make the OQL parsing self contained in the "oql" subdirectory.
SVN:2.2.0[3804]
2015-10-12 10:05:38 +00:00
Denis Flaven
96b10baeee Added a version number (arbitrary initialized to 2015-08-31 for iTop v2.2.0) to the OQL Lexer/parser.
SVN:2.2.0[3802]
2015-10-12 09:55:52 +00:00
Romain Quetiez
a98e1c838e #1159 Cannot add edge (impact analysis not working depending on the data model customizations)
SVN:2.2.0[3800]
2015-10-09 15:33:17 +00:00
Denis Flaven
e8279ca731 Do not rely on MetaModel::GetRootClass() to check the data model, use the abstraction of ModelReflection instead to keep the code portable.
SVN:2.2.0[3798]
2015-10-08 15:57:21 +00:00
Denis Flaven
2976e919f8 (retrofit from trunk) #1156: properly escape file paths containing spaces
SVN:2.2.0[3796]
2015-10-05 19:54:16 +00:00
Denis Flaven
9c7671e894 Better error reporting when the setup fails to create a directory.
SVN:2.2.0[3794]
2015-09-30 14:09:57 +00:00
Romain Quetiez
6c1e658fe4 Releasing 2.2.0
SVN:2.2.0[3791]
2015-09-23 08:40:56 +00:00
Denis Flaven
7105b7a5fa Make sure that the images are reloaded when the application is upgraded.
SVN:trunk[3789]
2015-09-22 16:14:37 +00:00
Denis Flaven
1992adfac2 Make sure that the images are reloaded when the application is upgraded.
SVN:trunk[3788]
2015-09-22 15:55:22 +00:00
Denis Flaven
c2e8eca577 Datamodel version number bumped to 2.2.0
SVN:trunk[3787]
2015-09-22 15:30:01 +00:00
Denis Flaven
0cc466dd7e Make sure that the images are reloaded when the application is upgraded.
SVN:trunk[3786]
2015-09-22 15:16:12 +00:00
Denis Flaven
3eec1d358c Make sure that the images are reloaded when the application is upgraded.
SVN:trunk[3785]
2015-09-22 15:14:04 +00:00
Denis Flaven
1cc38fb58e Make sure that the stylesheets and favicons are reloaded when the application is upgraded.
SVN:trunk[3784]
2015-09-22 14:27:53 +00:00
Denis Flaven
91479bba53 New favicon for the new logo!
SVN:trunk[3783]
2015-09-22 14:26:50 +00:00
Denis Flaven
1d4a3e780d Remove the left padding and the orange arrow when printing hyperlinks.
SVN:trunk[3782]
2015-09-22 14:26:12 +00:00
Romain Quetiez
af9a419e84 Release 2.2.0. Aligning the versions of the modules that have changed since the last (beta) release.
SVN:trunk[3781]
2015-09-22 13:55:37 +00:00
Romain Quetiez
b311e924cd Releasing 2.2.0 RC
SVN:trunk[3780]
2015-09-22 13:08:13 +00:00
Denis Flaven
5c9b221b4c Properly cut long case log entries before encoding them in HTML.
SVN:trunk[3779]
2015-09-22 12:52:28 +00:00
Romain Quetiez
e94282459e Historisation of attachments: (internal) record the attachment as an external key with an automatic reset (when the attachment gets deleted)
SVN:trunk[3778]
2015-09-22 12:16:25 +00:00
Romain Quetiez
77a0c0a7c6 Historisation of attachments: added/removed attachments must be tracked within the same change as for other attributes.
SVN:trunk[3777]
2015-09-22 11:55:44 +00:00
Denis Flaven
c8fa3870db Integration of the Czech translation provided by Lukáš Dvořák. Thanks a lot Lukáš !
SVN:trunk[3776]
2015-09-18 15:05:23 +00:00
Denis Flaven
4261923126 Updated readme for the 2.2.0 version...
SVN:trunk[3775]
2015-09-18 09:23:10 +00:00
Denis Flaven
554a462809 Preserve the initial sort order on lists by determining the default sort order of the tables based on the equivalence between the "friendlyname" and another actual field of the class.
SVN:trunk[3774]
2015-09-17 17:22:07 +00:00
Denis Flaven
1206cc42bc #1151 Error (with no explanation) when deleting some 1-N links
SVN:trunk[3773]
2015-09-17 17:00:56 +00:00
Denis Flaven
48ab835646 Concurrent lock still has some minor issues, don't enable it by default.
SVN:trunk[3772]
2015-09-17 16:39:05 +00:00
Denis Flaven
bcd9141db6 #384: (continued) ActionEmail should bre read-able by everyone...
SVN:trunk[3771]
2015-09-17 15:33:41 +00:00
Denis Flaven
e1fd65fe47 Typo...
SVN:trunk[3770]
2015-09-17 14:06:16 +00:00
Erwan Taloc
b2fe3cb033 French translation for the attribute servicefamily_id in the class Service (module Service for Provider)
SVN:trunk[3769]
2015-09-17 14:01:15 +00:00
Erwan Taloc
df9cb7f0d4 French translation for the attribut parent_problem_id in the clas Incident
SVN:trunk[3768]
2015-09-17 14:00:03 +00:00
Denis Flaven
07fdeb9284 Printer-friendly version: hide the "eye" icon inside "legends" when printing.
SVN:trunk[3767]
2015-09-17 07:47:42 +00:00
Denis Flaven
6d04633daf Protects the setup against non-existing classes... to be renamed! Useful for heavily customized models where some very basic classes have been deleted.
SVN:trunk[3766]
2015-09-16 17:05:44 +00:00
Denis Flaven
2d95c131fc #384: Triggers should not be in the "bizmodel" category. User rights do not apply to such objects...
SVN:trunk[3765]
2015-09-16 15:45:10 +00:00
Denis Flaven
853c96478b #1106, #1122: Added a new option 'start_tls' (false by default) and improved debugging capabilities for troubleshooting when something goes wrong with LDAP. Thanks to Karl (karkoff1212) for the hint.
SVN:trunk[3764]
2015-09-16 15:31:22 +00:00
Denis Flaven
86a7d133f3 Make the 'curl' options overridable when calling utils::DoPostRequest()
SVN:trunk[3763]
2015-09-16 14:38:31 +00:00
Denis Flaven
1a6559efde Computation of the impacted items in two passes to properly handle the "context" queries.
SVN:trunk[3762]
2015-09-16 14:03:00 +00:00
Denis Flaven
c7cf8a9f74 Nicer icons...
SVN:trunk[3761]
2015-09-16 13:57:53 +00:00
Denis Flaven
8593f00917 Enhancement: better display of the "Attachments" (addition/removal) in the history.
SVN:trunk[3760]
2015-09-14 14:48:13 +00:00
Denis Flaven
6fd2c81315 History display enhancement: whenever a new case log entry is added, display its content in the history. The display is truncated at a configurable max length. The user can expand/collapse the truncated text, entry per entry. The text is not truncated when printing.
SVN:trunk[3759]
2015-09-14 13:46:48 +00:00
Denis Flaven
3cbb0e974e Completed unit tests to cover 1-N links and to emulate the behavior of the user interface for N-N links.
SVN:trunk[3758]
2015-09-14 12:21:34 +00:00
Denis Flaven
02aa8339f8 Cosmetics on menus, details and the top bar...
SVN:trunk[3757]
2015-09-12 18:46:39 +00:00
Denis Flaven
7f64982fc0 Cosmetics: the refresh button is now displayed as part of the "actions" at the top-right of the "details".
SVN:trunk[3756]
2015-09-12 14:38:06 +00:00
Denis Flaven
d2e78d0292 Unit tests fixes...
SVN:trunk[3755]
2015-09-12 14:34:35 +00:00
Denis Flaven
11b768dace Update the position of the dialog's buttons after adjusting the disposition of the search form.
SVN:trunk[3754]
2015-09-12 14:33:32 +00:00
Denis Flaven
972c94bff7 #1148: Fixed dashboards upload: use the more modern fileupload component, since we now hook the ajax call in iTopWebPage and removed references to the old component ajax.fileupload from (almost) everywhere...
SVN:trunk[3753]
2015-09-12 12:06:33 +00:00
Denis Flaven
489820cfe7 #1049: CSV import (and edition) of n:n links. The Differences() function is NOT commutative: the original value (i.e. the one from the database) must the the first argument.
SVN:trunk[3752]
2015-09-12 09:29:32 +00:00
Denis Flaven
a3c4454090 Usability enhancement: don't clear the "Organizations" auto complete (for the silos) without purpose when clicking on it... empty the field only when the displayed value means "All organizations".
SVN:trunk[3751]
2015-09-12 09:18:27 +00:00
Denis Flaven
bc6acee1f1 Cosmetics on the "autocomplete": more compact by default (20 chars instead of 30), and buttons evenly spaced.
SVN:trunk[3750]
2015-09-12 09:14:39 +00:00
Romain Quetiez
49a189c920 Internal: allow to stop a stop watch at a specified time (case exchange) -requires testing
SVN:trunk[3749]
2015-09-11 15:21:35 +00:00
Romain Quetiez
a35488b540 Added unit tests for the recording of linksets from one end (->Set('xxxxx_list')
SVN:trunk[3748]
2015-09-11 15:00:57 +00:00
Romain Quetiez
6b7071726b #1147 Documented the limitations for links between connectable CIs and network devices
SVN:trunk[3747]
2015-09-11 13:31:19 +00:00
Romain Quetiez
7d0282e59d #1145 nad #1146 Documented the limitations for links between connectable CIs and network devices
SVN:trunk[3746]
2015-09-11 12:39:40 +00:00
Romain Quetiez
f26bcd812c Could not add more than one link between a given server and a given network device. This is a regression in 2.2.0 beta. This issue affect N-N links where duplicates are allowed. One single link is being affected in the standard datamodel.
SVN:trunk[3745]
2015-09-11 12:03:22 +00:00
Denis Flaven
33762796b8 #1087: the sort order on "group by" dashlets inside a dashboard is now saved as a user preference.
SVN:trunk[3744]
2015-09-10 07:33:33 +00:00
Denis Flaven
38b6582080 Finishing touch to the "Printer friendly version" of the details page.
SVN:trunk[3743]
2015-09-09 14:48:14 +00:00
Romain Quetiez
cd3122d597 #1144 Audit category having no rule -> PHP notices when showing the report + improved the behavior when the OQL of a rule is wrong.
SVN:trunk[3742]
2015-09-09 13:38:52 +00:00
Romain Quetiez
b28a4c029c #1143 Records any change (add/remove/modify) for link sets that can be considered as one of the characteristics of a class (currently those having edit mode = in place)
SVN:trunk[3741]
2015-09-09 13:19:00 +00:00
Denis Flaven
85899e6ac0 Cosmetics: pixel perfect alignment of the "actions" buttons.
SVN:trunk[3740]
2015-09-09 10:33:08 +00:00
Denis Flaven
e21656c550 #1142 Dashboard editor: protects from unwanted "exit" without saving the modifications:
- mark the dashboard as modified when a dashlet was added / moved / deleted
- prevent clicking on the hyperlinks inside the preview of the dashboard

Unrelated modification of the stylesheet to make "actions" buttons look nicer (no gap in the background color) when the displayed at a zoom level different from 100% (e.g. 90% or 75 %)

SVN:trunk[3739]
2015-09-09 09:51:02 +00:00
Denis Flaven
8fec8b7f80 Usability enhancement: Autocomplete: do NOT clear the typed text when the value does not match one of the possible values, but clear the actual underlying value so that the input field gets marked as "invalid" if it is mandatory.
SVN:trunk[3737]
2015-09-09 09:38:17 +00:00
Romain Quetiez
8b45928d11 Instrumented the code to help in solving the "restore runing" issue. Completion of commits [3733] znd [3595] = issue an exception if something is going wrong within iTopMutex::TryLock (exceptions are correctly handled in the various places where TryLock is invoked)
SVN:trunk[3736]
2015-09-09 09:23:54 +00:00
Denis Flaven
98150db0b4 Protects the onwership lock from a legitimate loss of the lock. No popup when leaving for real.
SVN:trunk[3735]
2015-09-08 15:58:20 +00:00
Romain Quetiez
96a4b83e31 Internal: buggy Exception handlers for some query APIs in CMDBSource
SVN:trunk[3734]
2015-09-08 15:46:09 +00:00
Romain Quetiez
84c31da226 Instrumented the code to help in solving the "restore runing" issue. We've added traces into the error.log file:
- Log restore begin/end
 - Log if detecting that a restore is running (and displaying the banner)
 - Log any Exception occuring during the detection (instead of just ignoring it)

SVN:trunk[3733]
2015-09-08 15:42:47 +00:00
Denis Flaven
cad5e703f8 Cosmetics:
- Better use of the space in the search form: multi-select drop down list are now small when closed and larger when opened
- Nicer feedback when hiding/showing sections in the "printable version" of a details page.

SVN:trunk[3732]
2015-09-08 14:06:00 +00:00
Romain Quetiez
62959a89bc #1091 CAS memberships broken (parameter "cas_memberof" NOT given as a regular expression, bugged since iTop 2.0 or earlier)
SVN:trunk[3731]
2015-09-08 12:39:02 +00:00
Denis Flaven
c29f2eccaf Dictionary: explanation of the "Impacted CIs" tab.
SVN:trunk[3730]
2015-09-07 15:18:15 +00:00
Denis Flaven
664cfbf014 Better protection of the impact analysis against invalid configuration of the "Context".
SVN:trunk[3729]
2015-09-07 15:16:30 +00:00
Romain Quetiez
e1acce6e6e #1134 Query returning a "null row": just make sure that the row gets displayed (still surprising... see ticket #1138 to follow up on the suppression of those ghost rows)
SVN:trunk[3728]
2015-09-07 14:42:30 +00:00
Romain Quetiez
5fa83c84d3 Exports: attribute "final class" is either translated or not, depending on the no_localize option (all formats concerned with this option: CSV and spreadsheet -That field is not present in the XML format output)
SVN:trunk[3727]
2015-09-07 14:30:07 +00:00
Romain Quetiez
1bb2d168fa Spreadsheet export: fix = do take the no_localize option into account
SVN:trunk[3726]
2015-09-07 14:27:55 +00:00
Denis Flaven
52ad33f5b2 Forced the PDF produced by the impact analysis to be downloaded as an attachment, otherwise on some browsers the result cannot be saved.
SVN:trunk[3725]
2015-09-07 14:18:08 +00:00
Denis Flaven
d9adcf01cd Export bug fix: French default value for the downloaded file should not contain a comma...
SVN:trunk[3724]
2015-09-07 13:33:06 +00:00
Denis Flaven
81d19c8804 Export bug fixes:
- Properly handle on utf-8 CSV exports
- Allow non administrators to run the export in interactive mode (since it is used by the "Export..." actions)

SVN:trunk[3723]
2015-09-07 13:27:27 +00:00
Denis Flaven
5cbcebb79e Assign a meaningful name (and mime type) to the files produced by the (non-interactive) web export.
SVN:trunk[3722]
2015-09-07 10:38:03 +00:00
Romain Quetiez
40990020b1 #1140 UNION queries not working -in fact, loss of the optimization on column load when filtering on org hierarchies (retrofit possible but the fix will be located in MetaModel)
SVN:trunk[3721]
2015-09-07 10:30:58 +00:00
Romain Quetiez
5153139581 #564 Prompt for an update in a case log on a lifecycle transition. Can be retrofitted easily. Associated with commit [r3687]
SVN:trunk[3720]
2015-09-04 13:33:04 +00:00
Denis Flaven
c852cd8e09 Regression due to the fix of #1107 (revision 3647): the settings query_cache_enabled was always written as "false" in the default configuration. This is now fixed.
Warning: if you upgrade your iTop installation from the 2.2 beta, you MUST change this value back to true in the configuration file to avoid a severe slow down of the application.

SVN:trunk[3719]
2015-09-04 13:31:25 +00:00
Romain Quetiez
680104109b Typo: a pint of what? (pRint preview)
SVN:trunk[3718]
2015-09-04 13:13:33 +00:00
Romain Quetiez
5f0938d01b Fixed regression introduced in 2.2.0 beta. Warning issued when opening an organization for modification
SVN:trunk[3717]
2015-09-04 12:44:14 +00:00
Denis Flaven
1e533b24d1 Fix: Make sure that the "ownership lock" is always released when clicking on the "Cancel" button of a form.
SVN:trunk[3716]
2015-09-04 09:52:22 +00:00
Denis Flaven
7fa99cedee Impact analysis cosmetics:
- remove empty groups, since it may happen
- properly scale the borders of groups and redundancy groups
- automatically rescale the graph when showing/hiding the "Filter" tab

SVN:trunk[3715]
2015-09-04 09:22:36 +00:00
Romain Quetiez
09cbf63c5a CSV import GUI: now matching more representations (object id and external fields that are external keys)
SVN:trunk[3714]
2015-09-04 08:40:32 +00:00
Denis Flaven
be3bce26ed Impact analysis enhancement:
- Some of the "context" rules are marked as "default=yes"
- Only the "default" context rules are used for the initial display of the impact analysis graph AND are used to compute the impacted items of a ticket.

SVN:trunk[3713]
2015-09-03 16:56:44 +00:00
Romain Quetiez
5425f55af7 Exports further improved:
- Support reconciliation keys for every external key
- Better support for Case logs and multiline text fields (both in the preview and in the results)
- Do not repeat identical columns in the list of proposed columns. Examples with UserRequest: friendlyname is equivalent to ref, UserRequest::caller_name is equivalent to UserRequest::caller_id->name
- Optimized the preview for huge data sets (OptimizeColumnLoad)
- Cosmetics on the preview
- Labels for ids aligned with the labels used by the CSV import feature
- Fixed Stop Watch output for PDF/HTML/spreadsheet formats

SVN:trunk[3712]
2015-09-03 16:16:17 +00:00
Denis Flaven
b6341741c3 Refresh of the "Groups" tab in the impact analysis display, when the whole graph is refreshed.
SVN:trunk[3711]
2015-09-03 09:11:09 +00:00
Denis Flaven
a4f1a8f5ff Impact analysis improvements:
- Better layout and grouping of the graph
- Made the tooltip for groups helpful

SVN:trunk[3710]
2015-09-02 16:43:32 +00:00
Romain Quetiez
aa2ab1118a Exports: a fields spec can now be an extended attribute code (e.g. location_id->org_id->parent_id->code)
SVN:trunk[3709]
2015-09-01 15:00:55 +00:00
Romain Quetiez
71048be499 Optimization (regression introduced in 2.1.0): huge tables and HTML exports of tickets can take long to execute (require 1 query per fetched row)
SVN:trunk[3708]
2015-09-01 14:38:33 +00:00
Denis Flaven
cba724d676 Prevent timeouts during the computation of the impact analysis + keep the columns (and ordering) in the lists of objects when creating a PDF of the impact analysis.
SVN:trunk[3707]
2015-09-01 14:36:47 +00:00
Denis Flaven
6f1d186287 #1137: portal configuration was too limited. Now one "allow" profile is enough to allow access to a given portal.
SVN:trunk[3706]
2015-09-01 12:55:46 +00:00
Romain Quetiez
4674658cfa User portal as a module: the Cancel button (ticket creation wizard) was not working
SVN:trunk[3705]
2015-09-01 09:46:08 +00:00
Romain Quetiez
4cfcb60e59 REST/JSON services. Take the user rights into account. Something was already done for core/create and core/delete, but the symptoms were not clear. The other verbs (update, apply_stimulus, get and get_related) had no protection at all.
SVN:trunk[3704]
2015-08-28 10:52:59 +00:00
Romain Quetiez
a230861afa Export (all formats but XML):
- code refactoring
- suppressed the '*' for mandatory ext keys (buggy anyway)
- fixed issue with redundant columns (resulting in a badly formatted export)
- check that the current user has the rights to "bulk read" the selected objects (that depend on the selected fields)

SVN:trunk[3703]
2015-08-28 09:06:46 +00:00
Romain Quetiez
e2207dc74c Export (legacy): bulk read must be authorized for all the queried classes
SVN:trunk[3702]
2015-08-27 13:20:42 +00:00
Romain Quetiez
16b68ee154 Export: prevent from usage by a non admin (at the page level)
SVN:trunk[3701]
2015-08-27 13:18:49 +00:00
Romain Quetiez
1331f91061 #1123/#1133 The optimization on loaded columns in SQL queries was inoperant for some queries, resulting in a stopper issue if such queries were added to a union query (2.2.0 beta)
SVN:trunk[3700]
2015-08-27 07:32:41 +00:00
Romain Quetiez
138423aeec Customizations/XML: clearer error reporting when encountering a duplicate value for an AttributeEnum
SVN:trunk[3699]
2015-08-26 15:25:55 +00:00
Romain Quetiez
20815f1a91 Exports: continuation of commit 3681 (Make the correct column name for friendly names (ext key -> ext field) enlarged to ALL formats with the exception of XML
SVN:trunk[3698]
2015-08-26 13:08:02 +00:00
Romain Quetiez
35c57bb10c Export/XML: documented options (no_localize / linksets) + added external fields and friendly name for the external keys, both on the exported objects and the links (linkets=1)
SVN:trunk[3697]
2015-08-26 09:41:32 +00:00
Romain Quetiez
5fd653dae5 Export/XML: new option to include link sets (default: no)
SVN:trunk[3696]
2015-08-24 14:44:28 +00:00
Romain Quetiez
5a9b8a7bb0 Query phrases: if the attribute 'fields' is left empty, then propose the legacy export URL and keep the user informed about the limitations
SVN:trunk[3695]
2015-08-24 10:10:04 +00:00
Romain Quetiez
5277a9eb38 Exports: support multi-column queries (e.g. SELECT l, p FROM Person AS p JOIN Location AS l ON p.location_id = l.id) with null values
SVN:trunk[3694]
2015-08-24 08:01:24 +00:00
Romain Quetiez
b1887ae431 #1111 Could not attach a UserRequest to a Problem (1-N links). Could not detach either! This fix requires attention: it is assumed that an item of a link set, if it is "modified" then its key to the current object has already been set.
SVN:trunk[3693]
2015-08-21 10:27:54 +00:00
Romain Quetiez
a3aed6aafc Printable view: cosmetics on object names (hyperlinks) in the actual print view -reverse merging a file that was NOT ready for committing
SVN:trunk[3692]
2015-08-20 14:15:02 +00:00
Romain Quetiez
6903a36298 Printable view: cosmetics on object names (hyperlinks) in the actual print view
SVN:trunk[3691]
2015-08-20 13:11:42 +00:00
Romain Quetiez
ea4c654af8 Printable view: do not show pagination controls (show the full list), must work with plugins calling cmdbAbstractObject::DiplaySet AND cmdbAbstractObjectObject::GetDisplaySet. Sill, GetDisplayExtendedSet should be hacked as well (?)
SVN:trunk[3690]
2015-08-20 08:07:42 +00:00
Romain Quetiez
ec61417e39 #1081 Customizations: adjust the dimensions of the HTML Editor (CKEditor). Also fixed an issue when specifying width/height with unit (e.g. "30em") for AttributeText/AttributeLongText
SVN:trunk[3689]
2015-08-19 17:10:28 +00:00
Romain Quetiez
3ba2c3d657 Log REST/JSON calls (config: 'log_rest_service' => true ; stored as EventRestService)
SVN:trunk[3688]
2015-08-19 14:35:08 +00:00
Romain Quetiez
8b5faf6b66 #564 Prompt for an update in a case log on a lifecycle transition. Can be retrofitted easily.
SVN:trunk[3687]
2015-08-19 12:42:49 +00:00
Romain Quetiez
76149633a1 #1074 Portal: errors when selecting Impact/Urgency, and if the user has access to his organization only.
SVN:trunk[3686]
2015-08-19 09:59:15 +00:00
Romain Quetiez
d8113a3304 #1130 CAS authentication security leak when cas_memberof is left empty (already committed into branch 2.1.0)
SVN:trunk[3685]
2015-08-18 13:48:12 +00:00
Romain Quetiez
3fc19bf160 Completion of [3668]: #1116 (and #1117): default values for ENUMs must always be expressed as strings.
SVN:trunk[3683]
2015-08-17 16:14:24 +00:00
Romain Quetiez
a30cb0b4c4 Export: for tabular exports, the label for the "friendly name" column must match the one recognized by CSV import (the very standard one, almost unused yet)
SVN:trunk[3682]
2015-08-17 15:28:19 +00:00
Romain Quetiez
d29e9b525d Export/CSV:
- Select the Character encoding (argument for CLI mode: charset)
- Make the correct column name for friendly names (ext key -> ext field)

SVN:trunk[3681]
2015-08-17 15:11:46 +00:00
Romain Quetiez
7c8a348ead Exports: Friendly names to be escaped and delimited in CSV (bug more exposed now that the export allows field selection)
SVN:trunk[3680]
2015-08-17 14:54:04 +00:00
Romain Quetiez
712931b728 #576 Printable view for object details. Possibility to hide/show chapters (the equivalent of tabs in the UI) or any fieldset. Requires testing and comments.
SVN:trunk[3679]
2015-08-17 14:12:36 +00:00
Denis Flaven
2f0b122101 Automatic installation of modules: remove duplicates if needed.
SVN:trunk[3678]
2015-08-14 12:42:51 +00:00
Denis Flaven
628b7644b7 Pan and zoom in the impact analysis view.
SVN:trunk[3677]
2015-08-14 12:38:50 +00:00
Denis Flaven
88717ac9ab Integration of the German translation provided by ITOMIG thanks to David Gümbel !
SVN:trunk[3676]
2015-08-14 12:18:19 +00:00
Romain Quetiez
e5e90b1faf Secure the server: prevent the users from browsing/getting files from the data and log directories. With Apache, it is still a must to enable htaccess with the spec "AllowOverride All". The index.php files are here to prevent from browsing whatever the HTTP server config.
SVN:trunk[3675]
2015-08-12 14:25:32 +00:00
Romain Quetiez
1a970d1372 #1120 Export V2 not working when using aliases (ex: SELECT Person AS p)
SVN:trunk[3674]
2015-08-12 10:02:37 +00:00
Romain Quetiez
b87b33c955 #1095 Object creation form and bulk modify (final step) not working when using apache-proxy
SVN:trunk[3673]
2015-08-12 09:21:19 +00:00
Denis Flaven
cfe9675709 #1118: fixed strange display of synchro data sources status.
SVN:trunk[3672]
2015-08-08 13:52:57 +00:00
Denis Flaven
b5f75271b9 #1121: Regression: "filters" on Triggers had no effect. The regression was caused by the new way of computing placeholders "on the fly" (#803).
SVN:trunk[3671]
2015-08-06 09:20:39 +00:00
Denis Flaven
90d5f5b8cf #1116 (and #1117): default values for ENUMs must always be expressed as strings.
SVN:trunk[3668]
2015-08-03 15:33:37 +00:00
Denis Flaven
f84f17a5be Fixed a potential XSS vulnerability.
SVN:trunk[3662]
2015-07-30 09:05:48 +00:00
Denis Flaven
ea8b254bd9 Readme updated due to the delayed release...
SVN:trunk[3661]
2015-07-28 14:16:52 +00:00
Denis Flaven
cb5f6e1ada Enhancement (internal) mark the "dict" entries as modified when loading them.
SVN:trunk[3660]
2015-07-28 12:55:51 +00:00
Denis Flaven
fa94dd257a File-based "transactions" dans log files better protected against concurrent access...
SVN:trunk[3659]
2015-07-28 12:51:46 +00:00
Denis Flaven
2a9ae8335d Use the new iTop logo (orange) in the portal as well.
SVN:trunk[3658]
2015-07-28 12:43:52 +00:00
Denis Flaven
567317386a - Fixed the "context" icons when displaying the impact analysis
- Bug fix: properly compute the list of impacted CIs on an Incident

SVN:trunk[3657]
2015-07-28 12:42:39 +00:00
Denis Flaven
24a54f146c Oups, one remaining typo in the German dictionary.
SVN:trunk[3656]
2015-07-28 12:36:47 +00:00
Denis Flaven
742abab420 Bug fix: typo causing the generation of invalid SQL queries (in some rare cases).
SVN:trunk[3653]
2015-07-28 12:25:19 +00:00
Denis Flaven
c1c3cd3dc9 #1099 and #1014: some German translations.
SVN:trunk[3652]
2015-07-28 12:14:08 +00:00
Denis Flaven
5e5739e37e Prepared the dictionaries for translating new entries (completed the french translation at the same time). Just look for the strings terminated by ~~ and translate them in place and you're done!
SVN:trunk[3651]
2015-07-28 11:30:31 +00:00
Denis Flaven
69c0bcd4ca Fixed typo in the french dictionary
SVN:trunk[3650]
2015-07-28 10:47:58 +00:00
Denis Flaven
d994bbffd0 Cosmetics on dict names (no spaces!)
SVN:trunk[3649]
2015-07-28 10:45:49 +00:00
Denis Flaven
8c5b020961 Enhancements to file based transactions (still experimental)
SVN:trunk[3648]
2015-07-28 10:40:53 +00:00
Denis Flaven
a426cf07e9 #1107: Make sure that all settings are preserved upon update.
SVN:trunk[3647]
2015-07-28 10:28:21 +00:00
Denis Flaven
d64641127a Fixed graphviz path check
SVN:trunk[3646]
2015-07-28 10:16:25 +00:00
Denis Flaven
cbc0e36057 Cosmetics on dict name
SVN:trunk[3645]
2015-07-28 10:13:45 +00:00
Denis Flaven
26405f8299 Make sure that the stylesheet can be loaded by the setup page at first run.
SVN:trunk[3644]
2015-07-15 15:15:55 +00:00
Denis Flaven
606e462b53 The path to Graphviz' dot program is now prompted interactively during the setup, since Graphviz is now mandatory for displaying the impact analysis.
SVN:trunk[3643]
2015-07-15 15:14:38 +00:00
Denis Flaven
d424addb4c Review of the readme for 2.2.0 beta.
SVN:trunk[3642]
2015-07-15 09:55:53 +00:00
Romain Quetiez
5427d6a466 Preparing the beta release: increment the module versions
SVN:trunk[3641]
2015-07-15 09:20:03 +00:00
Denis Flaven
b04298916c A little bit of polishing on the export feature to be ready for the beta.
SVN:trunk[3640]
2015-07-10 16:54:26 +00:00
Romain Quetiez
d3990ee2be Draft (the new features need to be further described)
SVN:trunk[3639]
2015-07-10 15:11:19 +00:00
Romain Quetiez
4e567585af #759 Ticket lists in CI: show only active tickets (exclude tickets in states rejected/resolved/closed) and display one list per leaf class so that the status column will be visible. It it not possible anymore to edit the ticket list from the CI.
SVN:trunk[3638]
2015-07-10 13:46:34 +00:00
Romain Quetiez
3bafb01202 Fixed bug with the new locking mecanism: lock not released when applying a stimulus
SVN:trunk[3637]
2015-07-10 13:33:01 +00:00
Romain Quetiez
dd5454591a Fixed "Strict standards" warning (introduced with the implementation of locks on edition)
SVN:trunk[3636]
2015-07-10 09:09:28 +00:00
Romain Quetiez
df9f25dc3c Optimization: improved the OQL cache:
- take benefit of the APC cache (if present)
- memory indexation could fail in case of long queries (query id based on a md5)
- added kpi measure on the OQL parsing

SVN:trunk[3635]
2015-07-09 14:37:29 +00:00
Romain Quetiez
a6b74d6538 Optimization: when displaying an object details, do not check data synchro for each and every attribute (the cache did exist but was inoperant)
SVN:trunk[3634]
2015-07-09 13:43:34 +00:00
Romain Quetiez
0b045e5dd0 Code cleanup: removed a code that was confusing while producing absolutely nothing
SVN:trunk[3633]
2015-07-09 07:49:04 +00:00
Romain Quetiez
7a139dddc0 Code cleanup: deprecated the unused (and empty) class CMDBSearchFilter, replaced by DBSearch or DBObjectSearch depending on the usage.
SVN:trunk[3632]
2015-07-09 07:36:39 +00:00
Romain Quetiez
baf54a7c02 #942 OQL now supporting unions. Unions support polymorphism and can be used anywhere in the application.
SVN:trunk[3631]
2015-07-08 17:10:40 +00:00
Denis Flaven
20e4dbfc1d A little bit of polishing on the impact analysis feature...
SVN:trunk[3630]
2015-07-08 15:56:34 +00:00
Denis Flaven
77388bed29 #714: localization of the date picker calendar. Get rid of the old jquery.datepicker.js file since iTop now relies on the built-in jQuery UI date picker widget.
SVN:trunk[3629]
2015-07-07 16:27:09 +00:00
Denis Flaven
cf5adc5ae7 #1062: bumped the version number of the REST/JSON API to 1.3 to be aligned with the documentation !
SVN:trunk[3628]
2015-07-07 14:00:47 +00:00
Denis Flaven
1070283349 #963: For security reasons, "Portal users" are no longer allowed to use the REST/JSON API.
SVN:trunk[3627]
2015-07-07 13:56:19 +00:00
Denis Flaven
4ee78ea59c #1078: Properly record the history of LinkedSet(Indirect)
SVN:trunk[3626]
2015-07-07 13:01:40 +00:00
Denis Flaven
b8f0ecb134 Bug fix: don't accept attachments (like images) via Chrome's copy/paste since it may duplicate the text content of a normal copy/paste and moreover causes troubles because there is no file name associated with the pasted content.
SVN:trunk[3621]
2015-07-06 14:28:36 +00:00
Denis Flaven
efec6f6ec9 Performance optimization: cache the result of the disk scan looking for icons for dashboards
SVN:trunk[3620]
2015-07-06 13:04:24 +00:00
Denis Flaven
7f460eda5a Better error reporting (thanks to Stefan Goethals for suggesting it).
SVN:trunk[3619]
2015-07-06 12:38:23 +00:00
Denis Flaven
678786c76c #765: prevent two persons to edit the same object at the same time. Typo.
SVN:trunk[3618]
2015-07-02 15:50:55 +00:00
Denis Flaven
9917d6355c #765: prevent two persons to edit the same object at the same time.
SVN:trunk[3617]
2015-07-02 15:40:39 +00:00
Romain Quetiez
7f65e9fd5e New lifecycle action SetCurrentPerson. Also improved the existing lifecycle action SetCurrentUser to prevent from calling it on an external key that is not pointing to users (!= contact), and if the target attribute is a string, then store the friendlyname there.
SVN:trunk[3616]
2015-07-02 09:43:15 +00:00
Denis Flaven
9f92e5e0be #788 Whenever a timeout is detected by an ajax request, a popup dialog warns the user to log-in again.
SVN:trunk[3613]
2015-06-25 15:32:30 +00:00
Denis Flaven
6e92438282 Bulk export: don't forget to cleanup in case of error.
SVN:trunk[3612]
2015-06-25 10:01:14 +00:00
Denis Flaven
f25980bb0d The Sass cache has nothing to do in SVN!
SVN:trunk[3611]
2015-06-23 16:32:37 +00:00
Denis Flaven
dd7861c5b4 Bulk Export redesign... finishing touch.
SVN:trunk[3610]
2015-06-23 16:29:45 +00:00
Denis Flaven
35a4112840 Small enhancement to the display of the meta model: in the list of transitions, display the code of the event as a tooltip.
SVN:trunk[3609]
2015-06-23 15:47:33 +00:00
Denis Flaven
2982f9cc9b Bulk Export redesign... change the menu to point to the new (interactive) export.
SVN:trunk[3608]
2015-06-23 14:59:35 +00:00
Denis Flaven
bbd83fba30 Bulk Export redesign... Typo in dict entry name.
SVN:trunk[3607]
2015-06-23 14:58:46 +00:00
Denis Flaven
cd5e5da526 Bulk Export redesign, addressing the tickets:
#1071 Bulk Read access rights
#1034 List of fields for Excel export
#772 Some attributes not exportedvia export.php
Main features:
- list and order of the fields taken into account
- interactive mode to specify all the parameters interactively (including the list and the order of fields)
- same behavior for all the formats: html, CSV, spreadsheet, XML
- new PDF export

SVN:trunk[3606]
2015-06-23 14:16:46 +00:00
Denis Flaven
f8df72b329 New look for iTop !
SVN:trunk[3602]
2015-06-22 08:14:25 +00:00
Denis Flaven
19e5130441 New look for iTop !
SVN:trunk[3601]
2015-06-20 15:02:24 +00:00
Denis Flaven
9ba1914524 Added an alternate implementation for storing "transaction" identifiers on disk instead of inside the $_SESSION variable.
SVN:trunk[3598]
2015-06-20 13:37:49 +00:00
Denis Flaven
586ec4515d Mutex instrumentation for troubleshooting...
SVN:trunk[3595]
2015-06-19 14:46:08 +00:00
Romain Quetiez
4c2543d6f4 JSON/REST: When specifying a case log entry (or the whole), it was not possible to set the user name without knowing a valid user id
SVN:trunk[3593]
2015-06-16 09:50:29 +00:00
Denis Flaven
1aa489890c Make sure that the SQL mutexes are specific to the current iTop instance, but still preserving the capability for the setup to detect an already running cron job with or without a valid config file.
SVN:trunk[3591]
2015-06-12 17:00:41 +00:00
Romain Quetiez
7a5bbd0613 Integrated the lexer/parser build tools (Lexer=0.4.0, Parser=0.1.7)
SVN:trunk[3590]
2015-06-10 13:23:03 +00:00
Denis Flaven
d9fcd83370 Impact analysis diagram is now considered as beta !
SVN:trunk[3588]
2015-05-26 16:39:51 +00:00
Romain Quetiez
73cd1274a5 Implemented GetForJSON and FromJSONToValue for AttributeLinkedSet (though this is not used for the Rest/JSON services which are doing much more) -retrofit from branch 2.1.0
SVN:trunk[3587]
2015-05-26 12:04:29 +00:00
Romain Quetiez
35e58f8cd2 Make it possible to overload RestUtils (static methods called with static:: instead of self::) - iTop NOW REQUIRES PHP 5.3: we have verified, there are very installations of iTop made on PHP 5.2. It is worth to note that PHP 5.3 is already end of life (5.4 will become end of life in 8 months)
SVN:trunk[3584]
2015-05-26 11:44:12 +00:00
Denis Flaven
0769b2c481 Relations & Impact analysis enhancements:
- Detailled tooltips in the graph
- Context queries ("knowing that")

SVN:trunk[3583]
2015-05-24 20:47:11 +00:00
Denis Flaven
80c0312219 Automatically save the PDF of the impact analysis as an attachement to the ticket.
SVN:trunk[3582]
2015-05-20 09:33:42 +00:00
Denis Flaven
3949632339 Removed unused function parameter.
SVN:trunk[3581]
2015-05-18 12:45:44 +00:00
Denis Flaven
1eb4b0cec4 Default value (=empty array) for excluded objects
SVN:trunk[3580]
2015-05-18 12:18:04 +00:00
Denis Flaven
a1ba5bec17 Impact analysis diagram uses jQuery context menus.
SVN:trunk[3579]
2015-05-18 12:17:16 +00:00
Denis Flaven
7ca7cb39ae Integration of the new impact analysis into the tickets.
SVN:trunk[3578]
2015-05-15 13:49:25 +00:00
Denis Flaven
d1a74589b1 More options for the PDF export of the 'impact' graph.
SVN:trunk[3577]
2015-05-10 09:07:19 +00:00
Denis Flaven
3e6896b8e6 Optimization of DisplayBlock::FromObjectSet, load only the needed column!
SVN:trunk[3576]
2015-05-06 17:13:30 +00:00
Romain Quetiez
3595434a05 XML Modelization of the relations: wrong computation of the upstream query (wrong computation of the redundancy when an Application Solution is made of CIS of various types)
SVN:trunk[3575]
2015-05-05 12:42:43 +00:00
Denis Flaven
7f1f1337fa Relation diagrams:
- Localization
- Handle the resize of the window
- Aysnchronous load/reload
- Filtering of the result based on the class

SVN:trunk[3574]
2015-05-05 08:04:23 +00:00
Romain Quetiez
7077879194 Enable queries on the synchronized objects (SynchroReplica::dest_id changed into an attribute of type AttributeObjectKey).
SVN:trunk[3573]
2015-05-04 09:36:22 +00:00
Romain Quetiez
f314036cef #1079 DBWriteLinks deleting related objects
SVN:trunk[3572]
2015-04-30 15:55:38 +00:00
Denis Flaven
af2835e505 Make sure that 'source' nodes for ComputedImpactedItems are not added twice to the ticket.
SVN:trunk[3571]
2015-04-29 17:27:40 +00:00
Denis Flaven
d63b4ef6d1 Integration of the new way to compute relations into the datamodel (ComputeImpactedItems)
SVN:trunk[3570]
2015-04-29 16:35:21 +00:00
Romain Quetiez
f69109bc43 #1069 (continuation of commit 3558) There may be some null values in the Database, making it impossible to upgrade. Defining a default value is far enough for external keys and hierarchical keys. Furthermore, this will be less time consuming during the setup (no need for table scans)
SVN:trunk[3569]
2015-04-29 15:31:28 +00:00
Romain Quetiez
255df92a30 Code refactoring: coloring a relation graph (purpose: distinguish potentially impacted CIs and really impacted CIs, when analyzing a change ticket)
SVN:trunk[3568]
2015-04-28 15:50:14 +00:00
Romain Quetiez
95defedf08 Improved the symptom when an error occurs in the "apply stimulus form". The symptom used to be: Object could not be written; unknown error. Now it will give the error message (e.g. Missing query arguments) so as to help in determining what's going on.
SVN:trunk[3567]
2015-04-27 09:39:41 +00:00
Romain Quetiez
ec97e6d2e0 ormStopWatch::GetElapsedTime not working in case of queries containing :this-> parameters (the prototype of GetElapsedTime has changed and is NOT compatible with the previous one)
SVN:trunk[3564]
2015-04-27 09:24:09 +00:00
Denis Flaven
161a92fef2 Impact analysis: migration to XML, bug fix for Server <=> Hypervisor
SVN:trunk[3563]
2015-04-24 10:47:42 +00:00
Romain Quetiez
da7ae0660e Fixed a regression introduced in [3518] (module parameters in XML)
SVN:trunk[3562]
2015-04-24 10:40:07 +00:00
Denis Flaven
ca794b421d Impact analysis: still an alpha version.
SVN:trunk[3561]
2015-04-24 10:10:51 +00:00
Romain Quetiez
520ccd361c Fixed a typo on the default document mimetype: application/x-octet-stream
SVN:trunk[3560]
2015-04-24 08:28:45 +00:00
Denis Flaven
aa93fde347 Impact analysis: still an alpha version.
SVN:trunk[3559]
2015-04-24 07:42:50 +00:00
Romain Quetiez
fedde33be1 #1069 Added a default value to the column definitions whenever possible: makes it less a pain to add a new hierarchical key when there are already some records in the DB
SVN:trunk[3558]
2015-04-23 17:15:07 +00:00
Romain Quetiez
dc356ae7b6 XML Modelization of the relations: XML definition moved so as to allow a minimal installation (no virtualization)
SVN:trunk[3557]
2015-04-23 17:12:07 +00:00
Romain Quetiez
fa333504c6 XML Modelization of the relations: no option to restrict the browsing to downstream
SVN:trunk[3556]
2015-04-23 10:11:33 +00:00
Denis Flaven
7c210f4d1c Replacement of the impact Flash based analysis graph by graphviz + Raphael + TCPDF. ALPHA version.
SVN:trunk[3555]
2015-04-23 10:03:18 +00:00
Denis Flaven
df47e2a9e9 Replacement of the impact Flash based analysis graph by graphviz + Raphael + TCPDF. ALPHA version.
SVN:trunk[3554]
2015-04-23 10:02:06 +00:00
Romain Quetiez
87a3b73024 XML Modelization of the relations: updated the conversion from 1.2 to 1.1
SVN:trunk[3553]
2015-04-23 09:33:04 +00:00
Romain Quetiez
eb379662ce Rework of the relation diagrams: configuration of the redundancy (AttributeRedundancySettings)
SVN:trunk[3552]
2015-04-22 15:33:07 +00:00
Denis Flaven
7176d5a19c Bug fix: prevent a crash of the web services when trying to log a non scalar paramater value...
SVN:trunk[3549]
2015-04-16 15:33:21 +00:00
Romain Quetiez
ff1514dc75 Modules implementing a lifecycle written in PHP (and having actions executed on transitions) do not work until 2.1.0. The compatibility patch had been implemented but it was not working. Good candidate for a retrofit to the branch 2.1.0
SVN:trunk[3547]
2015-04-16 13:49:36 +00:00
Romain Quetiez
59ebc262a3 Rework of the relation diagrams: added min_up data to the redundancy nodes
SVN:trunk[3546]
2015-04-15 14:00:05 +00:00
Romain Quetiez
26eb4c7083 Rework of the relation diagrams: implemented MetaModel::GetRelatedObjectsUp, and took the redundancy into account (still misses a GUI)
SVN:trunk[3545]
2015-04-15 09:06:50 +00:00
Romain Quetiez
ef8888c679 Rework of the relation diagrams: implemented MetaModel::GetRelatedObjectsDown (still not taking the redundancy into account)
SVN:trunk[3544]
2015-04-13 12:59:26 +00:00
Romain Quetiez
34ff5d6ac4 #1092 Caller not preset when creating a ticket from a contact
SVN:trunk[3543]
2015-04-10 12:02:23 +00:00
Romain Quetiez
e64b6d1d98 XML Modelization of the relations: reworked toward an asymetric definition (downstream: A impacts B, upstream: B depends on A)
- The queries are developped at runtime (cache)
- More complex algorithm to take into account the legacy type of specification (GetRelationQueries)
- New dictionary naming convention (preserving backward compatibility): "VerbUp" to be replaced by "DownStream
- Temporary hacks to preserve the relation 'depends on', until we have a new GUI
- Special handling for the relation LogicalVolume impacts VirtualDevice which had to be implemented in the bridge module
- Improved the backward compatibility by leaving legacy methods GetRelationQueries returning an empty definition, allowing for an eventual XML redefinition

SVN:trunk[3542]
2015-04-10 10:09:22 +00:00
Denis Flaven
b9b5287b37 Helper class to managed relation graphs.
SVN:trunk[3541]
2015-04-08 14:50:01 +00:00
Denis Flaven
5df6009f08 #1082 Dashlet badge: do not display search results everytime.
SVN:trunk[3539]
2015-04-07 13:19:11 +00:00
Denis Flaven
cca4737b91 #1088: support of HTMLEditor in the PortalWebPage, for example if the description of a ticket is in HTML.
SVN:trunk[3538]
2015-04-07 13:03:49 +00:00
Denis Flaven
bf1812ae83 Bug fix: properly compute the URLs/URIs for the soap server (and its extensions)
SVN:trunk[3536]
2015-04-07 09:55:50 +00:00
Denis Flaven
0000cfd234 #1083: HTML export: show a scroll bar when needed.
SVN:trunk[3535]
2015-04-03 09:36:40 +00:00
Denis Flaven
a876cd2186 #1059: fix for the Spanish localization first_name and last_name were swaped.
SVN:trunk[3534]
2015-04-03 09:00:57 +00:00
Denis Flaven
d9b1d0faf3 #1056: the 'zip' extension is now mandatory to install iTop, since the code relies on the ZipArchive class for the Excel export and the scheduled backup.
SVN:trunk[3533]
2015-04-03 08:50:03 +00:00
Denis Flaven
2856d53967 #1054: increase max_execution_time during the setup.
SVN:trunk[3532]
2015-04-03 08:37:55 +00:00
Denis Flaven
9772b58333 #1052: Fix for the German localization.
SVN:trunk[3531]
2015-04-03 08:30:40 +00:00
Denis Flaven
b74ab0614e Fixing a regression introduced by 3525
SVN:trunk[3530]
2015-04-03 08:28:15 +00:00
Denis Flaven
61a21520d1 #1050: Properly support the 'list' display style for external keys - as stated in the documentation!
SVN:trunk[3529]
2015-04-03 08:26:39 +00:00
Denis Flaven
ebfc9aa1e0 #1047: Fix for the FindTab method.
SVN:trunk[3528]
2015-04-03 08:02:20 +00:00
Denis Flaven
ff54d6dd6c #1045: Fix in the German localization.
SVN:trunk[3527]
2015-04-03 07:58:11 +00:00
Denis Flaven
828e4d6297 Oops, wrong commit, reverting these two files to their previous version.
SVN:trunk[3526]
2015-04-01 16:04:20 +00:00
Denis Flaven
030b4fa380 Enhancement: support injection of new modules treated as data.
SVN:trunk[3525]
2015-04-01 15:53:05 +00:00
Romain Quetiez
328a5e8077 XML Modelization of the relations: transformed the existing model (preserving the current behavior) to define the relations as an ATTRIBUTE whenever possible. Also took the opportunity to enforce a naming convention (neighbour id = target class name in lower case)
SVN:trunk[3524]
2015-03-30 15:38:37 +00:00
Romain Quetiez
e8cbb2d39d XML 1.2: handle the XML transformation. Added APIs to report the functionality loss when downgrading (snippets, portal, module parameters, relations and object key)
SVN:trunk[3523]
2015-03-30 14:17:29 +00:00
Romain Quetiez
887e73ea1d XML Modelization of the relations: declare the relations based on the XML (implicit declaration in XML, explicit in PHP -thus retrocompatible)
SVN:trunk[3522]
2015-03-30 14:14:26 +00:00
Romain Quetiez
e210996839 XML Modelization of the relations: fixed a bug in the compiler and transformed the datamodel files (2.x) into the latest 1.2 format
SVN:trunk[3521]
2015-03-30 08:24:45 +00:00
Denis Flaven
2ba3ab3057 Enhancement: PHP snippets inside the XML.
SVN:trunk[3520]
2015-03-27 17:16:40 +00:00
Romain Quetiez
3cf0fa3ee2 XML Modelization of the relations, with full support of the previous way (by implementing a method GetRelationQueries). Still, the standard data model has not been migrated to the new format.
SVN:trunk[3519]
2015-03-26 11:12:25 +00:00
Denis Flaven
8b36699893 Enhancement: the default value for a module's parameter can now be specified (and altered) via the XML and will no longer reside in the configuration file.
SVN:trunk[3518]
2015-03-25 15:11:24 +00:00
Denis Flaven
166f5ce73f Enhancement: allow the API to create entries with a specified user_login.
SVN:trunk[3514]
2015-03-24 17:05:50 +00:00
Denis Flaven
92baec128e #594: properly display attachments inside "properties" by closing the span and the fieldset in non-edit mode.
SVN:trunk[3510]
2015-03-23 17:52:17 +00:00
Denis Flaven
4919ca88ec Modularization of the portal. The entry points for portals is now defined in XML, and thus can be altered by an extension.
SVN:trunk[3509]
2015-03-23 16:02:44 +00:00
Romain Quetiez
fa0d408664 OQL enhancement: continuation... (bug fix with a query on object history)
SVN:trunk[3507]
2015-03-19 15:25:04 +00:00
Romain Quetiez
444d9e36c6 OQL enhancement: allow JOIN on a objclass/objkey pair of attributes (requires benchmarking)
SVN:trunk[3506]
2015-03-19 12:50:15 +00:00
Denis Flaven
95fc4d867d Fixed another regression of 3500: LongTextFields also support multiple forbidden lists...
SVN:trunk[3505]
2015-03-12 15:26:08 +00:00
Denis Flaven
6524a40eaa Enhancement: do not retrieve disabled fields.
SVN:trunk[3504]
2015-03-12 14:00:14 +00:00
Romain Quetiez
f53943e78c Meta information on lifecycle actions arguments: added type restrictions, and added the method ResetStopWatch
SVN:trunk[3503]
2015-03-12 10:42:51 +00:00
Denis Flaven
528a8901df Fixed a regression introduced by the revision 3500: the default value is NEVER "forbidden"
SVN:trunk[3502]
2015-03-11 15:46:53 +00:00
Denis Flaven
acd6d9679a Additional markup for JQuery scripts...
SVN:trunk[3501]
2015-03-11 15:33:01 +00:00
Denis Flaven
f7c7fc5dc4 Support several sets of forbidden values (with a specific "reason" message) per field.
SVN:trunk[3500]
2015-03-10 15:34:04 +00:00
Romain Quetiez
d575c48579 N°257 Aperçu des dashlets Badge partiellement hardcodé ("Search for objects of type Server")
SVN:trunk[3499]
2015-03-10 13:48:50 +00:00
Denis Flaven
930d833e1b #803: template placeholders are now built on demand. Yes !!
SVN:trunk[3498]
2015-02-27 10:02:44 +00:00
Denis Flaven
f7f77911be - Properly handle "suggested" attachments
- Properly pass the name of the uploaded file to the internal JS event

SVN:trunk[3496]
2015-02-12 17:59:08 +00:00
Romain Quetiez
508f82946f #1060 Internal: improved the symptoms when calling MetaModel::GetAttributeDef with an invalid attribute code (feedback on the class name and no more FATAL errors)
SVN:trunk[3492]
2015-02-09 13:11:49 +00:00
Romain Quetiez
6bb9754628 Internal: fixed the caching of DBObject::ToArgs()
1) Wasn't reset when the object was written the DB (thus having its ID set)
2) Wasn't taking the argument name into account (the list of placeholders was defined by the first caller)

SVN:trunk[3491]
2015-01-30 10:04:42 +00:00
Romain Quetiez
44fad50031 #1053 XML comments breaking the setup with message "Notice: Undefined property: DOMComment::$wholeText in ...modelfactory.class.inc.php on line 1280"
SVN:trunk[3490]
2015-01-14 13:51:37 +00:00
Denis Flaven
ed2cd2cea3 Change of the QueryReflection API to support DesignTime.
SVN:trunk[3489]
2015-01-12 14:20:20 +00:00
Romain Quetiez
eaf74a3f23 ModelFactory: Re-creating a class into another location in the class hierarchy it equivalent to moving that class => the delta must be a "redefine" for the class (improved the comment from the previous commit)
SVN:trunk[3487]
2015-01-08 11:05:18 +00:00
Romain Quetiez
1a99146b7a ModelFactory: Re-creating a class into another location in the class hierarchy it equivalent to moving that class => the delta must be a "redefine" for the class
SVN:trunk[3486]
2015-01-08 10:39:34 +00:00
Denis Flaven
4a8e9e71f4 Regression: the instance method is only available since jquery UI 1.11
SVN:trunk[3484]
2014-12-26 09:08:14 +00:00
Denis Flaven
6d2d0ff701 - Read-only "long text" fields no longer appear as editable
- Combo and FormSelector fields are now sorted by default (but sorting can be disabled if needed)

SVN:trunk[3483]
2014-12-23 15:23:12 +00:00
Denis Flaven
af3c93051f Protect against JS errors when the form is in read-only mode.
SVN:trunk[3482]
2014-12-23 14:49:06 +00:00
Denis Flaven
f594190005 Properly handle property_sheets with nested selector fields...
SVN:trunk[3481]
2014-12-23 13:42:25 +00:00
Denis Flaven
546d181ea9 Addition of the Danish localization contributed by Erik Bøg
SVN:trunk[3480]
2014-12-18 08:56:23 +00:00
Denis Flaven
143cefe4e3 #1041 Protect against some XSS injections
SVN:trunk[3479]
2014-12-18 08:50:04 +00:00
Romain Quetiez
ece152173f Advanced customization: a stop watch can be started in the past (incident ticket created from an alarm)
SVN:trunk[3478]
2014-12-18 08:37:00 +00:00
Denis Flaven
b08de31b3c Prevent duplicate declaration of the "Data Admin" menu (both in XML and PHP) which makes it impossible to customize.
SVN:trunk[3477]
2014-12-17 17:40:01 +00:00
Denis Flaven
0f967a41df Prevent a PHP crash when the icon tag is missing from a highlight_code definition in the XML.
SVN:trunk[3476]
2014-12-17 17:27:24 +00:00
Romain Quetiez
4c3bf70cc4 Completing [3423]: Problem/ev_assign still invoking the legacy verb SetAssignedDate
SVN:trunk[3475]
2014-12-17 08:55:02 +00:00
Romain Quetiez
35dd3f9610 The very final version (removed a misleading header in the readme file)
SVN:trunk[3473]
2014-12-16 15:13:09 +00:00
Romain Quetiez
a7f7424e54 #1039 Continuation of the fix implemented in [3465] that introduced a stopper regression (Fatal Error)
SVN:trunk[3472]
2014-12-16 13:54:40 +00:00
Denis Flaven
83e2974b10 #1040 Graphical display of "impact/depends on" is not consistent with the "list" tab
SVN:trunk[3471]
2014-12-16 13:40:51 +00:00
Romain Quetiez
715ba066d3 Adjusted dictionary entries (meta information about the lifecycle actions)
SVN:trunk[3470]
2014-12-16 09:02:03 +00:00
Romain Quetiez
9502003ff4 Updated the readme file with the latest changes
SVN:trunk[3469]
2014-12-15 16:11:15 +00:00
Romain Quetiez
57c827bb1a Updated the readme file with the latest changes
SVN:trunk[3468]
2014-12-15 16:08:50 +00:00
Denis Flaven
690ac9be75 #1038: dictionary cleanup to avoid misleading/duplicate names when importing Service Subcategories.
SVN:trunk[3467]
2014-12-15 16:08:10 +00:00
Romain Quetiez
4c3c31c44d Injectable methods: labels/descriptions given in the dictionary
SVN:trunk[3466]
2014-12-15 15:49:44 +00:00
Denis Flaven
3c9ace5b53 #1039: prevent concurrent executions of either synchro_import.php or synchro_exec.php for a given data source, since it would lead to unpredictable results.
SVN:trunk[3465]
2014-12-15 15:04:43 +00:00
Denis Flaven
bd5268dc42 Addition of the Ducth translation, thanks to Remie Malik.
SVN:trunk[3464]
2014-12-15 14:34:34 +00:00
Denis Flaven
133b6d4d29 #1037: refresh "priority" when either "impact" or "urgency" changes.
SVN:trunk[3463]
2014-12-15 14:09:17 +00:00
Denis Flaven
fba3990c61 - Proper handling of the validation hierarchy in property sheets.
- Correct behavior for animated submits...

SVN:trunk[3462]
2014-12-12 16:38:17 +00:00
Romain Quetiez
53e997cfba Instrumented Model Factory with means to keep track of touched nodes
SVN:trunk[3461]
2014-12-12 12:17:43 +00:00
Denis Flaven
e738ba35b7 The FormSelectorField now has its own widget to properly cope with its "subfields" in "property sheet" mode (continued).
SVN:trunk[3460]
2014-12-10 17:11:45 +00:00
Romain Quetiez
0773455ebc Cosmetics on the module names (consistency)
SVN:trunk[3459]
2014-12-10 10:48:47 +00:00
Denis Flaven
cafc6a8baf The FormSelectorField now has its own widget to properly cope with its "subfields" in "property sheet" mode.
SVN:trunk[3458]
2014-12-10 10:44:26 +00:00
Romain Quetiez
48f222df0b When adding a case log, existing objects could not be displayed anymore!
SVN:trunk[3457]
2014-12-09 16:07:06 +00:00
Denis Flaven
88726a0634 Support for some (optional) feedback during submit.
SVN:trunk[3456]
2014-12-08 13:19:09 +00:00
Denis Flaven
0ac522fc4c Support for some (optional) feedback during uploads.
SVN:trunk[3455]
2014-12-08 13:18:06 +00:00
Denis Flaven
aa97703b64 Add the appropriate "type" to the parameters passed to the actions on transitions.
SVN:trunk[3454]
2014-12-05 11:02:32 +00:00
Romain Quetiez
30af416394 Rework of the dictionaries: moved menu related entries to the module itop-welcome-itil (which does create most of those menus), while preserving the original copy of the entries so as to be compatible with customizations made with a copy of an older version of itop-welcome-itil
SVN:trunk[3453]
2014-12-04 16:05:11 +00:00
Denis Flaven
1f2ad9ecdb Demo mode: prevent the deletion of Users...
SVN:trunk[3452]
2014-12-04 10:02:14 +00:00
Romain Quetiez
d1f1889c42 Updated the release note with the changes made since the beta release
SVN:trunk[3451]
2014-12-03 16:43:28 +00:00
Romain Quetiez
ca7ee0f138 #1020 Restrict dashboard/shortcut refresh interval
SVN:trunk[3448]
2014-12-03 11:38:19 +00:00
Denis Flaven
909729e9f1 #1028: drop the support of non-numeric primary keys for DBObjects. This makes the queries related to the history (and the indexes) much more efficient. Beware, conversion time at setup can be long if the priv_changeop table is big.
SVN:trunk[3447]
2014-12-03 11:01:29 +00:00
Denis Flaven
a222ba998d Enhanced reporting during the setup: all the queries (create table / alter table) are now logged into "setup.log" along with their execution time.
SVN:trunk[3446]
2014-12-03 10:58:39 +00:00
Romain Quetiez
aedddf9dcc #1026 CSV import of tickets with impact = '', issuing a Notice
SVN:trunk[3445]
2014-12-02 14:45:19 +00:00
Denis Flaven
74de0d33ab #1021: fix alignment of multiple header dashlets in the same cell.
SVN:trunk[3444]
2014-12-02 13:10:44 +00:00
Romain Quetiez
8d8510a412 #1030 Missing values in the history tab (TTO/TTR)
The regression has been introduced in iTop 2.0.2 with the fix for #703 (escape HTML entities). There is no data loss: changes were correctly made and all the changes already recorded will be correctly displayed with the current fix.
The current change consists in a little rework: GetAsHTMLForHistory has been renamed DescribeChange, and it now calls oAttDef->GetAsHTMLForHistory instead of calling oAttDef->GetAsHTML, thus allowing for the factorization of the code that format the change description. AttributeSubitem does not overload DescribeChange. Rather, it simply overloads GetAsHTMLForHistory (viewing the diff may be confusing here).

SVN:trunk[3443]
2014-12-02 10:53:59 +00:00
Denis Flaven
4dd83a0eb6 #985: preserve the displayed sort order when refreshing a table.
SVN:trunk[3442]
2014-12-02 10:26:26 +00:00
Romain Quetiez
671ee353e8 #1027 Regression introduced in [3148] thus in 2.0.3 (cache the reconciliation for external keys on the CSV import) a cache hit on an ambiguous external key was not correctly handled
SVN:trunk[3440]
2014-12-01 19:12:19 +00:00
Denis Flaven
d714235d67 #1016: record the displayed value of the database_table_name field in the database.
This happens:
- when creating a new Synchro Data Source
- when modifying an "old" Synchro Data Source for which the field was empty.

SVN:trunk[3439]
2014-12-01 17:33:38 +00:00
Romain Quetiez
c4b039c9c6 #975 Continuation of the fix implemented in [r3431] - Renaming an enum requires to call RenameEnumValueInDB
SVN:trunk[3438]
2014-12-01 15:19:48 +00:00
Romain Quetiez
29e751278e #1029 Got rid of tags <format> that were not used at all and that were really misleading extension developers
SVN:trunk[3437]
2014-12-01 11:43:12 +00:00
Denis Flaven
87ed5d4a05 Fix for a regression introduced by [r3394] while fixing #1000: handling of OPT_ATT_MUSTCHANGE. The fix was not working when the original value contained several lines.
SVN:trunk[3436]
2014-11-28 17:03:13 +00:00
Denis Flaven
c8a20d01da Bug fix: proper handling of the validation of subforms...
SVN:trunk[3435]
2014-11-28 14:03:21 +00:00
Erwan Taloc
47add0eeea Allow linkage of organization to a Delivery model, directly from the tab "Customers"
SVN:trunk[3434]
2014-11-28 10:28:36 +00:00
Denis Flaven
2a9f69d70e More meta information about the interfaces.
SVN:trunk[3433]
2014-11-27 16:22:05 +00:00
Romain Quetiez
e90e570469 Datamodel version is now 2.1.0
SVN:trunk[3432]
2014-11-27 15:23:51 +00:00
Erwan Taloc
b0e56e5897 modify enumerated list for model type in order to manage Phone CIs : tack #975
SVN:trunk[3431]
2014-11-26 18:56:51 +00:00
Erwan Taloc
c96842d82c replace provider_name by provider_id in the search form of service-subcategories
SVN:trunk[3430]
2014-11-26 18:34:51 +00:00
Erwan Taloc
bc79aecd73 Improve french translation
SVN:trunk[3429]
2014-11-26 18:33:40 +00:00
Erwan Taloc
99755ad7e8 update highlight scale for request-management modules
SVN:trunk[3428]
2014-11-26 18:32:38 +00:00
Erwan Taloc
4867461f69 Add tab to link a problem to incidents
SVN:trunk[3427]
2014-11-26 17:38:53 +00:00
Erwan Taloc
3d21eecbba Translate missing tabs translation related_requests_list
SVN:trunk[3426]
2014-11-26 17:35:32 +00:00
Romain Quetiez
b822cff269 Instrumented the code to ease the troubleshooting of the computing of working hours
SVN:trunk[3425]
2014-11-25 15:25:28 +00:00
Romain Quetiez
3aa0b77751 Cosmetic on the XML: identify lifecycle functions parameters
SVN:trunk[3424]
2014-11-19 14:15:10 +00:00
Romain Quetiez
f4b10d3e81 #1022 Do cascade the resolution of an incident to its child requests + rework of the lifecycle/actions to ease the extensibility (New handlers: Rest, Copy, SetCurrentDate, SetCurrentUser, SetElapsedTime)
SVN:trunk[3423]
2014-11-19 09:44:52 +00:00
Denis Flaven
ca90f9b32a Oops, typo in "Prevent the JS validation (on focus) to create multiple entries for the same field, since it breaks the validation."
SVN:trunk[3422]
2014-11-18 17:25:08 +00:00
Denis Flaven
9ca051d9d0 Prevent the JS validation (on focus) to create multiple entries for the same field, since it breaks the validation.
SVN:trunk[3421]
2014-11-18 15:31:51 +00:00
Romain Quetiez
291f05683c New function: ormStopWatch::GetElapsedTime to compute the cumulated elapsed time on a stop watch still running -not used yet (but tested!)
SVN:trunk[3420]
2014-11-18 14:54:10 +00:00
Denis Flaven
4889ed5ac2 Read-only mode for icon selector widget: display at least the icon.
SVN:trunk[3419]
2014-11-18 09:22:48 +00:00
Denis Flaven
3fa354d00d Predefined objects are now handled by RuntimeEnvironment
SVN:trunk[3418]
2014-11-14 10:43:24 +00:00
Denis Flaven
f62934829d Fix of read_only flags for some attributes (previous verison had typos, i.e. "readonly" instead of "read_only", which made the factorisation of the state flags incorrect)
SVN:trunk[3416]
2014-11-10 13:58:37 +00:00
Denis Flaven
dee7f7b7aa Upgrade to Raphael 2.1.2
SVN:trunk[3415]
2014-11-07 13:25:37 +00:00
Romain Quetiez
1d0bfa7c92 XML format conversion utilities: no version means '1.0'
SVN:trunk[3414]
2014-11-06 15:51:08 +00:00
Denis Flaven
8922c435b4 Setup wizard cosmetics: added a "Reload" button to refresh the first page of the wizard when the prerequisites are not met.
SVN:trunk[3413]
2014-11-06 10:41:03 +00:00
Denis Flaven
ec1ec854fb Compiler: support "OPT_ATT_NORMAL" flags when an empty set of flags is defined in order to reset the flags on an attribute. Useful for inherited flags.
SVN:trunk[3412]
2014-11-06 10:39:44 +00:00
Romain Quetiez
dfc248b836 XML format conversion utilities: reworked the API to implement a CheckConvert and handle any kind of upgrade/downgrade (easy way to add a new format like 1.2)
SVN:trunk[3411]
2014-11-06 10:14:41 +00:00
Denis Flaven
481d05d082 Slightly better wording for describing the relations on the impact/dependency graph.
SVN:trunk[3410]
2014-11-05 18:30:39 +00:00
Denis Flaven
4e4d3cf3da Enhanced version of the SWF navigator providing a better stability for big numbers of elements.
SVN:trunk[3409]
2014-11-05 18:28:54 +00:00
Denis Flaven
5fc68557dc Enhanced version of the SWF navigator providing a better stability for big numbers of elements.
SVN:trunk[3408]
2014-11-05 18:27:56 +00:00
Denis Flaven
7482a52fd9 Reverting to revision 3397
SVN:trunk[3407]
2014-11-03 16:12:11 +00:00
Romain Quetiez
50ca6cdd0f Backup tools disabled when the demo mode is active
SVN:trunk[3406]
2014-11-03 15:12:47 +00:00
Romain Quetiez
e83e1262a5 Configuration editor disabled when the demo mode is active
SVN:trunk[3405]
2014-11-03 15:12:07 +00:00
Romain Quetiez
51bd403638 Draft of the readme file for 2.1.0 beta
SVN:trunk[3404]
2014-11-03 11:17:40 +00:00
Denis Flaven
c786e8308a Incrementation of the version number of the 2.x modules to reflect the change of the XML format.
SVN:trunk[3403]
2014-11-03 09:21:03 +00:00
Romain Quetiez
a7d3a5a488 New functionality: data backup / restore. By default, performs a daily backup, keeping the five last backups. Allows manual backups too.
SVN:trunk[3402]
2014-11-03 09:18:54 +00:00
Romain Quetiez
2c70c60d2b New functionality: a rudimentary configuration editor (admins only)
SVN:trunk[3401]
2014-11-03 09:17:27 +00:00
Denis Flaven
96f02d1557 Integration of the Excel (XLSX) export feature... (regression fix)
SVN:trunk[3400]
2014-11-01 07:46:57 +00:00
Denis Flaven
de6ddffe65 #998 Accurately check the configured mail transport and report information accordingly in the email.test page.
Add two "debug" transports for Swift mailer: Null and LogFile which are useful for staging environments where one does not want to send real emails.

SVN:trunk[3399]
2014-10-31 21:14:55 +00:00
Denis Flaven
2c59fb894f Integration of the Excel (XLSX) export feature. (Limitation: export.php takes into account neither the "fields" parameter nor the list of fields defined in the QueryPhrasebook when exporting in XLSX format)
SVN:trunk[3398]
2014-10-31 17:59:14 +00:00
Romain Quetiez
77cf399c72 Portal: handle mandatory attributes in Reopen/Close dialogs
SVN:trunk[3397]
2014-10-31 15:42:13 +00:00
Romain Quetiez
19a2180c2b #1000 Implemented the behavior for the flag OPT_ATT_MUSTCHANGE, and took the opportunity to add a feedback when a field is mandatory OR when the format is wrong
SVN:trunk[3394]
2014-10-31 14:55:12 +00:00
Romain Quetiez
18b73de512 #1012 Losing half of the connection when changing a port (connections between network devices). I took the opportunity to simplify the connection management as it was initiated in change [3388].
SVN:trunk[3393]
2014-10-30 10:53:30 +00:00
Denis Flaven
bb741c39f4 #1011 Proper resizing of the dialog box for managing 1:n links - removed a trace.
SVN:trunk[3390]
2014-10-30 08:54:48 +00:00
Denis Flaven
c73ef6ae72 #1011 Proper resizing of the dialog box for managing 1:n links.
SVN:trunk[3389]
2014-10-30 08:52:00 +00:00
Romain Quetiez
5abd9c6dad #1008 Error when deleting a Network Device connected to another Network Device (does not happen if the other end is another type of "ConnectedDevice")
SVN:trunk[3388]
2014-10-29 20:44:24 +00:00
Romain Quetiez
a6db04bafd #1007 Unexpected change of the case log when doing massive update of a User Request (+ hide the checkbox for the status because it makes no sense)
SVN:trunk[3387]
2014-10-29 15:46:44 +00:00
Romain Quetiez
04f46f4798 #979 Data synchro: recover the DB triggers (backup/restore). I've tested various combinations of the previous check (new columns to synchronize) and the new one (missing triggers).
SVN:trunk[3386]
2014-10-29 13:15:36 +00:00
Romain Quetiez
5ecf6b9e9d Fixed regression introduced in [3224] thus in iTop 2.0.3, in the data model view: could not see the OQL constraints on external keys
SVN:trunk[3385]
2014-10-28 16:07:42 +00:00
Romain Quetiez
0135573956 #995 Make sure that tto/ttr passed gets set even if the CRON has not been run (and as soon as some overrun has been counted)
SVN:trunk[3384]
2014-10-28 15:55:56 +00:00
Romain Quetiez
a59915e5ee Completing the change [3291]: the cron could not trigger a timeout on the stop watches, and the 'persistent' flag was forcibly set to true (whereas we expect the default value to be false!)
SVN:trunk[3383]
2014-10-28 15:51:55 +00:00
Denis Flaven
ebb3767ee4 RenderAsDialog now takes into account its "width" parameter.
SVN:trunk[3382]
2014-10-28 14:49:15 +00:00
Romain Quetiez
047166f002 #983 Sortering not possible on multi-column queries
SVN:trunk[3381]
2014-10-28 10:47:00 +00:00
Romain Quetiez
f45c783396 #965 Since 2.0.3, for each synchronized object, around 100 queries are performed (2 are required), and this is multiplied be the number of duplicate replicas (then resulting in a significant slowdown).
SVN:trunk[3380]
2014-10-28 08:53:30 +00:00
Denis Flaven
72f516685e #974 prevent multiple javascript refresh when reloading the "initial state" of a ticket.
SVN:trunk[3379]
2014-10-27 16:37:52 +00:00
Romain Quetiez
2b4400c55d XML: normalized the format (allows for implementing a quick diff algorithm)
SVN:trunk[3378]
2014-10-27 16:13:38 +00:00
Romain Quetiez
1df87b6331 #969 XML: the menu option enable_admin_only hides the menu for everyone
SVN:trunk[3377]
2014-10-23 19:22:18 +00:00
Romain Quetiez
cd7490472e #970 and #650 Corrupted attachements. Reworked the cleanup of undesired output, to protect it against the case when the output buffer is unfortunately closed. On the other hand, I found out that several output buffer can be stacked. Thus the protection could be further improved (difficulty: that can be web server dependent).
SVN:trunk[3376]
2014-10-23 15:48:49 +00:00
Romain Quetiez
b65131e4f5 #971 XML: could not specify an icon as a path to a file
SVN:trunk[3375]
2014-10-23 08:54:39 +00:00
Romain Quetiez
fac8604cc6 #972 Incomprehensible error message during setup, with a sample extension provided by Combodo! (empty user rights tag) -This fix improves a lot, but the final fix should be to compile user rights in a separate directory. To be further discussed.
SVN:trunk[3374]
2014-10-22 15:31:53 +00:00
Romain Quetiez
e25da2a7c4 Complete the commit [3291] that changes the structure returned by MetaModel::EnumTransitions. I have carefully reviewed every call to EnumTransitions... and found out only one single missing case in the data model view.
SVN:trunk[3373]
2014-10-22 10:44:58 +00:00
Romain Quetiez
6d9e7f690f #993 The about box does not show up when the directory extensions is missing
SVN:trunk[3372]
2014-10-22 09:48:13 +00:00
Denis Flaven
9c62952743 Typo: sKey => iKey.
Prefix for the dialogs to prevent a collision of IDs

SVN:trunk[3371]
2014-10-21 14:13:49 +00:00
Romain Quetiez
03c4964072 Protected the property fields against the collision of ids within the same page (even if that is a bug, make it work not too bad!)
SVN:trunk[3370]
2014-10-20 15:15:39 +00:00
Denis Flaven
711949414d Fixed the support of a non-default port for MySQL, thanks to theBigOne!
SVN:trunk[3368]
2014-10-16 14:32:10 +00:00
Romain Quetiez
055a87306e Forms: drop-down box default value label could be changed (or this entry could be entirely removed)
SVN:trunk[3367]
2014-10-16 10:15:41 +00:00
Romain Quetiez
37ebb51a2b Computation of user rights: added a config flag to force the legacy algorithm (user_rights_legacy, defaulting to false)
SVN:trunk[3366]
2014-10-16 10:14:17 +00:00
Romain Quetiez
1f8d4d379f Computation of user rights: deny on a parent class must give DENY even if the class is explicitely ALLOW on the same profile (that was already working if the rules are given on several profiles). Note that this has a cost when building the grant matrix!
SVN:trunk[3365]
2014-10-16 09:02:47 +00:00
Romain Quetiez
1300811007 Aligned the compiler with the new XML format (1.1):
- Profile actions (was buggy)
- Sort the states declaration (could be buggy depending on the order of the declaration withing the XML)

SVN:trunk[3364]
2014-10-15 15:07:52 +00:00
Denis Flaven
fbdd0dfd57 - Migration of the XML files to the version 1.1 of the XML schema.
- Refactoring of the "state flags" definitions to take advantage of the "inherit_flags_from" capability available in the new XML schema.

SVN:trunk[3363]
2014-10-14 09:56:21 +00:00
Denis Flaven
bc79663a3e Split in a separate "tool" class the upgrade of the format of the datamodel from 1.0 to 1.1.
Marked the XML as version 1.1.

SVN:trunk[3362]
2014-10-13 14:42:55 +00:00
Romain Quetiez
570e4f8589 New XML format, updated the transformation tool (UpgradeDocument)
SVN:trunk[3361]
2014-10-10 08:22:51 +00:00
Romain Quetiez
472802e11b Cosmetic improvement of an error message (data model compilation - collision of ids)
SVN:trunk[3360]
2014-10-07 14:41:11 +00:00
Denis Flaven
f794d0222e Forms enhancements and XML format touch-up.
SVN:trunk[3359]
2014-10-03 09:56:20 +00:00
Denis Flaven
b42a43d47b #968 (continued) do not loose the "advanced mode" flag in the interactive display.
SVN:trunk[3357]
2014-10-02 09:36:32 +00:00
Romain Quetiez
110aace270 Form fields: added callbacks ('equals' and 'get_field_value') to allow the implementation of enhanced form fields
SVN:trunk[3356]
2014-10-01 12:17:15 +00:00
Romain Quetiez
68dd0513c6 #968 Interactive CSV Export truncated or missing characters (since 2.0.3)
SVN:trunk[3354]
2014-09-25 15:21:20 +00:00
Romain Quetiez
08e757a08a #991 CSV export truncated (system dependent, since 2.0) due to a bug in iconv, the workaround is to do little by little
SVN:trunk[3352]
2014-09-25 15:01:45 +00:00
Romain Quetiez
996c590793 Code cleanup: reworked the user rights data model, while strictly preserving the current functionality (checked using the tool dump_profiles.php, with simple to full ITIL configurations). Class groups have been renamed/merged/removed. This is documented in the migration notes (wiki).
SVN:trunk[3351]
2014-09-24 09:18:14 +00:00
Romain Quetiez
c93cc3a5bf lnkVirtualDeviceToVolume and lnkTriggerAction are link classes and should be declared as such
SVN:trunk[3350]
2014-09-24 09:06:30 +00:00
Romain Quetiez
bdccf6ea72 Transmission of user rights along N-N links: must work both with DEL_AUTO and DEL_SILENT external keys (found with a code review, DEL_SILENT is still rarely used)
SVN:trunk[3349]
2014-09-24 08:56:08 +00:00
Romain Quetiez
ed60346ae6 Code cleanup (removed dead and misleading code branches), following revision [3347]
SVN:trunk[3348]
2014-09-23 13:38:22 +00:00
Denis Flaven
4953ea7701 Rework of the ModelFactory API to make it simpler and safer.
SVN:trunk[3347]
2014-09-22 15:19:56 +00:00
Denis Flaven
82a8a0bba7 #932: fixed a regression introduced by [r3319]... in case a criteria is present several times.
SVN:trunk[3344]
2014-09-17 15:41:35 +00:00
Romain Quetiez
55c818b6b7 Dehardcoded the size of the attachments preview
SVN:trunk[3343]
2014-09-16 12:26:50 +00:00
Romain Quetiez
372c0835f7 #988 Could not change the case of a login (cosmetic improvement to make sure this piece of code is the right example that can be shared amongst the developpers community)
SVN:trunk[3342]
2014-09-16 10:16:10 +00:00
Denis Flaven
52a028301f Bug fix: FetchAssoc was broken when dealing with in-memory sets.
SVN:trunk[3340]
2014-09-16 09:45:39 +00:00
Romain Quetiez
61b88d2689 #989 Developper issue: query arguments having a null value are dismissed
SVN:trunk[3339]
2014-09-16 09:45:17 +00:00
Romain Quetiez
fa856c32cd #988 Could not change the case of a login
SVN:trunk[3338]
2014-09-16 08:04:37 +00:00
Romain Quetiez
6d693bbfc7 #778 Issue on list sort order when editing an element - fixed a regression
SVN:trunk[3335]
2014-09-15 16:06:46 +00:00
Romain Quetiez
7117b751b3 #778 Issue on list sort order when editing an element (N-N link tabs)
SVN:trunk[3322]
2014-09-15 13:40:56 +00:00
Romain Quetiez
c30a88c9ef #986 Search form: handle indirect external keys
SVN:trunk[3321]
2014-09-15 10:01:19 +00:00
Romain Quetiez
c1085fc398 #987 Usage login prevents from user deletion
SVN:trunk[3320]
2014-09-12 12:58:18 +00:00
Romain Quetiez
42ac871f8e #932 Search form should be prefilled when running a search "shortcut" - very little progress: fixed the case when several criteria are given
SVN:trunk[3319]
2014-09-12 09:37:25 +00:00
Romain Quetiez
b1a404d909 #985 Shortcut auto refresh degrading table cosmetics
SVN:trunk[3318]
2014-09-12 08:18:06 +00:00
Romain Quetiez
ba82031a59 #984 Dashboard auto refresh degrading table functionalities like sorting
SVN:trunk[3317]
2014-09-12 08:15:14 +00:00
Romain Quetiez
c8568af43b #976 The fix [3306] introduced a regression: Call to undefined function DBInsert()
SVN:trunk[3315]
2014-09-11 08:17:37 +00:00
Romain Quetiez
7ed60f711c mproved the processing of background task to enable more advanced functionalities like queuing (protection against reentrance)
SVN:trunk[3313]
2014-09-03 09:53:55 +00:00
Romain Quetiez
e831b1a486 #978 Email test utility always reporting "default SMTP port"
SVN:trunk[3312]
2014-09-01 09:09:32 +00:00
Romain Quetiez
b5db86472d Fixed bug in the dashboard forms.
SVN:trunk[3311]
2014-08-29 12:54:55 +00:00
Denis Flaven
ed9ba815ab #976: (removed some debug traces) make sure that we do not bypass the method that computes the reference for newly created tickets.
SVN:trunk[3307]
2014-08-28 15:56:25 +00:00
Denis Flaven
7b47e8c480 #976: make sure that we do not bypass the method that computes the reference for newly created tickets.
SVN:trunk[3306]
2014-08-28 15:53:02 +00:00
Romain Quetiez
d2cd758ecc Automatically add an id on the user rights profile/actions to allow a finer granularity for the deltas.
SVN:trunk[3305]
2014-08-28 15:52:05 +00:00
Denis Flaven
d2ea4023c2 Protect dashboards against invalid queries in "grouped by" dashlets.
SVN:trunk[3304]
2014-08-28 12:07:26 +00:00
Romain Quetiez
f0ee5112b9 Improved the processing of background task to enable more advanced functionalities like queuing (factorized the error handling)
SVN:trunk[3302]
2014-08-21 08:53:16 +00:00
Romain Quetiez
ada70b97d4 Improved the processing of background task to enable more advanced functionalities like queuing
SVN:trunk[3300]
2014-08-19 10:08:35 +00:00
Denis Flaven
a546b39301 Support of more sophisticated forms layout...
SVN:trunk[3299]
2014-08-16 16:17:12 +00:00
Denis Flaven
8f2cd66bf6 Declaration of generic methods which can be run on tickets.
SVN:trunk[3298]
2014-08-14 15:46:43 +00:00
Denis Flaven
868d0d1b19 Renamed the "
SVN:trunk[3297]
2014-08-13 15:04:42 +00:00
Denis Flaven
7d619f278e Added <inherit_flags_from> to inherit the flags from another state.
SVN:trunk[3296]
2014-08-13 15:04:25 +00:00
Denis Flaven
fd9008a163 New concept in the XML: HighlightScale to avoid overloading GetIcon and GetHilightClass...
SVN:trunk[3291]
2014-07-28 15:16:16 +00:00
Denis Flaven
357ae4abb1 Automatically add an id on the transitions to allow a finer granularity for the deltas.
SVN:trunk[3287]
2014-07-24 12:46:56 +00:00
Denis Flaven
2108a74f87 Added an identifier on the transitions to allow a finer granularity for the deltas.
SVN:trunk[3286]
2014-07-24 12:42:40 +00:00
Romain Quetiez
165d01e2a2 Legacy user rights management: allow the deletion of a profile in one step (it was nearly impossible because of the numerous related records, mainly of type URP_ActionGrant, for which iTop was requesting a manual deletion)
SVN:trunk[3277]
2014-07-16 10:48:08 +00:00
Denis Flaven
4c0e176f3e #960 [RPM Packaging] Adjust line endings in READ and LICENSE files
#962 [RPM Packaging] Added the use of logrotate for cron.log and error.log

SVN:trunk[3274]
2014-07-15 09:21:47 +00:00
Denis Flaven
f0f519e351 #959 Fixing licensing mismatches for compatibility with the Fedora licensing policy (the modification only affects comments) .
SVN:trunk[3273]
2014-07-15 08:16:31 +00:00
Denis Flaven
9a68890660 Integration of the additions to the Russian localization by Vladimir Kunin.
SVN:trunk[3272]
2014-07-15 08:02:49 +00:00
Denis Flaven
806ed2102c [RPM Packaging] Preparing for the 2.0.3 release.
- Added php-ldap to the list of dependencies

SVN:trunk[3271]
2014-07-14 18:54:04 +00:00
Denis Flaven
14b9778c50 [RPM Packaging] small fix in the install script to adjust the rights in /etc/httpd/conf.d
SVN:trunk[3270]
2014-07-14 18:49:27 +00:00
Denis Flaven
0142910c26 #958 [RPM Packaging] Integrated Igor's patch to support both Apache 2.2 and 2.4.
SVN:trunk[3269]
2014-07-14 17:26:04 +00:00
Romain Quetiez
0068d7c8be Rolling back on the exclusion of portal users from the REST/JSON API. Let's communicate on this future change and leave some time for integrators to get ready for the change.
SVN:trunk[3268]
2014-07-11 14:37:55 +00:00
Romain Quetiez
302811986e Update for the upcoming release 2.0.3
SVN:trunk[3267]
2014-07-11 14:23:11 +00:00
Romain Quetiez
f773433a93 New REST service: core/check_credentials to implement single sign-on solutions
SVN:trunk[3266]
2014-07-11 14:21:30 +00:00
Denis Flaven
880653f191 Fixed a regression introduced by [r3242] (warning when sending an email with an empty "To")
SVN:trunk[3265]
2014-07-11 13:27:13 +00:00
Romain Quetiez
02ee41ef09 Readme ready for the Release Candidate of 2.0.3
SVN:trunk[3264]
2014-07-10 13:00:42 +00:00
Denis Flaven
d6344568c7 Update of the SwiftMailer licence
SVN:trunk[3263]
2014-07-10 12:51:09 +00:00
Denis Flaven
38a2fd75c0 Prevent a warning when sending an email with no recipient in To:
SVN:trunk[3262]
2014-07-10 12:49:39 +00:00
Denis Flaven
7ca8c3e834 One small step for making the portal more generic...
SVN:trunk[3261]
2014-07-10 12:48:00 +00:00
Romain Quetiez
7d3a93c374 #955 Translated the label "Open Requests" in the welcome menu
SVN:trunk[3260]
2014-07-10 10:29:44 +00:00
Denis Flaven
a978bb9c17 CSV import: "Ignore this field" can be used more than one time in the mapping.
SVN:trunk[3259]
2014-07-10 08:44:52 +00:00
Denis Flaven
3ed01cde51 Fixed regression introduced by "Provide feedback while loading asynchronous tabs.", provide feeback ONLY when loading!
SVN:trunk[3258]
2014-07-10 08:34:01 +00:00
Denis Flaven
67471e2636 Fix for a warning about mysql_free() revealing a problem with DBObjectSet
SVN:trunk[3257]
2014-07-10 08:27:16 +00:00
Denis Flaven
4c44159a88 Global search finalization:
- Skip abstract classes, since we search in all the child classes anyway
- Search in a class explicitely requested even it should be skipped by config
- Remove the "no object found" message if the "enlarged search" finds something

SVN:trunk[3256]
2014-07-09 10:32:30 +00:00
Romain Quetiez
5f5125a131 #854...ROLLBACK, undoing the commit [3185]. The implementation has been postponed to the next major relase.
SVN:trunk[3255]
2014-07-09 09:49:02 +00:00
Denis Flaven
f6d0bda737 Properly render the "CONCAT_WS" SQL expression used by the global search.
SVN:trunk[3254]
2014-07-09 09:47:21 +00:00
Denis Flaven
a64b299644 "Portal Users" are not allowed to use the REST/JSON webservices. This case is now properly handled with a specific message.
SVN:trunk[3253]
2014-07-08 10:47:50 +00:00
Denis Flaven
e022bf03db Oops: Protect against non-existing parameters... (occurs when configuring a list)
SVN:trunk[3252]
2014-07-08 10:01:47 +00:00
Denis Flaven
bab6ca7954 Protection against a non existing UserLDAP class (in case the LDAP module is not installed)
SVN:trunk[3251]
2014-07-07 17:02:40 +00:00
Romain Quetiez
bb70884041 Readme file ready for 2.0.3 (RC), planned for the 15th
SVN:trunk[3250]
2014-07-07 15:55:27 +00:00
Denis Flaven
58cb67ecd3 Protect against non-existing parameters... (occurs when configuring a list)
SVN:trunk[3249]
2014-07-07 10:25:52 +00:00
Denis Flaven
4920cc4aee Fix in the log of the setup: Properly compute the percentage of "useless" records in priv_change.
SVN:trunk[3248]
2014-07-04 15:30:36 +00:00
Denis Flaven
12c587896e #927 Detect IE7 (or compatibility mode) and warn the user that the application cannot be used with such browsers.
SVN:trunk[3247]
2014-07-04 15:22:00 +00:00
Denis Flaven
eb74288c85 Added an index on "item_org_id" to speed-up the setup and the silo on organizations.
SVN:trunk[3246]
2014-07-04 14:15:24 +00:00
Denis Flaven
922049e8f2 Provide feedback while loading asynchronous tabs.
SVN:trunk[3245]
2014-07-04 13:18:47 +00:00
Denis Flaven
d83edb803b Better handling of the user's configured display limit for displaying the CSV import history. Also provide feedback while reloading.
SVN:trunk[3244]
2014-07-04 13:18:07 +00:00
Denis Flaven
9f41c7e577 Automatic cleanup of the table 'priv_change' before upgrading.
SVN:trunk[3243]
2014-07-04 13:09:27 +00:00
Denis Flaven
1335a15c30 Fixed a regression introduced by [r3141]: display of objects templates was broken.
SVN:trunk[3242]
2014-07-03 16:51:50 +00:00
Romain Quetiez
d847272264 Reworked the asynchronous mechanism to ease its extension (added a "status", preset by the setup)
SVN:trunk[3241]
2014-07-03 14:31:54 +00:00
Denis Flaven
8054d7b17a #954 Stricter validation of mandatory tags/values during compilation.
SVN:trunk[3240]
2014-07-03 12:26:04 +00:00
Romain Quetiez
1ba2ed809c Added a warning when upgrading the application while a CRON is being executed on the same DB
SVN:trunk[3239]
2014-07-03 07:11:43 +00:00
Denis Flaven
b2feca5eb5 #944 Better initialization of the 'origin' field based on localized comments.
SVN:trunk[3238]
2014-07-02 09:32:05 +00:00
Denis Flaven
87ce86880e Added Jean-François Bilger to the "Thank you" section
SVN:trunk[3237]
2014-07-01 17:03:05 +00:00
Denis Flaven
d4de0d2ee3 Fixed a regression introduced by [3188], which affects the Audit results and also potentially the User rights. Many thanks to Jean-Francois Bilger for finding the issue (and the associated fix) !
SVN:trunk[3236]
2014-07-01 16:40:38 +00:00
Denis Flaven
730f522ab2 #944 Automatically create an index on the 'origin' field.
SVN:trunk[3235]
2014-07-01 15:44:24 +00:00
Denis Flaven
008cac25b4 #944 Speed-up the display of CSV import history. IF the feature is enabled, the CSV import history is:
1) Displayed asynchronously
2) Based on a specific 'origin' enum fields instead of parsing the userinfo comment

SVN:trunk[3234]
2014-07-01 14:09:18 +00:00
Denis Flaven
ede552968b Added the (missing) field status to the details of VirtualDevice, Hypervisor and Farm.
Fixed two French translations.

SVN:trunk[3233]
2014-07-01 09:15:54 +00:00
Denis Flaven
686848a0ae #953 Protect the background processing against corrupted (incomplete) objects.
SVN:trunk[3231]
2014-06-30 15:24:49 +00:00
Denis Flaven
61b5b5cc71 #906 Better way to handle the lock in order to prevent duplicates in the numbering of Tickets. Note that the iTopMutex now supports re-entrancy inside the same PHP page.
SVN:trunk[3230]
2014-06-27 13:45:48 +00:00
Denis Flaven
ec16c5f86f #940 "Close" and "Reopen" buttons were not working on IE (due to extra commas at the end of JS lists)
SVN:trunk[3228]
2014-06-27 11:02:52 +00:00
Denis Flaven
d602bb2190 #948 Finalization of the full text accelerators: two new parameters: 'skip' and 'enable_enlarge'. 'attributes' is now an optional parameter.
SVN:trunk[3227]
2014-06-26 14:27:26 +00:00
Denis Flaven
1b9d3d3304 Applied again the patch [r3025] to support an empty TO: header when using the PHP Mail transport.
SVN:trunk[3226]
2014-06-26 12:16:26 +00:00
Denis Flaven
02e5d57998 #949 Upgraded SwiftMailer to the latest version (5.2.1)
SVN:trunk[3225]
2014-06-26 12:07:35 +00:00
Denis Flaven
6236d96a65 #951 Properly display localized values for ENUMs in the data model view.
SVN:trunk[3224]
2014-06-25 16:51:26 +00:00
Denis Flaven
6473a8f245 #892 Refresh the "multi-select" when picking a value with the "hierarchical picker", inside a search form.
SVN:trunk[3223]
2014-06-25 14:37:13 +00:00
Denis Flaven
ab93eaefff #916 Hierarchical selector: clicking on the label now has the same effect as clicking on the radio button itself. (The label is no longer an hyperlink)
SVN:trunk[3222]
2014-06-24 15:56:24 +00:00
Denis Flaven
037803c033 Protect attachments against concurrent edition of a ticket.
SVN:trunk[3221]
2014-06-24 10:03:34 +00:00
Denis Flaven
06582cfe35 Oops, fixed a typo in: Add the ability to supply a default "from" email address for the "forgot password" feature, instead of using the same address as for the "to".
SVN:trunk[3219]
2014-06-18 15:57:25 +00:00
Denis Flaven
a72e15023f Proper handling of the re-entrence of the Lock/Unlock within the same PHP page. Previously the code was sometimes performing 2 Unlock() for each Lock().
SVN:trunk[3217]
2014-06-18 15:24:31 +00:00
Romain Quetiez
7ddd74a95e Fix to complete the commit [3193]: template abusively selected on the user portal
SVN:trunk[3216]
2014-06-17 13:10:41 +00:00
Denis Flaven
4fccf5c815 Add the ability to supply a default "from" email address for the "forgot password" feature, instead of using the same address as for the "to".
SVN:trunk[3213]
2014-06-16 15:14:40 +00:00
Romain Quetiez
a6d984e23f Removed a debug trace introduced with the file drag&drop capability
SVN:trunk[3212]
2014-06-13 09:48:31 +00:00
Denis Flaven
7bf3f97a72 Release of the 2.0.3-beta, adjusting the date!
SVN:trunk[3211]
2014-06-13 09:29:23 +00:00
Denis Flaven
e78743d309 Fixed a regression introduced by #930: a field can depend on a hidden fields, but this has no visible effect.
SVN:trunk[3210]
2014-06-13 09:10:17 +00:00
Denis Flaven
6b86ac5090 Protect the optimization in case the object's friendlyname is requested.
SVN:trunk[3209]
2014-06-12 14:45:33 +00:00
Romain Quetiez
14c78cb543 Fixed a regression on the global search, introduced in the commit [3188]
SVN:trunk[3208]
2014-06-12 09:27:09 +00:00
Denis Flaven
5007e6ffc3 Updating the readme and the licence with the drag and drop feature.
SVN:trunk[3207]
2014-06-11 07:43:15 +00:00
Denis Flaven
1d4784b0c7 Allow uploading files as Tickets' attachments via drag and drop (on "modern" browsers supporting HTML5 drag and drop)
SVN:trunk[3206]
2014-06-10 15:05:02 +00:00
Denis Flaven
d497020733 Oups, version number back to: 1.0.5 = beta 6, with the correct collection of VLANs as DistributedVirtualPortgroups
SVN:trunk[3205]
2014-06-10 15:01:45 +00:00
Romain Quetiez
98afb2dd49 Datamodel version changed to 2.0.3
SVN:trunk[3204]
2014-06-10 14:14:27 +00:00
Romain Quetiez
f89792e171 Demo mode: the profiles must be in read-only mode (it is not enough to have the users in read-only)
SVN:trunk[3203]
2014-06-10 11:50:20 +00:00
Romain Quetiez
8141f8c619 Fixed the readme file for 2.0.3 Beta
SVN:trunk[3202]
2014-06-06 15:53:14 +00:00
Denis Flaven
2009a10204 Preparing the readme for iTop 2.0.3-beta.
SVN:trunk[3201]
2014-06-06 14:18:48 +00:00
Romain Quetiez
d2961c585e #926 JSON/REST Delete: nasty report
SVN:trunk[3200]
2014-06-06 09:50:57 +00:00
Denis Flaven
9b5fc043cb #943: Fix for supporting drop-down lists/auto-completes based on a parametrized query in the portal.
SVN:trunk[3199]
2014-06-06 09:43:20 +00:00
Romain Quetiez
67ef671632 #865 CSV import : mapping several columns to the same fields gives an error. Fix: the wizard does not allow the user to move forward.
SVN:trunk[3198]
2014-06-05 14:42:53 +00:00
Romain Quetiez
d07ca49e53 #636 and #861 Set the focus on User Name in iTop Login Form
SVN:trunk[3197]
2014-06-05 09:48:59 +00:00
Denis Flaven
44aff84dfa #934: Support the <display_style> option for ExternalKeys.
SVN:trunk[3196]
2014-06-05 09:19:15 +00:00
Romain Quetiez
c9ffdb9342 Cleanup: the model.x file will be rebuilt from the xml file anyway!!!
SVN:trunk[3195]
2014-06-04 14:51:36 +00:00
Romain Quetiez
65343a485f Completed the portuguese translation (Brazil), provided in december... provided some time ago (by whom?) - modules updated: attachments, change, incident, request and request/ITIL, service for providers
SVN:trunk[3194]
2014-06-04 14:29:22 +00:00
Romain Quetiez
74cda2831f Portal + templates: Bug fix = when the user selects a template, then go back to select a service for which no template applies, he still gets the tempate fields in the final form.
SVN:trunk[3193]
2014-06-04 13:37:58 +00:00
Romain Quetiez
c4632cda1a Added a helper function to get an icon stored as an ormDocument: ormDocument::GetDownloadURL
SVN:trunk[3191]
2014-06-04 13:10:23 +00:00
Romain Quetiez
d7ba4166e5 #919 Circular references between tickets (parent/child). Protect the framework against infinite recursions on cascaded updates (done at the DBUpdate level).
SVN:trunk[3190]
2014-06-03 18:05:31 +00:00
Romain Quetiez
ee3795ef6d Full text search shortcuts: allow the use of class names containing underscores and numbers (e.g. Processus métier: écarissage)
SVN:trunk[3189]
2014-06-03 17:57:53 +00:00
Romain Quetiez
e8a7695353 #731 Full text search requires a string of at least three characters (configurable: full_text_needle_min)
SVN:trunk[3188]
2014-06-03 14:42:17 +00:00
Romain Quetiez
e4bb0acd94 #936 Tune the default (i.e. implicit) tracking level on link sets (and disable tracking on 1-N links, for fresh installations)
SVN:trunk[3187]
2014-06-03 13:36:35 +00:00
Denis Flaven
1572d9da5a #930 AttributeExternalFields displayed in a form are automatically refreshed when their "parent" field is modified...
SVN:trunk[3186]
2014-06-02 16:05:20 +00:00
Romain Quetiez
2d0ca37f27 #854 Flag Is null allowed not working on attributes Date and DateTime + the default value is now operant
SVN:trunk[3185]
2014-06-02 14:35:28 +00:00
Romain Quetiez
df09182878 Oooops... completes the commit 3175 (#929 Speed up the full text search)
SVN:trunk[3184]
2014-06-02 14:22:14 +00:00
Denis Flaven
20bb9a62bb #935 Better support of CheckToWrite() in object's transitions, improved by checking the data sooner for a consistent workflow.
SVN:trunk[3183]
2014-06-02 13:16:37 +00:00
Romain Quetiez
a6f5436d07 #873 Allow the character % in the path of an URL (requires the edition of the config file when upgrading)
SVN:trunk[3182]
2014-06-02 10:23:06 +00:00
Denis Flaven
53e58d5887 #872 Support notifications for the creation of a new user. Also fix the translation of the "Additional values" in ValueSetEnumClasses.
SVN:trunk[3181]
2014-05-30 17:11:37 +00:00
Denis Flaven
ef1178c78d #935 Better support of CheckToWrite() in object's transitions
SVN:trunk[3180]
2014-05-30 16:43:14 +00:00
Denis Flaven
5456d9d20a #933 Search form for Query Phrasebook items. If you implement your own menus (equivalent to itop-welcome-itil module), make sure that you update the menu definition to show the search form at the top.
SVN:trunk[3179]
2014-05-30 07:42:17 +00:00
Denis Flaven
7c8ff071d2 #931 Management of n:n links can be broken in case of insufficient user rights. Side effect: attribute_linkedset with the flag OPT_ATT_HIDDEN are now completely hidden (the tab is not displayed at all).
SVN:trunk[3178]
2014-05-28 16:37:25 +00:00
Romain Quetiez
e4010b4b13 #886 Delete change history so that if an ID is reused the history starts from scratch (and cleanup most of the data as soon as the object is deleted)
SVN:trunk[3177]
2014-05-28 16:00:24 +00:00
Denis Flaven
dbb9558b45 #862: Popup menu misplaced when the window scrolls (e.g. when displaying large lists of results)
SVN:trunk[3176]
2014-05-28 15:22:57 +00:00
Romain Quetiez
8f83970239 #929 Speed up the full text search (mostly from the end user perspective, requires a custom configuration)
SVN:trunk[3175]
2014-05-26 15:42:47 +00:00
Romain Quetiez
102b6c248b #928 Setup crashing if async_retries is configured
SVN:trunk[3174]
2014-05-26 14:45:07 +00:00
Denis Flaven
946b4212d3 Fix of a regression introduced by [3146]: due to formatting rules, empty friendlynames may not look like empty strings!
SVN:trunk[3172]
2014-05-23 15:49:30 +00:00
Denis Flaven
e2e6861b03 Properly handle external and basic authentication methods for REST web services.
SVN:trunk[3170]
2014-05-23 13:53:20 +00:00
Denis Flaven
7b7e69a890 Properly optimize the columns to load, when subitems are requested.
SVN:trunk[3168]
2014-05-23 13:08:31 +00:00
Romain Quetiez
92b2131d3b #483 Added placeholders for the notifications: html(caselog), head_html(caselog), html(linkset). The HTML can be customized. Fixes the issue about lines being wrapped in a curious way (root cause: swift mailer).
SVN:trunk[3167]
2014-05-23 12:32:08 +00:00
Romain Quetiez
43130aef71 Allow the use of any character into the help text on an attribute (usefull to explain a constraint implemented as a regular expression for instance.) Reminder: the text is given as a dictionary entry named like "Class:<class>/Attribute:<attcode>?"
SVN:trunk[3166]
2014-05-23 08:45:36 +00:00
Romain Quetiez
ba4e5ec786 #925 Completes the commit [3163], the link sets were still not correctly handled
SVN:trunk[3164]
2014-05-22 13:23:47 +00:00
Romain Quetiez
3e79dad435 #925 REST/JSON: Added an option to output all the fields of the object found (not only the fields of the queried class)
SVN:trunk[3163]
2014-05-20 08:15:53 +00:00
Romain Quetiez
b0a84f96f1 #897 Rest Services: improved the error reporting when an external key is specified with a final class that is not a subclass of the class of the external key
SVN:trunk[3162]
2014-05-19 15:02:42 +00:00
Romain Quetiez
85fa578f2a #876 Upgrade finishes with error "Cannot reload object id = -1" (root cause: DB in read-only mode, see config/access_mode)
SVN:trunk[3161]
2014-05-19 14:23:52 +00:00
Romain Quetiez
364bce90e4 #859 About box: also list the modules installed from the extensions folder
SVN:trunk[3160]
2014-05-19 11:43:45 +00:00
Romain Quetiez
fcfc1e7307 #875 Could not use OQL queries with a double quote in the condition
SVN:trunk[3159]
2014-05-19 08:28:39 +00:00
Romain Quetiez
85cb04a3f3 Fixed a compiler error message (wrong syntax when using a PHP class to implement the class methods)
SVN:trunk[3158]
2014-05-19 07:28:26 +00:00
Denis Flaven
ec77e58276 Limit the display of the status to the latest 100 runs of the synchro data source.
SVN:trunk[3157]
2014-05-16 05:02:38 +00:00
Denis Flaven
196fba7d81 Implement the iDisplay interface on any class derived from DBObject, but also limit the possible actions on such objects (disable edition)
SVN:trunk[3156]
2014-05-16 05:01:45 +00:00
Denis Flaven
317344da2e Proper output of boolean values in JSON.
SVN:trunk[3155]
2014-05-16 04:58:45 +00:00
Romain Quetiez
cd37a78800 Fixed a regression due to the optimization of the memory usage of DBObjectSet [3142]
SVN:trunk[3154]
2014-05-15 13:54:45 +00:00
Denis Flaven
9ed5ceb11e Fix for a regression introduced by [3149]
SVN:trunk[3153]
2014-05-14 13:47:40 +00:00
Denis Flaven
f47327fdd4 Second fix for a regression due to revisions 3141/3150
SVN:trunk[3152]
2014-05-14 13:24:19 +00:00
Denis Flaven
5ec37c8060 Fix for a regression due to revisions 3141/3150
SVN:trunk[3151]
2014-05-14 13:16:41 +00:00
Denis Flaven
bb65153351 Fixed regression introduced by [3141]: Code cleanup to implement the tabs handling (inside web pages) in one place.
SVN:trunk[3150]
2014-05-07 13:14:19 +00:00
Denis Flaven
389b97dc50 Two experimental perf. enhancements:
- maintain list the attributes (potentially) modified to speed-up ListChanges() by avoiding a systematic comparison between the content of linkedsets.
- cache the list of SynchroDataSources and use this in InSyncScope() to avoid searching in the SynchroReplicas when it's not needed...

Depending on the configuration, these optimizations may speed-up the CSV import by up to 40% !!

SVN:trunk[3149]
2014-05-06 15:45:04 +00:00
Denis Flaven
31e30810f6 Experimental perf. enhancement: cache the foreign keys to use when importing object to avoid searching for the same object several times during a given import. Seem to speed up the imports by 7 to 10%.
SVN:trunk[3148]
2014-05-06 15:39:01 +00:00
Denis Flaven
4bffe7aec9 Oops, removing a debug trace...
SVN:trunk[3147]
2014-05-06 15:34:26 +00:00
Denis Flaven
fafa442b08 Avoid using an attribute which may cause a Reload()... seems useless anyway.
SVN:trunk[3146]
2014-05-06 15:25:00 +00:00
Denis Flaven
b095c6b1a3 #909: faster display for the "details" of an object:
- object's history is only loaded when the "History" tab is clicked
- by default the history display is truncated to the 'max_history_length' (= 50) latest modifications

SVN:trunk[3145]
2014-05-06 15:15:57 +00:00
Denis Flaven
d950422912 #867 (and #907 as a dup') De-harcode set_time_limit (per loop) in lengthy operations. Default value is 30 seconds (per loop), configurable via the new parameter "max_execution_time_per_loop", instead of 5 seconds previously.
SVN:trunk[3144]
2014-05-06 14:09:55 +00:00
Denis Flaven
b504830f45 CSV export (from the toolkit menu) now displays an asynchronous page, to better cope with a huge number of objects (> 10000)
SVN:trunk[3143]
2014-05-06 13:54:45 +00:00
Denis Flaven
4dabb566a8 - Memory optimization: no longer store all DBObjects in memory while browsng through a Set, but pull them one by one from the MySQL client buffer as needed.
- Also renamed Merge to Append since it's really what it does (seems to be used only in the tests)

SVN:trunk[3142]
2014-05-06 13:46:45 +00:00
Denis Flaven
7459ec4844 Code cleanup to implement the tabs handling (inside web pages) in one place. Added the ability to provide asynchronously loaded tabs (content must come from the same server).
SVN:trunk[3141]
2014-05-06 13:42:18 +00:00
Denis Flaven
678f982024 #923: prevent XSS injection in forgot password page.
SVN:trunk[3139]
2014-05-06 08:26:54 +00:00
Denis Flaven
d628c4f670 Use the object oriented verison of the MySQLi API which seems free of memory leaks (compared to the procedural version of the same API).
SVN:trunk[3138]
2014-05-02 14:00:42 +00:00
Denis Flaven
e1336d7ebc Run Query enhancements
- Properly catch *all* exceptions and redisplay the entered OQL statement every time
- Post the form to force its refresh (i.e. running the query again) even if the query did not change

SVN:trunk[3137]
2014-05-02 13:30:08 +00:00
Romain Quetiez
01496f9595 #918 TTO/TTR status "passed" gets reset when the stop watch is stopped (using the status "triggered" instead)
SVN:trunk[3130]
2014-04-24 08:53:17 +00:00
Denis Flaven
a60d60bfab Better handling of the default choices in case of upgrade (for some specific configurations of the installation wizard).
SVN:trunk[3127]
2014-04-15 13:01:06 +00:00
Romain Quetiez
31cb0065b5 Integrated the portal with the module to dispatch incidents or user requests (the previous commit introduced too many changes, I did rollback unnecessary stuff)
SVN:trunk[3125]
2014-04-11 14:53:36 +00:00
Romain Quetiez
4dbc5d97b8 Integrated the portal with the module to dispatch incidents or user requests
SVN:trunk[3124]
2014-04-11 14:42:57 +00:00
Romain Quetiez
4800da1653 #913 Error when searching for child requests and no organization is specified. Still, I could not figure out WHY IT WAS WORKING WHEN AN ORG IS SELECTED as a search filter!
SVN:trunk[3123]
2014-04-09 14:00:09 +00:00
Denis Flaven
8f25fb8e64 #878: Missing sscrollbar in "linkset-direct" edition popup dialog
#881: Paginated list in popup dialog is broken
- Missing scrollbar in the popup when using the [+] button

SVN:trunk[3121]
2014-04-01 13:09:02 +00:00
Denis Flaven
b43884a760 Object's edition: keep track of what was typed in the case log fields when reloading the form (for example with a different "initial state")
SVN:trunk[3119]
2014-04-01 12:06:35 +00:00
Denis Flaven
caef02720c Protect Bulk Modify against XSS injection!
SVN:trunk[3117]
2014-04-01 10:12:11 +00:00
Denis Flaven
ce9806b01c Allow re-entrance in the same named mutex within the same PHP page.
SVN:trunk[3115]
2014-04-01 09:50:21 +00:00
Romain Quetiez
7a193d1c24 Fixed issue with 1.x datamodels: dashlets of type "badge" not working (preventing from editing an existing dashboard), since 2.0.2
SVN:trunk[3113]
2014-04-01 09:14:06 +00:00
Romain Quetiez
31ea53435e Aligned the authentication module with the one of 2.x, to enable the feature "Forgot password" for legacy data models
SVN:trunk[3111]
2014-04-01 08:31:08 +00:00
Denis Flaven
dcb48d0f35 Bug fix : missing semicolons were causing an error with IE9.
SVN:trunk[3107]
2014-03-26 14:58:32 +00:00
Romain Quetiez
40aa78bb3d Finalized the French transalation for some types of "Triggers"
SVN:trunk[3106]
2014-03-24 10:21:09 +00:00
Romain Quetiez
82e9e42939 #901 Added the attribute "filter" to the triggers
SVN:trunk[3104]
2014-03-21 10:52:20 +00:00
Romain Quetiez
1ecec1dd6d #905 The toolkit menu was visible in the portal for Administrators (but it was not usable). It is now hidden in any case.
SVN:trunk[3103]
2014-03-20 10:27:13 +00:00
Denis Flaven
8dc5b05ac4 #870: when a user deletes all her/his shortcuts at once, this was deleting all the shortcuts for all users.
SVN:trunk[3099]
2014-03-12 14:05:37 +00:00
Denis Flaven
3b257eeb3a Templates processing aligned with "templates-base" 2.1.1: allow template fields with the same name the attribute code of the curent object.
SVN:trunk[3097]
2014-03-11 14:03:14 +00:00
Denis Flaven
2574a0c8a4 #887 short term fix for preventing ToArgs to alter the content of an object...
SVN:trunk[3095]
2014-03-04 13:58:28 +00:00
Romain Quetiez
404f6772fd #896 XSS injection on the portal (any search form)
SVN:trunk[3093]
2014-03-03 14:46:48 +00:00
Denis Flaven
d6dbe0fce7 Trac #890: Dispatch the defines in the proper modules to make sure that the portal works with all possible combinations of tickets.
SVN:trunk[3092]
2014-03-03 13:48:56 +00:00
Romain Quetiez
8e26ca763b Internal: modified the prototype of GetDelta to add several attributes on the root node
SVN:trunk[3090]
2014-02-26 11:24:10 +00:00
Denis Flaven
b4bc3ad716 Trac #891: better error reporting when either the parameter auth_user or auth_pwd are missing.
SVN:trunk[3089]
2014-02-25 13:37:01 +00:00
Denis Flaven
80fac28106 Trac #890: Dispatch the defines in the proper modules to make sure that the portal works with all possible combinations of tickets.
SVN:trunk[3088]
2014-02-25 13:17:25 +00:00
Romain Quetiez
5f11c97aef #888 Security on the portal incompatible with customizations (regression introduced in 2.0.2), now requires to define PORTAL_USERREQUEST_DISPLAY_QUERY and PORTAL_USERREQUEST_DISPLAY_POWERUSER_QUERY
SVN:trunk[3086]
2014-02-24 15:52:04 +00:00
Denis Flaven
d36a03bfc3 Make the Basic Authentication (login_mode=basic) work with non-ASCII characters (in the username as well as in the password), though this may depend on the browser...
SVN:trunk[3084]
2014-02-19 17:34:53 +00:00
Denis Flaven
8e0c57fce0 Add a new flag "debug" (false by default) to turn off the debug traces of the module since the traces contain potentially sensitive information in clear text.
SVN:trunk[3083]
2014-02-14 17:51:50 +00:00
Romain Quetiez
cb4c2a8e84 Internal: Manage nested subforms in property sheet edition mode
SVN:trunk[3082]
2014-02-12 14:14:01 +00:00
Denis Flaven
f993f07751 Enhanced API to support icon upload...
SVN:trunk[3081]
2014-02-10 15:41:32 +00:00
Romain Quetiez
0167a66973 Demo mode: disable the pin button on the left pane (and keeps it open and resizable)
SVN:trunk[3080]
2014-02-10 13:21:21 +00:00
Romain Quetiez
b7d8953ecb Fix for Plugins: fixed a regression introduced in 3072 (dashboards not editable at all!!!)
SVN:trunk[3079]
2014-02-06 16:27:22 +00:00
Romain Quetiez
cba75527b3 #877 REST/JSON More flexibility on case log updates (in particular, it is now possible to write the entire case log), remains compatible with the previous API
SVN:trunk[3078]
2014-02-06 11:40:26 +00:00
Denis Flaven
bc0f48721b Bug fix: the JSON value for an enum should be the raw value, not its translated label.
SVN:trunk[3077]
2014-02-06 09:22:35 +00:00
Denis Flaven
a6693d9535 Protect the initialization with a try ... catch, in order to protect that rest of the page in case of trouble.
SVN:trunk[3074]
2014-02-05 17:08:33 +00:00
Romain Quetiez
6f2c404415 Fix for Plugins: if a page uses set_base then JS popup menu items were reloading the page. Still, set_base should not be used!
SVN:trunk[3072]
2014-02-04 16:04:50 +00:00
Romain Quetiez
b00aae2536 Enabled KPI tracing for the export page... fixed regression on the setup page (rewrote the change in a much more defensive way)
SVN:trunk[3071]
2014-02-04 14:20:20 +00:00
Romain Quetiez
6334370ef0 Optimization: map the extended attribute code to the corresponding external field when this if possible (ex: org_id->name to org_name); this reduces the number of queries, in particular when using the "export CSV" menu on a list.
SVN:trunk[3069]
2014-02-03 14:35:05 +00:00
Romain Quetiez
868748efb3 Enabled KPI tracing for the export page
SVN:trunk[3067]
2014-02-03 14:26:19 +00:00
Romain Quetiez
c67e7e18e6 Optimize the queries for the export page
SVN:trunk[3066]
2014-02-03 14:23:55 +00:00
Romain Quetiez
16f1fd56ec Resetting the stop watch...do clean the first start date when it is not running!
SVN:trunk[3065]
2014-02-03 14:22:37 +00:00
Romain Quetiez
1cab84e793 Allow to reset a running stop watch (without stopping it!)
SVN:trunk[3063]
2014-02-03 11:56:22 +00:00
Denis Flaven
991fe9ccc1 Compatibility with APCu (For PHP 5.5+), since it slightly different from APC.
SVN:trunk[3061]
2014-01-22 15:18:48 +00:00
Denis Flaven
0a53f8ec3f Preserve "hidden" template fields.
SVN:trunk[3060]
2014-01-22 14:09:53 +00:00
Denis Flaven
cd7af7a2ce Dictionary string for the portal should not depend on a module
SVN:trunk[3059]
2014-01-22 14:08:34 +00:00
Denis Flaven
319b3b81ce - Put back support of templates
- Make sure that unwanted parameters cannot be set when creating the ticket

SVN:trunk[3058]
2014-01-21 10:47:45 +00:00
Denis Flaven
4235eae9b3 #871: eMail validation pattern was too strict: now fully configurable (globally and per attribute).
SVN:trunk[3056]
2014-01-17 09:39:19 +00:00
Romain Quetiez
32ec19e09d #869 REST JSON was not outputing case log attributes (implemented in a structured way)
SVN:trunk[3054]
2014-01-15 11:04:25 +00:00
Denis Flaven
63ea142168 Record the very same installation time for all modules.
SVN:trunk[3052]
2014-01-03 14:00:44 +00:00
Denis Flaven
23ec4faa65 Test commit: bumped the year to 2014.
SVN:trunk[3051]
2014-01-02 18:02:39 +00:00
Denis Flaven
da36fc673e Test commit: just bumping the copyright year
SVN:trunk[3050]
2014-01-02 17:59:39 +00:00
Romain Quetiez
3b65f33325 Internal: correctly quote XPath literals within GetNodeById
SVN:trunk[3049]
2013-12-17 14:52:20 +00:00
Romain Quetiez
e980b051b1 Asynchronous emails: added a retry mechanism useful in case your SMTP server restricts the number of emails that can be sent over a period of time (usage: broadcasting a newsletter). The mechanism is not specific to sending email as it is implemented at the AsyncTask level.
SVN:trunk[3047]
2013-12-13 10:35:37 +00:00
Denis Flaven
c84a22c503 #856: allow asynchronous emails to have an empty 'to' recipient... (not used anyway)
SVN:trunk[3043]
2013-12-12 18:02:32 +00:00
Denis Flaven
5c2578169e Add "finalclass" as a reconciliation key on all abstract classes derived from FunctionalCI
SVN:trunk[3040]
2013-12-11 10:08:23 +00:00
Romain Quetiez
645731a76d Integrated an update of the portuguese (brazil) localization, made by Marco Tulio
SVN:trunk[3039]
2013-12-11 09:38:15 +00:00
Denis Flaven
3de2d654a0 Protection against attemp to delete a non-existing node in the XML...
SVN:trunk[3038]
2013-12-10 16:43:22 +00:00
Romain Quetiez
934e500253 Setup: fixed issue when upgrading a DB (no install dir specified, thus no config file) and requesting a backup: the backup is created without the config file in it.
(Updated the readme for the upcoming release!)

SVN:trunk[3037]
2013-12-10 15:47:33 +00:00
Romain Quetiez
cfd2a7baff Readme file updated for the (soon) upcoming release 2.0.2
SVN:trunk[3036]
2013-12-10 15:01:34 +00:00
Romain Quetiez
1867195c25 Portal: Removed the public log for the user request creation form (still available on the ticket details). That was already done in change [2828] for ITIL flavour.
SVN:trunk[3035]
2013-12-10 14:58:22 +00:00
Romain Quetiez
d4bcb9dff8 Regression of 2.0.2 beta: Configure this list + discard all columns: the list cannot be loaded anymore (if saved)
SVN:trunk[3034]
2013-12-10 13:48:32 +00:00
Romain Quetiez
4172cb2023 Readme file updated for the (soon) upcoming release 2.0.2
SVN:trunk[3033]
2013-12-10 12:48:10 +00:00
Denis Flaven
ebff827013 #805 (again): proper fix to avoid blocking when creating a ticket with DBInsert() (instead of DBInsertNoReload !!)
SVN:trunk[3032]
2013-12-10 11:07:24 +00:00
Denis Flaven
1afcf46970 #805: fix the issue when creating tickets from the SOAP web service.
SVN:trunk[3031]
2013-12-10 10:16:40 +00:00
Romain Quetiez
2e37ccc4c2 #770 and #853: When a list is configured directly from the table (NOT from the Dialog box), then these settings are not kept neither when creating a shortcut nor when opening the "configure this list" dialog
SVN:trunk[3030]
2013-12-09 20:38:05 +00:00
Romain Quetiez
3b188524ca #770 Complete the fix implemented in [2990], the configuration is kept even if not saved for the list
SVN:trunk[3029]
2013-12-09 15:55:11 +00:00
Denis Flaven
bd1096b0fc #756: allow incidents and user request to be linked to 'closed' problems.
SVN:trunk[3028]
2013-12-09 12:57:55 +00:00
Denis Flaven
d42443697c Security enhancements:
- ensure that a user can ony see the details of the ticket she/he is allowed to see, even if the id is typed manually
- add a define'd filter to filter the drop-down lists of the search form for searching closed tickets.

SVN:trunk[3027]
2013-12-09 11:39:25 +00:00
Romain Quetiez
8509237084 #851 Ampersand (aka '&') not welcomed in the setup wizard
SVN:trunk[3026]
2013-12-09 10:52:21 +00:00
Romain Quetiez
f16d1ee1e4 PHP Mail transport to allow 100% of recipients in BCC (proposed on github)
SVN:trunk[3025]
2013-12-05 12:06:30 +00:00
Romain Quetiez
5672bee85f Regression introduced with the capability to disable mandatory ext keys (defaults to true)
SVN:trunk[3024]
2013-12-04 16:15:26 +00:00
Denis Flaven
4d6ddb8586 Fixes to the packaging after a first test on CentOS
SVN:trunk[3023]
2013-12-03 15:21:22 +00:00
Denis Flaven
a71b3bc231 Protects the setup in case of missing PHP-JSON module
SVN:trunk[3022]
2013-12-03 11:47:42 +00:00
Romain Quetiez
723d51a871 Readme file updated for the upcoming release
SVN:trunk[3021]
2013-12-03 11:07:59 +00:00
Denis Flaven
4e1c3f321f First step toward a generic Linux packaging supporting both DEBs and RPMs...
SVN:trunk[3020]
2013-12-03 11:04:33 +00:00
Denis Flaven
2b58bca313 Use jQuery.inArray() instead of array.indexOf() to be compatible with IE8
SVN:trunk[3019]
2013-12-03 10:50:38 +00:00
Denis Flaven
9b1d383848 Spanish translation contributed by Miguel Turrubiates
SVN:trunk[3018]
2013-12-03 10:22:30 +00:00
Romain Quetiez
96c1ec42ed Internal: API to build the SQL statements to (re)-create an object
SVN:trunk[3017]
2013-12-03 09:59:42 +00:00
Romain Quetiez
7cb2fb9b02 Internal: new setting to tweak the data model by allowing null for all external keys (use with care!)
SVN:trunk[3016]
2013-12-03 09:58:29 +00:00
Romain Quetiez
6f90d626fc Code refactoring: MakeSelectFilter
SVN:trunk[3015]
2013-12-03 09:56:46 +00:00
Romain Quetiez
62302f9138 Reverted the change made in revision 2999: revision 3000 does protect against the annoying notice... and everything seems working fine... still to be tested with PH >=5.4
SVN:trunk[3014]
2013-12-03 08:42:48 +00:00
Romain Quetiez
278cb653db Fixed regression in the Notifications page (leading to a conflict between the two tabs)
SVN:trunk[3013]
2013-12-03 08:33:54 +00:00
Romain Quetiez
9f9baf9caa Finalized the about box
SVN:trunk[3012]
2013-11-29 16:18:01 +00:00
Denis Flaven
09ebce2587 Transparent background
SVN:trunk[3011]
2013-11-29 16:08:39 +00:00
Denis Flaven
3c8cf0e8fb Bug fix: duplicate ID was used for the VLAN badge
SVN:trunk[3010]
2013-11-29 15:13:52 +00:00
Romain Quetiez
5542cfd79e Module refactoring: Configuration management must not require the installation of a Service Management module
SVN:trunk[3009]
2013-11-29 14:57:34 +00:00
Denis Flaven
60e7c22ab4 #838 following on the bug fix: more standard naming for the parameters of the query
SVN:trunk[3008]
2013-11-29 11:38:51 +00:00
Denis Flaven
92502a7a88 bug fix: Allow Support Agents to perform "wait for approval" on a UserRequest ticket.
SVN:trunk[3007]
2013-11-29 11:28:39 +00:00
Romain Quetiez
47c65b161d Fixed bug with the brand new about box
SVN:trunk[3006]
2013-11-29 11:23:51 +00:00
Romain Quetiez
5f98c0dcab About box - alpha version
SVN:trunk[3005]
2013-11-29 10:50:53 +00:00
Denis Flaven
9e4b25e833 #849: fix for the special case of loading class tags into a hierarchy
SVN:trunk[3004]
2013-11-28 17:15:09 +00:00
Denis Flaven
4b095738d5 #849: fix for the special case of loading class tags into a hierarchy
SVN:trunk[3003]
2013-11-28 16:28:01 +00:00
Romain Quetiez
fa615638d9 Defensive programming: protected against the Notice "array to string conversion" that appears in PHP 5.4 (thus not on every systems)
SVN:trunk[3002]
2013-11-28 09:55:39 +00:00
Romain Quetiez
a24b4437aa #816 Completed the fix (still a Notice)
SVN:trunk[3001]
2013-11-28 09:52:33 +00:00
Romain Quetiez
9f95d951d4 #825 and #830: removed annoying Notices (array to string conversion)
SVN:trunk[3000]
2013-11-28 09:44:21 +00:00
Romain Quetiez
7400bd7dca #816 Suppresses a Notice for PHP >= 5.4, and potentially fixes a bug (no idea on how to produce it)
SVN:trunk[2999]
2013-11-27 13:29:32 +00:00
Romain Quetiez
258b4be167 #830 (continuation: suppressed the notice "array to string conversion"
SVN:trunk[2998]
2013-11-27 09:20:16 +00:00
Denis Flaven
4a849ee4db #764: Saving settings as "Default for all lists" now works as expected
SVN:trunk[2997]
2013-11-27 08:51:51 +00:00
Erwan Taloc
18664c8151 Fix bug #757
SVN:trunk[2996]
2013-11-26 22:00:00 +00:00
Erwan Taloc
85472fe67a Fix bug #837 Remove [+] button on external keys where it's not meaningful
+ button had been removed for all ExternalKey having a complex filter defined

SVN:trunk[2995]
2013-11-26 21:45:54 +00:00
Erwan Taloc
e1087d3f87 Fix bug #835 Remove dependency between ticket and Delivery model
SVN:trunk[2994]
2013-11-26 21:19:11 +00:00
Erwan Taloc
e789c6baec Fix enhancement request #834 Add IP for Virtual Machine
SVN:trunk[2993]
2013-11-26 21:06:56 +00:00
Erwan Taloc
817cc0476a Fix bug #824 Change ranking in CI Overview dashboard
SVN:trunk[2992]
2013-11-26 20:55:59 +00:00
Erwan Taloc
ea5908ac41 Fix bug #836 Adding linkset "applicationsolution_list" in details list
SVN:trunk[2991]
2013-11-26 20:51:48 +00:00
Romain Quetiez
f5d42b95b8 Code cleanup
SVN:trunk[2990]
2013-11-26 15:59:22 +00:00
Romain Quetiez
d7093a9a6f #770 Preserve list configuration (if dedicated) when creating a shortcut
SVN:trunk[2989]
2013-11-26 15:58:05 +00:00
Denis Flaven
7636b987b1 #823: proper figures for the CSV import "confirmation" pie chart.
SVN:trunk[2988]
2013-11-26 14:28:22 +00:00
Romain Quetiez
55f1763b60 #830 ... completes the change 2980 (that accompanied changes 2921 and 2948, for an optimization on bulk operations)
SVN:trunk[2987]
2013-11-26 14:27:24 +00:00
Denis Flaven
87e33c72b5 #775: preserve the open/closed status of the search form during auto-reload... by not reloading the form at all!
SVN:trunk[2986]
2013-11-26 14:19:13 +00:00
Denis Flaven
99695a0fc1 #841: properly select the current tab
SVN:trunk[2985]
2013-11-26 13:54:17 +00:00
Denis Flaven
0aa0de9f1c Make sure that we attach ONLY the elements that are visible in the form when using the "select all" mode for managing 1:n links.
SVN:trunk[2984]
2013-11-26 11:16:11 +00:00
Denis Flaven
ebe89b0af7 #839: Managing n:1 links asked to leave the page (under Chrome!)
SVN:trunk[2983]
2013-11-26 10:39:56 +00:00
Denis Flaven
74f895b5f4 #838: the OQL query for getting the SLTs is now a constant defined in the XML
SVN:trunk[2982]
2013-11-26 10:03:53 +00:00
Romain Quetiez
9bc5406abb #842 Internal: recode the notifications page to allow several types of actions
SVN:trunk[2981]
2013-11-26 09:39:34 +00:00
Romain Quetiez
dd1cf43d41 #830 Regression introduced in the beta. Related to the management of query arguments
SVN:trunk[2980]
2013-11-25 15:10:09 +00:00
Romain Quetiez
b62b9caaf2 #829 Corrupted pwd when attempting to create an account without any profile
SVN:trunk[2979]
2013-11-25 09:01:35 +00:00
Romain Quetiez
36149df584 Extensibility: make sure that checks implemented in an overload of CheckToWrite will be seen when parent::CheckToWrite is invoked at the end of the overload
SVN:trunk[2978]
2013-11-21 16:30:58 +00:00
Romain Quetiez
e48716753d Optimizations: when displaying a "short" list, made of objects having the same status, three additional queries were made (now just one Group By query is made)
SVN:trunk[2977]
2013-11-21 16:15:37 +00:00
Romain Quetiez
8c702a42e9 #827 Default language given at setup not taken into account (login page always in english)
SVN:trunk[2976]
2013-11-21 15:33:09 +00:00
Romain Quetiez
494e559748 Improved the KPI logging: setting log_kpi_duration to 2 will enable blaming of the FIRST caller (callstack entirely shown in the produced report)
SVN:trunk[2975]
2013-11-20 17:03:04 +00:00
Romain Quetiez
a1801e53a2 Got rid of an unnecessary query (responsible for 8% of the time spent in a standard iTop page!!!)
SVN:trunk[2974]
2013-11-20 16:16:19 +00:00
Romain Quetiez
f856859f83 #832 Typos in German (tickets overview)
SVN:trunk[2973]
2013-11-19 13:16:10 +00:00
Denis Flaven
7ebce0a841 Improved version of DoPostRequest which optionally uses cURL to workaround PHP/OpenSSL bugs !
SVN:trunk[2972]
2013-11-07 15:03:01 +00:00
Romain Quetiez
3f50d3ea59 Helper for HTTP POSTs: can return the headers if requested
SVN:trunk[2971]
2013-11-06 15:29:15 +00:00
Denis Flaven
898c235c0d Fix for the validation of the forms in the portal. The (lack of) localization was breaking it!
SVN:trunk[2970]
2013-10-29 16:26:46 +00:00
Romain Quetiez
85e261a5fa Added user rights for VLAN and related classes
SVN:trunk[2969]
2013-10-29 16:01:22 +00:00
Romain Quetiez
d912e7f4fb #804 (Continued) Fix for GetHilightClass (previous revision: 2952) was not sufficient + applied the same fix for GetIcon!
SVN:trunk[2968]
2013-10-29 15:55:00 +00:00
Romain Quetiez
bc14ad9e80 Datamodel version is 2.0.2 !
SVN:trunk[2967]
2013-10-29 14:44:27 +00:00
Romain Quetiez
e81d872306 Added missing cosmetic bitmap for the portal
SVN:trunk[2966]
2013-10-29 13:54:34 +00:00
Romain Quetiez
f83bb7fa90 Fixed regression introduced with "forgot password": button to reset the user password labelled as "Send now!"
SVN:trunk[2965]
2013-10-29 13:13:58 +00:00
Romain Quetiez
032947ff03 Portal with Incident tickets: added rights on Incident for Portal users
SVN:trunk[2964]
2013-10-29 11:37:04 +00:00
Romain Quetiez
9e39013d4c Portal: service type mapping can be an empty string
SVN:trunk[2963]
2013-10-29 11:00:31 +00:00
Romain Quetiez
5d02db5440 Show the new setting portal_tickets in the default config file
SVN:trunk[2962]
2013-10-29 09:36:23 +00:00
Romain Quetiez
7300698240 Update of the readme with the latest changes
SVN:trunk[2961]
2013-10-29 09:16:29 +00:00
Erwan Taloc
a47bbb3a9a Add a version for Documents
SVN:trunk[2960]
2013-10-28 18:51:39 +00:00
Romain Quetiez
a333bcb084 User portal: enable the creation of Incident tickets (ITIL + requires a change in the configuration file -see the readme file)
SVN:trunk[2959]
2013-10-28 16:50:13 +00:00
Romain Quetiez
e92d193347 Draft of the readme for 2.0.2 beta
SVN:trunk[2958]
2013-10-25 12:06:04 +00:00
Denis Flaven
d5a0808118 Cosmetic style adjustment for the portal...
SVN:trunk[2957]
2013-10-25 09:38:13 +00:00
Denis Flaven
864ce74cbc #805 The label of the ticket must be computed at the last minute, just before insertion, for the Mutex to be effective
SVN:trunk[2956]
2013-10-24 12:05:16 +00:00
Romain Quetiez
f684cb1745 Compiler: added "constants"
SVN:trunk[2955]
2013-10-24 09:49:56 +00:00
Denis Flaven
54769aa2d1 Oops, rollback of the previous (unexpected) commit...
SVN:trunk[2954]
2013-10-24 09:28:25 +00:00
Denis Flaven
272a249d14 #805 Use a mutex to turn the insertion of a new ticket into an atomic operation
SVN:trunk[2953]
2013-10-24 09:15:41 +00:00
Denis Flaven
02e6658439 #804 tickets' highlighting is now based on the computation performed by the stopwatch, in order to support non 24x7 working hours
SVN:trunk[2952]
2013-10-24 08:11:40 +00:00
Romain Quetiez
0c327f2c36 Added a demo mode (config: demo_mode = true). In that mode, logins get read-only (even for admins)
SVN:trunk[2951]
2013-10-23 13:36:44 +00:00
Romain Quetiez
dcb5a7208a #800 No need to track that last update has changed each time the ticket gets updated (common to all types of tickets)
SVN:trunk[2950]
2013-10-22 14:05:39 +00:00
Denis Flaven
94de069963 Fixed a regression in the computation of default choices.
SVN:trunk[2949]
2013-10-22 12:52:45 +00:00
Romain Quetiez
f0c66be7cd #783, #233 and #466 The recent revision (2921 for ticket #783) introduced a significant slow down when performing CSV import (but not only). This new revision does suppress the regression, and even speeds up bulk updates in general. This revision is candidate for retrofit into branch 2.0.1 (along with 2921)
SVN:trunk[2948]
2013-10-22 12:46:09 +00:00
Denis Flaven
0b7ed90e18 Fixed a regression introduced by revision [2856]: Avoid breaking pages with tabs when there is no BASE tag at all...
SVN:trunk[2947]
2013-10-22 11:49:38 +00:00
Romain Quetiez
20ba6242e7 No time limit for long operations like: Bulk delete, CSV import (interactive) and Bulk modify
SVN:trunk[2946]
2013-10-22 08:09:34 +00:00
Erwan Taloc
015919702b Change translation of incident menu in French dictionary
SVN:trunk[2945]
2013-10-21 13:15:27 +00:00
Erwan Taloc
ae8ff6b675 Fix Trac #796
Prevent Support agent to create ticket for obsolete Services and Service sub categories

SVN:trunk[2944]
2013-10-21 13:06:10 +00:00
Erwan Taloc
0ea6657610 Add VLANs on Subnet and Physical Interfaces
SVN:trunk[2943]
2013-10-21 12:35:57 +00:00
Denis Flaven
9d6d93d42f #757 Better UI to manage direct linksets: added the ability to provide the "reverse query" by specifying a '<filter>' tag on AttributeLinkedSet.
SVN:trunk[2942]
2013-10-21 12:25:07 +00:00
Denis Flaven
4f845ec98d #780 Dictionary entries for "Check All" and "Uncheck All"
SVN:trunk[2941]
2013-10-21 09:08:32 +00:00
Erwan Taloc
f65c84300f Change translation in French dictionary
SVN:trunk[2940]
2013-10-21 07:07:19 +00:00
Erwan Taloc
d8b9679346 Change translation in French dictionary
SVN:trunk[2939]
2013-10-21 06:59:56 +00:00
Erwan Taloc
e090b866e1 Change translation in Franch dictionnary
SVN:trunk[2938]
2013-10-21 06:59:20 +00:00
Erwan Taloc
d30f34afc1 Translate menu entry in French
SVN:trunk[2937]
2013-10-21 06:57:51 +00:00
Erwan Taloc
3b7aa49ca3 Translate menu entry in French
SVN:trunk[2936]
2013-10-21 06:57:26 +00:00
Erwan Taloc
441bd44f97 Hidde unused attribute end end in the search form
SVN:trunk[2935]
2013-10-21 06:46:49 +00:00
Erwan Taloc
7a18730949 Hidde unused attribute end end in the search form
SVN:trunk[2934]
2013-10-21 06:46:17 +00:00
Erwan Taloc
0e27be0aca Hidde unused attribute end end in the search form
SVN:trunk[2933]
2013-10-21 06:45:37 +00:00
Erwan Taloc
1c16365881 Remove duplicate display of attribute service provider
SVN:trunk[2932]
2013-10-21 06:44:53 +00:00
Erwan Taloc
585e06f096 Remove duplicate display of attribute service provider
SVN:trunk[2931]
2013-10-21 06:44:24 +00:00
Romain Quetiez
edce93282b #792 Duplicate entries in the parent/child tickets when updating the case log and applying a stimulus (e.g. Close the WO) at the same time.
SVN:trunk[2930]
2013-10-18 15:54:54 +00:00
Romain Quetiez
26dca89b19 #780 Auto refresh for the dashboards (+ cosmetics changes for the shortcuts)
SVN:trunk[2929]
2013-10-18 14:14:48 +00:00
Denis Flaven
9b58e736ff #787 Added buttons to check/uncheck all options at once in multi selects inside search forms.
SVN:trunk[2928]
2013-10-18 09:06:25 +00:00
Denis Flaven
36e6a6106b #757 Better UI to manage direct linksets... on going...
SVN:trunk[2927]
2013-10-18 08:26:31 +00:00
Romain Quetiez
bbb31e2b7f #780 Auto refresh for the shortcuts
SVN:trunk[2926]
2013-10-17 14:47:05 +00:00
Romain Quetiez
afa3c40c3e Improved the column load optimization which was causing object reloads in various circumstances (impact can be important when loading lists with many lines):
1) When changing column settings (menu "Configure this list")
2) When a plugin was systematically reading some data (e.g. Highlighting late tickets requires to read tto/trr/status/start_date!)

SVN:trunk[2925]
2013-10-17 09:46:10 +00:00
Romain Quetiez
401d61aa76 Creation of indexes on several columns (exploited for a few classes when it was obvious)
SVN:trunk[2924]
2013-10-16 15:21:20 +00:00
Romain Quetiez
eda203af26 #785 Share the results of a query phrase (preview of the results in the query details page -iif it has NO parameter)
SVN:trunk[2923]
2013-10-16 11:45:17 +00:00
Romain Quetiez
3022ba9b1a Fixed regression introduced a few months ago when reworking the dashboard edition (after the release of 2.0.1)
SVN:trunk[2922]
2013-10-16 11:43:21 +00:00
Romain Quetiez
440f50259b #783 Added the placeholder $this->xxx_list$ for emailing (names separated by a new line, truncated to 100 items)
SVN:trunk[2921]
2013-10-16 10:36:15 +00:00
Romain Quetiez
5d402a5f9d Reviewed the instrumentation to help in tuning the performance (added a message in the admin banner when logging is active)
SVN:trunk[2920]
2013-10-16 10:33:30 +00:00
Denis Flaven
2d83f331e2 #771: better display for "edit in place".
SVN:trunk[2919]
2013-10-16 09:34:48 +00:00
Denis Flaven
677cc2b19e #795 Issue when using the actual (id) value of an external key as a reconciliation field
SVN:trunk[2918]
2013-10-16 08:54:22 +00:00
Romain Quetiez
caa621eb04 Reviewed the instrumentation to help in tuning the performance
SVN:trunk[2916]
2013-10-15 16:08:43 +00:00
Denis Flaven
5ea2ac3fef #741 Complete localization of the CSV import confirmation dialog
SVN:trunk[2915]
2013-10-15 16:00:51 +00:00
Denis Flaven
09318b81c0 #790 Only report as installed the modules from the previous installation, not all previous installations.
SVN:trunk[2914]
2013-10-15 15:17:50 +00:00
Denis Flaven
d5be250640 #754 Prevent UserRequests to have themselves set as Parent Request (and same for Incidents)
SVN:trunk[2913]
2013-10-15 13:41:15 +00:00
Denis Flaven
fca3bb2a73 #738 Adding a space at the end of the mailto: URL to make it better recognized by mail clients (namely Outlook)
SVN:trunk[2911]
2013-10-15 08:09:15 +00:00
Denis Flaven
bf9cb67226 #791 Protect against single quotes in localized strings...
SVN:trunk[2909]
2013-10-14 16:19:03 +00:00
Denis Flaven
e54d6ecc12 #777 mandatory fields that are external keys are now displayed with a star before the arrow: ExtkeyName*->ReconciliationField. In import the old syntax is supported as well.
SVN:trunk[2908]
2013-10-14 15:36:49 +00:00
Romain Quetiez
30de6a1e39 Instrumented the code to measure the impact of object reloads
SVN:trunk[2907]
2013-10-14 15:19:26 +00:00
Romain Quetiez
6de4d93ef2 #562 and #760 Refresh of the german translation
SVN:trunk[2906]
2013-10-14 15:10:29 +00:00
Romain Quetiez
3c3d4a073d #769 Title of pies and charts are not consistent with the title of other dashlets
SVN:trunk[2905]
2013-10-14 14:48:09 +00:00
Romain Quetiez
c2efdfa0bb #794 Could not export the field friendlyname in format 'spreadsheet'
SVN:trunk[2903]
2013-10-14 14:17:07 +00:00
Romain Quetiez
2218003bec #758 REST service: key given in clear in the returned objects (incremented the API verion to 1.1)
SVN:trunk[2902]
2013-10-14 13:52:11 +00:00
Denis Flaven
3ffd289a5e #793 provide the default '=' and '!=' operators for all types of Computed Fields.
SVN:trunk[2901]
2013-10-14 13:49:21 +00:00
Romain Quetiez
fe4d55fbf6 Missing french translation
SVN:trunk[2900]
2013-10-14 13:44:50 +00:00
Romain Quetiez
b5d9e5a8b6 #773 Display boolean values from the stop watches as yes/no (localized, like enums) + took the opportunity to enable the export in spreadsheet format
SVN:trunk[2899]
2013-10-14 13:22:52 +00:00
Romain Quetiez
046a7b0e2d Fixed a regression due to the change [2877] Tooltip to preview attachments
SVN:trunk[2898]
2013-10-14 12:15:07 +00:00
Erwan Taloc
32ca9727f7 Fix Bug #762 : Remove wrong fields approval_date approval_comments for a Routine change
SVN:trunk[2896]
2013-10-11 11:42:22 +00:00
Erwan Taloc
08fc696f94 Modify Sample data for Service categories to set them to status "production" by default
SVN:trunk[2895]
2013-10-11 11:13:13 +00:00
Erwan Taloc
39ef3d13e6 Fix bug #768 to avoid to select obsolete service and service sub categories in the portal
SVN:trunk[2894]
2013-10-11 11:05:45 +00:00
Erwan Taloc
151b300856 Fix bug #789 to add up to 12 Digit for a IPInterface
SVN:trunk[2893]
2013-10-11 10:51:51 +00:00
Erwan Taloc
c5bf962095 Fix bug #755 to prevent modification of CIs and Contacts list for UserRequest and Incidents
SVN:trunk[2892]
2013-10-11 10:47:14 +00:00
Erwan Taloc
a6a4cf5d00 Bug #742 fixed. Allow portal user to modify a ticket when it is pending
SVN:trunk[2891]
2013-10-11 10:34:47 +00:00
Erwan Taloc
90f7aa04bb Fib bug #739 preventing a Support Agent to set a UserRequest to status "Pending"
SVN:trunk[2890]
2013-10-11 10:33:32 +00:00
Romain Quetiez
bb9f074670 Show all types of Actions from the "Notifications/Actions" tab.
SVN:trunk[2889]
2013-10-11 10:30:29 +00:00
Erwan Taloc
ef26f395bd Fix bug #751. Check that class Logical Volume exists when checking dependencies of a Server
Add attribute Subnet name on Subnet element

SVN:trunk[2888]
2013-10-11 10:24:32 +00:00
Denis Flaven
e34516745c Retrofit the useful DoPostRequest function which was used (and defined) in several extensions.
SVN:trunk[2886]
2013-10-11 08:38:38 +00:00
Erwan Taloc
8474b423fe Move definition of the delivery model of an organization from itop-config-mgmt to itop-service-mgmt module.
SVN:trunk[2885]
2013-10-11 08:36:26 +00:00
Denis Flaven
30b2d93bdf Added support of different (sub)classes of notifications in the "Notifications" tab on an object.
SVN:trunk[2884]
2013-10-10 16:01:44 +00:00
Denis Flaven
7162db0487 Fix for properly computing the default choices in case of upgrade...
SVN:trunk[2883]
2013-10-09 10:23:40 +00:00
Romain Quetiez
e08fa6b43b #745 Default menu is not computed correctly (depends on the customizations made to the menu -> order of declaration)
SVN:trunk[2882]
2013-10-08 14:27:27 +00:00
Denis Flaven
4b9e6edab8 File identifiers are no longer numeric only !
SVN:trunk[2881]
2013-10-08 12:46:37 +00:00
Romain Quetiez
7017bbf88b The login web page must NOT be cached by the web browsers
SVN:trunk[2880]
2013-10-08 08:28:25 +00:00
Romain Quetiez
4f4ceeadc6 Removed calls to console.log
SVN:trunk[2879]
2013-10-08 08:23:38 +00:00
Romain Quetiez
b0ecb2f6c6 #774 Sort the enums in the selection drop-down box (search forms) -initially based on the declaration order
SVN:trunk[2878]
2013-10-08 07:34:59 +00:00
Denis Flaven
4be0837ead #782: preview (as a tooltip) for image attachments.
SVN:trunk[2877]
2013-10-03 16:53:25 +00:00
Romain Quetiez
e3832a13a6 #784 Data sync: display the attribute code (as well as its label in the user language)
SVN:trunk[2876]
2013-10-03 15:50:56 +00:00
Romain Quetiez
169f576ccf #781 Plain text dashlet shown on one single line
SVN:trunk[2875]
2013-10-03 15:40:45 +00:00
Romain Quetiez
894b59eee1 #779 It is possible to record a wrong OQL in the phrase book, but then it cannot be edited anymore!
SVN:trunk[2874]
2013-10-03 15:30:29 +00:00
Denis Flaven
fe41d09acb Support for input values with no icon.
SVN:trunk[2873]
2013-10-02 14:34:45 +00:00
Denis Flaven
387e4c6f0b Upload image button should not submit the parent form !!!
SVN:trunk[2872]
2013-10-02 12:48:22 +00:00
Romain Quetiez
6f8be14711 Internal: failed authentication to return error 401 instead of prompting the end-user (to be exploited by the ajax calls)
SVN:trunk[2871]
2013-10-02 09:30:14 +00:00
Denis Flaven
7d824dd03c Removed an obsolete comment...
SVN:trunk[2869]
2013-09-30 14:41:08 +00:00
Denis Flaven
899a7c1ba0 New pattern accepting the new global Top Level Domains (gTLD)
SVN:trunk[2867]
2013-09-27 07:29:15 +00:00
Romain Quetiez
a84eff5c3b Fixed regression introduced on fix [2829]
SVN:trunk[2866]
2013-09-26 15:32:16 +00:00
Romain Quetiez
e0ae6484d3 Fixed regression on the change on change tracking (sic!)
SVN:trunk[2865]
2013-09-26 15:22:23 +00:00
Romain Quetiez
552e90f674 Logoff: display the message in the user language (used to be 100% english)
SVN:trunk[2864]
2013-09-25 10:20:33 +00:00
Romain Quetiez
898ee016c9 Generalized the option tracking_level to any kind of attributes. Defaults to 'all', can be set to 'none' to disable the change tracking on a single attribute (LinkSets still have the same allowed values: none, list, details and all).
SVN:trunk[2863]
2013-09-25 09:47:50 +00:00
Denis Flaven
7d87aad0bb Protect the deletion of objects with very long friendly names
SVN:trunk[2861]
2013-09-24 16:19:22 +00:00
Romain Quetiez
8d068b6a93 #767 JSON/REST Reconciliations made on loose criteria (forced to strict equality, no way to specify a loose criteria)
SVN:trunk[2860]
2013-09-24 13:38:49 +00:00
Romain Quetiez
ea36d6b147 New feature: Forgot password (prerequisite in the very standard authent local module)
SVN:trunk[2859]
2013-09-24 12:48:09 +00:00
Romain Quetiez
90e024b2bb Cosmetics on the login web page (2 of 2!)
SVN:trunk[2858]
2013-09-24 12:44:31 +00:00
Romain Quetiez
955beb70e4 Cosmetics on the login web page
SVN:trunk[2857]
2013-09-24 12:43:44 +00:00
Denis Flaven
1a60b7005b Avoid breaking pages with tabs when there is no BASE tag at all...
SVN:trunk[2856]
2013-09-24 10:05:33 +00:00
Romain Quetiez
fde3808cdf New feature: Forgot password -> email to reset (possibly disabled in the config file)
SVN:trunk[2855]
2013-09-24 09:15:52 +00:00
Denis Flaven
76e0ee66ae Allow for comparisons of the module's versions in the expression of dependencies. For example one can now say "itop-config-mgmt/>=2.0.2" for a dependency.
SVN:trunk[2853]
2013-09-23 12:48:55 +00:00
Romain Quetiez
a2a0ee5194 Fixed bug in the JSON REST API: core/create and core/update, could not reset an external key (0)
SVN:trunk[2852]
2013-09-19 11:42:06 +00:00
Romain Quetiez
0bced2f9ae ModelFactory: needed / define_if_not_exists were not equivalent
SVN:trunk[2850]
2013-09-12 08:09:15 +00:00
Romain Quetiez
ccc9729cc5 #763 Could not use "configure this list" once a stop watch has been added to the list, which is a pitty because such attributes are not aimed at being displayed in lists!
SVN:trunk[2848]
2013-09-11 09:53:31 +00:00
Romain Quetiez
cf383bcf6b Fixed bug (wrong DB charset after invoking AnalyzeInstallation!)
SVN:trunk[2847]
2013-09-06 15:18:41 +00:00
Romain Quetiez
9292d5fa33 Icon select: load images when the control becomes visible
SVN:trunk[2846]
2013-09-05 16:01:07 +00:00
Denis Flaven
3b6646f1b9 Load structural data for all selected modules indepentyl of:
- the load of sample data
- first install or upgrade

SVN:trunk[2845]
2013-09-03 15:37:10 +00:00
Romain Quetiez
ca1d4d8936 Management of environments: the banner must be injected by the mean of iPageUIExtension
SVN:trunk[2844]
2013-09-02 12:54:00 +00:00
Romain Quetiez
afa6399dce Regression due to the safe compile: fixed an issue depending on the OS: could not compile (bug with PHP rename)
SVN:trunk[2843]
2013-09-02 08:52:14 +00:00
Romain Quetiez
f93b1e1c1c Module installation information always loaded within the meta model
SVN:trunk[2842]
2013-08-30 14:20:20 +00:00
Denis Flaven
fd7adb2202 Make the logo transparent (background removal)
SVN:trunk[2841]
2013-08-30 08:30:28 +00:00
Denis Flaven
05f50c285c Fixed an "Undefined variable" error
SVN:trunk[2840]
2013-08-30 08:17:22 +00:00
Romain Quetiez
0aa2dc9ce3 Portal: cosmetics (same logo as the application logo)
SVN:trunk[2839]
2013-08-30 07:38:07 +00:00
Romain Quetiez
607236a7cb Compiler: added brand management
SVN:trunk[2838]
2013-08-29 08:35:44 +00:00
Romain Quetiez
564ba105eb CRON: report that CRON is already running BEFORE saying that the DB is read-only (re-entrance during an operation done in the background)
SVN:trunk[2837]
2013-08-27 14:20:12 +00:00
Romain Quetiez
73b492e892 New mechanism: a module page can be accessed by the mean of a canonical URL (utils::GetAbsoluteUrlModulePage to build the proper URL)
SVN:trunk[2836]
2013-08-27 14:04:59 +00:00
Romain Quetiez
e99d96e081 Safe compilation (works in a temporary directory, on success then move it into env-production)
SVN:trunk[2835]
2013-08-27 12:19:55 +00:00
Romain Quetiez
abae2129ad CRON: protection against re-entrance now relies on a bullet-proof mutex. Also added the option 'debug=1' to output the call stack in case an exception occurs (not always because of passwords being shown in the call stack)
SVN:trunk[2834]
2013-08-26 15:29:32 +00:00
Romain Quetiez
f8c3e0ddea Model factory: fixed two bugs
SVN:trunk[2833]
2013-08-23 14:37:43 +00:00
Romain Quetiez
a28a0aba7d Compiler: when creating a test environment, take the relevant delta file (source env != dest env)
SVN:trunk[2832]
2013-08-23 14:36:33 +00:00
Romain Quetiez
358911604b #752 Notifications sent several times (or too late) when MySQL is hosted on another server
SVN:trunk[2831]
2013-08-23 07:43:10 +00:00
Romain Quetiez
75eb44912f Setup: Source dir recorded with a trailing backslash under windows
SVN:trunk[2829]
2013-08-22 11:57:31 +00:00
Romain Quetiez
2893d16d58 Restored the behavior of itop-sla-computation (if present, then it becomes the default working hour computer)
SVN:trunk[2828]
2013-08-21 08:07:31 +00:00
Romain Quetiez
2dbcb6d416 Restored the logo on the portal (picture with transparent background)
SVN:trunk[2827]
2013-08-20 12:11:53 +00:00
Romain Quetiez
2b4ad2c50b Reviewed the portal (look and feel slightly improved)
SVN:trunk[2826]
2013-08-20 11:42:46 +00:00
Romain Quetiez
7e4b69d272 Improved the error reporting for the backup (in case mysqldump fails with a single error, then the error is displayed directly)
SVN:trunk[2825]
2013-08-19 15:16:32 +00:00
Romain Quetiez
d8c9044e15 Improved the error reporting for the backup (in case mysqldump fails with a single error, then the error is displayed directly)
SVN:trunk[2824]
2013-08-19 15:15:53 +00:00
Denis Flaven
b2e4cf2c09 Add a carriage return to the error message output when iTop is NOT yet installed.
SVN:trunk[2823]
2013-08-19 14:59:41 +00:00
Romain Quetiez
08fa8362e3 CRON: reschedule at startup IIF the task is inactive or it is planned in the future
SVN:trunk[2822]
2013-08-14 15:01:53 +00:00
Romain Quetiez
447736f585 CRON to exit gracefully if iTop not yet installed
SVN:trunk[2821]
2013-08-14 13:21:41 +00:00
Denis Flaven
5ed91c2223 New verb "AfterDatabaseSetup" for performing installation tasks after the completion of the DB creation (+predefined objects & admin account)
SVN:trunk[2820]
2013-08-14 07:34:07 +00:00
Romain Quetiez
4fa07536d5 Added datamodel delta (if any) to the backup file
SVN:trunk[2819]
2013-08-13 09:01:04 +00:00
Romain Quetiez
8881450d59 Delta revision id can be stored into the XML delta
SVN:trunk[2818]
2013-08-09 15:53:42 +00:00
Romain Quetiez
58af5528be Possibility to introduce a delta (not in a module) at compile time
SVN:trunk[2817]
2013-08-09 15:47:05 +00:00
Romain Quetiez
98a1242050 New capability for CRON: handle tasks scheduled at given date/time (as opposed to a task being executed more or less continuously).
SVN:trunk[2816]
2013-08-08 15:23:05 +00:00
Denis Flaven
9536c99422 Allow "Support Agents" to put an Incident in "Pending" state.
SVN:trunk[2814]
2013-08-01 08:24:19 +00:00
Denis Flaven
7cfd5ad2a3 Ugly fix for a nasty change in jQuery UI behavior: UI tabs were considered as "Ajax" tabs when the page has a "base" tag, which was not the case in previous versions. Cf http://bugs.jqueryui.com/ticket/8637
SVN:trunk[2813]
2013-07-31 16:45:33 +00:00
Denis Flaven
86ba340204 #747: protects against the non-existence of the UserRequest class (which is not always installed).
SVN:trunk[2811]
2013-07-30 16:24:52 +00:00
Denis Flaven
b32a142e14 Use the minified version of jquery-migrate, since the non-minified version (which produces debug traces) is excluded from the build.
SVN:trunk[2810]
2013-07-25 09:48:58 +00:00
Denis Flaven
7e45f34a86 #746 allow adding an AttributeBlob with is_null_allowed = true to an existing Data Model. (same issue fixed also for AttributeOneWayPassword).
SVN:trunk[2808]
2013-07-25 09:17:16 +00:00
Denis Flaven
1064feaa8e Properly handle nested forms in "PropertySheet" and "read-only" mode
SVN:trunk[2807]
2013-07-24 17:01:25 +00:00
Denis Flaven
17658d1b6a Bug fix: validation was broken when the first fields were not Ok.
SVN:trunk[2806]
2013-07-24 17:00:30 +00:00
Denis Flaven
ce643d9086 Export the content of the CaseLogs in "spreadsheet" format, with some tricks to preserve the formatting in Excel.
SVN:trunk[2804]
2013-07-17 16:54:27 +00:00
Denis Flaven
481515b419 IsInDefinition is needed => made it public
SVN:trunk[2803]
2013-07-17 14:48:10 +00:00
Denis Flaven
6d60d92b03 Fix for a non localized message.
SVN:trunk[2802]
2013-07-17 08:46:50 +00:00
Denis Flaven
3edbdf76f3 Fix for a non localized message.
SVN:trunk[2801]
2013-07-17 08:46:04 +00:00
Denis Flaven
80bac5275c Forms enhancements:
- The current value of a field is automatically excluded from the forbidden values


SVN:trunk[2800]
2013-07-16 09:16:54 +00:00
Denis Flaven
59fc9e24d9 Forms enhancements:
- The current value of a field is automatically excluded from the forbidden values
- Several levels of subforms can be nested, even when displaying as a property sheet
- Sortables fields re-implemented based on a widget.

SVN:trunk[2799]
2013-07-16 09:16:12 +00:00
Denis Flaven
7db7c0781f Make the portal (slightly) more configurable...
SVN:trunk[2797]
2013-07-12 13:55:28 +00:00
Romain Quetiez
358ddf6019 Fixed issue for the toolkit
SVN:trunk[2796]
2013-07-10 08:48:08 +00:00
Romain Quetiez
ebf08345af Forms: added the possibility to specify forbidden values + message to explain the issue(toolip) (fiwed a bug on the previous implementation, causing a javascript error, hence a stopper regression due to missing event binds)
SVN:trunk[2795]
2013-07-09 09:09:49 +00:00
Denis Flaven
a9ad236439 Allow filtering the Delta output...
SVN:trunk[2794]
2013-07-08 08:48:03 +00:00
Romain Quetiez
3066240ca0 Forms: added the possibility to specify forbidden values + message to explain the issue(toolip)
SVN:trunk[2793]
2013-07-08 08:25:13 +00:00
Romain Quetiez
d82326bfd4 Form as a dialog, possibility to specify an introduction message (cosmetics)
SVN:trunk[2792]
2013-07-05 13:57:07 +00:00
Romain Quetiez
f99ecb40d0 Form as a dialog, possibility to specify an introduction message
SVN:trunk[2791]
2013-07-05 13:54:16 +00:00
Romain Quetiez
d7124123e9 Compiler: allow to set the flags enable_class/enable_action etc. for a TemplateMenuNode (already taken into account at runtime)
SVN:trunk[2790]
2013-07-04 14:17:23 +00:00
Romain Quetiez
e517f2b6f5 Cosmetics on the dashboards
SVN:trunk[2789]
2013-07-03 15:52:31 +00:00
Denis Flaven
ea686059b6 Protect against non existing reconciliation keys...
SVN:trunk[2788]
2013-07-03 09:47:44 +00:00
Denis Flaven
3898371d44 Added support of CSS classes for styling the form
SVN:trunk[2787]
2013-07-03 09:46:16 +00:00
Romain Quetiez
f0a5a0a948 Completed the move of dashboards from separate definition files (e.g. overview.xml) into data model files (8 dashboards were concerned on the model 2.x, 6 for the model 1.x)
SVN:trunk[2786]
2013-07-02 09:18:40 +00:00
Denis Flaven
24ab96769a Re-position the popup menu each time the button is clicked, in case the button was moved...
SVN:trunk[2785]
2013-06-28 09:39:57 +00:00
Romain Quetiez
5e3a34d425 Updated the licences: 2012 -> 2013
SVN:trunk[2784]
2013-06-28 07:19:55 +00:00
Romain Quetiez
76724225e0 XML format: missing/wrong id on dashboards cells
SVN:trunk[2783]
2013-06-28 07:11:24 +00:00
Romain Quetiez
3ab539e2ba Dashboard re-engineering
SVN:trunk[2782]
2013-06-27 15:21:35 +00:00
Romain Quetiez
721a654152 Fixed regression due to the update of JQuery UI
SVN:trunk[2781]
2013-06-26 16:25:53 +00:00
Denis Flaven
57e51e44f1 Make sure that tabs (and tab panels) are properly identified
SVN:trunk[2780]
2013-06-26 13:56:24 +00:00
Denis Flaven
f7642283f3 Forget about IE 8 support ???
SVN:trunk[2779]
2013-06-26 13:55:27 +00:00
Denis Flaven
e7897b9139 Update for jQuery UI 1.10
SVN:trunk[2778]
2013-06-26 13:54:52 +00:00
Romain Quetiez
59ce84f7cb Cosmetics on the portal
SVN:trunk[2776]
2013-06-14 15:37:47 +00:00
Romain Quetiez
bb6d87e8ed Cosmetics on the portal
SVN:trunk[2775]
2013-06-14 15:15:13 +00:00
Romain Quetiez
46dae2f06f Cosmetics: User portal - added a title (usually, this would appear on the tab of your browser)
SVN:trunk[2774]
2013-06-14 15:05:03 +00:00
Romain Quetiez
0c1a366c07 OQL normalization and dashlets have been made independent from the class MetaModel (adjusted the API)
SVN:trunk[2773]
2013-06-13 14:11:13 +00:00
Romain Quetiez
71cc6f7e6b OQL normalization and dashlets have been made independent from the class MetaModel (reviewed the API)
SVN:trunk[2772]
2013-06-12 07:21:11 +00:00
Romain Quetiez
ba9a50b6fb #736 Could not delete objects unless you are authorized to bulk delete
SVN:trunk[2769]
2013-06-07 07:28:31 +00:00
Romain Quetiez
9ef41a37b8 OQL normalization and dashlets have been made independent from the class MetaModel (2 of 2!)
SVN:trunk[2768]
2013-06-03 13:49:51 +00:00
Romain Quetiez
26db86beb2 OQL normalization and dashlets have been made independent from the class MetaModel
Added OQL normalization unit tests (to be run on a standard installation)

SVN:trunk[2767]
2013-06-03 13:26:14 +00:00
Denis Flaven
69c37b07de Upgrade to jQuery 1.10 and jQuery UI 1.10
SVN:trunk[2766]
2013-05-30 09:48:59 +00:00
Denis Flaven
7844db0719 Upgrade to jQuery 1.10 and jQuery UI 1.10
SVN:trunk[2765]
2013-05-30 09:13:43 +00:00
Denis Flaven
6edb1e3482 Removed the use of the obsolete $.browser property, since we don't care about IE 7 anyway.
SVN:trunk[2764]
2013-05-30 08:11:12 +00:00
Denis Flaven
7b887f3ea5 #734 Fixed a regression on reconciliation keys during CSV import.
SVN:trunk[2761]
2013-05-29 08:53:07 +00:00
Denis Flaven
2fe407967b Preparing for the 2.0.1 release... update of the readme file.
SVN:trunk[2758]
2013-05-22 09:55:29 +00:00
Denis Flaven
703be73c95 #689: Logoff / Change Pwd buttons not visible in the portal if the window is too small: now allow line wrapping...
SVN:trunk[2757]
2013-05-22 09:43:26 +00:00
Denis Flaven
3060462edc #732: Change password: exit after building the page in case of wrong "old" password
SVN:trunk[2756]
2013-05-22 08:43:48 +00:00
Romain Quetiez
263acaf4e4 Missing localized label for some problem management menus, when the Known Error Management module is installed without the Problem Management module
SVN:trunk[2755]
2013-05-21 13:13:44 +00:00
Denis Flaven
db1be8f500 Add "Employee number" to the list of possible reconciliation keys for the class Person.
SVN:trunk[2754]
2013-05-21 11:04:01 +00:00
Romain Quetiez
452eca5288 The ticket class must be abstract!
SVN:trunk[2753]
2013-05-21 10:49:22 +00:00
Romain Quetiez
a728dfcf48 All the overloads of DisplayBareProperties must return an array (otherwise a warning is issued and field validation will not work as expected)
SVN:trunk[2752]
2013-05-21 10:44:13 +00:00
Denis Flaven
2027dc4a3d Make icons transparent...
SVN:trunk[2751]
2013-05-17 12:12:19 +00:00
Denis Flaven
1bc4e1431c Preparing for the 2.0.1 release... update of the readme file.
SVN:trunk[2750]
2013-05-17 08:34:42 +00:00
Denis Flaven
9afe28be20 Support non scalar posted parameters...
SVN:trunk[2749]
2013-05-16 15:45:57 +00:00
Denis Flaven
8073120351 JS syntax cleanup...
SVN:trunk[2748]
2013-05-16 15:44:05 +00:00
Denis Flaven
86c5b3e258 Typo...
SVN:trunk[2747]
2013-05-16 15:42:34 +00:00
Denis Flaven
b971faecda Cleanup of the log output. No need for such verbosity now that we are approaching the release.
SVN:trunk[2746]
2013-05-16 15:41:52 +00:00
Denis Flaven
2708b0de0e #728: the ticket's title is now an (external) attribute of the link between a ticket and a CI.
SVN:trunk[2745]
2013-05-16 14:28:23 +00:00
Romain Quetiez
8dd9893202 Readme: Documented the known issue #730 Leaving the backup temp files on disk
SVN:trunk[2744]
2013-05-16 14:23:35 +00:00
Denis Flaven
48d740da25 #727: prevent a crash in cron.php
SVN:trunk[2743]
2013-05-16 14:05:38 +00:00
Romain Quetiez
7ba5526fda #725 Specify a port for mySQL server
#729 Backup fails on Windows IIS

SVN:trunk[2742]
2013-05-16 13:57:52 +00:00
Denis Flaven
58dfa3335a Spanish localization enhancements, thanks to Miguel Turrubiates
SVN:trunk[2741]
2013-05-16 12:41:10 +00:00
Denis Flaven
deec1aa2a2 Fix the auto-resize (fit) of the dialog's content.
SVN:trunk[2740]
2013-05-16 12:13:18 +00:00
Romain Quetiez
a62c1946a6 #721 Unmet dependencies not detected
SVN:trunk[2739]
2013-05-16 08:54:50 +00:00
Romain Quetiez
a194308486 #726 Missing string in german dictionary
SVN:trunk[2738]
2013-05-16 07:41:36 +00:00
Denis Flaven
2e442dbaa0 #704: preserve the content of a "linkedset" when changing the initial state of the object being created !
SVN:trunk[2737]
2013-05-15 15:56:42 +00:00
Romain Quetiez
ad9ed96960 #673 Could not create a physical interface with default value for the speed (+ definitive corruption of the DB for the device on which the interface is being created!)
SVN:trunk[2735]
2013-05-15 10:23:06 +00:00
Romain Quetiez
efc3b4df07 Silently discard unknown attributes in object templates (in case some modules have not been installed)
SVN:trunk[2733]
2013-05-15 10:18:55 +00:00
Romain Quetiez
d6da043a32 #722 Document preview disappeared since 2.0 (not in 1.x)
SVN:trunk[2732]
2013-05-14 15:43:22 +00:00
Romain Quetiez
10a7a5aa38 Fix for reconciliation by id (Advanced Mode) -reintegrated from branch 2.0
SVN:trunk[2731]
2013-05-14 15:19:12 +00:00
Romain Quetiez
5684f1e196 #713 URL format reviewed:
- more formats allowed by default
- aligned between the wiki formatting and the URL attribute
- configurable globally with 'url_validation_pattern'
- can be defined at the attribute level with tag validation_pattern


SVN:trunk[2730]
2013-05-14 14:20:00 +00:00
Romain Quetiez
2376a63d18 #711 Audit error drill-down not working when zero error (requires to bookmark the link when there are some errors, then use it later when there is no error anymore)
SVN:trunk[2729]
2013-05-14 10:41:44 +00:00
Romain Quetiez
744b821d03 #710 Query phrase book "fields list" is too small
SVN:trunk[2728]
2013-05-14 10:19:33 +00:00
Romain Quetiez
f3eb6b5cb3 #705 Typo in the english dictionary
SVN:trunk[2727]
2013-05-14 10:09:42 +00:00
Romain Quetiez
d973f64576 #708 Cosmetics for the english dictionary
SVN:trunk[2726]
2013-05-14 10:07:20 +00:00
Denis Flaven
a6c9bcf780 #717: prevent flicker of unstyled content: make the pane visible only when their content is ready... + watchdog in case of error.
SVN:trunk[2725]
2013-05-14 10:03:23 +00:00
Romain Quetiez
7cae338e6d #723 Cron locking is too restrictive
SVN:trunk[2724]
2013-05-14 09:34:08 +00:00
Denis Flaven
ed344650c5 Prevent logoff problems depending on PHP's session.cache_limiter setting...
SVN:trunk[2723]
2013-05-13 15:45:10 +00:00
Romain Quetiez
2d03e95ece Fixed typos in the sample data for the service subcategories
SVN:trunk[2722]
2013-05-13 14:50:56 +00:00
Denis Flaven
69179f5d25 #707: oops, prevent a warning when no dict entry...
SVN:trunk[2721]
2013-05-13 14:50:26 +00:00
Romain Quetiez
27cf82b270 #693 Portal user cannot choose services linked via customer contract
SVN:trunk[2720]
2013-05-13 14:46:06 +00:00
Denis Flaven
d28891eaf4 #707: Make sure that DOCTYPE is the first statement of the page
- Enhancement: support for "target" in PopupMenuItems.. which was ignored before

SVN:trunk[2719]
2013-05-13 14:43:16 +00:00
Erwan Taloc
b1c1e5f9f2 Limit rack according to location for PDU (see track #638)
SVN:trunk[2718]
2013-05-13 10:56:55 +00:00
Erwan Taloc
8a86c6a637 change path for incident icon trac #699
SVN:trunk[2717]
2013-05-13 10:50:00 +00:00
Denis Flaven
631811145f Experimental support of icon uploads...
SVN:trunk[2716]
2013-05-07 17:27:37 +00:00
Denis Flaven
78ff062787 Proper reset of APC cache upon compilation !
SVN:trunk[2715]
2013-05-07 17:25:24 +00:00
Denis Flaven
427f50b390 Allow filtering of the languages to install
SVN:trunk[2713]
2013-05-02 08:09:10 +00:00
Romain Quetiez
21f0d96146 New way to compile the dictionaries, allowing for incremental modification via XML
SVN:trunk[2712]
2013-04-30 16:14:37 +00:00
Romain Quetiez
7fcf922ee0 Updated the unit test for the OQL parser
SVN:trunk[2711]
2013-04-29 09:01:06 +00:00
Denis Flaven
22dc44c9e5 Typo
SVN:trunk[2710]
2013-04-29 08:55:22 +00:00
Denis Flaven
6feb62d728 #383: support negative numbers in OQL
Enhancements: support MySQL bitwise operators (&, |, ^, <<, >>) and hexadecimal numbers (up to 64-bit).

SVN:trunk[2709]
2013-04-29 08:51:01 +00:00
Denis Flaven
29060f7b5e Enhancement: the expression of dependencies between modules can now use a complex boolean expression with a combination of "logical or" (||) and "logical and" (&&) instead of just a module name.
SVN:trunk[2708]
2013-04-29 08:39:23 +00:00
Denis Flaven
6df6af0df0 #663: Fix for emptying a directory which contains broken symbolic links
SVN:trunk[2707]
2013-04-29 08:34:19 +00:00
Romain Quetiez
d433a45200 #692 Compensate the effect of this fix committed as 2705
SVN:trunk[2706]
2013-04-26 10:32:11 +00:00
Romain Quetiez
d5e57ba0ba #692 Allow to set a default value for AttributeDuration (was forced to 0 anytime)
SVN:trunk[2705]
2013-04-26 10:24:44 +00:00
Romain Quetiez
d69236cb25 #701 Portal customization : enabled the calendar widget for input of a date+time type of attribute (it was only available for pure DATE attributes)
SVN:trunk[2704]
2013-04-26 10:07:13 +00:00
Romain Quetiez
422aa5b407 #715 Finalized the fix (added the year for group by day, but it is not displayed)
SVN:trunk[2703]
2013-04-26 09:54:39 +00:00
Romain Quetiez
f97f51b895 #715 Group by day -> month+day, group by month -> year+month, months are shown as a localized label
SVN:trunk[2702]
2013-04-26 09:46:31 +00:00
Denis Flaven
4ea0093f12 Enhancements to the Japanese localization, thanks to Shoji Seki
SVN:trunk[2701]
2013-04-25 09:24:35 +00:00
Romain Quetiez
2e35bb97d0 Compiler: added the capability to redefine icons in xml
SVN:trunk[2700]
2013-04-24 16:02:24 +00:00
Romain Quetiez
519b9f1a73 Needed => define if not exists
SVN:trunk[2699]
2013-04-24 15:50:44 +00:00
Denis Flaven
85c1f0d1aa New internal flag for generating the delta.
SVN:trunk[2698]
2013-04-23 15:33:40 +00:00
Romain Quetiez
f2738a79a0 User Portal:
- Portal power users can see ALL the tickets of the current customer
- Reopen and Close are done in a separate dialog box where the user will input requested data
- On closed tickets, the user satisfaction and comments are displayed

SVN:trunk[2697]
2013-04-18 15:23:43 +00:00
Romain Quetiez
4ae69372d4 User rights:
- for link classes, if no grant has been given explicitely, then check if one of the remote class has a grant (mix of compile-time and run-time changes)
- fixed an issue: when looking for a grant into the hierarchy, the most explicit declaration (aka the deepest class) must be found first -> reorder the parent classes (new flag on MetaModel::EnumParentClasses

SVN:trunk[2696]
2013-04-18 09:03:08 +00:00
Romain Quetiez
cce2509b2e #702 The second implementation of this fix was still incomplete (not compatible with some constraint queries)
SVN:trunk[2694]
2013-04-17 16:11:45 +00:00
Romain Quetiez
aa4d396960 #702 The first implementation of this fiw was not incomplete (not compatible with some constraint queries)
SVN:trunk[2692]
2013-04-17 15:53:34 +00:00
Romain Quetiez
5f11cc4cf8 #703 HTML entities not escaped in history tab
SVN:trunk[2690]
2013-04-16 13:23:10 +00:00
Denis Flaven
956ecda77e Fix for the "Notifications" tab: use the polymorphism to let each trigger determine which object is "In Scope" and thus can potentially have notifications related to it
SVN:trunk[2688]
2013-04-16 12:52:43 +00:00
Erwan Taloc
bb554b1271 define correct file name for en , es_cr, fr dictionnaries
Remove obsolete dictionaries

SVN:trunk[2685]
2013-04-16 11:44:51 +00:00
Erwan Taloc
15000ff48a remove unused dictionaries
SVN:trunk[2684]
2013-04-16 11:38:52 +00:00
Erwan Taloc
14d0ebdd71 define correct file name for en , es_cr, fr dictionnaries
SVN:trunk[2683]
2013-04-16 11:36:52 +00:00
Romain Quetiez
c95b8cf939 #702 Ignoring the class when looking up for a polymorphic ext key
SVN:trunk[2681]
2013-04-16 11:24:34 +00:00
Denis Flaven
74575440d7 Bug fix: properly serialize Emails with binary Attachments. Previously asynchronous emails with binary attachements were not working (they were queued but never sent).
SVN:trunk[2679]
2013-04-16 09:05:56 +00:00
Erwan Taloc
fcb7798e9e Remove dictionaries from module definition
SVN:trunk[2677]
2013-04-11 15:16:10 +00:00
Erwan Taloc
b0a054ada7 Remove dictionaries from module definition
SVN:trunk[2676]
2013-04-11 15:15:09 +00:00
Denis Flaven
b83d42efee Bug fix: allow printing lists fully expanded without the "pager" navigation.
Also try to completely hide the main menu on the left when printing.

SVN:trunk[2675]
2013-04-08 11:57:07 +00:00
Romain Quetiez
2e18c96328 Reviewed the readme file
SVN:trunk[2673]
2013-04-02 14:23:42 +00:00
Denis Flaven
2daccbe29d One more bug fix listed.
SVN:trunk[2672]
2013-04-02 14:13:09 +00:00
Denis Flaven
51d9c30315 #696: the message "Please fill all mandatory fields" is now localized (done in English, French and German)
SVN:trunk[2671]
2013-04-02 13:51:05 +00:00
Romain Quetiez
3d79e3fe8f #472 REST API: finalized the implementation of core/get_related + added the support for AttributeBlob (to manage documents and attachments)
SVN:trunk[2669]
2013-04-02 13:31:52 +00:00
Denis Flaven
57b34d2d91 Preparing for the 2.0.1-beta release...
SVN:trunk[2668]
2013-04-02 10:04:57 +00:00
Denis Flaven
a5a8863b52 Fixed transparent background issues...
SVN:trunk[2667]
2013-04-02 09:48:01 +00:00
Denis Flaven
d92b0aabee First (draft) version of the get_related verb for the REST API.
SVN:trunk[2666]
2013-04-02 09:42:26 +00:00
Denis Flaven
48472b6150 - Added support for creating symbolic links via the toolkit
- Added more debug info in the setup.log about the detection of the previously installed modules

SVN:trunk[2665]
2013-03-29 15:44:06 +00:00
Denis Flaven
fc29424600 #698 SeparatorPopupMenuItem was not working.
SVN:trunk[2664]
2013-03-29 13:53:51 +00:00
Denis Flaven
c4942dd747 #697: properly export NULL dates in "spreadsheet" format.
SVN:trunk[2662]
2013-03-29 13:32:47 +00:00
Denis Flaven
30b9afe165 REST API bug fix: properly handle key->finalclass
SVN:trunk[2661]
2013-03-29 10:54:23 +00:00
Denis Flaven
6d66969ff3 Better display of empty dates in the status report
SVN:trunk[2660]
2013-03-28 10:01:21 +00:00
Denis Flaven
667f258ec2 Preserve POSted parameters on the login web page (useful when the session expires)
SVN:trunk[2659]
2013-03-28 09:59:49 +00:00
Denis Flaven
badff05995 More readable edition for AttributeDuration (number are right justified)
SVN:trunk[2658]
2013-03-28 09:58:28 +00:00
Denis Flaven
052128da81 #694: make $__comp_menus__ really global !
SVN:trunk[2655]
2013-03-25 13:31:51 +00:00
Romain Quetiez
57629051bd #691 Notifications not sent if some recicipients have an empty address
SVN:trunk[2653]
2013-03-21 14:42:34 +00:00
Romain Quetiez
2cc89ad167 #690 XML export broken
SVN:trunk[2651]
2013-03-20 17:26:40 +00:00
Romain Quetiez
387ab05fd2 #674 request_type:servicerequest changed into service_request - added the DB update to allow an upgrade
SVN:trunk[2650]
2013-03-20 15:51:13 +00:00
Romain Quetiez
5de85c9d97 #688 ... regression in the search forms when the autocomplete is active
SVN:trunk[2648]
2013-03-20 15:10:18 +00:00
Romain Quetiez
d7fa2ca5b9 #688 When the autocomplete is activated, and the allowed values depend on another value, then it is possible to set a wrong value
SVN:trunk[2646]
2013-03-20 14:47:52 +00:00
Romain Quetiez
3d2866a2a0 #687 Finalized the bug fixed started in [2632], about attributes/hierarchies/labels (dictionary)
SVN:trunk[2644]
2013-03-20 10:00:19 +00:00
Denis Flaven
5d805a123e #683 : allow installation on a DB which names begins with numbers
+ directory cleanup for supporting symblinks

SVN:trunk[2639]
2013-03-19 11:38:53 +00:00
Erwan Taloc
584e1fade0 Add reconciliation keys for SLT in order to allow import for SLT having the same name
SVN:trunk[2638]
2013-03-18 17:49:27 +00:00
Erwan Taloc
98fa1fd071 Add reconciliation keys for SLT in order to allow import for SLT having the same name
SVN:trunk[2637]
2013-03-18 17:48:43 +00:00
Erwan Taloc
2b0b34a3a6 Remove wrong dependency to service_id on parent_request_id
SVN:trunk[2636]
2013-03-18 17:47:31 +00:00
Erwan Taloc
a3213bba43 Remove wrong dependency to service_id on parent_request_id
SVN:trunk[2635]
2013-03-18 17:46:56 +00:00
Erwan Taloc
1fc6b945f6 Add reconciliation key for Software (Fix Trac #666)
SVN:trunk[2634]
2013-03-18 17:46:02 +00:00
Denis Flaven
58aaed567f Bug fix: Data model alternate options were not properly checked when upgrading (especially when selecting ITIL tickets)
SVN:trunk[2633]
2013-03-18 17:39:26 +00:00
Romain Quetiez
51628604bf #687 Label for attribute Person:name always shown in english (Last Name)
SVN:trunk[2632]
2013-03-18 17:38:28 +00:00
Romain Quetiez
a35ff29363 #677 Cosmetics in the german localization (and a few other languages): first header of the config mgmt overview
SVN:trunk[2631]
2013-03-18 17:00:14 +00:00
Romain Quetiez
b52be60776 #684 CSV import / reconciliation using an enum does not take the translation into account
SVN:trunk[2630]
2013-03-18 10:26:14 +00:00
Romain Quetiez
d60d634208 #682 Order notifications (last first).
SVN:trunk[2629]
2013-03-15 16:25:02 +00:00
Romain Quetiez
b112597df8 Compiler: typo preventing from setting the property 'min_autocomplete_chars' on an external key
SVN:trunk[2628]
2013-03-15 10:07:50 +00:00
Romain Quetiez
a965bbd39f #626 Fixed missing translation in dictionaries (Tickets: "relations", and Contacts overview / count)
SVN:trunk[2627]
2013-03-14 17:37:19 +00:00
Romain Quetiez
cc70570e65 #657 JavaScript error when modifying UserLDAP object with Sync
SVN:trunk[2626]
2013-03-14 14:46:51 +00:00
Romain Quetiez
c1fae7fd24 #659 exception handling producing notices, hence causing confusion
SVN:trunk[2625]
2013-03-14 14:22:27 +00:00
Romain Quetiez
d620516055 #675 Error when drilling down on graph/pie/table with group by on a field that can be null (this case has been excluded)
SVN:trunk[2624]
2013-03-14 13:00:28 +00:00
Romain Quetiez
0918c81d58 #680 Setup failing to display the check report when DOM extension not enabled (php-xml not installed on redhat distributions)
SVN:trunk[2623]
2013-03-14 10:14:53 +00:00
Romain Quetiez
39b79e2a05 #660 Warning raised with ZendServer (with APC cache enabled) causing the setup to fail
SVN:trunk[2622]
2013-03-14 10:12:05 +00:00
Romain Quetiez
3340ca2b10 #679 Improved the reporting in case of an error while loading a module: 1) the list of already loaded modules is given, 2) the full path of the searched node is given
SVN:trunk[2621]
2013-03-14 09:07:35 +00:00
Denis Flaven
67dc148069 Better error reporting when loading a module fails.
SVN:trunk[2620]
2013-03-13 16:54:32 +00:00
Denis Flaven
c6ba656f1d Sort the modules before processing them for dependencies, in order to obtain a predictable result independent from the order of the modules in the file system... hopefully... (should fix Trac#679)
SVN:trunk[2619]
2013-03-13 16:52:10 +00:00
Romain Quetiez
acf4c7a28a #664 Could not login after upgrade of an iTop 1.x with a DB prefix
SVN:trunk[2618]
2013-03-13 14:37:46 +00:00
Romain Quetiez
b38dea4bba #634 Detection of HTTPS not working with nginx (iTop always considering the current connection as being secure)
SVN:trunk[2617]
2013-03-13 13:57:51 +00:00
Romain Quetiez
3cf398618e #472 REST Services: added core/delete (to bulk delete, full-featured), and validated the operation core/apply_stimulus
SVN:trunk[2616]
2013-03-13 11:01:16 +00:00
Romain Quetiez
462f163d8a #661 and #662 Could not create a user request (or ?) as soon as the autocomplete feature gets active
SVN:trunk[2615]
2013-03-12 16:09:03 +00:00
Erwan Taloc
96b3e9a891 Fix issue for creation of Logical Interface
SVN:trunk[2614]
2013-03-10 09:08:04 +00:00
Erwan Taloc
b56242808d Fix bug TTO / TTR computation for Service request (Trac 674)
SVN:trunk[2613]
2013-03-10 08:50:42 +00:00
Erwan Taloc
793f94d302 Correction to display IP field for Physical Device :
Network Device
Server
Storage System
NAS
Tape Library
SAN switch

SVN:trunk[2612]
2013-03-10 08:46:57 +00:00
Romain Quetiez
a23569e0c4 Fixed issue with the new CRON execution pattern
SVN:trunk[2611]
2013-03-08 14:03:55 +00:00
Romain Quetiez
80a8b63498 Modified the mechanism to display object dedicated messages (allows the plugin to add their message or replace standard ones)
Factorized the code to bulk update / bulk delete objects in an interactive way.

SVN:trunk[2610]
2013-03-08 13:36:31 +00:00
Denis Flaven
eee8d71381 Enhancement: take into account the "periodicity" of the background processes.
Bug fix: fixed a warning in CheckStopWatchThresholds

SVN:trunk[2609]
2013-03-01 17:14:54 +00:00
Denis Flaven
9013910cec Protect against unwanted output that may corrupt the JSON results
SVN:trunk[2607]
2013-02-27 16:14:16 +00:00
Denis Flaven
ddff9180ac Added 'core/apply_stimulus' as a possible operation for the REST web services.
SVN:trunk[2605]
2013-02-27 15:58:16 +00:00
Denis Flaven
4988d6eb04 Allow retrieving of the Filter used by a DisplayBlock (useful for extending search forms)
SVN:trunk[2603]
2013-02-27 14:31:27 +00:00
Denis Flaven
5ff86a40d9 Fix for making iUIPageExtension usable !
SVN:trunk[2600]
2013-02-27 13:37:12 +00:00
Denis Flaven
05133aa319 Fix for supporting the CSV export of big audit results.
SVN:trunk[2598]
2013-02-06 15:44:59 +00:00
Denis Flaven
626e2a1db1 Enable support of databases which name either is a reserved word or contains non-alphanumeric characters (i.e. itop-production).
SVN:trunk[2595]
2013-01-31 15:14:23 +00:00
Romain Quetiez
59e1a64f2d REST services: fixed issue with returned external keys
SVN:trunk[2594]
2013-01-30 14:09:32 +00:00
Denis Flaven
a9ac7d9e10 Added support for GET/JSON-P
SVN:trunk[2593]
2013-01-30 13:49:12 +00:00
Romain Quetiez
7b2789479d REST services: alpha2. It is now extensible (implement iRestServiceProvider). Still lacks two verbs: apply_stimulus and delete.
SVN:trunk[2592]
2013-01-30 09:46:55 +00:00
Denis Flaven
4e8db37060 Added credits to Stephan Rickauer about security.
SVN:trunk[2591]
2013-01-22 17:55:20 +00:00
Denis Flaven
80b0a8b942 Fix for Trac #670: XSS vulnerability issue.
SVN:trunk[2589]
2013-01-22 17:39:16 +00:00
Romain Quetiez
32924bc054 REST services: an alpha version. It is already possible to create/update/get objects. An example illustrates the possibilities.
SVN:trunk[2586]
2013-01-16 13:57:51 +00:00
Romain Quetiez
96530a5bdf Auto-documentation: first step => the extension APIs
SVN:trunk[2585]
2013-01-04 13:43:52 +00:00
Denis Flaven
18eee44ee6 Bug fix: properly record history of Hierarchical Keys
SVN:trunk[2584]
2012-12-21 15:37:45 +00:00
Denis Flaven
0d0cce9195 Icon update
SVN:trunk[2580]
2012-12-14 09:52:15 +00:00
Denis Flaven
92bf38a18e Icon update
SVN:trunk[2579]
2012-12-14 09:44:48 +00:00
Denis Flaven
08e4b306f0 Icons update
SVN:trunk[2578]
2012-12-14 09:34:04 +00:00
Denis Flaven
fcf04a4584 Added info about an IE8 patch
SVN:trunk[2577]
2012-12-14 09:03:59 +00:00
Denis Flaven
344932c063 Hide menu pane automatically if requested. Useful for automation.
SVN:trunk[2576]
2012-12-14 09:02:30 +00:00
Romain Quetiez
b8fd4014ec Update of the german dictionary by David Gumble (#654)
SVN:trunk[2575]
2012-12-14 08:47:58 +00:00
Romain Quetiez
b22eae043c #653 Document notes and FAQ: when data is longer than 64Kb, then at each modification of the text, an error is displayed and the change is not visible in the history tab (but data correctly saved).
SVN:trunk[2574]
2012-12-13 15:37:19 +00:00
Erwan Taloc
0f2815b202 change lable for simple ticket management module
SVN:trunk[2573]
2012-12-13 15:24:59 +00:00
Denis Flaven
ddc73d13c2 Don't show the CSV import history (by default) for a faster display.
SVN:trunk[2572]
2012-12-13 14:55:11 +00:00
Denis Flaven
cd82ff981f Removed the compiler's log from the overall setup log.
SVN:trunk[2571]
2012-12-13 14:51:45 +00:00
Denis Flaven
4fb7f612b1 Removed the "verbose" option from the mysqldump command.
SVN:trunk[2570]
2012-12-13 14:50:56 +00:00
Denis Flaven
58f12afdcd Restore authentication for generating the life-cycle charts in the right language.
SVN:trunk[2569]
2012-12-13 14:37:20 +00:00
Denis Flaven
8ab0b682db Patch to add the support of indexOf for IE8.
SVN:trunk[2568]
2012-12-13 14:33:32 +00:00
Erwan Taloc
646b46f2b6 Allow & inside email addresses
SVN:trunk[2567]
2012-12-13 14:23:28 +00:00
Erwan Taloc
b052ba5c59 fix typo in French dictionary
SVN:trunk[2566]
2012-12-13 14:21:26 +00:00
Erwan Taloc
2af1411a9e ConnectableCI is now an abstract class
SVN:trunk[2565]
2012-12-13 14:19:46 +00:00
Erwan Taloc
72118db350 Class VirtualDevice is now an abstract class
SVN:trunk[2564]
2012-12-13 14:18:57 +00:00
Erwan Taloc
fef19c5c99 lnkServerToVolume marked as a link in the datamodel
SVN:trunk[2563]
2012-12-13 14:16:40 +00:00
Romain Quetiez
cc8db51b33 Updated the readme for the release 2.0
SVN:trunk[2562]
2012-12-12 16:58:29 +00:00
Romain Quetiez
7245c67469 Completed missing impact/dependency rules (Datacenter device depends on power connection, and PDU depending on power connection)
SVN:trunk[2561]
2012-12-12 15:50:18 +00:00
Denis Flaven
d6a4ac64fd New icons replacing some non-free (?) ones.
SVN:trunk[2560]
2012-12-12 15:33:11 +00:00
Romain Quetiez
196826062a Typo in the data model
SVN:trunk[2559]
2012-12-12 14:57:37 +00:00
Denis Flaven
402b907c8e Fix for JS error in IE 8 (reported via the forum)
SVN:trunk[2558]
2012-12-11 13:35:43 +00:00
Romain Quetiez
b038bc3905 Cleanup in the setup manual instructions
SVN:trunk[2557]
2012-12-11 10:19:28 +00:00
Romain Quetiez
b41e7ab3db Cleanup in the setup manual instructions
SVN:trunk[2556]
2012-12-11 10:16:28 +00:00
Romain Quetiez
fd59c97ce1 Cleanup in the setup manual instructions (preliminary step)
SVN:trunk[2555]
2012-12-11 09:50:38 +00:00
Erwan Taloc
e2eb232e0f Correct wrong linkset in Change management ITIL module
SVN:trunk[2554]
2012-12-10 13:32:22 +00:00
Romain Quetiez
62b3f80a4c #629 Nested object creation (button plus) does not work if:
- PHP 5.4
- notification sent on object creation
- PHP error level allows notices

SVN:trunk[2553]
2012-12-07 13:38:14 +00:00
Romain Quetiez
4d9c632060 #560 typo for german translation of "Metric"
SVN:trunk[2552]
2012-12-07 12:32:37 +00:00
Denis Flaven
99240e997c Update of the lifecycle images
SVN:trunk[2551]
2012-12-07 12:06:16 +00:00
Romain Quetiez
bf067d4ac4 Fixed profile issue: nobody had the right to create/modify links btw VirtualDevice and LogicalVolume
SVN:trunk[2550]
2012-12-07 11:50:42 +00:00
Denis Flaven
b00b8dc05a Added some checks around the configuration of PHP sessions since it seems to cause a lot of troubles.
SVN:trunk[2549]
2012-12-07 10:57:46 +00:00
Denis Flaven
a651105675 Added some checks around the configuration of PHP sessions since it seems to cause a lot of troubles.
SVN:trunk[2548]
2012-12-07 10:33:26 +00:00
Erwan Taloc
55354389dc update French translation of menus for Change management module
SVN:trunk[2547]
2012-12-06 20:32:47 +00:00
Erwan Taloc
ce4c187fcf update Change ITIL overview
SVN:trunk[2546]
2012-12-06 20:30:22 +00:00
Erwan Taloc
25a03b4c5e update Change ITIL overview
update French translation of menus

SVN:trunk[2545]
2012-12-06 20:28:19 +00:00
Denis Flaven
f0ae02fd8e Integration of the "bridge" module and new mechanism for auto_select modules.
SVN:trunk[2544]
2012-12-06 17:51:52 +00:00
Denis Flaven
aa6cfc205e - Show only the "vsisible" parameters in the config file, or the ones already present in case of upgrade. Hide others for readability.
SVN:trunk[2543]
2012-12-06 17:33:57 +00:00
Romain Quetiez
ae1be5a53b New module to link storage and virtualization
SVN:trunk[2542]
2012-12-06 16:54:16 +00:00
Denis Flaven
d5467ca383 Remove black border around icons in the dashbaord editor (class and icon popup menus)
SVN:trunk[2541]
2012-12-06 15:09:36 +00:00
Romain Quetiez
cb4735ba6b When iTop is in read only mode, then the portal displays a banner and prevent the user from accessing the modification forms
SVN:trunk[2540]
2012-12-06 11:42:13 +00:00
Romain Quetiez
bee851155a Continuation of [2524]: fix the attachements URL issue (was not changed only on datamodel 1.x then overwritten on a more recent change)
This fixes the issue #645 for good, I hope.

SVN:trunk[2539]
2012-12-06 11:05:29 +00:00
Denis Flaven
7c13a6286e Typo causing a bug in IE8 when removing an element from a n:n linkset.
SVN:trunk[2538]
2012-12-06 10:55:23 +00:00
Denis Flaven
156993a517 - Show only the "vsisible" parameters in the config file, or the ones already present in case of upgrade. Hide others for readability.
- Properly check DB connection in case of upgrade (setup wizard)
- Cleanup old remains of the V1.x setup program.

SVN:trunk[2537]
2012-12-06 10:53:42 +00:00
Romain Quetiez
074551c506 #647: itop says "ticket updated" but nothing has been changed.
+ Do not send a notification if nothing has been changed from the portal web page
Related to #398 which seems to be fixed

SVN:trunk[2536]
2012-12-06 10:52:27 +00:00
Erwan Taloc
dead18c8fd Reset reject reason when approved
Fix display of reject reason when required

SVN:trunk[2535]
2012-12-06 10:49:29 +00:00
Erwan Taloc
0f2334052b Reset reject reason when approved
SVN:trunk[2534]
2012-12-06 10:48:40 +00:00
Erwan Taloc
7dc7b23dc3 Remove icone for Virtual Device and Virtual Host
SVN:trunk[2533]
2012-12-06 10:41:07 +00:00
Erwan Taloc
9857d9444c Fix icone issue for NASFileSystem and Fiber Channel Interface
SVN:trunk[2532]
2012-12-06 10:40:38 +00:00
Denis Flaven
4804a2994e Make auto_reload work on OQL menus
Fix for table configuration for tables resutling from an OQL menu

SVN:trunk[2531]
2012-12-06 09:47:53 +00:00
Denis Flaven
5b8dc80779 auto-reload set to fast everywhere it is used.
SVN:trunk[2530]
2012-12-06 09:40:58 +00:00
Erwan Taloc
04a037a91d update Case for Request management menu
SVN:trunk[2529]
2012-12-06 09:21:51 +00:00
Erwan Taloc
0b30cebd6a update profiles for problem manager and service manager to make sure they are allowed to manage the element they are responsible for
SVN:trunk[2528]
2012-12-06 09:17:57 +00:00
Erwan Taloc
64bc64dd95 Add icone for a work order
SVN:trunk[2527]
2012-12-06 09:09:54 +00:00
Romain Quetiez
7f45a40e5d #643 Output corrupted by characters after the PHP closing bracket (attachements get corrupted)
SVN:trunk[2526]
2012-12-06 08:56:50 +00:00
Denis Flaven
17d98c9236 Safer regexpr for DateTimes (don't let 2012-12-05 1900:00 pass through since it turns into 0000-00-00 00:00:00 in MySQL) but allow to omit the seconds (i.e. 2012-12-05 18:45 will become 2012-12-05 18:45:00 in MySQL)
SVN:trunk[2525]
2012-12-05 18:02:45 +00:00
Denis Flaven
b3e7068279 Bug fix: Incorrect URL for downloading attachments, that nobody really to noticed before since it seems to work on some web servers... weird !!!
SVN:trunk[2524]
2012-12-05 17:36:05 +00:00
Denis Flaven
5ffc5ff50a Fixed Trac #628: sort order and configurable tables
SVN:trunk[2523]
2012-12-05 14:59:51 +00:00
Erwan Taloc
cc8c034520 update Case for Service management menu
SVN:trunk[2522]
2012-12-05 14:58:33 +00:00
Erwan Taloc
b7e22f9ffe update Case for Service management menu
SVN:trunk[2521]
2012-12-05 14:58:12 +00:00
Erwan Taloc
3b1c46d13f update Case for request management menu
add transition to escalated ttr

SVN:trunk[2520]
2012-12-05 14:57:45 +00:00
Erwan Taloc
b6288aeb6b update Case for request management menu
add transition to escalated ttr

SVN:trunk[2519]
2012-12-05 14:57:14 +00:00
Erwan Taloc
0d95e11f04 update Case for Problem management menu
SVN:trunk[2518]
2012-12-05 14:56:37 +00:00
Erwan Taloc
b6c670a55b update Case for Problem management menu
SVN:trunk[2517]
2012-12-05 14:56:19 +00:00
Erwan Taloc
e4a6b37fb0 update Case for incident management menu
add transition to escalated ttr

SVN:trunk[2516]
2012-12-05 14:55:48 +00:00
Erwan Taloc
1cefd3b35f update Case for configuration management menu
SVN:trunk[2515]
2012-12-05 14:50:01 +00:00
Erwan Taloc
46cde34254 update Case for change management menu
SVN:trunk[2514]
2012-12-05 14:49:15 +00:00
Erwan Taloc
1f0acf7a0a update Case for change management menu
SVN:trunk[2513]
2012-12-05 14:48:47 +00:00
Romain Quetiez
020994c23a Russian updated (source: Shamil Khamit)
SVN:trunk[2512]
2012-12-05 14:46:18 +00:00
Denis Flaven
3e2e0be05e Set "portal" as the origin of user requests created from the portal.
SVN:trunk[2511]
2012-12-05 13:37:13 +00:00
Romain Quetiez
7561f8f8da Fixed regression due to the recent optimization of SQL queries
SVN:trunk[2510]
2012-12-05 12:43:35 +00:00
Romain Quetiez
64c9c1fead Added a comment to explain why the APIs AddCondition_PointingTo and AddCondition_ReferencedBy must be left unprotected (do not clone the passed filter). See trac #639
SVN:trunk[2509]
2012-12-05 10:32:13 +00:00
Romain Quetiez
fb8e2ce1a4 #640 Resolved requests shown in both lists (opened and resolved)
SVN:trunk[2508]
2012-12-05 09:52:19 +00:00
Denis Flaven
ed6bbe6d07 New extension API: iPageUIExtension to alter the display of *each* iTopWebPage.
SVN:trunk[2507]
2012-12-05 09:49:24 +00:00
Denis Flaven
0a0e7c01fd Perf enhancements: don't build trace information if trace is not required.
SVN:trunk[2506]
2012-12-05 09:45:53 +00:00
Denis Flaven
0221a65f82 Performance enhancement for impact analysis: avoid looping in the recursion.
SVN:trunk[2505]
2012-12-05 08:49:38 +00:00
Erwan Taloc
0e3b2a9bf1 update profile for change manager, implementor and supervisor for basic change management module
SVN:trunk[2504]
2012-12-04 18:25:07 +00:00
Denis Flaven
5808c0a8a7 Fix for a SQL error (regression) in the Portal
SVN:trunk[2503]
2012-12-04 17:25:02 +00:00
Denis Flaven
3b0e1d9a3b Prevent a crash when not authorized to see an object of a derived class.
SVN:trunk[2502]
2012-12-04 15:21:59 +00:00
Romain Quetiez
24435401a5 Fixed issue in CSV export: null enums rendered as 'undefined' whereas '' is the value expected in the import (See an export of Organization/status)
SVN:trunk[2501]
2012-12-04 15:17:22 +00:00
Denis Flaven
07d88199b4 New parameter to tweak the display of Impact anaysis: which tab to display first? list or graphics?
SVN:trunk[2500]
2012-12-04 14:50:59 +00:00
Romain Quetiez
46005c19ce Allow the deletion of a shortcut while preserving it from being edited through UI.php
SVN:trunk[2499]
2012-12-04 14:46:18 +00:00
Denis Flaven
442c0d6956 Prevent a server crash when using together APC cache and Mcrypt
SVN:trunk[2498]
2012-12-04 14:01:51 +00:00
Romain Quetiez
1e155ffc13 Fixed stopper issue (found with an audit) due to copies of DBObjectSearch not cloned (or not cloned well)
There is still one place where this should be fixed, but it reveals another bug so we've decided to leave it as is for the moment (see comment in DBObjectSearch::AddCondition_PointingTo)

SVN:trunk[2497]
2012-12-04 13:26:48 +00:00
Romain Quetiez
90bc24d5c0 Optimization of SQL queries: fixed two issues (SELECT to track object linked to... and SELECT ExternalUser)
SVN:trunk[2496]
2012-12-03 17:00:38 +00:00
Romain Quetiez
fe27ee4f22 Debugging: run_query now produces the debug output (log_kpi_duration)
SVN:trunk[2495]
2012-12-03 11:24:19 +00:00
Denis Flaven
84e498e744 Protect against empty email addresses
SVN:trunk[2494]
2012-12-03 09:13:39 +00:00
Erwan Taloc
2af564bc03 Update descriptions for each linkset
Add Application solution tab for Virtual machines


SVN:trunk[2493]
2012-12-01 16:54:06 +00:00
Erwan Taloc
b0c48ce2a7 Change Name into Last Name for a object person in English dictionary (Trac #530)
Fix icone issue for change mgmt
Allow selection of Location defined in each parent location for Person and Physical Devices (Trac #412)
Make sure all names are mandatory in the module Service Management , Service Management Provider (Trac #530)
Fix wrong impact rules for storage system

SVN:trunk[2492]
2012-12-01 12:46:23 +00:00
Erwan Taloc
14520dabc0 Prompt planned start and end date for change for status planned (Trac #563)
Change Name into Last Name for a object person in English dictionary (Trac #530)
Allow service manager to delete objects in the service catalog (Trac #506)

SVN:trunk[2491]
2012-12-01 12:41:41 +00:00
Erwan Taloc
8a3d922882 All Service Manager to delete objects from service catalog (Trac #506)
SVN:trunk[2490]
2012-11-30 17:31:00 +00:00
Erwan Taloc
faa1c37613 Add dependency between server and logical volume (Trac #617)
Add Pertual field for Licence (Trac #535)
Make Name mandatory for all CIS

SVN:trunk[2489]
2012-11-30 17:30:08 +00:00
Denis Flaven
a0d267ed2f Bug fix: "Configure this list" was not working on some ,lists (with a ID containing a colon (:) from a Menu)
SVN:trunk[2488]
2012-11-30 16:57:37 +00:00
Denis Flaven
09209533d0 Support multiple recipients in To: Cc: and Bcc:
SVN:trunk[2487]
2012-11-30 16:46:50 +00:00
Romain Quetiez
3784a41d9e Optimization of SQL queries: fixed!
SVN:trunk[2486]
2012-11-30 16:26:53 +00:00
Romain Quetiez
78cb9f793a Optimization of SQL queries: reduce the number of JOINS, assuming that data are consistent. Can be disabled with config setting query_optimization_enabled => 0.
Also fixed caching issue (reproduced when replaying a query log)

SVN:trunk[2485]
2012-11-30 13:34:46 +00:00
Romain Quetiez
941d056db4 Improved the query logging + replay mechanism
SVN:trunk[2484]
2012-11-30 13:27:07 +00:00
Denis Flaven
d69163199b Fix for Trac #569: Mandatory date (and time) fields are prefilled with the current date (and time).
SVN:trunk[2483]
2012-11-30 10:51:15 +00:00
Denis Flaven
b563a64eb6 Added PHP and MySQL versions and OS to the statistics
SVN:trunk[2482]
2012-11-30 10:08:26 +00:00
Denis Flaven
a975974fc0 Fix for Trac #608: install broken on PHP < 5.2.17
SVN:trunk[2481]
2012-11-30 09:23:17 +00:00
Denis Flaven
b87abb7603 Make sure that we don't call OnFormCancel during a true submit.
SVN:trunk[2480]
2012-11-28 17:51:46 +00:00
Denis Flaven
0313f54d3e Fix for "undefined property" as reported in Trac#629
SVN:trunk[2479]
2012-11-28 17:50:24 +00:00
Denis Flaven
63cb32b7a7 Fix for Trac #497: allow bulk modification of "duration" fields.
- Proper display of the modifications on the SynchroAttributes in the History of a SynchroDataSource
- Better display of the history of Boolean atrtibutes (false is no longer displayed as an empty string)

SVN:trunk[2478]
2012-11-28 17:45:00 +00:00
Denis Flaven
323147c7f4 Fixed Trac #619 : Added the option -- single-transaction to the mysqldump command to avoid locking issues with non-existing definer accounts.
SVN:trunk[2477]
2012-11-28 11:17:45 +00:00
Denis Flaven
a9d17903cf Fix for Trac#627: Don't log the parameters in the call stack in case of exception to protect sensitive data.
SVN:trunk[2476]
2012-11-28 10:53:43 +00:00
Denis Flaven
b3b19bcaf6 Prevent a undefined varialbe when logging directly to a details page (bookmarked page)
SVN:trunk[2475]
2012-11-28 09:04:28 +00:00
Denis Flaven
e0800944a8 Make sure that CSV files end with a proper carriage return
SVN:trunk[2474]
2012-11-28 09:03:30 +00:00
Denis Flaven
352a95decb Added a detailed log of the compiler (temporary ? for debugging purposes ?)
SVN:trunk[2473]
2012-11-28 09:01:43 +00:00
Romain Quetiez
85974da27b New developer tool: cosmetics
SVN:trunk[2472]
2012-11-27 17:24:35 +00:00
Denis Flaven
e1d2723283 Fix for Trac#589, do not use ExchangeException here ! Copy/paste killed us ?
SVN:trunk[2471]
2012-11-27 17:21:23 +00:00
Denis Flaven
0818a67e75 Fixed bug #624: corrupted display of the log when updating from the portal.
Also fixed a bit the portal's stylesheet and prevent calling OnFormCancel on a true submit !!

SVN:trunk[2470]
2012-11-27 17:11:47 +00:00
Romain Quetiez
9b982df4f0 New developer tool: set log_queries=1 to enable query logging into data/
The accumulative log data/queries.log can be replayed with test/replay_query_log.php which produces a result file (to check the stability of the results) and a benchmark file (to see the efficiency in CSV)

SVN:trunk[2469]
2012-11-27 16:53:24 +00:00
Denis Flaven
51af2e9662 Fix for Trac #625: CSV export of boolean attributes was broken (false => "")
SVN:trunk[2468]
2012-11-27 16:30:41 +00:00
Denis Flaven
f8f8c327af Typo: Sycnhro => Synchro
SVN:trunk[2467]
2012-11-27 16:28:22 +00:00
Denis Flaven
baf1e80cca Fix for Trac #584 (Documentation of needed piviledges)
SVN:trunk[2466]
2012-11-27 15:44:40 +00:00
Denis Flaven
5be8937518 Fix inconsistencies in the sample data.
SVN:trunk[2465]
2012-11-27 15:28:13 +00:00
Denis Flaven
2c9884cfa5 Fixed Trac #631: impossible to assign a routine or emergency change.
Cleanup: removed useless state definitions.

SVN:trunk[2464]
2012-11-27 14:52:53 +00:00
Denis Flaven
933bb62c6c Missing dict entries for "parent_incident_id".
SVN:trunk[2463]
2012-11-27 14:17:08 +00:00
Denis Flaven
b10b894b24 Protects the display of the history against renmaed/removed attributes.
SVN:trunk[2462]
2012-11-27 13:44:47 +00:00
Denis Flaven
35a3b145f8 Regression from previous fix...
SVN:trunk[2461]
2012-11-23 17:01:00 +00:00
Denis Flaven
c04ea6d220 Bug fix:
- Correct intial sort order of a table if the default sort column is not an "alphabetical" column (i.e. IP Address)
- Properly sort on the first column (name) when the name is made of a column with a different sort algorithm (i.e. IP Address).

SVN:trunk[2460]
2012-11-23 15:29:13 +00:00
Denis Flaven
ea48279a90 Bug fix:
- Correct intial sort order of a table if the default sort column is not an "alphabetical" column (i.e. IP Address)
- Properly sort on the first column (name) when the name is made of a column with a different sort algorithm (i.e. IP Address).

SVN:trunk[2459]
2012-11-23 15:25:55 +00:00
Romain Quetiez
9bc263b106 Fixed messed up dictionaries.
1) In english, we have the following:
- Delivery model class shown in japanese
- Server, Person and Subnet additional tabs shown in french
- Known errors menus shown in french
2) In ? we have the Attachments in german (but it is ok in english, for my installation...)


SVN:trunk[2458]
2012-11-22 09:08:41 +00:00
Denis Flaven
27619a9118 Changed Eugène to Eugene to work around an XML load problem.
SVN:trunk[2457]
2012-11-21 15:47:58 +00:00
Denis Flaven
ff2603a8fe Review of the readme. Ready for building Beta 2.
SVN:trunk[2456]
2012-11-21 15:22:21 +00:00
Romain Quetiez
4a0002058e Updated the readme for iTop 2.0 Beta 2
SVN:trunk[2455]
2012-11-21 14:58:08 +00:00
Romain Quetiez
656675206e #602 Description not shown in portal
SVN:trunk[2454]
2012-11-21 14:11:21 +00:00
Erwan Taloc
07fee4ac00 Fix tab translation for Subnet
SVN:trunk[2453]
2012-11-21 13:53:13 +00:00
Denis Flaven
084609bfad Increased memory_limit for the setup to 64 MB
SVN:trunk[2452]
2012-11-21 13:52:44 +00:00
Erwan Taloc
e1742fa24d Fix issue with Subnet usage computation
SVN:trunk[2451]
2012-11-21 13:48:20 +00:00
Romain Quetiez
241ac4c4d0 Fixed typo in spanish dictionary
SVN:trunk[2450]
2012-11-21 13:42:09 +00:00
Romain Quetiez
d75ad72057 Renamed classes/attributes and enums, for the latest dictionaries (based on names from 2.0 beta 1)
SVN:trunk[2449]
2012-11-21 13:31:23 +00:00
Denis Flaven
b2031c5e44 Changed mysqldump invocation to verbose mode (at least for the duration of the beta) in order to ease the troubleshooting of backup failures.
SVN:trunk[2448]
2012-11-21 13:26:10 +00:00
Denis Flaven
8fa41e2fd0 Localization update:
- German thanks to David Guembel
- Japanese thanks to Shoji Seki
- Spanish thanks to Miguel Turrubiates

SVN:trunk[2447]
2012-11-21 11:18:59 +00:00
Erwan Taloc
9016ce7149 add software list on PC devices
SVN:trunk[2446]
2012-11-21 11:18:52 +00:00
Erwan Taloc
cf38a3eec4 renumbering of dashlet id
SVN:trunk[2445]
2012-11-21 11:17:53 +00:00
Erwan Taloc
7bf1f9ca49 allow duplicate links for device / network connections
add PC software in Dashboard
All documentation of software on a PC device

SVN:trunk[2444]
2012-11-21 11:17:18 +00:00
Denis Flaven
1f9950507c Allow to automatically hide/show the menu pane as a parameter to the page (useful for automating the documentation)
SVN:trunk[2443]
2012-11-21 11:08:36 +00:00
Denis Flaven
a4ddc99446 Removed user-rights verification for generting the image... easier for generating the documentation
SVN:trunk[2442]
2012-11-21 11:06:31 +00:00
Denis Flaven
52c1830d42 GetTargetAttDef is made public (it's used for auto documenting)
SVN:trunk[2441]
2012-11-21 11:04:08 +00:00
Romain Quetiez
79e4a73408 Updated the russion translation, thanks to Shamil Khamit (note: this translation seems to be quite incomplete)
SVN:trunk[2440]
2012-11-21 11:03:27 +00:00
Denis Flaven
0f8f214367 Added more translators to the readme.
SVN:trunk[2439]
2012-11-21 11:01:15 +00:00
Romain Quetiez
914be40f02 Adapted the sample queries to the new datamodel.
SVN:trunk[2438]
2012-11-21 10:42:17 +00:00
Romain Quetiez
f196d03f6f error.log moved into the log directory
SVN:trunk[2437]
2012-11-21 10:19:28 +00:00
Romain Quetiez
f089d4282e Renamed priv_Userinternal into priv_userinternal: the upgrade must be idempotent!
SVN:trunk[2436]
2012-11-21 10:04:40 +00:00
Romain Quetiez
bedbc387eb Renamed priv_Userinternal into priv_userinternal, and added a check for table names (lowercase is the rule!)
SVN:trunk[2435]
2012-11-21 09:41:53 +00:00
Romain Quetiez
4d85ff60ab Fixed #618 and #621
+ renamed a few classes/enums/attributes to improve the classes naming consistency and ease the writing of OQLs
+ fixed errors in the profiles

SVN:trunk[2434]
2012-11-20 16:24:24 +00:00
Romain Quetiez
b38e818499 New feature: fixed bugs in shortcut edition (condition: list NOT paginated)
SVN:trunk[2433]
2012-11-19 10:25:38 +00:00
Erwan Taloc
b1fb7189de update profiles according to new data model
SVN:trunk[2432]
2012-11-19 09:07:00 +00:00
Romain Quetiez
7792b54d26 New feature: shortcuts to a search result. The feature is not automatically available with upgrade of custom versions -requires a ShortcutContainerMenuNode.
SVN:trunk[2431]
2012-11-16 11:21:00 +00:00
Erwan Taloc
8f398bd130 Change constraint on attribute agent_id for a workorder
SVN:trunk[2430]
2012-11-12 18:11:31 +00:00
Erwan Taloc
79db8a22e7 Make Request Mgmt module visible for all
Update translation for customer satisfaction

SVN:trunk[2429]
2012-11-12 18:11:12 +00:00
Erwan Taloc
058787a366 Make Request Mgmt module visible for all
Update translation for customer satisfaction

SVN:trunk[2428]
2012-11-12 18:09:35 +00:00
Erwan Taloc
4b99c584db Make Incident Mgmt module visible for all
Update translation for customer satisfaction

SVN:trunk[2427]
2012-11-12 18:09:07 +00:00
Erwan Taloc
b9d689baf1 update purchase_date type
SVN:trunk[2426]
2012-11-12 18:06:59 +00:00
Erwan Taloc
fe6c34030b update MakeIconeFrom name with good icone path
add transaltion for remated_problems_list

SVN:trunk[2425]
2012-11-12 18:05:02 +00:00
Erwan Taloc
b8f7fa975c update MakeIconeFrom name with good icone path
add transaltion for remated_problems_list

SVN:trunk[2424]
2012-11-12 18:03:37 +00:00
Denis Flaven
abdca9399d Bug fix: incorrect handling of "negative" selections in bulk delete
SVN:trunk[2423]
2012-11-12 15:05:17 +00:00
Romain Quetiez
3b93fcff3c #615 Fixed bug on multi column queries - wrong count resulting in strange effects in the display of results
SVN:trunk[2422]
2012-11-12 14:34:39 +00:00
Romain Quetiez
6987db54b5 #614 Fixed regression on multi column queries (could not display Null objects)
SVN:trunk[2421]
2012-11-12 14:32:29 +00:00
Denis Flaven
71d011b7f8 Prevent a crash when deleting objects accessed from a list of their abstract class (i.e. Select some Contacts, "delete...", select a Person, delete => crash).
SVN:trunk[2420]
2012-11-12 14:23:24 +00:00
Denis Flaven
b22c23dfee Bug fix: "save" in edit dashboard did nothing on Chrome on a *just reloaded* dashboard (actually on any page which URL contained a hash)
SVN:trunk[2419]
2012-11-12 10:36:34 +00:00
Romain Quetiez
59b7c0c025 Bug fix: display an empty string for the friendlyname of a NULL external key
SVN:trunk[2418]
2012-11-09 16:49:17 +00:00
Erwan Taloc
46e42f0ea2 Don't mind whether hidden fields are read-only or not.. they are hidden.
SVN:trunk[2417]
2012-11-09 15:12:16 +00:00
Romain Quetiez
bac286646b #611 Fixed regression in import.php
SVN:trunk[2416]
2012-11-09 08:44:00 +00:00
Denis Flaven
bd057018a0 Typo: Opened => Open
SVN:trunk[2415]
2012-11-08 14:22:29 +00:00
Denis Flaven
f2a63cbbce Bug fix: Trac#609 export/import was broken for dashboards with a non-alphanumeric identifier.
SVN:trunk[2414]
2012-11-08 13:02:38 +00:00
Romain Quetiez
247b1b4d78 Record the list of attachments (in EventNotificationEmail) + prerequisite to email-reply
SVN:trunk[2413]
2012-11-08 12:41:51 +00:00
Erwan Taloc
4ac9f0654c replace emergency by urgency for user portal variables
SVN:trunk[2412]
2012-11-02 08:56:04 +00:00
Denis Flaven
7cb5d60dd3 Rollback (temporarily) to fix the issue with linksets dependent on both sides.
SVN:trunk[2411]
2012-10-30 11:23:22 +00:00
Denis Flaven
2d04bfe2b9 Review of the readme for 2.0.0 beta
SVN:trunk[2410]
2012-10-30 09:28:16 +00:00
Romain Quetiez
954ae7cca0 Draft of the Readme for 2.0 beta
SVN:trunk[2409]
2012-10-30 08:53:08 +00:00
Romain Quetiez
0e6f2e2fb9 Workaround to an issue with reconciliation keys using "discarded" ext keys (target class implemented in an optional module)
SVN:trunk[2408]
2012-10-30 08:52:35 +00:00
Denis Flaven
73226092dc Change the versioning scheme: let's jump to 1.3.0 for this new branch.
SVN:trunk[2407]
2012-10-29 18:05:57 +00:00
Romain Quetiez
0abade970a MetaModel: when an ext key on a Link is discarded, then discard the corresponding linkset attributes
SVN:trunk[2406]
2012-10-29 17:55:51 +00:00
Erwan Taloc
78948c06fe Change Label of Friendly name from "Name" to "Friendy Name" to avoid misunderstanding when importing or synchronizing data
SVN:trunk[2405]
2012-10-29 17:47:37 +00:00
Denis Flaven
1df7fb44dd Make the two modules: Known Errors and Problem Mgmt independent from each other.
SVN:trunk[2403]
2012-10-29 13:54:21 +00:00
Denis Flaven
0048978cbd New option for MFNodes: define_if_not_exists !!
SVN:trunk[2402]
2012-10-29 13:51:39 +00:00
Romain Quetiez
cd745f1df1 Dashboards - make sure that dashlet ids are unique within a given dashboard (not a cell), which can become uneasy when the dashlets are spread amongst several modules
SVN:trunk[2401]
2012-10-29 12:56:40 +00:00
Denis Flaven
3a52e4cd83 Incorrect formatting visible only with IE !
SVN:trunk[2400]
2012-10-29 12:46:00 +00:00
Denis Flaven
a0d222d593 Correct detection of whether there are any changes or not compared to a std build.
SVN:trunk[2399]
2012-10-29 11:42:31 +00:00
Denis Flaven
b7ce575a5f Added the manifest of previous versions for safe upgrades.
SVN:trunk[2398]
2012-10-29 10:45:34 +00:00
Denis Flaven
0c35ef8085 Allow updating sample data while loading them !!
SVN:trunk[2397]
2012-10-26 17:01:53 +00:00
Denis Flaven
b59d5062c7 Invalid MACAddress value in sample data !!!
SVN:trunk[2396]
2012-10-26 16:08:30 +00:00
Denis Flaven
5105773996 Invalid MACAddress value in sample data !!!
SVN:trunk[2395]
2012-10-26 15:20:41 +00:00
Romain Quetiez
0c0a54db69 Internal: regression in the bulk install
SVN:trunk[2394]
2012-10-26 15:18:12 +00:00
Romain Quetiez
3a90af5fdc Internal: fixed reporting issue for bulk installer
SVN:trunk[2393]
2012-10-26 14:30:50 +00:00
Romain Quetiez
bbad591334 Internal: improved (a little) the API to detect datamodel inconsistencies
SVN:trunk[2392]
2012-10-26 14:29:17 +00:00
Denis Flaven
15edf33a56 Edit in-place for audit rules from their category.
SVN:trunk[2391]
2012-10-26 14:25:23 +00:00
Denis Flaven
e8341448b7 Cosmetics in the setup.
SVN:trunk[2390]
2012-10-26 14:12:51 +00:00
Denis Flaven
40cc93e5f5 Adapt the webservice to either 1.x or 2.x datamodels
SVN:trunk[2389]
2012-10-26 14:11:30 +00:00
Denis Flaven
5af0f5f320 Proper error reporting...
SVN:trunk[2388]
2012-10-26 13:57:40 +00:00
Erwan Taloc
050037acb2 set default order descending on ticket ref
SVN:trunk[2387]
2012-10-26 13:24:39 +00:00
Erwan Taloc
ba4c382fe8 add sample date related to virtualization
SVN:trunk[2386]
2012-10-26 13:17:34 +00:00
Erwan Taloc
2e7b991252 do not lot delivery model in this module
SVN:trunk[2385]
2012-10-26 13:15:57 +00:00
Erwan Taloc
1ada274c56 do not lot delivery model in this module
SVN:trunk[2384]
2012-10-26 13:15:34 +00:00
Erwan Taloc
5e3889a8a5 replace type Emergency by Urgency
SVN:trunk[2383]
2012-10-26 13:14:56 +00:00
Denis Flaven
39d8888890 Protect the webservices...
SVN:trunk[2382]
2012-10-26 13:11:02 +00:00
Erwan Taloc
d894ec0a9f replace type Emergency by Urgency
SVN:trunk[2381]
2012-10-26 13:09:47 +00:00
Erwan Taloc
9b8b95017d replace type Emergency by Urgency
SVN:trunk[2380]
2012-10-26 13:09:19 +00:00
Erwan Taloc
cbe7b7f3bb replace type Emergency by Urgency
SVN:trunk[2379]
2012-10-26 13:08:51 +00:00
Erwan Taloc
7103d524e0 update sample date to not load in CMDB core the data related to virtualization
SVN:trunk[2378]
2012-10-26 13:08:14 +00:00
Denis Flaven
bfa41966d9 Adapt the webservice to either 1.x or 2.x datamodels
SVN:trunk[2377]
2012-10-26 12:29:45 +00:00
Denis Flaven
85ee91a38c Fix: the configuration path changed in 2.0
SVN:trunk[2376]
2012-10-26 12:17:59 +00:00
Erwan Taloc
0bcbf5825a change translation for trigger on threshold reached
SVN:trunk[2375]
2012-10-26 11:54:26 +00:00
Denis Flaven
99b4719335 Log the response file as a standalone XML file to be able to easily replay each installation
SVN:trunk[2374]
2012-10-26 08:12:14 +00:00
Erwan Taloc
a1f5a330d4 add dictionary for change mgmt ITIL
Add external field for link SLA / SLT

SVN:trunk[2373]
2012-10-26 07:13:47 +00:00
Denis Flaven
0f4d2990f7 Bug fix: properly preserve user's choices in case of upgrade (for alternatives)
SVN:trunk[2372]
2012-10-25 16:20:14 +00:00
Denis Flaven
fa9237f04b Log the installation parameters in case an investigation is needed
SVN:trunk[2371]
2012-10-25 16:10:53 +00:00
Romain Quetiez
3c9082d978 Fixed datamodel consistency issue (acceptance_comment removed from emergency change)
SVN:trunk[2370]
2012-10-25 15:36:15 +00:00
Denis Flaven
f45d2520cf itop-tickets is a mandatory module, whatever happens, install it !
SVN:trunk[2369]
2012-10-25 15:19:03 +00:00
Romain Quetiez
89029d4410 Regression -a bug was hidden by the issue solved in revision [2356] !
SVN:trunk[2368]
2012-10-25 15:16:36 +00:00
Denis Flaven
c39f11193e Beware: the 'lifecycle' directories contain big images. Exclude them from the list of icons.
SVN:trunk[2367]
2012-10-25 14:57:01 +00:00
Denis Flaven
f14ebdb019 Allow non-integer ranks on dashlets/cells
SVN:trunk[2366]
2012-10-25 14:55:47 +00:00
Denis Flaven
e3698b07f6 Exclude Incidents from the Portal since this way of handling them would not work once opening the tickets in the normal UI.
SVN:trunk[2365]
2012-10-25 14:49:40 +00:00
Denis Flaven
a57e5e0af4 - Data model finalization... renamed ElectricalConnection to PowerConnection and Arrival to PowerSource
- Changes in the default choices for the setup

SVN:trunk[2364]
2012-10-25 14:47:54 +00:00
Romain Quetiez
d73405d1f4 Compiler: take into account the "is_null_allowed" flag for blobs
SVN:trunk[2363]
2012-10-25 14:36:06 +00:00
Romain Quetiez
cae32294a1 Integrated the multiple select in the portal (search closed tickets)
SVN:trunk[2362]
2012-10-25 14:24:38 +00:00
Romain Quetiez
ce63939855 Added/XMLized service overview dashboard, for every version + added an overview for change mgmt in ITIL version
SVN:trunk[2361]
2012-10-25 13:51:53 +00:00
Denis Flaven
13de0f5702 Redundant typo !
SVN:trunk[2360]
2012-10-25 13:40:26 +00:00
Romain Quetiez
cc331c7f72 Adjusted the text of the "Notifications" welcome page (added two types of triggers)
SVN:trunk[2359]
2012-10-25 13:25:23 +00:00
Denis Flaven
20103def45 Ignore silently unknown attributes when loading a XML file since we know have variations in the data model of the CMDB...
SVN:trunk[2358]
2012-10-25 13:24:22 +00:00
Denis Flaven
664adcb990 Fixed some interdependencies of the CMDB modules.
SVN:trunk[2357]
2012-10-25 13:17:13 +00:00
Romain Quetiez
db5d832207 Fixed error in JA dictionary, messing up the English and Japanese dictionaries for the class EventIssue
SVN:trunk[2356]
2012-10-25 13:16:11 +00:00
Denis Flaven
1210732852 Oops, missing file.
SVN:trunk[2355]
2012-10-25 10:54:49 +00:00
Denis Flaven
9f263ca603 Installation wizard icons for 2.x.
SVN:trunk[2354]
2012-10-25 10:47:41 +00:00
Denis Flaven
7597630d18 Moved the dashboards into the datamodel XML (thanks to Christophe)
SVN:trunk[2353]
2012-10-25 10:34:18 +00:00
Denis Flaven
66e3554308 Split of the datamodel into 5 modules:
- Core (as before, mandatory)
- Storage
- End-User devices
- Datacenter devices
- Virtualization

SVN:trunk[2352]
2012-10-25 10:22:41 +00:00
Romain Quetiez
467c4c6258 Added TriggerOnThresholdReached to the dictionary
SVN:trunk[2351]
2012-10-25 10:17:04 +00:00
Denis Flaven
4ccf908738 Prevent an error in case of exception !
SVN:trunk[2350]
2012-10-25 10:08:05 +00:00
Romain Quetiez
c1072bb744 Fixed regression in the licenses shown at setup (2 had disappeared)
SVN:trunk[2349]
2012-10-25 07:50:00 +00:00
Erwan Taloc
5eb4b557e0 Add specific module for Datacenter related CIs
SVN:trunk[2348]
2012-10-24 16:02:21 +00:00
Erwan Taloc
4d4c9674fa remove Datacenter related CIS
SVN:trunk[2347]
2012-10-24 16:01:15 +00:00
Romain Quetiez
217b7048c5 XML Datamodel: new format (fixed issue seen with some versions of PHP)
SVN:trunk[2346]
2012-10-24 14:40:13 +00:00
Denis Flaven
bbd581e9c5 Do not perform time consuming computations for building the menus if there are too many objects in a list (limit is configurable).
SVN:trunk[2345]
2012-10-24 14:17:27 +00:00
Romain Quetiez
d7e492b711 XML Datamodel: new format (version + zlists changed) - not compatible with 2.0 alpha!!!
SVN:trunk[2342]
2012-10-24 13:54:02 +00:00
Denis Flaven
80ec842042 Slight (incomptaible!) modification to the dashboards format to support incremental changes through the XML.
SVN:trunk[2336]
2012-10-24 08:04:17 +00:00
Romain Quetiez
b72151d968 Fixed typo in new XML datamodel
SVN:trunk[2335]
2012-10-24 07:51:20 +00:00
Denis Flaven
3c3c805298 Finishing touch on the setup program... ready for 2.0 beta ??
SVN:trunk[2334]
2012-10-24 07:30:43 +00:00
Romain Quetiez
721faa7e1e Updated copyright (2012) and license (LGPL changed to AGPL)
SVN:trunk[2333]
2012-10-23 21:41:36 +00:00
Denis Flaven
54a3c849ed Testing external fields pointing to external keys...
SVN:trunk[2332]
2012-10-23 09:34:51 +00:00
Romain Quetiez
5582607b76 CSV Import: when using cut&paste, the character set is de facto utf-8 (no user choice)
SVN:trunk[2331]
2012-10-23 09:33:31 +00:00
Romain Quetiez
3889bdf293 Fixed regression in 2.0: plugins ONDBInsert and ONDBUpdate where not called anymore
SVN:trunk[2330]
2012-10-23 09:26:28 +00:00
Denis Flaven
4981344c24 "Refresh" button replaced by an small icon
SVN:trunk[2329]
2012-10-23 09:11:10 +00:00
Denis Flaven
2ca4b02548 Bug fix: cloning a DBObject has a special meaning !!
SVN:trunk[2328]
2012-10-23 09:09:55 +00:00
Denis Flaven
180f215ab0 Small optimization: do not recompute external fields if the value of the external key did not change
SVN:trunk[2327]
2012-10-22 13:22:01 +00:00
Denis Flaven
9f47c09cdf Bug of month: make sure that GetFilter returns a usable filter (i.e. with the parameters)
SVN:trunk[2326]
2012-10-22 13:12:00 +00:00
Romain Quetiez
7b790cc84f Fixed two bugs revealed with specific constraints (query expression like 'SELECT b FROM a JOIN b', AND the organization context is set)
SVN:trunk[2325]
2012-10-22 13:02:15 +00:00
Denis Flaven
281a01fca5 Added more precomputed life cycle images
SVN:trunk[2324]
2012-10-22 09:14:57 +00:00
Erwan Taloc
b5af98b150 update on_target_delete rule to avoid automatic deletion in some cases
SVN:trunk[2321]
2012-10-21 09:01:02 +00:00
Erwan Taloc
216bbd0ce1 Add edit mode for Linkset
SVN:trunk[2320]
2012-10-21 08:40:50 +00:00
Erwan Taloc
a78b8a604c Add edit mode for Linkset
SVN:trunk[2319]
2012-10-21 08:39:49 +00:00
Erwan Taloc
6e7ee18e7f Add icone for this module
SVN:trunk[2318]
2012-10-21 08:17:54 +00:00
Erwan Taloc
aa49610e31 Rename Fiber Channel interface to correct typo
SVN:trunk[2317]
2012-10-21 08:10:55 +00:00
Erwan Taloc
e3a285e423 Remove obsolete attribute
SVN:trunk[2316]
2012-10-21 08:00:55 +00:00
Denis Flaven
37f75428d2 Typos
SVN:trunk[2315]
2012-10-20 20:26:29 +00:00
Denis Flaven
f9eacfa2d0 Typos ??
SVN:trunk[2314]
2012-10-20 20:19:00 +00:00
Denis Flaven
bc6d3df0c2 EMail, CSV export and Add To Dashbaord menu items are now in the "toolkit" menu.
SVN:trunk[2313]
2012-10-20 20:14:01 +00:00
Denis Flaven
79900e89a2 Cosmetic enhancements to ease the search for a class in the schema.
SVN:trunk[2312]
2012-10-20 19:22:57 +00:00
Denis Flaven
4efa2cb3c0 Added a refresh button (and creation /modification messages) on the details of an object
SVN:trunk[2311]
2012-10-20 17:16:46 +00:00
Denis Flaven
135abdb9e0 Fixed the "Reset(APC)Cache" at the end of the installation.
SVN:trunk[2310]
2012-10-20 15:42:39 +00:00
Denis Flaven
a48d2f97e8 Bug fix: do not overwrite the 'extensions' directory during the setup!
SVN:trunk[2309]
2012-10-20 14:55:29 +00:00
Denis Flaven
c9b1883905 Bug fix: preserve the previous settings in the configuration file in case of upgrade.
SVN:trunk[2308]
2012-10-20 14:39:14 +00:00
Denis Flaven
86b615a9a5 Added the proper mimetype for XML pages for the impact analysis graph.
SVN:trunk[2307]
2012-10-20 13:37:46 +00:00
Denis Flaven
08a320cf2b Ajax file upload is now part of the standard JS includes in the iTop pages.
SVN:trunk[2306]
2012-10-20 13:36:20 +00:00
Denis Flaven
c05c1062ce Implemented the "multiple choices" in search forms for Enums and External keys.
SVN:trunk[2305]
2012-10-20 13:32:59 +00:00
Denis Flaven
f61af1fe5c Added a new favicon
SVN:trunk[2304]
2012-10-20 13:06:31 +00:00
Denis Flaven
80f2f0f66f Cosmetics for the setup
SVN:trunk[2303]
2012-10-19 16:59:07 +00:00
Erwan Taloc
d3b6d468b0 Add edition in place for Physical Interfaces on Connectable CI
SVN:trunk[2302]
2012-10-19 15:35:42 +00:00
Romain Quetiez
27fec353dd #593 Losing attachments when performing massive change
SVN:trunk[2301]
2012-10-19 15:11:53 +00:00
Denis Flaven
ab6cfb56b4 Fixed a regression: don't put some JS script in every ajax_page (even though they claim to contain some HTML)
SVN:trunk[2300]
2012-10-19 14:11:57 +00:00
Romain Quetiez
a57aed9ffc Portal adapted to datamodels 1.x, 2.x (simple tickets, or ITIL tickets)
SVN:trunk[2299]
2012-10-19 13:07:35 +00:00
Denis Flaven
7a817ad1d8 Localization for in-place edition of 1:n linksets
SVN:trunk[2298]
2012-10-19 10:52:47 +00:00
Denis Flaven
b3caa1f002 Life cycle images enhancements:
- precomputed images are now stored in the "lifecycle" directory of the module in which the class is declared.
- graphviz.php makes a proper usage of the data directory for its data files
- life cycles images are now transparent

SVN:trunk[2297]
2012-10-19 10:17:47 +00:00
Denis Flaven
77606c32c5 Added pre-computed life-cycle images and a fixed typo.
SVN:trunk[2296]
2012-10-19 09:28:15 +00:00
Erwan Taloc
d89be97297 remove SLA computation herited from ticket
SVN:trunk[2295]
2012-10-19 09:16:08 +00:00
Erwan Taloc
da4419b848 remove SLA computation herited from ticket
SVN:trunk[2294]
2012-10-19 09:15:43 +00:00
Erwan Taloc
7809037963 Make requestype for Service sub category mandatory
SVN:trunk[2293]
2012-10-19 09:12:59 +00:00
Erwan Taloc
c28c4bd675 Make requestype for Service sub category mandatory
SVN:trunk[2292]
2012-10-19 09:12:42 +00:00
Romain Quetiez
fa8cbd08d4 Internal: you can add 'attachments' => array of ormDocument to the context of a trigger, the attachments will be added to the email sent
SVN:trunk[2291]
2012-10-18 17:15:06 +00:00
Denis Flaven
53aefa895b Implemented a new (optional) UI for managing 1:n linksets.
SVN:trunk[2290]
2012-10-18 12:03:33 +00:00
Romain Quetiez
c9d5743c4a Config: use app_icon_url to change the hyperlink used when clicking on the main icon
SVN:trunk[2289]
2012-10-18 10:17:49 +00:00
Erwan Taloc
7d5e2637c4 Rename class PCsoftware into PCSoftware
Add role attribute on link Person / Team

SVN:trunk[2288]
2012-10-18 09:51:13 +00:00
Romain Quetiez
e938dfb5c9 Internal: AttributeDateTime column changed from TIMESTAMP to DATETIME (issues with some bulk imports)
SVN:trunk[2287]
2012-10-18 09:50:39 +00:00
Romain Quetiez
ed15cde281 #583 Losing attachments when performing massive change
SVN:trunk[2284]
2012-10-18 09:33:49 +00:00
Erwan Taloc
96c62463d5 Initial load of data model 2.0
SVN:trunk[2283]
2012-10-18 09:26:30 +00:00
Romain Quetiez
ce77c65e6e #565 Fixed security issues (XSS)
SVN:trunk[2282]
2012-10-17 15:38:09 +00:00
Denis Flaven
0f9280399b Properly handle all types of fields when entering values via the state transition "wizard" screen
SVN:trunk[2280]
2012-10-17 14:11:26 +00:00
Romain Quetiez
4f9115ca02 Objects always recorded before the notifications are sent
SVN:trunk[2277]
2012-10-17 13:31:10 +00:00
Romain Quetiez
84a8310912 Portal: enable adding dependent attributes in the request creation form
SVN:trunk[2273]
2012-10-17 12:40:38 +00:00
Romain Quetiez
c45dbb2e07 Portal: enable adding dependent attributes in the request creation form
SVN:trunk[2270]
2012-10-17 12:03:29 +00:00
Romain Quetiez
b36ebc6e0f Fixed issue in the portal: the list of opened requests and closed request where messed up when pagination was activated on both lists
SVN:trunk[2267]
2012-10-17 09:02:24 +00:00
Romain Quetiez
09a1c17052 CSV import: show flag csv_import_history_display in the default config file
SVN:trunk[2264]
2012-10-16 14:40:20 +00:00
Romain Quetiez
bddda8e256 CSV import: added a flag to disable the history tab (too long to display, when the feature is heavily used)
SVN:trunk[2263]
2012-10-16 14:31:15 +00:00
Romain Quetiez
020089b1d3 Internal: MetaModel to ignore classes declared only with the purpose of implementing behaviors (missing function Init). Note: declaring such a class as abstract is recommended, though it seems enough to omit the Init method. Perfs: benchmarked as an additional 1ms out of 1s for the whole page.
SVN:trunk[2262]
2012-10-16 14:07:21 +00:00
Romain Quetiez
e454a12346 Dev productivity: SVN to ignore log/setup.log
SVN:trunk[2261]
2012-10-16 13:11:04 +00:00
Romain Quetiez
b304307ad7 Spurious chars corrupting CSV download:
- factorized in WebPage as TrashUnexpectedOutput()
- added early tracking and reporting into MetaModel::IncludeModule, use the new config flag 'debug_track_spurious_chars' to blame the harmful module/file.
- protected export.php
- removed a space at the beginning of page 'createfrommail.php'

SVN:trunk[2260]
2012-10-16 13:06:50 +00:00
Romain Quetiez
6039ea47fc Bug: download CSV result with search criteria
SVN:trunk[2259]
2012-10-15 16:00:25 +00:00
Denis Flaven
8d26d1dd34 Fixed the support of the toolkit
SVN:trunk[2258]
2012-10-15 15:28:48 +00:00
Romain Quetiez
1507259cfe Fix: removed a debug trace
SVN:trunk[2257]
2012-10-15 14:12:35 +00:00
Romain Quetiez
a79336116f Fixed regression - still, the API MetaModel::BulkDelete cannot be used in any case (e.g. hierarchical keys)
SVN:trunk[2256]
2012-10-15 13:57:19 +00:00
Romain Quetiez
f805e8b6c5 Fixed export.php usage description
SVN:trunk[2255]
2012-10-15 13:55:54 +00:00
Erwan Taloc
7015a44268 Allow utilization of place holder in from and reply_to fields for action emails
SVN:trunk[2254]
2012-10-15 12:27:09 +00:00
Romain Quetiez
e33523ddc8 CSV import/export reworked:
Trac #174 and #283: import.php localized by default, option no_localize to disable
Trac #554: export.php localized by default, option no_localize to disable
Trac #555: friendlyname abusively used as a reconciliation key
+ Default charset is ISO-8859-1 to be compatible with Excel (See config parameter csv_file_default_charset)
+ CSV export in UTF-8 with BOM to help Excel in getting it right (not all versions)
+ Fixed reporting issues (wrong class, exceptions, changed external key)
+ Fixed settings lost when navigating in the import wizard
+ Fixed issues when some html entities were found in the data (reporting + export)
+ Added a link to download the CSV export.php


SVN:trunk[2253]
2012-10-12 15:48:54 +00:00
Denis Flaven
ec3c42e87c - Changes to the setup program: Backup now works for real
- A few cosmetic changes...

SVN:trunk[2252]
2012-10-12 12:15:06 +00:00
Denis Flaven
8edfac5cff Smaller and nicer tar/zip icon
SVN:trunk[2251]
2012-10-12 11:10:46 +00:00
Romain Quetiez
1bd9e77a87 Brand new setup: fixed two issues
SVN:trunk[2250]
2012-10-10 14:22:22 +00:00
Denis Flaven
3e5581dd24 Not needed if there is no installation.xml file
SVN:trunk[2249]
2012-10-10 10:23:05 +00:00
Denis Flaven
ebc0114284 Installation.xml is useless for a 1.x datamodel
SVN:trunk[2248]
2012-10-10 10:21:48 +00:00
Denis Flaven
f788f589fa ResetCache requires the Dict class.
SVN:trunk[2247]
2012-10-10 10:16:29 +00:00
Denis Flaven
44aa1d34b7 Do not search for modules when its not needed
SVN:trunk[2246]
2012-10-10 10:02:05 +00:00
Denis Flaven
06aef10f25 Imported the "old" addon from the 1.2 branch
SVN:trunk[2245]
2012-10-10 09:49:58 +00:00
Denis Flaven
9c58240474 Start using the new setup 2.0 ! Be brave !!
SVN:trunk[2244]
2012-10-10 09:36:04 +00:00
Denis Flaven
4f29019351 Start using the new setup 2.0 ! Be brave !!
SVN:trunk[2243]
2012-10-10 09:35:34 +00:00
Denis Flaven
59df6b61d0 More information for the setup program
SVN:trunk[2242]
2012-10-10 09:33:48 +00:00
Denis Flaven
4cf90dee55 Rollback an unwanted - experimental - change.
SVN:trunk[2241]
2012-10-10 09:32:06 +00:00
Denis Flaven
01d4746b0c Moving the datamodel to its new place
SVN:trunk[2240]
2012-10-10 09:28:22 +00:00
Denis Flaven
011c08676e New way of storing the "source" data model(s)
SVN:trunk[2239]
2012-10-10 09:23:32 +00:00
Denis Flaven
ecaa870880 "extensions" is now the offical place for storing extension modules
SVN:trunk[2238]
2012-10-10 09:21:40 +00:00
Denis Flaven
847a538912 Some progress on the 2.0 setup...
SVN:trunk[2237]
2012-10-10 09:18:32 +00:00
Romain Quetiez
7dbbb1c299 #439 Record and display changes in the link sets (ex: Members of a team)
#439 Make sure that changes made by a plugin get recorded
+ simplified the change tracking for the plugins. Simply call DBObject::DBInsert (resp. Update and Delete) and the change will be recorded for the current page. This is compatible with the old (not mandatory anymore) way that was requiring DBInsertTracked APIs (resp. Update, Delete).

SVN:trunk[2236]
2012-10-08 12:17:56 +00:00
Romain Quetiez
60cf926fdb Toolkit: deharcoded source directory
SVN:trunk[2234]
2012-10-05 12:02:05 +00:00
Denis Flaven
5b93504709 - new Configuration option 'source_dir'
- pretty print of the configuration

SVN:trunk[2233]
2012-10-05 10:57:47 +00:00
Romain Quetiez
6c1866ce89 Fixed cosmetic regression (seen on first login if the welcome popup is shown)
SVN:trunk[2232]
2012-10-05 08:19:37 +00:00
Romain Quetiez
eab09c5e17 Toolkit : compile only installed modules (in database)
SVN:trunk[2231]
2012-10-04 15:47:31 +00:00
Denis Flaven
c69eb2e5d7 Don't perform computations inside GetAsHTML because this may cause an infinite recursion since GetAsHTML is called by ToArgs
SVN:trunk[2230]
2012-10-04 09:09:25 +00:00
Romain Quetiez
55cc5ad2b7 Dev productivity: SVN to ignore the folder env-*
SVN:trunk[2229]
2012-10-03 08:52:41 +00:00
Romain Quetiez
18933c33b3 Finalized the translation of some dashboards (1.2.1)
SVN:trunk[2228]
2012-10-03 08:46:07 +00:00
Denis Flaven
5c7604f950 - Implemented the 'symlinks' options
- Better handling of errors during the installation

SVN:trunk[2227]
2012-10-03 08:18:40 +00:00
Romain Quetiez
6b92cab445 Added a utility for the toolkit (adaptation to 2.0) + removed non-issues revealed by the toolkit
SVN:trunk[2226]
2012-10-02 16:06:10 +00:00
Denis Flaven
9af1de0bb4 Cleaner handling of "pure PHP" classes inside the data model
SVN:trunk[2225]
2012-10-02 13:54:22 +00:00
Denis Flaven
ddd98ebab2 Some progress on the 2.0 setup...
SVN:trunk[2224]
2012-09-29 16:47:13 +00:00
Denis Flaven
f5de7987bb Implementation of a new extension "iPopupMenuExtension" to allow a module to add menu items almost anywhere inside iTop. Oops...
SVN:trunk[2223]
2012-09-29 16:45:36 +00:00
Denis Flaven
12c06262ea Typos
SVN:trunk[2222]
2012-09-29 16:43:22 +00:00
Denis Flaven
1b55986e39 Protect against duplicate class definition in case of unattended installation
SVN:trunk[2221]
2012-09-29 16:42:29 +00:00
Denis Flaven
ef59badf5b Implementation of a new extension "iPopupMenuExtension" to allow a module to add menu items almost anywhere inside iTop.
SVN:trunk[2220]
2012-09-29 16:03:16 +00:00
Denis Flaven
cb57335c0b Work in progress on the 2.0 setup/ the "install from scratch" is complete.
SVN:trunk[2219]
2012-09-27 10:53:45 +00:00
Denis Flaven
5fc4775903 Bug fix: AddToDashboard crashed the menu.
SVN:trunk[2218]
2012-09-26 10:19:41 +00:00
Denis Flaven
088fcf9449 Protect the download of documents against spurious blank lines coming from nowhere !!
SVN:trunk[2217]
2012-09-26 09:19:43 +00:00
Erwan Taloc
5ed95f3a1c Mahe sure a unique ID is define for DIV
SVN:trunk[2216]
2012-09-26 08:08:44 +00:00
Romain Quetiez
a2e33634f5 Compiler: fixed issue with 'initial_state_path' (the real fix...)
SVN:trunk[2215]
2012-09-25 17:57:12 +00:00
Romain Quetiez
2d641a0de3 Compiler: fixed issue with 'initial_state_path'
SVN:trunk[2214]
2012-09-25 17:41:46 +00:00
Romain Quetiez
da75724296 Dashboards for 2.0, replaced external fields by the external key (group by) + fixed an issue in the numbering of dashlets
SVN:trunk[2213]
2012-09-25 16:35:17 +00:00
Romain Quetiez
50f0358556 Fixed issues with accentuated characters in the graphs (bars or pie) - added a comment
SVN:trunk[2212]
2012-09-25 14:35:31 +00:00
Romain Quetiez
0e4c79b2e7 Fixed issues with accentuated characters in the graphs (bars or pie)
SVN:trunk[2211]
2012-09-25 14:30:52 +00:00
Denis Flaven
a008107d38 Bug fix: properly show which column the table is sorted on, even for external keys.
Enhancement: sort the non-selected columns alphabetically

SVN:trunk[2210]
2012-09-25 12:57:39 +00:00
Romain Quetiez
dc4bfc6b30 Compiler: added 'initial_state_path'
SVN:trunk[2209]
2012-09-25 12:08:50 +00:00
Denis Flaven
2da369431c Properly parse accented characters inside the "autocomplete" widget.
Protects the dialog resizing against some JS errors
Code cleanup...

SVN:trunk[2208]
2012-09-25 11:10:17 +00:00
Denis Flaven
5e40d7b009 - Protect against a JS error when opening the dialog box
- Syntax cleaning

SVN:trunk[2207]
2012-09-25 10:15:43 +00:00
Denis Flaven
8d56d8eb79 Enhancement: prevent reloading a list while the configuration dialog is open.
SVN:trunk[2206]
2012-09-25 10:03:41 +00:00
Denis Flaven
ab1143e0e1 Bug fix: prevent 'assertion failed' when a block auto reloads: '0' is indeed a valid ID for a display block !!
SVN:trunk[2205]
2012-09-25 10:01:58 +00:00
Romain Quetiez
d747b1fe26 Fixed an issue revealed by fix [2201], occurring when filtering on organization (context) - the fix is not complete (see Trac #588)
SVN:trunk[2204]
2012-09-25 09:43:03 +00:00
Denis Flaven
20c5e1b6ad Protects against temporary objects that exist only in memory (id < 0)
SVN:trunk[2203]
2012-09-25 08:31:42 +00:00
Denis Flaven
aa9c7c7091 Protects against temporary objects that exist only in memory (id < 0)
SVN:trunk[2202]
2012-09-25 08:29:06 +00:00
Romain Quetiez
2f9922f7df Fixed issue in OQL rendering, reproduced with FROM and JOIN clauses
SVN:trunk[2201]
2012-09-24 07:56:35 +00:00
Denis Flaven
45bfee7ef0 Do not let portal users configure their lists... not bullet proof from the security point of view, but better than opening it wide.
SVN:trunk[2200]
2012-09-21 16:57:53 +00:00
Romain Quetiez
2eddb57cbd Dashboards - Fixed issues + added 3 confirmation (on exit, save or cancel)
SVN:trunk[2199]
2012-09-21 15:15:05 +00:00
Denis Flaven
4979a653ee Support of pre-2.0 modules by transforming the icon paths.
SVN:trunk[2198]
2012-09-21 15:07:16 +00:00
Denis Flaven
82ba3962b0 Special processing for extra "Well Known" parent classes.
SVN:trunk[2197]
2012-09-21 13:53:05 +00:00
Denis Flaven
04424f9762 - Small fixes to the custom display of lists.
- CSS improvement for dashboards

SVN:trunk[2196]
2012-09-21 11:05:10 +00:00
Romain Quetiez
dac73c1669 Dashboards - Finalized the implementation (inc. translation in french) - still some known issues
SVN:trunk[2195]
2012-09-20 15:56:15 +00:00
Denis Flaven
77129d010e Do NOT grab cursor hotkeys (CTRL + left arrow) to hide/show the menu pane.
SVN:trunk[2194]
2012-09-20 08:38:44 +00:00
Denis Flaven
d19116a88e Don't put $_FILES inside a javascript comment !!
SVN:trunk[2193]
2012-09-20 08:02:55 +00:00
Denis Flaven
d38b79de76 Various fixes for the selection of columns / sort order in the lists.
SVN:trunk[2192]
2012-09-20 07:55:25 +00:00
Denis Flaven
54a3a1a89a First integration pass of the new "2.0" setup, but still work in progress...
SVN:trunk[2191]
2012-09-19 12:36:44 +00:00
Romain Quetiez
d18ff8339a The new 2.0 setup is under way... disabled copy and backup
SVN:trunk[2190]
2012-09-19 09:24:29 +00:00
Denis Flaven
e463bd8b48 Preparing for the integration of the version 2.0 of the setup.
Some changes to the application layout: logs now go to the ./log folder ./data should be used to store application's data.

SVN:trunk[2189]
2012-09-18 19:55:56 +00:00
Romain Quetiez
93791255e6 The new 2.0 setup is under way... (forgot the class 'DBBackup')
SVN:trunk[2188]
2012-09-18 14:47:15 +00:00
Romain Quetiez
bb404f8a95 The new 2.0 setup is under way... (added backup + file copy + sample data + admin language)
SVN:trunk[2187]
2012-09-18 09:59:36 +00:00
Denis Flaven
308f40c11b Enhancement: added a new (hidden) configuration setting 'synchro_prevent_delete_all' (default to true) to deactivate the "safety belt" and allow the deletion of all replicas of a synchro task in one go.
SVN:trunk[2186]
2012-09-17 17:12:43 +00:00
Romain Quetiez
645b2f5ad8 Profiles defined in XML: fixed regression preventing from creating an object
SVN:trunk[2185]
2012-09-17 07:49:00 +00:00
Romain Quetiez
71208d362a The new 2.0 setup is under way...
SVN:trunk[2184]
2012-09-13 09:00:53 +00:00
Romain Quetiez
39d2534bda Friendly names: systematic rebuild of views (could be improved)
SVN:trunk[2183]
2012-09-13 08:51:36 +00:00
Romain Quetiez
6052e5c848 The new 2.0 setup is under way...
SVN:trunk[2182]
2012-09-12 15:54:37 +00:00
Denis Flaven
10aaa3f9bf Adaptations to potentially run the setup from the CLI
SVN:trunk[2181]
2012-09-12 13:44:12 +00:00
Denis Flaven
b372a38c5d Adaptations to potentially run the setup from the CLI
SVN:trunk[2180]
2012-09-12 13:43:18 +00:00
Denis Flaven
5eb97ae133 The new 2.0 setup is under way...
SVN:trunk[2179]
2012-09-12 13:31:46 +00:00
Denis Flaven
4cfeae9ca0 Adaptations to potentially run the setup from the CLI
SVN:trunk[2178]
2012-09-12 13:28:49 +00:00
Romain Quetiez
949a4639e2 #585 Error in CSV export (from a search result)
SVN:trunk[2176]
2012-09-11 14:39:09 +00:00
Romain Quetiez
e47251f2f4 Stop watches - removed inefficient workaround to issue #502 (modif not recorded in the history), the issue #502 must be fixed properly
SVN:trunk[2175]
2012-09-11 14:29:41 +00:00
Romain Quetiez
7026eb3b76 Stop watches - reviewed history of changes (+ started refactorization of change tracking into AttributeDef)
SVN:trunk[2174]
2012-09-11 10:55:11 +00:00
Romain Quetiez
e48a842881 Stop watches - reset of deadlines whenever the SW is stopped
SVN:trunk[2170]
2012-09-07 15:32:50 +00:00
Romain Quetiez
819b2663ca Stop watches - reset of deadlines + overrun
SVN:trunk[2169]
2012-09-07 15:23:28 +00:00
Romain Quetiez
95785ee22d Stop watches - workaround for the triggers
SVN:trunk[2168]
2012-09-07 14:39:29 +00:00
Denis Flaven
c37135af55 Support previous datamodels (profiles à la mode 1.2.x)
SVN:trunk[2167]
2012-09-07 13:25:59 +00:00
Romain Quetiez
f05f5865e9 Stop watches - beta (change tracking to be reviewed)
SVN:trunk[2166]
2012-09-07 12:51:26 +00:00
Romain Quetiez
2d9193e60e Friendly names: improved the behavior. Now fully compliant with end users expectations (e.g. a list of contacts shows the friendly name of the persons and team, not only the attribute 'name', the search can be performed on the friendly name as well)
SVN:trunk[2165]
2012-09-04 12:59:01 +00:00
Denis Flaven
8fe559e5d1 - Support of iTop 2.0
- Fix for deleting "suggested attachments"

SVN:trunk[2164]
2012-08-31 14:08:35 +00:00
Denis Flaven
d799585958 Bug fix: do NOT request the display value of a linkset when this value is useless and may generate an exception !!
SVN:trunk[2163]
2012-08-31 13:31:27 +00:00
Denis Flaven
4eb018ac50 Bug fix for supporting CKEditor (HTMLAttribute) inside a reloaded form
SVN:trunk[2162]
2012-08-31 13:30:12 +00:00
Denis Flaven
f0d87adc87 Bug fix: Support edition of the "latest modified" entry of a case log
SVN:trunk[2161]
2012-08-31 13:28:51 +00:00
Denis Flaven
bc557f6e42 Bug fix for queries where the selected class is not the first one in the list
SVN:trunk[2160]
2012-08-31 13:28:15 +00:00
Denis Flaven
c8393a7d3a Typo !
SVN:trunk[2159]
2012-08-31 13:15:02 +00:00
Denis Flaven
c44420c985 Menu icon for editing lists config.
SVN:trunk[2158]
2012-08-28 16:04:42 +00:00
Denis Flaven
9064b81461 Integration of the latest version of CKEditor: version 3.6.4, released on 17 July 2012
SVN:trunk[2157]
2012-08-28 13:52:07 +00:00
Denis Flaven
175e2351d6 Support edition of the "latest modified" entry of a case log
SVN:trunk[2156]
2012-08-28 12:09:02 +00:00
Denis Flaven
9eb1fbee1d Support edition of the "latest modified" entry of a case log
SVN:trunk[2155]
2012-08-28 12:08:15 +00:00
Denis Flaven
5726b2cd5c Bug fix: properly export boolean attributes to XML (a value of false was creating an empty XML tag)
SVN:trunk[2154]
2012-08-23 14:42:34 +00:00
Romain Quetiez
083c3d8613 Profiles defined in XML: reviewed the internal extension capability (GetReadOnlyAttributes and GetPredefinedObjects)
SVN:trunk[2152]
2012-08-21 13:25:32 +00:00
Romain Quetiez
cf63e128b1 Profiles defined in XML: aligned the user rights with 1.2.1 (current version)
SVN:trunk[2151]
2012-08-21 08:19:25 +00:00
Romain Quetiez
c01cdbb569 Profiles defined in XML: include subclasses (explicit declarations have precedence)
SVN:trunk[2150]
2012-08-20 13:25:11 +00:00
Romain Quetiez
38cca0c144 Profiles defined in XML (setup + runtime), beta version (stable, upgrade required)
SVN:trunk[2149]
2012-08-14 16:06:51 +00:00
Romain Quetiez
3b46af6a4f Fixed bug with the serialization of a multi-object OQL (hard to reproduce)
SVN:trunk[2147]
2012-08-06 13:43:26 +00:00
Romain Quetiez
b001476a6f Fixed typo in the menu reflection API
SVN:trunk[2146]
2012-08-06 13:17:04 +00:00
Denis Flaven
f0cd1cddb0 - Use the 'style' of the MenuBlock (inherited from DisplayBlock) to distinguish between a list of one object and the details of the same object.
SVN:trunk[2145]
2012-07-31 15:18:46 +00:00
Denis Flaven
a707e9fde1 In the 'context', pass menus by ID and no longer by index.
SVN:trunk[2144]
2012-07-31 14:07:35 +00:00
Denis Flaven
c612732037 In the 'context', pass menus by ID and no longer by index.
SVN:trunk[2143]
2012-07-31 14:06:38 +00:00
Denis Flaven
2b5c7a7f59 Move the "favorites" organization at the bottom of the page.
SVN:trunk[2142]
2012-07-31 13:25:58 +00:00
Denis Flaven
aa8ea8f2fe Enhancement of the Email class to preserve backward compatibility...
SVN:trunk[2141]
2012-07-27 10:09:41 +00:00
Denis Flaven
16651d53e1 Fixed a regression due to (apparently?) the upgrade of jQuery
SVN:trunk[2140]
2012-07-27 10:08:23 +00:00
Denis Flaven
b0ec706f79 The "Preferences" pages now allows a user to:
- change her/his favorite language
- set a global default for the length of all lists, overriding the system-wide configuration.

SVN:trunk[2139]
2012-07-27 10:07:20 +00:00
Denis Flaven
54f81153f3 The "Preferences" pages now allows a user to:
- change her/his favorite language
- set a global default for the length of all lists, overriding the system-wide configuration.

SVN:trunk[2138]
2012-07-27 10:06:07 +00:00
Denis Flaven
e726420a94 The "Preferences" pages now allows a user to:
- change her/his favorite language
- set a global default for the length of all lists, overriding the system-wide configuration.

SVN:trunk[2137]
2012-07-27 10:04:08 +00:00
Denis Flaven
148df28345 The "Preferences" pages now allows a user to:
- change her/his favorite language
- set a global default for the length of all lists, overriding the system-wide configuration.

SVN:trunk[2136]
2012-07-27 10:03:06 +00:00
Denis Flaven
e126137a91 Implemented Track #582: "stable name" for synchro_data_xxx tables; translations.
SVN:trunk[2134]
2012-07-25 14:30:43 +00:00
Denis Flaven
f9924cd635 Implemented Track #582: "stable name" for synchro_data_xxx tables.
Do not allow changing read-only attributes by CSV import.

SVN:trunk[2133]
2012-07-25 14:09:19 +00:00
Denis Flaven
55e0898587 Implemented Track #582: "stable name" for synchro_data_xxx tables.
SVN:trunk[2132]
2012-07-25 14:06:54 +00:00
Denis Flaven
dce0bce4ce Customizable tables implementation, beta version done.
SVN:trunk[2131]
2012-07-24 15:16:06 +00:00
Denis Flaven
82cf7776ca Customizable tables implementation, beta version done.
SVN:trunk[2130]
2012-07-24 15:15:36 +00:00
Denis Flaven
5430f52145 Customizable tables implementation...
SVN:trunk[2129]
2012-07-24 15:13:02 +00:00
Denis Flaven
5125000994 Customizable tables implementation...
SVN:trunk[2128]
2012-07-24 13:58:13 +00:00
Denis Flaven
367d97ffb2 Customizable tables implementation...
SVN:trunk[2127]
2012-07-24 13:56:45 +00:00
Denis Flaven
4a3321dd0a Customizable tables implementation...
SVN:trunk[2126]
2012-07-24 13:51:22 +00:00
Denis Flaven
fe55d35930 Customizable tables implementation...
SVN:trunk[2125]
2012-07-24 13:46:58 +00:00
Denis Flaven
fad06f8f91 Customizable tables implementation...
SVN:trunk[2124]
2012-07-24 13:32:54 +00:00
Denis Flaven
1840e24a3c Customizable tables implementation...
SVN:trunk[2123]
2012-07-24 13:32:09 +00:00
Romain Quetiez
b6c3b3c7ab Stop watches - alpha (GUI is missing)
SVN:trunk[2122]
2012-07-12 13:55:26 +00:00
Romain Quetiez
1757554f37 Reintegrated fixes from branch 1.2.1 (2116,2118,2119)
- HTML attributes > 64 Kb
- Log of notification displayed as HTML

SVN:trunk[2121]
2012-07-04 09:34:49 +00:00
Denis Flaven
9bfd7ae435 Prototype of extensibility rules
SVN:trunk[2114]
2012-06-26 12:05:13 +00:00
Denis Flaven
7c9e2df888 Prototype of extensibility rules
SVN:trunk[2113]
2012-06-26 10:04:39 +00:00
Denis Flaven
5a16c08bc9 Fix for Trac #559: ldap user can login with blank password
SVN:trunk[2110]
2012-06-14 16:31:16 +00:00
Denis Flaven
0ffe040546 Updated the test list to take into account Trac #558 (hexadecimal numbers inside strings)
SVN:trunk[2107]
2012-06-14 09:31:33 +00:00
Romain Quetiez
dc90de5a2b XML Export: do not export "unimplemented" link sets, so that the resulting output can be used as sample data in the setup
SVN:trunk[2106]
2012-06-14 09:26:23 +00:00
Denis Flaven
51ba8df159 Fixed Trac #558: properly parse strings containing hexadecimal sequences (i.e. 'QWERTY0xCUIOP').
Note that for now hexadecimal numbers are parsed but not interpreted properly...

SVN:trunk[2103]
2012-06-14 09:18:24 +00:00
Romain Quetiez
1e97829d7d Fixed regression (See #556) due to the existence of an overload of the protected API GetUserOrgs, reintegrated from branch 1.2.1
SVN:trunk[2101]
2012-06-14 09:12:56 +00:00
Romain Quetiez
cacad13702 Compiler: fixed issue with quotes in the OQL of a menu.
SVN:trunk[2098]
2012-06-13 05:56:00 +00:00
Denis Flaven
1a8b802276 Fixed the broken n:n links management UI (Add button remained disabled)
SVN:trunk[2097]
2012-06-12 15:21:11 +00:00
Denis Flaven
08deefb5a4 Fixed the broken n:n links management UI (Add button remained disabled)
SVN:trunk[2096]
2012-06-12 15:19:09 +00:00
Denis Flaven
e5bb006ba6 Fixed the broken n:n links management UI (Add button remained disabled)
SVN:trunk[2095]
2012-06-12 14:34:14 +00:00
Denis Flaven
08f3e36cc8 Fixed the broken n:n links management UI (Add button remained disabled)
SVN:trunk[2094]
2012-06-12 14:33:38 +00:00
Denis Flaven
a69c49e77a Proper management of scroll bars in the left (west) pane.
SVN:trunk[2093]
2012-06-12 14:31:57 +00:00
Denis Flaven
039f5042d9 Proper management of scroll bars in the left (west) pane.
SVN:trunk[2092]
2012-06-12 14:31:08 +00:00
Romain Quetiez
4562edb4ea Charts : disabled hrefs until it works
SVN:trunk[2091]
2012-06-12 13:06:54 +00:00
Romain Quetiez
463ade4bf4 Compiler: discard empty menu groups
SVN:trunk[2090]
2012-06-11 16:21:03 +00:00
Romain Quetiez
4f653d63b5 Compiler: fixed issue with simple quotes in the OQL of a menu.
SVN:trunk[2089]
2012-06-11 14:15:53 +00:00
Denis Flaven
34578ff831 Reload the impact/depends on graph only on demand for better performance, via the new Refresh button
SVN:trunk[2087]
2012-06-07 13:41:22 +00:00
Denis Flaven
d40f7aa7ba Reload the impact/depends on graph only on demand for better performance
SVN:trunk[2086]
2012-06-07 13:31:57 +00:00
Denis Flaven
d73e1f5fd3 Protect against a reload of the XML data while loading
SVN:trunk[2085]
2012-06-07 13:28:57 +00:00
Romain Quetiez
47f13d6b70 #556 Merged in trunk
SVN:trunk[2084]
2012-06-05 15:49:31 +00:00
Romain Quetiez
18031b0c34 Dashboard: bug fix
SVN:trunk[2079]
2012-05-31 07:57:42 +00:00
Denis Flaven
d953506950 Prototype of HeatMap dashlet
SVN:trunk[2078]
2012-05-30 16:30:59 +00:00
Denis Flaven
1a040a3a3a Prototype of HeatMap dashlet
SVN:trunk[2077]
2012-05-30 16:30:26 +00:00
Denis Flaven
e3dd7d4371 Better track unknown classes exceptions
SVN:trunk[2076]
2012-05-30 14:59:33 +00:00
Denis Flaven
814ff60c07 Allow editing the "Welcome" dashboard
SVN:trunk[2075]
2012-05-30 14:57:57 +00:00
Romain Quetiez
5cfc82437f Dashboard: improved the group by on two columns (to be continued)
SVN:trunk[2074]
2012-05-30 14:51:01 +00:00
Denis Flaven
ad1b2901be Protect dashboard/dashlets against exceptions, with a graceful degradation for non-existing classes
SVN:trunk[2073]
2012-05-30 14:21:48 +00:00
Denis Flaven
e64e008aad Protect dashboard/dashlets against exceptions, with a graceful degradation for non-existing classes
SVN:trunk[2072]
2012-05-30 14:21:10 +00:00
Denis Flaven
9e10de70c9 Typo
SVN:trunk[2071]
2012-05-30 11:55:39 +00:00
Denis Flaven
cc0590cd3d replace the 'static' template menu by a dashboard menu
SVN:trunk[2070]
2012-05-30 11:20:56 +00:00
Denis Flaven
716ba04f8e replace the 'static' template menu by a dashboard menu
SVN:trunk[2069]
2012-05-30 11:20:30 +00:00
Denis Flaven
04f9e823db replace the 'static' template menu by a dashboard menu
SVN:trunk[2068]
2012-05-30 11:19:55 +00:00
Denis Flaven
15a7517e4e Change in default values...
SVN:trunk[2067]
2012-05-30 11:02:30 +00:00
Romain Quetiez
2f0d058bfc Dashboard: improved the group by on two columns (to be continued)
SVN:trunk[2066]
2012-05-30 11:01:03 +00:00
Denis Flaven
9a1f0241ff Prevent an error in case of exception...
SVN:trunk[2065]
2012-05-30 10:57:52 +00:00
Denis Flaven
f0cfe78043 No more find/replace of : in Dict strings
SVN:trunk[2064]
2012-05-30 10:56:49 +00:00
Denis Flaven
d30cf0655d replace the 'static' template menu by a dashboard menu
SVN:trunk[2063]
2012-05-30 10:50:49 +00:00
Denis Flaven
2e26c056f5 replace the 'static' template menu by a dashboard menu
SVN:trunk[2062]
2012-05-30 10:50:26 +00:00
Denis Flaven
8d4bb6bcdf replace the 'static' template menu by a dashboard menu
SVN:trunk[2061]
2012-05-30 10:27:54 +00:00
Denis Flaven
c5a4a02971 replace the 'static' template menu by a dashboard menu
SVN:trunk[2060]
2012-05-30 09:58:39 +00:00
Denis Flaven
8c0fbe1913 Dashboards edition final touch...
SVN:trunk[2059]
2012-05-30 09:07:41 +00:00
Denis Flaven
5959be5929 Dashboards edition final touch...
SVN:trunk[2058]
2012-05-30 09:06:33 +00:00
Denis Flaven
d0bf023f42 Transparent icon to replace a missing Class' icon
SVN:trunk[2057]
2012-05-30 09:05:12 +00:00
Denis Flaven
38df2c71a2 Added icon_select widget
SVN:trunk[2056]
2012-05-30 09:04:12 +00:00
Romain Quetiez
8a8b8b87e3 Dashboard: finalized the beta version of the header dashlets
SVN:trunk[2055]
2012-05-29 16:00:38 +00:00
Denis Flaven
8a85abf6b4 Refresh the whole dashboard when extending the layout
SVN:trunk[2054]
2012-05-29 15:50:57 +00:00
Denis Flaven
eb9c5d8fe2 Refresh the whole dashboard when extending the layout
SVN:trunk[2053]
2012-05-29 15:50:25 +00:00
Denis Flaven
7cdc90354c More dashlet icons
SVN:trunk[2052]
2012-05-29 15:47:38 +00:00
Romain Quetiez
5ecf5815b8 Dashboard: fixed issue with Null values (group by)
SVN:trunk[2051]
2012-05-29 14:02:25 +00:00
Denis Flaven
74726ff742 New icons for some dashlets
SVN:trunk[2050]
2012-05-29 11:57:57 +00:00
Denis Flaven
79259814f3 Prototype of Pie chart using RaphaelJS / gRaphael
SVN:trunk[2049]
2012-05-29 11:56:20 +00:00
Denis Flaven
264d10ffd7 Starting to use RapahelJS for charts rendering
SVN:trunk[2048]
2012-05-29 11:55:32 +00:00
Romain Quetiez
5dcbdbad95 Compiler and co: defensive programming (computing the delta)
SVN:trunk[2047]
2012-05-29 11:48:24 +00:00
Denis Flaven
00873c1cb9 Fix issue when editing property sheets with subforms
SVN:trunk[2046]
2012-05-29 09:04:32 +00:00
Romain Quetiez
4b34fd4ecd Dashboard: fixed issue with subforms
SVN:trunk[2045]
2012-05-29 09:04:09 +00:00
Romain Quetiez
59e754d8db Dashboard: invoke directly the display blocks (fixes bugs with titles)
SVN:trunk[2044]
2012-05-27 16:37:21 +00:00
Romain Quetiez
4115ddbe13 Dashboard: display the most relevant label in the group by dashlets (eg: hyperlink to an ext key)
SVN:trunk[2043]
2012-05-27 15:33:54 +00:00
Romain Quetiez
479bef14e6 Compiler and Co: fixed bugs in the data model delta computation
SVN:trunk[2042]
2012-05-27 14:03:47 +00:00
Romain Quetiez
e4c113e412 Dashboard: optimized group by (done by MySQL) + proto of a group by on 2 dimensions
SVN:trunk[2041]
2012-05-25 16:04:18 +00:00
Denis Flaven
2c00c115d6 User editable dashboards... implementation in progress
SVN:trunk[2040]
2012-05-25 15:59:24 +00:00
Denis Flaven
a40e902808 User editable dashboards... implementation in progress
SVN:trunk[2039]
2012-05-25 15:58:59 +00:00
Denis Flaven
341a97d2ac User editable dashboards... implementation in progress
SVN:trunk[2038]
2012-05-25 15:58:17 +00:00
Denis Flaven
a970be510b User editable dashboards... implementation in progress
SVN:trunk[2037]
2012-05-25 15:57:55 +00:00
Denis Flaven
e56aac9ef8 User editable dashboards... implementation in progress
SVN:trunk[2036]
2012-05-25 10:22:22 +00:00
Denis Flaven
d6aa4118b0 User editable dashboards... implementation in progress
SVN:trunk[2035]
2012-05-25 10:18:38 +00:00
Denis Flaven
bb7c81a914 Cosmetics
SVN:trunk[2034]
2012-05-25 10:17:51 +00:00
Denis Flaven
0f0a881238 User editable dashboards... implementation in progress
SVN:trunk[2033]
2012-05-25 09:23:10 +00:00
Denis Flaven
f4d2ffcf34 User editable dashboards... implementation in progress
SVN:trunk[2032]
2012-05-25 09:22:29 +00:00
Denis Flaven
11ac749c24 User editable dashboards... implementation in progress
SVN:trunk[2031]
2012-05-25 09:21:50 +00:00
Denis Flaven
d681e58671 User editable dashboards... implementation in progress
SVN:trunk[2030]
2012-05-25 09:20:38 +00:00
Denis Flaven
d71a663208 User editable dashboards... implementation in progress
SVN:trunk[2029]
2012-05-25 09:19:27 +00:00
Denis Flaven
90202ec232 User editable Dashboards: allow several dashlets per cell.
SVN:trunk[2028]
2012-05-25 09:18:31 +00:00
Denis Flaven
f6559d43fc User editable Dashboards: allow several dashlets per cell.
SVN:trunk[2027]
2012-05-25 09:18:08 +00:00
Denis Flaven
5bf31905ac User editable Dashboards: allow several dashlets per cell.
SVN:trunk[2026]
2012-05-25 09:17:36 +00:00
Denis Flaven
7de5315a01 User editable dashboards... implementation in progress
SVN:trunk[2025]
2012-05-24 14:22:01 +00:00
Denis Flaven
536ea2cc21 User editable dashboards... implementation in progress
SVN:trunk[2024]
2012-05-24 14:21:42 +00:00
Denis Flaven
11a107b982 User Editable Dashboards update
SVN:trunk[2023]
2012-05-24 14:20:21 +00:00
Denis Flaven
5d797afbd8 User Editable Dashboards update
SVN:trunk[2022]
2012-05-24 14:19:45 +00:00
Denis Flaven
e540df4de9 User Editable Dashboards update
SVN:trunk[2021]
2012-05-24 14:19:20 +00:00
Romain Quetiez
b31b2a5939 Dashboard: fixed a bug in subforms having the same fields (the last one was submitted even if hidden)
SVN:trunk[2020]
2012-05-24 13:26:53 +00:00
Denis Flaven
a53be218f1 User editable dashboards... implementation in progress
SVN:trunk[2019]
2012-05-24 08:37:15 +00:00
Denis Flaven
ae22563177 User editable dashboards... implementation in progress
SVN:trunk[2018]
2012-05-24 08:36:50 +00:00
Denis Flaven
c87368ff42 User editable dashboards... implementation in progress
SVN:trunk[2017]
2012-05-24 08:36:29 +00:00
Denis Flaven
b2bf8df779 User editable dashboards... implementation in progress
SVN:trunk[2016]
2012-05-24 08:33:55 +00:00
Denis Flaven
debd0cf5bd User editable dashboards... implementation in progress
SVN:trunk[2015]
2012-05-24 08:33:28 +00:00
Romain Quetiez
28a7930a13 Dashboard: transformed a few overviews into customizable dashboards
SVN:trunk[2014]
2012-05-23 10:35:53 +00:00
Denis Flaven
ef453537d3 User editable dashboards... implementation in progress
SVN:trunk[2013]
2012-05-23 10:16:08 +00:00
Denis Flaven
f44a701d12 User editable dashboards... implementation in progress
SVN:trunk[2012]
2012-05-23 10:15:59 +00:00
Denis Flaven
08350f7c65 User editable dashboards... implementation in progress
SVN:trunk[2011]
2012-05-23 10:15:39 +00:00
Denis Flaven
25ff0e064c User editable dashboards... implementation in progress
SVN:trunk[2010]
2012-05-23 08:06:20 +00:00
Denis Flaven
050a4813be User editable dashboards... implementation in progress
SVN:trunk[2009]
2012-05-23 08:06:04 +00:00
Denis Flaven
1c7d6348c6 User editable dashboards... implementation in progress
SVN:trunk[2008]
2012-05-23 08:05:43 +00:00
Romain Quetiez
b31c54c979 Dashboard: continuing...
SVN:trunk[2007]
2012-05-22 16:17:28 +00:00
Romain Quetiez
7133788518 Dashboard: prerequisites to save/load dashlets
SVN:trunk[2006]
2012-05-22 15:06:17 +00:00
Romain Quetiez
b5576df143 Dashboard: alpha version of dashlets
SVN:trunk[2005]
2012-05-22 11:51:22 +00:00
Denis Flaven
eedb28e97b User editable dashboards... implementation in progress
SVN:trunk[2004]
2012-05-22 09:36:43 +00:00
Denis Flaven
25ddd1c2c8 User editable dashboards... implementation in progress
SVN:trunk[2003]
2012-05-22 09:24:46 +00:00
Denis Flaven
a6e5b0b087 User editable dashboards... implementation in progress
SVN:trunk[2002]
2012-05-22 09:22:43 +00:00
Denis Flaven
907d96b18d User editable dashboards... implementation in progress
SVN:trunk[2001]
2012-05-22 09:22:01 +00:00
Romain Quetiez
9b276405b9 Dashboard: forgot to commit new file
SVN:trunk[2000]
2012-05-22 09:21:47 +00:00
Denis Flaven
fe8a5080cb User editable dashboards... implementation in progress
SVN:trunk[1999]
2012-05-22 09:21:21 +00:00
Romain Quetiez
8e3b2d7da6 Dashboard: read user defined dashboards (setup needed to create new table)
SVN:trunk[1998]
2012-05-21 15:11:30 +00:00
Denis Flaven
c47b2b57ec User editable dashboards... implementation in progress
SVN:trunk[1997]
2012-05-21 13:52:12 +00:00
Denis Flaven
1e605c43b0 User editable dashboards... implementation in progress
SVN:trunk[1996]
2012-05-21 13:48:45 +00:00
Denis Flaven
3121cf602a User editable dashboards... implementation in progress
SVN:trunk[1995]
2012-05-21 13:48:23 +00:00
Denis Flaven
c68fbf6cd9 User editable dashboards... implementation in progress
SVN:trunk[1994]
2012-05-21 11:43:25 +00:00
Denis Flaven
508744262c User editable dashboards... implementation in progress
SVN:trunk[1993]
2012-05-21 11:42:37 +00:00
Denis Flaven
214a107792 User editable dashboards... implementation in progress
SVN:trunk[1992]
2012-05-21 11:42:08 +00:00
Romain Quetiez
c7db32f845 Compiler and co: fixed issue with the reset of the APC cache
SVN:trunk[1990]
2012-05-14 15:56:06 +00:00
Romain Quetiez
719870663a Compiler and co: fixed bug with 'is_null_allowed' missing
SVN:trunk[1989]
2012-05-14 14:19:17 +00:00
Romain Quetiez
7487a46888 Compiler and co: create a model.x file as soon as there are some menus OR classes
SVN:trunk[1988]
2012-05-14 13:19:02 +00:00
Denis Flaven
a4034bbaca Let the creation of profiles adapt automatically when some classes do not exist...
SVN:trunk[1987]
2012-05-14 12:44:56 +00:00
Romain Quetiez
33a8a594f8 Compiler and co: fixed issue with the deletion (read a delta)
SVN:trunk[1986]
2012-05-14 12:19:11 +00:00
Romain Quetiez
c009599ff8 Compiler and co: implicit naming and reconciliation scheme
SVN:trunk[1985]
2012-05-14 10:03:22 +00:00
Denis Flaven
014d25bcda Typo in a comment !
SVN:trunk[1984]
2012-05-14 07:12:17 +00:00
Romain Quetiez
1b686a3164 Compiler and co: added optional parameters for the attributes
SVN:trunk[1983]
2012-05-11 11:47:47 +00:00
Romain Quetiez
39be3b449e Fixed setup issues (improved modularity)
SVN:trunk[1982]
2012-05-10 09:30:48 +00:00
Denis Flaven
fb8d93319d Implementation and integration...
SVN:trunk[1981]
2012-05-10 09:06:36 +00:00
Denis Flaven
d27ab4128c Support several successive renaming of the same object
SVN:trunk[1980]
2012-05-09 09:03:13 +00:00
Denis Flaven
c4ffbf1798 Added capability to store/load the DOM in one go !
SVN:trunk[1979]
2012-05-07 09:10:32 +00:00
Denis Flaven
44c2beff51 Make GetConfig independent of the MetaModel
SVN:trunk[1978]
2012-05-07 09:09:25 +00:00
Denis Flaven
9c633bf0b8 Named tab containers instead of non-unique numbering !
SVN:trunk[1977]
2012-05-03 14:36:19 +00:00
Denis Flaven
793e0be473 Faster FindNode
SVN:trunk[1976]
2012-05-03 14:33:08 +00:00
Denis Flaven
3e3195d6cf Implementation on-going.
SVN:trunk[1975]
2012-04-27 12:43:16 +00:00
Romain Quetiez
dcecbee7d0 Compiler and co: fixed issue on the interpretation of the menus
SVN:trunk[1974]
2012-04-27 12:37:51 +00:00
Denis Flaven
dfc02ccb77 Bug fixes: add the namespace definition to the delta and cope with empty deltas
SVN:trunk[1973]
2012-04-25 12:20:32 +00:00
Romain Quetiez
554ececf51 Compiler qnd co: Added _delta="define" for the menus
SVN:trunk[1972]
2012-04-25 06:44:44 +00:00
Romain Quetiez
f9d5b88c06 Reviewed ModelFactory and the compiler
SVN:trunk[1971]
2012-04-24 16:06:11 +00:00
Denis Flaven
0dfbc28577 ModelFactory debugging
SVN:trunk[1970]
2012-04-24 12:55:02 +00:00
Denis Flaven
e0e7a2d71e added GetChildClasses
SVN:trunk[1969]
2012-04-23 10:03:20 +00:00
Denis Flaven
cc4f9800c7 New version of jQuery
SVN:trunk[1968]
2012-04-23 09:27:42 +00:00
Denis Flaven
f9cbd7d81a New version of jQuery
SVN:trunk[1967]
2012-04-23 09:25:32 +00:00
Denis Flaven
f1ae18afcf New version of jQuery
SVN:trunk[1966]
2012-04-23 09:23:40 +00:00
Denis Flaven
dc3dc9e904 Added high-level methods for class and fields processing
SVN:trunk[1965]
2012-04-20 16:25:55 +00:00
Romain Quetiez
2f4cbcac25 Compiler and co: fixed issue on delta load (delete then merge a node)
SVN:trunk[1964]
2012-04-19 14:37:41 +00:00
Romain Quetiez
65e55e0927 Compiler and co: reorganized the code (still some cleanup to perform)
SVN:trunk[1963]
2012-04-19 13:48:02 +00:00
Denis Flaven
d41e77823f Protects against too long string when logging web services events
SVN:trunk[1960]
2012-04-19 11:15:04 +00:00
Denis Flaven
2d51bf9bc1 Bug fix for layout panes
SVN:trunk[1959]
2012-04-19 07:52:42 +00:00
Denis Flaven
78cfb3229d New version of jQuery Layout
SVN:trunk[1958]
2012-04-19 07:51:51 +00:00
Denis Flaven
048fafca08 ModelFactory integration
SVN:trunk[1957]
2012-04-19 07:51:10 +00:00
Romain Quetiez
d0b201e73f Compiler and co: improved the robustness of xml datamodel
SVN:trunk[1956]
2012-04-18 14:57:57 +00:00
Romain Quetiez
60d63839f6 Compiler: reviewed the xml format to facilitate generalization of operations (e.g. compute a delta)
SVN:trunk[1955]
2012-04-15 08:35:31 +00:00
Romain Quetiez
b6ac94a8f1 Fixed cosmetic issue in the error reporting on OQL normalization (using a tree operator on a NON hierarchical key)
SVN:trunk[1954]
2012-04-13 07:54:25 +00:00
Romain Quetiez
46e3eac149 Setup: added detailed reporting on the data model validation issues
SVN:trunk[1953]
2012-04-11 15:21:08 +00:00
Romain Quetiez
6826e132a8 Compiler: clearer reporting on XML format issues + additional validation by the mean of an XSD (same reporting chanel). The validation provided by the XSD is not strict, but this is a good starting point.
SVN:trunk[1952]
2012-04-11 14:54:51 +00:00
Romain Quetiez
11c85d7710 Compiler: new format, checkable by the mean of a XSD (will come soon)
SVN:trunk[1951]
2012-04-11 14:06:11 +00:00
Romain Quetiez
d0025358da Fixed regression in the setup (due to a recent change in NiceWebPage)
SVN:trunk[1950]
2012-04-06 15:32:29 +00:00
Romain Quetiez
a7b7264903 Compiler: added the auto_load option + fixed issue with parent menu declared in another module
SVN:trunk[1949]
2012-04-06 15:09:18 +00:00
Romain Quetiez
da2abef030 When there is still no dictionary available, the menus / classes / attributes have a default label based on their raw names (replacing _ by a blank)
SVN:trunk[1948]
2012-04-06 13:25:32 +00:00
Romain Quetiez
3a841269ec #541 Fixed bug in the export for spreadsheet (time format)
SVN:trunk[1947]
2012-04-06 09:51:25 +00:00
Romain Quetiez
1241592fee Compiler: added the flag enable_admin_only for the menus
SVN:trunk[1944]
2012-04-05 09:16:38 +00:00
Romain Quetiez
885d5ecf9f Compiler: compile menus (updated the reference datamodel)
SVN:trunk[1943]
2012-04-05 08:57:05 +00:00
Denis Flaven
3205d48f84 Properly log-off (and report the issue in the log) in case we fail to create a user during the CAS Synchro
SVN:trunk[1940]
2012-04-04 09:28:13 +00:00
Romain Quetiez
c354fecad4 Updated schema.php to add web link to link class on linked set attributes
SVN:trunk[1939]
2012-04-03 11:29:48 +00:00
Romain Quetiez
009a91ab59 Itop design: added an intermediate root node above 'classes'
SVN:trunk[1936]
2012-04-03 10:09:52 +00:00
Romain Quetiez
7ef5c8ddd7 Compiler: allow optional zlists
SVN:trunk[1935]
2012-04-03 10:07:38 +00:00
Romain Quetiez
a2affcc224 #540 Data synchro: the option "write if empty" was not implemented
SVN:trunk[1934]
2012-03-29 13:34:46 +00:00
Denis Flaven
529b5d53fc Sorting of lists: sort is now always executed server-side via the mechanism used by paginated lists. This fixes Trac #411, #421, #520
SVN:trunk[1931]
2012-03-27 15:27:15 +00:00
Denis Flaven
144b5ae39f Bug fix: to do not try to access a DataSource while it's being deleted
SVN:trunk[1928]
2012-03-27 11:22:50 +00:00
Romain Quetiez
3679e7f8d5 Internal: reworked default ordering (added DBObjectSet::GetRealSortOrder)
SVN:trunk[1927]
2012-03-23 14:24:06 +00:00
Denis Flaven
645802ed32 Bug fix: support [+] button inside linkedsets.. with constraints
SVN:trunk[1925]
2012-03-22 17:15:53 +00:00
Romain Quetiez
38461b7be6 Compiler : fixed cosmetic issue
SVN:trunk[1924]
2012-03-22 16:25:36 +00:00
Romain Quetiez
c1ee1d4e8a Cleanup in data model XML files
SVN:trunk[1923]
2012-03-22 16:24:16 +00:00
Denis Flaven
66e1890b27 CAS integration:
- regression fix: support patterns for the MemberOf groups filtering
- activate/de-activate the profiles synchronization using the 'cas_update_profiles' configuration flag
- provide default profile(s) when creating a new user from CAS, only if no match is found for assigning profiles from the CAS MemberOf group(s).

SVN:trunk[1922]
2012-03-22 15:35:46 +00:00
Denis Flaven
a4fd923759 Bug fix: support [+] button inside linkedsets.. with constraints
SVN:trunk[1916]
2012-03-22 10:04:16 +00:00
Denis Flaven
2533e2eca3 Bug fix: support [+] button inside linkedsets.. with constraints
SVN:trunk[1915]
2012-03-22 10:02:25 +00:00
Denis Flaven
c96952a369 The date picker fills the "time" part of the field with 00:00:00 when picking a DateTime instead of just a Date.
SVN:trunk[1911]
2012-03-21 10:58:43 +00:00
Denis Flaven
94b6730697 Bug fix: support [+] button inside linkedsets
SVN:trunk[1909]
2012-03-21 10:39:15 +00:00
Denis Flaven
1cc2884541 Allow to specify the name of the PDF to download
SVN:trunk[1906]
2012-03-20 17:01:11 +00:00
Romain Quetiez
b37414df59 MetaModel: by default, show most recent tickets first
SVN:trunk[1903]
2012-03-20 15:40:50 +00:00
Romain Quetiez
eb5475cf3f Formatted XML files (line breaks and indentation)
SVN:trunk[1902]
2012-03-20 15:22:44 +00:00
Romain Quetiez
ef50592a8d Allow blanks (line breaks and indentation) in the XML model files
SVN:trunk[1901]
2012-03-20 11:08:42 +00:00
Romain Quetiez
18a28df55f #520 Capability to define a default sort order (PHP/XML)
SVN:trunk[1900]
2012-03-20 10:52:11 +00:00
Denis Flaven
be4ac95549 LinkedSets can be read-only too...
SVN:trunk[1899]
2012-03-19 17:21:11 +00:00
Romain Quetiez
619ce5f130 #421 Sort IP addresses on INET_ATON (API only, see #520 to have this as the default sort order for NW Interfaces)
SVN:trunk[1897]
2012-03-19 08:28:04 +00:00
Denis Flaven
18b234c200 Make the class "TriggerOnPortalUpdate" importable
SVN:trunk[1895]
2012-03-16 12:56:12 +00:00
Romain Quetiez
35fe13fb52 #467 Friendly names not up to date when sending notifications
SVN:trunk[1894]
2012-03-14 16:45:50 +00:00
Denis Flaven
794c111ee1 Added detecting of missing columns in the synchro_data_xxx tables (in case of duplicate SQL column names in the orignal data model). See Trac #503.
SVN:trunk[1893]
2012-03-14 16:41:32 +00:00
Romain Quetiez
6570b6a250 #527 Typo: license get an S in the US
SVN:trunk[1890]
2012-03-14 08:53:28 +00:00
Romain Quetiez
78ec612ba0 Dictionary cleanup: removed all entries related to the menu 'ConfigurationItemsMenu' which has been removed a long time ago
SVN:trunk[1889]
2012-03-14 08:47:30 +00:00
Romain Quetiez
de915644a2 #528 Typo: criticALity
SVN:trunk[1888]
2012-03-14 08:39:34 +00:00
Denis Flaven
9c4ef578f6 On going implementation of the designer
SVN:trunk[1887]
2012-03-09 08:45:12 +00:00
Denis Flaven
73e715efb4 Protect against a non-existent "MapContextParams" function
SVN:trunk[1886]
2012-03-09 08:44:06 +00:00
Denis Flaven
2c49d1f96f Restore the previous URLMaker in case the sending of a notification is not the last action of a page... (e.g. if the page displays the details of an object after sending the notifications...)
SVN:trunk[1885]
2012-03-07 18:57:54 +00:00
Denis Flaven
97647c5a5a Portal fixes (relative URLs and parameter validation)...
SVN:trunk[1884]
2012-03-07 18:55:16 +00:00
Denis Flaven
114adc3b0b Experimental support of PDF output for iTop pages, provided that mPDF is installed in lib/MPDF [1876]
SVN:trunk[1883]
2012-03-07 18:54:03 +00:00
Denis Flaven
166102da5f Update to the Italian dictionary (thanks Laura !)
SVN:trunk[1882]
2012-03-07 18:51:50 +00:00
Denis Flaven
57bb66fe41 Typo
SVN:trunk[1881]
2012-03-07 18:50:58 +00:00
Denis Flaven
4a16a76e37 - Current block Id not passed to the chart ?
SVN:trunk[1880]
2012-03-07 18:48:15 +00:00
Denis Flaven
52ef62258b Small fix for genericity
SVN:trunk[1879]
2012-03-07 18:42:15 +00:00
Denis Flaven
475f11e4bc Experimental support of PDF output for iTop pages, provided that mPDF is installed in lib/MPDF [1876]
SVN:trunk[1878]
2012-03-07 18:35:57 +00:00
Denis Flaven
1de3672173 - Current block Id not passed to the chart ? [1871]
SVN:trunk[1877]
2012-03-07 17:31:56 +00:00
Denis Flaven
0d6244b9eb On going implementation of the designer
SVN:trunk[1870]
2012-03-02 15:52:21 +00:00
Denis Flaven
453da6be38 - Bug fix: the hierarchical key in Organizations is not always named 'parent_id' !
SVN:trunk[1869]
2012-02-27 16:25:14 +00:00
Denis Flaven
22acd83abb Make sure that the creation of the data_synchro_xxx tables uses the utf8 charset and collation and the same DB Engine as the rest of the database.
SVN:trunk[1867]
2012-02-23 13:59:45 +00:00
Denis Flaven
b172c53aad Typo!
SVN:trunk[1863]
2012-02-21 14:05:23 +00:00
Denis Flaven
ff6ccacf9f Added the ability to Find then Remove a tab inside a page
SVN:trunk[1861]
2012-02-20 17:17:53 +00:00
Romain Quetiez
b3dadcba77 Reintegrated from branch 1.2: capability to develop a module to share objects between organizations (beta)
SVN:trunk[1859]
2012-02-17 15:11:00 +00:00
Denis Flaven
92c1a4af63 Delay the storage of the dictionary in the cache to allow for its alteration during the initialization of the classes
SVN:trunk[1857]
2012-02-17 12:50:22 +00:00
Denis Flaven
60fb1f1126 Distinguish between creation and modification rights
SVN:trunk[1855]
2012-02-17 12:46:34 +00:00
Denis Flaven
fe7f65893e Allow more than 64K for the email content (including attachments)
SVN:trunk[1851]
2012-02-10 12:31:02 +00:00
Romain Quetiez
d4816ddc51 Implemented the capability to modify queries by the mean of a plugin (reintegrated from branch 1.2, release candidate)
SVN:trunk[1849]
2012-02-08 15:16:16 +00:00
Denis Flaven
20077a4c3a Update to the Brazilian translation, thanks to Marco Tulio
SVN:trunk[1835]
2012-01-31 17:50:01 +00:00
Denis Flaven
392344dcd8 (Tried to) improve the display of the Synchronization Tooltip that "sometimes" does not work on IE 8...
SVN:trunk[1833]
2012-01-31 17:37:47 +00:00
Denis Flaven
137f712a96 Removed a (useless) hardcoded reference to FunctionalCI
SVN:trunk[1831]
2012-01-31 15:46:18 +00:00
Denis Flaven
429cd73e99 SQL Block with parameters were always displayed as table, whatever their type...
SVN:trunk[1829]
2012-01-31 15:08:48 +00:00
Denis Flaven
3a5f73fe65 Fixed typos during the copy/paste for automatic account creation
SVN:trunk[1827]
2012-01-31 12:44:16 +00:00
Denis Flaven
2645194f51 Typo !
SVN:trunk[1824]
2012-01-30 17:53:21 +00:00
Denis Flaven
b97066dad7 Make the OQLQuery class import-able
SVN:trunk[1823]
2012-01-30 17:52:16 +00:00
Denis Flaven
b7e5e7b1da Put some default reconciliation keys to ease the use of CSV import
SVN:trunk[1821]
2012-01-30 17:44:56 +00:00
Denis Flaven
a8f53294a9 Protect against empty list of reconciliation keys
SVN:trunk[1819]
2012-01-30 17:41:01 +00:00
Romain Quetiez
17f3318d63 Export for spreadsheets: transform keys (id of the queried object or external keys) into the corresponding friendly name (reintegrated from branch 1.2.1)
SVN:trunk[1816]
2012-01-30 11:09:07 +00:00
Romain Quetiez
cc1dc4a938 Object details: log always displayed AFTER plugin data (reintegrated from branch)
SVN:trunk[1813]
2012-01-27 13:56:00 +00:00
Denis Flaven
20b7944118 Fixed Trac#518: properly pass the context (i.e. org_id) to the auto-refresh lists
SVN:trunk[1811]
2012-01-25 10:31:03 +00:00
Denis Flaven
f0b3ec77da GetIcon now returns an absolute URL...
SVN:trunk[1808]
2012-01-24 17:38:41 +00:00
Denis Flaven
3c46ac9011 Added the ability to display a custom welcome/disclaimer message at the bottom of the login form.
SVN:trunk[1806]
2012-01-24 15:20:47 +00:00
Denis Flaven
ea1193b90f Fix for Trac#519 - change password bug !
SVN:trunk[1805]
2012-01-24 15:11:20 +00:00
Romain Quetiez
8b493177e6 Improved the check on data model consistency: detection of SQL columns used by two attributes (reintegrated from branch 1.2)
SVN:trunk[1803]
2012-01-24 13:09:41 +00:00
Romain Quetiez
e9f707e480 Improved the check on data model consistency: detection of SQL columns used by two attributes
SVN:trunk[1802]
2012-01-24 13:07:05 +00:00
Romain Quetiez
104959f9ff Fixed cosmetic bug in the tool to test queries (issue with double quote in the queries)
SVN:trunk[1798]
2012-01-23 08:34:17 +00:00
Romain Quetiez
e90e35dbc6 Optimized memory usage when auditing large volumes of CIs (10'000 items was requiring 200 Mb, it now runs with 32 Mb -including the 30Mb overhead!)
SVN:trunk[1797]
2012-01-20 15:26:45 +00:00
Denis Flaven
68ecf03f19 Properly initialize source_dir (and target_dir) to avoid scanning the complete subdirectory, which causes a duplicate load of the modules files in case of an upgrade and thus causes the upgrade to fail.
SVN:trunk[1792]
2012-01-19 16:39:43 +00:00
Romain Quetiez
afc6efe91c Case log now largely bigger than 64 Kilobytes...
SVN:trunk[1790]
2012-01-19 15:29:05 +00:00
Romain Quetiez
9fce2da012 Fixed regression in the tool to test queries: losing the query when there is a syntax error
+ Improved the reporting in case of OQL syntax error, and if the fix is straightforward

SVN:trunk[1789]
2012-01-19 10:19:09 +00:00
Romain Quetiez
5767d14c3e #516 and #517 Improved the export (specify fields for multi-column queries) and web queries (default field list) (reintegrated from branch 1.2)
SVN:trunk[1787]
2012-01-18 17:09:53 +00:00
Romain Quetiez
a401212e31 Updated the setup to fetch modules from 'datamodel'
SVN:trunk[1784]
2012-01-17 16:53:59 +00:00
Romain Quetiez
5510b3964d Moved modules to datamodel
SVN:trunk[1783]
2012-01-17 16:38:25 +00:00
Romain Quetiez
5e01e0659e Dehardcoded the datamodel source files from the subdirectory "modules" - done!
SVN:trunk[1782]
2012-01-17 16:26:52 +00:00
Romain Quetiez
1da33effd8 Dehardcoded the datamodel source files from the subdirectory "modules" - updated the compiler to take this into account
SVN:trunk[1781]
2012-01-17 16:13:12 +00:00
Romain Quetiez
f253b10ddd Dehardcoded the datamodel source files from the subdirectory "modules"
SVN:trunk[1780]
2012-01-17 15:54:48 +00:00
Romain Quetiez
39de3e9af7 De-hardcoded against the subdirectory "modules" (excepted the data model sources files)
SVN:trunk[1779]
2012-01-17 15:31:51 +00:00
Denis Flaven
851db98745 Added support for environment switching
SVN:trunk[1777]
2012-01-17 15:09:30 +00:00
Denis Flaven
123bac0e56 Added support for the 'replaced' operation which is useful for display lists
SVN:trunk[1776]
2012-01-17 15:08:17 +00:00
Denis Flaven
e888b11fff Make sure that we reference the config file using an absolute path... just in case...
SVN:trunk[1775]
2012-01-17 14:58:07 +00:00
Romain Quetiez
8dc13026a0 Environments: banner displaying a message if env is NOT 'production' + factorized some code to create/update a config from the page parameters
SVN:trunk[1774]
2012-01-16 16:41:18 +00:00
Romain Quetiez
2931a0fd42 Added 'is_link' to the definition of classes
SVN:trunk[1773]
2012-01-13 15:32:36 +00:00
Romain Quetiez
1392983a92 Compiler: added flag 'mandatory'
SVN:trunk[1772]
2012-01-13 13:37:29 +00:00
Romain Quetiez
b6b9ef35e9 Fixed issue with flags in modules
SVN:trunk[1771]
2012-01-13 13:36:39 +00:00
Denis Flaven
989f649405 Support pages that do not track tabs selection in their history
SVN:trunk[1770]
2012-01-13 11:06:34 +00:00
Romain Quetiez
01bae34a90 Ignore the conf dir in SVN
SVN:trunk[1769]
2012-01-13 10:52:44 +00:00
Romain Quetiez
c7c69660bc Setup: the conf dir is created by the setup program
SVN:trunk[1768]
2012-01-13 10:49:43 +00:00
Romain Quetiez
da4f475751 Bidouilling... to have the checkout create the empty "conf" directory
SVN:trunk[1767]
2012-01-13 09:52:51 +00:00
Romain Quetiez
a362494354 Fixed cosmetic bug (class icon - loss of styles)
SVN:trunk[1766]
2012-01-13 09:19:12 +00:00
Romain Quetiez
64e5d57ac3 Finalized the setup (missing files)
SVN:trunk[1765]
2012-01-13 09:11:10 +00:00
Romain Quetiez
cb5f09cbf2 Modules replaced by the brand new datamodel source (xml) - known issue : the flags are not correct (or not correctly interpreted in the compilation)
SVN:trunk[1764]
2012-01-12 17:15:27 +00:00
Denis Flaven
780fb6dc27 Fixed absolute/relative path issues in the JS and href places
SVN:trunk[1763]
2012-01-12 16:58:26 +00:00
Romain Quetiez
87bf09995d Updated session mechanisms: itop_env defaults to 'production', and adapted the setup to create the configuration
SVN:trunk[1762]
2012-01-12 14:46:49 +00:00
Romain Quetiez
63f4ec3f82 Factorized the tools to create/update a given installation (environment)
SVN:trunk[1761]
2012-01-11 16:06:23 +00:00
Denis Flaven
308ec94f8c Added self-registering / user synchronization extensibility
SVN:trunk[1759]
2012-01-11 15:59:29 +00:00
Romain Quetiez
6a9ea25b27 Setup based on either compiled modules or xml datamodel files (or both).
SetupWebPage is an alias for ModuleDiscovery and the module files should be updated progressively to invoke ModuleDiscovery::AddModule() instead.
The implementation of the module still assumes they are in the directory 'modules'... this has to be changed later to ensure the distinction between the source modules and the executed modules

SVN:trunk[1758]
2012-01-11 15:04:15 +00:00
Denis Flaven
f29d673ffb Added self-registering / user synchronization extensibility
SVN:trunk[1756]
2012-01-11 11:17:08 +00:00
Denis Flaven
cf65b58981 Make sure that the path/href base is correct to display the page (images, CSS...)
SVN:trunk[1755]
2012-01-09 08:57:48 +00:00
Denis Flaven
8f8f575c65 Bug fix: use the proper way to set the message-ID with Swift
SVN:trunk[1754]
2012-01-04 10:45:58 +00:00
Romain Quetiez
79509e5510 Readme updated, ready for 1.2.1 beta
SVN:trunk[1751]
2011-12-22 09:44:47 +00:00
Romain Quetiez
e147d54404 #512 Allow the CLI mode for export.php
SVN:trunk[1749]
2011-12-21 15:21:58 +00:00
Denis Flaven
5976862a3c Updated the readme file to prepare for the 1.2.1 beta release
SVN:trunk[1748]
2011-12-21 15:15:00 +00:00
Denis Flaven
b2d7c51e6d Fixed Trac #480: properly take into account the 'min_autocomplete_chars' settings.
SVN:trunk[1744]
2011-12-21 14:20:24 +00:00
Denis Flaven
248d4d3e10 Allow to filter which class(es) of objects are displayed in the graphical impact analysis view
SVN:trunk[1741]
2011-12-21 14:09:28 +00:00
Romain Quetiez
32dd31b69d #509 Error in the display of a replica not yet synchronized
SVN:trunk[1739]
2011-12-21 13:34:42 +00:00
Denis Flaven
263594bc4a Protects against a JS error while the synchro is running in chunk mode
SVN:trunk[1738]
2011-12-21 13:12:54 +00:00
Romain Quetiez
e07912af34 CSV import (both std and related to the data synchro) can be used with a tab (keyword 'tab', case insensitive)
SVN:trunk[1737]
2011-12-21 12:51:08 +00:00
Romain Quetiez
ea2843f04e Synchro: fixed issue in the counter (deletion of replicas) and improved the behavior in case of an error happening in the underlying process
SVN:trunk[1736]
2011-12-21 12:11:56 +00:00
Romain Quetiez
4869d5e92b Fixed typos in dictionaries
SVN:trunk[1735]
2011-12-21 11:14:53 +00:00
Romain Quetiez
d8fc264adf Synchro: need to order the replicas by id for the split algorithm to work fine
SVN:trunk[1734]
2011-12-21 10:03:23 +00:00
Romain Quetiez
72ac150faa Synchro: keep track of the memory usage peak (SynchroLog), should the synchro be executed at once or in several steps
SVN:trunk[1733]
2011-12-21 08:52:14 +00:00
Romain Quetiez
9ac4f84e22 #489 Run the synchro with parameter max_chunk_size to split the execution into several processes, each loading at most the given count of replica (note: the master process will continue to run while child processes are being forked one by one)
SVN:trunk[1732]
2011-12-20 15:55:07 +00:00
Denis Flaven
d74f562dba Allow a module to restrict the access to a given menu/group by redeclaring the menu with restricted rights. All rights are combined with the AND operator.
SVN:trunk[1731]
2011-12-20 15:26:08 +00:00
Denis Flaven
fe060e30e5 Fixed Trac #486: SQL dashboards limitations
SVN:trunk[1729]
2011-12-20 15:14:10 +00:00
Denis Flaven
3e2d906f0d Bug fix: missing (but useless) parameter in ExpandArgs causes a notice...
SVN:trunk[1727]
2011-12-20 12:39:15 +00:00
Denis Flaven
321e7dc309 Keep the current value iin the search form when reloading the search form for a different (sub)class. For example Contact => Person.
SVN:trunk[1725]
2011-12-19 17:27:24 +00:00
Denis Flaven
123f5c4cf5 When searching objects to add to the current object (n:n relationship), set the default search params in order to stay in the current silo.
SVN:trunk[1723]
2011-12-19 16:28:09 +00:00
Denis Flaven
0563b05b03 Bug fix: apply the AllowedValues constraints(as default values) when selecting elements via the "magnifier" button or creating an new element via the "plus" button... also make sure that allowed values is enforced
SVN:trunk[1720]
2011-12-15 10:41:01 +00:00
Romain Quetiez
701d49f70d Fixed regression in the setup: query class must be included in the default configuration
SVN:trunk[1718]
2011-12-14 17:40:26 +00:00
Romain Quetiez
90f3aa0c9d Forgot new file on previous commit
SVN:trunk[1717]
2011-12-14 17:32:48 +00:00
Romain Quetiez
ea10d202a6 #485 Improved the end-user experience with Excel and the web queries (added a phrasebook) + link to test the OQL attributes (query phrasebook or email actions, etc.) including the handlink of query arguments) + fixed wrong prototypes for a few implementations of GetBareProperties()
SVN:trunk[1716]
2011-12-14 17:23:01 +00:00
Denis Flaven
ec19ef982e Bug fix: apply the AllowedValues constraints(as default values) when selecting elements via the "magnifier" button or creating an new element via the "plus" button.
SVN:trunk[1714]
2011-12-13 14:37:31 +00:00
Romain Quetiez
487aa95ae2 Ooops...
SVN:trunk[1712]
2011-12-13 14:25:29 +00:00
Romain Quetiez
9197c6a17c #485 Export for MS Excel web queries: format=spreadsheet
SVN:trunk[1711]
2011-12-13 14:21:13 +00:00
Denis Flaven
8231420c44 - New way to handle sessions compatible with multiple environments
SVN:trunk[1710]
2011-12-08 15:37:48 +00:00
Denis Flaven
bb3d08b8a3 Make the data model load even without Contacts !
SVN:trunk[1709]
2011-12-08 15:31:10 +00:00
Denis Flaven
8656611dc2 Bug fix: paginated lists were broken in the "List" tab of the Impact Analysis
SVN:trunk[1707]
2011-12-07 10:38:05 +00:00
Romain Quetiez
6cb052043a Added a test for searching on SOAP
SVN:trunk[1706]
2011-12-06 14:29:56 +00:00
Denis Flaven
02b34515f3 Pass the current object to GetEditValue
SVN:trunk[1704]
2011-12-02 16:26:23 +00:00
Denis Flaven
d081d1f4df Pass the current objet to GetAsHTML
SVN:trunk[1703]
2011-12-02 16:16:53 +00:00
Denis Flaven
c16dce00ae - Bug ! Incorrectly appending a parameter ?version= to linked scripts already containing a parameter in their URL !
- changed the parameter name to itopversion to avoid collisions

SVN:trunk[1701]
2011-12-01 17:15:52 +00:00
Denis Flaven
99c4128ee6 Increased Suhosin minimum value for get_max_value to 2048 due to a bug seen on some installations
SVN:trunk[1699]
2011-11-30 13:21:38 +00:00
Denis Flaven
f17f4e1f78 Use the default language when creating a new user from CAS
SVN:trunk[1697]
2011-11-29 15:54:53 +00:00
Denis Flaven
a94ccb9091 Support patterns for the definition of casMemberOf groups.
SVN:trunk[1695]
2011-11-29 15:31:04 +00:00
Denis Flaven
32b312c871 Allow to log entries on behalf of another user
SVN:trunk[1693]
2011-11-24 16:17:09 +00:00
Denis Flaven
c1fce0d68a Implementing the use of GetDisplayValue for editing attributes
SVN:trunk[1692]
2011-11-23 15:51:59 +00:00
Romain Quetiez
b0f21fd656 In french, organiZation takes an S
SVN:trunk[1690]
2011-11-23 15:28:48 +00:00
Denis Flaven
e30bfb53c6 - Don't create warnings for empty ext keys (i.e. empty string)
- Properly record multiple warnings
- Don't record warnings in case of creation error (error has precedence)

SVN:trunk[1688]
2011-11-23 09:51:57 +00:00
Romain Quetiez
7a83237f11 Cosmetic on the API of the dictionnary (internal)
SVN:trunk[1687]
2011-11-21 17:07:58 +00:00
Denis Flaven
22f79b7d99 Properly handle restrictions (e.g. AllowedValues) on objects that are used as n:n relationships managed via LinksWidget.
SVN:trunk[1684]
2011-11-21 13:00:36 +00:00
Denis Flaven
7658f5dba5 Properly handle restrictions (e.g. AllowedValues) on objects that are used as n:n relationships managed via LinksWidget.
SVN:trunk[1682]
2011-11-21 10:40:00 +00:00
Denis Flaven
0c2390cce6 New type of attribute: percentage
SVN:trunk[1681]
2011-11-18 17:01:11 +00:00
Denis Flaven
bac970b5c7 New type of attribute: percentage
SVN:trunk[1680]
2011-11-18 16:53:17 +00:00
Denis Flaven
9f186f3b60 New type of attribute: percentage
SVN:trunk[1679]
2011-11-18 16:44:08 +00:00
Denis Flaven
e1dcdddebf Also improved the search/display of replicas
SVN:trunk[1677]
2011-11-18 11:33:27 +00:00
Denis Flaven
ee6c2fcffe Enhancement: when an (optional) external key cannot be reconciled, log a warning on the replica. the replicas containing a warning are then processed everytime in case the ext key changes
Also improved the search/display of replicas

SVN:trunk[1676]
2011-11-18 11:32:19 +00:00
Denis Flaven
18c9852db3 Enhancement: when an (optional) external key cannot be reconciled, log a warning on the replica. the replicas containing a warning are then processed everytime in case the ext key changes
Also improved the search/display of replicas

SVN:trunk[1675]
2011-11-18 11:29:04 +00:00
Romain Quetiez
a5547d4d26 Fixed bug in change tracking: it was impossible to factorize code creating objects in the background either in the context of an object update or in the context of the application of a stimulus
SVN:trunk[1673]
2011-11-18 10:43:30 +00:00
Denis Flaven
fb234f3b15 Prevent warnings when checking the available stimuli in the menu...
SVN:trunk[1671]
2011-11-16 17:23:37 +00:00
Denis Flaven
25d29da058 Prevent Javascript errors in case a name contains a quote.
SVN:trunk[1669]
2011-11-15 13:09:38 +00:00
Romain Quetiez
169bf4948b Data sync: fixed issue when a replica left in state 'new' becomes obsolete
SVN:trunk[1668]
2011-11-15 09:29:07 +00:00
Denis Flaven
17ef280b27 - Reload the object after applying a stimulus, in case an action has an effect on an external field...
SVN:trunk[1667]
2011-11-14 10:57:52 +00:00
Denis Flaven
017f767a6e - Reload the object after applying a stimulus, in case an action has an effect on an external field...
SVN:trunk[1666]
2011-11-14 10:54:03 +00:00
Denis Flaven
340ff612cd - Regression from previous fix: don't Reload an object before saving it !
SVN:trunk[1663]
2011-11-10 16:38:06 +00:00
Denis Flaven
3746dad8f0 - Removed some obsolete code that was causing a warning !
SVN:trunk[1661]
2011-11-10 10:23:30 +00:00
Denis Flaven
699f18394b - Don't activate triggers if the transition fails
- Reload the object, in case some custom action changed an external object

SVN:trunk[1660]
2011-11-10 10:20:28 +00:00
Denis Flaven
6963d630d5 Better error handling in case of OQL error
SVN:trunk[1656]
2011-11-09 17:01:42 +00:00
Denis Flaven
5c55291391 Better error handling in case of OQL error
SVN:trunk[1655]
2011-11-09 17:01:12 +00:00
Denis Flaven
48fc735d1f Allow computation of default values when managing relationships
SVN:trunk[1654]
2011-11-06 10:21:58 +00:00
Romain Quetiez
dfe7997f53 Added helpers to export the datamodel
SVN:trunk[1653]
2011-11-04 11:48:42 +00:00
Denis Flaven
53a2f9e527 Added the capability for plug-ins to "listen" to add/remove attachment events.
SVN:trunk[1651]
2011-10-28 12:43:46 +00:00
Denis Flaven
14b86abdbe Allow specifying the label of the sender email
SVN:trunk[1650]
2011-10-28 12:41:20 +00:00
Romain Quetiez
e00c32b867 Fixed SOAP test
SVN:trunk[1648]
2011-10-25 13:03:36 +00:00
Romain Quetiez
7ff6e52ede Internal issue: automated tests generating lots of warning
SVN:trunk[1647]
2011-10-25 12:51:37 +00:00
Romain Quetiez
f8a582b375 New helper class: TemplateString - to allow extended syntaxes such as $this->location_id->org_id->parent_id->name$... to be progressively introduced and replace the heavy ToArgs()
SVN:trunk[1645]
2011-10-24 13:37:37 +00:00
Denis Flaven
1eda4dde56 Bug fix: Trac #494. It seems that PHPSoap does not understand the <wsdl:documentation> tag.
SVN:trunk[1643]
2011-10-21 08:35:41 +00:00
Romain Quetiez
b0373f576c Fixed issue: nobody in the list of persons to notify for portal users (security takes precedence)
SVN:trunk[1642]
2011-10-21 08:10:39 +00:00
Romain Quetiez
ffb3750806 Fixed regressions due to the integration of SwiftMailer
SVN:trunk[1639]
2011-10-20 15:52:38 +00:00
Romain Quetiez
649f7c609b Generation of emails based on the lib SwiftMailer (implements the SMTP protocole)
SVN:trunk[1638]
2011-10-20 15:14:17 +00:00
Romain Quetiez
ff201e8b8a Improved the upgrade/toolkit: make sure that NULL is allowed for SQL columns found in the DB but not defined in the data model (attribute removed or renamed)
SVN:trunk[1637]
2011-10-20 11:45:52 +00:00
Denis Flaven
f979dd40e0 Merged-in the fix for Trac #493: incorrect display of the Users' Grant Matrix
SVN:trunk[1636]
2011-10-17 11:27:03 +00:00
Denis Flaven
32be1aafe0 Better way to keep track that an e,ail is linked to a given iTOp object: message-id + references
SVN:trunk[1633]
2011-10-13 14:43:45 +00:00
Denis Flaven
982c55738f Allow to override some default headers when sending an email
SVN:trunk[1632]
2011-10-13 14:37:36 +00:00
Romain Quetiez
bdebb33822 #488 Replaced mysql extension with mysqli
SVN:trunk[1630]
2011-10-10 15:42:10 +00:00
Denis Flaven
48cb402874 Fixed Trac #487: resizable textareas remained invisible when located on the second tab (Merged from 1.2 branch)
SVN:trunk[1629]
2011-10-04 10:57:09 +00:00
Denis Flaven
c15e4ffb10 Small fix for bulk modify: non-homogeneous fields may be mandatory...
SVN:trunk[1626]
2011-10-03 13:56:49 +00:00
Denis Flaven
cbafb5f1d1 Automatic synchro of CAS users
SVN:trunk[1625]
2011-10-03 13:54:58 +00:00
Denis Flaven
b1d0d039a3 More options for CAS to allow automatic synchro of CAS/LDAP users
SVN:trunk[1624]
2011-10-03 13:51:27 +00:00
Denis Flaven
d252d767b6 Initializes the admin contact's phone number, in case it is a mandatory field in the data model...
SVN:trunk[1622]
2011-09-30 08:01:17 +00:00
Denis Flaven
73968a5631 Prevent crash when trying to load the favicon during the setup !
SVN:trunk[1620]
2011-09-29 15:22:52 +00:00
Denis Flaven
0999f00912 Added a link to a favicon (icon in the browser's bar and tab)
SVN:trunk[1618]
2011-09-29 14:50:23 +00:00
Denis Flaven
6fb3195d6e GetValueLabel is used in some dashboards... make sure that it is available for any attribute
SVN:trunk[1615]
2011-09-29 09:35:26 +00:00
Denis Flaven
6ea3b62b55 Make sure that the organisation's drop-down list is not bigger than the left menu...
SVN:trunk[1614]
2011-09-29 09:32:17 +00:00
Romain Quetiez
ba3080594c #485 Export.php improved for integration into Excel / web queries (bug with IIS/HTTPS, limitation on the size of the OQL)
SVN:trunk[1612]
2011-09-29 08:07:37 +00:00
Romain Quetiez
a3a94cccbb #484 Fixed issue with IIS ("Wrong password" at first prompt)
SVN:trunk[1609]
2011-09-28 12:55:15 +00:00
Romain Quetiez
e8c8e4a0d7 #481 Special characters not transcribed correctly in Portal (regression in 1.2)
SVN:trunk[1607]
2011-09-28 10:17:53 +00:00
Denis Flaven
5b3350f6d5 Fixed Trac #482: link to OpenSearch Xml page was broken
SVN:trunk[1606]
2011-09-28 10:07:27 +00:00
Denis Flaven
b454b24e0f Rename the cron.params file to avoid overwriting the current config when updating.
SVN:trunk[1605]
2011-09-23 14:14:58 +00:00
Romain Quetiez
099afb5451 Fixed regression on attribute labels; introduced in [1581]
SVN:trunk[1602]
2011-09-23 13:30:21 +00:00
Denis Flaven
ec226ce76d Cosmetic fix: the "loading..." indicator was visible/spinning when reloading any other field of the form !
SVN:trunk[1601]
2011-09-22 16:37:47 +00:00
Denis Flaven
5080bd58d5 Removed some Debug/Warning messages
SVN:trunk[1600]
2011-09-22 16:35:33 +00:00
Denis Flaven
5ec37fd26a - Fix a problem with the upgrade (case sensitive table name & prefix)
SVN:trunk[1598]
2011-09-22 11:58:42 +00:00
Denis Flaven
8ae92cb50e - Fix a problem with the upgrade (case sensitive table name & prefix)
- Display a message when there is no attachment

SVN:trunk[1596]
2011-09-22 11:53:49 +00:00
Romain Quetiez
f82a4ada23 #478 Fixed issue in the audit: the results are wrong whenever an organization is selected
SVN:trunk[1595]
2011-09-22 11:40:30 +00:00
Denis Flaven
e5ce12ed0a Make the template system work for real !
SVN:trunk[1594]
2011-09-22 11:15:40 +00:00
Denis Flaven
428f1a26cc Typo ! Impact: Unknown ?
SVN:trunk[1593]
2011-09-22 11:08:34 +00:00
Romain Quetiez
9976999a88 Portal: updated dictionary for new buttton "reopen request"
SVN:trunk[1592]
2011-09-22 10:08:36 +00:00
Romain Quetiez
da2b8ab4c0 Fixed security issue: the attachments were visible by anybody (by forming URLs manually), whatever the allowed organizations. The change requires the execution of the setup/migration procedure.
SVN:trunk[1591]
2011-09-22 09:04:12 +00:00
Romain Quetiez
0de75db474 Portal (regression with the latest implementation, causing a warning)
SVN:trunk[1590]
2011-09-21 12:59:16 +00:00
Romain Quetiez
b014596172 #477 Could not specify more than one reconciliation key (regression) + took the opportunity to enhance protection against XSS injection (using column names in the data)
SVN:trunk[1588]
2011-09-21 12:27:08 +00:00
Romain Quetiez
b46ed4764d Fixed bug in the new portal: the wizard validation was inoperant when several selection lists were proposed
SVN:trunk[1587]
2011-09-20 13:50:11 +00:00
Romain Quetiez
2b1aecda53 Fixed recent regression on the portal (confusion btw GetContactId and GetUserId)
SVN:trunk[1586]
2011-09-20 08:22:54 +00:00
Romain Quetiez
19318973ca Fixed warning (strict standards) on previous fix
SVN:trunk[1584]
2011-09-19 11:44:04 +00:00
Romain Quetiez
cac76a7e08 #473 Could not load NW interfaces (reconciliation issue)
SVN:trunk[1581]
2011-09-19 10:36:47 +00:00
Romain Quetiez
abd645ca98 Fixed regressions in the portal
SVN:trunk[1579]
2011-09-15 15:25:30 +00:00
Romain Quetiez
7301cb9369 Updated the dictionnaries for the latest changes into the portal
SVN:trunk[1578]
2011-09-15 10:26:50 +00:00
Romain Quetiez
559de3d38f Portal reworked (code factorization, allowing for paginated lists) + added the list of closed tickets (+search)
SVN:trunk[1577]
2011-09-15 10:06:46 +00:00
Denis Flaven
552ffd79b1 Always use a drop-down list for external keys in search forms
SVN:trunk[1575]
2011-09-14 07:38:40 +00:00
Denis Flaven
aae8ca7b8b Added credentials for the security advices
SVN:trunk[1574]
2011-09-13 14:58:40 +00:00
Denis Flaven
13c636cab6 Prevent email header injection
SVN:trunk[1573]
2011-09-13 14:55:34 +00:00
Denis Flaven
cc40fc5d91 Exclude a (risky) and unused file for the build
SVN:trunk[1572]
2011-09-13 14:38:52 +00:00
Denis Flaven
bddfdb4a28 Fixed Trac #468: wrong path to the doc#installationInstructions
SVN:trunk[1571]
2011-09-12 11:22:29 +00:00
Denis Flaven
6bc639c6ce Enhanced error message in case of "file too big error" (more user friendly and not admin oriented)
SVN:trunk[1570]
2011-09-12 09:48:54 +00:00
Denis Flaven
9b8ae03413 - Properly display names containing XML entities (& < > and ")
- Don't drag items by their (invisible) tooltip !

SVN:trunk[1569]
2011-09-10 16:24:26 +00:00
Denis Flaven
17f00198d3 Fix for #404: context was lost when performing a drill-down in the flash navigator.
SVN:trunk[1568]
2011-09-10 16:20:50 +00:00
Denis Flaven
5041ec2e0c Regression on accentuated characters due to bad (forgotten) encoding on htmlentities (Trac #446)
SVN:trunk[1567]
2011-09-09 16:18:01 +00:00
Denis Flaven
ee4f7e5b6a Don't forget to track the count of updated objects...
SVN:trunk[1566]
2011-09-09 16:11:51 +00:00
Denis Flaven
cbb40b85dc Typo in a (not yet used) function !
SVN:trunk[1565]
2011-09-08 15:29:35 +00:00
Denis Flaven
9aca772209 Fix for Trac#446: prevent XSS vulnerabilities
SVN:trunk[1564]
2011-09-08 13:33:47 +00:00
Denis Flaven
c4db9cd84e More fixes for Trac#446: XSS vulnerabilities with vectors containing double quotes
SVN:trunk[1563]
2011-09-08 13:21:32 +00:00
Romain Quetiez
205e80f8a5 #446 XSS vector through the page title
SVN:trunk[1562]
2011-09-08 10:30:18 +00:00
Romain Quetiez
06e9bd0c25 #446 XSS vector on the login web page
SVN:trunk[1561]
2011-09-08 10:28:14 +00:00
Denis Flaven
1c812f8b34 Bug fix: subsequent audit results were wrong when a 'negative' rule was used.
SVN:trunk[1560]
2011-09-07 12:12:10 +00:00
Romain Quetiez
f3c5759721 Updated the readme file
SVN:trunk[1559]
2011-09-07 07:58:22 +00:00
Denis Flaven
69368a39cb Fix radio buttons for enum values.
SVN:trunk[1558]
2011-09-06 12:47:42 +00:00
Denis Flaven
ab5849b3cd Fix: properly manage validation field for vertical radio buttons.
SVN:trunk[1557]
2011-09-06 10:25:06 +00:00
Denis Flaven
1ab796bd40 One more fix for #404: context was lost when performing a bulk delete.
SVN:trunk[1556]
2011-09-06 08:07:18 +00:00
Denis Flaven
c1221054b4 Prevent a crash when displaying the details of a change (from the CSV "history" tab)
SVN:trunk[1555]
2011-09-05 16:28:31 +00:00
Denis Flaven
3bd681bc9d Bug fix: hyperlink to create a new object was wrong (typo)
SVN:trunk[1554]
2011-09-02 15:54:32 +00:00
Denis Flaven
b33989ec62 Added the ability to specify programmatically the scope for "favorite organizations". Note the the end-user can still restrict this list even further using the "Preferences" page.
SVN:trunk[1553]
2011-09-01 14:06:51 +00:00
Denis Flaven
b3121eebae Typos in the Brazilian localization, found by Bruno Cassaro. Thanks Bruno !
SVN:trunk[1552]
2011-09-01 13:07:09 +00:00
Denis Flaven
7d8971139b Missing sanitization filter for 1 parameter: fixed !
SVN:trunk[1551]
2011-09-01 11:50:34 +00:00
Denis Flaven
04f20860dc Add the name of the synchro data source as an external field in case we need to make a CSV export
SVN:trunk[1550]
2011-09-01 10:50:57 +00:00
Denis Flaven
8f21b9dabb Bug fix: prevent a warning when doing modify-all as a non-admin user.
SVN:trunk[1549]
2011-09-01 10:49:56 +00:00
Romain Quetiez
eb89610465 Readme file ready for release 1.2
SVN:trunk[1548]
2011-08-31 15:37:48 +00:00
Denis Flaven
13f4e90ee6 Make sure that we trigger a change event whenever we pickup a different value for an external key (either via the magnifier button or via the hierarchy button)
SVN:trunk[1547]
2011-08-31 14:39:55 +00:00
Denis Flaven
29f1dd5a69 Prevent an exception in some (rare?) conditions where an incorrect value was set in a temporary object....cf Trac #466
SVN:trunk[1546]
2011-08-31 14:28:41 +00:00
Romain Quetiez
a7e937ec58 Regression: the default language was German!
SVN:trunk[1545]
2011-08-31 13:09:50 +00:00
Denis Flaven
bc2a8a8c4a Improved verification to the PHP file upload settings to avoid troubles later... (Trac #284)
SVN:trunk[1544]
2011-08-31 09:03:26 +00:00
Denis Flaven
c9b0a2b561 Enhance ObjectDetailsTemplate to manage bulk actions and synchronized attributes.
SVN:trunk[1543]
2011-08-30 15:47:57 +00:00
Romain Quetiez
5fcea9bf69 #437 Regressions in the beta - wrong reporting of the bulk update
SVN:trunk[1542]
2011-08-30 15:09:54 +00:00
Denis Flaven
3b42c86f46 Regression due to the interaction of the two previous fixes. (Trac #444 and Trac #448)
SVN:trunk[1541]
2011-08-30 11:12:14 +00:00
Romain Quetiez
038fe30fbe #363 Charts not displaying with IE8 + IIS + HTTPS
SVN:trunk[1540]
2011-08-30 10:13:58 +00:00
Denis Flaven
aa95e51e00 Fixed Trac #444: preserve sort order and page size when reloading a paginated list.
SVN:trunk[1539]
2011-08-30 09:38:06 +00:00
Romain Quetiez
6a78c7dcc2 Internal: Fixed regression introduced in [1365] and present in the beta version (overviews not working with HTTPS)
SVN:trunk[1538]
2011-08-30 09:20:29 +00:00
Romain Quetiez
17aa1d1fc0 #464 While upgrading iTop, copy the values from change/start_date to ticket/start_date (iif the current value is null)
SVN:trunk[1537]
2011-08-29 16:28:08 +00:00
Romain Quetiez
24244f1bac Rollback: restored attributes removed by mistake
SVN:trunk[1536]
2011-08-29 16:26:17 +00:00
Romain Quetiez
f7be35848c Internal: detect unused columns while upgrading
SVN:trunk[1535]
2011-08-29 15:37:58 +00:00
Denis Flaven
1aa7ff14fe Removing duplicate fields
SVN:trunk[1534]
2011-08-29 15:02:59 +00:00
Denis Flaven
c33a508376 Removing duplicate fields
SVN:trunk[1533]
2011-08-29 15:02:12 +00:00
Romain Quetiez
37ec8b12f3 Upgrade/toolkit: improved the data sources update procedure (no need to create "direct" linkset attributes, remove old attributes, more verbose)
SVN:trunk[1532]
2011-08-29 13:23:57 +00:00
Romain Quetiez
dde3efc0c7 Upgrade: adjust data sources, and be more verbose about upgrades in the DB (HKeys and data sources)
SVN:trunk[1531]
2011-08-29 13:20:44 +00:00
Romain Quetiez
54f3dec7a9 Upgrade: fails to recreate a view when is has become invalid (missing attribute)
SVN:trunk[1530]
2011-08-29 13:18:30 +00:00
Denis Flaven
6be497ee7d Fixed Trac#465: incorrect logic when resetting the 'ConnectedToInterface'
SVN:trunk[1529]
2011-08-29 11:55:13 +00:00
Denis Flaven
5d9d44dc5e Fix: detection of the Suhosin extension during the installation and tell the user if the get_max_value is too small.
SVN:trunk[1528]
2011-08-29 11:49:22 +00:00
Denis Flaven
d547394ba2 Fixed Trac#448: paginated display broken after reloading a page containing a list where the page size is set to 'All'
SVN:trunk[1527]
2011-08-26 11:07:29 +00:00
Denis Flaven
ace0a3e278 Finishing fix of Trac449:display selection of favorite organizations
SVN:trunk[1526]
2011-08-26 10:45:46 +00:00
Denis Flaven
dd9cb27971 Fixed Trac #449: properly reload the current selection when the list of Favorite Organizations is a paginated list.
SVN:trunk[1525]
2011-08-26 10:34:54 +00:00
Denis Flaven
a3dc8ab3d7 Added the ability to reload a selection fro a paginated list.
SVN:trunk[1524]
2011-08-26 10:33:37 +00:00
Denis Flaven
496bf386be Force the rebuilding of hierarchical keys on every upgrade, since in 1.2-beta the rebuilding of HK was buggy. (May be the cause of the bugs: #459, #460 and #461)
SVN:trunk[1523]
2011-08-26 09:08:54 +00:00
Denis Flaven
c337616fea Fix: regression for tickets with just a default initial state.
SVN:trunk[1522]
2011-08-26 07:26:30 +00:00
Denis Flaven
186ba76092 Added a flag to force the import to cinutnue even in case of SQL error... useful for debugging don't use in production ;-)
SVN:trunk[1521]
2011-08-25 17:37:56 +00:00
Denis Flaven
20a06c9212 Fixed a warning...
SVN:trunk[1520]
2011-08-25 17:03:12 +00:00
Romain Quetiez
e515dfb434 Cosmetics: regression due to an unnecessary protection against XSS attacks
SVN:trunk[1519]
2011-08-25 16:35:38 +00:00
Romain Quetiez
2ee85dd407 Customization: added a check against reuse of an attribute code (collision can happen easily with inheritance)
SVN:trunk[1518]
2011-08-25 16:10:57 +00:00
Denis Flaven
6df6e4a9cc - Allow creation of an ticket in a different initial state via the new 'initial_state_path' attribute.
- Support update of CaseLog fields in bulk_modify mode.

SVN:trunk[1517]
2011-08-25 16:04:58 +00:00
Romain Quetiez
c74c955973 Internal: it is possible to override the user authorized organizations
SVN:trunk[1516]
2011-08-25 15:45:45 +00:00
Romain Quetiez
a3a4fd0a75 Dictionary: English was proposed twice in the list of available languages!
SVN:trunk[1515]
2011-08-25 12:01:04 +00:00
Romain Quetiez
c7f3ac0361 Internal: suppressed a warning on ormCaseLog
SVN:trunk[1514]
2011-08-25 11:57:18 +00:00
Romain Quetiez
21002574ce Notifications: case log in plain text (this->case_log) or the latest entry (this->head(case_log))
SVN:trunk[1513]
2011-08-25 11:49:15 +00:00
Romain Quetiez
64cae5b58b Portal: activate TriggerOnPortalUpdate
SVN:trunk[1512]
2011-08-25 11:46:23 +00:00
Romain Quetiez
cc1105ff60 Email test: under IIS it was not detecting Windows correctly, and the help message was therefore completely wrong.
SVN:trunk[1511]
2011-08-25 10:04:21 +00:00
Romain Quetiez
71655f0632 Fixed regression (shortcut actions, display a list of objects not deriving from cmdbAbstract)
SVN:trunk[1510]
2011-08-25 09:29:52 +00:00
Denis Flaven
6e4e5be35d Allow TriggerOnPortalUpdate to work also on derived classes: i.e a trigger defined for the class 'Ticket' will be called when a 'UserRequest' (derived from Ticket) will be updated from the portal.
SVN:trunk[1509]
2011-08-25 08:03:39 +00:00
Denis Flaven
872a424a78 Bug fix: allow modifying an object as if in target state, before applying the stimulus (cf PortalWebpage).
SVN:trunk[1508]
2011-08-24 16:00:28 +00:00
Denis Flaven
612c355e03 Bug fix: allow modifying an object as if in target state, before applying the stimulus (cf PortalWebpage).
SVN:trunk[1507]
2011-08-24 15:58:56 +00:00
Denis Flaven
ffa9b21364 Bug fix: allow modifying an object as if in target state, before applying the stimulus (cf PortalWebpage).
SVN:trunk[1506]
2011-08-24 15:58:22 +00:00
Denis Flaven
180311fd0a Fixed a side effect of Trac#446 (XSS)
SVN:trunk[1505]
2011-08-24 15:56:48 +00:00
Denis Flaven
16b7714139 Default shortcut actions are now a parameter
SVN:trunk[1504]
2011-08-24 14:11:57 +00:00
Denis Flaven
c6865cf63d Default shortcut actions are now a parameter
SVN:trunk[1503]
2011-08-24 14:11:25 +00:00
Denis Flaven
905d9b5931 Apply organization filtering to subclasses of Organization as well.
SVN:trunk[1502]
2011-08-24 13:54:27 +00:00
Denis Flaven
0850370275 Bug fix: let accented characters pass through the autocomplete
SVN:trunk[1501]
2011-08-24 13:53:19 +00:00
Romain Quetiez
38591f444e Fixed regression on latest fix (Hkeys and inheritance)
SVN:trunk[1500]
2011-08-24 10:49:39 +00:00
Denis Flaven
ae36aa1f74 German localization update, thanks to Stephan Rosenke.
SVN:trunk[1499]
2011-08-23 16:55:01 +00:00
Denis Flaven
264af56591 Pass the final class to GetShortcutActions
SVN:trunk[1498]
2011-08-23 13:56:09 +00:00
Denis Flaven
0a2c9d0e37 Automatic rebuild of the dictionary list during setup, based on the content of the 'dictionaries' folder.
SVN:trunk[1497]
2011-08-23 13:13:08 +00:00
Denis Flaven
9d9d53c967 Fix regression on hierarchical keys
SVN:trunk[1496]
2011-08-23 11:14:25 +00:00
Denis Flaven
54315f41e4 Bug fix: computation of allowed stimuli was wrong
SVN:trunk[1495]
2011-08-23 09:17:19 +00:00
Denis Flaven
a04e7ea375 Bug fix in HierarchicalKey: host class can be different from target class in case of derived classes.
SVN:trunk[1494]
2011-08-23 07:24:55 +00:00
Erwan Taloc
795296e6e3 update after test made by Erwan
SVN:trunk[1493]
2011-08-23 07:13:28 +00:00
Denis Flaven
98ac85c098 Added the display of the total count of objects above 'group by' blocks.
SVN:trunk[1492]
2011-08-22 16:42:28 +00:00
Romain Quetiez
e53c07393c Internal: improved the API of PortalWebPage
SVN:trunk[1491]
2011-08-22 10:09:21 +00:00
Denis Flaven
48d66447b7 Support an abstract "Organization" class: if it's the case don't create any Org neither Person during the setup.
SVN:trunk[1490]
2011-08-22 10:06:48 +00:00
Romain Quetiez
54b4f67ed6 Fixed regression due to the enhanced security (ignore a field in CSV import)
SVN:trunk[1489]
2011-08-22 08:49:52 +00:00
Denis Flaven
9836797da9 Regression due to XSS fix (#446): auto-refresh block was showing an assertion failed message. Fixed.
SVN:trunk[1488]
2011-08-22 08:37:08 +00:00
Romain Quetiez
931a260435 Fixed regression due to the enhanced security (param file)
SVN:trunk[1487]
2011-08-22 08:32:25 +00:00
Denis Flaven
2a6d36b9cd Bug fix: (post 1.2-beta) warning message (undefined variable aFavoriteActions) when displaying a list for a non-admin user...
SVN:trunk[1486]
2011-08-22 08:19:35 +00:00
Denis Flaven
9420c14c13 Fixed a (post 1.2-beta) regression: bulk modify and bluk apply stimulus were broken.
SVN:trunk[1485]
2011-08-22 08:18:07 +00:00
Romain Quetiez
272807ba56 Export.php - fixed a regression (specifying no field list resulting in an error)
SVN:trunk[1484]
2011-08-22 07:38:38 +00:00
Denis Flaven
d05ea1863c Fixed a (post 1.2-beta) regression: bulk modify and bluk apply stimulus were broken.
SVN:trunk[1483]
2011-08-21 16:41:23 +00:00
Denis Flaven
6c8e021727 Fixed Trac# 457: crash when deleting two organizations.
SVN:trunk[1482]
2011-08-19 17:14:06 +00:00
Romain Quetiez
266135b1b9 #452 Export.php - field list can be specified also for HTML output
SVN:trunk[1481]
2011-08-19 16:14:50 +00:00
Denis Flaven
02fc610a18 Bug fix: "shortcut" actions were not perserving the current tab. Fixed.
SVN:trunk[1480]
2011-08-19 16:08:27 +00:00
Denis Flaven
e2aa3cc69f Enhancement: provide a warning (popup) message when navigating away from an edition form.
SVN:trunk[1479]
2011-08-19 15:50:17 +00:00
Denis Flaven
de516c0ce4 Fixed Trac #458: back button was asking to fill the mandatory fields !
SVN:trunk[1478]
2011-08-19 15:41:05 +00:00
Romain Quetiez
92ac1fe2ca #148 Allow overloading attribute/enum labels in the dictionary
SVN:trunk[1477]
2011-08-19 15:30:43 +00:00
Romain Quetiez
cc7844ea10 Security: protected bulk modify against HTTP/Post piracy
SVN:trunk[1476]
2011-08-19 15:26:05 +00:00
Denis Flaven
22bdc02b1b Support the selection (via an autocomplete) in a list that contains duplicates
SVN:trunk[1475]
2011-08-19 15:00:33 +00:00
Romain Quetiez
e8586515e3 Fixed regression (HKeys)
SVN:trunk[1474]
2011-08-19 08:51:16 +00:00
Romain Quetiez
071a30d928 Cosmetic on the portal
SVN:trunk[1473]
2011-08-19 07:42:19 +00:00
Denis Flaven
488f1c47bf Fixed Trac #456: 'contact_list' was considered as read-only on tickets.
SVN:trunk[1472]
2011-08-19 07:32:19 +00:00
Romain Quetiez
c15976f148 Internal: leverage display block for displaying link sets in the portal
SVN:trunk[1471]
2011-08-18 16:32:31 +00:00
Romain Quetiez
6f8e2e60ce Internal: the transaction mechanism was inoperant when a form was built within the target page.
SVN:trunk[1470]
2011-08-18 15:00:39 +00:00
Denis Flaven
b7b8da0848 Fixed the computation of IPs in a subnet that failed (returned negative numbers) on some versions of PHP compiled in 32-bit.
SVN:trunk[1469]
2011-08-18 12:05:30 +00:00
Denis Flaven
2f1803b3b6 A mandatory case log field is now considered as 'filled' if it contains a previous entry
SVN:trunk[1468]
2011-08-18 10:45:41 +00:00
Denis Flaven
58e61c9d6b A mandatory case log field is now considered as 'filled' if it contains a previous entry
SVN:trunk[1467]
2011-08-18 10:44:37 +00:00
Denis Flaven
ddd98b9335 Different display for 'Date' fields: shorter field than DateTime since there is no "time" part.
SVN:trunk[1466]
2011-08-18 09:51:10 +00:00
Denis Flaven
278b84ee38 The default value for Date fields is different than for DateTime fields: no 'time' part at the end (use the attribute's own format)
SVN:trunk[1465]
2011-08-18 09:50:16 +00:00
Denis Flaven
c0764bce74 Added smart search for 'Date' fields (and not only DateTime)
SVN:trunk[1464]
2011-08-18 09:48:44 +00:00
Denis Flaven
7cf0e2f747 Bug fix: incorrect detection of the need to fix HK, leading to incorrect data in case of upgrade.
SVN:trunk[1463]
2011-08-18 07:01:08 +00:00
Romain Quetiez
c7e8627679 Fixed regression on the paginated lists
SVN:trunk[1462]
2011-08-17 15:56:34 +00:00
Denis Flaven
1c7d5b8fd2 Added support for an abstract Organization (or Person) class during setup !
SVN:trunk[1461]
2011-08-17 15:20:32 +00:00
Denis Flaven
18217e4adb Bug fix: (side effect of fixing #445) paginate the lists when performing a selection.
SVN:trunk[1460]
2011-08-16 17:07:54 +00:00
Denis Flaven
1357e7e225 Bug fix: paginated display was always displaying 5 items by default...
SVN:trunk[1459]
2011-08-16 16:42:02 +00:00
Denis Flaven
a941c2d14e Allow updating/recomputing of read-only attributes on the fly in the edit form.
SVN:trunk[1458]
2011-08-16 16:16:26 +00:00
Denis Flaven
cb55a75bcf Allow updating/recomputing of read-only attributes on the fly in the edit form.
Also fixed a regerssion due to #446

SVN:trunk[1457]
2011-08-16 15:58:09 +00:00
Denis Flaven
1e20253229 Bug fix: don't assume that the hierachical key is called 'parent_id'
SVN:trunk[1456]
2011-08-16 15:54:22 +00:00
Denis Flaven
53a48dc8e0 Fixed Trac #447: interfaces not showing up on the details of a server when an org is selected: there were collisions in the internal query parameters names ! This is now fixed.
SVN:trunk[1455]
2011-08-16 10:01:14 +00:00
Denis Flaven
2ad9d69396 Implemented the OQL operator ABOVE (and its variants ABOVE STRICT, NOT ABOVE and NOT ABOVE STRICT) that returns the (indirect) parents of a given node in a hierarchy. It's the opposite to 'BELOW' that returns the (indirect) children.
SVN:trunk[1454]
2011-08-16 08:14:11 +00:00
Denis Flaven
74baca3a7b Enhanced interface for complex SLA computations...
SVN:trunk[1453]
2011-08-15 14:06:03 +00:00
Denis Flaven
2bd8700c90 Implementing Trac #419: provide means for the toolkit to fix synchro data sources
SVN:trunk[1452]
2011-08-15 14:01:51 +00:00
Denis Flaven
51bd6cdf97 #446 continuing: sanitizing parameters...
SVN:trunk[1451]
2011-08-15 13:55:59 +00:00
Denis Flaven
b02021a4ff Still fixing regressions caused by Trac#446: XSS vulnerabilities...
SVN:trunk[1450]
2011-08-12 10:06:33 +00:00
Denis Flaven
96f3350029 Fixed the display of 'Used IP Addresses' (i.e. Network interfaces) in the details of a Subnet object.
SVN:trunk[1449]
2011-08-12 09:19:53 +00:00
Romain Quetiez
e2e4c53b0d Fix after security fix
SVN:trunk[1448]
2011-08-11 16:31:59 +00:00
Denis Flaven
9c6605f7e7 Allow export of the config information for documentation purposes
SVN:trunk[1447]
2011-08-11 12:01:53 +00:00
Denis Flaven
f481652666 Trac #445: output of export.php should not be "paginated" !
Bug fix: the number of items displayed in the "paginated" view should be "min_display_limit", as listed in the drop down-box and not always 10 !

SVN:trunk[1446]
2011-08-11 11:59:55 +00:00
Denis Flaven
212389f43e One more regression due to #446: fixed !
SVN:trunk[1445]
2011-08-11 11:57:44 +00:00
Denis Flaven
f68680ada1 Still working on #446: oops, one parameter missing...
SVN:trunk[1444]
2011-08-11 10:53:37 +00:00
Denis Flaven
6859326646 Fixed Trac#446: XSS vulnerabilities... to be tested !
Also fixed the display/download links on documents that were both doing exactly the same thing !

SVN:trunk[1443]
2011-08-11 10:17:03 +00:00
Romain Quetiez
a129c9814f Readme for 1.2 Beta
SVN:trunk[1441]
2011-08-09 09:37:48 +00:00
Denis Flaven
05ee0caec2 Fixed the "more information" url in the itop-config-mgmt module
SVN:trunk[1440]
2011-08-09 08:55:29 +00:00
Denis Flaven
6fff9d6a20 Make the 'agent_id' visible in the state "new" to allow the creation and assigment of a ticket in one click.
SVN:trunk[1439]
2011-08-09 08:34:14 +00:00
Denis Flaven
d2955557bf Allow $SERVER_NAME$ as a placeholder for the server's name in the application root url. Useful for multiple virtual hosts or test systems using DHCP...
SVN:trunk[1438]
2011-08-09 07:57:11 +00:00
Denis Flaven
aa00cd9ae7 "Shortcut" actions are displayed as buttons on the same line as the usual "Actions" button.
SVN:trunk[1437]
2011-08-09 07:51:08 +00:00
Denis Flaven
aa9d9ed578 New config parameter: buttons_position: top | bottom | both
SVN:trunk[1436]
2011-08-09 07:47:50 +00:00
Romain Quetiez
9bd6fa61c5 SQLBlock - removed Warnings in case of empty data set
SVN:trunk[1435]
2011-08-08 16:02:28 +00:00
Denis Flaven
f16997fb2d Productivity enhancement: apply directly the 'next_action' when creating or modifying an object with a life-cycle !
SVN:trunk[1434]
2011-08-08 15:40:51 +00:00
Romain Quetiez
f4c1cf0818 Fixed typo
SVN:trunk[1433]
2011-08-08 14:24:35 +00:00
Denis Flaven
01dd63f623 Cleanup of the way objects are displayed/edited: DisplayBareProperties is now used for displaying (i.e. read-only) or modifying/creating an object.
SVN:trunk[1432]
2011-08-08 12:52:32 +00:00
Denis Flaven
3393808c7a - Enhancement: can now specify 'order_by' in 'list' templates.
- New 'Shortcut Actions' displayed as buttons next to the 'Actions' popup menu.

SVN:trunk[1431]
2011-08-08 12:35:43 +00:00
Denis Flaven
772c892b15 New option DEL_MOVEUP (move the children up one level) for 'on_target_delete' for HierarchicalKey attributes.
SVN:trunk[1430]
2011-08-07 12:24:45 +00:00
Denis Flaven
435d943f47 Cleanup of the way objects are displayed/edited: DisplayBareProperties is now used for displaying (i.e. read-only) or modifying/creating an object. The means that:
1) the display/edition can be customized by overloading this method
2) the plug-ins's method OnDisplayProperties is now called in edition as well

A new class of template is introduced for building custom object displays: ObjectDetailsTemplate.

SVN:trunk[1429]
2011-08-07 08:12:07 +00:00
Denis Flaven
eab1060f8e Adapted the portal to support enhanced attachments either in 'properties' or 'relations'... this is transparent for the end-user of the portal.
SVN:trunk[1428]
2011-08-07 07:55:16 +00:00
Denis Flaven
cb8774370f Enhancement: new configuration parameter 'position' to enable 'attachments' to be displayed either as a separate tab 'Attachments' on inline in the 'Properties' tab (for example when used with a custom 'DisplayBareProperties'.)
SVN:trunk[1427]
2011-08-07 07:52:39 +00:00
Denis Flaven
23b1d15b64 New attribute 'display_style' for Strings, Enums and ExternalKeys: when the list is short, display it as a set of radio buttons instead of a drop-down list. Possible values: 'radio', 'radio_vertical' and 'radio_horizontal'. ('radio' == 'radio_vertical').
SVN:trunk[1426]
2011-08-06 09:51:06 +00:00
Denis Flaven
61727aca02 New configuration parameter: display_actions_at_top: avoid scrolling to see the buttons when assigning a ticket !
SVN:trunk[1425]
2011-08-05 13:28:21 +00:00
Denis Flaven
8cfebdf723 - New configuration parameter deadline_format to specify how to display Deadlines (date or time difference or a mix of the two)
SVN:trunk[1424]
2011-08-05 13:08:56 +00:00
Denis Flaven
506702c50b Enhancement: added the capability to filter (based on user's defined preferences the list of organizations displayed in the drop-down menu)
SVN:trunk[1423]
2011-08-05 11:45:12 +00:00
Denis Flaven
b9de1df6a8 Release note review
SVN:trunk[1422]
2011-08-03 13:49:27 +00:00
Denis Flaven
a7175007be Preparing for the 1.2-beta release
SVN:trunk[1421]
2011-08-03 10:37:55 +00:00
Denis Flaven
5d8acbb41a New type of trigger: TriggerOnPortalUpdate, called when the end-user updates a ticket from the portal.
SVN:trunk[1420]
2011-08-03 10:36:34 +00:00
Denis Flaven
3ccbeac996 Oops, missing Portuguese (Brazilian) dictionary files...
SVN:trunk[1419]
2011-08-03 09:25:19 +00:00
Denis Flaven
1643ba8e9c Added support for the 'Attachments' plug-in into the portal
SVN:trunk[1418]
2011-08-02 10:36:43 +00:00
Denis Flaven
768dbd8946 New module to easily manage attachments
SVN:trunk[1417]
2011-08-02 10:29:59 +00:00
Denis Flaven
c2d02a2394 Put back wsdl documentation
SVN:trunk[1416]
2011-08-02 08:30:43 +00:00
Denis Flaven
b26f2738f6 German localization enhancement by Stephan Rosenke. Thanks Stephan !
SVN:trunk[1415]
2011-08-02 07:53:39 +00:00
Denis Flaven
781ec7e33a In CLI mode, do not depend on the current directory: the script can be run from anywhere.
SVN:trunk[1414]
2011-08-01 16:13:37 +00:00
Denis Flaven
d09db3a920 Better error message if the configuration file exists but is not readable
SVN:trunk[1413]
2011-08-01 16:12:24 +00:00
Denis Flaven
998a8692cb Use a hierarchical key for the parent/child relationship with groups
SVN:trunk[1412]
2011-08-01 15:53:55 +00:00
Denis Flaven
b83ac447e8 Don't make the case log hidden, since it's not hidden in the portal !
SVN:trunk[1411]
2011-08-01 15:46:23 +00:00
Denis Flaven
6688f1811e Improved error handling when loading linkedset as attributes in one go
SVN:trunk[1410]
2011-08-01 15:08:22 +00:00
Denis Flaven
ec26750b58 Bug fix: when changing the currently selected organization, go back to the initial (Welcome) menu instead of trying to stay on the same menu... which caused troubles (e.g. "New Contact" => assertion failed)
SVN:trunk[1409]
2011-08-01 14:17:43 +00:00
Denis Flaven
0f0abc0501 Added a new web service to create UserRequest tickets (similarly to Incidents tickets). Based on code from Vincenzo Todisco.
SVN:trunk[1408]
2011-08-01 14:07:54 +00:00
Denis Flaven
9ad6f2c994 Draft of the 1.2 readme file...
SVN:trunk[1407]
2011-08-01 13:50:07 +00:00
Denis Flaven
abab47558b Adding Japanese translation kindly provided by Tadeshi Kaneda
SVN:trunk[1406]
2011-08-01 13:34:48 +00:00
Denis Flaven
f3147be8f6 Adding Japanese translation
SVN:trunk[1405]
2011-08-01 13:33:34 +00:00
Denis Flaven
af185b79c8 Bug fix: missing title for the menu 'All Opened Changes'
SVN:trunk[1404]
2011-08-01 12:50:26 +00:00
Denis Flaven
3fc686e403 Bug fix: do not modify the page's address (by changing the final anchor) when clicking on a page in a paginated display. Otherwise this interferes with the tabbed navigation.
SVN:trunk[1403]
2011-08-01 12:37:05 +00:00
Denis Flaven
f5ea073c2b Bug fix: when removing a module (like Incident Management) that contained Triggers to such objects, the remaining triggers must be deleted because they reference a non-existing class.
SVN:trunk[1402]
2011-08-01 11:51:11 +00:00
Denis Flaven
aa6d7578d1 Fixed issues when adding/removing modules during the setup:
- When adding modules: the data model was not refreshed in the cache before attempting to load "structure" (or "sample") data
- When removing a module: remaining (invalid) triggers were still used.

SVN:trunk[1401]
2011-08-01 10:30:22 +00:00
Denis Flaven
05338caa23 Modified misleading/outdated comment in the configuration file.
SVN:trunk[1400]
2011-08-01 10:24:36 +00:00
Denis Flaven
a6ac78d7c1 Fix: Make sure that the flash object respects the z-order otherwise the hierarchy/organization picker appears behind the Flash in Chrome and IE.
SVN:trunk[1399]
2011-07-31 19:38:27 +00:00
Denis Flaven
9bcf78bfd6 Enhancement: the number of items is displayed (statically) in the header of the each "details" tab.
SVN:trunk[1398]
2011-07-31 10:45:03 +00:00
Denis Flaven
9c97df5186 MapContextParam: call the parent just in case...
SVN:trunk[1397]
2011-07-31 09:40:00 +00:00
Denis Flaven
c2a8fd4e09 Enhancement: allow to search on Organization's parent and Location's parent.
SVN:trunk[1396]
2011-07-31 09:38:26 +00:00
Denis Flaven
c8b6ec08e7 Bug fix: properly process field names followed by a star (which simply indicates an important field in the CSV import)
SVN:trunk[1395]
2011-07-31 09:30:55 +00:00
Denis Flaven
4293230416 Don't display an error (assertion failed) if the user selects nothing (i.e -- select one --) in the "CSV template" tab.
SVN:trunk[1394]
2011-07-31 09:12:41 +00:00
Denis Flaven
bfddec242b - Fixed the combination of the "PointingTo" conditions, especially when combining several "BELOW" conditions for the same criteria (used for security and context purposes)
- Make sure that the "security" limitiation provided by GetSelectFilter is applied only once to a given filter to avoid the creation of unneeded complex queries

SVN:trunk[1393]
2011-07-31 09:01:45 +00:00
Denis Flaven
ee3652acad Added a useful function for debugging: DebugBacktrace which provides a "light" version of PHP's built-in debug_backtrace().
SVN:trunk[1392]
2011-07-31 08:57:04 +00:00
Denis Flaven
5baa213e6a Better initialization of objects from the "Context" values:
- map the parameters (for example for Provider Contract)
- set the default values when creating a "secondary" object via the (+) button

SVN:trunk[1391]
2011-07-31 08:54:59 +00:00
Denis Flaven
ca8ea8dd02 Fixed the parsing of OQL error messages
SVN:trunk[1390]
2011-07-30 17:25:49 +00:00
Denis Flaven
308d2626a9 Added test cases for the new BELOW operator
SVN:trunk[1389]
2011-07-30 17:23:39 +00:00
Denis Flaven
8cab8dd7b7 Filter audit results using the hierarchies
SVN:trunk[1388]
2011-07-29 10:29:28 +00:00
Denis Flaven
0a5e37c592 Oops: Make sure that the default add-on for SLA computation is enabled by default
SVN:trunk[1387]
2011-07-29 10:27:37 +00:00
Denis Flaven
72326435ce Make sure that the default add-on for SLA computation is enabled by default
SVN:trunk[1386]
2011-07-29 10:14:44 +00:00
Denis Flaven
ca9f17d6e1 Fixed a bug when using a hierarchy of locations, since locatins genreally depends on the object's organization
SVN:trunk[1385]
2011-07-29 10:09:14 +00:00
Denis Flaven
ff89c4d424 CAS authentication improvements:
- Check if the user is part of a group (memberOf)
- Fixed the use of the 'redirect_service' when logging-out

SVN:trunk[1384]
2011-07-28 17:39:49 +00:00
Denis Flaven
fa3b7ce545 Thumbs.db has nothing to do in SVN
SVN:trunk[1383]
2011-07-28 15:21:33 +00:00
Denis Flaven
c85feb7cea - Use the new HierarchicalKeys for Organization and Locations and use the hierarchy of organization for the profiles/user rights.
SVN:trunk[1382]
2011-07-28 15:14:49 +00:00
Denis Flaven
d50f812694 Fix: broken context usage when no context (neither org nor menu) at all
SVN:trunk[1381]
2011-07-28 15:12:16 +00:00
Denis Flaven
c7e27a836a Make sure that the root URL ends with a slash
SVN:trunk[1380]
2011-07-28 13:45:51 +00:00
Denis Flaven
c8a1380bef - Fixed the reporting about the previous version
SVN:trunk[1379]
2011-07-28 13:19:58 +00:00
Denis Flaven
7732c9bc6a Exclude build.bash from the build
SVN:trunk[1378]
2011-07-28 11:26:58 +00:00
Denis Flaven
82c57972c6 Fixing bug #404: context lost when doing certain actions. What was fixed:
- Run Query
- Display Data Model Schema
- Drill-down in charts (OQL & SQL)
- Paginated lists (actually a regression)

What remains:
- Global search...
- Drill-down in Flash "impacts / depends on"

SVN:trunk[1377]
2011-07-27 16:39:17 +00:00
Denis Flaven
0cc0c820a5 Fixed Trac #410: added translation for ticket status (and other enum fields) when displaying the History tab.
SVN:trunk[1376]
2011-07-27 13:06:45 +00:00
Denis Flaven
9150a569a7 Make sure that the dependent fields are initialized in the proper order when applying a stimulus on a n object.
SVN:trunk[1375]
2011-07-27 12:55:16 +00:00
Denis Flaven
3c76b90a48 Added an option to the paginated display: display all elements on one page.
SVN:trunk[1374]
2011-07-27 10:24:06 +00:00
Denis Flaven
5f2a5d9cfa - Fixed Trac #433: triggers creation was incorrect when iTop was installed with a 'prefix' for the DB tables.
- Fixed a bug due to the previous renaming of UpdateObject...

SVN:trunk[1372]
2011-07-26 16:45:02 +00:00
Denis Flaven
4e40702809 Fixed the construction of the menus' URLs:
- The context was not passed all the time (due to typos)
- When the context was empty an extra ampersand was added at the end of the URL, which caused a malformed URL for the 'tab selector' when modifying an object. (i.e the object always opened on the 'Properties' tab instead of the current tab).

SVN:trunk[1371]
2011-07-26 16:22:24 +00:00
Denis Flaven
a2b8813628 - Renamed the method 'UpdateObject' into 'UpdateObjectFromPostedForm' to avoid misunderstandings.
- Fixed (again !) Trac #427: unable to empty a linkset when editing an object.

SVN:trunk[1370]
2011-07-26 16:18:19 +00:00
Denis Flaven
751ab5a4b4 Implemented enhancement #130: keywords to narrow the scope of the global search
SVN:trunk[1369]
2011-07-26 14:18:33 +00:00
Denis Flaven
b6bcade4c0 Better (?) handling of deletion issues during the synchro...
SVN:trunk[1368]
2011-07-26 13:27:03 +00:00
Denis Flaven
d6be6ca92c Fix display issues with the portal's top menu bar.
SVN:trunk[1367]
2011-07-26 13:25:37 +00:00
Denis Flaven
9024e0b2db Use absolute URLs as much as possible to be independent from the page being executed...
SVN:trunk[1366]
2011-07-26 13:24:28 +00:00
Denis Flaven
ecca1aa070 Use absolute URLs as much as possible to be independent from the page being executed...
SVN:trunk[1365]
2011-07-26 13:22:45 +00:00
Denis Flaven
3fde682653 Make sure that the AJax calls use a full/absolute path to the page they call so that they can be embedded anywhere inside the application (i.e. in plug-ins supplied pages as well as 'regular' app pages).
SVN:trunk[1364]
2011-07-26 12:21:53 +00:00
Denis Flaven
aee8a98d84 Implemented two new options for CAS:
- logout_redirect_service
- memberOf

SVN:trunk[1363]
2011-07-26 09:43:21 +00:00
Denis Flaven
3ab670e8c2 Implemented two new options for CAS:
- logout_redirect_service
- memberOf

SVN:trunk[1362]
2011-07-26 09:42:46 +00:00
Denis Flaven
1fc7ce3b81 - Allow search forms to be open by default
- Don't make textarea resizable

SVN:trunk[1361]
2011-07-26 09:41:27 +00:00
Denis Flaven
e8c44951a1 Added two new options for CAS:
- logout_redirect_service
- memberOf

SVN:trunk[1360]
2011-07-26 09:39:45 +00:00
Denis Flaven
c11ca679d4 - Make sure that resulsts tables are always sortable.
SVN:trunk[1359]
2011-07-25 14:32:11 +00:00
Denis Flaven
46781c349f - Setup tested under IE8
- Added migration code, during the setup, for the hierarchical keys

SVN:trunk[1358]
2011-07-25 14:14:05 +00:00
Denis Flaven
189b802452 - Always display the number of elements (and the number of elements selected) when displaying a list.
- Nicer display of paginated lists

SVN:trunk[1357]
2011-07-25 12:25:50 +00:00
Denis Flaven
6b7687c80b - Nicer display of paginated lists
SVN:trunk[1356]
2011-07-25 12:24:23 +00:00
Denis Flaven
906abd5fe6 - Always display the number of elements (and the number of elements selected) when displaying a list.
- Nicer display of paginated lists

SVN:trunk[1355]
2011-07-25 12:23:40 +00:00
Denis Flaven
0c539aada9 Always display the number of elements (and the number of elements selected) when displaying a list.
SVN:trunk[1354]
2011-07-25 12:22:49 +00:00
Denis Flaven
a3611f7f63 Added support of hierarchy in the manipulation of external keys:
- widget

SVN:trunk[1353]
2011-07-22 12:21:21 +00:00
Denis Flaven
d62d6e14c8 Added support of hierarchy in the manipulation of external keys:
- widget

SVN:trunk[1352]
2011-07-22 12:15:49 +00:00
Denis Flaven
4cd4d91225 Added support of hierarchy in the manipulation of external keys:
- widget
- search criteria

SVN:trunk[1351]
2011-07-22 12:15:25 +00:00
Denis Flaven
5257287421 Added support of hierarchy in the manipulation of external keys:
- widget
- search criteria

SVN:trunk[1350]
2011-07-22 12:14:59 +00:00
Denis Flaven
7e60a9fce7 Implementation of a new type of ExternalKey attribute: HierarchicalKey. This attribute implements the "nested set" model and is used to define a hierarchy of an arbitrary depth of objects of the same class. With this new feature it is possible to retrieve in one OQL query (and in one sql query as well) all the children of a given organization.
I'm still keeping (commented out) some of the traces helpful for debugging the construction of the OQL queries.

SVN:trunk[1349]
2011-07-22 12:07:09 +00:00
Denis Flaven
b80cc36fb5 TO BE TESTED: New step in the wizard confirmation of the URL to be used for accessing the application.
SVN:trunk[1348]
2011-07-22 11:55:41 +00:00
Denis Flaven
0e75824247 For the setup: try to make the URL detection work even if the actual PHP file is not part of the URL (i.e when index.php is implicitely added when browsing a folder)
SVN:trunk[1347]
2011-07-22 11:53:02 +00:00
Denis Flaven
7470c8e72e Use the SLAComputation utility to allow plug-ins to implement more complex SLA computation schemes.
SVN:trunk[1346]
2011-07-18 15:08:44 +00:00
Denis Flaven
a04f0ee816 Helper module to plug complex SLA computation schemes. This basic modules performs the simplest computation corresponding to a 24x7 (no holidays) model.
SVN:trunk[1345]
2011-07-18 15:00:50 +00:00
Denis Flaven
d37244e6e2 - Search forms are now expandable/collapsible in the portal, like in the main UI
SVN:trunk[1344]
2011-07-12 11:07:56 +00:00
Denis Flaven
82c4f43c4f Manage properly default parameters for linksets
SVN:trunk[1343]
2011-07-12 11:07:04 +00:00
Romain Quetiez
1c2c244273 Veepee - fixed issue
SVN:trunk[1342]
2011-07-08 12:34:07 +00:00
Romain Quetiez
a991a84a8e New feature: online help on search inputs (date format and operators)
SVN:trunk[1341]
2011-07-08 11:40:57 +00:00
Romain Quetiez
532912984d Preset the object creation form with values (scalars or lists) by the mean of GET arguments. E.g. .../UI.php&operation=new&class=Team&default[org_id]=161&default[member_list][-1234][id]=&default[member_list][-1234][role]=manager
SVN:trunk[1340]
2011-07-07 15:45:35 +00:00
Romain Quetiez
265ef1e1ce Typo in french dictionary
SVN:trunk[1339]
2011-07-07 09:34:40 +00:00
Denis Flaven
70ad369ad6 - removed some debug traces !!
SVN:trunk[1337]
2011-07-06 17:12:18 +00:00
Denis Flaven
723685b4cb - Fixed Trac #429: web browser can crash when a text field contains several times the same URL !!!
SVN:trunk[1336]
2011-07-06 17:09:36 +00:00
Denis Flaven
061711afc8 Added support for OnFormCancel
SVN:trunk[1335]
2011-07-06 16:29:40 +00:00
Denis Flaven
8b6a8f02a4 - Bug fix: n:n wizard, context was lost when searching for objects of a derived class to be added.
- Bug fix: 'Apply stimulus multiple" was saying: "Please select at least one object"

SVN:trunk[1334]
2011-07-06 16:23:54 +00:00
Denis Flaven
893ec0f097 - Allow plug-ins to record specific changes in the object's history
SVN:trunk[1333]
2011-07-06 16:21:19 +00:00
Denis Flaven
7c9d4c76a9 Protect against a multiple stimulus on a set based on an abstract class.
SVN:trunk[1332]
2011-07-05 14:07:02 +00:00
Denis Flaven
52fb63c0ee Handle the OnBeforeUnload events to call OnFormCancel handlers when needed so that the plug-ins have a chance to perform some cleanup.
SVN:trunk[1328]
2011-07-05 08:46:31 +00:00
Denis Flaven
8a547bf104 Internal regressions fixes !!!
SVN:trunk[1327]
2011-07-04 16:36:07 +00:00
Denis Flaven
0019ab216a Fixed a typo: utils::ReadMultipleSelection
SVN:trunk[1326]
2011-07-04 15:44:07 +00:00
Denis Flaven
8d52cd3a58 Fixed a typo: utils::ReadMultipleSelection
SVN:trunk[1325]
2011-07-04 15:02:36 +00:00
Romain Quetiez
c7fbe56423 PPortal web page improvements: protected update feature (class/attcodes explicitely restricted)
SVN:trunk[1324]
2011-07-04 09:54:58 +00:00
Denis Flaven
5ff5ea71de Fixed Trac#424: error when updating the Data Synchro statistics
SVN:trunk[1323]
2011-07-04 08:29:16 +00:00
Denis Flaven
6297526da6 Fixed Trac #427: unable to remove all items from a linkset when editing an object.
SVN:trunk[1322]
2011-07-04 08:21:19 +00:00
Romain Quetiez
56ac89f6a6 Portal web page improvements: wizard buttons and welcome message
SVN:trunk[1321]
2011-07-04 07:26:16 +00:00
Romain Quetiez
aa46ab1a67 Portal users: limit their access depending on their organization!!!!
SVN:trunk[1320]
2011-07-01 15:09:29 +00:00
Romain Quetiez
645b02b2d1 #415 Could not limit user on some organization (symptom: wrong queries... org_id does not exist...)
SVN:trunk[1319]
2011-07-01 15:08:38 +00:00
Romain Quetiez
b2c5f183ec Portal web page improvements: integrating the attachments
SVN:trunk[1318]
2011-07-01 07:48:09 +00:00
Romain Quetiez
c6b60731d8 Internal cosmetic on duration attribute
SVN:trunk[1317]
2011-07-01 07:43:53 +00:00
Denis Flaven
fd2f40f070 - Protect against invalid attributes...
- Use the new absolute URL mechanism
- Added custom sort and Zebra tables for non paginated lists

SVN:trunk[1316]
2011-06-30 17:08:59 +00:00
Denis Flaven
9fef41404f - Added drill-down capability for OQL charts (pie and bars)
- fixed asbolute root path for drill down links

SVN:trunk[1315]
2011-06-30 16:04:18 +00:00
Denis Flaven
017aec3ae8 Added drill-down capability to the SQL blocks
SVN:trunk[1314]
2011-06-30 15:34:56 +00:00
Denis Flaven
2ae4aca687 Added support of 'drill-down' (i.e on_click) on bar charts
SVN:trunk[1313]
2011-06-30 14:38:13 +00:00
Denis Flaven
692fd91801 Finalizing the pagination: a few bug fixes and some cleanup...
SVN:trunk[1312]
2011-06-29 15:09:10 +00:00
Denis Flaven
12ec9c3ba1 Finalizing the pagination: a few bug fixes and some cleanup...
SVN:trunk[1311]
2011-06-29 15:08:36 +00:00
Romain Quetiez
ac5e40f4ed Factorized some code into PortalWebPage (still needs to be used in the std portal implementation)
SVN:trunk[1310]
2011-06-29 09:10:07 +00:00
Denis Flaven
f44afb8ee6 New implementation for displaying long lists: pagination
SVN:trunk[1309]
2011-06-29 08:57:39 +00:00
Denis Flaven
9e01277e49 New implementation for displaying long lists: pagination
SVN:trunk[1308]
2011-06-29 08:56:27 +00:00
Denis Flaven
73dc5eb922 New implementation for displaying long lists: pagination
SVN:trunk[1307]
2011-06-29 08:53:26 +00:00
Romain Quetiez
a5091d9af0 Fixed regression: magic_quote_runtime could be deprecated with recent versions of PHP (setup)
SVN:trunk[1306]
2011-06-28 15:13:10 +00:00
Romain Quetiez
a573d3cfa0 #423 Finalized
SVN:trunk[1305]
2011-06-28 14:28:01 +00:00
Romain Quetiez
c4b7497770 #423 Fixed issues with application root URL = f(mode CLI, modules, web server techno, etc.)
SVN:trunk[1304]
2011-06-28 10:30:03 +00:00
Romain Quetiez
1964658584 Fixed an issue in the internal error reporting (user rights not sufficient to update an object)
SVN:trunk[1303]
2011-06-28 10:23:41 +00:00
Romain Quetiez
077b7be2a4 Fixed regressions with DBObjectSet, impacting the new paginated lists and their menus
SVN:trunk[1302]
2011-06-28 09:32:13 +00:00
Denis Flaven
22c6df1a7a Fixed Trac #422 (detection of magic_quotes_runtime)
SVN:trunk[1301]
2011-06-24 14:46:21 +00:00
Romain Quetiez
54d5497f0a Fixed issues with pagination + core limitations in the use of Object Sets (wrong filter when adding objects to a set)
SVN:trunk[1300]
2011-06-24 09:53:45 +00:00
Romain Quetiez
9d2a8d0a67 Fixed regression with the display of lists based on class that is not a leaf (aka standalone) class
SVN:trunk[1299]
2011-06-23 12:32:15 +00:00
Denis Flaven
20024729ec New implementation for displaying long lists: pagination
SVN:trunk[1298]
2011-06-23 11:01:33 +00:00
Denis Flaven
43315f50ea New implementation for displaying long lists: pagination
SVN:trunk[1297]
2011-06-23 11:01:03 +00:00
Denis Flaven
46edbbbd2b New implementation for displaying long lists: pagination
SVN:trunk[1296]
2011-06-23 10:47:05 +00:00
Denis Flaven
dc3713f0ea New implementation for displaying long lists: pagination
SVN:trunk[1295]
2011-06-23 10:46:10 +00:00
Denis Flaven
01a4a04364 New implementation for displaying long lists: pagination
SVN:trunk[1294]
2011-06-23 10:43:41 +00:00
Romain Quetiez
7384191fb6 Fixed issues in ExtKey Widget
SVN:trunk[1293]
2011-06-22 15:26:36 +00:00
Romain Quetiez
b8c930016f Cosmetic
SVN:trunk[1292]
2011-06-22 08:16:59 +00:00
Romain Quetiez
5d37201391 Fixed issue in the computation of the application absolute path (Windows)
SVN:trunk[1291]
2011-06-22 08:15:30 +00:00
Romain Quetiez
2c8963784c #420 - Data synchro logs: increased the size of the attribute last_error
SVN:trunk[1290]
2011-06-21 15:01:56 +00:00
Romain Quetiez
c42fa13b92 Templates: new type of block = sqlblock, allows for displaying tables/charts based in SQL queries (much quicker for Group By operations)
SVN:trunk[1289]
2011-06-21 13:12:02 +00:00
Romain Quetiez
c02d62044c Dashboard templates: fixed issue with asynchronous mode (still some cosmetic issues) with itopblock and the table format
SVN:trunk[1288]
2011-06-21 13:10:11 +00:00
Denis Flaven
7bcb4ed3b2 New implementation of GetAbsoluteUrlRootPath that no longer requires any parameter.
SVN:trunk[1287]
2011-06-20 14:18:24 +00:00
Denis Flaven
834e9c48e1 Added a display value for the null (i.e empty) value of a combo/autocomplete.
SVN:trunk[1286]
2011-06-20 14:17:24 +00:00
Denis Flaven
ddfc84db6a - New implementation of the UIExtKeyWidget that is no longer limited to editing an attribute of an object
- When needed the drop-down list of organizations is replaced by an autocomplete

SVN:trunk[1285]
2011-06-15 17:06:46 +00:00
Denis Flaven
b4a7d0b86c - New implementation of the UIExtKeyWidget that is no longer limited to editing an attribute of an object
- When needed the drop-down list of organizations is replaced by an autocomplete

SVN:trunk[1284]
2011-06-15 17:06:26 +00:00
Denis Flaven
02cafd0e0b - New implementation of the UIExtKeyWidget that is no longer limited to editing an attribute of an object
- When needed the drop-down list of organizations is replaced by an autocomplete

SVN:trunk[1283]
2011-06-15 17:05:58 +00:00
Denis Flaven
f5891a531e New icon to trigger the filtering on organizations
SVN:trunk[1282]
2011-06-15 17:01:44 +00:00
Romain Quetiez
4681b03646 #122 Optimized the load of data set (do not load unused columns, was provoking the swapping of MySQL tmp memory tables)
SVN:trunk[1281]
2011-06-15 11:59:25 +00:00
Denis Flaven
733953ac99 CAS integration: added support of JA-SIG Central Authentication Service (CAS) with log-off support, using phpCAS API.
SVN:trunk[1280]
2011-06-10 14:51:17 +00:00
Romain Quetiez
4837984ac3 Optimization: autocomplete = do not load every object when determining the list of matches
SVN:trunk[1279]
2011-06-10 09:02:42 +00:00
Romain Quetiez
4efd93defe Optimization: cache the Count of items in an object set
SVN:trunk[1278]
2011-06-08 14:42:51 +00:00
Romain Quetiez
35f7d143b8 Optimization: displaying 1000 object would take real long if many organizations are loaded into iTop (querying all the orgs for each object)
SVN:trunk[1277]
2011-06-08 14:31:22 +00:00
Denis Flaven
d48fd1a12e First prototype (not yet tested) of CAS integration.
SVN:trunk[1276]
2011-06-08 13:34:43 +00:00
Romain Quetiez
a0900cd732 Optimization: do not load the full set of items when it comes to displaying an autocomplete!
SVN:trunk[1275]
2011-06-08 13:30:56 +00:00
Romain Quetiez
64b4922499 Fixed issue in the data model consistency check
SVN:trunk[1274]
2011-05-24 10:26:16 +00:00
Romain Quetiez
4574126428 #408 Case log not working with PHP < 5.3 - the fix preserves the compatibility with installed version (but the dates are lost)
SVN:trunk[1273]
2011-05-23 14:56:09 +00:00
Romain Quetiez
9704dd8e35 Improved import.php and synchro_import.php: added 'date_format' (example: %d/%m/%Y %H:%i:%s)
SVN:trunk[1272]
2011-05-23 13:42:33 +00:00
Romain Quetiez
f8d794bc93 Reduces the annoyance of bug #408 (dates not visible in the case log if PHP < 5.3)
SVN:trunk[1271]
2011-05-20 13:42:57 +00:00
Romain Quetiez
5c7c7b98ee Dehardcoded against 'ticket_log', allowing for several case log attributes
SVN:trunk[1270]
2011-05-19 14:38:46 +00:00
Romain Quetiez
3826c1f886 #405 Could not install without the module 'User Request Management'
SVN:trunk[1269]
2011-05-13 16:00:21 +00:00
Romain Quetiez
58844b04ef #403 Partial installation not working (error on ticket form)
SVN:trunk[1268]
2011-05-13 15:09:55 +00:00
Romain Quetiez
178f7f3426 Typo in dictionary
SVN:trunk[1267]
2011-05-06 09:05:22 +00:00
Denis Flaven
8b0214eeee Preparing for the 1.1 release.
SVN:trunk[1265]
2011-05-04 08:56:59 +00:00
Denis Flaven
c6454c5a8e Completed the Portuguese/Brazilian translation, thanks to Marco Tulio !
SVN:trunk[1264]
2011-05-02 15:36:32 +00:00
Denis Flaven
6fd3bdbef4 Final touch to the 1.1 build: readme "touch" and "doc" review.
SVN:trunk[1263]
2011-05-02 13:18:12 +00:00
Denis Flaven
4a12c531e6 Last finishing touch to the Italian localization. Now 100% complete !
SVN:trunk[1262]
2011-05-02 13:06:37 +00:00
Denis Flaven
df086c8ce2 - Better display of the "Resolved Request" form, where the user can submit their satisfaction & feedbacks. Now aligned with the display of the ticket's details.
SVN:trunk[1261]
2011-05-02 12:58:32 +00:00
Romain Quetiez
d66e413289 Typo in french dictionary
SVN:trunk[1260]
2011-05-02 12:37:14 +00:00
Romain Quetiez
938d383efd #379 GetAbsoluteUrlRoot becomes GetAbsoluteUrlAppRoot which will simplify the development of plugins, fixed a cosmetic issue in export.php (format: html + iTop not installed in the url root path)
SVN:trunk[1259]
2011-05-02 09:34:45 +00:00
Romain Quetiez
cc35e41028 Ooooops -removed debugging traces
SVN:trunk[1258]
2011-05-02 08:48:46 +00:00
Denis Flaven
0771fd0636 Completion of the Italian localization !
SVN:trunk[1257]
2011-04-29 14:26:17 +00:00
Denis Flaven
e4fa7ba983 Properly manage carriage returns/line feeds in CaseLog records.
SVN:trunk[1256]
2011-04-29 13:57:46 +00:00
Denis Flaven
109e8f6157 Undoing previous commit - keep the old icon, shouldn't have been changed.
SVN:trunk[1255]
2011-04-29 13:53:37 +00:00
Romain Quetiez
99bce469a1 CSV import: regression due to a recent enhancement (labels) and the fact that some labels are redundant (e.g. "Location" used both for Server/location_id and Server/location_name
SVN:trunk[1254]
2011-04-29 12:30:56 +00:00
Denis Flaven
e439797767 Fixed Character encoding issues in the Hungarian translation.
SVN:trunk[1253]
2011-04-29 09:48:56 +00:00
Denis Flaven
7f17acf553 Added Hungarian translation (and fixed the default dictionaries for Italian as well).
SVN:trunk[1252]
2011-04-28 17:23:45 +00:00
Denis Flaven
5e64834b06 Fixed Trac #301: don't force users back into the portal if they are not "portal" users.
SVN:trunk[1251]
2011-04-28 15:43:44 +00:00
Romain Quetiez
79fbec3675 Readme updated with latest fixes
SVN:trunk[1250]
2011-04-28 15:41:46 +00:00
Denis Flaven
f4e796f607 Implemented Trac #347: Allow portal users to update their tickets by adding a comment which gets appended to the log.
SVN:trunk[1249]
2011-04-28 15:31:22 +00:00
Romain Quetiez
0d710d0464 #355 (continuation of the fix) import.php to support localized names in the specification of reconciliation keys, and allowing to specify an external key though it is given as an external field in the input columns
SVN:trunk[1248]
2011-04-28 14:02:01 +00:00
Romain Quetiez
40701f3f36 Added helper tools to ease the setup of cron.php
SVN:trunk[1247]
2011-04-28 12:02:44 +00:00
Romain Quetiez
fcabb8fc43 CRON default duration set to 10 minutes
SVN:trunk[1246]
2011-04-28 08:47:44 +00:00
Romain Quetiez
4c64419f19 Finalized the doc and readme for release 1.1
SVN:trunk[1245]
2011-04-28 08:44:46 +00:00
Romain Quetiez
c8c388dac5 Using iTop in mode CLI: let PHP display the fatal error "unknown function mysql_connect:
SVN:trunk[1244]
2011-04-28 07:50:22 +00:00
Romain Quetiez
374858ef73 Fixed bug: Web services in CLI mode failing with an error when attempting to include APPROOT
SVN:trunk[1243]
2011-04-28 07:28:40 +00:00
Romain Quetiez
d45149a327 Readme for 1.1 RC (draft)
SVN:trunk[1242]
2011-04-27 08:39:13 +00:00
Denis Flaven
0ea9968797 Fixed Trac #391: Support Agent profile was wrong (no rights to modify UserRequest tickets).
SVN:trunk[1241]
2011-04-27 08:06:13 +00:00
Denis Flaven
b68ac3916f Aligned the display of the case log (and properties) of a ticket in the User Portal to what is done in the normal UI: better look (multi-column, fieldsets, wider case log at the bottom).
SVN:trunk[1240]
2011-04-26 16:49:28 +00:00
Romain Quetiez
e5c46340d1 Plugin API: added OnIsModified()
SVN:trunk[1239]
2011-04-26 16:03:01 +00:00
Romain Quetiez
1e6887062f Data synchro: added the support of link sets (to import user accounts)
SVN:trunk[1238]
2011-04-26 15:29:16 +00:00
Romain Quetiez
fc0f6b636e Data sync: fixed issue with the check while creating a new data source (depending on the target class)
SVN:trunk[1237]
2011-04-26 15:24:13 +00:00
Denis Flaven
bcb1864963 Fixed Trac#385: clearing an autocomplete foeld (for instance in a search form) now has an immediate effect.
SVN:trunk[1236]
2011-04-26 13:41:08 +00:00
Denis Flaven
3a1932b742 Typo: double PHP closing tag !
SVN:trunk[1235]
2011-04-26 13:38:56 +00:00
Denis Flaven
438f7f19c0 Italian localization
SVN:trunk[1234]
2011-04-26 13:37:58 +00:00
Denis Flaven
15b8b18b90 Adding Italian localization, thanks to Marialaura Colantoni
SVN:trunk[1233]
2011-04-22 15:59:31 +00:00
Denis Flaven
b9f467f08f Dictionary files that follow the naming convention <Lang>.dict.<ModuleName>.php are now loaded automatically without the need to specify them explicitely in the module definition file.
SVN:trunk[1232]
2011-04-22 15:53:21 +00:00
Denis Flaven
9f779b8dfe English dictionary cleanup/completion.
SVN:trunk[1231]
2011-04-22 15:51:23 +00:00
Denis Flaven
8ce3c48446 New option group_by_expr for "group_by' display blocks (to be used in templates) to specify a PHP expression to use for the group by. This allows to build dashboards where dates are grouped by the day of the month, for example.
SVN:trunk[1230]
2011-04-22 15:49:01 +00:00
Denis Flaven
f84d91e482 Removed the 'bgiframe' javascript , which was designed for fixing IE6 issues only and causes troubles with IE9.
SVN:trunk[1229]
2011-04-22 15:45:19 +00:00
Denis Flaven
c6493f6cdc Dictionary files that follow the naming convention <Lang>.dict.<ModuleName>.php are now loaded automatically without the need to specify them explicitely in the module definition file.
SVN:trunk[1228]
2011-04-22 15:44:50 +00:00
Romain Quetiez
fb87911677 #378 Data Synchro - leave the replica in state 'synchronized' if failing to update (update then delete)
SVN:trunk[1227]
2011-04-22 14:52:33 +00:00
Romain Quetiez
60b2162d84 Data synchro: added notifications in case of error + fixed a bug in the error reporting (reconciliation on null values, last error was not recorded)
SVN:trunk[1226]
2011-04-22 13:54:16 +00:00
Denis Flaven
e42b0513a0 Removed the 'bgiframe' javascript , which was designed for fixing IE6 issues only and causes troubles with IE9.
SVN:trunk[1225]
2011-04-22 13:30:41 +00:00
Romain Quetiez
f92a7df522 Data synchro: added "contact to notify in case of error"
SVN:trunk[1224]
2011-04-21 09:30:30 +00:00
Romain Quetiez
b7bd1a5943 Data synchro: if the object is deleted from within iTop, do cleanup sync table and replica as well
SVN:trunk[1223]
2011-04-21 09:04:34 +00:00
Denis Flaven
69f21b9b79 - English dictionary completion for 1.1 !
SVN:trunk[1222]
2011-04-20 16:11:47 +00:00
Romain Quetiez
6614a90e38 Wiki formatting: automatic detection of urls
SVN:trunk[1221]
2011-04-20 11:47:21 +00:00
Romain Quetiez
88a29fd8d1 #370 standard argument for CLI/REST services: param_file
SVN:trunk[1220]
2011-04-19 15:48:39 +00:00
Denis Flaven
7e2982b848 Added methods to trace the completion of the localization.
SVN:trunk[1219]
2011-04-19 13:22:18 +00:00
Denis Flaven
ed5d59153b Update/cleanup to the French dictionary.
SVN:trunk[1218]
2011-04-19 13:21:01 +00:00
Romain Quetiez
ab3fd7f1b4 Internal: keep track of class declaration source files
SVN:trunk[1217]
2011-04-19 12:22:32 +00:00
Denis Flaven
6e685feb9f Fixed the creation of the "Portal" profile that now behaves like a standard profile and therefore requires the proper rights.
SVN:trunk[1216]
2011-04-13 13:11:51 +00:00
Denis Flaven
d8c5f26aed Fix for IE8: On IE <button type="submit"> does not submit the form if an onClick action is specified !
SVN:trunk[1215]
2011-04-13 09:53:59 +00:00
Romain Quetiez
ada0b310ae cron.php - Fixed error message
SVN:trunk[1214]
2011-04-13 09:16:37 +00:00
Denis Flaven
1f43ba3654 Oops: missing file in the commit.
SVN:trunk[1213]
2011-04-13 09:16:07 +00:00
Denis Flaven
cb1628c759 Documentation updated for the 1.1-beta release. Still to be finalized for 1.1-RC.
SVN:trunk[1212]
2011-04-12 13:09:46 +00:00
Romain Quetiez
605edc261d Readme file ok for the Beta
SVN:trunk[1211]
2011-04-12 13:04:20 +00:00
Denis Flaven
46e7bbe2c8 Bug fix: prevent (infinite) recursion when building search form with external keys, that point to search form with external keys, etc...
Now support nested search dialogs properly...

SVN:trunk[1210]
2011-04-12 09:37:52 +00:00
Romain Quetiez
a6ac44204c Readme for 1.1 beta (draft)
SVN:trunk[1209]
2011-04-12 08:51:10 +00:00
Denis Flaven
af63233fac Styles fix for IE8.
SVN:trunk[1208]
2011-04-11 16:18:20 +00:00
Romain Quetiez
b1b913d891 Fixed mix btw French and English dictionaries (regression)
SVN:trunk[1207]
2011-04-11 16:18:14 +00:00
Denis Flaven
be5bbdf26c IE8 bug: one comma too many !
SVN:trunk[1206]
2011-04-11 16:17:25 +00:00
Denis Flaven
b03e9a969c Relative path to the setup's documentation.
SVN:trunk[1205]
2011-04-11 12:35:48 +00:00
Denis Flaven
c56c662be7 French translation cleanup: removed the doubled backslahes \\
SVN:trunk[1204]
2011-04-11 12:13:20 +00:00
Denis Flaven
6d34c2aa93 - Partial bug fix: enhancement to support extkey widgets (autocomplete) in nested search forms... still not completely fixed, bug showing some progress.
SVN:trunk[1203]
2011-04-11 12:11:57 +00:00
Denis Flaven
af20723c8c Fix the display width of case logs. Make sure that the field wraps even in case of very long strings inside the field.
SVN:trunk[1202]
2011-04-11 11:54:15 +00:00
Denis Flaven
35f09c3091 Don't use removed a field !
SVN:trunk[1201]
2011-04-11 10:27:12 +00:00
Denis Flaven
5f04a54df3 Profiles adjustements: make some links delete-able by Service Mgrs and Problem Mgrs.
SVN:trunk[1200]
2011-04-11 09:20:57 +00:00
Denis Flaven
19bbde6144 - Cleanup of the dictionary usage.
- Improvements to the French translation.

SVN:trunk[1199]
2011-04-11 09:11:00 +00:00
Denis Flaven
6fe81320c0 - English dictionary completion.
SVN:trunk[1198]
2011-04-11 08:39:18 +00:00
Erwan Taloc
d1c808b2c3 Update change Mgmt Module to display properly fields
Update French dictionary for Problem Management

SVN:trunk[1197]
2011-04-08 13:02:09 +00:00
Erwan Taloc
e3697c37f3 Mise à jour DEL MANUAL for SoftwareInstances
SVN:trunk[1196]
2011-04-08 12:42:18 +00:00
Denis Flaven
a9c6d81130 - Dictionary entries (partial) for the SynchroDataSource objects
SVN:trunk[1195]
2011-04-08 12:26:05 +00:00
Romain Quetiez
8ff40fb4b8 Deletion: entirely reviewed to take into account the objects being synchronized by an external data source, or the plugins constraints.
SVN:trunk[1194]
2011-04-08 11:29:25 +00:00
Denis Flaven
037d6cc4ba - Special handling (using some custom code - temporary solution) of the 'ticket_log' attribute: the attribute is displayed at the bottom of the "Properties" page and takes the whole width of the page.
SVN:trunk[1193]
2011-04-08 10:22:18 +00:00
Denis Flaven
4b1726c1ca Proper display of the menu !
SVN:trunk[1192]
2011-04-08 10:09:52 +00:00
Erwan Taloc
0fac58248e Update Display for tickets.
Add naming convention for Person and Database Instances

SVN:trunk[1191]
2011-04-07 19:09:42 +00:00
Denis Flaven
3a486f8edd - Adjusted the default ITIL profiles definitions
SVN:trunk[1190]
2011-04-07 16:12:07 +00:00
Denis Flaven
4fefaa5658 Make sure that we initialize the list of modules with just the default config and not the previous config. Setup now supports removing modules (even if the actual data are NOT removed from the database, yet).
SVN:trunk[1189]
2011-04-07 08:22:57 +00:00
Denis Flaven
65ef621e6f - Bug fix: Bulk modify was failing on a list of tickets in different states.
SVN:trunk[1188]
2011-04-07 07:47:28 +00:00
Denis Flaven
1a818f309d - Revised styles for a nicer/cleaner display of the details and forms.
SVN:trunk[1187]
2011-04-07 07:44:09 +00:00
Denis Flaven
1a74fe1ec3 Bug fix: when doing a bulk modify (or bulk apply stimulus) the external keys that were read-only in the edition form were displayed as "ClassName #id" instead of using their friendly name. This is now fixed, but requires a load of the target object.
SVN:trunk[1186]
2011-04-06 19:13:08 +00:00
Denis Flaven
089a9fcf4a - Make sure that the data (smaples and structure) from hidden modules are loaded as well. (Symptom: sample EmailNotifications were not loaded)
- Make sure that the "synchrodatasource" file gets loaded as part of the "standard" app modules, even when upgrading "in-place" an existing iTop configuration file.

SVN:trunk[1185]
2011-04-06 18:42:27 +00:00
Denis Flaven
e1df33c7d0 - Make the triggers and email action search-able and export/import-able using the standard user interface.
SVN:trunk[1184]
2011-04-06 18:40:22 +00:00
Denis Flaven
ee0fcdd42e Fixed Trac #371: popup dialog's dimensions (welcome screen and create or lookup dialogs) are now adjusted to fit into the screen.
SVN:trunk[1183]
2011-04-06 18:21:43 +00:00
Denis Flaven
7908b87881 - Make sure that the search form preserves it's "action" (i.e. target page) when reloading (when specifying a derived class). This was breaking the UniversalSearch page.
- Prepare for the style cleanup
- Don't display the menu to create a new 'Email Notification" when listing the notifications sent on a ticket (in the Notifications tab) !

SVN:trunk[1182]
2011-04-06 16:39:42 +00:00
Denis Flaven
8e359682ae - Make the navigation (Back / Cancel) work properly on bulk actions.
- Prepare for the style cleanup.

SVN:trunk[1181]
2011-04-06 16:34:44 +00:00
Denis Flaven
8f2089e1f6 - Fixed an error appearing only for non admins.
- Admin menu should never appear for non-admins, even empty
- Synchro Data Source objects are for admins only

SVN:trunk[1180]
2011-04-06 15:18:59 +00:00
Denis Flaven
8d61af3d22 Adjusting the French translation.
SVN:trunk[1179]
2011-04-06 09:43:35 +00:00
Denis Flaven
5e234eefe9 Special class/style for form details.
SVN:trunk[1178]
2011-04-05 13:42:43 +00:00
Romain Quetiez
25c529a797 #359 Dehardcoded the timezone (while preserving 'Europe/Paris' as the default)
SVN:trunk[1177]
2011-04-05 12:22:38 +00:00
Denis Flaven
446a8f74fb Enhancement of the forms presentation.
SVN:trunk[1176]
2011-04-05 12:13:44 +00:00
Denis Flaven
144af46e8b Fixed Track #375: Display scroll bars appropriately when dealing with big CSV load jobs.
SVN:trunk[1175]
2011-04-04 17:19:09 +00:00
Romain Quetiez
d33217285a New option: apc_cache.query_ttl (defaults to 3600s)
SVN:trunk[1174]
2011-04-04 16:20:18 +00:00
Romain Quetiez
6ff5b499b0 AttributeCaseLog finalized (history, easy migration of AttributeText, wiki formatting)
SVN:trunk[1173]
2011-04-04 14:15:33 +00:00
Denis Flaven
0c084c0b17 Delayed startup for all non-important javascript effects to speed-up the display of the pages.
SVN:trunk[1172]
2011-04-04 14:11:07 +00:00
Romain Quetiez
36dd9fc05e New option: apc_cache.enabled = true by default (beta)
SVN:trunk[1171]
2011-04-04 14:04:30 +00:00
Denis Flaven
4febe204e1 - Finishing touch to the DataExchange: various cosmetic fixes
SVN:trunk[1170]
2011-04-04 13:03:11 +00:00
Denis Flaven
9e2c556805 Load the config before using it !
SVN:trunk[1169]
2011-04-04 10:18:03 +00:00
Denis Flaven
3a0be981d6 Added the detection & logging of APC.
SVN:trunk[1168]
2011-04-04 09:57:35 +00:00
Romain Quetiez
44f661cea4 APC cache: fixed an issue with cache reset
SVN:trunk[1167]
2011-04-04 08:58:59 +00:00
Denis Flaven
195c1dabf8 New type of Attribute "CaseLog" (under construction !)
SVN:trunk[1166]
2011-04-04 08:37:30 +00:00
Denis Flaven
ce13b05363 Fixed Trac#367: typo !
SVN:trunk[1165]
2011-04-01 10:48:11 +00:00
Denis Flaven
be4674de11 Fixed Trac#368: (regression compared to 1.0.2) textarea was too small when exporting a list as CSV.
SVN:trunk[1164]
2011-04-01 10:43:07 +00:00
Denis Flaven
911d4177cc - Various fixes an finishing touch to the setup: support of "named" root modules, streamlined back/next processing, cleaner "summary" screen, etc...
SVN:trunk[1163]
2011-04-01 10:02:40 +00:00
Denis Flaven
237e36f562 - Protect against javascript js files being kept in the browser's cache when upgrading an iTop instance.
SVN:trunk[1162]
2011-04-01 09:58:53 +00:00
Romain Quetiez
5fa64ab3d2 Deletion: the message "object deleted" was displayed twice since the last review of the deletion
SVN:trunk[1161]
2011-04-01 07:29:08 +00:00
Romain Quetiez
ad98b8893f Cosmetic: changed error message when dependencies cannot be solved
SVN:trunk[1160]
2011-04-01 07:15:09 +00:00
Romain Quetiez
851b41670e New setup: fixed bug in the HTML (missing Next button on IE8)
SVN:trunk[1159]
2011-03-31 13:03:09 +00:00
Romain Quetiez
d4321ec1ce #366 Global search case sensitive or not working at all (issue with COLLATION)
SVN:trunk[1158]
2011-03-31 08:29:02 +00:00
Denis Flaven
e3ac7067f7 New implementation of the setup:
- All actions are performed asynchronously at the end of the setup
- Supports upgrading and reinstalling (to add modules)

SVN:trunk[1157]
2011-03-30 17:28:01 +00:00
Denis Flaven
eb3d226b3d Change the default collation to be consistent with the DB definition.
SVN:trunk[1156]
2011-03-30 15:37:10 +00:00
Romain Quetiez
5a30986690 Setup: Speedup profiles creation, and implemented incremental update for application upgrade
SVN:trunk[1155]
2011-03-30 08:34:39 +00:00
Romain Quetiez
b423e98295 Optimizations:
- Setup: grouped CREATE TABLE / ALTER TABLE statements in one single CREATE table
- Every page: cache the prepared queries and data model into APC
Cleanup: removed unused verb RegisterPlugin
Fix: GetObject could fail (could not find the reference from within the forum)

SVN:trunk[1154]
2011-03-30 08:30:42 +00:00
Romain Quetiez
bdf46e8136 Optimization in the setup: 10 queries to insert the 1500 action grant records
SVN:trunk[1153]
2011-03-29 14:43:22 +00:00
Romain Quetiez
d4bfa4c48f Optimization in the setup: 10 queries to insert the 1500 action grant records
SVN:trunk[1152]
2011-03-29 14:28:47 +00:00
Romain Quetiez
30a1528a28 Removed the AttributeWikiText which was strictly equivalent to AttributeText
SVN:trunk[1151]
2011-03-29 14:13:16 +00:00
Romain Quetiez
7ae02a7c5b Enablers for the current setup improvements
SVN:trunk[1150]
2011-03-28 14:03:34 +00:00
Denis Flaven
e5682fd2e9 Plug-ins integration: added the ability to manage attachments while creating a new object.
SVN:trunk[1149]
2011-03-25 17:09:50 +00:00
Romain Quetiez
c1b5614268 New verb for application extensions: OnFormCancel
SVN:trunk[1148]
2011-03-25 13:50:49 +00:00
Denis Flaven
5ad7ea1b7b Fixed Trac #365: Give the user some feedback when the password was successfully changed/set. Note that iTop does not check that the new password is different from the old one.
SVN:trunk[1147]
2011-03-24 17:34:00 +00:00
Romain Quetiez
30f4ff8765 Fixed a bug with APC version < 3.0.7
SVN:trunk[1146]
2011-03-24 17:01:59 +00:00
Denis Flaven
bec8b57fe1 Implementation of bulk modify and bulk apply stimulus... to be tested !
SVN:trunk[1145]
2011-03-23 16:22:35 +00:00
Romain Quetiez
9b50124513 Dev: improved performance measure tooling
SVN:trunk[1144]
2011-03-23 14:09:51 +00:00
Denis Flaven
c9b4468f17 Experimental support of APC to speed-up the application.
SVN:trunk[1143]
2011-03-23 14:09:39 +00:00
Romain Quetiez
3790c8f7d5 Fixed a regression in the deletion (and simplified the algorithm, though more queries will be issued)
SVN:trunk[1142]
2011-03-23 08:38:49 +00:00
Romain Quetiez
8f4ee29c69 #362 New capability for attributes derived from AttributeString: specify a validation_pattern (regexp)
SVN:trunk[1141]
2011-03-21 16:06:41 +00:00
Romain Quetiez
cf737bb8ad Added a query usage example with REGEXP
SVN:trunk[1140]
2011-03-21 15:28:31 +00:00
Romain Quetiez
a222b33566 CRON - admin only + queues with timer (field 'planned')
SVN:trunk[1139]
2011-03-21 15:17:08 +00:00
Romain Quetiez
78396eafe3 Cron - enabled mode CLI
SVN:trunk[1138]
2011-03-21 11:41:08 +00:00
Romain Quetiez
de5ccf5908 Background processes - API ready for periodicity management
SVN:trunk[1137]
2011-03-21 11:17:42 +00:00
Romain Quetiez
c3bd0b1b9e Check SLA for tickets, integrated into the internal CRON architecture (and moved into the Tickets module)
SVN:trunk[1136]
2011-03-21 10:57:37 +00:00
Romain Quetiez
736247f6e1 Email sent in asynchronous mode: forgot cron.php
SVN:trunk[1135]
2011-03-18 12:17:04 +00:00
Romain Quetiez
3074683274 Email sent in asynchronous mode: forgot two files...
SVN:trunk[1134]
2011-03-18 12:13:04 +00:00
Denis Flaven
4929f94c20 Protect the output of the config file against values containing quotes.
SVN:trunk[1133]
2011-03-18 11:22:23 +00:00
Romain Quetiez
b8b20f82d7 Email sent in asynchronous mode (established the base for an application internal CRON, task queue, etc.) with adjustable configuration (disable by default). Also improved the OQL: IS_NULL() and REGEXP have been added.
SVN:trunk[1132]
2011-03-18 11:15:09 +00:00
Romain Quetiez
7989cf622f CSV import: fixed a few issues (restricted to N-N links) + added arguments to the export page, to allow for exporting N-N links
SVN:trunk[1130]
2011-03-15 12:19:04 +00:00
Romain Quetiez
f3cc490295 #328 Added the capability to import/export link sets in CSV format
SVN:trunk[1129]
2011-03-14 14:11:08 +00:00
Romain Quetiez
52a47b193d #360 Wrong COUNT when using JOINS (+ other conditions)
SVN:trunk[1128]
2011-03-11 17:47:03 +00:00
Denis Flaven
cfbf77544e Adjust the relative path to 'ajax.render.php' so that it can be called from the portal pages as well.
SVN:trunk[1125]
2011-03-11 11:11:56 +00:00
Romain Quetiez
0b32f511b8 Plugin API - alpha version
SVN:trunk[1124]
2011-03-11 11:04:50 +00:00
Denis Flaven
bca713a964 Search forms enhancement: when a search criteria is an external (foreign) key, use an autocomplete instead of of drop-down list, when the number of different values is too big, as in other forms.
SVN:trunk[1123]
2011-03-09 16:39:58 +00:00
Romain Quetiez
692f7060ea #355 CSV Import (non interactive) now supporting localized column headers, making it possible to import directly data generated by the interactive export. NOTE: to achieve this, the default separator is now the coma (whereas the default separator in XCel sheets is the semicolumn)
SVN:trunk[1122]
2011-03-09 09:19:03 +00:00
Romain Quetiez
4fa9933dc7 #340 Fixed OQL parsing issue - parameters in IN()/NOT IN() clauses
SVN:trunk[1121]
2011-03-08 15:51:11 +00:00
Denis Flaven
45134e182d Fixed Trac#313: Provider contracts are filtered on the 'provider_id' - for filtering in the UI via the drop-down list of Organizations and for the security profiles ("Allowed Organizations"). The mapping for 'org_id', if any, is now taken into account by the security.
SVN:trunk[1120]
2011-03-08 12:31:16 +00:00
Romain Quetiez
38a502aa25 #271 Internal - Removed a workaround made unnecessary with fix [1108]
SVN:trunk[1119]
2011-03-08 12:23:04 +00:00
Denis Flaven
49e05acd4b Fixed Trac #156: make sure the hierarchical ZLists are supported everywhere (final cleanup).
SVN:trunk[1118]
2011-03-08 11:19:38 +00:00
Denis Flaven
089407119b Thanks to Schlobinux for the fix of Trac#336.
SVN:trunk[1117]
2011-03-08 10:52:58 +00:00
Denis Flaven
6262fa1d26 Fix Trac #336: verification of the directory of the temporary config file was wrong... however the script still assumes that the temporary config file and the final one are stored in the same place... at the root of the iTop installation.
SVN:trunk[1116]
2011-03-08 10:48:35 +00:00
Romain Quetiez
c4cca2b2f8 #348 Multi-column queries not working fine with open joins and if null values to be displayed
SVN:trunk[1115]
2011-03-08 10:46:03 +00:00
Denis Flaven
d5353c111c Fixed Trac#356 (Audit results filtered by context) and #357 (Audit results list not expandable)
SVN:trunk[1114]
2011-03-08 10:31:13 +00:00
Denis Flaven
fd79d30483 Fixed Trac #353: no menu for DBServerInstance objects.
SVN:trunk[1113]
2011-03-07 17:12:30 +00:00
Denis Flaven
d708ef585b Fixed Trac#344: default search behavior for enumerated attributes (and similar types: 'Class', 'Language' and 'FinalClass') is now a strict '=' instead of 'contains'.
SVN:trunk[1112]
2011-03-07 17:06:17 +00:00
Romain Quetiez
6ac06d7e47 #352 Web Service CreateTicket: Search the service subcategory given the found service_id (if not already specified)
SVN:trunk[1111]
2011-03-07 16:58:46 +00:00
Romain Quetiez
019888caff #305 Specified the charset in any call to htmlentities()
SVN:trunk[1110]
2011-03-07 16:43:20 +00:00
Romain Quetiez
e36646a3e6 Fixed undocumented issue: in read-only mode, stimulus where still allowed
SVN:trunk[1109]
2011-03-07 16:40:26 +00:00
Romain Quetiez
8b6672c0dd Data Exchange - Fixed issue in DBObjectSet, doubling the count of SynchroAttribute initially created
SVN:trunk[1108]
2011-03-03 08:34:24 +00:00
Denis Flaven
3d4e76d8b0 Synchro Data Sources Implementation on going...
SVN:trunk[1107]
2011-03-02 17:06:56 +00:00
Romain Quetiez
cf8174d54e Data Exchange - Improved to allow the import of complex attributes like passwords and documents + few fixes
SVN:trunk[1106]
2011-03-02 14:38:12 +00:00
Denis Flaven
33f1ec8bf3 Data Exchange implementation in progress...
SVN:trunk[1105]
2011-03-02 10:49:12 +00:00
Denis Flaven
be1d8674f3 Data Exchange implementation in progress...
SVN:trunk[1104]
2011-03-02 09:12:15 +00:00
Romain Quetiez
76b5bfb19e Data Exchange - Cleanup
SVN:trunk[1103]
2011-03-01 16:33:09 +00:00
Romain Quetiez
bddb8b4c92 Data Exchange - Format and save traces (config option: synchro_trace)
SVN:trunk[1102]
2011-03-01 16:21:04 +00:00
Romain Quetiez
67f8ddf5a8 Data Exchange - Implemented reconciliation on external keys
SVN:trunk[1101]
2011-03-01 13:47:09 +00:00
Denis Flaven
f3f2eb5c79 Synchro Data Sources Implementation on going...
SVN:trunk[1100]
2011-03-01 09:01:44 +00:00
Romain Quetiez
a1b9bdaf66 Data Exchange - Improved handling of deletion (inc. the reporting of it)
SVN:trunk[1099]
2011-02-25 17:22:51 +00:00
Denis Flaven
e020bad17d Synchro Data Sources Implementation on going...
SVN:trunk[1098]
2011-02-24 18:45:13 +00:00
Romain Quetiez
36278d59a8 Data Exchange - Implemented checks on sync startup (user identity, task state, protection against entire obsolescence) and added a "last error" into the logs
SVN:trunk[1097]
2011-02-24 17:21:48 +00:00
Denis Flaven
20d0d33b60 Synchro Data Sources Implementation on going...
SVN:trunk[1096]
2011-02-24 17:09:50 +00:00
Romain Quetiez
573712e9af Data Exchange - Fixed issue when a field is selected for reconciliation but not for update
SVN:trunk[1095]
2011-02-24 15:37:21 +00:00
Romain Quetiez
5897ae5bb9 Data Exchange - Simulation in synchro_import.php now relying on MySQL transactions
SVN:trunk[1094]
2011-02-24 08:47:46 +00:00
Romain Quetiez
cd71f02fad Data Exchange - Implemented reconciliation options + cosmetics on synchro_exec
SVN:trunk[1093]
2011-02-23 17:32:21 +00:00
Denis Flaven
e4df8c8c92 Added synchron statistics and their first display...
SVN:trunk[1092]
2011-02-23 16:49:00 +00:00
Romain Quetiez
d1879d0433 Data Exchange - Simulate mode on synchro_exec + cleanup in traces + handling of null values
SVN:trunk[1091]
2011-02-23 14:55:51 +00:00
Denis Flaven
9e636b1ce5 New display of the "Synchro status/log"
SVN:trunk[1090]
2011-02-23 14:37:23 +00:00
Denis Flaven
cccea50159 Added an extra parameter 'simulate'
SVN:trunk[1089]
2011-02-22 15:21:40 +00:00
Romain Quetiez
3441911781 Fixed regression on AttributeInteger
SVN:trunk[1088]
2011-02-22 14:26:00 +00:00
Romain Quetiez
c97f4d5ee7 Data Exchange - Log statistics
SVN:trunk[1087]
2011-02-22 14:20:46 +00:00
Romain Quetiez
4c637548ac Fixed regression in the audit (new way to translate OQL expression having an impact on DBObjectSearch::MergeWith)
SVN:trunk[1086]
2011-02-21 16:35:47 +00:00
Denis Flaven
721ca8c652 Continuing the implementation of the DataExchange
SVN:trunk[1085]
2011-02-16 17:31:32 +00:00
Romain Quetiez
293877d78a Data Exchange - Replica objects must not be derived from cmdbAbstractObject
SVN:trunk[1084]
2011-02-11 13:15:41 +00:00
Romain Quetiez
d92f89fbe6 Data Exchange - Finalized deletion, changed the definition of attributes (update/reconcile), added an automated test
SVN:trunk[1083]
2011-02-11 09:01:16 +00:00
Romain Quetiez
1da6a264d0 Automatic deletion of links lnkSolutionToCI
SVN:trunk[1082]
2011-02-10 15:13:24 +00:00
Denis Flaven
89b58d44cd Removed a "assertion failed" error message.
SVN:trunk[1080]
2011-02-09 09:44:00 +00:00
Denis Flaven
0b93e6f79e Fixed an incorrect detection of the maximum recursion level
SVN:trunk[1079]
2011-02-08 16:25:18 +00:00
Denis Flaven
eb0ba24d7d - Data Exchange implementation... in progress.
SVN:trunk[1078]
2011-02-08 13:34:47 +00:00
Romain Quetiez
64521eb758 Fixed regression (implementation of the data exchange)
SVN:trunk[1074]
2011-02-07 16:29:49 +00:00
Denis Flaven
2d65325f6f New configuration setting (and new class of Log objects) to keep track of the application's usage: an entry in the log is added each time a user connects to the application. (This feature is disabled by default)
SVN:trunk[1073]
2011-02-07 15:55:39 +00:00
Denis Flaven
a97238c3cd Fixed Trac #351: undefined variable sClass...
SVN:trunk[1072]
2011-02-07 15:51:24 +00:00
Romain Quetiez
ce993b60e4 Data Exchange - Removed the state 'deleted': instead, delete the replica!
SVN:trunk[1068]
2011-02-03 16:39:58 +00:00
Romain Quetiez
283f7dc9f8 CSV import web service - cosmetics on the reporting in case the data set is empty
SVN:trunk[1067]
2011-02-03 15:50:48 +00:00
Romain Quetiez
4441fb41b9 Data Exchange - Fixed a corner case issue (a replica going directly from new to obsolete, then touched again -> back into the picture)
SVN:trunk[1066]
2011-02-03 15:36:37 +00:00
Romain Quetiez
34322045df Data Exchange - forgot a change in the previous commit
SVN:trunk[1065]
2011-02-03 09:57:47 +00:00
Romain Quetiez
b73da47775 Data Exchange - new web service to update data tables
SVN:trunk[1064]
2011-02-03 09:55:17 +00:00
Denis Flaven
3d8ffba2fa Mention that the database user must have the "CREATE VIEW and TRIGGER" privileges for the setup to work fine.
SVN:trunk[1063]
2011-02-03 09:41:32 +00:00
Denis Flaven
f9a380b12d DataExchange: implementation in progress.
SVN:trunk[1062]
2011-02-02 14:48:45 +00:00
Romain Quetiez
a9a530a2cb Core: Join any table to an OQL query (prerequisite to Data Exchange)
SVN:trunk[1061]
2011-02-01 16:54:10 +00:00
Romain Quetiez
ba822a655e Wiki: add links to objects in text attributes
SVN:trunk[1060]
2011-02-01 13:40:51 +00:00
Romain Quetiez
20ec91e9f3 Data ExchangeData Exchange - data structure (ready for sync)
SVN:trunk[1059]
2011-02-01 12:56:26 +00:00
Romain Quetiez
735a9729c0 Data Exchange - data structure
SVN:trunk[1058]
2011-01-31 15:53:07 +00:00
Romain Quetiez
1a52f4de72 #149 Implemented friendly names
SVN:trunk[1057]
2011-01-26 09:43:35 +00:00
Denis Flaven
cb0610bf0b Readme for iTop 1.0.2
SVN:trunk[1055]
2011-01-18 11:38:24 +00:00
Denis Flaven
20ea4644b4 Remove an ambiguous naming when performing a CSV import of DBServerInstance objects.
SVN:trunk[1054]
2011-01-18 11:37:21 +00:00
Denis Flaven
fde336d8dd Cosmetics: make the login & change password form look the same on all browsers.
SVN:trunk[1053]
2011-01-18 09:07:46 +00:00
Denis Flaven
bc78b19ba7 Fixed Trac #348: no validation needed when pressing "Restart".
SVN:trunk[1052]
2011-01-18 09:00:26 +00:00
Denis Flaven
a4883076f8 Fixed Trac #338: when updating a 'dependent' field, don't forget to cascade the refresh in case this field has its own dependent fields.
SVN:trunk[1051]
2011-01-17 17:11:03 +00:00
Denis Flaven
57a0e2d47e Fixed Trac #345: hidden or read-only parameters in a form were not taken into account for computing the allowed values.
SVN:trunk[1050]
2011-01-17 15:13:28 +00:00
Denis Flaven
5a41992739 Trying to make CKEditor work with the "ExtKey" widget. (Trac #343)
Seems to be functional but there is still an "uncaught exception" that pops up from nowhere when closing the dialog.

SVN:trunk[1049]
2011-01-10 18:15:15 +00:00
Denis Flaven
15e07b69d6 Fixed Trac #341: broken form when an external key is part of the attrbiutes of an n-n link !
SVN:trunk[1048]
2011-01-10 18:07:54 +00:00
Denis Flaven
f6bfcb8355 Preparing the readme for the 1.0.2 release
SVN:trunk[1047]
2011-01-04 09:44:47 +00:00
Denis Flaven
019959504f Added the ability to (easily) attach files to a user request from the "portal" page. (French translation)
SVN:trunk[1046]
2011-01-04 09:35:17 +00:00
Denis Flaven
331b7ec2f0 Added the ability to (easily) attach files to a user request from the "portal" page.
SVN:trunk[1045]
2011-01-03 16:39:16 +00:00
Romain Quetiez
56ddf960a5 Readme for 1.0.2 -draft
SVN:trunk[1044]
2011-01-03 16:05:09 +00:00
Denis Flaven
35429228a2 Fixed a typo in German translation (Trac #339), thanks to ulmerspatz and Jonathan Lucas
SVN:trunk[1043]
2011-01-03 15:02:05 +00:00
Denis Flaven
78256bae0b - Display PHP errors during setup instead of hiding them ! (Trac #321)
- Set a longer timeout during setup (Trac #314)

SVN:trunk[1042]
2011-01-03 14:56:28 +00:00
Denis Flaven
8145c23899 Avoid using 'gethostname()' which is not always available.
SVN:trunk[1041]
2011-01-03 14:38:58 +00:00
Denis Flaven
3b629ab83c SVN:trunk[1040] 2011-01-03 13:46:06 +00:00
Denis Flaven
abc2c7b5e1 Small fix: using an unassigned variable.
SVN:trunk[1039]
2011-01-03 13:02:17 +00:00
Denis Flaven
e1d7e17c5d Added support for hierarchical ZLists when checking the data model consistency
SVN:trunk[1038]
2011-01-03 12:58:21 +00:00
Denis Flaven
64f452caba FIxed the processing of hierarchical ZLists to keep the display order when plain fields and fieldsets are mixed at the same level.
SVN:trunk[1037]
2011-01-03 12:56:07 +00:00
Denis Flaven
a8a77fdebb Make sure that the "+" (Create) button is never displayed for an abstract class.
SVN:trunk[1036]
2011-01-03 12:54:05 +00:00
Romain Quetiez
83e7e9c44b Chinese: added to the readme.txt
SVN:trunk[1035]
2010-12-20 13:47:06 +00:00
Romain Quetiez
6c73b3ed2e Localized in chinese
SVN:trunk[1034]
2010-12-20 13:44:15 +00:00
Romain Quetiez
adfed90733 Localized in turkish
SVN:trunk[1033]
2010-12-20 11:48:34 +00:00
Denis Flaven
c6a6af0aa2 Including approot is not needed here !
SVN:trunk[1032]
2010-12-20 11:45:38 +00:00
Denis Flaven
19463b370c - Updates to the Spanish localization
SVN:trunk[1031]
2010-12-20 11:30:16 +00:00
Denis Flaven
6bc3a80c8c - Small updates to the German localization
SVN:trunk[1030]
2010-12-20 11:28:50 +00:00
Denis Flaven
de4945bca6 - Added the russian localization (thanks to Vladimir Shilov)
SVN:trunk[1029]
2010-12-20 11:17:05 +00:00
Denis Flaven
6dd3eb9970 - Added the russian localization (thanks to Vladimir Shilov)
SVN:trunk[1028]
2010-12-20 11:13:54 +00:00
Denis Flaven
511c703b61 Bug fix: history of encrypted fields was not recorded properly.
SVN:trunk[1027]
2010-12-15 16:33:08 +00:00
Romain Quetiez
21722b9e80 Internal: added an API for logging tools events
SVN:trunk[1026]
2010-12-10 15:19:18 +00:00
Denis Flaven
203887589f - Yet another IE fix...
SVN:trunk[1025]
2010-12-10 13:58:12 +00:00
Denis Flaven
028f23214d - Fixed the CSV "confirmation pie chart" to work under IE & enhanced the look of the chart by removing unused values.
SVN:trunk[1024]
2010-12-10 13:43:55 +00:00
Romain Quetiez
b89a4d32d1 Improved the detection of MySQL Slave status
SVN:trunk[1023]
2010-12-09 10:04:38 +00:00
Romain Quetiez
b6883ed3b4 Internal: added an helper to get the currently loaded config file
SVN:trunk[1022]
2010-12-08 15:29:30 +00:00
Denis Flaven
6338775506 Split the usage of the iTop logo: one logo to be displayed internally (once logged) one externally (login/logoff).
SVN:trunk[1021]
2010-12-08 09:43:56 +00:00
Denis Flaven
25ea409228 Build the menus BEFORE the actual display of the page content... since the content is the result of a menu !
SVN:trunk[1020]
2010-12-07 19:54:27 +00:00
Denis Flaven
6a72ceb685 Patch for supporting a data model without any Person.
SVN:trunk[1019]
2010-12-07 15:24:16 +00:00
Denis Flaven
235203b3a0 The hyperlink to the online-help file is now configurable
SVN:trunk[1018]
2010-12-07 15:15:43 +00:00
Denis Flaven
2b9beb971b For fine-grained user rights detection...
SVN:trunk[1017]
2010-12-07 15:13:41 +00:00
Denis Flaven
5ff5e3f1a7 Patch for supporting a data model without any Person.
SVN:trunk[1016]
2010-12-07 15:06:17 +00:00
Denis Flaven
e3caff6689 Patch for supporting a data model without any Organization.
SVN:trunk[1015]
2010-12-07 14:55:40 +00:00
Romain Quetiez
59970be867 Menus created via a handler, at runtime
SVN:trunk[1014]
2010-12-07 14:32:13 +00:00
Romain Quetiez
98cfcc9cb8 Fixed an issue in the display of CSV imports history
SVN:trunk[1013]
2010-12-07 13:46:22 +00:00
Romain Quetiez
e66f06cda2 Internal: improved the user management API ChangePassword()
SVN:trunk[1012]
2010-12-06 17:07:07 +00:00
Romain Quetiez
35fe0b9ef5 Fixed bug in CSV sync mode (class given by the scope)
SVN:trunk[1011]
2010-12-06 15:58:37 +00:00
Romain Quetiez
cde184e2a3 Allow a module to provide a handler to override application settings: OnMetaModelStarted()
SVN:trunk[1010]
2010-12-06 09:14:20 +00:00
Denis Flaven
df8f4449e8 Fix for Trac #337: email validation. Use a simpler regular expression that is much faster to execute.
SVN:trunk[1009]
2010-12-03 12:03:05 +00:00
Romain Quetiez
b225b621b7 Cosmetic issue with the banner when the application is fully accessible
SVN:trunk[1008]
2010-12-03 10:28:25 +00:00
Romain Quetiez
d85aba8ebc Improved change tracking: user login replaced by the full name if available
Added a tab into the CSV import: browse the CSV imports history
Finalized the read-only mode (distinguish between users and everybody, admin message displayed on top of the main screen)

SVN:trunk[1007]
2010-12-03 10:18:28 +00:00
Denis Flaven
53d5867b93 Patch to support a different data model where contacts are not part of an org
SVN:trunk[1006]
2010-12-03 09:10:15 +00:00
Romain Quetiez
d4eaf43257 Fixed cosmetic issues in the CSV import (related to the recent changes)
SVN:trunk[1005]
2010-12-01 16:27:11 +00:00
Denis Flaven
dc96cbc482 - Ask for confirmation when doing a CSV import/synchro that is considered as "risky" (based on thresholds from the config file)
- Added a "Restart" button to quickly start over a CSV import/synchro

SVN:trunk[1004]
2010-12-01 15:21:29 +00:00
Denis Flaven
73e9107ae2 - Added the new "synchro" mode to the CSV load page.
SVN:trunk[1003]
2010-12-01 10:27:10 +00:00
Denis Flaven
9d1df1b799 - Bug fix: when creating a new object the "Cancel" button must go somewhere !
SVN:trunk[1002]
2010-11-30 15:55:40 +00:00
Romain Quetiez
018344578e Internal: BulkChange improved with synchro mode
SVN:trunk[1001]
2010-11-30 15:32:30 +00:00
Denis Flaven
bb4c6aecfa Added the capability to enable/disable menus based on the rights to apply a given stimulus.
SVN:trunk[1000]
2010-11-30 15:16:17 +00:00
Denis Flaven
06bb4a4f03 - Bug fix: support resizable elements inside tabs.
SVN:trunk[999]
2010-11-30 14:54:03 +00:00
Denis Flaven
dfe3ced440 - Provide some feedback while loading the "creation" dialog in case of a "SELECT".
SVN:trunk[998]
2010-11-30 14:27:11 +00:00
Denis Flaven
922d1dc734 Added the capability to enable/disable menus based on the rights to apply a given stimulus.
SVN:trunk[997]
2010-11-30 14:26:17 +00:00
Denis Flaven
bf2654409b Debug of the ExtKeyWidget...
SVN:trunk[996]
2010-11-30 13:51:46 +00:00
Denis Flaven
b6a001fedc - Cosmetics: prevent the extkey widget to warp.
SVN:trunk[995]
2010-11-30 13:22:12 +00:00
Denis Flaven
d8f2979c5a - Fixed Trac #318 (and #335): added the check of the mandatory DOM extension.
SVN:trunk[994]
2010-11-29 18:27:44 +00:00
Denis Flaven
7f8f4f5aab Exclude the special "data loader" tool from the standard build
SVN:trunk[993]
2010-11-29 15:33:27 +00:00
Romain Quetiez
653229b1bd XML data loader: fixed issue with transations
SVN:trunk[992]
2010-11-29 14:42:42 +00:00
Romain Quetiez
2b69c5210f Fixed issues in the compatibility with config files generated with 1.0
SVN:trunk[991]
2010-11-29 13:26:18 +00:00
Denis Flaven
7531dd8b19 Align properly the 'validation_error' icon in forms.
SVN:trunk[990]
2010-11-29 13:04:50 +00:00
Denis Flaven
49e101403f Small fix of unassigned variable when editing ext keys.
SVN:trunk[989]
2010-11-29 12:28:47 +00:00
Denis Flaven
d10c3ace5d Renamed 'autocompleteWidget' to 'extkeyWidget' since it's not always an autocomplete...
SVN:trunk[988]
2010-11-29 10:43:06 +00:00
Denis Flaven
4a6bc8a896 - added the ability to create objects pointed by ExtKeys even when the edit mode is a drop-down list and not an autocomplete
- made this behavior configurable globally or per external key, using the config-flag/option: allow_target_creation.

SVN:trunk[987]
2010-11-29 10:35:18 +00:00
Romain Quetiez
c428e07c4b XML data loader to request for credentials
SVN:trunk[986]
2010-11-29 10:19:33 +00:00
Romain Quetiez
540eb5f272 Moved the standards menus into the "welcome" module
SVN:trunk[985]
2010-11-29 09:17:44 +00:00
Romain Quetiez
699679318a Welcome menu (forgot at previous commit)
SVN:trunk[984]
2010-11-26 16:39:41 +00:00
Denis Flaven
4bd217d178 Keep backward compatibility with *old* config files without the 'webservices' entry in the config-file.
SVN:trunk[983]
2010-11-26 16:22:20 +00:00
Romain Quetiez
e37f6a0954 Welcome page moved out the application, into a dedicated module: itop-welcome-itil
SVN:trunk[982]
2010-11-26 15:59:41 +00:00
Romain Quetiez
9f000c15ff Cosmetic on setup: perform post DB install in the same page as admin account creation, and hide ticket prerequisites module
SVN:trunk[981]
2010-11-26 15:57:11 +00:00
Romain Quetiez
7d13ec00e4 User profiles: created in dedicated module itop-profiles-itil
SVN:trunk[980]
2010-11-26 15:55:38 +00:00
Denis Flaven
c8384cc8a4 Handle filtering of non-existent attributes in hierarchical ZLists.
SVN:trunk[979]
2010-11-26 13:25:00 +00:00
Denis Flaven
9e70feb29d - Fixed bug #334: proper handling of the "remove objects" button (was working only for the first linkset in the object).
- Improved feedback while searching and reloading added objects.

SVN:trunk[978]
2010-11-26 13:19:13 +00:00
Denis Flaven
ced4bf4259 - Fixed Trac #333: organizations' drop-down list is truncated on IE when the name of an organization is too long.
SVN:trunk[977]
2010-11-26 11:16:55 +00:00
Denis Flaven
51fa310664 - Fixed Trac #332: improved usability of the CSV import wizard with IE8.
SVN:trunk[976]
2010-11-26 10:43:25 +00:00
Romain Quetiez
64044e47e1 Modularity: allow a module to execute some specific installation procedures (customize the config file, do something in the database)
SVN:trunk[975]
2010-11-25 17:15:12 +00:00
Romain Quetiez
ebf9910579 #331 Import.php could not be run in HTTP mode (when PHP running in CGI mode) - fix getting more bullet proof
SVN:trunk[974]
2010-11-25 09:52:22 +00:00
Romain Quetiez
a60709a354 #331 Import.php could not be run in HTTP mode (when PHP running in CGI mode)
SVN:trunk[973]
2010-11-25 09:44:42 +00:00
Romain Quetiez
c63d25e223 Read-only mode relying successively on a DB property, and an application setting
SVN:trunk[972]
2010-11-24 16:52:55 +00:00
Romain Quetiez
7fbcdc407e Added class DBProperty
SVN:trunk[971]
2010-11-24 16:28:13 +00:00
Romain Quetiez
35c50d6689 Ajax pages: exceptions are traced into error.log
SVN:trunk[970]
2010-11-24 14:00:24 +00:00
Romain Quetiez
ff112faf5a Removed PHP "warning"
SVN:trunk[969]
2010-11-24 09:05:35 +00:00
Romain Quetiez
5ecbc66d7e Fixed regressions in the user portal
SVN:trunk[968]
2010-11-24 09:03:20 +00:00
Romain Quetiez
dc3a99200f Added an helper function to report the current configuration
SVN:trunk[967]
2010-11-23 17:07:51 +00:00
Romain Quetiez
40a613b4db Cleanup: replaced remaining occurences of ISO-8859-1
SVN:trunk[966]
2010-11-23 16:18:48 +00:00
Romain Quetiez
fcbd9b8911 Improved Web services: opened to services coming from an optional module
SVN:trunk[965]
2010-11-23 16:16:01 +00:00
Denis Flaven
9dd2061f60 Do not assume that a User is linked to a Person !
SVN:trunk[964]
2010-11-23 13:36:53 +00:00
Denis Flaven
2c4bdf6398 - Fixed the detection of relative path in the config file to make it compatible with *nix
SVN:trunk[963]
2010-11-23 11:54:41 +00:00
Romain Quetiez
d8bb6a45b2 REVIEWED THE FILE INCLUSION POLICY
- includes are relative to the application root folder, aka APPROOT
- changed the config file, while preserving the compatibility with older installs

SVN:trunk[962]
2010-11-22 17:53:52 +00:00
Romain Quetiez
d0f168adbb Read-only mode - draft
SVN:trunk[961]
2010-11-22 14:13:49 +00:00
Denis Flaven
b30d6c4888 - Safer way to pass information through the WizardHelper JS object.
- Removed an unneeded size=100% that bothers IE.

SVN:trunk[960]
2010-11-22 13:27:41 +00:00
Denis Flaven
ed39fa2c96 Continuing the 'autocomplete' debug.
SVN:trunk[959]
2010-11-22 13:21:45 +00:00
Denis Flaven
92045ef15f Continuing the 'autocomplete' debug.
SVN:trunk[958]
2010-11-22 12:52:29 +00:00
Denis Flaven
956ebb1962 Force a dummy timezone to prevent a warning during the setup...
SVN:trunk[957]
2010-11-22 12:36:38 +00:00
Denis Flaven
a1cc8deb47 Debugging of the autocomplete on IE8.
SVN:trunk[956]
2010-11-22 12:35:46 +00:00
Denis Flaven
0306c6691a - Implementing the creation of objects on the fly when editing an extrernal key... still to be fully tested.
SVN:trunk[955]
2010-11-22 10:44:43 +00:00
Romain Quetiez
d098bae484 French is french, not english
SVN:trunk[954]
2010-11-22 10:23:38 +00:00
Romain Quetiez
a524100d99 Fixed typo in French dictionary
SVN:trunk[953]
2010-11-22 10:20:09 +00:00
Romain Quetiez
cf888ea165 Added attribute types into the dictionary
SVN:trunk[952]
2010-11-22 10:06:52 +00:00
Romain Quetiez
8f7be02bc2 Clarified the states / attributes flags for the user requests
SVN:trunk[951]
2010-11-22 10:02:04 +00:00
Romain Quetiez
8e125cb743 Fixed issue in the lifecycle generation tool (escape double quotes)
SVN:trunk[950]
2010-11-19 15:34:11 +00:00
Denis Flaven
a97e2825b1 Make sure that each input field gets its own unique ID.
SVN:trunk[949]
2010-11-18 17:37:30 +00:00
Romain Quetiez
738d90d592 Regression on N-N links creation - passing over to Denis
SVN:trunk[948]
2010-11-18 17:09:02 +00:00
Denis Flaven
dc9a52c9e0 Improved implementation of the 'autocomplete' input and fix of quite a few related issue with aysnchronous inputs. Autocompletes are now restricted to external keys only.
Some details:
- Autocomplete now matches on 'contains' instead of 'begins with'
- The minimum size of this match is configurable in the config file and per attribute ('min_autocomplete_chars').
- The maximum size that turns a drop-down list into an autocomplete is configurable in the config-file and per attribute ('max_combo_length').
- Better feedback when expanding/collapsing search results lists.
- 'Pointer' cursor on the link to Expand/Collapse results lists.
- The 'mandatory' state of an attribute is no longer lost when some part of a form is reloaded asynchronously

SVN:trunk[947]
2010-11-18 16:41:09 +00:00
Romain Quetiez
5ea71a3d51 #320 Integrated an HTML Editor -still, no attribute has been changed, because it would require a migration procedure... to be continued
SVN:trunk[946]
2010-11-16 16:16:47 +00:00
Denis Flaven
8d80d73f35 - Allow DBObjects to be deleted by the standard UI 'Delete', which may be useful in case a DBObject has to be deleted as a dependent object of a CMDBObject.
SVN:trunk[945]
2010-11-16 13:21:21 +00:00
Denis Flaven
99907a92ea Use XMLPage passthrough mode to speed up and consume less memory for big XML exports.
SVN:trunk[944]
2010-11-12 16:49:47 +00:00
Denis Flaven
259b266a19 - Keep track of the object at the source of any email notification for further correlation (either in the user's mailbox or for automatic processing of incoming emails)
SVN:trunk[943]
2010-11-12 16:34:50 +00:00
Denis Flaven
effa212261 - Added a 'pre' method for transparent support of the CLI (Command Line Interface) mode.
SVN:trunk[942]
2010-11-12 16:28:17 +00:00
Denis Flaven
1a92a7b4f3 Ready for 1.0.1 release
SVN:trunk[940]
2010-11-03 15:33:46 +00:00
Romain Quetiez
4338bce526 Readme draft for 1.0.1
SVN:trunk[939]
2010-11-03 13:06:52 +00:00
Denis Flaven
4572bdbb02 Nicer display of the CSV import results...
SVN:trunk[938]
2010-11-02 16:33:26 +00:00
Denis Flaven
fcd72fed82 Fix for Trac #315: default organization not handled properly when there is just one organization allowed for the user.
SVN:trunk[937]
2010-11-02 16:30:46 +00:00
Romain Quetiez
1800120762 #311 CSV import: Improved the reporting on external keys (and changed a little the behavior)
SVN:trunk[936]
2010-11-02 16:29:41 +00:00
Denis Flaven
e5f0460703 AutoComplete usage: code cleanup
SVN:trunk[935]
2010-11-02 10:16:24 +00:00
Denis Flaven
72dbb9cb8f Fixed Trac #312: initial form validation for the "apply stimulus/change state" wizard.
SVN:trunk[934]
2010-11-02 09:29:55 +00:00
Romain Quetiez
562cfde587 #308 Free IPs: do not show the subnet address (the first one)
SVN:trunk[933]
2010-11-01 08:37:21 +00:00
Denis Flaven
6fe25d8b57 Fixed Trac #309 : Missing php-ofc-library files
SVN:trunk[932]
2010-10-29 11:11:09 +00:00
Romain Quetiez
248cdcea8c #286 Issue with HTTPS - reviewed the fix implemented in [896]
SVN:trunk[931]
2010-10-28 12:55:51 +00:00
Denis Flaven
0b95b3799e Fixed Trac #307: prevent form submission when the value typed into an autocomplete field does not correspond to a valid value.
SVN:trunk[930]
2010-10-26 08:12:31 +00:00
Denis Flaven
05a2443afe The previous fix for Trac #276 broke the auto mapping of external keys... now fixed.
SVN:trunk[929]
2010-10-25 17:05:12 +00:00
Denis Flaven
1860521e74 Fixed Trac #306: corrupted password when user creation fails at first attempt.
SVN:trunk[928]
2010-10-25 15:49:20 +00:00
Denis Flaven
8d39ddded6 Do not create a "mailto" hyperlink for empty email addresses.
SVN:trunk[927]
2010-10-25 14:27:48 +00:00
Denis Flaven
fd3317e99e Fixed the "Relationships" Flash navigator so that it works also on Safari. (tested with Safari 5.0.2 on Windows)
SVN:trunk[926]
2010-10-25 14:14:29 +00:00
Denis Flaven
7651aaed81 - Fixed the search form, and also fixed the search/selection of objects to link (n:n links) that was broken on IE8.
SVN:trunk[925]
2010-10-25 14:12:26 +00:00
Romain Quetiez
a5d2d067ff #284 Do not forget to adjust both upload_max_filesize AND post_max_size
SVN:trunk[924]
2010-10-25 11:33:48 +00:00
Denis Flaven
b27fda4cf5 German localization update. Thanks to ITOMIG.
SVN:trunk[923]
2010-10-25 08:19:50 +00:00
Romain Quetiez
2834c073cd #304 Fixed OQL issue (regression when using time intervals)
SVN:trunk[922]
2010-10-25 08:12:17 +00:00
Denis Flaven
97f36bd7e8 Fix to prevent IE 8 from running in IE7 compatibility mode... to be tested...
SVN:trunk[921]
2010-10-25 08:04:46 +00:00
Romain Quetiez
3d299f015b #246 Implemented the CLI mode for import.php
SVN:trunk[920]
2010-10-22 22:22:13 +00:00
Denis Flaven
8a5ed95c80 - Integrating the Brazilian transalation from Marco Tulio...
SVN:trunk[919]
2010-10-22 16:55:41 +00:00
Denis Flaven
f4b29d152b Special passthrough mode for big XML pages output.
SVN:trunk[918]
2010-10-22 16:46:53 +00:00
Denis Flaven
463bc0873b Allow n:n links to link several times to the same remote object (if "duplicates)=> true in the linkedset definition)
SVN:trunk[917]
2010-10-22 10:45:12 +00:00
Denis Flaven
368ea1cfdb SVN:trunk[916] 2010-10-22 10:42:39 +00:00
Denis Flaven
1b618ff57c (Partial) fix for Trac #285: turn email fields into click-able mailto: hyperlinks.
SVN:trunk[915]
2010-10-22 10:33:53 +00:00
Romain Quetiez
70149af5d6 Fixed a bug in the XML encoding function
SVN:trunk[914]
2010-10-22 07:47:05 +00:00
Denis Flaven
006453678b Fixed Trac#300: each iTop instance now uses its own name for the cookie storing the session ID. Therefore several instances can coexist independently on the same server.
SVN:trunk[913]
2010-10-22 06:09:51 +00:00
Denis Flaven
135353aa76 Fixed Trac #258: cleaner handling of search forms...
SVN:trunk[912]
2010-10-21 15:39:30 +00:00
Romain Quetiez
ddd52554f1 Fixed issues with the search form and context and page args... not finished
SVN:trunk[911]
2010-10-21 14:28:40 +00:00
Denis Flaven
22cf8e4986 Fixed Trac #292: enums can now be null (if allowed) !
SVN:trunk[910]
2010-10-21 09:23:41 +00:00
Denis Flaven
931075687a Fix for Trac #289: setup now checks that the "setup" directory is writeable.
SVN:trunk[909]
2010-10-20 16:20:52 +00:00
Denis Flaven
adaf3a376c Fixed Trac #258: cleanup of application context parameters.
SVN:trunk[908]
2010-10-20 15:55:30 +00:00
Romain Quetiez
1808e5fe65 Fixed #302, Ticket icons were not always displayed (+ error message on some installations)
SVN:trunk[907]
2010-10-20 14:48:45 +00:00
Romain Quetiez
cab0eb7758 #297 Fixed SOAP unit tests and improved the reporting when a reconciliation on an external key has failed
SVN:trunk[906]
2010-10-20 13:11:50 +00:00
Romain Quetiez
7e06dd100a #293 Added IP address in the lists of interfaces
SVN:trunk[905]
2010-10-20 10:35:36 +00:00
Denis Flaven
f145b872de - Implemented some hints to help users find out what are the mandatory fields for a CSV import (Trac #276). Though not 100% bullet-proof.
- Continue to implement support of hierarchical ZLists in modification forms.

SVN:trunk[904]
2010-10-19 15:03:24 +00:00
Denis Flaven
b66e489ec8 - Fixed Trac #298: use the proper content-type for CSV files to prevent the let the browser pick the most appropriate application to open such files.
SVN:trunk[903]
2010-10-19 14:15:25 +00:00
Denis Flaven
63da528882 Fixed Trac #245: Search form now retains the base class that was used when displaying the form. After searching a Person it is possible to select "Contact" and perform another search on Contacts.
SVN:trunk[902]
2010-10-19 14:07:14 +00:00
Denis Flaven
ff80b06ea2 - Fixed Trac #284: do not "consume" a transaction until it really worked.
SVN:trunk[901]
2010-10-19 13:42:54 +00:00
Denis Flaven
e9304bbcfa - new implementation (database independent) of the transaction mechanism, that prevents a user from submitting the same form twice.
SVN:trunk[900]
2010-10-19 13:20:23 +00:00
Romain Quetiez
0c34b47aac Fixed regression on the previous fix (change in the prototype of MakeQuery): this was impacting update/delete on a single object => applied to all objects of the same root class!
SVN:trunk[899]
2010-10-19 13:14:19 +00:00
Denis Flaven
484d4d1679 Fix Trac #296: Incorrect display of Service/Subcategory localized characters in the portal.
SVN:trunk[898]
2010-10-19 12:54:13 +00:00
Denis Flaven
4b5a25ea59 Fixed Trac #278: better detection of all PHP mandatory modules.
SVN:trunk[897]
2010-10-19 12:45:48 +00:00
Denis Flaven
7153162696 Fixed Trac #286: incorrect detection of HTTPS on IIS/ISAPI. Thanks to cprobst for reporting it.
SVN:trunk[896]
2010-10-19 12:34:33 +00:00
Romain Quetiez
fb59939d57 #288 Fixed OQL issues, related to multi-columns usages
SVN:trunk[895]
2010-10-14 16:56:42 +00:00
Romain Quetiez
629a87c99b #282 Fixed bug in OQL interpretation (functions requiring class aliasing)
SVN:trunk[894]
2010-10-13 19:51:36 +00:00
Romain Quetiez
3d4e0da019 Typo in dictionary (contract: start datA)
SVN:trunk[893]
2010-10-13 16:54:08 +00:00
Romain Quetiez
1e1e10aa00 #111 Improved the data loader, and added a REST service to load data from a file
SVN:trunk[892]
2010-10-13 16:36:51 +00:00
Romain Quetiez
0fff433e90 #283 Fixed issue with the default value of Enum attributes
SVN:trunk[891]
2010-10-13 08:23:05 +00:00
Romain Quetiez
05dbcb0a8b Fixed limitation: tickets named automatically even if a name is specified (attribute : ref) ; this is stopper when importing tickets from an existing workflow tool
SVN:trunk[890]
2010-10-12 16:04:09 +00:00
Romain Quetiez
bc6a3ab485 Fixed issues in DB integrity check
SVN:trunk[889]
2010-10-06 15:59:33 +00:00
2042 changed files with 411378 additions and 45502 deletions

View File

@@ -1,26 +1,26 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* UserRightsMatrix (User management Module)
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -149,6 +149,7 @@ class UserRightsMatrix extends UserRightsAddOnAPI
return ($oUser->GetKey() == 1);
}
// Deprecated - create a new module !
public function Setup()
{
// Users must be added manually
@@ -273,7 +274,7 @@ class UserRightsMatrix extends UserRightsAddOnAPI
return true;
}
public function GetSelectFilter($oUser, $sClass)
public function GetSelectFilter($oUser, $sClass, $aSettings = array())
{
$oNullFilter = new DBObjectSearch($sClass);
return $oNullFilter;

View File

@@ -1,27 +1,27 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* UserRightsNull
* User management Module - say Yeah! to everything
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class UserRightsNull extends UserRightsAddOnAPI
@@ -42,17 +42,12 @@ class UserRightsNull extends UserRightsAddOnAPI
return true;
}
public function Setup()
{
return true;
}
public function Init()
{
return true;
}
public function GetSelectFilter($oUser, $sClass)
public function GetSelectFilter($oUser, $sClass, $aSettings = array())
{
$oNullFilter = new DBObjectSearch($sClass);
return $oNullFilter;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,27 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* UserRightsProjection
* User management Module, basing the right on profiles and a matrix (similar to UserRightsProfile, but enhanced with dimensions and projection of classes and profile over the dimensions)
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
define('ADMIN_PROFILE_ID', 1);
@@ -97,7 +97,7 @@ class URP_Profiles extends UserRightsBaseClass
function DoShowGrantSumary($oPage)
{
if ($this->GetName() == "Administrator")
if ($this->GetRawName() == "Administrator")
{
// Looks dirty, but ok that's THE ONE
$oPage->p(Dict::S('UI:UserManagement:AdminProfile+'));
@@ -119,7 +119,7 @@ class URP_Profiles extends UserRightsBaseClass
$oGrant = $oUserRights->GetClassStimulusGrant($this->GetKey(), $sClass, $sStimulusCode);
if (is_object($oGrant) && ($oGrant->Get('permission') == 'yes'))
{
$aStimuli[] = '<span title="'.$sStimulusCode.': '.htmlentities($oStimulus->GetDescription()).'">'.htmlentities($oStimulus->GetLabel()).'</span>';
$aStimuli[] = '<span title="'.$sStimulusCode.': '.htmlentities($oStimulus->GetDescription(), ENT_QUOTES, 'UTF-8').'">'.htmlentities($oStimulus->GetLabel(), ENT_QUOTES, 'UTF-8').'</span>';
}
}
$sStimuli = implode(', ', $aStimuli);
@@ -658,19 +658,10 @@ class UserRightsProjection extends UserRightsAddOnAPI
// See implementation of userrightsprofile
}
public function Setup()
{
SetupProfiles::ComputeITILProfiles();
//SetupProfiles::ComputeBasicProfiles();
SetupProfiles::DoCreateDimensions();
SetupProfiles::DoCreateProfiles();
return true;
}
public function Init()
{
MetaModel::RegisterPlugin('userrights', 'ACbyProfile', array($this, 'CacheData'));
// CacheData to be invoked in a module extension
//MetaModel::RegisterPlugin('userrights', 'ACbyProfile', array($this, 'CacheData'));
}
protected $m_aDimensions = array(); // id -> object
@@ -743,7 +734,7 @@ exit;
return true;
}
public function GetSelectFilter($oUser, $sClass)
public function GetSelectFilter($oUser, $sClass, $aSettings = array())
{
$aConditions = array();
foreach ($this->m_aDimensions as $iDimension => $oDimension)
@@ -1256,343 +1247,6 @@ exit;
}
}
//
// Create simple profiles into our user management model:
// - administrator
// - readers
// - contributors
//
class SetupProfiles
{
protected static $m_aDimensions = array(
'organization' => array(
'description' => '',
'type' => 'Organization',
),
);
protected static $m_aActions = array(
UR_ACTION_READ => 'Read',
UR_ACTION_MODIFY => 'Modify',
UR_ACTION_DELETE => 'Delete',
UR_ACTION_BULK_READ => 'Bulk Read',
UR_ACTION_BULK_MODIFY => 'Bulk Modify',
UR_ACTION_BULK_DELETE => 'Bulk Delete',
);
// Note: It is possible to specify the same class in several modules
//
protected static $m_aModules = array();
protected static $m_aProfiles = array();
protected static function DoCreateClassProjection($iDimension, $sClass)
{
$oNewObj = MetaModel::NewObject("URP_ClassProjection");
$oNewObj->Set('dimensionid', $iDimension);
$oNewObj->Set('class', $sClass);
$oNewObj->Set('attribute', '');
$iId = $oNewObj->DBInsertNoReload();
return $iId;
}
protected static function DoCreateDimension($sName, $aDimensionData)
{
$oNewObj = MetaModel::NewObject("URP_Dimensions");
$oNewObj->Set('name', $sName);
$oNewObj->Set('description', $aDimensionData['description']);
$oNewObj->Set('type', $aDimensionData['type']);
$iId = $oNewObj->DBInsertNoReload();
return $iId;
}
protected static function DoCreateProfileProjection($iProfile, $iDimension)
{
$oNewObj = MetaModel::NewObject("URP_ProfileProjection");
$oNewObj->Set('profileid', $iProfile);
$oNewObj->Set('dimensionid', $iDimension);
$oNewObj->Set('value', '<any>');
$oNewObj->Set('attribute', '');
$iId = $oNewObj->DBInsertNoReload();
return $iId;
}
protected static function DoCreateActionGrant($iProfile, $iAction, $sClass, $bPermission = true)
{
$oNewObj = MetaModel::NewObject("URP_ActionGrant");
$oNewObj->Set('profileid', $iProfile);
$oNewObj->Set('permission', $bPermission ? 'yes' : 'no');
$oNewObj->Set('class', $sClass);
$oNewObj->Set('action', self::$m_aActions[$iAction]);
$iId = $oNewObj->DBInsertNoReload();
return $iId;
}
protected static function DoCreateStimulusGrant($iProfile, $sStimulusCode, $sClass)
{
$oNewObj = MetaModel::NewObject("URP_StimulusGrant");
$oNewObj->Set('profileid', $iProfile);
$oNewObj->Set('permission', 'yes');
$oNewObj->Set('class', $sClass);
$oNewObj->Set('stimulus', $sStimulusCode);
$iId = $oNewObj->DBInsertNoReload();
return $iId;
}
protected static function DoCreateAdminProfile()
{
$oNewObj = MetaModel::NewObject("URP_Profiles");
$oNewObj->Set('name', 'Administrator');
$oNewObj->Set('description', 'Has the rights on everything (bypassing any control)');
$iNewId = $oNewObj->DBInsertNoReload();
if ($iNewId != ADMIN_PROFILE_ID)
{
throw new CoreException('Admin profile could not be created with its standard id', array('requested'=>ADMIN_PROFILE_ID, 'obtained'=>$iNewId));
}
}
protected static function DoCreateOneProfile($sName, $aProfileData)
{
$sDescription = $aProfileData['description'];
if (strlen(trim($aProfileData['write_modules'])) == 0)
{
$aWriteModules = array();
}
else
{
$aWriteModules = explode(',', trim($aProfileData['write_modules']));
}
$aStimuli = $aProfileData['stimuli'];
$oNewObj = MetaModel::NewObject("URP_Profiles");
$oNewObj->Set('name', $sName);
$oNewObj->Set('description', $sDescription);
$iProfile = $oNewObj->DBInsertNoReload();
// Project in every dimension
//
$oDimensionSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_Dimensions"));
while ($oDimension = $oDimensionSet->Fetch())
{
$iDimension = $oDimension->GetKey();
self::DoCreateProfileProjection($iProfile, $iDimension);
}
// Grant read rights for everything
//
foreach (MetaModel::GetClasses('bizmodel') as $sClass)
{
// Skip non instantiable classes
if (MetaModel::IsAbstract($sClass)) continue;
self::DoCreateActionGrant($iProfile, UR_ACTION_READ, $sClass);
self::DoCreateActionGrant($iProfile, UR_ACTION_BULK_READ, $sClass);
}
// Grant write for given modules
// Start by compiling the information, because some modules may overlap
$aWriteableClasses = array();
foreach ($aWriteModules as $sModule)
{
//$oPage->p('Granting write access for the module"'.$sModule.'" - '.count(self::$m_aModules[$sModule]).' classes');
foreach (self::$m_aModules[$sModule] as $sClass)
{
$aWriteableClasses[$sClass] = true;
}
}
foreach ($aWriteableClasses as $sClass => $foo)
{
// Skip non instantiable classes
if (MetaModel::IsAbstract($sClass)) continue;
if (!MetaModel::IsValidClass($sClass))
{
throw new CoreException("Invalid class name '$sClass'");
}
self::DoCreateActionGrant($iProfile, UR_ACTION_MODIFY, $sClass);
self::DoCreateActionGrant($iProfile, UR_ACTION_DELETE, $sClass);
self::DoCreateActionGrant($iProfile, UR_ACTION_BULK_MODIFY, $sClass);
// By default, do not allow bulk deletion operations for standard users
// self::DoCreateActionGrant($iProfile, UR_ACTION_BULK_DELETE, $sClass);
}
// Grant stimuli for given classes
foreach ($aStimuli as $sClass => $sAllowedStimuli)
{
if (!MetaModel::IsValidClass($sClass))
{
// Could be a class defined in a module that wasn't installed
continue;
//throw new CoreException("Invalid class name '$sClass'");
}
if ($sAllowedStimuli == 'any')
{
$aAllowedStimuli = array_keys(MetaModel::EnumStimuli($sClass));
}
elseif ($sAllowedStimuli == 'none')
{
$aAllowedStimuli = array();
}
else
{
$aAllowedStimuli = explode(',', $sAllowedStimuli);
}
foreach ($aAllowedStimuli as $sStimulusCode)
{
self::DoCreateStimulusGrant($iProfile, $sStimulusCode, $sClass);
}
}
}
public static function DoCreateDimensions()
{
$aClass = MetaModel::GetClasses();
foreach(self::$m_aDimensions as $sName => $aDimensionData)
{
$iDimension = self::DoCreateDimension($sName, $aDimensionData);
foreach($aClass as $sClass)
{
// Skip non instantiable classes
if (MetaModel::IsAbstract($sClass)) continue;
if (!MetaModel::IsValidClass($sClass))
{
throw new CoreException("Invalid class name '$sClass'");
}
self::DoCreateClassProjection($iDimension, $sClass);
}
}
}
public static function DoCreateProfiles()
{
self::DoCreateAdminProfile();
foreach(self::$m_aProfiles as $sName => $aProfileData)
{
self::DoCreateOneProfile($sName, $aProfileData);
}
}
public static function ComputeBasicProfiles()
{
// In this profiling scheme, one single module represents all the classes
//
self::$m_aModules = array(
'UserData' => MetaModel::GetClasses('bizmodel'),
);
self::$m_aProfiles = array(
'Reader' => array(
'description' => 'Person having a ready-only access to the data',
'write_modules' => '',
'stimuli' => array(
),
),
'Writer' => array(
'description' => 'Contributor to the contents (read + write access)',
'write_modules' => 'UserData',
'stimuli' => array(
// any class => 'any'
),
),
);
}
public static function ComputeITILProfiles()
{
// In this profiling scheme, modules are based on ITIL recommendations
//
self::$m_aModules = array(
/*
'WriteModule' => array(
'someclass',
'anotherclass',
),
*/
'General' => MetaModel::GetClasses('structure'),
'Documentation' => MetaModel::GetClasses('documentation'),
'Configuration' => MetaModel::GetClasses('configmgmt'),
'Incident' => MetaModel::GetClasses('incidentmgmt'),
'Problem' => MetaModel::GetClasses('problemmgmt'),
'Change' => MetaModel::GetClasses('changemgmt'),
'Service' => MetaModel::GetClasses('servicemgmt'),
'Call' => MetaModel::GetClasses('requestmgmt'),
'KnownError' => MetaModel::GetClasses('knownerrormgmt'),
);
self::$m_aProfiles = array(
'Configuration Manager' => array(
'description' => 'Person in charge of the documentation of the managed CIs',
'write_modules' => 'General,Documentation,Configuration',
'stimuli' => array(
//'bizServer' => 'none',
//'bizContract' => 'none',
//'bizIncidentTicket' => 'none',
//'bizChangeTicket' => 'any',
),
),
'Service Desk Agent' => array(
'description' => 'Person in charge of creating incident reports',
'write_modules' => 'Incident,Call',
'stimuli' => array(
'Incident' => 'ev_assign',
'UserRequest' => 'ev_assign',
),
),
'Support Agent' => array(
'description' => 'Person analyzing and solving the current incidents or problems',
'write_modules' => 'Incident,Problem,KnownError',
'stimuli' => array(
'Incident' => 'ev_assign,ev_reassign,ev_resolve,ev_close',
'UserRequest' => 'ev_assign,ev_reassign,ev_resolve,ev_close,ev_freeze',
),
),
'Change Implementor' => array(
'description' => 'Person executing the changes',
'write_modules' => 'Change',
'stimuli' => array(
'NormalChange' => 'ev_plan,ev_replan,ev_implement,ev_monitor',
'EmergencyChange' => 'ev_plan,ev_replan,ev_implement,ev_monitor',
'RoutineChange' => 'ev_plan,ev_replan,ev_implement,ev_monitor',
),
),
'Change Supervisor' => array(
'description' => 'Person responsible for the overall change execution',
'write_modules' => 'Change',
'stimuli' => array(
'NormalChange' => 'ev_validate,ev_reject,ev_assign,ev_reopen,ev_finish',
'EmergencyChange' => 'ev_assign,ev_reopen,ev_finish',
'RoutineChange' => 'ev_assign,ev_reopen,ev_finish',
),
),
'Change Approver' => array(
'description' => 'Person who could be impacted by some changes',
'write_modules' => 'Change',
'stimuli' => array(
'NormalChange' => 'ev_approve,ev_notapprove',
'EmergencyChange' => 'ev_approve,ev_notapprove',
'RoutineChange' => 'none',
),
),
'Service Manager' => array(
'description' => 'Person responsible for the service delivered to the [internal] customer',
'write_modules' => 'Service',
'stimuli' => array(
),
),
'Document author' => array(
'description' => 'Any person who could contribute to documentation',
'write_modules' => 'Documentation',
'stimuli' => array(
),
),
);
}
}
UserRights::SelectModule('UserRightsProjection');

View File

@@ -1,38 +1,40 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2015 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Simple web page with no includes, header or fancy formatting, useful to
* generate HTML fragments when called by an AJAX method
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once("../application/webpage.class.inc.php");
require_once(APPROOT."/application/webpage.class.inc.php");
class ajax_page extends WebPage
class ajax_page extends WebPage implements iTabbedPage
{
/**
* Jquery style ready script
* @var Hash
*/
protected $m_sReadyScript;
protected $m_oTabs;
private $m_sMenu; // If set, then the menu will be updated
/**
* constructor for the web page
@@ -40,35 +42,248 @@ class ajax_page extends WebPage
*/
function __construct($s_title)
{
parent::__construct($s_title);
$sPrintable = utils::ReadParam('printable', '0');
$bPrintable = ($sPrintable == '1');
parent::__construct($s_title, $bPrintable);
$this->m_sReadyScript = "";
$this->add_header("Content-type: text/html; charset=utf-8");
//$this->add_header("Content-type: text/html; charset=utf-8");
$this->add_header("Cache-control: no-cache");
$this->m_oTabs = new TabManager();
$this->sContentType = 'text/html';
$this->sContentDisposition = 'inline';
$this->m_sMenu = "";
}
public function AddTabContainer($sTabContainer, $sPrefix = '')
{
$this->add($this->m_oTabs->AddTabContainer($sTabContainer, $sPrefix));
}
public function AddToTab($sTabContainer, $sTabLabel, $sHtml)
{
$this->add($this->m_oTabs->AddToTab($sTabContainer, $sTabLabel, $sHtml));
}
public function SetCurrentTabContainer($sTabContainer = '')
{
return $this->m_oTabs->SetCurrentTabContainer($sTabContainer);
}
public function SetCurrentTab($sTabLabel = '')
{
return $this->m_oTabs->SetCurrentTab($sTabLabel);
}
/**
* Add a tab which content will be loaded asynchronously via the supplied URL
*
* Limitations:
* Cross site scripting is not not allowed for security reasons. Use a normal tab with an IFRAME if you want to pull content from another server.
* Static content cannot be added inside such tabs.
*
* @param string $sTabLabel The (localised) label of the tab
* @param string $sUrl The URL to load (on the same server)
* @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. flase will cause the tab to be reloaded upon each activation.
* @since 2.0.3
*/
public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true)
{
$this->add($this->m_oTabs->AddAjaxTab($sTabLabel, $sUrl, $bCache));
}
public function GetCurrentTab()
{
return $this->m_oTabs->GetCurrentTab();
}
public function RemoveTab($sTabLabel, $sTabContainer = null)
{
$this->m_oTabs->RemoveTab($sTabLabel, $sTabContainer);
}
/**
* Finds the tab whose title matches a given pattern
* @return mixed The name of the tab as a string or false if not found
*/
public function FindTab($sPattern, $sTabContainer = null)
{
return $this->m_oTabs->FindTab($sPattern, $sTabContainer);
}
/**
* Make the given tab the active one, as if it were clicked
* DOES NOT WORK: apparently in the *old* version of jquery
* that we are using this is not supported... TO DO upgrade
* the whole jquery bundle...
*/
public function SelectTab($sTabContainer, $sTabLabel)
{
$this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabLabel));
}
public function AddToMenu($sHtml)
{
$this->m_sMenu .= $sHtml;
}
/**
* Echoes the content of the whole page
* @return void
*/
public function output()
{
if (!empty($this->sContentType))
{
$this->add_header('Content-type: '.$this->sContentType);
}
if (!empty($this->sContentDisposition))
{
$this->add_header('Content-Disposition: '.$this->sContentDisposition.'; filename="'.$this->sContentFileName.'"');
}
foreach($this->a_headers as $s_header)
{
header($s_header);
}
$s_captured_output = ob_get_contents();
ob_end_clean();
echo $this->s_content;
if ($this->m_oTabs->TabsContainerCount() > 0)
{
$this->add_ready_script(
<<<EOF
// The "tab widgets" to handle.
var tabs = $('div[id^=tabbedContent]');
// Ugly patch for a change in the behavior of jQuery UI:
// Before jQuery UI 1.9, tabs were always considered as "local" (opposed to Ajax)
// when their href was beginning by #. Starting with 1.9, a <base> tag in the page
// is taken into account and causes "local" tabs to be considered as Ajax
// unless their URL is equal to the URL of the page...
if ($('base').length > 0)
{
$('div[id^=tabbedContent] > ul > li > a').each(function() {
var sHash = location.hash;
var sCleanLocation = location.href.toString().replace(sHash, '').replace(/#$/, '');
$(this).attr("href", sCleanLocation+$(this).attr("href"));
});
}
if ($.bbq)
{
// This selector will be reused when selecting actual tab widget A elements.
var tab_a_selector = 'ul.ui-tabs-nav a';
// Enable tabs on all tab widgets. The `event` property must be overridden so
// that the tabs aren't changed on click, and any custom event name can be
// specified. Note that if you define a callback for the 'select' event, it
// will be executed for the selected tab whenever the hash changes.
tabs.tabs({ event: 'change' });
// Define our own click handler for the tabs, overriding the default.
tabs.find( tab_a_selector ).click(function()
{
var state = {};
// Get the id of this tab widget.
var id = $(this).closest( 'div[id^=tabbedContent]' ).attr( 'id' );
// Get the index of this tab.
var idx = $(this).parent().prevAll().length;
// Set the state!
state[ id ] = idx;
$.bbq.pushState( state );
});
}
else
{
tabs.tabs();
}
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)) )
{
$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
});
EOF
);
}
$s_captured_output = $this->ob_get_clean_safe();
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
{
// inline content != attachment && html => filter all scripts for malicious XSS scripts
echo self::FilterXSS($this->s_content);
}
else
{
echo $this->s_content;
}
if (!empty($this->m_sMenu))
{
$uid = time();
echo "<div id=\"accordion_temp_$uid\">\n";
echo "<div id=\"accordion\">\n";
echo "<!-- Beginning of the accordion menu -->\n";
echo self::FilterXSS($this->m_sMenu);
echo "<!-- End of the accordion menu-->\n";
echo "</div>\n";
echo "</div>\n";
echo "<script type=\"text/javascript\">\n";
echo "$('#inner_menu').html($('#accordion_temp_$uid').html());\n";
echo "$('#accordion_temp_$uid').remove();\n";
echo "$('#accordion').accordion({ header: 'h3', navigation: true, autoHeight: false, collapsible: false, icons: false });\n";
echo "\n</script>\n";
}
//echo $this->s_deferred_content;
if (count($this->a_scripts) > 0)
{
echo "<script type=\"text/javascript\">\n";
echo implode("\n", $this->a_scripts);
echo "\n</script>\n";
}
if (!empty($this->s_deferred_content))
{
echo "<script type=\"text/javascript\">\n";
echo "\$('body').append('".addslashes(str_replace("\n", '', $this->s_deferred_content))."');\n";
echo "\n</script>\n";
}
if (!empty($this->m_sReadyScript))
{
echo "<script>\n";
echo "<script type=\"text/javascript\">\n";
echo $this->m_sReadyScript; // Ready Scripts are output as simple scripts
echo "</script>\n";
echo "\n</script>\n";
}
if (trim($s_captured_output) != "")
{
echo $s_captured_output;
echo self::FilterXSS($s_captured_output);
}
if (class_exists('DBSearch'))
{
DBSearch::RecordQueryTrace();
}
}
@@ -81,6 +296,78 @@ class ajax_page extends WebPage
public function small_p($sText)
{
}
public function add($sHtml)
{
if (($this->m_oTabs->GetCurrentTabContainer() != '') && ($this->m_oTabs->GetCurrentTab() != ''))
{
$this->m_oTabs->AddToTab($this->m_oTabs->GetCurrentTabContainer(), $this->m_oTabs->GetCurrentTab(), $sHtml);
}
else
{
parent::add($sHtml);
}
}
/**
* Records the current state of the 'html' part of the page output
* @return mixed The current state of the 'html' output
*/
public function start_capture()
{
$sCurrentTabContainer = $this->m_oTabs->GetCurrentTabContainer();
$sCurrentTab = $this->m_oTabs->GetCurrentTab();
if (!empty($sCurrentTabContainer) && !empty($sCurrentTab))
{
$iOffset = $this->m_oTabs->GetCurrentTabLength();
return array('tc' => $sCurrentTabContainer, 'tab' => $sCurrentTab, 'offset' => $iOffset);
}
else
{
return parent::start_capture();
}
}
/**
* Returns the part of the html output that occurred since the call to start_capture
* and removes this part from the current html output
* @param $offset mixed The value returned by start_capture
* @return string The part of the html output that was added since the call to start_capture
*/
public function end_capture($offset)
{
if (is_array($offset))
{
if ($this->m_oTabs->TabExists($offset['tc'], $offset['tab']))
{
$sCaptured = $this->m_oTabs->TruncateTab($offset['tc'], $offset['tab'], $offset['offset']);
}
else
{
$sCaptured = '';
}
}
else
{
$sCaptured = parent::end_capture($offset);
}
return $sCaptured;
}
/**
* Add any text or HTML fragment (identified by an ID) at the end of the body of the page
* This is useful to add hidden content, DIVs or FORMs that should not
* be embedded into each other.
*/
public function add_at_the_end($s_html, $sId = '')
{
if ($sId != '')
{
$this->add_script("$('#{$sId}').remove();"); // Remove any previous instance of the same Id
}
$this->s_deferred_content .= $s_html;
}
/**
* Adds a script to be executed when the DOM is ready (typical JQuery use)
@@ -89,11 +376,22 @@ class ajax_page extends WebPage
*/
public function add_ready_script($sScript)
{
// Does nothing in ajax rendered content.. for now...
// Maybe we should add this as a simple <script> tag at the end of the output
// considering that at this time everything in the page is "ready"...
$this->m_sReadyScript .= $sScript;
$this->m_sReadyScript .= $sScript."\n";
}
/**
* Cannot be called in this context, since Ajax pages do not share
* any context with the calling page !!
*/
public function GetUniqueId()
{
assert(false);
return 0;
}
public static function FilterXSS($sHTML)
{
return str_ireplace(array('<script', '</script>'), array('<!-- <removed-script', '</removed-script> -->'), $sHTML);
}
}
?>

View File

@@ -1,35 +1,39 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Includes all the classes to have the application up and running
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('../application/applicationcontext.class.inc.php');
require_once('../application/cmdbabstract.class.inc.php');
require_once('../application/displayblock.class.inc.php');
require_once('../application/audit.category.class.inc.php');
require_once('../application/audit.rule.class.inc.php');
//require_once('../application/menunode.class.inc.php');
require_once('../application/utils.inc.php');
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');
require_once(APPROOT.'/setup/moduleinstallation.class.inc.php');
//require_once(APPROOT.'/application/menunode.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
class ApplicationException extends CoreException
{

View File

@@ -1,29 +1,67 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class ApplicationContext
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once("../application/utils.inc.php");
require_once(APPROOT."/application/utils.inc.php");
/**
* Interface for directing end-users to the relevant application
*/
interface iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId);
}
/**
* Direct end-users to the standard iTop application: UI.php
*/
class iTopStandardURLMaker implements iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId)
{
$sPage = DBObject::ComputeStandardUIPage($sClass);
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
$sUrl = "{$sAbsoluteUrl}pages/$sPage?operation=details&class=$sClass&id=$iId";
return $sUrl;
}
}
/**
* Direct end-users to the standard Portal application
*/
class PortalURLMaker implements iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId)
{
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
$sUrl = "{$sAbsoluteUrl}portal/index.php?operation=details&class=$sClass&id=$iId";
return $sUrl;
}
}
/**
* Helper class to store and manipulate the parameters that make the application's context
*
@@ -40,12 +78,16 @@ class ApplicationContext
protected $aValues;
protected static $aDefaultValues; // Cache shared among all instances
public function __construct()
public function __construct($bReadContext = true)
{
$this->aNames = array(
'org_id', 'menu'
);
$this->ReadContext();
if ($bReadContext)
{
$this->ReadContext();
}
}
/**
@@ -54,22 +96,56 @@ class ApplicationContext
*/
protected function ReadContext()
{
if (empty(self::$aDefaultValues))
if (!isset(self::$aDefaultValues))
{
self::$aDefaultValues = array();
$aContext = utils::ReadParam('c', array(), false, 'context_param');
foreach($this->aNames as $sName)
{
$sValue = utils::ReadParam($sName, '');
$sValue = isset($aContext[$sName]) ? $aContext[$sName] : '';
// TO DO: check if some of the context parameters are mandatory (or have default values)
if (!empty($sValue))
{
self::$aDefaultValues[$sName] = $sValue;
}
// Hmm, there must be a better (more generic) way to handle the case below:
// When there is only one possible (allowed) organization, the context must be
// fixed to this org
if ($sName == 'org_id')
{
if (MetaModel::IsValidClass('Organization'))
{
$oSearchFilter = new DBObjectSearch('Organization');
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oSet = new CMDBObjectSet($oSearchFilter);
$iCount = $oSet->Count();
if ($iCount == 1)
{
// Only one possible value for org_id, set it in the context
$oOrg = $oSet->Fetch();
self::$aDefaultValues[$sName] = $oOrg->GetKey();
}
}
}
}
}
$this->aValues = self::$aDefaultValues;
}
/**
* Returns the current value for the given parameter
* @param string $sParamName Name of the parameter to read
* @return mixed The value for this parameter
*/
public function GetCurrentValue($sParamName, $defaultValue = '')
{
if (isset($this->aValues[$sParamName]))
{
return $this->aValues[$sParamName];
}
return $defaultValue;
}
/**
* Returns the context as string with the format name1=value1&name2=value2....
* return string The context as a string to be appended to an href property
@@ -79,7 +155,7 @@ class ApplicationContext
$aParams = array();
foreach($this->aValues as $sName => $sValue)
{
$aParams[] = $sName.'='.urlencode($sValue);
$aParams[] = "c[$sName]".'='.urlencode($sValue);
}
return implode("&", $aParams);
}
@@ -93,7 +169,7 @@ class ApplicationContext
$sContext = "";
foreach($this->aValues as $sName => $sValue)
{
$sContext .= "<input type=\"hidden\" name=\"$sName\" value=\"$sValue\" />\n";
$sContext .= "<input type=\"hidden\" name=\"c[$sName]\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n";
}
return $sContext;
}
@@ -104,7 +180,12 @@ class ApplicationContext
*/
public function GetAsHash()
{
return $this->aValues;
$aReturn = array();
foreach($this->aValues as $sName => $sValue)
{
$aReturn["c[$sName]"] = $sValue;
}
return $aReturn;
}
/**
@@ -128,5 +209,154 @@ class ApplicationContext
unset($this->aValues[$sParamName]);
}
}
/**
* Initializes the given object with the default values provided by the context
*/
public function InitObjectFromContext(DBObject &$oObj)
{
$sClass = get_class($oObj);
foreach($this->GetNames() as $key)
{
$aCallSpec = array($sClass, 'MapContextParam');
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
if (MetaModel::IsValidAttCode($sClass, $sAttCode))
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->IsWritable())
{
$value = $this->GetCurrentValue($key, null);
if (!is_null($value))
{
$oObj->Set($sAttCode, $value);
}
}
}
}
}
}
static $m_sUrlMakerClass = null;
/**
* Set the current application url provider
* @param sClass string Class implementing iDBObjectURLMaker
* @return void
*/
public static function SetUrlMakerClass($sClass = 'iTopStandardURLMaker')
{
$sPrevious = self::GetUrlMakerClass();
self::$m_sUrlMakerClass = $sClass;
$_SESSION['UrlMakerClass'] = $sClass;
return $sPrevious;
}
/**
* Get the current application url provider
* @return string the name of the class
*/
public static function GetUrlMakerClass()
{
if (is_null(self::$m_sUrlMakerClass))
{
if (isset($_SESSION['UrlMakerClass']))
{
self::$m_sUrlMakerClass = $_SESSION['UrlMakerClass'];
}
else
{
self::$m_sUrlMakerClass = 'iTopStandardURLMaker';
}
}
return self::$m_sUrlMakerClass;
}
/**
* Get the current application url provider
* @return string the name of the class
*/
public static function MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass = null, $bWithNavigationContext = true)
{
$oAppContext = new ApplicationContext();
if (is_null($sUrlMakerClass))
{
$sUrlMakerClass = self::GetUrlMakerClass();
}
$sUrl = call_user_func(array($sUrlMakerClass, 'MakeObjectUrl'), $sObjClass, $sObjKey);
if (strlen($sUrl) > 0)
{
if ($bWithNavigationContext)
{
return $sUrl."&".$oAppContext->GetForLink();
}
else
{
return $sUrl;
}
}
else
{
return '';
}
}
protected static $m_aPluginProperties = null;
/**
* Load plugin properties for the current session
* @return void
*/
protected static function LoadPluginProperties()
{
if (isset($_SESSION['PluginProperties']))
{
self::$m_aPluginProperties = $_SESSION['PluginProperties'];
}
else
{
self::$m_aPluginProperties = array();
}
}
/**
* Set plugin properties
* @param sPluginClass string Class implementing any plugin interface
* @param sProperty string Name of the property
* @param value scalar Value (numeric or string)
* @return void
*/
public static function SetPluginProperty($sPluginClass, $sProperty, $value)
{
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
self::$m_aPluginProperties[$sPluginClass][$sProperty] = $value;
$_SESSION['PluginProperties'][$sPluginClass][$sProperty] = $value;
}
/**
* Get plugin properties
* @param sPluginClass string Class implementing any plugin interface
* @return array of sProperty=>value pairs
*/
public static function GetPluginProperties($sPluginClass)
{
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
if (array_key_exists($sPluginClass, self::$m_aPluginProperties))
{
return self::$m_aPluginProperties[$sPluginClass];
}
else
{
return array();
}
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -1,31 +1,32 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* This class manages the audit "categories". Each category defines a set of objects
* to check and is linked to a set of rules that determine the valid or invalid objects
* inside the set
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('../application/cmdbabstract.class.inc.php');
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
class AuditCategory extends cmdbAbstractObject
{
@@ -46,7 +47,7 @@ class AuditCategory extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeOQL("definition_set", array("allowed_values"=>null, "sql"=>"definition_set", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSet("rules_list", array("linked_class"=>"AuditRule", "ext_key_to_me"=>"category_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSet("rules_list", array("linked_class"=>"AuditRule", "ext_key_to_me"=>"category_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array(), "edit_mode" => LINKSET_EDITMODE_INPLACE, "tracking_level" => LINKSET_TRACKING_ALL)));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'rules_list')); // Attributes to be displayed for the complete details

View File

@@ -1,18 +1,21 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* This class manages the audit "rule" linked to a given audit category.
@@ -20,13 +23,11 @@
* or the "bad" ones. The core audit engines computes the complement to the definition
* set when needed to obtain either the valid objects, or the ones with an error
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('../application/audit.category.class.inc.php');
require_once(APPROOT.'/application/audit.category.class.inc.php');
class AuditRule extends cmdbAbstractObject
{

View File

@@ -0,0 +1,97 @@
<?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/>
/**
* CLI page
* The page adds the content-type text/XML and the encoding into the headers
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT."/application/webpage.class.inc.php");
class CLIPage implements Page
{
function __construct($s_title)
{
}
public function output()
{
if (class_exists('DBSearch'))
{
DBSearch::RecordQueryTrace();
}
if (class_exists('ExecutionKPI'))
{
ExecutionKPI::ReportStats();
}
}
public function add($sText)
{
echo $sText;
}
public function p($sText)
{
echo $sText."\n";
}
public function pre($sText)
{
echo $sText."\n";
}
public function add_comment($sText)
{
echo "#".$sText."\n";
}
public function table($aConfig, $aData, $aParams = array())
{
$aCells = array();
foreach($aConfig as $sName=>$aDef)
{
if (strlen($aDef['description']) > 0)
{
$aCells[] = $aDef['label'].' ('.$aDef['description'].')';
}
else
{
$aCells[] = $aDef['label'];
}
}
echo implode(';', $aCells)."\n";
foreach($aData as $aRow)
{
$aCells = array();
foreach($aConfig as $sName=>$aAttribs)
{
$sValue = $aRow["$sName"];
$aCells[] = $sValue;
}
echo implode(';', $aCells)."\n";
}
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -1,48 +1,64 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2015 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Simple web page with no includes or fancy formatting, useful to generateXML documents
* The page adds the content-type text/XML and the encoding into the headers
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once("../application/webpage.class.inc.php");
require_once(APPROOT."/application/webpage.class.inc.php");
class CSVPage extends WebPage
{
function __construct($s_title)
{
parent::__construct($s_title);
$this->add_header("Content-type: text/html; charset=iso-8859-1");
$this->add_header("Content-type: text/plain; charset=utf-8");
$this->add_header("Cache-control: no-cache");
//$this->add_header("Content-Transfer-Encoding: binary");
}
public function output()
{
$this->add_header("Content-Length: ".strlen(trim($this->s_content)));
$this->add_header("Content-Length: ".strlen(trim($this->s_content)));
// Get the unexpected output but do nothing with it
$sTrash = $this->ob_get_clean_safe();
foreach($this->a_headers as $s_header)
{
header($s_header);
}
echo trim($this->s_content);
echo "\n";
if (class_exists('DBSearch'))
{
DBSearch::RecordQueryTrace();
}
if (class_exists('ExecutionKPI'))
{
ExecutionKPI::ReportStats();
}
}
public function small_p($sText)
@@ -93,4 +109,3 @@ class CSVPage extends WebPage
}
}
?>

View File

@@ -0,0 +1,874 @@
<?php
// Copyright (C) 2010-2013 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
require_once(APPROOT.'application/dashboardlayout.class.inc.php');
require_once(APPROOT.'application/dashlet.class.inc.php');
require_once(APPROOT.'core/modelreflection.class.inc.php');
/**
* A user editable dashboard page
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class Dashboard
{
protected $sTitle;
protected $bAutoReload;
protected $iAutoReloadSec;
protected $sLayoutClass;
protected $aWidgetsData;
protected $oDOMNode;
protected $sId;
protected $aCells;
protected $oMetaModel;
public function __construct($sId)
{
$this->sTitle = '';
$this->sLayoutClass = 'DashboardLayoutOneCol';
$this->bAutoReload = false;
$this->iAutoReloadSec = MetaModel::GetConfig()->GetStandardReloadInterval();
$this->aCells = array();
$this->oDOMNode = null;
$this->sId = $sId;
}
public function FromXml($sXml)
{
$this->aCells = array(); // reset the content of the dashboard
set_error_handler(array('Dashboard', 'ErrorHandler'));
$oDoc = new DOMDocument();
$oDoc->loadXML($sXml);
restore_error_handler();
$this->FromDOMDocument($oDoc);
}
public function FromDOMDocument(DOMDocument $oDoc)
{
$this->oDOMNode = $oDoc->getElementsByTagName('dashboard')->item(0);
if ($oLayoutNode = $this->oDOMNode->getElementsByTagName('layout')->item(0))
{
$this->sLayoutClass = $oLayoutNode->textContent;
}
else
{
$this->sLayoutClass = 'DashboardLayoutOneCol';
}
if ($oTitleNode = $this->oDOMNode->getElementsByTagName('title')->item(0))
{
$this->sTitle = $oTitleNode->textContent;
}
else
{
$this->sTitle = '';
}
$this->bAutoReload = false;
$this->iAutoReloadSec = MetaModel::GetConfig()->GetStandardReloadInterval();
if ($oAutoReloadNode = $this->oDOMNode->getElementsByTagName('auto_reload')->item(0))
{
if ($oAutoReloadEnabled = $oAutoReloadNode->getElementsByTagName('enabled')->item(0))
{
$this->bAutoReload = ($oAutoReloadEnabled->textContent == 'true');
}
if ($oAutoReloadInterval = $oAutoReloadNode->getElementsByTagName('interval')->item(0))
{
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$oAutoReloadInterval->textContent);
}
}
if ($oCellsNode = $this->oDOMNode->getElementsByTagName('cells')->item(0))
{
$oCellsList = $oCellsNode->getElementsByTagName('cell');
$aCellOrder = array();
$iCellRank = 0;
foreach($oCellsList as $oCellNode)
{
$aDashletList = array();
$oCellRank = $oCellNode->getElementsByTagName('rank')->item(0);
if ($oCellRank)
{
$iCellRank = (float)$oCellRank->textContent;
}
$oDashletsNode = $oCellNode->getElementsByTagName('dashlets')->item(0);
{
$oDashletList = $oDashletsNode->getElementsByTagName('dashlet');
$iRank = 0;
$aDashletOrder = array();
foreach($oDashletList as $oDomNode)
{
$sDashletClass = $oDomNode->getAttribute('xsi:type');
$oRank = $oDomNode->getElementsByTagName('rank')->item(0);
if ($oRank)
{
$iRank = (float)$oRank->textContent;
}
$sId = $oDomNode->getAttribute('id');
$oNewDashlet = new $sDashletClass($this->oMetaModel, $sId);
$oNewDashlet->FromDOMNode($oDomNode);
$aDashletOrder[] = array('rank' => $iRank, 'dashlet' => $oNewDashlet);
}
usort($aDashletOrder, array(get_class($this), 'SortOnRank'));
$aDashletList = array();
foreach($aDashletOrder as $aItem)
{
$aDashletList[] = $aItem['dashlet'];
}
$aCellOrder[] = array('rank' => $iCellRank, 'dashlets' => $aDashletList);
}
}
usort($aCellOrder, array(get_class($this), 'SortOnRank'));
foreach($aCellOrder as $aItem)
{
$this->aCells[] = $aItem['dashlets'];
}
}
else
{
$this->aCells = array();
}
}
static function SortOnRank($aItem1, $aItem2)
{
return ($aItem1['rank'] > $aItem2['rank']) ? +1 : -1;
}
/**
* Error handler to turn XML loading warnings into exceptions
*/
public static function ErrorHandler($errno, $errstr, $errfile, $errline)
{
if ($errno == E_WARNING && (substr_count($errstr,"DOMDocument::loadXML()")>0))
{
throw new DOMException($errstr);
}
else
{
return false;
}
}
public function ToXml()
{
$oDoc = new DOMDocument();
$oDoc->formatOutput = true; // indent (must be loaded with option LIBXML_NOBLANKS)
$oDoc->preserveWhiteSpace = true; // otherwise the formatOutput option would have no effect
$oMainNode = $oDoc->createElement('dashboard');
$oMainNode->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance");
$oDoc->appendChild($oMainNode);
$this->ToDOMNode($oMainNode);
$sXml = $oDoc->saveXML();
return $sXml;
}
public function ToDOMNode($oDefinition)
{
$oDoc = $oDefinition->ownerDocument;
$oNode = $oDoc->createElement('layout', $this->sLayoutClass);
$oDefinition->appendChild($oNode);
$oNode = $oDoc->createElement('title', $this->sTitle);
$oDefinition->appendChild($oNode);
$oAutoReloadNode = $oDoc->createElement('auto_reload');
$oDefinition->appendChild($oAutoReloadNode);
$oNode = $oDoc->createElement('enabled', $this->bAutoReload ? 'true' : 'false');
$oAutoReloadNode->appendChild($oNode);
$oNode = $oDoc->createElement('interval', $this->iAutoReloadSec);
$oAutoReloadNode->appendChild($oNode);
$oCellsNode = $oDoc->createElement('cells');
$oDefinition->appendChild($oCellsNode);
$iCellRank = 0;
foreach ($this->aCells as $aCell)
{
$oCellNode = $oDoc->createElement('cell');
$oCellNode->setAttribute('id', $iCellRank);
$oCellsNode->appendChild($oCellNode);
$oCellRank = $oDoc->createElement('rank', $iCellRank);
$oCellNode->appendChild($oCellRank);
$iCellRank++;
$iDashletRank = 0;
$oDashletsNode = $oDoc->createElement('dashlets');
$oCellNode->appendChild($oDashletsNode);
foreach ($aCell as $oDashlet)
{
$oNode = $oDoc->createElement('dashlet');
$oDashletsNode->appendChild($oNode);
$oNode->setAttribute('id', $oDashlet->GetID());
$oNode->setAttribute('xsi:type', get_class($oDashlet));
$oDashletRank = $oDoc->createElement('rank', $iDashletRank);
$oNode->appendChild($oDashletRank);
$iDashletRank++;
$oDashlet->ToDOMNode($oNode);
}
}
}
public function FromParams($aParams)
{
$this->sLayoutClass = $aParams['layout_class'];
$this->sTitle = $aParams['title'];
$this->bAutoReload = $aParams['auto_reload'] == 'true';
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int) $aParams['auto_reload_sec']);
foreach($aParams['cells'] as $aCell)
{
$aCellDashlets = array();
foreach($aCell as $aDashletParams)
{
$sDashletClass = $aDashletParams['dashlet_class'];
$sId = $aDashletParams['dashlet_id'];
$oNewDashlet = new $sDashletClass($this->oMetaModel, $sId);
$oForm = $oNewDashlet->GetForm();
$oForm->SetParamsContainer($sId);
$oForm->SetPrefix('');
$aValues = $oForm->ReadParams();
$oNewDashlet->FromParams($aValues);
$aCellDashlets[] = $oNewDashlet;
}
$this->aCells[] = $aCellDashlets;
}
}
public function Save()
{
}
public function GetLayout()
{
return $this->sLayoutClass;
}
public function SetLayout($sLayoutClass)
{
$this->sLayoutClass = $sLayoutClass;
}
public function GetTitle()
{
return $this->sTitle;
}
public function SetTitle($sTitle)
{
$this->sTitle = $sTitle;
}
public function GetAutoReload()
{
return $this->bAutoReload;
}
public function SetAutoReload($bAutoReload)
{
$this->bAutoReload = $bAutoReload;
}
public function GetAutoReloadInterval()
{
return $this->iAutoReloadSec;
}
public function SetAutoReloadInterval($iAutoReloadSec)
{
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$iAutoReloadSec);
}
public function AddDashlet($oDashlet)
{
$sId = $this->GetNewDashletId();
$oDashlet->SetId($sId);
$this->aCells[] = array($oDashlet);
}
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
$oPage->add('<h1>'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</h1>');
$oLayout = new $this->sLayoutClass;
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
if (!$bEditMode)
{
$oPage->add_linked_script('../js/dashlet.js');
$oPage->add_linked_script('../js/dashboard.js');
}
}
public function RenderProperties($oPage)
{
// menu to pick a layout and edit other properties of the dashboard
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:Properties').'</div>');
$sUrl = utils::GetAbsoluteUrlAppRoot();
$oPage->add('<div style="text-align:center">'.Dict::S('UI:DashboardEdit:Layout').'</div>');
$oPage->add('<div id="select_layout" style="text-align:center">');
foreach( get_declared_classes() as $sLayoutClass)
{
if (is_subclass_of($sLayoutClass, 'DashboardLayout'))
{
$oReflection = new ReflectionClass($sLayoutClass);
if (!$oReflection->isAbstract())
{
$aCallSpec = array($sLayoutClass, 'GetInfo');
$aInfo = call_user_func($aCallSpec);
$sChecked = ($this->sLayoutClass == $sLayoutClass) ? 'checked' : '';
$oPage->add('<input type="radio" name="layout_class" '.$sChecked.' value="'.$sLayoutClass.'" id="layout_'.$sLayoutClass.'"><label for="layout_'.$sLayoutClass.'"><img src="'.$sUrl.$aInfo['icon'].'" /></label>'); // title="" on either the img or the label does nothing !
}
}
}
$oPage->add('</div>');
$oForm = new DesignerForm();
$oField = new DesignerHiddenField('dashboard_id', '', $this->sId);
$oForm->AddField($oField);
$oField = new DesignerLongTextField('dashboard_title', Dict::S('UI:DashboardEdit:DashboardTitle'), $this->sTitle);
$oForm->AddField($oField);
$oField = new DesignerBooleanField('auto_reload', Dict::S('UI:DashboardEdit:AutoReload'), $this->bAutoReload);
$oForm->AddField($oField);
$oField = new DesignerIntegerField('auto_reload_sec', Dict::S('UI:DashboardEdit:AutoReloadSec'), $this->iAutoReloadSec);
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
$oForm->AddField($oField);
$this->SetFormParams($oForm);
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
$oPage->add('</div>');
$sRateTitle = addslashes(Dict::Format('UI:DashboardEdit:AutoReloadSec+', MetaModel::GetConfig()->Get('min_reload_interval')));
$oPage->add_ready_script(
<<<EOF
// Note: the title gets deleted by the validation mechanism
$("#attr_auto_reload_sec").tooltip({items: 'input', content: '$sRateTitle'});
$("#attr_auto_reload_sec").prop('disabled', !$('#attr_auto_reload').is(':checked'));
$('#attr_auto_reload').change( function(ev) {
$("#attr_auto_reload_sec").prop('disabled', !$(this).is(':checked'));
} );
$('#select_layout').buttonset();
$('#select_dashlet').droppable({
accept: '.dashlet',
drop: function(event, ui) {
$( this ).find( ".placeholder" ).remove();
var oDashlet = ui.draggable.data('itopDashlet');
oDashlet._remove_dashlet();
},
});
$('#event_bus').bind('dashlet-selected', function(event, data){
var sDashletId = data.dashlet_id;
var sPropId = 'dashlet_properties_'+sDashletId;
$('.dashlet_properties').each(function() {
var sId = $(this).attr('id');
var bShow = (sId == sPropId);
if (bShow)
{
$(this).show();
}
else
{
$(this).hide();
}
});
});
EOF
);
}
public function RenderDashletsSelection($oPage)
{
// Toolbox/palette to drag and drop dashlets
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:Dashlets').'</div>');
$sUrl = utils::GetAbsoluteUrlAppRoot();
$oPage->add('<div id="select_dashlet" style="text-align:center">');
foreach( get_declared_classes() as $sDashletClass)
{
if (is_subclass_of($sDashletClass, 'Dashlet'))
{
$oReflection = new ReflectionClass($sDashletClass);
if (!$oReflection->isAbstract())
{
$aCallSpec = array($sDashletClass, 'IsVisible');
$bVisible = call_user_func($aCallSpec);
if ($bVisible)
{
$aCallSpec = array($sDashletClass, 'GetInfo');
$aInfo = call_user_func($aCallSpec);
$oPage->add('<span dashlet_class="'.$sDashletClass.'" class="dashlet_icon ui-widget-content ui-corner-all" id="dashlet_'.$sDashletClass.'" title="'.$aInfo['label'].'" style="width:34px; height:34px; display:inline-block; margin:2px;"><img src="'.$sUrl.$aInfo['icon'].'" /></span>');
}
}
}
}
$oPage->add('</div>');
$oPage->add('</div>');
$oPage->add_ready_script("$('.dashlet_icon').draggable({helper: 'clone', appendTo: 'body', zIndex: 10000, revert:'invalid'});");
}
public function RenderDashletsProperties($oPage)
{
// Toolbox/palette to edit the properties of each dashlet
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:DashletProperties').'</div>');
$oPage->add('<div id="dashlet_properties" style="text-align:center">');
foreach($this->aCells as $aCell)
{
foreach($aCell as $oDashlet)
{
$sId = $oDashlet->GetID();
$sClass = get_class($oDashlet);
if ($oDashlet->IsVisible())
{
$oPage->add('<div class="dashlet_properties" id="dashlet_properties_'.$sId.'" style="display:none">');
$oForm = $oDashlet->GetForm();
$this->SetFormParams($oForm);
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
$oPage->add('</div>');
}
}
}
$oPage->add('</div>');
$oPage->add('</div>');
}
protected function GetNewDashletId()
{
$iNewId = 0;
foreach($this->aCells as $aDashlets)
{
foreach($aDashlets as $oDashlet)
{
$iNewId = max($iNewId, (int)$oDashlet->GetID());
}
}
return $iNewId + 1;
}
abstract protected function SetFormParams($oForm);
}
class RuntimeDashboard extends Dashboard
{
protected $bCustomized;
public function __construct($sId)
{
parent::__construct($sId);
$this->bCustomized = false;
$this->oMetaModel = new ModelReflectionRuntime();
}
public function SetCustomFlag($bCustomized)
{
$this->bCustomized = $bCustomized;
}
protected function SetFormParams($oForm)
{
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property'));
}
public function Save()
{
$sXml = $this->ToXml();
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oUDSearch->AddCondition('menu_code', $this->sId, '=');
$oUDSet = new DBObjectSet($oUDSearch);
if ($oUDSet->Count() > 0)
{
// Assuming there is at most one couple {user, menu}!
$oUserDashboard = $oUDSet->Fetch();
$oUserDashboard->Set('contents', $sXml);
$oUserDashboard->DBUpdate();
}
else
{
// No such customized dasboard for the current user, let's create a new record
$oUserDashboard = new UserDashboard();
$oUserDashboard->Set('user_id', UserRights::GetUserId());
$oUserDashboard->Set('menu_code', $this->sId);
$oUserDashboard->Set('contents', $sXml);
$oUserDashboard->DBInsert();
}
}
public function Revert()
{
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oUDSearch->AddCondition('menu_code', $this->sId, '=');
$oUDSet = new DBObjectSet($oUDSearch);
if ($oUDSet->Count() > 0)
{
// Assuming there is at most one couple {user, menu}!
$oUserDashboard = $oUDSet->Fetch();
$oUserDashboard->DBDelete();
}
}
public function RenderEditionTools($oPage)
{
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.iframe-transport.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js');
$sEditMenu = "<td><span id=\"DashboardMenu\"><ul><li><img src=\"../images/pencil-menu.png\"><ul>";
$aActions = array();
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:Edit'), "return EditDashboard('{$this->sId}')");
$aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
if ($this->bCustomized)
{
$oRevert = new JSPopupMenuItem('UI:Dashboard:RevertConfirm', Dict::S('UI:Dashboard:Revert'),
"if (confirm('".addslashes(Dict::S('UI:Dashboard:RevertConfirm'))."')) return RevertDashboard('{$this->sId}'); else return false");
$aActions[$oRevert->GetUID()] = $oRevert->GetMenuItem();
}
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_DASHBOARD_ACTIONS, $this, $aActions);
$sEditMenu .= $oPage->RenderPopupMenuItems($aActions);
$sEditMenu = addslashes($sEditMenu);
//$sEditBtn = addslashes('<div style="display: inline-block; height: 55px; width:200px;vertical-align:center;line-height:60px;text-align:left;"><button onclick="EditDashboard(\''.$this->sId.'\');">Edit This Page</button></div>');
$oPage->add_ready_script(
<<<EOF
$('#logOffBtn').parent().before('$sEditMenu');
$('#DashboardMenu>ul').popupmenu();
EOF
);
$oPage->add_script(
<<<EOF
function EditDashboard(sId)
{
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'dashboard_editor', id: sId},
function(data)
{
$('body').append(data);
}
);
return false;
}
function RevertDashboard(sId)
{
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'revert_dashboard', dashboard_id: sId},
function(data)
{
$('body').append(data);
}
);
return false;
}
EOF
);
}
public function RenderProperties($oPage)
{
parent::RenderProperties($oPage);
$oPage->add_ready_script(
<<<EOF
$('#select_layout input').click( function() {
var sLayoutClass = $(this).val();
$('.itop-dashboard').runtimedashboard('option', {layout_class: sLayoutClass});
} );
$('#row_attr_dashboard_title').property_field('option', {parent_selector: '.itop-dashboard', auto_apply: false, 'do_apply': function() {
var sTitle = $('#attr_dashboard_title').val();
$('.itop-dashboard').runtimedashboard('option', {title: sTitle});
return true;
}
});
$('#row_attr_auto_reload').property_field('option', {parent_selector: '.itop-dashboard', auto_apply: true, 'do_apply': function() {
var bAutoReload = $('#attr_auto_reload').is(':checked');
$('.itop-dashboard').runtimedashboard('option', {auto_reload: bAutoReload});
return true;
}
});
$('#row_attr_auto_reload_sec').property_field('option', {parent_selector: '.itop-dashboard', auto_apply: true, 'do_apply': function() {
var iAutoReloadSec = $('#attr_auto_reload_sec').val();
$('.itop-dashboard').runtimedashboard('option', {auto_reload_sec: iAutoReloadSec});
return true;
}
});
EOF
);
}
public function RenderEditor($oPage)
{
$oPage->add('<div id="dashboard_editor">');
$oPage->add('<div class="ui-layout-center">');
$this->Render($oPage, true);
$oPage->add('</div>');
$oPage->add('<div class="ui-layout-east">');
$this->RenderProperties($oPage);
$this->RenderDashletsSelection($oPage);
$this->RenderDashletsProperties($oPage);
$oPage->add('</div>');
$oPage->add('<div id="event_bus"/>'); // For exchanging messages between the panes, same as in the designer
$oPage->add('</div>');
$sDialogTitle = Dict::S('UI:DashboardEdit:Title');
$sOkButtonLabel = Dict::S('UI:Button:Save');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$sId = addslashes($this->sId);
$sLayoutClass = addslashes($this->sLayoutClass);
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
$sAutoReloadSec = (string) $this->iAutoReloadSec;
$sTitle = addslashes($this->sTitle);
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
$sExitConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
$sCancelConfirmationMessage = addslashes(Dict::S('UI:CancelConfirmationMessage'));
$sAutoApplyConfirmationMessage = addslashes(Dict::S('UI:AutoApplyConfirmationMessage'));
$oPage->add_ready_script(
<<<EOF
window.bLeavingOnUserAction = false;
$('#dashboard_editor').dialog({
height: $('body').height() - 50,
width: $('body').width() - 50,
modal: true,
title: '$sDialogTitle',
buttons: [
{ text: "$sOkButtonLabel", click: function() {
var oDashboard = $('.itop-dashboard').data('itopRuntimedashboard');
if (oDashboard.is_dirty())
{
if (!confirm('$sAutoApplyConfirmationMessage'))
{
return;
}
else
{
oDashboard.apply_changes();
}
}
window.bLeavingOnUserAction = true;
oDashboard.save();
} },
{ text: "$sCancelButtonLabel", click: function() {
var oDashboard = $('.itop-dashboard').data('itopRuntimedashboard');
if (oDashboard.is_modified())
{
if (!confirm('$sCancelConfirmationMessage'))
{
return;
}
}
window.bLeavingOnUserAction = true;
$(this).dialog( "close" );
$(this).remove();
} },
],
close: function() { $(this).remove(); }
});
$('#dashboard_editor .ui-layout-center').runtimedashboard({
dashboard_id: '$sId', layout_class: '$sLayoutClass', title: '$sTitle',
auto_reload: $sAutoReload, auto_reload_sec: $sAutoReloadSec,
submit_to: '$sUrl', submit_parameters: {operation: 'save_dashboard'},
render_to: '$sUrl', render_parameters: {operation: 'render_dashboard'},
new_dashlet_parameters: {operation: 'new_dashlet'}
});
dashboard_prop_size = GetUserPreference('dashboard_prop_size', 350);
$('#dashboard_editor').layout({
east: {
minSize: 200,
size: dashboard_prop_size,
togglerLength_open: 0,
togglerLength_closed: 0,
onresize_end: function(name, elt, state, options, layout)
{
if (state.isSliding == false)
{
SetUserPreference('dashboard_prop_size', state.size, true);
}
},
}
});
window.onbeforeunload = function() {
if (!window.bLeavingOnUserAction)
{
var oDashboard = $('.itop-dashboard').data('itopRuntimedashboard');
if (oDashboard)
{
if (oDashboard.is_dirty())
{
return '$sExitConfirmationMessage';
}
if (oDashboard.is_modified())
{
return '$sExitConfirmationMessage';
}
}
}
// return nothing ! safer for IE
};
EOF
);
$oPage->add_ready_script("");
}
public static function GetDashletCreationForm($sOQL = null)
{
$oForm = new DesignerForm();
// Get the list of all 'dashboard' menus in which we can insert a dashlet
$aAllMenus = ApplicationMenu::ReflectionMenuNodes();
$aAllowedDashboards = array();
foreach($aAllMenus as $idx => $aMenu)
{
$oMenu = $aMenu['node'];
$sParentId = $aMenu['parent'];
if ($oMenu instanceof DashboardMenuNode)
{
$sMenuLabel = $oMenu->GetTitle();
$sParentLabel = Dict::S('Menu:'.$sParentId);
if ($sParentLabel != $sMenuLabel)
{
$aAllowedDashboards[$oMenu->GetMenuId()] = $sParentLabel.' - '.$sMenuLabel;
}
else
{
$aAllowedDashboards[$oMenu->GetMenuId()] = $sMenuLabel;
}
}
}
asort($aAllowedDashboards);
$aKeys = array_keys($aAllowedDashboards); // Select the first one by default
$sDefaultDashboard = $aKeys[0];
$oField = new DesignerComboField('menu_id', Dict::S('UI:DashletCreation:Dashboard'), $sDefaultDashboard);
$oField->SetAllowedValues($aAllowedDashboards);
$oField->SetMandatory(true);
$oForm->AddField($oField);
// Get the list of possible dashlets that support a creation from
// an OQL
$aDashlets = array();
foreach(get_declared_classes() as $sDashletClass)
{
if (is_subclass_of($sDashletClass, 'Dashlet'))
{
$oReflection = new ReflectionClass($sDashletClass);
if (!$oReflection->isAbstract())
{
$aCallSpec = array($sDashletClass, 'CanCreateFromOQL');
$bShorcutMode = call_user_func($aCallSpec);
if ($bShorcutMode)
{
$aCallSpec = array($sDashletClass, 'GetInfo');
$aInfo = call_user_func($aCallSpec);
$aDashlets[$sDashletClass] = array('label' => $aInfo['label'], 'class' => $sDashletClass, 'icon' => $aInfo['icon']);
}
}
}
}
$oSelectorField = new DesignerFormSelectorField('dashlet_class', Dict::S('UI:DashletCreation:DashletType'), '');
$oForm->AddField($oSelectorField);
foreach($aDashlets as $sDashletClass => $aDashletInfo)
{
$oSubForm = new DesignerForm();
$oMetaModel = new ModelReflectionRuntime();
$oDashlet = new $sDashletClass($oMetaModel, 0);
$oDashlet->GetPropertiesFieldsFromOQL($oSubForm, $sOQL);
$oSelectorField->AddSubForm($oSubForm, $aDashletInfo['label'], $aDashletInfo['class']);
}
$oField = new DesignerBooleanField('open_editor', Dict::S('UI:DashletCreation:EditNow'), true);
$oForm->AddField($oField);
return $oForm;
}
public static function GetDashletCreationDlgFromOQL($oPage, $sOQL)
{
$oPage->add('<div id="dashlet_creation_dlg">');
$oForm = self::GetDashletCreationForm($sOQL);
$oForm->Render($oPage);
$oPage->add('</div>');
$sDialogTitle = Dict::S('UI:DashletCreation:Title');
$sOkButtonLabel = Dict::S('UI:Button:Ok');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$oPage->add_ready_script(
<<<EOF
$('#dashlet_creation_dlg').dialog({
width: 400,
modal: true,
title: '$sDialogTitle',
buttons: [
{ text: "$sOkButtonLabel", click: function() {
var oForm = $(this).find('form');
var sFormId = oForm.attr('id');
var oParams = null;
var aErrors = ValidateForm(sFormId, false);
if (aErrors.length == 0)
{
oParams = ReadFormParams(sFormId);
}
oParams.operation = 'add_dashlet';
var me = $(this);
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function(data) {
me.dialog( "close" );
me.remove();
$('body').append(data);
});
} },
{ text: "$sCancelButtonLabel", click: function() {
$(this).dialog( "close" ); $(this).remove();
} },
],
close: function() { $(this).remove(); }
});
EOF
);
}
}

View File

@@ -0,0 +1,212 @@
<?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/>
/**
* Dashboard presentation
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class DashboardLayout
{
public function __construct()
{
}
abstract public function Render($oPage, $aDashlets, $bEditMode = false);
static public function GetInfo()
{
return array(
'label' => '',
'icon' => '',
'description' => '',
);
}
}
abstract class DashboardLayoutMultiCol extends DashboardLayout
{
protected $iNbCols;
public function __construct()
{
$this->iNbCols = 1;
}
protected function TrimCell($aDashlets)
{
$aKeys = array_reverse(array_keys($aDashlets));
$idx = 0;
$bNoVisibleFound = true;
while($idx < count($aKeys) && $bNoVisibleFound)
{
$oDashlet = $aDashlets[$aKeys[$idx]];
if ($oDashlet->IsVisible())
{
$bNoVisibleFound = false;
}
else
{
unset($aDashlets[$aKeys[$idx]]);
}
$idx++;
}
return $aDashlets;
}
protected function TrimCellsArray($aCells)
{
foreach($aCells as $key => $aDashlets)
{
$aCells[$key] = $this->TrimCell($aDashlets);
}
$aKeys = array_reverse(array_keys($aCells));
$idx = 0;
$bNoVisibleFound = true;
while($idx < count($aKeys) && $bNoVisibleFound)
{
$aDashlets = $aCells[$aKeys[$idx]];
if (count($aDashlets) > 0)
{
$bNoVisibleFound = false;
}
else
{
unset($aCells[$aKeys[$idx]]);
}
$idx++;
}
return $aCells;
}
public function Render($oPage, $aCells, $bEditMode = false, $aExtraParams = array())
{
// Trim the list of cells to remove the invisible/empty ones at the end of the array
$aCells = $this->TrimCellsArray($aCells);
$oPage->add('<table style="width:100%"><tbody>');
$iCellIdx = 0;
$fColSize = 100 / $this->iNbCols;
$sStyle = $bEditMode ? 'border: 1px #ccc dashed; width:'.$fColSize.'%;' : 'width: '.$fColSize.'%;';
$sClass = $bEditMode ? 'layout_cell edit_mode' : 'dashboard';
$iNbRows = ceil(count($aCells) / $this->iNbCols);
for($iRows = 0; $iRows < $iNbRows; $iRows++)
{
$oPage->add('<tr>');
for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
{
$sCellClass = ($iRows == $iNbRows-1) ? $sClass.' layout_last_used_rank' : $sClass;
$oPage->add("<td style=\"$sStyle\" class=\"$sCellClass\" data-dashboard-cell-index=\"$iCellIdx\">");
if (array_key_exists($iCellIdx, $aCells))
{
$aDashlets = $aCells[$iCellIdx];
if (count($aDashlets) > 0)
{
foreach($aDashlets as $oDashlet)
{
if ($oDashlet->IsVisible())
{
$oDashlet->DoRender($oPage, $bEditMode, true /* bEnclosingDiv */, $aExtraParams);
}
}
}
else
{
$oPage->add('&nbsp;');
}
}
else
{
$oPage->add('&nbsp;');
}
$oPage->add('</td>');
$iCellIdx++;
}
$oPage->add('</tr>');
}
if ($bEditMode) // Add one row for extensibility
{
$sStyle = 'style="border: 1px #ccc dashed; width:'.$fColSize.'%;" class="layout_cell edit_mode layout_extension" data-dashboard-cell-index="'.$iCellIdx.'"';
$oPage->add('<tr>');
for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
{
$oPage->add("<td $sStyle>");
$oPage->add('&nbsp;');
$oPage->add('</td>');
}
$oPage->add('</tr>');
}
$oPage->add('</tbody></table>');
}
}
class DashboardLayoutOneCol extends DashboardLayoutMultiCol
{
public function __construct()
{
parent::__construct();
$this->iNbCols = 1;
}
static public function GetInfo()
{
return array(
'label' => 'One Column',
'icon' => 'images/layout_1col.png',
'description' => '',
);
}
}
class DashboardLayoutTwoCols extends DashboardLayoutMultiCol
{
public function __construct()
{
parent::__construct();
$this->iNbCols = 2;
}
static public function GetInfo()
{
return array(
'label' => 'Two Columns',
'icon' => 'images/layout_2col.png',
'description' => '',
);
}
}
class DashboardLayoutThreeCols extends DashboardLayoutMultiCol
{
public function __construct()
{
parent::__construct();
$this->iNbCols = 3;
}
static public function GetInfo()
{
return array(
'label' => 'Two Columns',
'icon' => 'images/layout_3col.png',
'description' => '',
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design>
<portals>
<portal id="legacy_portal" _delta="define">
<url>portal/index.php</url>
<rank>1.0</rank>
<handler/>
<allow>
</allow>
<deny/>
</portal>
<portal id="backoffice" _delta="define">
<url>pages/UI.php</url>
<rank>2.0</rank>
<handler/>
<allow/>
<deny>
<profile id="Portal user"/>
</deny>
</portal>
</portals>
</itop_design>

View File

@@ -0,0 +1,949 @@
<?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/>
/**
* Data Table to display a set of objects in a tabular manner in HTML
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class DataTable
{
protected $iListId; // Unique ID inside the web page
protected $sTableId; // identifier for saving the settings (combined with the class aliases)
protected $oSet; // The set of objects to display
protected $aClassAliases; // The aliases (alias => class) inside the set
protected $iNbObjects; // Total number of objects inthe set
protected $bUseCustomSettings; // Whether or not the current display uses custom settings
protected $oDefaultSettings; // the default settings for displaying such a list
/**
* @param $iListId mixed Unique ID for this div/table in the page
* @param $oSet DBObjectSet The set of data to display
* @param $aClassAliases Hash The list of classes/aliases to be displayed in this set $sAlias => $sClassName
* @param $sTableId mixed A string (or null) identifying this table in order to persist its settings
*/
public function __construct($iListId, $oSet, $aClassAliases, $sTableId = null)
{
$this->iListId = utils::GetSafeId($iListId); // Make a "safe" ID for jQuery
$this->oSet = $oSet;
$this->aClassAliases = $aClassAliases;
$this->sTableId = $sTableId;
$this->iNbObjects = $oSet->Count();
$this->bUseCustomSettings = false;
$this->oDefaultSettings = null;
}
public function Display(WebPage $oPage, DataTableSettings $oSettings, $bActionsMenu, $sSelectMode, $bViewLink, $aExtraParams)
{
$this->oDefaultSettings = $oSettings;
// Identified tables can have their own specific settings
$oCustomSettings = DataTableSettings::GetTableSettings($this->aClassAliases, $this->sTableId);
if ($oCustomSettings != null)
{
// Custom settings overload the default ones
$this->bUseCustomSettings = true;
if ($this->oDefaultSettings->iDefaultPageSize == 0)
{
$oCustomSettings->iDefaultPageSize = 0;
}
}
else
{
$oCustomSettings = $oSettings;
}
if ($oCustomSettings->iDefaultPageSize > 0)
{
$this->oSet->SetLimit($oCustomSettings->iDefaultPageSize);
}
$this->oSet->SetOrderBy($oCustomSettings->GetSortOrder());
// Load only the requested columns
$aColumnsToLoad = array();
foreach($oCustomSettings->aColumns as $sAlias => $aColumnsInfo)
{
foreach($aColumnsInfo as $sAttCode => $aData)
{
if ($sAttCode != '_key_')
{
if ($aData['checked'])
{
$aColumnsToLoad[$sAlias][] = $sAttCode;
}
else
{
// See if this column is a must to load
$sClass = $this->aClassAliases[$sAlias];
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->alwaysLoadInTables())
{
$aColumnsToLoad[$sAlias][] = $sAttCode;
}
}
}
}
}
$this->oSet->OptimizeColumnLoad($aColumnsToLoad);
$bToolkitMenu = true;
if (isset($aExtraParams['toolkit_menu']))
{
$bToolkitMenu = (bool) $aExtraParams['toolkit_menu'];
}
if (UserRights::IsPortalUser())
{
// Portal users have a limited access to data, for now they can only see what's configured for them
$bToolkitMenu = false;
}
return $this->GetAsHTML($oPage, $oCustomSettings->iDefaultPageSize, $oCustomSettings->iDefaultPageSize, 0, $oCustomSettings->aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams);
}
public function GetAsHTML(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex, $aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams)
{
$sObjectsCount = $this->GetObjectCount($oPage, $sSelectMode);
$sPager = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex);
$sActionsMenu = '';
$sToolkitMenu = '';
if ($bActionsMenu)
{
$sActionsMenu = $this->GetActionsMenu($oPage, $aExtraParams);
}
if ($bToolkitMenu)
{
$sToolkitMenu = $this->GetToolkitMenu($oPage, $aExtraParams);
}
$sDataTable = $this->GetHTMLTable($oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
$sConfigDlg = $this->GetTableConfigDlg($oPage, $aColumns, $bViewLink, $iDefaultPageSize);
$sHtml = "<table id=\"datatable_{$this->iListId}\" class=\"datatable\">";
$sHtml .= "<tr><td>";
$sHtml .= "<table style=\"width:100%;\">";
$sHtml .= "<tr><td class=\"pagination_container\">$sObjectsCount</td><td class=\"menucontainer\">$sToolkitMenu $sActionsMenu</td></tr>";
$sHtml .= "<tr>$sPager</tr>";
$sHtml .= "</table>";
$sHtml .= "</td></tr>";
$sHtml .= "<tr><td class=\"datacontents\">$sDataTable</td></tr>";
$sHtml .= "</table>\n";
$oPage->add_at_the_end($sConfigDlg);
$aOptions = array(
'sPersistentId' => '',
'sFilter' => $this->oSet->GetFilter()->serialize(),
'oColumns' => $aColumns,
'sSelectMode' => $sSelectMode,
'sViewLink' => ($bViewLink ? 'true' : 'false'),
'iNbObjects' => $this->iNbObjects,
'iDefaultPageSize' => $iDefaultPageSize,
'iPageSize' => $iPageSize,
'iPageIndex' => $iPageIndex,
'oClassAliases' => $this->aClassAliases,
'sTableId' => $this->sTableId,
'oExtraParams' => $aExtraParams,
'sRenderUrl' => utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php',
'oRenderParameters' => array('str' => ''), // Forces JSON to encode this as a object...
'oDefaultSettings' => array('str' => ''), // Forces JSON to encode this as a object...
'oLabels' => array('moveup' => Dict::S('UI:Button:MoveUp'), 'movedown' => Dict::S('UI:Button:MoveDown')),
);
if($this->oDefaultSettings != null)
{
$aOptions['oDefaultSettings'] = $this->GetAsHash($this->oDefaultSettings);
}
$sJSOptions = json_encode($aOptions);
$oPage->add_ready_script("$('#datatable_{$this->iListId}').datatable($sJSOptions);");
return $sHtml;
}
/**
* When refreshing the body of a paginated table, get the rows of the table (inside the TBODY)
* return string The HTML rows to insert inside the <tbody> node
*/
public function GetAsHTMLTableRows(WebPage $oPage, $iPageSize, $aColumns, $sSelectMode, $bViewLink, $aExtraParams)
{
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
$sHtml = '';
foreach($aValues as $aRow)
{
$sHtml .= $oPage->GetTableRow($aRow, $aAttribs);
}
return $sHtml;
}
protected function GetObjectCount(WebPage $oPage, $sSelectMode)
{
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
{
$sHtml = '<div class="pagination_objcount">'.Dict::Format('UI:Pagination:HeaderSelection', '<span id="total">'.$this->iNbObjects.'</span>', '<span class="selectedCount">0</span>').'</div>';
}
else
{
$sHtml = '<div class="pagination_objcount">'.Dict::Format('UI:Pagination:HeaderNoSelection', '<span id="total">'.$this->iNbObjects.'</span>').'</div>';
}
return $sHtml;
}
protected function GetPager(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex)
{
$sHtml = '';
if ($iPageSize < 1) // Display all
{
$sPagerStyle = 'style="display:none"'; // no limit: display the full table, so hide the "pager" UI
// WARNING: mPDF does not take the "display" style into account
// when applied to a <td> or a <table> tag, so make sure you apply this to a div
}
else
{
$sPagerStyle = '';
}
$sCombo = '<select class="pagesize">';
for($iPage = 1; $iPage < 5; $iPage++)
{
$iNbItems = $iPage * $iDefaultPageSize;
$sSelected = ($iNbItems == $iPageSize) ? 'selected="selected"' : '';
$sCombo .= "<option $sSelected value=\"$iNbItems\">$iNbItems</option>";
}
$sSelected = ($iPageSize < 1) ? 'selected="selected"' : '';
$sCombo .= "<option $sSelected value=\"-1\">".Dict::S('UI:Pagination:All')."</option>";
$sCombo .= '</select>';
$sPages = Dict::S('UI:Pagination:PagesLabel');
$sPageSizeCombo = Dict::Format('UI:Pagination:PageSize', $sCombo);
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
if ($iNbPages == 1)
{
// No need to display the pager
$sPagerStyle = 'style="display:none"';
}
$aPagesToDisplay = array();
for($idx = 0; $idx <= min(4, $iNbPages-1); $idx++)
{
if ($idx == 0)
{
$aPagesToDisplay[$idx] = '<span page="0" class="curr_page">1</span>';
}
else
{
$aPagesToDisplay[$idx] = "<span id=\"gotopage_$idx\" class=\"gotopage\" page=\"$idx\">".(1+$idx)."</span>";
}
}
$iLastPageIdx = $iNbPages - 1;
if (!isset($aPagesToDisplay[$iLastPageIdx]))
{
unset($aPagesToDisplay[$idx - 1]); // remove the last page added to make room for the very last page
$aPagesToDisplay[$iLastPageIdx] = "<span id=\"gotopage_$iLastPageIdx\" class=\"gotopage\" page=\"$iLastPageIdx\">... $iNbPages</span>";
}
$sPagesLinks = implode('', $aPagesToDisplay);
$sPagesList = '['.implode(',', array_keys($aPagesToDisplay)).']';
$sSelectionMode = ($iNbPages == 1) ? '' : 'positive';
$sHtml =
<<<EOF
<td colspan="2">
<div $sPagerStyle>
<table id="pager{$this->iListId}" class="pager"><tr>
<td>$sPages</td>
<td><img src="../images/first.png" class="first"/></td>
<td><img src="../images/prev.png" class="prev"/></td>
<td><span id="index">$sPagesLinks</span></td>
<td><img src="../images/next.png" class="next"/></td>
<td><img src="../images/last.png" class="last"/></td>
<td>$sPageSizeCombo</td>
<td><span id="loading">&nbsp;</span><input type="hidden" name="selectionMode" value="$sSelectionMode"></input>
</td>
</tr>
</table>
</div>
</td>
EOF;
return $sHtml;
}
protected function GetActionsMenu(WebPage $oPage, $aExtraParams)
{
$oMenuBlock = new MenuBlock($this->oSet->GetFilter(), 'list');
$sHtml = $oMenuBlock->GetRenderContent($oPage, $aExtraParams, $this->iListId);
return $sHtml;
}
protected function GetToolkitMenu(WebPage $oPage, $aExtraParams)
{
if (!$oPage->IsPrintableVersion())
{
$sMenuTitle = Dict::S('UI:ConfigureThisList');
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><img src="../images/toolkit_menu.png?itopversion='.ITOP_VERSION.'"><ul>';
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
$aActions = array(
$oMenuItem1->GetUID() => $oMenuItem1->GetMenuItem(),
);
$this->oSet->Rewind();
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_OBJLIST_TOOLKIT, $this->oSet, $aActions, $this->sTableId, $this->iListId);
$this->oSet->Rewind();
$sHtml .= $oPage->RenderPopupMenuItems($aActions);
}
else
{
$sHtml = '';
}
return $sHtml;
}
protected function GetTableConfigDlg(WebPage $oPage, $aColumns, $bViewLink, $iDefaultPageSize)
{
$sHtml = "<div id=\"datatable_dlg_{$this->iListId}\" style=\"display: none;\">";
$sHtml .= "<form onsubmit=\"return false\">";
$sChecked = ($this->bUseCustomSettings) ? '' : 'checked';
$sHtml .= "<p><input id=\"dtbl_dlg_settings_{$this->iListId}\" type=\"radio\" name=\"settings\" $sChecked value=\"defaults\"><label for=\"dtbl_dlg_settings_{$this->iListId}\">&nbsp;".Dict::S('UI:UseDefaultSettings').'</label></p>';
$sHtml .= "<fieldset>";
$sChecked = ($this->bUseCustomSettings) ? 'checked': '';
$sHtml .= "<legend class=\"transparent\"><input id=\"dtbl_dlg_specific_{$this->iListId}\" type=\"radio\" class=\"specific_settings\" name=\"settings\" $sChecked value=\"specific\"><label for=\"dtbl_dlg_specific_{$this->iListId}\">&nbsp;".Dict::S('UI:UseSpecificSettings')."</label></legend>";
$sHtml .= Dict::S('UI:ColumnsAndSortOrder').'<br/><ul class="sortable_field_list" id="sfl_'.$this->iListId.'"></ul>';
$sHtml .= '<p>'.Dict::Format('UI:Display_X_ItemsPerPage', '<input type="text" size="4" name="page_size" value="'.$iDefaultPageSize.'">').'</p>';
$sHtml .= "</fieldset>";
$sHtml .= "<fieldset>";
$sSaveChecked = ($this->sTableId != null) ? 'checked' : '';
$sCustomDisabled = ($this->sTableId == null) ? 'disabled="disabled" stay-disabled="true" ' : '';
$sCustomChecked = ($this->sTableId != null) ? 'checked' : '';
$sGenericChecked = ($this->sTableId == null) ? 'checked' : '';
$sHtml .= "<legend class=\"transparent\"><input id=\"dtbl_dlg_save_{$this->iListId}\" type=\"checkbox\" $sSaveChecked name=\"save_settings\"><label for=\"dtbl_dlg_save_{$this->iListId}\">&nbsp;".Dict::S('UI:UseSavetheSettings')."</label></legend>";
$sHtml .= "<p><input id=\"dtbl_dlg_this_list_{$this->iListId}\" type=\"radio\" name=\"scope\" $sCustomChecked $sCustomDisabled value=\"this_list\"><label for=\"dtbl_dlg_this_list_{$this->iListId}\">&nbsp;".Dict::S('UI:OnlyForThisList').'</label>&nbsp;&nbsp;&nbsp;&nbsp;';
$sHtml .= "<input id=\"dtbl_dlg_all_{$this->iListId}\" type=\"radio\" name=\"scope\" $sGenericChecked value=\"defaults\"><label for=\"dtbl_dlg_all_{$this->iListId}\">&nbsp;".Dict::S('UI:ForAllLists').'</label></p>';
$sHtml .= "</fieldset>";
$sHtml .= '<table style="width:100%"><tr><td style="text-align:center;">';
$sHtml .= '<button type="button" onclick="$(\'#datatable_'.$this->iListId.'\').datatable(\'onDlgCancel\'); $(\'#datatable_dlg_'.$this->iListId.'\').dialog(\'close\')">'.Dict::S('UI:Button:Cancel').'</button>';
$sHtml .= '</td><td style="text-align:center;">';
$sHtml .= '<button type="submit" onclick="$(\'#datatable_'.$this->iListId.'\').datatable(\'onDlgOk\');$(\'#datatable_dlg_'.$this->iListId.'\').dialog(\'close\');">'.Dict::S('UI:Button:Ok').'</button>';
$sHtml .= '</td></tr></table>';
$sHtml .= "</form>";
$sHtml .= "</div>";
$sDlgTitle = addslashes(Dict::S('UI:ListConfigurationTitle'));
$oPage->add_ready_script("$('#datatable_dlg_{$this->iListId}').dialog({autoOpen: false, title: '$sDlgTitle', width: 500, close: function() { $('#datatable_{$this->iListId}').datatable('onDlgCancel'); } });");
return $sHtml;
}
public function GetAsHash($oSetting)
{
$aSettings = array('iDefaultPageSize' => $oSetting->iDefaultPageSize, 'oColumns' => $oSetting->aColumns);
return $aSettings;
}
protected function GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink)
{
$aAttribs = array();
if ($sSelectMode == 'multiple')
{
$aAttribs['form::select'] = array('label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->iListId}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>", 'description' => Dict::S('UI:SelectAllToggle+'));
}
else if ($sSelectMode == 'single')
{
$aAttribs['form::select'] = array('label' => "", 'description' => '');
}
foreach($this->aClassAliases as $sAlias => $sClassName)
{
foreach($aColumns[$sAlias] as $sAttCode => $aData)
{
if ($aData['checked'])
{
if ($sAttCode == '_key_')
{
$aAttribs['key_'.$sAlias] = array('label' => MetaModel::GetName($sClassName), 'description' => '');
}
else
{
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode);
$aAttribs[$sAttCode.'_'.$sAlias] = array('label' => MetaModel::GetLabel($sClassName, $sAttCode), 'description' => $oAttDef->GetOrderByHint());
}
}
}
}
return $aAttribs;
}
protected function GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
{
$bLocalize = true;
if (isset($aExtraParams['localize_values']))
{
$bLocalize = (bool) $aExtraParams['localize_values'];
}
$aValues = array();
$this->oSet->Seek(0);
$iMaxObjects = $iPageSize;
while (($aObjects = $this->oSet->FetchAssoc()) && ($iMaxObjects != 0))
{
$bFirstObject = true;
$aRow = array();
foreach($this->aClassAliases as $sAlias => $sClassName)
{
if (is_object($aObjects[$sAlias]))
{
$sHilightClass = $aObjects[$sAlias]->GetHilightClass();
if ($sHilightClass != '')
{
$aRow['@class'] = $sHilightClass;
}
if ((($sSelectMode == 'single') || ($sSelectMode == 'multiple')) && $bFirstObject)
{
if (array_key_exists('selection_enabled', $aExtraParams) && isset($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]))
{
$sDisabled = ($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]) ? '' : ' disabled="disabled"';
}
else
{
$sDisabled = '';
}
if ($sSelectMode == 'single')
{
$aRow['form::select'] = "<input type=\"radio\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
}
else
{
$aRow['form::select'] = "<input type=\"checkBox\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject[]\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
}
}
foreach($aColumns[$sAlias] as $sAttCode => $aData)
{
if ($aData['checked'])
{
if ($sAttCode == '_key_')
{
$aRow['key_'.$sAlias] = $aObjects[$sAlias]->GetHyperLink();
}
else
{
$aRow[$sAttCode.'_'.$sAlias] = $aObjects[$sAlias]->GetAsHTML($sAttCode, $bLocalize);
}
}
}
}
else
{
foreach($aColumns[$sAlias] as $sAttCode => $aData)
{
if ($aData['checked'])
{
if ($sAttCode == '_key_')
{
$aRow['key_'.$sAlias] = '';
}
else
{
$aRow[$sAttCode.'_'.$sAlias] = '';
}
}
}
}
$bFirstObject = false;
}
$aValues[] = $aRow;
$iMaxObjects--;
}
return $aValues;
}
public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
{
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
if ($iPageSize < 1)
{
$iPageSize = -1; // convention: no pagination
}
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
$sHtml = '<table class="listContainer">';
foreach($this->oSet->GetFilter()->GetInternalParams() as $sName => $sValue)
{
$aExtraParams['query_params'][$sName] = $sValue;
}
$sHtml .= "<tr><td>";
$sHtml .= $oPage->GetTable($aAttribs, $aValues);
$sHtml .= '</td></tr>';
$sHtml .= '</table>';
$iCount = $this->iNbObjects;
$aArgs = $this->oSet->GetArgs();
$sExtraParams = addslashes(str_replace('"', "'", json_encode(array_merge($aExtraParams, $aArgs)))); // JSON encode, change the style of the quotes and escape them
$sSelectModeJS = '';
$sHeaders = '';
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
{
$sSelectModeJS = $sSelectMode;
$sHeaders = 'headers: { 0: {sorter: false}},';
}
$sDisplayKey = ($bViewLink) ? 'true' : 'false';
// Protect against duplicate elements in the Zlist
$aUniqueOrderedList = array();
foreach($this->aClassAliases as $sAlias => $sClassName)
{
foreach($aColumns[$sAlias] as $sAttCode => $aData)
{
if ($aData['checked'])
{
$aUniqueOrderedList[$sAttCode] = true;
}
}
}
$aUniqueOrderedList = array_keys($aUniqueOrderedList);
$sJSColumns = json_encode($aColumns);
$sJSClassAliases = json_encode($this->aClassAliases);
$sCssCount = isset($aExtraParams['cssCount']) ? ", cssCount: '{$aExtraParams['cssCount']}'" : '';
$this->oSet->ApplyParameters();
// Display the actual sort order of the table
$aRealSortOrder = $this->oSet->GetRealSortOrder();
$aDefaultSort = array();
$iColOffset = 0;
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
{
$iColOffset += 1;
}
if ($bViewLink)
{
// $iColOffset += 1;
}
foreach($aRealSortOrder as $sColCode => $bAscending)
{
$iPos = array_search($sColCode, $aUniqueOrderedList);
if ($iPos !== false)
{
$aDefaultSort[] = "[".($iColOffset+$iPos).",".($bAscending ? '0' : '1')."]";
}
else if (($iPos = array_search(preg_replace('/_friendlyname$/', '', $sColCode), $aUniqueOrderedList)) !== false)
{
// if sorted on the friendly name of an external key, then consider it sorted on the column that shows the links
$aDefaultSort[] = "[".($iColOffset+$iPos).",".($bAscending ? '0' : '1')."]";
}
else if($sColCode == 'friendlyname' && $bViewLink)
{
$aDefaultSort[] = "[".($iColOffset).",".($bAscending ? '0' : '1')."]";
}
}
$sFakeSortList = '';
if (count($aDefaultSort) > 0)
{
$sFakeSortList = '['.implode(',', $aDefaultSort).']';
}
$sOQL = addslashes($this->oSet->GetFilter()->serialize());
$oPage->add_ready_script(
<<<EOF
var oTable = $('#{$this->iListId} table.listResults');
oTable.tableHover();
oTable.tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager{$this->iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectModeJS', displayKey: $sDisplayKey, columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount});
EOF
);
if ($sFakeSortList != '')
{
$oPage->add_ready_script("oTable.trigger(\"fakesorton\", [$sFakeSortList]);");
}
//if ($iNbPages == 1)
if (false)
{
if (isset($aExtraParams['cssCount']))
{
$sCssCount = $aExtraParams['cssCount'];
if ($sSelectMode == 'single')
{
$sSelectSelector = ":radio[name^=selectObj]";
}
else if ($sSelectMode == 'multiple')
{
$sSelectSelector = ":checkbox[name^=selectObj]";
}
$oPage->add_ready_script(
<<<EOF
$('#{$this->iListId} table.listResults $sSelectSelector').change(function() {
var c = $('{$sCssCount}');
var v = $('#{$this->iListId} table.listResults $sSelectSelector:checked').length;
c.val(v);
$('#{$this->iListId} .selectedCount').text(v);
c.trigger('change');
});
EOF
);
}
}
return $sHtml;
}
public function UpdatePager(WebPage $oPage, $iDefaultPageSize, $iStart)
{
$iPageSize = ($iDefaultPageSize < 1) ? 1 : $iDefaultPageSize;
$iPageIndex = 1 + floor($iStart / $iPageSize);
$sHtml = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex);
$oPage->add_ready_script("$('#pager{$this->iListId}').html('".str_replace("\n", ' ', addslashes($sHtml))."');");
if ($iDefaultPageSize < 1)
{
$oPage->add_ready_script("$('#pager{$this->iListId}').parent().hide()");
}
else
{
$oPage->add_ready_script("$('#pager{$this->iListId}').parent().show()");
}
}
}
/**
* Simplified version of the data table with less "decoration" (and no paging)
* which is optimized for printing
*/
class PrintableDataTable extends DataTable
{
public function GetAsHTML(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex, $aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams)
{
return $this->GetHTMLTable($oPage, $aColumns, $sSelectMode, -1, $bViewLink, $aExtraParams);
}
public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
{
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
if ($iPageSize < 1)
{
$iPageSize = -1; // convention: no pagination
}
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
$sHtml = $oPage->GetTable($aAttribs, $aValues);
return $sHtml;
}
}
class DataTableSettings implements Serializable
{
public $aClassAliases;
public $sTableId;
public $iDefaultPageSize;
public $aColumns;
public function __construct($aClassAliases, $sTableId = null)
{
$this->aClassAliases = $aClassAliases;
$this->sTableId = $sTableId;
$this->iDefaultPageSize = 10;
$this->aColumns = array();
}
protected function Init($iDefaultPageSize, $aSortOrder, $aColumns)
{
$this->iDefaultPageSize = $iDefaultPageSize;
$this->aColumns = $aColumns;
$this->FixVisibleColumns();
}
public function serialize()
{
// Save only the 'visible' columns
$aColumns = array();
foreach($this->aClassAliases as $sAlias => $sClass)
{
$aColumns[$sAlias] = array();
foreach($this->aColumns[$sAlias] as $sAttCode => $aData)
{
unset($aData['label']); // Don't save the display name
unset($aData['alias']); // Don't save the alias (redundant)
unset($aData['code']); // Don't save the code (redundant)
if ($aData['checked'])
{
$aColumns[$sAlias][$sAttCode] = $aData;
}
}
}
return serialize(
array(
'iDefaultPageSize' => $this->iDefaultPageSize,
'aColumns' => $aColumns,
)
);
}
public function unserialize($sData)
{
$aData = unserialize($sData);
$this->iDefaultPageSize = $aData['iDefaultPageSize'];
$this->aColumns = $aData['aColumns'];
foreach($this->aClassAliases as $sAlias => $sClass)
{
foreach($this->aColumns[$sAlias] as $sAttCode => $aData)
{
$aFieldData = false;
if ($sAttCode == '_key_')
{
$aFieldData = $this->GetFieldData($sAlias, $sAttCode, null, true /* bChecked */, $aData['sort']);
}
else if (MetaModel::isValidAttCode($sClass, $sAttCode))
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$aFieldData = $this->GetFieldData($sAlias, $sAttCode, $oAttDef, true /* bChecked */, $aData['sort']);
}
if ($aFieldData)
{
$this->aColumns[$sAlias][$sAttCode] = $aFieldData;
}
else
{
unset($this->aColumns[$sAlias][$sAttCode]);
}
}
}
$this->FixVisibleColumns();
}
static public function GetDataModelSettings($aClassAliases, $bViewLink, $aDefaultLists)
{
$oSettings = new DataTableSettings($aClassAliases);
// Retrieve the class specific settings for each class/alias based on the 'list' ZList
//TODO let the caller pass some other default settings (another Zlist, extre fields...)
$aColumns = array();
foreach($aClassAliases as $sAlias => $sClass)
{
if ($aDefaultLists == null)
{
$aList = cmdbAbstract::FlattenZList(MetaModel::GetZListItems($sClass, 'list'));
}
else
{
$aList = $aDefaultLists[$sAlias];
}
$aSortOrder = MetaModel::GetOrderByDefault($sClass);
if ($bViewLink)
{
$sSort = 'none';
if(array_key_exists('friendlyname', $aSortOrder))
{
$sSort = $aSortOrder['friendlyname'] ? 'asc' : 'desc';
}
$sNormalizedFName = MetaModel::NormalizeFieldSpec($sClass, 'friendlyname');
if(array_key_exists($sNormalizedFName, $aSortOrder))
{
$sSort = $aSortOrder[$sNormalizedFName] ? 'asc' : 'desc';
}
$aColumns[$sAlias]['_key_'] = $oSettings->GetFieldData($sAlias, '_key_', null, true /* bChecked */, $sSort);
}
foreach($aList as $sAttCode)
{
$sSort = 'none';
if(array_key_exists($sAttCode, $aSortOrder))
{
$sSort = $aSortOrder[$sAttCode] ? 'asc' : 'desc';
}
$oAttDef = Metamodel::GetAttributeDef($sClass, $sAttCode);
$aFieldData = $oSettings->GetFieldData($sAlias, $sAttCode, $oAttDef, true /* bChecked */, $sSort);
if ($aFieldData) $aColumns[$sAlias][$sAttCode] = $aFieldData;
}
}
$iDefaultPageSize = appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit());
$oSettings->Init($iDefaultPageSize, $aSortOrder, $aColumns);
return $oSettings;
}
protected function FixVisibleColumns()
{
foreach($this->aClassAliases as $sAlias => $sClass)
{
foreach($this->aColumns[$sAlias] as $sAttCode => $aData)
{
// Remove non-existent columns
// TODO: check if the existing ones are still valid (in case their type changed)
if (($sAttCode != '_key_') && (!MetaModel::IsValidAttCode($sClass, $sAttCode)))
{
unset($this->aColumns[$sAlias][$sAttCode]);
}
}
$aList = MetaModel::ListAttributeDefs($sClass);
// Add the other (non visible ones), sorted in alphabetical order
$aTempData = array();
foreach($aList as $sAttCode => $oAttDef)
{
if ( (!array_key_exists($sAttCode, $this->aColumns[$sAlias])) && (!$oAttDef instanceof AttributeLinkSet))
{
$aFieldData = $this->GetFieldData($sAlias, $sAttCode, $oAttDef, false /* bChecked */, 'none');
if ($aFieldData) $aTempData[$aFieldData['label']] = $aFieldData;
}
}
ksort($aTempData);
foreach($aTempData as $sLabel => $aFieldData)
{
$this->aColumns[$sAlias][$aFieldData['code']] = $aFieldData;
}
}
}
static public function GetTableSettings($aClassAliases, $sTableId = null, $bOnlyOnTable = false)
{
$pref = null;
$oSettings = new DataTableSettings($aClassAliases, $sTableId);
if ($sTableId != null)
{
// An identified table, let's fetch its own settings (if any)
$pref = appUserPreferences::GetPref($oSettings->GetPrefsKey($sTableId), null);
}
if ($pref == null)
{
if (!$bOnlyOnTable)
{
// Try the global preferred values for this class / set of classes
$pref = appUserPreferences::GetPref($oSettings->GetPrefsKey(null), null);
}
if ($pref == null)
{
// no such settings, use the default values provided by the data model
return null;
}
}
$oSettings->unserialize($pref);
return $oSettings;
}
public function GetSortOrder()
{
$aSortOrder = array();
foreach($this->aColumns as $sAlias => $aColumns)
{
foreach($aColumns as $aColumn)
{
if ($aColumn['sort'] != 'none')
{
$sCode = ($aColumn['code'] == '_key_') ? 'friendlyname' : $aColumn['code'];
$aSortOrder[$sCode] = ($aColumn['sort']=='asc'); // true for ascending, false for descending
}
}
break; // TODO: For now the Set object supports only sorting on the first class of the set
}
return $aSortOrder;
}
public function Save($sTargetTableId = null)
{
$sSaveId = is_null($sTargetTableId) ? $this->sTableId : $sTargetTableId;
if ($sSaveId == null) return false; // Cannot save, the table is not identified, use SaveAsDefault instead
$sSettings = $this->serialize();
appUserPreferences::SetPref($this->GetPrefsKey($sSaveId), $sSettings);
return true;
}
public function SaveAsDefault()
{
$sSettings = $this->serialize();
appUserPreferences::SetPref($this->GetPrefsKey(null), $sSettings);
return true;
}
/**
* Clear the preferences for this particular table
* @param $bResetAll boolean If true,the settings for all tables of the same class(es)/alias(es) are reset
*/
public function ResetToDefault($bResetAll)
{
if (($this->sTableId == null) && (!$bResetAll)) return false; // Cannot reset, the table is not identified, use force $bResetAll instead
if ($bResetAll)
{
// Turn the key into a suitable PCRE pattern
$sKey = $this->GetPrefsKey(null);
$sPattern = str_replace(array('|'), array('\\|'), $sKey); // escape the | character
$sPattern = '#^'.str_replace(array('*'), array('.*'), $sPattern).'$#'; // Don't use slash as the delimiter since it's used in our key to delimit aliases
appUserPreferences::UnsetPref($sPattern, true);
}
else
{
appUserPreferences::UnsetPref($this->GetPrefsKey($this->sTableId), false);
}
return true;
}
protected function GetPrefsKey($sTableId = null)
{
if ($sTableId == null) $sTableId = '*';
$aKeys = array();
foreach($this->aClassAliases as $sAlias => $sClass)
{
$aKeys[] = $sAlias.'-'.$sClass;
}
return implode('/', $aKeys).'|'.$sTableId;
}
protected function GetFieldData($sAlias, $sAttCode, $oAttDef, $bChecked, $sSort)
{
$ret = false;
if ($sAttCode == '_key_')
{
$sLabel = Dict::Format('UI:ExtKey_AsLink', MetaModel::GetName($this->aClassAliases[$sAlias]));
$ret = array(
'label' => $sLabel,
'checked' => true,
'disabled' => true,
'alias' => $sAlias,
'code' => $sAttCode,
'sort' => $sSort,
);
}
else if (!$oAttDef->IsLinkSet())
{
$sLabel = $oAttDef->GetLabel();
if ($oAttDef->IsExternalKey())
{
$sLabel = Dict::Format('UI:ExtKey_AsLink', $oAttDef->GetLabel());
}
else if ($oAttDef->IsExternalField())
{
$oExtAttDef = $oAttDef->GetExtAttDef();
$sLabel = Dict::Format('UI:ExtField_AsRemoteField', $oAttDef->GetLabel(), $oExtAttDef->GetLabel());
}
elseif ($oAttDef instanceof AttributeFriendlyName)
{
$sLabel = Dict::Format('UI:ExtKey_AsFriendlyName', $oAttDef->GetLabel());
}
$ret = array(
'label' => $sLabel,
'checked' => $bChecked,
'disabled' => false,
'alias' => $sAlias,
'code' => $sAttCode,
'sort' => $sSort,
);
}
return $ret;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,536 @@
<?php
require_once('xlsxwriter.class.php');
class ExcelExporter
{
protected $sToken;
protected $aStatistics;
protected $sState;
protected $fStartTime;
protected $oSearch;
protected $aObjectsIDs;
protected $aTableHeaders;
protected $aAuthorizedClasses;
protected $iChunkSize = 1000;
protected $iPosition;
protected $sOutputFilePath;
protected $bAdvancedMode;
public function __construct($sToken = null)
{
$this->aStatistics = array(
'objects_count' => 0,
'total_duration' => 0,
'data_retrieval_duration' => 0,
'excel_build_duration' => 0,
'excel_write_duration' => 0,
'peak_memory_usage' => 0,
);
$this->fStartTime = microtime(true);
$this->oSearch = null;
$this->sState = 'new';
$this->aObjectsIDs = array();
$this->iPosition = 0;
$this->aAuthorizedClasses = null;
$this->aTableHeaders = null;
$this->sOutputFilePath = null;
$this->bAdvancedMode = false;
$this->CheckDataDir();
if ($sToken == null)
{
$this->sToken = $this->GetNewToken();
}
else
{
$this->sToken = $sToken;
$this->ReloadState();
}
}
public function __destruct()
{
if (($this->sState != 'done') && ($this->sState != 'error') && ($this->sToken != null))
{
// Operation in progress, save the state
$this->SaveState();
}
else
{
// Operation completed, cleanup the temp files
@unlink($this->GetStateFile());
@unlink($this->GetDataFile());
}
self::CleanupOldFiles();
}
public function SetChunkSize($iChunkSize)
{
$this->iChunkSize = $iChunkSize;
}
public function SetOutputFilePath($sDestFilePath)
{
$this->sOutputFilePath = $sDestFilePath;
}
public function SetAdvancedMode($bAdvanced)
{
$this->bAdvancedMode = $bAdvanced;
}
public function SaveState()
{
$aState = array(
'state' => $this->sState,
'statistics' => $this->aStatistics,
'filter' => $this->oSearch->serialize(),
'position' => $this->iPosition,
'chunk_size' => $this->iChunkSize,
'object_ids' => $this->aObjectsIDs,
'output_file_path' => $this->sOutputFilePath,
'advanced_mode' => $this->bAdvancedMode,
);
file_put_contents($this->GetStateFile(), json_encode($aState));
return $this->sToken;
}
public function ReloadState()
{
if ($this->sToken == null)
{
throw new Exception('ExcelExporter not initialized with a token, cannot reload state');
}
if (!file_exists($this->GetStateFile()))
{
throw new Exception("ExcelExporter: missing status file '".$this->GetStateFile()."', cannot reload state.");
}
$sJson = file_get_contents($this->GetStateFile());
$aState = json_decode($sJson, true);
if ($aState === null)
{
throw new Exception("ExcelExporter:corrupted status file '".$this->GetStateFile()."', not a JSON, cannot reload state.");
}
$this->sState = $aState['state'];
$this->aStatistics = $aState['statistics'];
$this->oSearch = DBObjectSearch::unserialize($aState['filter']);
$this->iPosition = $aState['position'];
$this->iChunkSize = $aState['chunk_size'];
$this->aObjectsIDs = $aState['object_ids'];
$this->sOutputFilePath = $aState['output_file_path'];
$this->bAdvancedMode = $aState['advanced_mode'];
}
public function SetObjectList($oSearch)
{
$this->oSearch = $oSearch;
}
public function Run()
{
$sCode = 'error';
$iPercentage = 100;
$sMessage = Dict::Format('ExcelExporter:ErrorUnexpected_State', $this->sState);
$fTime = microtime(true);
try
{
switch($this->sState)
{
case 'new':
$oIDSet = new DBObjectSet($this->oSearch);
$oIDSet->OptimizeColumnLoad(array('id'));
$this->aObjectsIDs = array();
while($oObj = $oIDSet->Fetch())
{
$this->aObjectsIDs[] = $oObj->GetKey();
}
$sCode = 'retrieving-data';
$iPercentage = 5;
$sMessage = Dict::S('ExcelExporter:RetrievingData');
$this->iPosition = 0;
$this->aStatistics['objects_count'] = count($this->aObjectsIDs);
$this->aStatistics['data_retrieval_duration'] += microtime(true) - $fTime;
// The first line of the file is the "headers" specifying the label and the type of each column
$this->GetFieldsList($oIDSet, $this->bAdvancedMode);
$sRow = json_encode($this->aTableHeaders);
$hFile = @fopen($this->GetDataFile(), 'ab');
if ($hFile === false)
{
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for writing.');
}
fwrite($hFile, $sRow."\n");
fclose($hFile);
// Next state
$this->sState = 'retrieving-data';
break;
case 'retrieving-data':
$oCurrentSearch = clone $this->oSearch;
$aIDs = array_slice($this->aObjectsIDs, $this->iPosition, $this->iChunkSize);
$oCurrentSearch->AddCondition('id', $aIDs, 'IN');
$hFile = @fopen($this->GetDataFile(), 'ab');
if ($hFile === false)
{
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for writing.');
}
$oSet = new DBObjectSet($oCurrentSearch);
$this->GetFieldsList($oSet, $this->bAdvancedMode);
while($aObjects = $oSet->FetchAssoc())
{
$aRow = array();
foreach($this->aAuthorizedClasses as $sAlias => $sClassName)
{
$oObj = $aObjects[$sAlias];
if ($this->bAdvancedMode)
{
$aRow[] = $oObj->GetKey();
}
foreach($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef)
{
$value = $oObj->Get($sAttCodeEx);
if ($value instanceOf ormCaseLog)
{
// Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it!
$sExcelVal = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText()));
}
else
{
$sExcelVal = $oAttDef->GetEditValue($value, $oObj);
}
$aRow[] = $sExcelVal;
}
}
$sRow = json_encode($aRow);
fwrite($hFile, $sRow."\n");
}
fclose($hFile);
if (($this->iPosition + $this->iChunkSize) > count($this->aObjectsIDs))
{
// Next state
$this->sState = 'building-excel';
$sCode = 'building-excel';
$iPercentage = 80;
$sMessage = Dict::S('ExcelExporter:BuildingExcelFile');
}
else
{
$sCode = 'retrieving-data';
$this->iPosition += $this->iChunkSize;
$iPercentage = 5 + round(75 * ($this->iPosition / count($this->aObjectsIDs)));
$sMessage = Dict::S('ExcelExporter:RetrievingData');
}
break;
case 'building-excel':
$hFile = @fopen($this->GetDataFile(), 'rb');
if ($hFile === false)
{
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for reading.');
}
$sHeaders = fgets($hFile);
$aHeaders = json_decode($sHeaders, true);
$aData = array();
while($sLine = fgets($hFile))
{
$aRow = json_decode($sLine);
$aData[] = $aRow;
}
fclose($hFile);
@unlink($this->GetDataFile());
$fStartExcel = microtime(true);
$writer = new XLSXWriter();
$writer->setAuthor(UserRights::GetUserFriendlyName());
$writer->writeSheet($aData,'Sheet1', $aHeaders);
$fExcelTime = microtime(true) - $fStartExcel;
$this->aStatistics['excel_build_duration'] = $fExcelTime;
$fTime = microtime(true);
$writer->writeToFile($this->GetExcelFilePath());
$fExcelSaveTime = microtime(true) - $fTime;
$this->aStatistics['excel_write_duration'] = $fExcelSaveTime;
// Next state
$this->sState = 'done';
$sCode = 'done';
$iPercentage = 100;
$sMessage = Dict::S('ExcelExporter:Done');
break;
case 'done':
$this->sState = 'done';
$sCode = 'done';
$iPercentage = 100;
$sMessage = Dict::S('ExcelExporter:Done');
break;
}
}
catch(Exception $e)
{
$sCode = 'error';
$sMessage = $e->getMessage();
}
$this->aStatistics['total_duration'] += microtime(true) - $fTime;
$peak_memory = memory_get_peak_usage(true);
if ($peak_memory > $this->aStatistics['peak_memory_usage'])
{
$this->aStatistics['peak_memory_usage'] = $peak_memory;
}
return array(
'code' => $sCode,
'message' => $sMessage,
'percentage' => $iPercentage,
);
}
public function GetExcelFilePath()
{
if ($this->sOutputFilePath == null)
{
return APPROOT.'data/bulk_export/'.$this->sToken.'.xlsx';
}
else
{
return $this->sOutputFilePath;
}
}
public static function GetExcelFileFromToken($sToken)
{
return @file_get_contents(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
}
public static function CleanupFromToken($sToken)
{
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.status');
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.data');
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
}
public function Cleanup()
{
self::CleanupFromToken($this->sToken);
}
/**
* Delete all files in the data/bulk_export directory which are older than 1 day
* unless a different delay is configured.
*/
public static function CleanupOldFiles()
{
$aFiles = glob(APPROOT.'data/bulk_export/*.*');
$iDelay = MetaModel::GetConfig()->Get('xlsx_exporter_cleanup_old_files_delay');
if($iDelay > 0)
{
foreach($aFiles as $sFile)
{
$iModificationTime = filemtime($sFile);
if($iModificationTime < (time() - $iDelay))
{
// Temporary files older than one day are deleted
//echo "Supposed to delete: '".$sFile." (Unix Modification Time: $iModificationTime)'\n";
@unlink($sFile);
}
}
}
}
public function DisplayStatistics(Page $oPage)
{
$aStats = array(
'Number of objects exported' => $this->aStatistics['objects_count'],
'Total export duration' => sprintf('%.3f s', $this->aStatistics['total_duration']),
'Data retrieval duration' => sprintf('%.3f s', $this->aStatistics['data_retrieval_duration']),
'Excel build duration' => sprintf('%.3f s', $this->aStatistics['excel_build_duration']),
'Excel write duration' => sprintf('%.3f s', $this->aStatistics['excel_write_duration']),
'Peak memory usage' => self::HumanDisplay($this->aStatistics['peak_memory_usage']),
);
if ($oPage instanceof CLIPage)
{
$oPage->add($this->GetStatistics('text'));
}
else
{
$oPage->add($this->GetStatistics('html'));
}
}
public function GetStatistics($sFormat = 'html')
{
$sStats = '';
$aStats = array(
'Number of objects exported' => $this->aStatistics['objects_count'],
'Total export duration' => sprintf('%.3f s', $this->aStatistics['total_duration']),
'Data retrieval duration' => sprintf('%.3f s', $this->aStatistics['data_retrieval_duration']),
'Excel build duration' => sprintf('%.3f s', $this->aStatistics['excel_build_duration']),
'Excel write duration' => sprintf('%.3f s', $this->aStatistics['excel_write_duration']),
'Peak memory usage' => self::HumanDisplay($this->aStatistics['peak_memory_usage']),
);
if ($sFormat == 'text')
{
foreach($aStats as $sLabel => $sValue)
{
$sStats .= "+------------------------------+----------+\n";
$sStats .= sprintf("|%-30s|%10s|\n", $sLabel, $sValue);
}
$sStats .= "+------------------------------+----------+";
}
else
{
$sStats .= '<table><tbody>';
foreach($aStats as $sLabel => $sValue)
{
$sStats .= "<tr><td>$sLabel</td><td>$sValue</td></tr>";
}
$sStats .= '</tbody></table>';
}
return $sStats;
}
public static function HumanDisplay($iSize)
{
$aUnits = array('B','KB','MB','GB','TB','PB');
return @round($iSize/pow(1024,($i=floor(log($iSize,1024)))),2).' '.$aUnits[$i];
}
protected function CheckDataDir()
{
if(!is_dir(APPROOT."data/bulk_export"))
{
@mkdir(APPROOT."data/bulk_export", 0777, true /* recursive */);
clearstatcache();
}
if (!is_writable(APPROOT."data/bulk_export"))
{
throw new Exception('Data directory "'.APPROOT.'data/bulk_export" could not be written.');
}
}
protected function GetStateFile($sToken = null)
{
if ($sToken == null)
{
$sToken = $this->sToken;
}
return APPROOT."data/bulk_export/$sToken.status";
}
protected function GetDataFile()
{
return APPROOT.'data/bulk_export/'.$this->sToken.'.data';
}
protected function GetNewToken()
{
$iNum = rand();
do
{
$iNum++;
$sToken = sprintf("%08x", $iNum);
$sFileName = $this->GetStateFile($sToken);
$hFile = @fopen($sFileName, 'x');
}
while($hFile === false);
fclose($hFile);
return $sToken;
}
protected function GetFieldsList($oSet, $bFieldsAdvanced = false, $bLocalize = true, $aFields = null)
{
$this->aFieldsList = array();
$oAppContext = new ApplicationContext();
$aClasses = $oSet->GetFilter()->GetSelectedClasses();
$this->aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
{
$this->aAuthorizedClasses[$sAlias] = $sClassName;
}
}
$aAttribs = array();
$this->aTableHeaders = array();
foreach($this->aAuthorizedClasses as $sAlias => $sClassName)
{
$aList[$sAlias] = array();
foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef)
{
if (is_null($aFields) || (count($aFields) == 0))
{
// Standard list of attributes (no link sets)
if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField()))
{
$sAttCodeEx = $oAttDef->IsExternalField() ? $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode() : $sAttCode;
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
{
if ($bFieldsAdvanced)
{
$aList[$sAlias][$sAttCodeEx] = $oAttDef;
if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE))
{
$sRemoteClass = $oAttDef->GetTargetClass();
foreach(MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode)
{
$this->aFieldsList[$sAlias][$sAttCode.'->'.$sRemoteAttCode] = MetaModel::GetAttributeDef($sRemoteClass, $sRemoteAttCode);
}
}
}
}
else
{
// Any other attribute
$this->aFieldsList[$sAlias][$sAttCodeEx] = $oAttDef;
}
}
}
else
{
// User defined list of attributes
if (in_array($sAttCode, $aFields) || in_array($sAlias.'.'.$sAttCode, $aFields))
{
$this->aFieldsList[$sAlias][$sAttCode] = $oAttDef;
}
}
}
if ($bFieldsAdvanced)
{
$this->aTableHeaders['id'] = '0';
}
foreach($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef)
{
$sLabel = $bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx, isset($aParams['showMandatoryFields'])) : $sAttCodeEx;
if($oAttDef instanceof AttributeDateTime)
{
$this->aTableHeaders[$sLabel] = 'datetime';
}
else
{
$this->aTableHeaders[$sLabel] = 'string';
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,30 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Persistent class InputOutputTask
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('../application/cmdbabstract.class.inc.php');
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
/**
* This class manages the input/output tasks

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,27 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class iTopWizardWebPage
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('itopwebpage.class.inc.php');
@@ -48,7 +49,7 @@ class iTopWizardWebPage extends iTopWebPage
$sStyle = ($iIndex == $this->m_iCurrentStep) ? 'wizActiveStep' : 'wizStep';
$aSteps[] = "<div class=\"$sStyle\"><span>$sStepTitle</span></div>";
}
$sWizardHeader = "<div class=\"wizHeader\"><h1>{$this->s_title}</h1>\n".implode("<div class=\"wizSeparator\"><img align=\"bottom\" src=\"../images/wizArrow.gif\"></div>", $aSteps)."<br style=\"clear:both;\"/></div>\n";
$sWizardHeader = "<div class=\"wizHeader\"><h1>".htmlentities($this->s_title, ENT_QUOTES, 'UTF-8')."</h1>\n".implode("<div class=\"wizSeparator\"><img align=\"bottom\" src=\"../images/wizArrow.gif\"></div>", $aSteps)."<br style=\"clear:both;\"/></div>\n";
$this->s_content = "$sWizardHeader<div class=\"wizContainer\">".$this->s_content."</div>";
parent::output();
}

View File

@@ -1,97 +1,120 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2013 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class LoginWebPage
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once("../application/nicewebpage.class.inc.php");
require_once(APPROOT."/application/nicewebpage.class.inc.php");
require_once(APPROOT.'/application/portaldispatcher.class.inc.php');
/**
* Web page used for displaying the login form
*/
class LoginWebPage extends NiceWebPage
{
public function __construct()
{
parent::__construct("iTop Login");
$this->add_style(<<<EOF
body {
background: #eee;
margin: 0;
padding: 0;
}
#login-logo {
margin-top: 150px;
width: 300px;
padding-left: 20px;
padding-right: 20px;
padding-top: 10px;
padding-bottom: 10px;
margin-left: auto;
margin-right: auto;
background: #f6f6f1;
height: 54px;
border-top: 1px solid #000;
border-left: 1px solid #000;
border-right: 1px solid #000;
border-bottom: 0;
text-align: center;
}
#login-logo img {
border: 0;
}
#login {
width: 300px;
margin-left: auto;
margin-right: auto;
padding: 20px;
background-color: #fff;
border-bottom: 1px solid #000;
border-left: 1px solid #000;
border-right: 1px solid #000;
border-top: 0;
}
.center {
text-align: center;
}
const EXIT_PROMPT = 0;
const EXIT_HTTP_401 = 1;
const EXIT_RETURN = 2;
const EXIT_CODE_OK = 0;
const EXIT_CODE_MISSINGLOGIN = 1;
const EXIT_CODE_MISSINGPASSWORD = 2;
const EXIT_CODE_WRONGCREDENTIALS = 3;
const EXIT_CODE_MUSTBEADMIN = 4;
const EXIT_CODE_PORTALUSERNOTAUTHORIZED = 5;
protected static $sHandlerClass = __class__;
public static function RegisterHandler($sClass)
{
self::$sHandlerClass = $sClass;
}
h1 {
color: #1C94C4;
font-size: 16pt;
}
.v-spacer {
padding-top: 1em;
}
EOF
);
public static function NewLoginWebPage()
{
return new self::$sHandlerClass;
}
protected static $m_sLoginFailedMessage = '';
public function __construct($sTitle = 'iTop Login')
{
parent::__construct($sTitle);
$this->SetStyleSheet();
$this->add_header("Cache-control: no-cache");
}
public function SetStyleSheet()
{
$this->add_linked_stylesheet("../css/login.css");
}
public static function SetLoginFailedMessage($sMessage)
{
self::$m_sLoginFailedMessage = $sMessage;
}
public function EnableResetPassword()
{
return MetaModel::GetConfig()->Get('forgot_password');
}
public function DisplayLoginHeader($bMainAppLogo = false)
{
if ($bMainAppLogo)
{
$sLogo = 'itop-logo.png';
$sBrandingLogo = 'main-logo.png';
}
else
{
$sLogo = 'itop-logo-external.png';
$sBrandingLogo = 'login-logo.png';
}
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo.'?itopversion='.ITOP_VERSION;
if (file_exists(MODULESROOT.'branding/'.$sBrandingLogo))
{
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo.'?itopversion='.ITOP_VERSION;
}
$this->add("<div id=\"login-logo\"><a href=\"".htmlentities($sIconUrl, ENT_QUOTES, 'UTF-8')."\"><img title=\"$sVersionShort\" src=\"$sDisplayIcon\"></a></div>\n");
}
public function DisplayLoginForm($sLoginType, $bFailedLogin = false)
{
switch($sLoginType)
{
case 'cas':
utils::InitCASClient();
// force CAS authentication
phpCAS::forceAuthentication(); // Will redirect the user and exit since the user is not yet authenticated
break;
case 'basic':
case 'url':
$this->add_header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
$this->add_header('HTTP/1.0 401 Unauthorized');
$this->add_header('Content-type: text/html; charset=iso-8859-1');
// Note: displayed when the user will click on Cancel
$this->add('<p><strong>'.Dict::S('UI:Login:Error:AccessRestricted').'</strong></p>');
break;
@@ -99,16 +122,22 @@ EOF
case 'external':
case 'form':
default: // In case the settings get messed up...
$sAuthUser = utils::ReadParam('auth_user', '');
$sAuthPwd = utils::ReadParam('suggest_pwd', '');
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
$sAuthPwd = utils::ReadParam('suggest_pwd', '', true, 'raw_data');
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
$this->add("<div id=\"login-logo\"><a href=\"http://www.combodo.com/itop\"><img title=\"$sVersionShort\" src=\"../images/itop-logo.png\"></a></div>\n");
$this->DisplayLoginHeader();
$this->add("<div id=\"login\">\n");
$this->add("<h1>".Dict::S('UI:Login:Welcome')."</h1>\n");
if ($bFailedLogin)
{
$this->add("<p class=\"hilite\">".Dict::S('UI:Login:IncorrectLoginPassword')."</p>\n");
if (self::$m_sLoginFailedMessage != '')
{
$this->add("<p class=\"hilite\">".self::$m_sLoginFailedMessage."</p>\n");
}
else
{
$this->add("<p class=\"hilite\">".Dict::S('UI:Login:IncorrectLoginPassword')."</p>\n");
}
}
else
{
@@ -116,23 +145,240 @@ EOF
}
$this->add("<form method=\"post\">\n");
$this->add("<table>\n");
$this->add("<tr><td><label for=\"user\">".Dict::S('UI:Login:UserNamePrompt').":</label></td><td><input id=\"user\" type=\"text\" name=\"auth_user\" value=\"$sAuthUser\" /></td></tr>\n");
$this->add("<tr><td><label for=\"pwd\">".Dict::S('UI:Login:PasswordPrompt').":</label></td><td><input id=\"pwd\" type=\"password\" name=\"auth_pwd\" value=\"$sAuthPwd\" /></td></tr>\n");
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"> <input type=\"submit\" value=\"".Dict::S('UI:Button:Login')."\" /></td></tr>\n");
$sForgotPwd = $this->EnableResetPassword() ? $this->ForgotPwdLink() : '';
$this->add("<tr><td style=\"text-align:right\"><label for=\"user\">".Dict::S('UI:Login:UserNamePrompt').":</label></td><td style=\"text-align:left\"><input id=\"user\" type=\"text\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" /></td></tr>\n");
$this->add("<tr><td style=\"text-align:right\"><label for=\"pwd\">".Dict::S('UI:Login:PasswordPrompt').":</label></td><td style=\"text-align:left\"><input id=\"pwd\" type=\"password\" name=\"auth_pwd\" value=\"".htmlentities($sAuthPwd, ENT_QUOTES, 'UTF-8')."\" /></td></tr>\n");
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"submit\" value=\"".Dict::S('UI:Button:Login')."\" /></span></td></tr>\n");
if (strlen($sForgotPwd) > 0)
{
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\">$sForgotPwd</td></tr>\n");
}
$this->add("</table>\n");
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"login\" />\n");
$this->add_ready_script('$("#user").focus();');
// Keep the OTHER parameters posted
foreach($_POST as $sPostedKey => $postedValue)
{
if (!in_array($sPostedKey, array('auth_user', 'auth_pwd')))
{
if (is_array($postedValue))
{
foreach($postedValue as $sKey => $sValue)
{
$this->add("<input type=\"hidden\" name=\"".htmlentities($sPostedKey, ENT_QUOTES, 'UTF-8')."[".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."]\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n");
}
}
else
{
$this->add("<input type=\"hidden\" name=\"".htmlentities($sPostedKey, ENT_QUOTES, 'UTF-8')."\" value=\"".htmlentities($postedValue, ENT_QUOTES, 'UTF-8')."\" />\n");
}
}
}
$this->add("</form>\n");
$this->add(Dict::S('UI:Login:About'));
$this->add("</div>\n");
break;
}
}
/**
* Return '' to disable this feature
*/
public function ForgotPwdLink()
{
$sUrl = '?loginop=forgot_pwd';
$sHtml = "<a href=\"$sUrl\" target=\"_blank\">".Dict::S('UI:Login:ForgotPwd')."</a>";
return $sHtml;
}
public function DisplayForgotPwdForm($bFailedToReset = false, $sFailureReason = null)
{
$this->DisplayLoginHeader();
$this->add("<div id=\"login\">\n");
$this->add("<h1>".Dict::S('UI:Login:ForgotPwdForm')."</h1>\n");
$this->add("<p>".Dict::S('UI:Login:ForgotPwdForm+')."</p>\n");
if ($bFailedToReset)
{
$this->add("<p class=\"hilite\">".Dict::Format('UI:Login:ResetPwdFailed', htmlentities($sFailureReason, ENT_QUOTES, 'UTF-8'))."</p>\n");
}
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
$this->add("<form method=\"post\">\n");
$this->add("<table>\n");
$this->add("<tr><td colspan=\"2\" class=\"center\"><label for=\"user\">".Dict::S('UI:Login:UserNamePrompt').":</label><input id=\"user\" type=\"text\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" /></td></tr>\n");
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"button\" onClick=\"window.close();\" value=\"".Dict::S('UI:Button:Cancel')."\" /></span>&nbsp;&nbsp;<span class=\"btn_border\"><input type=\"submit\" value=\"".Dict::S('UI:Login:ResetPassword')."\" /></span></td></tr>\n");
$this->add("</table>\n");
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"forgot_pwd_go\" />\n");
$this->add("</form>\n");
$this->add("</div>\n");
$this->add_ready_script('$("#user").focus();');
}
protected function ForgotPwdGo()
{
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
try
{
UserRights::Login($sAuthUser); // Set the user's language (if possible!)
$oUser = UserRights::GetUserObject();
if ($oUser == null)
{
throw new Exception(Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser));
}
if (!MetaModel::IsValidAttCode(get_class($oUser), 'reset_pwd_token'))
{
throw new Exception(Dict::S('UI:ResetPwd-Error-NotPossible'));
}
if (!$oUser->CanChangePassword())
{
throw new Exception(Dict::S('UI:ResetPwd-Error-FixedPwd'));
}
$sTo = $oUser->GetResetPasswordEmail(); // throws Exceptions if not allowed
if ($sTo == '')
{
throw new Exception(Dict::S('UI:ResetPwd-Error-NoEmail'));
}
// This token allows the user to change the password without knowing the previous one
$sToken = substr(md5(APPROOT.uniqid()), 0, 16);
$oUser->Set('reset_pwd_token', $sToken);
CMDBObject::SetTrackInfo('Reset password');
$oUser->DBUpdate();
$oEmail = new Email();
$oEmail->SetRecipientTO($sTo);
$sFrom = MetaModel::GetConfig()->Get('forgot_password_from');
if ($sFrom == '')
{
$sFrom = $sTo;
}
$oEmail->SetRecipientFrom($sFrom);
$oEmail->SetSubject(Dict::S('UI:ResetPwd-EmailSubject'));
$sResetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=reset_pwd&auth_user='.urlencode($oUser->Get('login')).'&token='.urlencode($sToken);
$oEmail->SetBody(Dict::Format('UI:ResetPwd-EmailBody', $sResetUrl));
$iRes = $oEmail->Send($aIssues, true /* force synchronous exec */);
switch ($iRes)
{
//case EMAIL_SEND_PENDING:
case EMAIL_SEND_OK:
break;
case EMAIL_SEND_ERROR:
default:
IssueLog::Error('Failed to send the email with the NEW password for '.$oUser->Get('friendlyname').': '.implode(', ', $aIssues));
throw new Exception(Dict::S('UI:ResetPwd-Error-Send'));
}
$this->DisplayLoginHeader();
$this->add("<div id=\"login\">\n");
$this->add("<h1>".Dict::S('UI:Login:ForgotPwdForm')."</h1>\n");
$this->add("<p>".Dict::S('UI:ResetPwd-EmailSent')."</p>");
$this->add("<form method=\"post\">\n");
$this->add("<table>\n");
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><input type=\"button\" onClick=\"window.close();\" value=\"".Dict::S('UI:Button:Done')."\" /></td></tr>\n");
$this->add("</table>\n");
$this->add("</form>\n");
$this->add("</div\n");
}
catch(Exception $e)
{
$this->DisplayForgotPwdForm(true, $e->getMessage());
}
}
public function DisplayResetPwdForm()
{
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sToken = utils::ReadParam('token', '', false, 'raw_data');
UserRights::Login($sAuthUser); // Set the user's language
$oUser = UserRights::GetUserObject();
$this->DisplayLoginHeader();
$this->add("<div id=\"login\">\n");
$this->add("<h1>".Dict::S('UI:ResetPwd-Title')."</h1>\n");
if ($oUser == null)
{
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n");
}
elseif ($oUser->Get('reset_pwd_token') != $sToken)
{
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
}
else
{
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-EnterPassword', $oUser->GetFriendlyName())."</p>\n");
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch');
$this->add_script(
<<<EOF
function DoCheckPwd()
{
if ($('#new_pwd').val() != $('#retype_new_pwd').val())
{
alert('$sInconsistenPwdMsg');
return false;
}
return true;
}
EOF
);
$this->add("<form method=\"post\">\n");
$this->add("<table>\n");
$this->add("<tr><td style=\"text-align:right\"><label for=\"new_pwd\">".Dict::S('UI:Login:NewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"new_pwd\" name=\"new_pwd\" value=\"\" /></td></tr>\n");
$this->add("<tr><td style=\"text-align:right\"><label for=\"retype_new_pwd\">".Dict::S('UI:Login:RetypeNewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"retype_new_pwd\" name=\"retype_new_pwd\" value=\"\" /></td></tr>\n");
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></span></td></tr>\n");
$this->add("</table>\n");
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"do_reset_pwd\" />\n");
$this->add("<input type=\"hidden\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" />\n");
$this->add("<input type=\"hidden\" name=\"token\" value=\"".htmlentities($sToken, ENT_QUOTES, 'UTF-8')."\" />\n");
$this->add("</form>\n");
$this->add("</div\n");
}
}
public function DoResetPassword()
{
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sToken = utils::ReadParam('token', '', false, 'raw_data');
$sNewPwd = utils::ReadPostedParam('new_pwd', '', false, 'raw_data');
UserRights::Login($sAuthUser); // Set the user's language
$oUser = UserRights::GetUserObject();
$this->DisplayLoginHeader();
$this->add("<div id=\"login\">\n");
$this->add("<h1>".Dict::S('UI:ResetPwd-Title')."</h1>\n");
if ($oUser == null)
{
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n");
}
elseif ($oUser->Get('reset_pwd_token') != $sToken)
{
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
}
else
{
// Trash the token and change the password
$oUser->Set('reset_pwd_token', '');
$oUser->SetPassword($sNewPwd); // Does record the change into the DB
$this->add("<p>".Dict::S('UI:ResetPwd-Ready')."</p>");
$sUrl = utils::GetAbsoluteUrlAppRoot();
$this->add("<p><a href=\"$sUrl\">".Dict::S('UI:ResetPwd-Login')."</a></p>");
}
$this->add("</div\n");
}
public function DisplayChangePwdForm($bFailedLogin = false)
{
$sAuthUser = utils::ReadParam('auth_user', '');
$sAuthPwd = utils::ReadParam('suggest_pwd', '');
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch');
$this->add_script(<<<EOF
function GoBack()
@@ -151,7 +397,7 @@ function DoCheckPwd()
}
EOF
);
$this->add("<div id=\"login-logo\"><a href=\"http://www.combodo.com/itop\"><img title=\"$sVersionShort\" src=\"../images/itop-logo.png\"></a></div>\n");
$this->DisplayLoginHeader();
$this->add("<div id=\"login\">\n");
$this->add("<h1>".Dict::S('UI:Login:ChangeYourPassword')."</h1>\n");
if ($bFailedLogin)
@@ -160,10 +406,10 @@ EOF
}
$this->add("<form method=\"post\">\n");
$this->add("<table>\n");
$this->add("<tr><td><label for=\"old_pwd\">".Dict::S('UI:Login:OldPasswordPrompt').":</label></td><td><input type=\"password\" id=\"old_pwd\" name=\"old_pwd\" value=\"\" /></td></tr>\n");
$this->add("<tr><td><label for=\"new_pwd\">".Dict::S('UI:Login:NewPasswordPrompt').":</label></td><td><input type=\"password\" id=\"new_pwd\" name=\"new_pwd\" value=\"\" /></td></tr>\n");
$this->add("<tr><td><label for=\"retype_new_pwd\">".Dict::S('UI:Login:RetypeNewPasswordPrompt').":</label></td><td><input type=\"password\" id=\"retype_new_pwd\" name=\"retype_new_pwd\" value=\"\" /></td></tr>\n");
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"> <input type=\"button\" onClick=\"GoBack();\" value=\"".Dict::S('UI:Button:Cancel')."\" />&nbsp;&nbsp;<input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></td></tr>\n");
$this->add("<tr><td style=\"text-align:right\"><label for=\"old_pwd\">".Dict::S('UI:Login:OldPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"old_pwd\" name=\"old_pwd\" value=\"\" /></td></tr>\n");
$this->add("<tr><td style=\"text-align:right\"><label for=\"new_pwd\">".Dict::S('UI:Login:NewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"new_pwd\" name=\"new_pwd\" value=\"\" /></td></tr>\n");
$this->add("<tr><td style=\"text-align:right\"><label for=\"retype_new_pwd\">".Dict::S('UI:Login:RetypeNewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"retype_new_pwd\" name=\"retype_new_pwd\" value=\"\" /></td></tr>\n");
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"button\" onClick=\"GoBack();\" value=\"".Dict::S('UI:Button:Cancel')."\" /></span>&nbsp;&nbsp;<span class=\"btn_border\"><input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></span></td></tr>\n");
$this->add("</table>\n");
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"do_change_pwd\" />\n");
$this->add("</form>\n");
@@ -181,51 +427,58 @@ EOF
$sPreviousLoginMode = '';
}
// Unset all of the session variables.
$_SESSION = array();
unset($_SESSION['auth_user']);
unset($_SESSION['login_mode']);
unset($_SESSION['profile_list']);
// If it's desired to kill the session, also delete the session cookie.
// Note: This will destroy the session, and not just the session data!
if (isset($_COOKIE[session_name()]))
{
setcookie(session_name(), '', time()-3600, '/');
}
// Finally, destroy the session.
session_destroy();
}
static function SecureConnectionRequired()
{
return utils::GetConfig()->GetSecureConnectionRequired();
return MetaModel::GetConfig()->GetSecureConnectionRequired();
}
static function IsConnectionSecure()
/**
* Guess if a string looks like an UTF-8 string based on some ranges of multi-bytes encoding
* @param string $sString
* @return bool True if the string contains some typical UTF-8 multi-byte sequences
*/
static function LooksLikeUTF8($sString)
{
$bSecured = false;
if ( !empty($_SERVER['HTTPS']) && ($_SERVER['HTTPS']!= 'off') )
{
$bSecured = true;
}
return $bSecured;
return preg_match('%(?:
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
|\xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
|\xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
|\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
|[\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
|\xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)+%xs', $sString);
}
protected static function Login()
/**
* Attempt a login
*
* @param int iOnExit What action to take if the user is not logged on (one of the class constants EXIT_...)
* @return int One of the class constants EXIT_CODE_...
*/
protected static function Login($iOnExit)
{
if (self::SecureConnectionRequired() && !self::IsConnectionSecure())
if (self::SecureConnectionRequired() && !utils::IsConnectionSecure())
{
// Non secured URL... redirect to a secured one
$sUrl = Utils::GetAbsoluteUrl(true /* query string */, true /* force HTTPS */);
header("Location: $sUrl");
exit;
// Non secured URL... request for a secure connection
throw new Exception('Secure connection required!');
}
$aAllowedLoginTypes = utils::GetConfig()->GetAllowedLoginTypes();
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
if (isset($_SESSION['auth_user']))
{
//echo "User: ".$_SESSION['auth_user']."\n";
// Already authentified
UserRights::Login($_SESSION['auth_user']); // Login & set the user's language
return true;
return self::EXIT_CODE_OK;
}
else
{
@@ -237,11 +490,23 @@ EOF
$sLoginType = $aAllowedLoginTypes[$index];
switch($sLoginType)
{
case 'cas':
utils::InitCASClient();
// check CAS authentication
if (phpCAS::isAuthenticated())
{
$sAuthUser = phpCAS::getUser();
$sAuthPwd = '';
$sLoginMode = 'cas';
$sAuthentication = 'external';
}
break;
case 'form':
// iTop standard mode: form based authentication
$sAuthUser = utils::ReadPostedParam('auth_user', '');
$sAuthPwd = utils::ReadPostedParam('auth_pwd', '');
if ($sAuthUser != '')
$sAuthUser = utils::ReadPostedParam('auth_user', '', false, 'raw_data');
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, false, 'raw_data');
if (($sAuthUser != '') && ($sAuthPwd !== null))
{
$sLoginMode = 'form';
}
@@ -258,7 +523,22 @@ EOF
else if (isset($_SERVER['PHP_AUTH_USER']))
{
$sAuthUser = $_SERVER['PHP_AUTH_USER'];
// Unfortunately, the RFC is not clear about the encoding...
// IE and FF supply the user and password encoded in ISO-8859-1 whereas Chrome provides them encoded in UTF-8
// So let's try to guess if it's an UTF-8 string or not... fortunately all encodings share the same ASCII base
if (!self::LooksLikeUTF8($sAuthUser))
{
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
// Supposed to be harmless in case of a plain ASCII string...
$sAuthUser = iconv('iso-8859-1', 'utf-8', $sAuthUser);
}
$sAuthPwd = $_SERVER['PHP_AUTH_PW'];
if (!self::LooksLikeUTF8($sAuthPwd))
{
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
// Supposed to be harmless in case of a plain ASCII string...
$sAuthPwd = iconv('iso-8859-1', 'utf-8', $sAuthPwd);
}
$sLoginMode = 'basic';
}
break;
@@ -266,12 +546,10 @@ EOF
case 'external':
// Web server supplied authentication
$bExternalAuth = false;
$sExtAuthVar = utils::GetConfig()->GetExternalAuthenticationVariable(); // In which variable is the info passed ?
$sEval = '$bExternalAuth = isset('.$sExtAuthVar.');';
eval($sEval);
if ($bExternalAuth)
{
eval('$sAuthUser = '.$sExtAuthVar.';'); // Retrieve the value
$sExtAuthVar = MetaModel::GetConfig()->GetExternalAuthenticationVariable(); // In which variable is the info passed ?
eval('$sAuthUser = isset('.$sExtAuthVar.') ? '.$sExtAuthVar.' : false;'); // Retrieve the value
if ($sAuthUser && (strlen($sAuthUser) > 0))
{
$sAuthPwd = ''; // No password in this case the web server already authentified the user...
$sLoginMode = 'external';
$sAuthentication = 'external';
@@ -280,10 +558,10 @@ EOF
case 'url':
// Credentials passed directly in the url
$sAuthUser = utils::ReadParam('auth_user', '');
if ($sAuthUser != '')
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
if (($sAuthUser != '') && ($sAuthPwd !== null))
{
$sAuthPwd = utils::ReadParam('auth_pwd', '');
$sLoginMode = 'url';
}
break;
@@ -303,30 +581,115 @@ EOF
{
$sLoginMode = $aAllowedLoginTypes[0]; // First in the list...
}
$oPage = new LoginWebPage();
$oPage->DisplayLoginForm( $sLoginMode, false /* no previous failed attempt */);
$oPage->output();
exit;
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))
{
return self::EXIT_CODE_MISSINGPASSWORD;
}
else
{
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, $sAuthentication))
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $sLoginMode, $sAuthentication))
{
//echo "Check Credentials returned false for user $sAuthUser!";
self::ResetSession();
$oPage = new LoginWebPage();
$oPage->DisplayLoginForm( $sLoginMode, true /* failed attempt */);
$oPage->output();
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)
{
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;
}
}
}
return self::EXIT_CODE_OK;
}
/**
* Overridable: depending on the user, head toward a dedicated portal
* @param string|null $sRequestedPortalId
* @param int $iOnExit How to complete the call: redirect or return a code
*/
protected static function ChangeLocation($sRequestedPortalId = null, $iOnExit = self::EXIT_PROMPT)
{
$fStart = microtime(true);
$ret = call_user_func(array(self::$sHandlerClass, 'Dispatch'), $sRequestedPortalId);
if ($ret === true)
{
return self::EXIT_CODE_OK;
}
else if($ret === false)
{
throw new Exception('Nowhere to go??');
}
else
{
if ($iOnExit == self::EXIT_RETURN)
{
return self::EXIT_CODE_PORTALUSERNOTAUTHORIZED;
}
else
{
// No rights to be here, redirect to the portal
header('Location: '.$ret);
}
}
}
/**
@@ -335,12 +698,60 @@ EOF
* - if $bIsAllowedToPortalUsers is false and the user has only access to the portal, then the user is redirected to the portal
* @param bool $bMustBeAdmin Whether or not the user must be an admin to access the current page
* @param bool $bIsAllowedToPortalUsers Whether or not the current page is considered as part of the portal
* @param int iOnExit What action to take if the user is not logged on (one of the class constants EXIT_...)
*/
static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false)
static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false, $iOnExit = self::EXIT_PROMPT)
{
$sRequestedPortalId = $bIsAllowedToPortalUsers ? 'legacy_portal' : 'backoffice';
return self::DoLoginEx($sRequestedPortalId, $bMustBeAdmin, $iOnExit);
}
/**
* Check if the user is already authentified, if yes, then performs some additional validations to redirect towards the desired "portal"
* @param string|null $sRequestedPortalId The requested "portal" interface, null for any
* @param bool $bMustBeAdmin Whether or not the user must be an admin to access the current page
* @param int iOnExit What action to take if the user is not logged on (one of the class constants EXIT_...)
*/
static function DoLoginEx($sRequestedPortalId = null, $bMustBeAdmin = false, $iOnExit = self::EXIT_PROMPT)
{
$operation = utils::ReadParam('loginop', '');
session_start();
$sMessage = self::HandleOperations($operation); // May exit directly
$iRet = self::Login($iOnExit);
if ($iRet == self::EXIT_CODE_OK)
{
if ($bMustBeAdmin && !UserRights::IsAdministrator())
{
if ($iOnExit == self::EXIT_RETURN)
{
return self::EXIT_CODE_MUSTBEADMIN;
}
else
{
require_once(APPROOT.'/setup/setuppage.class.inc.php');
$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n");
$oP->p("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>");
$oP->output();
exit;
}
}
$iRet = call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $sRequestedPortalId, $iOnExit);
}
if ($iOnExit == self::EXIT_RETURN)
{
return $iRet;
}
else
{
return $sMessage;
}
}
protected static function HandleOperations($operation)
{
$sMessage = ''; // most of the operations never return, but some can return a message to be displayed
if ($operation == 'logoff')
{
if (isset($_SESSION['login_mode']))
@@ -349,7 +760,7 @@ EOF
}
else
{
$aAllowedLoginTypes = utils::GetConfig()->GetAllowedLoginTypes();
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
if (count($aAllowedLoginTypes) > 0)
{
$sLoginMode = $aAllowedLoginTypes[0];
@@ -360,16 +771,44 @@ EOF
}
}
self::ResetSession();
$oPage = new LoginWebPage();
$oPage = self::NewLoginWebPage();
$oPage->DisplayLoginForm( $sLoginMode, false /* not a failed attempt */);
$oPage->output();
exit;
}
}
else if ($operation == 'forgot_pwd')
{
$oPage = self::NewLoginWebPage();
$oPage->DisplayForgotPwdForm();
$oPage->output();
exit;
}
else if ($operation == 'forgot_pwd_go')
{
$oPage = self::NewLoginWebPage();
$oPage->ForgotPwdGo();
$oPage->output();
exit;
}
else if ($operation == 'reset_pwd')
{
$oPage = self::NewLoginWebPage();
$oPage->DisplayResetPwdForm();
$oPage->output();
exit;
}
else if ($operation == 'do_reset_pwd')
{
$oPage = self::NewLoginWebPage();
$oPage->DoResetPassword();
$oPage->output();
exit;
}
else if ($operation == 'change_pwd')
{
$sAuthUser = $_SESSION['auth_user'];
UserRights::Login($sAuthUser); // Set the user's language
$oPage = new LoginWebPage();
$oPage = self::NewLoginWebPage();
$oPage->DisplayChangePwdForm();
$oPage->output();
exit;
@@ -378,33 +817,65 @@ EOF
{
$sAuthUser = $_SESSION['auth_user'];
UserRights::Login($sAuthUser); // Set the user's language
$sOldPwd = utils::ReadPostedParam('old_pwd');
$sNewPwd = utils::ReadPostedParam('new_pwd');
$sOldPwd = utils::ReadPostedParam('old_pwd', '', false, 'raw_data');
$sNewPwd = utils::ReadPostedParam('new_pwd', '', false, 'raw_data');
if (UserRights::CanChangePassword() && ((!UserRights::CheckCredentials($sAuthUser, $sOldPwd)) || (!UserRights::ChangePassword($sOldPwd, $sNewPwd))))
{
$oPage = new LoginWebPage();
$oPage = self::NewLoginWebPage();
$oPage->DisplayChangePwdForm(true); // old pwd was wrong
$oPage->output();
exit;
}
$sMessage = Dict::S('UI:Login:PasswordChanged');
}
return $sMessage;
}
protected static function Dispatch($sRequestedPortalId)
{
if ($sRequestedPortalId === null) return true; // allowed to any portal => return true
$aPortalsConf = PortalDispatcherData::GetData();
$aDispatchers = array();
foreach($aPortalsConf as $sPortalId => $aConf)
{
$sHandlerClass = $aConf['handler'];
$aDispatchers[$sPortalId] = new $sHandlerClass($sPortalId);
}
self::Login();
if ($bMustBeAdmin && !UserRights::IsAdministrator())
{
require_once('../setup/setuppage.class.inc.php');
$oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n");
$oP->p("<a href=\"../pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>");
$oP->output();
exit;
}
elseif ( (!$bIsAllowedToPortalUsers) && (UserRights::IsPortalUser()))
if (array_key_exists($sRequestedPortalId, $aDispatchers) && $aDispatchers[$sRequestedPortalId]->IsUserAllowed())
{
// No rights to be here, redirect to the portal
header('Location: ../portal/index.php');
return true;
}
foreach($aDispatchers as $sPortalId => $oDispatcher)
{
if ($oDispatcher->IsUserAllowed()) return $oDispatcher->GetUrl();
}
return false; // nothing matched !!
}
public static function GetAllowedPortals()
{
$aAllowedPortals = array();
$aPortalsConf = PortalDispatcherData::GetData();
$aDispatchers = array();
foreach($aPortalsConf as $sPortalId => $aConf)
{
$sHandlerClass = $aConf['handler'];
$aDispatchers[$sPortalId] = new $sHandlerClass($sPortalId);
}
foreach($aDispatchers as $sPortalId => $oDispatcher)
{
if ($oDispatcher->IsUserAllowed())
{
$aAllowedPortals[] = array(
'id' => $sPortalId,
'label' => $oDispatcher->GetLabel(),
'url' => $oDispatcher->GetUrl(),
);
}
}
return $aAllowedPortals;
}
} // End of class
?>

View File

@@ -1,30 +1,32 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2013 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Construction and display of the application's main menu
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('../application/utils.inc.php');
require_once('../application/template.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/application/template.class.inc.php');
require_once(APPROOT."/application/user.dashboard.class.inc.php");
/**
@@ -59,46 +61,118 @@ require_once('../application/template.class.inc.php');
class ApplicationMenu
{
static $bAdditionalMenusLoaded = false;
static $aRootMenus = array();
static $aMenusIndex = array();
static $sFavoriteSiloQuery = 'SELECT Organization';
static public function LoadAdditionalMenus()
{
if (!self::$bAdditionalMenusLoaded)
{
// Build menus from module handlers
//
foreach(get_declared_classes() as $sPHPClass)
{
if (is_subclass_of($sPHPClass, 'ModuleHandlerAPI'))
{
$aCallSpec = array($sPHPClass, 'OnMenuCreation');
call_user_func($aCallSpec);
}
}
// Build menus from the menus themselves (e.g. the ShortcutContainerMenuNode will do that)
//
foreach(self::$aRootMenus as $aMenu)
{
$oMenuNode = self::GetMenuNode($aMenu['index']);
$oMenuNode->PopulateChildMenus();
}
self::$bAdditionalMenusLoaded = true;
}
}
/**
* Set the query used to limit the list of displayed organizations in the drop-down menu
* @param $sOQL string The OQL query returning a list of Organization objects
* @return none
*/
static public function SetFavoriteSiloQuery($sOQL)
{
self::$sFavoriteSiloQuery = $sOQL;
}
/**
* Get the query used to limit the list of displayed organizations in the drop-down menu
* @return string The OQL query returning a list of Organization objects
*/
static public function GetFavoriteSiloQuery()
{
return self::$sFavoriteSiloQuery;
}
/**
* Main function to add a menu entry into the application, can be called during the definition
* of the data model objects
*/
static public function InsertMenu(MenuNode $oMenuNode, $iParentIndex = -1, $fRank)
static public function InsertMenu(MenuNode $oMenuNode, $iParentIndex, $fRank)
{
$index = self::GetMenuIndexById($oMenuNode->GetMenuId());
if ($index == -1)
{
// The menu does not already exist, insert it
$index = count(self::$aMenusIndex);
self::$aMenusIndex[$index] = array( 'node' => $oMenuNode, 'children' => array());
if ($iParentIndex == -1)
{
$sParentId = '';
self::$aRootMenus[] = array ('rank' => $fRank, 'index' => $index);
}
else
{
$sParentId = self::$aMenusIndex[$iParentIndex]['node']->GetMenuId();
self::$aMenusIndex[$iParentIndex]['children'][] = array ('rank' => $fRank, 'index' => $index);
}
// Note: At the time when 'parent', 'rank' and 'source_file' have been added for the reflection API,
// they were not used to display the menus (redundant or unused)
//
$aBacktrace = debug_backtrace();
$sFile = isset($aBacktrace[2]["file"]) ? $aBacktrace[2]["file"] : $aBacktrace[1]["file"];
self::$aMenusIndex[$index] = array('node' => $oMenuNode, 'children' => array(), 'parent' => $sParentId, 'rank' => $fRank, 'source_file' => $sFile);
}
else
{
// the menu already exists, let's combine the conditions that make it visible
self::$aMenusIndex[$index]['node']->AddCondition($oMenuNode);
}
return $index;
}
/**
* Reflection API - Get menu entries
*/
static public function ReflectionMenuNodes()
{
self::LoadAdditionalMenus();
return self::$aMenusIndex;
}
/**
* Entry point to display the whole menu into the web page, used by iTopWebPage
*/
static public function DisplayMenu(iTopWebPage $oPage, $aExtraParams)
static public function DisplayMenu($oPage, $aExtraParams)
{
self::LoadAdditionalMenus();
// Sort the root menu based on the rank
usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank'));
$iAccordion = 0;
$iActiveMenu = ApplicationMenu::GetActiveNodeId();
$iActiveMenu = self::GetMenuIndexById(self::GetActiveNodeId());
foreach(self::$aRootMenus as $aMenu)
{
$oMenuNode = self::GetMenuNode($aMenu['index']);
if (($oMenuNode->GetMenuId() == 'AdminTools') && (!UserRights::IsAdministrator())) continue; // Don't display the admin menu for non admin users
if (!$oMenuNode->IsEnabled()) continue; // Don't display a non-enabled menu
$oPage->AddToMenu('<h3>'.$oMenuNode->GetTitle().'</h3>');
$oPage->AddToMenu('<div>');
@@ -110,8 +184,9 @@ class ApplicationMenu
$oPage->AddToMenu('</ul>');
if ($bActive)
{
$oPage->add_ready_script("$('#accordion').accordion('activate', $iAccordion);");
$oPage->add_ready_script("$('#accordion').accordion('option', {collapsible: true});"); // Make it auto-collapsible once it has been opened properly
//$oPage->add_ready_script("$('#accordion').accordion('activate', $iAccordion);");
// $oPage->add_ready_script("$('#accordion').accordion('option', {collapsible: true});"); // Make it auto-collapsible once it has been opened properly
$oPage->add_ready_script("$('#accordion').accordion('option', {collapsible: true, active: $iAccordion});"); // Make it auto-collapsible once it has been opened properly
}
}
$oPage->AddToMenu('</div>');
@@ -188,11 +263,11 @@ class ApplicationMenu
/**
* Helper function to get the list of child(ren) of a menu
*/
static protected function GetChildren($index)
static public function GetChildren($index)
{
return self::$aMenusIndex[$index]['children'];
}
/**
* Helper function to get the ID of a menu based on its name
* @param string $sTitle Title of the menu (as passed when creating the menu)
@@ -214,21 +289,23 @@ class ApplicationMenu
/**
* Retrieves the currently active menu (if any, otherwise the first menu is the default)
* @return MenuNode or null if there is no menu at all !
* @return string The Id of the currently active menu
*/
static public function GetActiveNodeId()
{
$iMenuIndex = utils::ReadParam('menu', -1);
if ($iMenuIndex == -1)
$oAppContext = new ApplicationContext();
$sMenuId = $oAppContext->GetCurrentValue('menu', null);
if ($sMenuId === null)
{
// Make sure the root menu is sorted on 'rank'
usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank'));
$oFirstGroup = self::GetMenuNode(self::$aRootMenus[0]['index']);
$oMenuNode = self::GetMenuNode(self::$aMenusIndex[$oFirstGroup->GetIndex()]['children'][0]['index']);
$iMenuIndex = $oMenuNode->GetIndex();
$aChildren = self::$aMenusIndex[$oFirstGroup->GetIndex()]['children'];
usort($aChildren, array('ApplicationMenu', 'CompareOnRank'));
$oMenuNode = self::GetMenuNode($aChildren[0]['index']);
$sMenuId = $oMenuNode->GetMenuId();
}
return $iMenuIndex;
return $sMenuId;
}
}
@@ -263,20 +340,30 @@ abstract class MenuNode
protected $sMenuId;
protected $index;
/**
* Properties reflecting how the node has been declared
*/
protected $aReflectionProperties;
/**
* Class of objects to check if the menu is enabled, null if none
*/
protected $m_sEnableClass;
protected $m_aEnableClasses;
/**
* User Rights Action code to check if the menu is enabled, null if none
*/
protected $m_iEnableAction;
protected $m_aEnableActions;
/**
* User Rights allowed results (actually a bitmask) to check if the menu is enabled, null if none
*/
protected $m_iEnableActionResults;
protected $m_aEnableActionResults;
/**
* Stimulus to check: if the user can 'apply' this stimulus, then she/he can see this menu
*/
protected $m_aEnableStimuli;
/**
* Create a menu item, sets the condition to have it displayed and inserts it into the application's main menu
@@ -284,18 +371,33 @@ abstract class MenuNode
* @param integer $iParentIndex ID of the parent menu, pass -1 for top level (group) items
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
* @param string $sEnableClass Name of class of object
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
* @param mixed $iActionCode UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @param string $sEnableStimulus The user can see this menu if she/he has enough rights to apply this stimulus
* @return MenuNode
*/
public function __construct($sMenuId, $iParentIndex = -1, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES)
public function __construct($sMenuId, $iParentIndex = -1, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
$this->sMenuId = $sMenuId;
$this->m_sEnableClass = $sEnableClass;
$this->m_iEnableAction = $iActionCode;
$this->m_iEnableActionResults = $iAllowedResults;
$this->aReflectionProperties = array();
if (strlen($sEnableClass) > 0)
{
$this->aReflectionProperties['enable_class'] = $sEnableClass;
$this->aReflectionProperties['enable_action'] = $iActionCode;
$this->aReflectionProperties['enable_permission'] = $iAllowedResults;
$this->aReflectionProperties['enable_stimulus'] = $sEnableStimulus;
}
$this->m_aEnableClasses = array($sEnableClass);
$this->m_aEnableActions = array($iActionCode);
$this->m_aEnableActionResults = array($iAllowedResults);
$this->m_aEnableStimuli = array($sEnableStimulus);
$this->index = ApplicationMenu::InsertMenu($this, $iParentIndex, $fRank);
}
public function ReflectionProperties()
{
return $this->aReflectionProperties;
}
public function GetMenuId()
{
@@ -304,12 +406,12 @@ abstract class MenuNode
public function GetTitle()
{
return Dict::S("Menu:$this->sMenuId");
return Dict::S("Menu:$this->sMenuId", str_replace('_', ' ', $this->sMenuId));
}
public function GetLabel()
{
return Dict::S("Menu:$this->sMenuId+");
return Dict::S("Menu:$this->sMenuId+", "");
}
public function GetIndex()
@@ -317,29 +419,70 @@ abstract class MenuNode
return $this->index;
}
public function PopulateChildMenus()
{
foreach (ApplicationMenu::GetChildren($this->GetIndex()) as $aMenu)
{
$index = $aMenu['index'];
$oMenu = ApplicationMenu::GetMenuNode($index);
$oMenu->PopulateChildMenus();
}
}
public function GetHyperlink($aExtraParams)
{
$aExtraParams['menu'] = $this->GetIndex();
return $this->AddParams('../pages/UI.php', $aExtraParams);
$aExtraParams['c[menu]'] = $this->GetMenuId();
return $this->AddParams(utils::GetAbsoluteUrlAppRoot().'pages/UI.php', $aExtraParams);
}
/**
* Add a limiting display condition for the same menu node. The conditions will be combined with a AND
* @param $oMenuNode MenuNode Another definition of the same menu node, with potentially different access restriction
* @return void
*/
public function AddCondition(MenuNode $oMenuNode)
{
foreach($oMenuNode->m_aEnableClasses as $index => $sClass )
{
$this->m_aEnableClasses[] = $sClass;
$this->m_aEnableActions[] = $oMenuNode->m_aEnableActions[$index];
$this->m_aEnableActionResults[] = $oMenuNode->m_aEnableActionResults[$index];
$this->m_aEnableStimuli[] = $oMenuNode->m_aEnableStimuli[$index];
}
}
/**
* Tells whether the menu is enabled (i.e. displayed) for the current user
* @return bool True if enabled, false otherwise
*/
public function IsEnabled()
{
if ($this->m_sEnableClass != null)
foreach($this->m_aEnableClasses as $index => $sClass)
{
if (MetaModel::IsValidClass($this->m_sEnableClass))
if ($sClass != null)
{
$iResult = UserRights::IsActionAllowed($this->m_sEnableClass, $this->m_iEnableAction);
if (($iResult & $this->m_iEnableActionResults))
if (MetaModel::IsValidClass($sClass))
{
return true;
if ($this->m_aEnableStimuli[$index] != null)
{
if (!UserRights::IsStimulusAllowed($sClass, $this->m_aEnableStimuli[$index]))
{
return false;
}
}
if ($this->m_aEnableActions[$index] != null)
{
$iResult = UserRights::IsActionAllowed($sClass, $this->m_aEnableActions[$index]);
if (!($iResult & $this->m_aEnableActionResults[$index]))
{
return false;
}
}
}
else
{
return false;
}
}
return false;
}
return true;
}
@@ -381,9 +524,9 @@ class MenuGroup extends MenuNode
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @return MenuGroup
*/
public function __construct($sMenuId, $fRank, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES)
public function __construct($sMenuId, $fRank, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
parent::__construct($sMenuId, -1 /* no parent, groups are at root level */, $fRank, $sEnableClass, $iActionCode, $iAllowedResults);
parent::__construct($sMenuId, -1 /* no parent, groups are at root level */, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
}
public function RenderContent(WebPage $oPage, $aExtraParams = array())
@@ -411,10 +554,11 @@ class TemplateMenuNode extends MenuNode
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @return MenuNode
*/
public function __construct($sMenuId, $sTemplateFile, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES)
public function __construct($sMenuId, $sTemplateFile, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults);
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sTemplateFile = $sTemplateFile;
$this->aReflectionProperties['template_file'] = $sTemplateFile;
}
public function GetHyperlink($aExtraParams)
@@ -428,6 +572,7 @@ class TemplateMenuNode extends MenuNode
$sTemplate = @file_get_contents($this->sTemplateFile);
if ($sTemplate !== false)
{
$aExtraParams['table_id'] = 'Menu_'.$this->GetMenuId();
$oTemplate = new DisplayTemplate($sTemplate);
$oTemplate->Render($oPage, $aExtraParams);
}
@@ -466,13 +611,15 @@ class OQLMenuNode extends MenuNode
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @return MenuNode
*/
public function __construct($sMenuId, $sOQL, $iParentIndex, $fRank = 0, $bSearch = false, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES)
public function __construct($sMenuId, $sOQL, $iParentIndex, $fRank = 0, $bSearch = false, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults);
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sPageTitle = "Menu:$sMenuId+";
$this->sOQL = $sOQL;
$this->bSearch = $bSearch;
$this->m_aParams = array();
$this->aReflectionProperties['oql'] = $sOQL;
$this->aReflectionProperties['do_search'] = $bSearch;
// Enhancement: we could set as the "enable" condition that the user has enough rights to "read" the objects
// of the class specified by the OQL...
}
@@ -484,47 +631,47 @@ class OQLMenuNode extends MenuNode
public function SetParameters($aParams)
{
$this->m_aParams = $aParams;
foreach($aParams as $sKey => $value)
{
$this->aReflectionProperties[$sKey] = $value;
}
}
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
$aExtraParams = array_merge($aExtraParams, $this->m_aParams);
try
{
$oSearch = DBObjectSearch::FromOQL($this->sOQL);
$sIcon = MetaModel::GetClassIcon($oSearch->GetClass());
}
catch(Exception $e)
{
$sIcon = '';
}
// The standard template used for all such pages: a (closed) search form at the top and a list of results at the bottom
$sTemplate = '';
OQLMenuNode::RenderOQLSearch
(
$this->sOQL,
Dict::S($this->sPageTitle),
'Menu_'.$this->GetMenuId(),
$this->bSearch, // Search pane
true, // Search open
$oPage,
array_merge($this->m_aParams, $aExtraParams)
);
}
if ($this->bSearch)
public static function RenderOQLSearch($sOql, $sTitle, $sUsageId, $bSearchPane, $bSearchOpen, WebPage $oPage, $aExtraParams = array())
{
$sUsageId = utils::GetSafeId($sUsageId);
$oSearch = DBObjectSearch::FromOQL($sOql);
$sIcon = MetaModel::GetClassIcon($oSearch->GetClass());
if ($bSearchPane)
{
$sTemplate .= <<<EOF
<itopblock BlockClass="DisplayBlock" type="search" asynchronous="false" encoding="text/oql">$this->sOQL</itopblock>
EOF;
$aParams = array_merge(array('open' => $bSearchOpen, 'table_id' => $sUsageId), $aExtraParams);
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
$oBlock->Display($oPage, 0);
}
$sParams = '';
if (!empty($this->m_aParams))
{
$sParams = 'parameters="';
foreach($this->m_aParams as $sName => $sValue)
{
$sParams .= $sName.':'.$sValue.';';
}
$sParams .= '"';
}
$sTemplate .= <<<EOF
<p class="page-header">$sIcon<itopstring>$this->sPageTitle</itopstring></p>
<itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql" $sParams>$this->sOQL</itopblock>
EOF;
$oTemplate = new DisplayTemplate($sTemplate);
$oTemplate->Render($oPage, $aExtraParams);
$oPage->add("<p class=\"page-header\">$sIcon ".Dict::S($sTitle)."</p>");
$aParams = array_merge(array('table_id' => $sUsageId), $aExtraParams);
$oBlock = new DisplayBlock($oSearch, 'list', false /* Asynchronous */, $aParams);
$oBlock->Display($oPage, $sUsageId);
}
}
/**
* This class defines a menu item that displays a search form for the given class of objects
*/
@@ -545,21 +692,20 @@ class SearchMenuNode extends MenuNode
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @return MenuNode
*/
public function __construct($sMenuId, $sClass, $iParentIndex, $fRank = 0, $bSearch = false, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES)
public function __construct($sMenuId, $sClass, $iParentIndex, $fRank = 0, $bSearch = false, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults);
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sPageTitle = "Menu:$sMenuId+";
$this->sClass = $sClass;
$this->aReflectionProperties['class'] = $sClass;
}
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
// The standard template used for all such pages: an open search form at the top
$sTemplate = <<<EOF
<itopblock BlockClass="DisplayBlock" type="search" asynchronous="false" encoding="text/oql" parameters="open:true">SELECT $this->sClass</itopblock>
EOF;
$oTemplate = new DisplayTemplate($sTemplate);
$oTemplate->Render($oPage, $aExtraParams);
$oSearch = new DBObjectSearch($this->sClass);
$aParams = array_merge(array('open' => true, 'table_id' => 'Menu_'.utils::GetSafeId($this->GetMenuId())), $aExtraParams);
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
$oBlock->Display($oPage, 0);
}
}
@@ -585,15 +731,16 @@ class WebPageMenuNode extends MenuNode
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @return MenuNode
*/
public function __construct($sMenuId, $sHyperlink, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES)
public function __construct($sMenuId, $sHyperlink, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults);
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sHyperlink = $sHyperlink;
$this->aReflectionProperties['url'] = $sHyperlink;
}
public function GetHyperlink($aExtraParams)
{
$aExtraParams['menu'] = $this->GetIndex();
$aExtraParams['c[menu]'] = $this->GetMenuId();
return $this->AddParams( $this->sHyperlink, $aExtraParams);
}
@@ -626,12 +773,13 @@ class NewObjectMenuNode extends MenuNode
{
parent::__construct($sMenuId, $iParentIndex, $fRank);
$this->sClass = $sClass;
$this->aReflectionProperties['class'] = $sClass;
}
public function GetHyperlink($aExtraParams)
{
$sHyperlink = '../pages/UI.php?operation=new&class='.$this->sClass;
$aExtraParams['menu'] = $this->GetIndex();
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class='.$this->sClass;
$aExtraParams['c[menu]'] = $this->GetMenuId();
return $this->AddParams($sHyperlink, $aExtraParams);
}
@@ -662,4 +810,241 @@ class NewObjectMenuNode extends MenuNode
assert(false); // Shall never be called, the external web page will handle the display by itself
}
}
?>
require_once(APPROOT.'application/dashboard.class.inc.php');
/**
* This class defines a menu item which content is based on XML dashboard.
*/
class DashboardMenuNode extends MenuNode
{
protected $sDashboardFile;
/**
* Create a menu item based on a custom template and inserts it into the application's main menu
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
* @param string $sTemplateFile Path (or URL) to the file that will be used as a template for displaying the page's content
* @param integer $iParentIndex ID of the parent menu
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
* @param string $sEnableClass Name of class of object
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @return MenuNode
*/
public function __construct($sMenuId, $sDashboardFile, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sDashboardFile = $sDashboardFile;
$this->aReflectionProperties['definition_file'] = $sDashboardFile;
}
public function GetHyperlink($aExtraParams)
{
if ($this->sDashboardFile == '') return '';
return parent::GetHyperlink($aExtraParams);
}
public function GetDashboard()
{
$sDashboardDefinition = @file_get_contents($this->sDashboardFile);
if ($sDashboardDefinition !== false)
{
$bCustomized = false;
// Search for an eventual user defined dashboard, overloading the existing one
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oUDSearch->AddCondition('menu_code', $this->sMenuId, '=');
$oUDSet = new DBObjectSet($oUDSearch);
if ($oUDSet->Count() > 0)
{
// Assuming there is at most one couple {user, menu}!
$oUserDashboard = $oUDSet->Fetch();
$sDashboardDefinition = $oUserDashboard->Get('contents');
$bCustomized = true;
}
$oDashboard = new RuntimeDashboard($this->sMenuId);
$oDashboard->FromXml($sDashboardDefinition);
$oDashboard->SetCustomFlag($bCustomized);
}
else
{
$oDashboard = null;
}
return $oDashboard;
}
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
$oDashboard = $this->GetDashboard();
if ($oDashboard != null)
{
$sDivId = preg_replace('/[^a-zA-Z0-9_]/', '', $this->sMenuId);
$oPage->add('<div class="dashboard_contents" id="'.$sDivId.'">');
$oDashboard->Render($oPage, false, $aExtraParams);
$oPage->add('</div>');
$oDashboard->RenderEditionTools($oPage);
if ($oDashboard->GetAutoReload())
{
$sId = $this->sMenuId;
$sExtraParams = json_encode($aExtraParams);
$iReloadInterval = 1000 * $oDashboard->GetAutoReloadInterval();
$oPage->add_script(
<<<EOF
setInterval("ReloadDashboard('$sDivId');", $iReloadInterval);
function ReloadDashboard(sDivId)
{
var oExtraParams = $sExtraParams;
// Do not reload when a dialog box is active
if (!($('.ui-dialog:visible').length > 0))
{
$('.dashboard_contents#'+sDivId).block();
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
{ operation: 'reload_dashboard', dashboard_id: '$sId', extra_params: oExtraParams},
function(data){
$('.dashboard_contents#'+sDivId).html(data);
$('.dashboard_contents#'+sDivId).unblock();
}
);
}
}
EOF
);
}
$bEdit = utils::ReadParam('edit', false);
if ($bEdit)
{
$sId = addslashes($this->sMenuId);
$oPage->add_ready_script("EditDashboard('$sId');");
}
}
else
{
$oPage->p("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
}
}
public function RenderEditor(WebPage $oPage)
{
$oDashboard = $this->GetDashboard();
if ($oDashboard != null)
{
$oDashboard->RenderEditor($oPage);
}
else
{
$oPage->p("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
}
}
public function AddDashlet($oDashlet)
{
$oDashboard = $this->GetDashboard();
if ($oDashboard != null)
{
$oDashboard->AddDashlet($oDashlet);
$oDashboard->Save();
}
else
{
$oPage->p("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
}
}
}
/**
* A shortcut container is the preferred destination of newly created shortcuts
*/
class ShortcutContainerMenuNode extends MenuNode
{
public function GetHyperlink($aExtraParams)
{
return '';
}
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
}
public function PopulateChildMenus()
{
// Load user shortcuts in DB
//
$oBMSearch = new DBObjectSearch('Shortcut');
$oBMSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oBMSet = new DBObjectSet($oBMSearch, array('friendlyname' => true)); // ascending on friendlyname
$fRank = 1;
while ($oShortcut = $oBMSet->Fetch())
{
$sName = $this->GetMenuId().'_'.$oShortcut->GetKey();
$oShortcutMenu = new ShortcutMenuNode($sName, $oShortcut, $this->GetIndex(), $fRank++);
}
// Complete the tree
//
parent::PopulateChildMenus();
}
}
require_once(APPROOT.'application/shortcut.class.inc.php');
/**
* This class defines a menu item which content is a shortcut.
*/
class ShortcutMenuNode extends MenuNode
{
protected $oShortcut;
/**
* Create a menu item based on a custom template and inserts it into the application's main menu
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
* @param object $oShortcut Shortcut object
* @param integer $iParentIndex ID of the parent menu
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
* @param string $sEnableClass Name of class of object
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
* @return MenuNode
*/
public function __construct($sMenuId, $oShortcut, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
{
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->oShortcut = $oShortcut;
$this->aReflectionProperties['shortcut'] = $oShortcut->GetKey();
}
public function GetHyperlink($aExtraParams)
{
$sContext = $this->oShortcut->Get('context');
$aContext = unserialize($sContext);
if (isset($aContext['menu']))
{
unset($aContext['menu']);
}
foreach ($aContext as $sArgName => $sArgValue)
{
$aExtraParams[$sArgName] = $sArgValue;
}
return parent::GetHyperlink($aExtraParams);
}
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
$this->oShortcut->RenderContent($oPage, $aExtraParams);
}
public function GetTitle()
{
return $this->oShortcut->Get('name');
}
public function GetLabel()
{
return $this->oShortcut->Get('name');
}
}

View File

@@ -1,59 +1,178 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2015 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class NiceWebPage
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once("../application/webpage.class.inc.php");
require_once(APPROOT."/application/webpage.class.inc.php");
/**
* Web page with some associated CSS and scripts (jquery) for a fancier display
*/
class NiceWebPage extends WebPage
{
var $m_aReadyScripts;
var $m_sRootUrl;
public function __construct($s_title)
public function __construct($s_title, $bPrintable = false)
{
parent::__construct($s_title);
parent::__construct($s_title, $bPrintable);
$this->m_aReadyScripts = array();
$this->add_linked_script("../js/jquery-1.4.2.min.js");
//$this->add_linked_script("../js/jquery.history_remote.pack.js");
$this->add_linked_stylesheet('../css/ui-lightness/jquery-ui-1.8.2.custom.css');
$this->add_linked_script('../js/jquery-ui-1.8.2.custom.min.js');
//$this->add_linked_script("../js/ui.resizable.js");
// $this->add_linked_script("../js/ui.tabs.js");
$this->add_linked_script("../js/jquery-1.10.0.min.js");
$this->add_linked_script("../js/jquery-migrate-1.2.1.min.js"); // Needed since many other plugins still rely on oldies like $.browser
$this->add_linked_stylesheet('../css/ui-lightness/jquery-ui-1.10.3.custom.min.css');
$this->add_linked_script('../js/jquery-ui-1.10.3.custom.min.js');
$this->add_linked_script("../js/hovertip.js");
// $this->add_linked_script("../js/jqModal.js");
$this->add_linked_stylesheet("../css/light-grey.css");
// $this->add_linked_stylesheet("../js/themes/light/light.tabs.css");
//$this->add_linked_stylesheet("../css/jquery.tabs-ie.css", "lte IE 7");
// $this->add_linked_stylesheet("../css/jqModal.css");
$this->add_ready_script(' window.setTimeout(hovertipInit, 1);');
}
// table sorting
$this->add_linked_script("../js/jquery.tablesorter.js");
$this->add_linked_script("../js/jquery.tablesorter.pager.js");
$this->add_linked_script("../js/jquery.tablehover.js");
$this->add_linked_script('../js/field_sorter.js');
$this->add_linked_script('../js/datatable.js');
$this->add_linked_script("../js/jquery.positionBy.js");
$this->add_linked_script("../js/jquery.popupmenu.js");
$this->add_ready_script(
<<< EOF
//add new widget called TruncatedList to properly display truncated lists when they are sorted
$.tablesorter.addWidget({
// give the widget a id
id: "truncatedList",
// format is called when the on init and when a sorting has finished
format: function(table)
{
// Check if there is a "truncated" line
this.truncatedList = false;
if ($("tr td.truncated",table).length > 0)
{
this.truncatedList = true;
}
if (this.truncatedList)
{
$("tr td",table).removeClass('truncated');
$("tr:last td",table).addClass('truncated');
}
}
});
$.tablesorter.addWidget({
// give the widget a id
id: "myZebra",
// format is called when the on init and when a sorting has finished
format: function(table)
{
// Replace the 'red even' lines by 'red_even' since most browser do not support 2 classes selector in CSS, etc..
$("tbody tr:even",table).addClass('even');
$("tbody tr.red:even",table).removeClass('red').removeClass('even').addClass('red_even');
$("tbody tr.orange:even",table).removeClass('orange').removeClass('even').addClass('orange_even');
$("tbody tr.green:even",table).removeClass('green').removeClass('even').addClass('green_even');
// In case we sort again the table, we need to remove the added 'even' classes on odd rows
$("tbody tr:odd",table).removeClass('even');
$("tbody tr.red_even:odd",table).removeClass('even').removeClass('red_even').addClass('red');
$("tbody tr.orange_even:odd",table).removeClass('even').removeClass('orange_even').addClass('orange');
$("tbody tr.green_even:odd",table).removeClass('even').removeClass('green_even').addClass('green');
}
});
$("table.listResults").tableHover(); // hover tables
EOF
);
$this->add_saas("css/light-grey.scss");
$this->m_sRootUrl = $this->GetAbsoluteUrlAppRoot();
$sAbsURLAppRoot = addslashes($this->m_sRootUrl);
$sAbsURLModulesRoot = addslashes($this->GetAbsoluteUrlModulesRoot());
$sEnvironment = addslashes(utils::GetCurrentEnvironment());
$sAppContext = addslashes($this->GetApplicationContext());
$this->add_script(
<<<EOF
function GetAbsoluteUrlAppRoot()
{
return '$sAbsURLAppRoot';
}
function GetAbsoluteUrlModulesRoot()
{
return '$sAbsURLModulesRoot';
}
function GetAbsoluteUrlModulePage(sModule, sPage, aArguments)
{
// aArguments is optional, it default to an empty hash
aArguments = typeof aArguments !== 'undefined' ? aArguments : {};
var sUrl = '$sAbsURLAppRoot'+'pages/exec.php?exec_module='+sModule+'&exec_page='+sPage+'&exec_env='+'$sEnvironment';
for (var sArgName in aArguments)
{
if (aArguments.hasOwnProperty(sArgName))
{
sUrl = sUrl + '&'+sArgName+'='+aArguments[sArgname];
}
}
return sUrl;
}
function AddAppContext(sURL)
{
var sContext = '$sAppContext';
if (sContext.length > 0)
{
if (sURL.indexOf('?') == -1)
{
return sURL+'?'+sContext;
}
return sURL+'&'+sContext;
}
return sURL;
}
EOF
);
}
public function SetRootUrl($sRootUrl)
{
$this->m_sRootUrl = $sRootUrl;
}
public function small_p($sText)
{
$this->add("<p style=\"font-size:smaller\">$sText</p>\n");
}
}
public function GetAbsoluteUrlAppRoot()
{
return utils::GetAbsoluteUrlAppRoot();
}
public function GetAbsoluteUrlModulesRoot()
{
return utils::GetAbsoluteUrlModulesRoot();
}
function GetApplicationContext()
{
$oAppContext = new ApplicationContext();
return $oAppContext->GetForLink();
}
// By Rom, used by CSVImport and Advanced search
public function MakeClassesSelect($sName, $sDefaultValue, $iWidthPx, $iActionCode = null)
@@ -100,6 +219,7 @@ class NiceWebPage extends WebPage
*/
public function output()
{
//$this->set_base($this->m_sRootUrl.'pages/');
if (count($this->m_aReadyScripts)>0)
{
$this->add_script("\$(document).ready(function() {\n".implode("\n", $this->m_aReadyScripts)."\n});");

View File

@@ -0,0 +1,194 @@
<?php
require_once(APPROOT.'lib/tcpdf/tcpdf.php');
/**
* Custom class derived from TCPDF for providing custom headers and footers
* @author denis
*
*/
class iTopPDF extends TCPDF
{
protected $sDocumentTitle;
public function SetDocumentTitle($sDocumentTitle)
{
$this->sDocumentTitle = $sDocumentTitle;
}
/**
* Builds the custom header. Called for each new page.
* @see TCPDF::Header()
*/
public function Header()
{
// Title
// Set font
$this->SetFont('dejavusans', 'B', 10);
$iPageNumberWidth = 25;
$aMargins = $this->getMargins();
// Display the title (centered)
$this->SetXY($aMargins['left'] + $iPageNumberWidth, 0);
$this->MultiCell($this->getPageWidth() - $aMargins['left'] - $aMargins['right'] - 2*$iPageNumberWidth, 15, $this->sDocumentTitle, 0, 'C', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
$this->SetFont('dejavusans', '', 10);
// Display the page number (right aligned)
// Warning: the 'R'ight alignment does not work when using placeholders like $this->getAliasNumPage() or $this->getAliasNbPages()
$this->MultiCell($iPageNumberWidth, 15, 'Page '.$this->page, 0, 'R', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
// Branding logo
$sBrandingIcon = APPROOT.'images/itop-logo.png';
if (file_exists(MODULESROOT.'branding/main-logo.png'))
{
$sBrandingIcon = MODULESROOT.'branding/main-logo.png';
}
$this->Image($sBrandingIcon, $aMargins['left'], 5, 0, 10);
}
// Page footer
public function Footer()
{
// No footer
}
}
/**
* Special class of WebPage for printing into a PDF document
*/
class PDFPage extends WebPage
{
/**
* Instance of the TCPDF object for creating the PDF
* @var TCPDF
*/
protected $oPdf;
public function __construct($s_title, $sPageFormat = 'A4', $sPageOrientation = 'L')
{
parent::__construct($s_title);
define(K_PATH_FONTS, APPROOT.'lib/tcpdf/fonts');
$this->oPdf = new iTopPDF($sPageOrientation, 'mm', $sPageFormat, true, 'UTF-8', false);
// set document information
$this->oPdf->SetCreator(PDF_CREATOR);
$this->oPdf->SetAuthor('iTop');
$this->oPdf->SetTitle($s_title);
$this->oPdf->SetDocumentTitle($s_title);
$this->oPdf->setFontSubsetting(true);
// Set font
// dejavusans is a UTF-8 Unicode font. Standard PDF fonts like helvetica or times new roman are NOT UTF-8
$this->oPdf->SetFont('dejavusans', '', 10, '', true);
// set auto page breaks
$this->oPdf->SetAutoPageBreak(true, 15); // 15 mm break margin at the bottom
$this->oPdf->SetTopMargin(15);
// Add a page, we're ready to start
$this->oPdf->AddPage();
$this->SetContentDisposition('inline', $s_title.'.pdf');
$this->SetDefaultStyle();
}
/**
* Sets a default style (suitable for printing) to be included each time $this->oPdf->writeHTML() is called
*/
protected function SetDefaultStyle()
{
$this->add_style(
<<<EOF
table {
padding: 2pt;
}
table.listResults td {
border: 0.5pt solid #000 ;
}
table.listResults th {
background-color: #eee;
border: 0.5pt solid #000 ;
}
a {
text-decoration: none;
color: #000;
}
table.section td {
vertical-align: middle;
font-size: 10pt;
background-color:#eee;
}
td.icon {
width: 30px;
}
EOF
);
}
/**
* Get access to the underlying TCPDF object
* @return TCPDF
*/
public function get_tcpdf()
{
$this->flush();
return $this->oPdf;
}
/**
* Writes the currently buffered HTML content into the PDF. This can be useful:
* - to sync the flow in case you want to access the underlying TCPDF object for some specific/graphic output
* - to process the HTML by smaller chunks instead of processing the whole page at once for performance reasons
*/
public function flush()
{
if (strlen($this->s_content) > 0)
{
$sHtml = '';
if (count($this->a_styles) > 0)
{
$sHtml .= "<style>\n".implode("\n", $this->a_styles)."\n</style>\n";
}
$sHtml .= $this->s_content;
$this->oPdf->writeHTML($sHtml); // The style(s) must be supplied each time we call writeHtml
$this->s_content = '';
}
}
/**
* Whether or not the page is a PDF page
* @return boolean
*/
public function is_pdf()
{
return true;
}
/**
* Generates the PDF document and returns the PDF content as a string
* @return string
* @see WebPage::output()
*/
public function output()
{
$this->add_header('Content-type: application/x-pdf');
if (!empty($this->sContentDisposition))
{
$this->add_header('Content-Disposition: '.$this->sContentDisposition.'; filename="'.$this->sContentFileName.'"');
}
foreach($this->a_headers as $s_header)
{
header($s_header);
}
$this->flush();
echo $this->oPdf->Output($this->s_title.'.pdf', 'S');
}
public function get_pdf()
{
$this->flush();
return $this->oPdf->Output($this->s_title.'.pdf', 'S');
}
}

View File

@@ -0,0 +1,70 @@
<?php
class PortalDispatcher
{
protected $sPortalid;
protected $aData;
public function __construct($sPortalId)
{
$this->sPortalid = $sPortalId;
$this->aData = PortalDispatcherData::GetData($sPortalId);
}
public function IsUserAllowed()
{
$bRet = true;
if (array_key_exists('profile_list', $_SESSION))
{
$aProfiles = $_SESSION['profile_list'];
}
else
{
$oUser = UserRights::GetUserObject();
$oSet = $oUser->Get('profile_list');
while(($oLnkUserProfile = $oSet->Fetch()) !== null)
{
$aProfiles[] = $oLnkUserProfile->Get('profileid_friendlyname');
}
$_SESSION['profile_list'] = $aProfiles;
}
foreach($this->aData['deny'] as $sDeniedProfile)
{
// If one denied profile is present, it's enough => return false
if (in_array($sDeniedProfile, $aProfiles))
{
return false;
}
}
// If there are some "allow" profiles, then by default the result is false
// since the user must have at least one of the profiles to be allowed
if (count($this->aData['allow']) > 0)
{
$bRet = false;
}
foreach($this->aData['allow'] as $sAllowProfile)
{
// If one "allow" profile is present, it's enough => return true
if (in_array($sAllowProfile, $aProfiles))
{
return true;
}
}
return $bRet;
}
public function GetURL()
{
return utils::GetAbsoluteUrlAppRoot().$this->aData['url'];
}
public function GetLabel()
{
return Dict::S('portal:'.$this->sPortalid);
}
public function GetRank()
{
return $this->aData['rank'];
}
}

View File

@@ -1,31 +1,43 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2013 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class PortalWebPage
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once("../application/nicewebpage.class.inc.php");
require_once("../application/applicationcontext.class.inc.php");
require_once("../application/user.preferences.class.inc.php");
require_once(APPROOT."/application/nicewebpage.class.inc.php");
require_once(APPROOT."/application/applicationcontext.class.inc.php");
require_once(APPROOT."/application/user.preferences.class.inc.php");
define('BUTTON_CANCEL', 1);
define('BUTTON_BACK', 2);
define('BUTTON_NEXT', 4);
define('BUTTON_FINISH', 8);
define('PARAM_ARROW_SEP', '_x_');
class TransactionException extends Exception
{
}
/**
* Web page with some associated CSS and scripts (jquery) for a fancier display
* of the Portal web page
@@ -35,16 +47,24 @@ class PortalWebPage extends NiceWebPage
/**
* Portal menu
*/
protected $m_sWelcomeMsg;
protected $m_aMenuButtons;
public function __construct($sTitle, $sAlternateStyleSheet = '')
{
$this->m_sWelcomeMsg = '';
$this->m_aMenuButtons = array();
parent::__construct($sTitle);
$this->add_header("Content-type: text/html; charset=utf-8");
$this->add_header("Cache-control: no-cache");
$this->add_linked_stylesheet("../css/jquery.treeview.css");
$this->add_linked_stylesheet("../css/jquery.autocomplete.css");
$this->add_linked_stylesheet("../css/jquery.multiselect.css");
$sAbsURLAppRoot = addslashes(utils::GetAbsoluteUrlAppRoot()); // Pass it to Javascript scripts
$sAbsURLModulesRoot = addslashes(utils::GetAbsoluteUrlModulesRoot()); // Pass it to Javascript scripts
$oAppContext = new ApplicationContext();
$sAppContext = addslashes($oAppContext->GetForLink());
$this->add_dict_entry('UI:FillAllMandatoryFields');
if ($sAlternateStyleSheet != '')
{
$this->add_linked_stylesheet("../portal/$sAlternateStyleSheet/portal.css");
@@ -58,15 +78,31 @@ class PortalWebPage extends NiceWebPage
$this->add_linked_script("../js/jquery.tablehover.js");
$this->add_linked_script("../js/jquery.treeview.js");
$this->add_linked_script("../js/jquery.autocomplete.js");
$this->add_linked_script("../js/jquery.bgiframe.js");
$this->add_linked_script("../js/jquery.positionBy.js");
$this->add_linked_script("../js/jquery.popupmenu.js");
$this->add_linked_script("../js/date.js");
$this->add_linked_script("../js/jquery.tablesorter.min.js");
$this->add_linked_script("../js/jquery.tablesorter.pager.js");
$this->add_linked_script("../js/jquery.blockUI.js");
$this->add_linked_script("../js/utils.js");
$this->add_linked_script("../js/forms-json-utils.js");
$this->add_linked_script("../js/swfobject.js");
$this->add_linked_script("../js/jquery.qtip-1.0.min.js");
$this->add_linked_script('../js/jquery.multiselect.js');
$this->add_linked_script("../js/ajaxfileupload.js");
$this->add_linked_script("../js/ckeditor/ckeditor.js");
$this->add_linked_script("../js/ckeditor/adapters/jquery.js");
$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');
$this->add_ready_script(
<<<EOF
try
@@ -107,18 +143,53 @@ try
}
});
$("table.listResults").tableHover(); // hover tables
$(".listResults").tablesorter( { widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
$(".date-pick").datepicker({
showOn: 'button',
buttonImage: '../images/calendar.png',
buttonImageOnly: true,
dateFormat: 'yy-mm-dd',
constrainInput: false,
changeMonth: true,
changeYear: true
});
$('.resizable').resizable(); // Make resizable everything that claims to be resizable !
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
});
//$('.resizable').resizable(); // Make resizable everything that claims to be resizable !
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry').toggle(); });
$(document).ajaxSend(function(event, jqxhr, options) {
jqxhr.setRequestHeader('X-Combodo-Ajax', 'true');
});
$(document).ajaxError(function(event, jqxhr, options) {
if (jqxhr.status == 401)
{
$('<div>'+$sJSDisconnectedMessage+'</div>').dialog({
modal:true,
title: $sJSTitle,
close: function() { $(this).remove(); },
minWidth: 400,
buttons: [
{ text: $sJSLoginAgain, click: function() { window.location.href= GetAbsoluteUrlAppRoot()+'pages/UI.php' } },
{ text: $sJSStayOnThePage, click: function() { $(this).dialog('close'); } }
]
});
}
});
}
catch(err)
{
@@ -130,9 +201,18 @@ EOF
$this->add_script(
<<<EOF
function CheckSelection(sMessage)
function CheckSelection(sMessage, sInputId)
{
var bResult = ($('input:checked').length > 0);
var bResult;
if (sInputId.length > 0)
{
bResult = ($('input[name='+sInputId+']:checked').length > 0);
}
else
{
// First select found...
bResult = ($('input:checked').length > 0);
}
if (!bResult)
{
alert(sMessage);
@@ -140,19 +220,99 @@ EOF
return bResult;
}
function GoBack()
function GetAbsoluteUrlAppRoot()
{
var form = $('#request_form');
var step = $('input[name=step]');
return '$sAbsURLAppRoot';
}
function GetAbsoluteUrlModulesRoot()
{
return '$sAbsURLModulesRoot';
}
function AddAppContext(sURL)
{
var sContext = '$sAppContext';
if (sContext.length > 0)
{
if (sURL.indexOf('?') == -1)
{
return sURL+'?'+sContext;
}
return sURL+'&'+sContext;
}
return sURL;
}
function GoBack(sFormId)
{
var form = $('#'+sFormId);
var step_back = $('input[name=step_back]');
form.unbind('submit'); // De-activate validation
step.val(step.val() -2); // To go Back one step: next step is x, current step is x-1, previous step is x-2
step_back.val(1);
form.submit(); // Go
}
function GoHome()
{
var form = $('FORM');
form.unbind('submit'); // De-activate validation
window.location.href = window.location.href.replace(/[&?]operation=[^&]*/, '');
return false;
}
function SetWizardNextStep(sStep)
{
var next_step = $('input[id=next_step]');
next_step.val(sStep);
}
// For disabling the CKEditor at init time when the corresponding textarea is disabled !
CKEDITOR.plugins.add( 'disabler',
{
init : function( editor )
{
editor.on( 'instanceReady', function(e)
{
e.removeListener();
$('#'+ editor.name).trigger('update');
});
}
});
EOF
);
// For Wizard helper to process the ajax replies
$this->add('<div id="ajax_content"></div>');
// Customize the logo (unless a customer CSS has been defined)
if ($sAlternateStyleSheet == '')
{
if (file_exists(MODULESROOT.'branding/portal-logo.png'))
{
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/portal-logo.png';
$this->add_style("div#portal #logo {background: url(\"$sDisplayIcon\") no-repeat scroll 0 0 transparent;}");
}
}
}
public function SetCurrentTab($sTabLabel = '')
{
}
/**
* Specify a welcome message (optional)
*/
public function SetWelcomeMessage($sMsg)
{
$this->m_sWelcomeMsg = $sMsg;
}
/**
* Add a button to the portal's main menu
@@ -161,16 +321,659 @@ EOF
{
$this->m_aMenuButtons[] = array('id' => $sId, 'label' => $sLabel, 'hyperlink' => $sHyperlink);
}
var $m_bEnableDisconnectButton = true;
public function EnableDisconnectButton($bEnable)
{
$this->m_bEnableDisconnectButton = $bEnable;
}
public function output()
{
$this->AddMenuButton('logoff', 'Portal:Disconnect', '../pages/logoff.php?portal=1'); // This menu is always present and is the last one
$sApplicationBanner = '';
if (!MetaModel::DBHasAccess(ACCESS_USER_WRITE))
{
$sReadOnly = Dict::S('UI:AccessRO-Users');
$sAdminMessage = trim(MetaModel::GetConfig()->Get('access_message'));
$sApplicationBanner .= '<div id="admin-banner">';
$sApplicationBanner .= '<img src="../images/locked.png" style="vertical-align:middle;">';
$sApplicationBanner .= '&nbsp;<b>'.$sReadOnly.'</b>';
if (strlen($sAdminMessage) > 0)
{
$sApplicationBanner .= '&nbsp;: '.$sAdminMessage.'';
}
$sApplicationBanner .= '</div>';
}
$sMenu = '';
if ($this->m_bEnableDisconnectButton)
{
$this->AddMenuButton('logoff', 'Portal:Disconnect', utils::GetAbsoluteUrlAppRoot().'pages/logoff.php?operation=do_logoff'); // This menu is always present and is the last one
}
foreach($this->m_aMenuButtons as $aMenuItem)
{
$sMenu .= "<a class=\"button\" id=\"{$aMenuItem['id']}\" href=\"{$aMenuItem['hyperlink']}\"><span>".Dict::S($aMenuItem['label'])."</span></a>";
}
$this->s_content = '<div id="portal"><div id="banner"><div id="logo"></div>'.$sMenu.'</div><div id="content">'.$this->s_content.'</div></div>';
$this->s_content = '<div id="portal"><div id="welcome">'.$this->m_sWelcomeMsg.'</div><div id="banner"><div id="logo"></div><div id="menu">'.$sMenu.'</div></div>'.$sApplicationBanner.'<div id="content">'.$this->s_content.'</div></div>';
parent::output();
}
/**
* Displays a list of objects, without any hyperlink (except for the object's details)
* @param DBObjectSet $oSet The set of objects to display
* @param Array $aZList The ZList (list of field codes) to use for the tabular display
* @param String $sEmptyListMessage Message displayed whenever the list is empty
* @return string The HTML text representing the list
*/
public function DisplaySet($oSet, $aZList, $sEmptyListMessage = '')
{
if ($oSet->Count() > 0)
{
$sClass = $oSet->GetClass();
if (is_subclass_of($sClass, 'cmdbAbstractObject'))
{
// Home-made and very limited display of an object set
$sUniqueId = $sClass.$this->GetUniqueId();
$this->add("<div id=\"$sUniqueId\">\n"); // The id here MUST be the same as currentId, otherwise the pagination will be broken
cmdbAbstractObject::DisplaySet($this, $oSet, array('currentId' => $sUniqueId, 'menu' => false, 'toolkit_menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList)));
$this->add("</div>\n");
}
else
{
// Home-made and very limited display of an object set
$aAttribs = array();
$aValues = array();
$aAttribs['key'] = array('label' => MetaModel::GetName($sClass), 'description' => '');
foreach($aZList as $sAttCode)
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$aAttribs[$sAttCode] = array('label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription());
}
while($oObj = $oSet->Fetch())
{
$aRow = array();
$aRow['key'] = '<a href="./index.php?operation=details&class='.get_class($oObj).'&id='.$oObj->GetKey().'">'.$oObj->GetName().'</a>';
$sHilightClass = $oObj->GetHilightClass();
if ($sHilightClass != '')
{
$aRow['@class'] = $sHilightClass;
}
foreach($aZList as $sAttCode)
{
$aRow[$sAttCode] = $oObj->GetAsHTML($sAttCode);
}
$aValues[$oObj->GetKey()] = $aRow;
}
$this->table($aAttribs, $aValues);
}
}
elseif (strlen($sEmptyListMessage) > 0)
{
$this->add($sEmptyListMessage);
}
}
/**
* Display the attributes of an object (no title, no form)
* @param Object $oObj Any kind of object
* @param aAttList The list of attributes to display
* @return void
*/
public function DisplayObjectDetails($oObj, $aAttList)
{
$sClass = get_class($oObj);
$aDetails = array();
foreach($aAttList as $sAttCode)
{
$iFlags = $oObj->GetAttributeFlags($sAttCode);
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
if ( (!$oAttDef->IsLinkSet()) && (($iFlags & OPT_ATT_HIDDEN) == 0) )
{
// Don't display linked set and non-visible attributes (in this state)
$sDisplayValue = $oObj->GetAsHTML($sAttCode);
$aDetails[] = array('label' => '<span title="'.MetaModel::GetDescription($sClass, $sAttCode).'">'.MetaModel::GetLabel($sClass, $sAttCode).'</span>', 'value' => $sDisplayValue);
}
}
$this->details($aDetails);
}
/**
* DisplayObjectLinkset
* @param Object $oObj Any kind of object
* @param $sLinkSetAttCode The attribute code of the link set attribute to display
* @param $sRemoteAttCode The external key on the linked class, pointing to the remote objects
* @param $aZList The list of attribute of the remote object
* @param $sEmptyListMessage The message to display if the list is empty
* @return void
*/
public function DisplayObjectLinkset($oObj, $sLinkSetAttCode, $sRemoteAttCode, $aZList, $sEmptyListMessage = '', $oSearchRestriction = null)
{
if (empty($sEmptyListMessage))
{
$sEmptyListMessage = Dict::S('UI:Search:NoObjectFound');
}
$oLinkSet = $oObj->Get($sLinkSetAttCode);
if ($oLinkSet->Count() > 0)
{
$sClass = $oLinkSet->GetClass();
$oExtKeyToRemote = MetaModel::GetAttributeDef($sClass, $sRemoteAttCode);
$sRemoteClass = $oExtKeyToRemote->GetTargetClass();
if (is_null($oSearchRestriction))
{
$oObjSearch = new DBObjectSearch($sRemoteClass);
}
else
{
$oObjSearch = $oSearchRestriction;
}
$oObjSearch->AddCondition_ReferencedBy($oLinkSet->GetFilter(), $sRemoteAttCode);
$aExtraParams = array('menu' => false, 'toolkit_menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList));
$oBlock = new DisplayBlock($oObjSearch, 'list', false);
$oBlock->Display($this, 1, $aExtraParams);
}
elseif (strlen($sEmptyListMessage) > 0)
{
$this->add($sEmptyListMessage);
}
}
protected function DisplaySearchField($sClass, $sAttSpec, $aExtraParams, $sPrefix, $sFieldName = null, $aFilterParams = array())
{
if (is_null($sFieldName))
{
$sFieldName = str_replace('->', PARAM_ARROW_SEP, $sAttSpec);
}
$iPos = strpos($sAttSpec, '->');
if ($iPos !== false)
{
$sAttCode = substr($sAttSpec, 0, $iPos);
$sSubSpec = substr($sAttSpec, $iPos + 2);
if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
{
throw new Exception("Invalid attribute code '$sClass/$sAttCode' in search specification '$sAttSpec'");
}
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->IsLinkSet())
{
$sTargetClass = $oAttDef->GetLinkedClass();
}
elseif ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
{
$sTargetClass = $oAttDef->GetTargetClass(EXTKEY_ABSOLUTE);
}
else
{
throw new Exception("Attribute specification '$sAttSpec', '$sAttCode' should be either a link set or an external key");
}
$this->DisplaySearchField($sTargetClass, $sSubSpec, $aExtraParams, $sPrefix, $sFieldName, $aFilterParams);
}
else
{
// $sAttSpec is an attribute code
//
$this->add('<span style="white-space: nowrap;padding:5px;display:inline-block;">');
$sFilterValue = '';
$sFilterValue = utils::ReadParam($sPrefix.$sFieldName, '', false, 'raw_data');
$sFilterOpCode = null; // Use the default 'loose' OpCode
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttSpec);
if ($oAttDef->IsExternalKey())
{
$sTargetClass = $oAttDef->GetTargetClass();
$sFilterDefName = 'PORTAL_TICKETS_SEARCH_FILTER_'.$sAttSpec;
if (defined($sFilterDefName))
{
try
{
$oFitlerWithParams = DBObjectSearch::FromOQL(constant($sFilterDefName));
$sFilterOQL = $oFitlerWithParams->ToOQL(true, $aFilterParams);
$oAllowedValues = new DBObjectSet(DBObjectSearch::FromOQL($sFilterOQL), array(), $aFilterParams);
}
catch(OQLException $e)
{
throw new Exception("Incorrect filter '$sFilterDefName' for attribute '$sAttcode': ".$e->getMessage());
}
}
else
{
$oAllowedValues = new DBObjectSet(new DBObjectSearch($sTargetClass));
}
$iFieldSize = $oAttDef->GetMaxSize();
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
$this->add("<label>".MetaModel::GetFilterLabel($sClass, $sAttSpec).":</label>&nbsp;");
//$oWidget = UIExtKeyWidget::DIsplayFromAttCode($sAttSpec, $sClass, $oAttDef->GetLabel(), $oAllowedValues, $sFilterValue, $sPrefix.$sFieldName, false, '', $sPrefix, '');
//$this->add($oWidget->Display($this, $aExtraParams, true /* bSearchMode */));
$aExtKeyParams = $aExtraParams;
$aExtKeyParams['iFieldSize'] = $oAttDef->GetMaxSize();
$aExtKeyParams['iMinChars'] = $oAttDef->GetMinAutoCompleteChars();
// DisplayFromAttCode($this, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '', $aArgs, $bSearchMode = false)
$sHtml = UIExtKeyWidget::DisplayFromAttCode($this, $sAttSpec, $sClass, $oAttDef->GetLabel(), $oAllowedValues, $sFilterValue, $sPrefix.$sFieldName, false, $sPrefix.$sFieldName, $sPrefix, $aExtKeyParams, true);
$this->add($sHtml);
}
else
{
$aAllowedValues = MetaModel::GetAllowedValues_flt($sClass, $sAttSpec, $aExtraParams);
if (is_null($aAllowedValues))
{
// Any value is possible, display an input box
$sSanitizedValue = htmlentities($sFilterValue, ENT_QUOTES, 'UTF-8');
$this->add("<label>".MetaModel::GetFilterLabel($sClass, $sAttSpec).":</label>&nbsp;<input class=\"textSearch\" name=\"$sPrefix$sFieldName\" value=\"$sSanitizedValue\"/>\n");
}
else
{
//Enum field or external key, display a combo
$sValue = "<select name=\"$sPrefix$sFieldName\">\n";
$sValue .= "<option value=\"\">".Dict::S('UI:SearchValue:Any')."</option>\n";
foreach($aAllowedValues as $key => $value)
{
if ($sFilterValue == $key)
{
$sSelected = ' selected';
}
else
{
$sSelected = '';
}
$sValue .= "<option value=\"$key\"$sSelected>$value</option>\n";
}
$sValue .= "</select>\n";
$this->add("<label>".MetaModel::GetFilterLabel($sClass, $sAttSpec).":</label>&nbsp;$sValue\n");
}
}
unset($aExtraParams[$sFieldName]);
$this->add('</span> ');
$sTip = $oAttDef->GetHelpOnSmartSearch();
if (strlen($sTip) > 0)
{
$sTip = addslashes($sTip);
$sTip = str_replace(array("\n", "\r"), " ", $sTip);
// :input does represent in form visible input (INPUT, SELECT, TEXTAREA)
$this->add_ready_script("$(':input[name={$sPrefix}$sFieldName]').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
}
}
}
/**
* Get The organization of the current user (i.e. the organization of its contact)
* @throws Exception
*/
function GetUserOrg()
{
$oOrg = null;
$iContactId = UserRights::GetContactId();
$oContact = MetaModel::GetObject('Contact', $iContactId, false); // false => Can fail
if (is_object($oContact))
{
$oOrg = MetaModel::GetObject('Organization', $oContact->Get('org_id'), false); // false => can fail
}
else
{
throw new Exception(Dict::S('Portal:ErrorNoContactForThisUser'));
}
return $oOrg;
}
public function DisplaySearchForm($sClass, $aAttList, $aExtraParams, $sPrefix, $bClosed = true)
{
$oUserOrg = $this->GetUserOrg();
$aFilterParams = array('org_id' => $oUserOrg->GetKey(), 'contact_id' => UserRights::GetContactId());
$sCSSClass = ($bClosed) ? 'DrawerClosed' : '';
$this->add("<div id=\"ds_$sPrefix\" class=\"SearchDrawer $sCSSClass\">\n");
$this->add_ready_script(
<<<EOF
$("#dh_$sPrefix").click( function() {
$("#ds_$sPrefix").slideToggle('normal', function() { $("#ds_$sPrefix").parent().resize(); } );
$("#dh_$sPrefix").toggleClass('open');
});
EOF
);
$this->add("<form id=\"search_$sClass\" action=\"\" method=\"post\">\n"); // Don't use $_SERVER['SCRIPT_NAME'] since the form may be called asynchronously (from ajax.php)
// $this->add("<h2>".Dict::Format('UI:SearchFor_Class_Objects', 'xxxxxx')."</h2>\n");
$this->add("<p>\n");
foreach($aAttList as $sAttSpec)
{
//$oAppContext->Reset($sAttSpec); // Make sure the same parameter will not be passed twice
$this->DisplaySearchField($sClass, $sAttSpec, $aExtraParams, $sPrefix, null, $aFilterParams);
}
$this->add("</p>\n");
$this->add("<p align=\"right\"><input type=\"submit\" value=\"".Dict::S('UI:Button:Search')."\"></p>\n");
foreach($aExtraParams as $sName => $sValue)
{
// Note: use DumpHiddenParams() to transmit arrays as hidden params
if (is_scalar($sValue))
{
$this->add("<input type=\"hidden\" name=\"$sName\" value=\"$sValue\" />\n");
}
}
// $this->add($oAppContext->GetForForm());
$this->add("</form>\n");
$this->add("</div>\n");
$this->add("<div class=\"HRDrawer\"></div>\n");
$this->add("<div id=\"dh_$sPrefix\" class=\"DrawerHandle\">".Dict::S('UI:SearchToggle')."</div>\n");
}
/**
* Read parameters from the page
* Parameters that were absent from the page's parameters are not set in the resulting hash array
* @input string $sMethod Either get or post
* @return Hash Array of name => value corresponding to the parameters that were passed to the page
*/
public function ReadAllParams($sParamList, $sPrefix = 'attr_')
{
$aParams = explode(',', $sParamList);
$aValues = array();
foreach($aParams as $sName)
{
$sName = trim($sName);
$value = utils::ReadParam($sPrefix.$sName, null, false, 'raw_data');
if (!is_null($value))
{
$aValues[$sName] = $value;
}
}
return $aValues;
}
/**
* Outputs a list of parameters as hidden fields
* Example: attr_dummy[-123][id] = "blah"
* @param Hash $aParameters Array name => value for the parameters
* @param Array $aExclude The list of parameters that must not be handled this way (probably already in the visible part of the form)
* @return void
*/
protected function DumpHiddenParamsInternal($sName, $value)
{
if (is_array($value))
{
foreach($value as $sKey => $item)
{
$this->DumpHiddenParamsInternal($sName.'['.$sKey.']', $item);
}
}
else
{
$this->Add("<input type=\"hidden\" name=\"$sName\" value=\"$value\">");
}
}
/**
* Outputs a list of parameters as hidden field into the current page
* (must be called when inside a form)
* @param Hash $aParameters Array name => value for the parameters
* @param Array $aExclude The list of parameters that must not be handled this way (probably already in the visible part of the form)
* @return void
*/
public function DumpHiddenParams($aParameters, $aExclude = null, $sPrefix = 'attr_')
{
foreach($aParameters as $sAttCode => $value)
{
if (is_null($aExclude) || !in_array($sAttCode, $aExclude))
{
$this->DumpHiddenParamsInternal($sPrefix.$sAttCode, $value);
}
}
}
public function PostedParamsToFilter($sClass, $aAttList, $sPrefix)
{
$oFilter = new DBObjectSearch($sClass);
$iCountParams = 0;
foreach($aAttList as $sAttSpec)
{
$sFieldName = str_replace('->', PARAM_ARROW_SEP, $sAttSpec);
$value = utils::ReadPostedParam($sPrefix.$sFieldName, null, 'raw_data');
if (!is_null($value) && (is_array($value) ? count($value)>0 : strlen($value)>0))
{
$oFilter->AddConditionAdvanced($sAttSpec, $value);
$iCountParams++;
}
}
if ($iCountParams == 0)
{
return null;
}
else
{
return $oFilter;
}
}
/**
* Updates the object form POSTED arguments, and writes it into the DB (applies a stimuli if requested)
* @param DBObject $oObj The object to update
* $param array $aAttList If set, this will limit the list of updated attributes
* @return void
*/
public function DoUpdateObjectFromPostedForm(DBObject $oObj, $aAttList = null)
{
$sTransactionId = utils::ReadPostedParam('transaction_id', '');
if (!utils::IsTransactionValid($sTransactionId))
{
throw new TransactionException();
}
$sClass = get_class($oObj);
$sStimulus = trim(utils::ReadPostedParam('apply_stimulus', ''));
$sTargetState = '';
if (!empty($sStimulus))
{
// Compute the target state
$aTransitions = $oObj->EnumTransitions();
if (!isset($aTransitions[$sStimulus]))
{
throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $oObj->GetName(), $oObj->GetStateLabel()));
}
$sTargetState = $aTransitions[$sStimulus]['target_state'];
}
$oObj->UpdateObjectFromPostedForm('' /* form prefix */, $aAttList, $sTargetState);
// Optional: apply a stimulus
//
if (!empty($sStimulus))
{
if (!$oObj->ApplyStimulus($sStimulus))
{
throw new Exception("Cannot apply stimulus '$sStimulus' to {$oObj->GetName()}");
}
}
if ($oObj->IsModified())
{
// Record the change
//
$oObj->DBUpdate();
// Trigger ?
//
$aClasses = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL);
$sClassList = implode(", ", CMDBSource::Quote($aClasses));
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnPortalUpdate AS t WHERE t.target_class IN ($sClassList)"));
while ($oTrigger = $oSet->Fetch())
{
$oTrigger->DoActivate($oObj->ToArgs('this'));
}
$this->p("<h1>".Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName())."</h1>\n");
}
$bLockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
if ($bLockEnabled)
{
// Release the concurrent lock, if any
$sOwnershipToken = utils::ReadPostedParam('ownership_token', null, false, 'raw_data');
if ($sOwnershipToken !== null)
{
// We're done, let's release the lock
iTopOwnershipLock::ReleaseLock(get_class($oObj), $oObj->GetKey(), $sOwnershipToken);
}
}
}
/**
* Find the object of the specified Class/ID.
* @param WebPage $oP The current page
* @return DBObject The found object, or throws an exception in case of failure
*/
public function FindObjectFromArgs($aAllowedClasses = null)
{
$sClass = utils::ReadParam('class', '', true, 'class');
$iId = utils::ReadParam('id', 0, true, 'integer');
if (empty($sClass))
{
throw new Exception("Missing argument 'class'");
}
if (!MetaModel::IsValidClass($sClass))
{
throw new Exception("Wrong value for argument 'class': $sClass");
}
if ($iId == 0)
{
throw new Exception("Missing argument 'id'");
}
if(!is_null($aAllowedClasses))
{
$bAllowed = false;
foreach($aAllowedClasses as $sParentClass)
{
if (MetaModel::IsParentClass($sParentClass, $sClass))
{
$bAllowed = true;
}
}
if (!$bAllowed)
{
throw new Exception("Class '$sClass not allowed in this implementation'");
}
}
$oObj = MetaModel::GetObject($sClass, $iId, false);
if (!is_object($oObj))
{
throw new Exception("Could not find the object $sClass/$iId");
}
return $oObj;
}
var $m_sWizardId = null;
public function WizardFormStart($sId = '', $sNextStep = null, $bAttachment = false, $sMethod = 'post')
{
$this->m_sWizardId = $sId;
// multipart... needed for file upload
$this->add("<form id=\"{$this->m_sWizardId}\" method=\"$sMethod\" enctype=\"multipart/form-data\" onsubmit=\"window.bInSubmit = true;\">\n");
$aPreviousSteps = $this->GetWizardStepHistory();
if (utils::ReadParam('step_back', 0) == 1)
{
// Back into the past history
array_pop($aPreviousSteps);
}
else
{
// Moving forward
array_push($aPreviousSteps, utils::ReadParam('next_step'));
}
$sStepHistory = implode(',', $aPreviousSteps);
$this->add("<input type=\"hidden\" id=\"step_history\" name=\"step_history\" value=\"".htmlentities($sStepHistory, ENT_QUOTES, 'UTF-8')."\">");
if (!is_null($sNextStep))
{
$this->add("<input type=\"hidden\" id=\"next_step\" name=\"next_step\" value=\"$sNextStep\">");
}
$this->add("<input type=\"hidden\" id=\"step_back\" name=\"step_back\" value=\"0\">");
$sTransactionId = utils::GetNewTransactionId();
$this->SetTransactionId($sTransactionId);
$this->add("<input type=\"hidden\" id=\"transaction_id\" name=\"transaction_id\" value=\"$sTransactionId\">\n");
$this->add_ready_script("$(window).unload(function() { OnUnload('$sTransactionId') } );\n");
}
public function WizardFormButtons($iButtonFlags)
{
$aButtons = array();
if ($iButtonFlags & BUTTON_CANCEL)
{
$aButtons[] = "<input id=\"btn_cancel\" type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"GoHome();\">";
}
if ($iButtonFlags & BUTTON_BACK)
{
if (utils::ReadParam('step_back', 1) != 1)
{
$aButtons[] = "<input id=\"btn_back\" type=\"submit\" value=\"".Dict::S('UI:Button:Back')."\" onClick=\"GoBack('{$this->m_sWizardId}');\">";
}
}
if ($iButtonFlags & BUTTON_NEXT)
{
$aButtons[] = "<input id=\"btn_next\" type=\"submit\" value=\"".Dict::S('UI:Button:Next')."\">";
}
if ($iButtonFlags & BUTTON_FINISH)
{
$aButtons[] = "<input id=\"btn_finish\" type=\"submit\" value=\"".Dict::S('UI:Button:Finish')."\">";
}
$this->add('<div id="buttons">');
$this->add(implode('', $aButtons));
$this->add('</div>');
}
public function WizardFormEnd()
{
$this->add("</form>\n");
}
public function GetWizardStep()
{
if (utils::ReadParam('step_back', 0) == 1)
{
// Take the value into the history - one level above
$aPreviousSteps = $this->GetWizardStepHistory();
array_pop($aPreviousSteps);
return end($aPreviousSteps);
}
else
{
return utils::ReadParam('next_step');
}
}
protected function GetWizardStepHistory()
{
$sRawHistory = trim(utils::ReadParam('step_history', '', false, 'raw_data'));
if (strlen($sRawHistory) == 0)
{
return array();
}
else
{
return explode(',', $sRawHistory);
}
}
public function WizardCheckSelectionOnSubmit($sMessageIfNoSelection, $sInputName = '')
{
$this->add_ready_script(
<<<EOF
$('#{$this->m_sWizardId}').submit(function() {
return CheckSelection('$sMessageIfNoSelection', '$sInputName');
});
EOF
);
}
}
?>
?>

View File

@@ -0,0 +1,141 @@
<?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/>
/**
* Persistent class Event and derived
* Application internal events
* There is also a file log
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class Query extends cmdbAbstractObject
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui,application",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_query",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("fields", array("allowed_values"=>null, "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'fields')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'fields')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
class QueryOQL extends Query
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui,application",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_query_oql",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeOQL("oql", array("allowed_values"=>null, "sql"=>"oql", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'oql', 'fields')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'fields', 'oql')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
$aFieldsMap = parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
if (!$bEditMode)
{
$sFields = trim($this->Get('fields'));
$bExportV1Recommended = ($sFields == '');
if ($bExportV1Recommended)
{
$oFieldAttDef = MetaModel::GetAttributeDef('QueryOQL', 'fields');
$oPage->add('<div class="message message_error" style="padding-left: 30px;"><div style="padding: 10px;">'.Dict::Format('UI:Query:UrlV1', $oFieldAttDef->GetLabel()).'</div></div>');
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
}
else
{
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
}
$sOql = $this->Get('oql');
$sMessage = null;
try
{
$oSearch = DBObjectSearch::FromOQL($sOql);
$aParameters = $oSearch->GetQueryParams();
foreach($aParameters as $sParam => $val)
{
$sUrl .= '&arg_'.$sParam.'=["'.$sParam.'"]';
}
$oPage->p(Dict::S('UI:Query:UrlForExcel').':<br/><textarea cols="80" rows="3" READONLY>'.$sUrl.'</textarea>');
if (count($aParameters) == 0)
{
$oBlock = new DisplayBlock($oSearch, 'list');
$aExtraParams = array(
//'menu' => $sShowMenu,
'table_id' => 'query_preview_'.$this->getKey(),
);
$sBlockId = 'block_query_preview_'.$this->GetKey(); // make a unique id (edition occuring in the same DOM)
$oBlock->Display($oPage, $sBlockId, $aExtraParams);
}
}
catch (OQLException $e)
{
$sMessage = '<div class="message message_error" style="padding-left: 30px;"><div style="padding: 10px;">'.Dict::Format('UI:RunQuery:Error', $e->getHtmlDesc()).'</div></div>';
$oPage->p($sMessage);
}
}
return $aFieldsMap;
}
}
?>

View File

@@ -0,0 +1,338 @@
<?php
// Copyright (C) 2010-2013 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/>
/**
* Persistent class Shortcut and derived
* Shortcuts of any kind
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class Shortcut extends DBObject implements iDisplay
{
public static function Init()
{
$aParams = array
(
"category" => "gui,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_shortcut",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("context", array("allowed_values"=>null, "sql"=>"context", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'context')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('name')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
abstract public function RenderContent(WebPage $oPage, $aExtraParams = array());
protected function OnInsert()
{
$this->Set('user_id', UserRights::GetUserId());
}
public function StartRenameDialog($oPage)
{
$oPage->add('<div id="shortcut_rename_dlg">');
$oForm = new DesignerForm();
$sDefault = $this->Get('name');
$oField = new DesignerTextField('name', Dict::S('Class:Shortcut/Attribute:name'), $sDefault);
$oField->SetMandatory(true);
$oForm->AddField($oField);
$oForm->Render($oPage);
$oPage->add('</div>');
$sDialogTitle = Dict::S('UI:ShortcutRenameDlg:Title');
$sOkButtonLabel = Dict::S('UI:Button:Ok');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$iShortcut = $this->GetKey();
$oPage->add_ready_script(
<<<EOF
function ShortcutRenameOK()
{
var oForm = $(this).find('form');
var sFormId = oForm.attr('id');
var oParams = null;
var aErrors = ValidateForm(sFormId, false);
if (aErrors.length == 0)
{
oParams = ReadFormParams(sFormId);
}
oParams.operation = 'shortcut_rename_go';
oParams.id = $iShortcut;
var me = $(this);
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function(data) {
me.dialog( "close" );
me.remove();
$('body').append(data);
});
}
$('#shortcut_rename_dlg form').bind('submit', function() { return false; });
$('#shortcut_rename_dlg').dialog({
width: 400,
modal: true,
title: '$sDialogTitle',
buttons: [
{ text: "$sOkButtonLabel", click: ShortcutRenameOK},
{ text: "$sCancelButtonLabel", click: function() {
$(this).dialog( "close" ); $(this).remove();
} },
],
close: function() { $(this).remove(); }
});
EOF
);
}
// Minimual implementation of iDisplay: to make the shortcut be listable
//
public static function MapContextParam($sContextParam)
{
return (($sContextParam == 'menu') ? null : $sContextParam);
}
public function GetHilightClass()
{
return HILIGHT_CLASS_NONE;
}
public static function GetUIPage()
{
return '';
}
function DisplayDetails(WebPage $oPage, $bEditMode = false)
{
}
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
return array();
}
// End of the minimal implementation of iDisplay
}
class ShortcutOQL extends Shortcut
{
public static function Init()
{
$aParams = array
(
"category" => "gui,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_shortcut_oql",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeOQL("oql", array("allowed_values"=>null, "sql"=>"oql", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("auto_reload", array("allowed_values"=>new ValueSetEnum('none,custom'), "sql"=>"auto_reload", "default_value"=>"none", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("auto_reload_sec", array("allowed_values"=>null, "sql"=>"auto_reload_sec", "default_value"=>60, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'context', 'oql')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('name')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
$oPage->set_title($this->Get('name'));
switch($this->Get('auto_reload'))
{
case 'custom':
$iRate = (int)$this->Get('auto_reload_sec');
if ($iRate > 0)
{
// Must a string otherwise it can be evaluated to 'true' and defaults to "standard" refresh rate!
$aExtraParams['auto_reload'] = (string)$iRate;
}
break;
default:
case 'none':
}
$bSearchPane = true;
$bSearchOpen = false;
try
{
OQLMenuNode::RenderOQLSearch($this->Get('oql'), $this->Get('name'), 'shortcut_'.$this->GetKey(), $bSearchPane, $bSearchOpen, $oPage, $aExtraParams);
}
catch (Exception $e)
{
throw new Exception("The OQL shortcut '".$this->Get('name')."' (id: ".$this->GetKey().") could not be displayed: ".$e->getMessage());
}
}
public function CloneTableSettings($sTableSettings)
{
$aTableSettings = json_decode($sTableSettings, true);
$oFilter = DBObjectSearch::FromOQL($this->Get('oql'));
$oCustomSettings = new DataTableSettings($oFilter->GetSelectedClasses());
$oCustomSettings->iDefaultPageSize = $aTableSettings['iPageSize'];
$oCustomSettings->aColumns = $aTableSettings['oColumns'];
$oCustomSettings->Save('shortcut_'.$this->GetKey());
}
public static function GetCreationForm($sOQL = null, $sTableSettings = null)
{
$oForm = new DesignerForm();
// Find a unique default name
// -> The class of the query + an index if necessary
if ($sOQL == null)
{
$sDefault = '';
}
else
{
$oBMSearch = new DBObjectSearch('Shortcut');
$oBMSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oBMSet = new DBObjectSet($oBMSearch);
$aNames = $oBMSet->GetColumnAsArray('name');
$oSearch = DBObjectSearch::FromOQL($sOQL);
$sDefault = utils::MakeUniqueName($oSearch->GetClass(), $aNames);
}
$oField = new DesignerTextField('name', Dict::S('Class:Shortcut/Attribute:name'), $sDefault);
$oField->SetMandatory(true);
$oForm->AddField($oField);
/*
$oField = new DesignerComboField('auto_reload', Dict::S('Class:ShortcutOQL/Attribute:auto_reload'), 'none');
$oAttDef = MetaModel::GetAttributeDef(__class__, 'auto_reload');
$oField->SetAllowedValues($oAttDef->GetAllowedValues());
$oField->SetMandatory(true);
$oForm->AddField($oField);
*/
$oField = new DesignerBooleanField('auto_reload', Dict::S('Class:ShortcutOQL/Attribute:auto_reload'), false);
$oForm->AddField($oField);
$oField = new DesignerIntegerField('auto_reload_sec', Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec'), MetaModel::GetConfig()->GetStandardReloadInterval());
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
$oField->SetMandatory(false);
$oForm->AddField($oField);
$oField = new DesignerHiddenField('oql', '', $sOQL);
$oForm->AddField($oField);
$oField = new DesignerHiddenField('table_settings', '', $sTableSettings);
$oForm->AddField($oField);
return $oForm;
}
public static function GetCreationDlgFromOQL($oPage, $sOQL, $sTableSettings)
{
$oPage->add('<div id="shortcut_creation_dlg">');
$oForm = self::GetCreationForm($sOQL, $sTableSettings);
$oForm->Render($oPage);
$oPage->add('</div>');
$sDialogTitle = Dict::S('UI:ShortcutListDlg:Title');
$sOkButtonLabel = Dict::S('UI:Button:Ok');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$sRateTitle = addslashes(Dict::Format('Class:ShortcutOQL/Attribute:auto_reload_sec/tip', MetaModel::GetConfig()->Get('min_reload_interval')));
$oPage->add_ready_script(
<<<EOF
// Note: the title gets deleted by the validation mechanism
$("#attr_auto_reload_sec").tooltip({items: 'input', content: '$sRateTitle'});
$("#attr_auto_reload_sec").prop('disabled', !$('#attr_auto_reload').is(':checked'));
$('#attr_auto_reload').change( function(ev) {
$("#attr_auto_reload_sec").prop('disabled', !$(this).is(':checked'));
} );
function ShortcutCreationOK()
{
var oForm = $('#shortcut_creation_dlg form');
var sFormId = oForm.attr('id');
var oParams = null;
var aErrors = ValidateForm(sFormId, false);
if (aErrors.length == 0)
{
oParams = ReadFormParams(sFormId);
}
oParams.operation = 'shortcut_list_create';
var me = $('#shortcut_creation_dlg');
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?$sContext', oParams, function(data) {
me.dialog( "close" );
me.remove();
$('body').append(data);
});
}
$('#shortcut_creation_dlg form').bind('submit', function() { ShortcutCreationOK(); return false; });
$('#shortcut_creation_dlg').dialog({
width: 400,
modal: true,
title: '$sDialogTitle',
buttons: [
{ text: "$sOkButtonLabel", click: ShortcutCreationOK },
{ text: "$sCancelButtonLabel", click: function() {
$(this).dialog( "close" ); $(this).remove();
} },
],
close: function() { $(this).remove(); }
});
EOF
);
}
}
?>

View File

@@ -0,0 +1,531 @@
<?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,31 +1,49 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* File to include to initialize the datamodel in memory
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('../core/cmdbobject.class.inc.php');
require_once('../application/utils.inc.php');
MetaModel::Startup(ITOP_CONFIG_FILE);
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
session_name('itop-'.md5(APPROOT));
session_start();
if (isset($_REQUEST['switch_env']))
{
$sEnv = $_REQUEST['switch_env'];
$_SESSION['itop_env'] = $sEnv;
// TODO: reset the credentials as well ??
}
else if (isset($_SESSION['itop_env']))
{
$sEnv = $_SESSION['itop_env'];
}
else
{
$sEnv = ITOP_DEFAULT_ENV;
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
MetaModel::Startup($sConfigFile);
?>

View File

@@ -1,29 +1,30 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class DisplayTemplate
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('../application/displayblock.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
/**
* This class manages the special template format used internally to build the iTop web pages
*/
@@ -35,7 +36,15 @@ class DisplayTemplate
public function __construct($sTemplate)
{
$this->m_aTags = array('itopblock', 'itopcheck', 'itoptabs', 'itoptab', 'itoptoggle', 'itopstring');
$this->m_aTags = array (
'itopblock',
'itopcheck',
'itoptabs',
'itoptab',
'itoptoggle',
'itopstring',
'sqlblock'
);
$this->m_sTemplate = $sTemplate;
}
@@ -161,7 +170,7 @@ class DisplayTemplate
$oPage->AddTabContainer('Tabs_'.$iTabContainerCount);
$oPage->SetCurrentTabContainer('Tabs_'.$iTabContainerCount);
$iTabContainerCount++;
//$oPage->p('Content:<pre>'.htmlentities($sContent).'</pre>');
//$oPage->p('Content:<pre>'.htmlentities($sContent, ENT_QUOTES, 'UTF-8').'</pre>');
$oTemplate = new DisplayTemplate($sContent);
$oTemplate->Render($oPage, array()); // no params to apply, they have already been applied
$oPage->SetCurrentTabContainer('');
@@ -185,7 +194,7 @@ class DisplayTemplate
$oPage->SetCurrentTab(Dict::S(str_replace('_', ' ', $aAttributes['name'])));
$oTemplate = new DisplayTemplate($sContent);
$oTemplate->Render($oPage, array()); // no params to apply, they have already been applied
//$oPage->p('iTop Tab Content:<pre>'.htmlentities($sContent).'</pre>');
//$oPage->p('iTop Tab Content:<pre>'.htmlentities($sContent, ENT_QUOTES, 'UTF-8').'</pre>');
$oPage->SetCurrentTab('');
break;
@@ -195,7 +204,7 @@ class DisplayTemplate
$oPage->StartCollapsibleSection(Dict::S($sName), $bOpen);
$oTemplate = new DisplayTemplate($sContent);
$oTemplate->Render($oPage, array()); // no params to apply, they have already been applied
//$oPage->p('iTop Tab Content:<pre>'.htmlentities($sContent).'</pre>');
//$oPage->p('iTop Tab Content:<pre>'.htmlentities($sContent, ENT_QUOTES, 'UTF-8').'</pre>');
$oPage->EndCollapsibleSection();
break;
@@ -203,6 +212,11 @@ class DisplayTemplate
$oPage->add(Dict::S($sContent));
break;
case 'sqlblock':
$oBlock = SqlBlock::FromTemplate($sContent);
$oBlock->RenderContent($oPage);
break;
case 'itopblock': // No longer used, handled by DisplayBlock::FromTemplate see above
$oPage->add("<!-- Application Error: should be handled by DisplayBlock::FromTemplate -->");
break;
@@ -218,8 +232,8 @@ class DisplayTemplate
*/
static public function UnitTest()
{
require_once('../application/startup.inc.php');
require_once("../application/itopwebpage.class.inc.php");
require_once(APPROOT.'/application/startup.inc.php');
require_once(APPROOT."/application/itopwebpage.class.inc.php");
$sTemplate = '<div class="page_header">
<div class="actions_details"><a href="#"><span>Actions</span></a></div>
@@ -227,7 +241,6 @@ class DisplayTemplate
<itopblock blockclass="HistoryBlock" type="toggle" encoding="text/oql">SELECT CMDBChangeOp WHERE objkey = $id$ AND objclass = \'$class$\'</itopblock>
</div>
<img src="../../images/connect_to_network.png" style="margin-top:-10px; margin-right:10px; float:right">
<itopblock blockclass="DisplayBlock" asynchronous="false" type="bare_details" encoding="text/oql">SELECT NetworkDevice AS d WHERE d.id = $id$</itopblock>
<itoptabs>
<itoptab name="Interfaces">
<itopblock blockclass="DisplayBlock" type="list" encoding="text/oql">SELECT Interface AS i WHERE i.device_id = $id$</itopblock>
@@ -240,14 +253,171 @@ class DisplayTemplate
</itoptab>
</itoptabs>';
$oPage = new iTopWebPage('Unit Test', 3);
//$oPage->add("Template content: <pre>".htmlentities($sTemplate)."</pre>\n");
$oPage = new iTopWebPage('Unit Test');
//$oPage->add("Template content: <pre>".htmlentities($sTemplate, ENT_QUOTES, 'UTF-8')."</pre>\n");
$oTemplate = new DisplayTemplate($sTemplate);
$oTemplate->Render($oPage, array('class'=>'Network device','pkey'=> 271, 'name' => 'deliversw01.mecanorama.fr', 'org_id' => 3));
$oPage->output();
}
}
//DisplayTemplate::UnitTest();
/**
* Special type of template for displaying the details of an object
* On top of the defaut 'blocks' managed by the parent class, the following placeholders
* are available in such a template:
* $attribute_code$ An attribute of the object (in edit mode this is the input for the attribute)
* $attribute_code->label()$ The label of an attribute
* $PlugIn:plugInClass->properties()$ The ouput of OnDisplayProperties of the specified plugInClass
*/
class ObjectDetailsTemplate extends DisplayTemplate
{
public function __construct($sTemplate, $oObj, $sFormPrefix = '')
{
parent::__construct($sTemplate);
$this->m_oObj = $oObj;
$this->m_sPrefix = $sFormPrefix;
}
public function Render(WebPage $oPage, $aParams = array(), $bEditMode = false)
{
$sStateAttCode = MetaModel :: GetStateAttributeCode(get_class($this->m_oObj));
$aTemplateFields = array();
preg_match_all('/\\$this->([a-z0-9_]+)\\$/', $this->m_sTemplate, $aMatches);
foreach ($aMatches[1] as $sAttCode)
{
if (MetaModel::IsValidAttCode(get_class($this->m_oObj), $sAttCode))
{
$aTemplateFields[] = $sAttCode;
}
else
{
$aParams['this->'.$sAttCode] = "<!--Unknown attribute: $sAttCode-->";
}
}
preg_match_all('/\\$this->field\\(([a-z0-9_]+)\\)\\$/', $this->m_sTemplate, $aMatches);
foreach ($aMatches[1] as $sAttCode)
{
if (MetaModel::IsValidAttCode(get_class($this->m_oObj), $sAttCode))
{
$aTemplateFields[] = $sAttCode;
}
else
{
$aParams['this->field('.$sAttCode.')'] = "<!--Unknown attribute: $sAttCode-->";
}
}
$aFieldsComments = (isset($aParams['fieldsComments'])) ? $aParams['fieldsComments'] : array();
$aFieldsMap = array();
$sClass = get_class($this->m_oObj);
// Renders the fields used in the template
foreach(MetaModel::ListAttributeDefs(get_class($this->m_oObj)) as $sAttCode => $oAttDef)
{
$aParams['this->label('.$sAttCode.')'] = $oAttDef->GetLabel();
$aParams['this->comments('.$sAttCode.')'] = isset($aFieldsComments[$sAttCode]) ? $aFieldsComments[$sAttCode] : '';
$iInputId = '2_'.$sAttCode; // TODO: generate a real/unique prefix...
if (in_array($sAttCode, $aTemplateFields))
{
if ($this->m_oObj->IsNew())
{
$iFlags = $this->m_oObj->GetInitialStateAttributeFlags($sAttCode);
}
else
{
$iFlags = $this->m_oObj->GetAttributeFlags($sAttCode);
}
if (($iFlags & OPT_ATT_MANDATORY) && $this->m_oObj->IsNew())
{
$iFlags = $iFlags & ~OPT_ATT_READONLY; // Mandatory fields cannot be read-only when creating an object
}
if ((!$oAttDef->IsWritable()) || ($sStateAttCode == $sAttCode))
{
$iFlags = $iFlags | OPT_ATT_READONLY;
}
if ($iFlags & OPT_ATT_HIDDEN)
{
$aParams['this->label('.$sAttCode.')'] = '';
$aParams['this->field('.$sAttCode.')'] = '';
$aParams['this->comments('.$sAttCode.')'] = '';
$aParams['this->'.$sAttCode] = '';
}
else
{
if ($bEditMode && ($iFlags & (OPT_ATT_READONLY|OPT_ATT_SLAVE)))
{
// Check if the attribute is not read-only because of a synchro...
$aReasons = array();
$sSynchroIcon = '';
if ($iFlags & OPT_ATT_SLAVE)
{
$iSynchroFlags = $this->m_oObj->GetSynchroReplicaFlags($sAttCode, $aReasons);
$sSynchroIcon = "&nbsp;<img id=\"synchro_$sInputId\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>";
$sTip = '';
foreach($aReasons as $aRow)
{
$sTip .= "<p>Synchronized with {$aRow['name']} - {$aRow['description']}</p>";
}
$oPage->add_ready_script("$('#synchro_$iInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
}
// Attribute is read-only
$sHTMLValue = "<span id=\"field_{$iInputId}\">".$this->m_oObj->GetAsHTML($sAttCode);
$sHTMLValue .= '<input type="hidden" id="'.$iInputId.'" name="attr_'.$sAttCode.'" value="'.htmlentities($this->m_oObj->Get($sAttCode), ENT_QUOTES, 'UTF-8').'"/></span>';
$aFieldsMap[$sAttCode] = $iInputId;
$aParams['this->comments('.$sAttCode.')'] = $sSynchroIcon;
}
if ($bEditMode && !($iFlags & OPT_ATT_READONLY)) //TODO: check the data synchro status...
{
$aParams['this->field('.$sAttCode.')'] = "<span id=\"field_{$iInputId}\">".$this->m_oObj->GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef,
$this->m_oObj->Get($sAttCode),
$this->m_oObj->GetEditValue($sAttCode),
$iInputId, // InputID
'',
$iFlags,
array('this' => $this->m_oObj) // aArgs
).'</span>';
$aFieldsMap[$sAttCode] = $iInputId;
}
else
{
$aParams['this->field('.$sAttCode.')'] = $this->m_oObj->GetAsHTML($sAttCode);
}
$aParams['this->'.$sAttCode] = "<table class=\"field\"><tr><td class=\"label\">".$aParams['this->label('.$sAttCode.')'].":</td><td>".$aParams['this->field('.$sAttCode.')']."</td><td>".$aParams['this->comments('.$sAttCode.')']."</td></tr></table>";
}
}
}
// Renders the PlugIns used in the template
preg_match_all('/\\$PlugIn:([A-Za-z0-9_]+)->properties\\(\\)\\$/', $this->m_sTemplate, $aMatches);
$aPlugInProperties = $aMatches[1];
foreach($aPlugInProperties as $sPlugInClass)
{
$oInstance = MetaModel::GetPlugins('iApplicationUIExtension', $sPlugInClass);
if ($oInstance != null) // Safety check...
{
$offset = $oPage->start_capture();
$oInstance->OnDisplayProperties($this->m_oObj, $oPage, $bEditMode);
$sContent = $oPage->end_capture($offset);
$aParams["PlugIn:{$sPlugInClass}->properties()"]= $sContent;
}
else
{
$aParams["PlugIn:{$sPlugInClass}->properties()"]= "Missing PlugIn: $sPlugInClass";
}
}
$offset = $oPage->start_capture();
parent::Render($oPage, $aParams);
$sContent = $oPage->end_capture($offset);
// Remove empty table rows in case some attributes are hidden...
$sContent = preg_replace('/<tr[^>]*>\s*(<td[^>]*>\s*<\\/td>)+\s*<\\/tr>/im', '', $sContent);
$oPage->add($sContent);
return $aFieldsMap;
}
}
//DisplayTemplate::UnitTest();
?>

View File

@@ -1,20 +0,0 @@
<div class="page_header" style="padding:0.5em;">
<h1><itopstring>UI:NotificationsMenu:Title</itopstring></h1>
</div>
<itoptoggle name="UI:NotificationsMenu:Help" open="true">
<div style="padding: 1em; font-size:10pt;background:#E8F3CF;margin-top: 0.25em;">
<img src="../images/bell.png" style="margin-top: -60px; margin-right: 10px; float: right;">
<itopstring>UI:NotificationsMenu:HelpContent</itopstring>
</div>
</itoptoggle>
<p>&nbsp;</p>
<itoptabs>
<itoptab name="UI:NotificationsMenu:Triggers">
<h2><itopstring>UI:NotificationsMenu:AvailableTriggers</itopstring></h2>
<itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql">SELECT Trigger</itopblock>
</itoptab>
<itoptab name="UI:NotificationsMenu:Actions">
<h2><itopstring>UI:NotificationsMenu:AvailableActions</itopstring></h2>
<itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql">SELECT ActionEmail</itopblock>
</itoptab>
</itoptabs>

View File

@@ -1,201 +0,0 @@
<style>
.main_container {
width: 100%;
border: 0;
overflow-y: hidden;
overflow-x: hidden;
margin-bottom: 5px;
margin-top: 5px;
border-bottom: #ccc 1px solid;
border-top: #ccc 1px solid;
}
.main_header {
width:100%;
height: 60px;
cursor: move;
background-color: #F1F1F6;
}
.main_header h1 {
color: #1C94C4;
margin-top:0;
margin-bottom:0;
padding-top: 10px;
padding-bottom: 10px;p
font-size: 16px;
line-height: 16px;
}
.main_header img {
margin-top: 10px;
margin-right: 10px;
float:left;
}
.item {
overflow-y: auto;
overflow-x: hidden;
background-color: #fff;
padding: 5px;
height: auto;
}
.main_menu {
overflow-y: auto;
overflow-x: hidden;
height: auto;
background-color: #eee;
width: 100%;
}
.element {
display: inline-block;
}
.element a {
font-size: 10px;
}
.element a.actions {
font-size: 16px;
text-decoration: none;
background: transparent;
color: #666;
}
.element a img {
border: 0;
}
.summary-details {
float:right;
margin-top: 5px;
}
.summary-details td {
text-align: center;
background: transparent;
padding: 5px;
}
.summary-details th {
text-align: center;
background: #CCC;
padding: 5px;
color: #eee;
}
a.summary {
text-decoration: none;
background: transparent;
color: #666;
}
a.summary:hover {
text-decoration: underline;
}
</style>
<script>
var sSerialized = '';
function DumpPositions()
{
sSerialized = $('.sortable').sortable('serialize', {key: 'menu'});
alert(sSerialized);
}
function RestorePositions()
{ height: auto;
var arr = $('#main_menu .main_container').get();
arr.reverse();
var sTemp = sSerialized.replace(/menu=/g, '');
alert(sTemp);
var aSerialized = sTemp.split('&');
console.log(aSerialized);
$.each(aSerialized, function(i,v) {
console.log(i+' => '+v);
var $menu = $('#menu_'+v);
if ($menu.length > 0) // Check that the menu exists
{
$('#main_menu').append($menu);
}
});
}
</script>
<div id="main_menu" class="sortable persistent">
<div id="menu_1" class="main_container v-resizable">
<div class="main_header drag_handle" id="main_cfg">
<img src="../modules/itop-config-mgmt-1.0.0/images/database.png"/>
<itopblock BlockClass="DisplayBlock" type="summary" asynchronous="false" encoding="text/oql" parameters="title[block]:Menu_ConfigManagement;context_filter:1;label[block]:UI_WelcomeMenu_AllConfigItems;status[block]:status;status_codes[block]:implementation,production,obsolete">SELECT FunctionalCI</itopblock>
</div>
<div class="item">
<div class="element">
<itopcheck class="BusinessProcess">
<itopblock BlockClass="DisplayBlock" type="actions" asynchronous="false" encoding="text/oql" parameters="context_filter:1">SELECT BusinessProcess</itopblock>
</itopcheck>
</div>
<div class="element">
<itopcheck class="Contact">
<itopblock BlockClass="DisplayBlock" type="actions" asynchronous="false" encoding="text/oql" parameters="context_filter:1">SELECT Contact</itopblock>
</itopcheck>
</div>
<div class="element">
<itopcheck class="Location">
<itopblock BlockClass="DisplayBlock" type="actions" asynchronous="false" encoding="text/oql" parameters="context_filter:1">SELECT Location</itopblock>
</itopcheck>
</div>
<div class="element">
<itopcheck class="Server">
<itopblock BlockClass="DisplayBlock" type="actions" asynchronous="false" encoding="text/oql" parameters="context_filter:1">SELECT Server</itopblock>
</itopcheck>
</div>
<div class="element">
<itopcheck class="DatabaseInstance">
<itopblock BlockClass="DisplayBlock" type="actions" asynchronous="false" encoding="text/oql" parameters="context_filter:1">SELECT DatabaseInstance</itopblock>
</itopcheck>
</div>
<div class="element">
<itopcheck class="NetworkDevice">
<itopblock BlockClass="DisplayBlock" type="actions" asynchronous="false" encoding="text/oql" parameters="context_filter:1">SELECT NetworkDevice</itopblock>
</itopcheck>
</div>
</div>
</div>
<itopcheck class="UserRequest">
<div id="menu_2" class="main_container v-resizable">
<div class="main_header drag_handle" id="main_helpdesk">
<img src="../modules/itop-request-mgmt-1.0.0/images/user-request-deadline.png"/>
<itopblock BlockClass="DisplayBlock" type="summary" asynchronous="false" encoding="text/oql" parameters="title[block]:Menu_RequestManagement;context_filter:1;label[block]:UI_WelcomeMenu_AllOpenRequests;status[block]:status;status_codes[block]:new,assigned,escalated_tto,escalated_ttr,resolved">SELECT UserRequest WHERE status != "closed"</itopblock>
</div>
<div class="item">
<p style="text-align:left; font-family:Verdana, Arial, sans-serif; font-size:14px;"><itopstring>UI:WelcomeMenu:MyCalls</itopstring></p>
<itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql" parameters="menu:1">SELECT UserRequest AS i WHERE i.caller_id = :current_contact_id AND status NOT IN ("closed", "resolved")</itopblock>
</div>
</div>
</itopcheck>
<itopcheck class="Incident">
<div id="menu_3" class="main_container v-resizable">
<div class="main_header drag_handle" id="main_incident">
<img src="../modules/itop-incident-mgmt-1.0.0/images/incident-escalated.png"/>
<itopblock BlockClass="DisplayBlock" type="summary" asynchronous="false" encoding="text/oql" parameters="title[block]:Menu_IncidentManagement;context_filter:1;label[block]:UI_WelcomeMenu_OpenIncidents;status[block]:status;status_codes[block]:new,assigned,escalated_tto,escalated_ttr,resolved">SELECT Incident WHERE status != "closed"</itopblock>
</div>
<div class="item">
<p style="text-align:left; font-family:Verdana, Arial, sans-serif; font-size:14px;"><itopstring>UI:WelcomeMenu:MyIncidents</itopstring></p>
<itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql" parameters="menu:1">SELECT Incident AS i WHERE i.agent_id = :current_contact_id AND status NOT IN ("closed", "resolved")</itopblock>
</div>
</itopcheck>
</div>
</div>

View File

@@ -1,15 +1,6 @@
<div style="width:100%;background: url(../images/welcome.jpg) top left no-repeat;">
<div style="width:100%;background: #fff url(../images/welcome.jpg) top left no-repeat;">
<style>
.dashboard {
vertical-align:top;
width:50%;
border:0px solid #000;
background-color:#F9F9F1;
padding:10px;
text-align:left;
font-size:10pt;
}
.dashboard2 {
.welcome_popup_cell {
vertical-align:top;
width:50%;
border:0px solid #000;
@@ -17,15 +8,7 @@ border:0px solid #000;
padding:5px;
text-align:left;
}
td.dashboard li {
margin-top: 5px;
display: list-item;
}
td div.display_block {
padding:0;
}
tr td.dashboard2, tr td.dashboard2 ul {
tr td.welcome_popup_cell, tr td.welcome_popup_cell ul {
font-size:10pt;
}
</style>
@@ -35,10 +18,10 @@ font-size:10pt;
<p></p>
<table border="0" style="padding:10px;border-spacing: 10px;width:100%">
<tr>
<td class="dashboard2">
<td class="welcome_popup_cell">
<itopstring>UI:WelcomeMenu:LeftBlock</itopstring>
</td>
<td class="dashboard2">
<td class="welcome_popup_cell">
<itopstring>UI:WelcomeMenu:RightBlock</itopstring>
</td>
</tr>

View File

@@ -1,110 +1,352 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2015 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* This class records the pending "transactions" corresponding to forms that have not been
* submitted yet, in order to prevent double submissions. When created a transaction remains valid
* until it is "used" by calling IsTransactionValid once, or until it
* expires (delay = TRANSACTION_EXPIRATION_DELAY, defaults to 4 hours)
* until the user's session expires. This class is actually a wrapper to the underlying implementation
* which choice is configured via the parameter 'transaction_storage'
*
* @package iTop
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
// How long a "transaction" is considered valid, i.e. when a form is submitted
// if the form dates back from too long a time, it is considered invalid. This is
// because since HTTP is not a connected protocol, we cannot know when a user disconnects
// from the application (maybe just by closing her browser), so we keep track - in the database - of all pending
// forms that have not yet been submitted. To limit this list we consider that after some time
// a "transaction" is no loger valid an gets purged from the table
define ('TRANSACTION_EXPIRATION_DELAY', 3600*4); // default: 4h
require_once('../core/dbobject.class.php');
class privUITransaction extends DBObject
class privUITransaction
{
public static function Init()
{
$aParams = array
(
"category" => "gui",
"key_type" => "autoincrement",
"name_attcode" => "expiration_date",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_transaction",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeDateTime("expiration_date", array("allowed_values"=>null, "sql"=>"expiration_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_SetZListItems('details', array('expiration_date')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('expiration_date')); // Attributes to be displayed for a list
}
/**
* Create a new transaction, store it in the database and return its id
* Create a new transaction id, store it in the session and return its id
* @param void
* @return int The identifier of the new transaction
*/
public static function GetNewTransactionId()
{
// First remove all the expired transactions...
self::CleanupExpiredTransactions();
$oTransaction = new privUITransaction();
$sDate = date('Y-m-d H:i:s', time()+TRANSACTION_EXPIRATION_DELAY);
$oTransaction->Set('expiration_date', $sDate); // 4 h delay by default
$oTransaction->DBInsert();
return sprintf("%d", $oTransaction->GetKey());
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
if (!$bTransactionsEnabled)
{
return 'notransactions'; // Any value will do
}
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
if (!class_exists($sClass, false))
{
IssueLog::Error("Incorrect value '".MetaModel::GetConfig()->Get('transaction_storage')."' for 'transaction_storage', the class '$sClass' does not exists. Using privUITransactionSession instead for storing sessions.");
$sClass = 'privUITransactionSession';
}
return (string)$sClass::GetNewTransactionId();
}
/**
* Check whether a transaction is valid or not and remove the valid transaction from
* the database so that another call to IsTransactionValid for the same transaction
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
* the session so that another call to IsTransactionValid for the same transaction id
* will return false
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
* @param bool $bRemoveTransaction True if the transaction must be removed
* @return bool True if the transaction is valid, false otherwise
*/
public static function IsTransactionValid($id, $bRemoveTransaction = true)
{
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
if (!$bTransactionsEnabled)
{
return true; // All values are valid
}
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
if (!class_exists($sClass, false))
{
$sClass = 'privUITransactionSession';
}
return $sClass::IsTransactionValid($id, $bRemoveTransaction);
}
/**
* Removes the transaction specified by its id
* @param int $id The Identifier (as returned by GetNewTranscationId) of the transaction to be removed.
* @return void
*/
public static function RemoveTransaction($id)
{
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
if (!$bTransactionsEnabled)
{
return; // Nothing to do
}
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
if (!class_exists($sClass, false))
{
$sClass = 'privUITransactionSession';
}
$sClass::RemoveTransaction($id);
}
}
/**
* The original (and by default) mechanism for storing transaction information
* as an array in the $_SESSION variable
*
*/
class privUITransactionSession
{
/**
* Create a new transaction id, store it in the session and return its id
* @param void
* @return int The identifier of the new transaction
*/
public static function GetNewTransactionId()
{
if (!isset($_SESSION['transactions']))
{
$_SESSION['transactions'] = array();
}
// Strictly speaking, the two lines below should be grouped together
// by a critical section
// sem_acquire($rSemIdentified);
$id = str_replace(array('.', ' '), '', microtime()); //1 + count($_SESSION['transactions']);
$_SESSION['transactions'][$id] = true;
// sem_release($rSemIdentified);
return (string)$id;
}
/**
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
* the session so that another call to IsTransactionValid for the same transaction id
* will return false
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
* @param bool $bRemoveTransaction True if the transaction must be removed
* @return bool True if the transaction is valid, false otherwise
*/
public static function IsTransactionValid($id)
public static function IsTransactionValid($id, $bRemoveTransaction = true)
{
// First remove all the expired transactions...
self::CleanupExpiredTransactions();
// TO DO put a critical section around this part to be 100% safe...
// sem_acquire(...)
$bResult = false;
$oTransaction = MetaModel::GetObject('privUITransaction', $id, false /* MustBeFound */);
if ($oTransaction)
$bResult = false;
if (isset($_SESSION['transactions']))
{
$bResult = true;
$oTransaction->DBDelete();
// Strictly speaking, the eight lines below should be grouped together
// inside the same critical section as above
// sem_acquire($rSemIdentified);
if (isset($_SESSION['transactions'][$id]))
{
$bResult = true;
if ($bRemoveTransaction)
{
unset($_SESSION['transactions'][$id]);
}
}
// sem_release($rSemIdentified);
}
return $bResult;
}
/**
* Removes the transaction specified by its id
* @param int $id The Identifier (as returned by GetNewTranscationId) of the transaction to be removed.
* @return void
*/
public static function RemoveTransaction($id)
{
if (isset($_SESSION['transactions']))
{
// Strictly speaking, the three lines below should be grouped together
// inside the same critical section as above
// sem_acquire($rSemIdentified);
if (isset($_SESSION['transactions'][$id]))
{
unset($_SESSION['transactions'][$id]);
}
// sem_release($rSemIdentified);
}
}
}
/**
* An alternate implementation for storing the transactions as temporary files
* Useful when using an in-memory storage for the session which do not
* guarantee mutual exclusion for writing
*/
class privUITransactionFile
{
/**
* Create a new transaction id, store it in the session and return its id
* @param void
* @return int The identifier of the new transaction
*/
public static function GetNewTransactionId()
{
if (!is_dir(APPROOT.'data/transactions'))
{
if (!is_writable(APPROOT.'data'))
{
throw new Exception('The directory "'.APPROOT.'data" must be writable to the application.');
}
if (!@mkdir(APPROOT.'data/transactions'))
{
throw new Exception('Failed to create the directory "'.APPROOT.'data/transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
}
}
if (!is_writable(APPROOT.'data/transactions'))
{
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
}
self::CleanupOldTransactions();
$id = basename(tempnam(APPROOT.'data/transactions', self::GetUserPrefix()));
self::Info('GetNewTransactionId: Created transaction: '.$id);
return (string)$id;
}
/**
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
* the session so that another call to IsTransactionValid for the same transaction id
* will return false
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
* @param bool $bRemoveTransaction True if the transaction must be removed
* @return bool True if the transaction is valid, false otherwise
*/
public static function IsTransactionValid($id, $bRemoveTransaction = true)
{
$sFilepath = APPROOT.'data/transactions/'.$id;
clearstatcache(true, $sFilepath);
$bResult = file_exists($sFilepath);
if ($bResult)
{
if ($bRemoveTransaction)
{
$bResult = @unlink($sFilepath);
if (!$bResult)
{
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
}
else
{
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
}
}
}
else
{
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
}
// sem_release(...)
return $bResult;
}
/**
* Remove from the database all transactions that have expired
* Removes the transaction specified by its id
* @param int $id The Identifier (as returned by GetNewTransactionId) of the transaction to be removed.
* @return void
*/
protected static function CleanupExpiredTransactions()
public static function RemoveTransaction($id)
{
$sQuery = 'SELECT privUITransaction WHERE expiration_date < NOW()';
$oSearch = DBObjectSearch::FromOQL($sQuery);
$oSet = new DBObjectSet($oSearch);
while($oTransaction = $oSet->Fetch())
$bSuccess = true;
$sFilepath = APPROOT.'data/transactions/'.$id;
clearstatcache(true, $sFilepath);
if(!file_exists($sFilepath))
{
$oTransaction->DBDelete();
$bSuccess = false;
self::Error("RemoveTransaction: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
}
$bSuccess = @unlink($sFilepath);
if (!$bSuccess)
{
self::Error('RemoveTransaction: FAILED to remove transaction '.$id);
}
else
{
self::Info('RemoveTransaction: OK '.$id);
}
return $bSuccess;
}
/**
* Cleanup old transactions which have been pending since more than 24 hours
* Use filemtime instead of filectime since filectime may be affected by operations on the directory (like changing the access rights)
*/
protected static function CleanupOldTransactions()
{
$iLimit = time() - 24*3600;
clearstatcache();
$aTransactions = glob(APPROOT.'data/transactions/*-*');
foreach($aTransactions as $sFileName)
{
if (filemtime($sFileName) < $iLimit)
{
@unlink($sFileName);
self::Info('CleanupOldTransactions: Deleted transaction: '.$sFileName);
}
}
}
/**
* For debugging purposes: gets the pending transactions of the current user
* as an array, with the date of the creation of the transaction file
*/
protected static function GetPendingTransactions()
{
clearstatcache();
$aResult = array();
$aTransactions = glob(APPROOT.'data/transactions/'.self::GetUserPrefix().'*');
foreach($aTransactions as $sFileName)
{
$aResult[] = date('Y-m-d H:i:s', filemtime($sFileName)).' - '.basename($sFileName);
}
sort($aResult);
return $aResult;
}
protected static function GetUserPrefix()
{
$sPrefix = substr(UserRights::GetUser(), 0, 10);
$sPrefix = preg_replace('/[^a-zA-Z0-9-_]/', '_', $sPrefix);
return $sPrefix.'-';
}
protected static function Info($sText)
{
self::Write('Info | '.$sText);
}
protected static function Warning($sText)
{
self::Write('Warning | '.$sText);
}
protected static function Error($sText)
{
self::Write('Error | '.$sText);
}
protected static function Write($sText)
{
$bLogEnabled = MetaModel::GetConfig()->Get('log_transactions');
if ($bLogEnabled)
{
$hLogFile = @fopen(APPROOT.'log/transactions.log', 'a');
if ($hLogFile !== false)
{
flock($hLogFile, LOCK_EX);
$sDate = date('Y-m-d H:i:s');
fwrite($hLogFile, "$sDate | $sText\n");
fflush($hLogFile);
flock($hLogFile, LOCK_UN);
fclose($hLogFile);
}
}
}
}
?>

View File

@@ -0,0 +1,585 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class UIExtKeyWidget
* UI wdiget for displaying and editing external keys when
* A simple drop-down list is not enough...
*
* The layout is the following
*
* +-- #label_<id> (input)-------+ +-----------+
* | | | Browse... |
* +-----------------------------+ +-----------+
*
* And the popup dialog has the following layout:
*
* +------------------- ac_dlg_<id> (div)-----------+
* + +--- ds_<id> (div)---------------------------+ |
* | | +------------- fs_<id> (form)------------+ | |
* | | | +--------+---+ | | |
* | | | | Class | V | | | |
* | | | +--------+---+ | | |
* | | | | | |
* | | | S e a r c h F o r m | | |
* | | | +--------+ | | |
* | | | | Search | | | |
* | | | +--------+ | | |
* | | +----------------------------------------+ | |
* | +--------------+-dh_<id>-+--------------------+ |
* | \ Search / |
* | +------+ |
* | +--- fr_<id> (form)--------------------------+ |
* | | +------------ dr_<id> (div)--------------+ | |
* | | | | | |
* | | | S e a r c h R e s u l t s | | |
* | | | | | |
* | | +----------------------------------------+ | |
* | | +--------+ +-----+ | |
* | | | Cancel | | Add | | |
* | | +--------+ +-----+ | |
* | +--------------------------------------------+ |
* +------------------------------------------------+
* @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/displayblock.class.inc.php');
class UIExtKeyWidget
{
protected $iId;
protected $sTargetClass;
protected $sAttCode;
protected $bSearchMode;
//public function __construct($sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '')
static public function DisplayFromAttCode($oPage, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '', $aArgs, $bSearchMode = false)
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sTargetClass = $oAttDef->GetTargetClass();
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
$bAllowTargetCreation = $oAttDef->AllowTargetCreation();
if (!$bSearchMode)
{
$sDisplayStyle = $oAttDef->GetDisplayStyle();
}
else
{
$sDisplayStyle = 'select'; // In search mode, always use a drop-down list
}
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode);
return $oWidget->Display($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix, $aArgs, null, $sDisplayStyle);
}
public function __construct($sTargetClass, $iInputId, $sAttCode = '', $bSearchMode = false)
{
$this->sTargetClass = $sTargetClass;
$this->iId = $iInputId;
$this->sAttCode = $sAttCode;
$this->bSearchMode = $bSearchMode;
}
/**
* Get the HTML fragment corresponding to the ext key editing widget
* @param WebPage $oP The web page used for all the output
* @param Hash $aArgs Extra context arguments
* @return string The HTML fragment to be inserted into the page
*/
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select', $bSearchMultiple = true)
{
if (!is_null($bSearchMode))
{
$this->bSearchMode = $bSearchMode;
}
$sTitle = addslashes($sTitle);
$oPage->add_linked_script('../js/extkeywidget.js');
$oPage->add_linked_script('../js/forms-json-utils.js');
$bCreate = (!$this->bSearchMode) && (!MetaModel::IsAbstract($this->sTargetClass)) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
$bExtensions = true;
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
$sHTMLValue = "<span style=\"white-space:nowrap\">"; // no wrap
$sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL());
if($this->bSearchMode)
{
$sWizHelper = 'null';
$sWizHelperJSON = "''";
$sJSSearchMode = 'true';
}
else
{
if (isset($aArgs['wizHelper']))
{
$sWizHelper = $aArgs['wizHelper'];
}
else
{
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
}
$sWizHelperJSON = $sWizHelper.'.UpdateWizardToJSON()';
$sJSSearchMode = 'false';
}
if (is_null($oAllowedValues))
{
throw new Exception('Implementation: null value for allowed values definition');
}
elseif ($oAllowedValues->Count() < $iMaxComboLength)
{
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
switch($sDisplayStyle)
{
case 'radio':
case 'radio_horizontal':
case 'radio_vertical':
$sValidationField = "<span id=\"v_{$this->iId}\"></span>";
$sHTMLValue = '';
$bVertical = ($sDisplayStyle != 'radio_horizontal');
$bExtensions = false;
$oAllowedValues->Rewind();
$aAllowedValues = array();
while($oObj = $oAllowedValues->Fetch())
{
$aAllowedValues[$oObj->GetKey()] = $oObj->GetName();
}
$sHTMLValue = $oPage->GetRadioButtons($aAllowedValues, $value, $this->iId, "{$sAttrFieldPrefix}{$sFieldName}", $bMandatory, $bVertical, $sValidationField);
$aEventsList[] ='change';
break;
case 'select':
case 'list':
default:
$sSelectMode = 'true';
$sHelpText = ''; //$this->oAttDef->GetHelpOnEdition();
if ($this->bSearchMode)
{
if ($bSearchMultiple)
{
$sHTMLValue = "<select class=\"multiselect\" multiple title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}[]\" id=\"$this->iId\">\n";
}
else
{
$sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : Dict::S('UI:SearchValue:Any');
$sHTMLValue .= "<option value=\"\">$sDisplayValue</option>\n";
}
}
else
{
$sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
$sHTMLValue .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
}
$oAllowedValues->Rewind();
while($oObj = $oAllowedValues->Fetch())
{
$key = $oObj->GetKey();
$display_value = $oObj->GetName();
if (($oAllowedValues->Count() == 1) && ($bMandatory == 'true') )
{
// When there is only once choice, select it by default
$sSelected = ' selected';
}
else
{
$sSelected = (is_array($value) && in_array($key, $value)) || ($value == $key) ? ' selected' : '';
}
$sHTMLValue .= "<option value=\"$key\"$sSelected>$display_value</option>\n";
}
$sHTMLValue .= "</select>\n";
if (($this->bSearchMode) && $bSearchMultiple)
{
$aOptions = array(
'header' => true,
'checkAllText' => Dict::S('UI:SearchValue:CheckAll'),
'uncheckAllText' => Dict::S('UI:SearchValue:UncheckAll'),
'noneSelectedText' => Dict::S('UI:SearchValue:Any'),
'selectedText' => Dict::S('UI:SearchValue:NbSelected'),
'selectedList' => 1,
);
$sJSOptions = json_encode($aOptions);
$oPage->add_ready_script("$('.multiselect').multiselect($sJSOptions);");
}
$oPage->add_ready_script(
<<<EOF
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
$('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
$('#$this->iId').bind('change', function() { $(this).trigger('extkeychange') } );
EOF
);
} // Switch
}
else
{
// Too many choices, use an autocomplete
$sSelectMode = 'false';
// Check that the given value is allowed
$oSearch = $oAllowedValues->GetFilter();
$oSearch->AddCondition('id', $value);
$oSet = new DBObjectSet($oSearch);
if ($oSet->Count() == 0)
{
$value = null;
}
if (is_null($value) || ($value == 0)) // Null values are displayed as ''
{
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : '';
}
else
{
$sDisplayValue = $this->GetObjectName($value);
}
$iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 3; //@@@ $this->oAttDef->GetMinAutoCompleteChars();
$iFieldSize = isset($aArgs['iFieldSize']) ? $aArgs['iFieldSize'] : 20; //@@@ $this->oAttDef->GetMaxSize();
// the input for the auto-complete
$sHTMLValue = "<input count=\"".$oAllowedValues->Count()."\" type=\"text\" id=\"label_$this->iId\" size=\"$iFieldSize\" value=\"$sDisplayValue\"/>&nbsp;";
$sHTMLValue .= "<img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.Search();\"/>";
// another hidden input to store & pass the object's Id
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
// Scripts to start the autocomplete and bind some events to it
$oPage->add_ready_script(
<<<EOF
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
$('#label_$this->iId').autocomplete(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, mustMatch: true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'$sFilter',bSearchMode:$JSSearchMode, json: function() { return $sWizHelperJSON; } }});
$('#label_$this->iId').keyup(function() { if ($(this).val() == '') { $('#$this->iId').val(''); } } ); // Useful for search forms: empty value in the "label", means no value, immediatly !
$('#label_$this->iId').result( function(event, data, formatted) { OnAutoComplete('{$this->iId}', event, data, formatted); } );
$('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
if ($('#ac_dlg_{$this->iId}').length == 0)
{
$('body').append('<div id="ac_dlg_{$this->iId}"></div>');
}
EOF
);
}
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false)
{
$sHTMLValue .= "<img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/>&nbsp;";
$oPage->add_ready_script(
<<<EOF
if ($('#ac_tree_{$this->iId}').length == 0)
{
$('body').append('<div id="ac_tree_{$this->iId}"></div>');
}
EOF
);
}
if ($bCreate && $bExtensions)
{
$sHTMLValue .= "<img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.CreateObject();\"/>&nbsp;";
$oPage->add_ready_script(
<<<EOF
if ($('#ajax_{$this->iId}').length == 0)
{
$('body').append('<div id="ajax_{$this->iId}"></div>');
}
EOF
);
}
if (($sDisplayStyle == 'select') || ($sDisplayStyle == 'list'))
{
$sHTMLValue .= "<span id=\"v_{$this->iId}\"></span>";
}
$sHTMLValue .= "</span>"; // end of no wrap
return $sHTMLValue;
}
public function GetSearchDialog(WebPage $oPage, $sTitle, $oCurrObject = null)
{
$sHTML = '<div class="wizContainer" style="vertical-align:top;"><div id="dc_'.$this->iId.'">';
if ( ($oCurrObject != null) && ($this->sAttCode != ''))
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oCurrObject), $this->sAttCode);
$aArgs = array('this' => $oCurrObject);
$aParams = array('query_params' => $aArgs);
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aArgs);
$oFilter = $oSet->GetFilter();
}
else
{
$aParams = array();
$oFilter = new DBObjectSearch($this->sTargetClass);
}
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oBlock = new DisplayBlock($oFilter, 'search', false, $aParams);
$sHTML .= $oBlock->GetDisplay($oPage, $this->iId, array('open' => true, 'currentId' => $this->iId));
$sHTML .= "<form id=\"fr_{$this->iId}\" OnSubmit=\"return oACWidget_{$this->iId}.DoOk();\">\n";
$sHTML .= "<div id=\"dr_{$this->iId}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
$sHTML .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
$sHTML .= "</div>\n";
$sHTML .= "<input type=\"button\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#ac_dlg_{$this->iId}').dialog('close');\">&nbsp;&nbsp;";
$sHTML .= "<input type=\"button\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\" onClick=\"oACWidget_{$this->iId}.DoOk();\">";
$sHTML .= "<input type=\"hidden\" id=\"count_{$this->iId}\" value=\"0\">";
$sHTML .= "</form>\n";
$sHTML .= '</div></div>';
$sDialogTitle = addslashes($sTitle);
$oPage->add_ready_script(
<<<EOF
$('#ac_dlg_{$this->iId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.UpdateSizes, close: oACWidget_{$this->iId}.OnClose });
$('#fs_{$this->iId}').bind('submit.uiAutocomplete', oACWidget_{$this->iId}.DoSearchObjects);
$('#dc_{$this->iId}').resize(oACWidget_{$this->iId}.UpdateSizes);
EOF
);
$oPage->add($sHTML);
}
/**
* Search for objects to be selected
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of m_sRemoteClass
* @param Array $aAlreadyLinkedIds List of IDs of objects of "remote" class already linked, to be filtered out of the search
*/
public function SearchObjectsToSelect(WebPage $oP, $sFilter, $sRemoteClass = '', $oObj = null)
{
if (is_null($sFilter))
{
throw new Exception('Implementation: null value for allowed values definition');
}
$oFilter = DBObjectSearch::FromOQL($sFilter);
if (strlen($sRemoteClass) > 0)
{
$oFilter->ChangeClass($sRemoteClass);
}
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oBlock = new DisplayBlock($oFilter, 'list', false, array('query_params' => array('this' => $oObj)));
$oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'table_id' => 'select_'.$this->sAttCode)); // Don't display the 'Actions' menu on the results
}
/**
* Search for objects to be selected
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
* @param string $sFilter The OQL expression used to define/limit limit the scope of possible values
* @param DBObject $oObj The current object for the OQL context
* @param string $sContains The text of the autocomplete to filter the results
*/
public function AutoComplete(WebPage $oP, $sFilter, $oObj = null, $sContains)
{
if (is_null($sFilter))
{
throw new Exception('Implementation: null value for allowed values definition');
}
$oValuesSet = new ValueSetObjects($sFilter, 'friendlyname'); // Bypass GetName() to avoid the encoding by htmlentities
$oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$aValues = $oValuesSet->GetValues(array('this' => $oObj), $sContains);
foreach($aValues as $sKey => $sFriendlyName)
{
$oP->add(trim($sFriendlyName)."\t".$sKey."\n");
}
}
/**
* Get the display name of the selected object, to fill back the autocomplete
*/
public function GetObjectName($iObjId)
{
$aModifierProps = array();
$aModifierProps['UserRightsGetSelectFilter']['bSearchMode'] = $this->bSearchMode;
$oObj = MetaModel::GetObject($this->sTargetClass, $iObjId, false, false, $aModifierProps);
if ($oObj)
{
return $oObj->GetName();
}
else
{
return '';
}
}
/**
* Get the form to create a new object of the 'target' class
*/
public function GetObjectCreationForm(WebPage $oPage, $oCurrObject)
{
// Set all the default values in an object and clone this "default" object
$oNewObj = MetaModel::NewObject($this->sTargetClass);
// 1st - set context values
$oAppContext = new ApplicationContext();
$oAppContext->InitObjectFromContext($oNewObj);
// 2nd set the default values from the constraint on the external key... if any
if ( ($oCurrObject != null) && ($this->sAttCode != ''))
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oCurrObject), $this->sAttCode);
$aParams = array('this' => $oCurrObject);
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aParams);
$aConsts = $oSet->ListConstantFields();
$sClassAlias = $oSet->GetFilter()->GetClassAlias();
if (isset($aConsts[$sClassAlias]))
{
foreach($aConsts[$sClassAlias] as $sAttCode => $value)
{
$oNewObj->Set($sAttCode, $value);
}
}
}
// 3rd - set values from the page argument 'default'
$oNewObj->UpdateObjectFromArg('default');
$sDialogTitle = '';
$oPage->add('<div id="ac_create_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div id="dcr_'.$this->iId.'">');
$oPage->add("<h1>".MetaModel::GetClassIcon($this->sTargetClass)."&nbsp;".Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sTargetClass))."</h1>\n");
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), array('formPrefix' => $this->iId, 'noRelations' => true));
$oPage->add('</div></div></div>');
// $oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: $(window).width()*0.8, height: 'auto', autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
$oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
$oPage->add_ready_script("$('#dcr_{$this->iId} form').removeAttr('onsubmit');");
$oPage->add_ready_script("$('#dcr_{$this->iId} form').bind('submit.uilinksWizard', oACWidget_{$this->iId}.DoCreateObject);");
}
/**
* Display the hierarchy of the 'target' class
*/
public function DisplayHierarchy(WebPage $oPage, $sFilter, $currValue, $oObj)
{
$sDialogTitle = addslashes(Dict::Format('UI:HierarchyOf_Class', MetaModel::GetName($this->sTargetClass)));
$oPage->add('<div id="dlg_tree_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div style="overflow:auto;background:#fff;margin-bottom:5px;" id="tree_'.$this->iId.'">');
$oPage->add('<table style="width:100%"><tr><td>');
if (is_null($sFilter))
{
throw new Exception('Implementation: null value for allowed values definition');
}
try
{
$oFilter = DBObjectSearch::FromOQL($sFilter);
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj));
}
catch(MissingQueryArgument $e)
{
// When used in a search form the $this parameter may be missing, in this case return all possible values...
// TODO check if we can improve this behavior...
$sOQL = 'SELECT '.$this->m_sTargetClass;
$oFilter = DBObjectSearch::FromOQL($sOQL);
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oSet = new DBObjectSet($oFilter);
}
$sHKAttCode = MetaModel::IsHierarchicalClass($this->sTargetClass);
$this->DumpTree($oPage, $oSet, $sHKAttCode, $currValue);
$oPage->add('</td></tr></table>');
$oPage->add('</div>');
$oPage->add("<input type=\"button\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_tree_{$this->iId}').dialog('close');\">&nbsp;&nbsp;");
$oPage->add("<input type=\"button\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\" onClick=\"oACWidget_{$this->iId}.DoHKOk();\">");
$oPage->add('</div></div>');
$oPage->add_ready_script("\$('#tree_$this->iId ul').treeview();\n");
$oPage->add_ready_script("\$('#dlg_tree_$this->iId').dialog({ width: 'auto', height: 'auto', autoOpen: true, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.OnHKResize, close: oACWidget_{$this->iId}.OnHKClose });\n");
}
/**
* Get the form to create a new object of the 'target' class
*/
public function DoCreateObject($oPage)
{
$oObj = MetaModel::NewObject($this->sTargetClass);
$aErrors = $oObj->UpdateObjectFromPostedForm($this->iId);
if (count($aErrors) == 0)
{
$oObj->DBInsert();
return array('name' => $oObj->GetName(), 'id' => $oObj->GetKey());
}
else
{
return array('name' => implode(' ', $aErrors), 'id' => 0);
}
}
function DumpTree($oP, $oSet, $sParentAttCode, $currValue)
{
$aTree = array();
$aNodes = array();
while($oObj = $oSet->Fetch())
{
$iParentId = $oObj->Get($sParentAttCode);
if (!isset($aTree[$iParentId]))
{
$aTree[$iParentId] = array();
}
$aTree[$iParentId][$oObj->GetKey()] = $oObj->GetName();
$aNodes[$oObj->GetKey()] = $oObj;
}
$aParents = array_keys($aTree);
$aRoots = array();
foreach($aParents as $id)
{
if (!array_key_exists($id, $aNodes))
{
$aRoots[] = $id;
}
}
foreach($aRoots as $iRootId)
{
$this->DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue);
}
}
function DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue)
{
$bSelect = true;
$bMultiple = false;
$sSelect = '';
if (array_key_exists($iRootId, $aTree))
{
$aSortedRoots = $aTree[$iRootId];
asort($aSortedRoots);
$oP->add("<ul>\n");
$fUniqueId = microtime(true);
foreach($aSortedRoots as $id => $sName)
{
if ($bSelect)
{
$sChecked = ($aNodes[$id]->GetKey() == $currValue) ? 'checked' : '';
if ($bMultiple)
{
$sSelect = '<input id="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'" type="checkbox" value="'.$aNodes[$id]->GetKey().'" name="selectObject[]" '.$sChecked.'>&nbsp;';
}
else
{
$sSelect = '<input id="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'" type="radio" value="'.$aNodes[$id]->GetKey().'" name="selectObject" '.$sChecked.'>&nbsp;';
}
}
$oP->add('<li>'.$sSelect.'<label for="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'">'.$aNodes[$id]->GetName().'</label>');
$this->DumpNodes($oP, $id, $aTree, $aNodes, $currValue);
$oP->add("</li>\n");
}
$oP->add("</ul>\n");
}
}
}
?>

View File

@@ -0,0 +1,107 @@
<?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/>
/**
* 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
* @license http://opensource.org/licenses/AGPL-3.0
*/
class UIHTMLEditorWidget
{
protected $m_iId;
protected $m_oAttDef;
protected $m_sAttCode;
protected $m_sNameSuffix;
protected $m_sFieldPrefix;
protected $m_sHelpText;
protected $m_sValidationField;
protected $m_sValue;
protected $m_sMandatory;
public function __construct($iInputId, $oAttDef, $sNameSuffix, $sFieldPrefix, $sHelpText, $sValidationField, $sValue, $sMandatory)
{
$this->m_iId = $iInputId;
$this->m_oAttDef = $oAttDef;
$this->m_sAttCode = $oAttDef->GetCode();
$this->m_sNameSuffix = $sNameSuffix;
$this->m_sHelpText = $sHelpText;
$this->m_sValidationField = $sValidationField;
$this->m_sValue = $sValue;
$this->m_sMandatory = $sMandatory;
$this->m_sFieldPrefix = $sFieldPrefix;
}
/**
* Get the HTML fragment corresponding to the HTML editor widget
* @param WebPage $oP The web page used for all the output
* @param Hash $aArgs Extra context arguments
* @return string The HTML fragment to be inserted into the page
*/
public function Display(WebPage $oPage, $aArgs = array())
{
$iId = $this->m_iId;
$sCode = $this->m_sAttCode.$this->m_sNameSuffix;
$sValue = $this->m_sValue;
$sHelpText = $this->m_sHelpText;
$sValidationField = $this->m_sValidationField;
$sHtmlValue = "<table><tr><td><textarea class=\"htmlEditor\" title=\"$sHelpText\" name=\"attr_{$this->m_sFieldPrefix}{$sCode}\" rows=\"10\" cols=\"10\" id=\"$iId\">$sValue</textarea></td><td>$sValidationField</td></tr></table>";
// Replace the text area with CKEditor
// To change the default settings of the editor,
// a) edit the file /js/ckeditor/config.js
// b) or override some of the configuration settings, using the second parameter of ckeditor()
$aConfig = array();
$sLanguage = strtolower(trim(UserRights::GetUserLanguage()));
$aConfig['language'] = $sLanguage;
$aConfig['contentsLanguage'] = $sLanguage;
$aConfig['extraPlugins'] = 'disabler';
$sWidthSpec = addslashes(trim($this->m_oAttDef->GetWidth()));
if ($sWidthSpec != '')
{
$aConfig['width'] = $sWidthSpec;
}
$sHeightSpec = addslashes(trim($this->m_oAttDef->GetHeight()));
if ($sHeightSpec != '')
{
$aConfig['height'] = $sHeightSpec;
}
$sConfigJS = json_encode($aConfig);
$oPage->add_ready_script("$('#$iId').ckeditor(function() { /* callback code */ }, $sConfigJS);"); // Transform $iId into a CKEdit
// Please read...
// ValidateCKEditField triggers a timer... calling itself indefinitely
// This design was the quickest way to achieve the field validation (only checking if the field is blank)
// because the ckeditor does not fire events like "change" or "keyup", etc.
// See http://dev.ckeditor.com/ticket/900 => won't fix
// The most relevant solution would be to implement a plugin to CKEdit, and handle the internal events like: setData, insertHtml, insertElement, loadSnapshot, key, afterUndo, afterRedo
// 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");
return $sHtmlValue;
}
}
?>

View File

@@ -0,0 +1,423 @@
<?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/>
/**
* Class UILinksWidgetDirect
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class UILinksWidgetDirect
{
protected $sClass;
protected $sAttCode;
protected $sInputid;
protected $sNameSuffix;
protected $sLinkedClass;
public function __construct($sClass, $sAttCode, $sInputId, $sNameSuffix = '')
{
$this->sClass = $sClass;
$this->sAttCode = $sAttCode;
$this->sInputid = $sInputId;
$this->sNameSuffix = $sNameSuffix;
$this->aZlist = array();
$this->sLinkedClass = '';
// Compute the list of attributes visible from the given objet:
// All the attributes from the "list" Zlist of the Link class except
// the ExternalKey that points to the current object and its related external fields
$oLinksetDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$this->sLinkedClass = $oLinksetDef->GetLinkedClass();
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
switch($oLinksetDef->GetEditMode())
{
case LINKSET_EDITMODE_INPLACE: // The whole linkset can be edited 'in-place'
$aZList = MetaModel::FlattenZList(MetaModel::GetZListItems($this->sLinkedClass, 'details'));
break;
default:
$aZList = MetaModel::FlattenZList(MetaModel::GetZListItems($this->sLinkedClass, 'list'));
array_unshift($aZList, 'friendlyname');
}
foreach($aZList as $sLinkedAttCode)
{
if ($sLinkedAttCode != $sExtKeyToMe)
{
$oAttDef = MetaModel::GetAttributeDef($this->sLinkedClass, $sLinkedAttCode);
if ((!$oAttDef->IsExternalField() || ($oAttDef->GetKeyAttCode() != $sExtKeyToMe)) &&
(!$oAttDef->IsLinkSet()) )
{
$this->aZlist[] = $sLinkedAttCode;
}
}
}
}
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
{
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
switch($oLinksetDef->GetEditMode())
{
case LINKSET_EDITMODE_NONE: // The linkset is read-only
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
break;
case LINKSET_EDITMODE_ADDONLY: // The only possible action is to open (in a new window) the form to create a new object
if ($oCurrentObj && !$oCurrentObj->IsNew())
{
$sTargetClass = $oLinksetDef->GetLinkedClass();
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
$sDefault = "default[$sExtKeyToMe]=".$oCurrentObj->GetKey();
$oAppContext = new ApplicationContext();
$sParams = $oAppContext->GetForLink();
$oPage->p("<a target=\"_blank\" href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class=$sTargetClass&$sParams&{$sDefault}\">".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sTargetClass))."</a>\n");
}
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
break;
case LINKSET_EDITMODE_INPLACE: // The whole linkset can be edited 'in-place'
$this->DisplayEditInPlace($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj);
break;
case LINKSET_EDITMODE_ADDREMOVE: // The whole linkset can be edited 'in-place'
$sTargetClass = $oLinksetDef->GetLinkedClass();
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
$oExtKeyDef = MetaModel::GetAttributeDef($sTargetClass, $sExtKeyToMe);
$aButtons = array('add');
if ($oExtKeyDef->IsNullAllowed())
{
$aButtons = array('add', 'remove');
}
$this->DisplayEditInPlace($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aButtons);
break;
case LINKSET_EDITMODE_ACTIONS:
default:
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, true /* bDisplayMenu*/);
}
}
protected function DisplayAsBlock(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $bDisplayMenu)
{
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$sTargetClass = $oLinksetDef->GetLinkedClass();
if ($oCurrentObj && $oCurrentObj->IsNew() && $bDisplayMenu)
{
$oPage->p(Dict::Format('UI:BeforeAdding_Class_ObjectsSaveThisObject', MetaModel::GetName($sTargetClass)));
}
else
{
$oFilter = new DBObjectSearch($sTargetClass);
$oFilter->AddCondition($oLinksetDef->GetExtKeyToMe(), $oCurrentObj->GetKey(),'=');
$aDefaults = array($oLinksetDef->GetExtKeyToMe() => $oCurrentObj->GetKey());
$oAppContext = new ApplicationContext();
foreach($oAppContext->GetNames() as $sKey)
{
// The linked object inherits the parent's value for the context
if (MetaModel::IsValidAttCode($this->sClass, $sKey) && $oCurrentObj)
{
$aDefaults[$sKey] = $oCurrentObj->Get($sKey);
}
}
$aParams = array(
'target_attr' => $oLinksetDef->GetExtKeyToMe(),
'object_id' => $oCurrentObj ? $oCurrentObj->GetKey() : null,
'menu' => $bDisplayMenu,
'default' => $aDefaults,
'table_id' => $this->sClass.'_'.$this->sAttCode,
);
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oPage, $this->sInputid, $aParams);
}
}
protected function DisplayEditInPlace(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
{
$aAttribs = $this->GetTableConfig();
$oValue->Rewind();
$oPage->add('<table class="listContainer" id="'.$this->sInputid.'"><tr><td>');
$aData = array();
while($oLinkObj = $oValue->Fetch())
{
$aRow = array();
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.$oLinkObj->GetKey().'"/>';
foreach($this->aZlist as $sLinkedAttCode)
{
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
}
$aData[] = $aRow;
}
$oPage->table($aAttribs, $aData);
$oPage->add('</td></tr></table>'); //listcontainer
$sInputName = $sFormPrefix.'attr_'.$this->sAttCode;
$aLabels = array(
'delete' => Dict::S('UI:Button:Delete'),
// 'modify' => 'Modify...' ,
'creation_title' => Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sLinkedClass)),
'create' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($this->sLinkedClass)),
'remove' => Dict::S('UI:Button:Remove'),
'add' => Dict::Format('UI:AddAnExisting_Class', MetaModel::GetName($this->sLinkedClass)),
'selection_title' => Dict::Format('UI:SelectionOf_Class', MetaModel::GetName($this->sLinkedClass)),
);
$oContext = new ApplicationContext();
$sSubmitUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?'.$oContext->GetForLink();
$sJSONLabels = json_encode($aLabels);
$sJSONButtons = json_encode($aButtons);
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
$oPage->add_ready_script("$('#{$this->sInputid}').directlinks({class_name: '$this->sClass', att_code: '$this->sAttCode', input_name:'$sInputName', labels: $sJSONLabels, submit_to: '$sSubmitUrl', buttons: $sJSONButtons, oWizardHelper: $sWizHelper });");
}
public function GetObjectCreationDlg(WebPage $oPage, $sProposedRealClass = '')
{
// For security reasons: check that the "proposed" class is actually a subclass of the linked class
// and that the current user is allowed to create objects of this class
$sRealClass = '';
$oPage->add('<div class="wizContainer" style="vertical-align:top;"><div>');
$aSubClasses = MetaModel::EnumChildClasses($this->sLinkedClass, ENUM_CHILD_CLASSES_ALL); // Including the specified class itself
$aPossibleClasses = array();
foreach($aSubClasses as $sCandidateClass)
{
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
{
if ($sCandidateClass == $sProposedRealClass)
{
$sRealClass = $sProposedRealClass;
}
$aPossibleClasses[$sCandidateClass] = MetaModel::GetName($sCandidateClass);
}
}
// Only one of the subclasses can be instantiated...
if (count($aPossibleClasses) == 1)
{
$aKeys = array_keys($aPossibleClasses);
$sRealClass = $aKeys[0];
}
if ($sRealClass != '')
{
$oPage->add("<h1>".MetaModel::GetClassIcon($sRealClass)."&nbsp;".Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($sRealClass))."</h1>\n");
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
$aFieldFlags = array( $sExtKeyToMe => OPT_ATT_HIDDEN);
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, null, array(), array('formPrefix' => $this->sInputid, 'noRelations' => true, 'fieldsFlags' => $aFieldFlags));
}
else
{
$sClassLabel = MetaModel::GetName($this->sLinkedClass);
$oPage->add('<p>'.Dict::Format('UI:SelectTheTypeOf_Class_ToCreate', $sClassLabel));
$oPage->add('<nobr><select name="class">');
asort($aPossibleClasses);
foreach($aPossibleClasses as $sClassName => $sClassLabel)
{
$oPage->add("<option value=\"$sClassName\">$sClassLabel</option>");
}
$oPage->add('</select>');
$oPage->add('&nbsp; <button type="button" onclick="$(\'#'.$this->sInputid.'\').directlinks(\'subclassSelected\');">'.Dict::S('UI:Button:Apply').'</button><span class="indicator" style="display:inline-block;width:16px"></span></nobr></p>');
}
$oPage->add('</div></div>');
}
public function GetObjectsSelectionDlg($oPage, $oCurrentObj)
{
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$valuesDef = $oLinksetDef->GetValuesDef();
if ($valuesDef === null)
{
$oFilter = new DBObjectSearch($this->sLinkedClass);
}
else
{
if (!$valuesDef instanceof ValueSetObjects)
{
throw new Exception('Error: only ValueSetObjects are supported for "allowed_values" in AttributeLinkedSet ('.$this->sClass.'/'.$this->sAttCode.').');
}
$oFilter = DBObjectSearch::FromOQL($valuesDef->GetFilterExpression());
}
if ($oCurrentObj != null)
{
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
}
$oBlock = new DisplayBlock($oFilter, 'search', false);
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->sInputid}", array('open' => true));
$sHtml .= "<form id=\"ObjectsAddForm_{$this->sInputid}\">\n";
$sHtml .= "<div id=\"SearchResultsToAdd_{$this->sInputid}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
$sHtml .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
$sHtml .= "</div>\n";
$sHtml .= "<input type=\"hidden\" id=\"count_{$this->sInputid}\" value=\"0\"/>";
$sHtml .= "<button type=\"button\" class=\"cancel\">".Dict::S('UI:Button:Cancel')."</button>&nbsp;&nbsp;<button type=\"button\" class=\"ok\" disabled=\"disabled\">".Dict::S('UI:Button:Add')."</button>";
$sHtml .= "</div>\n";
$sHtml .= "</form>\n";
$oPage->add($sHtml);
//$oPage->add_ready_script("$('#SearchFormToAdd_{$this->sAttCode}{$this->sNameSuffix} form').bind('submit.uilinksWizard', oWidget{$this->sInputId}.SearchObjectsToAdd);");
//$oPage->add_ready_script("$('#SearchFormToAdd_{$this->sAttCode}{$this->sNameSuffix}').resize(oWidget{$this->siInputId}.UpdateSizes);");
}
/**
* Search for objects to be linked to the current object (i.e "remote" objects)
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of $this->sLinkedClass
* @param array $aAlreadyLinked Array of indentifiers of objects which are already linke to the current object (or about to be linked)
* @param DBObject $oCurrentObj The object currently being edited... if known...
*/
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinked = array(), $oCurrentObj = null)
{
if ($sRemoteClass == '')
{
$sRemoteClass = $this->sLinkedClass;
}
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$valuesDef = $oLinksetDef->GetValuesDef();
if ($valuesDef === null)
{
$oFilter = new DBObjectSearch($sRemoteClass);
}
else
{
if (!$valuesDef instanceof ValueSetObjects)
{
throw new Exception('Error: only ValueSetObjects are supported for "allowed_values" in AttributeLinkedSet ('.$this->sClass.'/'.$this->sAttCode.').');
}
$oFilter = DBObjectSearch::FromOQL($valuesDef->GetFilterExpression());
}
if (($oCurrentObj != null) && MetaModel::IsSameFamilyBranch($sRemoteClass, $this->sClass))
{
// Prevent linking to self if the linked object is of the same family
// and laready present in the database
if (!$oCurrentObj->IsNew())
{
$oFilter->AddCondition('id', $oCurrentObj->GetKey(), '!=');
}
}
if (count($aAlreadyLinked) > 0)
{
$oFilter->AddCondition('id', $aAlreadyLinked, 'NOTIN');
}
if ($oCurrentObj != null)
{
$aArgs = array_merge($oCurrentObj->ToArgs('this'), $oFilter->GetInternalParams());
$oFilter->SetInternalParams($aArgs);
}
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, "ResultsToAdd_{$this->sInputid}", array('menu' => false, 'cssCount'=> '#count_'.$this->sInputid , 'selection_mode' => true, 'table_id' => 'add_'.$this->sInputid)); // Don't display the 'Actions' menu on the results
}
public function DoAddObjects(WebPage $oP, $oFullSetFilter)
{
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
foreach($aLinkedObjectIds as $iObjectId)
{
$oLinkObj = MetaModel::GetObject($this->sLinkedClass, $iObjectId);
$oP->add($this->GetObjectRow($oP, $oLinkObj, $oLinkObj->GetKey()));
}
}
public function GetObjectModificationDlg()
{
}
protected function GetTableConfig()
{
$aAttribs = array();
$aAttribs['form::select'] = array('label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->sInputid}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>", 'description' => Dict::S('UI:SelectAllToggle+'));
foreach($this->aZlist as $sLinkedAttCode)
{
$oAttDef = MetaModel::GetAttributeDef($this->sLinkedClass, $sLinkedAttCode);
$aAttribs[$sLinkedAttCode] = array('label' => MetaModel::GetLabel($this->sLinkedClass, $sLinkedAttCode), 'description' => $oAttDef->GetOrderByHint());
}
return $aAttribs;
}
public function GetRow($oPage, $sRealClass, $aValues, $iTempId)
{
if ($sRealClass == '')
{
$sRealClass = $this->sLinkedClass;
}
$oLinkObj = new $sRealClass();
$oLinkObj->UpdateObjectFromPostedForm($this->sInputid);
return $this->GetObjectRow($oPage, $oLinkObj, $iTempId);
}
protected function GetObjectRow($oPage, $oLinkObj, $iTempId)
{
$aAttribs = $this->GetTableConfig();
$aRow = array();
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.($iTempId).'"/>';
foreach($this->aZlist as $sLinkedAttCode)
{
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
}
return $oPage->GetTableRow($aRow, $aAttribs);
}
/**
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
* @param DBObject $oSourceObj
* @param DBSearch $oSearch
*/
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
{
$oAppContext = new ApplicationContext();
$sSrcClass = get_class($oSourceObj);
$sDestClass = $oSearch->GetClass();
foreach($oAppContext->GetNames() as $key)
{
// Find the value of the object corresponding to each 'context' parameter
$aCallSpec = array($sSrcClass, 'MapContextParam');
$sAttCode = '';
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
{
$oAttDef = MetaModel::GetAttributeDef($sSrcClass, $sAttCode);
$defaultValue = $oSourceObj->Get($sAttCode);
// Find the attcode for the same 'context' parameter in the destination class
// and sets its value as the default value for the search condition
$aCallSpec = array($sDestClass, 'MapContextParam');
$sAttCode = '';
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue))
{
$oSearch->AddCondition($sAttCode, $defaultValue);
}
}
}
}
}

View File

@@ -1,30 +1,31 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2015 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class UILinksWidget
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('../application/webpage.class.inc.php');
require_once('../application/displayblock.class.inc.php');
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
class UILinksWidget
{
@@ -34,22 +35,24 @@ class UILinksWidget
protected $m_iInputId;
protected $m_aAttributes;
protected $m_sExtKeyToRemote;
protected $m_sExtKeyToMe;
protected $m_sLinkedClass;
protected $m_sRemoteClass;
protected static $iWidgetIndex = 0;
protected $m_bDuplicatesAllowed;
public function __construct($sClass, $sAttCode, $iInputId, $sNameSuffix = '')
public function __construct($sClass, $sAttCode, $iInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
{
$this->m_sClass = $sClass;
$this->m_sAttCode = $sAttCode;
$this->m_sNameSuffix = $sNameSuffix;
$this->m_iInputId = $iInputId;
$this->m_bDuplicatesAllowed = $bDuplicatesAllowed;
$this->m_aEditableFields = array();
self::$iWidgetIndex++;
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode);
$this->m_sLinkedClass = $oAttDef->GetLinkedClass();
$this->m_sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
$this->m_sExtKeyToMe = $oAttDef->GetExtKeyToMe();
$oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote);
$this->m_sRemoteClass = $oLinkingAttDef->GetTargetClass();
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
@@ -58,15 +61,16 @@ class UILinksWidget
$this->m_aEditableFields = array();
$this->m_aTableConfig = array();
$this->m_aTableConfig['form::checkbox'] = array( 'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onChange=\"var value = this.checked; $('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection').each( function() { this.checked = value; } ); oWidget".self::$iWidgetIndex.".OnSelectChange();\">", 'description' => Dict::S('UI:SelectAllToggle+'));
$this->m_aTableConfig['form::checkbox'] = array( 'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_iInputId.".OnSelectChange();\">", 'description' => Dict::S('UI:SelectAllToggle+'));
foreach(MetaModel::ListAttributeDefs($this->m_sLinkedClass) as $sAttCode=>$oAttDef)
foreach(MetaModel::FlattenZList(MetaModel::GetZListItems($this->m_sLinkedClass, 'list')) as $sAttCode)
{
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sAttCode);
if ($sStateAttCode == $sAttCode)
{
// State attribute is always hidden from the UI
}
else if (!$oAttDef->IsExternalField() && ($sAttCode != $sExtKeyToMe) && ($sAttCode != $this->m_sExtKeyToRemote) && ($sAttCode != 'finalclass'))
else if ($oAttDef->IsWritable() && ($sAttCode != $sExtKeyToMe) && ($sAttCode != $this->m_sExtKeyToRemote) && ($sAttCode != 'finalclass'))
{
$iFlags = MetaModel::GetAttributeFlags($this->m_sLinkedClass, $sDefaultState, $sAttCode);
if ( !($iFlags & OPT_ATT_HIDDEN) && !($iFlags & OPT_ATT_READONLY) )
@@ -97,39 +101,122 @@ class UILinksWidget
* @param Hash $aArgs Extra context arguments
* @return string The HTML fragment of the one-row form
*/
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId = null, $aArgs = array() )
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId = null, $aArgs = array(), $oCurrentObj )
{
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
$aRow = array();
if(is_object($linkObjOrId))
$aFieldsMap = array();
if(is_object($linkObjOrId) && (!$linkObjOrId->IsNew()))
{
$key = $linkObjOrId->GetKey();
$iRemoteObjKey = $linkObjOrId->Get($this->m_sExtKeyToRemote);
$sPrefix .= "[$key][";
$sNameSuffix = "]"; // To make a tabular form
$aArgs['prefix'] = $sPrefix;
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onChange=\"oWidget".self::$iWidgetIndex.".OnSelectChange();\" value=\"$key\">";
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$key}";
$aArgs['this'] = $linkObjOrId;
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$key\">";
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"$key\">";
foreach($this->m_aEditableFields as $sFieldCode)
{
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId->GetKey().']';
$sSafeId = utils::GetSafeId($sFieldId);
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $linkObjOrId->Get($sFieldCode), '' /* DisplayValue */, $key, $sNameSuffix, 0, $aArgs);
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $linkObjOrId->Get($sFieldCode), '' /* DisplayValue */, $sSafeId, $sNameSuffix, 0, $aArgs);
$aFieldsMap[$sFieldCode] = $sSafeId;
}
$sState = $linkObjOrId->GetState();
}
else
{
// form for creating a new record
if (is_object($linkObjOrId))
{
// New link existing only in memory
$oNewLinkObj = $linkObjOrId;
$iRemoteObjKey = $oNewLinkObj->Get($this->m_sExtKeyToRemote);
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, $iRemoteObjKey);
$oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object also fills the related external fields
$linkObjOrId = -$iRemoteObjKey;
}
else
{
$iRemoteObjKey = -$linkObjOrId;
$oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass);
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, -$linkObjOrId);
$oNewLinkObj->Set($this->m_sExtKeyToRemote, $oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields
$oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object also fills the related external fields
}
$sPrefix .= "[$linkObjOrId][";
$sNameSuffix = "]"; // To make a tabular form
$aArgs['prefix'] = $sPrefix;
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onChange=\"oWidget".self::$iWidgetIndex.".OnSelectChange();\" value=\"$linkObjOrId\">";
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".(-$linkObjOrId);
$aArgs['this'] = $oNewLinkObj;
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$linkObjOrId\">";
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"\">";
foreach($this->m_aEditableFields as $sFieldCode)
{
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId.']';
$sSafeId = utils::GetSafeId($sFieldId);
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, '' /* TO DO/ call GetDefaultValue($oObject->ToArgs()) */, '' /* DisplayValue */, '' /* id */, $sNameSuffix, 0, $aArgs);
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $oNewLinkObj->Get($sFieldCode) /* TO DO/ call GetDefaultValue($oObject->ToArgs()) */, '' /* DisplayValue */, $sSafeId /* id */, $sNameSuffix, 0, $aArgs);
$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
});
EOF
);
}
$sExtKeyToMeId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToMe);
$aFieldsMap[$this->m_sExtKeyToMe] = $sExtKeyToMeId;
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToMeId\" value=\"".$oCurrentObj->GetKey()."\">";
$sExtKeyToRemoteId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToRemote);
$aFieldsMap[$this->m_sExtKeyToRemote] = $sExtKeyToRemoteId;
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToRemoteId\" value=\"$iRemoteObjKey\">";
$iFieldsCount = count($aFieldsMap);
$sJsonFieldsMap = json_encode($aFieldsMap);
$oP->add_script(
<<<EOF
var {$aArgs['wizHelper']} = new WizardHelper('{$this->m_sLinkedClass}', '', '$sState');
{$aArgs['wizHelper']}.SetFieldsMap($sJsonFieldsMap);
{$aArgs['wizHelper']}.SetFieldsCount($iFieldsCount);
EOF
);
$aRow['static::key'] = $oLinkedObj->GetHyperLink();
foreach(MetaModel::GetZListItems($this->m_sRemoteClass, 'list') as $sFieldCode)
{
@@ -164,7 +251,7 @@ class UILinksWidget
*/
protected function DisplayFormTable(WebPage $oP, $aConfig, $aData)
{
$sHtml = '';
$sHtml = "<input type=\"hidden\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\">";
$sHtml .= "<table class=\"listResults\">\n";
// Header
$sHtml .= "<thead>\n";
@@ -178,17 +265,17 @@ class UILinksWidget
// Content
$sHtml .= "</tbody>\n";
if (count($aData) == 0)
$sEmptyRowStyle = '';
if (count($aData) != 0)
{
$sHtml .= "<tr id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_empty_row\"><td colspan=\"".count($aConfig)."\" style=\"text-align:center;\">".Dict::S('UI:Message:EmptyList:UseAdd')."<input type=\"hidden\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\"></td></td>";
$sEmptyRowStyle = 'style="display:none;"';
}
else
foreach($aData as $iRowId => $aRow)
{
foreach($aData as $iRowId => $aRow)
{
$sHtml .= $this->DisplayFormRow($oP, $aConfig, $aRow, $iRowId);
}
}
$sHtml .= $this->DisplayFormRow($oP, $aConfig, $aRow, $iRowId);
}
$sHtml .= "<tr $sEmptyRowStyle id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_empty_row\"><td colspan=\"".count($aConfig)."\" style=\"text-align:center;\">".Dict::S('UI:Message:EmptyList:UseAdd')."</td></tr>";
$sHtml .= "</tbody>\n";
// Footer
@@ -203,125 +290,51 @@ class UILinksWidget
* @param WebPage $oP The web page used for all the output
* @param DBObjectSet The initial value of the linked set
* @param Hash $aArgs Extra context arguments
* @param string $sFormPrefix prefix of the fields in the current form
* @param DBObject $oCurrentObj the current object to which the linkset is related
* @return string The HTML fragment to be inserted into the page
*/
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array())
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
{
$iWidgetIndex = self::$iWidgetIndex;
$sHtmlValue = '';
$sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode);
$sHtmlValue .= "<div id=\"linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix}\">\n";
$sHtmlValue .= "<input type=\"hidden\" id=\"{$sFormPrefix}{$this->m_iInputId}\">\n";
$oValue->Rewind();
$aForm = array();
while($oCurrentLink = $oValue->Fetch())
{
$aRow = array();
$key = $oCurrentLink->GetKey();
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote));
if ($oCurrentLink->IsNew())
{
$key = -$oLinkedObj->GetKey();
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj);
}
else
{
$key = $oCurrentLink->GetKey();
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj);
}
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs);
}
$sHtmlValue .= $this->DisplayFormTable($oPage, $this->m_aTableConfig, $aForm);
$sDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
$oPage->add_ready_script(<<<EOF
oWidget$iWidgetIndex = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}');
oWidget$iWidgetIndex.Init();
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates, $sWizHelper, '{$this->m_sExtKeyToRemote}');
oWidget{$this->m_iInputId}.Init();
$('#{$this->m_iInputId}').bind('update_value', function() { $(this).val(oWidget{$this->m_iInputId}.GetUpdatedValue()); })
EOF
);
$sHtmlValue .= "<span style=\"float:left;\">&nbsp;&nbsp;&nbsp;<img src=\"../images/tv-item-last.gif\">&nbsp;&nbsp;<input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"oWidget$iWidgetIndex.RemoveSelected();\" >";
$sHtmlValue .= "&nbsp;&nbsp;&nbsp;<input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sRemoteClass))."\" onClick=\"oWidget$iWidgetIndex.AddObjects();\"></span>\n";
$sHtmlValue .= "<span style=\"float:left;\">&nbsp;&nbsp;&nbsp;<img src=\"../images/tv-item-last.gif\">&nbsp;&nbsp;<input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"oWidget{$this->m_iInputId}.RemoveSelected();\" >";
$sHtmlValue .= "&nbsp;&nbsp;&nbsp;<input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sRemoteClass))."\" onClick=\"oWidget{$this->m_iInputId}.AddObjects();\"><span id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_indicatorAdd\"></span></span>\n";
$sHtmlValue .= "<span style=\"clear:both;\"><p>&nbsp;</p></span>\n";
$sHtmlValue .= "</div>\n";
$oPage->add_at_the_end($this->GetObjectPickerDialog($oPage)); // To prevent adding forms inside the main form
$oPage->add_at_the_end("<div id=\"dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}\"></div>"); // To prevent adding forms inside the main form
return $sHtmlValue;
}
/**
* This static function is called by the Ajax Page when there is a need to fill an autocomplete combo
* @param $oPage WebPage The ajax page used for the output (sent back to the browser)
* @param $sClass string The name of the class of the current object being edited
* @param $sAttCode string The name of the attribute being edited
* @param $sName string The partial name that was typed by the user
* @param $iMaxCount integer The maximum number of items to return
* @return void
*/
static public function Autocomplete(WebPage $oPage, $sClass, $sAttCode, $sName, $iMaxCount)
{
// #@# todo - add context information, otherwise any value will be authorized for external keys
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, array() /* $aArgs */, $sName);
if ($aAllowedValues != null)
{
$iCount = $iMaxCount;
foreach($aAllowedValues as $key => $value)
{
$oPage->add($value."|".$key."\n");
$iCount--;
if ($iCount == 0) break;
}
}
else // No limitation to the allowed values
{
// Search for all the object of the linked class
$oAttDef = $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sLinkedClass = $oAttDef->GetLinkedClass();
$sSearchClass = self::GetTargetClass($sClass, $sAttCode);
$oFilter = new DBObjectSearch($sSearchClass);
$sSearchAttCode = MetaModel::GetNameAttributeCode($sSearchClass);
$oFilter->AddCondition($sSearchAttCode, $sName, 'Begins with');
$oSet = new CMDBObjectSet($oFilter, array($sSearchAttCode => true));
$iCount = 0;
while( ($iCount < $iMaxCount) && ($oObj = $oSet->fetch()) )
{
$oPage->add($oObj->GetName()."|".$oObj->GetKey()."\n");
$iCount++;
}
}
}
/**
* This static function is called by the Ajax Page display a set of objects being linked
* to the object being created
* @param $oPage WebPage The ajax page used for the put^put (sent back to the browser
* @param $sClass string The name of the 'linking class' which is the class of the objects to display
* @param $sSet JSON serialized set of objects
* @param $sExtKeyToMe Name of the attribute in sClass that is pointing to a given object
* @param $iObjectId The id of the object $sExtKeyToMe is pointing to
* @return void
*/
static public function RenderSet($oPage, $sClass, $sJSONSet, $sExtKeyToMe, $sExtKeyToRemote, $iObjectId)
{
$aSet = json_decode($sJSONSet, true); // true means hash array instead of object
$oSet = CMDBObjectSet::FromScratch($sClass);
foreach($aSet as $aObject)
{
$oObj = MetaModel::NewObject($sClass);
foreach($aObject as $sAttCode => $value)
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->IsExternalKey() && ($value != 0))
{
$oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value); // @@ optimization, don't do & query per object in the set !
$oObj->Set($sAttCode, $oTargetObj);
}
else
{
$oObj->Set($sAttCode, $value);
}
}
$oSet->AddObject($oObj);
}
$aExtraParams = array();
$aExtraParams['link_attr'] = $sExtKeyToMe;
$aExtraParams['object_id'] = $iObjectId;
$aExtraParams['target_attr'] = $sExtKeyToRemote;
$aExtraParams['menu'] = false;
$aExtraParams['select'] = false;
$aExtraParams['view_link'] = false;
cmdbAbstractObject::DisplaySet($oPage, $oSet, $aExtraParams);
}
protected static function GetTargetClass($sClass, $sAttCode)
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
@@ -341,28 +354,26 @@ EOF
return $sTargetClass;
}
protected function GetObjectPickerDialog($oPage)
public function GetObjectPickerDialog($oPage, $oCurrentObj)
{
$sHtml = "<div id=\"dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}\">";
$sHtml .= "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
$iWidgetIndex = self::$iWidgetIndex;
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
$oSet = new CMDBObjectSet($oFilter);
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$oBlock = new DisplayBlock($oFilter, 'search', false);
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}", array('open' => true));
$sHtml .= "<form id=\"ObjectsAddForm_{$this->m_sAttCode}{$this->m_sNameSuffix}\" OnSubmit=\"return oWidget$iWidgetIndex.DoAddObjects(this.id);\">\n";
$sHtml .= "<form id=\"ObjectsAddForm_{$this->m_sAttCode}{$this->m_sNameSuffix}\" OnSubmit=\"return oWidget{$this->m_iInputId}.DoAddObjects(this.id);\">\n";
$sHtml .= "<div id=\"SearchResultsToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
$sHtml .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
$sHtml .= "</div>\n";
$sHtml .= "<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('close');\">&nbsp;&nbsp;<input type=\"submit\" value=\"".Dict::S('UI:Button:Add')."\">";
$sHtml .= "<input type=\"hidden\" id=\"count_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"0\"/>";
$sHtml .= "<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('close');\">&nbsp;&nbsp;<input id=\"btn_ok_{$this->m_sAttCode}{$this->m_sNameSuffix}\" disabled=\"disabled\" type=\"submit\" value=\"".Dict::S('UI:Button:Add')."\">";
$sHtml .= "</div>\n";
$sHtml .= "</form>\n";
$sHtml .= "</div>\n";
$oPage->add_ready_script("$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, resizeStop: oWidget$iWidgetIndex.UpdateSizes });");
$oPage->add($sHtml);
$oPage->add_ready_script("$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, resizeStop: oWidget{$this->m_iInputId}.UpdateSizes });");
$oPage->add_ready_script("$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('option', {title:'".addslashes(Dict::Format('UI:AddObjectsOf_Class_LinkedWith_Class', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName($this->m_sClass)))."'});");
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix} form').bind('submit.uilinksWizard', oWidget$iWidgetIndex.SearchObjectsToAdd);");
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}').resize(oWidget$iWidgetIndex.UpdateSizes);");
return $sHtml;
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix} form').bind('submit.uilinksWizard', oWidget{$this->m_iInputId}.SearchObjectsToAdd);");
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}').resize(oWidget{$this->m_iInputId}.UpdateSizes);");
}
/**
@@ -383,7 +394,7 @@ EOF
// No remote class specified use the one defined in the linkedset
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
}
if (count($aAlreadyLinkedIds) > 0)
if (!$this->m_bDuplicatesAllowed && count($aAlreadyLinkedIds) > 0)
{
// Positive IDs correspond to existing link records
// negative IDs correspond to "remote" objects to be linked
@@ -416,18 +427,19 @@ EOF
}
$oSet = new CMDBObjectSet($oFilter);
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true, 'display_limit' => false)); // Don't display the 'Actions' menu on the results
$oBlock->Display($oP, "ResultsToAdd_{$this->m_sAttCode}", array('menu' => false, 'cssCount'=> '#count_'.$this->m_sAttCode.$this->m_sNameSuffix , 'selection_mode' => true, 'table_id' => 'add_'.$this->m_sAttCode)); // Don't display the 'Actions' menu on the results
}
public function DoAddObjects(WebPage $oP, $aLinkedObjectIds = array())
public function DoAddObjects(WebPage $oP, $oFullSetFilter, $oCurrentObj)
{
$aTable = array();
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
foreach($aLinkedObjectIds as $iObjectId)
{
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId);
if (is_object($oLinkedObj))
{
$aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId ); // Not yet created link get negative Ids
$aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId, array(), $oCurrentObj ); // Not yet created link get negative Ids
$oP->add($this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iObjectId));
}
else
@@ -436,5 +448,47 @@ EOF
}
}
}
/**
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
* @param DBObject $oSourceObj
* @param DBSearch $oSearch
*/
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
{
$oAppContext = new ApplicationContext();
$sSrcClass = get_class($oSourceObj);
$sDestClass = $oSearch->GetClass();
foreach($oAppContext->GetNames() as $key)
{
// Find the value of the object corresponding to each 'context' parameter
$aCallSpec = array($sSrcClass, 'MapContextParam');
$sAttCode = '';
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
{
$oAttDef = MetaModel::GetAttributeDef($sSrcClass, $sAttCode);
$defaultValue = $oSourceObj->Get($sAttCode);
// Find the attcode for the same 'context' parameter in the destination class
// and sets its value as the default value for the search condition
$aCallSpec = array($sDestClass, 'MapContextParam');
$sAttCode = '';
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue))
{
$oSearch->AddCondition($sAttCode, $defaultValue);
}
}
}
}
}
?>

View File

@@ -1,31 +1,31 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class UIPasswordWidget
* UI wdiget for displaying and editing one-way encrypted passwords
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('../application/webpage.class.inc.php');
require_once('../application/displayblock.class.inc.php');
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
class UIPasswordWidget
{
@@ -52,17 +52,36 @@ class UIPasswordWidget
{
$sCode = $this->sAttCode.$this->sNameSuffix;
$iWidgetIndex = self::$iWidgetIndex;
$sHtmlValue = '';
$sHtmlValue = '<input type="password" maxlength="255" name="attr_'.$sCode.'" id="'.$this->iId.'" value="*****"/>&nbsp;<span id="v_'.$this->iId.'"></span><br/>';
$sHtmlValue .= '<input type="password" maxlength="255" id="'.$this->iId.'_confirm" value="*****"/> '.Dict::S('UI:PasswordConfirm').' <input type="button" value="'.Dict::S('UI:Button:ResetPassword').'" onClick="ResetPwd(\''.$this->iId.'\');">';
$sHtmlValue .= '<input type="hidden" id="'.$this->iId.'_changed" name="attr_'.$sCode.'_changed" value="0"/>';
$oPage->add_ready_script("$('#$this->iId').bind('keyup change', function(evt) { return PasswordFieldChanged('$this->iId') } );"); // Bind to a custom event: validate
$aPasswordValues = utils::ReadPostedParam("attr_{$sCode}", null, 'raw_data');
$sPasswordValue = $aPasswordValues ? $aPasswordValues['value'] : '*****';
$sConfirmPasswordValue = $aPasswordValues ? $aPasswordValues['confirm'] : '*****';
$sChangedValue = (($sPasswordValue != '*****') || ($sConfirmPasswordValue != '*****')) ? 1 : 0;
$sHtmlValue = '';
$sHtmlValue = '<input type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/>&nbsp;<span class="form_validation" id="v_'.$this->iId.'"></span><br/>';
$sHtmlValue .= '<input type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/> '.Dict::S('UI:PasswordConfirm').' <input id="'.$this->iId.'_reset" type="button" value="'.Dict::S('UI:Button:ResetPassword').'" onClick="ResetPwd(\''.$this->iId.'\');">';
$sHtmlValue .= '<input type="hidden" id="'.$this->iId.'_changed" name="attr_'.$sCode.'[changed]" value="'.$sChangedValue.'"/>';
$oPage->add_ready_script("$('#$this->iId').bind('keyup change', function(evt) { return PasswordFieldChanged('$this->iId') } );"); // Bind to a custom event: validate
$oPage->add_ready_script("$('#$this->iId').bind('keyup change validate', function(evt, sFormId) { return ValidatePasswordField('$this->iId', sFormId) } );"); // Bind to a custom event: validate
$oPage->add_ready_script("$('#{$this->iId}_confirm').bind('keyup change', function(evt, sFormId) { return ValidatePasswordField('$this->iId', sFormId) } );"); // Bind to a custom event: validate
$oPage->add_ready_script("$('#{$this->iId}').bind('update', function(evt, sFormId)
{
if ($(this).attr('disabled'))
{
$('#{$this->iId}_confirm').attr('disabled', 'disabled');
$('#{$this->iId}_changed').attr('disabled', 'disabled');
$('#{$this->iId}_reset').attr('disabled', 'disabled');
}
else
{
$('#{$this->iId}_confirm').removeAttr('disabled');
$('#{$this->iId}_changed').removeAttr('disabled');
$('#{$this->iId}_reset').removeAttr('disabled');
}
}
);"); // Bind to a custom event: update to handle enabling/disabling
return $sHtmlValue;
}
}
?>
?>

View File

@@ -1,26 +1,27 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class UILinksWizard
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class UILinksWizard
@@ -85,7 +86,7 @@ class UILinksWizard
$sTargetClass = $oAttDef->GetTargetClass();
$oTargetObj = MetaModel::GetObject($sTargetClass, $this->m_iObjectId);
$oP->set_title("iTop - ".MetaModel::GetName($this->m_sLinkedClass)." objects linked with ".MetaModel::GetName(get_class($oTargetObj)).": ".$oTargetObj->GetName());
$oP->set_title("iTop - ".MetaModel::GetName($this->m_sLinkedClass)." objects linked with ".MetaModel::GetName(get_class($oTargetObj)).": ".$oTargetObj->GetRawName());
$oP->add("<div class=\"wizContainer\">\n");
$oP->add("<form method=\"post\">\n");
$oP->add("<div class=\"page_header\">\n");
@@ -97,15 +98,14 @@ class UILinksWizard
$oP->add("<input type=\"hidden\" name=\"linking_attcode\" value=\"{$this->m_sLinkingAttCode}\">\n");
$oP->add("<h1>".Dict::Format('UI:ManageObjectsOf_Class_LinkedWith_Class_Instance', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName(get_class($oTargetObj)), "<span class=\"hilite\">".$oTargetObj->GetHyperlink()."</span>")."</h1>\n");
$oP->add("</div>\n");
$oP->add("<script type=\"text/javascript\">\n");
$oP->add(
$oP->add_script(
<<<EOF
function OnSelectChange()
{
var nbChecked = $('.selection:checked').length;
if (nbChecked > 0)
{
$('#btnRemove').attr('disabled','');
$('#btnRemove').removeAttr('disabled');
}
else
{
@@ -132,7 +132,7 @@ class UILinksWizard
function AddObjects()
{
// TO DO: compute the list of objects already linked with the current Object
$.post( 'ajax.render.php', { 'operation': 'addObjects',
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { 'operation': 'addObjects',
'class': '{$this->m_sClass}',
'linkageAttr': '{$this->m_sLinkageAttr}',
'linkedClass': '{$this->m_sLinkedClass}',
@@ -175,7 +175,7 @@ class UILinksWizard
theMap['operation'] = 'searchObjectsToAdd';
// Run the query and display the results
$.post( 'ajax.render.php', theMap,
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,
function(data)
{
$('#SearchResultsToAdd').html(data);
@@ -223,7 +223,7 @@ class UILinksWizard
theMap['operation'] = 'doAddObjects';
// Run the query and display the results
$.post( 'ajax.render.php', theMap,
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,
function(data)
{
//console.log('Data: ' + data);
@@ -257,7 +257,6 @@ class UILinksWizard
}
EOF
);
$oP->Add("</script>\n");
$oP->add_ready_script("InitForm();");
$oFilter = new DBObjectSearch($this->m_sClass);
$oFilter->AddCondition($this->m_sLinkageAttr, $this->m_iObjectId, '=');
@@ -276,7 +275,7 @@ EOF
$this->DisplayFormTable($oP, $this->m_aTableConfig, $aForm);
$oP->add("<span style=\"float:left;\">&nbsp;&nbsp;&nbsp;<img src=\"../images/tv-item-last.gif\">&nbsp;&nbsp;<input id=\"btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"RemoveSelected();\" >");
$oP->add("&nbsp;&nbsp;&nbsp;<input id=\"btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sLinkedClass))."\" onClick=\"AddObjects();\"></span>\n");
$oP->add("<span style=\"float:right;\"><input id=\"btnCancel\" type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"BackToDetails('".$sTargetClass."', ".$this->m_iObjectId.");\">");
$oP->add("<span style=\"float:right;\"><input id=\"btnCancel\" type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"BackToDetails('".$sTargetClass."', ".$this->m_iObjectId.", '', '');\">");
$oP->add("&nbsp;&nbsp;&nbsp;<input id=\"btnOk\" type=\"submit\" value=\"".Dict::S('UI:Button:Ok')."\"></span>\n");
$oP->add("<span style=\"clear:both;\"><p>&nbsp;</p></span>\n");
$oP->add("</div>\n");
@@ -395,7 +394,7 @@ EOF
$oFilter = new DBObjectSearch($this->m_sLinkedClass);
$oSet = new CMDBObjectSet($oFilter);
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true, 'display_limit' => false)); // Don't display the 'Actions' menu on the results
$oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true)); // Don't display the 'Actions' menu on the results
}
public function DoAddObjects(WebPage $oP, $aLinkedObjectIds = array())

View File

@@ -1,26 +1,27 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class UIWizard
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class UIWizard
@@ -53,7 +54,7 @@ class UIWizard
{
if ($iStepIndex == 1) // one big form that contains everything, to make sure that the uploaded files are posted too
{
$this->m_oPage->add("<form method=\"post\" enctype=\"multipart/form-data\" action=\"../pages/UI.php\">\n");
$this->m_oPage->add("<form method=\"post\" enctype=\"multipart/form-data\" action=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php\">\n");
}
$this->m_oPage->add("<div class=\"wizContainer\" id=\"wizStep$iStepIndex\" style=\"display:none;\">\n");
$this->m_oPage->add("<a name=\"step$iStepIndex\" />\n");
@@ -112,8 +113,7 @@ class UIWizard
<input type=\"button\" value=\"".Dict::S('UI:Button:Next')."\" onClick=\"GoToStep($iStepIndex, 1+$iStepIndex)\" />
<input type=\"button\" value=\"".Dict::S('UI:Button:Finish')."\" $sDisabled onClick=\"GoToStep($iStepIndex, 1+$nbSteps)\" />
</div>\n");
$this->m_oPage->add("
<script type=\"text/javascript\">
$this->m_oPage->add_script("
function OnEnterStep{$iStepIndex}()
{
oWizardHelper.ResetQuery();
@@ -123,7 +123,7 @@ $sJSHandlerCode
oWizardHelper.AjaxQueryServer();
}
</script>\n");
");
$this->m_oPage->add("</div>\n\n");
}
@@ -139,16 +139,15 @@ $sJSHandlerCode
$this->m_oPage->add("<input type=\"hidden\" name=\"operation\" value=\"wizard_apply_new\" />\n");
$this->m_oPage->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\" />\n");
$this->m_oPage->add("<input type=\"hidden\" id=\"wizard_json_obj\" name=\"json_obj\" value=\"\" />\n");
$this->m_oPage->add("<script type=\"text/javascript\">\n");
$this->m_oPage->add("function OnEnterStep$iStepIndex() {\n");
$sScript = "function OnEnterStep$iStepIndex() {\n";
foreach($aFieldsMap as $iInputId => $sAttCode)
{
$this->m_oPage->add("\toWizardHelper.UpdateCurrentValue('$sAttCode');\n");
$sScript .= "\toWizardHelper.UpdateCurrentValue('$sAttCode');\n";
}
$this->m_oPage->add("\toWizardHelper.Preview('object_preview');\n");
$this->m_oPage->add("\t$('#wizard_json_obj').val(oWizardHelper.ToJSON());\n");
$this->m_oPage->add("}\n");
$this->m_oPage->add("</script>\n");
$sScript .= "\toWizardHelper.Preview('object_preview');\n";
$sScript .= "\t$('#wizard_json_obj').val(oWizardHelper.ToJSON());\n";
$sScript .= "}\n";
$this->m_oPage->add_script($sScript);
$this->m_oPage->add("<div id=\"object_preview\">\n");
$this->m_oPage->add("</div>\n");
$this->m_oPage->add($oAppContext->GetForForm());
@@ -227,7 +226,7 @@ $sJSHandlerCode
// Now use the dependencies between the fields to order them
// Start from the order of the 'details'
$aList = MetaModel::GetZListItems($this->m_sClass, 'details');
$aList = MetaModel::FlattenZlist(MetaModel::GetZListItems($this->m_sClass, 'details'));
$index = 0;
$aOrder = array();
foreach($aFields as $sAttCode => $void)

View File

@@ -0,0 +1,61 @@
<?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/>
/**
* Store and retrieve user custom dashboards
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'/core/dbobject.class.php');
/**
* This class is used to store, in a persistent manner, a dashboard edited by a user
*/
class UserDashboard extends DBObject
{
public static function Init()
{
$aParams = array
(
"category" => "gui",
"key_type" => "autoincrement",
"name_attcode" => "user_id",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_app_dashboards",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("menu_code", array("allowed_values"=>null, "sql"=>"menu_code", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("contents", array("allowed_values"=>null, "sql"=>"contents", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
}
/**
* Overloading this function here to secure a fix done right before the release
* The real fix should be to implement this verb in DBObject
*/
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
{
$this->DBDelete($oDeletionPlan);
}
}
?>

View File

@@ -1,29 +1,29 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Store and retrieve user's preferences (i.e persistent per user settings)
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('../core/dbobject.class.php');
require_once('../core/userrights.class.inc.php');
require_once(APPROOT.'/core/dbobject.class.php');
require_once(APPROOT.'/core/userrights.class.inc.php');
/**
* This class is used to store, in a persistent manner, user related settings (preferences)
@@ -77,6 +77,42 @@ class appUserPreferences extends DBObject
self::Save();
}
/**
* Clears the value for a given preference (or list of preferences that matches a pattern), and updates the database
* @param string $sPattern Code/Pattern of the properties/preferences to reset
* @param boolean $bPattern Whether or not the supplied code is a PCRE pattern
*/
static function UnsetPref($sCodeOrPattern, $bPattern = false)
{
if (self::$oUserPrefs == null)
{
self::Load();
}
$aPrefs = self::$oUserPrefs->Get('preferences');
if ($bPattern)
{
// the supplied code is a pattern, clear all preferences that match
foreach($aPrefs as $sKey => $void)
{
if (preg_match($sCodeOrPattern, $sKey))
{
unset($aPrefs[$sKey]);
}
}
self::$oUserPrefs->Set('preferences', $aPrefs);
}
else
{
unset($aPrefs[$sCodeOrPattern]);
self::$oUserPrefs->Set('preferences', $aPrefs);
}
// Save only if needed
if (self::$oUserPrefs->IsModified())
{
self::Save();
}
}
/**
* Call this function to get all the preferences for the user, packed as a JSON object
* @return string JSON representation of the preferences
@@ -94,7 +130,7 @@ class appUserPreferences extends DBObject
/**
* Call this function if the user has changed (like when doing a logoff...)
*/
static public function Reset()
static public function ResetPreferences()
{
self::$oUserPrefs = null;
}
@@ -169,9 +205,9 @@ class appUserPreferences extends DBObject
* Overloading this function here to secure a fix done right before the release
* The real fix should be to implement this verb in DBObject
*/
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
{
$this->DBDelete();
$this->DBDelete($oDeletionPlan);
}
}
?>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,30 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class WizardHelper
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('../application/uiwizard.class.inc.php');
require_once(APPROOT.'/application/uiwizard.class.inc.php');
class WizardHelper
{
@@ -50,8 +51,8 @@ class WizardHelper
foreach($this->m_aData['m_oCurrentValues'] as $sAttCode => $value)
{
// Because this is stored in a Javascript array, unused indexes
// are filled with null values
if ( ($sAttCode !='id') && ($sAttCode !== false) && ($value !== null))
// are filled with null values and unused keys (stored as strings) contain $$NULL$$
if ( ($sAttCode !='id') && ($sAttCode !== false) && ($value !== null) && ($value !== '$$NULL$$'))
{
$oAttDef = MetaModel::GetAttributeDef($this->m_aData['m_sClass'], $sAttCode);
if (($oAttDef->IsLinkSet()) && ($value != '') )
@@ -76,7 +77,7 @@ class WizardHelper
if ( isset($aLinkedObject[$sLinkedAttCode]) && ($aLinkedObject[$sLinkedAttCode] !== null) )
{
$sLinkedAttDef = MetaModel::GetAttributeDef($sLinkedClass, $sLinkedAttCode);
if (($sLinkedAttDef->IsExternalKey()) && ($aLinkedObject[$sLinkedAttCode] != '') && ($aLinkedObject[$sLinkedAttCode] != 0) )
if (($sLinkedAttDef->IsExternalKey()) && ($aLinkedObject[$sLinkedAttCode] != '') && ($aLinkedObject[$sLinkedAttCode] > 0) )
{
// For external keys: load the target object so that external fields
// get filled too
@@ -98,7 +99,7 @@ class WizardHelper
{
if ($bReadUploadedFiles)
{
$oDocument = utils::ReadPostedDocument('file_'.$sAttCode);
$oDocument = utils::ReadPostedDocument('attr_'.$sAttCode, 'fcontents');
$oObj->Set($sAttCode, $oDocument);
}
else
@@ -108,12 +109,20 @@ class WizardHelper
$oObj->Set($sAttCode, $oDocument);
}
}
else if (($oAttDef->IsExternalKey()) && (!empty($value)) )
else if (($oAttDef->IsExternalKey()) && (!empty($value)) && ($value > 0) )
{
// For external keys: load the target object so that external fields
// get filled too
$oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value);
$oObj->Set($sAttCode, $oTargetObj);
$oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value, false);
if ($oTargetObj)
{
$oObj->Set($sAttCode, $oTargetObj);
}
else
{
// May happen for security reasons (portal, see ticket #1074)
$oObj->Set($sAttCode, $value);
}
}
else
{
@@ -121,6 +130,11 @@ class WizardHelper
}
}
}
if (isset($this->m_aData['m_sState']) && !empty($this->m_aData['m_sState']))
{
$oObj->Set(MetaModel::GetStateAttributeCode($this->m_aData['m_sClass']), $this->m_aData['m_sState']);
}
$oObj->DoComputeValues();
return $oObj;
}
@@ -227,6 +241,11 @@ class WizardHelper
return $this->m_aData['m_sClass'];
}
public function GetFormPrefix()
{
return $this->m_aData['m_sFormPrefix'];
}
public function GetIdForField($sFieldName)
{
$sResult = '';
@@ -250,7 +269,7 @@ class WizardHelper
foreach($aLinkObj as $sAttCode => $value)
{
$oAttDef = MetaModel::GetAttributeDef($sLinkClass, $sAttCode);
if (($oAttDef->IsExternalKey()) && ($value != '') )
if (($oAttDef->IsExternalKey()) && ($value != '') && ($value > 0))
{
// For external keys: load the target object so that external fields
// get filled too

View File

@@ -0,0 +1,459 @@
<?php
/* @author Mark Jones
* @license MIT License
* */
if (!class_exists('ZipArchive')) { throw new Exception('ZipArchive not found'); }
Class XLSXWriter
{
//------------------------------------------------------------------
protected $author ='Doc Author';
protected $sheets_meta = array();
protected $shared_strings = array();//unique set
protected $shared_string_count = 0;//count of non-unique references to the unique set
protected $temp_files = array();
public function __construct(){}
public function setAuthor($author='') { $this->author=$author; }
public function __destruct()
{
if (!empty($this->temp_files)) {
foreach($this->temp_files as $temp_file) {
@unlink($temp_file);
}
}
}
protected function tempFilename()
{
$filename = tempnam("/tmp", "xlsx_writer_");
$this->temp_files[] = $filename;
return $filename;
}
public function writeToStdOut()
{
$temp_file = $this->tempFilename();
self::writeToFile($temp_file);
readfile($temp_file);
}
public function writeToString()
{
$temp_file = $this->tempFilename();
self::writeToFile($temp_file);
$string = file_get_contents($temp_file);
return $string;
}
public function writeToFile($filename)
{
@unlink($filename);//if the zip already exists, overwrite it
$zip = new ZipArchive();
if (empty($this->sheets_meta)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", no worksheets defined."); return; }
if (!$zip->open($filename, ZipArchive::CREATE)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", unable to create zip."); return; }
$zip->addEmptyDir("docProps/");
$zip->addFromString("docProps/app.xml" , self::buildAppXML() );
$zip->addFromString("docProps/core.xml", self::buildCoreXML());
$zip->addEmptyDir("_rels/");
$zip->addFromString("_rels/.rels", self::buildRelationshipsXML());
$zip->addEmptyDir("xl/worksheets/");
foreach($this->sheets_meta as $sheet_meta) {
$zip->addFile($sheet_meta['filename'], "xl/worksheets/".$sheet_meta['xmlname'] );
}
if (!empty($this->shared_strings)) {
$zip->addFile($this->writeSharedStringsXML(), "xl/sharedStrings.xml" ); //$zip->addFromString("xl/sharedStrings.xml", self::buildSharedStringsXML() );
}
$zip->addFromString("xl/workbook.xml" , self::buildWorkbookXML() );
$zip->addFile($this->writeStylesXML(), "xl/styles.xml" ); //$zip->addFromString("xl/styles.xml" , self::buildStylesXML() );
$zip->addFromString("[Content_Types].xml" , self::buildContentTypesXML() );
$zip->addEmptyDir("xl/_rels/");
$zip->addFromString("xl/_rels/workbook.xml.rels", self::buildWorkbookRelsXML() );
$zip->close();
}
public function writeSheet(array $data, $sheet_name='', array $header_types=array(), array $header_row=array() )
{
$data = empty($data) ? array( array('') ) : $data;
$sheet_filename = $this->tempFilename();
$sheet_default = 'Sheet'.(count($this->sheets_meta)+1);
$sheet_name = !empty($sheet_name) ? $sheet_name : $sheet_default;
$this->sheets_meta[] = array('filename'=>$sheet_filename, 'sheetname'=>$sheet_name ,'xmlname'=>strtolower($sheet_default).".xml" );
$header_offset = empty($header_types) ? 0 : 1;
$row_count = count($data) + $header_offset;
$column_count = count($data[self::array_first_key($data)]);
$max_cell = self::xlsCell( $row_count-1, $column_count-1 );
$tabselected = count($this->sheets_meta)==1 ? 'true' : 'false';//only first sheet is selected
$cell_formats_arr = empty($header_types) ? array_fill(0, $column_count, 'string') : array_values($header_types);
if (empty($header_row) && !empty($header_types))
{
$header_row = empty($header_types) ? array() : array_keys($header_types);
}
$fd = fopen($sheet_filename, "w+");
if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; }
fwrite($fd,'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
fwrite($fd,'<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">');
fwrite($fd, '<sheetPr filterMode="false">');
fwrite($fd, '<pageSetUpPr fitToPage="false"/>');
fwrite($fd, '</sheetPr>');
fwrite($fd, '<dimension ref="A1:'.$max_cell.'"/>');
fwrite($fd, '<sheetViews>');
fwrite($fd, '<sheetView colorId="64" defaultGridColor="true" rightToLeft="false" showFormulas="false" showGridLines="true" showOutlineSymbols="true" showRowColHeaders="true" showZeros="true" tabSelected="'.$tabselected.'" topLeftCell="A1" view="normal" windowProtection="false" workbookViewId="0" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100">');
fwrite($fd, '<selection activeCell="A1" activeCellId="0" pane="topLeft" sqref="A1"/>');
fwrite($fd, '</sheetView>');
fwrite($fd, '</sheetViews>');
fwrite($fd, '<cols>');
fwrite($fd, '<col collapsed="false" hidden="false" max="1025" min="1" style="0" width="19"/>');
fwrite($fd, '</cols>');
fwrite($fd, '<sheetData>');
if (!empty($header_row))
{
fwrite($fd, '<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'.(1).'">');
foreach($header_row as $k=>$v)
{
$this->writeCell($fd, 0, $k, $v, $cell_format='string');
}
fwrite($fd, '</row>');
}
foreach($data as $i=>$row)
{
fwrite($fd, '<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'.($i+$header_offset+1).'">');
foreach($row as $k=>$v)
{
$this->writeCell($fd, $i+$header_offset, $k, $v, $cell_formats_arr[$k]);
}
fwrite($fd, '</row>');
}
fwrite($fd, '</sheetData>');
fwrite($fd, '<printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"/>');
fwrite($fd, '<pageMargins left="0.5" right="0.5" top="1.0" bottom="1.0" header="0.5" footer="0.5"/>');
fwrite($fd, '<pageSetup blackAndWhite="false" cellComments="none" copies="1" draft="false" firstPageNumber="1" fitToHeight="1" fitToWidth="1" horizontalDpi="300" orientation="portrait" pageOrder="downThenOver" paperSize="1" scale="100" useFirstPageNumber="true" usePrinterDefaults="false" verticalDpi="300"/>');
fwrite($fd, '<headerFooter differentFirst="false" differentOddEven="false">');
fwrite($fd, '<oddHeader>&amp;C&amp;&quot;Times New Roman,Regular&quot;&amp;12&amp;A</oddHeader>');
fwrite($fd, '<oddFooter>&amp;C&amp;&quot;Times New Roman,Regular&quot;&amp;12Page &amp;P</oddFooter>');
fwrite($fd, '</headerFooter>');
fwrite($fd,'</worksheet>');
fclose($fd);
}
protected function writeCell($fd, $row_number, $column_number, $value, $cell_format)
{
static $styles = array('money'=>1,'dollar'=>1,'datetime'=>2,'date'=>3,'string'=>0);
$cell = self::xlsCell($row_number, $column_number);
$s = isset($styles[$cell_format]) ? $styles[$cell_format] : '0';
if (is_int($value) || is_float($value)) {
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.($value*1).'</v></c>');//int,float, etc
} else if ($cell_format=='date') {
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.intval(self::convert_date_time($value)).'</v></c>');
} else if ($cell_format=='datetime') {
if ($value === '') {
fwrite($fd,'<c r="'.$cell.'" s="0"/>');
} else {
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.self::convert_date_time($value).'</v></c>');
}
} else if ($value==''){
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'"/>');
} else if ($value{0}=='='){
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="s"><f>'.self::xmlspecialchars($value).'</f></c>');
} else if ($value!==''){
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="s"><v>'.self::xmlspecialchars($this->setSharedString($value)).'</v></c>');
}
}
protected function writeStylesXML()
{
$tempfile = $this->tempFilename();
$fd = fopen($tempfile, "w+");
if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; }
fwrite($fd, '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
fwrite($fd, '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">');
fwrite($fd, '<numFmts count="4">');
fwrite($fd, '<numFmt formatCode="GENERAL" numFmtId="164"/>');
fwrite($fd, '<numFmt formatCode="[$$-1009]#,##0.00;[RED]\-[$$-1009]#,##0.00" numFmtId="165"/>');
fwrite($fd, '<numFmt formatCode="YYYY-MM-DD\ HH:MM:SS" numFmtId="166"/>');
fwrite($fd, '<numFmt formatCode="YYYY-MM-DD" numFmtId="167"/>');
fwrite($fd, '</numFmts>');
fwrite($fd, '<fonts count="4">');
fwrite($fd, '<font><name val="Arial"/><charset val="1"/><family val="2"/><sz val="10"/></font>');
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
fwrite($fd, '</fonts>');
fwrite($fd, '<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>');
fwrite($fd, '<borders count="1"><border diagonalDown="false" diagonalUp="false"><left/><right/><top/><bottom/><diagonal/></border></borders>');
fwrite($fd, '<cellStyleXfs count="15">');
fwrite($fd, '<xf applyAlignment="true" applyBorder="true" applyFont="true" applyProtection="true" borderId="0" fillId="0" fontId="0" numFmtId="164">');
fwrite($fd, '<alignment horizontal="general" indent="0" shrinkToFit="false" textRotation="0" vertical="bottom" wrapText="false"/>');
fwrite($fd, '<protection hidden="false" locked="true"/>');
fwrite($fd, '</xf>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="43"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="41"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="44"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="42"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"/>');
fwrite($fd, '</cellStyleXfs>');
fwrite($fd, '<cellXfs count="4">');
fwrite($fd, '<xf applyAlignment="1" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"><alignment wrapText="1"/></xf>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="165" xfId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="166" xfId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="167" xfId="0"/>');
fwrite($fd, '</cellXfs>');
fwrite($fd, '<cellStyles count="1">');
fwrite($fd, '<cellStyle builtinId="0" customBuiltin="false" name="Normal" xfId="0"/>');
//fwrite($fd, '<cellStyle builtinId="3" customBuiltin="false" name="Comma" xfId="15"/>');
//fwrite($fd, '<cellStyle builtinId="6" customBuiltin="false" name="Comma [0]" xfId="16"/>');
//fwrite($fd, '<cellStyle builtinId="4" customBuiltin="false" name="Currency" xfId="17"/>');
//fwrite($fd, '<cellStyle builtinId="7" customBuiltin="false" name="Currency [0]" xfId="18"/>');
//fwrite($fd, '<cellStyle builtinId="5" customBuiltin="false" name="Percent" xfId="19"/>');
fwrite($fd, '</cellStyles>');
fwrite($fd, '</styleSheet>');
fclose($fd);
return $tempfile;
}
protected function setSharedString($v)
{
// Strip control characters which Excel does not seem to like...
$v = preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F]/u', '', $v);
if (isset($this->shared_strings[$v]))
{
$string_value = $this->shared_strings[$v];
}
else
{
$string_value = count($this->shared_strings);
$this->shared_strings[$v] = $string_value;
}
$this->shared_string_count++;//non-unique count
return $string_value;
}
protected function writeSharedStringsXML()
{
$tempfile = $this->tempFilename();
$fd = fopen($tempfile, "w+");
if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; }
fwrite($fd,'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
fwrite($fd,'<sst count="'.($this->shared_string_count).'" uniqueCount="'.count($this->shared_strings).'" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">');
foreach($this->shared_strings as $s=>$c)
{
fwrite($fd,'<si><t>'.self::xmlspecialchars($s).'</t></si>');
}
fwrite($fd, '</sst>');
fclose($fd);
return $tempfile;
}
protected function buildAppXML()
{
$app_xml="";
$app_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
$app_xml.='<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><TotalTime>0</TotalTime></Properties>';
return $app_xml;
}
protected function buildCoreXML()
{
$core_xml="";
$core_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
$core_xml.='<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">';
$core_xml.='<dcterms:created xsi:type="dcterms:W3CDTF">'.date("Y-m-d\TH:i:s.00\Z").'</dcterms:created>';//$date_time = '2013-07-25T15:54:37.00Z';
$core_xml.='<dc:creator>'.self::xmlspecialchars($this->author).'</dc:creator>';
$core_xml.='<cp:revision>0</cp:revision>';
$core_xml.='</cp:coreProperties>';
return $core_xml;
}
protected function buildRelationshipsXML()
{
$rels_xml="";
$rels_xml.='<?xml version="1.0" encoding="UTF-8"?>'."\n";
$rels_xml.='<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';
$rels_xml.='<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>';
$rels_xml.='<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>';
$rels_xml.='<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>';
$rels_xml.="\n";
$rels_xml.='</Relationships>';
return $rels_xml;
}
protected function buildWorkbookXML()
{
$workbook_xml="";
$workbook_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
$workbook_xml.='<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">';
$workbook_xml.='<fileVersion appName="Calc"/><workbookPr backupFile="false" showObjects="all" date1904="false"/><workbookProtection/>';
$workbook_xml.='<bookViews><workbookView activeTab="0" firstSheet="0" showHorizontalScroll="true" showSheetTabs="true" showVerticalScroll="true" tabRatio="212" windowHeight="8192" windowWidth="16384" xWindow="0" yWindow="0"/></bookViews>';
$workbook_xml.='<sheets>';
foreach($this->sheets_meta as $i=>$sheet_meta) {
$workbook_xml.='<sheet name="'.self::xmlspecialchars($sheet_meta['sheetname']).'" sheetId="'.($i+1).'" state="visible" r:id="rId'.($i+2).'"/>';
}
$workbook_xml.='</sheets>';
$workbook_xml.='<calcPr iterateCount="100" refMode="A1" iterate="false" iterateDelta="0.001"/></workbook>';
return $workbook_xml;
}
protected function buildWorkbookRelsXML()
{
$wkbkrels_xml="";
$wkbkrels_xml.='<?xml version="1.0" encoding="UTF-8"?>'."\n";
$wkbkrels_xml.='<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';
$wkbkrels_xml.='<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>';
foreach($this->sheets_meta as $i=>$sheet_meta) {
$wkbkrels_xml.='<Relationship Id="rId'.($i+2).'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/'.($sheet_meta['xmlname']).'"/>';
}
if (!empty($this->shared_strings)) {
$wkbkrels_xml.='<Relationship Id="rId'.(count($this->sheets_meta)+2).'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>';
}
$wkbkrels_xml.="\n";
$wkbkrels_xml.='</Relationships>';
return $wkbkrels_xml;
}
protected function buildContentTypesXML()
{
$content_types_xml="";
$content_types_xml.='<?xml version="1.0" encoding="UTF-8"?>'."\n";
$content_types_xml.='<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">';
$content_types_xml.='<Override PartName="/_rels/.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
$content_types_xml.='<Override PartName="/xl/_rels/workbook.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
foreach($this->sheets_meta as $i=>$sheet_meta) {
$content_types_xml.='<Override PartName="/xl/worksheets/'.($sheet_meta['xmlname']).'" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>';
}
if (!empty($this->shared_strings)) {
$content_types_xml.='<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>';
}
$content_types_xml.='<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>';
$content_types_xml.='<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>';
$content_types_xml.='<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>';
$content_types_xml.='<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>';
$content_types_xml.="\n";
$content_types_xml.='</Types>';
return $content_types_xml;
}
//------------------------------------------------------------------
/*
* @param $row_number int, zero based
* @param $column_number int, zero based
* @return Cell label/coordinates, ex: A1, C3, AA42
* */
public static function xlsCell($row_number, $column_number)
{
$n = $column_number;
for($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
$r = chr($n%26 + 0x41) . $r;
}
return $r . ($row_number+1);
}
//------------------------------------------------------------------
public static function log($string)
{
file_put_contents("php://stderr", date("Y-m-d H:i:s:").rtrim(is_array($string) ? json_encode($string) : $string)."\n");
}
//------------------------------------------------------------------
public static function xmlspecialchars($val)
{
return str_replace("'", "&#39;", htmlspecialchars($val));
}
//------------------------------------------------------------------
public static function array_first_key(array $arr)
{
reset($arr);
$first_key = key($arr);
return $first_key;
}
//------------------------------------------------------------------
public static function convert_date_time($date_input) //thanks to Excel::Writer::XLSX::Worksheet.pm (perl)
{
$days = 0; # Number of days since epoch
$seconds = 0; # Time expressed as fraction of 24h hours in seconds
$year=$month=$day=0;
$hour=$min =$sec=0;
$date_time = $date_input;
if (preg_match("/(\d{4})\-(\d{2})\-(\d{2})/", $date_time, $matches))
{
list($junk,$year,$month,$day) = $matches;
}
if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $date_time, $matches))
{
list($junk,$hour,$min,$sec) = $matches;
$seconds = ( $hour * 60 * 60 + $min * 60 + $sec ) / ( 24 * 60 * 60 );
}
//using 1900 as epoch, not 1904, ignoring 1904 special case
# Special cases for Excel.
if ("$year-$month-$day"=='1899-12-31') return $seconds ; # Excel 1900 epoch
if ("$year-$month-$day"=='1900-01-00') return $seconds ; # Excel 1900 epoch
if ("$year-$month-$day"=='1900-02-29') return 60 + $seconds ; # Excel false leapday
# We calculate the date by calculating the number of days since the epoch
# and adjust for the number of leap days. We calculate the number of leap
# days by normalising the year in relation to the epoch. Thus the year 2000
# becomes 100 for 4 and 100 year leapdays and 400 for 400 year leapdays.
$epoch = 1900;
$offset = 0;
$norm = 300;
$range = $year - $epoch;
# Set month days and check for leap year.
$leap = (($year % 400 == 0) || (($year % 4 == 0) && ($year % 100)) ) ? 1 : 0;
$mdays = array( 31, ($leap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
# Some boundary checks
if($year < $epoch || $year > 9999) return 0;
if($month < 1 || $month > 12) return 0;
if($day < 1 || $day > $mdays[ $month - 1 ]) return 0;
# Accumulate the number of days since the epoch.
$days = $day; # Add days for current month
$days += array_sum( array_slice($mdays, 0, $month-1 ) ); # Add days for past months
$days += $range * 365; # Add days for past years
$days += intval( ( $range ) / 4 ); # Add leapdays
$days -= intval( ( $range + $offset ) / 100 ); # Subtract 100 year leapdays
$days += intval( ( $range + $offset + $norm ) / 400 ); # Add 400 year leapdays
$days -= $leap; # Already counted above
# Adjust for Excel erroneously treating 1900 as a leap year.
if ($days > 59) { $days++;}
return $days + $seconds;
}
//------------------------------------------------------------------
}

View File

@@ -1,60 +1,106 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2015 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class XMLPage
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once("../application/webpage.class.inc.php");
require_once(APPROOT."/application/webpage.class.inc.php");
/**
* Simple web page with no includes or fancy formatting, useful to generateXML documents
* The page adds the content-type text/XML and the encoding into the headers
*/
class XMLPage extends WebPage
{
function __construct($s_title)
{
parent::__construct($s_title);
/**
* For big XML files, it's better NOT to store everything in memory and output the XML piece by piece
*/
var $m_bPassThrough;
var $m_bHeaderSent;
function __construct($s_title, $bPassThrough = false)
{
parent::__construct($s_title);
$this->m_bPassThrough = $bPassThrough;
$this->m_bHeaderSent = false;
$this->add_header("Content-type: text/xml; charset=utf-8");
$this->add_header("Cache-control: no-cache");
$this->add_header("Content-location: export.xml");
$this->add("<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n");
}
}
public function output()
{
$this->add_header("Content-Length: ".strlen(trim($this->s_content)));
foreach($this->a_headers as $s_header)
{
header($s_header);
}
echo trim($this->s_content);
}
public function small_p($sText)
{
public function output()
{
if (!$this->m_bPassThrough)
{
// Get the unexpected output but do nothing with it
$sTrash = $this->ob_get_clean_safe();
$this->s_content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n".trim($this->s_content);
$this->add_header("Content-Length: ".strlen($this->s_content));
foreach($this->a_headers as $s_header)
{
header($s_header);
}
echo $this->s_content;
}
if (class_exists('DBSearch'))
{
DBSearch::RecordQueryTrace();
}
}
public function add($sText)
{
if (!$this->m_bPassThrough)
{
parent::add($sText);
}
else
{
if ($this->m_bHeaderSent)
{
echo $sText;
}
else
{
$s_captured_output = $this->ob_get_clean_safe();
foreach($this->a_headers as $s_header)
{
header($s_header);
}
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n";
echo trim($s_captured_output);
echo trim($this->s_content);
echo $sText;
$this->m_bHeaderSent = true;
}
}
}
public function small_p($sText)
{
}
public function table($aConfig, $aData, $aParams = array())
{
}
}
?>

15
approot.inc.php Normal file
View File

@@ -0,0 +1,15 @@
<?php
define('APPROOT', dirname(__FILE__).'/');
define('APPCONF', APPROOT.'conf/');
define('ITOP_DEFAULT_ENV', 'production');
if (function_exists('microtime'))
{
$fItopStarted = microtime(true);
}
else
{
$fItopStarted = 1000 * time();
}
?>

View File

@@ -1,27 +1,28 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Various dev/debug helpers
* TODO: cleanup or at least re-organize
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -218,7 +219,7 @@ class MyHelpers
}
}
public static function get_callstack_html($iLevelsToIgnore = 0, $aCallStack = null)
public static function get_callstack($iLevelsToIgnore = 0, $aCallStack = null)
{
if ($aCallStack == null) $aCallStack = debug_backtrace();
@@ -230,6 +231,16 @@ class MyHelpers
{
$sLine = empty($aCallInfo['line']) ? "" : $aCallInfo['line'];
$sFile = empty($aCallInfo['file']) ? "" : $aCallInfo['file'];
if ($sFile != '')
{
$sFile = str_replace('\\', '/', $sFile);
$sAppRoot = str_replace('\\', '/', APPROOT);
$iPos = strpos($sFile, $sAppRoot);
if ($iPos !== false)
{
$sFile = substr($sFile, strlen($sAppRoot));
}
}
$sClass = empty($aCallInfo['class']) ? "" : $aCallInfo['class'];
$sType = empty($aCallInfo['type']) ? "" : $aCallInfo['type'];
$sFunction = empty($aCallInfo['function']) ? "" : $aCallInfo['function'];
@@ -258,11 +269,11 @@ class MyHelpers
$args .= $a;
break;
case 'string':
$a = Str::pure2html(self::beautifulstr($a, 1024, true, true));
$a = Str::pure2html(self::beautifulstr($a, 64, true, false));
$args .= "\"$a\"";
break;
case 'array':
$args .= 'Array('.count($a).')';
$args .= 'array('.count($a).')';
break;
case 'object':
$args .= 'Object('.get_class($a).')';
@@ -271,19 +282,25 @@ class MyHelpers
$args .= 'Resource('.strstr($a, '#').')';
break;
case 'boolean':
$args .= $a ? 'True' : 'False';
$args .= $a ? 'true' : 'false';
break;
case 'NULL':
$args .= 'Null';
$args .= 'null';
break;
default:
$args .= 'Unknown';
}
}
$sFunctionInfo = "$sClass $sType $sFunction($args)";
$sFunctionInfo = "$sClass$sType$sFunction($args)";
}
$aDigestCallStack[] = array('File'=>$sFile, 'Line'=>$sLine, 'Function'=>$sFunctionInfo);
}
return $aDigestCallStack;
}
public static function get_callstack_html($iLevelsToIgnore = 0, $aCallStack = null)
{
$aDigestCallStack = self::get_callstack($iLevelsToIgnore, $aCallStack);
return self::make_table_from_assoc_array($aDigestCallStack);
}
@@ -292,6 +309,17 @@ class MyHelpers
return self::get_callstack_html($iLevelsToIgnore, $aCallStack);
}
public static function get_callstack_text($iLevelsToIgnore = 0, $aCallStack = null)
{
$aDigestCallStack = self::get_callstack($iLevelsToIgnore, $aCallStack);
$aRes = array();
foreach ($aDigestCallStack as $aCall)
{
$aRes[] = $aCall['File'].' at '.$aCall['Line'].', '.$aCall['Function'];
}
return implode("\n", $aRes);
}
///////////////////////////////////////////////////////////////////////////////
// Source: New
// Last modif: 2004/12/20 RQU
@@ -443,9 +471,9 @@ class Str
public static function pure2html($pure, $maxLength = false)
{
// Check for HTML entities, but be careful the DB is in UTF-8
return $maxLength
? htmlentities(substr($pure, 0, $maxLength), ENT_COMPAT, 'UTF-8')
: htmlentities($pure, ENT_COMPAT, 'UTF-8');
return $maxLength
? htmlentities(substr($pure, 0, $maxLength), ENT_QUOTES, 'UTF-8')
: htmlentities($pure, ENT_QUOTES, 'UTF-8');
}
public static function pure2sql($pure, $maxLength = false)
{
@@ -488,7 +516,7 @@ class Str
*/
protected static function xmlencode($string)
{
return self::xmlentities(iconv("ISO-8859-1", "UTF-8//IGNORE",$string));
return self::xmlentities(iconv("UTF-8", "UTF-8//IGNORE",$string));
}
public static function islowcase($sString)

View File

@@ -1,30 +1,32 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Persistent classes (internal): user defined actions
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('../core/email.class.inc.php');
require_once(APPROOT.'/core/asynctask.class.inc.php');
require_once(APPROOT.'/core/email.class.inc.php');
/**
* A user defined action, to customize the application
@@ -41,7 +43,7 @@ abstract class Action extends cmdbAbstractObject
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => array('name'),
"db_table" => "priv_action",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
@@ -105,7 +107,7 @@ abstract class ActionNotification extends Action
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => array('name'),
"db_table" => "priv_action_notification",
"db_key_field" => "id",
"db_finalclass_field" => "",
@@ -134,11 +136,11 @@ class ActionEmail extends ActionNotification
{
$aParams = array
(
"category" => "core/cmdb",
"category" => "core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => array('name'),
"db_table" => "priv_action_email",
"db_key_field" => "id",
"db_finalclass_field" => "",
@@ -162,7 +164,7 @@ class ActionEmail extends ActionNotification
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'test_recipient', 'from', 'reply_to', 'to', 'cc', 'bcc', 'subject', 'body', 'importance', 'trigger_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('name', 'status', 'to', 'subject')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
MetaModel::Init_SetZListItems('standard_search', array('name','description', 'status', 'subject')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
@@ -182,6 +184,7 @@ class ActionEmail extends ActionNotification
try
{
$oSearch = DBObjectSearch::FromOQL($sOQL);
$oSearch->AllowAllData();
}
catch (OQLException $e)
{
@@ -210,8 +213,12 @@ class ActionEmail extends ActionNotification
$aRecipients = array();
while ($oObj = $oSet->Fetch())
{
$aRecipients[] = $oObj->Get($sEmailAttCode);
$this->m_iRecipients++;
$sAddress = trim($oObj->Get($sEmailAttCode));
if (strlen($sAddress) > 0)
{
$aRecipients[] = $sAddress;
$this->m_iRecipients++;
}
}
return implode(', ', $aRecipients);
}
@@ -219,113 +226,92 @@ class ActionEmail extends ActionNotification
public function DoExecute($oTrigger, $aContextArgs)
{
$this->m_iRecipients = 0;
$this->m_aMailErrors = array();
$bRes = false; // until we do succeed in sending the email
try
{
// Determine recicipients
//
$sTo = $this->FindRecipients('to', $aContextArgs);
$sCC = $this->FindRecipients('cc', $aContextArgs);
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
$sFrom = $this->Get('from');
$sReplyTo = $this->Get('reply_to');
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
$oEmail = new EMail();
if ($this->IsBeingTested())
{
$oEmail->SetSubject('TEST['.$sSubject.']');
$sTestBody = $sBody;
$sTestBody .= "<div style=\"border: dashed;\">\n";
$sTestBody .= "<h1>Testing email notification ".$this->GetHyperlink()."</h1>\n";
$sTestBody .= "<p>The email should be sent with the following properties\n";
$sTestBody .= "<ul>\n";
$sTestBody .= "<li>TO: $sTo</li>\n";
$sTestBody .= "<li>CC: $sCC</li>\n";
$sTestBody .= "<li>BCC: $sBCC</li>\n";
$sTestBody .= "<li>From: $sFrom</li>\n";
$sTestBody .= "<li>Reply-To: $sReplyTo</li>\n";
$sTestBody .= "</ul>\n";
$sTestBody .= "</p>\n";
$sTestBody .= "</div>\n";
$oEmail->SetBody($sTestBody);
$oEmail->SetRecipientTO($this->Get('test_recipient'));
$oEmail->SetRecipientFrom($this->Get('test_recipient'));
}
else
{
$oEmail->SetSubject($sSubject);
$oEmail->SetBody($sBody);
$oEmail->SetRecipientTO($sTo);
$oEmail->SetRecipientCC($sCC);
$oEmail->SetRecipientBCC($sBCC);
$oEmail->SetRecipientFrom($sFrom);
$oEmail->SetRecipientReplyTo($sReplyTo);
}
if (empty($this->m_aMailErrors))
{
if ($this->m_iRecipients == 0)
{
$this->m_aMailErrors[] = 'No recipient';
}
else
{
$oKPI = new ExecutionKPI();
$this->m_aMailErrors = array_merge($this->m_aMailErrors, $oEmail->Send());
$oKPI->ComputeStats('Send mail', $sTo);
}
}
}
catch (Exception $e)
{
$this->m_aMailErrors[] = $e->getMessage();
}
if (MetaModel::IsLogEnabledNotification())
{
$oLog = new EventNotificationEmail();
if (empty($this->m_aMailErrors))
if ($this->IsBeingTested())
{
if ($this->IsBeingTested())
{
$oLog->Set('message', 'TEST - Notification sent ('.$this->Get('test_recipient').')');
}
else
{
$oLog->Set('message', 'Notification sent');
}
$oLog->Set('message', 'TEST - Notification sent ('.$this->Get('test_recipient').')');
}
else
{
if (is_array($this->m_aMailErrors) && count($this->m_aMailErrors) > 0)
{
$sError = implode(', ', $this->m_aMailErrors);
}
else
{
$sError = 'Unknown reason';
}
if ($this->IsBeingTested())
{
$oLog->Set('message', 'TEST - Notification was not sent: '.$sError);
}
else
{
$oLog->Set('message', 'Notification was not sent: '.$sError);
}
$oLog->Set('message', 'Notification pending');
}
$oLog->Set('userinfo', UserRights::GetUser());
$oLog->Set('trigger_id', $oTrigger->GetKey());
$oLog->Set('action_id', $this->GetKey());
$oLog->Set('object_id', $aContextArgs['this->object()']->GetKey());
// Must be inserted now so that it gets a valid id that will make the link
// between an eventual asynchronous task (queued) and the log
$oLog->DBInsertNoReload();
}
else
{
$oLog = null;
}
try
{
$sRes = $this->_DoExecute($oTrigger, $aContextArgs, $oLog);
if ($this->IsBeingTested())
{
$sPrefix = 'TEST ('.$this->Get('test_recipient').') - ';
}
else
{
$sPrefix = '';
}
$oLog->Set('message', $sPrefix.$sRes);
}
catch (Exception $e)
{
if ($oLog)
{
$oLog->Set('message', 'Error: '.$e->getMessage());
}
}
if ($oLog)
{
$oLog->DBUpdate();
}
}
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
{
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();
try
{
$this->m_iRecipients = 0;
$this->m_aMailErrors = array();
$bRes = false; // until we do succeed in sending the email
// Determine recicipients
//
$sTo = $this->FindRecipients('to', $aContextArgs);
$sCC = $this->FindRecipients('cc', $aContextArgs);
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
$sFrom = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
$sReplyTo = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
$oObj = $aContextArgs['this->object()'];
$sMessageId = sprintf('iTop_%s_%d_%f@%s.openitop.org', get_class($oObj), $oObj->GetKey(), microtime(true /* get as float*/), MetaModel::GetEnvironmentId());
$sReference = '<'.$sMessageId.'>';
}
catch(Exception $e)
{
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
throw $e;
}
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
if (!is_null($oLog))
{
// Note: we have to secure this because those values are calculated
// inside the try statement, and we would like to keep track of as
// many data as we could while some variables may still be undefined
@@ -335,7 +321,90 @@ class ActionEmail extends ActionNotification
if (isset($sFrom)) $oLog->Set('from', $sFrom);
if (isset($sSubject)) $oLog->Set('subject', $sSubject);
if (isset($sBody)) $oLog->Set('body', $sBody);
$oLog->DBInsertNoReload();
}
$oEmail = new EMail();
if ($this->IsBeingTested())
{
$oEmail->SetSubject('TEST['.$sSubject.']');
$sTestBody = $sBody;
$sTestBody .= "<div style=\"border: dashed;\">\n";
$sTestBody .= "<h1>Testing email notification ".$this->GetHyperlink()."</h1>\n";
$sTestBody .= "<p>The email should be sent with the following properties\n";
$sTestBody .= "<ul>\n";
$sTestBody .= "<li>TO: $sTo</li>\n";
$sTestBody .= "<li>CC: $sCC</li>\n";
$sTestBody .= "<li>BCC: $sBCC</li>\n";
$sTestBody .= "<li>From: $sFrom</li>\n";
$sTestBody .= "<li>Reply-To: $sReplyTo</li>\n";
$sTestBody .= "<li>References: $sReference</li>\n";
$sTestBody .= "</ul>\n";
$sTestBody .= "</p>\n";
$sTestBody .= "</div>\n";
$oEmail->SetBody($sTestBody);
$oEmail->SetRecipientTO($this->Get('test_recipient'));
$oEmail->SetRecipientFrom($this->Get('test_recipient'));
$oEmail->SetReferences($sReference);
$oEmail->SetMessageId($sMessageId);
}
else
{
$oEmail->SetSubject($sSubject);
$oEmail->SetBody($sBody);
$oEmail->SetRecipientTO($sTo);
$oEmail->SetRecipientCC($sCC);
$oEmail->SetRecipientBCC($sBCC);
$oEmail->SetRecipientFrom($sFrom);
$oEmail->SetRecipientReplyTo($sReplyTo);
$oEmail->SetReferences($sReference);
$oEmail->SetMessageId($sMessageId);
}
if (isset($aContextArgs['attachments']))
{
$aAttachmentReport = array();
foreach($aContextArgs['attachments'] as $oDocument)
{
$oEmail->AddAttachment($oDocument->GetData(), $oDocument->GetFileName(), $oDocument->GetMimeType());
$aAttachmentReport[] = array($oDocument->GetFileName(), $oDocument->GetMimeType(), strlen($oDocument->GetData()));
}
$oLog->Set('attachments', $aAttachmentReport);
}
if (empty($this->m_aMailErrors))
{
if ($this->m_iRecipients == 0)
{
return 'No recipient';
}
else
{
$iRes = $oEmail->Send($aErrors, false, $oLog); // allow asynchronous mode
switch ($iRes)
{
case EMAIL_SEND_OK:
return "Sent";
case EMAIL_SEND_PENDING:
return "Pending";
case EMAIL_SEND_ERROR:
return "Errors: ".implode(', ', $aErrors);
}
}
}
else
{
if (is_array($this->m_aMailErrors) && count($this->m_aMailErrors) > 0)
{
$sError = implode(', ', $this->m_aMailErrors);
}
else
{
$sError = 'Unknown reason';
}
return 'Notification was not sent: '.$sError;
}
}
}

View File

@@ -1,26 +1,27 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Utility to import/export the DB from/to a ZIP file
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -142,7 +143,7 @@ class iTopArchive
public function WriteCatalog()
{
$sXml = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?".">\n"; // split the XML closing tag that disturbs PSPad's syntax coloring
$sXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?".">\n"; // split the XML closing tag that disturbs PSPad's syntax coloring
$sXml .= "<archive version=\"1.0\">\n";
$sXml .= "<title>{$this->m_sTitle}</title>\n";
$sXml .= "<description>{$this->m_sDescription}</description>\n";

View File

@@ -0,0 +1,359 @@
<?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/>
/**
* Persistent classes (internal): user defined actions
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ExecAsyncTask implements iBackgroundProcess
{
public function GetPeriodicity()
{
return 2; // seconds
}
public function Process($iTimeLimit)
{
$sNow = date('Y-m-d H:i:s');
// Criteria: planned, and expected to occur... ASAP or in the past
$sOQL = "SELECT AsyncTask WHERE (status = 'planned') AND (ISNULL(planned) OR (planned < '$sNow'))";
$iProcessed = 0;
while (time() < $iTimeLimit)
{
// Next one ?
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array(), null, 1 /* limit count */);
$oTask = $oSet->Fetch();
if (is_null($oTask))
{
// Nothing to be done
break;
}
$iProcessed++;
if ($oTask->Process())
{
$oTask->DBDelete();
}
}
return "processed $iProcessed tasks";
}
}
/**
* A
*
* @package iTopORM
*/
abstract class AsyncTask extends DBObject
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => array('created'),
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_async_task",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
// Null is allowed to ease the migration from iTop 2.0.2 and earlier, when the status did not exist, and because the default value is not taken into account in the SQL definition
// The value is set from null to planned in the setup program
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('planned,running,idle,error'), "sql"=>"status", "default_value"=>"planned", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("started", array("allowed_values"=>null, "sql"=>"started", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("planned", array("allowed_values"=>null, "sql"=>"planned", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("event_id", array("targetclass"=>"Event", "jointype"=> "", "allowed_values"=>null, "sql"=>"event_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("remaining_retries", array("allowed_values"=>null, "sql"=>"remaining_retries", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("last_error_code", array("allowed_values"=>null, "sql"=>"last_error_code", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("last_error", array("allowed_values"=>null, "sql"=>"last_error", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("last_attempt", array("allowed_values"=>null, "sql"=>"last_attempt", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
}
/**
* Every is fine
*/
const OK = 0;
/**
* The task no longer exists
*/
const DELETED = 1;
/**
* The task is already being executed
*/
const ALREADY_RUNNING = 2;
/**
* The current process requests the ownership on the task.
* In case the task can be accessed concurrently, this function can be overloaded to add a critical section.
* The function must not block the caller if another process is already owning the task
*
* @return integer A code among OK/DELETED/ALREADY_RUNNING.
*/
public function MarkAsRunning()
{
try
{
if ($this->Get('status') == 'running')
{
return self::ALREADY_RUNNING;
}
else
{
$this->Set('status', 'running');
$this->Set('started', time());
$this->DBUpdate();
return self::OK;
}
}
catch(Exception $e)
{
// Corrupted task !! (for example: "Failed to reload object")
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$e->getMessage().' - fatal error, deleting the task.');
if ($this->Get('event_id') != 0)
{
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', 'Failed, corrupted data: '.$e->getMessage());
$oEventLog->DBUpdate();
}
$this->DBDelete();
return self::DELETED;
}
}
public function GetRetryDelay($iErrorCode = null)
{
$iRetryDelay = 600;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
$iRetryDelay = $aConfig['retry_delay'];
}
return $iRetryDelay;
}
public function GetMaxRetries($iErrorCode = null)
{
$iMaxRetries = 0;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
$iMaxRetries = $aConfig['max_retries'];
}
}
/**
* Override to notify people that a task cannot be performed
*/
protected function OnDefinitiveFailure()
{
}
protected function OnInsert()
{
$this->Set('created', time());
}
/**
* @return boolean True if the task record can be deleted
*/
public function Process()
{
// By default: consider that the task is not completed
$bRet = false;
// Attempt to take the ownership
$iStatus = $this->MarkAsRunning();
if ($iStatus == self::OK)
{
try
{
$sStatus = $this->DoProcess();
if ($this->Get('event_id') != 0)
{
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', $sStatus);
$oEventLog->DBUpdate();
}
$bRet = true;
}
catch(Exception $e)
{
$this->HandleError($e->getMessage(), $e->getCode());
}
}
else
{
// Already done or being handled by another process... skip...
$bRet = false;
}
return $bRet;
}
/**
* Overridable to extend the behavior in case of error (logging)
*/
protected function HandleError($sErrorMessage, $iErrorCode)
{
if ($this->Get('last_attempt') == '')
{
// First attempt
$this->Set('remaining_retries', $this->GetMaxRetries($iErrorCode));
}
$this->Set('last_error', $sErrorMessage);
$this->Set('last_error_code', $iErrorCode); // Note: can be ZERO !!!
$this->Set('last_attempt', time());
$iRemaining = $this->Get('remaining_retries');
if ($iRemaining > 0)
{
$iRetryDelay = $this->GetRetryDelay($iErrorCode);
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage.' - remaining retries: '.$iRemaining.' - next retry in '.$iRetryDelay.'s');
$this->Set('remaining_retries', $iRemaining - 1);
$this->Set('status', 'planned');
$this->Set('started', null);
$this->Set('planned', time() + $iRetryDelay);
}
else
{
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage);
$this->Set('status', 'error');
$this->Set('started', null);
$this->Set('planned', null);
$this->OnDefinitiveFailure();
}
$this->DBUpdate();
}
/**
* Throws an exception (message and code)
*/
abstract public function DoProcess();
/**
* Describes the error codes that DoProcess can return by the mean of exceptions
*/
static public function EnumErrorCodes()
{
return array();
}
}
/**
* An email notification
*
* @package iTopORM
*/
class AsyncSendEmail extends AsyncTask
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "created",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_async_send_email",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeInteger("version", array("allowed_values"=>null, "sql"=>"version", "default_value"=>Email::ORIGINAL_FORMAT, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("to", array("allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLongText("message", array("allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists
// MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'test_recipient', 'from', 'reply_to', 'to', 'cc', 'bcc', 'subject', 'body', 'importance', 'trigger_list')); // Attributes to be displayed for the complete details
// MetaModel::Init_SetZListItems('list', array('name', 'status', 'to', 'subject')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
static public function AddToQueue(EMail $oEMail, $oLog)
{
$oNew = MetaModel::NewObject(__class__);
if ($oLog)
{
$oNew->Set('event_id', $oLog->GetKey());
}
$oNew->Set('to', $oEMail->GetRecipientTO(true /* string */));
$oNew->Set('subject', $oEMail->GetSubject());
// $oNew->Set('version', 1);
// $sMessage = serialize($oEMail);
$oNew->Set('version', 2);
$sMessage = $oEMail->SerializeV2();
$oNew->Set('message', $sMessage);
$oNew->DBInsert();
}
public function DoProcess()
{
$sMessage = $this->Get('message');
$iVersion = (int) $this->Get('version');
switch($iVersion)
{
case Email::FORMAT_V2:
$oEMail = Email::UnSerializeV2($sMessage);
break;
case Email::ORIGINAL_FORMAT:
$oEMail = unserialize($sMessage);
break;
default:
return 'Unknown version of the serialization format: '.$iVersion;
}
$iRes = $oEMail->Send($aIssues, true /* force synchro !!!!! */);
switch ($iRes)
{
case EMAIL_SEND_OK:
return "Sent";
case EMAIL_SEND_PENDING:
return "Bug - the email should be sent in synchronous mode";
case EMAIL_SEND_ERROR:
return "Failed: ".implode(', ', $aIssues);
}
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,67 @@
<?php
// Copyright (C) 2010-2013 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/>
/**
* interface iProcess
* Something that can be executed
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
interface iProcess
{
public function Process($iUnixTimeLimit);
}
/**
* interface iBackgroundProcess
* Any extension that must be called regularly to be executed in the background
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
interface iBackgroundProcess extends iProcess
{
/*
Gives the repetition rate in seconds
@returns integer
*/
public function GetPeriodicity();
}
/**
* interface iScheduledProcess
* A variant of process that must be called at specific times
*
* @copyright Copyright (C) 2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
interface iScheduledProcess extends iProcess
{
/*
Gives the exact time at which the process must be run next time
@returns DateTime
*/
public function GetNextOccurrence();
}
?>

View File

@@ -0,0 +1,76 @@
<?php
// Copyright (C) 2013 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class BackgroundTask
* A class to record information about the execution of background processes
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class BackgroundTask extends DBObject
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "class_name",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_backgroundtask",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("class_name", array("allowed_values"=>null, "sql"=>"class_name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("first_run_date", array("allowed_values"=>null, "sql"=>"first_run_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("latest_run_date", array("allowed_values"=>null, "sql"=>"latest_run_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("next_run_date", array("allowed_values"=>null, "sql"=>"next_run_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("total_exec_count", array("allowed_values"=>null, "sql"=>"total_exec_count", "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDecimal("latest_run_duration", array("allowed_values"=>null, "sql"=>"latest_run_duration", "digits"=> 8, "decimals"=> 3, "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDecimal("min_run_duration", array("allowed_values"=>null, "sql"=>"min_run_duration", "digits"=> 8, "decimals"=> 3, "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDecimal("max_run_duration", array("allowed_values"=>null, "sql"=>"max_run_duration", "digits"=> 8, "decimals"=> 3, "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDecimal("average_run_duration", array("allowed_values"=>null, "sql"=>"average_run_duration", "digits"=> 8, "decimals"=> 3, "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeBoolean("running", array("allowed_values"=>null, "sql"=>"running", "default_value"=>false, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('active,paused'), "sql"=>"status", "default_value"=>'active', "is_null_allowed"=>false, "depends_on"=>array())));
}
public function ComputeDurations($fLatestDuration)
{
$iTotalRun = $this->Get('total_exec_count');
$fAverageDuration = ($this->Get('average_run_duration') * $iTotalRun + $fLatestDuration) / (1+$iTotalRun);
$this->Set('average_run_duration', sprintf('%.3f',$fAverageDuration));
$this->Set('total_exec_count', 1+$iTotalRun);
if ($fLatestDuration < $this->Get('min_run_duration'))
{
$this->Set('min_run_duration', sprintf('%.3f',$fLatestDuration));
}
if ($fLatestDuration > $this->Get('max_run_duration'))
{
$this->Set('max_run_duration', sprintf('%.3f',$fLatestDuration));
}
$this->Set('latest_run_duration', sprintf('%.3f',$fLatestDuration));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,420 @@
<?php
// Copyright (C) 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/>
define('EXPORTER_DEFAULT_CHUNK_SIZE', 1000);
class BulkExportException extends Exception
{
protected $sLocalizedMessage;
public function __construct($message, $sLocalizedMessage, $code = null, $previous = null)
{
parent::__construct($message, $code, $previous);
$this->sLocalizedMessage = $sLocalizedMessage;
}
public function GetLocalizedMessage()
{
return $this->sLocalizedMessage;
}
}
class BulkExportMissingParameterException extends BulkExportException
{
public function __construct($sFieldCode)
{
parent::__construct('Missing parameter: '.$sFieldCode, Dict::Format('Core:BulkExport:MissingParameter_Param', $sFieldCode));
}
}
/**
* Class BulkExport
*
* @copyright Copyright (C) 2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class BulkExportResult extends DBObject
{
public static function Init()
{
$aParams = array
(
"category" => 'core/cmdb',
"key_type" => 'autoincrement',
"name_attcode" => array('created'),
"state_attcode" => '',
"reconc_keys" => array(),
"db_table" => 'priv_bulk_export_result',
"db_key_field" => 'id',
"db_finalclass_field" => '',
"display_template" => '',
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("user_id", array("allowed_values"=>null, "sql"=>"user_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("chunk_size", array("allowed_values"=>null, "sql"=>"chunk_size", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("format", array("allowed_values"=>null, "sql"=>"format", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("temp_file_path", array("allowed_values"=>null, "sql"=>"temp_file_path", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLongText("search", array("allowed_values"=>null, "sql"=>"search", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLongText("status_info", array("allowed_values"=>null, "sql"=>"status_info", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
}
public function ComputeValues()
{
$this->Set('user_id', UserRights::GetUserId());
}
}
/**
* Garbage collector for cleaning "old" export results from the database and the disk.
* This background process runs once per day and deletes the results of all exports which
* are older than one day.
*/
class BulkExportResultGC implements iBackgroundProcess
{
public function GetPeriodicity()
{
return 24*3600; // seconds
}
public function Process($iTimeLimit)
{
$sDateLimit = date('Y-m-d H:i:s', time() - 24*3600); // Every BulkExportResult older than one day will be deleted
$sOQL = "SELECT BulkExportResult WHERE created < '$sDateLimit'";
$iProcessed = 0;
while (time() < $iTimeLimit)
{
// Next one ?
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array(), null, 1 /* limit count */);
$oSet->OptimizeColumnLoad(array('temp_file_path'));
$oResult = $oSet->Fetch();
if (is_null($oResult))
{
// Nothing to be done
break;
}
$iProcessed++;
@unlink($oResult->Get('temp_file_path'));
$oResult->DBDelete();
}
return "Cleaned $iProcessed old export results(s).";
}
}
/**
* Class BulkExport
*
* @copyright Copyright (C) 2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class BulkExport
{
protected $oSearch;
protected $iChunkSize;
protected $sFormatCode;
protected $aStatusInfo;
protected $oBulkExportResult;
protected $sTmpFile;
protected $bLocalizeOutput;
public function __construct()
{
$this->oSearch = null;
$this->iChunkSize = 0;
$this->sFormatCode = null;
$this->aStatusInfo = array();
$this->oBulkExportResult = null;
$this->sTmpFile = '';
$this->bLocalizeOutput = false;
}
/**
* Find the first class capable of exporting the data in the given format
* @param string $sFormat The lowercase format (e.g. html, csv, spreadsheet, xlsx, xml, json, pdf...)
* @param DBSearch $oSearch The search/filter defining the set of objects to export or null when listing the supported formats
* @return iBulkExport|NULL
*/
static public function FindExporter($sFormatCode, $oSearch = null)
{
foreach(get_declared_classes() as $sPHPClass)
{
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->isSubclassOf('BulkExport') && !$oRefClass->isAbstract())
{
$oBulkExporter = new $sPHPClass();
if ($oBulkExporter->IsFormatSupported($sFormatCode, $oSearch))
{
if ($oSearch)
{
$oBulkExporter->SetObjectList($oSearch);
}
return $oBulkExporter;
}
}
}
return null;
}
/**
* Find the exporter corresponding to the given persistent token
* @param int $iPersistentToken The identifier of the BulkExportResult object storing the information
* @return iBulkExport|NULL
*/
static public function FindExporterFromToken($iPersistentToken = null)
{
$oBulkExporter = null;
$oInfo = MetaModel::GetObject('BulkExportResult', $iPersistentToken, false);
if ($oInfo && ($oInfo->Get('user_id') == UserRights::GetUserId()))
{
$sFormatCode = $oInfo->Get('format');
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
if ($oBulkExporter)
{
$oBulkExporter->SetFormat($sFormatCode);
$oBulkExporter->SetObjectList($oSearch);
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
$oBulkExporter->SetStatusInfo(json_decode($oInfo->Get('status_info'), true));
$oBulkExporter->sTmpFile = $oInfo->Get('temp_file_path');
$oBulkExporter->oBulkExportResult = $oInfo;
}
}
return $oBulkExporter;
}
public function AppendToTmpFile($data)
{
if ($this->sTmpFile == '')
{
$this->sTmpFile = $this->MakeTmpFile($this->GetFileExtension());
}
$hFile = fopen($this->sTmpFile, 'ab');
if ($hFile !== false)
{
fwrite($hFile, $data);
fclose($hFile);
}
}
public function GetTmpFilePath()
{
return $this->sTmpFile;
}
/**
* Lists all possible export formats. The output is a hash array in the form: 'format_code' => 'localized format label'
* @return multitype:string
*/
static public function FindSupportedFormats()
{
$aSupportedFormats = array();
foreach(get_declared_classes() as $sPHPClass)
{
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->isSubClassOf('BulkExport') && !$oRefClass->isAbstract())
{
$oBulkExporter = new $sPHPClass;
$aFormats = $oBulkExporter->GetSupportedFormats();
$aSupportedFormats = array_merge($aSupportedFormats, $aFormats);
}
}
return $aSupportedFormats;
}
/**
* (non-PHPdoc)
* @see iBulkExport::SetChunkSize()
*/
public function SetChunkSize($iChunkSize)
{
$this->iChunkSize = $iChunkSize;
}
/**
* (non-PHPdoc)
* @see iBulkExport::SetObjectList()
*/
public function SetObjectList(DBSearch $oSearch)
{
$this->oSearch = $oSearch;
}
public function SetFormat($sFormatCode)
{
$this->sFormatCode = $sFormatCode;
}
/**
* (non-PHPdoc)
* @see iBulkExport::IsFormatSupported()
*/
public function IsFormatSupported($sFormatCode, $oSearch = null)
{
return array_key_exists($sFormatCode, $this->GetSupportedFormats());
}
/**
* (non-PHPdoc)
* @see iBulkExport::GetSupportedFormats()
*/
public function GetSupportedFormats()
{
return array(); // return array('csv' => Dict::S('UI:ExportFormatCSV'));
}
public function SetHttpHeaders(WebPage $oPage)
{
}
public function GetHeader()
{
}
abstract public function GetNextChunk(&$aStatus);
public function GetFooter()
{
}
public function SaveState()
{
if ($this->oBulkExportResult === null)
{
$this->oBulkExportResult = new BulkExportResult();
$this->oBulkExportResult->Set('format', $this->sFormatCode);
$this->oBulkExportResult->Set('search', $this->oSearch->serialize());
$this->oBulkExportResult->Set('chunk_size', $this->iChunkSize);
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
}
$this->oBulkExportResult->Set('status_info', json_encode($this->GetStatusInfo()));
return $this->oBulkExportResult->DBWrite();
}
public function Cleanup()
{
if (($this->oBulkExportResult && (!$this->oBulkExportResult->IsNew())))
{
$sFilename = $this->oBulkExportResult->Get('temp_file_path');
if ($sFilename != '')
{
@unlink($sFilename);
}
$this->oBulkExportResult->DBDelete();
}
}
public function EnumFormParts()
{
return array();
}
public function DisplayFormPart(WebPage $oP, $sPartId)
{
}
public function DisplayUsage(Page $oP)
{
}
public function ReadParameters()
{
$this->bLocalizeOutput = !((bool)utils::ReadParam('no_localize', 0, true, 'integer'));
}
public function GetResultAsHtml()
{
}
public function GetRawResult()
{
}
public function GetMimeType()
{
}
public function GetFileExtension()
{
}
public function GetCharacterSet()
{
return 'UTF-8';
}
public function GetStatistics()
{
}
public function GetDownloadFileName()
{
return Dict::Format('Core:BulkExportOf_Class', MetaModel::GetName($this->oSearch->GetClass())).'.'.$this->GetFileExtension();
}
public function SetStatusInfo($aStatusInfo)
{
$this->aStatusInfo = $aStatusInfo;
}
public function GetStatusInfo()
{
return $this->aStatusInfo;
}
protected function MakeTmpFile($sExtension)
{
if(!is_dir(APPROOT."data/bulk_export"))
{
@mkdir(APPROOT."data/bulk_export", 0777, true /* recursive */);
clearstatcache();
}
if (!is_writable(APPROOT."data/bulk_export"))
{
throw new Exception('Data directory "'.APPROOT.'data/bulk_export" could not be written.');
}
$iNum = rand();
$sFileName = '';
do
{
$iNum++;
$sToken = sprintf("%08x", $iNum);
$sFileName = APPROOT."data/bulk_export/$sToken.".$sExtension;
$hFile = @fopen($sFileName, 'x');
}
while($hFile === false);
fclose($hFile);
return $sFileName;
}
}
// 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');
require_once(APPROOT.'core/csvbulkexport.class.inc.php');
require_once(APPROOT.'core/excelbulkexport.class.inc.php');
require_once(APPROOT.'core/spreadsheetbulkexport.class.inc.php');
require_once(APPROOT.'core/xmlbulkexport.class.inc.php');

View File

@@ -1,26 +1,27 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Persistent class (internal) cmdbChange
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -43,11 +44,43 @@ class CMDBChange extends DBObject
"db_table" => "priv_change",
"db_key_field" => "id",
"db_finalclass_field" => "",
'indexes' => array(
array('origin'),
)
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("origin", array("allowed_values"=>new ValueSetEnum('interactive,csv-interactive,csv-import.php,webservice-soap,webservice-rest,synchro-data-source,email-processing,custom-extension'), "sql"=>"origin", "default_value"=>"interactive", "is_null_allowed"=>true, "depends_on"=>array())));
}
// Helper to keep track of the author of a given change,
// taking into account a variety of cases (contact attached or not, impersonation)
static public function GetCurrentUserName()
{
if (UserRights::IsImpersonated())
{
$sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUserFriendlyName(), UserRights::GetUserFriendlyName());
}
else
{
$sUserString = UserRights::GetUserFriendlyName();
}
return $sUserString;
}
public function GetUserName()
{
if (preg_match('/^(.*)\\(CSV\\)$/i', $this->Get('userinfo'), $aMatches))
{
$sUser = $aMatches[1];
}
else
{
$sUser = $this->Get('userinfo');
}
return $sUser;
}
}

View File

@@ -1,26 +1,27 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2013 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Persistent classes (internal) : cmdbChangeOp and derived
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -44,6 +45,9 @@ class CMDBChangeOp extends DBObject
"db_table" => "priv_changeop",
"db_key_field" => "id",
"db_finalclass_field" => "optype",
'indexes' => array(
array('objclass', 'objkey'),
)
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
@@ -51,7 +55,7 @@ class CMDBChangeOp extends DBObject
MetaModel::Init_AddAttribute(new AttributeExternalField("date", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"date")));
MetaModel::Init_AddAttribute(new AttributeExternalField("userinfo", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"userinfo")));
MetaModel::Init_AddAttribute(new AttributeString("objclass", array("allowed_values"=>null, "sql"=>"objclass", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("objkey", array("allowed_values"=>null, "sql"=>"objkey", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeObjectKey("objkey", array("allowed_values"=>null, "class_attcode"=>"objclass", "sql"=>"objkey", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_SetZListItems('details', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details
@@ -64,6 +68,19 @@ class CMDBChangeOp extends DBObject
{
return '';
}
/**
* Safety net: in case the change is not given, let's guarantee that it will
* be set to the current ongoing change (or create a new one)
*/
protected function OnInsert()
{
if ($this->Get('change') <= 0)
{
$this->Set('change', CMDBObject::GetCurrentChange());
}
parent::OnInsert();
}
}
@@ -124,6 +141,11 @@ class CMDBChangeOpDelete extends CMDBChangeOp
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Final class of the object (objclass must be set to the root class for efficiency purposes)
MetaModel::Init_AddAttribute(new AttributeString("fclass", array("allowed_values"=>null, "sql"=>"fclass", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
// Last friendly name of the object
MetaModel::Init_AddAttribute(new AttributeString("fname", array("allowed_values"=>null, "sql"=>"fname", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
}
/**
* Describe (as a text string) the modifications corresponding to this change
@@ -200,9 +222,6 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute
*/
public function GetDescription()
{
// Temporary, until we change the options of GetDescription() -needs a more global revision
$bIsHtml = true;
$sResult = '';
$oTargetObjectClass = $this->Get('objclass');
$oTargetObjectKey = $this->Get('objkey');
@@ -212,44 +231,13 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (!MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) return ''; // Protects against renamed attributes...
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
$sNewValue = $this->Get('newvalue');
$sOldValue = $this->Get('oldvalue');
if ( (($oAttDef->GetType() == 'String') || ($oAttDef->GetType() == 'Text')) &&
(strlen($sNewValue) > strlen($sOldValue)) )
{
// Check if some text was not appended to the field
if (substr($sNewValue,0, strlen($sOldValue)) == $sOldValue) // Text added at the end
{
$sDelta = substr($sNewValue, strlen($sOldValue));
$sResult = Dict::Format('Change:Text_AppendedTo_AttName', $sDelta, $sAttName);
}
else if (substr($sNewValue, -strlen($sOldValue)) == $sOldValue) // Text added at the beginning
{
$sDelta = substr($sNewValue, 0, strlen($sNewValue) - strlen($sOldValue));
$sResult = Dict::Format('Change:Text_AppendedTo_AttName', $sDelta, $sAttName);
}
else
{
$sResult = Dict::Format('Change:AttName_SetTo_NewValue_PreviousValue_OldValue', $sAttName, $sNewValue, $sOldValue);
}
}
elseif($bIsHtml && $oAttDef->IsExternalKey())
{
$sTargetClass = $oAttDef->GetTargetClass();
$sFrom = MetaModel::GetHyperLink($sTargetClass, $sOldValue);
$sTo = MetaModel::GetHyperLink($sTargetClass, $sNewValue);
$sResult = "$sAttName set to $sTo (previous: $sFrom)";
}
elseif ($oAttDef instanceOf AttributeBlob)
{
$sResult = "#@# Issue... found an attribute for which other type of tracking should be made";
}
else
{
$sResult = Dict::Format('Change:AttName_SetTo_NewValue_PreviousValue_OldValue', $sAttName, $sNewValue, $sOldValue);
}
$sResult = $oAttDef->DescribeChangeAsHTML($sOldValue, $sNewValue);
}
return $sResult;
}
@@ -301,8 +289,16 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
}
else
{
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$oPrevDoc = $this->Get('prevdata');
$sDocView = $oPrevDoc->GetAsHtml();
$sDocView .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_',$oPrevDoc->GetDisplayLink(get_class($this), $this->GetKey(), 'prevdata')).", \n";
@@ -357,8 +353,16 @@ class CMDBChangeOpSetAttributeOneWayPassword extends CMDBChangeOpSetAttribute
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
}
else
{
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$sResult = Dict::Format('Change:AttName_Changed', $sAttName);
}
return $sResult;
@@ -409,8 +413,16 @@ class CMDBChangeOpSetAttributeEncrypted extends CMDBChangeOpSetAttribute
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
}
else
{
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$sPrevString = $this->Get('prevstring');
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sPrevString);
}
@@ -464,8 +476,16 @@ class CMDBChangeOpSetAttributeText extends CMDBChangeOpSetAttribute
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
}
else
{
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$sTextView = '<div>'.$this->GetAsHtml('prevdata').'</div>';
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
@@ -475,4 +495,381 @@ class CMDBChangeOpSetAttributeText extends CMDBChangeOpSetAttribute
}
}
/**
* Record the modification of a multiline string (text)
*
* @package iTopORM
*/
class CMDBChangeOpSetAttributeLongText extends CMDBChangeOpSetAttribute
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_longtext",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeLongText("prevdata", array("allowed_values"=>null, "sql"=>"prevdata", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for a list
}
/**
* Describe (as a text string) the modifications corresponding to this change
*/
public function GetDescription()
{
// Temporary, until we change the options of GetDescription() -needs a more global revision
$bIsHtml = true;
$sResult = '';
$oTargetObjectClass = $this->Get('objclass');
$oTargetObjectKey = $this->Get('objkey');
$oTargetSearch = new DBObjectSearch($oTargetObjectClass);
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
}
else
{
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$sTextView = '<div>'.$this->GetAsHtml('prevdata').'</div>';
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sTextView);
}
return $sResult;
}
}
/**
* Record the modification of a caselog (text)
* since the caselog itself stores the history
* of its entries, there is no need to duplicate
* the text here
*
* @package iTopORM
*/
class CMDBChangeOpSetAttributeCaseLog extends CMDBChangeOpSetAttribute
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_log",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeInteger("lastentry", array("allowed_values"=>null, "sql"=>"lastentry", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for a list
}
/**
* Describe (as a text string) the modifications corresponding to this change
*/
public function GetDescription()
{
// Temporary, until we change the options of GetDescription() -needs a more global revision
$bIsHtml = true;
$sResult = '';
$oTargetObjectClass = $this->Get('objclass');
$oTargetObjectKey = $this->Get('objkey');
$oTargetSearch = new DBObjectSearch($oTargetObjectClass);
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
}
else
{
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$oObj = $oMonoObjectSet->Fetch();
$oCaseLog = $oObj->Get($this->Get('attcode'));
$iMaxVisibleLength = MetaModel::getConfig()->Get('max_history_case_log_entry_length', 0);
$sTextEntry = $oCaseLog->GetEntryAt($this->Get('lastentry'));
if (($iMaxVisibleLength > 0) && (strlen($sTextEntry) > $iMaxVisibleLength))
{
if (function_exists('mb_strcut'))
{
// Safe with multi-byte strings
$sBefore = $this->ToHtml(mb_strcut($sTextEntry, 0, $iMaxVisibleLength, 'UTF-8'));
$sAfter = $this->ToHtml(mb_strcut($sTextEntry, $iMaxVisibleLength, null, 'UTF-8'));
}
else
{
// Let's hope we have no multi-byte characters around the cuttting point...
$sBefore = $this->ToHtml(substr($sTextEntry, 0, $iMaxVisibleLength));
$sAfter = $this->ToHtml(substr($sTextEntry, $iMaxVisibleLength));
}
$sTextEntry = '<span class="case-log-history-entry">'.$sBefore.'<span class="case-log-history-entry-end">'.$sAfter.'<span class="case-log-history-entry-toggle ui-icon ui-icon-circle-minus"></span></span><span class="case-log-history-entry-more">...<span class="case-log-history-entry-toggle ui-icon ui-icon-circle-plus"></span></span></span>';
}
else
{
$sTextEntry = $this->ToHtml($sTextEntry);
}
$sResult = Dict::Format('Change:AttName_EntryAdded', $sAttName, $sTextEntry);
}
return $sResult;
}
protected function ToHtml($sRawText)
{
return str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sRawText, ENT_QUOTES, 'UTF-8'));
}
}
/**
* Record an action made by a plug-in
*
* @package iTopORM
*/
class CMDBChangeOpPlugin extends CMDBChangeOp
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_plugin",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
/* May be used later when implementing an extension mechanism that will allow the plug-ins to store some extra information and still degrades gracefully when the plug-in is desinstalled
MetaModel::Init_AddAttribute(new AttributeString("extension_class", array("allowed_values"=>null, "sql"=>"extension_class", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("extension_id", array("allowed_values"=>null, "sql"=>"extension_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
*/
MetaModel::Init_InheritAttributes();
}
/**
* Describe (as a text string) the modifications corresponding to this change
*/
public function GetDescription()
{
return $this->Get('description');
}
}
/**
* Record added/removed objects from within a link set
*
* @package iTopORM
*/
abstract class CMDBChangeOpSetAttributeLinks extends CMDBChangeOpSetAttribute
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Note: item class/id points to the link class itself in case of a direct link set (e.g. Server::interface_list => Interface)
// item class/id points to the remote class in case of a indirect link set (e.g. Server::contract_list => Contract)
MetaModel::Init_AddAttribute(new AttributeString("item_class", array("allowed_values"=>null, "sql"=>"item_class", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("item_id", array("allowed_values"=>null, "sql"=>"item_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
}
}
/**
* Record added/removed objects from within a link set
*
* @package iTopORM
*/
class CMDBChangeOpSetAttributeLinksAddRemove extends CMDBChangeOpSetAttributeLinks
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links_addremove",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeEnum("type", array("allowed_values"=>new ValueSetEnum('added,removed'), "sql"=>"type", "default_value"=>"added", "is_null_allowed"=>false, "depends_on"=>array())));
}
/**
* Describe (as a text string) the modifications corresponding to this change
*/
public function GetDescription()
{
$sResult = '';
$oTargetObjectClass = $this->Get('objclass');
$oTargetObjectKey = $this->Get('objkey');
$oTargetSearch = new DBObjectSearch($oTargetObjectClass);
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (!MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) return ''; // Protects against renamed attributes...
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
$sItemDesc = MetaModel::GetHyperLink($this->Get('item_class'), $this->Get('item_id'));
$sResult = $sAttName.' - ';
switch ($this->Get('type'))
{
case 'added':
$sResult .= Dict::Format('Change:LinkSet:Added', $sItemDesc);
break;
case 'removed':
$sResult .= Dict::Format('Change:LinkSet:Removed', $sItemDesc);
break;
}
}
return $sResult;
}
}
/**
* Record attribute changes from within a link set
* A single record redirects to the modifications made within the same change
*
* @package iTopORM
*/
class CMDBChangeOpSetAttributeLinksTune extends CMDBChangeOpSetAttributeLinks
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links_tune",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeInteger("link_id", array("allowed_values"=>null, "sql"=>"link_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
}
/**
* Describe (as a text string) the modifications corresponding to this change
*/
public function GetDescription()
{
$sResult = '';
$oTargetObjectClass = $this->Get('objclass');
$oTargetObjectKey = $this->Get('objkey');
$oTargetSearch = new DBObjectSearch($oTargetObjectClass);
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (!MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) return ''; // Protects against renamed attributes...
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
$sLinkClass = $oAttDef->GetLinkedClass();
$aLinkClasses = MetaModel::EnumChildClasses($sLinkClass, ENUM_CHILD_CLASSES_ALL);
// Search for changes on the corresponding link
//
$oSearch = new DBObjectSearch('CMDBChangeOpSetAttribute');
$oSearch->AddCondition('change', $this->Get('change'), '=');
$oSearch->AddCondition('objkey', $this->Get('link_id'), '=');
if (count($aLinkClasses) == 1)
{
// Faster than the whole building of the expression below for just one value ??
$oSearch->AddCondition('objclass', $sLinkClass, '=');
}
else
{
$oField = new FieldExpression('objclass', $oSearch->GetClassAlias());
$sListExpr = '('.implode(', ', CMDBSource::Quote($aLinkClasses)).')';
$sOQLCondition = $oField->Render()." IN $sListExpr";
$oNewCondition = Expression::FromOQL($sOQLCondition);
$oSearch->AddConditionExpression($oNewCondition);
}
$oSet = new DBObjectSet($oSearch);
$aChanges = array();
while ($oChangeOp = $oSet->Fetch())
{
$aChanges[] = $oChangeOp->GetDescription();
}
if (count($aChanges) == 0)
{
return '';
}
$sItemDesc = MetaModel::GetHyperLink($this->Get('item_class'), $this->Get('item_id'));
$sResult = $sAttName.' - ';
$sResult .= Dict::Format('Change:LinkSet:Modified', $sItemDesc);
$sResult .= ' : '.implode(', ', $aChanges);
}
return $sResult;
}
}
?>

View File

@@ -1,26 +1,27 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2015 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class cmdbObject
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -45,20 +46,26 @@ require_once('stimulus.class.inc.php');
require_once('valuesetdef.class.inc.php');
require_once('MyHelpers.class.inc.php');
require_once('expression.class.inc.php');
require_once('cmdbsource.class.inc.php');
require_once('sqlquery.class.inc.php');
require_once('oql/expression.class.inc.php');
require_once('oql/oqlquery.class.inc.php');
require_once('oql/oqlexception.class.inc.php');
require_once('oql/oql-parser.php');
require_once('oql/oql-lexer.php');
require_once('oql/oqlinterpreter.class.inc.php');
require_once('cmdbsource.class.inc.php');
require_once('sqlquery.class.inc.php');
require_once('sqlobjectquery.class.inc.php');
require_once('sqlunionquery.class.inc.php');
require_once('dbobject.class.php');
require_once('dbobjectsearch.class.php');
require_once('dbsearch.class.php');
require_once('dbobjectset.class.php');
require_once('backgroundprocess.inc.php');
require_once('asynctask.class.inc.php');
require_once('dbproperty.class.inc.php');
// db change tracking data model
require_once('cmdbchange.class.inc.php');
require_once('cmdbchangeop.class.inc.php');
@@ -72,6 +79,7 @@ require_once('cmdbchangeop.class.inc.php');
// Romain: temporary moved into application.inc.php (see explanations there)
//require_once('event.class.inc.php');
require_once('templatestring.class.inc.php');
require_once('csvparser.class.inc.php');
require_once('bulkchange.class.inc.php');
@@ -86,47 +94,174 @@ abstract class CMDBObject extends DBObject
protected $m_datUpdated;
// Note: this value is static, but that could be changed because it is sometimes a real issue (see update of interfaces / connected_to
protected static $m_oCurrChange = null;
private function RecordObjCreation(CMDBChange $oChange)
protected static $m_sInfo = null; // null => the information is built in a standard way
protected static $m_sOrigin = null; // null => the origin is 'interactive'
/**
* Specify another change (this is mainly for backward compatibility)
*/
public static function SetCurrentChange(CMDBChange $oChange)
{
self::$m_oCurrChange = $oChange;
}
//
// Todo: simplify the APIs and do not pass the current change as an argument anymore
// SetTrackInfo to be invoked in very few cases (UI.php, CSV import, Data synchro)
// SetCurrentChange is an alternative to SetTrackInfo (csv ?)
// GetCurrentChange to be called ONCE (!) by CMDBChangeOp::OnInsert ($this->Set('change', ..GetCurrentChange())
// GetCurrentChange to create a default change if not already done in the current context
//
/**
* Get a change record (create it if not existing)
*/
public static function GetCurrentChange($bAutoCreate = true)
{
if ($bAutoCreate && is_null(self::$m_oCurrChange))
{
self::CreateChange();
}
return self::$m_oCurrChange;
}
/**
* Override the additional information (defaulting to user name)
* A call to this verb should replace every occurence of
* $oMyChange = MetaModel::NewObject("CMDBChange");
* $oMyChange->Set("date", time());
* $oMyChange->Set("userinfo", 'this is done by ... for ...');
* $iChangeId = $oMyChange->DBInsert();
*/
public static function SetTrackInfo($sInfo)
{
self::$m_sInfo = $sInfo;
}
/**
* Provides information about the origin of the change
* @param $sOrigin String: one of: interactive, csv-interactive, csv-import.php, webservice-soap, webservice-rest, syncho-data-source, email-processing, custom-extension
*/
public static function SetTrackOrigin($sOrigin)
{
self::$m_sOrigin = $sOrigin;
}
/**
* Get the additional information (defaulting to user name)
*/
protected static function GetTrackInfo()
{
if (is_null(self::$m_sInfo))
{
return CMDBChange::GetCurrentUserName();
}
else
{
return self::$m_sInfo;
}
}
/**
* Get the 'origin' information (defaulting to 'interactive')
*/
protected static function GetTrackOrigin()
{
if (is_null(self::$m_sOrigin))
{
return 'interactive';
}
else
{
return self::$m_sOrigin;
}
}
/**
* Create a standard change record (done here 99% of the time, and nearly once per page)
*/
protected static function CreateChange()
{
self::$m_oCurrChange = MetaModel::NewObject("CMDBChange");
self::$m_oCurrChange->Set("date", time());
self::$m_oCurrChange->Set("userinfo", self::GetTrackInfo());
self::$m_oCurrChange->Set("origin", self::GetTrackOrigin());
self::$m_oCurrChange->DBInsert();
}
protected function RecordObjCreation()
{
// Delete any existing change tracking about the current object (IDs can be reused due to InnoDb bug; see TRAC #886)
//
// 1 - remove the deletion record(s)
// Note that objclass contain the ROOT class
$oFilter = new DBObjectSearch('CMDBChangeOpDelete');
$oFilter->AddCondition('objclass', MetaModel::GetRootClass(get_class($this)), '=');
$oFilter->AddCondition('objkey', $this->GetKey(), '=');
MetaModel::PurgeData($oFilter);
// 2 - any other change tracking information left prior to 2.0.3 (when the purge of the history has been implemented in RecordObjDeletion
// In that case, objclass is the final class of the object
$oFilter = new DBObjectSearch('CMDBChangeOp');
$oFilter->AddCondition('objclass', get_class($this), '=');
$oFilter->AddCondition('objkey', $this->GetKey(), '=');
MetaModel::PurgeData($oFilter);
parent::RecordObjCreation();
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpCreate");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$iId = $oMyChangeOp->DBInsertNoReload();
}
private function RecordObjDeletion(CMDBChange $oChange, $objkey)
protected function RecordObjDeletion($objkey)
{
$sRootClass = MetaModel::GetRootClass(get_class($this));
// Delete any existing change tracking about the current object
$oFilter = new DBObjectSearch('CMDBChangeOp');
$oFilter->AddCondition('objclass', get_class($this), '=');
$oFilter->AddCondition('objkey', $objkey, '=');
MetaModel::PurgeData($oFilter);
parent::RecordObjDeletion($objkey);
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpDelete");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objclass", MetaModel::GetRootClass(get_class($this)));
$oMyChangeOp->Set("objkey", $objkey);
$oMyChangeOp->Set("fclass", get_class($this));
$oMyChangeOp->Set("fname", substr($this->GetRawName(), 0, 255)); // Protect against very long friendly names
$iId = $oMyChangeOp->DBInsertNoReload();
}
private function RecordAttChanges(CMDBChange $oChange, array $aValues, array $aOrigValues)
protected function RecordAttChanges(array $aValues, array $aOrigValues)
{
parent::RecordAttChanges($aValues, $aOrigValues);
// $aValues is an array of $sAttCode => $value
//
foreach ($aValues as $sAttCode=> $value)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
if ($oAttDef->IsLinkSet()) continue; // #@# temporary
if ($oAttDef->IsExternalField()) continue;
if ($oAttDef->IsLinkSet()) continue;
if ($oAttDef->GetTrackingLevel() == ATTRIBUTE_TRACKING_NONE) continue;
if (array_key_exists($sAttCode, $aOrigValues))
{
$original = $aOrigValues[$sAttCode];
}
else
{
$original = null;
}
if ($oAttDef instanceOf AttributeOneWayPassword)
{
// One Way encrypted passwords' history is stored -one way- encrypted
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeOneWayPassword");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (array_key_exists($sAttCode, $aOrigValues))
{
$original = $aOrigValues[$sAttCode];
}
else
if (is_null($original))
{
$original = '';
}
@@ -137,81 +272,129 @@ abstract class CMDBObject extends DBObject
{
// Encrypted string history is stored encrypted
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeEncrypted");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (array_key_exists($sAttCode, $aOrigValues))
{
$original = $aOrigValues[$sAttCode];
}
else
if (is_null($original))
{
$original = '';
}
$oMyChangeOp->Set("prevdata", $original);
$oMyChangeOp->Set("prevstring", $original);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeBlob)
{
// Data blobs
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeBlob");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (array_key_exists($sAttCode, $aOrigValues))
{
$original = $aOrigValues[$sAttCode];
}
else
if (is_null($original))
{
$original = new ormDocument();
}
$oMyChangeOp->Set("prevdata", $original);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeStopWatch)
{
// Stop watches - record changes for sub items only (they are visible, the rest is not visible)
//
if (is_null($original))
{
$original = new OrmStopWatch();
}
foreach ($oAttDef->ListSubItems() as $sSubItemAttCode => $oSubItemAttDef)
{
$item_value = $oSubItemAttDef->GetValue($value);
$item_original = $oSubItemAttDef->GetValue($original);
if ($item_value != $item_original)
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sSubItemAttCode);
$oMyChangeOp->Set("oldvalue", $item_original);
$oMyChangeOp->Set("newvalue", $item_value);
$iId = $oMyChangeOp->DBInsertNoReload();
}
}
}
elseif ($oAttDef instanceOf AttributeCaseLog)
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCaseLog");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
$oMyChangeOp->Set("lastentry", $value->GetLatestEntryIndex());
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeLongText)
{
// Data blobs
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeLongText");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (!is_null($original) && ($original instanceof ormCaseLog))
{
$original = $original->GetText();
}
$oMyChangeOp->Set("prevdata", $original);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeText)
{
// Data blobs
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeText");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (array_key_exists($sAttCode, $aOrigValues))
if (!is_null($original) && ($original instanceof ormCaseLog))
{
$original = $aOrigValues[$sAttCode];
}
else
{
$original = null;
$original = $original->GetText();
}
$oMyChangeOp->Set("prevdata", $original);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeBoolean)
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
$oMyChangeOp->Set("oldvalue", $original ? 1 : 0);
$oMyChangeOp->Set("newvalue", $value ? 1 : 0);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeHierarchicalKey)
{
// Hierarchical keys
//
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
$oMyChangeOp->Set("oldvalue", $original);
$oMyChangeOp->Set("newvalue", $value[$sAttCode]);
$iId = $oMyChangeOp->DBInsertNoReload();
}
else
{
// Scalars
//
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (array_key_exists($sAttCode, $aOrigValues))
{
$sOriginalValue = $aOrigValues[$sAttCode];
}
else
{
$sOriginalValue = 'undefined';
}
$oMyChangeOp->Set("oldvalue", $sOriginalValue);
$oMyChangeOp->Set("oldvalue", $original);
$oMyChangeOp->Set("newvalue", $value);
$iId = $oMyChangeOp->DBInsertNoReload();
}
@@ -232,7 +415,7 @@ abstract class CMDBObject extends DBObject
// and we decided that it was too risky to activate it
// Anyhow, users willing to have a very strong security could set
// skip_strong_security = 0, in the config file
$bSkipStrongSecurity = utils::GetConfig()->Get('skip_strong_security');
$bSkipStrongSecurity = MetaModel::GetConfig()->Get('skip_strong_security');
}
if (!$bSkipStrongSecurity)
{
@@ -247,63 +430,51 @@ abstract class CMDBObject extends DBObject
}
public function DBInsert()
{
if(!is_object(self::$m_oCurrChange))
{
throw new CoreException("DBInsert() could not be used here, please use DBInsertTracked() instead");
}
return $this->DBInsertTracked_Internal();
}
public function DBInsertTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
self::$m_oCurrChange = $oChange;
$ret = $this->DBInsertTracked_Internal();
self::$m_oCurrChange = null;
return $ret;
}
public function DBInsertTrackedNoReload(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
self::$m_oCurrChange = $oChange;
$ret = $this->DBInsertTracked_Internal(true);
self::$m_oCurrChange = null;
return $ret;
}
/**
* To Be Obsoleted: DO NOT rely on an overload of this method since
* DBInsertTracked (resp. DBInsertTrackedNoReload) may call directly
* DBInsert (resp. DBInsertNoReload) in future versions of iTop.
* @param bool $bDoNotReload
* @return integer Identifier of the created object
*/
protected function DBInsertTracked_Internal($bDoNotReload = false)
{
if ($bDoNotReload)
{
$ret = parent::DBInsertNoReload();
$ret = $this->DBInsertNoReload();
}
else
{
$ret = parent::DBInsert();
$ret = $this->DBInsert();
}
$this->RecordObjCreation(self::$m_oCurrChange);
return $ret;
}
public function DBClone($newKey = null)
{
if(!self::$m_oCurrChange)
{
throw new CoreException("DBClone() could not be used here, please use DBCloneTracked() instead");
}
return $this->DBCloneTracked_Internal();
}
public function DBCloneTracked(CMDBChange $oChange, $newKey = null)
{
self::$m_oCurrChange = $oChange;
self::SetCurrentChange($oChange);
$this->DBCloneTracked_Internal($newKey);
self::$m_oCurrChange = null;
}
protected function DBCloneTracked_Internal($newKey = null)
@@ -311,127 +482,60 @@ abstract class CMDBObject extends DBObject
$newKey = parent::DBClone($newKey);
$oClone = MetaModel::GetObject(get_class($this), $newKey);
$oClone->RecordObjCreation(self::$m_oCurrChange);
return $newKey;
}
public function DBUpdate()
{
if(!self::$m_oCurrChange)
{
throw new CoreException("DBUpdate() could not be used here, please use DBUpdateTracked() instead");
}
return $this->DBUpdateTracked_internal();
}
public function DBUpdateTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
self::$m_oCurrChange = $oChange;
$this->DBUpdateTracked_Internal();
self::$m_oCurrChange = null;
}
protected function DBUpdateTracked_Internal()
{
// Copy the changes list before the update (the list should be reset afterwards)
$aChanges = $this->ListChanges();
if (count($aChanges) == 0)
{
//throw new CoreWarning("Attempting to update an unchanged object");
return;
}
// Save the original values (will be reset to the new values when the object get written to the DB)
$aOriginalValues = $this->m_aOrigValues;
$ret = parent::DBUpdate();
$this->RecordAttChanges(self::$m_oCurrChange, $aChanges, $aOriginalValues);
return $ret;
}
public function DBDelete()
public function DBUpdateTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
if(!self::$m_oCurrChange)
{
throw new CoreException("DBDelete() could not be used here, please use DBDeleteTracked() instead");
}
return $this->DBDeleteTracked_Internal();
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$this->DBUpdate();
}
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
public function DBDelete(&$oDeletionPlan = null)
{
return $this->DBDeleteTracked_Internal($oDeletionPlan);
}
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_DELETE);
self::$m_oCurrChange = $oChange;
$this->DBDeleteTracked_Internal();
self::$m_oCurrChange = null;
$this->DBDeleteTracked_Internal($oDeletionPlan);
}
protected function DBDeleteTracked_Internal()
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
{
$prevkey = $this->GetKey();
$ret = parent::DBDelete();
$this->RecordObjDeletion(self::$m_oCurrChange, $prevkey);
$ret = parent::DBDelete($oDeletionPlan);
return $ret;
}
public static function BulkDelete(DBObjectSearch $oFilter)
public static function BulkUpdate(DBSearch $oFilter, array $aValues)
{
if(!self::$m_oCurrChange)
{
throw new CoreException("BulkDelete() could not be used here, please use BulkDeleteTracked() instead");
}
return $this->BulkDeleteTracked_Internal($oFilter);
}
public static function BulkDeleteTracked(CMDBChange $oChange, DBObjectSearch $oFilter)
{
self::$m_oCurrChange = $oChange;
$this->BulkDeleteTracked_Internal($oFilter);
self::$m_oCurrChange = null;
}
protected static function BulkDeleteTracked_Internal(DBObjectSearch $oFilter)
{
throw new CoreWarning("Change tracking not tested for bulk operations");
// Get the list of objects to delete (and record data before deleting the DB records)
$oObjSet = new CMDBObjectSet($oFilter);
$aObjAndKeys = array(); // array of id=>object
while ($oItem = $oObjSet->Fetch())
{
$aObjAndKeys[$oItem->GetKey()] = $oItem;
}
$oObjSet->FreeResult();
// Delete in one single efficient query
$ret = parent::BulkDelete($oFilter);
// Record... in many queries !!!
foreach($aObjAndKeys as $prevkey=>$oItem)
{
$oItem->RecordObjDeletion(self::$m_oCurrChange, $prevkey);
}
return $ret;
}
public static function BulkUpdate(DBObjectSearch $oFilter, array $aValues)
{
if(!self::$m_oCurrChange)
{
throw new CoreException("BulkUpdate() could not be used here, please use BulkUpdateTracked() instead");
}
return $this->BulkUpdateTracked_Internal($oFilter, $aValues);
}
public static function BulkUpdateTracked(CMDBChange $oChange, DBObjectSearch $oFilter, array $aValues)
public static function BulkUpdateTracked(CMDBChange $oChange, DBSearch $oFilter, array $aValues)
{
self::$m_oCurrChange = $oChange;
self::SetCurrentChange($oChange);
$this->BulkUpdateTracked_Internal($oFilter, $aValues);
self::$m_oCurrChange = null;
}
protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues)
protected static function BulkUpdateTracked_Internal(DBSearch $oFilter, array $aValues)
{
// $aValues is an array of $sAttCode => $value
@@ -455,7 +559,7 @@ abstract class CMDBObject extends DBObject
while ($oItem = $oObjSet->Fetch())
{
$aChangedValues = $oItem->ListChangedValues($aValues);
$oItem->RecordAttChanges(self::$m_oCurrChange, $aChangedValues, $aOriginalValues[$oItem->GetKey()]);
$oItem->RecordAttChanges($aChangedValues, $aOriginalValues[$oItem->GetKey()]);
}
return $ret;
}
@@ -478,25 +582,23 @@ class CMDBObjectSet extends DBObjectSet
static public function FromScratch($sClass)
{
$oFilter = new CMDBSearchFilter($sClass);
$oRetSet = new CMDBObjectSet($oFilter); // THE ONLY DIFF IS HERE
// NOTE: THIS DOES NOT WORK IF m_bLoaded is private...
// BUT IT THAT CASE YOU DO NOT GET ANY ERROR !!!!!
$oFilter = new DBObjectSearch($sClass);
$oFilter->AddConditionExpression(new FalseExpression());
$oRetSet = new self($oFilter);
// NOTE: THIS DOES NOT WORK IF m_bLoaded is private in the base class (and you will not get any error message)
$oRetSet->m_bLoaded = true; // no DB load
return $oRetSet;
}
// create an object set ex nihilo
// input = array of objects
static public function FromArray($sClass, $aObjects)
{
$oFilter = new CMDBSearchFilter($sClass);
$oRetSet = new CMDBObjectSet($oFilter); // THE ONLY DIFF IS HERE
// NOTE: THIS DOES NOT WORK IF m_bLoaded is private...
// BUT IT THAT CASE YOU DO NOT GET ANY ERROR !!!!!
$oRetSet->m_bLoaded = true; // no DB load
$oRetSet->AddObjectArray($aObjects);
$oRetSet = self::FromScratch($sClass);
$oRetSet->AddObjectArray($aObjects, $sClass);
return $oRetSet;
}
static public function FromArrayAssoc($aClasses, $aObjects)
{
// In a perfect world, we should create a complete tree of DBObjectSearch,
@@ -504,7 +606,7 @@ class CMDBObjectSet extends DBObjectSet
// let's create one search definition
$sClass = reset($aClasses);
$sAlias = key($aClasses);
$oFilter = new CMDBSearchFilter($sClass, $sAlias);
$oFilter = new DBObjectSearch($sClass, $sAlias);
$oRetSet = new CMDBObjectSet($oFilter);
$oRetSet->m_bLoaded = true; // no DB load
@@ -516,16 +618,3 @@ class CMDBObjectSet extends DBObjectSet
return $oRetSet;
}
}
/**
* TODO: investigate how to get rid of this class that was made to workaround some language limitation... or a poor design!
*
* @package iTopORM
*/
class CMDBSearchFilter extends DBObjectSearch
{
// this is the public interface (?)
}
?>

View File

@@ -1,36 +1,46 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2015 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* DB Server abstraction
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('MyHelpers.class.inc.php');
require_once(APPROOT.'core/kpi.class.inc.php');
class MySQLException extends CoreException
{
public function __construct($sIssue, $aContext)
public function __construct($sIssue, $aContext, $oException = null)
{
$aContext['mysql_error'] = mysql_error();
$aContext['mysql_errno'] = mysql_errno();
if ($oException != null)
{
$aContext['mysql_error'] = $oException->getCode();
$aContext['mysql_errno'] = $oException->getMessage();
}
else
{
$aContext['mysql_error'] = CMDBSource::GetError();
$aContext['mysql_errno'] = CMDBSource::GetErrNo();
}
parent::__construct($sIssue, $aContext);
}
}
@@ -48,7 +58,7 @@ class CMDBSource
protected static $m_sDBUser;
protected static $m_sDBPwd;
protected static $m_sDBName;
protected static $m_resDBLink;
protected static $m_oMysqli;
public static function Init($sServer, $sUser, $sPwd, $sSource = '')
{
@@ -56,15 +66,41 @@ class CMDBSource
self::$m_sDBUser = $sUser;
self::$m_sDBPwd = $sPwd;
self::$m_sDBName = $sSource;
if (!self::$m_resDBLink = @mysql_pconnect($sServer, $sUser, $sPwd))
self::$m_oMysqli = null;
mysqli_report(MYSQLI_REPORT_STRICT); // *some* errors (like connection errors) will throw mysqli_sql_exception instead
// of generating warnings printed to the output but some other errors will still
// cause the query() method to return false !!!
try
{
throw new MySQLException('Could not connect to the DB server', array('host'=>$sServer, 'user'=>$sUser));
$aConnectInfo = explode(':', self::$m_sDBHost);
if (count($aConnectInfo) > 1)
{
// Override the default port
$sServer = $aConnectInfo[0];
$iPort = (int)$aConnectInfo[1];
self::$m_oMysqli = new mysqli($sServer, self::$m_sDBUser, self::$m_sDBPwd, '', $iPort);
}
else
{
self::$m_oMysqli = new mysqli(self::$m_sDBHost, self::$m_sDBUser, self::$m_sDBPwd);
}
}
catch(mysqli_sql_exception $e)
{
throw new MySQLException('Could not connect to the DB server', array('host'=>self::$m_sDBHost, 'user'=>self::$m_sDBUser), $e);
}
if (!empty($sSource))
{
if (!mysql_select_db($sSource, self::$m_resDBLink))
try
{
throw new MySQLException('Could not select DB', array('host'=>$sServer, 'user'=>$sUser, 'db_name'=>$sSource));
mysqli_report(MYSQLI_REPORT_STRICT); // Errors, in the next query, will throw mysqli_sql_exception
self::$m_oMysqli->query("USE `$sSource`");
}
catch(mysqli_sql_exception $e)
{
throw new MySQLException('Could not select DB', array('host'=>self::$m_sDBHost, 'user'=>self::$m_sDBUser, 'db_name'=>self::$m_sDBName), $e);
}
}
}
@@ -84,6 +120,17 @@ class CMDBSource
}
}
public static function SetTimezone($sTimezone = null)
{
// Note: requires the installation of MySQL special tables,
// otherwise, only 'SYSTEM' or "+10:00' may be specified which is NOT sufficient because of day light saving times
if (!is_null($sTimezone))
{
$sQuotedTimezone = self::Quote($sTimezone);
self::Query("SET time_zone = $sQuotedTimezone");
}
}
public static function ListDB()
{
$aDBs = self::QueryToCol('SHOW DATABASES', 'Database');
@@ -107,7 +154,7 @@ class CMDBSource
{
// In case we don't have rights to enumerate the databases
// Let's try to connect directly
return @mysql_select_db($sSource, self::$m_resDBLink);
return @((bool)self::$m_oMysqli->query("USE `$sSource`"));
}
}
@@ -120,7 +167,7 @@ class CMDBSource
public static function SelectDB($sSource)
{
if (!mysql_select_db($sSource, self::$m_resDBLink))
if (!((bool)self::$m_oMysqli->query("USE `$sSource`")))
{
throw new MySQLException('Could not select DB', array('db_name'=>$sSource));
}
@@ -160,6 +207,30 @@ class CMDBSource
return $res;
}
public static function GetErrNo()
{
if (self::$m_oMysqli->errno != 0)
{
return self::$m_oMysqli->errno;
}
else
{
return self::$m_oMysqli->connect_errno;
}
}
public static function GetError()
{
if (self::$m_oMysqli->error != '')
{
return self::$m_oMysqli->error;
}
else
{
return self::$m_oMysqli->connect_error;
}
}
public static function DBHost() {return self::$m_sDBHost;}
public static function DBUser() {return self::$m_sDBUser;}
public static function DBPwd() {return self::$m_sDBPwd;}
@@ -197,43 +268,50 @@ class CMDBSource
// Quote if not a number or a numeric string
if ($bAlways || is_string($value))
{
$value = $cQuoteStyle . mysql_real_escape_string($value, self::$m_resDBLink) . $cQuoteStyle;
$value = $cQuoteStyle . self::$m_oMysqli->real_escape_string($value) . $cQuoteStyle;
}
return $value;
}
public static function Query($sSQLQuery)
{
// Add info into the query as a comment, for easier error tracking
// disabled until we need it really!
//
//$aTraceInf['file'] = __FILE__;
// $sSQLQuery .= MyHelpers::MakeSQLComment($aTraceInf);
$oKPI = new ExecutionKPI();
$result = mysql_query($sSQLQuery, self::$m_resDBLink);
if (!$result)
try
{
$oResult = self::$m_oMysqli->query($sSQLQuery);
}
catch(mysqli_sql_exception $e)
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSQLQuery, $e));
}
$oKPI->ComputeStats('Query exec (mySQL)', $sSQLQuery);
if ($oResult === false)
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSQLQuery));
}
$oKPI->ComputeStats('Query exec (mySQL)', $sSQLQuery);
return $result;
return $oResult;
}
public static function GetNextInsertId($sTable)
{
$sSQL = "SHOW TABLE STATUS LIKE '$sTable'";
$result = self::Query($sSQL);
$aRow = mysql_fetch_assoc($result);
$oResult = self::Query($sSQL);
$aRow = $oResult->fetch_assoc();
$iNextInsertId = $aRow['Auto_increment'];
return $iNextInsertId;
}
public static function GetInsertId()
{
return mysql_insert_id(self::$m_resDBLink);
$iRes = self::$m_oMysqli->insert_id;
if (is_null($iRes))
{
return 0;
}
return $iRes;
}
public static function InsertInto($sSQLQuery)
{
if (self::Query($sSQLQuery))
@@ -243,19 +321,66 @@ class CMDBSource
return false;
}
public static function QueryToArray($sSql)
public static function DeleteFrom($sSQLQuery)
{
$aData = array();
$result = mysql_query($sSql, self::$m_resDBLink);
if (!$result)
self::Query($sSQLQuery);
}
public static function QueryToScalar($sSql)
{
$oKPI = new ExecutionKPI();
try
{
$oResult = self::$m_oMysqli->query($sSql);
}
catch(mysqli_sql_exception $e)
{
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
}
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
if ($oResult === false)
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
}
while ($aRow = mysql_fetch_array($result, MYSQL_BOTH))
if ($aRow = $oResult->fetch_array(MYSQLI_BOTH))
{
$res = $aRow[0];
}
else
{
$oResult->free();
throw new MySQLException('Found no result for query', array('query' => $sSql));
}
$oResult->free();
return $res;
}
public static function QueryToArray($sSql)
{
$aData = array();
$oKPI = new ExecutionKPI();
try
{
$oResult = self::$m_oMysqli->query($sSql);
}
catch(mysqli_sql_exception $e)
{
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
}
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
if ($oResult === false)
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
}
while ($aRow = $oResult->fetch_array(MYSQLI_BOTH))
{
$aData[] = $aRow;
}
mysql_free_result($result);
$oResult->free();
return $aData;
}
@@ -273,71 +398,94 @@ class CMDBSource
public static function ExplainQuery($sSql)
{
$aData = array();
$result = mysql_query("EXPLAIN $sSql", self::$m_resDBLink);
if (!$result)
try
{
$oResult = self::$m_oMysqli->query($sSql);
}
catch(mysqli_sql_exception $e)
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
}
if ($oResult === false)
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
}
$aNames = self::GetColumns($result);
$aNames = self::GetColumns($oResult);
$aData[] = $aNames;
while ($aRow = mysql_fetch_array($result, MYSQL_ASSOC))
while ($aRow = $oResult->fetch_array(MYSQLI_ASSOC))
{
$aData[] = $aRow;
}
mysql_free_result($result);
$oResult->free();
return $aData;
}
public static function TestQuery($sSql)
{
$result = mysql_query("EXPLAIN $sSql", self::$m_resDBLink);
if (!$result)
try
{
return mysql_error();
$oResult = self::$m_oMysqli->query($sSql);
}
catch(mysqli_sql_exception $e)
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
}
if ($oResult === false)
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
}
mysql_free_result($result);
if (is_object($oResult))
{
$oResult->free();
}
return '';
}
public static function NbRows($result)
public static function NbRows($oResult)
{
return mysql_num_rows($result);
return $oResult->num_rows;
}
public static function FetchArray($result)
public static function AffectedRows()
{
return mysql_fetch_array($result, MYSQL_ASSOC);
return self::$m_oMysqli->affected_rows;
}
public static function GetColumns($result)
public static function FetchArray($oResult)
{
return $oResult->fetch_array(MYSQLI_ASSOC);
}
public static function GetColumns($oResult)
{
$aNames = array();
for ($i = 0; $i < mysql_num_fields($result) ; $i++)
for ($i = 0; $i < (($___mysqli_tmp = $oResult->field_count) ? $___mysqli_tmp : 0) ; $i++)
{
$meta = mysql_fetch_field($result, $i);
if (!$meta)
{
$meta = $oResult->fetch_field_direct($i);
if (!$meta)
{
throw new MySQLException('mysql_fetch_field: No information available', array('query'=>$sSql, 'i'=>$i));
}
else
{
$aNames[] = $meta->name;
}
}
else
{
$aNames[] = $meta->name;
}
}
return $aNames;
}
public static function Seek($result, $iRow)
public static function Seek($oResult, $iRow)
{
return mysql_data_seek($result, $iRow);
return $oResult->data_seek($iRow);
}
public static function FreeResult($result)
public static function FreeResult($oResult)
{
return mysql_free_result($result);
$oResult->free(); /* returns void */
return true;
}
public static function IsTable($sTable)
@@ -393,14 +541,54 @@ class CMDBSource
return ($aFieldData["Type"]);
}
public static function HasIndex($sTable, $sField)
public static function GetFieldSpec($sTable, $sField)
{
$aTableInfo = self::GetTableInfo($sTable);
if (empty($aTableInfo)) return false;
if (!array_key_exists($sField, $aTableInfo["Fields"])) return false;
$aFieldData = $aTableInfo["Fields"][$sField];
// $aFieldData could be 'PRI' for the primary key, or 'MUL', or ?
return (strlen($aFieldData["Key"]) > 0);
$sRet = $aFieldData["Type"];
if ($aFieldData["Null"] == 'NO')
{
$sRet .= ' NOT NULL';
}
if (is_numeric($aFieldData["Default"]))
{
if (strtolower(substr($aFieldData["Type"], 0, 5)) == 'enum(')
{
// Force quotes to match the column declaration statement
$sRet .= ' DEFAULT '.self::Quote($aFieldData["Default"], true);
}
else
{
$default = $aFieldData["Default"] + 0; // Coerce to a numeric variable
$sRet .= ' DEFAULT '.self::Quote($default);
}
}
elseif (is_string($aFieldData["Default"]) == 'string')
{
$sRet .= ' DEFAULT '.self::Quote($aFieldData["Default"]);
}
return $sRet;
}
public static function HasIndex($sTable, $sIndexId, $aFields = null)
{
$aTableInfo = self::GetTableInfo($sTable);
if (empty($aTableInfo)) return false;
if (!array_key_exists($sIndexId, $aTableInfo['Indexes'])) return false;
if ($aFields == null)
{
// Just searching for the name
return true;
}
// Compare the columns
$sSearchedIndex = implode(',', $aFields);
$sExistingIndex = implode(',', $aTableInfo['Indexes'][$sIndexId]);
return ($sSearchedIndex == $sExistingIndex);
}
// Returns an array of (fieldname => array of field info)
@@ -450,6 +638,17 @@ class CMDBSource
// Table does not exist
self::$m_aTablesInfo[strtolower($sTableName)] = null;
}
if (!is_null(self::$m_aTablesInfo[strtolower($sTableName)]))
{
$aIndexes = self::QueryToArray("SHOW INDEXES FROM `$sTableName`");
$aMyIndexes = array();
foreach ($aIndexes as $aIndexColumn)
{
$aMyIndexes[$aIndexColumn['Key_name']][$aIndexColumn['Seq_in_index']-1] = $aIndexColumn['Column_name'];
}
self::$m_aTablesInfo[strtolower($sTableName)]["Indexes"] = $aMyIndexes;
}
}
//public static function EnumTables()
//{
@@ -475,18 +674,25 @@ class CMDBSource
public static function DumpTable($sTable)
{
$sSql = "SELECT * FROM `$sTable`";
$result = mysql_query($sSql, self::$m_resDBLink);
if (!$result)
try
{
$oResult = self::$m_oMysqli->query($sSql);
}
catch(mysqli_sql_exception $e)
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql), $e);
}
if ($oResult === false)
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
}
$aRows = array();
while ($aRow = mysql_fetch_array($result, MYSQL_ASSOC))
while ($aRow = $oResult->fetch_array(MYSQLI_ASSOC))
{
$aRows[] = $aRow;
}
mysql_free_result($result);
$oResult->free();
return $aRows;
}
@@ -516,7 +722,7 @@ class CMDBSource
{
try
{
$result = self::Query('SHOW GRANTS'); // [ FOR CURRENT_USER()]
$oResult = self::Query('SHOW GRANTS'); // [ FOR CURRENT_USER()]
}
catch(MySQLException $e)
{
@@ -524,16 +730,58 @@ class CMDBSource
}
$aRes = array();
while ($aRow = mysql_fetch_array($result, MYSQL_NUM))
while ($aRow = $oResult->fetch_array(MYSQLI_NUM))
{
// so far, only one column...
$aRes[] = implode('/', $aRow);
}
mysql_free_result($result);
$oResult->free();
// so far, only one line...
return implode(', ', $aRes);
}
}
/**
* Determine the slave status of the server
* @return bool true if the server is slave
*/
public static function IsSlaveServer()
{
try
{
$oResult = self::Query('SHOW SLAVE STATUS');
}
catch(MySQLException $e)
{
throw new CoreException("Current user not allowed to check the status", array('mysql_error' => $e->getMessage()));
}
?>
if ($oResult->num_rows == 0)
{
return false;
}
// Returns one single row anytime
$aRow = $oResult->fetch_array(MYSQLI_ASSOC);
$oResult->free();
if (!isset($aRow['Slave_IO_Running']))
{
return false;
}
if (!isset($aRow['Slave_SQL_Running']))
{
return false;
}
// If at least one slave thread is running, then we consider that the slave is enabled
if ($aRow['Slave_IO_Running'] == 'Yes')
{
return true;
}
if ($aRow['Slave_SQL_Running'] == 'Yes')
{
return true;
}
return false;
}
}

139
core/computing.inc.php Normal file
View File

@@ -0,0 +1,139 @@
<?php
// Copyright (C) 2010-2014 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/>
/**
* Any extension to compute things like a stop watch deadline or working hours
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* Metric computing for stop watches
*/
interface iMetricComputer
{
public static function GetDescription();
public function ComputeMetric($oObject);
}
/**
* Working time computing for stop watches
*/
interface iWorkingTimeComputer
{
public static function GetDescription();
/**
* Get the date/time corresponding to a given delay in the future from the present
* considering only the valid (open) hours for a specified object
* @param $oObject DBObject The object for which to compute the deadline
* @param $iDuration integer The duration (in seconds) in the future
* @param $oStartDate DateTime The starting point for the computation
* @return DateTime The date/time for the deadline
*/
public function GetDeadline($oObject, $iDuration, DateTime $oStartDate);
/**
* Get duration (considering only open hours) elapsed bewteen two given DateTimes
* @param $oObject DBObject The object for which to compute the duration
* @param $oStartDate DateTime The starting point for the computation (default = now)
* @param $oEndDate DateTime The ending point for the computation (default = now)
* @return integer The duration (number of seconds) of open hours elapsed between the two dates
*/
public function GetOpenDuration($oObject, DateTime $oStartDate, DateTime $oEndDate);
}
/**
* Default implementation oof deadline computing: NO deadline
*/
class DefaultMetricComputer implements iMetricComputer
{
public static function GetDescription()
{
return "Null";
}
public function ComputeMetric($oObject)
{
return null;
}
}
/**
* Default implementation of working time computing
*/
class DefaultWorkingTimeComputer implements iWorkingTimeComputer
{
public static function GetDescription()
{
return "24x7, no holidays";
}
/**
* Get the date/time corresponding to a given delay in the future from the present
* considering only the valid (open) hours for a specified object
* @param $oObject DBObject The object for which to compute the deadline
* @param $iDuration integer The duration (in seconds) in the future
* @param $oStartDate DateTime The starting point for the computation
* @return DateTime The date/time for the deadline
*/
public function GetDeadline($oObject, $iDuration, DateTime $oStartDate)
{
if (class_exists('WorkingTimeRecorder'))
{
WorkingTimeRecorder::Trace(WorkingTimeRecorder::TRACE_DEBUG, __class__.'::'.__function__);
}
//echo "GetDeadline - default: ".$oStartDate->format('Y-m-d H:i:s')." + $iDuration<br/>\n";
// Default implementation: 24x7, no holidays: to compute the deadline, just add
// the specified duration to the given date/time
$oResult = clone $oStartDate;
$oResult->modify('+'.$iDuration.' seconds');
if (class_exists('WorkingTimeRecorder'))
{
WorkingTimeRecorder::SetValues($oStartDate->format('U'), $oResult->format('U'), $iDuration, WorkingTimeRecorder::COMPUTED_END);
}
return $oResult;
}
/**
* Get duration (considering only open hours) elapsed bewteen two given DateTimes
* @param $oObject DBObject The object for which to compute the duration
* @param $oStartDate DateTime The starting point for the computation (default = now)
* @param $oEndDate DateTime The ending point for the computation (default = now)
* @return integer The duration (number of seconds) of open hours elapsed between the two dates
*/
public function GetOpenDuration($oObject, DateTime $oStartDate, DateTime $oEndDate)
{
if (class_exists('WorkingTimeRecorder'))
{
WorkingTimeRecorder::Trace(WorkingTimeRecorder::TRACE_DEBUG, __class__.'::'.__function__);
}
//echo "GetOpenDuration - default: ".$oStartDate->format('Y-m-d H:i:s')." to ".$oEndDate->format('Y-m-d H:i:s')."<br/>\n";
$iDuration = abs($oEndDate->format('U') - $oStartDate->format('U'));
if (class_exists('WorkingTimeRecorder'))
{
WorkingTimeRecorder::SetValues($oStartDate->format('U'), $oEndDate->format('U'), $iDuration, WorkingTimeRecorder::COMPUTED_DURATION);
}
return $iDuration;
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,27 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Exception management
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/

View File

@@ -0,0 +1,310 @@
<?php
// Copyright (C) 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/>
/**
* Bulk export: CSV export
*
* @copyright Copyright (C) 2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class CSVBulkExport extends TabularBulkExport
{
public function DisplayUsage(Page $oP)
{
$oP->p(" * csv format options:");
$oP->p(" *\tfields: (mandatory) the comma separated list of field codes to export (e.g: name,org_id,service_name...).");
$oP->p(" *\tseparator: (optional) character to be used as the separator (default is ',').");
$oP->p(" *\tcharset: (optional) character set for encoding the result (default is 'UTF-8').");
$oP->p(" *\ttext-qualifier: (optional) character to be used around text strings (default is '\"').");
$oP->p(" *\tno_localize: set to 1 to retrieve non-localized values (for instance for ENUM values). Default is 0 (= localized values)");
}
public function ReadParameters()
{
parent::ReadParameters();
$this->aStatusInfo['separator'] = utils::ReadParam('separator', ',', true, 'raw_data');
if (strtolower($this->aStatusInfo['separator']) == 'tab')
{
$this->aStatusInfo['separator'] = "\t";
}
else if (strtolower($this->aStatusInfo['separator']) == 'other')
{
$this->aStatusInfo['separator'] = utils::ReadParam('other-separator', ',', true, 'raw_data');
}
$this->aStatusInfo['text_qualifier'] = utils::ReadParam('text-qualifier', '"', true, 'raw_data');
if (strtolower($this->aStatusInfo['text_qualifier']) == 'other')
{
$this->aStatusInfo['text_qualifier'] = utils::ReadParam('other-text-qualifier', '"', true, 'raw_data');
}
$this->aStatusInfo['charset'] = strtoupper(utils::ReadParam('charset', 'UTF-8', true, 'raw_data'));
}
protected function SuggestField($sClass, $sAttCode)
{
switch($sAttCode)
{
case 'id': // replace 'id' by 'friendlyname'
$sAttCode = 'friendlyname';
break;
default:
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef instanceof AttributeExternalKey)
{
$sAttCode .= '_friendlyname';
}
}
return parent::SuggestField($sClass, $sAttCode);
}
public function EnumFormParts()
{
return array_merge(parent::EnumFormParts(), array('csv_options' => array('separator', 'charset', 'text-qualifier', 'no_localize') ,'interactive_fields_csv' => array('interactive_fields_csv')));
}
public function DisplayFormPart(WebPage $oP, $sPartId)
{
switch($sPartId)
{
case 'interactive_fields_csv':
$this->GetInteractiveFieldsWidget($oP, 'interactive_fields_csv');
break;
case 'csv_options':
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:CSVOptions').'</legend>');
$oP->add('<table class="export_parameters"><tr><td style="vertical-align:top">');
$oP->add('<h3>'.Dict::S('UI:CSVImport:SeparatorCharacter').'</h3>');
$sRawSeparator = utils::ReadParam('separator', ',', true, 'raw_data');
$aSep = array(
';' => Dict::S('UI:CSVImport:SeparatorSemicolon+'),
',' => Dict::S('UI:CSVImport:SeparatorComma+'),
'tab' => Dict::S('UI:CSVImport:SeparatorTab+'),
);
$sOtherSeparator = '';
if (!array_key_exists($sRawSeparator, $aSep))
{
$sOtherSeparator = $sRawSeparator;
$sRawSeparator = 'other';
}
$aSep['other'] = Dict::S('UI:CSVImport:SeparatorOther').' <input type="text" size="3" name="other-separator" value="'.htmlentities($sOtherSeparator, ENT_QUOTES, 'UTF-8').'"/>';
foreach($aSep as $sVal => $sLabel)
{
$sChecked = ($sVal == $sRawSeparator) ? 'checked' : '';
$oP->add('<input type="radio" name="separator" value="'.htmlentities($sVal, ENT_QUOTES, 'UTF-8').'" '.$sChecked.'/>&nbsp;'.$sLabel.'<br/>');
}
$oP->add('</td><td style="vertical-align:top">');
$oP->add('<h3>'.Dict::S('UI:CSVImport:TextQualifierCharacter').'</h3>');
$sRawQualifier = utils::ReadParam('text-qualifier', '"', true, 'raw_data');
$aQualifiers = array(
'"' => Dict::S('UI:CSVImport:QualifierDoubleQuote+'),
'\'' => Dict::S('UI:CSVImport:QualifierSimpleQuote+'),
);
$sOtherQualifier = '';
if (!array_key_exists($sRawQualifier, $aQualifiers))
{
$sOtherQualifier = $sRawQualifier;
$sRawQualifier = 'other';
}
$aQualifiers['other'] = Dict::S('UI:CSVImport:QualifierOther').' <input type="text" size="3" name="other-text-qualifier" value="'.htmlentities($sOtherQualifier, ENT_QUOTES, 'UTF-8').'"/>';
foreach($aQualifiers as $sVal => $sLabel)
{
$sChecked = ($sVal == $sRawQualifier) ? 'checked' : '';
$oP->add('<input type="radio" name="text-qualifier" value="'.htmlentities($sVal, ENT_QUOTES, 'UTF-8').'" '.$sChecked.'/>&nbsp;'.$sLabel.'<br/>');
}
$sChecked = (utils::ReadParam('no_localize', 0) == 1) ? ' checked ' : '';
$oP->add('</td><td style="vertical-align:top">');
$oP->add('<h3>'.Dict::S('Core:BulkExport:CSVLocalization').'</h3>');
$oP->add('<input type="checkbox" id="csv_no_localize" name="no_localize" value="1"'.$sChecked.'><label for="csv_no_localize"> '.Dict::S('Core:BulkExport:OptionNoLocalize').'</label>');
$oP->add('<br/>');
$oP->add('<br/>');
$oP->add(Dict::S('UI:CSVImport:Encoding').': <select name="charset" style="font-family:Arial,Helvetica,Sans-serif">'); // IE 8 has some troubles if the font is different
$aPossibleEncodings = utils::GetPossibleEncodings(MetaModel::GetConfig()->GetCSVImportCharsets());
$sDefaultEncoding = MetaModel::GetConfig()->Get('csv_file_default_charset');
foreach($aPossibleEncodings as $sIconvCode => $sDisplayName )
{
$sSelected = '';
if ($sIconvCode == $sDefaultEncoding)
{
$sSelected = ' selected';
}
$oP->add('<option value="'.$sIconvCode.'"'.$sSelected.'>'.$sDisplayName.'</option>');
}
$oP->add('</select>');
$oP->add('</td></tr></table>');
$oP->add('</fieldset>');
break;
default:
return parent:: DisplayFormPart($oP, $sPartId);
}
}
protected function GetSampleData($oObj, $sAttCode)
{
return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
}
protected function GetValue($oObj, $sAttCode)
{
switch($sAttCode)
{
case 'id':
$sRet = $oObj->GetKey();
break;
default:
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
}
return $sRet;
}
public function GetHeader()
{
$oSet = new DBObjectSet($this->oSearch);
$this->aStatusInfo['status'] = 'running';
$this->aStatusInfo['position'] = 0;
$this->aStatusInfo['total'] = $oSet->Count();
$aData = array();
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
$aData[] = $aFieldSpec['sColLabel'];
}
$sFrom = array("\r\n", $this->aStatusInfo['text_qualifier']);
$sTo = array("\n", $this->aStatusInfo['text_qualifier'].$this->aStatusInfo['text_qualifier']);
foreach($aData as $idx => $sData)
{
// Escape and encode (if needed) the headers
$sEscaped = str_replace($sFrom, $sTo, (string)$sData);
$aData[$idx] = $this->aStatusInfo['text_qualifier'].$sEscaped.$this->aStatusInfo['text_qualifier'];
if ($this->aStatusInfo['charset'] != 'UTF-8')
{
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
// and thus to convert field by field and not the whole row or file at once (see ticket #991)
$aData[$idx] = iconv('UTF-8', $this->aStatusInfo['charset'].'//IGNORE//TRANSLIT', $aData[$idx]);
}
}
$sData = implode($this->aStatusInfo['separator'], $aData)."\n";
return $sData;
}
public function GetNextChunk(&$aStatus)
{
$sRetCode = 'run';
$iPercentage = 0;
$oSet = new DBObjectSet($this->oSearch);
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
$this->OptimizeColumnLoad($oSet);
$iCount = 0;
$sData = '';
$iPreviousTimeLimit = ini_get('max_execution_time');
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
while($aRow = $oSet->FetchAssoc())
{
set_time_limit($iLoopTimeLimit);
$aData = array();
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
$sAlias = $aFieldSpec['sAlias'];
$sAttCode = $aFieldSpec['sAttCode'];
$sField = '';
$oObj = $aRow[$sAlias];
if ($oObj != null)
{
switch($sAttCode)
{
case 'id':
$sField = $oObj->GetKey();
break;
default:
$sField = $oObj->GetAsCSV($sAttCode, $this->aStatusInfo['separator'], $this->aStatusInfo['text_qualifier'], $this->bLocalizeOutput);
}
}
if ($this->aStatusInfo['charset'] != 'UTF-8')
{
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
// and thus to convert field by field and not the whole row or file at once (see ticket #991)
$aData[] = iconv('UTF-8', $this->aStatusInfo['charset'].'//IGNORE//TRANSLIT', $sField);
}
else
{
$aData[] = $sField;
}
}
$sData .= implode($this->aStatusInfo['separator'], $aData)."\n";
$iCount++;
}
set_time_limit($iPreviousTimeLimit);
$this->aStatusInfo['position'] += $this->iChunkSize;
if ($this->aStatusInfo['total'] == 0)
{
$iPercentage = 100;
}
else
{
$iPercentage = floor(min(100.0, 100.0*$this->aStatusInfo['position']/$this->aStatusInfo['total']));
}
if ($iCount < $this->iChunkSize)
{
$sRetCode = 'done';
}
$aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage);
return $sData;
}
public function GetSupportedFormats()
{
return array('csv' => Dict::S('Core:BulkExport:CSVFormat'));
}
public function GetMimeType()
{
return 'text/csv';
}
public function GetFileExtension()
{
return 'csv';
}
public function GetCharacterSet()
{
return $this->aStatusInfo['charset'];
}
}

View File

@@ -1,26 +1,27 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* CSV parser
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -40,6 +41,8 @@ define('evTEXTQUAL', 3); // used for escaping as well
define('evOTHERCHAR', 4);
define('evEND', 5);
define('NULL_VALUE', '<NULL>');
/**
* CSVParser
@@ -82,6 +85,10 @@ class CSVParser
{
$sCell = $this->m_sCurrCell;
}
if ($sCell == NULL_VALUE)
{
$sCell = null;
}
if (!is_null($aFieldMap))
{
@@ -178,6 +185,7 @@ class CSVParser
{
if ($i == $iDataLength)
{
$c = null;
$iEvent = evEND;
}
else

View File

@@ -1,27 +1,28 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2015 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* data generator
* helps the consultants in creating dummy data sets, for various test purposes (validation, usability, scalability)
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
@@ -266,7 +267,7 @@ class cmdbDataGenerator
function GenerateKey($sClass, $aFilterCriteria)
{
$retKey = null;
$oFilter = new CMDBSearchFilter($sClass);
$oFilter = new DBObjectSearch($sClass);
foreach($aFilterCriteria as $sFilterCode => $filterValue)
{
$oFilter->AddCondition($sFilterCode, $filterValue, '=');
@@ -345,7 +346,7 @@ class cmdbDataGenerator
*/
protected function OrganizationExists($sCode)
{
$oFilter = new CMDBSearchFilter('bizOrganization');
$oFilter = new DBObjectSearch('Organization');
$oFilter->AddCondition('code', $sCode, '=');
$oSet = new CMDBObjectSet($oFilter);
return ($oSet->Count() > 0);
@@ -360,7 +361,7 @@ class cmdbDataGenerator
protected function GetOrganization($sId)
{
$oOrg = null;
$oFilter = new CMDBSearchFilter('bizOrganization');
$oFilter = new DBObjectSearch('Organization');
$oFilter->AddCondition('id', $sId, '=');
$oSet = new CMDBObjectSet($oFilter);
if ($oSet->Count() > 0)

3
core/datamodel.core.xml Normal file
View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design>
</itop_design>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,160 @@
<?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/>
/**
* Database properties - manage database instances in a complex installation
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* A database property
*
* @package iTopORM
*/
class DBProperty extends DBObject
{
public static function Init()
{
$aParams = array
(
"category" => "cloud",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_db_properties",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("value", array("allowed_values"=>null, "sql"=>"value", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("change_date", array("allowed_values"=>null, "sql"=>"change_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("change_comment", array("allowed_values"=>null, "sql"=>"change_comment", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
}
/**
* Helper to check wether the table has been created into the DB
* (this table did not exist in 1.0.1 and older versions)
*/
public static function IsInstalled()
{
$sTable = MetaModel::DBGetTable(__CLASS__);
if (CMDBSource::IsTable($sTable))
{
return true;
}
else
{
return false;
}
return false;
}
public static function SetProperty($sName, $sValue, $sComment = '', $sDescription = null)
{
try
{
$oSearch = DBObjectSearch::FromOQL('SELECT DBProperty WHERE name = :name');
$oSet = new DBObjectSet($oSearch, array(), array('name' => $sName));
if ($oSet->Count() == 0)
{
$oProp = new DBProperty();
$oProp->Set('name', $sName);
$oProp->Set('description', $sDescription);
$oProp->Set('value', $sValue);
$oProp->Set('change_date', time());
$oProp->Set('change_comment', $sComment);
$oProp->DBInsert();
}
elseif ($oSet->Count() == 1)
{
$oProp = $oSet->fetch();
if (!is_null($sDescription))
{
$oProp->Set('description', $sDescription);
}
$oProp->Set('value', $sValue);
$oProp->Set('change_date', time());
$oProp->Set('change_comment', $sComment);
$oProp->DBUpdate();
}
else
{
// Houston...
throw new CoreException('duplicate db property');
}
}
catch (MySQLException $e)
{
// This might be because the table could not be found,
// let's check it and discard silently if this is really the case
if (self::IsInstalled())
{
throw $e;
}
IssueLog::Error('Attempting to write a DBProperty while the module has not been installed');
}
}
public static function GetProperty($sName, $default = null)
{
try
{
$oSearch = DBObjectSearch::FromOQL('SELECT DBProperty WHERE name = :name');
$oSet = new DBObjectSet($oSearch, array(), array('name' => $sName));
$iCount = $oSet->Count();
if ($iCount == 0)
{
//throw new CoreException('unknown db property', array('name' => $sName));
$sValue = $default;
}
elseif ($iCount == 1)
{
$oProp = $oSet->fetch();
$sValue = $oProp->Get('value');
}
else
{
// $iCount > 1
// Houston...
throw new CoreException('duplicate db property', array('name' => $sName, 'count' => $iCount));
}
}
catch (MySQLException $e)
{
// This might be because the table could not be found,
// let's check it and discard silently if this is really the case
if (self::IsInstalled())
{
throw $e;
}
$sValue = $default;
}
return $sValue;
}
}
?>

740
core/dbsearch.class.php Normal file
View File

@@ -0,0 +1,740 @@
<?php
// Copyright (C) 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/>
require_once('dbobjectsearch.class.php');
require_once('dbunionsearch.class.php');
/**
* An object search
*
* Note: in the ancient times of iTop, a search was named after DBObjectSearch.
* When the UNION has been introduced, it has been decided to:
* - declare a hierarchy of search classes, with two leafs :
* - one class to cope with a single query (A JOIN B... WHERE...)
* - and the other to cope with several queries (query1 UNION query2)
* - in order to preserve forward/backward compatibility of the existing modules
* - keep the name of DBObjectSearch even if it a little bit confusing
* - do not provide a type-hint for function parameters defined in the modules
* - leave the statements DBObjectSearch::FromOQL in the modules, though DBSearch is more relevant
*
* @copyright Copyright (C) 2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class DBSearch
{
protected $m_bDataFiltered = 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()
{
}
/**
* Perform a deep clone (as opposed to "clone" which does copy a reference to the underlying objects)
**/
public function DeepClone()
{
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;}
public function IsDataFiltered() {return $this->m_bDataFiltered; }
public function SetDataFiltered() {$this->m_bDataFiltered = true;}
public function SetModifierProperty($sPluginClass, $sProperty, $value)
{
$this->m_aModifierProperties[$sPluginClass][$sProperty] = $value;
}
public function GetModifierProperties($sPluginClass)
{
if (array_key_exists($sPluginClass, $this->m_aModifierProperties))
{
return $this->m_aModifierProperties[$sPluginClass];
}
else
{
return array();
}
}
abstract public function GetClassName($sAlias);
abstract public function GetClass();
abstract public function GetClassAlias();
/**
* Change the class (only subclasses are supported as of now, because the conditions must fit the new class)
* Defaults to the first selected class (most of the time it is also the first joined class
*/
abstract public function ChangeClass($sNewClass, $sAlias = null);
abstract public function GetSelectedClasses();
abstract public function IsAny();
public function Describe(){return 'deprecated - use ToOQL() instead';}
public function DescribeConditionPointTo($sExtKeyAttCode, $aPointingTo){return 'deprecated - use ToOQL() instead';}
public function DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode){return 'deprecated - use ToOQL() instead';}
public function DescribeConditionRelTo($aRelInfo){return 'deprecated - use ToOQL() instead';}
public function DescribeConditions(){return 'deprecated - use ToOQL() instead';}
public function __DescribeHTML(){return 'deprecated - use ToOQL() instead';}
abstract public function ResetCondition();
abstract public function MergeConditionExpression($oExpression);
abstract public function AddConditionExpression($oExpression);
abstract public function AddNameCondition($sName);
abstract public function AddCondition($sFilterCode, $value, $sOpCode = null);
/**
* Specify a condition on external keys or link sets
* @param sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
* Example: infra_list->ci_id->location_id->country
* @param value The value to match (can be an array => IN(val1, val2...)
* @return void
*/
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);
abstract public function Intersect(DBSearch $oFilter);
abstract public function SetInternalParams($aParams);
abstract public function GetInternalParams();
abstract public function GetQueryParams();
abstract public function ListConstantFields();
/**
* Turn the parameters (:xxx) into scalar values in order to easily
* serialize a search
*/
abstract public function ApplyParameters($aArgs);
public function serialize($bDevelopParams = false, $aContextParams = null)
{
$sOql = $this->ToOql($bDevelopParams, $aContextParams);
return base64_encode(serialize(array($sOql, $this->GetInternalParams(), $this->m_aModifierProperties)));
}
static public function unserialize($sValue)
{
$aData = unserialize(base64_decode($sValue));
$sOql = $aData[0];
$aParams = $aData[1];
// We've tried to use gzcompress/gzuncompress, but for some specific queries
// it was not working at all (See Trac #193)
// gzuncompress was issuing a warning "data error" and the return object was null
$oRetFilter = self::FromOQL($sOql, $aParams);
$oRetFilter->m_aModifierProperties = $aData[2];
return $oRetFilter;
}
abstract public function ToOQL($bDevelopParams = false, $aContextParams = null);
static protected $m_aOQLQueries = array();
// Do not filter out depending on user rights
// In particular when we are currently in the process of evaluating the user rights...
static public function FromOQL_AllData($sQuery, $aParams = null)
{
$oRes = self::FromOQL($sQuery, $aParams);
$oRes->AllowAllData();
return $oRes;
}
static public function FromOQL($sQuery, $aParams = null)
{
if (empty($sQuery)) return null;
// Query caching
$sQueryId = md5($sQuery);
$bOQLCacheEnabled = true;
if ($bOQLCacheEnabled)
{
if (array_key_exists($sQueryId, self::$m_aOQLQueries))
{
// hit!
$oResultFilter = self::$m_aOQLQueries[$sQueryId]->DeepClone();
}
elseif (self::$m_bUseAPCCache)
{
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
//
$sAPCCacheId = 'itop-'.MetaModel::GetEnvironmentId().'-dbsearch-cache-'.$sQueryId;
$oKPI = new ExecutionKPI();
$result = apc_fetch($sAPCCacheId);
$oKPI->ComputeStats('Search APC (fetch)', $sQuery);
if (is_object($result))
{
$oResultFilter = $result;
self::$m_aOQLQueries[$sQueryId] = $oResultFilter->DeepClone();
}
}
}
if (!isset($oResultFilter))
{
$oKPI = new ExecutionKPI();
$oOql = new OqlInterpreter($sQuery);
$oOqlQuery = $oOql->ParseQuery();
$oMetaModel = new ModelReflectionRuntime();
$oOqlQuery->Check($oMetaModel, $sQuery); // Exceptions thrown in case of issue
$oResultFilter = $oOqlQuery->ToDBSearch($sQuery);
$oKPI->ComputeStats('Parse OQL', $sQuery);
if ($bOQLCacheEnabled)
{
self::$m_aOQLQueries[$sQueryId] = $oResultFilter->DeepClone();
if (self::$m_bUseAPCCache)
{
$oKPI = new ExecutionKPI();
apc_store($sAPCCacheId, $oResultFilter, self::$m_iQueryCacheTTL);
$oKPI->ComputeStats('Search APC (store)', $sQueryId);
}
}
}
if (!is_null($aParams))
{
$oResultFilter->SetInternalParams($aParams);
}
return $oResultFilter;
}
// Alternative to object mapping: the data are transfered directly into an array
// This is 10 times faster than creating a set of objects, and makes sense when optimization is required
/**
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
*/
public function ToDataArray($aColumns = array(), $aOrderBy = array(), $aArgs = array())
{
$sSQL = $this->MakeSelectQuery($aOrderBy, $aArgs);
$resQuery = CMDBSource::Query($sSQL);
if (!$resQuery) return;
if (count($aColumns) == 0)
{
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
// Add the standard id (as first column)
array_unshift($aColumns, 'id');
}
$aQueryCols = CMDBSource::GetColumns($resQuery);
$sClassAlias = $this->GetClassAlias();
$aColMap = array();
foreach ($aColumns as $sAttCode)
{
$sColName = $sClassAlias.$sAttCode;
if (in_array($sColName, $aQueryCols))
{
$aColMap[$sAttCode] = $sColName;
}
}
$aRes = array();
while ($aRow = CMDBSource::FetchArray($resQuery))
{
$aMappedRow = array();
foreach ($aColMap as $sAttCode => $sColName)
{
$aMappedRow[$sAttCode] = $aRow[$sColName];
}
$aRes[] = $aMappedRow;
}
CMDBSource::FreeResult($resQuery);
return $aRes;
}
////////////////////////////////////////////////////////////////////////////
//
// Construction of the SQL queries
//
////////////////////////////////////////////////////////////////////////////
protected static $m_aQueryStructCache = array();
public function MakeGroupByQuery($aArgs, $aGroupByExpr, $bExcludeNullValues = false)
{
if ($bExcludeNullValues)
{
// Null values are not handled (though external keys set to 0 are allowed)
$oQueryFilter = $this->DeepClone();
foreach ($aGroupByExpr as $oGroupByExp)
{
$oNull = new FunctionExpression('ISNULL', array($oGroupByExp));
$oNotNull = new BinaryExpression($oNull, '!=', new TrueExpression());
$oQueryFilter->AddConditionExpression($oNotNull);
}
}
else
{
$oQueryFilter = $this;
}
$aAttToLoad = array();
$oSQLQuery = $oQueryFilter->GetSQLQuery(array(), $aArgs, $aAttToLoad, null, 0, 0, false, $aGroupByExpr);
$aScalarArgs = array_merge(MetaModel::PrepareQueryArguments($aArgs), $this->GetInternalParams());
try
{
$bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
$sRes = $oSQLQuery->RenderGroupBy($aScalarArgs, $bBeautifulSQL);
}
catch (MissingQueryArgument $e)
{
// Add some information...
$e->addInfo('OQL', $this->ToOQL());
throw $e;
}
$this->AddQueryTraceGroupBy($aArgs, $aGroupByExpr, $sRes);
return $sRes;
}
/**
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
*/
public function MakeSelectQuery($aOrderBy = array(), $aArgs = array(), $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false)
{
// Check the order by specification, and prefix with the class alias
// and make sure that the ordering columns are going to be selected
//
$sClass = $this->GetClass();
$sClassAlias = $this->GetClassAlias();
$aOrderSpec = array();
foreach ($aOrderBy as $sFieldAlias => $bAscending)
{
if (!is_bool($bAscending))
{
throw new CoreException("Wrong direction in ORDER BY spec, found '$bAscending' and expecting a boolean value");
}
$iDotPos = strpos($sFieldAlias, '.');
if ($iDotPos === false)
{
$sAttClass = $sClass;
$sAttClassAlias = $sClassAlias;
$sAttCode = $sFieldAlias;
}
else
{
$sAttClassAlias = substr($sFieldAlias, 0, $iDotPos);
$sAttClass = $this->GetClassName($sAttClassAlias);
$sAttCode = substr($sFieldAlias, $iDotPos + 1);
}
if ($sAttCode != 'id')
{
MyHelpers::CheckValueInArray('field name in ORDER BY spec', $sAttCode, MetaModel::GetAttributesList($sAttClass));
$oAttDef = MetaModel::GetAttributeDef($sAttClass, $sAttCode);
foreach($oAttDef->GetOrderBySQLExpressions($sAttClassAlias) as $sSQLExpression)
{
$aOrderSpec[$sSQLExpression] = $bAscending;
}
}
else
{
$aOrderSpec['`'.$sAttClassAlias.$sAttCode.'`'] = $bAscending;
}
// Make sure that the columns used for sorting are present in the loaded columns
if (!is_null($aAttToLoad) && !isset($aAttToLoad[$sAttClassAlias][$sAttCode]))
{
$aAttToLoad[$sAttClassAlias][$sAttCode] = MetaModel::GetAttributeDef($sAttClass, $sAttCode);
}
}
$oSQLQuery = $this->GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount);
$aScalarArgs = array_merge(MetaModel::PrepareQueryArguments($aArgs), $this->GetInternalParams());
try
{
$bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, $bBeautifulSQL);
if ($sClassAlias == '_itop_')
{
IssueLog::Info('SQL Query (_itop_): '.$sRes);
}
}
catch (MissingQueryArgument $e)
{
// Add some information...
$e->addInfo('OQL', $this->ToOQL());
throw $e;
}
$this->AddQueryTraceSelect($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $sRes);
return $sRes;
}
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();
}
}
// Join to an additional table, if required...
//
if ($aExtendedDataSpec != null)
{
$sTableAlias = '_extended_data_';
$aExtendedFields = array();
foreach($aExtendedDataSpec['fields'] as $sColumn)
{
$sColRef = $oSearch->GetClassAlias().'_extdata_'.$sColumn;
$aExtendedFields[$sColRef] = new FieldExpressionResolved($sColumn, $sTableAlias);
}
$oSQLQueryExt = new SQLObjectQuery($aExtendedDataSpec['table'], $sTableAlias, $aExtendedFields);
$oSQLQuery->AddInnerJoin($oSQLQueryExt, 'id', $aExtendedDataSpec['join_key'] /*, $sTableAlias*/);
}
return $oSQLQuery;
}
////////////////////////////////////////////////////////////////////////////
//
// Cache/Trace/Log queries
//
////////////////////////////////////////////////////////////////////////////
protected static $m_bDebugQuery = false;
protected static $m_aQueriesLog = array();
protected static $m_bQueryCacheEnabled = false;
protected static $m_bUseAPCCache = false;
protected static $m_iQueryCacheTTL = 3600;
protected static $m_bTraceQueries = false;
protected static $m_bIndentQueries = false;
protected static $m_bOptimizeQueries = false;
public static function StartDebugQuery()
{
$aBacktrace = debug_backtrace();
self::$m_bDebugQuery = true;
}
public static function StopDebugQuery()
{
self::$m_bDebugQuery = false;
}
public static function EnableQueryCache($bEnabled, $bUseAPC, $iTimeToLive = 3600)
{
self::$m_bQueryCacheEnabled = $bEnabled;
self::$m_bUseAPCCache = $bUseAPC;
self::$m_iQueryCacheTTL = $iTimeToLive;
}
public static function EnableQueryTrace($bEnabled)
{
self::$m_bTraceQueries = $bEnabled;
}
public static function EnableQueryIndentation($bEnabled)
{
self::$m_bIndentQueries = $bEnabled;
}
public static function EnableOptimizeQuery($bEnabled)
{
self::$m_bOptimizeQueries = $bEnabled;
}
protected function AddQueryTraceSelect($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $sSql)
{
if (self::$m_bTraceQueries)
{
$aQueryData = array(
'type' => 'select',
'filter' => $this,
'order_by' => $aOrderBy,
'args' => $aArgs,
'att_to_load' => $aAttToLoad,
'extended_data_spec' => $aExtendedDataSpec,
'limit_count' => $iLimitCount,
'limit_start' => $iLimitStart,
'is_count' => $bGetCount
);
$sOql = $this->ToOQL(true, $aArgs);
self::AddQueryTrace($aQueryData, $sOql, $sSql);
}
}
protected function AddQueryTraceGroupBy($aArgs, $aGroupByExpr, $sSql)
{
if (self::$m_bTraceQueries)
{
$aQueryData = array(
'type' => 'group_by',
'filter' => $this,
'args' => $aArgs,
'group_by_expr' => $aGroupByExpr
);
$sOql = $this->ToOQL(true, $aArgs);
self::AddQueryTrace($aQueryData, $sOql, $sSql);
}
}
protected static function AddQueryTrace($aQueryData, $sOql, $sSql)
{
if (self::$m_bTraceQueries)
{
$sQueryId = md5(serialize($aQueryData));
$sMySQLQueryId = md5($sSql);
if(!isset(self::$m_aQueriesLog[$sQueryId]))
{
self::$m_aQueriesLog[$sQueryId]['data'] = serialize($aQueryData);
self::$m_aQueriesLog[$sQueryId]['oql'] = $sOql;
self::$m_aQueriesLog[$sQueryId]['hits'] = 1;
}
else
{
self::$m_aQueriesLog[$sQueryId]['hits']++;
}
if(!isset(self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]))
{
self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['sql'] = $sSql;
self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['count'] = 1;
$iTableCount = count(CMDBSource::ExplainQuery($sSql));
self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['table_count'] = $iTableCount;
}
else
{
self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['count']++;
}
}
}
public static function RecordQueryTrace()
{
if (!self::$m_bTraceQueries) return;
$iOqlCount = count(self::$m_aQueriesLog);
$iSqlCount = 0;
foreach (self::$m_aQueriesLog as $sQueryId => $aOqlData)
{
$iSqlCount += $aOqlData['hits'];
}
$sHtml = "<h2>Stats on SELECT queries: OQL=$iOqlCount, SQL=$iSqlCount</h2>\n";
foreach (self::$m_aQueriesLog as $sQueryId => $aOqlData)
{
$sOql = $aOqlData['oql'];
$sHits = $aOqlData['hits'];
$sHtml .= "<p><b>$sHits</b> hits for OQL query: $sOql</p>\n";
$sHtml .= "<ul id=\"ClassesRelationships\" class=\"treeview\">\n";
foreach($aOqlData['queries'] as $aSqlData)
{
$sQuery = $aSqlData['sql'];
$sSqlHits = $aSqlData['count'];
$iTableCount = $aSqlData['table_count'];
$sHtml .= "<li><b>$sSqlHits</b> hits for SQL ($iTableCount tables): <pre style=\"font-size:60%\">$sQuery</pre></li>\n";
}
$sHtml .= "</ul>\n";
}
$sLogFile = 'queries.latest';
file_put_contents(APPROOT.'data/'.$sLogFile.'.html', $sHtml);
$sLog = "<?php\n\$aQueriesLog = ".var_export(self::$m_aQueriesLog, true).";";
file_put_contents(APPROOT.'data/'.$sLogFile.'.log', $sLog);
// Cumulate the queries
$sAllQueries = APPROOT.'data/queries.log';
if (file_exists($sAllQueries))
{
// Merge the new queries into the existing log
include($sAllQueries);
foreach (self::$m_aQueriesLog as $sQueryId => $aOqlData)
{
if (!array_key_exists($sQueryId, $aQueriesLog))
{
$aQueriesLog[$sQueryId] = $aOqlData;
}
}
}
else
{
$aQueriesLog = self::$m_aQueriesLog;
}
$sLog = "<?php\n\$aQueriesLog = ".var_export($aQueriesLog, true).";";
file_put_contents($sAllQueries, $sLog);
}
protected static function DbgTrace($value)
{
if (!self::$m_bDebugQuery) return;
$aBacktrace = debug_backtrace();
$iCallStackPos = count($aBacktrace) - self::$m_bDebugQuery;
$sIndent = "";
for ($i = 0 ; $i < $iCallStackPos ; $i++)
{
$sIndent .= " .-=^=-. ";
}
$aCallers = array();
foreach($aBacktrace as $aStackInfo)
{
$aCallers[] = $aStackInfo["function"];
}
$sCallers = "Callstack: ".implode(', ', $aCallers);
$sFunction = "<b title=\"$sCallers\">".$aBacktrace[1]["function"]."</b>";
if (is_string($value))
{
echo "$sIndent$sFunction: $value<br/>\n";
}
else if (is_object($value))
{
echo "$sIndent$sFunction:\n<pre>\n";
print_r($value);
echo "</pre>\n";
}
else
{
echo "$sIndent$sFunction: $value<br/>\n";
}
}
}

View File

@@ -0,0 +1,432 @@
<?php
// Copyright (C) 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/>
/**
* A union of DBObjectSearches
*
* @copyright Copyright (C) 2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class DBUnionSearch extends DBSearch
{
protected $aSearches; // source queries
protected $aSelectedClasses; // alias => classes (lowest common ancestors) computed at construction
public function __construct($aSearches)
{
if (count ($aSearches) == 0)
{
throw new CoreException('A DBUnionSearch must be made of at least one search');
}
$this->aSearches = array();
foreach ($aSearches as $oSearch)
{
if ($oSearch instanceof DBUnionSearch)
{
foreach ($oSearch->aSearches as $oSubSearch)
{
$this->aSearches[] = $oSubSearch->DeepClone();
}
}
else
{
$this->aSearches[] = $oSearch->DeepClone();
}
}
// 1 - Collect all the column/classes
$aColumnToClasses = array();
foreach ($this->aSearches as $iPos => $oSearch)
{
$aSelected = array_values($oSearch->GetSelectedClasses());
if ($iPos != 0)
{
if (count($aSelected) < count($aColumnToClasses))
{
throw new Exception('Too few selected classes in the subquery #'.($iPos+1));
}
if (count($aSelected) > count($aColumnToClasses))
{
throw new Exception('Too many selected classes in the subquery #'.($iPos+1));
}
}
foreach ($aSelected as $iColumn => $sClass)
{
$aColumnToClasses[$iColumn][] = $sClass;
}
}
// 2 - Build the index column => alias
$oFirstSearch = $this->aSearches[0];
$aColumnToAlias = array_keys($oFirstSearch->GetSelectedClasses());
// 3 - Compute alias => lowest common ancestor
$this->aSelectedClasses = array();
foreach ($aColumnToClasses as $iColumn => $aClasses)
{
$sAlias = $aColumnToAlias[$iColumn];
$sAncestor = MetaModel::GetLowestCommonAncestor($aClasses);
if (is_null($sAncestor))
{
throw new Exception('Could not find a common ancestor for the column '.($iColumn+1).' (Classes: '.implode(', ', $aClasses).')');
}
$this->aSelectedClasses[$sAlias] = $sAncestor;
}
}
public function GetSearches()
{
return $this->aSearches;
}
/**
* Limited to the selected classes
*/
public function GetClassName($sAlias)
{
if (array_key_exists($sAlias, $this->aSelectedClasses))
{
return $this->aSelectedClasses[$sAlias];
}
else
{
throw new CoreException("Invalid class alias '$sAlias'");
}
}
public function GetClass()
{
return reset($this->aSelectedClasses);
}
public function GetClassAlias()
{
reset($this->aSelectedClasses);
return key($this->aSelectedClasses);
}
/**
* Change the class (only subclasses are supported as of now, because the conditions must fit the new class)
* Defaults to the first selected class
* Only the selected classes can be changed
*/
public function ChangeClass($sNewClass, $sAlias = null)
{
if (is_null($sAlias))
{
$sAlias = $this->GetClassAlias();
}
elseif (!array_key_exists($sAlias, $this->aSelectedClasses))
{
// discard silently - necessary when recursing (??? copied from DBObjectSearch)
return;
}
// 1 - identify the impacted column
$iColumn = array_search($sAlias, array_keys($this->aSelectedClasses));
// 2 - change for each search
foreach ($this->aSearches as $oSearch)
{
$aSearchAliases = array_keys($oSearch->GetSelectedClasses());
$sSearchAlias = $aSearchAliases[$iColumn];
$oSearch->ChangeClass($sNewClass, $sSearchAlias);
}
// 3 - record the change
$this->aSelectedClasses[$sAlias] = $sNewClass;
}
public function GetSelectedClasses()
{
return $this->aSelectedClasses;
}
public function IsAny()
{
$bIsAny = true;
foreach ($this->aSearches as $oSearch)
{
if (!$oSearch->IsAny())
{
$bIsAny = false;
break;
}
}
return $bIsAny;
}
public function ResetCondition()
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->ResetCondition();
}
}
public function MergeConditionExpression($oExpression)
{
$aAliases = array_keys($this->aSelectedClasses);
foreach ($this->aSearches as $iSearchIndex => $oSearch)
{
$oClonedExpression = $oExpression->DeepClone();
if ($iSearchIndex != 0)
{
foreach (array_keys($oSearch->GetSelectedClasses()) as $iColumn => $sSearchAlias)
{
$oClonedExpression->RenameAlias($aAliases[$iColumn], $sSearchAlias);
}
}
$oSearch->MergeConditionExpression($oClonedExpression);
}
}
public function AddConditionExpression($oExpression)
{
$aAliases = array_keys($this->aSelectedClasses);
foreach ($this->aSearches as $iSearchIndex => $oSearch)
{
$oClonedExpression = $oExpression->DeepClone();
if ($iSearchIndex != 0)
{
foreach (array_keys($oSearch->GetSelectedClasses()) as $iColumn => $sSearchAlias)
{
$oClonedExpression->RenameAlias($aAliases[$iColumn], $sSearchAlias);
}
}
$oSearch->AddConditionExpression($oClonedExpression);
}
}
public function AddNameCondition($sName)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddNameCondition($sName);
}
}
public function AddCondition($sFilterCode, $value, $sOpCode = null)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddCondition($sFilterCode, $value, $sOpCode);
}
}
/**
* Specify a condition on external keys or link sets
* @param sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
* Example: infra_list->ci_id->location_id->country
* @param value The value to match (can be an array => IN(val1, val2...)
* @return void
*/
public function AddConditionAdvanced($sAttSpec, $value)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddConditionAdvanced($sAttSpec, $value);
}
}
public function AddCondition_FullText($sFullText)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddCondition_FullText($sFullText);
}
}
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode);
}
}
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddCondition_ReferencedBy($oFilter, $sForeignExtKeyAttCode);
}
}
public function Intersect(DBSearch $oFilter)
{
$aSearches = array();
foreach ($this->aSearches as $oSearch)
{
$aSearches[] = $oSearch->Intersect($oFilter);
}
return new DBUnionSearch($aSearches);
}
public function SetInternalParams($aParams)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->SetInternalParams($aParams);
}
}
public function GetInternalParams()
{
$aParams = array();
foreach ($this->aSearches as $oSearch)
{
$aParams = array_merge($oSearch->GetInternalParams(), $aParams);
}
return $aParams;
}
public function GetQueryParams()
{
$aParams = array();
foreach ($this->aSearches as $oSearch)
{
$aParams = array_merge($oSearch->GetQueryParams(), $aParams);
}
return $aParams;
}
public function ListConstantFields()
{
// Somewhat complex to implement for unions, for a poor benefit
return array();
}
/**
* Turn the parameters (:xxx) into scalar values in order to easily
* serialize a search
*/
public function ApplyParameters($aArgs)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->ApplyParameters($aArgs);
}
}
/**
* Overloads for query building
*/
public function ToOQL($bDevelopParams = false, $aContextParams = null)
{
$aSubQueries = array();
foreach ($this->aSearches as $oSearch)
{
$aSubQueries[] = $oSearch->ToOQL($bDevelopParams, $aContextParams);
}
$sRet = implode(' UNION ', $aSubQueries);
return $sRet;
}
////////////////////////////////////////////////////////////////////////////
//
// Construction of the SQL queries
//
////////////////////////////////////////////////////////////////////////////
public function MakeDeleteQuery($aArgs = array())
{
throw new Exception('MakeDeleteQuery is not implemented for the unions!');
}
public function MakeUpdateQuery($aValues, $aArgs = array())
{
throw new Exception('MakeUpdateQuery is not implemented for the unions!');
}
protected function MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
{
if (count($this->aSearches) == 1)
{
return $this->aSearches[0]->MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr);
}
$aSQLQueries = array();
$aAliases = array_keys($this->aSelectedClasses);
foreach ($this->aSearches as $iSearch => $oSearch)
{
$aSearchAliases = array_keys($oSearch->GetSelectedClasses());
// The selected classes from the query build perspective are the lowest common ancestors amongst the various queries
// (used when it comes to determine which attributes must be selected)
$aSearchSelectedClasses = array();
foreach ($aSearchAliases as $iColumn => $sSearchAlias)
{
$sAlias = $aAliases[$iColumn];
$aSearchSelectedClasses[$sSearchAlias] = $this->aSelectedClasses[$sAlias];
}
if (is_null($aAttToLoad))
{
$aQueryAttToLoad = null;
}
else
{
// (Eventually) Transform the aliases
$aQueryAttToLoad = array();
foreach ($aAttToLoad as $sAlias => $aAttributes)
{
$iColumn = array_search($sAlias, $aAliases);
$sQueryAlias = ($iColumn === false) ? $sAlias : $aSearchAliases[$iColumn];
$aQueryAttToLoad[$sQueryAlias] = $aAttributes;
}
}
if (is_null($aGroupByExpr))
{
$aQueryGroupByExpr = null;
}
else
{
// Clone (and eventually transform) the group by expressions
$aQueryGroupByExpr = array();
$aTranslationData = array();
$aQueryColumns = array_keys($oSearch->GetSelectedClasses());
foreach ($aAliases as $iColumn => $sAlias)
{
$sQueryAlias = $aQueryColumns[$iColumn];
$aTranslationData[$sAlias]['*'] = $sQueryAlias;
$aQueryGroupByExpr[$sAlias.'id'] = new FieldExpression('id', $sQueryAlias);
}
foreach ($aGroupByExpr as $sExpressionAlias => $oExpression)
{
$aQueryGroupByExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
}
}
$oSubQuery = $oSearch->MakeSQLQuery($aQueryAttToLoad, false, $aModifierProperties, $aQueryGroupByExpr, $aSearchSelectedClasses);
$aSQLQueries[] = $oSubQuery;
}
$oSQLQuery = new SQLUnionQuery($aSQLQueries, $aGroupByExpr);
//MyHelpers::var_dump_html($oSQLQuery, true);
//MyHelpers::var_dump_html($oSQLQuery->RenderSelect(), true);
if (self::$m_bDebugQuery) $oSQLQuery->DisplayHtml();
return $oSQLQuery;
}
}

View File

@@ -0,0 +1,298 @@
<?php
// Copyright (C) 2010-2013 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/>
/**
* Algorithm to delete object(s) and maintain data integrity
*
* @copyright Copyright (C) 2010-2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class DeleteException extends CoreException
{
}
/**
* Deletion plan (other objects to be deleted/modified, eventual issues, etc.)
*
* @package iTopORM
*/
class DeletionPlan
{
//protected $m_aIssues;
protected $m_bFoundStopper;
protected $m_bFoundSecurityIssue;
protected $m_bFoundManualDelete;
protected $m_bFoundManualOperation;
protected $m_iToDelete;
protected $m_iToUpdate;
protected $m_aToDelete;
protected $m_aToUpdate;
protected static $m_aModeUpdate = array(
DEL_SILENT => array(
DEL_SILENT => DEL_SILENT,
DEL_AUTO => DEL_AUTO,
DEL_MANUAL => DEL_MANUAL
),
DEL_MANUAL => array(
DEL_SILENT => DEL_MANUAL,
DEL_AUTO => DEL_AUTO,
DEL_MANUAL => DEL_MANUAL
),
DEL_AUTO => array(
DEL_SILENT => DEL_AUTO,
DEL_AUTO => DEL_AUTO,
DEL_MANUAL => DEL_AUTO
)
);
public function __construct()
{
$this->m_iToDelete = 0;
$this->m_iToUpdate = 0;
$this->m_aToDelete = array();
$this->m_aToUpdate = array();
$this->m_bFoundStopper = false;
$this->m_bFoundSecurityIssue = false;
$this->m_bFoundManualDelete = false;
$this->m_bFoundManualOperation = false;
}
public function ComputeResults()
{
$this->m_iToDelete = 0;
$this->m_iToUpdate = 0;
foreach($this->m_aToDelete as $sClass => $aToDelete)
{
foreach($aToDelete as $iId => $aData)
{
$this->m_iToDelete++;
if (isset($aData['issue']))
{
$this->m_bFoundStopper = true;
$this->m_bFoundManualOperation = true;
if (isset($aData['issue_security']))
{
$this->m_bFoundSecurityIssue = true;
}
}
if ($aData['mode'] == DEL_MANUAL)
{
$this->m_aToDelete[$sClass][$iId]['issue'] = $sClass.'::'.$iId.' '.Dict::S('UI:Delete:MustBeDeletedManually');
$this->m_bFoundStopper = true;
$this->m_bFoundManualDelete = true;
}
}
}
// Getting and setting time limit are not symetric:
// www.php.net/manual/fr/function.set-time-limit.php#72305
$iPreviousTimeLimit = ini_get('max_execution_time');
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
foreach($this->m_aToUpdate as $sClass => $aToUpdate)
{
foreach($aToUpdate as $iId => $aData)
{
set_time_limit($iLoopTimeLimit);
$this->m_iToUpdate++;
$oObject = $aData['to_reset'];
$aExtKeyLabels = array();
foreach ($aData['attributes'] as $sRemoteExtKey => $aRemoteAttDef)
{
$oObject->Set($sRemoteExtKey, $aData['values'][$sRemoteExtKey]);
$aExtKeyLabels[] = $aRemoteAttDef->GetLabel();
}
$this->m_aToUpdate[$sClass][$iId]['attributes_list'] = implode(', ', $aExtKeyLabels);
list($bRes, $aIssues, $bSecurityIssues) = $oObject->CheckToWrite();
if (!$bRes)
{
$this->m_aToUpdate[$sClass][$iId]['issue'] = implode(', ', $aIssues);
$this->m_bFoundStopper = true;
if ($bSecurityIssues)
{
$this->m_aToUpdate[$sClass][$iId]['issue_security'] = true;
$this->m_bFoundSecurityIssue = true;
}
}
}
}
set_time_limit($iPreviousTimeLimit);
}
public function GetIssues()
{
$aIssues = array();
foreach ($this->m_aToDelete as $sClass => $aToDelete)
{
foreach ($aToDelete as $iId => $aData)
{
if (isset($aData['issue']))
{
$aIssues[] = $aData['issue'];
}
}
}
foreach ($this->m_aToUpdate as $sClass => $aToUpdate)
{
foreach ($aToUpdate as $iId => $aData)
{
if (isset($aData['issue']))
{
$aIssues[] = $aData['issue'];
}
}
}
return $aIssues;
}
public function ListDeletes()
{
return $this->m_aToDelete;
}
public function ListUpdates()
{
return $this->m_aToUpdate;
}
public function GetTargetCount()
{
return $this->m_iToDelete + $this->m_iToUpdate;
}
public function FoundStopper()
{
return $this->m_bFoundStopper;
}
public function FoundSecurityIssue()
{
return $this->m_bFoundSecurityIssue;
}
public function FoundManualOperation()
{
return $this->m_bFoundManualOperation;
}
public function FoundManualDelete()
{
return $this->m_bFoundManualDelete;
}
public function FoundManualUpdate()
{
}
public function AddToDelete($oObject, $iDeletionMode = null)
{
if (is_null($iDeletionMode))
{
$bRequestedExplicitely = true;
$iDeletionMode = DEL_AUTO;
}
else
{
$bRequestedExplicitely = false;
}
$sClass = get_class($oObject);
$iId = $oObject->GetKey();
if (isset($this->m_aToUpdate[$sClass][$iId]))
{
unset($this->m_aToUpdate[$sClass][$iId]);
}
if (isset($this->m_aToDelete[$sClass][$iId]))
{
if ($this->m_aToDelete[$sClass][$iId]['requested_explicitely'])
{
// No change: let it in mode DEL_AUTO
}
else
{
$iPrevDeletionMode = $this->m_aToDelete[$sClass][$iId]['mode'];
$iNewDeletionMode = self::$m_aModeUpdate[$iPrevDeletionMode][$iDeletionMode];
$this->m_aToDelete[$sClass][$iId]['mode'] = $iNewDeletionMode;
if ($bRequestedExplicitely)
{
// This object was in the root list
$this->m_aToDelete[$sClass][$iId]['requested_explicitely'] = true;
$this->m_aToDelete[$sClass][$iId]['mode'] = DEL_AUTO;
}
}
}
else
{
$this->m_aToDelete[$sClass][$iId] = array(
'to_delete' => $oObject,
'mode' => $iDeletionMode,
'requested_explicitely' => $bRequestedExplicitely,
);
}
}
public function SetDeletionIssues($oObject, $aIssues, $bSecurityIssue)
{
if (count($aIssues) > 0)
{
$sClass = get_class($oObject);
$iId = $oObject->GetKey();
$this->m_aToDelete[$sClass][$iId]['issue'] = implode(', ', $aIssues);
if ($bSecurityIssue)
{
$this->m_aToDelete[$sClass][$iId]['issue_security'] = true;
}
}
}
public function AddToUpdate($oObject, $oAttDef, $value = 0)
{
$sClass = get_class($oObject);
$iId = $oObject->GetKey();
if (isset($this->m_aToDelete[$sClass][$iId]))
{
// skip... it should be deleted anyhow !
}
else
{
if (!isset($this->m_aToUpdate[$sClass][$iId]))
{
$this->m_aToUpdate[$sClass][$iId] = array(
'to_reset' => $oObject,
);
}
$this->m_aToUpdate[$sClass][$iId]['attributes'][$oAttDef->GetCode()] = $oAttDef;
$this->m_aToUpdate[$sClass][$iId]['values'][$oAttDef->GetCode()] = $value;
}
}
}
?>

View File

@@ -1,228 +1,335 @@
<?php
// Copyright (C) 2010 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class Dict
* Management of localizable strings
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
class DictException extends CoreException
{
}
class DictExceptionUnknownLanguage extends DictException
{
public function __construct($sLanguageCode)
{
$aContext = array();
$aContext['language_code'] = $sLanguageCode;
parent::__construct('Unknown localization language', $aContext);
}
}
class DictExceptionMissingString extends DictException
{
public function __construct($sLanguageCode, $sStringCode)
{
$aContext = array();
$aContext['language_code'] = $sLanguageCode;
$aContext['string_code'] = $sStringCode;
parent::__construct('Missing localized string', $aContext);
}
}
define('DICT_ERR_STRING', 1); // when a string is missing, return the identifier
define('DICT_ERR_EXCEPTION', 2); // when a string is missing, throw an exception
//define('DICT_ERR_LOG', 3); // when a string is missing, log an error
class Dict
{
protected static $m_iErrorMode = DICT_ERR_STRING;
protected static $m_sDefaultLanguage = 'EN US';
protected static $m_sCurrentLanguage = null; // No language selected by default
protected static $m_aLanguages = array(); // array( code => array( 'description' => '...', 'localized_description' => '...') ...)
protected static $m_aData = array();
public static function SetDefaultLanguage($sLanguageCode)
{
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
{
throw new DictExceptionUnknownLanguage($sLanguageCode);
}
self::$m_sDefaultLanguage = $sLanguageCode;
}
public static function SetUserLanguage($sLanguageCode)
{
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
{
throw new DictExceptionUnknownLanguage($sLanguageCode);
}
self::$m_sCurrentLanguage = $sLanguageCode;
}
public static function GetCurrentLanguage()
{
if (self::$m_sCurrentLanguage == null) // May happen when no user is logged in (i.e login screen, non authentifed page)
{
// In which case let's use the default language
return self::$m_sDefaultLanguage;
}
return self::$m_sCurrentLanguage;
}
//returns a hash array( code => array( 'description' => '...', 'localized_description' => '...') ...)
public static function GetLanguages()
{
return self::$m_aLanguages;
}
// iErrorMode from {DICT_ERR_STRING, DICT_ERR_EXCEPTION}
public static function SetErrorMode($iErrorMode)
{
self::$m_iErrorMode = $iErrorMode;
}
public static function S($sStringCode, $sDefault = null)
{
// Attempt to find the string in the user language
//
if (!array_key_exists(self::GetCurrentLanguage(), self::$m_aData))
{
// It may happen, when something happens before the dictionnaries get loaded
return $sStringCode;
}
$aCurrentDictionary = self::$m_aData[self::GetCurrentLanguage()];
if (array_key_exists($sStringCode, $aCurrentDictionary))
{
return $aCurrentDictionary[$sStringCode];
}
// Attempt to find the string in the default language
//
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
if (array_key_exists($sStringCode, $aDefaultDictionary))
{
return $aDefaultDictionary[$sStringCode];
}
// Could not find the string...
//
switch (self::$m_iErrorMode)
{
case DICT_ERR_STRING:
if (is_null($sDefault))
{
return $sStringCode;
}
else
{
return $sDefault;
}
break;
case DICT_ERR_EXCEPTION:
default:
throw new DictExceptionMissingString(self::$m_sCurrentLanguage, $sStringCode);
break;
}
return 'bug!';
}
public static function Format($sFormatCode /*, ... arguments ....*/)
{
$sLocalizedFormat = self::S($sFormatCode);
$aArguments = func_get_args();
if ($sLocalizedFormat == $sFormatCode)
{
// Make sure the information will be displayed (ex: an error occuring before the dictionary gets loaded)
return $sFormatCode.' - '.implode(', ', $aArguments);
}
array_shift($aArguments);
return vsprintf($sLocalizedFormat, $aArguments);
}
// sLanguageCode: Code identifying the language i.e. FR-FR
// sEnglishLanguageDesc: Description of the language code, in English. i.e. French (France)
// sLocalizedLanguageDesc: Description of the language code, in its own language. i.e. Français (France)
// aEntries: Hash array of dictionnary entries
public static function Add($sLanguageCode, $sEnglishLanguageDesc, $sLocalizedLanguageDesc, $aEntries)
{
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
{
self::$m_aLanguages[$sLanguageCode] = array('description' => $sEnglishLanguageDesc, 'localized_description' => $sLocalizedLanguageDesc);
self::$m_aData[$sLanguageCode] = array();
}
self::$m_aData[$sLanguageCode] = array_merge(self::$m_aData[$sLanguageCode], $aEntries);
}
public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US')
{
$aMissing = array(); // Strings missing for the target language
$aUnexpected = array(); // Strings defined for the target language, but not found in the reference dictionary
$aNotTranslated = array(); // Strings having the same value in both dictionaries
$aOK = array(); // Strings having different values in both dictionaries
foreach (self::$m_aData[$sLanguageRef] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageCode]))
{
$aMissing[$sStringCode] = $sValue;
}
}
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef]))
{
$aUnexpected[$sStringCode] = $sValue;
}
else
{
// The value exists in the reference
$sRefValue = self::$m_aData[$sLanguageRef][$sStringCode];
if ($sValue == $sRefValue)
{
$aNotTranslated[$sStringCode] = $sValue;
}
else
{
$aOK[$sStringCode] = $sValue;
}
}
}
return array($aMissing, $aUnexpected, $aNotTranslated, $aOK);
}
public static function Dump()
{
MyHelpers::var_dump_html(self::$m_aData);
}
}
?>
<?php
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class Dict
* Management of localizable strings
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class DictException extends CoreException
{
}
class DictExceptionUnknownLanguage extends DictException
{
public function __construct($sLanguageCode)
{
$aContext = array();
$aContext['language_code'] = $sLanguageCode;
parent::__construct('Unknown localization language', $aContext);
}
}
class DictExceptionMissingString extends DictException
{
public function __construct($sLanguageCode, $sStringCode)
{
$aContext = array();
$aContext['language_code'] = $sLanguageCode;
$aContext['string_code'] = $sStringCode;
parent::__construct('Missing localized string', $aContext);
}
}
define('DICT_ERR_STRING', 1); // when a string is missing, return the identifier
define('DICT_ERR_EXCEPTION', 2); // when a string is missing, throw an exception
//define('DICT_ERR_LOG', 3); // when a string is missing, log an error
class Dict
{
protected static $m_bTraceFiles = false;
protected static $m_aEntryFiles = array();
protected static $m_iErrorMode = DICT_ERR_STRING;
protected static $m_sDefaultLanguage = 'EN US';
protected static $m_sCurrentLanguage = null; // No language selected by default
protected static $m_aLanguages = array(); // array( code => array( 'description' => '...', 'localized_description' => '...') ...)
protected static $m_aData = array();
public static function EnableTraceFiles()
{
self::$m_bTraceFiles = true;
}
public static function GetEntryFiles()
{
return self::$m_aEntryFiles;
}
public static function SetDefaultLanguage($sLanguageCode)
{
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
{
throw new DictExceptionUnknownLanguage($sLanguageCode);
}
self::$m_sDefaultLanguage = $sLanguageCode;
}
public static function SetUserLanguage($sLanguageCode)
{
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
{
throw new DictExceptionUnknownLanguage($sLanguageCode);
}
self::$m_sCurrentLanguage = $sLanguageCode;
}
public static function GetUserLanguage()
{
if (self::$m_sCurrentLanguage == null) // May happen when no user is logged in (i.e login screen, non authentifed page)
{
// In which case let's use the default language
return self::$m_sDefaultLanguage;
}
return self::$m_sCurrentLanguage;
}
//returns a hash array( code => array( 'description' => '...', 'localized_description' => '...') ...)
public static function GetLanguages()
{
return self::$m_aLanguages;
}
// iErrorMode from {DICT_ERR_STRING, DICT_ERR_EXCEPTION}
public static function SetErrorMode($iErrorMode)
{
self::$m_iErrorMode = $iErrorMode;
}
public static function S($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
{
// Attempt to find the string in the user language
//
if (!array_key_exists(self::GetUserLanguage(), self::$m_aData))
{
// It may happen, when something happens before the dictionnaries get loaded
return $sStringCode;
}
$aCurrentDictionary = self::$m_aData[self::GetUserLanguage()];
if (array_key_exists($sStringCode, $aCurrentDictionary))
{
return $aCurrentDictionary[$sStringCode];
}
if (!$bUserLanguageOnly)
{
// Attempt to find the string in the default language
//
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
if (array_key_exists($sStringCode, $aDefaultDictionary))
{
return $aDefaultDictionary[$sStringCode];
}
// Attempt to find the string in english
//
$aDefaultDictionary = self::$m_aData['EN US'];
if (array_key_exists($sStringCode, $aDefaultDictionary))
{
return $aDefaultDictionary[$sStringCode];
}
}
// Could not find the string...
//
switch (self::$m_iErrorMode)
{
case DICT_ERR_STRING:
if (is_null($sDefault))
{
return $sStringCode;
}
else
{
return $sDefault;
}
break;
case DICT_ERR_EXCEPTION:
default:
throw new DictExceptionMissingString(self::$m_sCurrentLanguage, $sStringCode);
break;
}
return 'bug!';
}
public static function Format($sFormatCode /*, ... arguments ....*/)
{
$sLocalizedFormat = self::S($sFormatCode);
$aArguments = func_get_args();
array_shift($aArguments);
if ($sLocalizedFormat == $sFormatCode)
{
// Make sure the information will be displayed (ex: an error occuring before the dictionary gets loaded)
return $sFormatCode.' - '.implode(', ', $aArguments);
}
return vsprintf($sLocalizedFormat, $aArguments);
}
// sLanguageCode: Code identifying the language i.e. FR-FR
// sEnglishLanguageDesc: Description of the language code, in English. i.e. French (France)
// sLocalizedLanguageDesc: Description of the language code, in its own language. i.e. Français (France)
// aEntries: Hash array of dictionnary entries
// ~~ or ~* can be used to indicate entries still to be translated.
public static function Add($sLanguageCode, $sEnglishLanguageDesc, $sLocalizedLanguageDesc, $aEntries)
{
if (self::$m_bTraceFiles)
{
$aBacktrace = debug_backtrace();
$sFile = $aBacktrace[0]["file"];
foreach($aEntries as $sKey => $sValue)
{
self::$m_aEntryFiles[$sLanguageCode][$sKey] = array(
'file' => $sFile,
'value' => $sValue
);
}
}
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
{
self::$m_aLanguages[$sLanguageCode] = array('description' => $sEnglishLanguageDesc, 'localized_description' => $sLocalizedLanguageDesc);
self::$m_aData[$sLanguageCode] = array();
}
foreach($aEntries as $sCode => $sValue)
{
self::$m_aData[$sLanguageCode][$sCode] = self::FilterString($sValue);
}
}
/**
* Clone a string in every language (if it exists in that language)
*/
public static function CloneString($sSourceCode, $sDestCode)
{
foreach(self::$m_aLanguages as $sLanguageCode => $foo)
{
if (isset(self::$m_aData[$sLanguageCode][$sSourceCode]))
{
self::$m_aData[$sLanguageCode][$sDestCode] = self::$m_aData[$sLanguageCode][$sSourceCode];
}
}
}
public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US')
{
$aMissing = array(); // Strings missing for the target language
$aUnexpected = array(); // Strings defined for the target language, but not found in the reference dictionary
$aNotTranslated = array(); // Strings having the same value in both dictionaries
$aOK = array(); // Strings having different values in both dictionaries
foreach (self::$m_aData[$sLanguageRef] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageCode]))
{
$aMissing[$sStringCode] = $sValue;
}
}
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef]))
{
$aUnexpected[$sStringCode] = $sValue;
}
else
{
// The value exists in the reference
$sRefValue = self::$m_aData[$sLanguageRef][$sStringCode];
if ($sValue == $sRefValue)
{
$aNotTranslated[$sStringCode] = $sValue;
}
else
{
$aOK[$sStringCode] = $sValue;
}
}
}
return array($aMissing, $aUnexpected, $aNotTranslated, $aOK);
}
public static function Dump()
{
MyHelpers::var_dump_html(self::$m_aData);
}
public static function InCache($sApplicationPrefix)
{
if (function_exists('apc_fetch'))
{
$bResult = false;
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
//
self::$m_aData = apc_fetch($sApplicationPrefix.'-dict');
if (is_bool(self::$m_aData) && (self::$m_aData === false))
{
self::$m_aData = array();
}
else
{
self::$m_aLanguages = apc_fetch($sApplicationPrefix.'-languages');
if (is_bool(self::$m_aLanguages) && (self::$m_aLanguages === false))
{
self::$m_aLanguages = array();
}
else
{
$bResult = true;
}
}
return $bResult;
}
return false;
}
public static function InitCache($sApplicationPrefix)
{
if (function_exists('apc_store'))
{
apc_store($sApplicationPrefix.'-languages', self::$m_aLanguages);
apc_store($sApplicationPrefix.'-dict', self::$m_aData);
}
}
public static function ResetCache($sApplicationPrefix)
{
if (function_exists('apc_delete'))
{
apc_delete($sApplicationPrefix.'-languages');
apc_delete($sApplicationPrefix.'-dict');
}
}
protected static function FilterString($s)
{
return str_replace(array('~~', '~*'), '', $s);
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -1,135 +1,479 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Send an mail (for notification, testing,... purposes)
* #@# TODO - replace by a more sophisticated mean (and update the prototype)
* Send an email (abstraction for synchronous/asynchronous modes)
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'/lib/swiftmailer/lib/swift_required.php');
Swift_Preferences::getInstance()->setCharset('UTF-8');
define ('EMAIL_SEND_OK', 0);
define ('EMAIL_SEND_PENDING', 1);
define ('EMAIL_SEND_ERROR', 2);
class EMail
{
protected $m_sBody;
protected $m_sSubject;
protected $m_sTo;
protected $m_aHeaders; // array of key=>value
// Serialization formats
const ORIGINAL_FORMAT = 1; // Original format, consisting in serializing the whole object, inculding the Swift Mailer's object.
// Did not work with attachements since their binary representation cannot be stored as a valid UTF-8 string
const FORMAT_V2 = 2; // New format, only the raw data are serialized (base64 encoded if needed)
protected static $m_oConfig = null;
protected $m_aData; // For storing data to serialize
public function LoadConfig($sConfigFile = ITOP_DEFAULT_CONFIG_FILE)
{
if (is_null(self::$m_oConfig))
{
self::$m_oConfig = new Config($sConfigFile);
}
}
protected $m_oMessage;
public function __construct()
{
$this->m_sTo = '';
$this->m_sSubject = '';
$this->m_sBody = '';
$this->m_aHeaders = array();
$this->m_aData = array();
$this->m_oMessage = Swift_Message::newInstance();
$oEncoder = new Swift_Mime_ContentEncoder_PlainContentEncoder('8bit');
$this->m_oMessage->setEncoder($oEncoder);
}
// Errors management : not that simple because we need that function to be
// executed in the background, while making sure that any issue would be reported clearly
protected $m_aMailErrors; //array of strings explaining the issues
public function mail_error_handler($errno, $errstr, $errfile, $errline)
/**
* Custom serialization method
* No longer use the brute force "serialize" method since
* 1) It does not work with binary attachments (since they cannot be stored in a UTF-8 text field)
* 2) The size tends to be quite big (sometimes ten times the size of the email)
*/
public function SerializeV2()
{
$sCleanMessage= str_replace("mail() [<a href='function.mail'>function.mail</a>]: ", "", $errstr);
$this->m_aMailErrors[] = $sCleanMessage;
return serialize($this->m_aData);
}
// returns a list of issues if any
public function Send()
/**
* Custom de-serialization method
* @param string $sSerializedMessage The serialized representation of the message
*/
static public function UnSerializeV2($sSerializedMessage)
{
$sHeaders = 'MIME-Version: 1.0' . "\r\n";
// ! the case is important for MS-Outlook
$sHeaders .= 'Content-Type: text/html; charset=UTF-8' . "\r\n";
$sHeaders .= 'Content-Transfer-Encoding: 8bit' . "\r\n";
foreach ($this->m_aHeaders as $sKey => $sValue)
$aData = unserialize($sSerializedMessage);
$oMessage = new Email();
if (array_key_exists('body', $aData))
{
$sHeaders .= "$sKey: $sValue\r\n";
$oMessage->SetBody($aData['body']['body'], $aData['body']['mimeType']);
}
if (array_key_exists('message_id', $aData))
{
$oMessage->SetMessageId($aData['message_id']);
}
if (array_key_exists('bcc', $aData))
{
$oMessage->SetRecipientBCC($aData['bcc']);
}
if (array_key_exists('cc', $aData))
{
$oMessage->SetRecipientCC($aData['cc']);
}
if (array_key_exists('from', $aData))
{
$oMessage->SetRecipientFrom($aData['from']['address'], $aData['from']['label']);
}
if (array_key_exists('reply_to', $aData))
{
$oMessage->SetRecipientReplyTo($aData['reply_to']);
}
if (array_key_exists('to', $aData))
{
$oMessage->SetRecipientTO($aData['to']);
}
if (array_key_exists('subject', $aData))
{
$oMessage->SetSubject($aData['subject']);
}
if (array_key_exists('headers', $aData))
{
foreach($aData['headers'] as $sKey => $sValue)
{
$oMessage->AddToHeader($sKey, $sValue);
}
}
if (array_key_exists('parts', $aData))
{
foreach($aData['parts'] as $aPart)
{
$oMessage->AddPart($aPart['text'], $aPart['mimeType']);
}
}
if (array_key_exists('attachments', $aData))
{
foreach($aData['attachments'] as $aAttachment)
{
$oMessage->AddAttachment(base64_decode($aAttachment['data']), $aAttachment['filename'], $aAttachment['mimeType']);
}
}
return $oMessage;
}
protected function SendAsynchronous(&$aIssues, $oLog = null)
{
try
{
AsyncSendEmail::AddToQueue($this, $oLog);
}
catch(Exception $e)
{
$aIssues = array($e->GetMessage());
return EMAIL_SEND_ERROR;
}
$aIssues = array();
return EMAIL_SEND_PENDING;
}
protected function SendSynchronous(&$aIssues, $oLog = null)
{
$this->LoadConfig();
$sTransport = self::$m_oConfig->Get('email_transport');
switch ($sTransport)
{
case 'SMTP':
$sHost = self::$m_oConfig->Get('email_transport_smtp.host');
$sPort = self::$m_oConfig->Get('email_transport_smtp.port');
$sEncryption = self::$m_oConfig->Get('email_transport_smtp.encryption');
$sUserName = self::$m_oConfig->Get('email_transport_smtp.username');
$sPassword = self::$m_oConfig->Get('email_transport_smtp.password');
$oTransport = Swift_SmtpTransport::newInstance($sHost, $sPort, $sEncryption);
if (strlen($sUserName) > 0)
{
$oTransport->setUsername($sUserName);
$oTransport->setPassword($sPassword);
}
break;
case 'Null':
$oTransport = Swift_NullTransport::newInstance();
break;
case 'LogFile':
$oTransport = Swift_LogFileTransport::newInstance();
$oTransport->setLogFile(APPROOT.'log/mail.log');
break;
case 'PHPMail':
default:
$oTransport = Swift_MailTransport::newInstance();
}
// Under Windows (not yet proven for Linux/PHP) mail may issue a warning
// that I could not mask (tried error_reporting(), etc.)
$this->m_aMailErrors = array();
set_error_handler(array($this, 'mail_error_handler'));
$bRes = mail
(
$this->m_sTo,
$this->m_sSubject,
$this->m_sBody,
$sHeaders
);
restore_error_handler();
if (!$bRes && empty($this->m_aMailErrors))
$oMailer = Swift_Mailer::newInstance($oTransport);
$aFailedRecipients = array();
$iSent = $oMailer->send($this->m_oMessage, $aFailedRecipients);
if ($iSent === 0)
{
$this->m_aMailErrors[] = 'Unknown reason';
// Beware: it seems that $aFailedRecipients sometimes contains the recipients that actually received the message !!!
IssueLog::Warning('Email sending failed: Some recipients were invalid, aFailedRecipients contains: '.implode(', ', $aFailedRecipients));
$aIssues = array('Some recipients were invalid.');
return EMAIL_SEND_ERROR;
}
else
{
$aIssues = array();
return EMAIL_SEND_OK;
}
return $this->m_aMailErrors;
}
protected function AddToHeader($sKey, $sValue)
public function Send(&$aIssues, $bForceSynchronous = false, $oLog = null)
{
if ($bForceSynchronous)
{
return $this->SendSynchronous($aIssues, $oLog);
}
else
{
$bConfigASYNC = MetaModel::GetConfig()->Get('email_asynchronous');
if ($bConfigASYNC)
{
return $this->SendAsynchronous($aIssues, $oLog);
}
else
{
return $this->SendSynchronous($aIssues, $oLog);
}
}
}
public function AddToHeader($sKey, $sValue)
{
if (!array_key_exists('headers', $this->m_aData))
{
$this->m_aData['headers'] = array();
}
$this->m_aData['headers'][$sKey] = $sValue;
if (strlen($sValue) > 0)
{
$this->m_aHeaders[$sKey] = $sValue;
$oHeaders = $this->m_oMessage->getHeaders();
switch(strtolower($sKey))
{
default:
$oHeaders->addTextHeader($sKey, $sValue);
}
}
}
public function SetBody($sBody)
public function SetMessageId($sId)
{
$this->m_sBody = $sBody;
$this->m_aData['message_id'] = $sId;
// Note: Swift will add the angle brackets for you
// so let's remove the angle brackets if present, for historical reasons
$sId = str_replace(array('<', '>'), '', $sId);
$oMsgId = $this->m_oMessage->getHeaders()->get('Message-ID');
$oMsgId->SetId($sId);
}
public function SetReferences($sReferences)
{
$this->AddToHeader('References', $sReferences);
}
public function SetSubject($aSubject)
public function SetBody($sBody, $sMimeType = 'text/html')
{
$this->m_sSubject = $aSubject;
$this->m_aData['body'] = array('body' => $sBody, 'mimeType' => $sMimeType);
$this->m_oMessage->setBody($sBody, $sMimeType);
}
public function AddPart($sText, $sMimeType = 'text/html')
{
if (!array_key_exists('parts', $this->m_aData))
{
$this->m_aData['parts'] = array();
}
$this->m_aData['parts'][] = array('text' => $sText, 'mimeType' => $sMimeType);
$this->m_oMessage->addPart($sText, $sMimeType);
}
public function AddAttachment($data, $sFileName, $sMimeType)
{
if (!array_key_exists('attachments', $this->m_aData))
{
$this->m_aData['attachments'] = array();
}
$this->m_aData['attachments'][] = array('data' => base64_encode($data), 'filename' => $sFileName, 'mimeType' => $sMimeType);
$this->m_oMessage->attach(Swift_Attachment::newInstance($data, $sFileName, $sMimeType));
}
public function SetSubject($sSubject)
{
$this->m_aData['subject'] = $sSubject;
$this->m_oMessage->setSubject($sSubject);
}
public function GetSubject()
{
return $this->m_oMessage->getSubject();
}
/**
* Helper to transform and sanitize addresses
* - get rid of empty addresses
*/
protected function AddressStringToArray($sAddressCSVList)
{
$aAddresses = array();
foreach(explode(',', $sAddressCSVList) as $sAddress)
{
$sAddress = trim($sAddress);
if (strlen($sAddress) > 0)
{
$aAddresses[] = $sAddress;
}
}
return $aAddresses;
}
public function SetRecipientTO($sAddress)
{
$this->m_sTo = $sAddress;
$this->m_aData['to'] = $sAddress;
if (!empty($sAddress))
{
$aAddresses = $this->AddressStringToArray($sAddress);
$this->m_oMessage->setTo($aAddresses);
}
}
public function GetRecipientTO($bAsString = false)
{
$aRes = $this->m_oMessage->getTo();
if ($aRes === null)
{
// There is no "To" header field
$aRes = array();
}
if ($bAsString)
{
$aStrings = array();
foreach ($aRes as $sEmail => $sName)
{
if (is_null($sName))
{
$aStrings[] = $sEmail;
}
else
{
$sName = str_replace(array('<', '>'), '', $sName);
$aStrings[] = "$sName <$sEmail>";
}
}
return implode(', ', $aStrings);
}
else
{
return $aRes;
}
}
public function SetRecipientCC($sAddress)
{
$this->AddToHeader('Cc', $sAddress);
$this->m_aData['cc'] = $sAddress;
if (!empty($sAddress))
{
$aAddresses = $this->AddressStringToArray($sAddress);
$this->m_oMessage->setCc($aAddresses);
}
}
public function SetRecipientBCC($sAddress)
{
$this->AddToHeader('Bcc', $sAddress);
$this->m_aData['bcc'] = $sAddress;
if (!empty($sAddress))
{
$aAddresses = $this->AddressStringToArray($sAddress);
$this->m_oMessage->setBcc($aAddresses);
}
}
public function SetRecipientFrom($sAddress)
public function SetRecipientFrom($sAddress, $sLabel = '')
{
$this->AddToHeader('From', $sAddress);
// This is required on Windows because otherwise I would get the error
// "sendmail_from" not set in php.ini" even if it is correctly working
// (apparently, once it worked the SMTP server won't claim anymore for it)
ini_set("sendmail_from", $sAddress);
$this->m_aData['from'] = array('address' => $sAddress, 'label' => $sLabel);
if ($sLabel != '')
{
$this->m_oMessage->setFrom(array($sAddress => $sLabel));
}
else if (!empty($sAddress))
{
$this->m_oMessage->setFrom($sAddress);
}
}
public function SetRecipientReplyTo($sAddress)
{
$this->AddToHeader('Reply-To', $sAddress);
$this->m_aData['reply_to'] = $sAddress;
if (!empty($sAddress))
{
$this->m_oMessage->setReplyTo($sAddress);
}
}
}
?>
/////////////////////////////////////////////////////////////////////////////////////
/**
* Extension to SwiftMailer: "debug" transport that pretends messages have been sent,
* but just log them to a file.
*
* @package Swift
* @author Denis Flaven
*/
class Swift_Transport_LogFileTransport extends Swift_Transport_NullTransport
{
protected $sLogFile;
/**
* Sends the given message.
*
* @param Swift_Mime_Message $message
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int The number of sent emails
*/
public function send(Swift_Mime_Message $message, &$failedRecipients = null)
{
$hFile = @fopen($this->sLogFile, 'a');
if ($hFile)
{
$sTxt = "================== ".date('Y-m-d H:i:s')." ==================\n";
$sTxt .= $message->toString()."\n";
@fwrite($hFile, $sTxt);
@fclose($hFile);
}
return parent::send($message, $failedRecipients);
}
public function setLogFile($sFilename)
{
$this->sLogFile = $sFilename;
}
}
/**
* Pretends messages have been sent, but just log them to a file.
*
* @package Swift
* @author Denis Flaven
*/
class Swift_LogFileTransport extends Swift_Transport_LogFileTransport
{
/**
* Create a new LogFileTransport.
*/
public function __construct()
{
call_user_func_array(
array($this, 'Swift_Transport_LogFileTransport::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('transport.null')
);
}
/**
* Create a new LogFileTransport instance.
*
* @return Swift_LogFileTransport
*/
public static function newInstance()
{
return new self();
}
}

View File

@@ -1,31 +1,32 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2013 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Persistent class Event and derived
* Application internal events
* There is also a file log
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class Event extends cmdbAbstractObject
class Event extends DBObject implements iDisplay
{
public static function Init()
{
@@ -40,20 +41,85 @@ class Event extends cmdbAbstractObject
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
"display_template" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeText("message", array("allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('message', 'date', 'userinfo')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'finalclass', 'message')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
/**
* Maps the given context parameter name to the appropriate filter/search code for this class
* @param string $sContextParam Name of the context parameter, i.e. 'org_id'
* @return string Filter code, i.e. 'customer_id'
*/
public static function MapContextParam($sContextParam)
{
if ($sContextParam == 'menu')
{
return null;
}
else
{
return $sContextParam;
}
}
/**
* This function returns a 'hilight' CSS class, used to hilight a given row in a table
* There are currently (i.e defined in the CSS) 4 possible values HILIGHT_CLASS_CRITICAL,
* HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE
* To Be overridden by derived classes
* @param void
* @return String The desired higlight class for the object/row
*/
public function GetHilightClass()
{
// Possible return values are:
// HILIGHT_CLASS_CRITICAL, HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE
return HILIGHT_CLASS_NONE; // Not hilighted by default
}
public static function GetUIPage()
{
return 'UI.php';
}
function DisplayDetails(WebPage $oPage, $bEditMode = false)
{
// Object's details
//$this->DisplayBareHeader($oPage, $bEditMode);
$oPage->AddTabContainer(OBJECT_PROPERTIES_TAB);
$oPage->SetCurrentTabContainer(OBJECT_PROPERTIES_TAB);
$oPage->SetCurrentTab(Dict::S('UI:PropertiesTab'));
$this->DisplayBareProperties($oPage, $bEditMode);
}
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
if ($bEditMode) return array(); // Not editable
$aDetails = array();
$sClass = get_class($this);
$aZList = MetaModel::FlattenZlist(MetaModel::GetZListItems($sClass, 'details'));
foreach( $aZList as $sAttCode)
{
$sDisplayValue = $this->GetAsHTML($sAttCode);
$aDetails[] = array('label' => '<span title="'.MetaModel::GetDescription($sClass, $sAttCode).'">'.MetaModel::GetLabel($sClass, $sAttCode).'</span>', 'value' => $sDisplayValue);
}
$oPage->Details($aDetails);
return array();
}
}
class EventNotification extends Event
@@ -71,6 +137,10 @@ class EventNotification extends Event
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
"order_by_default" => array('date' => false),
'indexes' => array(
array('object_id'),
)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
@@ -79,8 +149,8 @@ class EventNotification extends Event
MetaModel::Init_AddAttribute(new AttributeInteger("object_id", array("allowed_values"=>null, "sql"=>"object_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'trigger_id', 'action_id', 'object_id')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo', 'trigger_id', 'action_id', 'object_id')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'message')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
@@ -103,6 +173,7 @@ class EventNotificationEmail extends EventNotification
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
@@ -111,11 +182,12 @@ class EventNotificationEmail extends EventNotification
MetaModel::Init_AddAttribute(new AttributeText("bcc", array("allowed_values"=>null, "sql"=>"bcc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("from", array("allowed_values"=>null, "sql"=>"from", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("body", array("allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeHTML("body", array("allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeTable("attachments", array("allowed_values"=>null, "sql"=>"attachments", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_id', 'to', 'cc', 'bcc', 'from', 'subject', 'body')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'message', 'to', 'subject')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_id', 'to', 'cc', 'bcc', 'from', 'subject', 'body', 'attachments')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'message', 'to', 'subject', 'attachments')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
@@ -139,6 +211,7 @@ class EventIssue extends Event
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
@@ -238,6 +311,7 @@ class EventWebService extends Event
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
@@ -258,4 +332,80 @@ class EventWebService extends Event
}
}
class EventRestService extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_restservice",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("operation", array("allowed_values"=>null, "sql"=>"operation", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("version", array("allowed_values"=>null, "sql"=>"version", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("json_input", array("allowed_values"=>null, "sql"=>"json_input", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("code", array("allowed_values"=>null, "sql"=>"code", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("json_output", array("allowed_values"=>null, "sql"=>"json_output", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("provider", array("allowed_values"=>null, "sql"=>"provider", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'operation', 'version', 'json_input', 'message', 'code', 'json_output', 'provider')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'operation', 'message')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
class EventLoginUsage extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_loginusage",
"db_key_field" => "id",
"db_finalclass_field" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
$aZList = array('date', 'user_id');
if (MetaModel::IsValidAttCode('Contact', 'name'))
{
MetaModel::Init_AddAttribute(new AttributeExternalField("contact_name", array("allowed_values"=>null, "extkey_attcode"=>"user_id", "target_attcode"=>"contactid", "is_null_allowed"=>true, "depends_on"=>array())));
$aZList[] = 'contact_name';
}
if (MetaModel::IsValidAttCode('Contact', 'email'))
{
MetaModel::Init_AddAttribute(new AttributeExternalField("contact_email", array("allowed_values"=>null, "extkey_attcode"=>"user_id", "target_attcode"=>"email", "is_null_allowed"=>true, "depends_on"=>array())));
$aZList[] = 'contact_email';
}
// Display lists
MetaModel::Init_SetZListItems('details', array_merge($aZList, array('userinfo', 'message'))); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array_merge($aZList, array('userinfo'))); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', $aZList); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
?>

View File

@@ -0,0 +1,274 @@
<?php
// Copyright (C) 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/>
/**
* Bulk export: Excel (xlsx) export
*
* @copyright Copyright (C) 2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'application/xlsxwriter.class.php');
class ExcelBulkExport extends TabularBulkExport
{
protected $sData;
public function __construct()
{
parent::__construct();
$this->aStatusInfo['status'] = 'not_started';
$this->aStatusInfo['position'] = 0;
}
public function Cleanup()
{
@unlink($this->aStatusInfo['tmp_file']);
parent::Cleanup();
}
public function DisplayUsage(Page $oP)
{
$oP->p(" * xlsx format options:");
$oP->p(" *\tfields: the comma separated list of field codes to export (e.g: name,org_id,service_name...).");
}
public function EnumFormParts()
{
return array_merge(parent::EnumFormParts(), array('interactive_fields_xlsx' => array('interactive_fields_xlsx')));
}
public function DisplayFormPart(WebPage $oP, $sPartId)
{
switch($sPartId)
{
case 'interactive_fields_xlsx':
$this->GetInteractiveFieldsWidget($oP, 'interactive_fields_xlsx');
break;
default:
return parent:: DisplayFormPart($oP, $sPartId);
}
}
protected function SuggestField($sClass, $sAttCode)
{
switch($sAttCode)
{
case 'id': // replace 'id' by 'friendlyname'
$sAttCode = 'friendlyname';
break;
default:
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef instanceof AttributeExternalKey)
{
$sAttCode .= '_friendlyname';
}
}
return parent::SuggestField($sClass, $sAttCode);
}
protected function GetSampleData($oObj, $sAttCode)
{
return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
}
protected function GetValue($oObj, $sAttCode)
{
switch($sAttCode)
{
case 'id':
$sRet = $oObj->GetKey();
break;
default:
$value = $oObj->Get($sAttCode);
if ($value instanceOf ormCaseLog)
{
// Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it!
$sRet = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText()));
}
else if ($value instanceOf DBObjectSet)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
}
else
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetEditValue($value, $oObj);
}
}
return $sRet;
}
public function GetHeader()
{
$oSet = new DBObjectSet($this->oSearch);
$this->aStatusInfo['status'] = 'retrieving';
$this->aStatusInfo['tmp_file'] = $this->MakeTmpFile('data');
$this->aStatusInfo['position'] = 0;
$this->aStatusInfo['total'] = $oSet->Count();
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
$sExtendedAttCode = $aFieldSpec['sFieldSpec'];
$sAttCode = $aFieldSpec['sAttCode'];
$sColLabel = $aFieldSpec['sColLabel'];
switch($sAttCode)
{
case 'id':
$sType = '0';
break;
default:
$oAttDef = MetaModel::GetAttributeDef($aFieldSpec['sClass'], $aFieldSpec['sAttCode']);
$sType = 'string';
if($oAttDef instanceof AttributeDateTime)
{
$sType = 'datetime';
}
}
$aTableHeaders[] = array('label' => $sColLabel, 'type' => $sType);
}
$sRow = json_encode($aTableHeaders);
$hFile = @fopen($this->aStatusInfo['tmp_file'], 'ab');
if ($hFile === false)
{
throw new Exception('ExcelBulkExport: Failed to open temporary data file: "'.$this->aStatusInfo['tmp_file'].'" for writing.');
}
fwrite($hFile, $sRow."\n");
fclose($hFile);
return '';
}
public function GetNextChunk(&$aStatus)
{
$sRetCode = 'run';
$iPercentage = 0;
$hFile = fopen($this->aStatusInfo['tmp_file'], 'ab');
$oSet = new DBObjectSet($this->oSearch);
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
$this->OptimizeColumnLoad($oSet);
$iCount = 0;
$iPreviousTimeLimit = ini_get('max_execution_time');
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
while($aRow = $oSet->FetchAssoc())
{
set_time_limit($iLoopTimeLimit);
$aData = array();
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
$sAlias = $aFieldSpec['sAlias'];
$sAttCode = $aFieldSpec['sAttCode'];
$oObj = $aRow[$sAlias];
$sField = '';
if ($oObj)
{
$sField = $this->GetValue($oObj, $sAttCode);
}
$aData[] = $sField;
}
fwrite($hFile, json_encode($aData)."\n");
$iCount++;
}
set_time_limit($iPreviousTimeLimit);
$this->aStatusInfo['position'] += $this->iChunkSize;
if ($this->aStatusInfo['total'] == 0)
{
$iPercentage = 100;
$sRetCode = 'done'; // Next phase (GetFooter) will be to build the xlsx file
}
else
{
$iPercentage = floor(min(100.0, 100.0*$this->aStatusInfo['position']/$this->aStatusInfo['total']));
}
if ($iCount < $this->iChunkSize)
{
$sRetCode = 'done';
}
$aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage);
return ''; // The actual XLSX file is built in GetFooter();
}
public function GetFooter()
{
$hFile = @fopen($this->aStatusInfo['tmp_file'], 'rb');
if ($hFile === false)
{
throw new Exception('ExcelBulkExport: Failed to open temporary data file: "'.$this->aStatusInfo['tmp_file'].'" for reading.');
}
$sHeaders = fgets($hFile);
$aHeaders = json_decode($sHeaders, true);
$aData = array();
while($sLine = fgets($hFile))
{
$aRow = json_decode($sLine);
$aData[] = $aRow;
}
fclose($hFile);
$fStartExcel = microtime(true);
$writer = new XLSXWriter();
$writer->setAuthor(UserRights::GetUserFriendlyName());
$aHeaderTypes = array();
$aHeaderNames = array();
foreach($aHeaders as $Header)
{
$aHeaderNames[] = $Header['label'];
$aHeaderTypes[] = $Header['type'];
}
$writer->writeSheet($aData,'Sheet1', $aHeaderTypes, $aHeaderNames);
$fExcelTime = microtime(true) - $fStartExcel;
//$this->aStatistics['excel_build_duration'] = $fExcelTime;
$fTime = microtime(true);
$data = $writer->writeToString();
$fExcelSaveTime = microtime(true) - $fTime;
//$this->aStatistics['excel_write_duration'] = $fExcelSaveTime;
@unlink($this->aStatusInfo['tmp_file']);
return $data;
}
public function GetMimeType()
{
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
}
public function GetFileExtension()
{
return 'xlsx';
}
public function GetSupportedFormats()
{
return array('xlsx' => Dict::S('Core:BulkExport:XLSXFormat'));
}
}

View File

@@ -1,575 +1,4 @@
<?php
// Copyright (C) 2010 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* General definition of an expression tree (could be OQL, SQL or whatever)
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
class MissingQueryArgument extends CoreException
{
}
abstract class Expression
{
// recursive translation of identifiers
abstract public function Translate($aTranslationData, $bMatchAll = true);
// recursive rendering (aArgs used as input by default, or used as output if bRetrofitParams set to True
abstract public function Render(&$aArgs = null, $bRetrofitParams = false);
// recursively builds an array of class => fieldname
abstract public function ListRequiredFields();
abstract public function IsTrue();
public function RequiresField($sClass, $sFieldName)
{
// #@# todo - optimize : this is called quite often when building a single query !
$aRequired = $this->ListRequiredFields();
if (!in_array($sClass.'.'.$sFieldName, $aRequired)) return false;
return true;
}
public function serialize()
{
return base64_encode($this->Render());
}
static public function unserialize($sValue)
{
return self::FromOQL(base64_decode($sValue));
}
static public function FromOQL($sConditionExpr)
{
$oOql = new OqlInterpreter($sConditionExpr);
$oExpression = $oOql->ParseExpression();
return $oExpression;
}
public function LogAnd($oExpr)
{
if ($this->IsTrue()) return clone $oExpr;
if ($oExpr->IsTrue()) return clone $this;
return new BinaryExpression($this, 'AND', $oExpr);
}
public function LogOr($oExpr)
{
return new BinaryExpression($this, 'OR', $oExpr);
}
}
class BinaryExpression extends Expression
{
protected $m_oLeftExpr; // filter code or an SQL expression (later?)
protected $m_oRightExpr;
protected $m_sOperator;
public function __construct($oLeftExpr, $sOperator, $oRightExpr)
{
if (!is_object($oLeftExpr))
{
throw new CoreException('Expecting an Expression object on the left hand', array('found_type' => gettype($oLeftExpr)));
}
if (!is_object($oRightExpr))
{
throw new CoreException('Expecting an Expression object on the right hand', array('found_type' => gettype($oRightExpr)));
}
if (!$oLeftExpr instanceof Expression)
{
throw new CoreException('Expecting an Expression object on the left hand', array('found_class' => get_class($oLeftExpr)));
}
if (!$oRightExpr instanceof Expression)
{
throw new CoreException('Expecting an Expression object on the right hand', array('found_class' => get_class($oRightExpr)));
}
$this->m_oLeftExpr = $oLeftExpr;
$this->m_oRightExpr = $oRightExpr;
$this->m_sOperator = $sOperator;
}
public function IsTrue()
{
// return true if we are certain that it will be true
if ($this->m_sOperator == 'AND')
{
if ($this->m_oLeftExpr->IsTrue() && $this->m_oLeftExpr->IsTrue()) return true;
}
return false;
}
public function GetLeftExpr()
{
return $this->m_oLeftExpr;
}
public function GetRightExpr()
{
return $this->m_oRightExpr;
}
public function GetOperator()
{
return $this->m_sOperator;
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
{
$sOperator = $this->GetOperator();
$sLeft = $this->GetLeftExpr()->Render($aArgs, $bRetrofitParams);
$sRight = $this->GetRightExpr()->Render($aArgs, $bRetrofitParams);
return "($sLeft $sOperator $sRight)";
}
public function Translate($aTranslationData, $bMatchAll = true)
{
$oLeft = $this->GetLeftExpr()->Translate($aTranslationData, $bMatchAll);
$oRight = $this->GetRightExpr()->Translate($aTranslationData, $bMatchAll);
return new BinaryExpression($oLeft, $this->GetOperator(), $oRight);
}
public function ListRequiredFields()
{
$aLeft = $this->GetLeftExpr()->ListRequiredFields();
$aRight = $this->GetRightExpr()->ListRequiredFields();
return array_merge($aLeft, $aRight);
}
}
class UnaryExpression extends Expression
{
protected $m_value;
public function __construct($value)
{
$this->m_value = $value;
}
public function IsTrue()
{
// return true if we are certain that it will be true
return ($this->m_value == 1);
}
public function GetValue()
{
return $this->m_value;
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
{
if ($bRetrofitParams)
{
$iParamIndex = count($aArgs) + 1; // 1-based indexation
$aArgs['param'.$iParamIndex] = $this->m_value;
return ':param'.$iParamIndex;
}
else
{
return CMDBSource::Quote($this->m_value);
}
}
public function Translate($aTranslationData, $bMatchAll = true)
{
return clone $this;
}
public function ListRequiredFields()
{
return array();
}
}
class ScalarExpression extends UnaryExpression
{
public function __construct($value)
{
if (!is_scalar($value))
{
throw new CoreException('Attempt to create a scalar expression from a non scalar', array('var_type'=>gettype($value)));
}
parent::__construct($value);
}
}
class TrueExpression extends ScalarExpression
{
public function __construct()
{
parent::__construct(1);
}
public function IsTrue()
{
return true;
}
}
class FalseExpression extends ScalarExpression
{
public function __construct()
{
parent::__construct(0);
}
public function IsTrue()
{
return false;
}
}
class FieldExpression extends UnaryExpression
{
protected $m_sParent;
protected $m_sName;
public function __construct($sName, $sParent = '')
{
parent::__construct("$sParent.$sName");
$this->m_sParent = $sParent;
$this->m_sName = $sName;
}
public function IsTrue()
{
// return true if we are certain that it will be true
return false;
}
public function GetParent() {return $this->m_sParent;}
public function GetName() {return $this->m_sName;}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
{
if (empty($this->m_sParent))
{
return "`{$this->m_sName}`";
}
return "`{$this->m_sParent}`.`{$this->m_sName}`";
}
public function Translate($aTranslationData, $bMatchAll = true)
{
if (!array_key_exists($this->m_sParent, $aTranslationData))
{
if ($bMatchAll) throw new CoreException('Unknown parent id in translation table', array('parent_id' => $this->m_sParent, 'translation_table' => array_keys($aTranslationData)));
return clone $this;
}
if (!array_key_exists($this->m_sName, $aTranslationData[$this->m_sParent]))
{
if (!array_key_exists('*', $aTranslationData[$this->m_sParent]))
{
// #@# debug - if ($bMatchAll) MyHelpers::var_dump_html($aTranslationData, true);
if ($bMatchAll) throw new CoreException('Unknown name in translation table', array('name' => $this->m_sName, 'parent_id' => $this->m_sParent, 'translation_table' => array_keys($aTranslationData[$this->m_sParent])));
return clone $this;
}
$sNewParent = $aTranslationData[$this->m_sParent]['*'];
$sNewName = $this->m_sName;
}
else
{
$sNewParent = $aTranslationData[$this->m_sParent][$this->m_sName][0];
$sNewName = $aTranslationData[$this->m_sParent][$this->m_sName][1];
}
return new FieldExpression($sNewName, $sNewParent);
}
public function ListRequiredFields()
{
return array($this->m_sParent.'.'.$this->m_sName);
}
}
class VariableExpression extends UnaryExpression
{
protected $m_sName;
public function __construct($sName)
{
parent::__construct($sName);
$this->m_sName = $sName;
}
public function IsTrue()
{
// return true if we are certain that it will be true
return false;
}
public function GetName() {return $this->m_sName;}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
{
if (is_null($aArgs))
{
return ':'.$this->m_sName;
}
elseif (array_key_exists($this->m_sName, $aArgs))
{
return CMDBSource::Quote($aArgs[$this->m_sName]);
}
elseif ($bRetrofitParams)
{
//$aArgs[$this->m_sName] = null;
return ':'.$this->m_sName;
}
else
{
throw new MissingQueryArgument('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>$aArgs));
}
}
}
// Temporary, until we implement functions and expression casting!
// ... or until we implement a real full text search based in the MATCH() expression
class ListExpression extends Expression
{
protected $m_aExpressions;
public function __construct($aExpressions)
{
$this->m_aExpressions = $aExpressions;
}
public static function FromScalars($aScalars)
{
$aExpressions = array();
foreach($aScalars as $value)
{
$aExpressions[] = new ScalarExpression($value);
}
return new ListExpression($aExpressions);
}
public function IsTrue()
{
// return true if we are certain that it will be true
return false;
}
public function GetItems()
{
return $this->m_aExpressions;
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
{
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
$aRes[] = $oExpr->Render($aArgs, $bRetrofitParams);
}
return '('.implode(', ', $aRes).')';
}
public function Translate($aTranslationData, $bMatchAll = true)
{
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
$aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
}
return new ListExpression($aRes);
}
public function ListRequiredFields()
{
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
$aRes = array_merge($aRes, $oExpr->ListRequiredFields());
}
return $aRes;
}
}
class FunctionExpression extends Expression
{
protected $m_sVerb;
protected $m_aArgs; // array of expressions
public function __construct($sVerb, $aArgExpressions)
{
$this->m_sVerb = $sVerb;
$this->m_aArgs = $aArgExpressions;
}
public function IsTrue()
{
// return true if we are certain that it will be true
return false;
}
public function GetVerb()
{
return $this->m_sVerb;
}
public function GetArgs()
{
return $this->m_aArgs;
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
{
$aRes = array();
foreach ($this->m_aArgs as $oExpr)
{
$aRes[] = $oExpr->Render($aArgs, $bRetrofitParams);
}
return $this->m_sVerb.'('.implode(', ', $aRes).')';
}
public function Translate($aTranslationData, $bMatchAll = true)
{
$aRes = array();
foreach ($this->m_aArgs as $oExpr)
{
$aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
}
return new FunctionExpression($this->m_sVerb, $aRes);
}
public function ListRequiredFields()
{
$aRes = array();
foreach ($this->m_aArgs as $oExpr)
{
$aRes = array_merge($aRes, $oExpr->ListRequiredFields());
}
return $aRes;
}
}
class IntervalExpression extends Expression
{
protected $m_oValue; // expression
protected $m_sUnit;
public function __construct($oValue, $sUnit)
{
$this->m_oValue = $oValue;
$this->m_sUnit = $sUnit;
}
public function IsTrue()
{
// return true if we are certain that it will be true
return false;
}
public function GetValue()
{
return $this->m_oValue;
}
public function GetUnit()
{
return $this->m_sUnit;
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
{
return 'INTERVAL '.$this->m_oValue->Render($aArgs, $bRetrofitParams).' '.$this->m_sUnit;
}
public function Translate($aTranslationData, $bMatchAll = true)
{
return new IntervalExpression($this->m_oValue->Translate($aTranslationData, $bMatchAll), $this->m_sUnit);
}
public function ListRequiredFields()
{
return array();
}
}
class CharConcatExpression extends Expression
{
protected $m_aExpressions;
public function __construct($aExpressions)
{
$this->m_aExpressions = $aExpressions;
}
public function IsTrue()
{
// return true if we are certain that it will be true
return false;
}
public function GetItems()
{
return $this->m_aExpressions;
}
// recursive rendering
public function Render(&$aArgs = null, $bRetrofitParams = false)
{
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
$sCol = $oExpr->Render($aArgs, $bRetrofitParams);
// Concat will be globally NULL if one single argument is null !
$aRes[] = "COALESCE($sCol, '')";
}
return "CAST(CONCAT(".implode(', ', $aRes).") AS CHAR)";
}
public function Translate($aTranslationData, $bMatchAll = true)
{
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
$aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
}
return new CharConcatExpression($aRes);
}
public function ListRequiredFields()
{
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
$aRes = array_merge($aRes, $oExpr->ListRequiredFields());
}
return $aRes;
}
}
?>
// The file has been moved in iTop 2.2.0+ (revision 3803)
// Preserve backward compatibility with some external tools (Cf. toolkit)
require_once(APPROOT.'core/oql/expression.class.inc.php');

View File

@@ -1,27 +1,28 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Definition of a filter
* Most of the time, a filter corresponds to an attribute, but we could imagine other search criteria
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -50,21 +51,6 @@ abstract class FilterDefinition
$this->ConsistencyCheck();
}
public function OverloadParams($aParams)
{
foreach ($aParams as $sParam => $value)
{
if (!array_key_exists($sParam, $this->m_aParams))
{
throw new CoreException("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}");
}
else
{
$this->m_aParams[$sParam] = $value;
}
}
}
// to be overloaded
static protected function ListExpectedParams()
{
@@ -170,12 +156,13 @@ class FilterFromAttribute extends FilterDefinition
return array_merge(parent::ListExpectedParams(), array("refattribute"));
}
public function __construct($oRefAttribute, $aParam = array())
public function __construct($oRefAttribute, $sSuffix = '')
{
// In this very specific case, the code is the one of the attribute
// (this to get a very very simple syntax upon declaration)
$aParam = array();
$aParam["refattribute"] = $oRefAttribute;
parent::__construct($oRefAttribute->GetCode(), $aParam);
parent::__construct($oRefAttribute->GetCode().$sSuffix, $aParam);
}
public function GetType() {return "Basic";}
@@ -198,10 +185,10 @@ class FilterFromAttribute extends FilterDefinition
return $oAttDef->GetValuesDef();
}
public function GetAllowedValues($aArgs = array(), $sBeginsWith = '')
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
$oAttDef = $this->Get("refattribute");
return $oAttDef->GetAllowedValues($aArgs, $sBeginsWith);
return $oAttDef->GetAllowedValues($aArgs, $sContains);
}
public function GetOperators()

View File

@@ -0,0 +1,195 @@
<?php
// Copyright (C) 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/>
/**
* Bulk export: HTML export
*
* @copyright Copyright (C) 2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class HTMLBulkExport extends TabularBulkExport
{
public function DisplayUsage(Page $oP)
{
$oP->p(" * html format options:");
$oP->p(" *\tfields: (mandatory) the comma separated list of field codes to export (e.g: name,org_id,service_name...).");
}
public function EnumFormParts()
{
return array_merge(parent::EnumFormParts(), array('interactive_fields_html' => array('interactive_fields_html')));
}
public function DisplayFormPart(WebPage $oP, $sPartId)
{
switch($sPartId)
{
case 'interactive_fields_html':
$this->GetInteractiveFieldsWidget($oP, 'interactive_fields_html');
break;
default:
return parent:: DisplayFormPart($oP, $sPartId);
}
}
protected function GetSampleData($oObj, $sAttCode)
{
return $this->GetValue($oObj, $sAttCode);
}
protected function GetValue($oObj, $sAttCode)
{
switch($sAttCode)
{
case 'id':
$sRet = $oObj->GetHyperlink();
break;
default:
$value = $oObj->Get($sAttCode);
if ($value instanceof ormCaseLog)
{
$sRet = $value->GetAsSimpleHtml();
}
elseif ($value instanceof ormStopWatch)
{
$sRet = $value->GetTimeSpent();
}
else
{
$sRet = $oObj->GetAsHtml($sAttCode);
}
}
return $sRet;
}
public function GetHeader()
{
$sData = '';
$oSet = new DBObjectSet($this->oSearch);
$this->aStatusInfo['status'] = 'running';
$this->aStatusInfo['position'] = 0;
$this->aStatusInfo['total'] = $oSet->Count();
$sData .= "<table class=\"listResults\">\n";
$sData .= "<thead>\n";
$sData .= "<tr>\n";
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
$sData .= "<th>".$aFieldSpec['sColLabel']."</th>\n";
}
$sData .= "</tr>\n";
$sData .= "</thead>\n";
$sData .= "<tbody>\n";
return $sData;
}
public function GetNextChunk(&$aStatus)
{
$sRetCode = 'run';
$iPercentage = 0;
$oSet = new DBObjectSet($this->oSearch);
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
$this->OptimizeColumnLoad($oSet);
$sFirstAlias = $this->oSearch->GetClassAlias();
$iCount = 0;
$sData = '';
$iPreviousTimeLimit = ini_get('max_execution_time');
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
while($aRow = $oSet->FetchAssoc())
{
set_time_limit($iLoopTimeLimit);
$oMainObj = $aRow[$sFirstAlias];
$sHilightClass = '';
if ($oMainObj)
{
$sHilightClass = $aRow[$sFirstAlias]->GetHilightClass();
}
if ($sHilightClass != '')
{
$sData .= "<tr class=\"$sHilightClass\">";
}
else
{
$sData .= "<tr>";
}
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
$sAlias = $aFieldSpec['sAlias'];
$sAttCode = $aFieldSpec['sAttCode'];
$oObj = $aRow[$sAlias];
$sField = '';
if ($oObj)
{
$sField = $this->GetValue($oObj, $sAttCode);
}
$sValue = ($sField === '') ? '&nbsp;' : $sField;
$sData .= "<td>$sValue</td>";
}
$sData .= "</tr>";
$iCount++;
}
set_time_limit($iPreviousTimeLimit);
$this->aStatusInfo['position'] += $this->iChunkSize;
if ($this->aStatusInfo['total'] == 0)
{
$iPercentage = 100;
}
else
{
$iPercentage = floor(min(100.0, 100.0*$this->aStatusInfo['position']/$this->aStatusInfo['total']));
}
if ($iCount < $this->iChunkSize)
{
$sRetCode = 'done';
}
$aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage);
return $sData;
}
public function GetFooter()
{
$sData = "</tbody>\n";
$sData .= "</table>\n";
return $sData;
}
public function GetSupportedFormats()
{
return array('html' => Dict::S('Core:BulkExport:HTMLFormat'));
}
public function GetMimeType()
{
return 'text/html';
}
public function GetFileExtension()
{
return 'html';
}
}

View File

@@ -1,96 +1,256 @@
<?php
// Copyright (C) 2010 Combodo SARL
// Copyright (C) 2010-2012 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
// This file is part of iTop.
//
// This program is distributed in the hope that it will be useful,
// 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 General Public License for more details.
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Measures operations duration, memory usage, etc. (and some other KPIs)
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ExecutionKPI
{
static protected $m_bEnabled_Duration = false;
static protected $m_bEnabled_Memory = false;
static protected $m_bBlameCaller = false;
static protected $m_sAllowedUser = '*';
static protected $m_aStats = array();
static protected $m_aStats = array(); // Recurrent operations
static protected $m_aExecData = array(); // One shot operations
protected $m_fStarted = null;
protected $m_iInitialMemory = null;
static public function EnableDuration()
static public function EnableDuration($iLevel)
{
self::$m_bEnabled_Duration = true;
if ($iLevel > 0)
{
self::$m_bEnabled_Duration = true;
if ($iLevel > 1)
{
self::$m_bBlameCaller = true;
}
}
}
static public function EnableMemory()
static public function EnableMemory($iLevel)
{
self::$m_bEnabled_Memory = true;
if ($iLevel > 0)
{
self::$m_bEnabled_Memory = true;
}
}
/**
* @param string sUser A user login or * for all users
*/
static public function SetAllowedUser($sUser)
{
self::$m_sAllowedUser = $sUser;
}
static public function IsEnabled()
{
if (self::$m_bEnabled_Duration || self::$m_bEnabled_Memory)
{
if ((self::$m_sAllowedUser == '*') || (UserRights::GetUser() == trim(self::$m_sAllowedUser)))
{
return true;
}
}
return false;
}
static public function GetDescription()
{
$aFeatures = array();
if (self::$m_bEnabled_Duration) $aFeatures[] = 'Duration';
if (self::$m_bEnabled_Memory) $aFeatures[] = 'Memory usage';
$sFeatures = implode(', ', $aFeatures);
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
return "KPI logging is active for $sFor. Measures: $sFeatures";
}
static public function ReportStats()
{
if (!self::IsEnabled()) return;
global $fItopStarted;
$sExecId = microtime(); // id to differentiate the hrefs!
$aBeginTimes = array();
foreach (self::$m_aExecData as $aOpStats)
{
$aBeginTimes[] = $aOpStats['time_begin'];
}
array_multisort($aBeginTimes, self::$m_aExecData);
$sTableStyle = 'background-color: #ccc; margin: 10px;';
self::Report("<hr/>");
self::Report("<div style=\"background-color: grey; padding: 10px;\">");
self::Report("<h3><a name=\"".md5($sExecId)."\">KPIs</a> - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")</h3>");
self::Report("<p>".date('Y-m-d H:i:s', $fItopStarted)."</p>");
self::Report("<p>log_kpi_user_id: ".MetaModel::GetConfig()->Get('log_kpi_user_id')."</p>");
self::Report("<div>");
self::Report("<table border=\"1\" style=\"$sTableStyle\">");
self::Report("<thead>");
self::Report(" <th>Operation</th><th>Begin</th><th>End</th><th>Duration</th><th>Memory start</th><th>Memory end</th><th>Memory peak</th>");
self::Report("</thead>");
foreach (self::$m_aExecData as $aOpStats)
{
$sOperation = $aOpStats['op'];
$sBegin = $sEnd = $sDuration = $sMemBegin = $sMemEnd = $sMemPeak = '?';
$sBegin = round($aOpStats['time_begin'], 3);
$sEnd = round($aOpStats['time_end'], 3);
$fDuration = $aOpStats['time_end'] - $aOpStats['time_begin'];
$sDuration = round($fDuration, 3);
if (isset($aOpStats['mem_begin']))
{
$sMemBegin = self::MemStr($aOpStats['mem_begin']);
$sMemEnd = self::MemStr($aOpStats['mem_end']);
if (isset($aOpStats['mem_peak']))
{
$sMemPeak = self::MemStr($aOpStats['mem_peak']);
}
}
self::Report("<tr>");
self::Report(" <td>$sOperation</td><td>$sBegin</td><td>$sEnd</td><td>$sDuration</td><td>$sMemBegin</td><td>$sMemEnd</td><td>$sMemPeak</td>");
self::Report("</tr>");
}
self::Report("</table>");
self::Report("</div>");
$aConsolidatedStats = array();
foreach (self::$m_aStats as $sOperation => $aOpStats)
{
echo "<h2>KPIs for $sOperation</h2>\n";
$fTotalOp = 0;
$iTotalOp = 0;
$fMinOp = null;
$fMaxOp = 0;
echo "<ul>\n";
$sMaxOpArguments = null;
foreach ($aOpStats as $sArguments => $aEvents)
{
foreach ($aEvents as $aEventData)
{
$fDuration = $aEventData['time'];
$fTotalOp += $fDuration;
$iTotalOp++;
$fMinOp = is_null($fMinOp) ? $fDuration : min($fMinOp, $fDuration);
if ($fDuration > $fMaxOp)
{
$sMaxOpArguments = $sArguments;
$fMaxOp = $fDuration;
}
}
}
$aConsolidatedStats[$sOperation] = array(
'count' => $iTotalOp,
'duration' => $fTotalOp,
'min' => $fMinOp,
'max' => $fMaxOp,
'avg' => $fTotalOp / $iTotalOp,
'max_args' => $sMaxOpArguments
);
}
self::Report("<div>");
self::Report("<table border=\"1\" style=\"$sTableStyle\">");
self::Report("<thead>");
self::Report(" <th>Operation</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th><th>Avg</th>");
self::Report("</thead>");
foreach ($aConsolidatedStats as $sOperation => $aOpStats)
{
$sOperation = '<a href="#'.md5($sExecId.$sOperation).'">'.$sOperation.'</a>';
$sCount = $aOpStats['count'];
$sDuration = round($aOpStats['duration'], 3);
$sMin = round($aOpStats['min'], 3);
$sMax = '<a href="#'.md5($sExecId.$aOpStats['max_args']).'">'.round($aOpStats['max'], 3).'</a>';
$sAvg = round($aOpStats['avg'], 3);
self::Report("<tr>");
self::Report(" <td>$sOperation</td><td>$sCount</td><td>$sDuration</td><td>$sMin</td><td>$sMax</td><td>$sAvg</td>");
self::Report("</tr>");
}
self::Report("</table>");
self::Report("</div>");
self::Report("</div>");
// Report operation details
foreach (self::$m_aStats as $sOperation => $aOpStats)
{
$sOperationHtml = '<a name="'.md5($sExecId.$sOperation).'">'.$sOperation.'</a>';
self::Report("<h4>$sOperationHtml</h4>");
self::Report("<p><a href=\"#".md5($sExecId)."\">Back to page stats</a></p>");
self::Report("<table border=\"1\" style=\"$sTableStyle\">");
self::Report("<thead>");
self::Report(" <th>Operation details (+ blame caller if log_kpi_duration = 2)</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th>");
self::Report("</thead>");
foreach ($aOpStats as $sArguments => $aEvents)
{
$sHtmlArguments = '<a name="'.md5($sExecId.$sArguments).'"><div style="white-space: pre-wrap;">'.$sArguments.'</div></a>';
if ($aConsolidatedStats[$sOperation]['max_args'] == $sArguments)
{
$sHtmlArguments = '<span style="color: red;">'.$sHtmlArguments.'</span>';
}
if (isset($aEvents[0]['callers']))
{
$sHtmlArguments .= '<div style="padding: 10px;">';
$sHtmlArguments .= '<table border="1" bgcolor="#cfc">';
$sHtmlArguments .= '<tr><td colspan="2" bgcolor="#e9b96">Call stack for the <b>FIRST</b> caller</td></tr>';
foreach ($aEvents[0]['callers'] as $aCall)
{
$sHtmlArguments .= '<tr>';
$sHtmlArguments .= '<td>'.$aCall['Function'].'</td>';
$sHtmlArguments .= '<td>'.$aCall['File'].':'.$aCall['Line'].'</td>';
$sHtmlArguments .= '</tr>';
}
$sHtmlArguments .= '</table>';
$sHtmlArguments .= '</div>';
}
$fTotalInter = 0;
$fMinInter = null;
$fMaxInter = 0;
foreach ($aEvents as $fDuration)
foreach ($aEvents as $aEventData)
{
$fDuration = $aEventData['time'];
$fTotalInter += $fDuration;
$fMinInter = is_null($fMinInter) ? $fDuration : min($fMinInter, $fDuration);
$fMaxInter = max($fMaxInter, $fDuration);
$fMinOp = is_null($fMinOp) ? $fDuration : min($fMinOp, $fDuration);
$fMaxOp = max($fMaxOp, $fDuration);
}
$fTotalOp += $fTotalInter;
$iTotalOp++;
$iCountInter = count($aEvents);
$sTotalInter = round($fTotalInter, 3)."s";
if ($iCountInter > 1)
{
$sMinInter = round($fMinInter, 3)."s";
$sMaxInter = round($fMaxInter, 3)."s";
$sTimeDesc = "$sTotalInter (from $sMinInter to $sMaxInter) in $iCountInter times";
}
else
{
$sTimeDesc = "$sTotalInter";
}
echo "<li>Spent $sTimeDesc, on: <span style=\"font-size:60%\">$sArguments</span></li>\n";
$sTotalInter = round($fTotalInter, 3);
$sMinInter = round($fMinInter, 3);
$sMaxInter = round($fMaxInter, 3);
self::Report("<tr>");
self::Report(" <td>$sHtmlArguments</td><td>$iCountInter</td><td>$sTotalInter</td><td>$sMinInter</td><td>$sMaxInter</td>");
self::Report("</tr>");
}
echo "</ul>\n";
echo "<ul>Sumary for $sOperation\n";
echo "<li>Total: $iTotalOp (".round($fTotalOp, 3).")</li>\n";
echo "<li>Min: ".round($fMinOp, 3)."</li>\n";
echo "<li>Max: ".round($fMaxOp, 3)."</li>\n";
echo "<li>Avg: ".round($fTotalOp / $iTotalOp, 3)."</li>\n";
echo "</ul>\n";
self::Report("</table>");
}
}
@@ -104,25 +264,43 @@ class ExecutionKPI
//
public function ComputeAndReport($sOperationDesc)
{
global $fItopStarted;
$aNewEntry = null;
if (self::$m_bEnabled_Duration)
{
$fStopped = MyHelpers::getmicrotime();
$fDuration = $fStopped - $this->m_fStarted;
$this->Report($sOperationDesc.' / duration: '.round($fDuration, 3));
$aNewEntry = array(
'op' => $sOperationDesc,
'time_begin' => $this->m_fStarted - $fItopStarted,
'time_end' => $fStopped - $fItopStarted,
);
// Reset for the next operation (if the object is recycled)
$this->m_fStarted = $fStopped;
}
if (self::$m_bEnabled_Memory)
{
$iMemory = self::memory_get_usage();
$iMemoryUsed = $iMemory - $this->m_iInitialMemory;
$this->Report($sOperationDesc.' / memory: '.self::MemStr($iMemoryUsed).' (Total: '.self::MemStr($iMemory).')');
$iCurrentMemory = self::memory_get_usage();
if (is_null($aNewEntry))
{
$aNewEntry = array('op' => $sOperationDesc);
}
$aNewEntry['mem_begin'] = $this->m_iInitialMemory;
$aNewEntry['mem_end'] = $iCurrentMemory;
if (function_exists('memory_get_peak_usage'))
{
$iMemoryPeak = memory_get_peak_usage();
$this->Report($sOperationDesc.' / memory peak: '.self::MemStr($iMemoryPeak));
$aNewEntry['mem_peak'] = memory_get_peak_usage();
}
// Reset for the next operation (if the object is recycled)
$this->m_iInitialMemory = $iCurrentMemory;
}
if (!is_null($aNewEntry))
{
self::$m_aExecData[] = $aNewEntry;
}
$this->ResetCounters();
}
@@ -132,7 +310,19 @@ class ExecutionKPI
{
$fStopped = MyHelpers::getmicrotime();
$fDuration = $fStopped - $this->m_fStarted;
self::$m_aStats[$sOperation][$sArguments][] = $fDuration;
if (self::$m_bBlameCaller)
{
self::$m_aStats[$sOperation][$sArguments][] = array(
'time' => $fDuration,
'callers' => MyHelpers::get_callstack(1),
);
}
else
{
self::$m_aStats[$sOperation][$sArguments][] = array(
'time' => $fDuration
);
}
}
}
@@ -149,9 +339,11 @@ class ExecutionKPI
}
}
protected function Report($sText)
const HtmlReportFile = 'log/kpi.html';
static protected function Report($sText)
{
echo "$sText<br/>\n";
file_put_contents(APPROOT.self::HtmlReportFile, "$sText\n", FILE_APPEND | LOCK_EX);
}
static protected function MemStr($iMemory)
@@ -191,6 +383,15 @@ class ExecutionKPI
return $output[1] * 1024;
}
}
static public function memory_get_peak_usage($bRealUsage = false)
{
if (function_exists('memory_get_peak_usage'))
{
return memory_get_peak_usage($bRealUsage);
}
// PHP > 5.2.1 - this verb depends on a compilation option
return 0;
}
}
?>

View File

@@ -1,120 +1,149 @@
<?php
// Copyright (C) 2010 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* File logging
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
class FileLog
{
protected $m_sFile = ''; // log is disabled if this is empty
public function __construct($sFileName = '')
{
$this->m_sFile = $sFileName;
}
public function Error($sText)
{
self::Write("Error | ".$sText);
}
public function Warning($sText)
{
self::Write("Warning | ".$sText);
}
public function Info($sText)
{
self::Write("Info | ".$sText);
}
public function Ok($sText)
{
self::Write("Ok | ".$sText);
}
protected function Write($sText)
{
if (strlen($this->m_sFile) == 0) return;
$hLogFile = @fopen($this->m_sFile, 'a');
if ($hLogFile !== false)
{
$sDate = date('Y-m-d H:i:s');
fwrite($hLogFile, "$sDate | $sText\n");
fclose($hLogFile);
}
}
}
class SetupLog
{
protected static $m_oFileLog;
public static function Enable($sTargetFile)
{
self::$m_oFileLog = new FileLog($sTargetFile);
}
public static function Error($sText)
{
self::$m_oFileLog->Error($sText);
}
public static function Warning($sText)
{
self::$m_oFileLog->Warning($sText);
}
public static function Info($sText)
{
self::$m_oFileLog->Info($sText);
}
public static function Ok($sText)
{
self::$m_oFileLog->Ok($sText);
}
}
class IssueLog
{
protected static $m_oFileLog;
public static function Enable($sTargetFile)
{
self::$m_oFileLog = new FileLog($sTargetFile);
}
public static function Error($sText)
{
self::$m_oFileLog->Error($sText);
}
public static function Warning($sText)
{
self::$m_oFileLog->Warning($sText);
}
public static function Info($sText)
{
self::$m_oFileLog->Info($sText);
}
public static function Ok($sText)
{
self::$m_oFileLog->Ok($sText);
}
}
?>
<?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/>
/**
* File logging
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class FileLog
{
protected $m_sFile = ''; // log is disabled if this is empty
public function __construct($sFileName = '')
{
$this->m_sFile = $sFileName;
}
public function Error($sText)
{
self::Write("Error | ".$sText);
}
public function Warning($sText)
{
self::Write("Warning | ".$sText);
}
public function Info($sText)
{
self::Write("Info | ".$sText);
}
public function Ok($sText)
{
self::Write("Ok | ".$sText);
}
protected function Write($sText)
{
if (strlen($this->m_sFile) == 0) return;
$hLogFile = @fopen($this->m_sFile, 'a');
if ($hLogFile !== false)
{
flock($hLogFile, LOCK_EX);
$sDate = date('Y-m-d H:i:s');
fwrite($hLogFile, "$sDate | $sText\n");
fflush($hLogFile);
flock($hLogFile, LOCK_UN);
fclose($hLogFile);
}
}
}
class SetupLog
{
protected static $m_oFileLog;
public static function Enable($sTargetFile)
{
self::$m_oFileLog = new FileLog($sTargetFile);
}
public static function Error($sText)
{
self::$m_oFileLog->Error($sText);
}
public static function Warning($sText)
{
self::$m_oFileLog->Warning($sText);
}
public static function Info($sText)
{
self::$m_oFileLog->Info($sText);
}
public static function Ok($sText)
{
self::$m_oFileLog->Ok($sText);
}
}
class IssueLog
{
protected static $m_oFileLog;
public static function Enable($sTargetFile)
{
self::$m_oFileLog = new FileLog($sTargetFile);
}
public static function Error($sText)
{
self::$m_oFileLog->Error($sText);
}
public static function Warning($sText)
{
self::$m_oFileLog->Warning($sText);
}
public static function Info($sText)
{
self::$m_oFileLog->Info($sText);
}
public static function Ok($sText)
{
self::$m_oFileLog->Ok($sText);
}
}
class ToolsLog
{
protected static $m_oFileLog;
public static function Enable($sTargetFile)
{
self::$m_oFileLog = new FileLog($sTargetFile);
}
public static function Error($sText)
{
self::$m_oFileLog->Error($sText);
}
public static function Warning($sText)
{
self::$m_oFileLog->Warning($sText);
}
public static function Info($sText)
{
self::$m_oFileLog->Info($sText);
}
public static function Ok($sText)
{
self::$m_oFileLog->Ok($sText);
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
<?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/>
/**
* Any extension to hook the initialization of the metamodel
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
interface iOnClassInitialization
{
public function OnAfterClassInitialization($sClass);
}
?>

View File

@@ -0,0 +1,288 @@
<?php
// Copyright (C) 2013 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/>
/**
* Reflection API for the MetaModel (partial)
*
* @copyright Copyright (C) 2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* Exclude the parent class from the list
*
* @package iTopORM
*/
define('ENUM_CHILD_CLASSES_EXCLUDETOP', 1);
/**
* Include the parent class in the list
*
* @package iTopORM
*/
define('ENUM_CHILD_CLASSES_ALL', 2);
abstract class ModelReflection
{
abstract public function GetClassIcon($sClass, $bImgTag = true);
abstract public function IsValidAttCode($sClass, $sAttCode);
abstract public function GetName($sClass);
abstract public function GetLabel($sClass, $sAttCodeEx);
abstract public function GetValueLabel($sClass, $sAttCode, $sValue);
abstract public function ListAttributes($sClass, $sScope = null);
abstract public function GetAttributeProperty($sClass, $sAttCode, $sPropName, $default = null);
abstract public function GetAllowedValues_att($sClass, $sAttCode);
abstract public function HasChildrenClasses($sClass);
abstract public function GetClasses($sCategories = '', $bExcludeLinks = false);
abstract public function IsValidClass($sClass);
abstract public function IsSameFamilyBranch($sClassA, $sClassB);
abstract public function GetParentClass($sClass);
abstract public function GetFiltersList($sClass);
abstract public function IsValidFilterCode($sClass, $sFilterCode);
abstract public function GetQuery($sOQL);
abstract public function DictString($sStringCode, $sDefault = null, $bUserLanguageOnly = false);
public function DictFormat($sFormatCode /*, ... arguments ....*/)
{
$sLocalizedFormat = $this->DictString($sFormatCode);
$aArguments = func_get_args();
array_shift($aArguments);
if ($sLocalizedFormat == $sFormatCode)
{
// Make sure the information will be displayed (ex: an error occuring before the dictionary gets loaded)
return $sFormatCode.' - '.implode(', ', $aArguments);
}
return vsprintf($sLocalizedFormat, $aArguments);
}
abstract public function GetIconSelectionField($sCode, $sLabel = '', $defaultValue = '');
abstract public function GetRootClass($sClass);
abstract public function EnumChildClasses($sClass, $iOption = ENUM_CHILD_CLASSES_EXCLUDETOP);
}
abstract class QueryReflection
{
/**
* Throws an exception in case of an invalid syntax
*/
abstract public function __construct($sOQL, ModelReflection $oModelReflection);
abstract public function GetClass();
abstract public function GetClassAlias();
}
class ModelReflectionRuntime extends ModelReflection
{
public function __construct()
{
}
public function GetClassIcon($sClass, $bImgTag = true)
{
return MetaModel::GetClassIcon($sClass, $bImgTag);
}
public function IsValidAttCode($sClass, $sAttCode)
{
return MetaModel::IsValidAttCode($sClass, $sAttCode);
}
public function GetName($sClass)
{
return MetaModel::GetName($sClass);
}
public function GetLabel($sClass, $sAttCodeEx)
{
return MetaModel::GetLabel($sClass, $sAttCodeEx);
}
public function GetValueLabel($sClass, $sAttCode, $sValue)
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
return $oAttDef->GetValueLabel($sValue);
}
public function ListAttributes($sClass, $sScope = null)
{
$aScope = null;
if ($sScope != null)
{
$aScope = array();
foreach (explode(',', $sScope) as $sScopeClass)
{
$aScope[] = trim($sScopeClass);
}
}
$aAttributes = array();
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
$sAttributeClass = get_class($oAttDef);
if ($aScope != null)
{
foreach ($aScope as $sScopeClass)
{
if (($sAttributeClass == $sScopeClass) || is_subclass_of($sAttributeClass, $sScopeClass))
{
$aAttributes[$sAttCode] = $sAttributeClass;
break;
}
}
}
else
{
$aAttributes[$sAttCode] = $sAttributeClass;
}
}
return $aAttributes;
}
public function GetAttributeProperty($sClass, $sAttCode, $sPropName, $default = null)
{
$ret = $default;
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$aParams = $oAttDef->GetParams();
if (array_key_exists($sPropName, $aParams))
{
$ret = $aParams[$sPropName];
}
if ($oAttDef instanceof AttributeHierarchicalKey)
{
if ($sPropName == 'targetclass')
{
$ret = $sClass;
}
}
return $ret;
}
public function GetAllowedValues_att($sClass, $sAttCode)
{
return MetaModel::GetAllowedValues_att($sClass, $sAttCode);
}
public function HasChildrenClasses($sClass)
{
return MetaModel::HasChildrenClasses($sClass);
}
public function GetClasses($sCategories = '', $bExcludeLinks = false)
{
$aClasses = MetaModel::GetClasses($sCategories);
if ($bExcludeLinks)
{
$aExcluded = MetaModel::GetLinkClasses();
$aRes = array();
foreach ($aClasses as $sClass)
{
if (!array_key_exists($sClass, $aExcluded))
{
$aRes[] = $sClass;
}
}
}
else
{
$aRes = $aClasses;
}
return $aRes;
}
public function IsValidClass($sClass)
{
return MetaModel::IsValidClass($sClass);
}
public function IsSameFamilyBranch($sClassA, $sClassB)
{
return MetaModel::IsSameFamilyBranch($sClassA, $sClassB);
}
public function GetParentClass($sClass)
{
return MetaModel::GetParentClass($sClass);
}
public function GetFiltersList($sClass)
{
return MetaModel::GetFiltersList($sClass);
}
public function IsValidFilterCode($sClass, $sFilterCode)
{
return MetaModel::IsValidFilterCode($sClass, $sFilterCode);
}
public function GetQuery($sOQL)
{
return new QueryReflectionRuntime($sOQL, $this);
}
public function DictString($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
{
return Dict::S($sStringCode, $sDefault, $bUserLanguageOnly);
}
public function GetIconSelectionField($sCode, $sLabel = '', $defaultValue = '')
{
return new RunTimeIconSelectionField($sCode, $sLabel, $defaultValue);
}
public function GetRootClass($sClass)
{
return MetaModel::GetRootClass($sClass);
}
public function EnumChildClasses($sClass, $iOption = ENUM_CHILD_CLASSES_EXCLUDETOP)
{
return MetaModel::EnumChildClasses($sClass, $iOption);
}
}
class QueryReflectionRuntime extends QueryReflection
{
protected $oFilter;
/**
* throws an exception in case of a wrong syntax
*/
public function __construct($sOQL, ModelReflection $oModelReflection)
{
$this->oFilter = DBObjectSearch::FromOQL($sOQL);
}
public function GetClass()
{
return $this->oFilter->GetClass();
}
public function GetClassAlias()
{
return $this->oFilter->GetClassAlias();
}
}

View File

@@ -0,0 +1,38 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class ModuleHandler
* Defines the API to implement module specific actions during page execution
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class ModuleHandlerAPI
{
public static function OnMetaModelStarted()
{
}
public static function OnMenuCreation()
{
}
}
?>

239
core/mutex.class.inc.php Normal file
View File

@@ -0,0 +1,239 @@
<?php
// Copyright (C) 2013-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/>
/**
* Class iTopMutex
* A class to serialize the execution of some code sections
* Emulates the API of PECL Mutex class
* Relies on MySQL locks because the API sem_get is not always present in the
* installed PHP.
*
* @copyright Copyright (C) 2013-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class iTopMutex
{
protected $sName;
protected $hDBLink;
protected $bLocked; // Whether or not this instance of the Mutex is locked
static protected $aAcquiredLocks = array(); // Number of instances of the Mutex, having the lock, in this page
public function __construct($sName, $sDBHost = null, $sDBUser = null, $sDBPwd = null)
{
// Compute the name of a lock for mysql
// Note: names are server-wide!!! So let's make the name specific to this iTop instance
$oConfig = utils::GetConfig(); // Will return an empty config when called during the setup
$sDBName = $oConfig->GetDBName();
$sDBSubname = $oConfig->GetDBSubname();
$this->sName = 'itop.'.$sName;
if (substr($sName, -strlen($sDBName.$sDBSubname)) != $sDBName.$sDBSubname)
{
// If the name supplied already ends with the expected suffix
// don't add it twice, since the setup may try to detect an already
// running cron job by its mutex, without knowing if the config already exists or not
$this->sName .= $sDBName.$sDBSubname;
}
$this->bLocked = false; // Not yet locked
if (!array_key_exists($this->sName, self::$aAcquiredLocks))
{
self::$aAcquiredLocks[$this->sName] = 0;
}
// It is a MUST to create a dedicated session each time a lock is required, because
// using GET_LOCK anytime on the same session will RELEASE the current and unique session lock (known issue)
$sDBHost = is_null($sDBHost) ? $oConfig->GetDBHost() : $sDBHost;
$sDBUser = is_null($sDBUser) ? $oConfig->GetDBUser() : $sDBUser;
$sDBPwd = is_null($sDBPwd) ? $oConfig->GetDBPwd() : $sDBPwd;
$this->InitMySQLSession($sDBHost, $sDBUser, $sDBPwd);
}
public function __destruct()
{
if ($this->bLocked)
{
$this->Unlock();
}
mysqli_close($this->hDBLink);
}
/**
* Acquire the mutex
*/
public function Lock()
{
if ($this->bLocked)
{
// Lock already acquired
return;
}
if (self::$aAcquiredLocks[$this->sName] == 0)
{
do
{
$res = $this->QueryToScalar("SELECT GET_LOCK('".$this->sName."', 3600)");
if (is_null($res))
{
throw new Exception("Failed to acquire the lock '".$this->sName."'");
}
// $res === '1' means I hold the lock
// $res === '0' means it timed out
}
while ($res !== '1');
}
$this->bLocked = true;
self::$aAcquiredLocks[$this->sName]++;
}
/**
* Attempt to acquire the mutex
* @returns bool True if the mutex is acquired, false if already locked elsewhere
*/
public function TryLock()
{
if ($this->bLocked)
{
return true; // Already acquired
}
if (self::$aAcquiredLocks[$this->sName] > 0)
{
self::$aAcquiredLocks[$this->sName]++;
$this->bLocked = true;
return true;
}
$res = $this->QueryToScalar("SELECT GET_LOCK('".$this->sName."', 0)");
if (is_null($res))
{
throw new Exception("Failed to acquire the lock '".$this->sName."'");
}
// $res === '1' means I hold the lock
// $res === '0' means it timed out
if ($res === '1')
{
$this->bLocked = true;
self::$aAcquiredLocks[$this->sName]++;
}
if (($res !== '1') && ($res !== '0'))
{
$sMsg = 'GET_LOCK('.$this->sName.', 0) returned: '.var_export($res, true).'. Expected values are: 0, 1 or null';
IssueLog::Error($sMsg);
throw new Exception($sMsg);
}
return ($res !== '0');
}
/**
* Check if the mutex is locked WITHOUT TRYING TO ACQUIRE IT
* @returns bool True if the mutex is in use, false otherwise
*/
public function IsLocked()
{
if ($this->bLocked)
{
return true; // Already acquired
}
if (self::$aAcquiredLocks[$this->sName] > 0)
{
return true;
}
$res = $this->QueryToScalar("SELECT IS_FREE_LOCK('".$this->sName."')"); // IS_FREE_LOCK detects some error cases that IS_USED_LOCK do not detect
if (is_null($res))
{
$sMsg = "MySQL Error, IS_FREE_LOCK('".$this->sName."') returned null. Error (".mysqli_errno($this->hDBLink).") = '".mysqli_error($this->hDBLink)."'";
IssueLog::Error($sMsg);
throw new Exception($sMsg);
}
else if ($res == '1')
{
// Lock is free
return false;
}
return true;
}
/**
* Release the mutex
*/
public function Unlock()
{
if (!$this->bLocked)
{
// ??? the lock is not acquired, exit
return;
}
if (self::$aAcquiredLocks[$this->sName] == 0)
{
return; // Safety net
}
if (self::$aAcquiredLocks[$this->sName] == 1)
{
$res = $this->QueryToScalar("SELECT RELEASE_LOCK('".$this->sName."')");
}
$this->bLocked = false;
self::$aAcquiredLocks[$this->sName]--;
}
public function InitMySQLSession($sHost, $sUser, $sPwd)
{
$aConnectInfo = explode(':', $sHost);
if (count($aConnectInfo) > 1)
{
// Override the default port
$sServer = $aConnectInfo[0];
$iPort = $aConnectInfo[1];
$this->hDBLink = @mysqli_connect($sServer, $sUser, $sPwd, '', $iPort);
}
else
{
$this->hDBLink = @mysqli_connect($sHost, $sUser, $sPwd);
}
if (!$this->hDBLink)
{
throw new Exception("Could not connect to the DB server (host=$sHost, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
}
}
protected function QueryToScalar($sSql)
{
$result = mysqli_query($this->hDBLink, $sSql);
if (!$result)
{
throw new Exception("Failed to issue MySQL query '".$sSql."': ".mysqli_error($this->hDBLink).' (mysql errno: '.mysqli_errno($this->hDBLink).')');
}
if ($aRow = mysqli_fetch_array($result, MYSQLI_BOTH))
{
$res = $aRow[0];
}
else
{
mysqli_free_result($result);
throw new Exception("No result for query '".$sSql."'");
}
mysqli_free_result($result);
return $res;
}
}

View File

@@ -1,3 +0,0 @@
c:\itop\php-5.2.3\php.exe -q "C:\itop\PHP-5.2.3\PEAR\PHP\LexerGenerator\cli.php" oql-lexer.plex
c:\itop\php-5.2.3\php.exe -q "C:\itop\PHP-5.2.3\PEAR\PHP\ParserGenerator\cli.php" oql-parser.y
pause

View File

@@ -0,0 +1,937 @@
<?php
/* Driver template for the PHP_ParserGenerator parser generator. (PHP port of LEMON)
*/
/**
* This can be used to store both the string representation of
* a token, and any useful meta-data associated with the token.
*
* meta-data should be stored as an array
*/
class ParseyyToken implements ArrayAccess
{
public $string = '';
public $metadata = array();
function __construct($s, $m = array())
{
if ($s instanceof ParseyyToken) {
$this->string = $s->string;
$this->metadata = $s->metadata;
} else {
$this->string = (string) $s;
if ($m instanceof ParseyyToken) {
$this->metadata = $m->metadata;
} elseif (is_array($m)) {
$this->metadata = $m;
}
}
}
function __toString()
{
return $this->string;
}
function offsetExists($offset)
{
return isset($this->metadata[$offset]);
}
function offsetGet($offset)
{
return $this->metadata[$offset];
}
function offsetSet($offset, $value)
{
if ($offset === null) {
if (isset($value[0])) {
$x = ($value instanceof ParseyyToken) ?
$value->metadata : $value;
$this->metadata = array_merge($this->metadata, $x);
return;
}
$offset = count($this->metadata);
}
if ($value === null) {
return;
}
if ($value instanceof ParseyyToken) {
if ($value->metadata) {
$this->metadata[$offset] = $value->metadata;
}
} elseif ($value) {
$this->metadata[$offset] = $value;
}
}
function offsetUnset($offset)
{
unset($this->metadata[$offset]);
}
}
/** The following structure represents a single element of the
* parser's stack. Information stored includes:
*
* + The state number for the parser at this level of the stack.
*
* + The value of the token stored at this level of the stack.
* (In other words, the "major" token.)
*
* + The semantic value stored at this level of the stack. This is
* the information used by the action routines in the grammar.
* It is sometimes called the "minor" token.
*/
class ParseyyStackEntry
{
public $stateno; /* The state-number */
public $major; /* The major token value. This is the code
** number for the token at this stack level */
public $minor; /* The user-supplied minor token value. This
** is the value of the token */
};
// code external to the class is included here
%%
// declare_class is output here
%%
{
/* First off, code is included which follows the "include_class" declaration
** in the input file. */
%%
/* Next is all token values, as class constants
*/
/*
** These constants (all generated automatically by the parser generator)
** specify the various kinds of tokens (terminals) that the parser
** understands.
**
** Each symbol here is a terminal symbol in the grammar.
*/
%%
/* Next are that tables used to determine what action to take based on the
** current state and lookahead token. These tables are used to implement
** functions that take a state number and lookahead value and return an
** action integer.
**
** Suppose the action integer is N. Then the action is determined as
** follows
**
** 0 <= N < self::YYNSTATE Shift N. That is,
** push the lookahead
** token onto the stack
** and goto state N.
**
** self::YYNSTATE <= N < self::YYNSTATE+self::YYNRULE Reduce by rule N-YYNSTATE.
**
** N == self::YYNSTATE+self::YYNRULE A syntax error has occurred.
**
** N == self::YYNSTATE+self::YYNRULE+1 The parser accepts its
** input. (and concludes parsing)
**
** N == self::YYNSTATE+self::YYNRULE+2 No such action. Denotes unused
** slots in the yy_action[] table.
**
** The action table is constructed as a single large static array $yy_action.
** Given state S and lookahead X, the action is computed as
**
** self::$yy_action[self::$yy_shift_ofst[S] + X ]
**
** If the index value self::$yy_shift_ofst[S]+X is out of range or if the value
** self::$yy_lookahead[self::$yy_shift_ofst[S]+X] is not equal to X or if
** self::$yy_shift_ofst[S] is equal to self::YY_SHIFT_USE_DFLT, it means that
** the action is not in the table and that self::$yy_default[S] should be used instead.
**
** The formula above is for computing the action when the lookahead is
** a terminal symbol. If the lookahead is a non-terminal (as occurs after
** a reduce action) then the static $yy_reduce_ofst array is used in place of
** the static $yy_shift_ofst array and self::YY_REDUCE_USE_DFLT is used in place of
** self::YY_SHIFT_USE_DFLT.
**
** The following are the tables generated in this section:
**
** self::$yy_action A single table containing all actions.
** self::$yy_lookahead A table containing the lookahead for each entry in
** yy_action. Used to detect hash collisions.
** self::$yy_shift_ofst For each state, the offset into self::$yy_action for
** shifting terminals.
** self::$yy_reduce_ofst For each state, the offset into self::$yy_action for
** shifting non-terminals after a reduce.
** self::$yy_default Default action for each state.
*/
%%
/* The next thing included is series of defines which control
** various aspects of the generated parser.
** self::YYNOCODE is a number which corresponds
** to no legal terminal or nonterminal number. This
** number is used to fill in empty slots of the hash
** table.
** self::YYFALLBACK If defined, this indicates that one or more tokens
** have fall-back values which should be used if the
** original value of the token will not parse.
** self::YYSTACKDEPTH is the maximum depth of the parser's stack.
** self::YYNSTATE the combined number of states.
** self::YYNRULE the number of rules in the grammar
** self::YYERRORSYMBOL is the code number of the error symbol. If not
** defined, then do no error processing.
*/
%%
/** The next table maps tokens into fallback tokens. If a construct
* like the following:
*
* %fallback ID X Y Z.
*
* appears in the grammer, then ID becomes a fallback token for X, Y,
* and Z. Whenever one of the tokens X, Y, or Z is input to the parser
* but it does not parse, the type of the token is changed to ID and
* the parse is retried before an error is thrown.
*/
static public $yyFallback = array(
%%
);
/**
* Turn parser tracing on by giving a stream to which to write the trace
* and a prompt to preface each trace message. Tracing is turned off
* by making either argument NULL
*
* Inputs:
*
* - A stream resource to which trace output should be written.
* If NULL, then tracing is turned off.
* - A prefix string written at the beginning of every
* line of trace output. If NULL, then tracing is
* turned off.
*
* Outputs:
*
* - None.
* @param resource
* @param string
*/
static function Trace($TraceFILE, $zTracePrompt)
{
if (!$TraceFILE) {
$zTracePrompt = 0;
} elseif (!$zTracePrompt) {
$TraceFILE = 0;
}
self::$yyTraceFILE = $TraceFILE;
self::$yyTracePrompt = $zTracePrompt;
}
/**
* Output debug information to output (php://output stream)
*/
static function PrintTrace()
{
self::$yyTraceFILE = fopen('php://output', 'w');
self::$yyTracePrompt = '';
}
/**
* @var resource|0
*/
static public $yyTraceFILE;
/**
* String to prepend to debug output
* @var string|0
*/
static public $yyTracePrompt;
/**
* @var int
*/
public $yyidx = -1; /* Index of top element in stack */
/**
* @var int
*/
public $yyerrcnt; /* Shifts left before out of the error */
/**
* @var array
*/
public $yystack = array(); /* The parser's stack */
/**
* For tracing shifts, the names of all terminals and nonterminals
* are required. The following table supplies these names
* @var array
*/
static public $yyTokenName = array(
%%
);
/**
* For tracing reduce actions, the names of all rules are required.
* @var array
*/
static public $yyRuleName = array(
%%
);
/**
* This function returns the symbolic name associated with a token
* value.
* @param int
* @return string
*/
function tokenName($tokenType)
{
if ($tokenType === 0) {
return 'End of Input';
}
if ($tokenType > 0 && $tokenType < count(self::$yyTokenName)) {
return self::$yyTokenName[$tokenType];
} else {
return "Unknown";
}
}
/**
* The following function deletes the value associated with a
* symbol. The symbol can be either a terminal or nonterminal.
* @param int the symbol code
* @param mixed the symbol's value
*/
static function yy_destructor($yymajor, $yypminor)
{
switch ($yymajor) {
/* Here is inserted the actions which take place when a
** terminal or non-terminal is destroyed. This can happen
** when the symbol is popped from the stack during a
** reduce or during error processing or when a parser is
** being destroyed before it is finished parsing.
**
** Note: during a reduce, the only symbols destroyed are those
** which appear on the RHS of the rule, but which are not used
** inside the C code.
*/
%%
default: break; /* If no destructor action specified: do nothing */
}
}
/**
* Pop the parser's stack once.
*
* If there is a destructor routine associated with the token which
* is popped from the stack, then call it.
*
* Return the major token number for the symbol popped.
* @param ParseyyParser
* @return int
*/
function yy_pop_parser_stack()
{
if (!count($this->yystack)) {
return;
}
$yytos = array_pop($this->yystack);
if (self::$yyTraceFILE && $this->yyidx >= 0) {
fwrite(self::$yyTraceFILE,
self::$yyTracePrompt . 'Popping ' . self::$yyTokenName[$yytos->major] .
"\n");
}
$yymajor = $yytos->major;
self::yy_destructor($yymajor, $yytos->minor);
$this->yyidx--;
return $yymajor;
}
/**
* Deallocate and destroy a parser. Destructors are all called for
* all stack elements before shutting the parser down.
*/
function __destruct()
{
while ($this->yyidx >= 0) {
$this->yy_pop_parser_stack();
}
if (is_resource(self::$yyTraceFILE)) {
fclose(self::$yyTraceFILE);
}
}
/**
* Based on the current state and parser stack, get a list of all
* possible lookahead tokens
* @param int
* @return array
*/
function yy_get_expected_tokens($token)
{
$state = $this->yystack[$this->yyidx]->stateno;
$expected = self::$yyExpectedTokens[$state];
if (in_array($token, self::$yyExpectedTokens[$state], true)) {
return $expected;
}
$stack = $this->yystack;
$yyidx = $this->yyidx;
do {
$yyact = $this->yy_find_shift_action($token);
if ($yyact >= self::YYNSTATE && $yyact < self::YYNSTATE + self::YYNRULE) {
// reduce action
$done = 0;
do {
if ($done++ == 100) {
$this->yyidx = $yyidx;
$this->yystack = $stack;
// too much recursion prevents proper detection
// so give up
return array_unique($expected);
}
$yyruleno = $yyact - self::YYNSTATE;
$this->yyidx -= self::$yyRuleInfo[$yyruleno]['rhs'];
$nextstate = $this->yy_find_reduce_action(
$this->yystack[$this->yyidx]->stateno,
self::$yyRuleInfo[$yyruleno]['lhs']);
if (isset(self::$yyExpectedTokens[$nextstate])) {
$expected += self::$yyExpectedTokens[$nextstate];
if (in_array($token,
self::$yyExpectedTokens[$nextstate], true)) {
$this->yyidx = $yyidx;
$this->yystack = $stack;
return array_unique($expected);
}
}
if ($nextstate < self::YYNSTATE) {
// we need to shift a non-terminal
$this->yyidx++;
$x = new ParseyyStackEntry;
$x->stateno = $nextstate;
$x->major = self::$yyRuleInfo[$yyruleno]['lhs'];
$this->yystack[$this->yyidx] = $x;
continue 2;
} elseif ($nextstate == self::YYNSTATE + self::YYNRULE + 1) {
$this->yyidx = $yyidx;
$this->yystack = $stack;
// the last token was just ignored, we can't accept
// by ignoring input, this is in essence ignoring a
// syntax error!
return array_unique($expected);
} elseif ($nextstate === self::YY_NO_ACTION) {
$this->yyidx = $yyidx;
$this->yystack = $stack;
// input accepted, but not shifted (I guess)
return $expected;
} else {
$yyact = $nextstate;
}
} while (true);
}
break;
} while (true);
return array_unique($expected);
}
/**
* Based on the parser state and current parser stack, determine whether
* the lookahead token is possible.
*
* The parser will convert the token value to an error token if not. This
* catches some unusual edge cases where the parser would fail.
* @param int
* @return bool
*/
function yy_is_expected_token($token)
{
if ($token === 0) {
return true; // 0 is not part of this
}
$state = $this->yystack[$this->yyidx]->stateno;
if (in_array($token, self::$yyExpectedTokens[$state], true)) {
return true;
}
$stack = $this->yystack;
$yyidx = $this->yyidx;
do {
$yyact = $this->yy_find_shift_action($token);
if ($yyact >= self::YYNSTATE && $yyact < self::YYNSTATE + self::YYNRULE) {
// reduce action
$done = 0;
do {
if ($done++ == 100) {
$this->yyidx = $yyidx;
$this->yystack = $stack;
// too much recursion prevents proper detection
// so give up
return true;
}
$yyruleno = $yyact - self::YYNSTATE;
$this->yyidx -= self::$yyRuleInfo[$yyruleno]['rhs'];
$nextstate = $this->yy_find_reduce_action(
$this->yystack[$this->yyidx]->stateno,
self::$yyRuleInfo[$yyruleno]['lhs']);
if (isset(self::$yyExpectedTokens[$nextstate]) &&
in_array($token, self::$yyExpectedTokens[$nextstate], true)) {
$this->yyidx = $yyidx;
$this->yystack = $stack;
return true;
}
if ($nextstate < self::YYNSTATE) {
// we need to shift a non-terminal
$this->yyidx++;
$x = new ParseyyStackEntry;
$x->stateno = $nextstate;
$x->major = self::$yyRuleInfo[$yyruleno]['lhs'];
$this->yystack[$this->yyidx] = $x;
continue 2;
} elseif ($nextstate == self::YYNSTATE + self::YYNRULE + 1) {
$this->yyidx = $yyidx;
$this->yystack = $stack;
if (!$token) {
// end of input: this is valid
return true;
}
// the last token was just ignored, we can't accept
// by ignoring input, this is in essence ignoring a
// syntax error!
return false;
} elseif ($nextstate === self::YY_NO_ACTION) {
$this->yyidx = $yyidx;
$this->yystack = $stack;
// input accepted, but not shifted (I guess)
return true;
} else {
$yyact = $nextstate;
}
} while (true);
}
break;
} while (true);
$this->yyidx = $yyidx;
$this->yystack = $stack;
return true;
}
/**
* Find the appropriate action for a parser given the terminal
* look-ahead token iLookAhead.
*
* If the look-ahead token is YYNOCODE, then check to see if the action is
* independent of the look-ahead. If it is, return the action, otherwise
* return YY_NO_ACTION.
* @param int The look-ahead token
*/
function yy_find_shift_action($iLookAhead)
{
$stateno = $this->yystack[$this->yyidx]->stateno;
/* if ($this->yyidx < 0) return self::YY_NO_ACTION; */
if (!isset(self::$yy_shift_ofst[$stateno])) {
// no shift actions
return self::$yy_default[$stateno];
}
$i = self::$yy_shift_ofst[$stateno];
if ($i === self::YY_SHIFT_USE_DFLT) {
return self::$yy_default[$stateno];
}
if ($iLookAhead == self::YYNOCODE) {
return self::YY_NO_ACTION;
}
$i += $iLookAhead;
if ($i < 0 || $i >= self::YY_SZ_ACTTAB ||
self::$yy_lookahead[$i] != $iLookAhead) {
if (count(self::$yyFallback) && $iLookAhead < count(self::$yyFallback)
&& ($iFallback = self::$yyFallback[$iLookAhead]) != 0) {
if (self::$yyTraceFILE) {
fwrite(self::$yyTraceFILE, self::$yyTracePrompt . "FALLBACK " .
self::$yyTokenName[$iLookAhead] . " => " .
self::$yyTokenName[$iFallback] . "\n");
}
return $this->yy_find_shift_action($iFallback);
}
return self::$yy_default[$stateno];
} else {
return self::$yy_action[$i];
}
}
/**
* Find the appropriate action for a parser given the non-terminal
* look-ahead token $iLookAhead.
*
* If the look-ahead token is self::YYNOCODE, then check to see if the action is
* independent of the look-ahead. If it is, return the action, otherwise
* return self::YY_NO_ACTION.
* @param int Current state number
* @param int The look-ahead token
*/
function yy_find_reduce_action($stateno, $iLookAhead)
{
/* $stateno = $this->yystack[$this->yyidx]->stateno; */
if (!isset(self::$yy_reduce_ofst[$stateno])) {
return self::$yy_default[$stateno];
}
$i = self::$yy_reduce_ofst[$stateno];
if ($i == self::YY_REDUCE_USE_DFLT) {
return self::$yy_default[$stateno];
}
if ($iLookAhead == self::YYNOCODE) {
return self::YY_NO_ACTION;
}
$i += $iLookAhead;
if ($i < 0 || $i >= self::YY_SZ_ACTTAB ||
self::$yy_lookahead[$i] != $iLookAhead) {
return self::$yy_default[$stateno];
} else {
return self::$yy_action[$i];
}
}
/**
* Perform a shift action.
* @param int The new state to shift in
* @param int The major token to shift in
* @param mixed the minor token to shift in
*/
function yy_shift($yyNewState, $yyMajor, $yypMinor)
{
$this->yyidx++;
if ($this->yyidx >= self::YYSTACKDEPTH) {
$this->yyidx--;
if (self::$yyTraceFILE) {
fprintf(self::$yyTraceFILE, "%sStack Overflow!\n", self::$yyTracePrompt);
}
while ($this->yyidx >= 0) {
$this->yy_pop_parser_stack();
}
/* Here code is inserted which will execute if the parser
** stack ever overflows */
%%
return;
}
$yytos = new ParseyyStackEntry;
$yytos->stateno = $yyNewState;
$yytos->major = $yyMajor;
$yytos->minor = $yypMinor;
array_push($this->yystack, $yytos);
if (self::$yyTraceFILE && $this->yyidx > 0) {
fprintf(self::$yyTraceFILE, "%sShift %d\n", self::$yyTracePrompt,
$yyNewState);
fprintf(self::$yyTraceFILE, "%sStack:", self::$yyTracePrompt);
for ($i = 1; $i <= $this->yyidx; $i++) {
fprintf(self::$yyTraceFILE, " %s",
self::$yyTokenName[$this->yystack[$i]->major]);
}
fwrite(self::$yyTraceFILE,"\n");
}
}
/**
* The following table contains information about every rule that
* is used during the reduce.
*
* <pre>
* array(
* array(
* int $lhs; Symbol on the left-hand side of the rule
* int $nrhs; Number of right-hand side symbols in the rule
* ),...
* );
* </pre>
*/
static public $yyRuleInfo = array(
%%
);
/**
* The following table contains a mapping of reduce action to method name
* that handles the reduction.
*
* If a rule is not set, it has no handler.
*/
static public $yyReduceMap = array(
%%
);
/* Beginning here are the reduction cases. A typical example
** follows:
** #line <lineno> <grammarfile>
** function yy_r0($yymsp){ ... } // User supplied code
** #line <lineno> <thisfile>
*/
%%
/**
* placeholder for the left hand side in a reduce operation.
*
* For a parser with a rule like this:
* <pre>
* rule(A) ::= B. { A = 1; }
* </pre>
*
* The parser will translate to something like:
*
* <code>
* function yy_r0(){$this->_retvalue = 1;}
* </code>
*/
private $_retvalue;
/**
* Perform a reduce action and the shift that must immediately
* follow the reduce.
*
* For a rule such as:
*
* <pre>
* A ::= B blah C. { dosomething(); }
* </pre>
*
* This function will first call the action, if any, ("dosomething();" in our
* example), and then it will pop three states from the stack,
* one for each entry on the right-hand side of the expression
* (B, blah, and C in our example rule), and then push the result of the action
* back on to the stack with the resulting state reduced to (as described in the .out
* file)
* @param int Number of the rule by which to reduce
*/
function yy_reduce($yyruleno)
{
//int $yygoto; /* The next state */
//int $yyact; /* The next action */
//mixed $yygotominor; /* The LHS of the rule reduced */
//ParseyyStackEntry $yymsp; /* The top of the parser's stack */
//int $yysize; /* Amount to pop the stack */
$yymsp = $this->yystack[$this->yyidx];
if (self::$yyTraceFILE && $yyruleno >= 0
&& $yyruleno < count(self::$yyRuleName)) {
fprintf(self::$yyTraceFILE, "%sReduce (%d) [%s].\n",
self::$yyTracePrompt, $yyruleno,
self::$yyRuleName[$yyruleno]);
}
$this->_retvalue = $yy_lefthand_side = null;
if (array_key_exists($yyruleno, self::$yyReduceMap)) {
// call the action
$this->_retvalue = null;
$this->{'yy_r' . self::$yyReduceMap[$yyruleno]}();
$yy_lefthand_side = $this->_retvalue;
}
$yygoto = self::$yyRuleInfo[$yyruleno]['lhs'];
$yysize = self::$yyRuleInfo[$yyruleno]['rhs'];
$this->yyidx -= $yysize;
for ($i = $yysize; $i; $i--) {
// pop all of the right-hand side parameters
array_pop($this->yystack);
}
$yyact = $this->yy_find_reduce_action($this->yystack[$this->yyidx]->stateno, $yygoto);
if ($yyact < self::YYNSTATE) {
/* If we are not debugging and the reduce action popped at least
** one element off the stack, then we can push the new element back
** onto the stack here, and skip the stack overflow test in yy_shift().
** That gives a significant speed improvement. */
if (!self::$yyTraceFILE && $yysize) {
$this->yyidx++;
$x = new ParseyyStackEntry;
$x->stateno = $yyact;
$x->major = $yygoto;
$x->minor = $yy_lefthand_side;
$this->yystack[$this->yyidx] = $x;
} else {
$this->yy_shift($yyact, $yygoto, $yy_lefthand_side);
}
} elseif ($yyact == self::YYNSTATE + self::YYNRULE + 1) {
$this->yy_accept();
}
}
/**
* The following code executes when the parse fails
*
* Code from %parse_fail is inserted here
*/
function yy_parse_failed()
{
if (self::$yyTraceFILE) {
fprintf(self::$yyTraceFILE, "%sFail!\n", self::$yyTracePrompt);
}
while ($this->yyidx >= 0) {
$this->yy_pop_parser_stack();
}
/* Here code is inserted which will be executed whenever the
** parser fails */
%%
}
/**
* The following code executes when a syntax error first occurs.
*
* %syntax_error code is inserted here
* @param int The major type of the error token
* @param mixed The minor type of the error token
*/
function yy_syntax_error($yymajor, $TOKEN)
{
%%
}
/**
* The following is executed when the parser accepts
*
* %parse_accept code is inserted here
*/
function yy_accept()
{
if (self::$yyTraceFILE) {
fprintf(self::$yyTraceFILE, "%sAccept!\n", self::$yyTracePrompt);
}
while ($this->yyidx >= 0) {
$stack = $this->yy_pop_parser_stack();
}
/* Here code is inserted which will be executed whenever the
** parser accepts */
%%
}
/**
* The main parser program.
*
* The first argument is the major token number. The second is
* the token value string as scanned from the input.
*
* @param int $yymajor the token number
* @param mixed $yytokenvalue the token value
* @param mixed ... any extra arguments that should be passed to handlers
*
* @return void
*/
function doParse($yymajor, $yytokenvalue)
{
// $yyact; /* The parser action. */
// $yyendofinput; /* True if we are at the end of input */
$yyerrorhit = 0; /* True if yymajor has invoked an error */
/* (re)initialize the parser, if necessary */
if ($this->yyidx === null || $this->yyidx < 0) {
/* if ($yymajor == 0) return; // not sure why this was here... */
$this->yyidx = 0;
$this->yyerrcnt = -1;
$x = new ParseyyStackEntry;
$x->stateno = 0;
$x->major = 0;
$this->yystack = array();
array_push($this->yystack, $x);
}
$yyendofinput = ($yymajor==0);
if (self::$yyTraceFILE) {
fprintf(
self::$yyTraceFILE,
"%sInput %s\n",
self::$yyTracePrompt,
self::$yyTokenName[$yymajor]
);
}
do {
$yyact = $this->yy_find_shift_action($yymajor);
if ($yymajor < self::YYERRORSYMBOL
&& !$this->yy_is_expected_token($yymajor)
) {
// force a syntax error
$yyact = self::YY_ERROR_ACTION;
}
if ($yyact < self::YYNSTATE) {
$this->yy_shift($yyact, $yymajor, $yytokenvalue);
$this->yyerrcnt--;
if ($yyendofinput && $this->yyidx >= 0) {
$yymajor = 0;
} else {
$yymajor = self::YYNOCODE;
}
} elseif ($yyact < self::YYNSTATE + self::YYNRULE) {
$this->yy_reduce($yyact - self::YYNSTATE);
} elseif ($yyact == self::YY_ERROR_ACTION) {
if (self::$yyTraceFILE) {
fprintf(
self::$yyTraceFILE,
"%sSyntax Error!\n",
self::$yyTracePrompt
);
}
if (self::YYERRORSYMBOL) {
/* A syntax error has occurred.
** The response to an error depends upon whether or not the
** grammar defines an error token "ERROR".
**
** This is what we do if the grammar does define ERROR:
**
** * Call the %syntax_error function.
**
** * Begin popping the stack until we enter a state where
** it is legal to shift the error symbol, then shift
** the error symbol.
**
** * Set the error count to three.
**
** * Begin accepting and shifting new tokens. No new error
** processing will occur until three tokens have been
** shifted successfully.
**
*/
if ($this->yyerrcnt < 0) {
$this->yy_syntax_error($yymajor, $yytokenvalue);
}
$yymx = $this->yystack[$this->yyidx]->major;
if ($yymx == self::YYERRORSYMBOL || $yyerrorhit ) {
if (self::$yyTraceFILE) {
fprintf(
self::$yyTraceFILE,
"%sDiscard input token %s\n",
self::$yyTracePrompt,
self::$yyTokenName[$yymajor]
);
}
$this->yy_destructor($yymajor, $yytokenvalue);
$yymajor = self::YYNOCODE;
} else {
while ($this->yyidx >= 0
&& $yymx != self::YYERRORSYMBOL
&& ($yyact = $this->yy_find_shift_action(self::YYERRORSYMBOL)) >= self::YYNSTATE
) {
$this->yy_pop_parser_stack();
}
if ($this->yyidx < 0 || $yymajor==0) {
$this->yy_destructor($yymajor, $yytokenvalue);
$this->yy_parse_failed();
$yymajor = self::YYNOCODE;
} elseif ($yymx != self::YYERRORSYMBOL) {
$u2 = 0;
$this->yy_shift($yyact, self::YYERRORSYMBOL, $u2);
}
}
$this->yyerrcnt = 3;
$yyerrorhit = 1;
} else {
/* YYERRORSYMBOL is not defined */
/* This is what we do if the grammar does not define ERROR:
**
** * Report an error message, and throw away the input token.
**
** * If the input token is $, then fail the parse.
**
** As before, subsequent error messages are suppressed until
** three input tokens have been successfully shifted.
*/
if ($this->yyerrcnt <= 0) {
$this->yy_syntax_error($yymajor, $yytokenvalue);
}
$this->yyerrcnt = 3;
$this->yy_destructor($yymajor, $yytokenvalue);
if ($yyendofinput) {
$this->yy_parse_failed();
}
$yymajor = self::YYNOCODE;
}
} else {
$this->yy_accept();
$yymajor = self::YYNOCODE;
}
} while ($yymajor != self::YYNOCODE && $this->yyidx >= 0);
}
}

View File

@@ -0,0 +1,332 @@
<?php
/**
* PHP_LexerGenerator, a php 5 lexer generator.
*
* This lexer generator translates a file in a format similar to
* re2c ({@link http://re2c.org}) and translates it into a PHP 5-based lexer
*
* PHP version 5
*
* LICENSE:
*
* Copyright (c) 2006, Gregory Beaver <cellog@php.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution.
* * Neither the name of the PHP_LexerGenerator nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category php
* @package PHP_LexerGenerator
* @author Gregory Beaver <cellog@php.net>
* @copyright 2006 Gregory Beaver
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: LexerGenerator.php 294970 2010-02-12 03:46:38Z clockwerx $
* @since File available since Release 0.1.0
*/
/**
* The Lexer generation parser
*/
require_once 'PHP/LexerGenerator/Parser.php';
/**
* Hand-written lexer for lex2php format files
*/
require_once 'PHP/LexerGenerator/Lexer.php';
/**
* The basic home class for the lexer generator. A lexer scans text and
* organizes it into tokens for usage by a parser.
*
* Sample Usage:
* <code>
* require_once 'PHP/LexerGenerator.php';
* $lex = new PHP_LexerGenerator('/path/to/lexerfile.plex');
* </code>
*
* A file named "/path/to/lexerfile.php" will be created.
*
* File format consists of a PHP file containing specially
* formatted comments like so:
*
* <code>
* /*!lex2php
* {@*}
* </code>
*
* All lexer definition files must contain at least two lex2php comment blocks:
* - 1 regex declaration block
* - 1 or more rule declaration blocks
*
* The first lex2php comment is the regex declaration block and must contain
* several processor instruction as well as defining a name for all
* regular expressions. Processor instructions start with
* a "%" symbol and must be:
*
* - %counter
* - %input
* - %token
* - %value
* - %line
*
* token and counter should define the class variables used to define lexer input
* and the index into the input. token and value should be used to define the class
* variables used to store the token number and its textual value. Finally, line
* should be used to define the class variable used to define the current line number
* of scanning.
*
* For example:
* <code>
* /*!lex2php
* %counter {$this->N}
* %input {$this->data}
* %token {$this->token}
* %value {$this->value}
* %line {%this->linenumber}
* {@*}
* </code>
*
* Patterns consist of an identifier containing an letters or an underscore, and
* a descriptive match pattern.
*
* Descriptive match patterns may either be regular expressions (regexes) or
* quoted literal strings. Here are some examples:
*
* <pre>
* pattern = "quoted literal"
* ANOTHER = /[a-zA-Z_]+/
* COMPLEX = @<([a-zA-Z_]+)( +(([a-zA-Z_]+)=((["\'])([^\6]*)\6))+){0,1}>[^<]*</\1>@
* </pre>
*
* Quoted strings must escape the \ and " characters with \" and \\.
*
* Regex patterns must be in Perl-compatible regular expression format (preg).
* special characters (like \t \n or \x3H) can only be used in regexes, all
* \ will be escaped in literal strings.
*
* Sub-patterns may be defined and back-references (like \1) may be used. Any sub-
* patterns detected will be passed to the token handler in the variable
* $yysubmatches.
*
* In addition, lookahead expressions, and once-only expressions are allowed.
* Lookbehind expressions are impossible (scanning always occurs from the
* current position forward), and recursion (?R) can't work and is not allowed.
*
* <code>
* /*!lex2php
* %counter {$this->N}
* %input {$this->data}
* %token {$this->token}
* %value {$this->value}
* %line {%this->linenumber}
* alpha = /[a-zA-Z]/
* alphaplus = /[a-zA-Z]+/
* number = /[0-9]/
* numerals = /[0-9]+/
* whitespace = /[ \t\n]+/
* blah = "$\""
* blahblah = /a\$/
* GAMEEND = @(?:1\-0|0\-1|1/2\-1/2)@
* PAWNMOVE = /P?[a-h]([2-7]|[18]\=(Q|R|B|N))|P?[a-h]x[a-h]([2-7]|[18]\=(Q|R|B|N))/
* {@*}
* </code>
*
* All regexes must be delimited. Any legal preg delimiter can be used (as in @ or / in
* the example above)
*
* Rule lex2php blocks each define a lexer state. You can optionally name the state
* with the %statename processor instruction. State names can be used to transfer to
* a new lexer state with the yybegin() method
*
* <code>
* /*!lexphp
* %statename INITIAL
* blah {
* $this->yybegin(self::INBLAH);
* // note - $this->yybegin(2) would also work
* }
* {@*}
* /*!lex2php
* %statename INBLAH
* ANYTHING {
* $this->yybegin(self::INITIAL);
* // note - $this->yybegin(1) would also work
* }
* {@*}
* </code>
*
* You can maintain a parser state stack simply by using yypushstate() and
* yypopstate() instead of yybegin():
*
* <code>
* /*!lexphp
* %statename INITIAL
* blah {
* $this->yypushstate(self::INBLAH);
* }
* {@*}
* /*!lex2php
* %statename INBLAH
* ANYTHING {
* $this->yypopstate();
* // now INBLAH doesn't care where it was called from
* }
* {@*}
* </code>
*
* Code blocks can choose to skip the current token and cycle to the next token by
* returning "false"
*
* <code>
* /*!lex2php
* WHITESPACE {
* return false;
* }
* {@*}
* </code>
*
* If you wish to re-process the current token in a new state, simply return true.
* If you forget to change lexer state, this will cause an unterminated loop,
* so be careful!
*
* <code>
* /*!lex2php
* "(" {
* $this->yypushstate(self::INPARAMS);
* return true;
* }
* {@*}
* </code>
*
* Lastly, if you wish to cycle to the next matching rule, return any value other than
* true, false or null:
*
* <code>
* /*!lex2php
* "{@" ALPHA {
* if ($this->value == '{@internal') {
* return 'more';
* }
* ...
* }
* "{@internal" {
* ...
* }
* {@*}
* </code>
*
* Note that this procedure is exceptionally inefficient, and it would be far better
* to take advantage of PHP_LexerGenerator's top-down precedence and instead code:
*
* <code>
* /*!lex2php
* "{@internal" {
* ...
* }
* "{@" ALPHA {
* ...
* }
* {@*}
* </code>
* @package PHP_LexerGenerator
* @author Gregory Beaver <cellog@php.net>
* @copyright 2006 Gregory Beaver
* @license http://www.php.net/license/3_01.txt PHP License 3.01
* @version @package_version@
* @since Class available since Release 0.1.0
* @example TestLexer.plex Example lexer source
* @example TestLexer.php Example lexer generated php code
* @example usage.php Example usage of PHP_LexerGenerator
* @example Lexer.plex File_ChessPGN lexer source (complex)
* @example Lexer.php File_ChessPGN lexer generated php code
*/
class PHP_LexerGenerator
{
/**
* Plex file lexer.
* @var PHP_LexerGenerator_Lexer
*/
private $_lex;
/**
* Plex file parser.
* @var PHP_LexerGenerator_Parser
*/
private $_parser;
/**
* Path to the output PHP file.
* @var string
*/
private $_outfile;
/**
* Debug flag. When set, Parser trace information is generated.
* @var boolean
*/
public $debug = false;
/**
* Create a lexer generator and optionally generate a lexer file.
*
* @param string Optional plex file {@see PHP_LexerGenerator::create}.
* @param string Optional output file {@see PHP_LexerGenerator::create}.
*/
function __construct($lexerfile = '', $outfile = '')
{
if ($lexerfile) {
$this -> create($lexerfile, $outfile);
}
}
/**
* Create a lexer file from its skeleton plex file.
*
* @param string Path to the plex file.
* @param string Optional path to output file. Default is lexerfile with
* extension of ".php".
*/
function create($lexerfile, $outfile = '')
{
$this->_lex = new PHP_LexerGenerator_Lexer(file_get_contents($lexerfile));
$info = pathinfo($lexerfile);
if ($outfile) {
$this->outfile = $outfile;
} else {
$this->outfile = $info['dirname'] . DIRECTORY_SEPARATOR .
substr($info['basename'], 0,
strlen($info['basename']) - strlen($info['extension'])) . 'php';
}
$this->_parser = new PHP_LexerGenerator_Parser($this->outfile, $this->_lex);
if ($this -> debug) {
$this->_parser->PrintTrace();
}
while ($this->_lex->advance($this->_parser)) {
$this->_parser->doParse($this->_lex->token, $this->_lex->value);
}
$this->_parser->doParse(0, 0);
}
}
//$a = new PHP_LexerGenerator('/development/File_ChessPGN/ChessPGN/Lexer.plex');
?>

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