Compare commits

..

94 Commits

Author SHA1 Message Date
Romain Quetiez
1bde9def1d Releasing 2.1.0-beta
SVN:2.1.0-beta[3417]
2014-11-12 09:56:18 +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
123 changed files with 7794 additions and 6221 deletions

View File

@@ -430,7 +430,7 @@ class URP_ActionGrant extends UserRightsBaseClass
//MetaModel::Init_InheritAttributes();
// Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked)
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name")));
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category"=>"", "more_values"=>"", "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
@@ -467,7 +467,7 @@ class URP_StimulusGrant extends UserRightsBaseClass
//MetaModel::Init_InheritAttributes();
// Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked)
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name")));
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category"=>"", "more_values"=>"", "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
@@ -503,7 +503,7 @@ class URP_AttributeGrant extends UserRightsBaseClass
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("actiongrantid", array("targetclass"=>"URP_ActionGrant", "jointype"=> "", "allowed_values"=>null, "sql"=>"actiongrantid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("actiongrantid", array("targetclass"=>"URP_ActionGrant", "jointype"=> "", "allowed_values"=>null, "sql"=>"actiongrantid", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("attcode", array("allowed_values"=>null, "sql"=>"attcode", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010-2014 Combodo SARL
//
// This file is part of iTop.
//
@@ -225,9 +225,8 @@ EOF
EOF
);
}
$s_captured_output = ob_get_contents();
ob_end_clean();
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
$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);
@@ -393,4 +392,3 @@ EOF
}
}
?>

View File

@@ -125,27 +125,29 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
// Master data sources
$sSynchroIcon = '';
$oReplicaSet = $this->GetMasterReplica();
$bSynchronized = false;
$oCreatorTask = null;
$bCanBeDeletedByTask = false;
$bCanBeDeletedByUser = true;
$aMasterSources = array();
if ($oReplicaSet->Count() > 0)
$aSyncData = $this->GetSynchroData();
if (count($aSyncData) > 0)
{
$bSynchronized = true;
while($aData = $oReplicaSet->FetchAssoc())
foreach ($aSyncData as $iSourceId => $aSourceData)
{
// Assumption: $aData['datasource'] will not be null because the data source id is always set...
$sApplicationURL = $aData['datasource']->GetApplicationUrl($this, $aData['replica']);
$sLink = $aData['datasource']->GetName();
$oDataSource = $aSourceData['source'];
$oReplica = reset($aSourceData['replica']); // Take the first one!
$sApplicationURL = $oDataSource->GetApplicationUrl($this, $oReplica);
$sLink = $oDataSource->GetName();
if (!empty($sApplicationURL))
{
$sLink = "<a href=\"$sApplicationURL\" target=\"_blank\">".$aData['datasource']->GetName()."</a>";
$sLink = "<a href=\"$sApplicationURL\" target=\"_blank\">".$oDataSource->GetName()."</a>";
}
if ($aData['replica']->Get('status_dest_creator') == 1)
if ($oReplica->Get('status_dest_creator') == 1)
{
$oCreatorTask = $aData['datasource'];
$oCreatorTask = $oDataSource;
$bCreatedByTask = true;
}
else
@@ -154,12 +156,12 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
}
if ($bCreatedByTask)
{
$sDeletePolicy = $aData['datasource']->Get('delete_policy');
$sDeletePolicy = $oDataSource->Get('delete_policy');
if (($sDeletePolicy == 'delete') || ($sDeletePolicy == 'update_then_delete'))
{
$bCanBeDeletedByTask = true;
}
$sUserDeletePolicy = $aData['datasource']->Get('user_delete_policy');
$sUserDeletePolicy = $oDataSource->Get('user_delete_policy');
if ($sUserDeletePolicy == 'nobody')
{
$bCanBeDeletedByUser = false;
@@ -172,9 +174,9 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
{
}
}
$aMasterSources[$aData['datasource']->GetKey()]['datasource'] = $aData['datasource'];
$aMasterSources[$aData['datasource']->GetKey()]['url'] = $sLink;
$aMasterSources[$aData['datasource']->GetKey()]['last_synchro'] = $aData['replica']->Get('status_last_seen');
$aMasterSources[$iSourceId]['datasource'] = $oDataSource;
$aMasterSources[$iSourceId]['url'] = $sLink;
$aMasterSources[$iSourceId]['last_synchro'] = $oReplica->Get('status_last_seen');
}
if (is_object($oCreatorTask))
@@ -941,12 +943,12 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
return $oDataTable->Display($oPage, $oSettings, $bDisplayMenu, $sSelectMode, $bViewLink, $aExtraParams);
}
static function DisplaySetAsCSV(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
static function DisplaySetAsCSV(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array(), $sCharset = 'UTF-8')
{
$oPage->add(self::GetSetAsCSV($oSet, $aParams));
$oPage->add(self::GetSetAsCSV($oSet, $aParams, $sCharset));
}
static function GetSetAsCSV(DBObjectSet $oSet, $aParams = array())
static function GetSetAsCSV(DBObjectSet $oSet, $aParams = array(), $sCharset = 'UTF-8')
{
$sSeparator = isset($aParams['separator']) ? $aParams['separator'] : ','; // default separator is comma
$sTextQualifier = isset($aParams['text_qualifier']) ? $aParams['text_qualifier'] : '"'; // default text qualifier is double quote
@@ -1064,7 +1066,8 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
else
{
$value = $oObj->Get($sAttCodeEx);
$aRow[] = $oAttDef->GetAsCSV($value, $sSeparator, $sTextQualifier, $oObj, $bLocalize);
$sCSVValue = $oAttDef->GetAsCSV($value, $sSeparator, $sTextQualifier, $oObj, $bLocalize);
$aRow[] = iconv('UTF-8', $sCharset.'//IGNORE//TRANSLIT', $sCSVValue);
}
}
}
@@ -1401,6 +1404,8 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
$sHtml .= "<p>\n";
$aFilterCriteria = $oSet->GetFilter()->GetCriteria();
$aMapCriteria = array();
// Todo: Investigate... The search criteria is an expression, i.e. a tree!
// I wonder if that code could work... cleanup required/recommended
foreach($aFilterCriteria as $aCriteria)
{
$aMapCriteria[$aCriteria['filtercode']][] = array('value' => $aCriteria['value'], 'opcode' => $aCriteria['opcode']);
@@ -1428,6 +1433,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
$sFilterValue = $aMapCriteria[$sFilterCode][0]['value'];
$sFilterOpCode = $aMapCriteria[$sFilterCode][0]['opcode'];
}
// Todo: Investigate...
if ($sFilterCode != 'company')
{
$oUnlimitedFilter->AddCondition($sFilterCode, $sFilterValue, $sFilterOpCode);
@@ -1436,20 +1442,24 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
}
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sFilterCode);
if ($oAttDef->IsExternalKey())
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
{
$sTargetClass = $oAttDef->GetTargetClass();
$oKeyAttDef = $oAttDef->GetFinalAttDef();
$sKeyAttClass = $oKeyAttDef->GetHostClass();
$sKeyAttCode = $oKeyAttDef->GetCode();
$sTargetClass = $oKeyAttDef->GetTargetClass();
$oSearch = new DBObjectSearch($sTargetClass);
$oSearch->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oAllowedValues = new DBObjectSet($oSearch);
$iFieldSize = $oAttDef->GetMaxSize();
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
$sHtml .= "<label>".MetaModel::GetFilterLabel($sClassName, $sFilterCode).":</label>&nbsp;";
$iFieldSize = $oKeyAttDef->GetMaxSize();
$iMaxComboLength = $oKeyAttDef->GetMaximumComboLength();
$sHtml .= "<label>".MetaModel::GetFilterLabel($sKeyAttClass, $sKeyAttCode).":</label>&nbsp;";
$aExtKeyParams = $aExtraParams;
$aExtKeyParams['iFieldSize'] = $oAttDef->GetMaxSize();
$aExtKeyParams['iMinChars'] = $oAttDef->GetMinAutoCompleteChars();
$sHtml .= UIExtKeyWidget::DisplayFromAttCode($oPage, $sFilterCode, $sClassName, $oAttDef->GetLabel(), $oAllowedValues, $sFilterValue, $sSearchFormId.'search_'.$sFilterCode, false, $sFilterCode, '', $aExtKeyParams, true);
$aExtKeyParams['iFieldSize'] = $oKeyAttDef->GetMaxSize();
$aExtKeyParams['iMinChars'] = $oKeyAttDef->GetMinAutoCompleteChars();
$sHtml .= UIExtKeyWidget::DisplayFromAttCode($oPage, $sKeyAttCode, $sKeyAttClass, $oAttDef->GetLabel(), $oAllowedValues, $sFilterValue, $sSearchFormId.'search_'.$sFilterCode, false, $sFilterCode, '', $aExtKeyParams, true);
}
else
{
@@ -1844,14 +1854,19 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
{
$sNullValue = "'$sNullValue'"; // Add quotes to turn this into a JS string if it's not a number
}
$oPage->add_ready_script("$('#$iId').bind('".implode(' ', $aEventsList)."', function(evt, sFormId) { return ValidateField('$iId', '$sPattern', $bMandatory, sFormId, $sNullValue) } );\n"); // Bind to a custom event: validate
$sOriginalValue = ($iFlags & OPT_ATT_MUSTCHANGE) ? "'".addslashes($value)."'" : 'undefined';
$oPage->add_ready_script("$('#$iId').bind('".implode(' ', $aEventsList)."', function(evt, sFormId) { return ValidateField('$iId', '$sPattern', $bMandatory, sFormId, $sNullValue, $sOriginalValue) } );\n"); // Bind to a custom event: validate
}
$aDependencies = MetaModel::GetDependentAttributes($sClass, $sAttCode); // List of attributes that depend on the current one
if (count($aDependencies) > 0)
{
$oPage->add_ready_script("$('#$iId').bind('change', function(evt, sFormId) { return oWizardHelper{$sFormPrefix}.UpdateDependentFields(['".implode("','", $aDependencies)."']) } );\n"); // Bind to a custom event: validate
// Unbind first to avoid duplicate event handlers in case of reload of the whole (or part of the) form
$oPage->add_ready_script("$('#$iId').unbind('change.dependencies').bind('change.dependencies', function(evt, sFormId) { return oWizardHelper{$sFormPrefix}.UpdateDependentFields(['".implode("','", $aDependencies)."']) } );\n"); // Bind to a custom event: validate
}
}
$oPage->add_dict_entry('UI:ValueMustBeSet');
$oPage->add_dict_entry('UI:ValueMustBeChanged');
$oPage->add_dict_entry('UI:ValueInvalidFormat');
return "<div>{$sHTMLValue}</div>";
}
@@ -2460,7 +2475,7 @@ EOF
{
// Possible return values are:
// HILIGHT_CLASS_CRITICAL, HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE
$current = HILIGHT_CLASS_NONE; // Not hilighted by default
$current = parent::GetHilightClass(); // Default computation
// Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
@@ -3074,7 +3089,7 @@ EOF
// Attribute is read-only
$sHTMLValue = $this->GetAsHTML($sAttCode);
$sHTMLValue .= '<input type="hidden" id="'.$sInputId.'" name="attr_'.$sPrefix.$sAttCode.'" value="'.htmlentities($this->Get($sAttCode), ENT_QUOTES, 'UTF-8').'"/>';
$sHTMLValue .= '<input type="hidden" id="'.$sInputId.'" name="attr_'.$sPrefix.$sAttCode.'" value="'.htmlentities($this->GetEditValue($sAttCode), ENT_QUOTES, 'UTF-8').'"/>';
$aFieldsMap[$sAttCode] = $sInputId;
$sComment .= $sSynchroIcon;
}
@@ -3251,7 +3266,11 @@ EOF
$currValue = $aKeys[0]; // The only value is the first key
//echo "<p>current value for $sAttCode : $currValue</p>";
$oDummyObj->Set($sAttCode, $currValue);
$aComments[$sAttCode] = '<input type="checkbox" checked id="enable_'.$iFormId.'_'.$sAttCode.'" onClick="ToogleField(this.checked, \''.$iFormId.'_'.$sAttCode.'\')"/>';
$aComments[$sAttCode] = '';
if ($sAttCode != MetaModel::GetStateAttributeCode($sClass))
{
$aComments[$sAttCode] .= '<input type="checkbox" checked id="enable_'.$iFormId.'_'.$sAttCode.'" onClick="ToogleField(this.checked, \''.$iFormId.'_'.$sAttCode.'\')"/>';
}
$aComments[$sAttCode] .= '<div class="mono_value">1</div>';
}
else
@@ -3278,7 +3297,11 @@ EOF
$sReadyScript .= "$('#multi_values_$sAttCode').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );";
$oDummyObj->Set($sAttCode, null);
$aComments[$sAttCode] = '<input type="checkbox" id="enable_'.$iFormId.'_'.$sAttCode.'" onClick="ToogleField(this.checked, \''.$iFormId.'_'.$sAttCode.'\')"/>';
$aComments[$sAttCode] = '';
if ($sAttCode != MetaModel::GetStateAttributeCode($sClass))
{
$aComments[$sAttCode] .= '<input type="checkbox" id="enable_'.$iFormId.'_'.$sAttCode.'" onClick="ToogleField(this.checked, \''.$iFormId.'_'.$sAttCode.'\')"/>';
}
$aComments[$sAttCode] .= '<div class="multi_values" id="multi_values_'.$sAttCode.'">'.$iCount.'</div>';
}
$sReadyScript .= 'ToogleField('.(($iCount == 1) ? 'true': 'false').', \''.$iFormId.'_'.$sAttCode.'\');'."\n";

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010-2014 Combodo SARL
//
// This file is part of iTop.
//
@@ -39,7 +39,11 @@ class CSVPage extends WebPage
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);
@@ -105,4 +109,3 @@ class CSVPage extends WebPage
}
}
?>

View File

@@ -506,11 +506,19 @@ abstract class DashletGroupBy extends Dashlet
$sGroupBy = $this->aProperties['group_by'];
$sStyle = $this->aProperties['style'];
// First perform the query - if the OQL is not ok, it will generate an exception : no need to go further
$oQuery = $this->oModelReflection->GetQuery($sQuery);
$sClass = $oQuery->GetClass();
$sClassAlias = $oQuery->GetClassAlias();
// First perform the query - if the OQL is not ok, it will generate an exception : no need to go further
try
{
$oQuery = $this->oModelReflection->GetQuery($sQuery);
$sClass = $oQuery->GetClass();
$sClassAlias = $oQuery->GetClassAlias();
}
catch(Exception $e)
{
// Invalid query, let the user edit the dashlet/dashboard anyhow
$sClass = '';
$sClassAlias = '';
}
// Check groupby... it can be wrong at this stage
if (preg_match('/^(.*):(.*)$/', $sGroupBy, $aMatches))
{

View File

@@ -551,6 +551,7 @@ EOF;
$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
);

View File

@@ -783,7 +783,8 @@ class DisplayBlock
$sLinkToToggle = $sLinkToToggle.'&advanced=1';
$sChecked = '';
}
$sAjaxLink = $sDownloadLink.'&charset=UTF-8'; // Includes &fields_advanced=1 if in advanced mode
/*
$sCSVData = cmdbAbstractObject::GetSetAsCSV($this->m_oSet, array('fields_advanced' => $bAdvancedMode));
$sCharset = MetaModel::GetConfig()->Get('csv_file_default_charset');
@@ -833,7 +834,7 @@ class DisplayBlock
$sHtml .= "<div id=\"csv_content_loading\"><div style=\"width: 250px; height: 20px; background: url(../setup/orange-progress.gif); border: 1px #999 solid; margin-left:auto; margin-right: auto; text-align: center;\">".Dict::S('UI:Loading')."</div></div><textarea id=\"csv_content\" style=\"display:none;\">\n";
//$sHtml .= htmlentities($sCSVData, ENT_QUOTES, 'UTF-8');
$sHtml .= "</textarea>\n";
$oPage->add_ready_script("$.post('$sDownloadLink', {}, function(data) { $('#csv_content').html(data); $('#csv_content_loading').hide(); $('#csv_content').show();} );");
$oPage->add_ready_script("$.post('$sAjaxLink', {}, function(data) { $('#csv_content').html(data); $('#csv_content_loading').hide(); $('#csv_content').show();} );");
break;
case 'modify':

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';
}
}
}
}
}

View File

@@ -38,7 +38,10 @@ class DesignerForm
protected $bReadOnly;
protected $sHierarchyPath; // Needed to manage the visibility of nested subform
protected $sHierarchyParent; // Needed to manage the visibility of nested subform
protected $sHierarchySelector; // Needed to manage the visibility of nested subform
protected $bDisplayed;
protected $aDefaultValues;
protected $sFieldsSuffix;
public function __construct()
{
@@ -47,14 +50,17 @@ class DesignerForm
$this->sScript = '';
$this->sReadyScript = '';
$this->sFormPrefix = '';
$this->sFieldsSuffix = '';
$this->sParamsContainer = '';
$this->sFormId = 'form_'.rand();
$this->oParentForm = null;
$this->bReadOnly = false;
$this->sHierarchyPath = '';
$this->sHierarchyParent = '';
$this->sHierarchySelector = '';
$this->StartFieldSet($this->sCurrentFieldSet);
$this->bDisplayed = true;
$this->aDefaultvalues = array();
}
public function AddField(DesignerFormField $oField)
@@ -140,6 +146,11 @@ class DesignerForm
$oP->add($sReturn);
}
}
public function GetFieldSets()
{
return $this->aFieldSets;
}
public function SetSubmitParams($sSubmitToUrl, $aSubmitParams)
{
@@ -153,6 +164,11 @@ class DesignerForm
$this->aSubmitParams = $oParentForm->aSubmitParams;
}
public function GetSubmitParams()
{
return array( 'url' => $this->sSubmitTo, 'params' => $this->aSubmitParams);
}
/**
* Helper to handle subforms hide/show
*/
@@ -168,7 +184,7 @@ class DesignerForm
{
return $this->sHierarchyPath;
}
/**
* Helper to handle subforms hide/show
*/
@@ -208,7 +224,7 @@ class DesignerForm
$aDetails = array();
if ($sLabel != '')
{
$sReturn .= '<tr><th colspan="4">'.$sLabel.'</th></tr>';
$sReturn .= $this->StartRow().'<th colspan="4">'.$sLabel.'</th>'.$this->EndRow();
}
@@ -219,17 +235,29 @@ class DesignerForm
{
$sFieldId = $this->GetFieldId($oField->GetCode());
$sValidation = $this->GetValidationArea($oField->GetCode(), '<span title="Apply" class="ui-icon ui-icon-circle-check"/>');
$sValidationFields = '</td><td class="prop_icon prop_apply">'.$sValidation.'</td><td class="prop_icon prop_cancel"><span title="Revert" class="ui-icon ui-icon-circle-close"/></td></tr>';
$sReturn .= '<tr id="row_'.$sFieldId.'"><td class="prop_label">'.$aRow['label'].'</td><td class="prop_value">'.$aRow['value'];
if (!($oField instanceof DesignerFormSelectorField))
$sValidationFields = '</td><td class="prop_icon prop_apply">'.$sValidation.'</td><td class="prop_icon prop_cancel"><span title="Revert" class="ui-icon ui-icon-circle-close"/></td>'.$this->EndRow();
$sPath = $this->GetHierarchyPath().'/'.$oField->GetCode();
if (is_null($aRow['label']))
{
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_value" colspan="2">'.$aRow['value'];
}
else
{
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_label">'.$aRow['label'].'</td><td class="prop_value">'.$aRow['value'];
}
if (!($oField instanceof DesignerFormSelectorField) && !($oField instanceof DesignerMultipleSubFormField))
{
$sReturn .= $sValidationFields;
}
$sNotifyParentSelectorJS = is_null($sNotifyParentSelector) ? 'null' : "'".addslashes($sNotifyParentSelector)."'";
$sAutoApply = $oField->IsAutoApply() ? 'true' : 'false';
$sHandlerEquals = $oField->GetHandlerEquals();
$sHandlerGetValue = $oField->GetHandlerGetValue();
$this->AddReadyScript(
<<<EOF
$('#row_$sFieldId').property_field({parent_selector: $sNotifyParentSelectorJS, field_id: '$sFieldId', auto_apply: $sAutoApply, value: '', submit_to: '$sActionUrl', submit_parameters: $sJSSubmitParams });
$('#row_$sFieldId').property_field({parent_selector: $sNotifyParentSelectorJS, field_id: '$sFieldId', equals: $sHandlerEquals, get_field_value: $sHandlerGetValue, auto_apply: $sAutoApply, value: '', submit_to: '$sActionUrl', submit_parameters: $sJSSubmitParams });
EOF
);
}
@@ -247,7 +275,7 @@ EOF
$sReturn .= '</table>';
$sReturn .= $sHiddenFields;
$sReturn .= '</form>';
$sReturn .= '<div id="prop_submit_result"/>'; // for the return of the submit operation
$sReturn .= '<div id="prop_submit_result"></div>'; // for the return of the submit operation
}
else
{
@@ -287,9 +315,26 @@ EOF
{
$oP->add($sReturn);
}
}
}
public function StartRow($sFieldId = null)
{
if ($sFieldId != null)
{
return '<tr id="row_'.$sFieldId.'" data-path="'.$this->GetHierarchyPath().'" data-selector="'.$this->GetHierarchyParent().'">';
}
return '<tr data-path="'.$this->GetHierarchyPath().'" data-selector="'.$this->GetHierarchyParent().'">';
}
public function EndRow()
{
return '</tr>';
}
public function RenderAsDialog($oPage, $sDialogId, $sDialogTitle, $iDialogWidth, $sOkButtonLabel, $sIntroduction = null)
{
$this->SetPrefix('dlg_'); // To make sure that the controls have different IDs that the property sheet which may be displayed at the same time
$sDialogTitle = addslashes($sDialogTitle);
$sOkButtonLabel = addslashes($sOkButtonLabel);
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
@@ -306,7 +351,7 @@ EOF
<<<EOF
$('#$sDialogId').dialog({
height: 'auto',
width: 500,
width: $iDialogWidth,
modal: true,
title: '$sDialogTitle',
buttons: [
@@ -344,9 +389,29 @@ EOF
public function GetPrefix()
{
return $this->sFormPrefix;
$sPrefix = '';
if ($this->oParentForm != null)
{
$sPrefix = $this->oParentForm->GetPrefix();
}
return $sPrefix.$this->sFormPrefix;
}
public function SetSuffix($sSuffix)
{
$this->sFieldsSuffix = $sSuffix;
}
public function GetSuffix()
{
$sSuffix = '';
if ($this->oParentForm != null)
{
$sSuffix = $this->oParentForm->GetSuffix();
}
return $sSuffix.$this->sFieldsSuffix;
}
public function SetReadOnly($bReadOnly = true)
{
$this->bReadOnly = $bReadOnly;
@@ -386,6 +451,25 @@ EOF
$this->oParentForm = $oParentForm;
}
public function SetDefaultValues($aDefaultValues)
{
if (!is_array($aDefaultValues)) return;
foreach($this->aFieldSets as $sLabel => $aFields)
{
foreach($aFields as $oField)
{
$oField->SetDefaultValueFrom($aDefaultValues);
}
}
}
public function GetDefaultValues()
{
return $this->aDefaultValues;
}
public function GetParentForm()
{
return $this->oParentForm;
@@ -420,17 +504,17 @@ EOF
public function GetFieldId($sCode)
{
return $this->sFormPrefix.'attr_'.$sCode;
return $this->GetPrefix().'attr_'.$sCode;
}
public function GetFieldName($sCode)
{
return 'attr_'.$sCode;
return 'attr_'.$sCode.$this->GetSuffix();
}
public function GetParamName($sCode)
{
return 'attr_'.$sCode;
return 'attr_'.$sCode.$this->GetSuffix();
}
public function GetValidationArea($sCode, $sContent = '')
@@ -441,6 +525,21 @@ EOF
{
return $this->sAsyncActionClass;
}
public function FindField($sFieldCode)
{
$oFoundField = false;
foreach($this->aFieldSets as $sLabel => $aFields)
{
foreach($aFields as $oField)
{
$oFoundField = $oField->FindField($sFieldCode);
if ($oFoundField !== false) break;
}
if ($oFoundField !== false) break;
}
return $oFoundField;
}
}
class DesignerTabularForm extends DesignerForm
@@ -456,6 +555,11 @@ class DesignerTabularForm extends DesignerForm
{
$this->aTable[] = $aRow;
}
public function RenderAsPropertySheet($oP, $bReturnHTML = false, $sNotifyParentSelector = null)
{
return $this->Render($oP, $bReturnHTML);
}
public function Render($oP, $bReturnHTML = false)
{
@@ -615,6 +719,11 @@ class DesignerFormField
return $this->bDisplayed;
}
public function GetFieldId()
{
return $this->oForm->GetFieldId($this->sCode);
}
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
{
$sId = $this->oForm->GetFieldId($this->sCode);
@@ -658,6 +767,36 @@ class DesignerFormField
{
$this->aCSSClasses[] = $sCSSClass;
}
/**
* A way to set/change the default value after constructing the field
*/
public function SetDefaultValueFrom($aAllDefaultValue)
{
if (array_key_exists($this->GetCode(), $aAllDefaultValue))
{
$this->defaultValue = $aAllDefaultValue[$this->GetCode()];
}
}
public function FindField($sFieldCode)
{
if ($this->sCode == $sFieldCode)
{
return $this;
}
return false;
}
public function GetHandlerEquals()
{
return 'null';
}
public function GetHandlerGetValue()
{
return 'null';
}
}
class DesignerLabelField extends DesignerFormField
@@ -674,7 +813,7 @@ class DesignerLabelField extends DesignerFormField
{
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
return array('label' => $this->sLabel, 'value' => $sDescription);
return array('label' => $this->sLabel, 'value' => $this->sDescription);
}
public function ReadParam(&$aValues)
@@ -723,6 +862,7 @@ class DesignerTextField extends DesignerFormField
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
{
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
if ($this->IsReadOnly())
{
@@ -819,6 +959,7 @@ class DesignerComboField extends DesignerFormField
protected $aAllowedValues;
protected $bMultipleSelection;
protected $bOtherChoices;
protected $sNullLabel;
public function __construct($sCode, $sLabel = '', $defaultValue = '')
{
@@ -826,6 +967,7 @@ class DesignerComboField extends DesignerFormField
$this->aAllowedValues = array();
$this->bMultipleSelection = false;
$this->bOtherChoices = false;
$this->sNullLabel = Dict::S('UI:SelectOne');
$this->bAutoApply = true;
}
@@ -844,11 +986,17 @@ class DesignerComboField extends DesignerFormField
{
$this->bOtherChoices = $bOtherChoices;
}
/**
* An empty label will disable the default empty value
*/
public function SetNullLabel($sLabel)
{
$this->sNullLabel = $sLabel;
}
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
{
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
$sChecked = $this->defaultValue ? 'checked' : '';
@@ -893,7 +1041,10 @@ class DesignerComboField extends DesignerFormField
else
{
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\">";
$sHtml .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>";
if ($this->sNullLabel != '')
{
$sHtml .= "<option value=\"\">".$this->sNullLabel."</option>";
}
}
foreach($this->aAllowedValues as $sKey => $sDisplayValue)
{
@@ -993,7 +1144,6 @@ class DesignerBooleanField extends DesignerFormField
}
}
class DesignerHiddenField extends DesignerFormField
{
public function __construct($sCode, $sLabel = '', $defaultValue = '')
@@ -1192,6 +1342,8 @@ class DesignerFormSelectorField extends DesignerFormField
$sName = $this->oForm->GetFieldName($this->sCode);
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
$this->aCSSClasses[] = 'formSelector';
$sCSSClasses = '';
if (count($this->aCSSClasses) > 0)
{
@@ -1205,12 +1357,12 @@ class DesignerFormSelectorField extends DesignerFormField
$aHiddenValues = array();
$sDisplayValue = '';
$sHiddenValue = '';
foreach($this->aSubForms as $sKey => $aFormData)
foreach($this->aSubForms as $iKey => $aFormData)
{
if ($sKey == $this->defaultValue)
if ($iKey == $this->defaultValue) // Default value is actually the index
{
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
$sHiddenValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\"/>";
$sHiddenValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($iKey, ENT_QUOTES, 'UTF-8')."\"/>";
break;
}
}
@@ -1221,11 +1373,11 @@ class DesignerFormSelectorField extends DesignerFormField
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
foreach($this->aSubForms as $sKey => $aFormData)
foreach($this->aSubForms as $iKey => $aFormData)
{
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');;
$sSelected = ($sKey == $this->defaultValue) ? 'selected' : '';
$sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>".$sDisplayValue."</option>";
$sSelected = ($iKey == $this->defaultValue) ? 'selected' : '';
$sHtml .= "<option value=\"$iKey\" $sSelected>".$sDisplayValue."</option>";
}
$sHtml .= "</select>";
}
@@ -1254,19 +1406,21 @@ class DesignerFormSelectorField extends DesignerFormField
// - data-path : uniquely identifies the combination of users choices that must be made to show the node
// - data-state : records the state, depending on the user choice on the FormSelectorField just above the node, but indepentantly from the visibility in the page (can be visible in the form itself being in a hidden form)
// Then a series of actions are performed to hide and show the relevant nodes, depending on the user choice
$sSelector = $this->oForm->GetHierarchyPath().'/'.$this->sCode;
$sSelector = $this->oForm->GetHierarchyPath().'/'.$this->sCode.$this->oForm->GetSuffix();
$oSubForm->SetHierarchyParent($sSelector);
$sPath = $this->oForm->GetHierarchyPath().'/'.$this->sCode.':'.$sKey;
$sPath = $this->oForm->GetHierarchyPath().'/'.$this->sCode.$this->oForm->GetSuffix().'-'.$sKey;
$oSubForm->SetHierarchyPath($sPath);
$oSubForm->SetDisplayed($sKey == $this->defaultValue);
$sState = ($sKey == $this->defaultValue) ? 'visible' : 'hidden';
$sHtml .= "</tbody><tbody data-selector=\"$sSelector\" data-path=\"$sPath\" data-state=\"$sState\" $sStyle>";
//$sHtml .= "</tbody><tbody data-selector=\"$sSelector\" data-path=\"$sPath\" data-state=\"$sState\" $sStyle>";
$sHtml .= $oSubForm->RenderAsPropertySheet($oP, true);
$sState = $this->oForm->IsDisplayed() ? 'visible' : 'hidden';
$sParentStyle = '';
if ($oParent = $this->oForm->GetParentForm())
{
$sParentStyle = ($oParent->IsDisplayed()) ? '' : 'style="display:none"';
$sParentSelector = $oParent->GetHierarchyParent();
$sParentPath = $oParent->GetHierarchyPath();
}
@@ -1275,7 +1429,8 @@ class DesignerFormSelectorField extends DesignerFormField
$sParentSelector = '';
$sParentPath = '';
}
$sHtml .= "</tbody><tbody data-selector=\"$sParentSelector\" data-path=\"$sParentPath\" data-state=\"$sState\" $sStyle>";
//$sHtml .= "</tbody><tbody data-selector=\"$sParentSelector\" data-path=\"$sParentPath\" data-state=\"$sState\" $sParentStyle>";
}
else
{
@@ -1287,27 +1442,8 @@ class DesignerFormSelectorField extends DesignerFormField
if ($sRenderMode == 'property')
{
$sSelector = $this->oForm->GetHierarchyPath().'/'.$this->sCode;
$oP->add_ready_script(
<<<EOF
$('#$sId').bind('change reverted', function() {
// Mark all the direct children as hidden
$('tbody[data-selector="$sSelector"]').attr('data-state', 'hidden');
// Mark the selected one as visible
var sSelectedHierarchy = '$sSelector:'+this.value;
$('tbody[data-path="'+sSelectedHierarchy+'"]').attr('data-state', 'visible');
// Show all items behind the current one
$('tbody[data-path^="$sSelector"]').show();
// Hide items behind the current one as soon as it is behind a hidden node (or itself is marked as hidden)
$('tbody[data-path^="$sSelector"][data-state="hidden"]').each(function(){
$(this).hide();
var sPath = $(this).attr('data-path');
$('tbody[data-path^="'+sPath+'/"]').hide();
});
});
EOF
);
$sSelector = $this->oForm->GetHierarchyPath().'/'.$this->sCode.$this->oForm->GetSuffix();
$oP->add_ready_script("InitFormSelectorField('$sId', '$sSelector');");
}
else
{
@@ -1330,6 +1466,44 @@ EOF
$this->aSubForms[$sKey]['form']->SetParentForm($this->oForm);
$this->aSubForms[$sKey]['form']->ReadParams($aValues);
}
public function SetDefaultValueFrom($aAllDefaultValues)
{
if (array_key_exists($this->GetCode(), $aAllDefaultValues))
{
$selectedValue = $aAllDefaultValues[$this->GetCode()];
foreach($this->aSubForms as $iKey => $aFormData)
{
$sId = $this->oForm->GetFieldId($this->sCode);
if ($selectedValue == $aFormData['value'])
{
$this->defaultValue =$iKey;
$aDefaultValues = $this->oForm->GetDefaultValues();
$oSubForm = $aFormData['form'];
$oSubForm->SetDefaultValues($aAllDefaultValues);
}
}
}
}
public function FindField($sFieldCode)
{
$oField = parent::FindField($sFieldCode);
if ($oField === false)
{
// Look in the subforms
foreach($this->aSubForms as $sKey => $aFormData)
{
$oSubForm = $aFormData['form'];
$oField = $oSubForm->FindField($sFieldCode);
if ($oField !== false)
{
break;
}
}
}
return $oField;
}
}
class DesignerSubFormField extends DesignerFormField
@@ -1362,6 +1536,31 @@ class DesignerSubFormField extends DesignerFormField
$this->oSubForm->SetParentForm($this->oForm);
$this->oSubForm->ReadParams($aValues);
}
public function FindField($sFieldCode)
{
$oField = parent::FindField($sFieldCode);
if ($oField === false)
{
// Look in the subform
$oField = $this->oSubForm->FindField($sFieldCode);
}
return $oField;
}
}
class DesignerStaticTextField extends DesignerFormField
{
public function __construct($sCode, $sLabel = '', $defaultValue = '')
{
parent::__construct($sCode, $sLabel, $defaultValue);
}
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
{
return array('label' => $this->sLabel, 'value' => $this->defaultValue);
}
}
?>

View File

@@ -587,8 +587,7 @@ EOF
header($s_header);
}
}
$s_captured_output = ob_get_contents();
ob_end_clean();
$s_captured_output = $this->ob_get_clean_safe();
$sHtml = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
$sHtml .= "<html>\n";
$sHtml .= "<head>\n";
@@ -1039,4 +1038,3 @@ EOF
$this->m_sMessage = $sMessage;
}
}
?>

