mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-15 08:24:10 +01:00
Compare commits
188 Commits
support/2.
...
support/2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89e98a4ad7 | ||
|
|
649a9833e9 | ||
|
|
b3c2396da8 | ||
|
|
5717ae1889 | ||
|
|
823b33b0f5 | ||
|
|
9561ae6292 | ||
|
|
e25a89dac1 | ||
|
|
cc7f3d46a6 | ||
|
|
95d6b640b1 | ||
|
|
4ee0c62701 | ||
|
|
15f521d470 | ||
|
|
86e6c8295f | ||
|
|
9f1338ee2a | ||
|
|
96c8ee5e4d | ||
|
|
8e863d4890 | ||
|
|
cd769d5e49 | ||
|
|
04677fc2c7 | ||
|
|
ed6a464d8b | ||
|
|
5794f7d1ca | ||
|
|
7d42aa48cd | ||
|
|
614dd21aa9 | ||
|
|
706e6c56ff | ||
|
|
f048c6cb1f | ||
|
|
1804903ee4 | ||
|
|
17a8a896dc | ||
|
|
ed85260bb3 | ||
|
|
3c7fac7504 | ||
|
|
93feb700e8 | ||
|
|
48456082a9 | ||
|
|
fbc0456496 | ||
|
|
0fc3ee12b1 | ||
|
|
ce5f8c93cd | ||
|
|
b38eb97323 | ||
|
|
e6eb32cd21 | ||
|
|
c2429f2d05 | ||
|
|
4e4f2b4db9 | ||
|
|
2d7f963316 | ||
|
|
546d181ea9 | ||
|
|
143cefe4e3 | ||
|
|
ece152173f | ||
|
|
b08de31b3c | ||
|
|
0f967a41df | ||
|
|
4c3bf70cc4 | ||
|
|
35dd3f9610 | ||
|
|
a7f7424e54 | ||
|
|
83e2974b10 | ||
|
|
715ba066d3 | ||
|
|
9502003ff4 | ||
|
|
57c827bb1a | ||
|
|
690ac9be75 | ||
|
|
4c3c31c44d | ||
|
|
3c9ace5b53 | ||
|
|
bd5268dc42 | ||
|
|
133b6d4d29 | ||
|
|
fba3990c61 | ||
|
|
53e997cfba | ||
|
|
e738ba35b7 | ||
|
|
0773455ebc | ||
|
|
cafc6a8baf | ||
|
|
48f222df0b | ||
|
|
88726a0634 | ||
|
|
0ac522fc4c | ||
|
|
aa97703b64 | ||
|
|
30af416394 | ||
|
|
1f2ad9ecdb | ||
|
|
d1f1889c42 | ||
|
|
ca7ee0f138 | ||
|
|
909729e9f1 | ||
|
|
a222ba998d | ||
|
|
aedddf9dcc | ||
|
|
74de0d33ab | ||
|
|
8d8510a412 | ||
|
|
4dd83a0eb6 | ||
|
|
671ee353e8 | ||
|
|
d714235d67 | ||
|
|
c4b039c9c6 | ||
|
|
29e751278e | ||
|
|
87ed5d4a05 | ||
|
|
c8a20d01da | ||
|
|
47add0eeea | ||
|
|
2a9f69d70e | ||
|
|
e90e570469 | ||
|
|
b0e56e5897 | ||
|
|
c96842d82c | ||
|
|
bc79aecd73 | ||
|
|
99755ad7e8 | ||
|
|
4867461f69 | ||
|
|
3d21eecbba | ||
|
|
b822cff269 | ||
|
|
3aa0b77751 | ||
|
|
f4b10d3e81 | ||
|
|
ca90f9b32a | ||
|
|
9ca051d9d0 | ||
|
|
291f05683c | ||
|
|
4889ed5ac2 | ||
|
|
3fa354d00d | ||
|
|
f62934829d | ||
|
|
dee7f7b7aa | ||
|
|
1d0bfa7c92 | ||
|
|
8922c435b4 | ||
|
|
ec1ec854fb | ||
|
|
dfc248b836 | ||
|
|
481d05d082 | ||
|
|
4e4d3cf3da | ||
|
|
5fc68557dc | ||
|
|
7482a52fd9 | ||
|
|
50ca6cdd0f | ||
|
|
e83e1262a5 | ||
|
|
51bd403638 | ||
|
|
c786e8308a | ||
|
|
a7d3a5a488 | ||
|
|
2c70c60d2b | ||
|
|
96f02d1557 | ||
|
|
de6ddffe65 | ||
|
|
2c59fb894f | ||
|
|
77cf399c72 | ||
|
|
19a2180c2b | ||
|
|
18b73de512 | ||
|
|
bb741c39f4 | ||
|
|
c73ef6ae72 | ||
|
|
5abd9c6dad | ||
|
|
a6db04bafd | ||
|
|
04f46f4798 | ||
|
|
5ecf6b9e9d | ||
|
|
0135573956 | ||
|
|
a59915e5ee | ||
|
|
ebb3767ee4 | ||
|
|
047166f002 | ||
|
|
f45c783396 | ||
|
|
72f516685e | ||
|
|
2b4400c55d | ||
|
|
1df87b6331 | ||
|
|
cd7490472e | ||
|
|
b65131e4f5 | ||
|
|
fac8604cc6 | ||
|
|
e25da2a7c4 | ||
|
|
6d9e7f690f | ||
|
|
9c62952743 | ||
|
|
03c4964072 | ||
|
|
711949414d | ||
|
|
055a87306e | ||
|
|
37ebb51a2b | ||
|
|
1f8d4d379f | ||
|
|
1300811007 | ||
|
|
fbdd0dfd57 | ||
|
|
bc79663a3e | ||
|
|
570e4f8589 | ||
|
|
472802e11b | ||
|
|
f794d0222e | ||
|
|
b42a43d47b | ||
|
|
110aace270 | ||
|
|
68dd0513c6 | ||
|
|
08e757a08a | ||
|
|
996c590793 | ||
|
|
c93cc3a5bf | ||
|
|
bdccf6ea72 | ||
|
|
ed60346ae6 | ||
|
|
4953ea7701 | ||
|
|
82a8a0bba7 | ||
|
|
55c818b6b7 | ||
|
|
372c0835f7 | ||
|
|
52a028301f | ||
|
|
61b88d2689 | ||
|
|
fa856c32cd | ||
|
|
6d693bbfc7 | ||
|
|
7117b751b3 | ||
|
|
c30a88c9ef | ||
|
|
c1085fc398 | ||
|
|
42ac871f8e | ||
|
|
b1a404d909 | ||
|
|
ba82031a59 | ||
|
|
c8568af43b | ||
|
|
7ed60f711c | ||
|
|
e831b1a486 | ||
|
|
b5db86472d | ||
|
|
ed9ba815ab | ||
|
|
7b47e8c480 | ||
|
|
d2cd758ecc | ||
|
|
d2ea4023c2 | ||
|
|
f0ee5112b9 | ||
|
|
ada70b97d4 | ||
|
|
a546b39301 | ||
|
|
8f2cd66bf6 | ||
|
|
868d0d1b19 | ||
|
|
7d619f278e | ||
|
|
fd9008a163 | ||
|
|
357ae4abb1 | ||
|
|
2108a74f87 |
@@ -271,6 +271,19 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Profile', $this->Get('userlogin'), $this->Get('profile'));
|
||||
}
|
||||
|
||||
public function CheckToDelete(&$oDeletionPlan)
|
||||
{
|
||||
if (MetaModel::GetConfig()->Get('demo_mode'))
|
||||
{
|
||||
// Users deletion is NOT allowed in demo mode
|
||||
$oDeletionPlan->AddToDelete($this, null);
|
||||
$oDeletionPlan->SetDeletionIssues($this, array('deletion not allowed in demo mode.'), true);
|
||||
$oDeletionPlan->ComputeResults();
|
||||
return false;
|
||||
}
|
||||
return parent::CheckToDelete($oDeletionPlan);
|
||||
}
|
||||
}
|
||||
|
||||
class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -781,8 +781,12 @@ class RestUtils
|
||||
$oSearch = new DBObjectSearch($sClass);
|
||||
foreach ($oCriteria as $sAttCode => $value)
|
||||
{
|
||||
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
||||
$oSearch->AddCondition($sAttCode, $realValue, '=');
|
||||
if (is_object($value) || is_array($value))
|
||||
{
|
||||
$value = json_encode($value);
|
||||
}
|
||||
$aCriteriaReport[] = "$sAttCode: $value ($realValue)";
|
||||
}
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
@@ -814,7 +818,7 @@ class RestUtils
|
||||
{
|
||||
if (is_object($key))
|
||||
{
|
||||
$res = self::FindObjectFromCriteria($sClass, $key);
|
||||
$res = static::FindObjectFromCriteria($sClass, $key);
|
||||
}
|
||||
elseif (is_numeric($key))
|
||||
{
|
||||
@@ -878,7 +882,7 @@ class RestUtils
|
||||
$oSearch = new DBObjectSearch($sClass);
|
||||
foreach ($key as $sAttCode => $value)
|
||||
{
|
||||
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
||||
$oSearch->AddCondition($sAttCode, $realValue, '=');
|
||||
}
|
||||
}
|
||||
@@ -922,7 +926,7 @@ class RestUtils
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef instanceof AttributeExternalKey)
|
||||
{
|
||||
$oExtKeyObject = self::FindObjectFromKey($oAttDef->GetTargetClass(), $value, true /* allow null */);
|
||||
$oExtKeyObject = static::FindObjectFromKey($oAttDef->GetTargetClass(), $value, true /* allow null */);
|
||||
$value = ($oExtKeyObject != null) ? $oExtKeyObject->GetKey() : 0;
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeLinkedSet)
|
||||
@@ -935,7 +939,7 @@ class RestUtils
|
||||
$aLinks = array();
|
||||
foreach($value as $oValues)
|
||||
{
|
||||
$oLnk = self::MakeObjectFromFields($sLnkClass, $oValues);
|
||||
$oLnk = static::MakeObjectFromFields($sLnkClass, $oValues);
|
||||
$aLinks[] = $oLnk;
|
||||
}
|
||||
$value = DBObjectSet::FromArray($sLnkClass, $aLinks);
|
||||
@@ -966,7 +970,7 @@ class RestUtils
|
||||
$oObject = MetaModel::NewObject($sClass);
|
||||
foreach ($aFields as $sAttCode => $value)
|
||||
{
|
||||
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
||||
try
|
||||
{
|
||||
$oObject->Set($sAttCode, $realValue);
|
||||
@@ -993,7 +997,7 @@ class RestUtils
|
||||
$sClass = get_class($oObject);
|
||||
foreach ($aFields as $sAttCode => $value)
|
||||
{
|
||||
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
||||
try
|
||||
{
|
||||
$oObject->Set($sAttCode, $realValue);
|
||||
|
||||
@@ -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> ";
|
||||
$iFieldSize = $oKeyAttDef->GetMaxSize();
|
||||
$iMaxComboLength = $oKeyAttDef->GetMaximumComboLength();
|
||||
$sHtml .= "<label>".MetaModel::GetFilterLabel($sKeyAttClass, $sKeyAttCode).":</label> ";
|
||||
$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
|
||||
{
|
||||
@@ -1512,7 +1522,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
{
|
||||
if (is_scalar($sValue))
|
||||
{
|
||||
$sHtml .= "<input type=\"hidden\" name=\"$sName\" value=\"$sValue\" />\n";
|
||||
$sHtml .= "<input type=\"hidden\" name=\"$sName\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
}
|
||||
}
|
||||
$sHtml .= "<input type=\"hidden\" name=\"class\" value=\"$sClassName\" />\n";
|
||||
@@ -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) ? json_encode($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";
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -91,7 +91,7 @@ abstract class Dashboard
|
||||
}
|
||||
if ($oAutoReloadInterval = $oAutoReloadNode->getElementsByTagName('interval')->item(0))
|
||||
{
|
||||
$this->iAutoReloadSec = max(5, (int)$oAutoReloadInterval->textContent);
|
||||
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$oAutoReloadInterval->textContent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ abstract class Dashboard
|
||||
$this->sLayoutClass = $aParams['layout_class'];
|
||||
$this->sTitle = $aParams['title'];
|
||||
$this->bAutoReload = $aParams['auto_reload'] == 'true';
|
||||
$this->iAutoReloadSec = max(5, (int) $aParams['auto_reload_sec']);
|
||||
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int) $aParams['auto_reload_sec']);
|
||||
|
||||
foreach($aParams['cells'] as $aCell)
|
||||
{
|
||||
@@ -300,7 +300,7 @@ abstract class Dashboard
|
||||
|
||||
public function SetAutoReloadInterval($iAutoReloadSec)
|
||||
{
|
||||
$this->iAutoReloadSec = max(5, (int)$iAutoReloadSec);
|
||||
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$iAutoReloadSec);
|
||||
}
|
||||
|
||||
public function AddDashlet($oDashlet)
|
||||
@@ -312,7 +312,7 @@ abstract class Dashboard
|
||||
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
||||
{
|
||||
$oPage->add('<h1>'.Dict::S($this->sTitle).'</h1>');
|
||||
$oPage->add('<h1>'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</h1>');
|
||||
$oLayout = new $this->sLayoutClass;
|
||||
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
|
||||
if (!$bEditMode)
|
||||
@@ -357,16 +357,17 @@ abstract class Dashboard
|
||||
$oField = new DesignerBooleanField('auto_reload', Dict::S('UI:DashboardEdit:AutoReload'), $this->bAutoReload);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerTextField('auto_reload_sec', Dict::S('UI:DashboardEdit:AutoReloadSec'), $this->iAutoReloadSec);
|
||||
$oField->SetValidationPattern('^$|^0*([5-9]|[1-9][0-9]+)$'); // Can be empty, or a number > 4
|
||||
$oField = new DesignerIntegerField('auto_reload_sec', Dict::S('UI:DashboardEdit:AutoReloadSec'), $this->iAutoReloadSec);
|
||||
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
|
||||
$oForm->AddField($oField);
|
||||
|
||||
|
||||
$this->SetFormParams($oForm);
|
||||
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
|
||||
|
||||
$oPage->add('</div>');
|
||||
|
||||
$sRateTitle = addslashes(Dict::S('UI:DashboardEdit:AutoReloadSec+'));
|
||||
$sRateTitle = addslashes(Dict::Format('UI:DashboardEdit:AutoReloadSec+', MetaModel::GetConfig()->Get('min_reload_interval')));
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
// Note: the title gets deleted by the validation mechanism
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
@@ -725,7 +733,8 @@ abstract class DashletGroupBy extends Dashlet
|
||||
if (is_subclass_of($sAttType, 'AttributeFriendlyName')) continue;
|
||||
if ($sAttType == 'AttributeExternalField') continue;
|
||||
if (is_subclass_of($sAttType, 'AttributeExternalField')) continue;
|
||||
|
||||
if ($sAttType == 'AttributeOneWayPassword') continue;
|
||||
|
||||
$sLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
|
||||
$aGroupBy[$sAttCode] = $sLabel;
|
||||
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -226,7 +226,7 @@ class DisplayBlock
|
||||
if (is_numeric($aExtraParams['auto_reload']) && ($aExtraParams['auto_reload'] > 0))
|
||||
{
|
||||
$bAutoReload = true;
|
||||
$iReloadInterval = max(5, $aExtraParams['auto_reload'])*1000;
|
||||
$iReloadInterval = max(MetaModel::GetConfig()->Get('min_reload_interval'), $aExtraParams['auto_reload'])*1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -393,7 +393,7 @@ class DisplayBlock
|
||||
{
|
||||
if (isset($aExtraParams['group_by_label']))
|
||||
{
|
||||
$oGroupByExp = Expression::FromOQL($aExtraParams['group_by']);
|
||||
$oGroupByExp = Expression::FromOQL($aExtraParams['group_by']);
|
||||
$sGroupByLabel = $aExtraParams['group_by_label'];
|
||||
}
|
||||
else
|
||||
@@ -404,6 +404,21 @@ class DisplayBlock
|
||||
$sGroupByLabel = MetaModel::GetLabel($this->m_oFilter->GetClass(), $aExtraParams['group_by']);
|
||||
}
|
||||
|
||||
// Security filtering
|
||||
$aFields = $oGroupByExp->ListRequiredFields();
|
||||
foreach($aFields as $sFieldAlias)
|
||||
{
|
||||
if (preg_match('/^([^.]+)\\.([^.]+)$/', $sFieldAlias, $aMatches))
|
||||
{
|
||||
$sFieldClass = $this->m_oFilter->GetClassName($aMatches[1]);
|
||||
$oAttDef = MetaModel::GetAttributeDef($sFieldClass, $aMatches[2]);
|
||||
if ($oAttDef instanceof AttributeOneWayPassword)
|
||||
{
|
||||
throw new Exception('Grouping on password fields is not supported.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||
@@ -765,6 +780,7 @@ class DisplayBlock
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.urlencode($this->m_oFilter->serialize());
|
||||
$sHtml .= '<h1>'.Dict::S(str_replace('_', ':', $sTitle)).'</h1>';
|
||||
$sHtml .= '<a class="summary" href="'.$sHyperlink.'">'.Dict::Format(str_replace('_', ':', $sLabel), $iCount).'</a>';
|
||||
$sHtml .= '<div style="clear:both;"></div>';
|
||||
break;
|
||||
|
||||
case 'csv':
|
||||
@@ -783,7 +799,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 +850,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':
|
||||
|
||||
536
application/excelexporter.class.inc.php
Normal file
536
application/excelexporter.class.inc.php
Normal 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';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -78,15 +84,14 @@ class DesignerForm
|
||||
|
||||
public function Render($oP, $bReturnHTML = false)
|
||||
{
|
||||
$sFormId = $this->GetFormId();
|
||||
if ($this->oParentForm == null)
|
||||
{
|
||||
$sFormId = $this->sFormId;
|
||||
$sReturn = '<form id="'.$sFormId.'">';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sReturn = '';
|
||||
$sFormId = $this->oParentForm->sFormId;
|
||||
}
|
||||
$sHiddenFields = '';
|
||||
foreach($this->aFieldSets as $sLabel => $aFields)
|
||||
@@ -102,7 +107,7 @@ class DesignerForm
|
||||
$aRow = $oField->Render($oP, $sFormId);
|
||||
if ($oField->IsVisible())
|
||||
{
|
||||
$sValidation = ' <span class="prop_apply">'.$this->GetValidationArea($oField->GetCode()).'</span>';
|
||||
$sValidation = ' <span class="prop_apply">'.$this->GetValidationArea($oField->GetFieldId()).'</span>';
|
||||
$sField = $aRow['value'].$sValidation;
|
||||
$aDetails[] = array('label' => $aRow['label'], 'value' => $sField);
|
||||
}
|
||||
@@ -140,6 +145,11 @@ class DesignerForm
|
||||
$oP->add($sReturn);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetFieldSets()
|
||||
{
|
||||
return $this->aFieldSets;
|
||||
}
|
||||
|
||||
public function SetSubmitParams($sSubmitToUrl, $aSubmitParams)
|
||||
{
|
||||
@@ -153,6 +163,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 +183,7 @@ class DesignerForm
|
||||
{
|
||||
return $this->sHierarchyPath;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to handle subforms hide/show
|
||||
*/
|
||||
@@ -191,24 +206,21 @@ class DesignerForm
|
||||
$sReturn = '';
|
||||
$sActionUrl = addslashes($this->sSubmitTo);
|
||||
$sJSSubmitParams = json_encode($this->aSubmitParams);
|
||||
$sFormId = $this->GetFormId();
|
||||
if ($this->oParentForm == null)
|
||||
{
|
||||
$sFormId = $this->sFormId;
|
||||
$sReturn = '<form id="'.$sFormId.'" onsubmit="return false;">';
|
||||
$sReturn .= '<table class="prop_table">';
|
||||
$sReturn .= '<thead><tr><th class="prop_header">'.Dict::S('UI:Form:Property').'</th><th class="prop_header">'.Dict::S('UI:Form:Value').'</th><th colspan="2" class="prop_header"> </th></tr></thead><tbody>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sFormId = $this->oParentForm->sFormId;
|
||||
}
|
||||
|
||||
$sHiddenFields = '';
|
||||
foreach($this->aFieldSets as $sLabel => $aFields)
|
||||
{
|
||||
$aDetails = array();
|
||||
if ($sLabel != '')
|
||||
{
|
||||
$sReturn .= '<tr><th colspan="4">'.$sLabel.'</th></tr>';
|
||||
$sReturn .= $this->StartRow().'<th colspan="4">'.$sLabel.'</th>'.$this->EndRow();
|
||||
}
|
||||
|
||||
|
||||
@@ -218,18 +230,42 @@ class DesignerForm
|
||||
if ($oField->IsVisible())
|
||||
{
|
||||
$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))
|
||||
$sValidation = $this->GetValidationArea($sFieldId, '<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>'.$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();
|
||||
|
||||
$sWidgetClass = $oField->GetWidgetClass();
|
||||
$sJSExtraParams = '';
|
||||
if (count($oField->GetWidgetExtraParams()) > 0)
|
||||
{
|
||||
$aExtraParams = array();
|
||||
foreach($oField->GetWidgetExtraParams() as $key=> $value)
|
||||
{
|
||||
$aExtraParams[] = "'$key': ".json_encode($value);
|
||||
}
|
||||
$sJSExtraParams = ', '.implode(', ', $aExtraParams);
|
||||
}
|
||||
$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').$sWidgetClass({parent_selector: $sNotifyParentSelectorJS, field_id: '$sFieldId', equals: $sHandlerEquals, get_field_value: $sHandlerGetValue, auto_apply: $sAutoApply, value: '', submit_to: '$sActionUrl', submit_parameters: $sJSSubmitParams $sJSExtraParams });
|
||||
EOF
|
||||
);
|
||||
}
|
||||
@@ -247,7 +283,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 +323,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,13 +359,21 @@ EOF
|
||||
<<<EOF
|
||||
$('#$sDialogId').dialog({
|
||||
height: 'auto',
|
||||
width: 500,
|
||||
width: $iDialogWidth,
|
||||
modal: true,
|
||||
title: '$sDialogTitle',
|
||||
buttons: [
|
||||
{ text: "$sOkButtonLabel", click: function() {
|
||||
var oForm = $(this).closest('.ui-dialog').find('form');
|
||||
oForm.submit();
|
||||
if (AnimateDlgButtons)
|
||||
{
|
||||
sFormId = oForm.attr('id');
|
||||
if (oFormValidation[sFormId].length == 0)
|
||||
{
|
||||
AnimateDlgButtons(this);
|
||||
}
|
||||
}
|
||||
} },
|
||||
{ text: "$sCancelButtonLabel", click: function() { KillAllMenus(); $(this).dialog( "close" ); $(this).remove(); } },
|
||||
],
|
||||
@@ -344,9 +405,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,11 +467,39 @@ 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;
|
||||
}
|
||||
|
||||
public function GetFormId()
|
||||
{
|
||||
if ($this->oParentForm)
|
||||
{
|
||||
$this->oParentForm->GetFormId();
|
||||
}
|
||||
return $this->sFormId;
|
||||
}
|
||||
|
||||
public function SetDisplayed($bDisplayed)
|
||||
{
|
||||
$this->bDisplayed = $bDisplayed;
|
||||
@@ -420,27 +529,42 @@ 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 = '')
|
||||
public function GetValidationArea($sId, $sContent = '')
|
||||
{
|
||||
return "<span style=\"display:inline-block;width:20px;\" id=\"v_{$this->sFormPrefix}attr_$sCode\"><span class=\"ui-icon ui-icon-alert\"></span>$sContent</span>";
|
||||
return "<span style=\"display:inline-block;width:20px;\" id=\"v_{$sId}\"><span class=\"ui-icon ui-icon-alert\"></span>$sContent</span>";
|
||||
}
|
||||
public function GetAsyncActionClass()
|
||||
{
|
||||
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 +580,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)
|
||||
{
|
||||
@@ -556,6 +685,7 @@ class DesignerFormField
|
||||
protected $bAutoApply;
|
||||
protected $aCSSClasses;
|
||||
protected $bDisplayed;
|
||||
protected $aWidgetExtraParams;
|
||||
|
||||
public function __construct($sCode, $sLabel, $defaultValue)
|
||||
{
|
||||
@@ -567,6 +697,7 @@ class DesignerFormField
|
||||
$this->bAutoApply = false;
|
||||
$this->aCSSClasses = array();
|
||||
$this->bDisplayed = true;
|
||||
$this->aWidgetExtraParams = array();
|
||||
}
|
||||
|
||||
public function GetCode()
|
||||
@@ -615,6 +746,21 @@ class DesignerFormField
|
||||
return $this->bDisplayed;
|
||||
}
|
||||
|
||||
public function GetFieldId()
|
||||
{
|
||||
return $this->oForm->GetFieldId($this->sCode);
|
||||
}
|
||||
|
||||
public function GetWidgetClass()
|
||||
{
|
||||
return 'property_field';
|
||||
}
|
||||
|
||||
public function GetWidgetExtraParams()
|
||||
{
|
||||
return $this->aWidgetExtraParams;
|
||||
}
|
||||
|
||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||
{
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
@@ -658,6 +804,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 +850,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 +899,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())
|
||||
{
|
||||
@@ -744,7 +921,7 @@ class DesignerTextField extends DesignerFormField
|
||||
$sMandatory = $this->bMandatory ? 'true' : 'false';
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', '$sFormId', $sForbiddenValues, '$sExplainForbiddenValues'); } );
|
||||
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', $(this).closest('form').attr('id'), $sForbiddenValues, '$sExplainForbiddenValues'); } );
|
||||
{
|
||||
var myTimer = null;
|
||||
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
|
||||
@@ -798,7 +975,7 @@ class DesignerLongTextField extends DesignerTextField
|
||||
$sReadOnly = $this->IsReadOnly() ? 'readonly' : '';
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', '$sFormId', $sForbiddenValues, '$sExplainForbiddenValues'); } );
|
||||
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', $(this).closest('form').attr('id'), $sForbiddenValues, '$sExplainForbiddenValues'); } );
|
||||
{
|
||||
var myTimer = null;
|
||||
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
|
||||
@@ -814,11 +991,80 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
class DesignerIntegerField extends DesignerFormField
|
||||
{
|
||||
protected $iMin; // Lower boundary, inclusive
|
||||
protected $iMax; // Higher boundary, inclusive
|
||||
|
||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||
{
|
||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||
$this->iMin = 0; // Positive integer is the default
|
||||
$this->iMax = null;
|
||||
}
|
||||
|
||||
public function SetBoundaries($iMin = null, $iMax = null)
|
||||
{
|
||||
$this->iMin = $iMin;
|
||||
$this->iMax = $iMax;
|
||||
}
|
||||
|
||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||
{
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
if ($this->IsReadOnly())
|
||||
{
|
||||
$sHtmlValue = "<span>".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\"/></span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sMin = json_encode($this->iMin);
|
||||
$sMax = json_encode($this->iMax);
|
||||
$sMandatory = $this->bMandatory ? 'true' : 'false';
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').bind('change keyup validate', function() { ValidateInteger('$sId', $sMandatory, $(this).closest('form').attr('id'), $sMin, $sMax); } );
|
||||
{
|
||||
var myTimer = null;
|
||||
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$sCSSClasses = '';
|
||||
if (count($this->aCSSClasses) > 0)
|
||||
{
|
||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||
}
|
||||
$sHtmlValue = "<input type=\"text\" $sCSSClasses id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">";
|
||||
}
|
||||
return array('label' => $this->sLabel, 'value' => $sHtmlValue);
|
||||
}
|
||||
|
||||
public function ReadParam(&$aValues)
|
||||
{
|
||||
parent::ReadParam($aValues);
|
||||
|
||||
if (!is_null($this->iMin) && ($aValues[$this->sCode] < $this->iMin))
|
||||
{
|
||||
// Reject the value...
|
||||
$aValues[$this->sCode] = $this->defaultValue;
|
||||
}
|
||||
if (!is_null($this->iMax) && ($aValues[$this->sCode] > $this->iMax))
|
||||
{
|
||||
// Reject the value...
|
||||
$aValues[$this->sCode] = $this->defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DesignerComboField extends DesignerFormField
|
||||
{
|
||||
protected $aAllowedValues;
|
||||
protected $bMultipleSelection;
|
||||
protected $bOtherChoices;
|
||||
protected $sNullLabel;
|
||||
|
||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||
{
|
||||
@@ -826,6 +1072,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 +1091,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 +1146,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)
|
||||
{
|
||||
@@ -916,7 +1172,7 @@ class DesignerComboField extends DesignerFormField
|
||||
}
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').bind('change validate', function() { ValidateWithPattern('$sId', $sMandatory, '', '$sFormId', null, null); } );
|
||||
$('#$sId').bind('change validate', function() { ValidateWithPattern('$sId', $sMandatory, '', $(this).closest('form').attr('id'), null, null); } );
|
||||
EOF
|
||||
);
|
||||
}
|
||||
@@ -993,7 +1249,6 @@ class DesignerBooleanField extends DesignerFormField
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DesignerHiddenField extends DesignerFormField
|
||||
{
|
||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||
@@ -1055,14 +1310,19 @@ class DesignerIconSelectionField extends DesignerFormField
|
||||
$sPostUploadTo = ($this->sUploadUrl == null) ? 'null' : "'{$this->sUploadUrl}'";
|
||||
if (!$this->IsReadOnly())
|
||||
{
|
||||
$sValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$this->defaultValue}\"/>";
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').icon_select({current_idx: $idx, items: $sJSItems, post_upload_to: $sPostUploadTo});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sValue = '<img src="'.$this->MakeFileUrl($this->defaultValue).'" />';
|
||||
}
|
||||
$sReadOnly = $this->IsReadOnly() ? 'disabled' : '';
|
||||
return array('label' =>$this->sLabel, 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$this->defaultValue}\"/>");
|
||||
return array('label' => $this->sLabel, 'value' => $sValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1175,6 +1435,11 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
$this->aSubForms = array();
|
||||
}
|
||||
|
||||
public function GetWidgetClass()
|
||||
{
|
||||
return 'selector_property_field';
|
||||
}
|
||||
|
||||
public function AddSubForm($oSubForm, $sLabel, $sValue)
|
||||
{
|
||||
$idx = count($this->aSubForms);
|
||||
@@ -1192,6 +1457,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 +1472,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 +1488,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 +1521,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 +1544,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 +1557,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();
|
||||
$this->aWidgetExtraParams['data_selector'] = $sSelector;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1330,6 +1581,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 +1651,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);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -305,16 +305,20 @@ class LoginWebPage extends NiceWebPage
|
||||
{
|
||||
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n");
|
||||
}
|
||||
elseif ($oUser->Get('reset_pwd_token') != $sToken)
|
||||
{
|
||||
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-EnterPassword', $oUser->GetFriendlyName())."</p>\n");
|
||||
|
||||
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch');
|
||||
$this->add_script(
|
||||
$oEncryptedToken = $oUser->Get('reset_pwd_token');
|
||||
|
||||
if (!$oEncryptedToken->CheckPassword($sToken))
|
||||
{
|
||||
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-EnterPassword', $oUser->GetFriendlyName())."</p>\n");
|
||||
|
||||
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch');
|
||||
$this->add_script(
|
||||
<<<EOF
|
||||
function DoCheckPwd()
|
||||
{
|
||||
@@ -326,18 +330,19 @@ function DoCheckPwd()
|
||||
return true;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"new_pwd\">".Dict::S('UI:Login:NewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"new_pwd\" name=\"new_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"retype_new_pwd\">".Dict::S('UI:Login:RetypeNewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"retype_new_pwd\" name=\"retype_new_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></span></td></tr>\n");
|
||||
$this->add("</table>\n");
|
||||
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"do_reset_pwd\" />\n");
|
||||
$this->add("<input type=\"hidden\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" />\n");
|
||||
$this->add("<input type=\"hidden\" name=\"token\" value=\"".htmlentities($sToken, ENT_QUOTES, 'UTF-8')."\" />\n");
|
||||
$this->add("</form>\n");
|
||||
$this->add("</div\n");
|
||||
);
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"new_pwd\">".Dict::S('UI:Login:NewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"new_pwd\" name=\"new_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"retype_new_pwd\">".Dict::S('UI:Login:RetypeNewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"retype_new_pwd\" name=\"retype_new_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><span class=\"btn_border\"><input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></span></td></tr>\n");
|
||||
$this->add("</table>\n");
|
||||
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"do_reset_pwd\" />\n");
|
||||
$this->add("<input type=\"hidden\" name=\"auth_user\" value=\"".htmlentities($sAuthUser, ENT_QUOTES, 'UTF-8')."\" />\n");
|
||||
$this->add("<input type=\"hidden\" name=\"token\" value=\"".htmlentities($sToken, ENT_QUOTES, 'UTF-8')."\" />\n");
|
||||
$this->add("</form>\n");
|
||||
$this->add("</div\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,21 +362,25 @@ EOF
|
||||
{
|
||||
$this->add("<p>".Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)."</p>\n");
|
||||
}
|
||||
elseif ($oUser->Get('reset_pwd_token') != $sToken)
|
||||
{
|
||||
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Trash the token and change the password
|
||||
$oUser->Set('reset_pwd_token', '');
|
||||
$oUser->SetPassword($sNewPwd); // Does record the change into the DB
|
||||
|
||||
$this->add("<p>".Dict::S('UI:ResetPwd-Ready')."</p>");
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
$this->add("<p><a href=\"$sUrl\">".Dict::S('UI:ResetPwd-Login')."</a></p>");
|
||||
$oEncryptedPassword = $oUser->Get('reset_pwd_token');
|
||||
if (!$oEncryptedPassword->CheckPassword($sToken))
|
||||
{
|
||||
$this->add("<p>".Dict::S('UI:ResetPwd-Error-InvalidToken')."</p>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Trash the token and change the password
|
||||
$oUser->Set('reset_pwd_token', '');
|
||||
$oUser->SetPassword($sNewPwd); // Does record the change into the DB
|
||||
|
||||
$this->add("<p>".Dict::S('UI:ResetPwd-Ready')."</p>");
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
$this->add("<p><a href=\"$sUrl\">".Dict::S('UI:ResetPwd-Login')."</a></p>");
|
||||
}
|
||||
$this->add("</div\n");
|
||||
}
|
||||
$this->add("</div\n");
|
||||
}
|
||||
|
||||
public function DisplayChangePwdForm($bFailedLogin = false)
|
||||
|
||||
@@ -90,6 +90,8 @@ class PortalWebPage extends NiceWebPage
|
||||
$this->add_linked_script("../js/jquery.qtip-1.0.min.js");
|
||||
$this->add_linked_script('../js/jquery.multiselect.min.js');
|
||||
$this->add_linked_script("../js/ajaxfileupload.js");
|
||||
$this->add_linked_script("../js/ckeditor/ckeditor.js");
|
||||
$this->add_linked_script("../js/ckeditor/adapters/jquery.js");
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
try
|
||||
@@ -231,6 +233,20 @@ EOF
|
||||
var next_step = $('input[id=next_step]');
|
||||
next_step.val(sStep);
|
||||
}
|
||||
|
||||
// For disabling the CKEditor at init time when the corresponding textarea is disabled !
|
||||
CKEDITOR.plugins.add( 'disabler',
|
||||
{
|
||||
init : function( editor )
|
||||
{
|
||||
editor.on( 'instanceReady', function(e)
|
||||
{
|
||||
e.removeListener();
|
||||
$('#'+ editor.name).trigger('update');
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
EOF
|
||||
);
|
||||
|
||||
@@ -826,7 +842,7 @@ EOF
|
||||
}
|
||||
|
||||
$sStepHistory = implode(',', $aPreviousSteps);
|
||||
$this->add("<input type=\"hidden\" id=\"step_history\" name=\"step_history\" value=\"$sStepHistory\">");
|
||||
$this->add("<input type=\"hidden\" id=\"step_history\" name=\"step_history\" value=\"".htmlentities($sStepHistory, ENT_QUOTES, 'UTF-8')."\">");
|
||||
|
||||
if (!is_null($sNextStep))
|
||||
{
|
||||
|
||||
@@ -254,8 +254,8 @@ class ShortcutOQL extends Shortcut
|
||||
$oField = new DesignerBooleanField('auto_reload', Dict::S('Class:ShortcutOQL/Attribute:auto_reload'), false);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerTextField('auto_reload_sec', Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec'), MetaModel::GetConfig()->GetStandardReloadInterval());
|
||||
$oField->SetValidationPattern('^$|^0*([5-9]|[1-9][0-9]+)$'); // Can be empty, or a number > 4
|
||||
$oField = new DesignerIntegerField('auto_reload_sec', Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec'), MetaModel::GetConfig()->GetStandardReloadInterval());
|
||||
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
|
||||
$oField->SetMandatory(false);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
@@ -284,7 +284,7 @@ class ShortcutOQL extends Shortcut
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
|
||||
$sRateTitle = addslashes(Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec+'));
|
||||
$sRateTitle = addslashes(Dict::Format('Class:ShortcutOQL/Attribute:auto_reload_sec/tip', MetaModel::GetConfig()->Get('min_reload_interval')));
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
|
||||
@@ -28,10 +28,11 @@ require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
session_name('itop-'.md5(APPROOT));
|
||||
session_start();
|
||||
if (isset($_REQUEST['switch_env']))
|
||||
$sSwitchEnv = utils::ReadParam('switch_env', null);
|
||||
if (($sSwitchEnv != null) && (file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE)))
|
||||
{
|
||||
$sEnv = $_REQUEST['switch_env'];
|
||||
$_SESSION['itop_env'] = $sEnv;
|
||||
$_SESSION['itop_env'] = $sSwitchEnv;
|
||||
$sEnv = $sSwitchEnv;
|
||||
// TODO: reset the credentials as well ??
|
||||
}
|
||||
else if (isset($_SESSION['itop_env']))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,8 @@
|
||||
/**
|
||||
* This class records the pending "transactions" corresponding to forms that have not been
|
||||
* submitted yet, in order to prevent double submissions. When created a transaction remains valid
|
||||
* until the user's session expires
|
||||
* until the user's session expires. This class is actually a wrapper to the underlying implementation
|
||||
* which choice is configured via the parameter 'transaction_storage'
|
||||
*
|
||||
* @package iTop
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
@@ -28,6 +29,81 @@
|
||||
|
||||
|
||||
class privUITransaction
|
||||
{
|
||||
/**
|
||||
* Create a new transaction id, store it in the session and return its id
|
||||
* @param void
|
||||
* @return int The identifier of the new transaction
|
||||
*/
|
||||
public static function GetNewTransactionId()
|
||||
{
|
||||
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
|
||||
if (!$bTransactionsEnabled)
|
||||
{
|
||||
return 'notransactions'; // Any value will do
|
||||
}
|
||||
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
|
||||
if (!class_exists($sClass, false))
|
||||
{
|
||||
IssueLog::Error("Incorrect value '".MetaModel::GetConfig()->Get('transaction_storage')."' for 'transaction_storage', the class '$sClass' does not exists. Using privUITransactionSession instead for storing sessions.");
|
||||
$sClass = 'privUITransactionSession';
|
||||
}
|
||||
|
||||
return (string)$sClass::GetNewTransactionId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
|
||||
* the session so that another call to IsTransactionValid for the same transaction id
|
||||
* will return false
|
||||
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
|
||||
* @param bool $bRemoveTransaction True if the transaction must be removed
|
||||
* @return bool True if the transaction is valid, false otherwise
|
||||
*/
|
||||
public static function IsTransactionValid($id, $bRemoveTransaction = true)
|
||||
{
|
||||
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
|
||||
if (!$bTransactionsEnabled)
|
||||
{
|
||||
return true; // All values are valid
|
||||
}
|
||||
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
|
||||
if (!class_exists($sClass, false))
|
||||
{
|
||||
$sClass = 'privUITransactionSession';
|
||||
}
|
||||
|
||||
return $sClass::IsTransactionValid($id, $bRemoveTransaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the transaction specified by its id
|
||||
* @param int $id The Identifier (as returned by GetNewTranscationId) of the transaction to be removed.
|
||||
* @return void
|
||||
*/
|
||||
public static function RemoveTransaction($id)
|
||||
{
|
||||
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
|
||||
if (!$bTransactionsEnabled)
|
||||
{
|
||||
return; // Nothing to do
|
||||
}
|
||||
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
|
||||
if (!class_exists($sClass, false))
|
||||
{
|
||||
$sClass = 'privUITransactionSession';
|
||||
}
|
||||
|
||||
$sClass::RemoveTransaction($id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The original (and by default) mechanism for storing transaction information
|
||||
* as an array in the $_SESSION variable
|
||||
*
|
||||
*/
|
||||
class privUITransactionSession
|
||||
{
|
||||
/**
|
||||
* Create a new transaction id, store it in the session and return its id
|
||||
@@ -99,4 +175,178 @@ class privUITransaction
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
/**
|
||||
* An alternate implementation for storing the transactions as temporary files
|
||||
* Useful when using an in-memory storage for the session which do not
|
||||
* guarantee mutual exclusion for writing
|
||||
*/
|
||||
class privUITransactionFile
|
||||
{
|
||||
/**
|
||||
* Create a new transaction id, store it in the session and return its id
|
||||
* @param void
|
||||
* @return int The identifier of the new transaction
|
||||
*/
|
||||
public static function GetNewTransactionId()
|
||||
{
|
||||
if (!is_dir(APPROOT.'data/transactions'))
|
||||
{
|
||||
if (!is_writable(APPROOT.'data'))
|
||||
{
|
||||
throw new Exception('The directory "'.APPROOT.'data" must be writable to the application.');
|
||||
}
|
||||
if (!@mkdir(APPROOT.'data/transactions'))
|
||||
{
|
||||
throw new Exception('Failed to create the directory "'.APPROOT.'data/transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
|
||||
}
|
||||
}
|
||||
if (!is_writable(APPROOT.'data/transactions'))
|
||||
{
|
||||
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
|
||||
}
|
||||
self::CleanupOldTransactions();
|
||||
$id = basename(tempnam(APPROOT.'data/transactions', self::GetUserPrefix()));
|
||||
self::Info('GetNewTransactionId: Created transaction: '.$id);
|
||||
|
||||
return (string)$id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
|
||||
* the session so that another call to IsTransactionValid for the same transaction id
|
||||
* will return false
|
||||
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
|
||||
* @param bool $bRemoveTransaction True if the transaction must be removed
|
||||
* @return bool True if the transaction is valid, false otherwise
|
||||
*/
|
||||
public static function IsTransactionValid($id, $bRemoveTransaction = true)
|
||||
{
|
||||
$sFilepath = APPROOT.'data/transactions/'.$id;
|
||||
clearstatcache(true, $sFilepath);
|
||||
$bResult = file_exists($sFilepath);
|
||||
if ($bResult)
|
||||
{
|
||||
if ($bRemoveTransaction)
|
||||
{
|
||||
$bResult = @unlink($sFilepath);
|
||||
if (!$bResult)
|
||||
{
|
||||
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
|
||||
}
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the transaction specified by its id
|
||||
* @param int $id The Identifier (as returned by GetNewTransactionId) of the transaction to be removed.
|
||||
* @return void
|
||||
*/
|
||||
public static function RemoveTransaction($id)
|
||||
{
|
||||
$bSuccess = true;
|
||||
$sFilepath = APPROOT.'data/transactions/'.$id;
|
||||
clearstatcache(true, $sFilepath);
|
||||
if(!file_exists($sFilepath))
|
||||
{
|
||||
$bSuccess = false;
|
||||
self::Error("RemoveTransaction: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
|
||||
}
|
||||
$bSuccess = @unlink($sFilepath);
|
||||
if (!$bSuccess)
|
||||
{
|
||||
self::Error('RemoveTransaction: FAILED to remove transaction '.$id);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info('RemoveTransaction: OK '.$id);
|
||||
}
|
||||
return $bSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup old transactions which have been pending since more than 24 hours
|
||||
* Use filemtime instead of filectime since filectime may be affected by operations on the directory (like changing the access rights)
|
||||
*/
|
||||
protected static function CleanupOldTransactions()
|
||||
{
|
||||
$iLimit = time() - 24*3600;
|
||||
clearstatcache();
|
||||
$aTransactions = glob(APPROOT.'data/transactions/*-*');
|
||||
foreach($aTransactions as $sFileName)
|
||||
{
|
||||
if (filemtime($sFileName) < $iLimit)
|
||||
{
|
||||
@unlink($sFileName);
|
||||
self::Info('CleanupOldTransactions: Deleted transaction: '.$sFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For debugging purposes: gets the pending transactions of the current user
|
||||
* as an array, with the date of the creation of the transaction file
|
||||
*/
|
||||
protected static function GetPendingTransactions()
|
||||
{
|
||||
clearstatcache();
|
||||
$aResult = array();
|
||||
$aTransactions = glob(APPROOT.'data/transactions/'.self::GetUserPrefix().'*');
|
||||
foreach($aTransactions as $sFileName)
|
||||
{
|
||||
$aResult[] = date('Y-m-d H:i:s', filemtime($sFileName)).' - '.basename($sFileName);
|
||||
}
|
||||
sort($aResult);
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
protected static function GetUserPrefix()
|
||||
{
|
||||
$sPrefix = substr(UserRights::GetUser(), 0, 10);
|
||||
$sPrefix = preg_replace('/[^a-zA-Z0-9-_]/', '_', $sPrefix);
|
||||
return $sPrefix.'-';
|
||||
}
|
||||
|
||||
protected static function Info($sText)
|
||||
{
|
||||
self::Write('Info | '.$sText);
|
||||
}
|
||||
|
||||
protected static function Warning($sText)
|
||||
{
|
||||
self::Write('Warning | '.$sText);
|
||||
}
|
||||
|
||||
protected static function Error($sText)
|
||||
{
|
||||
self::Write('Error | '.$sText);
|
||||
}
|
||||
|
||||
protected static function Write($sText)
|
||||
{
|
||||
$bLogEnabled = MetaModel::GetConfig()->Get('log_transactions');
|
||||
if ($bLogEnabled)
|
||||
{
|
||||
$hLogFile = @fopen(APPROOT.'log/transactions.log', 'a');
|
||||
if ($hLogFile !== false)
|
||||
{
|
||||
flock($hLogFile, LOCK_EX);
|
||||
$sDate = date('Y-m-d H:i:s');
|
||||
fwrite($hLogFile, "$sDate | $sText\n");
|
||||
fflush($hLogFile);
|
||||
flock($hLogFile, LOCK_UN);
|
||||
fclose($hLogFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ EOF
|
||||
$sHTMLValue .= "<img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif\" onClick=\"oACWidget_{$this->iId}.Search();\"/> ";
|
||||
|
||||
// another hidden input to store & pass the object's Id
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"$value\" />\n";
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
|
||||
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
|
||||
// Scripts to start the autocomplete and bind some events to it
|
||||
|
||||
@@ -80,7 +80,7 @@ class UIHTMLEditorWidget
|
||||
|
||||
// Could also be bound to 'instanceReady.ckeditor'
|
||||
$oPage->add_ready_script("$('#$iId').bind('validate', function(evt, sFormId) { return ValidateCKEditField('$iId', '', {$this->m_sMandatory}, sFormId, '') } );\n");
|
||||
$oPage->add_ready_script("$('#$iId').bind('update', function() { BlockField('cke_$iId', $('#$iId').attr('disabled')); } );\n");
|
||||
$oPage->add_ready_script("$('#$iId').bind('update', function() { BlockField('cke_$iId', $('#$iId').attr('disabled')); $(this).data('ckeditorInstance').setReadOnly($(this).prop('disabled')); } );\n");
|
||||
|
||||
return $sHtmlValue;
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ class UILinksWidgetDirect
|
||||
$valuesDef = $oLinksetDef->GetValuesDef();
|
||||
if ($valuesDef === null)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($this->sLinkedClass);
|
||||
$oFilter = new DBObjectSearch($sRemoteClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -130,7 +130,7 @@ class appUserPreferences extends DBObject
|
||||
/**
|
||||
* Call this function if the user has changed (like when doing a logoff...)
|
||||
*/
|
||||
static public function Reset()
|
||||
static public function ResetPreferences()
|
||||
{
|
||||
self::$oUserPrefs = null;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
456
application/xlsxwriter.class.php
Normal file
456
application/xlsxwriter.class.php
Normal 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>&C&"Times New Roman,Regular"&12&A</oddHeader>');
|
||||
fwrite($fd, '<oddFooter>&C&"Times New Roman,Regular"&12Page &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("'", "'", 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;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -480,6 +480,14 @@ abstract class AttributeDefinition
|
||||
return (string)$sValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to differentiate a value displayed in the UI or in the history
|
||||
*/
|
||||
public function GetAsHTMLForHistory($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
return $this->GetAsHTML($sValue, $oHostObject, $bLocalize);
|
||||
}
|
||||
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
$oValSetDef = $this->GetValuesDef();
|
||||
@@ -487,15 +495,18 @@ abstract class AttributeDefinition
|
||||
return $oValSetDef->GetValues($aArgs, $sContains);
|
||||
}
|
||||
|
||||
public function GetAsHTMLForHistory($sOldValue, $sNewValue, $sLabel = null)
|
||||
/**
|
||||
* Explain the change of the attribute (history)
|
||||
*/
|
||||
public function DescribeChangeAsHTML($sOldValue, $sNewValue, $sLabel = null)
|
||||
{
|
||||
if (is_null($sLabel))
|
||||
{
|
||||
$sLabel = $this->GetLabel();
|
||||
}
|
||||
|
||||
$sNewValueHtml = $this->GetAsHTML($sNewValue);
|
||||
$sOldValueHtml = $this->GetAsHTML($sOldValue);
|
||||
$sNewValueHtml = $this->GetAsHTMLForHistory($sNewValue);
|
||||
$sOldValueHtml = $this->GetAsHTMLForHistory($sOldValue);
|
||||
|
||||
if($this->IsExternalKey())
|
||||
{
|
||||
@@ -881,6 +892,106 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
return $oSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get a value that will be JSON encoded
|
||||
* The operation is the opposite to FromJSONToValue
|
||||
*/
|
||||
public function GetForJSON($value)
|
||||
{
|
||||
$aRet = array();
|
||||
if (is_object($value) && ($value instanceof DBObjectSet))
|
||||
{
|
||||
$value->Rewind();
|
||||
while ($oObj = $value->Fetch())
|
||||
{
|
||||
$sObjClass = get_class($oObj);
|
||||
// Show only relevant information (hide the external key to the current object)
|
||||
$aAttributes = array();
|
||||
foreach(MetaModel::ListAttributeDefs($sObjClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($sAttCode == 'finalclass')
|
||||
{
|
||||
if ($sObjClass == $this->GetLinkedClass())
|
||||
{
|
||||
// Simplify the output if the exact class could be determined implicitely
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($sAttCode == $this->GetExtKeyToMe()) continue;
|
||||
if ($oAttDef->IsExternalField()) continue;
|
||||
if (!$oAttDef->IsDirectField()) continue;
|
||||
if (!$oAttDef->IsScalar()) continue;
|
||||
$attValue = $oObj->Get($sAttCode);
|
||||
$aAttributes[$sAttCode] = $oAttDef->GetForJSON($attValue);
|
||||
}
|
||||
$aRet[] = $aAttributes;
|
||||
}
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to form a value, given JSON decoded data
|
||||
* The operation is the opposite to GetForJSON
|
||||
*/
|
||||
public function FromJSONToValue($json)
|
||||
{
|
||||
$sTargetClass = $this->Get('linked_class');
|
||||
|
||||
$aLinks = array();
|
||||
foreach($json as $aValues)
|
||||
{
|
||||
if (isset($aValues['finalclass']))
|
||||
{
|
||||
$sLinkClass = $aValues['finalclass'];
|
||||
if (!is_subclass_of($sLinkClass, $sTargetClass))
|
||||
{
|
||||
throw new CoreException('Wrong class for link attribute specification', array('requested_class' => $sLinkClass, 'expected_class' => $sTargetClass));
|
||||
}
|
||||
}
|
||||
elseif (MetaModel::IsAbstract($sTargetClass))
|
||||
{
|
||||
throw new CoreException('Missing finalclass for link attribute specification');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLinkClass = $sTargetClass;
|
||||
}
|
||||
|
||||
$oLink = MetaModel::NewObject($sLinkClass);
|
||||
foreach ($aValues as $sAttCode => $sValue)
|
||||
{
|
||||
$oLink->Set($sAttCode, $sValue);
|
||||
}
|
||||
|
||||
// Check (roughly) if such a link is valid
|
||||
$aErrors = array();
|
||||
foreach(MetaModel::ListAttributeDefs($sTargetClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
if (($oAttDef->GetTargetClass() == $this->GetHostClass()) || (is_subclass_of($this->GetHostClass(), $oAttDef->GetTargetClass())))
|
||||
{
|
||||
continue; // Don't check the key to self
|
||||
}
|
||||
}
|
||||
|
||||
if ($oAttDef->IsWritable() && $oAttDef->IsNull($oLink->Get($sAttCode)) && !$oAttDef->IsNullAllowed())
|
||||
{
|
||||
$aErrors[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
if (count($aErrors) > 0)
|
||||
{
|
||||
throw new CoreException("Missing value for mandatory attribute(s): ".implode(', ', $aErrors));
|
||||
}
|
||||
|
||||
$aLinks[] = $oLink;
|
||||
}
|
||||
$oSet = DBObjectSet::FromArray($sTargetClass, $aLinks);
|
||||
return $oSet;
|
||||
}
|
||||
|
||||
public function Equals($val1, $val2)
|
||||
{
|
||||
if ($val1 === $val2) return true;
|
||||
@@ -1982,7 +2093,7 @@ class AttributeCaseLog extends AttributeLongText
|
||||
|
||||
public function FromSQLToValue($aCols, $sPrefix = '')
|
||||
{
|
||||
if (!isset($aCols[$sPrefix]))
|
||||
if (!array_key_exists($sPrefix, $aCols))
|
||||
{
|
||||
$sAvailable = implode(', ', array_keys($aCols));
|
||||
throw new MissingColumnException("Missing column '$sPrefix' from {$sAvailable}");
|
||||
@@ -3426,12 +3537,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 +3885,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 +3893,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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3923,19 +4092,17 @@ class AttributeStopWatch extends AttributeDefinition
|
||||
return Dict::S('BooleanLabel:'.$sDictKey, 'def:'.$sDictKey);
|
||||
}
|
||||
|
||||
public function GetSubItemAsHTMLForHistory($sItemCode, $sOldValue, $sNewValue, $sLabel)
|
||||
public function GetSubItemAsHTMLForHistory($sItemCode, $sValue)
|
||||
{
|
||||
switch($sItemCode)
|
||||
{
|
||||
case 'timespent':
|
||||
$sHtmlOld = (int)$sOldValue ? AttributeDuration::FormatDuration($sOldValue) : null;
|
||||
$sHtmlNew = (int)$sNewValue ? AttributeDuration::FormatDuration($sNewValue) : null;
|
||||
$sHtml = (int)$sValue ? Str::pure2html(AttributeDuration::FormatDuration($sValue)) : null;
|
||||
break;
|
||||
case 'started':
|
||||
case 'laststart':
|
||||
case 'stopped':
|
||||
$sHtmlOld = (int)$sOldValue ? date(self::GetDateFormat(), (int)$sOldValue) : null;
|
||||
$sHtmlNew = (int)$sNewValue ? date(self::GetDateFormat(), (int)$sNewValue) : null;
|
||||
$sHtml = (int)$sValue ? date(self::GetDateFormat(), (int)$sValue) : null;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -3949,26 +4116,21 @@ class AttributeStopWatch extends AttributeDefinition
|
||||
switch($sThresholdCode)
|
||||
{
|
||||
case 'deadline':
|
||||
$sHtmlOld = (int)$sOldValue ? date(self::GetDateFormat(true /*full*/), (int)$sOldValue) : null;
|
||||
$sHtmlNew = (int)$sNewValue ? date(self::GetDateFormat(true /*full*/), (int)$sNewValue) : null;
|
||||
$sHtml = (int)$sValue ? date(self::GetDateFormat(true /*full*/), (int)$sValue) : null;
|
||||
break;
|
||||
case 'passed':
|
||||
$sHtmlOld = $this->GetBooleanLabel((int)$sOldValue);
|
||||
$sHtmlNew = $this->GetBooleanLabel((int)$sNewValue);
|
||||
$sHtml = $this->GetBooleanLabel((int)$sValue);
|
||||
break;
|
||||
case 'triggered':
|
||||
$sHtmlOld = $this->GetBooleanLabel((int)$sOldValue);
|
||||
$sHtmlNew = $this->GetBooleanLabel((int)$sNewValue);
|
||||
$sHtml = $this->GetBooleanLabel((int)$sValue);
|
||||
break;
|
||||
case 'overrun':
|
||||
$sHtmlOld = (int)$sOldValue > 0 ? AttributeDuration::FormatDuration((int)$sOldValue) : '';
|
||||
$sHtmlNew = (int)$sNewValue > 0 ? AttributeDuration::FormatDuration((int)$sNewValue) : '';
|
||||
$sHtml = (int)$sValue > 0 ? Str::pure2html(AttributeDuration::FormatDuration((int)$sValue)) : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$sRes = parent::GetAsHTMLForHistory($sHtmlOld, $sHtmlNew, $sLabel);
|
||||
return $sRes;
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
static protected function GetDateFormat($bFull = false)
|
||||
@@ -4283,6 +4445,13 @@ class AttributeSubItem extends AttributeDefinition
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function GetAsHTMLForHistory($value, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
$res = $oParent->GetSubItemAsHTMLForHistory($this->Get('item_code'), $value);
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
@@ -4297,15 +4466,6 @@ class AttributeSubItem extends AttributeDefinition
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function GetAsHTMLForHistory($sOldValue, $sNewValue, $sLabel = null)
|
||||
{
|
||||
$sLabel = $this->GetLabel();
|
||||
|
||||
$oParent = $this->GetTargetAttDef();
|
||||
$sValue = $oParent->GetSubItemAsHTMLForHistory($this->Get('item_code'), $sOldValue, $sNewValue, $sLabel);
|
||||
return $sValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* As of now, this function must be implemented to have the value in spreadsheet format
|
||||
*/
|
||||
@@ -4865,7 +5025,7 @@ class AttributeFriendlyName extends AttributeComputedFieldVoid
|
||||
}
|
||||
|
||||
// Do not display friendly names in the history of change
|
||||
public function GetAsHTMLForHistory($sOldValue, $sNewValue, $sLabel = null)
|
||||
public function DescribeChangeAsHTML($sOldValue, $sNewValue, $sLabel = null)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -432,7 +432,7 @@ class BulkChange
|
||||
break;
|
||||
|
||||
default:
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $oExtObjects->Count());
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $iCount);
|
||||
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $iCount, $sOQL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class CMDBChangeOp extends DBObject
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("date", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"date")));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("userinfo", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"userinfo")));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("objclass", array("allowed_values"=>null, "sql"=>"objclass", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("objkey", array("allowed_values"=>null, "sql"=>"objkey", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("objkey", array("allowed_values"=>null, "sql"=>"objkey", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_SetZListItems('details', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details
|
||||
@@ -237,7 +237,7 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
$sNewValue = $this->Get('newvalue');
|
||||
$sOldValue = $this->Get('oldvalue');
|
||||
$sResult = $oAttDef->GetAsHTMLForHistory($sOldValue, $sNewValue);
|
||||
$sResult = $oAttDef->DescribeChangeAsHTML($sOldValue, $sNewValue);
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -96,11 +96,19 @@ class DefaultWorkingTimeComputer implements iWorkingTimeComputer
|
||||
*/
|
||||
public function GetDeadline($oObject, $iDuration, DateTime $oStartDate)
|
||||
{
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::Trace(WorkingTimeRecorder::TRACE_DEBUG, __class__.'::'.__function__);
|
||||
}
|
||||
//echo "GetDeadline - default: ".$oStartDate->format('Y-m-d H:i:s')." + $iDuration<br/>\n";
|
||||
// Default implementation: 24x7, no holidays: to compute the deadline, just add
|
||||
// the specified duration to the given date/time
|
||||
$oResult = clone $oStartDate;
|
||||
$oResult->modify('+'.$iDuration.' seconds');
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::SetValues($oStartDate->format('U'), $oResult->format('U'), $iDuration, WorkingTimeRecorder::COMPUTED_END);
|
||||
}
|
||||
return $oResult;
|
||||
}
|
||||
|
||||
@@ -113,8 +121,17 @@ class DefaultWorkingTimeComputer implements iWorkingTimeComputer
|
||||
*/
|
||||
public function GetOpenDuration($oObject, DateTime $oStartDate, DateTime $oEndDate)
|
||||
{
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::Trace(WorkingTimeRecorder::TRACE_DEBUG, __class__.'::'.__function__);
|
||||
}
|
||||
//echo "GetOpenDuration - default: ".$oStartDate->format('Y-m-d H:i:s')." to ".$oEndDate->format('Y-m-d H:i:s')."<br/>\n";
|
||||
return abs($oEndDate->format('U') - $oStartDate->format('U'));
|
||||
$iDuration = abs($oEndDate->format('U') - $oStartDate->format('U'));
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::SetValues($oStartDate->format('U'), $oEndDate->format('U'), $iDuration, WorkingTimeRecorder::COMPUTED_DURATION);
|
||||
}
|
||||
return $iDuration;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -769,6 +769,70 @@ 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,
|
||||
),
|
||||
'min_reload_interval' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Minimum refresh interval (seconds) for dashboards, shortcuts, etc. Even if the interval is set programmatically, it is forced to that minimum',
|
||||
'default' => 5, // In iTop 2.0.3, this was the hardcoded value
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'relations_max_depth' => array(
|
||||
'type' => 'integer',
|
||||
'description' => 'Maximum number of successive levels (depth) to explore when displaying the impact/depends on relations.',
|
||||
'default' => 20, // In iTop 2.0.3, this was the hardcoded value
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'transaction_storage' => array(
|
||||
'type' => 'string',
|
||||
'description' => 'The type of mechanism to use for storing the unique identifiers for transactions (Session|File).',
|
||||
'default' => 'Session',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'transactions_enabled' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether or not the whole mechanism to prevent multiple submissions of a page is enabled.',
|
||||
'default' => true,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'log_transactions' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether or not to enable the debug log for the transactions.',
|
||||
'default' => false,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
);
|
||||
|
||||
public function IsProperty($sPropCode)
|
||||
@@ -1422,6 +1486,8 @@ class Config
|
||||
$aSettings['log_notification'] = $this->m_bLogNotification;
|
||||
$aSettings['log_issue'] = $this->m_bLogIssue;
|
||||
$aSettings['log_web_service'] = $this->m_bLogWebService;
|
||||
$aSettings['log_queries'] = $this->m_bLogQueries;
|
||||
$aSettings['query_cache_enabled'] = $this->m_bQueryCacheEnabled;
|
||||
$aSettings['min_display_limit'] = $this->m_iMinDisplayLimit;
|
||||
$aSettings['max_display_limit'] = $this->m_iMaxDisplayLimit;
|
||||
$aSettings['standard_reload_interval'] = $this->m_iStandardReloadInterval;
|
||||
@@ -1429,6 +1495,7 @@ class Config
|
||||
$aSettings['secure_connection_required'] = $this->m_bSecureConnectionRequired;
|
||||
$aSettings['default_language'] = $this->m_sDefaultLanguage;
|
||||
$aSettings['allowed_login_types'] = $this->m_sAllowedLoginTypes;
|
||||
$aSettings['ext_auth_variable'] = $this->m_sExtAuthVariable;
|
||||
$aSettings['encryption_key'] = $this->m_sEncryptionKey;
|
||||
$aSettings['csv_import_charsets'] = $this->m_aCharsets;
|
||||
|
||||
@@ -1494,6 +1561,8 @@ class Config
|
||||
'log_notification' => $this->m_bLogNotification,
|
||||
'log_issue' => $this->m_bLogIssue,
|
||||
'log_web_service' => $this->m_bLogWebService,
|
||||
'log_queries' => $this->m_bLogQueries,
|
||||
'query_cache_enabled' => $this->m_bQueryCacheEnabled,
|
||||
'secure_connection_required' => $this->m_bSecureConnectionRequired,
|
||||
);
|
||||
foreach($aBoolValues as $sKey => $bValue)
|
||||
@@ -1532,6 +1601,7 @@ class Config
|
||||
'db_collation' => $this->m_sDBCollation,
|
||||
'default_language' => $this->m_sDefaultLanguage,
|
||||
'allowed_login_types' => $this->m_sAllowedLoginTypes,
|
||||
'ext_auth_variable' => $this->m_sExtAuthVariable,
|
||||
'encryption_key' => $this->m_sEncryptionKey,
|
||||
'csv_import_charsets' => $this->m_aCharsets,
|
||||
);
|
||||
|
||||
@@ -94,7 +94,9 @@ 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;
|
||||
protected $m_aCallbacks = array();
|
||||
|
||||
// Use the MetaModel::NewObject to build an object (do we have to force it?)
|
||||
public function __construct($aRow = null, $sClassAlias = '', $aAttToLoad = null, $aExtendedDataSpec = null)
|
||||
@@ -337,7 +339,7 @@ abstract class DBObject implements iDisplay
|
||||
if ($sAttCode == 'finalclass')
|
||||
{
|
||||
// Ignore it - this attribute is set upon object creation and that's it
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
@@ -415,6 +417,9 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
// Make sure we do not reload it anymore... before saving it
|
||||
$this->RegisterAsDirty();
|
||||
|
||||
// This function is eligible as a lifecycle action: returning true upon success is a must
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetLabel($sAttCode)
|
||||
@@ -557,6 +562,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 +848,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,54 +1182,51 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final public function CheckToDelete(&$oDeletionPlan)
|
||||
public function CheckToDelete(&$oDeletionPlan)
|
||||
{
|
||||
$this->MakeDeletionPlan($oDeletionPlan);
|
||||
$oDeletionPlan->ComputeResults();
|
||||
@@ -1508,6 +1585,15 @@ abstract class DBObject implements iDisplay
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
|
||||
// Callbacks registered with RegisterCallback
|
||||
if (isset($this->m_aCallbacks[self::CALLBACK_AFTERINSERT]))
|
||||
{
|
||||
foreach ($this->m_aCallbacks[self::CALLBACK_AFTERINSERT] as $aCallBackData)
|
||||
{
|
||||
call_user_func_array($aCallBackData['callback'], $aCallBackData['params']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->RecordObjCreation();
|
||||
|
||||
return $this->m_iKey;
|
||||
@@ -1998,17 +2084,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.1.0 modules) action definition without any parameter
|
||||
$aActionCallSpec = array($this, $actionHandler);
|
||||
|
||||
if (!is_callable($aActionCallSpec))
|
||||
{
|
||||
throw new CoreException("Unable to call action: ".get_class($this)."::$actionHandler");
|
||||
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;
|
||||
}
|
||||
@@ -2072,6 +2196,80 @@ abstract class DBObject implements iDisplay
|
||||
$this->Set($sAttCode, $oSW);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle action: Recover the default value (aka when an object is being created)
|
||||
*/
|
||||
public function Reset($sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$this->Set($sAttCode, $oAttDef->GetDefaultValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle action: Copy an attribute to another
|
||||
*/
|
||||
public function Copy($sDestAttCode, $sSourceAttCode)
|
||||
{
|
||||
$this->Set($sDestAttCode, $this->Get($sSourceAttCode));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle action: Set the current date/time for the given attribute
|
||||
*/
|
||||
public function SetCurrentDate($sAttCode)
|
||||
{
|
||||
$this->Set($sAttCode, time());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle action: Set the current logged in user for the given attribute
|
||||
*/
|
||||
public function SetCurrentUser($sAttCode)
|
||||
{
|
||||
$this->Set($sAttCode, UserRights::GetUserId());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle action: Set the time elapsed since a reference point
|
||||
*/
|
||||
public function SetElapsedTime($sAttCode, $sRefAttCode, $sWorkingTimeComputer = null)
|
||||
{
|
||||
if (is_null($sWorkingTimeComputer))
|
||||
{
|
||||
$sWorkingTimeComputer = class_exists('SLAComputation') ? 'SLAComputation' : 'DefaultWorkingTimeComputer';
|
||||
}
|
||||
$oComputer = new $sWorkingTimeComputer();
|
||||
$aCallSpec = array($oComputer, 'GetOpenDuration');
|
||||
if (!is_callable($aCallSpec))
|
||||
{
|
||||
throw new CoreException("Unknown class/verb '$sWorkingTimeComputer/GetOpenDuration'");
|
||||
}
|
||||
|
||||
$iStartTime = AttributeDateTime::GetAsUnixSeconds($this->Get($sRefAttCode));
|
||||
$oStartDate = new DateTime('@'.$iStartTime); // setTimestamp not available in PHP 5.2
|
||||
$oEndDate = new DateTime(); // now
|
||||
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
WorkingTimeRecorder::Start($this, time(), "DBObject-SetElapsedTime-$sAttCode-$sRefAttCode", 'Core:ExplainWTC:ElapsedTime', array("Class:$sClass/Attribute:$sAttCode"));
|
||||
}
|
||||
$iElapsed = call_user_func($aCallSpec, $this, $oStartDate, $oEndDate);
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::End();
|
||||
}
|
||||
|
||||
$this->Set($sAttCode, $iElapsed);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Create query parameters (SELECT ... WHERE service = :this->service_id)
|
||||
* to be used with the APIs DBObjectSearch/DBObjectSet
|
||||
@@ -2474,45 +2672,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 +2793,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;
|
||||
}
|
||||
|
||||
@@ -2581,6 +2820,28 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
$oPage->details($aValues);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const CALLBACK_AFTERINSERT = 0;
|
||||
|
||||
/**
|
||||
* Register a call back that will be called when some internal event happens
|
||||
*
|
||||
* @param $iType string Any of the CALLBACK_x constants
|
||||
* @param $callback callable Call specification like a function name, or array('<class>', '<method>') or array($object, '<method>')
|
||||
* @param $aParameters Array Values that will be passed to the callback, after $this
|
||||
*/
|
||||
public function RegisterCallback($iType, $callback, $aParameters = array())
|
||||
{
|
||||
$sCallBackName = '';
|
||||
if (!is_callable($callback, false, $sCallBackName))
|
||||
{
|
||||
throw new Exception('Registering an unknown/protected function or wrong syntax for the call spec: '.$sCallBackName);
|
||||
}
|
||||
$this->m_aCallbacks[$iType][] = array(
|
||||
'callback' => $callback,
|
||||
'params' => $aParameters
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,9 +53,8 @@ class DBObjectSearch
|
||||
public function __construct($sClass, $sClassAlias = null)
|
||||
{
|
||||
if (is_null($sClassAlias)) $sClassAlias = $sClass;
|
||||
assert('is_string($sClass)');
|
||||
assert('MetaModel::IsValidClass($sClass)'); // #@# could do better than an assert, or at least give the caller's reference
|
||||
// => idee d'un assert avec call stack (autre utilisation = echec sur query SQL)
|
||||
if(!is_string($sClass)) throw new Exception('DBObjectSearch::__construct called with a non-string parameter: $sClass = '.print_r($sClass, true));
|
||||
if(!MetaModel::IsValidClass($sClass)) throw new Exception('DBObjectSearch::__construct called for an invalid class: "'.$sClass.'"');
|
||||
|
||||
$this->m_aSelectedClasses = array($sClassAlias => $sClass);
|
||||
$this->m_aClasses = array($sClassAlias => $sClass);
|
||||
@@ -449,7 +448,7 @@ class DBObjectSearch
|
||||
|
||||
public function AddCondition($sFilterCode, $value, $sOpCode = null)
|
||||
{
|
||||
MyHelpers::CheckKeyInArray('filter code', $sFilterCode, MetaModel::GetClassFilterDefs($this->GetClass()));
|
||||
MyHelpers::CheckKeyInArray('filter code in class: '.$this->GetClass(), $sFilterCode, MetaModel::GetClassFilterDefs($this->GetClass()));
|
||||
$oFilterDef = MetaModel::GetClassFilterDef($this->GetClass(), $sFilterCode);
|
||||
|
||||
$oField = new FieldExpression($sFilterCode, $this->GetClassAlias());
|
||||
@@ -972,6 +971,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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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'))
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -59,8 +59,11 @@ class FileLog
|
||||
$hLogFile = @fopen($this->m_sFile, 'a');
|
||||
if ($hLogFile !== false)
|
||||
{
|
||||
flock($hLogFile, LOCK_EX);
|
||||
$sDate = date('Y-m-d H:i:s');
|
||||
fwrite($hLogFile, "$sDate | $sText\n");
|
||||
fflush($hLogFile);
|
||||
flock($hLogFile, LOCK_UN);
|
||||
fclose($hLogFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -835,6 +836,10 @@ abstract class MetaModel
|
||||
final static public function GetAttributeDef($sClass, $sAttCode)
|
||||
{
|
||||
self::_check_subclass($sClass);
|
||||
if (!isset(self::$m_aAttribDefs[$sClass][$sAttCode]))
|
||||
{
|
||||
throw new Exception("Unknown attribute $sAttCode from class $sClass");
|
||||
}
|
||||
return self::$m_aAttribDefs[$sClass][$sAttCode];
|
||||
}
|
||||
|
||||
@@ -1908,6 +1913,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 +2171,10 @@ abstract class MetaModel
|
||||
{
|
||||
$aScalarArgs[$sArgName] = (string) $value;
|
||||
}
|
||||
elseif (is_null($value))
|
||||
{
|
||||
$aScalarArgs[$sArgName] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add standard contextual arguments
|
||||
@@ -2169,6 +2222,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 +2235,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3011,7 +3079,7 @@ abstract class MetaModel
|
||||
$sExtAttCode = $oAtt->GetExtAttCode();
|
||||
// Translate mainclass.extfield => remoteclassalias.remotefieldcode
|
||||
$oRemoteAttDef = self::GetAttributeDef($sKeyClass, $sExtAttCode);
|
||||
foreach ($oRemoteAttDef->GetSQLExpressions() as $sColID => $sRemoteAttExpr)
|
||||
foreach ($oRemoteAttDef->GetSQLExpressions() as $sColId => $sRemoteAttExpr)
|
||||
{
|
||||
$aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias);
|
||||
//echo "<p><b>aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias);</b></p>\n";
|
||||
@@ -3386,6 +3454,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 +3578,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)).".";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3681,7 +3805,7 @@ abstract class MetaModel
|
||||
}
|
||||
|
||||
|
||||
public static function DBCreate()
|
||||
public static function DBCreate($aCallback = null)
|
||||
{
|
||||
// Note: we have to check if the DB does exist, because we may share the DB
|
||||
// with other applications (in which case the DB does exist, not the tables with the given prefix)
|
||||
@@ -3689,18 +3813,24 @@ abstract class MetaModel
|
||||
{
|
||||
CMDBSource::CreateDB(self::$m_sDBName);
|
||||
}
|
||||
self::DBCreateTables();
|
||||
self::DBCreateTables($aCallback);
|
||||
self::DBCreateViews();
|
||||
}
|
||||
|
||||
protected static function DBCreateTables()
|
||||
protected static function DBCreateTables($aCallback = null)
|
||||
{
|
||||
list($aErrors, $aSugFix, $aCondensedQueries) = self::DBCheckFormat();
|
||||
|
||||
//$sSQL = implode('; ', $aCondensedQueries); Does not work - multiple queries not allowed
|
||||
foreach($aCondensedQueries as $sQuery)
|
||||
{
|
||||
$fStart = microtime(true);
|
||||
CMDBSource::CreateTable($sQuery);
|
||||
$fDuration = microtime(true) - $fStart;
|
||||
if ($aCallback != null)
|
||||
{
|
||||
call_user_func($aCallback, $sQuery, $fDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4718,6 +4848,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 +4885,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 +4947,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.
|
||||
@@ -5292,9 +5432,11 @@ abstract class MetaModel
|
||||
{
|
||||
// Some environment parameters are objects, we just need scalars
|
||||
if (is_object($replace)) continue;
|
||||
|
||||
$aSearches[] = '$'.$sSearch.'$';
|
||||
$aReplacements[] = (string) $replace;
|
||||
else
|
||||
{
|
||||
$aSearches[] = '$'.$sSearch.'$';
|
||||
$aReplacements[] = (string) $replace;
|
||||
}
|
||||
}
|
||||
return str_replace($aSearches, $aReplacements, $aInput);
|
||||
}
|
||||
@@ -5394,5 +5536,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"));
|
||||
|
||||
?>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2013 Combodo SARL
|
||||
// Copyright (C) 2013-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -24,7 +24,7 @@
|
||||
* Relies on MySQL locks because the API sem_get is not always present in the
|
||||
* installed PHP.
|
||||
*
|
||||
* @copyright Copyright (C) 2013 Combodo SARL
|
||||
* @copyright Copyright (C) 2013-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
class iTopMutex
|
||||
@@ -37,8 +37,19 @@ class iTopMutex
|
||||
public function __construct($sName, $sDBHost = null, $sDBUser = null, $sDBPwd = null)
|
||||
{
|
||||
// Compute the name of a lock for mysql
|
||||
// Note: the name is server-wide!!!
|
||||
// Note: names are server-wide!!! So let's make the name specific to this iTop instance
|
||||
$oConfig = utils::GetConfig(); // Will return an empty config when called during the setup
|
||||
$sDBName = $oConfig->GetDBName();
|
||||
$sDBSubname = $oConfig->GetDBSubname();
|
||||
$this->sName = 'itop.'.$sName;
|
||||
if (substr($sName, -strlen($sDBName.$sDBSubname)) != $sDBName.$sDBSubname)
|
||||
{
|
||||
// If the name supplied already ends with the expected suffix
|
||||
// don't add it twice, since the setup may try to detect an already
|
||||
// running cron job by its mutex, without knowing if the config already exists or not
|
||||
$this->sName .= $sDBName.$sDBSubname;
|
||||
}
|
||||
|
||||
$this->bLocked = false; // Not yet locked
|
||||
|
||||
if (!array_key_exists($this->sName, self::$aAcquiredLocks))
|
||||
@@ -48,7 +59,6 @@ class iTopMutex
|
||||
|
||||
// It is a MUST to create a dedicated session each time a lock is required, because
|
||||
// using GET_LOCK anytime on the same session will RELEASE the current and unique session lock (known issue)
|
||||
$oConfig = utils::GetConfig();
|
||||
$sDBHost = is_null($sDBHost) ? $oConfig->GetDBHost() : $sDBHost;
|
||||
$sDBUser = is_null($sDBUser) ? $oConfig->GetDBUser() : $sDBUser;
|
||||
$sDBPwd = is_null($sDBPwd) ? $oConfig->GetDBPwd() : $sDBPwd;
|
||||
@@ -121,7 +131,43 @@ class iTopMutex
|
||||
$this->bLocked = true;
|
||||
self::$aAcquiredLocks[$this->sName]++;
|
||||
}
|
||||
return ($res === '1');
|
||||
if (($res !== '1') && ($res !== '0'))
|
||||
{
|
||||
$sMsg = 'GET_LOCK('.$this->sName.', 0) returned: '.var_export($res, true).'. Expected values are: 0, 1 or null';
|
||||
IssueLog::Error($sMsg);
|
||||
throw new Exception($sMsg);
|
||||
}
|
||||
return ($res !== '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the mutex is locked WITHOUT TRYING TO ACQUIRE IT
|
||||
* @returns bool True if the mutex is in use, false otherwise
|
||||
*/
|
||||
public function IsLocked()
|
||||
{
|
||||
if ($this->bLocked)
|
||||
{
|
||||
return true; // Already acquired
|
||||
}
|
||||
if (self::$aAcquiredLocks[$this->sName] > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$res = $this->QueryToScalar("SELECT IS_FREE_LOCK('".$this->sName."')"); // IS_FREE_LOCK detects some error cases that IS_USED_LOCK do not detect
|
||||
if (is_null($res))
|
||||
{
|
||||
$sMsg = "MySQL Error, IS_FREE_LOCK('".$this->sName."') returned null. Error (".mysqli_errno($this->hDBLink).") = '".mysqli_error($this->hDBLink)."'";
|
||||
IssueLog::Error($sMsg);
|
||||
throw new Exception($sMsg);
|
||||
}
|
||||
else if ($res == '1')
|
||||
{
|
||||
// Lock is free
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -23,7 +23,7 @@ define('CASELOG_SEPARATOR', "\n".'========== %1$s : %2$s (%3$d) ============'."\
|
||||
/**
|
||||
* Class to store a "case log" in a structured way, keeping track of its successive entries
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
class ormCaseLog {
|
||||
@@ -384,7 +384,7 @@ class ormCaseLog {
|
||||
}
|
||||
|
||||
|
||||
public function AddLogEntryFromJSON($oJson)
|
||||
public function AddLogEntryFromJSON($oJson, $bCheckUserId = true)
|
||||
{
|
||||
$sText = isset($oJson->message) ? $oJson->message : '';
|
||||
|
||||
@@ -394,16 +394,24 @@ class ormCaseLog {
|
||||
{
|
||||
throw new Exception("Only administrators can set the user id", RestResult::UNAUTHORIZED);
|
||||
}
|
||||
try
|
||||
if ($bCheckUserId && ($oJson->user_id != 0))
|
||||
{
|
||||
$oUser = RestUtils::FindObjectFromKey('User', $oJson->user_id);
|
||||
try
|
||||
{
|
||||
$oUser = RestUtils::FindObjectFromKey('User', $oJson->user_id);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
throw new Exception('user_id: '.$e->getMessage(), $e->getCode());
|
||||
}
|
||||
$iUserId = $oUser->GetKey();
|
||||
$sOnBehalfOf = $oUser->GetFriendlyName();
|
||||
}
|
||||
catch(Exception $e)
|
||||
else
|
||||
{
|
||||
throw new Exception('user_id: '.$e->getMessage(), $e->getCode());
|
||||
$iUserId = $oJson->user_id;
|
||||
$sOnBehalfOf = $oJson->user_login;
|
||||
}
|
||||
$iUserId = $oUser->GetKey();
|
||||
$sOnBehalfOf = $oUser->GetFriendlyName();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2014 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -81,6 +82,26 @@ class ormStopWatch
|
||||
return $this->iTimeSpent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the working elapsed time since the start of the stop watch
|
||||
* even if it is currently running
|
||||
* @param oAttDef AttributeDefinition Attribute hosting the stop watch
|
||||
* @param oObject Hosting object (used for query parameters)
|
||||
*/
|
||||
public function GetElapsedTime($oAttDef, $oObject)
|
||||
{
|
||||
if (is_null($this->iLastStart))
|
||||
{
|
||||
return $this->GetTimeSpent();
|
||||
}
|
||||
else
|
||||
{
|
||||
$iElapsed = $this->ComputeDuration($oObject, $oAttDef, $this->iLastStart, time());
|
||||
return $this->iTimeSpent + $iElapsed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetStartDate()
|
||||
{
|
||||
return $this->iStarted;
|
||||
@@ -129,6 +150,10 @@ class ormStopWatch
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
if (isset($aThresholdData['overrun']) && ($aThresholdData['overrun'] > 0))
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
@@ -143,6 +168,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)
|
||||
{
|
||||
@@ -281,7 +331,7 @@ class ormStopWatch
|
||||
* It is the responsibility of the caller to compute the deadlines
|
||||
* (to avoid computing twice for the same result)
|
||||
*/
|
||||
public function Start($oObject, $oAttDef)
|
||||
public function Start($oObject, $oAttDef, $iNow = null)
|
||||
{
|
||||
if (!is_null($this->iLastStart))
|
||||
{
|
||||
@@ -289,11 +339,16 @@ class ormStopWatch
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_null($iNow))
|
||||
{
|
||||
$iNow = time();
|
||||
}
|
||||
|
||||
if (is_null($this->iStarted))
|
||||
{
|
||||
$this->iStarted = time();
|
||||
$this->iStarted = $iNow;
|
||||
}
|
||||
$this->iLastStart = time();
|
||||
$this->iLastStart = $iNow;
|
||||
$this->iStopped = null;
|
||||
|
||||
return true;
|
||||
@@ -309,8 +364,9 @@ class ormStopWatch
|
||||
// Currently stopped - do nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$iDurationGoal = $this->ComputeGoal($oObject, $oAttDef);
|
||||
$iComputationRefTime = time();
|
||||
foreach ($this->aThresholds as $iPercent => &$aThresholdData)
|
||||
{
|
||||
if (is_null($iDurationGoal))
|
||||
@@ -321,9 +377,20 @@ class ormStopWatch
|
||||
else
|
||||
{
|
||||
$iThresholdDuration = round($iPercent * $iDurationGoal / 100);
|
||||
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
$sAttCode = $oAttDef->GetCode();
|
||||
WorkingTimeRecorder::Start($oObject, $iComputationRefTime, "ormStopWatch-Deadline-$iPercent-$sAttCode", 'Core:ExplainWTC:StopWatch-Deadline', array("Class:$sClass/Attribute:$sAttCode", $iPercent));
|
||||
}
|
||||
$aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $this->iLastStart, $iThresholdDuration - $this->iTimeSpent);
|
||||
// OR $aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $this->iStarted, $iThresholdDuration);
|
||||
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::End();
|
||||
}
|
||||
}
|
||||
if (is_null($aThresholdData['deadline']) || ($aThresholdData['deadline'] > time()))
|
||||
{
|
||||
@@ -353,8 +420,18 @@ class ormStopWatch
|
||||
return false;
|
||||
}
|
||||
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
$sAttCode = $oAttDef->GetCode();
|
||||
WorkingTimeRecorder::Start($oObject, time(), "ormStopWatch-TimeSpent-$sAttCode", 'Core:ExplainWTC:StopWatch-TimeSpent', array("Class:$sClass/Attribute:$sAttCode"), true /*cumulative*/);
|
||||
}
|
||||
$iElapsed = $this->ComputeDuration($oObject, $oAttDef, $this->iLastStart, time());
|
||||
$this->iTimeSpent = $this->iTimeSpent + $iElapsed;
|
||||
if (class_exists('WorkingTimeRecorder'))
|
||||
{
|
||||
WorkingTimeRecorder::End();
|
||||
}
|
||||
|
||||
foreach ($this->aThresholds as $iPercent => &$aThresholdData)
|
||||
{
|
||||
@@ -424,9 +501,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.1.0) 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"
|
||||
|
||||
@@ -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())));
|
||||
|
||||
@@ -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)
|
||||
@@ -341,6 +347,29 @@ abstract class User extends cmdbAbstractObject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function CheckToDelete(&$oDeletionPlan)
|
||||
{
|
||||
if (MetaModel::GetConfig()->Get('demo_mode'))
|
||||
{
|
||||
// Users deletion is NOT allowed in demo mode
|
||||
$oDeletionPlan->AddToDelete($this, null);
|
||||
$oDeletionPlan->SetDeletionIssues($this, array('deletion not allowed in demo mode.'), true);
|
||||
$oDeletionPlan->ComputeResults();
|
||||
return false;
|
||||
}
|
||||
return parent::CheckToDelete($oDeletionPlan);
|
||||
}
|
||||
|
||||
protected function DBDeleteSingleObject()
|
||||
{
|
||||
if (MetaModel::GetConfig()->Get('demo_mode'))
|
||||
{
|
||||
// Users deletion is NOT allowed in demo mode
|
||||
return;
|
||||
}
|
||||
parent::DBDeleteSingleObject();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,7 +400,7 @@ abstract class UserInternal extends User
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// When set, this token allows for password reset
|
||||
MetaModel::Init_AddAttribute(new AttributeString("reset_pwd_token", array("allowed_values"=>null, "sql"=>"reset_pwd_token", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeOneWayPassword("reset_pwd_token", array("allowed_values"=>null, "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details
|
||||
@@ -1274,8 +1303,9 @@ class CAS_SelfRegister implements iSelfRegister
|
||||
}
|
||||
else
|
||||
{
|
||||
// No membership required, anybody will pass
|
||||
$bFound = true;
|
||||
// No membership: no way to create the user that should exist prior to authentication
|
||||
phpCAS::log("User ".phpCAS::getUser().": missing user account in iTop (or iTop badly configured, Cf setting cas_memberof)");
|
||||
$bFound = false;
|
||||
}
|
||||
|
||||
if (!$bFound)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -206,6 +206,11 @@ legend.transparent {
|
||||
padding-left:14px;
|
||||
background: url(../images/mini-arrow-orange.gif) no-repeat left;
|
||||
}
|
||||
|
||||
.ui-widget-content td a.cke_toolbox_collapser {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
p a:hover, td a:hover {
|
||||
text-decoration:underline;
|
||||
color:#EB8F00;
|
||||
@@ -1416,3 +1421,7 @@ div.ui-dialog-header {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 7px;
|
||||
}
|
||||
.form_field_error {
|
||||
border: 1px solid #933;
|
||||
background: #fcc;
|
||||
}
|
||||
@@ -22,7 +22,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s %2$s</format>
|
||||
<attributes>
|
||||
<attribute id="item_class"/>
|
||||
<attribute id="temp_id"/>
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ref"/>
|
||||
</attributes>
|
||||
@@ -457,7 +456,7 @@
|
||||
<hidden/>
|
||||
</attribute>
|
||||
<attribute id="agent_id">
|
||||
<must_change/>
|
||||
<must_prompt/>
|
||||
</attribute>
|
||||
<attribute id="agent_email">
|
||||
<hidden/>
|
||||
@@ -466,13 +465,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/>
|
||||
@@ -1205,7 +1204,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ref"/>
|
||||
</attributes>
|
||||
@@ -1306,7 +1304,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_assign">
|
||||
<stimulus>ev_assign</stimulus>
|
||||
<target>assigned</target>
|
||||
<actions/>
|
||||
@@ -1488,7 +1486,7 @@
|
||||
<hidden/>
|
||||
</attribute>
|
||||
<attribute id="agent_id">
|
||||
<must_change/>
|
||||
<must_prompt/>
|
||||
</attribute>
|
||||
<attribute id="agent_email">
|
||||
<hidden/>
|
||||
@@ -1497,13 +1495,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 +1511,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_plan">
|
||||
<stimulus>ev_plan</stimulus>
|
||||
<target>plannedscheduled</target>
|
||||
<actions/>
|
||||
@@ -1589,7 +1587,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_implement">
|
||||
<stimulus>ev_implement</stimulus>
|
||||
<target>implemented</target>
|
||||
<actions/>
|
||||
@@ -1796,12 +1794,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 +1877,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_finish">
|
||||
<stimulus>ev_finish</stimulus>
|
||||
<target>closed</target>
|
||||
<actions>
|
||||
@@ -2166,7 +2164,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ref"/>
|
||||
</attributes>
|
||||
@@ -2465,7 +2462,7 @@
|
||||
<hidden/>
|
||||
</attribute>
|
||||
<attribute id="agent_id">
|
||||
<must_change/>
|
||||
<must_prompt/>
|
||||
</attribute>
|
||||
<attribute id="agent_email">
|
||||
<hidden/>
|
||||
@@ -2474,13 +2471,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/>
|
||||
@@ -3124,7 +3121,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ref"/>
|
||||
</attributes>
|
||||
@@ -3248,12 +3244,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 +3335,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_assign">
|
||||
<stimulus>ev_assign</stimulus>
|
||||
<target>assigned</target>
|
||||
<actions/>
|
||||
@@ -3422,7 +3418,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_reopen">
|
||||
<stimulus>ev_reopen</stimulus>
|
||||
<target>new</target>
|
||||
<actions/>
|
||||
@@ -3471,7 +3467,7 @@
|
||||
<hidden/>
|
||||
</attribute>
|
||||
<attribute id="agent_id">
|
||||
<must_change/>
|
||||
<must_prompt/>
|
||||
</attribute>
|
||||
<attribute id="agent_email">
|
||||
<hidden/>
|
||||
@@ -3480,13 +3476,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 +3498,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_plan">
|
||||
<stimulus>ev_plan</stimulus>
|
||||
<target>plannedscheduled</target>
|
||||
<actions/>
|
||||
@@ -3589,12 +3585,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 +3676,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_implement">
|
||||
<stimulus>ev_implement</stimulus>
|
||||
<target>implemented</target>
|
||||
<actions/>
|
||||
@@ -3763,7 +3759,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_replan">
|
||||
<stimulus>ev_replan</stimulus>
|
||||
<target>plannedscheduled</target>
|
||||
<actions/>
|
||||
@@ -3849,12 +3845,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 +3940,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_finish">
|
||||
<stimulus>ev_finish</stimulus>
|
||||
<target>closed</target>
|
||||
<actions>
|
||||
@@ -4246,7 +4242,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ref"/>
|
||||
</attributes>
|
||||
@@ -4353,7 +4348,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_assign">
|
||||
<stimulus>ev_assign</stimulus>
|
||||
<target>assigned</target>
|
||||
<actions/>
|
||||
@@ -4547,7 +4542,7 @@
|
||||
<hidden/>
|
||||
</attribute>
|
||||
<attribute id="agent_id">
|
||||
<must_change/>
|
||||
<must_prompt/>
|
||||
</attribute>
|
||||
<attribute id="agent_email">
|
||||
<hidden/>
|
||||
@@ -4556,13 +4551,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 +4573,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_plan">
|
||||
<stimulus>ev_plan</stimulus>
|
||||
<target>plannedscheduled</target>
|
||||
<actions/>
|
||||
@@ -4660,12 +4655,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 +4741,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_implement">
|
||||
<stimulus>ev_implement</stimulus>
|
||||
<target>implemented</target>
|
||||
<actions/>
|
||||
@@ -4823,7 +4818,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_replan">
|
||||
<stimulus>ev_replan</stimulus>
|
||||
<target>plannedscheduled</target>
|
||||
<actions/>
|
||||
@@ -4903,12 +4898,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 +4987,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_finish">
|
||||
<stimulus>ev_finish</stimulus>
|
||||
<target>closed</target>
|
||||
<actions>
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -116,7 +115,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -289,7 +287,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field>finalclass</db_final_class_field>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -487,7 +484,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s %2$s</format>
|
||||
<attributes>
|
||||
<attribute id="first_name"/>
|
||||
<attribute id="name"/>
|
||||
@@ -626,7 +622,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -745,7 +740,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="team_id"/>
|
||||
</attributes>
|
||||
@@ -865,7 +859,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field>finalclass</db_final_class_field>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -1031,7 +1024,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -1140,7 +1132,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -1248,7 +1239,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -1371,7 +1361,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -1540,7 +1529,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s / %2$s</format>
|
||||
<attributes>
|
||||
<attribute id="ip"/>
|
||||
<attribute id="ip_mask"/>
|
||||
@@ -1700,7 +1688,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -1821,7 +1808,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field>finalclass</db_final_class_field>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -1887,7 +1873,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -1952,7 +1937,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -2018,7 +2002,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="patch_id"/>
|
||||
</attributes>
|
||||
@@ -2110,7 +2093,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field>finalclass</db_final_class_field>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -2297,7 +2279,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s - %2$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
<attribute id="device_id_friendlyname"/>
|
||||
@@ -2496,7 +2477,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s - %2$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
<attribute id="device_id_friendlyname"/>
|
||||
@@ -2645,7 +2625,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s - %2$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
<attribute id="device_id_friendlyname"/>
|
||||
@@ -2785,7 +2764,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s - %2$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
<attribute id="db_server_instance_id_friendlyname"/>
|
||||
@@ -2945,7 +2923,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -3092,7 +3069,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -3222,7 +3198,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -3373,7 +3348,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s - %2$s</format>
|
||||
<attributes>
|
||||
<attribute id="device_id_friendlyname"/>
|
||||
<attribute id="name"/>
|
||||
@@ -3737,7 +3711,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -3905,7 +3878,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -4097,7 +4069,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -4220,7 +4191,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -4374,7 +4344,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -4562,7 +4531,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -4772,7 +4740,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -4993,7 +4960,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -5201,7 +5167,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -5345,7 +5310,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="group_id"/>
|
||||
</attributes>
|
||||
@@ -5452,7 +5416,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ci_id"/>
|
||||
</attributes>
|
||||
@@ -5565,7 +5528,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ci_id"/>
|
||||
</attributes>
|
||||
@@ -5682,7 +5644,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="solution_id"/>
|
||||
</attributes>
|
||||
@@ -5789,7 +5750,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="solution_id"/>
|
||||
</attributes>
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ref"/>
|
||||
</attributes>
|
||||
@@ -54,7 +53,7 @@
|
||||
<read_only/>
|
||||
</attribute>
|
||||
<attribute id="description">
|
||||
<must_change/>
|
||||
<must_prompt/>
|
||||
</attribute>
|
||||
<attribute id="start_date">
|
||||
<read_only/>
|
||||
@@ -63,28 +62,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 +129,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_assign">
|
||||
<stimulus>ev_assign</stimulus>
|
||||
<target>assigned</target>
|
||||
<actions>
|
||||
@@ -139,7 +138,7 @@
|
||||
</action>
|
||||
</actions>
|
||||
</transition>
|
||||
<transition>
|
||||
<transition id="ev_timeout">
|
||||
<stimulus>ev_timeout</stimulus>
|
||||
<target>escalated_tto</target>
|
||||
<actions/>
|
||||
@@ -204,7 +203,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_assign">
|
||||
<stimulus>ev_assign</stimulus>
|
||||
<target>assigned</target>
|
||||
<actions>
|
||||
@@ -284,17 +283,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 +376,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 +545,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>
|
||||
@@ -1042,7 +1041,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ticket_id"/>
|
||||
</attributes>
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -224,7 +223,6 @@
|
||||
<db_key_field>link_id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>lnkInfraError</format>
|
||||
<attributes/>
|
||||
</naming>
|
||||
<display_template/>
|
||||
@@ -326,7 +324,6 @@
|
||||
<db_key_field>link_id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="link_type"/>
|
||||
</attributes>
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ref"/>
|
||||
</attributes>
|
||||
@@ -213,7 +212,7 @@
|
||||
<read_only/>
|
||||
</attribute>
|
||||
<attribute id="description">
|
||||
<must_change/>
|
||||
<must_prompt/>
|
||||
</attribute>
|
||||
<attribute id="ticket_log">
|
||||
<hidden/>
|
||||
@@ -222,28 +221,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 +267,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_assign">
|
||||
<stimulus>ev_assign</stimulus>
|
||||
<target>assigned</target>
|
||||
<actions>
|
||||
@@ -324,12 +323,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 +397,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>
|
||||
@@ -539,7 +538,11 @@
|
||||
3 => 3,
|
||||
),
|
||||
);
|
||||
$iPriority = $aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')];
|
||||
$iPriority = 1;
|
||||
if (isset($aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')]))
|
||||
{
|
||||
$iPriority = $aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')];
|
||||
}
|
||||
return $iPriority;
|
||||
}]]></code>
|
||||
</method>
|
||||
@@ -753,7 +756,7 @@
|
||||
<parent>ProblemManagement</parent>
|
||||
<definition>
|
||||
<layout>DashboardLayoutTwoCols</layout>
|
||||
<title></title>
|
||||
<title/>
|
||||
<cells>
|
||||
<cell id="0">
|
||||
<rank>0</rank>
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ref"/>
|
||||
</attributes>
|
||||
@@ -91,7 +90,7 @@
|
||||
<read_only/>
|
||||
</attribute>
|
||||
<attribute id="description">
|
||||
<must_change/>
|
||||
<must_prompt/>
|
||||
</attribute>
|
||||
<attribute id="start_date">
|
||||
<read_only/>
|
||||
@@ -100,28 +99,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 +172,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_assign">
|
||||
<stimulus>ev_assign</stimulus>
|
||||
<target>assigned</target>
|
||||
<actions>
|
||||
@@ -182,7 +181,7 @@
|
||||
</action>
|
||||
</actions>
|
||||
</transition>
|
||||
<transition>
|
||||
<transition id="ev_timeout">
|
||||
<stimulus>ev_timeout</stimulus>
|
||||
<target>escalated_tto</target>
|
||||
<actions/>
|
||||
@@ -253,7 +252,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_assign">
|
||||
<stimulus>ev_assign</stimulus>
|
||||
<target>assigned</target>
|
||||
<actions>
|
||||
@@ -339,17 +338,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 +360,7 @@
|
||||
</action>
|
||||
</actions>
|
||||
</transition>
|
||||
<transition>
|
||||
<transition id="ev_freeze">
|
||||
<stimulus>ev_freeze</stimulus>
|
||||
<target>frozen</target>
|
||||
<actions/>
|
||||
@@ -443,12 +442,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 +535,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 +640,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>
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field>finalclass</db_final_class_field>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -201,7 +200,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -376,7 +374,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -567,7 +564,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="customer_contract_id"/>
|
||||
</attributes>
|
||||
@@ -670,7 +666,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="contract_id"/>
|
||||
</attributes>
|
||||
@@ -777,7 +772,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="contract_id"/>
|
||||
</attributes>
|
||||
@@ -886,7 +880,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="contract_id"/>
|
||||
</attributes>
|
||||
@@ -996,7 +989,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="contract_id"/>
|
||||
</attributes>
|
||||
@@ -1091,7 +1083,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -1271,7 +1262,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -1372,7 +1362,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -1455,7 +1444,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
</attributes>
|
||||
@@ -1588,7 +1576,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="sla_id"/>
|
||||
</attributes>
|
||||
@@ -1723,7 +1710,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="service_id"/>
|
||||
</attributes>
|
||||
@@ -1832,7 +1818,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="service_id"/>
|
||||
</attributes>
|
||||
@@ -1941,7 +1926,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="service_id"/>
|
||||
</attributes>
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field>finalclass</db_final_class_field>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ref"/>
|
||||
</attributes>
|
||||
@@ -162,7 +161,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ref"/>
|
||||
</attributes>
|
||||
@@ -422,7 +420,7 @@
|
||||
<read_only/>
|
||||
</attribute>
|
||||
<attribute id="description">
|
||||
<must_change/>
|
||||
<must_prompt/>
|
||||
</attribute>
|
||||
<attribute id="start_date">
|
||||
<read_only/>
|
||||
@@ -431,28 +429,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 +496,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_assign">
|
||||
<stimulus>ev_assign</stimulus>
|
||||
<target>assigned</target>
|
||||
<actions>
|
||||
@@ -507,7 +505,7 @@
|
||||
</action>
|
||||
</actions>
|
||||
</transition>
|
||||
<transition>
|
||||
<transition id="ev_timeout">
|
||||
<stimulus>ev_timeout</stimulus>
|
||||
<target>escalated_tto</target>
|
||||
<actions/>
|
||||
@@ -572,7 +570,7 @@
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions>
|
||||
<transition>
|
||||
<transition id="ev_assign">
|
||||
<stimulus>ev_assign</stimulus>
|
||||
<target>assigned</target>
|
||||
<actions>
|
||||
@@ -652,17 +650,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 +743,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 +912,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>
|
||||
@@ -1157,7 +1155,11 @@
|
||||
3 => 3,
|
||||
),
|
||||
);
|
||||
$iPriority = $aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')];
|
||||
$iPriority = 1;
|
||||
if (isset($aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')]))
|
||||
{
|
||||
$iPriority = $aPriorities[(int)$this->Get('impact')][(int)$this->Get('urgency')];
|
||||
}
|
||||
return $iPriority;
|
||||
}]]></code>
|
||||
</method>
|
||||
@@ -1484,7 +1486,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ticket_id"/>
|
||||
</attributes>
|
||||
@@ -1567,7 +1568,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ticket_id"/>
|
||||
</attributes>
|
||||
@@ -1677,7 +1677,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ticket_id"/>
|
||||
</attributes>
|
||||
|
||||
30
datamodels/2.x/authent-external/da.dict.authent-external.php
Normal file
30
datamodels/2.x/authent-external/da.dict.authent-external.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* @author Erik Bøg <erik@boegmoeller.dk>
|
||||
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @licence http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
Dict::Add('DA DA', 'Danish', 'Dansk', array(
|
||||
'Class:UserExternal' => 'Extern Bruger',
|
||||
'Class:UserExternal+' => 'Bruger udenfor iTop',
|
||||
));
|
||||
?>
|
||||
32
datamodels/2.x/authent-ldap/da.dict.authent-ldap.php
Normal file
32
datamodels/2.x/authent-ldap/da.dict.authent-ldap.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* @author Erik Bøg <erik@boegmoeller.dk>
|
||||
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @licence http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
Dict::Add('DA DA', 'Danish', 'Dansk', array(
|
||||
'Class:UserLDAP' => 'LDAP-Bruger',
|
||||
'Class:UserLDAP+' => 'Bruger der godkendes via LDAP',
|
||||
'Class:UserLDAP/Attribute:password' => 'Password',
|
||||
'Class:UserLDAP/Attribute:password+' => 'Brugerens password',
|
||||
));
|
||||
?>
|
||||
32
datamodels/2.x/authent-local/da.dict.authent-local.php
Normal file
32
datamodels/2.x/authent-local/da.dict.authent-local.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* @author Erik Bøg <erik@boegmoeller.dk>
|
||||
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @licence http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
Dict::Add('DA DA', 'Danish', 'Dansk', array(
|
||||
'Class:UserLocal' => 'iTop-Bruger',
|
||||
'Class:UserLocal+' => 'Bruger der godkendes af iTop',
|
||||
'Class:UserLocal/Attribute:password' => 'Password',
|
||||
'Class:UserLocal/Attribute:password+' => 'Brugerens password',
|
||||
));
|
||||
?>
|
||||
40
datamodels/2.x/itop-attachments/da.dict.itop-attachments.php
Normal file
40
datamodels/2.x/itop-attachments/da.dict.itop-attachments.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* @author Erik Bøg <erik@boegmoeller.dk>
|
||||
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @licence http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
Dict::Add('DA DA', 'Danish', 'Dansk', array(
|
||||
'Attachments:TabTitle_Count' => 'Vedhæftninger (%1$d)',
|
||||
'Attachments:EmptyTabTitle' => 'Vedhæftninger',
|
||||
'Attachments:FieldsetTitle' => 'Vedhæftninger',
|
||||
'Attachments:DeleteBtn' => 'Slet',
|
||||
'Attachments:History_File_Added' => 'Vedhæftning %1$s tilføjet.',
|
||||
'Attachments:History_File_Removed' => 'Vedhæftning %1$s fjernet.',
|
||||
'Attachments:AddAttachment' => 'Vedhæft: ',
|
||||
'Attachments:UploadNotAllowedOnThisSystem' => 'Upload IKKE tilladt i dette system.',
|
||||
'Attachment:Max_Go' => '(Maksimal størrelse: %1$s GB)',
|
||||
'Attachment:Max_Mo' => '(Maksimal størrelse: %1$s MB)',
|
||||
'Attachment:Max_Ko' => '(Maksimal størrelse: %1$s KB)',
|
||||
'Attachments:NoAttachment' => 'Intet vedhæftet. ',
|
||||
));
|
||||
?>
|
||||
@@ -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>
|
||||
@@ -22,7 +22,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s %2$s</format>
|
||||
<attributes>
|
||||
<attribute id="item_class"/>
|
||||
<attribute id="temp_id"/>
|
||||
@@ -37,20 +36,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>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -250,6 +250,7 @@ EOF
|
||||
$('#attachment_plugin').trigger('remove_attachment', [att_id]);
|
||||
return false; // Do not submit the form !
|
||||
}
|
||||
|
||||
function ajaxFileUpload()
|
||||
{
|
||||
//starting setting some animation when the ajax starts and completes
|
||||
@@ -347,7 +348,7 @@ EOF
|
||||
$sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName);
|
||||
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents';
|
||||
$sPreview = $oDoc->IsPreviewAvailable() ? 'true' : 'false';
|
||||
$oPage->add('<div class="attachment" id="display_attachment_'.$iAttId.'"><a data-preview="'.$sPreview.'" href="'.$sDownloadLink.'"><img src="'.$sIcon.'"><br/>'.$sFileName.'<input id="attachment_'+data.result.att_id+'" type="hidden" name="attachments[]" value="'.$iAttId.'"/></a><br/> <input id="btn_remove_'.$iAttId.'" type="button" class="btn_hidden" value="Delete" onClick="RemoveAttachment('.$iAttId.');"/> </div>');
|
||||
$oPage->add('<div class="attachment" id="display_attachment_'.$iAttId.'"><a data-preview="'.$sPreview.'" href="'.$sDownloadLink.'"><img src="'.$sIcon.'"><br/>'.$sFileName.'<input id="attachment_'.$iAttId.'" type="hidden" name="attachments[]" value="'.$iAttId.'"/></a><br/> <input id="btn_remove_'.$iAttId.'" type="button" class="btn_hidden" value="Delete" onClick="RemoveAttachment('.$iAttId.');"/> </div>');
|
||||
$oPage->add_ready_script("$('#attachment_plugin').trigger('add_attachment', [$iAttId, '".addslashes($sFileName)."']);");
|
||||
}
|
||||
}
|
||||
@@ -368,6 +369,7 @@ $oPage->add_ready_script(
|
||||
url: GetAbsoluteUrlModulesRoot()+'itop-attachments/ajax.attachment.php',
|
||||
formData: { operation: 'add', temp_id: '$sTempId', obj_class: '$sClass' },
|
||||
dataType: 'json',
|
||||
pasteZone: null, // Don't accept files via Chrome's copy/paste
|
||||
done: function (e, data) {
|
||||
if(typeof(data.result.error) != 'undefined')
|
||||
{
|
||||
@@ -383,7 +385,7 @@ $oPage->add_ready_script(
|
||||
{
|
||||
$('#display_attachment_'+data.result.att_id).hover( function() { $(this).children(':button').toggleClass('btn_hidden'); } );
|
||||
}
|
||||
$('#attachment_plugin').trigger('add_attachment', [data.result.att_id, data.msg]);
|
||||
$('#attachment_plugin').trigger('add_attachment', [data.result.att_id, data.result.msg]);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -440,7 +442,6 @@ EOF
|
||||
);
|
||||
$oPage->p('<span style="display:none;" id="attachment_loading">Loading, please wait...</span>');
|
||||
$oPage->p('<input type="hidden" id="attachment_plugin" name="attachment_plugin"/>');
|
||||
$oPage->add('</fieldset>');
|
||||
if ($this->m_bDeleteEnabled)
|
||||
{
|
||||
$oPage->add_ready_script('$(".attachment").hover( function() {$(this).children(":button").toggleClass("btn_hidden"); } );');
|
||||
@@ -466,9 +467,12 @@ EOF
|
||||
$oPage->add('<div class="attachment" id="attachment_'.$iAttId.'"><a data-preview="'.$sPreview.'" href="'.$sDownloadLink.'"><img src="'.$sIcon.'"><br/>'.$sFileName.'</a><input type="hidden" name="attachments[]" value="'.$iAttId.'"/><br/> </div>');
|
||||
}
|
||||
}
|
||||
$oPage->add('</span>');
|
||||
}
|
||||
$oPage->add('</fieldset>');
|
||||
$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)
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
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
|
||||
//
|
||||
'label' => 'Tickets attachments',
|
||||
'label' => 'Tickets Attachments',
|
||||
'category' => 'business',
|
||||
|
||||
// Setup
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
48
datamodels/2.x/itop-attachments/nl.dict.itop-attachments.php
Normal file
48
datamodels/2.x/itop-attachments/nl.dict.itop-attachments.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* @author LinProfs <info@linprofs.com>
|
||||
*
|
||||
* Linux & Open Source Professionals
|
||||
* http://www.linprofs.com
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @licence http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'Attachments:TabTitle_Count' => 'Bijlagen (%1$d)',
|
||||
'Attachments:EmptyTabTitle' => 'Bijlagen',
|
||||
'Attachments:FieldsetTitle' => 'Bijlagen',
|
||||
'Attachments:DeleteBtn' => 'Verwijder',
|
||||
'Attachments:History_File_Added' => 'Bijlage %1$s toegevoegd.',
|
||||
'Attachments:History_File_Removed' => 'Bijlage %1$s verwijderd.',
|
||||
'Attachments:AddAttachment' => 'Voeg bijlage toe: ',
|
||||
'Attachments:UploadNotAllowedOnThisSystem' => 'Bestand upload is NIET toegestaan op dit systeem.',
|
||||
'Attachment:Max_Go' => '(Maximum bestandsgrootte: %1$s Go)',
|
||||
'Attachment:Max_Mo' => '(Maximum bestandsgrootte: %1$s Mo)',
|
||||
'Attachment:Max_Ko' => '(Maximum bestandsgrootte: %1$s Ko)',
|
||||
'Attachments:NoAttachment' => 'Geen bijlage. ',
|
||||
'Attachments:PreviewNotAvailable' => 'Een voorbeeld is niet beschikbaar voor dit type bijlage.',
|
||||
'Class:Attachment' => 'Bijlage',
|
||||
'Class:Attachment+' => '',
|
||||
|
||||
));
|
||||
?>
|
||||
181
datamodels/2.x/itop-backup/ajax.backup.php
Normal file
181
datamodels/2.x/itop-backup/ajax.backup.php
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
// Copyright (C) 2013-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* Backup from an interactive session
|
||||
*
|
||||
* @copyright Copyright (C) 2013-2016 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->IsLocked())
|
||||
{
|
||||
$sFile = utils::ReadParam('file', '', false, 'raw_data');
|
||||
$sToken = str_replace(' ', '', (string)microtime());
|
||||
$sTokenFile = APPROOT.'/data/restore.'.$sToken.'.tok';
|
||||
file_put_contents($sTokenFile, $sFile);
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$("#restore_token").val('$sToken');
|
||||
EOF
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->p(Dict::S('bkp-restore-running'));
|
||||
}
|
||||
$oPage->output();
|
||||
break;
|
||||
|
||||
|
||||
case 'restore_exec':
|
||||
require_once(APPROOT."setup/runtimeenv.class.inc.php");
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/setup/backup.class.inc.php');
|
||||
require_once(dirname(__FILE__).'/dbrestore.class.inc.php');
|
||||
|
||||
IssueLog::Enable(APPROOT.'log/error.log');
|
||||
|
||||
$oPage = new ajax_page("");
|
||||
$oPage->no_cache();
|
||||
$oPage->SetContentType('text/html');
|
||||
|
||||
if (utils::GetConfig()->Get('demo_mode'))
|
||||
{
|
||||
$oPage->add("<div data-error-stimulus=\"Error\">Sorry, iTop is in <b>demonstration mode</b>: the feature is disabled.</div>");
|
||||
}
|
||||
else
|
||||
{
|
||||
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
|
||||
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
|
||||
IssueLog::Info("Backup Restore - Acquiring the LOCK 'restore.$sEnvironment'");
|
||||
$oRestoreMutex->Lock();
|
||||
IssueLog::Info('Backup Restore - LOCK acquired, executing...');
|
||||
try
|
||||
{
|
||||
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);
|
||||
|
||||
IssueLog::Info('Backup Restore - Done, releasing the LOCK');
|
||||
$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());
|
||||
}
|
||||
|
||||
?>
|
||||
54
datamodels/2.x/itop-backup/backup.params.distrib
Normal file
54
datamodels/2.x/itop-backup/backup.params.distrib
Normal 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
|
||||
|
||||
200
datamodels/2.x/itop-backup/backup.php
Normal file
200
datamodels/2.x/itop-backup/backup.php
Normal 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();
|
||||
?>
|
||||
267
datamodels/2.x/itop-backup/check-backup.php
Normal file
267
datamodels/2.x/itop-backup/check-backup.php
Normal 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'");
|
||||
}
|
||||
|
||||
?>
|
||||
129
datamodels/2.x/itop-backup/dbrestore.class.inc.php
Normal file
129
datamodels/2.x/itop-backup/dbrestore.class.inc.php
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
48
datamodels/2.x/itop-backup/de.dict.itop-backup.php
Normal file
48
datamodels/2.x/itop-backup/de.dict.itop-backup.php
Normal 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.',
|
||||
));
|
||||
46
datamodels/2.x/itop-backup/en.dict.itop-backup.php
Normal file
46
datamodels/2.x/itop-backup/en.dict.itop-backup.php
Normal 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.',
|
||||
));
|
||||
?>
|
||||
46
datamodels/2.x/itop-backup/fr.dict.itop-backup.php
Normal file
46
datamodels/2.x/itop-backup/fr.dict.itop-backup.php
Normal 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.',
|
||||
));
|
||||
?>
|
||||
286
datamodels/2.x/itop-backup/main.itop-backup.php
Normal file
286
datamodels/2.x/itop-backup/main.itop-backup.php
Normal file
@@ -0,0 +1,286 @@
|
||||
<?php
|
||||
// Copyright (C) 2014-2016 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
|
||||
{
|
||||
$oRestoreMutex = new iTopMutex('restore.'.utils::GetCurrentEnvironment());
|
||||
if ($oRestoreMutex->IsLocked())
|
||||
{
|
||||
IssueLog::Info(__class__.'::'.__function__.' A user is trying to use iTop while a restore is running. The requested page is in read-only mode.');
|
||||
MetaModel::GetConfig()->Set('access_mode', ACCESS_READONLY, 'itop-backup');
|
||||
MetaModel::GetConfig()->Set('access_message', ' - '.dict::S('bkp-restore-running'), 'itop-backup');
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
IssueLog::Error(__class__.'::'.__function__.' Failed to check if a backup/restore is running: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
60
datamodels/2.x/itop-backup/module.itop-backup.php
Normal file
60
datamodels/2.x/itop-backup/module.itop-backup.php
Normal 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
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
?>
|
||||
401
datamodels/2.x/itop-backup/status.php
Normal file
401
datamodels/2.x/itop-backup/status.php
Normal file
@@ -0,0 +1,401 @@
|
||||
<?php
|
||||
// Copyright (C) 2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Monitor the backup
|
||||
*
|
||||
* @copyright Copyright (C) 2016 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->IsLocked())
|
||||
{
|
||||
$sDisableRestore = 'disabled="disabled"';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDisableRestore = '';
|
||||
}
|
||||
|
||||
// 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->IsLocked())
|
||||
{
|
||||
$oP->p(Dict::S('bkp-backup-running'));
|
||||
}
|
||||
$oRestoreMutex = new iTopMutex('restore.'.utils::GetCurrentEnvironment());
|
||||
if ($oRestoreMutex->IsLocked())
|
||||
{
|
||||
$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();
|
||||
?>
|
||||
@@ -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>
|
||||
@@ -11,7 +12,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="volume_id"/>
|
||||
</attributes>
|
||||
@@ -91,4 +91,4 @@
|
||||
</presentation>
|
||||
</class>
|
||||
</classes>
|
||||
</itop_design>
|
||||
</itop_design>
|
||||
|
||||
@@ -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
|
||||
//
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* @author Erik Bøg <erik@boegmoeller.dk>
|
||||
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @licence http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
Dict::Add('DA DA', 'Danish', 'Dansk', array(
|
||||
'Class:RoutineChange' => 'Rutine Ændring',
|
||||
'Class:RoutineChange+' => '',
|
||||
'Class:RoutineChange/Stimulus:ev_assign' => 'Tildel',
|
||||
'Class:RoutineChange/Stimulus:ev_assign+' => '',
|
||||
'Class:RoutineChange/Stimulus:ev_reopen' => 'Genåben',
|
||||
'Class:RoutineChange/Stimulus:ev_reopen+' => '',
|
||||
'Class:RoutineChange/Stimulus:ev_plan' => 'Planlæg',
|
||||
'Class:RoutineChange/Stimulus:ev_plan+' => '',
|
||||
'Class:RoutineChange/Stimulus:ev_approve' => 'Godkend',
|
||||
'Class:RoutineChange/Stimulus:ev_approve+' => '',
|
||||
'Class:RoutineChange/Stimulus:ev_replan' => 'Genplanlæg',
|
||||
'Class:RoutineChange/Stimulus:ev_replan+' => '',
|
||||
'Class:RoutineChange/Stimulus:ev_notapprove' => 'Godkend Ikke',
|
||||
'Class:RoutineChange/Stimulus:ev_notapprove+' => '',
|
||||
'Class:RoutineChange/Stimulus:ev_implement' => 'Implementer',
|
||||
'Class:RoutineChange/Stimulus:ev_implement+' => '',
|
||||
'Class:RoutineChange/Stimulus:ev_monitor' => 'Overvåg',
|
||||
'Class:RoutineChange/Stimulus:ev_monitor+' => '',
|
||||
'Class:RoutineChange/Stimulus:ev_finish' => 'Afslut',
|
||||
'Class:RoutineChange/Stimulus:ev_finish+' => '',
|
||||
'Class:NormalChange' => 'Normal Ændring',
|
||||
'Class:NormalChange+' => '',
|
||||
'Class:NormalChange/Attribute:acceptance_date' => 'Dato for accept',
|
||||
'Class:NormalChange/Attribute:acceptance_date+' => '',
|
||||
'Class:NormalChange/Attribute:acceptance_comment' => 'Kommentar til accept',
|
||||
'Class:NormalChange/Attribute:acceptance_comment+' => '',
|
||||
'Class:NormalChange/Stimulus:ev_validate' => 'Valider',
|
||||
'Class:NormalChange/Stimulus:ev_validate+' => '',
|
||||
'Class:NormalChange/Stimulus:ev_reject' => 'Afslå',
|
||||
'Class:NormalChange/Stimulus:ev_reject+' => '',
|
||||
'Class:NormalChange/Stimulus:ev_assign' => 'Tildel',
|
||||
'Class:NormalChange/Stimulus:ev_assign+' => '',
|
||||
'Class:NormalChange/Stimulus:ev_reopen' => 'Genåben',
|
||||
'Class:NormalChange/Stimulus:ev_reopen+' => '',
|
||||
'Class:NormalChange/Stimulus:ev_plan' => 'Planlæg',
|
||||
'Class:NormalChange/Stimulus:ev_plan+' => '',
|
||||
'Class:NormalChange/Stimulus:ev_approve' => 'Godkend',
|
||||
'Class:NormalChange/Stimulus:ev_approve+' => '',
|
||||
'Class:NormalChange/Stimulus:ev_replan' => 'Genplanlæg',
|
||||
'Class:NormalChange/Stimulus:ev_replan+' => '',
|
||||
'Class:NormalChange/Stimulus:ev_notapprove' => 'Godkent Ikke',
|
||||
'Class:NormalChange/Stimulus:ev_notapprove+' => '',
|
||||
'Class:NormalChange/Stimulus:ev_implement' => 'Implementer',
|
||||
'Class:NormalChange/Stimulus:ev_implement+' => '',
|
||||
'Class:NormalChange/Stimulus:ev_monitor' => 'Overvåg',
|
||||
'Class:NormalChange/Stimulus:ev_monitor+' => '',
|
||||
'Class:NormalChange/Stimulus:ev_finish' => 'Afslut',
|
||||
'Class:NormalChange/Stimulus:ev_finish+' => '',
|
||||
'Class:EmergencyChange' => 'Emergency Ændring',
|
||||
'Class:EmergencyChange+' => '',
|
||||
'Class:EmergencyChange/Stimulus:ev_assign' => 'Tildel',
|
||||
'Class:EmergencyChange/Stimulus:ev_assign+' => '',
|
||||
'Class:EmergencyChange/Stimulus:ev_reopen' => 'Genåben',
|
||||
'Class:EmergencyChange/Stimulus:ev_reopen+' => '',
|
||||
'Class:EmergencyChange/Stimulus:ev_plan' => 'Planlæg',
|
||||
'Class:EmergencyChange/Stimulus:ev_plan+' => '',
|
||||
'Class:EmergencyChange/Stimulus:ev_approve' => 'Godkend',
|
||||
'Class:EmergencyChange/Stimulus:ev_approve+' => '',
|
||||
'Class:EmergencyChange/Stimulus:ev_replan' => 'Genplanlæg',
|
||||
'Class:EmergencyChange/Stimulus:ev_replan+' => '',
|
||||
'Class:EmergencyChange/Stimulus:ev_notapprove' => 'Godkend Ikke',
|
||||
'Class:EmergencyChange/Stimulus:ev_notapprove+' => '',
|
||||
'Class:EmergencyChange/Stimulus:ev_implement' => 'Implementer',
|
||||
'Class:EmergencyChange/Stimulus:ev_implement+' => '',
|
||||
'Class:EmergencyChange/Stimulus:ev_monitor' => 'Overvåg',
|
||||
'Class:EmergencyChange/Stimulus:ev_monitor+' => '',
|
||||
'Class:EmergencyChange/Stimulus:ev_finish' => 'Afslut',
|
||||
'Class:EmergencyChange/Stimulus:ev_finish+' => '',
|
||||
'Menu:ChangeManagement' => 'Change Management',
|
||||
'Menu:Change:Overview' => 'Oversigt',
|
||||
'Menu:Change:Overview+' => '',
|
||||
'Menu:NewChange' => 'Ny Change',
|
||||
'Menu:NewChange+' => 'Opret en ny Change ticket',
|
||||
'Menu:SearchChanges' => 'Søg efter Changes',
|
||||
'Menu:SearchChanges+' => 'Søg efter Change Tickets',
|
||||
'Menu:Change:Shortcuts' => 'Genveje',
|
||||
'Menu:Change:Shortcuts+' => '',
|
||||
'Menu:WaitingAcceptance' => 'Changes, som afventer accept',
|
||||
'Menu:WaitingAcceptance+' => '',
|
||||
'Menu:WaitingApproval' => 'Changes, som afventer godkendelse',
|
||||
'Menu:WaitingApproval+' => '',
|
||||
'Menu:Changes' => 'Offene Changes',
|
||||
'Menu:Changes+' => 'Alle åbne Changes',
|
||||
'Menu:MyChanges' => 'Mine Changes',
|
||||
'Menu:MyChanges+' => 'Changes som er tildelt mig',
|
||||
'UI-ChangeManagementOverview-ChangeByCategory-last-7-days' => 'Changes de sidste 7 dage, efter kategori',
|
||||
'UI-ChangeManagementOverview-Last-7-days' => 'Antal Changes de sidste 7 dage',
|
||||
'UI-ChangeManagementOverview-ChangeByDomain-last-7-days' => 'Changes de sidste 7 dage efter type',
|
||||
'UI-ChangeManagementOverview-ChangeByStatus-last-7-days' => 'Changes de sidste 7 dage efter status',
|
||||
'Class:Change' => 'Change',
|
||||
'Class:Change+' => '',
|
||||
'Class:Change/Attribute:status' => 'Status',
|
||||
'Class:Change/Attribute:status+' => '',
|
||||
'Class:Change/Attribute:status/Value:new' => 'Ny',
|
||||
'Class:Change/Attribute:status/Value:new+' => '',
|
||||
'Class:Change/Attribute:status/Value:validated' => 'Valideret',
|
||||
'Class:Change/Attribute:status/Value:validated+' => '',
|
||||
'Class:Change/Attribute:status/Value:rejected' => 'Afslået',
|
||||
'Class:Change/Attribute:status/Value:rejected+' => '',
|
||||
'Class:Change/Attribute:status/Value:assigned' => 'Tildelt',
|
||||
'Class:Change/Attribute:status/Value:assigned+' => '',
|
||||
'Class:Change/Attribute:status/Value:plannedscheduled' => 'Planlagt og tidssat',
|
||||
'Class:Change/Attribute:status/Value:plannedscheduled+' => '',
|
||||
'Class:Change/Attribute:status/Value:approved' => 'Godkendt',
|
||||
'Class:Change/Attribute:status/Value:approved+' => '',
|
||||
'Class:Change/Attribute:status/Value:notapproved' => 'Ikke Godkendt',
|
||||
'Class:Change/Attribute:status/Value:notapproved+' => '',
|
||||
'Class:Change/Attribute:status/Value:implemented' => 'Implementeret',
|
||||
'Class:Change/Attribute:status/Value:implemented+' => '',
|
||||
'Class:Change/Attribute:status/Value:monitored' => 'Overvåget',
|
||||
'Class:Change/Attribute:status/Value:monitored+' => '',
|
||||
'Class:Change/Attribute:status/Value:closed' => 'Lukket',
|
||||
'Class:Change/Attribute:status/Value:closed+' => '',
|
||||
'Class:Change/Attribute:reason' => 'Årsag',
|
||||
'Class:Change/Attribute:reason+' => '',
|
||||
'Class:Change/Attribute:requestor_id' => 'Rekvirent',
|
||||
'Class:Change/Attribute:requestor_id+' => '',
|
||||
'Class:Change/Attribute:requestor_email' => 'Rekvirent e-mail',
|
||||
'Class:Change/Attribute:requestor_email+' => '',
|
||||
'Class:Change/Attribute:creation_date' => 'Oprettet dato',
|
||||
'Class:Change/Attribute:creation_date+' => '',
|
||||
'Class:Change/Attribute:impact' => 'Indvirkning',
|
||||
'Class:Change/Attribute:impact+' => '',
|
||||
'Class:Change/Attribute:supervisor_group_id' => 'Supervisor-Team',
|
||||
'Class:Change/Attribute:supervisor_group_id+' => '',
|
||||
'Class:Change/Attribute:supervisor_group_name' => 'Supervisor-Teamnavn',
|
||||
'Class:Change/Attribute:supervisor_group_name+' => '',
|
||||
'Class:Change/Attribute:supervisor_id' => 'Supervisor',
|
||||
'Class:Change/Attribute:supervisor_id+' => '',
|
||||
'Class:Change/Attribute:supervisor_email' => 'Supervisor e-mail',
|
||||
'Class:Change/Attribute:supervisor_email+' => '',
|
||||
'Class:Change/Attribute:manager_group_id' => 'Manager-Team',
|
||||
'Class:Change/Attribute:manager_group_id+' => '',
|
||||
'Class:Change/Attribute:manager_group_name' => 'Manager-Team',
|
||||
'Class:Change/Attribute:manager_group_name+' => '',
|
||||
'Class:Change/Attribute:manager_id' => 'Manager',
|
||||
'Class:Change/Attribute:manager_id+' => '',
|
||||
'Class:Change/Attribute:manager_email' => 'Manager',
|
||||
'Class:Change/Attribute:manager_email+' => '',
|
||||
'Class:Change/Attribute:outage' => 'Nedetid',
|
||||
'Class:Change/Attribute:outage+' => '',
|
||||
'Class:Change/Attribute:outage/Value:no' => 'Nej',
|
||||
'Class:Change/Attribute:outage/Value:no+' => '',
|
||||
'Class:Change/Attribute:outage/Value:yes' => 'Ja',
|
||||
'Class:Change/Attribute:outage/Value:yes+' => '',
|
||||
'Class:Change/Attribute:fallback' => 'Fallback-Plan',
|
||||
'Class:Change/Attribute:fallback+' => '',
|
||||
'Class:Change/Attribute:parent_id' => 'Parent Change',
|
||||
'Class:Change/Attribute:parent_id+' => '',
|
||||
'Class:Change/Attribute:parent_name' => 'Parent Change Ref',
|
||||
'Class:Change/Attribute:parent_name+' => '',
|
||||
'Class:Change/Attribute:related_request_list' => 'Relaterede Requests',
|
||||
'Class:Change/Attribute:related_request_list+' => '',
|
||||
'Class:Change/Attribute:related_problems_list' => 'Relaterede Problemer',
|
||||
'Class:Change/Attribute:related_problems_list+' => '',
|
||||
'Class:Change/Attribute:child_changes_list' => 'Afledte Changes',
|
||||
'Class:Change/Attribute:child_changes_list+' => '',
|
||||
'Class:Change/Attribute:parent_id_friendlyname' => 'Parent Friendly Name',
|
||||
'Class:Change/Attribute:parent_id_friendlyname+' => '',
|
||||
'Class:Change/Attribute:parent_id_finalclass_recall' => 'Change-Type',
|
||||
'Class:Change/Attribute:parent_id_finalclass_recall+' => '',
|
||||
'Class:Change/Stimulus:ev_validate' => 'Valider',
|
||||
'Class:Change/Stimulus:ev_validate+' => '',
|
||||
'Class:Change/Stimulus:ev_reject' => 'Afslå',
|
||||
'Class:Change/Stimulus:ev_reject+' => '',
|
||||
'Class:Change/Stimulus:ev_assign' => 'Tildel',
|
||||
'Class:Change/Stimulus:ev_assign+' => '',
|
||||
'Class:Change/Stimulus:ev_reopen' => 'Genåben',
|
||||
'Class:Change/Stimulus:ev_reopen+' => '',
|
||||
'Class:Change/Stimulus:ev_plan' => 'Planlæg',
|
||||
'Class:Change/Stimulus:ev_plan+' => '',
|
||||
'Class:Change/Stimulus:ev_approve' => 'Godkend',
|
||||
'Class:Change/Stimulus:ev_approve+' => '',
|
||||
'Class:Change/Stimulus:ev_replan' => 'Genplanlæg',
|
||||
'Class:Change/Stimulus:ev_replan+' => '',
|
||||
'Class:Change/Stimulus:ev_notapprove' => 'Afslå',
|
||||
'Class:Change/Stimulus:ev_notapprove+' => '',
|
||||
'Class:Change/Stimulus:ev_implement' => 'Implementer',
|
||||
'Class:Change/Stimulus:ev_implement+' => '',
|
||||
'Class:Change/Stimulus:ev_monitor' => 'Overvåg',
|
||||
'Class:Change/Stimulus:ev_monitor+' => '',
|
||||
'Class:Change/Stimulus:ev_finish' => 'Afslut',
|
||||
'Class:Change/Stimulus:ev_finish+' => '',
|
||||
'Class:RoutineChange/Stimulus:ev_validate' => 'Valider',
|
||||
'Class:RoutineChange/Stimulus:ev_validate+' => '',
|
||||
'Class:RoutineChange/Stimulus:ev_reject' => 'Afslå',
|
||||
'Class:RoutineChange/Stimulus:ev_reject+' => '',
|
||||
'Class:ApprovedChange' => 'Godkendte Changes',
|
||||
'Class:ApprovedChange+' => '',
|
||||
'Class:ApprovedChange/Attribute:approval_date' => 'Dato for godkendelse',
|
||||
'Class:ApprovedChange/Attribute:approval_date+' => '',
|
||||
'Class:ApprovedChange/Attribute:approval_comment' => 'Kommentar til godkendelsen',
|
||||
'Class:ApprovedChange/Attribute:approval_comment+' => '',
|
||||
'Class:ApprovedChange/Stimulus:ev_validate' => 'Valider',
|
||||
'Class:ApprovedChange/Stimulus:ev_validate+' => '',
|
||||
'Class:ApprovedChange/Stimulus:ev_reject' => 'Afslå',
|
||||
'Class:ApprovedChange/Stimulus:ev_reject+' => '',
|
||||
'Class:ApprovedChange/Stimulus:ev_assign' => 'Tildel',
|
||||
'Class:ApprovedChange/Stimulus:ev_assign+' => '',
|
||||
'Class:ApprovedChange/Stimulus:ev_reopen' => 'Genåben',
|
||||
'Class:ApprovedChange/Stimulus:ev_reopen+' => '',
|
||||
'Class:ApprovedChange/Stimulus:ev_plan' => 'Planlæg',
|
||||
'Class:ApprovedChange/Stimulus:ev_plan+' => '',
|
||||
'Class:ApprovedChange/Stimulus:ev_approve' => 'Godkend',
|
||||
'Class:ApprovedChange/Stimulus:ev_approve+' => '',
|
||||
'Class:ApprovedChange/Stimulus:ev_replan' => 'Genplanlæg',
|
||||
'Class:ApprovedChange/Stimulus:ev_replan+' => '',
|
||||
'Class:ApprovedChange/Stimulus:ev_notapprove' => 'Tilbagekald godkendelse',
|
||||
'Class:ApprovedChange/Stimulus:ev_notapprove+' => '',
|
||||
'Class:ApprovedChange/Stimulus:ev_implement' => 'Implementer',
|
||||
'Class:ApprovedChange/Stimulus:ev_implement+' => '',
|
||||
'Class:ApprovedChange/Stimulus:ev_monitor' => 'Overvåg',
|
||||
'Class:ApprovedChange/Stimulus:ev_monitor+' => '',
|
||||
'Class:ApprovedChange/Stimulus:ev_finish' => 'Afslut',
|
||||
'Class:ApprovedChange/Stimulus:ev_finish+' => '',
|
||||
'Class:EmergencyChange/Stimulus:ev_validate' => 'Valider',
|
||||
'Class:EmergencyChange/Stimulus:ev_validate+' => '',
|
||||
'Class:EmergencyChange/Stimulus:ev_reject' => 'Afslå',
|
||||
'Class:EmergencyChange/Stimulus:ev_reject+' => '',
|
||||
));
|
||||
?>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
//
|
||||
|
||||
115
datamodels/2.x/itop-change-mgmt/da.dict.itop-change-mgmt.php
Normal file
115
datamodels/2.x/itop-change-mgmt/da.dict.itop-change-mgmt.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* @author Erik Bøg <erik@boegmoeller.dk>
|
||||
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @licence http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
Dict::Add('DA DA', 'Danish', 'Dansk', array(
|
||||
'Class:Change' => 'Change',
|
||||
'Class:Change+' => '',
|
||||
'Class:Change/Attribute:status' => 'Status',
|
||||
'Class:Change/Attribute:status+' => 'Status for emnet',
|
||||
'Class:Change/Attribute:status/Value:new' => 'Ny',
|
||||
'Class:Change/Attribute:status/Value:new+' => 'Opret ny',
|
||||
'Class:Change/Attribute:status/Value:assigned' => 'Tildelt',
|
||||
'Class:Change/Attribute:status/Value:assigned+' => '',
|
||||
'Class:Change/Attribute:status/Value:planned' => 'Planlagt',
|
||||
'Class:Change/Attribute:status/Value:planned+' => '',
|
||||
'Class:Change/Attribute:status/Value:rejected' => 'Afslået',
|
||||
'Class:Change/Attribute:status/Value:rejected+' => '',
|
||||
'Class:Change/Attribute:status/Value:approved' => 'Godkendt',
|
||||
'Class:Change/Attribute:status/Value:approved+' => '',
|
||||
'Class:Change/Attribute:status/Value:closed' => 'Lukket',
|
||||
'Class:Change/Attribute:status/Value:closed+' => '',
|
||||
'Class:Change/Attribute:category' => 'Kategori',
|
||||
'Class:Change/Attribute:category+' => '',
|
||||
'Class:Change/Attribute:category/Value:application' => 'Applikation',
|
||||
'Class:Change/Attribute:category/Value:application+' => '',
|
||||
'Class:Change/Attribute:category/Value:hardware' => 'Hardware',
|
||||
'Class:Change/Attribute:category/Value:hardware+' => '',
|
||||
'Class:Change/Attribute:category/Value:network' => 'Netværk',
|
||||
'Class:Change/Attribute:category/Value:network+' => '',
|
||||
'Class:Change/Attribute:category/Value:other' => 'Andet',
|
||||
'Class:Change/Attribute:category/Value:other+' => '',
|
||||
'Class:Change/Attribute:category/Value:software' => 'Software',
|
||||
'Class:Change/Attribute:category/Value:software+' => '',
|
||||
'Class:Change/Attribute:category/Value:system' => 'System',
|
||||
'Class:Change/Attribute:category/Value:system+' => '',
|
||||
'Class:Change/Attribute:reject_reason' => 'Årsag til afslag',
|
||||
'Class:Change/Attribute:reject_reason+' => '',
|
||||
'Class:Change/Attribute:changemanager_id' => 'Change Manager',
|
||||
'Class:Change/Attribute:changemanager_id+' => '',
|
||||
'Class:Change/Attribute:parent_id' => 'Parent Change',
|
||||
'Class:Change/Attribute:parent_id+' => '',
|
||||
'Class:Change/Attribute:creation_date' => 'Oprettelsesdato',
|
||||
'Class:Change/Attribute:creation_date+' => '',
|
||||
'Class:Change/Attribute:approval_date' => 'Godkendelsesdato',
|
||||
'Class:Change/Attribute:approval_date+' => '',
|
||||
'Class:Change/Attribute:fallback_plan' => 'Fallback-Plan',
|
||||
'Class:Change/Attribute:fallback_plan+' => '',
|
||||
'Class:Change/Attribute:related_request_list' => 'Relaterede Requests',
|
||||
'Class:Change/Attribute:related_request_list+' => '',
|
||||
'Class:Change/Attribute:child_changes_list' => 'Afledte Changes',
|
||||
'Class:Change/Attribute:child_changes_list+' => '',
|
||||
'Class:Change/Stimulus:ev_assign' => 'Tildel',
|
||||
'Class:Change/Stimulus:ev_assign+' => '',
|
||||
'Class:Change/Stimulus:ev_plan' => 'Planlæg',
|
||||
'Class:Change/Stimulus:ev_plan+' => '',
|
||||
'Class:Change/Stimulus:ev_reject' => 'Afslåp',
|
||||
'Class:Change/Stimulus:ev_reject+' => '',
|
||||
'Class:Change/Stimulus:ev_reopen' => 'Genåben',
|
||||
'Class:Change/Stimulus:ev_reopen+' => '',
|
||||
'Class:Change/Stimulus:ev_approve' => 'Godkend',
|
||||
'Class:Change/Stimulus:ev_approve+' => '',
|
||||
'Class:Change/Stimulus:ev_finish' => 'Luk',
|
||||
'Class:Change/Stimulus:ev_finish+' => '',
|
||||
'Menu:ChangeManagement' => 'Change Management',
|
||||
'Menu:Change:Overview' => 'Oversigt',
|
||||
'Menu:Change:Overview+' => '',
|
||||
'Menu:NewChange' => 'Ny Change',
|
||||
'Menu:NewChange+' => '',
|
||||
'Menu:SearchChanges' => 'Søg efter Changes',
|
||||
'Menu:SearchChanges+' => '',
|
||||
'Menu:Change:Shortcuts' => 'Genveje',
|
||||
'Menu:Change:Shortcuts+' => '',
|
||||
'Menu:WaitingAcceptance' => 'Changes der afventer accept',
|
||||
'Menu:WaitingAcceptance+' => '',
|
||||
'Menu:WaitingApproval' => 'Changes der afventer godkendelse',
|
||||
'Menu:WaitingApproval+' => '',
|
||||
'Menu:Changes' => 'Åbne Changes',
|
||||
'Menu:Changes+' => '',
|
||||
'Menu:MyChanges' => 'Changes tildelt til mig',
|
||||
'Menu:MyChanges+' => '',
|
||||
'UI-ChangeManagementOverview-ChangeByCategory-last-7-days' => 'Changes de sidste 7 dage efter kategori',
|
||||
'UI-ChangeManagementOverview-Last-7-days' => 'Antal Changes i de sidste 7 dage',
|
||||
'UI-ChangeManagementOverview-ChangeByDomain-last-7-days' => 'Changes de sidste 7 dage efter type',
|
||||
'UI-ChangeManagementOverview-ChangeByStatus-last-7-days' => 'Changes de sidste 7 dage efter status',
|
||||
'Class:Change/Attribute:changemanager_email' => 'Change Manager Email',
|
||||
'Class:Change/Attribute:changemanager_email+' => '',
|
||||
'Class:Change/Attribute:parent_name' => 'Parent Change ref',
|
||||
'Class:Change/Attribute:parent_name+' => '',
|
||||
'Class:Change/Attribute:related_incident_list' => 'Relaterede Incidents',
|
||||
'Class:Change/Attribute:related_incident_list+' => '',
|
||||
'Class:Change/Attribute:parent_id_friendlyname' => 'Parent Change Friendly Name',
|
||||
'Class:Change/Attribute:parent_id_friendlyname+' => '',
|
||||
));
|
||||
?>
|
||||
@@ -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>
|
||||
@@ -17,7 +17,6 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="ref"/>
|
||||
</attributes>
|
||||
@@ -38,12 +37,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 +50,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 +106,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 +144,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 +217,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 +234,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 +258,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,43 +265,61 @@
|
||||
<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>
|
||||
<verb>SetApprovalDate</verb>
|
||||
<verb>SetCurrentDate</verb>
|
||||
<params>
|
||||
<param xsi:type="attcode">approval_date</param>
|
||||
</params>
|
||||
</action>
|
||||
<action>
|
||||
<verb>ResetRejectReason</verb>
|
||||
<verb>Reset</verb>
|
||||
<params>
|
||||
<param xsi:type="attcode">reject_reason</param>
|
||||
</params>
|
||||
</action>
|
||||
</actions>
|
||||
</transition>
|
||||
</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 +331,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 +347,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 +376,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,78 +403,37 @@
|
||||
<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>
|
||||
<verb>SetClosureDate</verb>
|
||||
<verb>SetCurrentDate</verb>
|
||||
<params>
|
||||
<param xsi:type="attcode">close_date</param>
|
||||
</params>
|
||||
</action>
|
||||
</actions>
|
||||
</transition>
|
||||
</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 +446,6 @@
|
||||
<attribute id="parent_id">
|
||||
<read_only/>
|
||||
</attribute>
|
||||
<attribute id="outage">
|
||||
<read_only/>
|
||||
</attribute>
|
||||
</flags>
|
||||
<transitions/>
|
||||
</state>
|
||||
@@ -558,6 +453,10 @@
|
||||
</lifecycle>
|
||||
<methods>
|
||||
<method id="SetApprovalDate">
|
||||
<comment><![CDATA[/**
|
||||
* To be deprecated: use SetCurrentDate() instead
|
||||
* @return void
|
||||
*/]]></comment>
|
||||
<static>false</static>
|
||||
<access>public</access>
|
||||
<type>LifecycleAction</type>
|
||||
@@ -568,6 +467,10 @@
|
||||
}]]></code>
|
||||
</method>
|
||||
<method id="ResetRejectReason">
|
||||
<comment><![CDATA[/**
|
||||
* To be deprecated: use SetCurrentDate() instead
|
||||
* @return void
|
||||
*/]]></comment>
|
||||
<static>false</static>
|
||||
<access>public</access>
|
||||
<type>LifecycleAction</type>
|
||||
@@ -578,6 +481,10 @@
|
||||
}]]></code>
|
||||
</method>
|
||||
<method id="SetClosureDate">
|
||||
<comment><![CDATA[/**
|
||||
* To be deprecated: use SetCurrentDate() instead
|
||||
* @return void
|
||||
*/]]></comment>
|
||||
<static>false</static>
|
||||
<access>public</access>
|
||||
<type>LifecycleAction</type>
|
||||
@@ -633,63 +540,6 @@
|
||||
$this->Set('last_update', time());
|
||||
}]]></code>
|
||||
</method>
|
||||
<method id="GetIcon">
|
||||
<comment>/**
|
||||
* 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
|
||||
*/</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>
|
||||
|
||||
@@ -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
|
||||
//
|
||||
|
||||
146
datamodels/2.x/itop-change-mgmt/nl.dict.itop-change-mgmt.php
Normal file
146
datamodels/2.x/itop-change-mgmt/nl.dict.itop-change-mgmt.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* @author LinProfs <info@linprofs.com>
|
||||
*
|
||||
* Linux & Open Source Professionals
|
||||
* http://www.linprofs.com
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @licence http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||
'Menu:ChangeManagement' => 'Change management',
|
||||
'Menu:Change:Overview' => 'Overzicht',
|
||||
'Menu:Change:Overview+' => '',
|
||||
'Menu:NewChange' => 'Nieuwe change',
|
||||
'Menu:NewChange+' => 'Maak een nieuwe change ticket aan',
|
||||
'Menu:SearchChanges' => 'Zoek voor changes',
|
||||
'Menu:SearchChanges+' => 'Zoek voor change tickets',
|
||||
'Menu:Change:Shortcuts' => 'Snelkoppelingen',
|
||||
'Menu:Change:Shortcuts+' => '',
|
||||
'Menu:WaitingAcceptance' => 'Changes die nog acceptatie vereisen',
|
||||
'Menu:WaitingAcceptance+' => '',
|
||||
'Menu:WaitingApproval' => 'Changes die nog goedkeuring vereisen',
|
||||
'Menu:WaitingApproval+' => '',
|
||||
'Menu:Changes' => 'Open changes',
|
||||
'Menu:Changes+' => 'Alle open changes',
|
||||
'Menu:MyChanges' => 'Changes toegewezen aan mij',
|
||||
'Menu:MyChanges+' => 'Changes toegewezen door mij (als Agent)',
|
||||
'UI-ChangeManagementOverview-ChangeByCategory-last-7-days' => 'Changes per categorie van de afgelopen 7 dagen',
|
||||
'UI-ChangeManagementOverview-Last-7-days' => 'Aantal changes van de afgelopen 7 dagen',
|
||||
'UI-ChangeManagementOverview-ChangeByDomain-last-7-days' => 'Changes per domein van de afgelopen 7 dagen',
|
||||
'UI-ChangeManagementOverview-ChangeByStatus-last-7-days' => 'Changes per status van de afgelopen 7 dagen',
|
||||
));
|
||||
|
||||
// Dictionnay conventions
|
||||
// Class:<class_name>
|
||||
// Class:<class_name>+
|
||||
// Class:<class_name>/Attribute:<attribute_code>
|
||||
// Class:<class_name>/Attribute:<attribute_code>+
|
||||
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>
|
||||
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+
|
||||
// Class:<class_name>/Stimulus:<stimulus_code>
|
||||
// Class:<class_name>/Stimulus:<stimulus_code>+
|
||||
|
||||
|
||||
//
|
||||
// Class: Change
|
||||
//
|
||||
|
||||
Dict::Add('NL NL', "Dutch", "Nederlands", array(
|
||||
'Class:Change' => 'Change',
|
||||
'Class:Change+' => '',
|
||||
'Class:Change/Attribute:status' => 'Status',
|
||||
'Class:Change/Attribute:status+' => '',
|
||||
'Class:Change/Attribute:status/Value:new' => 'Nieuw',
|
||||
'Class:Change/Attribute:status/Value:new+' => '',
|
||||
'Class:Change/Attribute:status/Value:assigned' => 'Toegewezen',
|
||||
'Class:Change/Attribute:status/Value:assigned+' => '',
|
||||
'Class:Change/Attribute:status/Value:planned' => 'Gepland',
|
||||
'Class:Change/Attribute:status/Value:planned+' => '',
|
||||
'Class:Change/Attribute:status/Value:rejected' => 'Rejected',
|
||||
'Class:Change/Attribute:status/Value:rejected+' => '',
|
||||
'Class:Change/Attribute:status/Value:approved' => 'Goedgekeurd',
|
||||
'Class:Change/Attribute:status/Value:approved+' => '',
|
||||
'Class:Change/Attribute:status/Value:closed' => 'Gesloten',
|
||||
'Class:Change/Attribute:status/Value:closed+' => '',
|
||||
'Class:Change/Attribute:category' => 'Categorie',
|
||||
'Class:Change/Attribute:category+' => '',
|
||||
'Class:Change/Attribute:category/Value:application' => 'applicatie',
|
||||
'Class:Change/Attribute:category/Value:application+' => 'applicatie',
|
||||
'Class:Change/Attribute:category/Value:hardware' => 'hardware',
|
||||
'Class:Change/Attribute:category/Value:hardware+' => 'hardware',
|
||||
'Class:Change/Attribute:category/Value:network' => 'netwerk',
|
||||
'Class:Change/Attribute:category/Value:network+' => 'netwerk',
|
||||
'Class:Change/Attribute:category/Value:other' => 'anders',
|
||||
'Class:Change/Attribute:category/Value:other+' => 'anders',
|
||||
'Class:Change/Attribute:category/Value:software' => 'software',
|
||||
'Class:Change/Attribute:category/Value:software+' => 'software',
|
||||
'Class:Change/Attribute:category/Value:system' => 'systeem',
|
||||
'Class:Change/Attribute:category/Value:system+' => 'systeem',
|
||||
'Class:Change/Attribute:reject_reason' => 'Reden van afwijzing',
|
||||
'Class:Change/Attribute:reject_reason+' => '',
|
||||
'Class:Change/Attribute:changemanager_id' => 'Change manager',
|
||||
'Class:Change/Attribute:changemanager_id+' => '',
|
||||
'Class:Change/Attribute:changemanager_email' => 'Change manager email',
|
||||
'Class:Change/Attribute:changemanager_email+' => '',
|
||||
'Class:Change/Attribute:parent_id' => 'Hoofd change',
|
||||
'Class:Change/Attribute:parent_id+' => '',
|
||||
'Class:Change/Attribute:parent_name' => 'Hoofd change ref',
|
||||
'Class:Change/Attribute:parent_name+' => '',
|
||||
'Class:Change/Attribute:creation_date' => 'Creatie datum',
|
||||
'Class:Change/Attribute:creation_date+' => '',
|
||||
'Class:Change/Attribute:approval_date' => 'Goedkeuring datum',
|
||||
'Class:Change/Attribute:approval_date+' => '',
|
||||
'Class:Change/Attribute:fallback_plan' => 'Backup plan',
|
||||
'Class:Change/Attribute:fallback_plan+' => '',
|
||||
'Class:Change/Attribute:related_request_list' => 'Gerelateerde verzoeken',
|
||||
'Class:Change/Attribute:related_request_list+' => 'Alle gebruikersverzoeken gelinkt aan deze change',
|
||||
'Class:Change/Attribute:related_incident_list' => 'Gerelateerde incidenten',
|
||||
'Class:Change/Attribute:related_incident_list+' => 'Alle incidenten die gelinkt zijn aan deze change',
|
||||
'Class:Change/Attribute:related_problems_list' => 'Gerelateerde problemen',
|
||||
'Class:Change/Attribute:related_problems_list+' => 'Alle problemen gelinkt aan deze change',
|
||||
'Class:Change/Attribute:child_changes_list' => 'Sub changes',
|
||||
'Class:Change/Attribute:child_changes_list+' => 'Alle sub changes gelinkt aan deze change',
|
||||
'Class:Change/Attribute:parent_id_friendlyname' => 'Hoofd change friendly name',
|
||||
'Class:Change/Attribute:parent_id_friendlyname+' => '',
|
||||
'Class:Change/Stimulus:ev_assign' => 'Wijs toe',
|
||||
'Class:Change/Stimulus:ev_assign+' => '',
|
||||
'Class:Change/Stimulus:ev_plan' => 'Plan',
|
||||
'Class:Change/Stimulus:ev_plan+' => '',
|
||||
'Class:Change/Stimulus:ev_reject' => 'Wijs af',
|
||||
'Class:Change/Stimulus:ev_reject+' => '',
|
||||
'Class:Change/Stimulus:ev_reopen' => 'Heropen',
|
||||
'Class:Change/Stimulus:ev_reopen+' => '',
|
||||
'Class:Change/Stimulus:ev_approve' => 'Keur goed',
|
||||
'Class:Change/Stimulus:ev_approve+' => '',
|
||||
'Class:Change/Stimulus:ev_finish' => 'Sluit',
|
||||
'Class:Change/Stimulus:ev_finish+' => '',
|
||||
'Class:Change/Attribute:outage' => 'Storing',
|
||||
'Class:Change/Attribute:outage+' => '',
|
||||
'Class:Change/Attribute:outage/Value:no' => 'Nee',
|
||||
'Class:Change/Attribute:outage/Value:no+' => '',
|
||||
'Class:Change/Attribute:outage/Value:yes' => 'Yes',
|
||||
'Class:Change/Attribute:outage/Value:yes+' => '',
|
||||
));
|
||||
|
||||
?>
|
||||
1099
datamodels/2.x/itop-config-mgmt/da.dict.itop-config-mgmt.php
Normal file
1099
datamodels/2.x/itop-config-mgmt/da.dict.itop-config-mgmt.php
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -289,8 +289,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:Model/Attribute:type/Value:Tablet+' => '',
|
||||
'Class:Model/Attribute:type/Value:TapeLibrary' => 'Tape-Library',
|
||||
'Class:Model/Attribute:type/Value:TapeLibrary+' => '',
|
||||
'Class:Model/Attribute:type/Value:Telephone' => 'Telefon',
|
||||
'Class:Model/Attribute:type/Value:Telephone+' => '',
|
||||
'Class:Model/Attribute:type/Value:Phone' => 'Telefon',
|
||||
'Class:Model/Attribute:type/Value:Phone+' => '',
|
||||
'Class:Model/Attribute:physicaldevices_list' => 'Phyische Geräte',
|
||||
'Class:Model/Attribute:physicaldevices_list+' => '',
|
||||
'Class:NetworkDeviceType' => 'Netzerkgerätetyp',
|
||||
@@ -1019,10 +1019,14 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:lnkGroupToCI/Attribute:group_name+' => '',
|
||||
'Class:lnkGroupToCI/Attribute:ci_name' => 'Name',
|
||||
'Class:lnkGroupToCI/Attribute:ci_name+' => '',
|
||||
'Menu:DataAdministration' => 'Data Management',
|
||||
'Menu:DataAdministration+' => 'Data Management',
|
||||
'Menu:Catalogs' => 'Kataloge',
|
||||
'Menu:Catalogs+' => 'Datentypen',
|
||||
'Menu:Audit' => 'Audit',
|
||||
'Menu:Audit+' => 'Audit',
|
||||
'Menu:CSVImport' => 'CSV-Import',
|
||||
'Menu:CSVImport+' => 'Massenerstellung oder -aktualisierung',
|
||||
'Menu:Organization' => 'Organisationen',
|
||||
'Menu:Organization+' => 'Alle Organisationen',
|
||||
'Menu:Application' => 'Anwendungen',
|
||||
|
||||
@@ -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...',
|
||||
));
|
||||
@@ -1327,8 +1327,8 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Class:Model/Attribute:type/Value:Tablet+' => 'Tablet',
|
||||
'Class:Model/Attribute:type/Value:TapeLibrary' => 'Tape Library',
|
||||
'Class:Model/Attribute:type/Value:TapeLibrary+' => 'Tape Library',
|
||||
'Class:Model/Attribute:type/Value:Telephone' => 'Telephone',
|
||||
'Class:Model/Attribute:type/Value:Telephone+' => 'Telephone',
|
||||
'Class:Model/Attribute:type/Value:Phone' => 'Telephone',
|
||||
'Class:Model/Attribute:type/Value:Phone+' => 'Telephone',
|
||||
'Class:Model/Attribute:physicaldevices_list' => 'Physical devices',
|
||||
'Class:Model/Attribute:physicaldevices_list+' => 'All the physical devices corresponding to this model',
|
||||
));
|
||||
@@ -1770,10 +1770,14 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
//
|
||||
|
||||
Dict::Add('EN US', 'English', 'English', array(
|
||||
'Menu:DataAdministration' => 'Data administration',
|
||||
'Menu:DataAdministration+' => 'Data administration',
|
||||
'Menu:Catalogs' => 'Catalogs',
|
||||
'Menu:Catalogs+' => 'Data types',
|
||||
'Menu:Audit' => 'Audit',
|
||||
'Menu:Audit+' => 'Audit',
|
||||
'Menu:CSVImport' => 'CSV import',
|
||||
'Menu:CSVImport+' => 'Bulk creation or update',
|
||||
'Menu:Organization' => 'Organizations',
|
||||
'Menu:Organization+' => 'All organizations',
|
||||
'Menu:Application' => 'Applications',
|
||||
|
||||
@@ -1331,8 +1331,8 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:Model/Attribute:type/Value:Tablet+' => 'Tableta',
|
||||
'Class:Model/Attribute:type/Value:TapeLibrary' => 'Librería de Cinta',
|
||||
'Class:Model/Attribute:type/Value:TapeLibrary+' => 'Librería de Cinta',
|
||||
'Class:Model/Attribute:type/Value:Telephone' => 'Teléfono',
|
||||
'Class:Model/Attribute:type/Value:Telephone+' => 'Teléfono',
|
||||
'Class:Model/Attribute:type/Value:Phone' => 'Teléfono',
|
||||
'Class:Model/Attribute:type/Value:Phone+' => 'Teléfono',
|
||||
'Class:Model/Attribute:physicaldevices_list' => 'Dispositivo Físico',
|
||||
'Class:Model/Attribute:physicaldevices_list+' => 'Dispositivo Físico',
|
||||
));
|
||||
@@ -1768,10 +1768,14 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Menu:DataAdministration' => 'Administración de Datos',
|
||||
'Menu:DataAdministration+' => 'Administración de Datos',
|
||||
'Menu:Catalogs' => 'Catálogos',
|
||||
'Menu:Catalogs+' => 'Tipos de Datos',
|
||||
'Menu:Audit' => 'Auditoría',
|
||||
'Menu:Audit+' => 'Auditoría',
|
||||
'Menu:CSVImport' => 'Importar CSV',
|
||||
'Menu:CSVImport+' => 'Creación o Actualización Másiva',
|
||||
'Menu:Organization' => 'Organizaciones',
|
||||
'Menu:Organization+' => 'Organizaciones',
|
||||
'Menu:Application' => 'Aplicaciones',
|
||||
|
||||
@@ -1269,8 +1269,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'Class:Model/Attribute:type/Value:Tablet+' => 'Tablette',
|
||||
'Class:Model/Attribute:type/Value:TapeLibrary' => 'Bandothèque',
|
||||
'Class:Model/Attribute:type/Value:TapeLibrary+' => 'Bandothèque',
|
||||
'Class:Model/Attribute:type/Value:Telephone' => 'Téléphone',
|
||||
'Class:Model/Attribute:type/Value:Telephone+' => 'Téléphone',
|
||||
'Class:Model/Attribute:type/Value:Phone' => 'Téléphone',
|
||||
'Class:Model/Attribute:type/Value:Phone+' => 'Téléphone',
|
||||
'Class:Model/Attribute:physicaldevices_list' => 'Matériels',
|
||||
'Class:Model/Attribute:physicaldevices_list+' => '',
|
||||
));
|
||||
@@ -1717,10 +1717,14 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
|
||||
|
||||
Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'Menu:DataAdministration' => 'Administration des données',
|
||||
'Menu:DataAdministration+' => 'Administration des données',
|
||||
'Menu:Catalogs' => 'Catalogues',
|
||||
'Menu:Catalogs+' => 'Types de données',
|
||||
'Menu:Audit' => 'Audit',
|
||||
'Menu:Audit+' => 'Audit',
|
||||
'Menu:CSVImport' => 'Import CSV',
|
||||
'Menu:CSVImport+' => 'Import ou mise à jour en masse',
|
||||
'Menu:Organization' => 'Organisations',
|
||||
'Menu:Organization+' => 'Toutes les organisations',
|
||||
'Menu:Application' => 'Logiciels',
|
||||
@@ -1817,7 +1821,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',
|
||||
|
||||
@@ -641,10 +641,14 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
'Class:Subnet/Tab:FreeIPs' => 'Szabad IP-k',
|
||||
'Class:Subnet/Tab:FreeIPs-count' => 'Szabad IP-k: %1$s',
|
||||
'Class:Subnet/Tab:FreeIPs-explain' => '10 szabad IP cím kivonata',
|
||||
'Menu:DataAdministration' => 'Adat adminisztráció',
|
||||
'Menu:DataAdministration+' => '',
|
||||
'Menu:Catalogs' => 'Katalógusok',
|
||||
'Menu:Catalogs+' => '',
|
||||
'Menu:Audit' => 'Audit',
|
||||
'Menu:Audit+' => '',
|
||||
'Menu:CSVImport' => 'CSV import',
|
||||
'Menu:CSVImport+' => '',
|
||||
'Menu:Organization' => 'Szervezet',
|
||||
'Menu:Organization+' => '',
|
||||
'Menu:Application' => 'Alkalmazások',
|
||||
|
||||
@@ -641,10 +641,14 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
|
||||
'Class:Subnet/Tab:FreeIPs' => 'IP liberi',
|
||||
'Class:Subnet/Tab:FreeIPs-count' => 'IP liberi: %1$s',
|
||||
'Class:Subnet/Tab:FreeIPs-explain' => 'Qui c\'è un estratto di 10 indirizzi IP liberi',
|
||||
'Menu:DataAdministration' => 'Dati di amministrazione',
|
||||
'Menu:DataAdministration+' => '',
|
||||
'Menu:Catalogs' => 'Cataloghi',
|
||||
'Menu:Catalogs+' => 'Tipi di dato',
|
||||
'Menu:Audit' => 'Audit',
|
||||
'Menu:Audit+' => 'Audit',
|
||||
'Menu:CSVImport' => 'Importazione CSV',
|
||||
'Menu:CSVImport+' => '',
|
||||
'Menu:Organization' => 'Organizzazioni',
|
||||
'Menu:Organization+' => 'Tutte le organizzazioni',
|
||||
'Menu:Application' => 'Applicazioni',
|
||||
|
||||
@@ -294,8 +294,8 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
|
||||
'Class:Model/Attribute:type/Value:Tablet+' => 'タブレット',
|
||||
'Class:Model/Attribute:type/Value:TapeLibrary' => 'テープライブラリ',
|
||||
'Class:Model/Attribute:type/Value:TapeLibrary+' => 'テープライブラリ',
|
||||
'Class:Model/Attribute:type/Value:Telephone' => '電話',
|
||||
'Class:Model/Attribute:type/Value:Telephone+' => '電話',
|
||||
'Class:Model/Attribute:type/Value:Phone' => '電話',
|
||||
'Class:Model/Attribute:type/Value:Phone+' => '電話',
|
||||
'Class:Model/Attribute:physicaldevices_list' => '物理デバイス',
|
||||
'Class:Model/Attribute:physicaldevices_list+' => '',
|
||||
'Class:NetworkDeviceType' => 'ネットワークデバイスタイプ',
|
||||
@@ -1012,10 +1012,14 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
|
||||
'Class:lnkGroupToCI/Attribute:group_name+' => '',
|
||||
'Class:lnkGroupToCI/Attribute:ci_name' => '名前',
|
||||
'Class:lnkGroupToCI/Attribute:ci_name+' => '',
|
||||
'Menu:DataAdministration' => 'データ管理',
|
||||
'Menu:DataAdministration+' => 'データ管理',
|
||||
'Menu:Catalogs' => 'カタログ',
|
||||
'Menu:Catalogs+' => 'データタイプ',
|
||||
'Menu:Audit' => '監査',
|
||||
'Menu:Audit+' => '監査',
|
||||
'Menu:CSVImport' => 'CSV インポート',
|
||||
'Menu:CSVImport+' => '一括作成/一括更新',
|
||||
'Menu:Organization' => '組織',
|
||||
'Menu:Organization+' => '全組織',
|
||||
'Menu:Application' => 'アプリケーション',
|
||||
@@ -1085,6 +1089,8 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
|
||||
'Menu:Typology+' => 'トポロジー構成',
|
||||
'Menu:Software' => 'ソフトウエアカタログ',
|
||||
'Menu:Software+' => 'ソフトウエアカタログ',
|
||||
'Menu:OSVersion' => 'OS バージョン',
|
||||
'Menu:OSVersion+' => '',
|
||||
'UI_WelcomeMenu_AllConfigItems' => 'サマリー',
|
||||
'Menu:ConfigManagement:Typology' => '分類構成',
|
||||
'Server:baseinfo' => '基本情報',
|
||||
|
||||
@@ -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
|
||||
//
|
||||
@@ -89,6 +89,7 @@ if (!class_exists('ConfigMgmtInstaller'))
|
||||
self::RenameEnumValueInDB('Software', 'type', 'Webserver', 'WebServer');
|
||||
self::RenameEnumValueInDB('Model', 'type', 'SANswitch', 'SANSwitch');
|
||||
self::RenameEnumValueInDB('Model', 'type', 'IpPhone', 'IPPhone');
|
||||
self::RenameEnumValueInDB('Model', 'type', 'Telephone', 'Phone');
|
||||
self::RenameClassInDB('DBserver', 'DBServer');
|
||||
self::RenameClassInDB('OSfamily', 'OSFamily');
|
||||
self::RenameClassInDB('OSversion', 'OSVersion');
|
||||
|
||||
1875
datamodels/2.x/itop-config-mgmt/nl.dict.itop-config-mgmt.php
Normal file
1875
datamodels/2.x/itop-config-mgmt/nl.dict.itop-config-mgmt.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1322,8 +1322,8 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'Class:Model/Attribute:type/Value:Tablet+' => 'Tablet',
|
||||
'Class:Model/Attribute:type/Value:TapeLibrary' => 'Tape Library',
|
||||
'Class:Model/Attribute:type/Value:TapeLibrary+' => 'Tape Library',
|
||||
'Class:Model/Attribute:type/Value:Telephone' => 'Telefone',
|
||||
'Class:Model/Attribute:type/Value:Telephone+' => 'Telefone',
|
||||
'Class:Model/Attribute:type/Value:Phone' => 'Telefone',
|
||||
'Class:Model/Attribute:type/Value:Phone+' => 'Telefone',
|
||||
'Class:Model/Attribute:physicaldevices_list' => 'Dispositivo físico',
|
||||
'Class:Model/Attribute:physicaldevices_list+' => 'Todos os dispositivos físicos correspondentes a esse modelo',
|
||||
));
|
||||
@@ -1764,17 +1764,20 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
//
|
||||
|
||||
Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||
'Menu:DataAdministration' => 'Administração Dados',
|
||||
'Menu:DataAdministration+' => 'Administração Dados',
|
||||
'Menu:Catalogs' => 'Catálogos',
|
||||
'Menu:Catalogs+' => 'Tipos dados',
|
||||
'Menu:Audit' => 'Auditoria',
|
||||
'Menu:Audit+' => 'Auditoria',
|
||||
'Menu:CSVImport' => 'Importar CSV',
|
||||
'Menu:CSVImport+' => 'Criação ou atualização em massa',
|
||||
'Menu:Organization' => 'Organizações',
|
||||
'Menu:Organization+' => 'Todas organizações',
|
||||
'Menu:Application' => 'Applicações',
|
||||
'Menu:Application+' => 'Todas aplicações',
|
||||
'Menu:DBServer' => 'Serviços Banco de Dados',
|
||||
'Menu:DBServer+' => 'Serviços Banco de Dados',
|
||||
'Menu:Audit' => 'Auditoria',
|
||||
'Menu:ConfigManagement' => 'Gerenciamento Configurações',
|
||||
'Menu:ConfigManagement+' => 'Gerenciamento Configurações',
|
||||
'Menu:ConfigManagementOverview' => 'Visão geral',
|
||||
|
||||
@@ -1322,8 +1322,8 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
|
||||
'Class:Model/Attribute:type/Value:Tablet+' => 'Планшет',
|
||||
'Class:Model/Attribute:type/Value:TapeLibrary' => 'Ленточная библиотека',
|
||||
'Class:Model/Attribute:type/Value:TapeLibrary+' => 'Ленточная библиотека',
|
||||
'Class:Model/Attribute:type/Value:Telephone' => 'Телефон',
|
||||
'Class:Model/Attribute:type/Value:Telephone+' => 'Телефон',
|
||||
'Class:Model/Attribute:type/Value:Phone' => 'Телефон',
|
||||
'Class:Model/Attribute:type/Value:Phone+' => 'Телефон',
|
||||
'Class:Model/Attribute:physicaldevices_list' => 'Устройства',
|
||||
'Class:Model/Attribute:physicaldevices_list+' => 'Устройства',
|
||||
));
|
||||
@@ -1765,10 +1765,14 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
|
||||
//
|
||||
|
||||
Dict::Add('RU RU', 'Russian', 'Русский', array(
|
||||
'Menu:DataAdministration' => 'Административные данные',
|
||||
'Menu:DataAdministration+' => 'Административные данные',
|
||||
'Menu:Catalogs' => 'Каталоги',
|
||||
'Menu:Catalogs+' => 'Каталоги',
|
||||
'Menu:Audit' => 'Аудит',
|
||||
'Menu:Audit+' => 'Аудит',
|
||||
'Menu:CSVImport' => 'Импорт CSV',
|
||||
'Menu:CSVImport+' => 'Пакетное создание или обновление',
|
||||
'Menu:Organization' => 'Организации',
|
||||
'Menu:Organization+' => 'Все организации',
|
||||
'Menu:Application' => 'Приложения',
|
||||
|
||||
@@ -990,10 +990,14 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
|
||||
//
|
||||
|
||||
Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
|
||||
'Menu:DataAdministration' => 'Veri Yönetimi',
|
||||
'Menu:DataAdministration+' => 'Veri Yönetimi',
|
||||
'Menu:Catalogs' => 'Kataloglar',
|
||||
'Menu:Catalogs+' => 'Veri tipleri',
|
||||
'Menu:Audit' => 'Denetleme',
|
||||
'Menu:Audit+' => 'Denetleme',
|
||||
'Menu:CSVImport' => 'CSV dışardan al',
|
||||
'Menu:CSVImport+' => 'Çoklu yaratım veya güncelleme',
|
||||
'Menu:Organization' => 'Kurumlar',
|
||||
'Menu:Organization+' => 'Tüm Kurumlar',
|
||||
'Menu:Application' => 'Uygulamalar',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user