View File

@@ -239,7 +239,7 @@ EOF
*/
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";
@@ -259,11 +259,11 @@ EOF
$sEmptyRowStyle = 'style="display:none;"';
}
$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')."<input type=\"hidden\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\"></td></td>";
foreach($aData as $iRowId => $aRow)
{
$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

View File

@@ -783,11 +783,16 @@ class utils
$sOQL = addslashes($param->GetFilter()->ToOQL(true));
$sFilter = urlencode($param->GetFilter()->serialize());
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/xlsx-export.js');
$sXlsxFilter = $param->GetFilter()->serialize();
$sXlsxJSFilter = addslashes($sXlsxFilter);
$aResult = array(
new SeparatorPopupMenuItem(),
// Static menus: Email this page, CSV Export & Add to Dashboard
new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'), "mailto:?body=".urlencode($sUrl).' '), // Add an extra space to make it work in Outlook
new URLPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), $sUrl."&format=csv"),
new JSPopupMenuItem('xlsx-export', Dict::S('ExcelExporter:ExportMenu'), "XlsxExportDialog('$sXlsxJSFilter');", array()),
new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL')"),
new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')"),
);
@@ -802,11 +807,14 @@ class utils
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage(get_class($oObj));
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/xlsx-export.js');
$sXlsxJSFilter = addslashes($sFilter);
$aResult = array(
new SeparatorPopupMenuItem(),
// Static menus: Email this page & CSV Export
new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'), "mailto:?subject=".urlencode($oObj->GetRawName())."&body=".urlencode($sUrl).' '), // Add an extra space to make it work in Outlook
new URLPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".urlencode($sFilter)."&format=csv&{$sContext}"),
new JSPopupMenuItem('xlsx-export', Dict::S('ExcelExporter:ExportMenu'), "XlsxExportDialog('$sXlsxJSFilter');", array()),
);
break;

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2013 Combodo SARL
// Copyright (C) 2010-2014 Combodo SARL
//
// This file is part of iTop.
//
@@ -68,6 +68,7 @@ class WebPage implements Page
protected $sContentType;
protected $sContentDisposition;
protected $sContentFileName;
protected $bTrashUnexpectedOutput;
protected $s_sOutputFormat;
protected $a_OutputOptions;
@@ -88,6 +89,7 @@ class WebPage implements Page
$this->sContentType = '';
$this->sContentDisposition = '';
$this->sContentFileName = '';
$this->bTrashUnexpectedOutput = false;
$this->s_OutputFormat = utils::ReadParam('output_format', 'html');
$this->a_OutputOptions = array();
ob_start(); // Start capturing the output
@@ -392,22 +394,55 @@ class WebPage implements Page
}
/**
* Discard unexpected output data
* Discard unexpected output data (such as PHP warnings)
* This is a MUST when the Page output is DATA (download of a document, download CSV export, download ...)
*/
public function TrashUnexpectedOutput()
{
// This protection is redundant with a protection implemented in MetaModel::IncludeModule
// which detects such issues while loading module files
// Here, the purpose is to detect and discard characters produced by the code execution (echo)
$sPreviousContent = ob_get_clean();
if (trim($sPreviousContent) != '')
$this->bTrashUnexpectedOutput = true;
}
/**
* Read the output buffer and deal with its contents:
* - trash unexpected output if the flag has been set
* - report unexpected behaviors such as the output buffering being stopped
*
* Possible improvement: I've noticed that several output buffers are stacked,
* if they are not empty, the output will be corrupted. The solution would
* consist in unstacking all of them (and concatenate the contents).
*/
protected function ob_get_clean_safe()
{
$sOutput = ob_get_contents();
if ($sOutput === false)
{
if (Utils::GetConfig() && Utils::GetConfig()->Get('debug_report_spurious_chars'))
$sMsg = "Design/integration issue: No output buffer. Some piece of code has called ob_get_clean() or ob_end_clean() without calling ob_start()";
if ($this->bTrashUnexpectedOutput)
{
IssueLog::Error("Output already started before downloading file:\nContent was:'$sPreviousContent'\n");
IssueLog::Error($sMsg);
$sOutput = '';
}
else
{
$sOutput = $sMsg;
}
}
else
{
ob_end_clean(); // on some versions of PHP doing so when the output buffering is stopped can cause a notice
if ($this->bTrashUnexpectedOutput)
{
if (trim($sOutput) != '')
{
if (Utils::GetConfig() && Utils::GetConfig()->Get('debug_report_spurious_chars'))
{
IssueLog::Error("Trashing unexpected output:'$s_captured_output'\n");
}
}
$sOutput = '';
}
}
return $sOutput;
}
/**
@@ -419,8 +454,8 @@ class WebPage implements Page
{
header($s_header);
}
$s_captured_output = ob_get_contents();
ob_end_clean();
$s_captured_output = $this->ob_get_clean_safe();
echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
echo "<html>\n";
echo "<head>\n";

View File

@@ -0,0 +1,456 @@
<?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() )
{
$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);
$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_numeric($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="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"/>');
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,5 +1,5 @@
<?php
// Copyright (C) 2010-2013 Combodo SARL
// Copyright (C) 2010-2014 Combodo SARL
//
// This file is part of iTop.
//
@@ -51,6 +51,9 @@ class XMLPage extends WebPage
{
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)
@@ -79,8 +82,7 @@ class XMLPage extends WebPage
}
else
{
$s_captured_output = ob_get_contents();
ob_end_clean();
$s_captured_output = $this->ob_get_clean_safe();
foreach($this->a_headers as $s_header)
{
header($s_header);
@@ -101,13 +103,4 @@ class XMLPage extends WebPage
public function table($aConfig, $aData, $aParams = array())
{
}
public function TrashUnexpectedOutput()
{
if (!$this->m_bPassThrough)
{
parent::TrashUnexpectedOutput();
}
}
}
?>

View File

@@ -91,6 +91,9 @@ abstract class AsyncTask extends DBObject
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())));
}
/**
@@ -117,27 +120,34 @@ abstract class AsyncTask extends DBObject
{
try
{
$this->Set('status', 'running');
$this->Set('started', time());
$this->DBUpdate();
return self::OK;
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();
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()
public function GetRetryDelay($iErrorCode = null)
{
$iRetryDelay = 600;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
@@ -149,7 +159,7 @@ abstract class AsyncTask extends DBObject
return $iRetryDelay;
}
public function GetMaxRetries()
public function GetMaxRetries($iErrorCode = null)
{
$iMaxRetries = 0;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
@@ -160,10 +170,16 @@ abstract class AsyncTask extends DBObject
}
}
/**
* Override to notify people that a task cannot be performed
*/
protected function OnDefinitiveFailure()
{
}
protected function OnInsert()
{
$this->Set('created', time());
$this->Set('remaining_retries', $this->GetMaxRetries());
}
/**
@@ -191,22 +207,7 @@ abstract class AsyncTask extends DBObject
}
catch(Exception $e)
{
$iRemaining = $this->Get('remaining_retries');
if ($iRemaining > 0)
{
$iRetryDelay = $this->GetRetryDelay();
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$e->getMessage().' - 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);
$this->DBUpdate();
}
else
{
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$e->getMessage());
}
$this->HandleError($e->getMessage(), $e->getCode());
}
}
else
@@ -217,7 +218,56 @@ abstract class AsyncTask extends DBObject
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();
}
}
/**

View File

@@ -3426,12 +3426,69 @@ class AttributeBlob extends AttributeDefinition
}
// Facilitate things: allow the user to Set the value from a string
// Facilitate things: allow administrators to upload a document
// from a CSV by specifying its path/URL
public function MakeRealValue($proposedValue, $oHostObj)
{
if (!is_object($proposedValue))
{
return new ormDocument($proposedValue, 'text/plain');
if (file_exists($proposedValue) && UserRights::IsAdministrator())
{
$sContent = file_get_contents($proposedValue);
$sExtension = strtolower(pathinfo($proposedValue, PATHINFO_EXTENSION));
$sMimeType = "application/x-octoet-stream";
$aKnownExtensions = array(
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12.xlsx',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'png' => 'image/png',
'pdf' => 'application/pdf',
'doc' => 'application/msword',
'dot' => 'application/msword',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'vsd' => 'application/x-visio',
'vdx' => 'application/visio.drawing',
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'zip' => 'application/zip',
'txt' => 'text/plain',
'htm' => 'text/html',
'html' => 'text/html',
'exe' => 'application/octet-stream'
);
if (!array_key_exists($sExtension, $aKnownExtensions) && extension_loaded('fileinfo'))
{
$finfo = new finfo(FILEINFO_MIME);
$sMimeType = $finfo->file($proposedValue);
}
return new ormDocument($sContent, $sMimeType);
}
else
{
return new ormDocument($proposedValue, 'text/plain');
}
}
return $proposedValue;
}
@@ -3717,7 +3774,7 @@ class AttributeStopWatch extends AttributeDefinition
);
$aThresholds = array();
foreach ($this->ListThresholds() as $iThreshold => $aFoo)
foreach ($this->ListThresholds() as $iThreshold => $aDefinition)
{
$sThPrefix = '_'.$iThreshold;
$value->DefineThreshold(
@@ -3725,7 +3782,8 @@ class AttributeStopWatch extends AttributeDefinition
self::DateToSeconds($aCols[$sPrefix.$sThPrefix.'_deadline']),
(bool)($aCols[$sPrefix.$sThPrefix.'_passed'] == 1),
(bool)($aCols[$sPrefix.$sThPrefix.'_triggered'] == 1),
$aCols[$sPrefix.$sThPrefix.'_overrun']
$aCols[$sPrefix.$sThPrefix.'_overrun'],
array_key_exists('highlight', $aDefinition) ? $aDefinition['highlight'] : null
);
}

View File

@@ -428,38 +428,38 @@ abstract class CMDBObject extends DBObject
}
public function DBInsert()
{
return $this->DBInsertTracked_Internal();
}
public function DBInsertTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$ret = $this->DBInsertTracked_Internal();
return $ret;
}
public function DBInsertTrackedNoReload(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$ret = $this->DBInsertTracked_Internal(true);
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();
}
return $ret;
}

View File

@@ -79,7 +79,7 @@ class CMDBSource
// Override the default port
$sServer = $aConnectInfo[0];
$iPort = (int)$aConnectInfo[1];
self::$m_oMysqli = new mysqli(self::$m_sDBHost, self::$m_sDBUser, self::$m_sDBPwd, '', $iPort);
self::$m_oMysqli = new mysqli($sServer, self::$m_sDBUser, self::$m_sDBPwd, '', $iPort);
}
else
{

View File

@@ -769,6 +769,30 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'user_rights_legacy' => array(
'type' => 'bool',
'description' => 'Set to true to restore the buggy algorithm for the computation of user rights (within the same profile, ALLOW on the class itself has precedence on DENY of a parent class)',
'default' => false,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'xlsx_exporter_cleanup_old_files_delay' => array(
'type' => 'int',
'description' => 'Delay (in seconds) for which to let the exported XLSX files on the server so that the user who initiated the export can download the result',
'default' => 86400,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'xlsx_exporter_memory_limit' => array(
'type' => 'string',
'description' => 'Memory limit to use when (interactively) exporting data to Excel',
'default' => '2048M', // Huuuuuuge 2GB!
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
);
public function IsProperty($sPropCode)

View File

@@ -94,7 +94,8 @@ abstract class DBObject implements iDisplay
private $m_bFullyLoaded = false; // Compound objects can be partially loaded
private $m_aLoadedAtt = array(); // Compound objects can be partially loaded, array of sAttCode
protected $m_aModifiedAtt = array(); // list of (potentially) modified sAttCodes
protected $m_oMasterReplicaSet = null; // Set of SynchroReplica related to this object
protected $m_aSynchroData = null; // Set of Synch data related to this object
protected $m_sHighlightCode = null;
// Use the MetaModel::NewObject to build an object (do we have to force it?)
public function __construct($aRow = null, $sClassAlias = '', $aAttToLoad = null, $aExtendedDataSpec = null)
@@ -557,6 +558,64 @@ abstract class DBObject implements iDisplay
{
return $this->m_aExtendedData;
}
/**
* Set the HighlightCode if the given code has a greater rank than the current HilightCode
* @param string $sCode
* @return void
*/
protected function SetHighlightCode($sCode)
{
$aHighlightScale = MetaModel::GetHighlightScale(get_class($this));
$fCurrentRank = 0.0;
if (($this->m_sHighlightCode !== null) && array_key_exists($this->m_sHighlightCode, $aHighlightScale))
{
$fCurrentRank = $aHighlightScale[$this->m_sHighlightCode]['rank'];
}
if (array_key_exists($sCode, $aHighlightScale))
{
$fRank = $aHighlightScale[$sCode]['rank'];
if ($fRank > $fCurrentRank)
{
$this->m_sHighlightCode = $sCode;
}
}
}
/**
* Get the current HighlightCode
* @return string The Hightlight code (null if none set, meaning rank = 0)
*/
protected function GetHighlightCode()
{
return $this->m_sHighlightCode;
}
protected function ComputeHighlightCode()
{
// First if the state defines a HiglightCode, apply it
$sState = $this->GetState();
if ($sState != '')
{
$sCode = MetaModel::GetHighlightCode(get_class($this), $sState);
$this->SetHighlightCode($sCode);
}
// The check for each StopWatch if a HighlightCode is effective
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
{
if ($oAttDef instanceof AttributeStopWatch)
{
$oStopWatch = $this->Get($sAttCode);
$sCode = $oStopWatch->GetHighlightCode();
if ($sCode !== '')
{
$this->SetHighlightCode($sCode);
}
}
}
return $this->GetHighlightCode();
}
/**
* Updates the value of an external field by (re)loading the object
@@ -785,10 +844,27 @@ abstract class DBObject implements iDisplay
/**
* Get the icon representing this object
* @param boolean $bImgTag If true the result is a full IMG tag (or an emtpy string if no icon is defined)
* @return string Either the full IMG tag ($bImgTag == true) or just the path to the icon file
* @return string Either the full IMG tag ($bImgTag == true) or just the URL to the icon file
*/
public function GetIcon($bImgTag = true)
{
$sCode = $this->ComputeHighlightCode();
if($sCode != '')
{
$aHighlightScale = MetaModel::GetHighlightScale(get_class($this));
if (array_key_exists($sCode, $aHighlightScale))
{
$sIconUrl = $aHighlightScale[$sCode]['icon'];
if($bImgTag)
{
return "<img src=\"$sIconUrl\" style=\"vertical-align:middle\"/>";
}
else
{
return $sIconUrl;
}
}
}
return MetaModel::GetClassIcon(get_class($this), $bImgTag);
}
@@ -1102,48 +1178,45 @@ abstract class DBObject implements iDisplay
if ($this->InSyncScope())
{
$oReplicaSet = $this->GetMasterReplica();
if ($oReplicaSet->Count() > 0)
foreach ($this->GetSynchroData() as $iSourceId => $aSourceData)
{
while($aData = $oReplicaSet->FetchAssoc())
foreach ($aSourceData['replica'] as $oReplica)
{
$oDataSource = $aData['datasource'];
$oReplica = $aData['replica'];
$oDeletionPlan->AddToDelete($oReplica, DEL_SILENT);
}
$oDataSource = $aSourceData['source'];
if ($oDataSource->GetKey() == SynchroExecution::GetCurrentTaskId())
{
// The current task has the right to delete the object
continue;
}
$oReplica = reset($aSourceData['replica']); // Take the first one
if ($oReplica->Get('status_dest_creator') != 1)
{
// The object is not owned by the task
continue;
}
if ($oDataSource->GetKey() == SynchroExecution::GetCurrentTaskId())
{
// The current task has the right to delete the object
continue;
}
if ($oReplica->Get('status_dest_creator') != 1)
{
// The object is not owned by the task
continue;
}
$sLink = $oDataSource->GetName();
$sUserDeletePolicy = $oDataSource->Get('user_delete_policy');
switch($sUserDeletePolicy)
{
case 'nobody':
$this->m_aDeleteIssues[] = Dict::Format('Core:Synchro:TheObjectCannotBeDeletedByUser_Source', $sLink);
break;
$sLink = $oDataSource->GetName();
$sUserDeletePolicy = $oDataSource->Get('user_delete_policy');
switch($sUserDeletePolicy)
case 'administrators':
if (!UserRights::IsAdministrator())
{
case 'nobody':
$this->m_aDeleteIssues[] = Dict::Format('Core:Synchro:TheObjectCannotBeDeletedByUser_Source', $sLink);
break;
case 'administrators':
if (!UserRights::IsAdministrator())
{
$this->m_aDeleteIssues[] = Dict::Format('Core:Synchro:TheObjectCannotBeDeletedByUser_Source', $sLink);
}
break;
case 'everybody':
default:
// Ok
break;
}
break;
case 'everybody':
default:
// Ok
break;
}
}
}
@@ -1998,17 +2071,55 @@ abstract class DBObject implements iDisplay
// array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD
$bSuccess = true;
foreach ($aTransitionDef['actions'] as $sActionHandler)
foreach ($aTransitionDef['actions'] as $actionHandler)
{
// std PHP spec
$aActionCallSpec = array($this, $sActionHandler);
if (!is_callable($aActionCallSpec))
if (is_string($actionHandler))
{
throw new CoreException("Unable to call action: ".get_class($this)."::$sActionHandler");
return;
// Old (pre-2.0.4) action definition without any parameter
$aActionCallSpec = array($this, $sActionHandler);
if (!is_callable($aActionCallSpec))
{
throw new CoreException("Unable to call action: ".get_class($this)."::$sActionHandler");
return;
}
$bRet = call_user_func($aActionCallSpec, $sStimulusCode);
}
else // if (is_array($actionHandler))
{
// New syntax: 'verb' and typed parameters
$sAction = $actionHandler['verb'];
$aParams = array();
foreach($actionHandler['params'] as $aDefinition)
{
$sParamType = array_key_exists('type', $aDefinition) ? $aDefinition['type'] : 'string';
switch($sParamType)
{
case 'int':
$value = (int)$aDefinition['value'];
break;
case 'float':
$value = (float)$aDefinition['value'];
break;
case 'bool':
$value = (bool)$aDefinition['value'];
break;
case 'reference':
$value = ${$aDefinition['value']};
break;
case 'string':
default:
$value = (string)$aDefinition['value'];
}
$aParams[] = $value;
}
$aCallSpec = array($this, $sAction);
$bRet = call_user_func_array($aCallSpec, $aParams);
}
$bRet = call_user_func($aActionCallSpec, $sStimulusCode);
// if one call fails, the whole is considered as failed
if (!$bRet) $bSuccess = false;
}
@@ -2474,45 +2585,77 @@ abstract class DBObject implements iDisplay
}
/**
* WILL DEPRECATED SOON
* Caching relying on an object set is not efficient since 2.0.3
* Use GetSynchroData instead
*
* Get all the synchro replica related to this object
* @param none
* @return DBObjectSet Set with two columns: R=SynchroReplica S=SynchroDataSource
*/
public function GetMasterReplica()
{
if ($this->m_oMasterReplicaSet == null)
$sOQL = "SELECT replica,datasource FROM SynchroReplica AS replica JOIN SynchroDataSource AS datasource ON replica.sync_source_id=datasource.id WHERE replica.dest_class = :dest_class AND replica.dest_id = :dest_id";
$oReplicaSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array() /* order by*/, array('dest_class' => get_class($this), 'dest_id' => $this->GetKey()));
return $oReplicaSet;
}
/**
* Get all the synchro data related to this object
* @param none
* @return array of data_source_id => array
* 'source' => $oSource,
* 'attributes' => array of $oSynchroAttribute
* 'replica' => array of $oReplica (though only one should exist, misuse of the data sync can have this consequence)
*/
public function GetSynchroData()
{
if ($this->m_aSynchroData == null)
{
//$aParentClasses = MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL);
//$sClassesList = "'".implode("','", $aParentClasses)."'";
$sOQL = "SELECT replica,datasource FROM SynchroReplica AS replica JOIN SynchroDataSource AS datasource ON replica.sync_source_id=datasource.id WHERE replica.dest_class = :dest_class AND replica.dest_id = :dest_id";
$oReplicaSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array() /* order by*/, array('dest_class' => get_class($this), 'dest_id' => $this->GetKey()));
$this->m_oMasterReplicaSet = $oReplicaSet;
$this->m_aSynchroData = array();
while($aData = $oReplicaSet->FetchAssoc())
{
$iSourceId = $aData['datasource']->GetKey();
if (!array_key_exists($iSourceId, $this->m_aSynchroData))
{
$aAttributes = array();
$oAttrSet = $aData['datasource']->Get('attribute_list');
while($oSyncAttr = $oAttrSet->Fetch())
{
$aAttributes[$oSyncAttr->Get('attcode')] = $oSyncAttr;
}
$this->m_aSynchroData[$iSourceId] = array(
'source' => $aData['datasource'],
'attributes' => $aAttributes,
'replica' => array()
);
}
// Assumption: $aData['datasource'] will not be null because the data source id is always set...
$this->m_aSynchroData[$iSourceId]['replica'][] = $aData['replica'];
}
}
else
{
$this->m_oMasterReplicaSet->Rewind();
}
return $this->m_oMasterReplicaSet;
return $this->m_aSynchroData;
}
public function GetSynchroReplicaFlags($sAttCode, &$aReason)
{
$iFlags = OPT_ATT_NORMAL;
$oSet = $this->GetMasterReplica();
while($aData = $oSet->FetchAssoc())
foreach ($this->GetSynchroData() as $iSourceId => $aSourceData)
{
if ($aData['datasource']->GetKey() == SynchroExecution::GetCurrentTaskId())
if ($iSourceId == SynchroExecution::GetCurrentTaskId())
{
// Ignore the current task (check to write => ok)
continue;
}
// Assumption: $aData['datasource'] will not be null because the data source id is always set...
$oReplica = $aData['replica'];
$oSource = $aData['datasource'];
$oAttrSet = $oSource->Get('attribute_list');
while($oSyncAttr = $oAttrSet->Fetch())
// Assumption: one replica - take the first one!
$oReplica = reset($aSourceData['replica']);
$oSource = $aSourceData['source'];
if (array_key_exists($sAttCode, $aSourceData['attributes']))
{
if (($oSyncAttr->Get('attcode') == $sAttCode) && ($oSyncAttr->Get('update') == 1) && ($oSyncAttr->Get('update_policy') == 'master_locked'))
$oSyncAttr = $aSourceData['attributes'][$sAttCode];
if (($oSyncAttr->Get('update') == 1) && ($oSyncAttr->Get('update_policy') == 'master_locked'))
{
$iFlags |= OPT_ATT_SLAVE;
$sUrl = $oSource->GetApplicationUrl($this, $oReplica);
@@ -2563,6 +2706,15 @@ abstract class DBObject implements iDisplay
public function GetHilightClass()
{
$sCode = $this->ComputeHighlightCode();
if($sCode != '')
{
$aHighlightScale = MetaModel::GetHighlightScale(get_class($this));
if (array_key_exists($sCode, $aHighlightScale))
{
return $aHighlightScale[$sCode]['color'];
}
}
return HILIGHT_CLASS_NONE;
}

View File

@@ -972,6 +972,9 @@ class DBObjectSearch
// 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 = MetaModel::MakeSelectQuery($this, $aOrderBy, $aArgs);

View File

@@ -48,7 +48,7 @@ class DBObjectSet
* Create a new set based on a Search definition.
*
* @param DBObjectSearch $oFilter The search filter defining the objects which are part of the set (multiple columns/objects per row are supported)
* @param hash $aOrderBy
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
* @param hash $aArgs Values to substitute for the search/query parameters (if any). Format: param_name => value
* @param hash $aExtendedDataSpec
* @param int $iLimitCount Maximum number of rows to load (i.e. equivalent to MySQL's LIMIT start, count)
@@ -654,7 +654,11 @@ class DBObjectSet
else
{
// Pick the row from the objects added *in memory*
$oRetObj = $this->m_aAddedObjects[$this->m_iCurrRow - $this->m_iNumLoadedDBRows][$sRequestedClassAlias];
$aRetObjects = array();
foreach ($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass)
{
$aRetObjects[$sClassAlias] = $this->m_aAddedObjects[$this->m_iCurrRow - $this->m_iNumLoadedDBRows][$sClassAlias];
}
}
$this->m_iCurrRow++;
return $aRetObjects;
@@ -1094,8 +1098,11 @@ class DBObjectSet
{
foreach($aVals as $sCode => $oExpr)
{
$oScalarExpr = $oExpr->GetAsScalar($aScalarArgs);
$aConst[$sClassAlias][$sCode] = $oScalarExpr->GetValue();
if (is_object($oExpr)) // Array_merge_recursive creates an array when the same key is present multiple times... ignore them
{
$oScalarExpr = $oExpr->GetAsScalar($aScalarArgs);
$aConst[$sClassAlias][$sCode] = $oScalarExpr->GetValue();
}
}
}
return $aConst;
@@ -1149,5 +1156,3 @@ function HashCountComparison($a, $b) // Sort descending on 'count'
}
return ($a['count'] > $b['count']) ? -1 : 1;
}
?>

View File

@@ -177,6 +177,15 @@ class EMail
}
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();
@@ -396,4 +405,75 @@ class EMail
}
?>
/////////////////////////////////////////////////////////////////////////////////////
/**
* 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

@@ -351,7 +351,7 @@ class EventLoginUsage extends Event
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_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
$aZList = array('date', 'user_id');
if (MetaModel::IsValidAttCode('Contact', 'name'))
{

View File

@@ -299,14 +299,11 @@ class BinaryExpression extends Expression
{
$aResult[$this->m_oRightExpr->GetParent()][$this->m_oRightExpr->GetName()] = $this->m_oLeftExpr;
}
else
{
$aResult = array_merge($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields()) ;
}
}
else
else if ($this->m_sOperator == 'AND')
{
$aResult = array_merge($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields()) ;
// Strictly, this should be done only for the AND operator
$aResult = array_merge_recursive($this->m_oRightExpr->ListConstantFields(), $this->m_oLeftExpr->ListConstantFields());
}
return $aResult;
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2013 Combodo SARL
// Copyright (C) 2010-2014 Combodo SARL
//
// This file is part of iTop.
//
@@ -279,6 +279,7 @@ abstract class MetaModel
private static $m_aChildClasses = array(); // array of ("classname" => array of "childclass")
private static $m_aClassParams = array(); // array of ("classname" => array of class information)
private static $m_aHighlightScales = array(); // array of ("classname" => array of highlightscale information)
static public function GetParentPersistentClass($sRefClass)
{
@@ -1908,6 +1909,50 @@ abstract class MetaModel
self::$m_aTransitions[$sTargetClass][$sStateCode] = array();
}
public static function Init_DefineHighlightScale($aHighlightScale)
{
$sTargetClass = self::GetCallersPHPClass("Init");
self::$m_aHighlightScales[$sTargetClass] = $aHighlightScale;
}
public static function GetHighlightScale($sTargetClass)
{
$aScale = array();
$aParentScale = array();
$sParentClass = self::GetParentPersistentClass($sTargetClass);
if (!empty($sParentClass))
{
// inherit the scale from the parent class
$aParentScale = self::GetHighlightScale($sParentClass);
}
if (array_key_exists($sTargetClass, self::$m_aHighlightScales))
{
$aScale = self::$m_aHighlightScales[$sTargetClass];
}
return array_merge($aParentScale, $aScale); // Merge both arrays, the values from the last one have precedence
}
public static function GetHighlightCode($sTargetClass, $sStateCode)
{
$sCode = '';
if ( array_key_exists($sTargetClass, self::$m_aStates)
&& array_key_exists($sStateCode, self::$m_aStates[$sTargetClass])
&& array_key_exists('highlight', self::$m_aStates[$sTargetClass][$sStateCode]) )
{
$sCode = self::$m_aStates[$sTargetClass][$sStateCode]['highlight']['code'];
}
else
{
// Check the parent's definition
$sParentClass = self::GetParentPersistentClass($sTargetClass);
if (!empty($sParentClass))
{
$sCode = self::GetHighlightCode($sParentClass, $sStateCode);
}
}
return $sCode;
}
public static function Init_OverloadStateAttribute($sStateCode, $sAttCode, $iFlags)
{
// Warning: this is not sufficient: the flags have to be copied to the states that are inheriting from this state
@@ -2122,6 +2167,10 @@ abstract class MetaModel
{
$aScalarArgs[$sArgName] = (string) $value;
}
elseif (is_null($value))
{
$aScalarArgs[$sArgName] = null;
}
}
}
// Add standard contextual arguments
@@ -2169,6 +2218,9 @@ abstract class MetaModel
}
/**
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
*/
public static function MakeSelectQuery(DBObjectSearch $oFilter, $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
@@ -2179,32 +2231,44 @@ abstract class MetaModel
$aOrderSpec = array();
foreach ($aOrderBy as $sFieldAlias => $bAscending)
{
if ($sFieldAlias != 'id')
{
MyHelpers::CheckValueInArray('field name in ORDER BY spec', $sFieldAlias, self::GetAttributesList($sClass));
}
if (!is_bool($bAscending))
{
throw new CoreException("Wrong direction in ORDER BY spec, found '$bAscending' and expecting a boolean value");
}
if (self::IsValidAttCode($sClass, $sFieldAlias))
$iDotPos = strpos($sFieldAlias, '.');
if ($iDotPos === false)
{
$oAttDef = self::GetAttributeDef($sClass, $sFieldAlias);
foreach($oAttDef->GetOrderBySQLExpressions($sClassAlias) as $sSQLExpression)
$sAttClass = $sClass;
$sAttClassAlias = $sClassAlias;
$sAttCode = $sFieldAlias;
}
else
{
$sAttClassAlias = substr($sFieldAlias, 0, $iDotPos);
$sAttClass = $oFilter->GetClassName($sAttClassAlias);
$sAttCode = substr($sFieldAlias, $iDotPos + 1);
}
if ($sAttCode != 'id')
{
MyHelpers::CheckValueInArray('field name in ORDER BY spec', $sAttCode, self::GetAttributesList($sAttClass));
$oAttDef = self::GetAttributeDef($sAttClass, $sAttCode);
foreach($oAttDef->GetOrderBySQLExpressions($sAttClassAlias) as $sSQLExpression)
{
$aOrderSpec[$sSQLExpression] = $bAscending;
}
}
else
{
$aOrderSpec['`'.$sClassAlias.$sFieldAlias.'`'] = $bAscending;
$aOrderSpec['`'.$sAttClassAlias.$sAttCode.'`'] = $bAscending;
}
// Make sure that the columns used for sorting are present in the loaded columns
if (!is_null($aAttToLoad) && !isset($aAttToLoad[$sClassAlias][$sFieldAlias]))
if (!is_null($aAttToLoad) && !isset($aAttToLoad[$sAttClassAlias][$sAttCode]))
{
$aAttToLoad[$sClassAlias][$sFieldAlias] = MetaModel::GetAttributeDef($sClass, $sFieldAlias);
$aAttToLoad[$sAttClassAlias][$sAttCode] = MetaModel::GetAttributeDef($sAttClass, $sAttCode);
}
}
@@ -3386,6 +3450,33 @@ abstract class MetaModel
{
// Do nothing...
}
else if ($oAttDef instanceof AttributeStopWatch)
{
$aThresholds = $oAttDef->ListThresholds();
if (is_array($aThresholds))
{
foreach($aThresholds as $iPercent => $aDef)
{
if (array_key_exists('highlight', $aDef))
{
if(!array_key_exists('code', $aDef['highlight']))
{
$aErrors[$sClass][] = "The 'code' element is missing for the 'highlight' property of the $iPercent% threshold in the attribute: '$sAttCode'.";
$aSugFix[$sClass][] = "Add a 'code' entry specifying the value of the highlight code for this threshold.";
}
else
{
$aScale = self::GetHighlightScale($sClass);
if (!array_key_exists($aDef['highlight']['code'], $aScale))
{
$aErrors[$sClass][] = "'{$aDef['highlight']['code']}' is not a valid value for the 'code' element of the $iPercent% threshold in the attribute: '$sAttCode'.";
$aSugFix[$sClass][] = "The possible highlight codes for this class are: ".implode(', ', array_keys($aScale)).".";
}
}
}
}
}
}
else // standard attributes
{
// Check that the default values definition is a valid object!
@@ -3483,17 +3574,46 @@ abstract class MetaModel
}
}
// Lifcycle - check that the action handlers are defined
// Lifecycle - check that the action handlers are defined
foreach (self::EnumStates($sClass) as $sStateCode => $aStateDef)
{
foreach(self::EnumTransitions($sClass, $sStateCode) as $sStimulusCode => $aTransitionDef)
{
foreach ($aTransitionDef['actions'] as $sActionHandler)
foreach ($aTransitionDef['actions'] as $actionHandler)
{
if (!method_exists($sClass, $sActionHandler))
if (is_string($actionHandler))
{
$aErrors[$sClass][] = "Unknown function '$sActionHandler' in transition [$sStateCode/$sStimulusCode] for state attribute '$sStateAttCode'";
$aSugFix[$sClass][] = "Specify a function which prototype is in the form [public function $sActionHandler(\$sStimulusCode){return true;}]";
if (!method_exists($sClass, $actionHandler))
{
$aErrors[$sClass][] = "Unknown function '$sActionHandler' in transition [$sStateCode/$sStimulusCode] for state attribute '$sStateAttCode'";
$aSugFix[$sClass][] = "Specify a function which prototype is in the form [public function $sActionHandler(\$sStimulusCode){return true;}]";
}
}
else // if(is_array($actionHandler))
{
$sActionHandler = $actionHandler['verb'];
if (!method_exists($sClass, $sActionHandler))
{
$aErrors[$sClass][] = "Unknown function '$sActionHandler' in transition [$sStateCode/$sStimulusCode] for state attribute '$sStateAttCode'";
$aSugFix[$sClass][] = "Specify a function which prototype is in the form [public function $sActionHandler(...){return true;}]";
}
}
}
}
if (array_key_exists('highlight', $aStateDef))
{
if(!array_key_exists('code', $aStateDef['highlight']))
{
$aErrors[$sClass][] = "The 'code' element is missing for the 'highlight' property of state: '$sStateCode'.";
$aSugFix[$sClass][] = "Add a 'code' entry specifying the value of the highlight code for this state.";
}
else
{
$aScale = self::GetHighlightScale($sClass);
if (!array_key_exists($aStateDef['highlight']['code'], $aScale))
{
$aErrors[$sClass][] = "'{$aStateDef['highlight']['code']}' is not a valid value for the 'code' element in the 'highlight' property of state: '$sStateCode'.";
$aSugFix[$sClass][] = "The possible highlight codes for this class are: ".implode(', ', array_keys($aScale)).".";
}
}
}
@@ -4718,6 +4838,7 @@ abstract class MetaModel
self::$m_aStates = $result['m_aStates'];
self::$m_aStimuli = $result['m_aStimuli'];
self::$m_aTransitions = $result['m_aTransitions'];
self::$m_aHighlightScales = $result['m_aHighlightScales'];
}
$oKPI->ComputeAndReport('Metamodel APC (fetch + read)');
}
@@ -4754,6 +4875,7 @@ abstract class MetaModel
$aCache['m_aStates'] = self::$m_aStates; // array of ("classname" => array of "statecode"=>array('label'=>..., attribute_inherit=> attribute_list=>...))
$aCache['m_aStimuli'] = self::$m_aStimuli; // array of ("classname" => array of ("stimuluscode"=>array('label'=>...)))
$aCache['m_aTransitions'] = self::$m_aTransitions; // array of ("classname" => array of ("statcode_from"=>array of ("stimuluscode" => array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD)))
$aCache['m_aHighlightScales'] = self::$m_aHighlightScales; // array of ("classname" => array of higlightcodes)))
apc_store($sOqlAPCCacheId, $aCache);
$oKPI->ComputeAndReport('Metamodel APC (store)');
}
@@ -4815,7 +4937,15 @@ abstract class MetaModel
if (!file_exists($sFile))
{
$sConfigFile = self::$m_oConfig->GetLoadedFile();
throw new CoreException('Wrong filename in configuration file', array('file' => $sConfigFile, 'module' => $sModuleType, 'filename' => $sFile));
if (strlen($sConfigFile) > 0)
{
throw new CoreException('Include: wrong file name in configuration file', array('config file' => $sConfigFile, 'section' => $sModuleType, 'filename' => $sFile));
}
else
{
// The configuration is in memory only
throw new CoreException('Include: wrong file name in configuration file (in memory)', array('section' => $sModuleType, 'filename' => $sFile));
}
}
// Note: We do not expect the modules to output characters while loading them.
@@ -5394,5 +5524,3 @@ MetaModel::RegisterZList("preview", array("description"=>"All attributes visible
MetaModel::RegisterZList("standard_search", array("description"=>"List of criteria for the standard search", "type"=>"filters"));
MetaModel::RegisterZList("advanced_search", array("description"=>"List of criteria for the advanced search", "type"=>"filters"));
?>

View File

@@ -62,12 +62,13 @@ class ormStopWatch
return (string) $this->iTimeSpent;
}
public function DefineThreshold($iPercent, $tDeadline = null, $bPassed = false, $bTriggered = false, $iOverrun = null)
public function DefineThreshold($iPercent, $tDeadline = null, $bPassed = false, $bTriggered = false, $iOverrun = null, $aHighlightDef = null)
{
$this->aThresholds[$iPercent] = array(
'deadline' => $tDeadline, // unix time (seconds)
'triggered' => $bTriggered,
'overrun' => $iOverrun
'overrun' => $iOverrun,
'highlight' => $aHighlightDef, // array('code' => string, 'persistent' => boolean)
);
}
@@ -129,6 +130,10 @@ class ormStopWatch
{
$bRet = true;
}
if (isset($aThresholdData['overrun']) && ($aThresholdData['overrun'] > 0))
{
$bRet = true;
}
}
return $bRet;
}
@@ -143,6 +148,31 @@ class ormStopWatch
return false;
}
}
public function GetHighlightCode()
{
$sCode = '';
// Process the thresholds in ascending order
$aPercents = array();
foreach($this->aThresholds as $iPercent => $aDefs)
{
$aPercents[] = $iPercent;
}
sort($aPercents, SORT_NUMERIC);
foreach($aPercents as $iPercent)
{
$aDefs = $this->aThresholds[$iPercent];
if (array_key_exists('highlight', $aDefs) && is_array($aDefs['highlight']) && $this->IsThresholdPassed($iPercent))
{
// If persistant or SW running...
if (($aDefs['highlight']['persistent'] == true) || (($aDefs['highlight']['persistent'] == false) && !is_null($this->iLastStart)))
{
$sCode = $aDefs['highlight']['code'];
}
}
}
return $sCode;
}
public function GetAsHTML($oAttDef, $oHostObject = null)
{
@@ -424,9 +454,44 @@ class CheckStopWatchThresholds implements iBackgroundProcess
{
$sVerb = $aActionData['verb'];
$aParams = $aActionData['params'];
$sParams = implode(', ', $aParams);
$aValues = array();
foreach($aParams as $def)
{
if (is_string($def))
{
// Old method (pre-2.0.4) non typed parameters
$aValues[] = $def;
}
else // if(is_array($def))
{
$sParamType = array_key_exists('type', $def) ? $def['type'] : 'string';
switch($sParamType)
{
case 'int':
$value = (int)$def['value'];
break;
case 'float':
$value = (float)$def['value'];
break;
case 'bool':
$value = (bool)$def['value'];
break;
case 'reference':
$value = ${$def['value']};
break;
case 'string':
default:
$value = (string)$def['value'];
}
$aValues[] = $value;
}
}
$aCallSpec = array($oObj, $sVerb);
call_user_func_array($aCallSpec, $aParams);
call_user_func_array($aCallSpec, $aValues);
}
// Mark the threshold as "triggered"

View File

@@ -342,6 +342,7 @@ class lnkTriggerAction extends cmdbAbstractObject
"db_key_field" => "link_id",
"db_finalclass_field" => "",
"display_template" => "",
"is_link" => true,
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass"=>"Action", "jointype"=> '', "allowed_values"=>null, "sql"=>"action_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));

View File

@@ -234,12 +234,19 @@ abstract class User extends cmdbAbstractObject
$aChanges = $this->ListChanges();
if (array_key_exists('login', $aChanges))
{
$sNewLogin = $aChanges['login'];
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT User WHERE login = :newlogin");
$oSet = new DBObjectSet($oSearch, array(), array('newlogin' => $sNewLogin));
if ($oSet->Count() > 0)
if (strcasecmp($this->Get('login'), $this->GetOriginal('login')) !== 0)
{
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:LoginMustBeUnique', $sNewLogin);
$sNewLogin = $aChanges['login'];
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT User WHERE login = :newlogin");
if (!$this->IsNew())
{
$oSearch->AddCondition('id', $this->GetKey(), '!=');
}
$oSet = new DBObjectSet($oSearch, array(), array('newlogin' => $sNewLogin));
if ($oSet->Count() > 0)
{
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:LoginMustBeUnique', $sNewLogin);
}
}
}
// Check that this user has at least one profile assigned
@@ -248,7 +255,6 @@ abstract class User extends cmdbAbstractObject
{
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:AtLeastOneProfileIsNeeded');
}
}
function GetGrantAsHtml($sClass, $iAction)

View File

@@ -100,6 +100,9 @@ class ValueSetObjects extends ValueSetDefinition
private $m_bAllowAllData;
private $m_aModifierProperties;
/**
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
*/
public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array(), $bAllowAllData = false, $aModifierProperties = array())
{
$this->m_sContains = '';
@@ -415,5 +418,3 @@ class ValueSetEnumClasses extends ValueSetEnum
return true;
}
}
?>

View File

@@ -1416,3 +1416,7 @@ div.ui-dialog-header {
padding-bottom: 10px;
padding-top: 7px;
}
.form_field_error {
border: 1px solid #933;
background: #fcc;
}

View File

@@ -457,7 +457,7 @@
<hidden/>
</attribute>
<attribute id="agent_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="agent_email">
<hidden/>
@@ -466,13 +466,13 @@
<mandatory/>
</attribute>
<attribute id="supervisor_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="manager_group_id">
<mandatory/>
</attribute>
<attribute id="manager_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="outage">
<hidden/>
@@ -1306,7 +1306,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_assign">
<stimulus>ev_assign</stimulus>
<target>assigned</target>
<actions/>
@@ -1488,7 +1488,7 @@
<hidden/>
</attribute>
<attribute id="agent_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="agent_email">
<hidden/>
@@ -1497,13 +1497,13 @@
<mandatory/>
</attribute>
<attribute id="supervisor_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="manager_group_id">
<mandatory/>
</attribute>
<attribute id="manager_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="outage">
<hidden/>
@@ -1513,7 +1513,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_plan">
<stimulus>ev_plan</stimulus>
<target>plannedscheduled</target>
<actions/>
@@ -1589,7 +1589,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_implement">
<stimulus>ev_implement</stimulus>
<target>implemented</target>
<actions/>
@@ -1796,12 +1796,12 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_monitor">
<stimulus>ev_monitor</stimulus>
<target>monitored</target>
<actions/>
</transition>
<transition>
<transition id="ev_finish">
<stimulus>ev_finish</stimulus>
<target>closed</target>
<actions>
@@ -1879,7 +1879,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_finish">
<stimulus>ev_finish</stimulus>
<target>closed</target>
<actions>
@@ -2465,7 +2465,7 @@
<hidden/>
</attribute>
<attribute id="agent_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="agent_email">
<hidden/>
@@ -2474,13 +2474,13 @@
<mandatory/>
</attribute>
<attribute id="supervisor_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="manager_group_id">
<mandatory/>
</attribute>
<attribute id="manager_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="outage">
<hidden/>
@@ -3248,12 +3248,12 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_validate">
<stimulus>ev_validate</stimulus>
<target>validated</target>
<actions/>
</transition>
<transition>
<transition id="ev_reject">
<stimulus>ev_reject</stimulus>
<target>rejected</target>
<actions/>
@@ -3339,7 +3339,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_assign">
<stimulus>ev_assign</stimulus>
<target>assigned</target>
<actions/>
@@ -3422,7 +3422,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_reopen">
<stimulus>ev_reopen</stimulus>
<target>new</target>
<actions/>
@@ -3471,7 +3471,7 @@
<hidden/>
</attribute>
<attribute id="agent_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="agent_email">
<hidden/>
@@ -3480,13 +3480,13 @@
<mandatory/>
</attribute>
<attribute id="supervisor_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="manager_group_id">
<mandatory/>
</attribute>
<attribute id="manager_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="outage">
<hidden/>
@@ -3502,7 +3502,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_plan">
<stimulus>ev_plan</stimulus>
<target>plannedscheduled</target>
<actions/>
@@ -3589,12 +3589,12 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_approve">
<stimulus>ev_approve</stimulus>
<target>approved</target>
<actions/>
</transition>
<transition>
<transition id="ev_notapprove">
<stimulus>ev_notapprove</stimulus>
<target>notapproved</target>
<actions/>
@@ -3680,7 +3680,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_implement">
<stimulus>ev_implement</stimulus>
<target>implemented</target>
<actions/>
@@ -3763,7 +3763,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_replan">
<stimulus>ev_replan</stimulus>
<target>plannedscheduled</target>
<actions/>
@@ -3849,12 +3849,12 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_monitor">
<stimulus>ev_monitor</stimulus>
<target>monitored</target>
<actions/>
</transition>
<transition>
<transition id="ev_finish">
<stimulus>ev_finish</stimulus>
<target>closed</target>
<actions>
@@ -3944,7 +3944,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_finish">
<stimulus>ev_finish</stimulus>
<target>closed</target>
<actions>
@@ -4353,7 +4353,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_assign">
<stimulus>ev_assign</stimulus>
<target>assigned</target>
<actions/>
@@ -4547,7 +4547,7 @@
<hidden/>
</attribute>
<attribute id="agent_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="agent_email">
<hidden/>
@@ -4556,13 +4556,13 @@
<mandatory/>
</attribute>
<attribute id="supervisor_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="manager_group_id">
<mandatory/>
</attribute>
<attribute id="manager_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="outage">
<hidden/>
@@ -4578,7 +4578,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_plan">
<stimulus>ev_plan</stimulus>
<target>plannedscheduled</target>
<actions/>
@@ -4660,12 +4660,12 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_approve">
<stimulus>ev_approve</stimulus>
<target>approved</target>
<actions/>
</transition>
<transition>
<transition id="ev_notapprove">
<stimulus>ev_notapprove</stimulus>
<target>notapproved</target>
<actions/>
@@ -4746,7 +4746,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_implement">
<stimulus>ev_implement</stimulus>
<target>implemented</target>
<actions/>
@@ -4823,7 +4823,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_replan">
<stimulus>ev_replan</stimulus>
<target>plannedscheduled</target>
<actions/>
@@ -4903,12 +4903,12 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_monitor">
<stimulus>ev_monitor</stimulus>
<target>monitored</target>
<actions/>
</transition>
<transition>
<transition id="ev_finish">
<stimulus>ev_finish</stimulus>
<target>closed</target>
<actions>
@@ -4992,7 +4992,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_finish">
<stimulus>ev_finish</stimulus>
<target>closed</target>
<actions>

View File

@@ -54,7 +54,7 @@
<read_only/>
</attribute>
<attribute id="description">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="start_date">
<read_only/>
@@ -63,28 +63,28 @@
<mandatory/>
</attribute>
<attribute id="org_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="service_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="servicesubcategory_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="product">
<must_prompt/>
</attribute>
<attribute id="impact">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="urgency">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="priority">
<read_only/>
</attribute>
<attribute id="workgroup_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="agent_email">
<hidden/>
@@ -130,7 +130,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_assign">
<stimulus>ev_assign</stimulus>
<target>assigned</target>
<actions>
@@ -139,7 +139,7 @@
</action>
</actions>
</transition>
<transition>
<transition id="ev_timeout">
<stimulus>ev_timeout</stimulus>
<target>escalated_tto</target>
<actions/>
@@ -204,7 +204,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_assign">
<stimulus>ev_assign</stimulus>
<target>assigned</target>
<actions>
@@ -284,17 +284,17 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_reassign">
<stimulus>ev_reassign</stimulus>
<target>assigned</target>
<actions/>
</transition>
<transition>
<transition id="ev_reassign">
<stimulus>ev_timeout</stimulus>
<target>escalated_ttr</target>
<actions/>
</transition>
<transition>
<transition id="ev_reassign">
<stimulus>ev_resolve</stimulus>
<target>resolved</target>
<actions>
@@ -377,12 +377,12 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_reassign">
<stimulus>ev_reassign</stimulus>
<target>escalated_ttr</target>
<actions/>
</transition>
<transition>
<transition id="ev_resolve">
<stimulus>ev_resolve</stimulus>
<target>resolved</target>
<actions>
@@ -546,12 +546,12 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_reassign">
<stimulus>ev_reassign</stimulus>
<target>assigned</target>
<actions/>
</transition>
<transition>
<transition id="ev_close">
<stimulus>ev_close</stimulus>
<target>closed</target>
<actions>

View File

@@ -213,7 +213,7 @@
<read_only/>
</attribute>
<attribute id="description">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="ticket_log">
<hidden/>
@@ -222,28 +222,28 @@
<read_only/>
</attribute>
<attribute id="org_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="service_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="servicesubcategory_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="product">
<must_prompt/>
</attribute>
<attribute id="impact">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="urgency">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="priority">
<read_only/>
</attribute>
<attribute id="workgroup_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="agent_id">
<hidden/>
@@ -268,7 +268,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_assign">
<stimulus>ev_assign</stimulus>
<target>assigned</target>
<actions>
@@ -324,12 +324,12 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_reassign">
<stimulus>ev_reassign</stimulus>
<target>assigned</target>
<actions/>
</transition>
<transition>
<transition id="ev_resolve">
<stimulus>ev_resolve</stimulus>
<target>resolved</target>
<actions>
@@ -398,12 +398,12 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_reassign">
<stimulus>ev_reassign</stimulus>
<target>assigned</target>
<actions/>
</transition>
<transition>
<transition id="ev_close">
<stimulus>ev_close</stimulus>
<target>closed</target>
<actions>
@@ -753,7 +753,7 @@
<parent>ProblemManagement</parent>
<definition>
<layout>DashboardLayoutTwoCols</layout>
<title></title>
<title/>
<cells>
<cell id="0">
<rank>0</rank>

View File

@@ -91,7 +91,7 @@
<read_only/>
</attribute>
<attribute id="description">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="start_date">
<read_only/>
@@ -100,28 +100,28 @@
<mandatory/>
</attribute>
<attribute id="org_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="service_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="servicesubcategory_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="product">
<must_prompt/>
</attribute>
<attribute id="impact">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="urgency">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="priority">
<read_only/>
</attribute>
<attribute id="workgroup_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="agent_email">
<hidden/>
@@ -173,7 +173,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_assign">
<stimulus>ev_assign</stimulus>
<target>assigned</target>
<actions>
@@ -182,7 +182,7 @@
</action>
</actions>
</transition>
<transition>
<transition id="ev_timeout">
<stimulus>ev_timeout</stimulus>
<target>escalated_tto</target>
<actions/>
@@ -253,7 +253,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_assign">
<stimulus>ev_assign</stimulus>
<target>assigned</target>
<actions>
@@ -339,17 +339,17 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_reassign">
<stimulus>ev_reassign</stimulus>
<target>assigned</target>
<actions/>
</transition>
<transition>
<transition id="ev_timeout">
<stimulus>ev_timeout</stimulus>
<target>escalated_ttr</target>
<actions/>
</transition>
<transition>
<transition id="ev_resolve">
<stimulus>ev_resolve</stimulus>
<target>resolved</target>
<actions>
@@ -361,7 +361,7 @@
</action>
</actions>
</transition>
<transition>
<transition id="ev_freeze">
<stimulus>ev_freeze</stimulus>
<target>frozen</target>
<actions/>
@@ -443,12 +443,12 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_reassign">
<stimulus>ev_reassign</stimulus>
<target>escalated_ttr</target>
<actions/>
</transition>
<transition>
<transition id="ev_resolve">
<stimulus>ev_resolve</stimulus>
<target>resolved</target>
<actions>
@@ -536,17 +536,17 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_timeout">
<stimulus>ev_timeout</stimulus>
<target>escalated_ttr</target>
<actions/>
</transition>
<transition>
<transition id="ev_assign">
<stimulus>ev_assign</stimulus>
<target>assigned</target>
<actions/>
</transition>
<transition>
<transition id="ev_resolve">
<stimulus>ev_resolve</stimulus>
<target>resolved</target>
<actions/>
@@ -641,12 +641,12 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_reassign">
<stimulus>ev_reassign</stimulus>
<target>assigned</target>
<actions/>
</transition>
<transition>
<transition id="ev_close">
<stimulus>ev_close</stimulus>
<target>closed</target>
<actions>

View File

@@ -422,7 +422,7 @@
<read_only/>
</attribute>
<attribute id="description">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="start_date">
<read_only/>
@@ -431,28 +431,28 @@
<mandatory/>
</attribute>
<attribute id="org_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="service_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="servicesubcategory_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="product">
<must_prompt/>
</attribute>
<attribute id="impact">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="urgency">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="priority">
<read_only/>
</attribute>
<attribute id="workgroup_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="agent_email">
<hidden/>
@@ -498,7 +498,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_assign">
<stimulus>ev_assign</stimulus>
<target>assigned</target>
<actions>
@@ -507,7 +507,7 @@
</action>
</actions>
</transition>
<transition>
<transition id="ev_timeout">
<stimulus>ev_timeout</stimulus>
<target>escalated_tto</target>
<actions/>
@@ -572,7 +572,7 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_assign">
<stimulus>ev_assign</stimulus>
<target>assigned</target>
<actions>
@@ -652,17 +652,17 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_reassign">
<stimulus>ev_reassign</stimulus>
<target>assigned</target>
<actions/>
</transition>
<transition>
<transition id="ev_timeout">
<stimulus>ev_timeout</stimulus>
<target>escalated_ttr</target>
<actions/>
</transition>
<transition>
<transition id="ev_resolve">
<stimulus>ev_resolve</stimulus>
<target>resolved</target>
<actions>
@@ -745,12 +745,12 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_reassign">
<stimulus>ev_reassign</stimulus>
<target>escalated_ttr</target>
<actions/>
</transition>
<transition>
<transition id="ev_resolve">
<stimulus>ev_resolve</stimulus>
<target>resolved</target>
<actions>
@@ -914,12 +914,12 @@
</attribute>
</flags>
<transitions>
<transition>
<transition id="ev_reassign">
<stimulus>ev_reassign</stimulus>
<target>assigned</target>
<actions/>
</transition>
<transition>
<transition id="ev_close">
<stimulus>ev_close</stimulus>
<target>closed</target>
<actions>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<classes>
<class id="Attachment" _delta="define">
<parent>DBObject</parent>
@@ -37,20 +37,20 @@
</reconciliation>
<indexes>
<index id="1">
<attributes>
<attribute id="temp_id"/>
</attributes>
<attributes>
<attribute id="temp_id"/>
</attributes>
</index>
<index id="2">
<attributes>
<attribute id="item_class"/>
<attribute id="item_id"/>
</attributes>
<attributes>
<attribute id="item_class"/>
<attribute id="item_id"/>
</attributes>
</index>
<index id="3">
<attributes>
<attribute id="item_org_id"/>
</attributes>
<attributes>
<attribute id="item_org_id"/>
</attributes>
</index>
</indexes>
</properties>

View File

@@ -468,7 +468,8 @@ EOF
}
}
$sPreviewNotAvailable = addslashes(Dict::S('Attachments:PreviewNotAvailable'));
$oPage->add_ready_script("$(document).tooltip({ items: '.attachment a', position: { my: 'left top', at: 'right top', using: function( position, feedback ) { $( this ).css( position ); }}, content: function() { if ($(this).attr('data-preview') == 'true') { return('<img style=\"max-width:290px\" src=\"'+$(this).attr('href')+'\"></img>');} else { return '$sPreviewNotAvailable'; }}});");
$iMaxWidth = MetaModel::GetModuleSetting('itop-attachments', 'preview_max_width', 290);
$oPage->add_ready_script("$(document).tooltip({ items: '.attachment a', position: { my: 'left top', at: 'right top', using: function( position, feedback ) { $( this ).css( position ); }}, content: function() { if ($(this).attr('data-preview') == 'true') { return('<img style=\"max-width:{$iMaxWidth}px\" src=\"'+$(this).attr('href')+'\"></img>');} else { return '$sPreviewNotAvailable'; }}});");
}
protected static function UpdateAttachments($oObject, $oChange = null)

View File

@@ -19,7 +19,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-attachments/1.0.0',
'itop-attachments/2.1.0',
array(
// Identification
//
@@ -64,6 +64,7 @@ SetupWebPage::AddModule(
'settings' => array(
'allowed_classes' => array('Ticket'), // List of classes for which to manage "Attachments"
'position' => 'relations', // Where to display the attachments: relations | properties
'preview_max_width' => 290,
),
)
);

View File

@@ -0,0 +1,179 @@
<?php
// Copyright (C) 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/>
/**
* Backup from an interactive session
*
* @copyright Copyright (C) 2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
require_once(__DIR__.'/../../approot.inc.php');
require_once(APPROOT.'/application/application.inc.php');
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/ajaxwebpage.class.inc.php');
require_once(APPROOT.'core/mutex.class.inc.php');
try
{
$sOperation = utils::ReadParam('operation', '');
switch($sOperation)
{
case 'backup':
require_once(APPROOT.'/application/startup.inc.php');
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin)
$oPage = new ajax_page("");
$oPage->no_cache();
$oPage->SetContentType('text/html');
if (utils::GetConfig()->Get('demo_mode'))
{
$oPage->add("<div data-error-stimulus=\"Error\">Sorry, iTop is in <b>demonstration mode</b>: the feature is disabled.</div>");
}
else
{
try
{
set_time_limit(0);
$oBB = new BackupExec(APPROOT.'data/backups/manual/', 0 /*iRetentionCount*/);
$sRes = $oBB->Process(time() + 36000); // 10 hours to complete should be sufficient!
}
catch (Exception $e)
{
$oPage->p('Error: '.$e->getMessage());
}
}
$oPage->output();
break;
case 'restore_get_token':
require_once(APPROOT.'/application/startup.inc.php');
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin)
$oPage = new ajax_page("");
$oPage->no_cache();
$oPage->SetContentType('text/html');
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
if ($oRestoreMutex->TryLock())
{
$oRestoreMutex->Unlock();
$sFile = utils::ReadParam('file', '', false, 'raw_data');
$sToken = str_replace(' ', '', (string)microtime());
$sTokenFile = APPROOT.'/data/restore.'.$sToken.'.tok';
file_put_contents($sTokenFile, $sFile);
$oPage->add_ready_script(
<<<EOF
$("#restore_token").val('$sToken');
EOF
);
}
else
{
$oPage->p(Dict::S('bkp-restore-running'));
}
$oPage->output();
break;
case 'restore_exec':
require_once(APPROOT."setup/runtimeenv.class.inc.php");
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/setup/backup.class.inc.php');
require_once(dirname(__FILE__).'/dbrestore.class.inc.php');
IssueLog::Enable(APPROOT.'log/error.log');
$oPage = new ajax_page("");
$oPage->no_cache();
$oPage->SetContentType('text/html');
if (utils::GetConfig()->Get('demo_mode'))
{
$oPage->add("<div data-error-stimulus=\"Error\">Sorry, iTop is in <b>demonstration mode</b>: the feature is disabled.</div>");
}
else
{
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
$oRestoreMutex->Lock();
try
{
set_time_limit(0);
// Get the file and destroy the token (single usage)
$sToken = utils::ReadParam('token', '', false, 'raw_data');
$sTokenFile = APPROOT.'/data/restore.'.$sToken.'.tok';
$sFile = file_get_contents($sTokenFile);
unlink($sTokenFile);
$sMySQLBinDir = utils::ReadParam('mysql_bindir', '', false, 'raw_data');
$sDBHost = utils::ReadParam('db_host', '', false, 'raw_data');
$sDBUser = utils::ReadParam('db_user', '', false, 'raw_data');
$sDBPwd = utils::ReadParam('db_pwd', '', false, 'raw_data');
$sDBName = utils::ReadParam('db_name', '', false, 'raw_data');
$sDBSubName = utils::ReadParam('db_subname', '', false, 'raw_data');
$oDBRS = new DBRestore($sDBHost, $sDBUser, $sDBPwd, $sDBName, $sDBSubName);
$oDBRS->SetMySQLBinDir($sMySQLBinDir);
$sBackupDir = APPROOT.'data/backups/';
$sBackupFile = $sBackupDir.$sFile;
$sRes = $oDBRS->RestoreFromZip($sBackupFile, $sEnvironment);
$oRestoreMutex->Unlock();
}
catch (Exception $e)
{
$oRestoreMutex->Unlock();
$oPage->p('Error: '.$e->getMessage());
}
}
$oPage->output();
break;
case 'download':
require_once(APPROOT.'/application/startup.inc.php');
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin)
if (utils::GetConfig()->Get('demo_mode'))
{
throw new Exception('iTop is in demonstration mode: the feature is disabled');
}
$sFile = utils::ReadParam('file', '', false, 'raw_data');
$oBackup = new DBBackupScheduled();
$sBackupDir = APPROOT.'data/backups/';
$oBackup->DownloadBackup($sBackupDir.$sFile);
break;
}
}
catch (Exception $e)
{
IssueLog::Error($e->getMessage());
}
?>

View File

@@ -0,0 +1,54 @@
# Parameters file for backup.php and check-backup.php
#
# Usage:
# backup.php --param_file=<this file>[,<another one>]
# or
# http://.../itop-backup/backup.php?param_file=<this file>[,<another one>]
#
# If a parameter is given both in the file and in the arguments,
# then the value given as argument is retained
#
# Note: most of the default values provided here should work fine
# if you have created sample data with the setup program
# MySQL coming with Easy PHP (Windows)
mysql_bindir = C:\Program Files\EasyPHP-5.3.6.0\mysql\bin
# Authentication
auth_user = admin
auth_pwd = admin
# Target file - path and filename (optional)
#
# Formatting rules:
# %Y-%m-%d => 2011-01-25... see PHP documentation of strftime()
# Placeholders:
# __HOST__ MySQL server
# __DB__ Database name
# __SUBNAME__ Tables prefix
#
backup_file = /var/log/__DB__-%Y-%m-%d
# Check thresholds (check-backup.php)
#
check_size_min = 20000 # bytes
check_size_reduction_max = 10 # percentage
# Ticket creation (check-backup.php)
#
# If the backup has failed, a ticket will be created
# This process relies on the SOAP service "CreateIncident"
#
# Root URL of an instance of iTop, into which the ticket will be created
check_ticket_itop = http://localhost/myiTop
# Any of the above paramaters are mandatory
check_ticket_login = admin # must have the right to create an Incident Ticket
check_ticket_pwd = admin
check_ticket_title = Backup check failed
check_ticket_customer = Demo
check_ticket_service = Computers and peripherals
check_ticket_service_subcategory = Repair
check_ticket_workgroup = Hardware support
check_ticket_impacted_server = dbserver1.demo.com

View File

@@ -0,0 +1,200 @@
<?php
// Copyright (C) 2014 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
if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
require_once(__DIR__.'/../../approot.inc.php');
require_once(APPROOT.'application/application.inc.php');
require_once(APPROOT.'application/webpage.class.inc.php');
require_once(APPROOT.'application/csvpage.class.inc.php');
require_once(APPROOT.'application/clipage.class.inc.php');
require_once(APPROOT.'application/ajaxwebpage.class.inc.php');
require_once(APPROOT.'core/log.class.inc.php');
require_once(APPROOT.'application/startup.inc.php');
class MyDBBackup extends DBBackup
{
protected function LogInfo($sMsg)
{
$this->oPage->p($sMsg);
}
protected function LogError($sMsg)
{
$this->oPage->p('Error: '.$sMsg);
ToolsLog::Error($sMsg);
}
protected $oPage;
public function __construct($oPage)
{
$this->oPage = $oPage;
parent::__construct();
}
}
/**
* Checks if a parameter (possibly empty) was specified when calling this page
*/
function CheckParam($sParamName)
{
global $argv;
if (isset($_REQUEST[$sParamName])) return true; // HTTP parameter either GET or POST
if (!is_array($argv)) return false;
foreach($argv as $sArg)
{
if ($sArg == '--'.$sParamName) return true; // Empty command line parameter, long unix style
if ($sArg == $sParamName) return true; // Empty command line parameter, Windows style
if ($sArg == '-'.$sParamName) return true; // Empty command line parameter, short unix style
if (preg_match('/^--'.$sParamName.'=(.*)$/', $sArg, $aMatches)) return true; // Command parameter with a value
}
return false;
}
function Usage($oP)
{
$oP->p('Perform a backup of the iTop database by running mysqldump');
$oP->p('Parameters:');
if (utils::IsModeCLI())
{
$oP->p('auth_user: login, must be administrator');
$oP->p('auth_pwd: ...');
}
$oP->p('backup_file [optional]: name of the file to store the backup into. Follows the PHP strftime format spec. The following placeholders are available: __HOST__, __DB__, __SUBNAME__');
$oP->p('simulate [optional]: set to check the name of the file that would be created');
$oP->p('mysql_bindir [optional]: specify the path for mysqldump');
if (utils::IsModeCLI())
{
$oP->p('Example: php -q backup.php --auth_user=admin --auth_pwd=myPassw0rd');
$oP->p('Known limitation: the current directory must be the directory of backup.php');
}
else
{
$oP->p('Example: .../backup.php?backup_file=/tmp/backup.__DB__-__SUBNAME__.%Y-%m');
}
}
function ExitError($oP, $sMessage)
{
ToolsLog::Error($sMessage);
$oP->p($sMessage);
$oP->output();
exit;
}
function ReadMandatoryParam($oP, $sParam)
{
$sValue = utils::ReadParam($sParam, null, true /* Allow CLI */, 'raw_data');
if (is_null($sValue))
{
ExitError($oP, "ERROR: Missing argument '$sParam'");
}
return trim($sValue);
}
/////////////////////////////////
// Main program
set_time_limit(0);
if (utils::IsModeCLI())
{
$oP = new CLIPage("iTop - Database Backup");
}
else
{
$oP = new WebPage("iTop - Database Backup");
}
try
{
utils::UseParamFile();
}
catch(Exception $e)
{
ExitError($oP, $e->GetMessage());
}
if (utils::IsModeCLI())
{
$oP->p(date('Y-m-d H:i:s')." - running backup utility");
$sAuthUser = ReadMandatoryParam($oP, 'auth_user');
$sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd');
$bDownloadBackup = false;
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
{
UserRights::Login($sAuthUser); // Login & set the user's language
}
else
{
ExitError($oP, "Access restricted or wrong credentials ('$sAuthUser')");
}
}
else
{
require_once(APPROOT.'application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(); // Check user rights and prompt if needed
$bDownloadBackup = utils::ReadParam('download', false);
}
if (!UserRights::IsAdministrator())
{
ExitError($oP, "Access restricted to administors");
}
if (CheckParam('?') || CheckParam('h') || CheckParam('help'))
{
Usage($oP);
$oP->output();
exit;
}
$sDefaultBackupFileName = SetupUtils::GetTmpDir().'/'."__DB__-%Y-%m-%d";
$sBackupFile = utils::ReadParam('backup_file', $sDefaultBackupFileName, true, 'raw_data');
// Interpret strftime specifications (like %Y) and database placeholders
$oBackup = new MyDBBackup($oP);
$oBackup->SetMySQLBinDir(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''));
$sBackupFile = $oBackup->MakeName($sBackupFile);
$sZipArchiveFile = $sBackupFile.'.zip';
$bSimulate = utils::ReadParam('simulate', false, true);
$res = false;
if ($bSimulate)
{
$oP->p("Simulate: would create file '$sZipArchiveFile'");
}
elseif (MetaModel::GetConfig()->Get('demo_mode'))
{
$oP->p("Sorry, iTop is in demonstration mode: the feature is disabled");
}
else
{
$oBackup->CreateZip($sZipArchiveFile);
}
if ($res && $bDownloadBackup)
{
$oBackup->DownloadBackup($sZipArchiveFile);
}
$oP->output();
?>

View File

@@ -0,0 +1,267 @@
<?php
// Copyright (C) 2014 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
// Purpose: check that the backup has been successfully executed
// this script is aimed at being invoked in CLI mode only
// Developer's notes:
// Duplicated code: sys_get_temp_dir, the computation of the target filename, etc.
// Recommended usage in CRON
// /usr/bin/php -q /var/www/combodo/modules/itop-backup/backup.php --backup_file=/home/backups/combodo-crm-%Y-%m-%d
if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
require_once(__DIR__.'/../../approot.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/core/config.class.inc.php');
function ReadMandatoryParam($sParam)
{
$value = utils::ReadParam($sParam, null, true /* Allow CLI */, 'raw_data');
if (is_null($value))
{
throw new Exception("Missing argument '$sParam'");
}
return $value;
}
if (!function_exists('sys_get_temp_dir'))
{
// Based on http://www.phpit.net/
// article/creating-zip-tar-archives-dynamically-php/2/
function sys_get_temp_dir()
{
// Try to get from environment variable
if (!empty($_ENV['TMP']))
{
return realpath($_ENV['TMP']);
}
else if (!empty($_ENV['TMPDIR']))
{
return realpath($_ENV['TMPDIR']);
}
else if (!empty($_ENV['TEMP']))
{
return realpath($_ENV['TEMP']);
}
// Detect by creating a temporary file
else
{
// Try to use system's temporary directory
// as random name shouldn't exist
$temp_file = tempnam(md5(uniqid(rand(), TRUE)), '');
if ($temp_file)
{
$temp_dir = realpath(dirname($temp_file));
unlink($temp_file);
return $temp_dir;
}
else
{
return FALSE;
}
}
}
}
function MakeArchiveFileName($iRefTime = null)
{
$sDefaultBackupFileName = sys_get_temp_dir().'/'."__DB__-%Y-%m-%d";
$sBackupFile = utils::ReadParam('backup_file', $sDefaultBackupFileName, true, 'raw_data');
$oConfig = new Config(APPCONF.'production/config-itop.php');
$sBackupFile = str_replace('__HOST__', $oConfig->GetDBHost(), $sBackupFile);
$sBackupFile = str_replace('__DB__', $oConfig->GetDBName(), $sBackupFile);
$sBackupFile = str_replace('__SUBNAME__', $oConfig->GetDBSubName(), $sBackupFile);
if (is_null($iRefTime))
{
$sBackupFile = strftime($sBackupFile);
}
else
{
$sBackupFile = strftime($sBackupFile, $iRefTime);
}
return $sBackupFile.'.zip';
}
function RaiseAlarm($sMessage)
{
echo "$sMessage\n";
try
{
$sTicketLogin = ReadMandatoryParam('check_ticket_login');
$sTicketPwd = ReadMandatoryParam('check_ticket_pwd');
$sTicketTitle = ReadMandatoryParam('check_ticket_title');
$sTicketCustomer = ReadMandatoryParam('check_ticket_customer');
$sTicketService = ReadMandatoryParam('check_ticket_service');
$sTicketSubcategory = ReadMandatoryParam('check_ticket_service_subcategory');
$sTicketWorkgroup = ReadMandatoryParam('check_ticket_workgroup');
$sTicketImpactedServer = ReadMandatoryParam('check_ticket_impacted_server');
}
catch (Exception $e)
{
echo "The ticket could not be created: ".$e->GetMessage()."\n";
return;
}
$sMessage = "Server: [[Server:".$sTicketImpactedServer."]]\n".$sMessage;
require_once(APPROOT.'webservices/itopsoaptypes.class.inc.php');
//$sItopRootDefault = 'http'.((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']!='off')) ? 's' : '').'://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].dirname($_SERVER['SCRIPT_NAME']).'/../..';
//$sItopRoot = utils::ReadParam('check_ticket_itop', $sItopRootDefault);
$sItopRoot = ReadMandatoryParam('check_ticket_itop');
$sWsdlUri = $sItopRoot.'/webservices/itop.wsdl.php';
//$sWsdlUri .= '?service_category=';
$aSOAPMapping = SOAPMapping::GetMapping();
ini_set("soap.wsdl_cache_enabled","0");
$oSoapClient = new SoapClient(
$sWsdlUri,
array(
'trace' => 1,
'classmap' => $aSOAPMapping, // defined in itopsoaptypes.class.inc.php
)
);
try
{
$oRes = $oSoapClient->CreateIncidentTicket
(
$sTicketLogin, /* login */
$sTicketPwd, /* password */
$sTicketTitle, /* title */
$sMessage, /* description */
null, /* caller */
new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', $sTicketCustomer))), /* customer */
new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', $sTicketService))), /* service */
new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', $sTicketSubcategory))), /* service subcategory */
'', /* product */
new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', $sTicketWorkgroup))), /* workgroup */
array(
new SOAPLinkCreationSpec(
'Server',
array(new SOAPSearchCondition('name', $sTicketImpactedServer)),
array()
),
), /* impacted cis */
'1', /* impact */
'1' /* urgency */
);
}
catch(Exception $e)
{
echo "The ticket could not be created: SOAP Exception = '".$e->getMessage()."'\n";
}
//echo "<pre>\n";
//print_r($oRes);
//echo "</pre>\n";
if ($oRes->status)
{
$sTicketName = $oRes->result[0]->values[1]->value;
echo "Created ticket: $sTicketName\n";
}
else
{
echo "ERROR: Failed to create the ticket in target iTop ($sItopRoot)\n";
foreach ($oRes->errors->messages as $oMessage)
{
echo $oMessage->text."\n";
}
}
}
//////////
// Main
try
{
utils::UseParamFile();
}
catch(Exception $e)
{
echo "Error: ".$e->GetMessage()."\n";
exit;
}
$sZipArchiveFile = MakeArchiveFileName();
echo date('Y-m-d H:i:s')." - Checking file: $sZipArchiveFile\n";
if (file_exists($sZipArchiveFile))
{
if ($aStat = stat($sZipArchiveFile))
{
$iSize = (int) $aStat['size'];
$iMIN = utils::ReadParam('check_size_min', 0);
if ($iSize > $iMIN)
{
echo "Found the archive\n";
$sOldArchiveFile = MakeArchiveFileName(time() - 86400); // yesterday's archive
if (file_exists($sOldArchiveFile))
{
if ($aOldStat = stat($sOldArchiveFile))
{
echo "Comparing its size with older file: $sOldArchiveFile\n";
$iOldSize = (int) $aOldStat['size'];
$fVariationPercent = 100 * ($iSize - $iOldSize) / $iOldSize;
$sVariation = round($fVariationPercent, 2)." percent(s)";
$iREDUCTIONMAX = utils::ReadParam('check_size_reduction_max');
if ($fVariationPercent < -$iREDUCTIONMAX)
{
RaiseAlarm("Backup file '$sZipArchiveFile' changed by $sVariation, expecting a reduction limited to $iREDUCTIONMAX percents of the original size");
}
elseif ($fVariationPercent < 0)
{
echo "Size variation: $sVariation (the maximum allowed reduction is $iREDUCTIONMAX) \n";
}
else
{
echo "The archive grew by: $sVariation\n";
}
}
}
}
else
{
RaiseAlarm("Backup file '$sZipArchiveFile' too small (Found: $iSize, while expecting $iMIN bytes)");
}
}
else
{
RaiseAlarm("Failed to stat backup file '$sZipArchiveFile'");
}
}
else
{
RaiseAlarm("Missing backup file '$sZipArchiveFile'");
}
?>

View File

@@ -0,0 +1,129 @@
<?php
// Copyright (C) 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/>
class DBRestore extends DBBackup
{
protected function LogInfo($sMsg)
{
//IssueLog::Info('non juste info: '.$sMsg);
}
protected function LogError($sMsg)
{
IssueLog::Error($sMsg);
}
protected function LoadDatabase($sDataFile)
{
$this->LogInfo("Loading data onto $this->sDBHost/$this->sDBName(suffix:'$this->sDBSubName')");
// Just to check the connection to the DB (more accurate than getting the retcode of mysql)
$oMysqli = $this->DBConnect();
$sHost = self::EscapeShellArg($this->sDBHost);
$sUser = self::EscapeShellArg($this->sDBUser);
$sPwd = self::EscapeShellArg($this->sDBPwd);
$sDBName = self::EscapeShellArg($this->sDBName);
if (empty($this->sMySQLBinDir))
{
$sMySQLExe = 'mysql';
}
else
{
$sMySQLExe = '"'.$this->sMySQLBinDir.'/mysql"';
}
if (is_null($this->iDBPort))
{
$sPortOption = '';
}
else
{
$sPortOption = '--port='.$this->iDBPort.' ';
}
$sDataFileEscaped = self::EscapeShellArg($sDataFile);
$sCommand = "$sMySQLExe --default-character-set=utf8 --host=$sHost $sPortOption --user=$sUser --password=$sPwd $sDBName <$sDataFileEscaped 2>&1";
$sCommandDisplay = "$sMySQLExe --default-character-set=utf8 --host=$sHost $sPortOption --user=xxxx --password=xxxx $sDBName <$sDataFileEscaped 2>&1";
// Now run the command for real
$this->LogInfo("Executing command: $sCommandDisplay");
$aOutput = array();
$iRetCode = 0;
exec($sCommand, $aOutput, $iRetCode);
foreach($aOutput as $sLine)
{
$this->LogInfo("mysql said: $sLine");
}
if ($iRetCode != 0)
{
$this->LogError("Failed to execute: $sCommandDisplay. The command returned:$iRetCode");
foreach($aOutput as $sLine)
{
$this->LogError("mysql said: $sLine");
}
if (count($aOutput) == 1)
{
$sMoreInfo = trim($aOutput[0]);
}
else
{
$sMoreInfo = "Check the log file '".realpath(APPROOT.'/log/error.log')."' for more information.";
}
throw new BackupException("Failed to execute mysql: ".$sMoreInfo);
}
}
public function RestoreFromZip($sZipFile, $sEnvironment = 'production')
{
$this->LogInfo("Starting restore of ".basename($sZipFile));
$oZip = new ZipArchive();
$res = $oZip->open($sZipFile);
// Load the database
//
$sDataDir = tempnam(SetupUtils::GetTmpDir(), 'itop-');
unlink($sDataDir); // I need a directory, not a file...
SetupUtils::builddir($sDataDir); // Here is the directory
$oZip->extractTo($sDataDir, 'itop-dump.sql');
$sDataFile = $sDataDir.'/itop-dump.sql';
$this->LoadDatabase($sDataFile);
unlink($sDataFile);
// Update the code
//
$sDeltaFile = APPROOT.'data/'.$sEnvironment.'.delta.xml';
if ($oZip->locateName('delta.xml') !== false)
{
// Extract and rename delta.xml => <env>.delta.xml;
file_put_contents($sDeltaFile, $oZip->getFromName('delta.xml'));
}
else
{
@unlink($sDeltaFile);
}
$sConfigFile = APPROOT.'conf/'.$sEnvironment.'/config-itop.php';
@chmod($sConfigFile, 0770); // Allow overwriting the file
$oZip->extractTo(APPROOT.'conf/'.$sEnvironment, 'config-itop.php');
@chmod($sConfigFile, 0444); // Read-only
$oEnvironment = new RunTimeEnvironment($sEnvironment);
$oEnvironment->CompileFrom($sEnvironment);
}
}

View File

@@ -0,0 +1,48 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 Combodo
* @license http://opensource.org/licenses/AGPL-3.0
* @author Robert Jaehne <robert.jaehne@itomig.de>
*/
Dict::Add('DE DE', 'German', 'Deutsch', array(
'bkp-backup-running' => 'Backup wird durchgeführt. Bitte warten ...',
'bkp-restore-running' => 'Wiederherstellung läuft. Bitte warten ...',
'Menu:BackupStatus' => 'Geplante Backups',
'bkp-status-title' => 'Geplante Backups',
'bkp-status-checks' => 'Einstellungen und Prüfungen',
'bkp-mysqldump-ok' => 'mysqldump ist vorhanden: %1$s',
'bkp-mysqldump-notfound' => 'mysqldump wurde nicht gefunden: %1$s - Stellen sie sicher, das er eingespielt und im Pfad verfügbar ist oder editieren sie die Konfigurationsdatei um das MySQL bindir anzupassen.',
'bkp-mysqldump-issue' => 'mysqldump konnte nicht eingespielt werden (retcode=%1$d): Stellen sie sicher, das er eingespielt und im Pfad verfügbar ist oder editieren sie die Konfigurationsdatei um das MySQL bindir anzupassen.',
'bkp-missing-dir' => 'Zielverzeichniss %1$s nicht gefunden',
'bkp-free-disk-space' => '<b>%1$s frei</b> in %2$s',
'bkp-dir-not-writeable' => '%1$s ist nicht schreibbar',
'bkp-wrong-format-spec' => 'Die verwendete Definition zur Formatierung von Dateinamen ist nicht korrekt (%1$s). Die Standard-Definition %2$s wird verwendet',
'bkp-name-sample' => 'Backup-Dateien werden abhängig von Datum, Zeit und Datenbank-Identifier erstellt. Beispiel: %1$s',
'bkp-week-days' => 'Backups werden <b>jeden %1$s um %2$s durchgeführt</b>',
'bkp-retention' => 'Mindestens <b>%1$d Backups werden im Zielverzeichniss vorgehalten</b>',
'bkp-next-to-delete' => 'Wird gelöscht, wenn das nächste Backup angelegt wird (unter Einstellungen "Menge vorhalten")',
'bkp-table-file' => 'Datei',
'bkp-table-file+' => 'Nur Dateien mit der Endung .zip werden als Backup-Dateien berücksichtigt.',
'bkp-table-size' => 'Grösse',
'bkp-table-size+' => '',
'bkp-table-actions' => 'Aktionen',
'bkp-table-actions+' => '',
'bkp-status-backups-auto' => 'Geplante Backups',
'bkp-status-backups-manual' => 'Manuelle Backups',
'bkp-status-backups-none' => 'Kein Backup vorhanden',
'bkp-next-backup' => 'Das nächste Backup wird am <b>%1$s</b> (%2$s) um %3$s durchgeführt',
'bkp-button-backup-now' => 'Backup läuft!',
'bkp-button-restore-now' => 'Wiederherstellen!',
'bkp-confirm-backup' => 'Bitte bestätigen sie, dass sie jetzt ein Backup erstellen wollen now.',
'bkp-confirm-restore' => 'Bitte bestätigen sie, dass sie mit Backup %1$s eine Wiederherstellung durchführen wollen.',
'bkp-wait-backup' => 'Bitte warten, bis das Backup abgeschlossen ist ...',
'bkp-wait-restore' => 'Bitte warten, bis die Wiederherstellung abgeschlossen ist ...',
'bkp-success-restore' => 'Wiederherstellung erfolgreich.',
));

View File

@@ -0,0 +1,46 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 Combodo
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('EN US', 'English', 'English', array(
'bkp-backup-running' => 'A backup is running. Please wait...',
'bkp-restore-running' => 'A restore is running. Please wait...',
'Menu:BackupStatus' => 'Scheduled Backups',
'bkp-status-title' => 'Scheduled Backups',
'bkp-status-checks' => 'Settings and checks',
'bkp-mysqldump-ok' => 'mysqldump is present: %1$s',
'bkp-mysqldump-notfound' => 'mysqldump could not be found: %1$s - Please make sure it is installed and in the path, or edit the configuration file to tune mysql_bindir.',
'bkp-mysqldump-issue' => 'mysqldump could not be executed (retcode=%1$d): Please make sure it is installed and in the path, or edit the configuration file to tune mysql_bindir',
'bkp-missing-dir' => 'The target directory %1$s count not be found',
'bkp-free-disk-space' => '<b>%1$s free</b> in %2$s',
'bkp-dir-not-writeable' => '%1$s is not writeable',
'bkp-wrong-format-spec' => 'The current specification to format the file names is wrong (%1$s). A default specification will apply: %2$s',
'bkp-name-sample' => 'Backup files are named depending on DB identifiers, date and time. Example: %1$s',
'bkp-week-days' => 'Backups will occur <b>every %1$s at %2$s</b>',
'bkp-retention' => 'At most <b>%1$d backup files will be kept</b> in the target directory.',
'bkp-next-to-delete' => 'Will be deleted when the next backup occurs (see the setting "retention_count")',
'bkp-table-file' => 'File',
'bkp-table-file+' => 'Only files having the extension .zip are considered as being backup files',
'bkp-table-size' => 'Size',
'bkp-table-size+' => '',
'bkp-table-actions' => 'Actions',
'bkp-table-actions+' => '',
'bkp-status-backups-auto' => 'Scheduled backups',
'bkp-status-backups-manual' => 'Manual backups',
'bkp-status-backups-none' => 'No backup yet',
'bkp-next-backup' => 'The next backup will occur on <b>%1$s</b> (%2$s) at %3$s',
'bkp-button-backup-now' => 'Backup now!',
'bkp-button-restore-now' => 'Restore!',
'bkp-confirm-backup' => 'Please confirm that you do request the backup to occur right now.',
'bkp-confirm-restore' => 'Please confirm that you do want to restore the backup %1$s.',
'bkp-wait-backup' => 'Please wait for the backup to complete...',
'bkp-wait-restore' => 'Please wait for the restore to complete...',
'bkp-success-restore' => 'Restore successfully completed.',
));
?>

View File

@@ -0,0 +1,46 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 Combodo
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('FR FR', 'French', 'Français', array(
'bkp-backup-running' => 'Une sauvegarde est en cours. Veuillez patienter...',
'bkp-restore-running' => 'Une restauration des données est en cours. Veuillez patienter...',
'Menu:BackupStatus' => 'Sauvegarde automatique',
'bkp-status-title' => 'Sauvegarde automatique',
'bkp-status-checks' => 'Réglages et vérifications',
'bkp-mysqldump-ok' => 'mysqldump est installé: %1$s',
'bkp-mysqldump-notfound' => 'mysqldump n\'a pas été trouvé: %1$s - Veuillez vous assurer que les outils mysql sont installés et qu\'ils sont accessibles en ligne de commande, ou bien éditez le fichier de configuration pour en donner le chemin via mysql_bindir.',
'bkp-mysqldump-issue' => 'mysqldump n\'a pas pu être exécuté (code de retour: %1$d). Veuillez vérifier que les outils mysql sont installés et qu\'ils sont accessibles en ligne de commande, ou bien éditez le fichier de configuration pour en donner le chemin via mysql_bindir.',
'bkp-missing-dir' => 'Le répertoire cible \'%1$s\' n\'existe pas ou ne peut pas être lu.',
'bkp-free-disk-space' => 'Vous disposez de <b>%1$s d\'espace disque</b> sur %2$s',
'bkp-dir-not-writeable' => 'Le répertoire cible \'%1$s\' n\'est pas accessible en écriture.',
'bkp-wrong-format-spec' => 'La spécification de format pour le nom des sauvegarde est incorrecte (%1$s). La spécification par défaut sera appliquée: %2$s',
'bkp-name-sample' => 'Les fichiers de sauvegardes seront nommés en fonction de la base, la date et l\'heure. Par exemple: %1$s',
'bkp-week-days' => 'Les sauvegardes seront effectuées <b>tous les %1$s à %2$s</b>',
'bkp-retention' => 'Au plus <b>%1$d fichiers de sauvegardes seront conservés</b> dans le répertoire cible.',
'bkp-next-to-delete' => 'Sera effacé lors de la prochaine sauvegarde (Cf. réglage "retention_count")',
'bkp-table-file' => 'Fichier',
'bkp-table-file+' => 'Seuls les fichiers ayant l\'extension .zip sont considérés comme étant des fichiers de sauvegarde',
'bkp-table-size' => 'Taille',
'bkp-table-size+' => '',
'bkp-table-actions' => 'Actions',
'bkp-table-actions+' => '',
'bkp-status-backups-auto' => 'Sauvegardes automatiques',
'bkp-status-backups-manual' => 'Sauvegardes manuelles',
'bkp-status-backups-none' => 'Aucune sauvegarde n\'a été faite jusqu\' à présent.',
'bkp-next-backup' => 'La prochaine sauvegarde aura lieu <b>%1$s</b> (%2$s) à %3$s',
'bkp-button-backup-now' => 'Sauvegarder maintenant !',
'bkp-button-restore-now' => 'Restaurer !',
'bkp-confirm-backup' => 'Veuillez confirmer que vous souhaiter effectuer une sauvegarde maintenant.',
'bkp-confirm-restore' => 'Veuillez confirmer que vous souhaiter effectuer la restauration de \'%1$s\' maintenant.',
'bkp-wait-backup' => 'Sauvegarde en cours...',
'bkp-wait-restore' => 'Restauration des données en cours...',
'bkp-success-restore' => 'Restauration des données terminée.',
));
?>

View File

@@ -0,0 +1,302 @@
<?php
// Copyright (C) 2014 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
require_once(APPROOT.'setup/setuputils.class.inc.php');
require_once(APPROOT.'setup/backup.class.inc.php');
require_once(APPROOT.'core/mutex.class.inc.php');
define('BACKUP_DEFAULT_FORMAT', '__DB__-%Y-%m-%d_%H_%M');
class BackupHandler extends ModuleHandlerAPI
{
public static function OnMetaModelStarted()
{
try
{
$oBackupMutex = new iTopMutex('backup.'.utils::GetCurrentEnvironment());
if ($oBackupMutex->TryLock())
{
$oBackupMutex->Unlock();
}
else
{
// Not needed: the DB dump is done in a single transaction
//MetaModel::GetConfig()->Set('access_mode', ACCESS_READONLY, 'itop-backup');
//MetaModel::GetConfig()->Set('access_message', ' - '.dict::S('bkp-backup-running'), 'itop-backup');
}
$oRestoreMutex = new iTopMutex('restore.'.utils::GetCurrentEnvironment());
if ($oRestoreMutex->TryLock())
{
$oRestoreMutex->Unlock();
}
else
{
MetaModel::GetConfig()->Set('access_mode', ACCESS_READONLY, 'itop-backup');
MetaModel::GetConfig()->Set('access_message', ' - '.dict::S('bkp-restore-running'), 'itop-backup');
}
}
catch(Exception $e)
{
}
}
}
class DBBackupScheduled extends DBBackup
{
protected function LogInfo($sMsg)
{
static $bDebug = null;
if ($bDebug == null)
{
$bDebug = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'debug', false);
}
if ($bDebug)
{
echo $sMsg."\n";
}
}
protected function LogError($sMsg)
{
static $bDebug = null;
if ($bDebug == null)
{
$bDebug = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'debug', false);
}
IssueLog::Error($sMsg);
if ($bDebug)
{
echo 'Error: '.$sMsg."\n";
}
}
/**
* List and order by date the backups in the given directory
* Note: the algorithm is currently based on the file modification date... because there is no "creation date" in general
*/
public function ListFiles($sBackupDir)
{
$aFiles = array();
$aTimes = array();
foreach(glob($sBackupDir.'*.zip') as $sFilePath)
{
$aFiles[] = $sFilePath;
$aTimes[] = filemtime($sFilePath); // unix time
}
array_multisort($aTimes, $aFiles);
return $aFiles;
}
}
class BackupExec implements iScheduledProcess
{
protected $sBackupDir;
protected $iRetentionCount;
/**
* Constructor
* @param sBackupDir string Target directory, defaults to APPROOT/data/backups/auto
* @param iRetentionCount int Rotation (default to the value given in the configuration file 'retentation_count') set to 0 to disable this feature
*/
public function __construct($sBackupDir = null, $iRetentionCount = null)
{
if (is_null($sBackupDir))
{
$this->sBackupDir = APPROOT.'data/backups/auto/';
}
else
{
$this->sBackupDir = $sBackupDir;
}
if (is_null($iRetentionCount))
{
$this->iRetentionCount = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'retention_count', 5);
}
else
{
$this->iRetentionCount = $iRetentionCount;
}
}
public function Process($iUnixTimeLimit)
{
$oMutex = new iTopMutex('backup.'.utils::GetCurrentEnvironment());
$oMutex->Lock();
try
{
// Make sure the target directory exists
SetupUtils::builddir($this->sBackupDir);
$oBackup = new DBBackupScheduled();
// Eliminate files exceeding the retention setting
//
if ($this->iRetentionCount > 0)
{
$aFiles = $oBackup->ListFiles($this->sBackupDir);
while (count($aFiles) >= $this->iRetentionCount)
{
$sFileToDelete = array_shift($aFiles);
unlink($sFileToDelete);
if (file_exists($sFileToDelete))
{
// Ok, do not loop indefinitely on this
break;
}
}
}
// Do execute the backup
//
$oBackup->SetMySQLBinDir(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''));
$sBackupFile = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'file_name_format', '__DB__-%Y-%m-%d_%H_%M');
$sName = $oBackup->MakeName($sBackupFile);
if ($sName == '')
{
$sName = $oBackup->MakeName(BACKUP_DEFAULT_FORMAT);
}
$sZipFile = $this->sBackupDir.$sName.'.zip';
$sSourceConfigFile = APPCONF.utils::GetCurrentEnvironment().'/'.ITOP_CONFIG_FILE;
$oBackup->CreateZip($sZipFile, $sSourceConfigFile);
}
catch (Exception $e)
{
$oMutex->Unlock();
throw $e;
}
$oMutex->Unlock();
return "Created the backup: $sZipFile";
}
/*
Interpret current setting for the week days
@returns array of int (monday = 1)
*/
public function InterpretWeekDays()
{
static $aWEEKDAYTON = array('monday' => 1, 'tuesday' => 2, 'wednesday' => 3, 'thursday' => 4, 'friday' => 5, 'saturday' => 6, 'sunday' => 7);
$aDays = array();
$sWeekDays = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'week_days', 'monday, tuesday, wednesday, thursday, friday');
if ($sWeekDays != '')
{
$aWeekDaysRaw = explode(',', $sWeekDays);
foreach ($aWeekDaysRaw as $sWeekDay)
{
$sWeekDay = strtolower(trim($sWeekDay));
if (array_key_exists($sWeekDay, $aWEEKDAYTON))
{
$aDays[] = $aWEEKDAYTON[$sWeekDay];
}
else
{
throw new Exception("'itop-backup: wrong format for setting 'week_days' (found '$sWeekDay')");
}
}
}
if (count($aDays) == 0)
{
throw new Exception("'itop-backup: missing setting 'week_days'");
}
$aDays = array_unique($aDays);
sort($aDays);
return $aDays;
}
/*
Gives the exact time at which the process must be run next time
@returns DateTime
*/
public function GetNextOccurrence()
{
$bEnabled = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'enabled', true);
if (!$bEnabled)
{
$oRet = new DateTime('3000-01-01');
}
else
{
// 1st - Interpret the list of days as ordered numbers (monday = 1)
//
$aDays = $this->InterpretWeekDays();
// 2nd - Find the next active week day
//
$sBackupTime = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'time', '23:30');
if (!preg_match('/[0-2][0-9]:[0-5][0-9]/', $sBackupTime))
{
throw new Exception("'itop-backup: wrong format for setting 'time' (found '$sBackupTime')");
}
$oNow = new DateTime();
$iNextPos = false;
for ($iDay = $oNow->format('N') ; $iDay <= 7 ; $iDay++)
{
$iNextPos = array_search($iDay, $aDays);
if ($iNextPos !== false)
{
if (($iDay > $oNow->format('N')) || ($oNow->format('H:i') < $sBackupTime))
{
break;
}
}
}
// 3rd - Compute the result
//
if ($iNextPos === false)
{
// Jump to the first day within the next week
$iFirstDayOfWeek = $aDays[0];
$iDayMove = $oNow->format('N') - $iFirstDayOfWeek;
$oRet = clone $oNow;
$oRet->modify('-'.$iDayMove.' days');
$oRet->modify('+1 weeks');
}
else
{
$iNextDayOfWeek = $aDays[$iNextPos];
$iMove = $iNextDayOfWeek - $oNow->format('N');
$oRet = clone $oNow;
$oRet->modify('+'.$iMove.' days');
}
list($sHours, $sMinutes) = explode(':', $sBackupTime);
$oRet->setTime((int)$sHours, (int) $sMinutes);
}
return $oRet;
}
}
class ItopBackup extends ModuleHandlerAPI
{
public static function OnMenuCreation()
{
if (UserRights::IsAdministrator())
{
$oAdminMenu = new MenuGroup('AdminTools', 80 /* fRank */);
new WebPageMenuNode('BackupStatus', utils::GetAbsoluteUrlModulePage('itop-backup', 'status.php'), $oAdminMenu->GetIndex(), 15 /* fRank */);
}
}
}

View File

@@ -0,0 +1,60 @@
<?php
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-backup/2.1.1',
array(
// Identification
//
'label' => 'Backup utilities',
'category' => 'Application management',
// Setup
//
'dependencies' => array(
),
'mandatory' => true,
'visible' => false,
// Components
//
'datamodel' => array(
'main.itop-backup.php',
//'model.itop-backup.php',
),
'webservice' => array(
//'webservices.itop-backup.php',
),
'dictionary' => array(
'en.dict.itop-backup.php',
'fr.dict.itop-backup.php',
//'de.dict.itop-backup.php',
),
'data.struct' => array(
//'data.struct.itop-backup.xml',
),
'data.sample' => array(
//'data.sample.itop-backup.xml',
),
// Documentation
//
'doc.manual_setup' => '',
'doc.more_information' => '',
// Default settings
//
'settings' => array(
'mysql_bindir' => '',
'week_days' => 'monday, tuesday, wednesday, thursday, friday',
'time' => '23:30',
//'file_name_format' => '__DB__-%Y-%m-%d_%H_%M',
'retention_count' => 5,
'enabled' => true,
'debug' => false
),
)
);
?>

View File

@@ -0,0 +1,410 @@
<?php
// Copyright (C) 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/>
/**
* Monitor the backup
*
* @copyright Copyright (C) 2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
require_once(__DIR__.'/../../approot.inc.php');
require_once(APPROOT.'application/application.inc.php');
require_once(APPROOT.'application/itopwebpage.class.inc.php');
require_once(APPROOT.'application/startup.inc.php');
require_once(APPROOT.'application/loginwebpage.class.inc.php');
/////////////////////////////////////////////////////////////////////
// Main program
//
LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin)
//$sOperation = utils::ReadParam('operation', 'menu');
//$oAppContext = new ApplicationContext();
$oP = new iTopWebPage(Dict::S('bkp-status-title'));
$oP->set_base(utils::GetAbsoluteUrlAppRoot().'pages/');
try
{
$oP->add("<h1>".Dict::S('bkp-status-title')."</h1>");
if (MetaModel::GetConfig()->Get('demo_mode'))
{
$oP->add("<div class=\"header_message message_info\">iTop is in <b>demonstration mode</b>: the feature is disabled.</div>");
}
$sImgOk = '<img src="../images/validation_ok.png"> ';
$sImgError = '<img src="../images/validation_error.png"> ';
$oP->add("<fieldset>");
$oP->add("<legend>".Dict::S('bkp-status-checks')."</legend>");
// Availability of mysqldump
//
$sMySQLBinDir = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', '');
$sMySQLBinDir = utils::ReadParam('mysql_bindir', $sMySQLBinDir, true);
if (empty($sMySQLBinDir))
{
$sMySQLDump = 'mysqldump';
}
else
{
//echo 'Info - Found mysql_bindir: '.$sMySQLBinDir;
$sMySQLDump = '"'.$sMySQLBinDir.'/mysqldump"';
}
$sCommand = "$sMySQLDump -V 2>&1";
$aOutput = array();
$iRetCode = 0;
exec($sCommand, $aOutput, $iRetCode);
if ($iRetCode == 0)
{
$sMySqlDump = $sImgOk.Dict::Format("bkp-mysqldump-ok", $aOutput[0]);
}
elseif ($iRetCode == 1)
{
$sMySqlDump = $sImgError.Dict::Format("bkp-mysqldump-notfound", implode(' ', $aOutput));
}
else
{
$sMySqlDump = $sImgError.Dict::Format("bkp-mysqldump-issue", $iRetCode);
}
foreach($aOutput as $sLine)
{
//echo 'Info - mysqldump -V said: '.$sLine;
}
$oP->p($sMySqlDump);
// Destination directory
//
// Make sure the target directory exists and is writeable
$sBackupDir = APPROOT.'data/backups/';
SetupUtils::builddir($sBackupDir);
if (!is_dir($sBackupDir))
{
$oP->p($sImgError.Dict::Format('bkp-missing-dir', $sBackupDir));
}
else
{
$oP->p(Dict::Format('bkp-free-disk-space', SetupUtils::HumanReadableSize(SetupUtils::CheckDiskSpace($sBackupDir)), $sBackupDir));
if (!is_writable($sBackupDir))
{
$oP->p($sImgError.Dict::Format('bkp-dir-not-writeable', $sBackupDir));
}
}
$sBackupDirAuto = $sBackupDir.'auto/';
SetupUtils::builddir($sBackupDirAuto);
$sBackupDirManual = $sBackupDir.'manual/';
SetupUtils::builddir($sBackupDirManual);
// Wrong format
//
$sBackupFile = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'file_name_format', BACKUP_DEFAULT_FORMAT);
$oBackup = new DBBackupScheduled();
$sZipName = $oBackup->MakeName($sBackupFile);
if ($sZipName == '')
{
$oP->p($sImgError.Dict::Format('bkp-wrong-format-spec', $sBackupFile, BACKUP_DEFAULT_FORMAT));
}
else
{
$oP->p(Dict::Format('bkp-name-sample', $sZipName));
}
// Week Days
//
$aWeekDayToString = array(
1 => Dict::S('DayOfWeek-Monday'),
2 => Dict::S('DayOfWeek-Tuesday'),
3 => Dict::S('DayOfWeek-Wednesday'),
4 => Dict::S('DayOfWeek-Thursday'),
5 => Dict::S('DayOfWeek-Friday'),
6 => Dict::S('DayOfWeek-Saturday'),
7 => Dict::S('DayOfWeek-Sunday')
);
$aDayLabels = array();
$oBackupExec = new BackupExec();
foreach ($oBackupExec->InterpretWeekDays() as $iDay)
{
$aDayLabels[] = $aWeekDayToString[$iDay];
}
$sDays = implode(', ', $aDayLabels);
$sBackupTime = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'time', '23:30');
$oP->p(Dict::Format('bkp-week-days', $sDays, $sBackupTime));
$iRetention = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'retention_count', 5);
$oP->p(Dict::Format('bkp-retention', $iRetention));
$oP->add("</fieldset>");
// List of backups
//
$aFiles = $oBackup->ListFiles($sBackupDirAuto);
$aFilesToDelete = array();
while (count($aFiles) > $iRetention - 1)
{
$aFilesToDelete[] = array_shift($aFiles);
}
$oRestoreMutex = new iTopMutex('restore.'.utils::GetCurrentEnvironment());
if ($oRestoreMutex->TryLock())
{
$oRestoreMutex->Unlock();
$sDisableRestore = '';
}
else
{
$sDisableRestore = 'disabled="disabled"';
}
// 1st table: list the backups made in the background
//
$aDetails = array();
foreach ($oBackup->ListFiles($sBackupDirAuto) as $sBackupFile)
{
$sFileName = basename($sBackupFile);
$sFilePath = 'auto/'.$sFileName;
if (MetaModel::GetConfig()->Get('demo_mode'))
{
$sName = $sFileName;
}
else
{
$sAjax = utils::GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php', array('operation' => 'download', 'file' => $sFilePath));
$sName = "<a href=\"$sAjax\">".$sFileName.'</a>';
}
$sSize = SetupUtils::HumanReadableSize(filesize($sBackupFile));
$sConfirmRestore = addslashes(Dict::Format('bkp-confirm-restore', $sFileName));
$sFileEscaped = addslashes($sFilePath);
$sRestoreBtn = '<button class="restore" onclick="LaunchRestoreNow(\''.$sFileEscaped.'\', \''.$sConfirmRestore.'\');" '.$sDisableRestore.'>'.Dict::S('bkp-button-restore-now').'</button>';
if (in_array($sBackupFile, $aFilesToDelete))
{
$aDetails[] = array('file' => $sName.' <span class="next_to_delete" title="'.Dict::S('bkp-next-to-delete').'">*</span>', 'size' => $sSize, 'actions' => $sRestoreBtn);
}
else
{
$aDetails[] = array('file' => $sName, 'size' => $sSize, 'actions' => $sRestoreBtn);
}
}
$aConfig = array(
'file' => array('label' => Dict::S('bkp-table-file'), 'description' => Dict::S('bkp-table-file+')),
'size' => array('label' => Dict::S('bkp-table-size'), 'description' => Dict::S('bkp-table-size+')),
'actions' => array('label' => Dict::S('bkp-table-actions'), 'description' => Dict::S('bkp-table-actions+')),
);
$oP->add("<fieldset>");
$oP->add("<legend>".Dict::S('bkp-status-backups-auto')."</legend>");
if (count($aDetails) > 0)
{
$oP->add('<div style="max-height:400px; overflow: auto;">');
$oP->table($aConfig, array_reverse($aDetails));
$oP->add('</div>');
}
else
{
$oP->p(Dict::S('bkp-status-backups-none'));
}
$oP->add("</fieldset>");
// 2nd table: list the backups made manually
//
$aDetails = array();
foreach ($oBackup->ListFiles($sBackupDirManual) as $sBackupFile)
{
$sFileName = basename($sBackupFile);
$sFilePath = 'manual/'.$sFileName;
if (MetaModel::GetConfig()->Get('demo_mode'))
{
$sName = $sFileName;
}
else
{
$sAjax = utils::GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php', array('operation' => 'download', 'file' => $sFilePath));
$sName = "<a href=\"$sAjax\">".$sFileName.'</a>';
}
$sSize = SetupUtils::HumanReadableSize(filesize($sBackupFile));
$sConfirmRestore = addslashes(Dict::Format('bkp-confirm-restore', $sFileName));
$sFileEscaped = addslashes($sFilePath);
$sRestoreBtn = '<button class="restore" onclick="LaunchRestoreNow(\''.$sFileEscaped.'\', \''.$sConfirmRestore.'\');" '.$sDisableRestore.'>'.Dict::S('bkp-button-restore-now').'</button>';
$aDetails[] = array('file' => $sName, 'size' => $sSize, 'actions' => $sRestoreBtn);
}
$aConfig = array(
'file' => array('label' => Dict::S('bkp-table-file'), 'description' => Dict::S('bkp-table-file+')),
'size' => array('label' => Dict::S('bkp-table-size'), 'description' => Dict::S('bkp-table-size+')),
'actions' => array('label' => Dict::S('bkp-table-actions'), 'description' => Dict::S('bkp-table-actions+')),
);
$oP->add("<fieldset>");
$oP->add("<legend>".Dict::S('bkp-status-backups-manual')."</legend>");
if (count($aDetails) > 0)
{
$oP->add('<div style="max-height:400px; overflow: auto;">');
$oP->table($aConfig, array_reverse($aDetails));
$oP->add('</div>');
}
else
{
$oP->p(Dict::S('bkp-status-backups-none'));
}
$oP->add("</fieldset>");
// Ongoing operation ?
//
$oBackupMutex = new iTopMutex('backup.'.utils::GetCurrentEnvironment());
if ($oBackupMutex->TryLock())
{
$oBackupMutex->Unlock();
}
else
{
$oP->p(Dict::S('bkp-backup-running'));
}
$oRestoreMutex = new iTopMutex('restore.'.utils::GetCurrentEnvironment());
if ($oRestoreMutex->TryLock())
{
$oRestoreMutex->Unlock();
}
else
{
$oP->p(Dict::S('bkp-restore-running'));
}
// Do backup now
//
$oBackupExec = new BackupExec();
$oNext = $oBackupExec->GetNextOccurrence();
$oP->p(Dict::Format('bkp-next-backup', $aWeekDayToString[$oNext->Format('N')], $oNext->Format('Y-m-d'), $oNext->Format('H:i')));
$oP->p('<button onclick="LaunchBackupNow();">'.Dict::S('bkp-button-backup-now').'</button>');
$oP->add('<div id="backup_success" class="header_message message_ok" style="display: none;"></div>');
$oP->add('<div id="backup_errors" class="header_message message_error" style="display: none;"></div>');
$oP->add('<input type="hidden" name="restore_token" id="restore_token"/>');
$sConfirmBackup = addslashes(Dict::S('bkp-confirm-backup'));
$sPleaseWaitBackup = addslashes(Dict::S('bkp-wait-backup'));
$sPleaseWaitRestore = addslashes(Dict::S('bkp-wait-restore'));
$sRestoreDone = addslashes(Dict::S('bkp-success-restore'));
$sMySQLBinDir = addslashes(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''));
$sDBHost = addslashes(MetaModel::GetConfig()->GetDBHost());
$sDBUser = addslashes(MetaModel::GetConfig()->GetDBUser());
$sDBPwd = addslashes(MetaModel::GetConfig()->GetDBPwd());
$sDBName = addslashes(MetaModel::GetConfig()->GetDBName());
$sDBSubName = addslashes(MetaModel::GetConfig()->GetDBSubName());
$sEnvironment = addslashes(utils::GetCurrentEnvironment());
$oP->add_script(
<<<EOF
function LaunchBackupNow()
{
$('#backup_success').hide();
$('#backup_errors').hide();
if (confirm('$sConfirmBackup'))
{
$.blockUI({ message: '<h1><img src="../images/indicator.gif" /> $sPleaseWaitBackup</h1>' });
var oParams = {};
oParams.operation = 'backup';
$.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){
if (data.search(/error|exceptio|notice|warning/i) != -1)
{
$('#backup_errors').html(data);
$('#backup_errors').show();
}
else
{
window.location.reload();
}
$.unblockUI();
});
}
}
function LaunchRestoreNow(sBackupFile, sConfirmationMessage)
{
if (confirm(sConfirmationMessage))
{
$.blockUI({ message: '<h1><img src="../images/indicator.gif" /> $sPleaseWaitRestore</h1>' });
$('#backup_success').hide();
$('#backup_errors').hide();
var oParams = {};
oParams.operation = 'restore_get_token';
oParams.file = sBackupFile;
$.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){
// Get the value of restore_token
$('#backup_errors').append(data);
var oParams = {};
oParams.operation = 'restore_exec';
oParams.token = $("#restore_token").val();
oParams.mysql_bindir = '$sMySQLBinDir';
oParams.db_host = '$sDBHost';
oParams.db_user = '$sDBUser';
oParams.db_pwd = '$sDBPwd';
oParams.db_name = '$sDBName';
oParams.db_subname = '$sDBSubName';
oParams.environment = '$sEnvironment';
if (oParams.token.length > 0)
{
$.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){
if (data.search(/error|exceptio|notice|warning/i) != -1)
{
$('#backup_success').hide();
$('#backup_errors').html(data);
$('#backup_errors').show();
}
else
{
$('#backup_errors').hide();
$('#backup_success').html('$sRestoreDone');
$('#backup_success').show();
}
$.unblockUI();
});
}
else
{
$('button.restore').attr('disabled', 'disabled');
$.unblockUI();
}
});
}
}
EOF
);
if (MetaModel::GetConfig()->Get('demo_mode'))
{
$oP->add_ready_script("$('button').attr('disabled', 'disabled').attr('title', 'Disabled in demonstration mode')");
}
}
catch(Exception $e)
{
$oP->p('<b>'.$e->getMessage().'</b>');
}
$oP->output();
?>

View File

@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<classes>
<class id="lnkVirtualDeviceToVolume" _delta="define">
<parent>cmdbAbstractObject</parent>
<properties>
<is_link>1</is_link>
<category>bizmodel,configmgmt</category>
<abstract>false</abstract>
<key_type>autoincrement</key_type>
@@ -91,4 +92,4 @@
</presentation>
</class>
</classes>
</itop_design>
</itop_design>

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-virtualization-storage/2.0.0',
'itop-bridge-virtualization-storage/2.1.0',
array(
// Identification
//

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-change-mgmt-itil/2.0.0',
'itop-change-mgmt-itil/2.1.0',
array(
// Identification
//

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<classes>
<class id="Change" _delta="define">
<parent>Ticket</parent>
@@ -38,12 +38,12 @@
<fields>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>new</value>
<value>assigned</value>
<value>planned</value>
<value>approved</value>
<value>closed</value>
<value>rejected</value>
<value id="new">new</value>
<value id="assigned">assigned</value>
<value id="planned">planned</value>
<value id="approved">approved</value>
<value id="closed">closed</value>
<value id="rejected">rejected</value>
</values>
<sql>status</sql>
<default_value>new</default_value>
@@ -51,12 +51,12 @@
</field>
<field id="category" xsi:type="AttributeEnum">
<values>
<value>hardware</value>
<value>software</value>
<value>system</value>
<value>network</value>
<value>application</value>
<value>other</value>
<value id="hardware">hardware</value>
<value id="software">software</value>
<value id="system">system</value>
<value id="network">network</value>
<value id="application">application</value>
<value id="other">other</value>
</values>
<sql>category</sql>
<default_value>hardware</default_value>
@@ -107,8 +107,8 @@
</field>
<field id="outage" xsi:type="AttributeEnum">
<values>
<value>yes</value>
<value>no</value>
<value id="yes">yes</value>
<value id="no">no</value>
</values>
<sql>outage</sql>
<default_value>no</default_value>
@@ -145,6 +145,23 @@
</field>
</fields>
<lifecycle>
<highlight_scale>
<item id="approved">
<rank>1</rank>
<color>HIGHLIGHT_CLASS_NONE</color>
<icon>images/change-approved.png</icon>
</item>
<item id="rejected">
<rank>2</rank>
<color>HIGHLIGHT_CLASS_NONE</color>
<icon>images/change-rejected.png</icon>
</item>
<item id="closed">
<rank>3</rank>
<color>HIGHLIGHT_CLASS_NONE</color>
<icon>images/change-closed.png</icon>
</item>
</highlight_scale>
<attribute>status</attribute>
<stimuli>
<stimulus id="ev_assign" xsi:type="StimulusUserAction"/>
@@ -201,21 +218,15 @@
</attribute>
</flags>
<transitions>
<transition>
<stimulus>ev_assign</stimulus>
<transition id="ev_assign">
<target>assigned</target>
<actions/>
</transition>
</transitions>
</state>
<state id="assigned">
<inherit_flags_from>new</inherit_flags_from>
<flags>
<attribute id="ref">
<read_only/>
</attribute>
<attribute id="org_id">
<mandatory/>
</attribute>
<attribute id="team_id">
<mandatory/>
<must_prompt/>
@@ -224,57 +235,22 @@
<mandatory/>
<must_prompt/>
</attribute>
<attribute id="title">
<mandatory/>
</attribute>
<attribute id="description">
<mandatory/>
</attribute>
<attribute id="last_update">
<read_only/>
</attribute>
<attribute id="close_date">
<hidden/>
</attribute>
<attribute id="reject_reason">
<hidden/>
</attribute>
<attribute id="changemanager_id">
<mandatory/>
<must_prompt/>
</attribute>
<attribute id="creation_date">
<read_only/>
</attribute>
<attribute id="approval_date">
<hidden/>
</attribute>
<attribute id="outage">
<hidden/>
</attribute>
<attribute id="caller_id"/>
</flags>
<transitions>
<transition>
<stimulus>ev_plan</stimulus>
<transition id="ev_plan">
<target>planned</target>
<actions/>
</transition>
</transitions>
</state>
<state id="planned">
<inherit_flags_from>assigned</inherit_flags_from>
<flags>
<attribute id="ref">
<read_only/>
</attribute>
<attribute id="org_id">
<mandatory/>
</attribute>
<attribute id="title">
<mandatory/>
</attribute>
<attribute id="description">
<mandatory/>
</attribute>
<attribute id="start_date">
<mandatory/>
<must_prompt/>
@@ -283,21 +259,6 @@
<mandatory/>
<must_prompt/>
</attribute>
<attribute id="last_update">
<read_only/>
</attribute>
<attribute id="close_date">
<hidden/>
</attribute>
<attribute id="reject_reason">
<hidden/>
</attribute>
<attribute id="creation_date">
<read_only/>
</attribute>
<attribute id="approval_date">
<hidden/>
</attribute>
<attribute id="fallback_plan">
<mandatory/>
<must_prompt/>
@@ -305,28 +266,17 @@
<attribute id="caller_id">
<mandatory/>
</attribute>
<attribute id="team_id">
<mandatory/>
</attribute>
<attribute id="agent_id">
<mandatory/>
</attribute>
<attribute id="changemanager_id">
<mandatory/>
</attribute>
<attribute id="outage">
<mandatory/>
<must_prompt/>
</attribute>
</flags>
<transitions>
<transition>
<stimulus>ev_reject</stimulus>
<transition id="ev_reject">
<target>rejected</target>
<actions/>
</transition>
<transition>
<stimulus>ev_approve</stimulus>
<transition id="ev_approve">
<target>approved</target>
<actions>
<action>
@@ -340,8 +290,31 @@
</transitions>
</state>
<state id="rejected">
<highlight>
<code>rejected</code>
</highlight>
<inherit_flags_from>assigned</inherit_flags_from>
<flags>
<attribute id="ref">
<attribute id="start_date">
<read_only/>
</attribute>
<attribute id="end_date">
<read_only/>
</attribute>
<attribute id="private_log">
<read_only/>
<read_only/>
</attribute>
<attribute id="caller_id">
<read_only/>
</attribute>
<attribute id="fallback_plan">
<read_only/>
</attribute>
<attribute id="category">
<read_only/>
</attribute>
<attribute id="parent_id">
<read_only/>
</attribute>
<attribute id="org_id">
@@ -353,34 +326,13 @@
<attribute id="description">
<read_only/>
</attribute>
<attribute id="last_update">
<read_only/>
</attribute>
<attribute id="close_date">
<hidden/>
</attribute>
<attribute id="start_date">
<read_only/>
</attribute>
<attribute id="end_date">
<read_only/>
</attribute>
<attribute id="private_log">
<read_only/>
</attribute>
<attribute id="reject_reason">
<mandatory/>
<must_prompt/>
</attribute>
<attribute id="creation_date">
<read_only/>
</attribute>
<attribute id="approval_date">
<read_only/>
</attribute>
<attribute id="caller_id">
<read_only/>
</attribute>
<attribute id="team_id">
<read_only/>
</attribute>
@@ -390,35 +342,26 @@
<attribute id="changemanager_id">
<read_only/>
</attribute>
<attribute id="private_log">
<read_only/>
</attribute>
<attribute id="fallback_plan">
<read_only/>
</attribute>
<attribute id="category">
<read_only/>
</attribute>
<attribute id="parent_id">
<read_only/>
</attribute>
<attribute id="outage">
<read_only/>
</attribute>
</flags>
<transitions>
<transition>
<stimulus>ev_reopen</stimulus>
<transition id="ev_reopen">
<target>assigned</target>
<actions/>
</transition>
</transitions>
</state>
<state id="approved">
<highlight>
<code>approved</code>
</highlight>
<inherit_flags_from>planned</inherit_flags_from>
<flags>
<attribute id="ref">
<read_only/>
</attribute>
<attribute id="private_log"/>
<attribute id="category"/>
<attribute id="parent_id"/>
<attribute id="org_id">
<read_only/>
</attribute>
@@ -428,21 +371,12 @@
<attribute id="description">
<read_only/>
</attribute>
<attribute id="last_update">
<read_only/>
</attribute>
<attribute id="close_date">
<hidden/>
</attribute>
<attribute id="start_date">
<read_only/>
</attribute>
<attribute id="end_date">
<read_only/>
</attribute>
<attribute id="private_log">
<normal/>
</attribute>
<attribute id="reject_reason">
<read_only/>
</attribute>
@@ -464,22 +398,13 @@
<attribute id="changemanager_id">
<read_only/>
</attribute>
<attribute id="fallback_plan">
<normal/>
</attribute>
<attribute id="category">
<normal/>
</attribute>
<attribute id="parent_id">
<normal/>
</attribute>
<attribute id="fallback_plan"/>
<attribute id="outage">
<read_only/>
</attribute>
</flags>
<transitions>
<transition>
<stimulus>ev_finish</stimulus>
<transition id="ev_finish">
<target>closed</target>
<actions>
<action>
@@ -490,52 +415,17 @@
</transitions>
</state>
<state id="closed">
<highlight>
<code>closed</code>
</highlight>
<inherit_flags_from>approved</inherit_flags_from>
<flags>
<attribute id="ref">
<read_only/>
</attribute>
<attribute id="org_id">
<read_only/>
</attribute>
<attribute id="title">
<read_only/>
</attribute>
<attribute id="description">
<read_only/>
</attribute>
<attribute id="last_update">
<read_only/>
</attribute>
<attribute id="close_date">
<read_only/>
</attribute>
<attribute id="start_date">
<read_only/>
</attribute>
<attribute id="end_date">
<read_only/>
</attribute>
<attribute id="reject_reason">
<read_only/>
</attribute>
<attribute id="creation_date">
<read_only/>
</attribute>
<attribute id="approval_date">
<read_only/>
</attribute>
<attribute id="caller_id">
<read_only/>
</attribute>
<attribute id="team_id">
<read_only/>
</attribute>
<attribute id="agent_id">
<read_only/>
</attribute>
<attribute id="changemanager_id">
<read_only/>
</attribute>
<attribute id="private_log">
<read_only/>
</attribute>
@@ -548,9 +438,6 @@
<attribute id="parent_id">
<read_only/>
</attribute>
<attribute id="outage">
<read_only/>
</attribute>
</flags>
<transitions/>
</state>
@@ -633,63 +520,6 @@
$this->Set('last_update', time());
}]]></code>
</method>
<method id="GetIcon">
<comment>/**&#13;
* Get the icon representing this object&#13;
* @param boolean $bImgTag If true the result is a full IMG tag (or an emtpy string if no icon is defined)&#13;
* @return string Either the full IMG tag ($bImgTag == true) or just the path to the icon file&#13;
*/</comment>
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[ public function GetIcon($bImgTag = true)
{
$sStatus = $this->Get('status');
switch($this->GetState())
{
case 'approved':
case 'implemented':
case 'monitored':
$sIcon = self::MakeIconFromName('change-approved.png');
break;
case 'rejected':
case 'notapproved':
$sIcon = self::MakeIconFromName('change-rejected.png');
break;
case 'closed':
$sIcon = self::MakeIconFromName('change-closed.png');
break;
default:
$sIcon = MetaModel::GetClassIcon(get_class($this), $bImgTag);
}
return $sIcon;
}]]></code>
</method>
<method id="MakeIconFromName">
<static>true</static>
<access>protected</access>
<type>Overload-DBObject</type>
<code><![CDATA[ protected static function MakeIconFromName($sIconName, $bImgTag = true)
{
$sIcon = '';
if ($sIconName != '')
{
$sPath = '../env-'.utils::GetCurrentEnvironment().'/itop-change-mgmt/images/'.$sIconName;
if ($bImgTag)
{
$sIcon = "<img src=\"$sPath\" style=\"vertical-align:middle;\"/>";
}
else
{
$sIcon = $sPath;
}
}
return $sIcon;
}]]></code>
</method>
</methods>
<presentation>
<details>

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-change-mgmt/2.0.0',
'itop-change-mgmt/2.1.0',
array(
// Identification
//

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<classes>
<class id="Organization" _delta="define">
<parent>cmdbAbstractObject</parent>
@@ -44,8 +44,8 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>active</value>
<value>inactive</value>
<value id="active">active</value>
<value id="inactive">inactive</value>
</values>
<sql>status</sql>
<default_value>active</default_value>
@@ -144,8 +144,8 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>active</value>
<value>inactive</value>
<value id="active">active</value>
<value id="inactive">inactive</value>
</values>
<sql>status</sql>
<default_value>active</default_value>
@@ -307,8 +307,8 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>active</value>
<value>inactive</value>
<value id="active">active</value>
<value id="inactive">inactive</value>
</values>
<sql>status</sql>
<default_value>active</default_value>
@@ -337,8 +337,8 @@
</field>
<field id="notify" xsi:type="AttributeEnum">
<values>
<value>yes</value>
<value>no</value>
<value id="yes">yes</value>
<value id="no">no</value>
</values>
<sql>notify</sql>
<default_value>yes</default_value>
@@ -486,7 +486,7 @@
<target_class>Location</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="location_name" xsi:type="AttributeExternalField">
<extkey_attcode>location_id</extkey_attcode>
@@ -835,9 +835,9 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>draft</value>
<value>published</value>
<value>obsolete</value>
<value id="draft">draft</value>
<value id="published">published</value>
<value id="obsolete">obsolete</value>
</values>
<sql>status</sql>
<default_value/>
@@ -972,7 +972,7 @@
<static>false</static>
<access>public</access>
<type>Overload-cmdbAbstractObject</type>
<comment><![CDATA[/**
<comment><![CDATA[/**
* Overload the display of the properties to add a tab (the first one)
* with the preview of the document
*/
@@ -1320,9 +1320,9 @@
</field>
<field id="business_criticity" xsi:type="AttributeEnum">
<values>
<value>high</value>
<value>medium</value>
<value>low</value>
<value id="high">high</value>
<value id="medium">medium</value>
<value id="low">low</value>
</values>
<sql>business_criticity</sql>
<default_value>low</default_value>
@@ -1526,7 +1526,7 @@
<target_class>Location</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="location_name" xsi:type="AttributeExternalField">
<extkey_attcode>location_id</extkey_attcode>
@@ -1534,10 +1534,10 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>production</value>
<value>implementation</value>
<value>stock</value>
<value>obsolete</value>
<value id="production">production</value>
<value id="implementation">implementation</value>
<value id="stock">stock</value>
<value id="obsolete">obsolete</value>
</values>
<sql>status</sql>
<default_value>production</default_value>
@@ -1708,8 +1708,6 @@
</list>
</presentation>
</class>
<class id="ConnectableCI" _delta="define">
<parent>PhysicalDevice</parent>
<properties>
@@ -1940,7 +1938,7 @@
<target_class>Rack</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="rack_name" xsi:type="AttributeExternalField">
<extkey_attcode>rack_id</extkey_attcode>
@@ -1955,7 +1953,7 @@
<target_class>Enclosure</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="enclosure_name" xsi:type="AttributeExternalField">
<extkey_attcode>enclosure_id</extkey_attcode>
@@ -1980,7 +1978,7 @@
<target_class>PowerConnection</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="powerA_name" xsi:type="AttributeExternalField">
<extkey_attcode>powerA_id</extkey_attcode>
@@ -1995,7 +1993,7 @@
<target_class>PowerConnection</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="powerB_name" xsi:type="AttributeExternalField">
<extkey_attcode>powerB_id</extkey_attcode>
@@ -2835,8 +2833,6 @@
</list>
</presentation>
</class>
<class id="ApplicationSolution" _delta="define">
<parent>FunctionalCI</parent>
<properties>
@@ -2881,8 +2877,8 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>active</value>
<value>inactive</value>
<value id="active">active</value>
<value id="inactive">inactive</value>
</values>
<sql>status</sql>
<default_value>active</default_value>
@@ -3030,8 +3026,8 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>active</value>
<value>inactive</value>
<value id="active">active</value>
<value id="inactive">inactive</value>
</values>
<sql>status</sql>
<default_value>active</default_value>
@@ -3163,7 +3159,7 @@
<target_class>FunctionalCI</target_class>
<is_null_allowed>false</is_null_allowed>
<on_target_delete>DEL_AUTO</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="system_name" xsi:type="AttributeExternalField">
<extkey_attcode>system_id</extkey_attcode>
@@ -3189,7 +3185,7 @@
<target_class>SoftwareLicence</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="softwarelicence_name" xsi:type="AttributeExternalField">
<extkey_attcode>softwarelicence_id</extkey_attcode>
@@ -3202,8 +3198,8 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>active</value>
<value>inactive</value>
<value id="active">active</value>
<value id="inactive">inactive</value>
</values>
<sql>status</sql>
<default_value/>
@@ -4501,11 +4497,11 @@
</field>
<field id="type" xsi:type="AttributeEnum">
<values>
<value>Middleware</value>
<value>DBServer</value>
<value>PCSoftware</value>
<value>OtherSoftware</value>
<value>WebServer</value>
<value id="Middleware">Middleware</value>
<value id="DBServer">DBServer</value>
<value id="PCSoftware">PCSoftware</value>
<value id="OtherSoftware">OtherSoftware</value>
<value id="WebServer">WebServer</value>
</values>
<sql>type</sql>
<default_value/>
@@ -4926,8 +4922,8 @@
</field>
<field id="perpetual" xsi:type="AttributeEnum">
<values>
<value>yes</value>
<value>no</value>
<value id="yes">yes</value>
<value id="no">no</value>
</values>
<sql>perpetual</sql>
<default_value>no</default_value>
@@ -5736,24 +5732,24 @@
</field>
<field id="type" xsi:type="AttributeEnum">
<values>
<value>NetworkDevice</value>
<value>Server</value>
<value>SANSwitch</value>
<value>StorageSystem</value>
<value>Rack</value>
<value>Enclosure</value>
<value>PC</value>
<value>Tablet</value>
<value>Telephone</value>
<value>MobilePhone</value>
<value>Printer</value>
<value>DiskArray</value>
<value>NAS</value>
<value>TapeLibrary</value>
<value>IPPhone</value>
<value>Peripheral</value>
<value>PowerSource</value>
<value>PDU</value>
<value id="NetworkDevice">NetworkDevice</value>
<value id="Server">Server</value>
<value id="SANSwitch">SANSwitch</value>
<value id="StorageSystem">StorageSystem</value>
<value id="Rack">Rack</value>
<value id="Enclosure">Enclosure</value>
<value id="PC">PC</value>
<value id="Tablet">Tablet</value>
<value id="Telephone">Telephone</value>
<value id="MobilePhone">MobilePhone</value>
<value id="Printer">Printer</value>
<value id="DiskArray">DiskArray</value>
<value id="NAS">NAS</value>
<value id="TapeLibrary">TapeLibrary</value>
<value id="IPPhone">IPPhone</value>
<value id="Peripheral">Peripheral</value>
<value id="PowerSource">PowerSource</value>
<value id="PDU">PDU</value>
</values>
<sql>type</sql>
<default_value/>
@@ -6817,8 +6813,6 @@
</list>
</presentation>
</class>
<class id="NetworkInterface" _delta="define">
<parent>cmdbAbstractObject</parent>
<properties>
@@ -6874,7 +6868,6 @@
</list>
</presentation>
</class>
<class id="IPInterface" _delta="define">
<parent>NetworkInterface</parent>
<properties>
@@ -7214,7 +7207,6 @@
</list>
</presentation>
</class>
<class id="lnkConnectableCIToNetworkDevice" _delta="define">
<parent>cmdbAbstractObject</parent>
<properties>
@@ -7274,8 +7266,8 @@
</field>
<field id="connection_type" xsi:type="AttributeEnum">
<values>
<value>uplink</value>
<value>downlink</value>
<value id="uplink">uplink</value>
<value id="downlink">downlink</value>
</values>
<sql>type</sql>
<default_value>downlink</default_value>
@@ -7290,13 +7282,11 @@
<type>Overload-cmdbAbstractObject</type>
<code><![CDATA[ protected function AddConnectedNetworkDevice()
{
$iNetworkDeviceID = $this->Get('networkdevice_id');
$iDeviceID = $this->Get('connectableci_id');
$oDevice = MetaModel::GetObject('ConnectableCI', $this->Get('connectableci_id'));
$sOQL = "SELECT lnkConnectableCIToNetworkDevice WHERE connectableci_id = :device AND networkdevice_id = :network AND network_port = :nwport AND device_port = :devport";
$oConnectionSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
if (is_object($oDevice) && (get_class($oDevice) == 'NetworkDevice'))
{
$sOQL = "SELECT lnkConnectableCIToNetworkDevice WHERE connectableci_id = :device AND networkdevice_id = :network AND network_port = :nwport AND device_port = :devport";
$oConnectionSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
array(),
array(
'network' => $this->Get('connectableci_id'),
@@ -7304,36 +7294,21 @@
'devport' => $this->Get('network_port'),
'nwport' => $this->Get('device_port'),
)
);
$iAlreadyExist = $oConnectionSet->count();
if ((get_class($oDevice) == 'NetworkDevice') && ($iAlreadyExist == 0))
{
$sLink = $this->Get('connection_type');
$sConnLink = ($sLink == 'uplink') ? 'downlink' : 'uplink';
$oNewLink = new lnkConnectableCIToNetworkDevice();
$oNewLink->Set('networkdevice_id', $this->Get('connectableci_id'));
$oNewLink->Set('connectableci_id', $this->Get('networkdevice_id'));
$oNewLink->Set('network_port', $this->Get('device_port'));
$oNewLink->Set('device_port', $this->Get('network_port'));
$oNewLink->Set('connection_type', $sConnLink);
$oMyChange = MetaModel::NewObject("CMDBChange");
$oMyChange->Set("date", time());
if (UserRights::IsImpersonated())
);
if ($oConnectionSet->Count() == 0)
{
$sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser());
}
else
{
$sUserString = UserRights::GetUser();
}
$oMyChange->Set("userinfo", $sUserString);
$iChangeId = $oMyChange->DBInsert();
$oNewLink->DBInsertTracked($oMyChange);
$sLink = $this->Get('connection_type');
$sConnLink = ($sLink == 'uplink') ? 'downlink' : 'uplink';
$oNewLink = new lnkConnectableCIToNetworkDevice();
$oNewLink->Set('networkdevice_id', $this->Get('connectableci_id'));
$oNewLink->Set('connectableci_id', $this->Get('networkdevice_id'));
$oNewLink->Set('network_port', $this->Get('device_port'));
$oNewLink->Set('device_port', $this->Get('network_port'));
$oNewLink->Set('connection_type', $sConnLink);
$oNewLink->DBInsert();
}
}
}]]></code>
</method>
<method id="UpdateConnectedNetworkDevice">
@@ -7342,45 +7317,32 @@
<type>Overload-cmdbAbstractObject</type>
<code><![CDATA[ protected function UpdateConnectedNetworkDevice()
{
$iNetworkDeviceID = $this->Get('networkdevice_id');
$iDeviceID = $this->Get('connectableci_id');
$oDevice = MetaModel::GetObject('ConnectableCI', $this->Get('connectableci_id'));
$sOQL = "SELECT lnkConnectableCIToNetworkDevice WHERE connectableci_id = :device AND networkdevice_id = :network AND network_port = :nwport AND device_port = :devport";
$oConnectionSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
if (is_object($oDevice) && (get_class($oDevice) == 'NetworkDevice'))
{
// Note: in case a port has been changed, search with the original values
$sOQL = "SELECT lnkConnectableCIToNetworkDevice WHERE connectableci_id = :device AND networkdevice_id = :network AND network_port = :nwport AND device_port = :devport";
$oConnectionSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
array(),
array(
'network' => $this->Get('connectableci_id'),
'device' => $this->Get('networkdevice_id'),
'devport' => $this->Get('network_port'),
'nwport' => $this->Get('device_port'),
'devport' => $this->GetOriginal('network_port'),
'nwport' => $this->GetOriginal('device_port'),
)
);
$iAlreadyExist = $oConnectionSet->count();
if ((get_class($oDevice) == 'NetworkDevice') && ($iAlreadyExist != 0))
{
);
$sLink = $this->Get('connection_type');
$sConnLink = ($sLink == 'uplink') ? 'downlink' : 'uplink';
$oMyChange = MetaModel::NewObject("CMDBChange");
$oMyChange->Set("date", time());
if (UserRights::IsImpersonated())
// There should be one link - do it in a safe manner anyway
while ($oConnection = $oConnectionSet->Fetch())
{
$sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser());
$oConnection->Set('connection_type', $sConnLink);
$oConnection->Set('network_port', $this->Get('device_port'));
$oConnection->Set('device_port', $this->Get('network_port'));
$oConnection->DBUpdate();
}
else
{
$sUserString = UserRights::GetUser();
}
$oMyChange->Set("userinfo", $sUserString);
$iChangeId = $oMyChange->DBInsert();
$oConnection = $oConnectionSet->Fetch();
$oConnection->Set('connection_type', $sConnLink);
$oConnection->DBUpdateTracked($oMyChange);
}
}]]></code>
</method>
<method id="DeleteConnectedNetworkDevice">
@@ -7389,13 +7351,13 @@
<type>Overload-cmdbAbstractObject</type>
<code><![CDATA[ protected function DeleteConnectedNetworkDevice()
{
$iNetworkDeviceID = $this->Get('networkdevice_id');
$iDeviceID = $this->Get('connectableci_id');
$oDevice = MetaModel::GetObject('ConnectableCI', $this->Get('connectableci_id'));
$sOQL = "SELECT lnkConnectableCIToNetworkDevice WHERE connectableci_id = :device AND networkdevice_id = :network AND network_port = :nwport AND device_port = :devport";
$oConnectionSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
// The device might be already deleted (reentrance in the current procedure when both device are NETWORK devices!)
$oDevice = MetaModel::GetObject('ConnectableCI', $this->Get('connectableci_id'), false);
if (is_object($oDevice) && (get_class($oDevice) == 'NetworkDevice'))
{
// Track and delete the counterpart link
$sOQL = "SELECT lnkConnectableCIToNetworkDevice WHERE connectableci_id = :device AND networkdevice_id = :network AND network_port = :nwport AND device_port = :devport";
$oConnectionSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),
array(),
array(
'network' => $this->Get('connectableci_id'),
@@ -7403,27 +7365,13 @@
'devport' => $this->Get('network_port'),
'nwport' => $this->Get('device_port'),
)
);
$iAlreadyExist = $oConnectionSet->count();
if ((get_class($oDevice) == 'NetworkDevice') && ($iAlreadyExist != 0))
{
$oMyChange = MetaModel::NewObject("CMDBChange");
$oMyChange->Set("date", time());
if (UserRights::IsImpersonated())
);
// There should be one link - do it in a safe manner anyway
while ($oConnection = $oConnectionSet->Fetch())
{
$sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser());
$oConnection->DBDelete();
}
else
{
$sUserString = UserRights::GetUser();
}
$oMyChange->Set("userinfo", $sUserString);
$iChangeId = $oMyChange->DBInsert();
$oConnection = $oConnectionSet->Fetch();
$oConnection->DBDeleteTracked($oMyChange);
}
}
}]]></code>
</method>
<method id="AfterInsert">
@@ -7815,9 +7763,9 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>production</value>
<value>implementation</value>
<value>obsolete</value>
<value id="production">production</value>
<value id="implementation">implementation</value>
<value id="obsolete">obsolete</value>
</values>
<sql>status</sql>
<default_value>implementation</default_value>
@@ -8053,146 +8001,146 @@
<rank>1</rank>
<parent>ConfigManagement</parent>
<definition>
<layout>DashboardLayoutOneCol</layout>
<title></title>
<cells>
<cell id="0">
<rank>0</rank>
<dashlets>
<dashlet id="1" xsi:type="DashletHeaderStatic">
<rank>0</rank>
<title>Menu:ConfigManagement:AllDevices</title>
<icon>itop-config-mgmt/images/server.png</icon>
<subtitle>Menu_ConfigManagement_AllDevices</subtitle>
</dashlet>
<dashlet id="2" xsi:type="DashletBadge">
<rank>3</rank>
<class>Server</class>
</dashlet>
<dashlet id="3" xsi:type="DashletBadge">
<rank>4</rank>
<class>NetworkDevice</class>
</dashlet>
</dashlets>
</cell>
<cell id="3">
<rank>3</rank>
<dashlets>
<dashlet id="4" xsi:type="DashletHeaderStatic">
<rank>0</rank>
<title>Menu:ConfigManagement:SWAndApps</title>
<icon>itop-config-mgmt/images/application.png</icon>
</dashlet>
<dashlet id="5" xsi:type="DashletBadge">
<rank>1</rank>
<class>Middleware</class>
</dashlet>
<dashlet id="6" xsi:type="DashletBadge">
<rank>2</rank>
<class>DBServer</class>
</dashlet>
<dashlet id="7" xsi:type="DashletBadge">
<rank>3</rank>
<class>WebServer</class>
</dashlet>
<dashlet id="8" xsi:type="DashletBadge">
<rank>4</rank>
<class>PCSoftware</class>
</dashlet>
<dashlet id="9" xsi:type="DashletBadge">
<rank>5</rank>
<class>OtherSoftware</class>
</dashlet>
<dashlet id="10" xsi:type="DashletBadge">
<rank>6</rank>
<class>MiddlewareInstance</class>
</dashlet>
<dashlet id="11" xsi:type="DashletBadge">
<rank>7</rank>
<class>DatabaseSchema</class>
</dashlet>
<dashlet id="12" xsi:type="DashletBadge">
<rank>8</rank>
<class>WebApplication</class>
</dashlet>
<dashlet id="13" xsi:type="DashletBadge">
<rank>9</rank>
<class>Patch</class>
</dashlet>
<dashlet id="14" xsi:type="DashletBadge">
<rank>10</rank>
<class>Licence</class>
</dashlet>
</dashlets>
</cell>
<cell id="4">
<rank>4</rank>
<dashlets>
<dashlet id="15" xsi:type="DashletHeaderStatic">
<rank>0</rank>
<title>Menu:ConfigManagement:Misc</title>
<icon>itop-config-mgmt/images/subnet.png</icon>
</dashlet>
<dashlet id="16" xsi:type="DashletBadge">
<rank>1</rank>
<class>NetworkInterface</class>
</dashlet>
<dashlet id="17" xsi:type="DashletBadge">
<rank>2</rank>
<class>Subnet</class>
</dashlet>
<dashlet id="vlan" xsi:type="DashletBadge">
<rank>2.1</rank>
<class>VLAN</class>
</dashlet>
<dashlet id="18" xsi:type="DashletBadge">
<rank>4</rank>
<class>BusinessProcess</class>
</dashlet>
<dashlet id="19" xsi:type="DashletBadge">
<rank>5</rank>
<class>ApplicationSolution</class>
</dashlet>
<dashlet id="20" xsi:type="DashletBadge">
<rank>6</rank>
<class>Group</class>
</dashlet>
</dashlets>
</cell>
</cells>
<layout>DashboardLayoutOneCol</layout>
<title/>
<cells>
<cell id="0">
<rank>0</rank>
<dashlets>
<dashlet id="1" xsi:type="DashletHeaderStatic">
<rank>0</rank>
<title>Menu:ConfigManagement:AllDevices</title>
<icon>itop-config-mgmt/images/server.png</icon>
<subtitle>Menu_ConfigManagement_AllDevices</subtitle>
</dashlet>
<dashlet id="2" xsi:type="DashletBadge">
<rank>3</rank>
<class>Server</class>
</dashlet>
<dashlet id="3" xsi:type="DashletBadge">
<rank>4</rank>
<class>NetworkDevice</class>
</dashlet>
</dashlets>
</cell>
<cell id="3">
<rank>3</rank>
<dashlets>
<dashlet id="4" xsi:type="DashletHeaderStatic">
<rank>0</rank>
<title>Menu:ConfigManagement:SWAndApps</title>
<icon>itop-config-mgmt/images/application.png</icon>
</dashlet>
<dashlet id="5" xsi:type="DashletBadge">
<rank>1</rank>
<class>Middleware</class>
</dashlet>
<dashlet id="6" xsi:type="DashletBadge">
<rank>2</rank>
<class>DBServer</class>
</dashlet>
<dashlet id="7" xsi:type="DashletBadge">
<rank>3</rank>
<class>WebServer</class>
</dashlet>
<dashlet id="8" xsi:type="DashletBadge">
<rank>4</rank>
<class>PCSoftware</class>
</dashlet>
<dashlet id="9" xsi:type="DashletBadge">
<rank>5</rank>
<class>OtherSoftware</class>
</dashlet>
<dashlet id="10" xsi:type="DashletBadge">
<rank>6</rank>
<class>MiddlewareInstance</class>
</dashlet>
<dashlet id="11" xsi:type="DashletBadge">
<rank>7</rank>
<class>DatabaseSchema</class>
</dashlet>
<dashlet id="12" xsi:type="DashletBadge">
<rank>8</rank>
<class>WebApplication</class>
</dashlet>
<dashlet id="13" xsi:type="DashletBadge">
<rank>9</rank>
<class>Patch</class>
</dashlet>
<dashlet id="14" xsi:type="DashletBadge">
<rank>10</rank>
<class>Licence</class>
</dashlet>
</dashlets>
</cell>
<cell id="4">
<rank>4</rank>
<dashlets>
<dashlet id="15" xsi:type="DashletHeaderStatic">
<rank>0</rank>
<title>Menu:ConfigManagement:Misc</title>
<icon>itop-config-mgmt/images/subnet.png</icon>
</dashlet>
<dashlet id="16" xsi:type="DashletBadge">
<rank>1</rank>
<class>NetworkInterface</class>
</dashlet>
<dashlet id="17" xsi:type="DashletBadge">
<rank>2</rank>
<class>Subnet</class>
</dashlet>
<dashlet id="vlan" xsi:type="DashletBadge">
<rank>2.1</rank>
<class>VLAN</class>
</dashlet>
<dashlet id="18" xsi:type="DashletBadge">
<rank>4</rank>
<class>BusinessProcess</class>
</dashlet>
<dashlet id="19" xsi:type="DashletBadge">
<rank>5</rank>
<class>ApplicationSolution</class>
</dashlet>
<dashlet id="20" xsi:type="DashletBadge">
<rank>6</rank>
<class>Group</class>
</dashlet>
</dashlets>
</cell>
</cells>
</definition>
</menu>
<menu id="Contact" xsi:type="DashboardMenuNode" _delta="define">
<rank>2</rank>
<parent>ConfigManagement</parent>
<definition>
<layout>DashboardLayoutOneCol</layout>
<title></title>
<cells>
<cell id="0">
<rank>0</rank>
<dashlets>
<dashlet id="1" xsi:type="DashletHeaderDynamic">
<rank>1</rank>
<title>Menu:Contact</title>
<icon>itop-config-mgmt/images/team.png</icon>
<subtitle>Menu:Contact:Count</subtitle>
<query>SELECT Contact</query>
<group_by>status</group_by>
<values>active,inactive</values>
</dashlet>
<dashlet id="2" xsi:type="DashletBadge">
<rank>2</rank>
<class>Team</class>
</dashlet>
<dashlet id="3" xsi:type="DashletBadge">
<rank>3</rank>
<class>Person</class>
</dashlet>
</dashlets>
</cell>
</cells>
</definition>
<layout>DashboardLayoutOneCol</layout>
<title/>
<cells>
<cell id="0">
<rank>0</rank>
<dashlets>
<dashlet id="1" xsi:type="DashletHeaderDynamic">
<rank>1</rank>
<title>Menu:Contact</title>
<icon>itop-config-mgmt/images/team.png</icon>
<subtitle>Menu:Contact:Count</subtitle>
<query>SELECT Contact</query>
<group_by>status</group_by>
<values>active,inactive</values>
</dashlet>
<dashlet id="2" xsi:type="DashletBadge">
<rank>2</rank>
<class>Team</class>
</dashlet>
<dashlet id="3" xsi:type="DashletBadge">
<rank>3</rank>
<class>Person</class>
</dashlet>
</dashlets>
</cell>
</cells>
</definition>
</menu>
<menu id="NewContact" xsi:type="NewObjectMenuNode" _delta="define">
<rank>3</rank>
@@ -8238,57 +8186,57 @@
<oql>SELECT Group</oql>
<do_search>1</do_search>
</menu>
<menu id="Typology" xsi:type="DashboardMenuNode" _delta="define">
<rank>80</rank>
<parent>Catalogs</parent>
<definition>
<layout>DashboardLayoutOneCol</layout>
<title>Menu:ConfigManagement:Typology</title>
<cells>
<cell id="0">
<rank>0</rank>
<dashlets>
<dashlet id="1" xsi:type="DashletHeaderStatic">
<rank>0</rank>
<title>Menu:ConfigManagement:Typology</title>
<icon>itop-config-mgmt/images/typology.png</icon>
</dashlet>
<dashlet id="2" xsi:type="DashletBadge">
<rank>1</rank>
<class>Brand</class>
</dashlet>
<dashlet id="3" xsi:type="DashletBadge">
<rank>2</rank>
<class>Model</class>
</dashlet>
<dashlet id="4" xsi:type="DashletBadge">
<rank>3</rank>
<class>OSFamily</class>
</dashlet>
<dashlet id="5" xsi:type="DashletBadge">
<rank>4</rank>
<class>OSVersion</class>
</dashlet>
<dashlet id="6" xsi:type="DashletBadge">
<rank>5</rank>
<class>IOSVersion</class>
</dashlet>
<dashlet id="7" xsi:type="DashletBadge">
<rank>6</rank>
<class>NetworkDeviceType</class>
</dashlet>
<dashlet id="9" xsi:type="DashletBadge">
<rank>8</rank>
<class>ContactType</class>
</dashlet>
<dashlet id="10" xsi:type="DashletBadge">
<rank>9</rank>
<class>DocumentType</class>
</dashlet>
</dashlets>
</cell>
</cells>
</definition>
</menu>
<menu id="Typology" xsi:type="DashboardMenuNode" _delta="define">
<rank>80</rank>
<parent>Catalogs</parent>
<definition>
<layout>DashboardLayoutOneCol</layout>
<title>Menu:ConfigManagement:Typology</title>
<cells>
<cell id="0">
<rank>0</rank>
<dashlets>
<dashlet id="1" xsi:type="DashletHeaderStatic">
<rank>0</rank>
<title>Menu:ConfigManagement:Typology</title>
<icon>itop-config-mgmt/images/typology.png</icon>
</dashlet>
<dashlet id="2" xsi:type="DashletBadge">
<rank>1</rank>
<class>Brand</class>
</dashlet>
<dashlet id="3" xsi:type="DashletBadge">
<rank>2</rank>
<class>Model</class>
</dashlet>
<dashlet id="4" xsi:type="DashletBadge">
<rank>3</rank>
<class>OSFamily</class>
</dashlet>
<dashlet id="5" xsi:type="DashletBadge">
<rank>4</rank>
<class>OSVersion</class>
</dashlet>
<dashlet id="6" xsi:type="DashletBadge">
<rank>5</rank>
<class>IOSVersion</class>
</dashlet>
<dashlet id="7" xsi:type="DashletBadge">
<rank>6</rank>
<class>NetworkDeviceType</class>
</dashlet>
<dashlet id="9" xsi:type="DashletBadge">
<rank>8</rank>
<class>ContactType</class>
</dashlet>
<dashlet id="10" xsi:type="DashletBadge">
<rank>9</rank>
<class>DocumentType</class>
</dashlet>
</dashlets>
</cell>
</cells>
</definition>
</menu>
</menus>
</itop_design>

View File

@@ -33,7 +33,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Relation:impacts/Description' => 'Elements impacted by',
'Relation:impacts/VerbUp' => 'Impact...',
'Relation:impacts/VerbDown' => 'Elements impacted by...',
'Relation:depends on/Description' => 'Elements this element depends on',
'Relation:depends on/Description' => 'Elements impacting',
'Relation:depends on/VerbUp' => 'Depends on...',
'Relation:depends on/VerbDown' => 'Impacts...',
));

View File

@@ -1817,7 +1817,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Relation:impacts/Description' => 'Eléments impactés par',
'Relation:impacts/VerbUp' => 'Impacte...',
'Relation:impacts/VerbDown' => 'Dépend de...',
'Relation:depends on/Description' => 'Eléments dont dépend cet élément',
'Relation:depends on/Description' => 'Eléments dont dépend',
'Relation:depends on/VerbUp' => 'Dépend de...',
'Relation:depends on/VerbDown' => 'Impacte...',
'Menu:ConfigManagement:Typology' => 'Configuration des typologies',

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-config-mgmt/2.0.0',
'itop-config-mgmt/2.1.0',
array(
// Identification
//

View File

@@ -0,0 +1,223 @@
<?php
// Copyright (C) 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/>
/**
* Monitor the backup
*
* @copyright Copyright (C) 2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once('../../approot.inc.php');
require_once(APPROOT.'application/application.inc.php');
require_once(APPROOT.'application/itopwebpage.class.inc.php');
require_once(APPROOT.'application/startup.inc.php');
require_once(APPROOT.'application/loginwebpage.class.inc.php');
function TestConfig($sContents, $oP)
{
try
{
ini_set('display_errors', 1);
ob_start();
eval('?'.'>'.trim($sContents));
$sNoise = trim(ob_get_contents());
ob_end_clean();
}
catch (Exception $e)
{
// well, never reach in case of parsing error :-(
throw new Exception('Error in configuration: '.$e->getMessage());
}
if (strlen($sNoise) > 0)
{
if (preg_match("/(Error|Parse error|Notice|Warning): (.+) in \S+ : eval\(\)'d code on line (\d+)/i", strip_tags($sNoise), $aMatches))
{
$sMessage = $aMatches[2];
$sLine = $aMatches[3];
$iLine = (int) $sLine;
// Highlight the line
$aLines = explode("\n", $sContents);
$iStart = 0;
for ($i = 0 ; $i < $iLine - 1; $i++) $iStart += strlen($aLines[$i]);
$iEnd = $iStart + strlen($aLines[$iLine - 1]);
$iTotalLines = count($aLines);
$oP->add_ready_script(
<<<EOF
setCursorPos($('#new_config')[0], $iStart, $iEnd);
$('#new_config')[0].focus();
var iScroll = Math.floor($('#new_config')[0].scrollHeight * ($iLine - 20) / $iTotalLines);
$('#new_config').scrollTop(iScroll);
EOF
);
$sMessage = Dict::Format('config-parse-error', $sMessage, $sLine);
throw new Exception($sMessage);
}
else
{
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
throw new Exception('Syntax error in configuration file: <tt>'.$sNoise.'</tt>');
}
}
}
/////////////////////////////////////////////////////////////////////
// Main program
//
LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin)
//$sOperation = utils::ReadParam('operation', 'menu');
//$oAppContext = new ApplicationContext();
$oP = new iTopWebPage(Dict::S('config-edit-title'));
$oP->set_base(utils::GetAbsoluteUrlAppRoot().'pages/');
try
{
$sOperation = utils::ReadParam('operation', '');
$oP->add("<h1>".Dict::S('config-edit-title')."</h1>");
if (MetaModel::GetConfig()->Get('demo_mode'))
{
$oP->add("<div class=\"header_message message_info\">Sorry, iTop is in <b>demonstration mode</b>: the configuration file cannot be edited.</div>");
}
else
{
$oP->add_style(
<<<EOF
textarea {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
height: 550px;
}
.current_line {
display: none;
margin-left: 20px;
}
EOF
);
$sConfigFile = APPROOT.'conf/'.utils::GetCurrentEnvironment().'/config-itop.php';
if ($sOperation == 'save')
{
$sConfig = utils::ReadParam('new_config', '', false, 'raw_data');
$sOrginalConfig = utils::ReadParam('prev_config', '', false, 'raw_data');
if ($sConfig == $sOrginalConfig)
{
$oP->add('<div id="save_result" class="header_message">'.Dict::S('config-no-change').'</div>');
}
else
{
try
{
TestConfig($sConfig, $oP); // throws exceptions
@chmod($sConfigFile, 0770); // Allow overwriting the file
file_put_contents($sConfigFile, $sConfig);
@chmod($sConfigFile, 0444); // Read-only
$oP->p('<div id="save_result" class="header_message message_ok">'.Dict::S('Successfully recorded.').'</div>');
$sOrginalConfig = str_replace("\r\n", "\n", file_get_contents($sConfigFile));
}
catch (Exception $e)
{
$oP->p('<div id="save_result" class="header_message message_error">'.$e->getMessage().'</div>');
}
}
}
else
{
$sConfig = str_replace("\r\n", "\n", file_get_contents($sConfigFile));
$sOrginalConfig = $sConfig;
}
$sConfigEscaped = htmlentities($sConfig, ENT_QUOTES, 'UTF-8');
$sOriginalConfigEscaped = htmlentities($sOrginalConfig, ENT_QUOTES, 'UTF-8');
$oP->p(Dict::S('config-edit-intro'));
$oP->add("<form method=\"POST\">");
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"save\">");
$oP->add("<input type=\"submit\" value=\"".Dict::S('config-apply')."\"><button onclick=\"ResetConfig(); return false;\">".Dict::S('config-cancel')."</button>");
$oP->add("<span class=\"current_line\">".Dict::Format('config-current-line', "<span class=\"line_number\"></span>")."</span>");
$oP->add("<input type=\"hidden\" id=\"prev_config\" name=\"prev_config\" value=\"$sOriginalConfigEscaped\">");
$oP->add("<textarea id =\"new_config\" name=\"new_config\" onkeyup=\"UpdateLineNumber();\" onmouseup=\"UpdateLineNumber();\">$sConfigEscaped</textarea>");
$oP->add("<input type=\"submit\" value=\"".Dict::S('config-apply')."\"><button onclick=\"ResetConfig(); return false;\">".Dict::S('config-cancel')."</button>");
$oP->add("<span class=\"current_line\">".Dict::Format('config-current-line', "<span class=\"line_number\"></span>")."</span>");
$oP->add("</form>");
$sConfirmCancel = addslashes(Dict::S('config-confirm-cancel'));
$oP->add_script(
<<<EOF
function UpdateLineNumber()
{
var oTextArea = $('#new_config')[0];
$('.line_number').html(oTextArea.value.substr(0, oTextArea.selectionStart).split("\\n").length);
$('.current_line').show();
}
function ResetConfig()
{
if ($('#new_config').val() != $('#prev_config').val())
{
if (confirm('$sConfirmCancel'))
{
$('#new_config').val($('#prev_config').val());
}
}
$('.current_line').hide();
$('#save_result').hide();
return false;
}
function setCursorPos(input, start, end) {
if (arguments.length < 3) end = start;
if ("selectionStart" in input) {
setTimeout(function() {
input.selectionStart = start;
input.selectionEnd = end;
}, 1);
}
else if (input.createTextRange) {
var rng = input.createTextRange();
rng.moveStart("character", start);
rng.collapse();
rng.moveEnd("character", end - start);
rng.select();
}
}
EOF
);
}
}
catch(Exception $e)
{
$oP->p('<b>'.$e->getMessage().'</b>');
}
$oP->output();

View File

@@ -0,0 +1,20 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 Combodo
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Menu:ConfigEditor' => 'Konfiguration',
'config-edit-title' => 'Konfigurations-Editor',
'config-edit-intro' => 'Seien sie bei der Bearbeitung der Konfigurationsdatei sehr vorsichtig. Normalerweise sollten es aureichen die Einträge im oberen Teil der Konfiguration zu bearbeiten (Bsp.: die globalen Konfigurations- und Moduleinstellungen).',
'config-apply' => 'Anwenden',
'config-cancel' => 'Zurücksetzen',
'config-confirm-cancel' => 'Ihre Änderungen werden nicht gespeichert.',
'config-no-change' => 'Keine Änderungen: Die Datei wurde nicht verändert.',
'config-parse-error' => 'Zeile %2$d: %1$s.<br/>Die Datei wurde nicht aktualisiert.',
'config-current-line' => 'Editiere Zeile: %1$s',
));

View File

@@ -0,0 +1,21 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 Combodo
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('EN US', 'English', 'English', array(
'Menu:ConfigEditor' => 'Configuration',
'config-edit-title' => 'Configuration File Editor',
'config-edit-intro' => 'Be very cautious when editing the configuration file. In particular, only the upper items (i.e. the global configuration and modules settings) should be edited.',
'config-apply' => 'Apply',
'config-cancel' => 'Reset',
'config-confirm-cancel' => 'Your changes will be lost.',
'config-no-change' => 'No change: the file has been left unchanged.',
'config-parse-error' => 'Line %2$d: %1$s.<br/>The file has NOT been updated.',
'config-current-line' => 'Editing line: %1$s',
));
?>

View File

@@ -0,0 +1,21 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2013 Combodo
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('FR FR', 'French', 'Français', array(
'Menu:ConfigEditor' => 'Configuration',
'config-edit-title' => 'Editeur du Fichier de Configuration',
'config-edit-intro' => 'Attention: une configuration incorrecte peut rendre iTop indisponible. En particulier, vous ne devriez éditer QUE les deux premiers éléments, à savoir la configuration globale et la configuration des modules.',
'config-save' => 'Appliquer',
'config-restore' => 'Réinitialiser',
'config-confirm-cancel' => 'Vos modifications seront perdues.',
'config-no-change' => 'Aucun changement : le fichier n\'a pas été altéré.',
'config-parse-error' => 'Ligne %2$d: %1$s.<br/>Le fichier n\'a PAS été modifié.',
'config-current-line' => 'Ligne en édition : %1$s',
));
?>

View File

@@ -0,0 +1,30 @@
<?php
// Copyright (C) 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 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
//require_once(APPROOT.'setup/setuputils.class.inc.php');
class ItopConfigEditor extends ModuleHandlerAPI
{
public static function OnMenuCreation()
{
if (UserRights::IsAdministrator())
{
$oAdminMenu = new MenuGroup('AdminTools', 80 /* fRank */);
new WebPageMenuNode('ConfigEditor', utils::GetAbsoluteUrlModulesRoot().'itop-config/config.php', $oAdminMenu->GetIndex(), 18 /* fRank */);
}
}
}

View File

@@ -0,0 +1,53 @@
<?php
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-config/1.0.2',
array(
// Identification
//
'label' => 'Configuration editor',
'category' => 'Application management',
// Setup
//
'dependencies' => array(
),
'mandatory' => true,
'visible' => false,
// Components
//
'datamodel' => array(
'main.itop-config.php',
//'model.itop-config.php',
),
'webservice' => array(
//'webservices.itop-config.php',
),
'dictionary' => array(
'en.dict.itop-config.php',
'fr.dict.itop-config.php',
//'de.dict.itop-config.php',
),
'data.struct' => array(
//'data.struct.itop-config.xml',
),
'data.sample' => array(
//'data.sample.itop-config.xml',
),
// Documentation
//
'doc.manual_setup' => '',
'doc.more_information' => '',
// Default settings
//
'settings' => array(
),
)
);
?>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<classes>
<class id="Rack" _delta="define">
<parent>PhysicalDevice</parent>
@@ -769,7 +769,7 @@
<target_class>Rack</target_class>
<is_null_allowed>false</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="rack_name" xsi:type="AttributeExternalField">
<extkey_attcode>rack_id</extkey_attcode>
@@ -784,8 +784,8 @@
<target_class>PowerConnection</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="powerstart_name" xsi:type="AttributeExternalField">
<extkey_attcode>powerstart_id</extkey_attcode>
@@ -958,25 +958,25 @@
</classes>
<menus>
<menu id="ConfigManagementOverview" xsi:type="DashboardMenuNode" _delta="must_exist">
<definition>
<cells>
<cell id="0" _delta="must_exist">
<dashlets>
<dashlet id="21" xsi:type="DashletBadge" _delta="define">
<rank>1</rank>
<class>Rack</class>
</dashlet>
<dashlet id="22" xsi:type="DashletBadge" _delta="define">
<rank>2</rank>
<class>Enclosure</class>
</dashlet>
<dashlet id="23" xsi:type="DashletBadge" _delta="define">
<rank>9</rank>
<class>PowerConnection</class>
</dashlet>
</dashlets>
</cell>
</cells>
<definition>
<cells>
<cell id="0" _delta="must_exist">
<dashlets>
<dashlet id="21" xsi:type="DashletBadge" _delta="define">
<rank>1</rank>
<class>Rack</class>
</dashlet>
<dashlet id="22" xsi:type="DashletBadge" _delta="define">
<rank>2</rank>
<class>Enclosure</class>
</dashlet>
<dashlet id="23" xsi:type="DashletBadge" _delta="define">
<rank>9</rank>
<class>PowerConnection</class>
</dashlet>
</dashlets>
</cell>
</cells>
</definition>
</menu>
</menus>

View File

@@ -18,7 +18,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-datacenter-mgmt/2.0.0',
'itop-datacenter-mgmt/2.1.0',
array(
// Identification
//

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<classes>
<class id="TelephonyCI" _delta="define">
<class id="TelephonyCI" _delta="define">
<parent>PhysicalDevice</parent>
<properties>
<category>bizmodel,searchable</category>
@@ -860,8 +860,8 @@
</field>
<field id="type" xsi:type="AttributeEnum">
<values>
<value>laptop</value>
<value>desktop</value>
<value id="laptop">laptop</value>
<value id="desktop">desktop</value>
</values>
<sql>type</sql>
<default_value/>
@@ -1242,7 +1242,6 @@
</list>
</presentation>
</class>
<class id="Peripheral" _delta="define">
<parent>PhysicalDevice</parent>
<properties>
@@ -1396,51 +1395,51 @@
</list>
</presentation>
</class>
</classes>
<menus>
<menu id="ConfigManagementOverview" xsi:type="DashboardMenuNode" _delta="must_exist">
<definition>
<cells>
<cell id="2" _delta="define">
<rank>2</rank>
<dashlets>
<dashlet id="25" xsi:type="DashletHeaderStatic">
<rank>0</rank>
<title>Menu:ConfigManagement:EndUsers</title>
<icon>itop-config-mgmt/images/team.png</icon>
</dashlet>
<dashlet id="26" xsi:type="DashletBadge">
<rank>1</rank>
<class>PC</class>
</dashlet>
<dashlet id="27" xsi:type="DashletBadge">
<rank>2</rank>
<class>Phone</class>
</dashlet>
<dashlet id="28" xsi:type="DashletBadge">
<rank>3</rank>
<class>IPPhone</class>
</dashlet>
<dashlet id="29" xsi:type="DashletBadge">
<rank>4</rank>
<class>MobilePhone</class>
</dashlet>
<dashlet id="30" xsi:type="DashletBadge">
<rank>5</rank>
<class>Tablet</class>
</dashlet>
<dashlet id="31" xsi:type="DashletBadge">
<rank>6</rank>
<class>Printer</class>
</dashlet>
<dashlet id="32" xsi:type="DashletBadge">
<rank>7</rank>
<class>Peripheral</class>
</dashlet>
</dashlets>
</cell>
</cells>
</definition>
</menu>
</menus>
</classes>
<menus>
<menu id="ConfigManagementOverview" xsi:type="DashboardMenuNode" _delta="must_exist">
<definition>
<cells>
<cell id="2" _delta="define">
<rank>2</rank>
<dashlets>
<dashlet id="25" xsi:type="DashletHeaderStatic">
<rank>0</rank>
<title>Menu:ConfigManagement:EndUsers</title>
<icon>itop-config-mgmt/images/team.png</icon>
</dashlet>
<dashlet id="26" xsi:type="DashletBadge">
<rank>1</rank>
<class>PC</class>
</dashlet>
<dashlet id="27" xsi:type="DashletBadge">
<rank>2</rank>
<class>Phone</class>
</dashlet>
<dashlet id="28" xsi:type="DashletBadge">
<rank>3</rank>
<class>IPPhone</class>
</dashlet>
<dashlet id="29" xsi:type="DashletBadge">
<rank>4</rank>
<class>MobilePhone</class>
</dashlet>
<dashlet id="30" xsi:type="DashletBadge">
<rank>5</rank>
<class>Tablet</class>
</dashlet>
<dashlet id="31" xsi:type="DashletBadge">
<rank>6</rank>
<class>Printer</class>
</dashlet>
<dashlet id="32" xsi:type="DashletBadge">
<rank>7</rank>
<class>Peripheral</class>
</dashlet>
</dashlets>
</cell>
</cells>
</definition>
</menu>
</menus>
</itop_design>

View File

@@ -25,7 +25,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-endusers-devices/2.0.0',
'itop-endusers-devices/2.1.0',
array(
// Identification
//

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-incident-mgmt-itil/2.0.0',
'itop-incident-mgmt-itil/2.1.0',
array(
// Identification
//

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<classes>
<class id="KnownError" _delta="define">
<parent>cmdbAbstractObject</parent>
@@ -81,10 +81,10 @@
</field>
<field id="domain" xsi:type="AttributeEnum">
<values>
<value>Network</value>
<value>Server</value>
<value>Application</value>
<value>Desktop</value>
<value id="Network">Network</value>
<value id="Server">Server</value>
<value id="Application">Application</value>
<value id="Desktop">Desktop</value>
</values>
<sql>domain</sql>
<default_value>Application</default_value>
@@ -573,7 +573,7 @@
<menu id="Problem:Shortcuts" xsi:type="TemplateMenuNode" _delta="define_if_not_exists">
<rank>5</rank>
<parent>ProblemManagement</parent>
<template_file />
<template_file/>
</menu>
<menu id="NewError" xsi:type="NewObjectMenuNode" _delta="define">
<rank>3</rank>

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-knownerror-mgmt/2.0.0',
'itop-knownerror-mgmt/2.1.0',
array(
// Identification
//

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<classes>
<class id="Problem" _delta="define">
<parent>Ticket</parent>
@@ -38,10 +38,10 @@
<fields>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>new</value>
<value>assigned</value>
<value>resolved</value>
<value>closed</value>
<value id="new">new</value>
<value id="assigned">assigned</value>
<value id="resolved">resolved</value>
<value id="closed">closed</value>
</values>
<sql>status</sql>
<default_value>new</default_value>
@@ -57,7 +57,7 @@
<target_class>Service</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="service_name" xsi:type="AttributeExternalField">
<extkey_attcode>service_id</extkey_attcode>
@@ -72,7 +72,7 @@
<target_class>ServiceSubcategory</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="servicesubcategory_name" xsi:type="AttributeExternalField">
<extkey_attcode>servicesubcategory_id</extkey_attcode>
@@ -85,9 +85,9 @@
</field>
<field id="impact" xsi:type="AttributeEnum">
<values>
<value>1</value>
<value>2</value>
<value>3</value>
<value id="1">1</value>
<value id="2">2</value>
<value id="3">3</value>
</values>
<sql>impact</sql>
<default_value>1</default_value>
@@ -96,10 +96,10 @@
</field>
<field id="urgency" xsi:type="AttributeEnum">
<values>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
<value id="1">1</value>
<value id="2">2</value>
<value id="3">3</value>
<value id="4">4</value>
</values>
<sql>urgency</sql>
<default_value>1</default_value>
@@ -108,10 +108,10 @@
</field>
<field id="priority" xsi:type="AttributeEnum">
<values>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
<value id="1">1</value>
<value id="2">2</value>
<value id="3">3</value>
<value id="4">4</value>
</values>
<sql>priority</sql>
<default_value>1</default_value>
@@ -169,7 +169,7 @@
<read_only/>
</attribute>
<attribute id="org_id">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="team_id">
<hidden/>
@@ -178,7 +178,7 @@
<hidden/>
</attribute>
<attribute id="description">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="start_date">
<read_only/>
@@ -199,10 +199,10 @@
<normal/>
</attribute>
<attribute id="impact">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="urgency">
<must_change/>
<must_prompt/>
</attribute>
<attribute id="priority">
<read_only/>
@@ -218,8 +218,7 @@
</attribute>
</flags>
<transitions>
<transition>
<stimulus>ev_assign</stimulus>
<transition id="ev_assign">
<target>assigned</target>
<actions>
<action>
@@ -230,13 +229,9 @@
</transitions>
</state>
<state id="assigned">
<inherit_flags_from>new</inherit_flags_from>
<flags>
<attribute id="ref">
<read_only/>
</attribute>
<attribute id="org_id">
<normal/>
</attribute>
<attribute id="title"/>
<attribute id="team_id">
<mandatory/>
<must_prompt/>
@@ -245,39 +240,22 @@
<mandatory/>
<must_prompt/>
</attribute>
<attribute id="title">
<normal/>
</attribute>
<attribute id="description">
<normal/>
</attribute>
<attribute id="start_date">
<read_only/>
</attribute>
<attribute id="last_update">
<read_only/>
</attribute>
<attribute id="close_date">
<hidden/>
</attribute>
<attribute id="priority">
<read_only/>
</attribute>
<attribute id="assignment_date">
<read_only/>
</attribute>
<attribute id="resolution_date">
<hidden/>
</attribute>
<attribute id="service_id"/>
<attribute id="servicesubcategory_id"/>
<attribute id="product"/>
<attribute id="impact"/>
<attribute id="urgency"/>
<attribute id="related_change_id"/>
</flags>
<transitions>
<transition>
<stimulus>ev_reassign</stimulus>
<transition id="ev_reassign">
<target>assigned</target>
<actions/>
</transition>
<transition>
<stimulus>ev_resolve</stimulus>
<transition id="ev_resolve">
<target>resolved</target>
<actions>
<action>
@@ -288,37 +266,11 @@
</transitions>
</state>
<state id="resolved">
<inherit_flags_from>assigned</inherit_flags_from>
<flags>
<attribute id="ref">
<read_only/>
</attribute>
<attribute id="org_id">
<read_only/>
</attribute>
<attribute id="team_id">
<read_only/>
</attribute>
<attribute id="agent_id">
<read_only/>
</attribute>
<attribute id="caller_id">
<read_only/>
</attribute>
<attribute id="title">
<read_only/>
</attribute>
<attribute id="description">
<read_only/>
</attribute>
<attribute id="start_date">
<read_only/>
</attribute>
<attribute id="last_update">
<read_only/>
</attribute>
<attribute id="close_date">
<hidden/>
</attribute>
<attribute id="service_id">
<mandatory/>
</attribute>
@@ -334,24 +286,28 @@
<attribute id="urgency">
<read_only/>
</attribute>
<attribute id="priority">
<attribute id="org_id">
<read_only/>
</attribute>
<attribute id="assignment_date">
<attribute id="team_id">
<read_only/>
</attribute>
<attribute id="resolution_date">
<hidden/>
<attribute id="agent_id">
<read_only/>
</attribute>
<attribute id="title">
<read_only/>
</attribute>
<attribute id="description">
<read_only/>
</attribute>
</flags>
<transitions>
<transition>
<stimulus>ev_reassign</stimulus>
<transition id="ev_reassign">
<target>assigned</target>
<actions/>
</transition>
<transition>
<stimulus>ev_close</stimulus>
<transition id="ev_close">
<target>closed</target>
<actions>
<action>
@@ -362,40 +318,17 @@
</transitions>
</state>
<state id="closed">
<inherit_flags_from>resolved</inherit_flags_from>
<flags>
<attribute id="ref">
<attribute id="private_log">
<read_only/>
</attribute>
<attribute id="org_id">
<read_only/>
</attribute>
<attribute id="team_id">
<read_only/>
</attribute>
<attribute id="agent_id">
<read_only/>
</attribute>
<attribute id="caller_id">
<read_only/>
</attribute>
<attribute id="title">
<read_only/>
</attribute>
<attribute id="description">
<read_only/>
</attribute>
<attribute id="start_date">
<read_only/>
</attribute>
<attribute id="last_update">
<attribute id="related_change_id">
<read_only/>
</attribute>
<attribute id="close_date">
<read_only/>
</attribute>
<attribute id="private_log">
<read_only/>
</attribute>
<attribute id="service_id">
<read_only/>
</attribute>
@@ -405,24 +338,6 @@
<attribute id="product">
<read_only/>
</attribute>
<attribute id="impact">
<read_only/>
</attribute>
<attribute id="urgency">
<read_only/>
</attribute>
<attribute id="priority">
<read_only/>
</attribute>
<attribute id="assignment_date">
<read_only/>
</attribute>
<attribute id="resolution_date">
<hidden/>
</attribute>
<attribute id="related_change_id">
<read_only/>
</attribute>
</flags>
<transitions/>
</state>

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-problem-mgmt/2.0.0',
'itop-problem-mgmt/2.1.0',
array(
// Identification
//

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<classes/>
<user_rights>
<groups>
@@ -8,17 +8,11 @@
<class id="Organization"/>
<class id="Location"/>
<class id="Contact"/>
<class id="Person"/>
<class id="Team"/>
<class id="lnkPersonToTeam"/>
</classes>
</group>
<group id="Documentation" _delta="define">
<classes>
<class id="Document"/>
<class id="WebDoc"/>
<class id="Note"/>
<class id="FileDoc"/>
</classes>
</group>
<group id="Configuration" _delta="define">
@@ -27,76 +21,30 @@
<class id="Subnet"/>
<class id="Patch"/>
<class id="Software"/>
<class id="Application"/>
<class id="DBServer"/>
<class id="lnkPatchToCI"/>
<class id="FunctionalCI"/>
<class id="SoftwareInstance"/>
<class id="DBServerInstance"/>
<class id="ApplicationInstance"/>
<class id="DatabaseInstance"/>
<class id="ApplicationSolution"/>
<class id="BusinessProcess"/>
<class id="ConnectableCI"/>
<class id="NetworkInterface"/>
<class id="Device"/>
<class id="PC"/>
<class id="MobileCI"/>
<class id="MobilePhone"/>
<class id="InfrastructureCI"/>
<class id="NetworkDevice"/>
<class id="Server"/>
<class id="Printer"/>
<class id="Group"/>
<class id="lnkDocumentToLicence"/>
<class id="Typology"/>
<class id="NASFileSystem"/>
<class id="LogicalVolume"/>
<class id="Tape"/>
<class id="VLAN"/>
<class id="lnkPhysicalInterfaceToVLAN"/>
<class id="lnkSubnetToVLAN"/>
<class id="lnkApplicationSolutionToBusinessProcess"/>
<class id="lnkApplicationSolutionToFunctionalCI"/>
<class id="lnkConnectableCIToNetworkDevice"/>
<class id="lnkContactToFunctionalCI"/>
<class id="lnkDocumentToFunctionalCI"/>
<class id="lnkDocumentToLicence"/>
<class id="lnkDocumentToPatch"/>
<class id="lnkDocumentToSoftware"/>
<class id="lnkFunctionalCIToOSPatch"/>
<class id="lnkGroupToCI"/>
<class id="lnkSanToDatacenterDevice"/>
<class id="lnkServerToVolume"/>
<class id="lnkVirtualDeviceToVolume"/>
<class id="lnkSoftwareInstanceToSoftwarePatch"/>
</classes>
</group>
<group id="Incident" _delta="define">
<classes>
<class id="lnkTicketToDoc"/>
<class id="lnkTicketToContact"/>
<class id="lnkTicketToCI"/>
<class id="Incident"/>
<class id="lnkTicketToIncident"/>
</classes>
</group>
<group id="Problem" _delta="define">
<classes>
<class id="lnkTicketToDoc"/>
<class id="lnkTicketToContact"/>
<class id="lnkTicketToCI"/>
<class id="Problem"/>
</classes>
</group>
<group id="Change" _delta="define">
<classes>
<class id="lnkTicketToDoc"/>
<class id="lnkTicketToContact"/>
<class id="lnkTicketToCI"/>
<class id="Change"/>
<class id="RoutineChange"/>
<class id="ApprovedChange"/>
<class id="NormalChange"/>
<class id="EmergencyChange"/>
</classes>
@@ -106,107 +54,44 @@
<class id="Contract"/>
<class id="ProviderContract"/>
<class id="CustomerContract"/>
<class id="lnkCustomerContractToProviderContract"/>
<class id="lnkCustomerContractToFunctionalCI"/>
<class id="ServiceFamily"/>
<class id="Service"/>
<class id="ServiceSubcategory"/>
<class id="SLA"/>
<class id="SLT"/>
<class id="DeliveryModel"/>
<class id="lnkContactToContract"/>
<class id="lnkContactToService"/>
<class id="lnkContractToDocument"/>
<class id="lnkCustomerContractToService"/>
<class id="lnkDeliveryModelToContact"/>
<class id="lnkDocumentToService"/>
<class id="lnkFunctionalCIToProviderContract"/>
<class id="lnkFunctionalCIToService"/>
<class id="lnkProviderContractToService"/>
<class id="lnkSLAToSLT"/>
</classes>
</group>
<group id="Call" _delta="define">
<group id="UserRequest" _delta="define">
<classes>
<class id="UserRequest"/>
<class id="lnkFunctionalCIToTicket"/>
<class id="lnkContactToTicket"/>
</classes>
</group>
<group id="KnownError" _delta="define">
<classes>
<class id="KnownError"/>
<class id="lnkErrorToFunctionalCI"/>
<class id="lnkDocumentToError"/>
<class id="FAQ"/>
<class id="FAQCategory"/>
<class id="FAQCategory"/>
</classes>
</group>
<group id="LnkTickets" _delta="define">
<group id="Ticketing" _delta="define">
<classes>
<class id="lnkFunctionalCIToTicket"/>
<class id="lnkContactToTicket"/>
<class id="WorkOrder"/>
</classes>
</group>
<group id="LnkServices" _delta="define">
<classes>
<class id="lnkContactToContract"/>
<class id="lnkContactToService"/>
<class id="lnkContractToDocument"/>
<class id="lnkCustomerContractToService"/>
<class id="lnkDeliveryModelToContact"/>
<class id="lnkDocumentToService"/>
<class id="lnkFunctionalCIToProviderContract"/>
<class id="lnkFunctionalCIToService"/>
<class id="lnkProviderContractToService"/>
<class id="lnkSLAToSLT"/>
</classes>
</group>
<group id="LnkKnownErrors" _delta="define">
<classes>
<class id="lnkErrorToFunctionalCI"/>
<class id="lnkDocumentToError"/>
</classes>
</group>
<group id="Portal user - write" _delta="define">
<classes>
<class id="FileDoc"/>
<class id="lnkTicketToDoc"/>
<class id="UserRequest"/>
</classes>
</group>
<group id="Portal user - delete" _delta="define">
<classes>
<class id="lnkTicketToDoc"/>
</classes>
</group>
<group id="class:UserRequest" _delta="define">
<classes>
<class id="UserRequest"/>
</classes>
</group>
<group id="class:Incident" _delta="define">
<classes>
<class id="Incident"/>
</classes>
</group>
<group id="class:Problem" _delta="define">
<classes>
<class id="Problem"/>
</classes>
</group>
<group id="class:NormalChange" _delta="define">
<group id="NormalChange" _delta="define">
<classes>
<class id="NormalChange"/>
</classes>
</group>
<group id="class:EmergencyChange" _delta="define">
<group id="EmergencyChange" _delta="define">
<classes>
<class id="EmergencyChange"/>
</classes>
</group>
<group id="class:RoutineChange" _delta="define">
<group id="RoutineChange" _delta="define">
<classes>
<class id="RoutineChange"/>
</classes>
@@ -219,29 +104,29 @@
<groups>
<group id="General">
<actions>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action xsi:type="delete">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="action:delete">allow</action>
</actions>
</group>
<group id="Documentation">
<actions>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action xsi:type="delete">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="action:delete">allow</action>
</actions>
</group>
<group id="Configuration">
<actions>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action xsi:type="delete">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="action:delete">allow</action>
</actions>
</group>
<group id="*">
<actions>
<action xsi:type="read">allow</action>
<action xsi:type="bulk read">allow</action>
<action id="action:read">allow</action>
<action id="action:bulk read">allow</action>
</actions>
</group>
</groups>
@@ -250,40 +135,32 @@
<name>Service Desk Agent</name>
<description>Person in charge of creating incident reports</description>
<groups>
<group id="Ticketing">
<actions>
<action id="action:delete">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="stimulus:ev_close">allow</action>
</actions>
</group>
<group id="Incident">
<actions>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="stimulus:ev_assign">allow</action>
</actions>
</group>
<group id="Call">
<group id="UserRequest">
<actions>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
</actions>
</group>
<group id="LnkTickets">
<actions>
<action xsi:type="delete">allow</action>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action id="ev_close" xsi:type="stimulus">allow</action>
</actions>
</group>
<group id="class:Incident">
<actions>
<action id="ev_assign" xsi:type="stimulus">allow</action>
</actions>
</group>
<group id="class:UserRequest">
<actions>
<action id="ev_assign" xsi:type="stimulus">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="stimulus:ev_assign">allow</action>
</actions>
</group>
<group id="*">
<actions>
<action xsi:type="read">allow</action>
<action xsi:type="bulk read">allow</action>
<action id="action:read">allow</action>
<action id="action:bulk read">allow</action>
</actions>
</group>
</groups>
@@ -292,49 +169,41 @@
<name>Support Agent</name>
<description>Person analyzing and solving the current incidents</description>
<groups>
<group id="Ticketing">
<actions>
<action id="action:delete">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="stimulus:ev_close">allow</action>
</actions>
</group>
<group id="Incident">
<actions>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="stimulus:ev_assign">allow</action>
<action id="stimulus:ev_reassign">allow</action>
<action id="stimulus:ev_resolve">allow</action>
<action id="stimulus:ev_close">allow</action>
<action id="stimulus:ev_pending">allow</action>
</actions>
</group>
<group id="Call">
<group id="UserRequest">
<actions>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
</actions>
</group>
<group id="LnkTickets">
<actions>
<action xsi:type="delete">allow</action>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action id="ev_close" xsi:type="stimulus">allow</action>
</actions>
</group>
<group id="class:Incident">
<actions>
<action id="ev_assign" xsi:type="stimulus">allow</action>
<action id="ev_reassign" xsi:type="stimulus">allow</action>
<action id="ev_resolve" xsi:type="stimulus">allow</action>
<action id="ev_close" xsi:type="stimulus">allow</action>
<action id="ev_pending" xsi:type="stimulus">allow</action>
</actions>
</group>
<group id="class:UserRequest">
<actions>
<action id="ev_assign" xsi:type="stimulus">allow</action>
<action id="ev_reassign" xsi:type="stimulus">allow</action>
<action id="ev_resolve" xsi:type="stimulus">allow</action>
<action id="ev_close" xsi:type="stimulus">allow</action>
<action id="ev_pending" xsi:type="stimulus">allow</action>
<action id="ev_wait_for_approval" xsi:type="stimulus">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="stimulus:ev_assign">allow</action>
<action id="stimulus:ev_reassign">allow</action>
<action id="stimulus:ev_resolve">allow</action>
<action id="stimulus:ev_close">allow</action>
<action id="stimulus:ev_pending">allow</action>
<action id="stimulus:ev_wait_for_approval">allow</action>
</actions>
</group>
<group id="*">
<actions>
<action xsi:type="read">allow</action>
<action xsi:type="bulk read">allow</action>
<action id="action:read">allow</action>
<action id="action:bulk read">allow</action>
</actions>
</group>
</groups>
@@ -343,44 +212,35 @@
<name>Problem Manager</name>
<description>Person analyzing and solving the current problems</description>
<groups>
<group id="Problem">
<actions>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
</actions>
</group>
<group id="KnownError">
<actions>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action xsi:type="delete">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="action:delete">allow</action>
</actions>
</group>
<group id="LnkTickets">
<group id="Ticketing">
<actions>
<action xsi:type="delete">allow</action>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action id="ev_close" xsi:type="stimulus">allow</action>
<action id="action:delete">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="stimulus:ev_close">allow</action>
</actions>
</group>
<group id="LnkKnownErrors">
<group id="Problem">
<actions>
<action xsi:type="delete">allow</action>
</actions>
</group>
<group id="class:Problem">
<actions>
<action id="ev_assign" xsi:type="stimulus">allow</action>
<action id="ev_reassign" xsi:type="stimulus">allow</action>
<action id="ev_resolve" xsi:type="stimulus">allow</action>
<action id="ev_close" xsi:type="stimulus">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="stimulus:ev_assign">allow</action>
<action id="stimulus:ev_reassign">allow</action>
<action id="stimulus:ev_resolve">allow</action>
<action id="stimulus:ev_close">allow</action>
</actions>
</group>
<group id="*">
<actions>
<action xsi:type="read">allow</action>
<action xsi:type="bulk read">allow</action>
<action id="action:read">allow</action>
<action id="action:bulk read">allow</action>
</actions>
</group>
</groups>
@@ -391,50 +251,50 @@
<groups>
<group id="Change">
<actions>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action id="ev_assign" xsi:type="stimulus">allow</action>
<action id="ev_plan" xsi:type="stimulus">allow</action>
<action id="ev_reopen" xsi:type="stimulus">allow</action>
<action id="ev_finish" xsi:type="stimulus">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="stimulus:ev_assign">allow</action>
<action id="stimulus:ev_plan">allow</action>
<action id="stimulus:ev_reopen">allow</action>
<action id="stimulus:ev_finish">allow</action>
</actions>
</group>
<group id="LnkTickets">
<group id="Ticketing">
<actions>
<action xsi:type="delete">allow</action>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action id="ev_close" xsi:type="stimulus">allow</action>
<action id="action:delete">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="stimulus:ev_close">allow</action>
</actions>
</group>
<group id="class:NormalChange">
<group id="NormalChange">
<actions>
<action id="ev_plan" xsi:type="stimulus">allow</action>
<action id="ev_replan" xsi:type="stimulus">allow</action>
<action id="ev_implement" xsi:type="stimulus">allow</action>
<action id="ev_monitor" xsi:type="stimulus">allow</action>
<action id="stimulus:ev_plan">allow</action>
<action id="stimulus:ev_replan">allow</action>
<action id="stimulus:ev_implement">allow</action>
<action id="stimulus:ev_monitor">allow</action>
</actions>
</group>
<group id="class:EmergencyChange">
<group id="EmergencyChange">
<actions>
<action id="ev_plan" xsi:type="stimulus">allow</action>
<action id="ev_replan" xsi:type="stimulus">allow</action>
<action id="ev_implement" xsi:type="stimulus">allow</action>
<action id="ev_monitor" xsi:type="stimulus">allow</action>
<action id="stimulus:ev_plan">allow</action>
<action id="stimulus:ev_replan">allow</action>
<action id="stimulus:ev_implement">allow</action>
<action id="stimulus:ev_monitor">allow</action>
</actions>
</group>
<group id="class:RoutineChange">
<group id="RoutineChange">
<actions>
<action id="ev_plan" xsi:type="stimulus">allow</action>
<action id="ev_replan" xsi:type="stimulus">allow</action>
<action id="ev_implement" xsi:type="stimulus">allow</action>
<action id="ev_monitor" xsi:type="stimulus">allow</action>
<action id="stimulus:ev_plan">allow</action>
<action id="stimulus:ev_replan">allow</action>
<action id="stimulus:ev_implement">allow</action>
<action id="stimulus:ev_monitor">allow</action>
</actions>
</group>
<group id="*">
<actions>
<action xsi:type="read">allow</action>
<action xsi:type="bulk read">allow</action>
<action id="action:read">allow</action>
<action id="action:bulk read">allow</action>
</actions>
</group>
</groups>
@@ -445,48 +305,48 @@
<groups>
<group id="Change">
<actions>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action id="ev_assign" xsi:type="stimulus">allow</action>
<action id="ev_reopen" xsi:type="stimulus">allow</action>
<action id="ev_finish" xsi:type="stimulus">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="stimulus:ev_assign">allow</action>
<action id="stimulus:ev_reopen">allow</action>
<action id="stimulus:ev_finish">allow</action>
</actions>
</group>
<group id="LnkTickets">
<group id="Ticketing">
<actions>
<action xsi:type="delete">allow</action>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action id="ev_close" xsi:type="stimulus">allow</action>
<action id="action:delete">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="stimulus:ev_close">allow</action>
</actions>
</group>
<group id="class:NormalChange">
<group id="NormalChange">
<actions>
<action id="ev_validate" xsi:type="stimulus">allow</action>
<action id="ev_reject" xsi:type="stimulus">allow</action>
<action id="ev_assign" xsi:type="stimulus">allow</action>
<action id="ev_reopen" xsi:type="stimulus">allow</action>
<action id="ev_finish" xsi:type="stimulus">allow</action>
<action id="stimulus:ev_validate">allow</action>
<action id="stimulus:ev_reject">allow</action>
<action id="stimulus:ev_assign">allow</action>
<action id="stimulus:ev_reopen">allow</action>
<action id="stimulus:ev_finish">allow</action>
</actions>
</group>
<group id="class:EmergencyChange">
<group id="EmergencyChange">
<actions>
<action id="ev_assign" xsi:type="stimulus">allow</action>
<action id="ev_reopen" xsi:type="stimulus">allow</action>
<action id="ev_finish" xsi:type="stimulus">allow</action>
<action id="stimulus:ev_assign">allow</action>
<action id="stimulus:ev_reopen">allow</action>
<action id="stimulus:ev_finish">allow</action>
</actions>
</group>
<group id="class:RoutineChange">
<group id="RoutineChange">
<actions>
<action id="ev_assign" xsi:type="stimulus">allow</action>
<action id="ev_reopen" xsi:type="stimulus">allow</action>
<action id="ev_finish" xsi:type="stimulus">allow</action>
<action id="stimulus:ev_assign">allow</action>
<action id="stimulus:ev_reopen">allow</action>
<action id="stimulus:ev_finish">allow</action>
</actions>
</group>
<group id="*">
<actions>
<action xsi:type="read">allow</action>
<action xsi:type="bulk read">allow</action>
<action id="action:read">allow</action>
<action id="action:bulk read">allow</action>
</actions>
</group>
</groups>
@@ -497,39 +357,39 @@
<groups>
<group id="Change">
<actions>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action id="ev_approve" xsi:type="stimulus">allow</action>
<action id="ev_reject" xsi:type="stimulus">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="stimulus:ev_approve">allow</action>
<action id="stimulus:ev_reject">allow</action>
</actions>
</group>
<group id="LnkTickets">
<group id="Ticketing">
<actions>
<action xsi:type="delete">allow</action>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action id="ev_close" xsi:type="stimulus">allow</action>
<action id="action:delete">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="stimulus:ev_close">allow</action>
</actions>
</group>
<group id="class:NormalChange">
<group id="NormalChange">
<actions>
<action id="ev_approve" xsi:type="stimulus">allow</action>
<action id="ev_notapprove" xsi:type="stimulus">allow</action>
<action id="stimulus:ev_approve">allow</action>
<action id="stimulus:ev_notapprove">allow</action>
</actions>
</group>
<group id="class:EmergencyChange">
<group id="EmergencyChange">
<actions>
<action id="ev_approve" xsi:type="stimulus">allow</action>
<action id="ev_notapprove" xsi:type="stimulus">allow</action>
<action id="stimulus:ev_approve">allow</action>
<action id="stimulus:ev_notapprove">allow</action>
</actions>
</group>
<group id="class:RoutineChange">
<group id="RoutineChange">
<actions/>
</group>
<group id="*">
<actions>
<action xsi:type="read">allow</action>
<action xsi:type="bulk read">allow</action>
<action id="action:read">allow</action>
<action id="action:bulk read">allow</action>
</actions>
</group>
</groups>
@@ -540,20 +400,15 @@
<groups>
<group id="Service">
<actions>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action xsi:type="delete">allow</action>
</actions>
</group>
<group id="LnkServices">
<actions>
<action xsi:type="delete">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="action:delete">allow</action>
</actions>
</group>
<group id="*">
<actions>
<action xsi:type="read">allow</action>
<action xsi:type="bulk read">allow</action>
<action id="action:read">allow</action>
<action id="action:bulk read">allow</action>
</actions>
</group>
</groups>
@@ -564,15 +419,15 @@
<groups>
<group id="Documentation">
<actions>
<action xsi:type="write">allow</action>
<action xsi:type="bulk write">allow</action>
<action xsi:type="delete">allow</action>
<action id="action:write">allow</action>
<action id="action:bulk write">allow</action>
<action id="action:delete">allow</action>
</actions>
</group>
<group id="*">
<actions>
<action xsi:type="read">allow</action>
<action xsi:type="bulk read">allow</action>
<action id="action:read">allow</action>
<action id="action:bulk read">allow</action>
</actions>
</group>
</groups>
@@ -581,26 +436,17 @@
<name>Portal user</name>
<description>Has the rights to access to the user portal. People having this profile will not be allowed to access the standard application, they will be automatically redirected to the user portal.</description>
<groups>
<group id="Portal user - write">
<group id="UserRequest">
<actions>
<action xsi:type="write">allow</action>
</actions>
</group>
<group id="Portal user - delete">
<actions>
<action xsi:type="delete">allow</action>
</actions>
</group>
<group id="class:UserRequest">
<actions>
<action id="ev_close" xsi:type="stimulus">allow</action>
<action id="ev_reopen" xsi:type="stimulus">allow</action>
<action id="action:write">allow</action>
<action id="stimulus:ev_close">allow</action>
<action id="stimulus:ev_reopen">allow</action>
</actions>
</group>
<group id="*">
<actions>
<action xsi:type="read">allow</action>
<action xsi:type="bulk read">allow</action>
<action id="action:read">allow</action>
<action id="action:bulk read">allow</action>
</actions>
</group>
</groups>

View File

@@ -19,7 +19,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-profiles-itil/1.0.0',
'itop-profiles-itil/2.1.0',
array(
// Identification
//

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-request-mgmt-itil/2.0.0',
'itop-request-mgmt-itil/2.1.0',
array(
// Identification
//

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-request-mgmt/2.0.0',
'itop-request-mgmt/2.1.0',
array(
// Identification
//

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<classes>
<class id="Organization">
<fields>
@@ -18,7 +18,7 @@
<presentation>
<details>
<items>
<item id="deliverymodel_id" _delta="define">
<item id="deliverymodel_id" _delta="define">
<rank>50</rank>
</item>
</items>
@@ -26,7 +26,7 @@
</presentation>
</class>
<class id="Ticket">
<fields>
<fields>
<field id="team_id" xsi:type="AttributeExternalKey" _delta="redefine">
<filter><![CDATA[SELECT Team AS t JOIN lnkDeliveryModelToContact AS l1 ON l1.contact_id=t.id JOIN DeliveryModel AS dm ON l1.deliverymodel_id=dm.id JOIN Organization AS o ON o.deliverymodel_id=dm.id WHERE o.id = :this->org_id]]></filter>
<dependencies>
@@ -36,7 +36,7 @@
<target_class>Team</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_AUTO</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
</fields>
</class>
@@ -123,8 +123,8 @@
</field>
<field id="cost_currency" xsi:type="AttributeEnum">
<values>
<value>dollars</value>
<value>euros</value>
<value id="dollars">dollars</value>
<value id="euros">euros</value>
</values>
<sql>cost_currency</sql>
<default_value/>
@@ -163,9 +163,9 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>production</value>
<value>implementation</value>
<value>obsolete</value>
<value id="production">production</value>
<value id="implementation">implementation</value>
<value id="obsolete">obsolete</value>
</values>
<sql>status</sql>
<default_value/>
@@ -1069,9 +1069,9 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>production</value>
<value>implementation</value>
<value>obsolete</value>
<value id="production">production</value>
<value id="implementation">implementation</value>
<value id="obsolete">obsolete</value>
</values>
<sql>status</sql>
<default_value/>
@@ -1383,8 +1383,8 @@
</field>
<field id="request_type" xsi:type="AttributeEnum">
<values>
<value>incident</value>
<value>service_request</value>
<value id="incident">incident</value>
<value id="service_request">service_request</value>
</values>
<sql>request_type</sql>
<default_value>incident</default_value>
@@ -1393,9 +1393,9 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>production</value>
<value>implementation</value>
<value>obsolete</value>
<value id="production">production</value>
<value id="implementation">implementation</value>
<value id="obsolete">obsolete</value>
</values>
<sql>status</sql>
<default_value/>
@@ -1610,10 +1610,10 @@
</field>
<field id="priority" xsi:type="AttributeEnum">
<values>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
<value id="1">1</value>
<value id="2">2</value>
<value id="3">3</value>
<value id="4">4</value>
</values>
<sql>priority</sql>
<default_value/>
@@ -1622,8 +1622,8 @@
</field>
<field id="request_type" xsi:type="AttributeEnum">
<values>
<value>service_request</value>
<value>incident</value>
<value id="service_request">service_request</value>
<value id="incident">incident</value>
</values>
<sql>request_type</sql>
<default_value/>
@@ -1632,8 +1632,8 @@
</field>
<field id="metric" xsi:type="AttributeEnum">
<values>
<value>tto</value>
<value>ttr</value>
<value id="tto">tto</value>
<value id="ttr">ttr</value>
</values>
<sql>metric</sql>
<default_value/>
@@ -1647,8 +1647,8 @@
</field>
<field id="unit" xsi:type="AttributeEnum">
<values>
<value>hours</value>
<value>minutes</value>
<value id="hours">hours</value>
<value id="minutes">minutes</value>
</values>
<sql>unit</sql>
<default_value/>

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-service-mgmt-provider/2.0.0',
'itop-service-mgmt-provider/2.1.0',
array(
// Identification
//

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<classes>
<class id="Organization">
<fields>
@@ -18,7 +18,7 @@
<presentation>
<details>
<items>
<item id="deliverymodel_id" _delta="define">
<item id="deliverymodel_id" _delta="define">
<rank>50</rank>
</item>
</items>
@@ -26,7 +26,7 @@
</presentation>
</class>
<class id="Ticket">
<fields>
<fields>
<field id="team_id" xsi:type="AttributeExternalKey" _delta="redefine">
<filter><![CDATA[SELECT Team AS t JOIN lnkDeliveryModelToContact AS l1 ON l1.contact_id=t.id JOIN DeliveryModel AS dm ON l1.deliverymodel_id=dm.id JOIN Organization AS o ON o.deliverymodel_id=dm.id WHERE o.id = :this->org_id]]></filter>
<dependencies>
@@ -36,7 +36,7 @@
<target_class>Team</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_AUTO</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
</fields>
</class>
@@ -123,8 +123,8 @@
</field>
<field id="cost_currency" xsi:type="AttributeEnum">
<values>
<value>dollars</value>
<value>euros</value>
<value id="dollars">dollars</value>
<value id="euros">euros</value>
</values>
<sql>cost_currency</sql>
<default_value/>
@@ -163,9 +163,9 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>production</value>
<value>implementation</value>
<value>obsolete</value>
<value id="production">production</value>
<value id="implementation">implementation</value>
<value id="obsolete">obsolete</value>
</values>
<sql>status</sql>
<default_value/>
@@ -1042,9 +1042,9 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>production</value>
<value>implementation</value>
<value>obsolete</value>
<value id="production">production</value>
<value id="implementation">implementation</value>
<value id="obsolete">obsolete</value>
</values>
<sql>status</sql>
<default_value/>
@@ -1378,8 +1378,8 @@
</field>
<field id="request_type" xsi:type="AttributeEnum">
<values>
<value>incident</value>
<value>service_request</value>
<value id="incident">incident</value>
<value id="service_request">service_request</value>
</values>
<sql>request_type</sql>
<default_value>incident</default_value>
@@ -1388,9 +1388,9 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>production</value>
<value>implementation</value>
<value>obsolete</value>
<value id="production">production</value>
<value id="implementation">implementation</value>
<value id="obsolete">obsolete</value>
</values>
<sql>status</sql>
<default_value/>
@@ -1605,10 +1605,10 @@
</field>
<field id="priority" xsi:type="AttributeEnum">
<values>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
<value id="1">1</value>
<value id="2">2</value>
<value id="3">3</value>
<value id="4">4</value>
</values>
<sql>priority</sql>
<default_value/>
@@ -1617,8 +1617,8 @@
</field>
<field id="request_type" xsi:type="AttributeEnum">
<values>
<value>service_request</value>
<value>incident</value>
<value id="service_request">service_request</value>
<value id="incident">incident</value>
</values>
<sql>request_type</sql>
<default_value/>
@@ -1627,8 +1627,8 @@
</field>
<field id="metric" xsi:type="AttributeEnum">
<values>
<value>tto</value>
<value>ttr</value>
<value id="tto">tto</value>
<value id="ttr">ttr</value>
</values>
<sql>metric</sql>
<default_value/>
@@ -1642,8 +1642,8 @@
</field>
<field id="unit" xsi:type="AttributeEnum">
<values>
<value>hours</value>
<value>minutes</value>
<value id="hours">hours</value>
<value id="minutes">minutes</value>
</values>
<sql>unit</sql>
<default_value/>

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-service-mgmt/2.0.0',
'itop-service-mgmt/2.1.0',
array(
// Identification
//

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<classes>
<class id="StorageSystem" _delta="define">
<parent>DatacenterDevice</parent>
@@ -1757,48 +1757,47 @@
</list>
</presentation>
</class>
</classes>
<menus>
</classes>
<menus>
<menu id="ConfigManagementOverview" xsi:type="DashboardMenuNode" _delta="must_exist">
<rank>1</rank>
<parent>ConfigManagement</parent>
<definition>
<layout>DashboardLayoutOneCol</layout>
<title></title>
<cells>
<cell id="0" _delta="must_exist">
<rank>0</rank>
<dashlets>
<dashlet id="33" xsi:type="DashletBadge" _delta="define">
<rank>5</rank>
<class>StorageSystem</class>
</dashlet>
<dashlet id="34" xsi:type="DashletBadge" _delta="define">
<rank>6</rank>
<class>SANSwitch</class>
</dashlet>
<dashlet id="35" xsi:type="DashletBadge" _delta="define">
<rank>7</rank>
<class>NAS</class>
</dashlet>
<dashlet id="36" xsi:type="DashletBadge" _delta="define">
<rank>8</rank>
<class>TapeLibrary</class>
</dashlet>
</dashlets>
</cell>
<cell id="4" _delta="must_exist">
<rank>4</rank>
<dashlets>
<dashlet id="37" xsi:type="DashletBadge" _delta="define">
<rank>3</rank>
<class>LogicalVolume</class>
</dashlet>
</dashlets>
</cell>
</cells>
</definition>
<layout>DashboardLayoutOneCol</layout>
<title/>
<cells>
<cell id="0" _delta="must_exist">
<rank>0</rank>
<dashlets>
<dashlet id="33" xsi:type="DashletBadge" _delta="define">
<rank>5</rank>
<class>StorageSystem</class>
</dashlet>
<dashlet id="34" xsi:type="DashletBadge" _delta="define">
<rank>6</rank>
<class>SANSwitch</class>
</dashlet>
<dashlet id="35" xsi:type="DashletBadge" _delta="define">
<rank>7</rank>
<class>NAS</class>
</dashlet>
<dashlet id="36" xsi:type="DashletBadge" _delta="define">
<rank>8</rank>
<class>TapeLibrary</class>
</dashlet>
</dashlets>
</cell>
<cell id="4" _delta="must_exist">
<rank>4</rank>
<dashlets>
<dashlet id="37" xsi:type="DashletBadge" _delta="define">
<rank>3</rank>
<class>LogicalVolume</class>
</dashlet>
</dashlets>
</cell>
</cells>
</definition>
</menu>
</menus>
</menus>
</itop_design>

View File

@@ -25,7 +25,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-storage-mgmt/2.0.0',
'itop-storage-mgmt/2.1.0',
array(
// Identification
//

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<constants>
<constant id="RESPONSE_TICKET_SLT_QUERY" xsi:type="string" _delta="define"><![CDATA[SELECT SLT AS slt JOIN lnkSLAToSLT AS l1 ON l1.slt_id=slt.id JOIN SLA AS sla ON l1.sla_id=sla.id JOIN lnkCustomerContractToService AS l2 ON l2.sla_id=sla.id JOIN CustomerContract AS sc ON l2.customercontract_id=sc.id WHERE slt.metric = :metric AND l2.service_id = :this->service_id AND sc.org_id = :this->org_id AND slt.request_type = :request_type AND slt.priority = :this->priority]]></constant>
<constant id="PORTAL_POWER_USER_PROFILE" xsi:type="string" _delta="define"><![CDATA[Portal power user]]></constant>
@@ -93,7 +93,7 @@
<target_class>Person</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_AUTO</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="agent_name" xsi:type="AttributeExternalField">
<extkey_attcode>agent_id</extkey_attcode>
@@ -213,7 +213,7 @@
}
]]></code>
</method>
</methods>
</methods>
<presentation>
<details>
<items>
@@ -529,7 +529,6 @@
</naming>
<display_template/>
<icon>images/workorder.png</icon>
<icon/>
<reconciliation>
<attributes>
<attribute id="name"/>
@@ -545,8 +544,8 @@
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>open</value>
<value>closed</value>
<value id="open">open</value>
<value id="closed">closed</value>
</values>
<sql>status</sql>
<default_value>open</default_value>
@@ -588,7 +587,7 @@
<target_class>Person</target_class>
<is_null_allowed>true</is_null_allowed>
<on_target_delete>DEL_MANUAL</on_target_delete>
<allow_target_creation>false</allow_target_creation>
<allow_target_creation>false</allow_target_creation>
</field>
<field id="agent_email" xsi:type="AttributeExternalField">
<extkey_attcode>agent_id</extkey_attcode>
@@ -619,8 +618,7 @@
<state id="open">
<flags/>
<transitions>
<transition>
<stimulus>ev_close</stimulus>
<transition id="ev_close">
<target>closed</target>
<actions/>
</transition>
@@ -637,6 +635,8 @@
<static>false</static>
<access>public</access>
<type>StimulusUserAction</type>
<arguments>
</arguments>
<code><![CDATA[ public function UpdateParentTicketLog()
{
$oLog = $this->Get('log');
@@ -741,4 +741,67 @@
</presentation>
</class>
</classes>
<meta>
<classes>
<class id="cmdbAbstractObject" _delta="define">
<methods>
<method id="Set">
<arguments>
<argument>
<label>Field Code</label>
<type>attcode</type>
<mandatory>true</mandatory>
<description>The field to set, in the current object</description>
</argument>
<argument>
<label>Value</label>
<type>string</type>
<mandatory>true</mandatory>
<description>The value to set</description>
</argument>
</arguments>
</method>
<method id="SetCurrentDate">
<arguments>
<argument>
<label>Field Code</label>
<type>attcode</type>
<mandatory>true</mandatory>
<description>The field, in the current object, to set to
the current date</description>
</argument>
</arguments>
</method>
<method id="Reset">
<arguments>
<argument>
<label>Field Code</label>
<type>attcode</type>
<mandatory>true</mandatory>
<description>The field, in the current object, to reset
to its default value</description>
</argument>
</arguments>
</method>
<method id="Copy">
<arguments>
<argument>
<label>Destination</label>
<type>attcode</type>
<mandatory>true</mandatory>
<description>The field, in the current object, to set</description>
</argument>
<argument>
<label>Source</label>
<type>attcode</type>
<mandatory>true</mandatory>
<description>The field, in the current object, to get
the value from</description>
</argument>
</arguments>
</method>
</methods>
</class>
</classes>
</meta>
</itop_design>

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__,
'itop-tickets/2.0.0',
'itop-tickets/2.1.0',
array(
// Identification
//

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<classes>
<class id="VirtualDevice" _delta="define">
<parent>FunctionalCI</parent>
@@ -30,10 +30,10 @@
<fields>
<field id="status" xsi:type="AttributeEnum">
<values>
<value>production</value>
<value>implementation</value>
<value>stock</value>
<value>obsolete</value>
<value id="production">production</value>
<value id="implementation">implementation</value>
<value id="stock">stock</value>
<value id="obsolete">obsolete</value>
</values>
<sql>status</sql>
<default_value>production</default_value>
@@ -967,35 +967,35 @@
</list>
</presentation>
</class>
</classes>
<menus>
<menu id="ConfigManagementOverview" xsi:type="DashboardMenuNode" _delta="must_exist">
<definition>
<cells>
<cell id="1" _delta="define">
<rank>1</rank>
<dashlets>
<dashlet id="38" xsi:type="DashletHeaderStatic">
<rank>0</rank>
<title>Menu:ConfigManagement:virtualization</title>
<icon>itop-virtualization-mgmt/images/hypervisor-sw.png</icon>
</dashlet>
<dashlet id="39" xsi:type="DashletBadge">
<rank>1</rank>
<class>Farm</class>
</dashlet>
<dashlet id="40" xsi:type="DashletBadge">
<rank>2</rank>
<class>Hypervisor</class>
</dashlet>
<dashlet id="41" xsi:type="DashletBadge">
<rank>3</rank>
<class>VirtualMachine</class>
</dashlet>
</dashlets>
</cell>
</cells>
</definition>
</menu>
</menus>
</classes>
<menus>
<menu id="ConfigManagementOverview" xsi:type="DashboardMenuNode" _delta="must_exist">
<definition>
<cells>
<cell id="1" _delta="define">
<rank>1</rank>
<dashlets>
<dashlet id="38" xsi:type="DashletHeaderStatic">
<rank>0</rank>
<title>Menu:ConfigManagement:virtualization</title>
<icon>itop-virtualization-mgmt/images/hypervisor-sw.png</icon>
</dashlet>
<dashlet id="39" xsi:type="DashletBadge">
<rank>1</rank>
<class>Farm</class>
</dashlet>
<dashlet id="40" xsi:type="DashletBadge">
<rank>2</rank>
<class>Hypervisor</class>
</dashlet>
<dashlet id="41" xsi:type="DashletBadge">
<rank>3</rank>
<class>VirtualMachine</class>
</dashlet>
</dashlets>
</cell>
</cells>
</definition>
</menu>
</menus>
</itop_design>

View File

@@ -16,7 +16,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-virtualization-mgmt/2.0.0',
'itop-virtualization-mgmt/2.1.0',
array(
// Identification
//

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1">
<menus>
<menu id="WelcomeMenu" xsi:type="MenuGroup" _delta="define">
<rank>10</rank>
@@ -9,7 +9,7 @@
<parent>WelcomeMenu</parent>
<definition>
<layout>DashboardLayoutOneCol</layout>
<title></title>
<title/>
<cells>
<cell id="0">
<rank>0</rank>

View File

@@ -3,7 +3,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-welcome-itil/2.0.0',
'itop-welcome-itil/2.1.0',
array(
// Identification
//

View File

@@ -1196,7 +1196,10 @@ When associated with a trigger, each action is given an "order" number, specifyi
'Class:ShortcutOQL/Attribute:auto_reload_sec+' => 'The minimum allowed is 5 seconds',
'UI:FillAllMandatoryFields' => 'Please fill all mandatory fields.',
'UI:ValueMustBeSet' => 'Please specify a value',
'UI:ValueMustBeChanged' => 'Please change the value',
'UI:ValueInvalidFormat' => 'Invalid format',
'UI:CSVImportConfirmTitle' => 'Please confirm the operation',
'UI:CSVImportConfirmMessage' => 'Are you sure you want to do this?',
'UI:CSVImportError_items' => 'Errors: %1$d',
@@ -1214,5 +1217,16 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:About:Support' => 'Support information',
'UI:About:Licenses' => 'Licenses',
'UI:About:Modules' => 'Installed modules',
'ExcelExporter:ExportMenu' => 'Excel Export...',
'ExcelExporter:ExportDialogTitle' => 'Excel Export',
'ExcelExporter:ExportButton' => 'Export',
'ExcelExporter:DownloadButton' => 'Download %1$s',
'ExcelExporter:RetrievingData' => 'Retrieving data...',
'ExcelExporter:BuildingExcelFile' => 'Building the Excel file...',
'ExcelExporter:Done' => 'Done.',
'ExcelExport:AutoDownload' => 'Start the download automatically when the export is ready',
'ExcelExport:PreparingExport' => 'Preparing the export...',
'ExcelExport:Statistics' => 'Statistics',
));
?>

View File

@@ -1036,6 +1036,9 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'Class:ShortcutOQL/Attribute:auto_reload_sec+' => 'Le minimum permis est de 5 secondes',
'UI:FillAllMandatoryFields' => 'Veuillez remplir tous les champs obligatoires.',
'UI:ValueMustBeSet' => 'Veuillez spécifier une valeur pour ce champ',
'UI:ValueMustBeChanged' => 'Veuillez modifier la valeur de ce champ',
'UI:ValueInvalidFormat' => 'Format invalide',
'UI:CSVImportConfirmTitle' => 'Veuillez confirmer cette opération',
'UI:CSVImportConfirmMessage' => 'Etes-vous sûr(e) de vouloir faire cela ?',
@@ -1054,5 +1057,16 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:About:Support' => 'Informations pour le support',
'UI:About:Licenses' => 'Licences',
'UI:About:Modules' => 'Modules installés',
'ExcelExporter:ExportMenu' => 'Exporter pour Excel...',
'ExcelExporter:ExportDialogTitle' => 'Export au format Excel',
'ExcelExporter:ExportButton' => 'Exporter',
'ExcelExporter:DownloadButton' => 'Télécharger %1$s',
'ExcelExporter:RetrievingData' => 'Récupération des données...',
'ExcelExporter:BuildingExcelFile' => 'Construction du fichier Excel...',
'ExcelExporter:Done' => 'Terminé.',
'ExcelExport:AutoDownload' => 'Téléchargement automatique dès que le fichier est prêt',
'ExcelExport:PreparingExport' => 'Préparation de l\'export...',
'ExcelExport:Statistics' => 'Statistiques',
));
?>

BIN
images/xlsx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -146,7 +146,7 @@ function CheckFields(sFormId, bDisplayAlert)
return (oFormErrors['err_'+sFormId] == 0); // If no error, submit the form
}
function ReportFieldValidationStatus(sFieldId, sFormId, bValid)
function ReportFieldValidationStatus(sFieldId, sFormId, bValid, sExplain)
{
if (bValid)
{
@@ -163,13 +163,21 @@ function ReportFieldValidationStatus(sFieldId, sFormId, bValid)
oFormErrors['input_'+sFormId] = sFieldId;
}
// Visual feedback
$('#v_'+sFieldId).html('<img src="../images/validation_error.png" style="vertical-align:middle"/>');
$('#v_'+sFieldId).html('<img src="../images/validation_error.png" style="vertical-align:middle" data-tooltip="'+sExplain+'"/>');
$('#v_'+sFieldId).tooltip({
items: 'span',
tooltipClass: 'form_field_error',
content: function() {
return $(this).find('img').attr('data-tooltip'); // As opposed to the default 'content' handler, do not escape the contents of 'title'
}
});
}
}
function ValidateField(sFieldId, sPattern, bMandatory, sFormId, nullValue)
function ValidateField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue)
{
var bValid = true;
var sExplain = '';
if ($('#'+sFieldId).attr('disabled'))
{
bValid = true; // disabled fields are not checked
@@ -185,6 +193,19 @@ function ValidateField(sFieldId, sPattern, bMandatory, sFormId, nullValue)
else if (bMandatory && (currentVal == nullValue))
{
bValid = false;
sExplain = Dict.S('UI:ValueMustBeSet');
}
else if ((originalValue != undefined) && (currentVal == originalValue))
{
bValid = false;
if (originalValue == nullValue)
{
sExplain = Dict.S('UI:ValueMustBeSet');
}
else
{
sExplain = Dict.S('UI:ValueMustBeChanged');
}
}
else if (currentVal == nullValue)
{
@@ -196,9 +217,10 @@ function ValidateField(sFieldId, sPattern, bMandatory, sFormId, nullValue)
re = new RegExp(sPattern);
//console.log('Validating field: '+sFieldId + ' current value: '+currentVal + ' pattern: '+sPattern );
bValid = re.test(currentVal);
sExplain = Dict.S('UI:ValueInvalidFormat');
}
}
ReportFieldValidationStatus(sFieldId, sFormId, bValid);
ReportFieldValidationStatus(sFieldId, sFormId, bValid, sExplain);
//console.log('Form: '+sFormId+' Validating field: '+sFieldId + ' current value: '+currentVal+' pattern: '+sPattern+' result: '+bValid );
return true; // Do not stop propagation ??
}
@@ -229,7 +251,7 @@ function ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue)
bValid = true;
}
ReportFieldValidationStatus(sFieldId, sFormId, bValid);
ReportFieldValidationStatus(sFieldId, sFormId, bValid, '');
setTimeout(function(){ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue);}, 500);
}
@@ -321,7 +343,7 @@ function ValidateCaseLogField(sFieldId, bMandatory, sFormId)
}
}
}
ReportFieldValidationStatus(sFieldId, sFormId, bValid);
ReportFieldValidationStatus(sFieldId, sFormId, bValid, '');
return bValid;
}
// Manage a 'duration' field

View File

@@ -139,7 +139,7 @@ $(function()
this.datatable.tableHover();
this.datatable.find('.selectList'+this.id).bind('change', function() { me._updateButtons(); });
},
_updateDlgSize: function()
_updateDlgPosition: function()
{
this.oDlg.dialog('option', { position: { my: "center", at: "center", of: window }});
},
@@ -171,7 +171,7 @@ $(function()
});
me.indicator.html('');
me.oButtons['create'].removeAttr('disabled');
me._updateDlgSize();
me._updateDlgPosition();
});
},
_selectToAdd: function()
@@ -197,20 +197,23 @@ $(function()
me.oDlg.find('form').removeAttr('onsubmit').bind('submit', function() { me._onSearchToAdd(); return false; } );
me.oDlg.find('button.cancel').unbind('click').click( function() { me.oDlg.dialog('close'); } );
me.oDlg.find('button.ok').unbind('click').click( function() { me._onDoAdd(); } );
$('#SearchFormToAdd_'+me.id).resize(function() { me._onSearchDlgUpdateSize(); });
me.oDlg.dialog({
title: me.options.labels['selection_title'],
modal: true,
width: 'auto',
height: 'auto',
width: $(window).width()*0.8,
height: $(window).height()*0.8,
maxHeight: $(window).height() - 50,
position: { my: "center", at: "center", of: window },
close: function() { me._onDlgClose(); }
close: function() { me._onDlgClose(); },
resizeStop: function() { me._onSearchDlgUpdateSize(); }
});
me.indicator.html('');
me.oButtons['add'].removeAttr('disabled');
me._onSearchToAdd();
me._updateDlgSize();
me._updateDlgPosition();
me._onSearchDlgUpdateSize();
});
},
_onSearchToAdd: function()
@@ -255,8 +258,8 @@ $(function()
me._onUpdateDlgButtons(c);
});
$('#SearchResultsToAdd_'+me.id).unblock();
me._onSearchDlgUpdateSize();
});
//alert("C'est parti mon kiki !");
return false; // Stay on the page, no submit
},
_getSelection: function(sName)
@@ -373,7 +376,7 @@ $(function()
me.oDlg.html(data);
me.oDlg.find('form').removeAttr('onsubmit').bind('submit', function() { me._onCreateRow(); return false; } );
me.oDlg.find('button.cancel').unbind('click').click( function() { me.oDlg.dialog('close'); } );
me._updateDlgSize();
me._updateDlgPosition();
});
},
_onCreateRow: function()
@@ -426,6 +429,13 @@ $(function()
this.oDlg.remove();
this.oDlg = null;
},
_onSearchDlgUpdateSize: function()
{
var searchHeight = $('#SearchFormToAdd_'+this.id).outerHeight();
var dlgHeight = this.oDlg.height();
$('.wizContainer', this.oDlg).height(dlgHeight - 20);
$('#SearchResultsToAdd_'+this.id).height(dlgHeight - 50 - searchHeight);
},
_deleteRow: function(oCheckbox)
{
var iObjKey = parseInt(oCheckbox.val(), 10); // Number in base 10

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