mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 15:34:12 +01:00
Compare commits
43 Commits
3.0.0-rc
...
support/2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd831b5652 | ||
|
|
225b062330 | ||
|
|
5952a861f7 | ||
|
|
2ccb455a54 | ||
|
|
5170af8875 | ||
|
|
e2e0f3f020 | ||
|
|
6f44a11be2 | ||
|
|
017b52d41f | ||
|
|
49a94dac1f | ||
|
|
30509e1b5b | ||
|
|
58785083d5 | ||
|
|
473f7571d7 | ||
|
|
b9fae09164 | ||
|
|
7e6982a9ad | ||
|
|
a0b191f3a3 | ||
|
|
5d56a60361 | ||
|
|
e41b42bb73 | ||
|
|
f062819425 | ||
|
|
17c517f9ed | ||
|
|
ccd4ebf98b | ||
|
|
b0f5432b10 | ||
|
|
6c8a152a23 | ||
|
|
31a58d2e99 | ||
|
|
e7f04ec05e | ||
|
|
c403a3b686 | ||
|
|
046deb125a | ||
|
|
8c967e5e9d | ||
|
|
29dbd73464 | ||
|
|
4a9a5c9f53 | ||
|
|
cc1d89bb4d | ||
|
|
be4a2e52bc | ||
|
|
d2f9458516 | ||
|
|
b038ef5a73 | ||
|
|
4ea82dbf49 | ||
|
|
d02f761c11 | ||
|
|
194a7a2256 | ||
|
|
21e87c74e6 | ||
|
|
8336991c7b | ||
|
|
3407dcc434 | ||
|
|
13c9114a0f | ||
|
|
fb94cd13f5 | ||
|
|
fcef3ef57e | ||
|
|
a0ff64a7ee |
@@ -227,3 +227,437 @@ interface iPageUIExtension
|
||||
public function GetBannerHtml(iTopWebPage $oPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add new operations to the REST/JSON web service
|
||||
*
|
||||
* @package Extensibility
|
||||
* @api
|
||||
* @since 2.0.1
|
||||
*/
|
||||
interface iRestServiceProvider
|
||||
{
|
||||
/**
|
||||
* Enumerate services delivered by this class
|
||||
* @param string $sVersion The version (e.g. 1.0) supported by the services
|
||||
* @return array An array of hash 'verb' => verb, 'description' => description
|
||||
*/
|
||||
public function ListOperations($sVersion);
|
||||
/**
|
||||
* Enumerate services delivered by this class
|
||||
* @param string $sVersion The version (e.g. 1.0) supported by the services
|
||||
* @return RestResult The standardized result structure (at least a message)
|
||||
* @throws Exception in case of internal failure.
|
||||
*/
|
||||
public function ExecOperation($sVersion, $sVerb, $aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimal REST response structure. Derive this structure to add response data and error codes.
|
||||
*
|
||||
* @package Extensibility
|
||||
* @api
|
||||
* @since 2.0.1
|
||||
*/
|
||||
class RestResult
|
||||
{
|
||||
/**
|
||||
* Result: no issue has been encountered
|
||||
*/
|
||||
const OK = 0;
|
||||
/**
|
||||
* Result: missing/wrong credentials or the user does not have enough rights to perform the requested operation
|
||||
*/
|
||||
const UNAUTHORIZED = 1;
|
||||
/**
|
||||
* Result: the parameter 'version' is missing
|
||||
*/
|
||||
const MISSING_VERSION = 2;
|
||||
/**
|
||||
* Result: the parameter 'json_data' is missing
|
||||
*/
|
||||
const MISSING_JSON = 3;
|
||||
/**
|
||||
* Result: the input structure is not a valid JSON string
|
||||
*/
|
||||
const INVALID_JSON = 4;
|
||||
/**
|
||||
* Result: no operation is available for the specified version
|
||||
*/
|
||||
const UNSUPPORTED_VERSION = 10;
|
||||
/**
|
||||
* Result: the requested operation is not valid for the specified version
|
||||
*/
|
||||
const UNKNOWN_OPERATION = 11;
|
||||
/**
|
||||
* Result: the requested operation cannot be performed because it can cause data (integrity) loss
|
||||
*/
|
||||
const UNSAFE = 12;
|
||||
/**
|
||||
* Result: the operation could not be performed, see the message for troubleshooting
|
||||
*/
|
||||
const INTERNAL_ERROR = 100;
|
||||
|
||||
/**
|
||||
* Default constructor - ok!
|
||||
*
|
||||
* @param DBObject $oObject The object being reported
|
||||
* @param string $sAttCode The attribute code (must be valid)
|
||||
* @return string A scalar representation of the value
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->code = RestResult::OK;
|
||||
}
|
||||
|
||||
public $code;
|
||||
public $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpers for implementing REST services
|
||||
*
|
||||
* @package Extensibility
|
||||
* @api
|
||||
*/
|
||||
class RestUtils
|
||||
{
|
||||
/**
|
||||
* Registering tracking information. Any further object modification be associated with the given comment, when the modification gets recorded into the DB
|
||||
*
|
||||
* @param StdClass $oData Structured input data. Must contain 'comment'.
|
||||
* @return void
|
||||
* @throws Exception
|
||||
* @api
|
||||
*/
|
||||
public static function InitTrackingComment($oData)
|
||||
{
|
||||
$sComment = self::GetMandatoryParam($oData, 'comment');
|
||||
CMDBObject::SetTrackInfo($sComment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a mandatory parameter from from a Rest/Json structure.
|
||||
*
|
||||
* @param StdClass $oData Structured input data. Must contain the entry defined by sParamName.
|
||||
* @param string $sParamName Name of the parameter to fetch from the input data
|
||||
* @return void
|
||||
* @throws Exception If the parameter is missing
|
||||
* @api
|
||||
*/
|
||||
public static function GetMandatoryParam($oData, $sParamName)
|
||||
{
|
||||
if (isset($oData->$sParamName))
|
||||
{
|
||||
return $oData->$sParamName;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Missing parameter '$sParamName'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read an optional parameter from from a Rest/Json structure.
|
||||
*
|
||||
* @param StdClass $oData Structured input data.
|
||||
* @param string $sParamName Name of the parameter to fetch from the input data
|
||||
* @param mixed $default Default value if the parameter is not found in the input data
|
||||
* @return void
|
||||
* @throws Exception
|
||||
* @api
|
||||
*/
|
||||
public static function GetOptionalParam($oData, $sParamName, $default)
|
||||
{
|
||||
if (isset($oData->$sParamName))
|
||||
{
|
||||
return $oData->$sParamName;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a class from a Rest/Json structure.
|
||||
*
|
||||
* @param StdClass $oData Structured input data. Must contain the entry defined by sParamName.
|
||||
* @param string $sParamName Name of the parameter to fetch from the input data
|
||||
* @return void
|
||||
* @throws Exception If the parameter is missing or the class is unknown
|
||||
* @api
|
||||
*/
|
||||
public static function GetClass($oData, $sParamName)
|
||||
{
|
||||
$sClass = self::GetMandatoryParam($oData, $sParamName);
|
||||
if (!MetaModel::IsValidClass($sClass))
|
||||
{
|
||||
throw new Exception("$sParamName: '$sClass' is not a valid class'");
|
||||
}
|
||||
return $sClass;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a list of attribute codes from a Rest/Json structure.
|
||||
*
|
||||
* @param string $sClass Name of the class
|
||||
* @param StdClass $oData Structured input data.
|
||||
* @param string $sParamName Name of the parameter to fetch from the input data
|
||||
* @return void
|
||||
* @throws Exception
|
||||
* @api
|
||||
*/
|
||||
public static function GetFieldList($sClass, $oData, $sParamName)
|
||||
{
|
||||
$sFields = self::GetOptionalParam($oData, $sParamName, '*');
|
||||
$aShowFields = array();
|
||||
if ($sFields == '*')
|
||||
{
|
||||
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
$aShowFields[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach(explode(',', $sFields) as $sAttCode)
|
||||
{
|
||||
$sAttCode = trim($sAttCode);
|
||||
if (($sAttCode != 'id') && (!MetaModel::IsValidAttCode($sClass, $sAttCode)))
|
||||
{
|
||||
throw new Exception("$sParamName: invalid attribute code '$sAttCode'");
|
||||
}
|
||||
$aShowFields[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
return $aShowFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and interpret object search criteria from a Rest/Json structure
|
||||
*
|
||||
* @param string $sClass Name of the class
|
||||
* @param StdClass $oCriteria Hash of attribute code => value (can be a substructure or a scalar, depending on the nature of the attriute)
|
||||
* @return object The object found
|
||||
* @throws Exception If the input structure is not valid or it could not find exactly one object
|
||||
*/
|
||||
protected static function FindObjectFromCriteria($sClass, $oCriteria)
|
||||
{
|
||||
$aCriteriaReport = array();
|
||||
if (isset($oCriteria->finalclass))
|
||||
{
|
||||
$sClass = $oCriteria->finalclass;
|
||||
if (!MetaModel::IsValidClass($sClass))
|
||||
{
|
||||
throw new Exception("finalclass: Unknown class '$sClass'");
|
||||
}
|
||||
}
|
||||
$oSearch = new DBObjectSearch($sClass);
|
||||
foreach ($oCriteria as $sAttCode => $value)
|
||||
{
|
||||
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||
$oSearch->AddCondition($sAttCode, $realValue);
|
||||
$aCriteriaReport[] = "$sAttCode: $value ($realValue)";
|
||||
}
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$iCount = $oSet->Count();
|
||||
if ($iCount == 0)
|
||||
{
|
||||
throw new Exception("No item found with criteria: ".implode(', ', $aCriteriaReport));
|
||||
}
|
||||
elseif ($iCount > 1)
|
||||
{
|
||||
throw new Exception("Several items found ($iCount) with criteria: ".implode(', ', $aCriteriaReport));
|
||||
}
|
||||
$res = $oSet->Fetch();
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find an object from a polymorph search specification (Rest/Json)
|
||||
*
|
||||
* @param string $sClass Name of the class
|
||||
* @param mixed $key Either search criteria (substructure), or an object or an OQL string.
|
||||
* @return DBObject The object found
|
||||
* @throws Exception If the input structure is not valid or it could not find exactly one object
|
||||
* @api
|
||||
*/
|
||||
public static function FindObjectFromKey($sClass, $key)
|
||||
{
|
||||
if (is_object($key))
|
||||
{
|
||||
$res = self::FindObjectFromCriteria($sClass, $key);
|
||||
}
|
||||
elseif (is_numeric($key))
|
||||
{
|
||||
$res = MetaModel::GetObject($sClass, $key, false);
|
||||
if (is_null($res))
|
||||
{
|
||||
throw new Exception("Invalid object $sClass::$key");
|
||||
}
|
||||
}
|
||||
elseif (is_string($key))
|
||||
{
|
||||
// OQL
|
||||
$oSearch = DBObjectSearch::FromOQL($key);
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$iCount = $oSet->Count();
|
||||
if ($iCount == 0)
|
||||
{
|
||||
throw new Exception("No item found for query: $key");
|
||||
}
|
||||
elseif ($iCount > 1)
|
||||
{
|
||||
throw new Exception("Several items found ($iCount) for query: $key");
|
||||
}
|
||||
$res = $oSet->Fetch();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Wrong format for key");
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search objects from a polymorph search specification (Rest/Json)
|
||||
*
|
||||
* @param string $sClass Name of the class
|
||||
* @param mixed $key Either search criteria (substructure), or an object or an OQL string.
|
||||
* @return DBObjectSet The search result set
|
||||
* @throws Exception If the input structure is not valid
|
||||
*/
|
||||
public static function GetObjectSetFromKey($sClass, $key)
|
||||
{
|
||||
if (is_object($key))
|
||||
{
|
||||
if (isset($key->finalclass))
|
||||
{
|
||||
$sClass = $key->finalclass;
|
||||
if (!MetaModel::IsValidClass($sClass))
|
||||
{
|
||||
throw new Exception("finalclass: Unknown class '$sClass'");
|
||||
}
|
||||
}
|
||||
|
||||
$oSearch = new DBObjectSearch($sClass);
|
||||
foreach ($key as $sAttCode => $value)
|
||||
{
|
||||
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||
$oSearch->AddCondition($sAttCode, $realValue);
|
||||
}
|
||||
}
|
||||
elseif (is_numeric($key))
|
||||
{
|
||||
$oSearch = new DBObjectSearch($sClass);
|
||||
$oSearch->AddCondition('id', $key);
|
||||
}
|
||||
elseif (is_string($key))
|
||||
{
|
||||
// OQL
|
||||
$oSearch = DBObjectSearch::FromOQL($key);
|
||||
$oObjectSet = new DBObjectSet($oSearch);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Wrong format for key");
|
||||
}
|
||||
$oObjectSet = new DBObjectSet($oSearch);
|
||||
return $oObjectSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpret the Rest/Json value and get a valid attribute value
|
||||
*
|
||||
* @param string $sClass Name of the class
|
||||
* @param string $sAttCode Attribute code
|
||||
* @param mixed $value Depending on the type of attribute (a scalar, or search criteria, or list of related objects...)
|
||||
* @return mixed The value that can be used with DBObject::Set()
|
||||
* @throws Exception If the specification of the value is not valid.
|
||||
* @api
|
||||
*/
|
||||
public static function MakeValue($sClass, $sAttCode, $value)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
|
||||
{
|
||||
throw new Exception("Unknown attribute");
|
||||
}
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef instanceof AttributeExternalKey)
|
||||
{
|
||||
$oExtKeyObject = self::FindObjectFromKey($oAttDef->GetTargetClass(), $value);
|
||||
$value = $oExtKeyObject->GetKey();
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeLinkedSet)
|
||||
{
|
||||
if (!is_array($value))
|
||||
{
|
||||
throw new Exception("A link set must be defined by an array of objects");
|
||||
}
|
||||
$sLnkClass = $oAttDef->GetLinkedClass();
|
||||
$aLinks = array();
|
||||
foreach($value as $oValues)
|
||||
{
|
||||
$oLnk = self::MakeObjectFromFields($sLnkClass, $oValues);
|
||||
$aLinks[] = $oLnk;
|
||||
}
|
||||
$value = DBObjectSet::FromArray($sLnkClass, $aLinks);
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = $oAttDef->FromJSONToValue($value);
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
throw new Exception("$sAttCode: ".$e->getMessage(), $e->getCode());
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpret a Rest/Json structure that defines attribute values, and build an object
|
||||
*
|
||||
* @param string $sClass Name of the class
|
||||
* @param array $aFields A hash of attribute code => value specification.
|
||||
* @return DBObject The newly created object
|
||||
* @throws Exception If the specification of the values is not valid
|
||||
* @api
|
||||
*/
|
||||
public static function MakeObjectFromFields($sClass, $aFields)
|
||||
{
|
||||
$oObject = MetaModel::NewObject($sClass);
|
||||
foreach ($aFields as $sAttCode => $value)
|
||||
{
|
||||
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||
$oObject->Set($sAttCode, $realValue);
|
||||
}
|
||||
return $oObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpret a Rest/Json structure that defines attribute values, and update the given object
|
||||
*
|
||||
* @param DBObject $oObject The object being modified
|
||||
* @param array $aFields A hash of attribute code => value specification.
|
||||
* @return DBObject The object modified
|
||||
* @throws Exception If the specification of the values is not valid
|
||||
* @api
|
||||
*/
|
||||
public static function UpdateObjectFromFields($oObject, $aFields)
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
foreach ($aFields as $sAttCode => $value)
|
||||
{
|
||||
$realValue = self::MakeValue($sClass, $sAttCode, $value);
|
||||
$oObject->Set($sAttCode, $realValue);
|
||||
}
|
||||
return $oObject;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,38 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
{
|
||||
return 'UI.php';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a message diplayed to the end-user next time this object will be displayed
|
||||
* Messages are uniquely identified so that plugins can override standard messages (the final work is given to the last plugin to set the message for a given message id)
|
||||
* In practice, standard messages are recorded at the end but they will not overwrite existing messages
|
||||
*
|
||||
* @param string $sClass The class of the object (must be the final class)
|
||||
* @param int $iKey The identifier of the object
|
||||
* @param string $sMessageId Your id or one of the well-known ids: 'create', 'update' and 'apply_stimulus'
|
||||
* @param string $sMessage The HTML message (must be correctly escaped)
|
||||
* @param string $sSeverity Any of the following: ok, info, error.
|
||||
* @param float $fRank Ordering of the message: smallest displayed first (can be negative)
|
||||
* @param bool $bMustNotExist Do not alter any existing message (considering the id)
|
||||
*
|
||||
*/
|
||||
public static function SetSessionMessage($sClass, $iKey, $sMessageId, $sMessage, $sSeverity, $fRank, $bMustNotExist = false)
|
||||
{
|
||||
$sMessageKey = $sClass.'::'.$iKey;
|
||||
if (!isset($_SESSION['obj_messages'][$sMessageKey]))
|
||||
{
|
||||
$_SESSION['obj_messages'][$sMessageKey] = array();
|
||||
}
|
||||
if (!$bMustNotExist || !array_key_exists($sMessageId, $_SESSION['obj_messages'][$sMessageKey]))
|
||||
{
|
||||
$_SESSION['obj_messages'][$sMessageKey][$sMessageId] = array(
|
||||
'rank' => $fRank,
|
||||
'severity' => $sSeverity,
|
||||
'message' => $sMessage
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
// Standard Header with name, actions menu and history block
|
||||
@@ -102,8 +133,19 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
$sMessageKey = get_class($this).'::'.$this->GetKey();
|
||||
if (array_key_exists('obj_messages', $_SESSION) && array_key_exists($sMessageKey, $_SESSION['obj_messages']))
|
||||
{
|
||||
$sMsgClass = 'message_'.$_SESSION['obj_messages'][$sMessageKey]['severity'];
|
||||
$oPage->add("<div class=\"header_message $sMsgClass\">".$_SESSION['obj_messages'][$sMessageKey]['message']."</div>");
|
||||
$aMessages = array();
|
||||
$aRanks = array();
|
||||
foreach ($_SESSION['obj_messages'][$sMessageKey] as $sMessageId => $aMessageData)
|
||||
{
|
||||
$sMsgClass = 'message_'.$aMessageData['severity'];
|
||||
$aMessages[] = "<div class=\"header_message $sMsgClass\">".$aMessageData['message']."</div>";
|
||||
$aRanks[] = $aMessageData['rank'];
|
||||
}
|
||||
array_multisort($aRanks, $aMessages);
|
||||
foreach ($aMessages as $sMessage)
|
||||
{
|
||||
$oPage->add($sMessage);
|
||||
}
|
||||
unset($_SESSION['obj_messages'][$sMessageKey]);
|
||||
}
|
||||
|
||||
@@ -364,18 +406,24 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
// Display Notifications after the other tabs since this tab disappears in edition
|
||||
if (!$bEditMode)
|
||||
{
|
||||
// Get the actual class of the current object
|
||||
// And look for triggers referring to it
|
||||
// Look for any trigger that considers this object as "In Scope"
|
||||
// If any trigger has been found then display a tab with notifications
|
||||
//
|
||||
$sClass = get_class($this);
|
||||
$sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||
$oTriggerSet = new CMDBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObject AS T WHERE T.target_class IN ('$sClassList')"));
|
||||
if ($oTriggerSet->Count() > 0)
|
||||
$oTriggerSet = new CMDBObjectSet(new DBObjectSearch('Trigger'));
|
||||
$aTriggers = array();
|
||||
while($oTrigger = $oTriggerSet->Fetch())
|
||||
{
|
||||
if($oTrigger->IsInScope($this))
|
||||
{
|
||||
$aTriggers[] = $oTrigger->GetKey();
|
||||
}
|
||||
}
|
||||
if (count($aTriggers) > 0)
|
||||
{
|
||||
// Display notifications regarding the object
|
||||
$iId = $this->GetKey();
|
||||
$oNotifSearch = DBObjectSearch::FromOQL("SELECT EventNotificationEmail AS Ev JOIN TriggerOnObject AS T ON Ev.trigger_id = T.id WHERE T.target_class IN ('$sClassList') AND Ev.object_id = $iId");
|
||||
$sTriggersList = implode(',', $aTriggers);
|
||||
$oNotifSearch = DBObjectSearch::FromOQL("SELECT EventNotificationEmail AS Ev JOIN Trigger AS T ON Ev.trigger_id = T.id WHERE T.id IN ($sTriggersList) AND Ev.object_id = $iId");
|
||||
$oNotifSet = new DBObjectSet($oNotifSearch);
|
||||
$sCount = ($oNotifSet->Count() > 0) ? ' ('.$oNotifSet->Count().')' : '';
|
||||
$oPage->SetCurrentTab(Dict::S('UI:NotificationsTab').$sCount);
|
||||
@@ -504,6 +552,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
{
|
||||
$sTip .= "<p>Synchronized with {$aRow['name']} - {$aRow['description']}</p>";
|
||||
}
|
||||
$sTip = addslashes($sTip);
|
||||
$oPage->add_ready_script("$('#synchro_$sInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
||||
}
|
||||
|
||||
@@ -757,7 +806,10 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
$iDefaultPageSize = appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit());
|
||||
$oSettings->iDefaultPageSize = $iDefaultPageSize;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$oSettings->iDefaultPageSize = 0;
|
||||
}
|
||||
$oSettings->aSortOrder = MetaModel::GetOrderByDefault($sClassName);
|
||||
|
||||
return $oDataTable->Display($oPage, $oSettings, $bDisplayMenu, $sSelectMode, $bViewLink, $aExtraParams);
|
||||
@@ -1143,9 +1195,18 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
$oFinalAttDef = $oAttDef->GetFinalAttDef();
|
||||
if (get_class($oFinalAttDef) == 'AttributeDateTime')
|
||||
{
|
||||
$iDate = AttributeDateTime::GetAsUnixSeconds($oObj->Get($sAttCodeEx));
|
||||
$aRow[] = '<td>'.date('Y-m-d', $iDate).'</td>';
|
||||
$aRow[] = '<td>'.date('H:i:s', $iDate).'</td>';
|
||||
$sDate = $oObj->Get($sAttCodeEx);
|
||||
if ($sDate === null)
|
||||
{
|
||||
$aRow[] = '<td></td>';
|
||||
$aRow[] = '<td></td>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$iDate = AttributeDateTime::GetAsUnixSeconds($sDate);
|
||||
$aRow[] = '<td>'.date('Y-m-d', $iDate).'</td>';
|
||||
$aRow[] = '<td>'.date('H:i:s', $iDate).'</td>';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1754,7 +1815,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
}
|
||||
return "<div>{$sHTMLValue}</div>";
|
||||
}
|
||||
|
||||
|
||||
public function DisplayModifyForm(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
self::$iGlobalFormId++;
|
||||
@@ -2790,6 +2851,7 @@ EOF
|
||||
{
|
||||
$sTip .= "<p>Synchronized with {$aRow['name']} - {$aRow['description']}</p>";
|
||||
}
|
||||
$sTip = addslashes($sTip);
|
||||
$oPage->add_ready_script("$('#synchro_$sInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
||||
}
|
||||
|
||||
@@ -2868,5 +2930,579 @@ EOF
|
||||
}
|
||||
return $aComputedAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form for modifying several objects at once
|
||||
* The form will be submitted to the current page, with the specified additional values
|
||||
*/
|
||||
public static function DisplayBulkModifyForm($oP, $sClass, $aSelectedObj, $sCustomOperation, $sCancelUrl, $aExcludeAttributes = array(), $aContextData = array())
|
||||
{
|
||||
if (count($aSelectedObj) > 0)
|
||||
{
|
||||
$iAllowedCount = count($aSelectedObj);
|
||||
$sSelectedObj = implode(',', $aSelectedObj);
|
||||
|
||||
$sOQL = "SELECT $sClass WHERE id IN (".$sSelectedObj.")";
|
||||
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL));
|
||||
|
||||
// Compute the distribution of the values for each field to determine which of the "scalar" fields are homogenous
|
||||
$aList = MetaModel::ListAttributeDefs($sClass);
|
||||
$aValues = array();
|
||||
foreach($aList as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($oAttDef->IsScalar())
|
||||
{
|
||||
$aValues[$sAttCode] = array();
|
||||
}
|
||||
}
|
||||
while($oObj = $oSet->Fetch())
|
||||
{
|
||||
foreach($aList as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($oAttDef->IsScalar() && $oAttDef->IsWritable())
|
||||
{
|
||||
$currValue = $oObj->Get($sAttCode);
|
||||
if ($oAttDef instanceof AttributeCaseLog)
|
||||
{
|
||||
$currValue = ' '; // Don't put an empty string, in case the field would be considered as mandatory...
|
||||
}
|
||||
if (is_object($currValue)) continue; // Skip non scalar values...
|
||||
if(!array_key_exists($currValue, $aValues[$sAttCode]))
|
||||
{
|
||||
$aValues[$sAttCode][$currValue] = array('count' => 1, 'display' => $oObj->GetAsHTML($sAttCode));
|
||||
}
|
||||
else
|
||||
{
|
||||
$aValues[$sAttCode][$currValue]['count']++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now create an object that has values for the homogenous values only
|
||||
$oDummyObj = new $sClass(); // @@ What if the class is abstract ?
|
||||
$aComments = array();
|
||||
function MyComparison($a, $b) // Sort descending
|
||||
{
|
||||
if ($a['count'] == $b['count'])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return ($a['count'] > $b['count']) ? -1 : 1;
|
||||
}
|
||||
|
||||
$iFormId = cmdbAbstractObject::GetNextFormId(); // Identifier that prefixes all the form fields
|
||||
$sReadyScript = '';
|
||||
$aDependsOn = array();
|
||||
$sFormPrefix = '2_';
|
||||
foreach($aList as $sAttCode => $oAttDef)
|
||||
{
|
||||
$aPrerequisites = MetaModel::GetPrequisiteAttributes($sClass, $sAttCode); // List of attributes that are needed for the current one
|
||||
if (count($aPrerequisites) > 0)
|
||||
{
|
||||
// When 'enabling' a field, all its prerequisites must be enabled too
|
||||
$sFieldList = "['{$sFormPrefix}".implode("','{$sFormPrefix}", $aPrerequisites)."']";
|
||||
$oP->add_ready_script("$('#enable_{$sFormPrefix}{$sAttCode}').bind('change', function(evt, sFormId) { return PropagateCheckBox( this.checked, $sFieldList, true); } );\n");
|
||||
}
|
||||
$aDependents = MetaModel::GetDependentAttributes($sClass, $sAttCode); // List of attributes that are needed for the current one
|
||||
if (count($aDependents) > 0)
|
||||
{
|
||||
// When 'disabling' a field, all its dependent fields must be disabled too
|
||||
$sFieldList = "['{$sFormPrefix}".implode("','{$sFormPrefix}", $aDependents)."']";
|
||||
$oP->add_ready_script("$('#enable_{$sFormPrefix}{$sAttCode}').bind('change', function(evt, sFormId) { return PropagateCheckBox( this.checked, $sFieldList, false); } );\n");
|
||||
}
|
||||
if ($oAttDef->IsScalar() && $oAttDef->IsWritable())
|
||||
{
|
||||
if ($oAttDef->GetEditClass() == 'One Way Password')
|
||||
{
|
||||
|
||||
$sTip = "Unknown values";
|
||||
$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] .= '<div class="multi_values" id="multi_values_'.$sAttCode.'"> ? </div>';
|
||||
$sReadyScript .= 'ToogleField(false, \''.$iFormId.'_'.$sAttCode.'\');'."\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = count($aValues[$sAttCode]);
|
||||
if ($iCount == 1)
|
||||
{
|
||||
// Homogenous value
|
||||
reset($aValues[$sAttCode]);
|
||||
$aKeys = array_keys($aValues[$sAttCode]);
|
||||
$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] .= '<div class="mono_value">1</div>';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-homogenous value
|
||||
$aMultiValues = $aValues[$sAttCode];
|
||||
uasort($aMultiValues, 'MyComparison');
|
||||
$iMaxCount = 5;
|
||||
$sTip = "<p><b>".Dict::Format('UI:BulkModify_Count_DistinctValues', $iCount)."</b><ul>";
|
||||
$index = 0;
|
||||
foreach($aMultiValues as $sCurrValue => $aVal)
|
||||
{
|
||||
$sDisplayValue = empty($aVal['display']) ? '<i>'.Dict::S('Enum:Undefined').'</i>' : str_replace(array("\n", "\r"), " ", $aVal['display']);
|
||||
$sTip .= "<li>".Dict::Format('UI:BulkModify:Value_Exists_N_Times', $sDisplayValue, $aVal['count'])."</li>";
|
||||
$index++;
|
||||
if ($iMaxCount == $index)
|
||||
{
|
||||
$sTip .= "<li>".Dict::Format('UI:BulkModify:N_MoreValues', count($aMultiValues) - $iMaxCount)."</li>";
|
||||
break;
|
||||
}
|
||||
}
|
||||
$sTip .= "</ul></p>";
|
||||
$sTip = addslashes($sTip);
|
||||
$sReadyScript .= "$('#multi_values_$sAttCode').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );";
|
||||
|
||||
$oDummyObj->Set($sAttCode, null);
|
||||
$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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
|
||||
if (($sStateAttCode != '') && ($oDummyObj->GetState() == ''))
|
||||
{
|
||||
// Hmmm, it's not gonna work like this ! Set a default value for the "state"
|
||||
// Maybe we should use the "state" that is the most common among the objects...
|
||||
$aMultiValues = $aValues[$sStateAttCode];
|
||||
uasort($aMultiValues, 'MyComparison');
|
||||
foreach($aMultiValues as $sCurrValue => $aVal)
|
||||
{
|
||||
$oDummyObj->Set($sStateAttCode, $sCurrValue);
|
||||
break;
|
||||
}
|
||||
//$oStateAtt = MetaModel::GetAttributeDef($sClass, $sStateAttCode);
|
||||
//$oDummyObj->Set($sStateAttCode, $oStateAtt->GetDefaultValue());
|
||||
}
|
||||
$oP->add("<div class=\"page_header\">\n");
|
||||
$oP->add("<h1>".$oDummyObj->GetIcon()." ".Dict::Format('UI:Modify_M_ObjectsOf_Class_OutOf_N', $iAllowedCount, $sClass, $iAllowedCount)."</h1>\n");
|
||||
$oP->add("</div>\n");
|
||||
|
||||
$oP->add("<div class=\"wizContainer\">\n");
|
||||
$sDisableFields = json_encode($aExcludeAttributes);
|
||||
|
||||
$aParams = array
|
||||
(
|
||||
'fieldsComments' => $aComments,
|
||||
'noRelations' => true,
|
||||
'custom_operation' => $sCustomOperation,
|
||||
'custom_button' => Dict::S('UI:Button:PreviewModifications'),
|
||||
'selectObj' => $sSelectedObj,
|
||||
'preview_mode' => true,
|
||||
'disabled_fields' => $sDisableFields,
|
||||
'disable_plugins' => true
|
||||
);
|
||||
$aParams = $aParams + $aContextData; // merge keeping associations
|
||||
|
||||
$oDummyObj->DisplayModifyForm($oP, $aParams);
|
||||
$oP->add("</div>\n");
|
||||
$oP->add_ready_script($sReadyScript);
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('.wizContainer button.cancel').unbind('click');
|
||||
$('.wizContainer button.cancel').click( function() { window.location.href = '$sCancelUrl'; } );
|
||||
EOF
|
||||
);
|
||||
|
||||
} // Else no object selected ???
|
||||
else
|
||||
{
|
||||
$oP->p("No object selected !, nothing to do");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the reply made from a form built with DisplayBulkModifyForm
|
||||
*/
|
||||
public static function DoBulkModify($oP, $sClass, $aSelectedObj, $sCustomOperation, $bPreview, $sCancelUrl, $aContextData = array())
|
||||
{
|
||||
$aHeaders = array(
|
||||
'form::select' => array('label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList:not(:disabled)', this.checked);\"></input>", 'description' => Dict::S('UI:SelectAllToggle+')),
|
||||
'object' => array('label' => MetaModel::GetName($sClass), 'description' => Dict::S('UI:ModifiedObject')),
|
||||
'status' => array('label' => Dict::S('UI:BulkModifyStatus'), 'description' => Dict::S('UI:BulkModifyStatus+')),
|
||||
'errors' => array('label' => Dict::S('UI:BulkModifyErrors'), 'description' => Dict::S('UI:BulkModifyErrors+')),
|
||||
);
|
||||
$aRows = array();
|
||||
|
||||
$oP->add("<div class=\"page_header\">\n");
|
||||
$oP->add("<h1>".MetaModel::GetClassIcon($sClass)." ".Dict::Format('UI:Modify_N_ObjectsOf_Class', count($aSelectedObj), $sClass)."</h1>\n");
|
||||
$oP->add("</div>\n");
|
||||
$oP->set_title(Dict::Format('UI:Modify_N_ObjectsOf_Class', count($aSelectedObj), $sClass));
|
||||
if (!$bPreview)
|
||||
{
|
||||
// Not in preview mode, do the update for real
|
||||
$sTransactionId = utils::ReadPostedParam('transaction_id', '');
|
||||
if (!utils::IsTransactionValid($sTransactionId, false))
|
||||
{
|
||||
throw new Exception(Dict::S('UI:Error:ObjectAlreadyUpdated'));
|
||||
}
|
||||
utils::RemoveTransaction($sTransactionId);
|
||||
}
|
||||
foreach($aSelectedObj as $iId)
|
||||
{
|
||||
$oObj = MetaModel::GetObject($sClass, $iId);
|
||||
$aErrors = $oObj->UpdateObjectFromPostedForm('');
|
||||
$bResult = (count($aErrors) == 0);
|
||||
if ($bResult)
|
||||
{
|
||||
list($bResult, $aErrors) = $oObj->CheckToWrite(true /* Enforce Read-only fields */);
|
||||
}
|
||||
if ($bPreview)
|
||||
{
|
||||
$sStatus = $bResult ? Dict::S('UI:BulkModifyStatusOk') : Dict::S('UI:BulkModifyStatusError');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sStatus = $bResult ? Dict::S('UI:BulkModifyStatusModified') : Dict::S('UI:BulkModifyStatusSkipped');
|
||||
}
|
||||
$sCSSClass = $bResult ? HILIGHT_CLASS_NONE : HILIGHT_CLASS_CRITICAL;
|
||||
$sChecked = $bResult ? 'checked' : '';
|
||||
$sDisabled = $bResult ? '' : 'disabled';
|
||||
$aRows[] = array(
|
||||
'form::select' => "<input type=\"checkbox\" class=\"selectList\" $sChecked $sDisabled\"></input>",
|
||||
'object' => $oObj->GetHyperlink(),
|
||||
'status' => $sStatus,
|
||||
'errors' => '<p>'.($bResult ? '': implode('</p><p>', $aErrors)).'</p>',
|
||||
'@class' => $sCSSClass,
|
||||
);
|
||||
if ($bResult && (!$bPreview))
|
||||
{
|
||||
$oObj->DBUpdate();
|
||||
}
|
||||
}
|
||||
$oP->Table($aHeaders, $aRows);
|
||||
if ($bPreview)
|
||||
{
|
||||
$sFormAction = $_SERVER['SCRIPT_NAME']; // No parameter in the URL, the only parameter will be the ones passed through the form
|
||||
// Form to submit:
|
||||
$oP->add("<form method=\"post\" action=\"$sFormAction\" enctype=\"multipart/form-data\">\n");
|
||||
$aDefaults = utils::ReadParam('default', array());
|
||||
$oAppContext = new ApplicationContext();
|
||||
$oP->add($oAppContext->GetForForm());
|
||||
foreach ($aContextData as $sKey => $value)
|
||||
{
|
||||
$oP->add("<input type=\"hidden\" name=\"{$sKey}\" value=\"$value\">\n");
|
||||
}
|
||||
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"$sCustomOperation\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"preview_mode\" value=\"0\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\">\n");
|
||||
$oP->add("<button type=\"button\" class=\"action cancel\" onClick=\"window.location.href='$sCancelUrl'\">".Dict::S('UI:Button:Cancel')."</button> \n");
|
||||
$oP->add("<button type=\"submit\" class=\"action\"><span>".Dict::S('UI:Button:ModifyAll')."</span></button>\n");
|
||||
foreach($_POST as $sKey => $value)
|
||||
{
|
||||
if (preg_match('/attr_(.+)/', $sKey, $aMatches))
|
||||
{
|
||||
// Beware: some values (like durations) are passed as arrays
|
||||
if (is_array($value))
|
||||
{
|
||||
foreach($value as $vKey => $vValue)
|
||||
{
|
||||
$oP->add("<input type=\"hidden\" name=\"{$sKey}[$vKey]\" value=\"$vValue\">\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->add("<input type=\"hidden\" name=\"$sKey\" value=\"$value\">\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
$oP->add("</form>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->add("<button type=\"button\" onClick=\"window.location.href='$sCancelUrl'\" class=\"action\"><span>".Dict::S('UI:Button:Done')."</span></button>\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform all the needed checks to delete one (or more) objects
|
||||
*/
|
||||
public static function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bPreview, $sCustomOperation, $aContextData = array())
|
||||
{
|
||||
$oDeletionPlan = new DeletionPlan();
|
||||
|
||||
foreach($aObjects as $oObj)
|
||||
{
|
||||
if ($bPreview)
|
||||
{
|
||||
$oObj->CheckToDelete($oDeletionPlan);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oObj->DBDeleteTracked(CMDBObject::GetCurrentChange(), null, $oDeletionPlan);
|
||||
}
|
||||
}
|
||||
|
||||
if ($bPreview)
|
||||
{
|
||||
if (count($aObjects) == 1)
|
||||
{
|
||||
$oObj = $aObjects[0];
|
||||
$oP->add("<h1>".Dict::Format('UI:Delete:ConfirmDeletionOf_Name', $oObj->GetName())."</h1>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->add("<h1>".Dict::Format('UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass))."</h1>\n");
|
||||
}
|
||||
// Explain what should be done
|
||||
//
|
||||
$aDisplayData = array();
|
||||
foreach ($oDeletionPlan->ListDeletes() as $sTargetClass => $aDeletes)
|
||||
{
|
||||
foreach ($aDeletes as $iId => $aData)
|
||||
{
|
||||
$oToDelete = $aData['to_delete'];
|
||||
$bAutoDel = (($aData['mode'] == DEL_SILENT) || ($aData['mode'] == DEL_AUTO));
|
||||
if (array_key_exists('issue', $aData))
|
||||
{
|
||||
if ($bAutoDel)
|
||||
{
|
||||
if (isset($aData['requested_explicitely']))
|
||||
{
|
||||
$sConsequence = Dict::Format('UI:Delete:CannotDeleteBecause', $aData['issue']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConsequence = Dict::Format('UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible', $aData['issue']);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConsequence = Dict::Format('UI:Delete:MustBeDeletedManuallyButNotPossible', $aData['issue']);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($bAutoDel)
|
||||
{
|
||||
if (isset($aData['requested_explicitely']))
|
||||
{
|
||||
$sConsequence = ''; // not applicable
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConsequence = Dict::S('UI:Delete:WillBeDeletedAutomatically');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConsequence = Dict::S('UI:Delete:MustBeDeletedManually');
|
||||
}
|
||||
}
|
||||
$aDisplayData[] = array(
|
||||
'class' => MetaModel::GetName(get_class($oToDelete)),
|
||||
'object' => $oToDelete->GetHyperLink(),
|
||||
'consequence' => $sConsequence,
|
||||
);
|
||||
}
|
||||
}
|
||||
foreach ($oDeletionPlan->ListUpdates() as $sRemoteClass => $aToUpdate)
|
||||
{
|
||||
foreach ($aToUpdate as $iId => $aData)
|
||||
{
|
||||
$oToUpdate = $aData['to_reset'];
|
||||
if (array_key_exists('issue', $aData))
|
||||
{
|
||||
$sConsequence = Dict::Format('UI:Delete:CannotUpdateBecause_Issue', $aData['issue']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConsequence = Dict::Format('UI:Delete:WillAutomaticallyUpdate_Fields', $aData['attributes_list']);
|
||||
}
|
||||
$aDisplayData[] = array(
|
||||
'class' => MetaModel::GetName(get_class($oToUpdate)),
|
||||
'object' => $oToUpdate->GetHyperLink(),
|
||||
'consequence' => $sConsequence,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$iImpactedIndirectly = $oDeletionPlan->GetTargetCount() - count($aObjects);
|
||||
if ($iImpactedIndirectly > 0)
|
||||
{
|
||||
if (count($aObjects) == 1)
|
||||
{
|
||||
$oObj = $aObjects[0];
|
||||
$oP->p(Dict::Format('UI:Delete:Count_Objects/LinksReferencing_Object', $iImpactedIndirectly, $oObj->GetName()));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->p(Dict::Format('UI:Delete:Count_Objects/LinksReferencingTheObjects', $iImpactedIndirectly));
|
||||
}
|
||||
$oP->p(Dict::S('UI:Delete:ReferencesMustBeDeletedToEnsureIntegrity'));
|
||||
}
|
||||
|
||||
if (($iImpactedIndirectly > 0) || $oDeletionPlan->FoundStopper())
|
||||
{
|
||||
$aDisplayConfig = array();
|
||||
$aDisplayConfig['class'] = array('label' => 'Class', 'description' => '');
|
||||
$aDisplayConfig['object'] = array('label' => 'Object', 'description' => '');
|
||||
$aDisplayConfig['consequence'] = array('label' => 'Consequence', 'description' => Dict::S('UI:Delete:Consequence+'));
|
||||
$oP->table($aDisplayConfig, $aDisplayData);
|
||||
}
|
||||
|
||||
if ($oDeletionPlan->FoundStopper())
|
||||
{
|
||||
if ($oDeletionPlan->FoundSecurityIssue())
|
||||
{
|
||||
$oP->p(Dict::S('UI:Delete:SorryDeletionNotAllowed'));
|
||||
}
|
||||
elseif ($oDeletionPlan->FoundManualOperation())
|
||||
{
|
||||
$oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations'));
|
||||
}
|
||||
else // $bFoundManualOp
|
||||
{
|
||||
$oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations'));
|
||||
}
|
||||
$oAppContext = new ApplicationContext();
|
||||
$oP->add("<form method=\"post\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::ReadParam('transaction_id')."\">\n");
|
||||
$oP->add("<input type=\"button\" onclick=\"window.history.back();\" value=\"".Dict::S('UI:Button:Back')."\">\n");
|
||||
$oP->add("<input DISABLED type=\"submit\" name=\"\" value=\"".Dict::S('UI:Button:Delete')."\">\n");
|
||||
$oP->add($oAppContext->GetForForm());
|
||||
$oP->add("</form>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (count($aObjects) == 1)
|
||||
{
|
||||
$oObj = $aObjects[0];
|
||||
$id = $oObj->GetKey();
|
||||
$oP->p('<h1>'.Dict::Format('UI:Delect:Confirm_Object', $oObj->GetHyperLink()).'</h1>');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->p('<h1>'.Dict::Format('UI:Delect:Confirm_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass)).'</h1>');
|
||||
}
|
||||
foreach($aObjects as $oObj)
|
||||
{
|
||||
$aKeys[] = $oObj->GetKey();
|
||||
}
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
$oFilter->AddCondition('id', $aKeys, 'IN');
|
||||
$oSet = new CMDBobjectSet($oFilter);
|
||||
$oP->add('<div id="0">');
|
||||
CMDBAbstractObject::DisplaySet($oP, $oSet, array('display_limit' => false, 'menu' => false));
|
||||
$oP->add("</div>\n");
|
||||
$oP->add("<form method=\"post\">\n");
|
||||
foreach ($aContextData as $sKey => $value)
|
||||
{
|
||||
$oP->add("<input type=\"hidden\" name=\"{$sKey}\" value=\"$value\">\n");
|
||||
}
|
||||
$oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"$sCustomOperation\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"filter\" value=\"".$oFilter->Serialize()."\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">\n");
|
||||
foreach($aObjects as $oObj)
|
||||
{
|
||||
$oP->add("<input type=\"hidden\" name=\"selectObject[]\" value=\"".$oObj->GetKey()."\">\n");
|
||||
}
|
||||
$oP->add("<input type=\"button\" onclick=\"window.history.back();\" value=\"".Dict::S('UI:Button:Back')."\">\n");
|
||||
$oP->add("<input type=\"submit\" name=\"\" value=\"".Dict::S('UI:Button:Delete')."\">\n");
|
||||
$oAppContext = new ApplicationContext();
|
||||
$oP->add($oAppContext->GetForForm());
|
||||
$oP->add("</form>\n");
|
||||
}
|
||||
}
|
||||
else // if ($bPreview)...
|
||||
{
|
||||
// Execute the deletion
|
||||
//
|
||||
if (count($aObjects) == 1)
|
||||
{
|
||||
$oObj = $aObjects[0];
|
||||
$oP->add("<h1>".Dict::Format('UI:Title:DeletionOf_Object', $oObj->GetName())."</h1>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->add("<h1>".Dict::Format('UI:Title:BulkDeletionOf_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass))."</h1>\n");
|
||||
}
|
||||
// Security - do not allow the user to force a forbidden delete by the mean of page arguments...
|
||||
if ($oDeletionPlan->FoundSecurityIssue())
|
||||
{
|
||||
throw new CoreException(Dict::S('UI:Error:NotEnoughRightsToDelete'));
|
||||
}
|
||||
if ($oDeletionPlan->FoundManualOperation())
|
||||
{
|
||||
throw new CoreException(Dict::S('UI:Error:CannotDeleteBecauseManualOpNeeded'));
|
||||
}
|
||||
if ($oDeletionPlan->FoundManualDelete())
|
||||
{
|
||||
throw new CoreException(Dict::S('UI:Error:CannotDeleteBecauseOfDepencies'));
|
||||
}
|
||||
|
||||
// Report deletions
|
||||
//
|
||||
$aDisplayData = array();
|
||||
foreach ($oDeletionPlan->ListDeletes() as $sTargetClass => $aDeletes)
|
||||
{
|
||||
foreach ($aDeletes as $iId => $aData)
|
||||
{
|
||||
$oToDelete = $aData['to_delete'];
|
||||
|
||||
if (isset($aData['requested_explicitely']))
|
||||
{
|
||||
$sMessage = Dict::S('UI:Delete:Deleted');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sMessage = Dict::S('UI:Delete:AutomaticallyDeleted');
|
||||
}
|
||||
$aDisplayData[] = array(
|
||||
'class' => MetaModel::GetName(get_class($oToDelete)),
|
||||
'object' => $oToDelete->GetName(),
|
||||
'consequence' => $sMessage,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Report updates
|
||||
//
|
||||
foreach ($oDeletionPlan->ListUpdates() as $sTargetClass => $aToUpdate)
|
||||
{
|
||||
foreach ($aToUpdate as $iId => $aData)
|
||||
{
|
||||
$oToUpdate = $aData['to_reset'];
|
||||
$aDisplayData[] = array(
|
||||
'class' => MetaModel::GetName(get_class($oToUpdate)),
|
||||
'object' => $oToUpdate->GetHyperLink(),
|
||||
'consequence' => Dict::Format('UI:Delete:AutomaticResetOf_Fields', $aData['attributes_list']),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Report automatic jobs
|
||||
//
|
||||
if ($oDeletionPlan->GetTargetCount() > 0)
|
||||
{
|
||||
if (count($aObjects) == 1)
|
||||
{
|
||||
$oObj = $aObjects[0];
|
||||
$oP->p(Dict::Format('UI:Delete:CleaningUpRefencesTo_Object', $oObj->GetName()));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->p(Dict::Format('UI:Delete:CleaningUpRefencesTo_Several_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass)));
|
||||
}
|
||||
$aDisplayConfig = array();
|
||||
$aDisplayConfig['class'] = array('label' => 'Class', 'description' => '');
|
||||
$aDisplayConfig['object'] = array('label' => 'Object', 'description' => '');
|
||||
$aDisplayConfig['consequence'] = array('label' => 'Done', 'description' => Dict::S('UI:Delete:Done+'));
|
||||
$oP->table($aDisplayConfig, $aDisplayData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -60,6 +60,10 @@ class DataTable
|
||||
{
|
||||
// Custom settings overload the default ones
|
||||
$this->bUseCustomSettings = true;
|
||||
if ($this->oDefaultSettings->iDefaultPageSize == 0)
|
||||
{
|
||||
$oCustomSettings->iDefaultPageSize = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -176,6 +180,8 @@ class DataTable
|
||||
if ($iPageSize < 1) // Display all
|
||||
{
|
||||
$sPagerStyle = 'style="display:none"'; // no limit: display the full table, so hide the "pager" UI
|
||||
// WARNING: mPDF does not take the "display" style into account
|
||||
// when applied to a <td> or a <table> tag, so make sure you apply this to a div
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -226,7 +232,8 @@ class DataTable
|
||||
$sSelectionMode = ($iNbPages == 1) ? '' : 'positive';
|
||||
$sHtml =
|
||||
<<<EOF
|
||||
<td $sPagerStyle colspan="2">
|
||||
<td colspan="2">
|
||||
<div $sPagerStyle>
|
||||
<table id="pager{$this->iListId}" class="pager"><tr>
|
||||
<td>$sPages</td>
|
||||
<td><img src="../images/first.png" class="first"/></td>
|
||||
@@ -239,6 +246,7 @@ class DataTable
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
EOF;
|
||||
return $sHtml;
|
||||
|
||||
@@ -59,6 +59,11 @@ class DisplayBlock
|
||||
$this->m_aParams = $aParams;
|
||||
$this->m_oSet = $oSet;
|
||||
}
|
||||
|
||||
public function GetFilter()
|
||||
{
|
||||
return $this->m_oFilter;
|
||||
}
|
||||
/**
|
||||
* Constructs a DisplayBlock object from a DBObjectSet already in memory
|
||||
* @param $oSet DBObjectSet
|
||||
@@ -390,7 +395,7 @@ class DisplayBlock
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy);
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
|
||||
$aGroupBy = array();
|
||||
@@ -888,7 +893,7 @@ EOF
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy);
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
|
||||
$aGroupBy = array();
|
||||
@@ -963,7 +968,7 @@ EOF
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy);
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
|
||||
$aGroupBy = array();
|
||||
@@ -1044,7 +1049,8 @@ EOF
|
||||
|
||||
$aGroupBy = array();
|
||||
$aGroupBy['grouped_by_1'] = $oGroupByExp;
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy);
|
||||
|
||||
$sSql = MetaModel::MakeGroupByQuery($this->m_oFilter, $aQueryParams, $aGroupBy, true);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
|
||||
$aGroupBy = array();
|
||||
|
||||
@@ -480,6 +480,25 @@ EOF
|
||||
$sForm = $this->GetSiloSelectionForm();
|
||||
$this->DisplayMenu(); // Compute the menu
|
||||
|
||||
// Call the extensions to add content to the page, so that they can also add styles or scripts
|
||||
$sBannerExtraHtml = '';
|
||||
foreach (MetaModel::EnumPlugins('iPageUIExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sBannerExtraHtml .= $oExtensionInstance->GetBannerHtml($this);
|
||||
}
|
||||
|
||||
$sNorthPane = '';
|
||||
foreach (MetaModel::EnumPlugins('iPageUIExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sNorthPane .= $oExtensionInstance->GetNorthPaneHtml($this);
|
||||
}
|
||||
|
||||
$sSouthPane = '';
|
||||
foreach (MetaModel::EnumPlugins('iPageUIExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sSouthPane .= $oExtensionInstance->GetSouthPaneHtml($this);
|
||||
}
|
||||
|
||||
// Put here the 'ready scripts' that must be executed after all others
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
@@ -711,26 +730,13 @@ EOF
|
||||
$sApplicationBanner .= '<div id="admin-banner"><span style="padding:5px;">'.Dict::Format('UI:ApplicationEnvironment', $sEnvLabel).$sBackButton.'<span></div>';
|
||||
}
|
||||
|
||||
foreach (MetaModel::EnumPlugins('iPageUIExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sApplicationBanner .= $oExtensionInstance->GetBannerHtml($this);
|
||||
}
|
||||
$sApplicationBanner .= $sBannerExtraHtml;
|
||||
|
||||
$sNorthPane = '';
|
||||
foreach (MetaModel::EnumPlugins('iPageUIExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sNorthPane .= $oExtensionInstance->GetNorthPaneHtml($this);
|
||||
}
|
||||
if (!empty($sNorthPane))
|
||||
{
|
||||
$sNorthPane = '<div id="bottom-pane" class="ui-layout-south">'.$sNorthPane.'</div>';
|
||||
$sNorthPane = '<div id="bottom-pane" class="ui-layout-north">'.$sNorthPane.'</div>';
|
||||
}
|
||||
|
||||
$sSouthPane = '';
|
||||
foreach (MetaModel::EnumPlugins('iPageUIExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sSouthPane .= $oExtensionInstance->GetSouthPaneHtml($this);
|
||||
}
|
||||
if (!empty($sSouthPane))
|
||||
{
|
||||
$sSouthPane = '<div id="bottom-pane" class="ui-layout-south">'.$sSouthPane.'</div>';
|
||||
@@ -761,7 +767,7 @@ EOF
|
||||
$sHtml .= ' </div>';
|
||||
$sHtml .= ' </div> <!-- /inner menu -->';
|
||||
$sHtml .= ' </div> <!-- /menu -->';
|
||||
$sHtml .= ' <div class="footer ui-layout-south"><a href="http://www.combodo.com" title="www.combodo.com" target="_blank"><img src="../images/logo-combodo.png"/></a></div>';
|
||||
$sHtml .= ' <div class="footer ui-layout-south"><div id="combodo_logo"><a href="http://www.combodo.com" title="www.combodo.com" target="_blank"><img src="../images/logo-combodo.png"/></a></div></div>';
|
||||
$sHtml .= '<!-- End of the left pane -->';
|
||||
$sHtml .= '</div>';
|
||||
|
||||
|
||||
@@ -283,9 +283,29 @@ class ObjectDetailsTemplate extends DisplayTemplate
|
||||
$sStateAttCode = MetaModel :: GetStateAttributeCode(get_class($this->m_oObj));
|
||||
$aTemplateFields = array();
|
||||
preg_match_all('/\\$this->([a-z0-9_]+)\\$/', $this->m_sTemplate, $aMatches);
|
||||
$aTemplateFields = $aMatches[1];
|
||||
foreach ($aMatches[1] as $sAttCode)
|
||||
{
|
||||
if (MetaModel::IsValidAttCode(get_class($this->m_oObj), $sAttCode))
|
||||
{
|
||||
$aTemplateFields[] = $sAttCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aParams['this->'.$sAttCode] = "<!--Unknown attribute: $sAttCode-->";
|
||||
}
|
||||
}
|
||||
preg_match_all('/\\$this->field\\(([a-z0-9_]+)\\)\\$/', $this->m_sTemplate, $aMatches);
|
||||
$aTemplateFields = array_merge($aTemplateFields, $aMatches[1]);
|
||||
foreach ($aMatches[1] as $sAttCode)
|
||||
{
|
||||
if (MetaModel::IsValidAttCode(get_class($this->m_oObj), $sAttCode))
|
||||
{
|
||||
$aTemplateFields[] = $sAttCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aParams['this->field('.$sAttCode.')'] = "<!--Unknown attribute: $sAttCode-->";
|
||||
}
|
||||
}
|
||||
$aFieldsComments = (isset($aParams['fieldsComments'])) ? $aParams['fieldsComments'] : array();
|
||||
$aFieldsMap = array();
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ class UIExtKeyWidget
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTML fragment corresponding to the linkset editing widget
|
||||
* Get the HTML fragment corresponding to the ext key editing widget
|
||||
* @param WebPage $oP The web page used for all the output
|
||||
* @param Hash $aArgs Extra context arguments
|
||||
* @return string The HTML fragment to be inserted into the page
|
||||
@@ -225,6 +225,15 @@ EOF
|
||||
// Too many choices, use an autocomplete
|
||||
$sSelectMode = 'false';
|
||||
|
||||
// Check that the given value is allowed
|
||||
$oSearch = $oAllowedValues->GetFilter();
|
||||
$oSearch->AddCondition('id', $value);
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
if ($oSet->Count() == 0)
|
||||
{
|
||||
$value = null;
|
||||
}
|
||||
|
||||
if (is_null($value) || ($value == 0)) // Null values are displayed as ''
|
||||
{
|
||||
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : '';
|
||||
@@ -346,6 +355,10 @@ EOF
|
||||
}
|
||||
|
||||
$oFilter = DBObjectSearch::FromOQL($sFilter);
|
||||
if (strlen($sRemoteClass) > 0)
|
||||
{
|
||||
$oFilter->ChangeClass($sRemoteClass);
|
||||
}
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false, array('query_params' => array('this' => $oObj)));
|
||||
$oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'table_id' => 'select_'.$this->sAttCode)); // Don't display the 'Actions' menu on the results
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -37,69 +37,77 @@ class XMLPage extends WebPage
|
||||
var $m_bPassThrough;
|
||||
var $m_bHeaderSent;
|
||||
|
||||
function __construct($s_title, $bPassThrough = false)
|
||||
{
|
||||
parent::__construct($s_title);
|
||||
$this->m_bPassThrough = $bPassThrough;
|
||||
$this->m_bHeaderSent = false;
|
||||
$this->add_header("Content-type: text/xml; charset=utf-8");
|
||||
function __construct($s_title, $bPassThrough = false)
|
||||
{
|
||||
parent::__construct($s_title);
|
||||
$this->m_bPassThrough = $bPassThrough;
|
||||
$this->m_bHeaderSent = false;
|
||||
$this->add_header("Content-type: text/xml; charset=utf-8");
|
||||
$this->add_header("Cache-control: no-cache");
|
||||
$this->add_header("Content-location: export.xml");
|
||||
}
|
||||
}
|
||||
|
||||
public function output()
|
||||
{
|
||||
if (!$this->m_bPassThrough)
|
||||
{
|
||||
$this->add("<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n");
|
||||
$this->add_header("Content-Length: ".strlen(trim($this->s_content)));
|
||||
foreach($this->a_headers as $s_header)
|
||||
{
|
||||
header($s_header);
|
||||
}
|
||||
echo trim($this->s_content);
|
||||
}
|
||||
if (class_exists('MetaModel'))
|
||||
{
|
||||
MetaModel::RecordQueryTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public function add($sText)
|
||||
{
|
||||
if (!$this->m_bPassThrough)
|
||||
{
|
||||
parent::add($sText);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($this->m_bHeaderSent)
|
||||
{
|
||||
echo $sText;
|
||||
}
|
||||
else
|
||||
{
|
||||
$s_captured_output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
foreach($this->a_headers as $s_header)
|
||||
{
|
||||
header($s_header);
|
||||
}
|
||||
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n";
|
||||
echo trim($s_captured_output);
|
||||
echo trim($this->s_content);
|
||||
echo $sText;
|
||||
$this->m_bHeaderSent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function small_p($sText)
|
||||
{
|
||||
public function output()
|
||||
{
|
||||
if (!$this->m_bPassThrough)
|
||||
{
|
||||
$this->s_content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n".trim($this->s_content);
|
||||
$this->add_header("Content-Length: ".strlen($this->s_content));
|
||||
foreach($this->a_headers as $s_header)
|
||||
{
|
||||
header($s_header);
|
||||
}
|
||||
echo $this->s_content;
|
||||
}
|
||||
if (class_exists('MetaModel'))
|
||||
{
|
||||
MetaModel::RecordQueryTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function add($sText)
|
||||
{
|
||||
if (!$this->m_bPassThrough)
|
||||
{
|
||||
parent::add($sText);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($this->m_bHeaderSent)
|
||||
{
|
||||
echo $sText;
|
||||
}
|
||||
else
|
||||
{
|
||||
$s_captured_output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
foreach($this->a_headers as $s_header)
|
||||
{
|
||||
header($s_header);
|
||||
}
|
||||
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n";
|
||||
echo trim($s_captured_output);
|
||||
echo trim($this->s_content);
|
||||
echo $sText;
|
||||
$this->m_bHeaderSent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function small_p($sText)
|
||||
{
|
||||
}
|
||||
|
||||
public function table($aConfig, $aData, $aParams = array())
|
||||
{
|
||||
}
|
||||
|
||||
public function TrashUnexpectedOutput()
|
||||
{
|
||||
if (!$this->m_bPassThrough)
|
||||
{
|
||||
parent::TrashUnexpectedOutput();
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -213,8 +213,12 @@ class ActionEmail extends ActionNotification
|
||||
$aRecipients = array();
|
||||
while ($oObj = $oSet->Fetch())
|
||||
{
|
||||
$aRecipients[] = $oObj->Get($sEmailAttCode);
|
||||
$this->m_iRecipients++;
|
||||
$sAddress = trim($oObj->Get($sEmailAttCode));
|
||||
if (strlen($sAddress) > 0)
|
||||
{
|
||||
$aRecipients[] = $sAddress;
|
||||
$this->m_iRecipients++;
|
||||
}
|
||||
}
|
||||
return implode(', ', $aRecipients);
|
||||
}
|
||||
|
||||
@@ -139,6 +139,7 @@ class AsyncSendEmail extends AsyncTask
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("version", array("allowed_values"=>null, "sql"=>"version", "default_value"=>Email::ORIGINAL_FORMAT, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("to", array("allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLongText("message", array("allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
@@ -161,7 +162,10 @@ class AsyncSendEmail extends AsyncTask
|
||||
$oNew->Set('to', $oEMail->GetRecipientTO(true /* string */));
|
||||
$oNew->Set('subject', $oEMail->GetSubject());
|
||||
|
||||
$sMessage = serialize($oEMail);
|
||||
// $oNew->Set('version', 1);
|
||||
// $sMessage = serialize($oEMail);
|
||||
$oNew->Set('version', 2);
|
||||
$sMessage = $oEMail->SerializeV2();
|
||||
$oNew->Set('message', $sMessage);
|
||||
$oNew->DBInsert();
|
||||
}
|
||||
@@ -169,7 +173,20 @@ class AsyncSendEmail extends AsyncTask
|
||||
public function DoProcess()
|
||||
{
|
||||
$sMessage = $this->Get('message');
|
||||
$oEMail = unserialize($sMessage);
|
||||
$iVersion = (int) $this->Get('version');
|
||||
switch($iVersion)
|
||||
{
|
||||
case Email::FORMAT_V2:
|
||||
$oEMail = Email::UnSerializeV2($sMessage);
|
||||
break;
|
||||
|
||||
case Email::ORIGINAL_FORMAT:
|
||||
$oEMail = unserialize($sMessage);
|
||||
break;
|
||||
|
||||
default:
|
||||
return 'Unknown version of the serialization format: '.$iVersion;
|
||||
}
|
||||
$iRes = $oEMail->Send($aIssues, true /* force synchro !!!!! */);
|
||||
switch ($iRes)
|
||||
{
|
||||
|
||||
@@ -212,15 +212,12 @@ abstract class AttributeDefinition
|
||||
public function IsNullAllowed() {return true;}
|
||||
public function GetCode() {return $this->m_sCode;}
|
||||
|
||||
public function GetLabel($sDefault = null)
|
||||
/**
|
||||
* Helper to browse the hierarchy of classes, searching for a label
|
||||
*/
|
||||
protected function SearchLabel($sDictEntrySuffix, $sDefault, $bUserLanguageOnly)
|
||||
{
|
||||
// If no default value is specified, let's define the most relevant one for developping purposes
|
||||
if (is_null($sDefault))
|
||||
{
|
||||
$sDefault = str_replace('_', ' ', $this->m_sCode);
|
||||
}
|
||||
|
||||
$sLabel = Dict::S('Class:'.$this->m_sHostClass.'/Attribute:'.$this->m_sCode, '');
|
||||
$sLabel = Dict::S('Class:'.$this->m_sHostClass.$sDictEntrySuffix, '', $bUserLanguageOnly);
|
||||
if (strlen($sLabel) == 0)
|
||||
{
|
||||
// Nothing found: go higher in the hierarchy (if possible)
|
||||
@@ -232,13 +229,29 @@ abstract class AttributeDefinition
|
||||
if (MetaModel::IsValidAttCode($sParentClass, $this->m_sCode))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sParentClass, $this->m_sCode);
|
||||
$sLabel = $oAttDef->GetLabel($sDefault);
|
||||
$sLabel = $oAttDef->SearchLabel($sDictEntrySuffix, $sDefault, $bUserLanguageOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
|
||||
public function GetLabel($sDefault = null)
|
||||
{
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode, null, true /*user lang*/);
|
||||
if (is_null($sLabel))
|
||||
{
|
||||
// If no default value is specified, let's define the most relevant one for developping purposes
|
||||
if (is_null($sDefault))
|
||||
{
|
||||
$sDefault = str_replace('_', ' ', $this->m_sCode);
|
||||
}
|
||||
// Browse the hierarchy again, accepting default (english) translations
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode, $sDefault, false);
|
||||
}
|
||||
return $sLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label corresponding to the given value (in plain text)
|
||||
* To be overloaded for localized enums
|
||||
@@ -272,52 +285,32 @@ abstract class AttributeDefinition
|
||||
|
||||
public function GetDescription($sDefault = null)
|
||||
{
|
||||
// If no default value is specified, let's define the most relevant one for developping purposes
|
||||
if (is_null($sDefault))
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'+', null, true /*user lang*/);
|
||||
if (is_null($sLabel))
|
||||
{
|
||||
$sDefault = '';
|
||||
}
|
||||
$sLabel = Dict::S('Class:'.$this->m_sHostClass.'/Attribute:'.$this->m_sCode.'+', '');
|
||||
if (strlen($sLabel) == 0)
|
||||
{
|
||||
// Nothing found: go higher in the hierarchy (if possible)
|
||||
//
|
||||
$sLabel = $sDefault;
|
||||
$sParentClass = MetaModel::GetParentClass($this->m_sHostClass);
|
||||
if ($sParentClass)
|
||||
// If no default value is specified, let's define the most relevant one for developping purposes
|
||||
if (is_null($sDefault))
|
||||
{
|
||||
if (MetaModel::IsValidAttCode($sParentClass, $this->m_sCode))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sParentClass, $this->m_sCode);
|
||||
$sLabel = $oAttDef->GetDescription($sDefault);
|
||||
}
|
||||
$sDefault = '';
|
||||
}
|
||||
// Browse the hierarchy again, accepting default (english) translations
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'+', $sDefault, false);
|
||||
}
|
||||
return $sLabel;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetHelpOnEdition($sDefault = null)
|
||||
{
|
||||
// If no default value is specified, let's define the most relevant one for developping purposes
|
||||
if (is_null($sDefault))
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'?', null, true /*user lang*/);
|
||||
if (is_null($sLabel))
|
||||
{
|
||||
$sDefault = '';
|
||||
}
|
||||
$sLabel = Dict::S('Class:'.$this->m_sHostClass.'/Attribute:'.$this->m_sCode.'?', '');
|
||||
if (strlen($sLabel) == 0)
|
||||
{
|
||||
// Nothing found: go higher in the hierarchy (if possible)
|
||||
//
|
||||
$sLabel = $sDefault;
|
||||
$sParentClass = MetaModel::GetParentClass($this->m_sHostClass);
|
||||
if ($sParentClass)
|
||||
// If no default value is specified, let's define the most relevant one for developping purposes
|
||||
if (is_null($sDefault))
|
||||
{
|
||||
if (MetaModel::IsValidAttCode($sParentClass, $this->m_sCode))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sParentClass, $this->m_sCode);
|
||||
$sLabel = $oAttDef->GetHelpOnEdition($sDefault);
|
||||
}
|
||||
$sDefault = '';
|
||||
}
|
||||
// Browse the hierarchy again, accepting default (english) translations
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'?', $sDefault, false);
|
||||
}
|
||||
return $sLabel;
|
||||
}
|
||||
@@ -433,6 +426,26 @@ abstract class AttributeDefinition
|
||||
return (string)$sValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get a value that will be JSON encoded
|
||||
* The operation is the opposite to FromJSONToValue
|
||||
*/
|
||||
public function GetForJSON($value)
|
||||
{
|
||||
// In most of the cases, that will be the expected behavior...
|
||||
return $this->GetEditValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to form a value, given JSON decoded data
|
||||
* The operation is the opposite to GetForJSON
|
||||
*/
|
||||
public function FromJSONToValue($json)
|
||||
{
|
||||
// Passthrough in most of the cases
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to display the value in the GUI
|
||||
*/
|
||||
@@ -471,11 +484,14 @@ abstract class AttributeDefinition
|
||||
$sLabel = $this->GetLabel();
|
||||
}
|
||||
|
||||
$sNewValueHtml = $this->GetAsHTML($sNewValue);
|
||||
$sOldValueHtml = $this->GetAsHTML($sOldValue);
|
||||
|
||||
if($this->IsExternalKey())
|
||||
{
|
||||
$sTargetClass = $this->GetTargetClass();
|
||||
$sOldValue = (int)$sOldValue ? MetaModel::GetHyperLink($sTargetClass, (int)$sOldValue) : null;
|
||||
$sNewValue = (int)$sNewValue ? MetaModel::GetHyperLink($sTargetClass, (int)$sNewValue) : null;
|
||||
$sOldValueHtml = (int)$sOldValue ? MetaModel::GetHyperLink($sTargetClass, (int)$sOldValue) : null;
|
||||
$sNewValueHtml = (int)$sNewValue ? MetaModel::GetHyperLink($sTargetClass, (int)$sNewValue) : null;
|
||||
}
|
||||
if ( (($this->GetType() == 'String') || ($this->GetType() == 'Text')) &&
|
||||
(strlen($sNewValue) > strlen($sOldValue)) )
|
||||
@@ -483,27 +499,27 @@ abstract class AttributeDefinition
|
||||
// Check if some text was not appended to the field
|
||||
if (substr($sNewValue,0, strlen($sOldValue)) == $sOldValue) // Text added at the end
|
||||
{
|
||||
$sDelta = substr($sNewValue, strlen($sOldValue));
|
||||
$sDelta = $this->GetAsHTML(substr($sNewValue, strlen($sOldValue)));
|
||||
$sResult = Dict::Format('Change:Text_AppendedTo_AttName', $sDelta, $sLabel);
|
||||
}
|
||||
else if (substr($sNewValue, -strlen($sOldValue)) == $sOldValue) // Text added at the beginning
|
||||
{
|
||||
$sDelta = substr($sNewValue, 0, strlen($sNewValue) - strlen($sOldValue));
|
||||
$sDelta = $this->GetAsHTML(substr($sNewValue, 0, strlen($sNewValue) - strlen($sOldValue)));
|
||||
$sResult = Dict::Format('Change:Text_AppendedTo_AttName', $sDelta, $sLabel);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strlen($sOldValue) == 0)
|
||||
{
|
||||
$sResult = Dict::Format('Change:AttName_SetTo', $sLabel, $sNewValue);
|
||||
$sResult = Dict::Format('Change:AttName_SetTo', $sLabel, $sNewValueHtml);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_null($sNewValue))
|
||||
{
|
||||
$sNewValue = Dict::S('UI:UndefinedObject');
|
||||
$sNewValueHtml = Dict::S('UI:UndefinedObject');
|
||||
}
|
||||
$sResult = Dict::Format('Change:AttName_SetTo_NewValue_PreviousValue_OldValue', $sLabel, $sNewValue, $sOldValue);
|
||||
$sResult = Dict::Format('Change:AttName_SetTo_NewValue_PreviousValue_OldValue', $sLabel, $sNewValueHtml, $sOldValueHtml);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -511,15 +527,15 @@ abstract class AttributeDefinition
|
||||
{
|
||||
if (strlen($sOldValue) == 0)
|
||||
{
|
||||
$sResult = Dict::Format('Change:AttName_SetTo', $sLabel, $sNewValue);
|
||||
$sResult = Dict::Format('Change:AttName_SetTo', $sLabel, $sNewValueHtml);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_null($sNewValue))
|
||||
{
|
||||
$sNewValue = Dict::S('UI:UndefinedObject');
|
||||
$sNewValueHtml = Dict::S('UI:UndefinedObject');
|
||||
}
|
||||
$sResult = Dict::Format('Change:AttName_SetTo_NewValue_PreviousValue_OldValue', $sLabel, $sNewValue, $sOldValue);
|
||||
$sResult = Dict::Format('Change:AttName_SetTo_NewValue_PreviousValue_OldValue', $sLabel, $sNewValueHtml, $sOldValueHtml);
|
||||
}
|
||||
}
|
||||
return $sResult;
|
||||
@@ -1214,14 +1230,14 @@ class AttributeDecimal extends AttributeDBField
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue)) return null;
|
||||
if ($proposedValue == '') return null;
|
||||
if ($proposedValue === '') return null;
|
||||
return (string)$proposedValue;
|
||||
}
|
||||
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
assert(is_null($value) || preg_match('/'.$this->GetValidationPattern().'/', $value));
|
||||
return (string)$value; // treated as a string
|
||||
return $value; // null or string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2224,19 +2240,12 @@ class AttributeEnum extends AttributeString
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLabel = Dict::S('Class:'.$this->GetHostClass().'/Attribute:'.$this->GetCode().'/Value:'.$sValue, '');
|
||||
if (strlen($sLabel) == 0)
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sValue, null, true /*user lang*/);
|
||||
if (is_null($sLabel))
|
||||
{
|
||||
$sLabel = str_replace('_', ' ', $sValue);
|
||||
$sParentClass = MetaModel::GetParentClass($this->m_sHostClass);
|
||||
if ($sParentClass)
|
||||
{
|
||||
if (MetaModel::IsValidAttCode($sParentClass, $this->m_sCode))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sParentClass, $this->m_sCode);
|
||||
$sLabel = $oAttDef->GetValueLabel($sValue);
|
||||
}
|
||||
}
|
||||
$sDefault = str_replace('_', ' ', $sValue);
|
||||
// Browse the hierarchy again, accepting default (english) translations
|
||||
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sValue, $sDefault, false);
|
||||
}
|
||||
}
|
||||
return $sLabel;
|
||||
@@ -2251,7 +2260,7 @@ class AttributeEnum extends AttributeString
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDescription = Dict::S('Class:'.$this->GetHostClass().'/Attribute:'.$this->GetCode().'/Value:'.$sValue.'+', '');
|
||||
$sDescription = Dict::S('Class:'.$this->GetHostClass().'/Attribute:'.$this->GetCode().'/Value:'.$sValue.'+', '', true /* user language only */);
|
||||
if (strlen($sDescription) == 0)
|
||||
{
|
||||
$sParentClass = MetaModel::GetParentClass($this->m_sHostClass);
|
||||
@@ -2333,14 +2342,6 @@ class AttributeEnum extends AttributeString
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAsHTMLForHistory($sOldValue, $sNewValue, $sLabel = null)
|
||||
{
|
||||
$sOldValue = is_null($sOldValue) ? null : $this->GetAsHTML($sOldValue);
|
||||
$sNewValue = is_null($sNewValue) ? null : $this->GetAsHTML($sNewValue);
|
||||
$sResult = parent::GetAsHTMLForHistory($sOldValue, $sNewValue, $sLabel);
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
$aRawValues = parent::GetAllowedValues($aArgs, $sContains);
|
||||
@@ -3336,6 +3337,11 @@ class AttributeBlob extends AttributeDefinition
|
||||
public function GetDefaultValue() {return "";}
|
||||
public function IsNullAllowed() {return $this->GetOptional("is_null_allowed", false);}
|
||||
|
||||
public function GetEditValue($sValue, $oHostObj = null)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
// Facilitate things: allow the user to Set the value from a string
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
@@ -3464,6 +3470,44 @@ class AttributeBlob extends AttributeDefinition
|
||||
{
|
||||
return ''; // Not exportable in XML, or as CDATA + some subtags ??
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get a value that will be JSON encoded
|
||||
* The operation is the opposite to FromJSONToValue
|
||||
*/
|
||||
public function GetForJSON($value)
|
||||
{
|
||||
if ($value instanceOf ormDocument)
|
||||
{
|
||||
$aValues = array();
|
||||
$aValues['data'] = base64_encode($value->GetData());
|
||||
$aValues['mimetype'] = $value->GetMimeType();
|
||||
$aValues['filename'] = $value->GetFileName();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aValues = null;
|
||||
}
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to form a value, given JSON decoded data
|
||||
* The operation is the opposite to GetForJSON
|
||||
*/
|
||||
public function FromJSONToValue($json)
|
||||
{
|
||||
if (isset($json->data))
|
||||
{
|
||||
$data = base64_decode($json->data);
|
||||
$value = new ormDocument($data, $json->mimetype, $json->filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = null;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -297,7 +297,9 @@ class BulkChange
|
||||
foreach ($this->m_aExtKeys[$sAttCode] as $sForeignAttCode => $iCol)
|
||||
{
|
||||
// The foreign attribute is one of our reconciliation key
|
||||
$oReconFilter->AddCondition($sForeignAttCode, $aRowData[$iCol], '=');
|
||||
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sForeignAttCode);
|
||||
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
|
||||
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
|
||||
}
|
||||
|
||||
@@ -359,7 +361,16 @@ class BulkChange
|
||||
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
||||
{
|
||||
// The foreign attribute is one of our reconciliation key
|
||||
$oReconFilter->AddCondition($sForeignAttCode, $aRowData[$iCol], '=');
|
||||
if ($sForeignAttCode == 'id')
|
||||
{
|
||||
$value = $aRowData[$iCol];
|
||||
}
|
||||
else
|
||||
{
|
||||
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sForeignAttCode);
|
||||
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
}
|
||||
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
|
||||
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
|
||||
}
|
||||
$oExtObjects = new CMDBObjectSet($oReconFilter);
|
||||
@@ -824,7 +835,15 @@ class BulkChange
|
||||
{
|
||||
// The value is given in the data row
|
||||
$iCol = $this->m_aAttList[$sAttCode];
|
||||
$valuecondition = $aRowData[$iCol];
|
||||
if ($sAttCode == 'id')
|
||||
{
|
||||
$valuecondition = $aRowData[$iCol];
|
||||
}
|
||||
else
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
$valuecondition = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
}
|
||||
}
|
||||
if (is_null($valuecondition))
|
||||
{
|
||||
|
||||
@@ -322,6 +322,18 @@ abstract class CMDBObject extends DBObject
|
||||
$oMyChangeOp->Set("newvalue", $value ? 1 : 0);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeHierarchicalKey)
|
||||
{
|
||||
// Hierarchical keys
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value[$sAttCode]);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scalars
|
||||
|
||||
@@ -64,7 +64,7 @@ class CMDBSource
|
||||
}
|
||||
if (!empty($sSource))
|
||||
{
|
||||
if (!((bool)mysqli_query(self::$m_resDBLink, "USE $sSource")))
|
||||
if (!((bool)mysqli_query(self::$m_resDBLink, "USE `$sSource`")))
|
||||
{
|
||||
throw new MySQLException('Could not select DB', array('host'=>$sServer, 'user'=>$sUser, 'db_name'=>$sSource));
|
||||
}
|
||||
@@ -120,7 +120,7 @@ class CMDBSource
|
||||
{
|
||||
// In case we don't have rights to enumerate the databases
|
||||
// Let's try to connect directly
|
||||
return @((bool)mysqli_query(self::$m_resDBLink, "USE $sSource"));
|
||||
return @((bool)mysqli_query(self::$m_resDBLink, "USE `$sSource`"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -133,7 +133,7 @@ class CMDBSource
|
||||
|
||||
public static function SelectDB($sSource)
|
||||
{
|
||||
if (!((bool)mysqli_query(self::$m_resDBLink, "USE $sSource")))
|
||||
if (!((bool)mysqli_query(self::$m_resDBLink, "USE `$sSource`")))
|
||||
{
|
||||
throw new MySQLException('Could not select DB', array('db_name'=>$sSource));
|
||||
}
|
||||
|
||||
@@ -118,6 +118,60 @@ class DBObjectSearch
|
||||
return key($this->m_aClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the class (only subclasses are supported as of now, because the conditions must fit the new class)
|
||||
* Defaults to the first selected class (most of the time it is also the first joined class
|
||||
*/
|
||||
public function ChangeClass($sNewClass, $sAlias = null)
|
||||
{
|
||||
if (is_null($sAlias))
|
||||
{
|
||||
$sAlias = $this->GetClassAlias();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!array_key_exists($sAlias, $this->m_aClasses))
|
||||
{
|
||||
// discard silently - necessary when recursing on the related nodes (see code below)
|
||||
return;
|
||||
}
|
||||
}
|
||||
$sCurrClass = $this->GetClassName($sAlias);
|
||||
if (!MetaModel::IsParentClass($sCurrClass, $sNewClass))
|
||||
{
|
||||
throw new Exception("Could not change the search class from '$sCurrClass' to '$sNewClass'. Only child classes are permitted.");
|
||||
}
|
||||
|
||||
// Change for this node
|
||||
//
|
||||
$this->m_aSelectedClasses[$sAlias] = $sNewClass;
|
||||
$this->m_aClasses[$sAlias] = $sNewClass;
|
||||
|
||||
// Change for all the related node (yes, this was necessary with some queries - strange effects otherwise)
|
||||
//
|
||||
foreach($this->m_aRelatedTo as $aRelatedTo)
|
||||
{
|
||||
$aRelatedTo['flt']->ChangeClass($sNewClass, $sAlias);
|
||||
}
|
||||
foreach($this->m_aPointingTo as $sExtKeyAttCode=>$aPointingTo)
|
||||
{
|
||||
foreach($aPointingTo as $iOperatorCode => $aFilter)
|
||||
{
|
||||
foreach($aFilter as $oExtFilter)
|
||||
{
|
||||
$oExtFilter->ChangeClass($sNewClass, $sAlias);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach($this->m_aReferencedBy as $sForeignClass => $aReferences)
|
||||
{
|
||||
foreach($aReferences as $sForeignExtKeyAttCode => $oForeignFilter)
|
||||
{
|
||||
$oForeignFilter->ChangeClass($sNewClass, $sAlias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function SetSelectedClasses($aNewSet)
|
||||
{
|
||||
$this->m_aSelectedClasses = array();
|
||||
|
||||
@@ -272,7 +272,9 @@ class DBObjectSet
|
||||
{
|
||||
// Make sure that we carry on the parameters of the set with the filter
|
||||
$oFilter = $this->m_oFilter->DeepClone();
|
||||
$oFilter->SetInternalParams(array_merge($oFilter->GetInternalParams(), $this->m_aArgs));
|
||||
// Note: the arguments found within a set can be object (but not in a filter)
|
||||
// That's why PrepareQueryArguments must be invoked there
|
||||
$oFilter->SetInternalParams(array_merge($oFilter->GetInternalParams(), MetaModel::PrepareQueryArguments($this->m_aArgs)));
|
||||
|
||||
if (count($this->m_aAddedIds) == 0)
|
||||
{
|
||||
|
||||
@@ -82,6 +82,9 @@ class DeletionPlan
|
||||
|
||||
public function ComputeResults()
|
||||
{
|
||||
$this->m_iToDelete = 0;
|
||||
$this->m_iToUpdate = 0;
|
||||
|
||||
foreach($this->m_aToDelete as $sClass => $aToDelete)
|
||||
{
|
||||
foreach($aToDelete as $iId => $aData)
|
||||
|
||||
@@ -120,7 +120,7 @@ class Dict
|
||||
}
|
||||
|
||||
|
||||
public static function S($sStringCode, $sDefault = null)
|
||||
public static function S($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
||||
{
|
||||
// Attempt to find the string in the user language
|
||||
//
|
||||
@@ -134,19 +134,22 @@ class Dict
|
||||
{
|
||||
return $aCurrentDictionary[$sStringCode];
|
||||
}
|
||||
// Attempt to find the string in the default language
|
||||
//
|
||||
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
|
||||
if (array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
if (!$bUserLanguageOnly)
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
}
|
||||
// Attempt to find the string in english
|
||||
//
|
||||
$aDefaultDictionary = self::$m_aData['EN US'];
|
||||
if (array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
// Attempt to find the string in the default language
|
||||
//
|
||||
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
|
||||
if (array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
}
|
||||
// Attempt to find the string in english
|
||||
//
|
||||
$aDefaultDictionary = self::$m_aData['EN US'];
|
||||
if (array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
}
|
||||
}
|
||||
// Could not find the string...
|
||||
//
|
||||
|
||||
@@ -33,10 +33,15 @@ define ('EMAIL_SEND_OK', 0);
|
||||
define ('EMAIL_SEND_PENDING', 1);
|
||||
define ('EMAIL_SEND_ERROR', 2);
|
||||
|
||||
|
||||
class EMail
|
||||
{
|
||||
// Serialization formats
|
||||
const ORIGINAL_FORMAT = 1; // Original format, consisting in serializing the whole object, inculding the Swift Mailer's object.
|
||||
// Did not work with attachements since their binary representation cannot be stored as a valid UTF-8 string
|
||||
const FORMAT_V2 = 2; // New format, only the raw data are serialized (base64 encoded if needed)
|
||||
|
||||
protected static $m_oConfig = null;
|
||||
protected $m_aData; // For storing data to serialize
|
||||
|
||||
public function LoadConfig($sConfigFile = ITOP_DEFAULT_CONFIG_FILE)
|
||||
{
|
||||
@@ -46,17 +51,95 @@ class EMail
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected $m_oMessage;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->m_aData = array();
|
||||
$this->m_oMessage = Swift_Message::newInstance();
|
||||
|
||||
$oEncoder = new Swift_Mime_ContentEncoder_PlainContentEncoder('8bit');
|
||||
$this->m_oMessage->setEncoder($oEncoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom serialization method
|
||||
* No longer use the brute force "serialize" method since
|
||||
* 1) It does not work with binary attachments (since they cannot be stored in a UTF-8 text field)
|
||||
* 2) The size tends to be quite big (sometimes ten times the size of the email)
|
||||
*/
|
||||
public function SerializeV2()
|
||||
{
|
||||
return serialize($this->m_aData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom de-serialization method
|
||||
* @param string $sSerializedMessage The serialized representation of the message
|
||||
*/
|
||||
static public function UnSerializeV2($sSerializedMessage)
|
||||
{
|
||||
$aData = unserialize($sSerializedMessage);
|
||||
$oMessage = new Email();
|
||||
|
||||
if (array_key_exists('body', $aData))
|
||||
{
|
||||
$oMessage->SetBody($aData['body']['body'], $aData['body']['mimeType']);
|
||||
}
|
||||
if (array_key_exists('message_id', $aData))
|
||||
{
|
||||
$oMessage->SetMessageId($aData['message_id']);
|
||||
}
|
||||
if (array_key_exists('bcc', $aData))
|
||||
{
|
||||
$oMessage->SetRecipientBCC($aData['bcc']);
|
||||
}
|
||||
if (array_key_exists('cc', $aData))
|
||||
{
|
||||
$oMessage->SetRecipientCC($aData['cc']);
|
||||
}
|
||||
if (array_key_exists('from', $aData))
|
||||
{
|
||||
$oMessage->SetRecipientFrom($aData['from']['address'], $aData['from']['label']);
|
||||
}
|
||||
if (array_key_exists('reply_to', $aData))
|
||||
{
|
||||
$oMessage->SetRecipientReplyTo($aData['reply_to']);
|
||||
}
|
||||
if (array_key_exists('to', $aData))
|
||||
{
|
||||
$oMessage->SetRecipientTO($aData['to']);
|
||||
}
|
||||
if (array_key_exists('subject', $aData))
|
||||
{
|
||||
$oMessage->SetSubject($aData['subject']);
|
||||
}
|
||||
|
||||
|
||||
if (array_key_exists('headers', $aData))
|
||||
{
|
||||
foreach($aData['headers'] as $sKey => $sValue)
|
||||
{
|
||||
$oMessage->AddToHeader($sKey, $sValue);
|
||||
}
|
||||
}
|
||||
if (array_key_exists('parts', $aData))
|
||||
{
|
||||
foreach($aData['parts'] as $aPart)
|
||||
{
|
||||
$oMessage->AddPart($aPart['text'], $aPart['mimeType']);
|
||||
}
|
||||
}
|
||||
if (array_key_exists('attachments', $aData))
|
||||
{
|
||||
foreach($aData['attachments'] as $aAttachment)
|
||||
{
|
||||
$oMessage->AddAttachment(base64_decode($aAttachment['data']), $aAttachment['filename'], $aAttachment['mimeType']);
|
||||
}
|
||||
}
|
||||
return $oMessage;
|
||||
}
|
||||
|
||||
protected function SendAsynchronous(&$aIssues, $oLog = null)
|
||||
{
|
||||
try
|
||||
@@ -102,9 +185,9 @@ class EMail
|
||||
$oMailer = Swift_Mailer::newInstance($oTransport);
|
||||
|
||||
$iSent = $oMailer->send($this->m_oMessage);
|
||||
if ($iSent === false)
|
||||
if ($iSent === 0)
|
||||
{
|
||||
$aIssues = 'une erreur s\'est produite... mais quoi !!!';
|
||||
$aIssues = array('No valid recipient for this message.');
|
||||
return EMAIL_SEND_ERROR;
|
||||
}
|
||||
else
|
||||
@@ -136,6 +219,12 @@ class EMail
|
||||
|
||||
public function AddToHeader($sKey, $sValue)
|
||||
{
|
||||
if (!array_key_exists('headers', $this->m_aData))
|
||||
{
|
||||
$this->m_aData['headers'] = array();
|
||||
}
|
||||
$this->m_aData['headers'][$sKey] = $sValue;
|
||||
|
||||
if (strlen($sValue) > 0)
|
||||
{
|
||||
$oHeaders = $this->m_oMessage->getHeaders();
|
||||
@@ -149,6 +238,8 @@ class EMail
|
||||
|
||||
public function SetMessageId($sId)
|
||||
{
|
||||
$this->m_aData['message_id'] = $sId;
|
||||
|
||||
// Note: Swift will add the angle brackets for you
|
||||
// so let's remove the angle brackets if present, for historical reasons
|
||||
$sId = str_replace(array('<', '>'), '', $sId);
|
||||
@@ -164,22 +255,34 @@ class EMail
|
||||
|
||||
public function SetBody($sBody, $sMimeType = 'text/html')
|
||||
{
|
||||
$this->m_aData['body'] = array('body' => $sBody, 'mimeType' => $sMimeType);
|
||||
$this->m_oMessage->setBody($sBody, $sMimeType);
|
||||
}
|
||||
|
||||
public function AddPart($sText, $sMimeType = 'text/html')
|
||||
{
|
||||
if (!array_key_exists('parts', $this->m_aData))
|
||||
{
|
||||
$this->m_aData['parts'] = array();
|
||||
}
|
||||
$this->m_aData['parts'][] = array('text' => $sText, 'mimeType' => $sMimeType);
|
||||
$this->m_oMessage->addPart($sText, $sMimeType);
|
||||
}
|
||||
|
||||
public function AddAttachment($data, $sFileName, $sMimeType)
|
||||
{
|
||||
if (!array_key_exists('attachments', $this->m_aData))
|
||||
{
|
||||
$this->m_aData['attachments'] = array();
|
||||
}
|
||||
$this->m_aData['attachments'][] = array('data' => base64_encode($data), 'filename' => $sFileName, 'mimeType' => $sMimeType);
|
||||
$this->m_oMessage->attach(Swift_Attachment::newInstance($data, $sFileName, $sMimeType));
|
||||
}
|
||||
|
||||
public function SetSubject($aSubject)
|
||||
public function SetSubject($sSubject)
|
||||
{
|
||||
$this->m_oMessage->setSubject($aSubject);
|
||||
$this->m_aData['subject'] = $sSubject;
|
||||
$this->m_oMessage->setSubject($sSubject);
|
||||
}
|
||||
|
||||
public function GetSubject()
|
||||
@@ -187,11 +290,30 @@ class EMail
|
||||
return $this->m_oMessage->getSubject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to transform and sanitize addresses
|
||||
* - get rid of empty addresses
|
||||
*/
|
||||
protected function AddressStringToArray($sAddressCSVList)
|
||||
{
|
||||
$aAddresses = array();
|
||||
foreach(explode(',', $sAddressCSVList) as $sAddress)
|
||||
{
|
||||
$sAddress = trim($sAddress);
|
||||
if (strlen($sAddress) > 0)
|
||||
{
|
||||
$aAddresses[] = $sAddress;
|
||||
}
|
||||
}
|
||||
return $aAddresses;
|
||||
}
|
||||
|
||||
public function SetRecipientTO($sAddress)
|
||||
{
|
||||
$this->m_aData['to'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
$aAddresses = explode(', ', $sAddress);
|
||||
$aAddresses = $this->AddressStringToArray($sAddress);
|
||||
$this->m_oMessage->setTo($aAddresses);
|
||||
}
|
||||
}
|
||||
@@ -224,24 +346,27 @@ class EMail
|
||||
|
||||
public function SetRecipientCC($sAddress)
|
||||
{
|
||||
$this->m_aData['cc'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
$aAddresses = explode(', ', $sAddress);
|
||||
$aAddresses = $this->AddressStringToArray($sAddress);
|
||||
$this->m_oMessage->setCc($aAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
public function SetRecipientBCC($sAddress)
|
||||
{
|
||||
$this->m_aData['bcc'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
$aAddresses = explode(', ', $sAddress);
|
||||
$aAddresses = $this->AddressStringToArray($sAddress);
|
||||
$this->m_oMessage->setBcc($aAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
public function SetRecipientFrom($sAddress, $sLabel = '')
|
||||
{
|
||||
$this->m_aData['from'] = array('address' => $sAddress, 'label' => $sLabel);
|
||||
if ($sLabel != '')
|
||||
{
|
||||
$this->m_oMessage->setFrom(array($sAddress => $sLabel));
|
||||
@@ -254,6 +379,7 @@ class EMail
|
||||
|
||||
public function SetRecipientReplyTo($sAddress)
|
||||
{
|
||||
$this->m_aData['reply_to'] = $sAddress;
|
||||
if (!empty($sAddress))
|
||||
{
|
||||
$this->m_oMessage->setReplyTo($sAddress);
|
||||
|
||||
@@ -41,6 +41,7 @@ class Event extends DBObject implements iDisplay
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "realclass",
|
||||
"display_template" => "",
|
||||
"order_by_default" => array('date' => false)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
@@ -135,6 +136,7 @@ class EventNotification extends Event
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
"order_by_default" => array('date' => false)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
@@ -167,6 +169,7 @@ class EventNotificationEmail extends EventNotification
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
"order_by_default" => array('date' => false)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
@@ -204,6 +207,7 @@ class EventIssue extends Event
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
"order_by_default" => array('date' => false)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
@@ -303,6 +307,7 @@ class EventWebService extends Event
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
"order_by_default" => array('date' => false)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
@@ -337,6 +342,7 @@ class EventLoginUsage extends Event
|
||||
"db_table" => "priv_event_loginusage",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"order_by_default" => array('date' => false)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
@@ -618,7 +618,7 @@ class VariableExpression extends UnaryExpression
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new MissingQueryArgument('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>$aArgs));
|
||||
throw new MissingQueryArgument('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>array_keys($aArgs)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2076,10 +2076,27 @@ abstract class MetaModel
|
||||
return $aScalarArgs;
|
||||
}
|
||||
|
||||
public static function MakeGroupByQuery(DBObjectSearch $oFilter, $aArgs, $aGroupByExpr)
|
||||
public static function MakeGroupByQuery(DBObjectSearch $oFilter, $aArgs, $aGroupByExpr, $bExcludeNullValues = false)
|
||||
{
|
||||
$aAttToLoad = array();
|
||||
$oSelect = self::MakeSelectStructure($oFilter, array(), $aArgs, $aAttToLoad, null, 0, 0, false, $aGroupByExpr);
|
||||
|
||||
if ($bExcludeNullValues)
|
||||
{
|
||||
// Null values are not handled (though external keys set to 0 are allowed)
|
||||
$oQueryFilter = $oFilter->DeepClone();
|
||||
foreach ($aGroupByExpr as $oGroupByExp)
|
||||
{
|
||||
$oNull = new FunctionExpression('ISNULL', array($oGroupByExp));
|
||||
$oNotNull = new BinaryExpression($oNull, '!=', new TrueExpression());
|
||||
$oQueryFilter->AddConditionExpression($oNotNull);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oQueryFilter = $oFilter;
|
||||
}
|
||||
|
||||
$oSelect = self::MakeSelectStructure($oQueryFilter, array(), $aArgs, $aAttToLoad, null, 0, 0, false, $aGroupByExpr);
|
||||
|
||||
$aScalarArgs = array_merge(self::PrepareQueryArguments($aArgs), $oFilter->GetInternalParams());
|
||||
try
|
||||
@@ -5148,7 +5165,7 @@ abstract class MetaModel
|
||||
}
|
||||
$aEntries = array();
|
||||
$aCacheUserData = @apc_cache_info('user');
|
||||
if (is_array($aCacheUserData))
|
||||
if (is_array($aCacheUserData) && isset($aCacheUserData['cache_list']))
|
||||
{
|
||||
$sPrefix = 'itop-'.$sEnvironment.'-';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -162,8 +162,7 @@ class ormStopWatch
|
||||
}
|
||||
else
|
||||
{
|
||||
$iElapsedTemp = ''; //$this->ComputeDuration($oHostObject, $oAttDef, $this->iLastStart, time());
|
||||
$aProperties['Elapsed'] = $this->iTimeSpent.' + '.$iElapsedTemp.' s + <img src="../images/indicator.gif">';
|
||||
$aProperties['Elapsed'] = 'running <img src="../images/indicator.gif">';
|
||||
}
|
||||
|
||||
$aProperties['Started'] = $oAttDef->SecondsToDate($this->iStarted);
|
||||
@@ -183,7 +182,7 @@ class ormStopWatch
|
||||
}
|
||||
$aProperties[$iPercent.'%'] = $sThresholdDesc;
|
||||
}
|
||||
$sRes = "<TABLE class=\"listResults\">";
|
||||
$sRes = "<TABLE>";
|
||||
$sRes .= "<TBODY>";
|
||||
foreach ($aProperties as $sProperty => $sValue)
|
||||
{
|
||||
@@ -387,6 +386,7 @@ class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
|
||||
public function Process($iTimeLimit)
|
||||
{
|
||||
$aList = array();
|
||||
foreach (MetaModel::GetClasses() as $sClass)
|
||||
{
|
||||
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
@@ -397,17 +397,15 @@ class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
{
|
||||
$iPercent = $aThresholdData['percent']; // could be different than the index !
|
||||
|
||||
$sExpression = "SELECT $sClass WHERE {$sAttCode}_laststart AND {$sAttCode}_{$iThreshold}_triggered = 0 AND {$sAttCode}_{$iThreshold}_deadline < NOW()";
|
||||
//echo $sExpression."<br/>\n";
|
||||
$sNow = date('Y-m-d H:i:s');
|
||||
$sExpression = "SELECT $sClass WHERE {$sAttCode}_laststart AND {$sAttCode}_{$iThreshold}_triggered = 0 AND {$sAttCode}_{$iThreshold}_deadline < '$sNow'";
|
||||
$oFilter = DBObjectSearch::FromOQL($sExpression);
|
||||
$aList = array();
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
while ((time() < $iTimeLimit) && ($oObj = $oSet->Fetch()))
|
||||
{
|
||||
$sClass = get_class($oObj);
|
||||
|
||||
$aList[] = $sClass.'::'.$oObj->GetKey().' '.$sAttCode.' '.$iThreshold;
|
||||
//echo $sClass.'::'.$oObj->GetKey().' '.$sAttCode.' '.$iThreshold."\n";
|
||||
|
||||
// Execute planned actions
|
||||
//
|
||||
@@ -416,7 +414,6 @@ class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
$sVerb = $aActionData['verb'];
|
||||
$aParams = $aActionData['params'];
|
||||
$sParams = implode(', ', $aParams);
|
||||
//echo "Calling: $sVerb($sParams)<br/>\n";
|
||||
$aCallSpec = array($oObj, $sVerb);
|
||||
call_user_func_array($aCallSpec, $aParams);
|
||||
}
|
||||
@@ -438,12 +435,12 @@ class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
// Activate any existing trigger
|
||||
//
|
||||
$sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||
$oSet = new DBObjectSet(
|
||||
$oTriggerSet = new DBObjectSet(
|
||||
DBObjectSearch::FromOQL("SELECT TriggerOnThresholdReached AS t WHERE t.target_class IN ('$sClassList') AND stop_watch_code=:stop_watch_code AND threshold_index = :threshold_index"),
|
||||
array(), // order by
|
||||
array('stop_watch_code' => $sAttCode, 'threshold_index' => $iThreshold)
|
||||
);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
while ($oTrigger = $oTriggerSet->Fetch())
|
||||
{
|
||||
$oTrigger->DoActivate($oObj->ToArgs('this'));
|
||||
}
|
||||
@@ -454,9 +451,6 @@ class CheckStopWatchThresholds implements iBackgroundProcess
|
||||
}
|
||||
|
||||
$iProcessed = count($aList);
|
||||
return "Triggered $iProcessed threshold(s)";
|
||||
return "Triggered $iProcessed threshold(s):".implode(", ", $aList);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
|
||||
595
core/restservices.class.inc.php
Normal file
595
core/restservices.class.inc.php
Normal file
@@ -0,0 +1,595 @@
|
||||
<?php
|
||||
// Copyright (C) 2013 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* REST/json services
|
||||
*
|
||||
* Definition of common structures + the very minimum service provider (manage objects)
|
||||
*
|
||||
* @package REST Services
|
||||
* @copyright Copyright (C) 2013 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @api
|
||||
*/
|
||||
|
||||
/**
|
||||
* Element of the response formed by RestResultWithObjects
|
||||
*
|
||||
* @package REST Services
|
||||
*/
|
||||
class ObjectResult
|
||||
{
|
||||
public $code;
|
||||
public $message;
|
||||
public $fields;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->code = RestResult::OK;
|
||||
$this->message = '';
|
||||
$this->fields = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to make an output value for a given attribute
|
||||
*
|
||||
* @param DBObject $oObject The object being reported
|
||||
* @param string $sAttCode The attribute code (must be valid)
|
||||
* @return string A scalar representation of the value
|
||||
*/
|
||||
protected function MakeResultValue(DBObject $oObject, $sAttCode)
|
||||
{
|
||||
if ($sAttCode == 'id')
|
||||
{
|
||||
$value = $oObject->GetKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef instanceof AttributeLinkedSet)
|
||||
{
|
||||
$value = array();
|
||||
|
||||
// Make the list of required attributes
|
||||
// - Skip attributes pointing to the current object (redundant data)
|
||||
// - Skip link sets refering to the current data (infinite recursion!)
|
||||
$aRelevantAttributes = array();
|
||||
$sLnkClass = $oAttDef->GetLinkedClass();
|
||||
foreach (MetaModel::ListAttributeDefs($sLnkClass) as $sLnkAttCode => $oLnkAttDef)
|
||||
{
|
||||
// Skip any attribute of the link that points to the current object
|
||||
//
|
||||
if ($sLnkAttCode == $oAttDef->GetExtKeyToMe()) continue;
|
||||
if (method_exists($oLnkAttDef, 'GetKeyAttCode'))
|
||||
{
|
||||
if ($oLnkAttDef->GetKeyAttCode() ==$oAttDef->GetExtKeyToMe()) continue;
|
||||
}
|
||||
|
||||
$aRelevantAttributes[] = $sLnkAttCode;
|
||||
}
|
||||
|
||||
// Iterate on the set and build an array of array of attcode=>value
|
||||
$oSet = $oObject->Get($sAttCode);
|
||||
while ($oLnk = $oSet->Fetch())
|
||||
{
|
||||
$aLnkValues = array();
|
||||
foreach ($aRelevantAttributes as $sLnkAttCode)
|
||||
{
|
||||
$aLnkValues[$sLnkAttCode] = $this->MakeResultValue($oLnk, $sLnkAttCode);
|
||||
}
|
||||
$value[] = $aLnkValues;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = $oAttDef->GetForJSON($oObject->Get($sAttCode));
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the value for the given object attribute
|
||||
*
|
||||
* @param DBObject $oObject The object being reported
|
||||
* @param string $sAttCode The attribute code (must be valid)
|
||||
* @return void
|
||||
*/
|
||||
public function AddField(DBObject $oObject, $sAttCode)
|
||||
{
|
||||
$this->fields[$sAttCode] = $this->MakeResultValue($oObject, $sAttCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* REST response for services managing objects. Derive this structure to add information and/or constants
|
||||
*
|
||||
* @package Extensibility
|
||||
* @package REST Services
|
||||
* @api
|
||||
*/
|
||||
class RestResultWithObjects extends RestResult
|
||||
{
|
||||
public $objects;
|
||||
|
||||
/**
|
||||
* Report the given object
|
||||
*
|
||||
* @param int An error code (RestResult::OK is no issue has been found)
|
||||
* @param string $sMessage Description of the error if any, an empty string otherwise
|
||||
* @param DBObject $oObject The object being reported
|
||||
* @param array $aFields An array of attribute codes. List of the attributes to be reported.
|
||||
* @return void
|
||||
*/
|
||||
public function AddObject($iCode, $sMessage, $oObject, $aFields)
|
||||
{
|
||||
$oObjRes = new ObjectResult();
|
||||
$oObjRes->code = $iCode;
|
||||
$oObjRes->message = $sMessage;
|
||||
|
||||
$oObjRes->class = get_class($oObject);
|
||||
foreach ($aFields as $sAttCode)
|
||||
{
|
||||
$oObjRes->AddField($oObject, $sAttCode);
|
||||
}
|
||||
|
||||
$sObjKey = get_class($oObject).'::'.$oObject->GetKey();
|
||||
$this->objects[$sObjKey] = $oObjRes;
|
||||
}
|
||||
}
|
||||
|
||||
class RestResultWithRelations extends RestResultWithObjects
|
||||
{
|
||||
public $relations;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->relations = array();
|
||||
}
|
||||
|
||||
public function AddRelation($sSrcKey, $sDestKey)
|
||||
{
|
||||
if (!array_key_exists($sSrcKey, $this->relations))
|
||||
{
|
||||
$this->relations[$sSrcKey] = array();
|
||||
}
|
||||
$this->relations[$sSrcKey][] = array('key' => $sDestKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletion result codes for a target object (either deleted or updated)
|
||||
*
|
||||
* @package Extensibility
|
||||
* @api
|
||||
* @since 2.0.1
|
||||
*/
|
||||
class RestDelete
|
||||
{
|
||||
/**
|
||||
* Result: Object deleted as per the initial request
|
||||
*/
|
||||
const OK = 0;
|
||||
/**
|
||||
* Result: general issue (user rights or ... ?)
|
||||
*/
|
||||
const ISSUE = 1;
|
||||
/**
|
||||
* Result: Must be deleted to preserve database integrity
|
||||
*/
|
||||
const AUTO_DELETE = 2;
|
||||
/**
|
||||
* Result: Must be deleted to preserve database integrity, but that is NOT possible
|
||||
*/
|
||||
const AUTO_DELETE_ISSUE = 3;
|
||||
/**
|
||||
* Result: Must be deleted to preserve database integrity, but this must be requested explicitely
|
||||
*/
|
||||
const REQUEST_EXPLICITELY = 4;
|
||||
/**
|
||||
* Result: Must be updated to preserve database integrity
|
||||
*/
|
||||
const AUTO_UPDATE = 5;
|
||||
/**
|
||||
* Result: Must be updated to preserve database integrity, but that is NOT possible
|
||||
*/
|
||||
const AUTO_UPDATE_ISSUE = 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of core REST services (create/get/update... objects)
|
||||
*
|
||||
* @package Core
|
||||
*/
|
||||
class CoreServices implements iRestServiceProvider
|
||||
{
|
||||
/**
|
||||
* Enumerate services delivered by this class
|
||||
*
|
||||
* @param string $sVersion The version (e.g. 1.0) supported by the services
|
||||
* @return array An array of hash 'verb' => verb, 'description' => description
|
||||
*/
|
||||
public function ListOperations($sVersion)
|
||||
{
|
||||
$aOps = array();
|
||||
if ($sVersion == '1.0')
|
||||
{
|
||||
$aOps[] = array(
|
||||
'verb' => 'core/create',
|
||||
'description' => 'Create an object'
|
||||
);
|
||||
$aOps[] = array(
|
||||
'verb' => 'core/update',
|
||||
'description' => 'Update an object'
|
||||
);
|
||||
$aOps[] = array(
|
||||
'verb' => 'core/apply_stimulus',
|
||||
'description' => 'Apply a stimulus to change the state of an object'
|
||||
);
|
||||
$aOps[] = array(
|
||||
'verb' => 'core/get',
|
||||
'description' => 'Search for objects'
|
||||
);
|
||||
$aOps[] = array(
|
||||
'verb' => 'core/delete',
|
||||
'description' => 'Delete objects'
|
||||
);
|
||||
$aOps[] = array(
|
||||
'verb' => 'core/get_related',
|
||||
'description' => 'Get related objects through the specified relation'
|
||||
);
|
||||
}
|
||||
return $aOps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate services delivered by this class
|
||||
* @param string $sVersion The version (e.g. 1.0) supported by the services
|
||||
* @return RestResult The standardized result structure (at least a message)
|
||||
* @throws Exception in case of internal failure.
|
||||
*/
|
||||
public function ExecOperation($sVersion, $sVerb, $aParams)
|
||||
{
|
||||
$oResult = new RestResultWithObjects();
|
||||
switch ($sVerb)
|
||||
{
|
||||
case 'core/create':
|
||||
RestUtils::InitTrackingComment($aParams);
|
||||
$sClass = RestUtils::GetClass($aParams, 'class');
|
||||
$aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
|
||||
$aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
|
||||
|
||||
$oObject = RestUtils::MakeObjectFromFields($sClass, $aFields);
|
||||
$oObject->DBInsert();
|
||||
|
||||
$oResult->AddObject(0, 'created', $oObject, $aShowFields);
|
||||
break;
|
||||
|
||||
case 'core/update':
|
||||
RestUtils::InitTrackingComment($aParams);
|
||||
$sClass = RestUtils::GetClass($aParams, 'class');
|
||||
$key = RestUtils::GetMandatoryParam($aParams, 'key');
|
||||
$aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
|
||||
$aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
|
||||
|
||||
$oObject = RestUtils::FindObjectFromKey($sClass, $key);
|
||||
RestUtils::UpdateObjectFromFields($oObject, $aFields);
|
||||
$oObject->DBUpdate();
|
||||
|
||||
$oResult->AddObject(0, 'updated', $oObject, $aShowFields);
|
||||
break;
|
||||
|
||||
case 'core/apply_stimulus':
|
||||
RestUtils::InitTrackingComment($aParams);
|
||||
$sClass = RestUtils::GetClass($aParams, 'class');
|
||||
$key = RestUtils::GetMandatoryParam($aParams, 'key');
|
||||
$aFields = RestUtils::GetMandatoryParam($aParams, 'fields');
|
||||
$aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
|
||||
$sStimulus = RestUtils::GetMandatoryParam($aParams, 'stimulus');
|
||||
|
||||
$oObject = RestUtils::FindObjectFromKey($sClass, $key);
|
||||
RestUtils::UpdateObjectFromFields($oObject, $aFields);
|
||||
|
||||
$aTransitions = $oObject->EnumTransitions();
|
||||
$aStimuli = MetaModel::EnumStimuli(get_class($oObject));
|
||||
if (!isset($aTransitions[$sStimulus]))
|
||||
{
|
||||
// Invalid stimulus
|
||||
$oResult->code = RestResult::INTERNAL_ERROR;
|
||||
$oResult->message = "Invalid stimulus: '$sStimulus' on the object ".$oObject->GetName()." in state '".$oObject->GetState()."'";
|
||||
}
|
||||
else
|
||||
{
|
||||
$aTransition = $aTransitions[$sStimulus];
|
||||
$sTargetState = $aTransition['target_state'];
|
||||
$aStates = MetaModel::EnumStates($sClass);
|
||||
$aTargetStateDef = $aStates[$sTargetState];
|
||||
$aExpectedAttributes = $aTargetStateDef['attribute_list'];
|
||||
|
||||
$aMissingMandatory = array();
|
||||
foreach($aExpectedAttributes as $sAttCode => $iExpectCode)
|
||||
{
|
||||
if ( ($iExpectCode & OPT_ATT_MANDATORY) && ($oObject->Get($sAttCode) == ''))
|
||||
{
|
||||
$aMissingMandatory[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
if (count($aMissingMandatory) == 0)
|
||||
{
|
||||
// If all the mandatory fields are already present, just apply the transition silently...
|
||||
if ($oObject->ApplyStimulus($sStimulus))
|
||||
{
|
||||
$oObject->DBUpdate();
|
||||
$oResult->AddObject(0, 'updated', $oObject, $aShowFields);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Missing mandatory attributes for the transition
|
||||
$oResult->code = RestResult::INTERNAL_ERROR;
|
||||
$oResult->message = 'Missing mandatory attribute(s) for applying the stimulus: '.implode(', ', $aMissingMandatory).'.';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'core/get':
|
||||
$sClass = RestUtils::GetClass($aParams, 'class');
|
||||
$key = RestUtils::GetMandatoryParam($aParams, 'key');
|
||||
$aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
|
||||
|
||||
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
{
|
||||
$oResult->AddObject(0, '', $oObject, $aShowFields);
|
||||
}
|
||||
$oResult->message = "Found: ".$oObjectSet->Count();
|
||||
break;
|
||||
|
||||
case 'core/delete':
|
||||
$sClass = RestUtils::GetClass($aParams, 'class');
|
||||
$key = RestUtils::GetMandatoryParam($aParams, 'key');
|
||||
$bSimulate = RestUtils::GetOptionalParam($aParams, 'simulate', false);
|
||||
|
||||
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
|
||||
$aObjects = $oObjectSet->ToArray();
|
||||
$this->DeleteObjects($oResult, $aObjects, $bSimulate);
|
||||
break;
|
||||
|
||||
case 'core/get_related':
|
||||
$oResult = new RestResultWithRelations();
|
||||
$sClass = RestUtils::GetClass($aParams, 'class');
|
||||
$key = RestUtils::GetMandatoryParam($aParams, 'key');
|
||||
$sRelation = RestUtils::GetMandatoryParam($aParams, 'relation');
|
||||
$iMaxRecursionDepth = RestUtils::GetOptionalParam($aParams, 'depth', 20 /* = MAX_RECURSION_DEPTH */);
|
||||
$aShowFields = array('id', 'friendlyname');
|
||||
|
||||
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
|
||||
$aIndexByClass = array();
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
{
|
||||
$aRelated = array();
|
||||
$aGraph = array();
|
||||
$aIndexByClass[get_class($oObject)][$oObject->GetKey()] = null;
|
||||
$oResult->AddObject(0, '', $oObject, $aShowFields);
|
||||
$this->GetRelatedObjects($oObject, $sRelation, $iMaxRecursionDepth, $aRelated, $aGraph);
|
||||
|
||||
foreach($aRelated as $sClass => $aObjects)
|
||||
{
|
||||
foreach($aObjects as $oRelatedObj)
|
||||
{
|
||||
$aIndexByClass[get_class($oRelatedObj)][$oRelatedObj->GetKey()] = null;
|
||||
$oResult->AddObject(0, '', $oRelatedObj, $aShowFields);
|
||||
}
|
||||
}
|
||||
foreach($aGraph as $sSrcKey => $aDestinations)
|
||||
{
|
||||
foreach ($aDestinations as $sDestKey)
|
||||
{
|
||||
$oResult->AddRelation($sSrcKey, $sDestKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($aIndexByClass) > 0)
|
||||
{
|
||||
$aStats = array();
|
||||
foreach ($aIndexByClass as $sClass => $aIds)
|
||||
{
|
||||
$aStats[] = $sClass.'= '.count($aIds);
|
||||
}
|
||||
$oResult->message = "Scope: ".$oObjectSet->Count()."; Related objects: ".implode(', ', $aStats);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oResult->message = "Nothing found";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// unknown operation: handled at a higher level
|
||||
}
|
||||
return $oResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for object deletion
|
||||
*/
|
||||
public function DeleteObjects($oResult, $aObjects, $bSimulate)
|
||||
{
|
||||
$oDeletionPlan = new DeletionPlan();
|
||||
foreach($aObjects as $oObj)
|
||||
{
|
||||
if ($bSimulate)
|
||||
{
|
||||
$oObj->CheckToDelete($oDeletionPlan);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oObj->DBDelete($oDeletionPlan);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($oDeletionPlan->ListDeletes() as $sTargetClass => $aDeletes)
|
||||
{
|
||||
foreach ($aDeletes as $iId => $aData)
|
||||
{
|
||||
$oToDelete = $aData['to_delete'];
|
||||
$bAutoDel = (($aData['mode'] == DEL_SILENT) || ($aData['mode'] == DEL_AUTO));
|
||||
if (array_key_exists('issue', $aData))
|
||||
{
|
||||
if ($bAutoDel)
|
||||
{
|
||||
if (isset($aData['requested_explicitely'])) // i.e. in the initial list of objects to delete
|
||||
{
|
||||
$iCode = RestDelete::ISSUE;
|
||||
$sPlanned = 'Cannot be deleted: '.$aData['issue'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCode = RestDelete::AUTO_DELETE_ISSUE;
|
||||
$sPlanned = 'Should be deleted automatically... but: '.$aData['issue'];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCode = RestDelete::REQUEST_EXPLICITELY;
|
||||
$sPlanned = 'Must be deleted explicitely... but: '.$aData['issue'];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($bAutoDel)
|
||||
{
|
||||
if (isset($aData['requested_explicitely']))
|
||||
{
|
||||
$iCode = RestDelete::OK;
|
||||
$sPlanned = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCode = RestDelete::AUTO_DELETE;
|
||||
$sPlanned = 'Deleted automatically';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCode = RestDelete::REQUEST_EXPLICITELY;
|
||||
$sPlanned = 'Must be deleted explicitely';
|
||||
}
|
||||
}
|
||||
$oResult->AddObject($iCode, $sPlanned, $oToDelete, array('id', 'friendlyname'));
|
||||
}
|
||||
}
|
||||
foreach ($oDeletionPlan->ListUpdates() as $sRemoteClass => $aToUpdate)
|
||||
{
|
||||
foreach ($aToUpdate as $iId => $aData)
|
||||
{
|
||||
$oToUpdate = $aData['to_reset'];
|
||||
if (array_key_exists('issue', $aData))
|
||||
{
|
||||
$iCode = RestDelete::AUTO_UPDATE_ISSUE;
|
||||
$sPlanned = 'Should be updated automatically... but: '.$aData['issue'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCode = RestDelete::AUTO_UPDATE;
|
||||
$sPlanned = 'Reset external keys: '.$aData['attributes_list'];
|
||||
}
|
||||
$oResult->AddObject($iCode, $sPlanned, $oToUpdate, array('id', 'friendlyname'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($oDeletionPlan->FoundStopper())
|
||||
{
|
||||
if ($oDeletionPlan->FoundSecurityIssue())
|
||||
{
|
||||
$iRes = RestResult::UNAUTHORIZED;
|
||||
$sRes = 'Deletion not allowed on some objects';
|
||||
}
|
||||
elseif ($oDeletionPlan->FoundManualOperation())
|
||||
{
|
||||
$iRes = RestResult::UNSAFE;
|
||||
$sRes = 'The deletion requires that other objects be deleted/updated, and those operations must be requested explicitely';
|
||||
}
|
||||
else
|
||||
{
|
||||
$iRes = RestResult::INTERNAL_ERROR;
|
||||
$sRes = 'Some issues have been encountered. See the list of planned changes for more information about the issue(s).';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iRes = RestResult::OK;
|
||||
$sRes = 'Deleted: '.count($aObjects);
|
||||
$iIndirect = $oDeletionPlan->GetTargetCount() - count($aObjects);
|
||||
if ($iIndirect > 0)
|
||||
{
|
||||
$sRes .= ' plus (for DB integrity) '.$iIndirect;
|
||||
}
|
||||
}
|
||||
$oResult->code = $iRes;
|
||||
if ($bSimulate)
|
||||
{
|
||||
$oResult->message = 'SIMULATING: '.$sRes;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oResult->message = $sRes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the related objects up to the given depth along with the "graph" of the relation
|
||||
* @param DBObject $oObject Starting point of the computation
|
||||
* @param string $sRelation Code of the relation (i.e; 'impact', 'depends on'...)
|
||||
* @param integer $iMaxRecursionDepth Maximum level of recursion
|
||||
* @param Hash $aRelated Two dimensions hash of the already related objects: array( 'class' => array(key => ))
|
||||
* @param Hash $aGraph Hash array for the topology of the relation: source => related: array('class:key' => array( DBObjects ))
|
||||
* @param integer $iRecursionDepth Current level of recursion
|
||||
*/
|
||||
protected function GetRelatedObjects(DBObject $oObject, $sRelation, $iMaxRecursionDepth, &$aRelated, &$aGraph, $iRecursionDepth = 1)
|
||||
{
|
||||
// Avoid loops
|
||||
if ((array_key_exists(get_class($oObject), $aRelated)) && (array_key_exists($oObject->GetKey(), $aRelated[get_class($oObject)]))) return;
|
||||
// Stop at maximum recursion level
|
||||
if ($iRecursionDepth > $iMaxRecursionDepth) return;
|
||||
|
||||
$sSrcKey = get_class($oObject).'::'.$oObject->GetKey();
|
||||
$aNewRelated = array();
|
||||
$oObject->GetRelatedObjects($sRelation, 1, $aNewRelated);
|
||||
foreach($aNewRelated as $sClass => $aObjects)
|
||||
{
|
||||
if (!array_key_exists($sSrcKey, $aGraph))
|
||||
{
|
||||
$aGraph[$sSrcKey] = array();
|
||||
}
|
||||
foreach($aObjects as $oRelatedObject)
|
||||
{
|
||||
$aRelated[$sClass][$oRelatedObject->GetKey()] = $oRelatedObject;
|
||||
$aGraph[$sSrcKey][] = get_class($oRelatedObject).'::'.$oRelatedObject->GetKey();
|
||||
$this->GetRelatedObjects($oRelatedObject, $sRelation, $iMaxRecursionDepth, $aRelated, $aGraph, $iRecursionDepth+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,6 +76,19 @@ abstract class Trigger extends cmdbAbstractObject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given object is in the scope of this trigger
|
||||
* and can potentially be the subject of notifications
|
||||
* @param DBObject $oObject The object to check
|
||||
* @return bool
|
||||
*/
|
||||
public function IsInScope(DBObject $oObject)
|
||||
{
|
||||
// By default the answer is no
|
||||
// Overload this function in your own derived class for a different behavior
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TriggerOnObject extends Trigger
|
||||
@@ -105,6 +118,18 @@ abstract class TriggerOnObject extends Trigger
|
||||
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given object is in the scope of this trigger
|
||||
* and can potentially be the subject of notifications
|
||||
* @param DBObject $oObject The object to check
|
||||
* @return bool
|
||||
*/
|
||||
public function IsInScope(DBObject $oObject)
|
||||
{
|
||||
$sRootClass = $this->Get('target_class');
|
||||
return ($oObject instanceof $sRootClass);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* To trigger notifications when a ticket is updated from the portal
|
||||
|
||||
@@ -1102,9 +1102,6 @@ table.pagination tr td {
|
||||
.pagination_container {
|
||||
padding-left: 3px;
|
||||
}
|
||||
.pager {
|
||||
display:inline-block;
|
||||
}
|
||||
.pager p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
@CHARSET "UTF-8";
|
||||
#left-pane { display: none; }
|
||||
span.ui-layout-resizer { display: none; }
|
||||
#header-logo { display: none; }
|
||||
#logo { display: none; }
|
||||
div.header-menu { display:none; }
|
||||
div.footer { display:none; }
|
||||
#top-bar { display: none; }
|
||||
#menu { display: none; }
|
||||
#inner_menu { display: none; }
|
||||
div.actions_button { display:none; }
|
||||
div.itop_popup { display:none; }
|
||||
div.HRDrawer { display:none; }
|
||||
div.DrawerHandle { display:none; }
|
||||
a.tab { display:none; }
|
||||
div.itop-tab { border: #ccc 1px solid; margin-top: 1em; padding-bottom:1em; }
|
||||
div.itop-tab { border: #ccc 1px solid; margin-top: 1em; padding-bottom:1em; }
|
||||
#combodo_logo { display:none; };
|
||||
|
||||
@@ -350,7 +350,8 @@ EOF
|
||||
$oPage->add('</span>');
|
||||
$oPage->add('<div style="clear:both"></div>');
|
||||
$sMaxUpload = $this->GetMaxUpload();
|
||||
$oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file" onChange="ajaxFileUpload();"><span style="display:none;" id="attachment_loading"> <img src="../images/indicator.gif"></span> '.$sMaxUpload);
|
||||
$oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file"><span style="display:none;" id="attachment_loading"> <img src="../images/indicator.gif"></span> '.$sMaxUpload);
|
||||
$oPage->add_ready_script('$("#file").off("change.itop-attachments").on("change.itop-attachments", function() {ajaxFileUpload();});'); // Workaround for a Chrome 36 bug causing multiple (12!) times the same upload. See http://www.redmine.org/issues/17151
|
||||
$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>');
|
||||
|
||||
@@ -45,8 +45,7 @@ SetupWebPage::AddModule(
|
||||
|
||||
),
|
||||
'dictionary' => array(
|
||||
'en.dict.attachments.php',
|
||||
'fr.dict.attachments.php',
|
||||
|
||||
),
|
||||
'data.struct' => array(
|
||||
// add your 'structure' definition XML files here,
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Localized data
|
||||
*
|
||||
* @author Stephan Rosenke <stephan.rosenke@itomig.de>
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Attachments:TabTitle_Count' => 'Attachments (%1$d)',
|
||||
'Attachments:EmptyTabTitle' => 'Attachments',
|
||||
'Attachments:FieldsetTitle' => 'Attachments',
|
||||
'Attachments:DeleteBtn' => 'Löschen',
|
||||
'Attachments:History_File_Added' => 'Attachment %1$s hinzugefügt.',
|
||||
'Attachments:History_File_Removed' => 'Attachment %1$s entfernt.',
|
||||
'Attachments:AddAttachment' => 'Attachment hinzufügen: ',
|
||||
'Attachments:UploadNotAllowedOnThisSystem' => 'Datei-Upload auf diesem System NICHT erlaubt.',
|
||||
'Attachment:Max_Go' => '(Maximale Datei-Größe: %1$s Go)',
|
||||
'Attachment:Max_Mo' => '(Maximale Datei-Größe: %1$s Mo)',
|
||||
'Attachment:Max_Ko' => '(Maximale Datei-Größe: %1$s Ko)',
|
||||
'Attachments:NoAttachment' => 'Kein Attachment.',
|
||||
));
|
||||
?>
|
||||
0
datamodels/2.x/itop-attachments/es_cr.dict.attachments.php → datamodels/2.x/itop-attachments/es_cr.dict.itop-attachments.php
Normal file → Executable file
0
datamodels/2.x/itop-attachments/es_cr.dict.attachments.php → datamodels/2.x/itop-attachments/es_cr.dict.itop-attachments.php
Normal file → Executable file
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @licence http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
Dict::Add('JA JP', 'Japanese', '日本語', array(
|
||||
'Attachments:TabTitle_Count' => '添付 (%1$d)',
|
||||
'Attachments:EmptyTabTitle' => '添付',
|
||||
'Attachments:FieldsetTitle' => '添付',
|
||||
'Attachments:DeleteBtn' => '削除',
|
||||
'Attachments:History_File_Added' => '添付 %1$s を追加しました。',
|
||||
'Attachments:History_File_Removed' => '添付 %1$s を削除しました。',
|
||||
'Attachments:AddAttachment' => '添付追加: ',
|
||||
'Attachments:UploadNotAllowedOnThisSystem' => 'このシステムでは、ファイルのアップロードは許可されていません。',
|
||||
'Attachment:Max_Go' => '(最大ファイルサイズ: %1$s GB)',
|
||||
'Attachment:Max_Mo' => '(最大ファイルサイズ: %1$s MB)',
|
||||
'Attachment:Max_Ko' => '(最大ファイルサイズ: %1$s KB)',
|
||||
'Attachments:NoAttachment' => '添付はありません。',
|
||||
));
|
||||
?>
|
||||
@@ -350,7 +350,8 @@ EOF
|
||||
$oPage->add('</span>');
|
||||
$oPage->add('<div style="clear:both"></div>');
|
||||
$sMaxUpload = $this->GetMaxUpload();
|
||||
$oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file" onChange="ajaxFileUpload();"><span style="display:none;" id="attachment_loading"> <img src="../images/indicator.gif"></span> '.$sMaxUpload);
|
||||
$oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file"><span style="display:none;" id="attachment_loading"> <img src="../images/indicator.gif"></span> '.$sMaxUpload);
|
||||
$oPage->add_ready_script('$("#file").off("change.itop-attachments").on("change.itop-attachments", function() {ajaxFileUpload();});'); // Workaround for a Chrome 36 bug causing multiple (12!) times the same upload. See http://www.redmine.org/issues/17151
|
||||
$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>');
|
||||
|
||||
@@ -45,8 +45,7 @@ SetupWebPage::AddModule(
|
||||
|
||||
),
|
||||
'dictionary' => array(
|
||||
'en.dict.attachments.php',
|
||||
'fr.dict.attachments.php',
|
||||
|
||||
),
|
||||
'data.struct' => array(
|
||||
// add your 'structure' definition XML files here,
|
||||
|
||||
@@ -2327,6 +2327,9 @@
|
||||
<item id="iosversion_id">
|
||||
<rank>40</rank>
|
||||
</item>
|
||||
<item id="managementip">
|
||||
<rank>45</rank>
|
||||
</item>
|
||||
<item id="ram">
|
||||
<rank>50</rank>
|
||||
</item>
|
||||
@@ -2395,6 +2398,9 @@
|
||||
<item id="location_id">
|
||||
<rank>50</rank>
|
||||
</item>
|
||||
<item id="managementip">
|
||||
<rank>55</rank>
|
||||
</item>
|
||||
<item id="brand_id">
|
||||
<rank>60</rank>
|
||||
</item>
|
||||
@@ -2647,6 +2653,9 @@
|
||||
<item id="osversion_id">
|
||||
<rank>40</rank>
|
||||
</item>
|
||||
<item id="managementip">
|
||||
<rank>45</rank>
|
||||
</item>
|
||||
<item id="oslicence_id">
|
||||
<rank>50</rank>
|
||||
</item>
|
||||
@@ -2721,6 +2730,9 @@
|
||||
<item id="location_id">
|
||||
<rank>50</rank>
|
||||
</item>
|
||||
<item id="managementip">
|
||||
<rank>55</rank>
|
||||
</item>
|
||||
<item id="brand_id">
|
||||
<rank>60</rank>
|
||||
</item>
|
||||
@@ -4409,6 +4421,8 @@
|
||||
<reconciliation>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
<attribute id="version"/>
|
||||
<attribute id="vendor"/>
|
||||
</attributes>
|
||||
</reconciliation>
|
||||
</properties>
|
||||
|
||||
@@ -298,7 +298,6 @@
|
||||
<field id="parent_request_id" xsi:type="AttributeExternalKey">
|
||||
<filter><![CDATA[SELECT UserRequest WHERE id != :this->id AND status NOT IN ('rejected','resolved','closed')]]></filter>
|
||||
<dependencies>
|
||||
<attribute id="service_id"/>
|
||||
</dependencies>
|
||||
<sql>parent_request_id</sql>
|
||||
<target_class>UserRequest</target_class>
|
||||
|
||||
@@ -304,7 +304,6 @@
|
||||
<field id="parent_request_id" xsi:type="AttributeExternalKey">
|
||||
<filter><![CDATA[SELECT UserRequest WHERE id != :this->id AND status NOT IN ('rejected','resolved','closed')]]></filter>
|
||||
<dependencies>
|
||||
<attribute id="service_id"/>
|
||||
</dependencies>
|
||||
<sql>parent_request_id</sql>
|
||||
<target_class>UserRequest</target_class>
|
||||
|
||||
@@ -1560,6 +1560,9 @@
|
||||
<reconciliation>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
<attribute id="priority"/>
|
||||
<attribute id="request_type"/>
|
||||
<attribute id="metric"/>
|
||||
</attributes>
|
||||
</reconciliation>
|
||||
</properties>
|
||||
|
||||
@@ -1555,6 +1555,9 @@
|
||||
<reconciliation>
|
||||
<attributes>
|
||||
<attribute id="name"/>
|
||||
<attribute id="priority"/>
|
||||
<attribute id="request_type"/>
|
||||
<attribute id="metric"/>
|
||||
</attributes>
|
||||
</reconciliation>
|
||||
</properties>
|
||||
|
||||
@@ -127,6 +127,9 @@
|
||||
<item id="model_id">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
<item id="managementip">
|
||||
<rank>25</rank>
|
||||
</item>
|
||||
<item id="nb_u">
|
||||
<rank>30</rank>
|
||||
</item>
|
||||
@@ -198,6 +201,9 @@
|
||||
<item id="model_id">
|
||||
<rank>70</rank>
|
||||
</item>
|
||||
<item id="managementip">
|
||||
<rank>75</rank>
|
||||
</item>
|
||||
<item id="serialnumber">
|
||||
<rank>80</rank>
|
||||
</item>
|
||||
@@ -373,6 +379,9 @@
|
||||
<item id="model_id">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
<item id="managementip">
|
||||
<rank>35</rank>
|
||||
</item>
|
||||
<item id="nb_u">
|
||||
<rank>30</rank>
|
||||
</item>
|
||||
@@ -444,6 +453,9 @@
|
||||
<item id="model_id">
|
||||
<rank>70</rank>
|
||||
</item>
|
||||
<item id="managementip">
|
||||
<rank>75</rank>
|
||||
</item>
|
||||
<item id="serialnumber">
|
||||
<rank>80</rank>
|
||||
</item>
|
||||
@@ -618,6 +630,9 @@
|
||||
<item id="model_id">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
<item id="managementip">
|
||||
<rank>25</rank>
|
||||
</item>
|
||||
<item id="nb_u">
|
||||
<rank>30</rank>
|
||||
</item>
|
||||
@@ -689,6 +704,9 @@
|
||||
<item id="model_id">
|
||||
<rank>70</rank>
|
||||
</item>
|
||||
<item id="managementip">
|
||||
<rank>75</rank>
|
||||
</item>
|
||||
<item id="serialnumber">
|
||||
<rank>80</rank>
|
||||
</item>
|
||||
@@ -863,6 +881,9 @@
|
||||
<item id="model_id">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
<item id="managementip">
|
||||
<rank>25</rank>
|
||||
</item>
|
||||
<item id="nb_u">
|
||||
<rank>30</rank>
|
||||
</item>
|
||||
@@ -934,6 +955,9 @@
|
||||
<item id="model_id">
|
||||
<rank>70</rank>
|
||||
</item>
|
||||
<item id="managementip">
|
||||
<rank>75</rank>
|
||||
</item>
|
||||
<item id="serialnumber">
|
||||
<rank>80</rank>
|
||||
</item>
|
||||
|
||||
@@ -877,6 +877,9 @@
|
||||
<presentation>
|
||||
<details>
|
||||
<items>
|
||||
<item id="name">
|
||||
<rank>5</rank>
|
||||
</item>
|
||||
<item id="ipaddress">
|
||||
<rank>10</rank>
|
||||
</item>
|
||||
@@ -902,6 +905,9 @@
|
||||
</details>
|
||||
<search>
|
||||
<items>
|
||||
<item id="name">
|
||||
<rank>5</rank>
|
||||
</item>
|
||||
<item id="ipaddress">
|
||||
<rank>10</rank>
|
||||
</item>
|
||||
|
||||
@@ -754,7 +754,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'UI:Apply_Stimulus_On_Object_In_State_ToTarget_State' => 'Applying %1$s on object: %2$s in state %3$s to target state: %4$s.',
|
||||
'UI:ObjectCouldNotBeWritten' => 'The object could not be written: %1$s',
|
||||
'UI:PageTitle:FatalError' => 'iTop - Fatal Error',
|
||||
'UI:SystemIntrusion' => 'Access denied. You have trying to perform an operation that is not allowed for you.',
|
||||
'UI:SystemIntrusion' => 'Access denied. You have requested an operation that is not allowed for you.',
|
||||
'UI:FatalErrorMessage' => 'Fatal error, iTop cannot continue.',
|
||||
'UI:Error_Details' => 'Error: %1$s.',
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ jQuery.extend(
|
||||
var fileId = 'jUploadFile' + id;
|
||||
var form = jQuery('<form action="" method="POST" name="' + formId + '" id="' + formId + '" enctype="multipart/form-data"></form>');
|
||||
var oldElement = jQuery('#' + fileElementId);
|
||||
var newElement = jQuery(oldElement).clone();
|
||||
var newElement = jQuery(oldElement).clone(true);
|
||||
jQuery(oldElement).attr('id', fileId);
|
||||
jQuery(oldElement).before(newElement);
|
||||
jQuery(oldElement).appendTo(form);
|
||||
@@ -131,12 +131,12 @@ jQuery.extend(
|
||||
jQuery.handleError(s, xml, null, e);
|
||||
}
|
||||
|
||||
}, 100)
|
||||
}, 100);
|
||||
|
||||
xml = null
|
||||
xml = null;
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
// Timeout checker
|
||||
if ( s.timeout > 0 )
|
||||
{
|
||||
|
||||
@@ -361,6 +361,7 @@ $.Autocompleter = function(input, options) {
|
||||
$.ajax({
|
||||
// try to leverage ajaxQueue plugin to abort previous requests
|
||||
mode: "abort",
|
||||
type: "POST",
|
||||
// limit abortion to this input
|
||||
port: "autocomplete" + input.name,
|
||||
dataType: options.dataType,
|
||||
|
||||
@@ -134,6 +134,9 @@ function WizardHelper(sClass, sFormPrefix, sState)
|
||||
//console.log(sFieldCode);
|
||||
this.UpdateCurrentValue(sCleanFieldCode);
|
||||
}
|
||||
// Remove unnecessary stuff
|
||||
this.m_oData.m_oDefaultValue = {};
|
||||
this.m_oData.m_oAllowedValues = {};
|
||||
}
|
||||
|
||||
this.UpdateWizardToJSON = function ()
|
||||
|
||||
640
pages/UI.php
640
pages/UI.php
@@ -24,288 +24,6 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Perform all the needed checks to delete one (or more) objects
|
||||
*/
|
||||
function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed, $oFilter = null)
|
||||
{
|
||||
$oDeletionPlan = new DeletionPlan();
|
||||
|
||||
foreach($aObjects as $oObj)
|
||||
{
|
||||
if ($bDeleteConfirmed)
|
||||
{
|
||||
$oObj->DBDeleteTracked(CMDBObject::GetCurrentChange(), null, $oDeletionPlan);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oObj->CheckToDelete($oDeletionPlan);
|
||||
}
|
||||
}
|
||||
|
||||
if ($bDeleteConfirmed)
|
||||
{
|
||||
if (count($aObjects) == 1)
|
||||
{
|
||||
$oObj = $aObjects[0];
|
||||
$oP->add("<h1>".Dict::Format('UI:Title:DeletionOf_Object', $oObj->GetName())."</h1>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->add("<h1>".Dict::Format('UI:Title:BulkDeletionOf_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass))."</h1>\n");
|
||||
}
|
||||
// Security - do not allow the user to force a forbidden delete by the mean of page arguments...
|
||||
if ($oDeletionPlan->FoundSecurityIssue())
|
||||
{
|
||||
throw new CoreException(Dict::S('UI:Error:NotEnoughRightsToDelete'));
|
||||
}
|
||||
if ($oDeletionPlan->FoundManualOperation())
|
||||
{
|
||||
throw new CoreException(Dict::S('UI:Error:CannotDeleteBecauseManualOpNeeded'));
|
||||
}
|
||||
if ($oDeletionPlan->FoundManualDelete())
|
||||
{
|
||||
throw new CoreException(Dict::S('UI:Error:CannotDeleteBecauseOfDepencies'));
|
||||
}
|
||||
|
||||
// Report deletions
|
||||
//
|
||||
$aDisplayData = array();
|
||||
foreach ($oDeletionPlan->ListDeletes() as $sTargetClass => $aDeletes)
|
||||
{
|
||||
foreach ($aDeletes as $iId => $aData)
|
||||
{
|
||||
$oToDelete = $aData['to_delete'];
|
||||
|
||||
if (isset($aData['requested_explicitely']))
|
||||
{
|
||||
$sMessage = Dict::S('UI:Delete:Deleted');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sMessage = Dict::S('UI:Delete:AutomaticallyDeleted');
|
||||
}
|
||||
$aDisplayData[] = array(
|
||||
'class' => MetaModel::GetName(get_class($oToDelete)),
|
||||
'object' => $oToDelete->GetName(),
|
||||
'consequence' => $sMessage,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Report updates
|
||||
//
|
||||
foreach ($oDeletionPlan->ListUpdates() as $sTargetClass => $aToUpdate)
|
||||
{
|
||||
foreach ($aToUpdate as $iId => $aData)
|
||||
{
|
||||
$oToUpdate = $aData['to_reset'];
|
||||
$aDisplayData[] = array(
|
||||
'class' => MetaModel::GetName(get_class($oToUpdate)),
|
||||
'object' => $oToUpdate->GetHyperLink(),
|
||||
'consequence' => Dict::Format('UI:Delete:AutomaticResetOf_Fields', $aData['attributes_list']),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Report automatic jobs
|
||||
//
|
||||
if ($oDeletionPlan->GetTargetCount() > 0)
|
||||
{
|
||||
if (count($aObjects) == 1)
|
||||
{
|
||||
$oObj = $aObjects[0];
|
||||
$oP->p(Dict::Format('UI:Delete:CleaningUpRefencesTo_Object', $oObj->GetName()));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->p(Dict::Format('UI:Delete:CleaningUpRefencesTo_Several_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass)));
|
||||
}
|
||||
$aDisplayConfig = array();
|
||||
$aDisplayConfig['class'] = array('label' => 'Class', 'description' => '');
|
||||
$aDisplayConfig['object'] = array('label' => 'Object', 'description' => '');
|
||||
$aDisplayConfig['consequence'] = array('label' => 'Done', 'description' => Dict::S('UI:Delete:Done+'));
|
||||
$oP->table($aDisplayConfig, $aDisplayData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (count($aObjects) == 1)
|
||||
{
|
||||
$oObj = $aObjects[0];
|
||||
$oP->add("<h1>".Dict::Format('UI:Delete:ConfirmDeletionOf_Name', $oObj->GetName())."</h1>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->add("<h1>".Dict::Format('UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass))."</h1>\n");
|
||||
}
|
||||
// Explain what should be done
|
||||
//
|
||||
$aDisplayData = array();
|
||||
foreach ($oDeletionPlan->ListDeletes() as $sTargetClass => $aDeletes)
|
||||
{
|
||||
foreach ($aDeletes as $iId => $aData)
|
||||
{
|
||||
$oToDelete = $aData['to_delete'];
|
||||
$bAutoDel = (($aData['mode'] == DEL_SILENT) || ($aData['mode'] == DEL_AUTO));
|
||||
if (array_key_exists('issue', $aData))
|
||||
{
|
||||
if ($bAutoDel)
|
||||
{
|
||||
if (isset($aData['requested_explicitely']))
|
||||
{
|
||||
$sConsequence = Dict::Format('UI:Delete:CannotDeleteBecause', $aData['issue']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConsequence = Dict::Format('UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible', $aData['issue']);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConsequence = Dict::Format('UI:Delete:MustBeDeletedManuallyButNotPossible', $aData['issue']);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($bAutoDel)
|
||||
{
|
||||
if (isset($aData['requested_explicitely']))
|
||||
{
|
||||
$sConsequence = ''; // not applicable
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConsequence = Dict::S('UI:Delete:WillBeDeletedAutomatically');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConsequence = Dict::S('UI:Delete:MustBeDeletedManually');
|
||||
}
|
||||
}
|
||||
$aDisplayData[] = array(
|
||||
'class' => MetaModel::GetName(get_class($oToDelete)),
|
||||
'object' => $oToDelete->GetHyperLink(),
|
||||
'consequence' => $sConsequence,
|
||||
);
|
||||
}
|
||||
}
|
||||
foreach ($oDeletionPlan->ListUpdates() as $sRemoteClass => $aToUpdate)
|
||||
{
|
||||
foreach ($aToUpdate as $iId => $aData)
|
||||
{
|
||||
$oToUpdate = $aData['to_reset'];
|
||||
if (array_key_exists('issue', $aData))
|
||||
{
|
||||
$sConsequence = Dict::Format('UI:Delete:CannotUpdateBecause_Issue', $aData['issue']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sConsequence = Dict::Format('UI:Delete:WillAutomaticallyUpdate_Fields', $aData['attributes_list']);
|
||||
}
|
||||
$aDisplayData[] = array(
|
||||
'class' => MetaModel::GetName(get_class($oToUpdate)),
|
||||
'object' => $oToUpdate->GetHyperLink(),
|
||||
'consequence' => $sConsequence,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$iImpactedIndirectly = $oDeletionPlan->GetTargetCount() - count($aObjects);
|
||||
if ($iImpactedIndirectly > 0)
|
||||
{
|
||||
if (count($aObjects) == 1)
|
||||
{
|
||||
$oObj = $aObjects[0];
|
||||
$oP->p(Dict::Format('UI:Delete:Count_Objects/LinksReferencing_Object', $iImpactedIndirectly, $oObj->GetName()));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->p(Dict::Format('UI:Delete:Count_Objects/LinksReferencingTheObjects', $iImpactedIndirectly));
|
||||
}
|
||||
$oP->p(Dict::S('UI:Delete:ReferencesMustBeDeletedToEnsureIntegrity'));
|
||||
}
|
||||
|
||||
if (($iImpactedIndirectly > 0) || $oDeletionPlan->FoundStopper())
|
||||
{
|
||||
$aDisplayConfig = array();
|
||||
$aDisplayConfig['class'] = array('label' => 'Class', 'description' => '');
|
||||
$aDisplayConfig['object'] = array('label' => 'Object', 'description' => '');
|
||||
$aDisplayConfig['consequence'] = array('label' => 'Consequence', 'description' => Dict::S('UI:Delete:Consequence+'));
|
||||
$oP->table($aDisplayConfig, $aDisplayData);
|
||||
}
|
||||
|
||||
if ($oDeletionPlan->FoundStopper())
|
||||
{
|
||||
if ($oDeletionPlan->FoundSecurityIssue())
|
||||
{
|
||||
$oP->p(Dict::S('UI:Delete:SorryDeletionNotAllowed'));
|
||||
}
|
||||
elseif ($oDeletionPlan->FoundManualOperation())
|
||||
{
|
||||
$oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations'));
|
||||
}
|
||||
else // $bFoundManualOp
|
||||
{
|
||||
$oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations'));
|
||||
}
|
||||
$oAppContext = new ApplicationContext();
|
||||
$oP->add("<form method=\"post\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::ReadParam('transaction_id')."\">\n");
|
||||
$oP->add("<input type=\"button\" onclick=\"window.history.back();\" value=\"".Dict::S('UI:Button:Back')."\">\n");
|
||||
$oP->add("<input DISABLED type=\"submit\" name=\"\" value=\"".Dict::S('UI:Button:Delete')."\">\n");
|
||||
$oP->add($oAppContext->GetForForm());
|
||||
$oP->add("</form>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
if (count($aObjects) == 1)
|
||||
{
|
||||
$oObj = $aObjects[0];
|
||||
$id = $oObj->GetKey();
|
||||
$oP->p('<h1>'.Dict::Format('UI:Delect:Confirm_Object', $oObj->GetHyperLink()).'</h1>');
|
||||
$oP->add("<form method=\"post\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::ReadParam('transaction_id')."\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"delete_confirmed\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"id\" value=\"$id\">\n");
|
||||
$oP->add("<input type=\"button\" onclick=\"window.history.back();\" value=\"".Dict::S('UI:Button:Back')."\">\n");
|
||||
$oP->add("<input type=\"submit\" name=\"\" value=\"".Dict::S('UI:Button:Delete')."\">\n");
|
||||
$oP->add($oAppContext->GetForForm());
|
||||
$oP->add("</form>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->p('<h1>'.Dict::Format('UI:Delect:Confirm_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass)).'</h1>');
|
||||
foreach($aObjects as $oObj)
|
||||
{
|
||||
$aKeys[] = $oObj->GetKey();
|
||||
}
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
$oFilter->AddCondition('id', $aKeys, 'IN');
|
||||
$oSet = new CMDBobjectSet($oFilter);
|
||||
$oP->add('<div id="0">');
|
||||
CMDBAbstractObject::DisplaySet($oP, $oSet, array('display_limit' => false, 'menu' => false));
|
||||
$oP->add("</div>\n");
|
||||
$oP->add("<form method=\"post\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::ReadParam('transaction_id')."\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"bulk_delete_confirmed\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"filter\" value=\"".$oFilter->Serialize()."\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">\n");
|
||||
foreach($aObjects as $oObj)
|
||||
{
|
||||
$oP->add("<input type=\"hidden\" name=\"selectObject[]\" value=\"".$oObj->GetKey()."\">\n");
|
||||
}
|
||||
$oP->add("<input type=\"button\" onclick=\"window.history.back();\" value=\"".Dict::S('UI:Button:Back')."\">\n");
|
||||
$oP->add("<input type=\"submit\" name=\"\" value=\"".Dict::S('UI:Button:Delete')."\">\n");
|
||||
$oP->add($oAppContext->GetForForm());
|
||||
$oP->add("</form>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a popup welcome message, once per session at maximum
|
||||
@@ -390,13 +108,12 @@ function ApplyNextAction(Webpage $oP, CMDBObject $oObj, $sNextAction)
|
||||
}
|
||||
}
|
||||
|
||||
function ReloadAndDisplay($oPage, $oObj, $sMessage = '', $sSeverity)
|
||||
function ReloadAndDisplay($oPage, $oObj, $sMessageId = '', $sMessage = '', $sSeverity = null)
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sMessageKey = get_class($oObj).'::'.$oObj->GetKey();
|
||||
if ($sMessage != '')
|
||||
if ($sMessageId != '')
|
||||
{
|
||||
$_SESSION['obj_messages'][$sMessageKey] = array('severity' => $sSeverity, 'message' => $sMessage);
|
||||
cmdbAbstractObject::SetSessionMessage(get_class($oObj), $oObj->GetKey(), $sMessageId, $sMessage, $sSeverity, 0, true /* must not exist */);
|
||||
}
|
||||
$oPage->add_header('Location: '.utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class='.get_class($oObj).'&id='.$oObj->getKey().'&'.$oAppContext->GetForLink());
|
||||
}
|
||||
@@ -817,7 +534,7 @@ try
|
||||
else
|
||||
{
|
||||
$oP->set_title(Dict::S('UI:SearchResultsPageTitle'));
|
||||
$oP->p("<h1>".Dict::Format('UI:FullTextSearchTitle_Text', $sFullText)."</h1>");
|
||||
$oP->p("<h1>".Dict::Format('UI:FullTextSearchTitle_Text', htmlentities($sFullText, ENT_QUOTES, 'UTF-8'))."</h1>");
|
||||
$iCount = 0;
|
||||
$iBlock = 0;
|
||||
// Search in full text mode in all the classes
|
||||
@@ -959,174 +676,9 @@ try
|
||||
$sClass = utils::ReadParam('class', '', false, 'class');
|
||||
$oFullSetFilter = DBObjectSearch::unserialize($sFilter);
|
||||
$aSelectedObj = utils::ReadMultipleSelection($oFullSetFilter);
|
||||
if (count($aSelectedObj) > 0)
|
||||
{
|
||||
$iAllowedCount = count($aSelectedObj);
|
||||
$sSelectedObj = implode(',', $aSelectedObj);
|
||||
|
||||
$sOQL = "SELECT $sClass WHERE id IN (".$sSelectedObj.")";
|
||||
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL));
|
||||
|
||||
// Compute the distribution of the values for each field to determine which of the "scalar" fields are homogenous
|
||||
$aList = MetaModel::ListAttributeDefs($sClass);
|
||||
$aValues = array();
|
||||
foreach($aList as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($oAttDef->IsScalar())
|
||||
{
|
||||
$aValues[$sAttCode] = array();
|
||||
}
|
||||
}
|
||||
while($oObj = $oSet->Fetch())
|
||||
{
|
||||
foreach($aList as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($oAttDef->IsScalar() && $oAttDef->IsWritable())
|
||||
{
|
||||
$currValue = $oObj->Get($sAttCode);
|
||||
if ($oAttDef instanceof AttributeCaseLog)
|
||||
{
|
||||
$currValue = ' '; // Don't put an empty string, in case the field would be considered as mandatory...
|
||||
}
|
||||
if (is_object($currValue)) continue; // Skip non scalar values...
|
||||
if(!array_key_exists($currValue, $aValues[$sAttCode]))
|
||||
{
|
||||
$aValues[$sAttCode][$currValue] = array('count' => 1, 'display' => $oObj->GetAsHTML($sAttCode));
|
||||
}
|
||||
else
|
||||
{
|
||||
$aValues[$sAttCode][$currValue]['count']++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now create an object that has values for the homogenous values only
|
||||
$oDummyObj = new $sClass(); // @@ What if the class is abstract ?
|
||||
$aComments = array();
|
||||
function MyComparison($a, $b) // Sort descending
|
||||
{
|
||||
if ($a['count'] == $b['count'])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return ($a['count'] > $b['count']) ? -1 : 1;
|
||||
}
|
||||
|
||||
$iFormId = cmdbAbstractObject::GetNextFormId(); // Identifier that prefixes all the form fields
|
||||
$sReadyScript = '';
|
||||
$aDependsOn = array();
|
||||
$sFormPrefix = '2_';
|
||||
foreach($aList as $sAttCode => $oAttDef)
|
||||
{
|
||||
$aPrerequisites = MetaModel::GetPrequisiteAttributes($sClass, $sAttCode); // List of attributes that are needed for the current one
|
||||
if (count($aPrerequisites) > 0)
|
||||
{
|
||||
// When 'enabling' a field, all its prerequisites must be enabled too
|
||||
$sFieldList = "['{$sFormPrefix}".implode("','{$sFormPrefix}", $aPrerequisites)."']";
|
||||
$oP->add_ready_script("$('#enable_{$sFormPrefix}{$sAttCode}').bind('change', function(evt, sFormId) { return PropagateCheckBox( this.checked, $sFieldList, true); } );\n");
|
||||
}
|
||||
$aDependents = MetaModel::GetDependentAttributes($sClass, $sAttCode); // List of attributes that are needed for the current one
|
||||
if (count($aDependents) > 0)
|
||||
{
|
||||
// When 'disabling' a field, all its dependent fields must be disabled too
|
||||
$sFieldList = "['{$sFormPrefix}".implode("','{$sFormPrefix}", $aDependents)."']";
|
||||
$oP->add_ready_script("$('#enable_{$sFormPrefix}{$sAttCode}').bind('change', function(evt, sFormId) { return PropagateCheckBox( this.checked, $sFieldList, false); } );\n");
|
||||
}
|
||||
if ($oAttDef->IsScalar() && $oAttDef->IsWritable())
|
||||
{
|
||||
if ($oAttDef->GetEditClass() == 'One Way Password')
|
||||
{
|
||||
|
||||
$sTip = "Unknown values";
|
||||
$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] .= '<div class="multi_values" id="multi_values_'.$sAttCode.'"> ? </div>';
|
||||
$sReadyScript .= 'ToogleField(false, \''.$iFormId.'_'.$sAttCode.'\');'."\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = count($aValues[$sAttCode]);
|
||||
if ($iCount == 1)
|
||||
{
|
||||
// Homogenous value
|
||||
reset($aValues[$sAttCode]);
|
||||
$aKeys = array_keys($aValues[$sAttCode]);
|
||||
$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] .= '<div class="mono_value">1</div>';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-homogenous value
|
||||
$aMultiValues = $aValues[$sAttCode];
|
||||
uasort($aMultiValues, 'MyComparison');
|
||||
$iMaxCount = 5;
|
||||
$sTip = "<p><b>".Dict::Format('UI:BulkModify_Count_DistinctValues', $iCount)."</b><ul>";
|
||||
$index = 0;
|
||||
foreach($aMultiValues as $sCurrValue => $aVal)
|
||||
{
|
||||
$sDisplayValue = empty($aVal['display']) ? '<i>'.Dict::S('Enum:Undefined').'</i>' : str_replace(array("\n", "\r"), " ", $aVal['display']);
|
||||
$sTip .= "<li>".Dict::Format('UI:BulkModify:Value_Exists_N_Times', $sDisplayValue, $aVal['count'])."</li>";
|
||||
$index++;
|
||||
if ($iMaxCount == $index)
|
||||
{
|
||||
$sTip .= "<li>".Dict::Format('UI:BulkModify:N_MoreValues', count($aMultiValues) - $iMaxCount)."</li>";
|
||||
break;
|
||||
}
|
||||
}
|
||||
$sTip .= "</ul></p>";
|
||||
$sTip = addslashes($sTip);
|
||||
$sReadyScript .= "$('#multi_values_$sAttCode').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );";
|
||||
|
||||
$oDummyObj->Set($sAttCode, null);
|
||||
$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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
|
||||
if (($sStateAttCode != '') && ($oDummyObj->GetState() == ''))
|
||||
{
|
||||
// Hmmm, it's not gonna work like this ! Set a default value for the "state"
|
||||
// Maybe we should use the "state" that is the most common among the objects...
|
||||
$aMultiValues = $aValues[$sStateAttCode];
|
||||
uasort($aMultiValues, 'MyComparison');
|
||||
foreach($aMultiValues as $sCurrValue => $aVal)
|
||||
{
|
||||
$oDummyObj->Set($sStateAttCode, $sCurrValue);
|
||||
break;
|
||||
}
|
||||
//$oStateAtt = MetaModel::GetAttributeDef($sClass, $sStateAttCode);
|
||||
//$oDummyObj->Set($sStateAttCode, $oStateAtt->GetDefaultValue());
|
||||
}
|
||||
$oP->add("<div class=\"page_header\">\n");
|
||||
$oP->add("<h1>".$oDummyObj->GetIcon()." ".Dict::Format('UI:Modify_M_ObjectsOf_Class_OutOf_N', $iAllowedCount, $sClass, $iAllowedCount)."</h1>\n");
|
||||
$oP->add("</div>\n");
|
||||
|
||||
$oP->add("<div class=\"wizContainer\">\n");
|
||||
$oDummyObj->DisplayModifyForm($oP, array('fieldsComments' => $aComments, 'noRelations' => true, 'custom_operation' => 'preview_or_modify_all', 'custom_button' => Dict::S('UI:Button:PreviewModifications'), 'selectObj' => $sSelectedObj, 'filter' => $sFilter, 'preview_mode' => true, 'disabled_fields' => '{}', 'disable_plugins' => true));
|
||||
$oP->add("</div>\n");
|
||||
$oP->add_ready_script($sReadyScript);
|
||||
$sURL = "./UI.php?operation=search&filter=".urlencode($sFilter)."&".$oAppContext->GetForLink();
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('.wizContainer button.cancel').unbind('click');
|
||||
$('.wizContainer button.cancel').click( function() { window.location.href = '$sURL'; } );
|
||||
EOF
|
||||
);
|
||||
|
||||
} // Else no object selected ???
|
||||
else
|
||||
{
|
||||
$oP->p("No object selected !, nothing to do");
|
||||
}
|
||||
$sCancelUrl = "./UI.php?operation=search&filter=".urlencode($sFilter)."&".$oAppContext->GetForLink();
|
||||
$aContext = array('filter' => $sFilter);
|
||||
cmdbAbstractObject::DisplayBulkModifyForm($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $sCancelUrl, array(), $aContext);
|
||||
break;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1145,101 +697,12 @@ EOF
|
||||
throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObj'));
|
||||
}
|
||||
$aSelectedObj = explode(',', $sSelectedObj);
|
||||
$aHeaders = array(
|
||||
'form::select' => array('label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList:not(:disabled)', this.checked);\"></input>", 'description' => Dict::S('UI:SelectAllToggle+')),
|
||||
'object' => array('label' => MetaModel::GetName($sClass), 'description' => Dict::S('UI:ModifiedObject')),
|
||||
'status' => array('label' => Dict::S('UI:BulkModifyStatus'), 'description' => Dict::S('UI:BulkModifyStatus+')),
|
||||
'errors' => array('label' => Dict::S('UI:BulkModifyErrors'), 'description' => Dict::S('UI:BulkModifyErrors+')),
|
||||
$sCancelUrl = "./UI.php?operation=search&filter=".urlencode($sFilter)."&".$oAppContext->GetForLink();
|
||||
$aContext = array(
|
||||
'filter' => $sFilter,
|
||||
'selectObj' => $sSelectedObj,
|
||||
);
|
||||
$aRows = array();
|
||||
|
||||
$oP->add("<div class=\"page_header\">\n");
|
||||
$oP->add("<h1>".MetaModel::GetClassIcon($sClass)." ".Dict::Format('UI:Modify_N_ObjectsOf_Class', count($aSelectedObj), $sClass)."</h1>\n");
|
||||
$oP->add("</div>\n");
|
||||
$oP->set_title(Dict::Format('UI:Modify_N_ObjectsOf_Class', count($aSelectedObj), $sClass));
|
||||
if (!$bPreview)
|
||||
{
|
||||
// Not in preview mode, do the update for real
|
||||
$sTransactionId = utils::ReadPostedParam('transaction_id', '');
|
||||
if (!utils::IsTransactionValid($sTransactionId, false))
|
||||
{
|
||||
throw new Exception(Dict::S('UI:Error:ObjectAlreadyUpdated'));
|
||||
}
|
||||
utils::RemoveTransaction($sTransactionId);
|
||||
}
|
||||
foreach($aSelectedObj as $iId)
|
||||
{
|
||||
$oObj = MetaModel::GetObject($sClass, $iId);
|
||||
$aErrors = $oObj->UpdateObjectFromPostedForm('');
|
||||
$bResult = (count($aErrors) == 0);
|
||||
if ($bResult)
|
||||
{
|
||||
list($bResult, $aErrors) = $oObj->CheckToWrite(true /* Enforce Read-only fields */);
|
||||
}
|
||||
if ($bPreview)
|
||||
{
|
||||
$sStatus = $bResult ? Dict::S('UI:BulkModifyStatusOk') : Dict::S('UI:BulkModifyStatusError');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sStatus = $bResult ? Dict::S('UI:BulkModifyStatusModified') : Dict::S('UI:BulkModifyStatusSkipped');
|
||||
}
|
||||
$sCSSClass = $bResult ? HILIGHT_CLASS_NONE : HILIGHT_CLASS_CRITICAL;
|
||||
$sChecked = $bResult ? 'checked' : '';
|
||||
$sDisabled = $bResult ? '' : 'disabled';
|
||||
$aRows[] = array(
|
||||
'form::select' => "<input type=\"checkbox\" class=\"selectList\" $sChecked $sDisabled\"></input>",
|
||||
'object' => $oObj->GetHyperlink(),
|
||||
'status' => $sStatus,
|
||||
'errors' => '<p>'.($bResult ? '': implode('</p><p>', $aErrors)).'</p>',
|
||||
'@class' => $sCSSClass,
|
||||
);
|
||||
if ($bResult && (!$bPreview))
|
||||
{
|
||||
$oObj->DBUpdate();
|
||||
}
|
||||
}
|
||||
$oP->Table($aHeaders, $aRows);
|
||||
$sURL = "./UI.php?operation=search&filter=".urlencode($sFilter)."&".$oAppContext->GetForLink();
|
||||
if ($bPreview)
|
||||
{
|
||||
// Form to submit:
|
||||
$oP->add("<form method=\"post\" action=\"UI.php\" enctype=\"multipart/form-data\">\n");
|
||||
$aDefaults = utils::ReadParam('default', array());
|
||||
$oP->add($oAppContext->GetForForm());
|
||||
$oP->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"filter\" value=\"$sFilter\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"selectObj\" value=\"$sSelectedObj\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"preview_or_modify_all\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"preview_mode\" value=\"0\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\">\n");
|
||||
$oP->add("<button type=\"button\" class=\"action cancel\" onClick=\"window.location.href='$sURL'\">".Dict::S('UI:Button:Cancel')."</button> \n");
|
||||
$oP->add("<button type=\"submit\" class=\"action\"><span>".Dict::S('UI:Button:ModifyAll')."</span></button>\n");
|
||||
foreach($_POST as $sKey => $value)
|
||||
{
|
||||
if (preg_match('/attr_(.+)/', $sKey, $aMatches))
|
||||
{
|
||||
// Beware: some values (like durations) are passed as arrays
|
||||
if (is_array($value))
|
||||
{
|
||||
foreach($value as $vKey => $vValue)
|
||||
{
|
||||
$oP->add("<input type=\"hidden\" name=\"{$sKey}[$vKey]\" value=\"$vValue\">\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->add("<input type=\"hidden\" name=\"$sKey\" value=\"$value\">\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
$oP->add("</form>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$sURL = "./UI.php?operation=search&filter=".urlencode($sFilter)."&".$oAppContext->GetForLink();
|
||||
$oP->add("<button type=\"button\" onClick=\"window.location.href='$sURL'\" class=\"action\"><span>".Dict::S('UI:Button:Done')."</span></button>\n");
|
||||
}
|
||||
cmdbAbstractObject::DoBulkModify($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $bPreview, $sCancelUrl, $aContext);
|
||||
break;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1431,7 +894,7 @@ EOF
|
||||
else
|
||||
{
|
||||
// Nothing more to do
|
||||
ReloadAndDisplay($oP, $oObj, $sMessage, $sSeverity);
|
||||
ReloadAndDisplay($oP, $oObj, 'update', $sMessage, $sSeverity);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1464,47 +927,58 @@ EOF
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case 'delete':
|
||||
case 'bulk_delete': // Actual bulk deletion (if confirmed)
|
||||
$sClass = utils::ReadPostedParam('class', '');
|
||||
$sClass = utils::ReadParam('class', '', false, 'class');
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
$sFilter = utils::ReadPostedParam('filter', '');
|
||||
$oFullSetFilter = DBObjectSearch::unserialize($sFilter);
|
||||
$aSelectObject = utils::ReadMultipleSelection($oFullSetFilter);
|
||||
$aObjects = array();
|
||||
if ( empty($sClass) || empty($aSelectObject)) // TO DO: check that the class name is valid !
|
||||
if ($operation == 'delete')
|
||||
{
|
||||
throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObject[]'));
|
||||
// Single object
|
||||
$id = utils::ReadParam('id', '');
|
||||
$oObj = MetaModel::GetObject($sClass, $id);
|
||||
$aObjects[] = $oObj;
|
||||
if (!UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, DBObjectSet::FromObject($oObj)))
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Error:DeleteNotAllowedOn_Class', $sClassLabel));
|
||||
}
|
||||
}
|
||||
foreach($aSelectObject as $iId)
|
||||
else
|
||||
{
|
||||
$aObjects[] = MetaModel::GetObject($sClass, $iId);
|
||||
// Several objects
|
||||
$sFilter = utils::ReadPostedParam('filter', '');
|
||||
$oFullSetFilter = DBObjectSearch::unserialize($sFilter);
|
||||
$aSelectObject = utils::ReadMultipleSelection($oFullSetFilter);
|
||||
if ( empty($sClass) || empty($aSelectObject)) // TO DO: check that the class name is valid !
|
||||
{
|
||||
throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObject[]'));
|
||||
}
|
||||
foreach($aSelectObject as $iId)
|
||||
{
|
||||
$aObjects[] = MetaModel::GetObject($sClass, $iId);
|
||||
}
|
||||
if (count($aObjects) == 1)
|
||||
{
|
||||
if (!UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, DBObjectSet::FromArray($sClass, $aObjects)))
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Error:BulkDeleteNotAllowedOn_Class', $sClassLabel));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_DELETE, DBObjectSet::FromArray($sClass, $aObjects)))
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Error:BulkDeleteNotAllowedOn_Class', $sClassLabel));
|
||||
}
|
||||
$oP->set_title(Dict::S('UI:BulkDeletePageTitle'));
|
||||
}
|
||||
}
|
||||
if (!UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_DELETE, DBObjectSet::FromArray($sClass, $aObjects)))
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Error:BulkDeleteNotAllowedOn_Class', $sClass));
|
||||
}
|
||||
$oP->set_title(Dict::S('UI:BulkDeletePageTitle'));
|
||||
DeleteObjects($oP, $sClass, $aObjects, ($operation == 'bulk_delete_confirmed'), $oFullSetFilter);
|
||||
break;
|
||||
// Go for the common part... (delete single, delete bulk, delete confirmed)
|
||||
cmdbAbstractObject::DeleteObjects($oP, $sClass, $aObjects, ($operation != 'bulk_delete_confirmed'), 'bulk_delete_confirmed');
|
||||
break;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case 'delete': // Deletion (preview)
|
||||
case 'delete_confirmed': // Deletion (confirmed)
|
||||
$sClass = utils::ReadParam('class', '', false, 'class');
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
$id = utils::ReadParam('id', '');
|
||||
$oObj = MetaModel::GetObject($sClass, $id);
|
||||
|
||||
if (!UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, DBObjectSet::FromObject($oObj)))
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Error:DeleteNotAllowedOn_Class', $sClass));
|
||||
}
|
||||
DeleteObjects($oP, $sClass, array($oObj), ($operation == 'delete_confirmed'));
|
||||
break;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case 'apply_new': // Creation of a new object
|
||||
$sClass = utils::ReadPostedParam('class', '', 'class');
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
@@ -1555,7 +1029,7 @@ EOF
|
||||
else
|
||||
{
|
||||
// Nothing more to do
|
||||
ReloadAndDisplay($oP, $oObj, $sMessage, 'ok');
|
||||
ReloadAndDisplay($oP, $oObj, 'create', $sMessage, 'ok');
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2116,7 +1590,7 @@ EOF
|
||||
$sSeverity = 'error';
|
||||
}
|
||||
}
|
||||
ReloadAndDisplay($oP, $oObj, $sMessage, $sSeverity);
|
||||
ReloadAndDisplay($oP, $oObj, 'apply_stimulus', $sMessage, $sSeverity);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -936,6 +936,7 @@ EOF
|
||||
|
||||
case 'shortcut_delete_go':
|
||||
$oSearch = new DBObjectSearch('Shortcut');
|
||||
$oSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$aShortcuts = utils::ReadMultipleSelection($oSearch);
|
||||
foreach ($aShortcuts as $iShortcut)
|
||||
{
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Execute and shows the data quality audit
|
||||
*
|
||||
@@ -147,6 +146,8 @@ try
|
||||
require_once('../approot.inc.php');
|
||||
require_once(APPROOT.'/application/application.inc.php');
|
||||
require_once(APPROOT.'/application/itopwebpage.class.inc.php');
|
||||
require_once(APPROOT.'/application/csvpage.class.inc.php');
|
||||
|
||||
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
$operation = utils::ReadParam('operation', '');
|
||||
@@ -159,6 +160,67 @@ try
|
||||
|
||||
switch($operation)
|
||||
{
|
||||
case 'csv':
|
||||
// Big result sets cause long OQL that cannot be passed (serialized) as a GET parameter
|
||||
// Therefore we don't use the standard "search_oql" operation of UI.php to display the CSV
|
||||
$iCategory = utils::ReadParam('category', '');
|
||||
$iRuleIndex = utils::ReadParam('rule', 0);
|
||||
|
||||
$oAuditCategory = MetaModel::GetObject('AuditCategory', $iCategory);
|
||||
$oDefinitionFilter = DBObjectSearch::FromOQL($oAuditCategory->Get('definition_set'));
|
||||
FilterByContext($oDefinitionFilter, $oAppContext);
|
||||
$oDefinitionSet = new CMDBObjectSet($oDefinitionFilter);
|
||||
$oFilter = GetRuleResultFilter($iRuleIndex, $oDefinitionFilter, $oAppContext);
|
||||
$oErrorObjectSet = new CMDBObjectSet($oFilter);
|
||||
$oAuditRule = MetaModel::GetObject('AuditRule', $iRuleIndex);
|
||||
$sFileName = utils::ReadParam('filename', null, true, 'string');
|
||||
$bAdvanced = utils::ReadParam('advanced', false);
|
||||
$sAdvanced = $bAdvanced ? '&advanced=1' : '';
|
||||
|
||||
if ($sFileName != null)
|
||||
{
|
||||
$oP = new CSVPage("iTop - Export");
|
||||
$sCSVData = cmdbAbstractObject::GetSetAsCSV($oErrorObjectSet, array('localize_values' => true, 'fields_advanced' => $bAdvanced));
|
||||
$sCharset = MetaModel::GetConfig()->Get('csv_file_default_charset');
|
||||
if ($sCharset == 'UTF-8')
|
||||
{
|
||||
$sOutputData = UTF8_BOM.iconv('UTF-8', 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOutputData = iconv('UTF-8', $sCharset.'//IGNORE//TRANSLIT', $sCSVData);
|
||||
}
|
||||
if ($sFileName == '')
|
||||
{
|
||||
// Plain text => Firefox will NOT propose to download the file
|
||||
$oP->add_header("Content-type: text/plain; charset=$sCharset");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->add_header("Content-type: text/csv; charset=$sCharset");
|
||||
}
|
||||
$oP->add($sOutputData);
|
||||
$oP->TrashUnexpectedOutput();
|
||||
$oP->output();
|
||||
exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->add('<div class="page_header"><h1>Audit Errors: <span class="hilite">'.$oAuditRule->Get('description').'</span></h1><img style="margin-top: -20px; margin-right: 10px; float: right;" src="../images/stop.png"/></div>');
|
||||
$oP->p('<a href="./audit.php?'.$oAppContext->GetForLink().'">[Back to audit results]</a>');
|
||||
$sBlockId = 'audit_errors';
|
||||
$oP->p("<div id=\"$sBlockId\" style=\"clear:both\">\n");
|
||||
$oBlock = DisplayBlock::FromObjectSet($oErrorObjectSet, 'csv');
|
||||
$oBlock->Display($oP, 1);
|
||||
$oP->p("</div>\n");
|
||||
// Adjust the size of the Textarea containing the CSV to fit almost all the remaining space
|
||||
$oP->add_ready_script(" $('#1>textarea').height(400);"); // adjust the size of the block
|
||||
$sExportUrl = utils::GetAbsoluteUrlAppRoot()."pages/audit.php?operation=csv&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey();
|
||||
$oP->add_ready_script("$('a[href*=\"webservices/export.php?expression=\"]').attr('href', '".$sExportUrl."&filename=audit.csv".$sAdvanced."');");
|
||||
$oP->add_ready_script("$('#1 :checkbox').removeAttr('onclick').click( function() { var sAdvanced = ''; if (this.checked) sAdvanced = '&advanced=1'; window.location.href='$sExportUrl'+sAdvanced; } );");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'errors':
|
||||
$iCategory = utils::ReadParam('category', '');
|
||||
$iRuleIndex = utils::ReadParam('rule', 0);
|
||||
@@ -176,7 +238,9 @@ try
|
||||
$oP->p("<div id=\"$sBlockId\" style=\"clear:both\">\n");
|
||||
$oBlock = DisplayBlock::FromObjectSet($oErrorObjectSet, 'list');
|
||||
$oBlock->Display($oP, 1);
|
||||
$oP->p("</div>\n");
|
||||
$oP->p("</div>\n");
|
||||
$sExportUrl = utils::GetAbsoluteUrlAppRoot()."pages/audit.php?operation=csv&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey();
|
||||
$oP->add_ready_script("$('a[href*=\"pages/UI.php?operation=search\"]').attr('href', '".$sExportUrl."')");
|
||||
break;
|
||||
|
||||
case 'audit':
|
||||
@@ -233,7 +297,7 @@ try
|
||||
{
|
||||
$aObjectsWithErrors[$aErrorRow['id']] = true;
|
||||
}
|
||||
$aRow['nb_errors'] = ($iErrorsCount == 0) ? '0' : "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">$iErrorsCount</a>";
|
||||
$aRow['nb_errors'] = ($iErrorsCount == 0) ? '0' : "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">$iErrorsCount</a> <a href=\"?operation=csv&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">(CSV)</a>";
|
||||
$aRow['percent_ok'] = sprintf('%.2f', 100.0 * (($iCount - $iErrorsCount) / $iCount));
|
||||
$aRow['class'] = GetReportColor($iCount, $iErrorsCount);
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ try
|
||||
|
||||
$oP->add("<form method=\"get\">\n");
|
||||
$oP->add(Dict::S('UI:RunQuery:ExpressionToEvaluate')."<br/>\n");
|
||||
$oP->add("<textarea cols=\"120\" rows=\"8\" name=\"expression\">$sExpression</textarea>\n");
|
||||
$oP->add("<textarea cols=\"120\" rows=\"8\" name=\"expression\">".htmlentities($sExpression, ENT_QUOTES, 'UTF-8')."</textarea>\n");
|
||||
|
||||
if (count($aArgs) > 0)
|
||||
{
|
||||
@@ -187,7 +187,7 @@ try
|
||||
|
||||
$oP->p('');
|
||||
$oP->StartCollapsibleSection(Dict::S('UI:RunQuery:MoreInfo'), false);
|
||||
$oP->p(Dict::S('UI:RunQuery:DevelopedQuery').$oFilter->ToOQL());
|
||||
$oP->p(Dict::S('UI:RunQuery:DevelopedQuery').htmlentities($oFilter->ToOQL(), ENT_QUOTES, 'UTF-8'));
|
||||
$oP->p(Dict::S('UI:RunQuery:SerializedFilter').$oFilter->serialize());
|
||||
$oP->EndCollapsibleSection();
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ function FatalErrorCatcher($sOutput)
|
||||
if ( preg_match('|<phpfatalerror>.*</phpfatalerror>|s', $sOutput, $aMatches) )
|
||||
{
|
||||
header("HTTP/1.0 500 Internal server error.");
|
||||
$errors = '';
|
||||
foreach ($aMatches as $sMatch)
|
||||
{
|
||||
$errors .= strip_tags($sMatch)."\n";
|
||||
|
||||
@@ -375,11 +375,17 @@ class ApplicationInstaller
|
||||
|
||||
SetupPage::log_error('An exception occurred: '.$e->getMessage().' at line '.$e->getLine().' in file '.$e->getFile());
|
||||
$idx = 0;
|
||||
// Log the call stack, but log the parameters since they may contain passwords or other sensitive data
|
||||
// Log the call stack, but not the parameters since they may contain passwords or other sensitive data
|
||||
SetupPage::log("Call stack:");
|
||||
foreach($e->getTrace() as $aTrace)
|
||||
{
|
||||
SetupPage::log("#$idx {$aTrace['file']}({$aTrace['line']}): {$aTrace['function']}(...)");
|
||||
$sLine = empty($aTrace['line']) ? "" : $aTrace['line'];
|
||||
$sFile = empty($aTrace['file']) ? "" : $aTrace['file'];
|
||||
$sClass = empty($aTrace['class']) ? "" : $aTrace['class'];
|
||||
$sType = empty($aTrace['type']) ? "" : $aTrace['type'];
|
||||
$sFunction = empty($aTrace['function']) ? "" : $aTrace['function'];
|
||||
$sVerb = empty($sClass) ? $sFunction : "$sClass{$sType}$sFunction";
|
||||
SetupPage::log("#$idx $sFile($sLine): $sVerb(...)");
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
@@ -548,18 +554,18 @@ class ApplicationInstaller
|
||||
// Starting 2.0, all table names must be lowercase
|
||||
if ($sMode != 'install')
|
||||
{
|
||||
SetupPage::log_info("Renaming 'priv_internalUser' into 'priv_internaluser' (lowercase)");
|
||||
SetupPage::log_info("Renaming '{$sDBPrefix}priv_internalUser' into '{$sDBPrefix}priv_internaluser' (lowercase)");
|
||||
// This command will have no effect under Windows...
|
||||
// and it has been written in two steps so as to make it work under windows!
|
||||
CMDBSource::SelectDB($sDBName);
|
||||
try
|
||||
{
|
||||
$sRepair = "RENAME TABLE `priv_internalUser` TO `priv_internaluser_other`, `priv_internaluser_other` TO `priv_internaluser`";
|
||||
$sRepair = "RENAME TABLE `{$sDBPrefix}priv_internalUser` TO `{$sDBPrefix}priv_internaluser_other`, `{$sDBPrefix}priv_internaluser_other` TO `{$sDBPrefix}priv_internaluser`";
|
||||
CMDBSource::Query($sRepair);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
SetupPage::log_info("Renaming 'priv_internalUser' failed (already done in a previous upgrade?)");
|
||||
SetupPage::log_info("Renaming '{$sDBPrefix}priv_internalUser' failed (already done in a previous upgrade?)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -146,6 +146,8 @@ class MFCompiler
|
||||
// Menus
|
||||
//
|
||||
|
||||
global \$__comp_menus__; // ensure that the global variable is indeed global !
|
||||
|
||||
EOF;
|
||||
// Preliminary: determine parent menus not defined within the current module
|
||||
$aMenusToLoad = array();
|
||||
|
||||
@@ -213,7 +213,7 @@ class ModelFactory
|
||||
{
|
||||
echo "Dumping target doc - looking for '$sParentId'<br/>\n";
|
||||
$this->oDOMDocument->firstChild->Dump();
|
||||
throw new Exception("XML datamodel loader: could not find parent node for $oSourceNode->tagName / ".$oSourceNode->getAttribute('id')." with parent id $sParentId");
|
||||
throw new Exception("could not find parent node for $oSourceNode->tagName(id:".$oSourceNode->getAttribute('id').") with parent id $sParentId");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -223,7 +223,7 @@ class ModelFactory
|
||||
{
|
||||
echo "Dumping target doc - looking for '".$oSourceNode->getAttribute('id')."'<br/>\n";
|
||||
$this->oDOMDocument->firstChild->Dump();
|
||||
throw new Exception("XML datamodel loader: could not find node for $oSourceNode->tagName/".$oSourceNode->getAttribute('id'));
|
||||
throw new Exception("could not find node for $oSourceNode->tagName(id:".$oSourceNode->getAttribute('id').")");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -297,59 +297,71 @@ class ModelFactory
|
||||
*/
|
||||
public function LoadModule(MFModule $oModule)
|
||||
{
|
||||
$aDataModels = $oModule->GetDataModelFiles();
|
||||
$sModuleName = $oModule->GetName();
|
||||
$aClasses = array();
|
||||
self::$aLoadedModules[] = $oModule;
|
||||
|
||||
// For persistence in the cache
|
||||
$oModuleNode = $this->oDOMDocument->CreateElement('module');
|
||||
$oModuleNode->setAttribute('id', $oModule->GetId());
|
||||
$oModuleNode->AppendChild($this->oDOMDocument->CreateElement('root_dir', $oModule->GetRootDir()));
|
||||
$oModuleNode->AppendChild($this->oDOMDocument->CreateElement('label', $oModule->GetLabel()));
|
||||
$this->oModules->AppendChild($oModuleNode);
|
||||
|
||||
foreach($aDataModels as $sXmlFile)
|
||||
try
|
||||
{
|
||||
$oDocument = new MFDocument();
|
||||
libxml_clear_errors();
|
||||
$oDocument->load($sXmlFile);
|
||||
//$bValidated = $oDocument->schemaValidate(APPROOT.'setup/itop_design.xsd');
|
||||
$aErrors = libxml_get_errors();
|
||||
if (count($aErrors) > 0)
|
||||
$aDataModels = $oModule->GetDataModelFiles();
|
||||
$sModuleName = $oModule->GetName();
|
||||
$aClasses = array();
|
||||
self::$aLoadedModules[] = $oModule;
|
||||
|
||||
// For persistence in the cache
|
||||
$oModuleNode = $this->oDOMDocument->CreateElement('module');
|
||||
$oModuleNode->setAttribute('id', $oModule->GetId());
|
||||
$oModuleNode->AppendChild($this->oDOMDocument->CreateElement('root_dir', $oModule->GetRootDir()));
|
||||
$oModuleNode->AppendChild($this->oDOMDocument->CreateElement('label', $oModule->GetLabel()));
|
||||
$this->oModules->AppendChild($oModuleNode);
|
||||
|
||||
foreach($aDataModels as $sXmlFile)
|
||||
{
|
||||
self::$aLoadErrors[$sModuleName] = $aErrors;
|
||||
return;
|
||||
}
|
||||
|
||||
$oXPath = new DOMXPath($oDocument);
|
||||
$oNodeList = $oXPath->query('/itop_design/classes//class');
|
||||
foreach($oNodeList as $oNode)
|
||||
{
|
||||
if ($oNode->getAttribute('_created_in') == '')
|
||||
$oDocument = new MFDocument();
|
||||
libxml_clear_errors();
|
||||
$oDocument->load($sXmlFile);
|
||||
//$bValidated = $oDocument->schemaValidate(APPROOT.'setup/itop_design.xsd');
|
||||
$aErrors = libxml_get_errors();
|
||||
if (count($aErrors) > 0)
|
||||
{
|
||||
$oNode->SetAttribute('_created_in', $sModuleName);
|
||||
self::$aLoadErrors[$sModuleName] = $aErrors;
|
||||
return;
|
||||
}
|
||||
}
|
||||
$oNodeList = $oXPath->query('/itop_design/menus/menu');
|
||||
foreach($oNodeList as $oNode)
|
||||
{
|
||||
if ($oNode->getAttribute('_created_in') == '')
|
||||
|
||||
$oXPath = new DOMXPath($oDocument);
|
||||
$oNodeList = $oXPath->query('/itop_design/classes//class');
|
||||
foreach($oNodeList as $oNode)
|
||||
{
|
||||
$oNode->SetAttribute('_created_in', $sModuleName);
|
||||
if ($oNode->getAttribute('_created_in') == '')
|
||||
{
|
||||
$oNode->SetAttribute('_created_in', $sModuleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
$oUserRightsNode = $oXPath->query('/itop_design/user_rights')->item(0);
|
||||
if ($oUserRightsNode)
|
||||
{
|
||||
if ($oUserRightsNode->getAttribute('_created_in') == '')
|
||||
$oNodeList = $oXPath->query('/itop_design/menus/menu');
|
||||
foreach($oNodeList as $oNode)
|
||||
{
|
||||
$oUserRightsNode->SetAttribute('_created_in', $sModuleName);
|
||||
if ($oNode->getAttribute('_created_in') == '')
|
||||
{
|
||||
$oNode->SetAttribute('_created_in', $sModuleName);
|
||||
}
|
||||
}
|
||||
$oUserRightsNode = $oXPath->query('/itop_design/user_rights')->item(0);
|
||||
if ($oUserRightsNode)
|
||||
{
|
||||
if ($oUserRightsNode->getAttribute('_created_in') == '')
|
||||
{
|
||||
$oUserRightsNode->SetAttribute('_created_in', $sModuleName);
|
||||
}
|
||||
}
|
||||
|
||||
$oDeltaRoot = $oDocument->childNodes->item(0);
|
||||
$this->LoadDelta($oDocument, $oDeltaRoot, $this->oDOMDocument);
|
||||
}
|
||||
|
||||
$oDeltaRoot = $oDocument->childNodes->item(0);
|
||||
$this->LoadDelta($oDocument, $oDeltaRoot, $this->oDOMDocument);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$aLoadedModuleNames = array();
|
||||
foreach (self::$aLoadedModules as $oModule)
|
||||
{
|
||||
$aLoadedModuleNames[] = $oModule->GetName();
|
||||
}
|
||||
throw new Exception('Error loading module "'.$oModule->GetName().'": '.$e->getMessage().' - Loaded modules: '.implode(',', $aLoadedModuleNames));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1282,6 +1294,14 @@ EOF;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow the setup page to load and perform its checks (including the check about the required extensions)
|
||||
*/
|
||||
if (!class_exists('DOMElement'))
|
||||
{
|
||||
class DOMElement {function __construct(){throw new Exception('The dom extension is not enabled');}}
|
||||
}
|
||||
|
||||
/**
|
||||
* MFElement: helper to read/change the DOM
|
||||
* @package ModelFactory
|
||||
@@ -1684,7 +1704,7 @@ class MFElement extends DOMElement
|
||||
{
|
||||
if ($bMustExist)
|
||||
{
|
||||
throw new Exception("XML datamodel loader: found mandatory node $this->tagName/$sSearchId marked as deleted in $oContainer->tagName");
|
||||
throw new Exception("found mandatory node $this->tagName(id:$sSearchId) marked as deleted in ".$oContainer->getNodePath());
|
||||
}
|
||||
// Beware: ImportNode(xxx, false) DOES NOT copy the node's attribute on *some* PHP versions (<5.2.17)
|
||||
// So use this workaround to import a node and its attributes on *any* PHP version
|
||||
@@ -1698,7 +1718,7 @@ class MFElement extends DOMElement
|
||||
{
|
||||
echo "Dumping parent node<br/>\n";
|
||||
$oContainer->Dump();
|
||||
throw new Exception("XML datamodel loader: could not find $this->tagName/$sSearchId in $oContainer->tagName");
|
||||
throw new Exception("could not find $this->tagName(id:$sSearchId) in ".$oContainer->getNodePath());
|
||||
}
|
||||
// Beware: ImportNode(xxx, false) DOES NOT copy the node's attribute on *some* PHP versions (<5.2.17)
|
||||
// So use this workaround to import a node and its attributes on *any* PHP version
|
||||
@@ -1767,6 +1787,14 @@ class MFElement extends DOMElement
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow the setup page to load and perform its checks (including the check about the required extensions)
|
||||
*/
|
||||
if (!class_exists('DOMDocument'))
|
||||
{
|
||||
class DOMDocument {function __construct(){throw new Exception('The dom extension is not enabled');}}
|
||||
}
|
||||
|
||||
/**
|
||||
* MFDocument - formating rules for XML input/output
|
||||
* @package ModelFactory
|
||||
|
||||
@@ -114,6 +114,7 @@ class ModuleDiscovery
|
||||
{
|
||||
$aDependencies[$sId] = $aModule['dependencies'];
|
||||
}
|
||||
ksort($aDependencies);
|
||||
$aOrderedModules = array();
|
||||
$iLoopCount = 1;
|
||||
while(($iLoopCount < count(self::$m_aModules)) && (count($aDependencies) > 0) )
|
||||
|
||||
@@ -535,6 +535,10 @@ class SetupUtils
|
||||
{
|
||||
if (function_exists('symlink'))
|
||||
{
|
||||
if (file_exists($sDest.'/'.$sFile))
|
||||
{
|
||||
unlink($sDest.'/'.$sFile);
|
||||
}
|
||||
symlink($sSource.'/'.$sFile, $sDest.'/'.$sFile);
|
||||
}
|
||||
else
|
||||
@@ -544,6 +548,10 @@ class SetupUtils
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_link($sDest.'/'.$sFile))
|
||||
{
|
||||
unlink($sDest.'/'.$sFile);
|
||||
}
|
||||
copy($sSource.'/'.$sFile, $sDest.'/'.$sFile);
|
||||
}
|
||||
}
|
||||
@@ -738,7 +746,7 @@ function ValidateField(sFieldId, bUsed)
|
||||
{
|
||||
if (sValue != "")
|
||||
{
|
||||
if (sValue.match(/^[A-Za-z][A-Za-z0-9_]*$/))
|
||||
if (sValue.match(/^[A-Za-z0-9_]*$/))
|
||||
{
|
||||
var bCollision = false;
|
||||
if (sFieldId == 'db_new_name')
|
||||
|
||||
@@ -480,7 +480,7 @@ EOF
|
||||
{
|
||||
// Special case for upgrading some development versions (temporary)
|
||||
$sCompatibleDMDir = SetupUtils::GetLatestDataModelDir();
|
||||
$sInstalledDataModelVersion = SetupUtils::GetDataModelVersion($sLatestDMDir);
|
||||
$sInstalledDataModelVersion = SetupUtils::GetDataModelVersion($sCompatibleDMDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1209,6 +1209,7 @@ EOF
|
||||
foreach($aOptions as $index => $aChoice)
|
||||
{
|
||||
$sChoiceId = $sParentId.self::$SEP.$index;
|
||||
$aScores[$sChoiceId] = 0;
|
||||
if (!$this->bUpgrade && isset($aChoice['default']) && $aChoice['default'])
|
||||
{
|
||||
$aDefaults[$sChoiceId] = $sChoiceId;
|
||||
@@ -1272,7 +1273,6 @@ EOF
|
||||
{
|
||||
$sChoiceName = $sChoiceId;
|
||||
}
|
||||
$aScores[$sChoiceId] = 0;
|
||||
if (array_key_exists('modules', $aChoice))
|
||||
{
|
||||
foreach($aChoice['modules'] as $sModuleId)
|
||||
@@ -1286,7 +1286,8 @@ EOF
|
||||
$iScore = 99; // The whole parent choice is selected
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$iMaxScore = max($iMaxScore, isset($aScores[$sChoiceId]) ? $aScores[$sChoiceId] : 0);
|
||||
}
|
||||
}
|
||||
if ($iMaxScore > 0)
|
||||
|
||||
193
webservices/itoprest.examples.php
Normal file
193
webservices/itoprest.examples.php
Normal file
@@ -0,0 +1,193 @@
|
||||
<?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/>
|
||||
|
||||
|
||||
/**
|
||||
* Shows a usage of the SOAP queries
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper to execute an HTTP POST request
|
||||
* Source: http://netevil.org/blog/2006/nov/http-post-from-php-without-curl
|
||||
* originaly named after do_post_request
|
||||
*/
|
||||
function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null)
|
||||
{
|
||||
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
|
||||
|
||||
$sData = http_build_query($aData);
|
||||
|
||||
$aParams = array('http' => array(
|
||||
'method' => 'POST',
|
||||
'content' => $sData,
|
||||
'header'=> "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
|
||||
));
|
||||
if ($sOptionnalHeaders !== null)
|
||||
{
|
||||
$aParams['http']['header'] .= $sOptionnalHeaders;
|
||||
}
|
||||
$ctx = stream_context_create($aParams);
|
||||
|
||||
$fp = @fopen($sUrl, 'rb', false, $ctx);
|
||||
if (!$fp)
|
||||
{
|
||||
global $php_errormsg;
|
||||
if (isset($php_errormsg))
|
||||
{
|
||||
throw new Exception("Problem with $sUrl, $php_errormsg");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Problem with $sUrl");
|
||||
}
|
||||
}
|
||||
$response = @stream_get_contents($fp);
|
||||
if ($response === false)
|
||||
{
|
||||
throw new Exception("Problem reading data from $sUrl, $php_errormsg");
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
// If the library curl is installed.... use this function
|
||||
//
|
||||
function DoPostRequest_curl($sUrl, $aData)
|
||||
{
|
||||
$curl = curl_init($sUrl);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_POST, true);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $aData);
|
||||
$response = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Main program
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Define the operations to perform (one operation per call the rest service)
|
||||
//
|
||||
|
||||
$aOperations = array(
|
||||
array(
|
||||
'operation' => 'list_operations', // operation code
|
||||
),
|
||||
array(
|
||||
'operation' => 'core/create', // operation code
|
||||
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
|
||||
'class' => 'UserRequest',
|
||||
'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
|
||||
// Values for the object to create
|
||||
'fields' => array(
|
||||
'org_id' => "SELECT Organization WHERE name = 'Demo'",
|
||||
'caller_id' => array('name' => 'monet', 'first_name' => 'claude'),
|
||||
'title' => 'issue blah',
|
||||
'description' => 'something happened'
|
||||
),
|
||||
),
|
||||
array(
|
||||
'operation' => 'core/update', // operation code
|
||||
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
|
||||
'class' => 'UserRequest',
|
||||
'key' => 'SELECT UserRequest WHERE id=1',
|
||||
'output_fields' => 'id, friendlyname, title', // list of fields to show in the results (* or a,b,c)
|
||||
// Values for the object to create
|
||||
'fields' => array(
|
||||
'title' => 'Issue #'.rand(0, 100),
|
||||
'contacts_list' => array(
|
||||
array(
|
||||
'role' => 'fireman #'.rand(0, 100),
|
||||
'contact_id' => array('finalclass' => 'Person', 'name' => 'monet', 'first_name' => 'claude'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'operation' => 'core/get', // operation code
|
||||
'class' => 'UserRequest',
|
||||
'key' => 'SELECT UserRequest',
|
||||
'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
|
||||
),
|
||||
array(
|
||||
'operation' => 'core/delete', // operation code
|
||||
'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
|
||||
'class' => 'UserRequest',
|
||||
'key' => 'SELECT UserRequest WHERE org_id = 2',
|
||||
'simulate' => true,
|
||||
),
|
||||
array(
|
||||
'operation' => 'core/apply_stimulus', // operation code
|
||||
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
|
||||
'class' => 'UserRequest',
|
||||
'key' => 1,
|
||||
'stimulus' => 'ev_assign',
|
||||
// Values to set
|
||||
'fields' => array(
|
||||
'team_id' => 15, // Helpdesk
|
||||
'agent_id' => 9 // Jules Verne
|
||||
),
|
||||
'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
|
||||
),
|
||||
array(
|
||||
'operation' => 'core/get_related', // operation code
|
||||
'class' => 'Server',
|
||||
'key' => 'SELECT Server',
|
||||
'relation' => 'impacts', // relation code
|
||||
'depth' => 4, // max recursion depth
|
||||
),
|
||||
);
|
||||
|
||||
$sUrl = "http://localhost/rest-services/webservices/rest.php?version=1.0";
|
||||
|
||||
$aData = array();
|
||||
$aData['auth_user'] = 'admin';
|
||||
$aData['auth_pwd'] = 'admin';
|
||||
|
||||
foreach ($aOperations as $iOp => $aOperation)
|
||||
{
|
||||
echo "======================================\n";
|
||||
echo "Operation #$iOp: ".$aOperation['operation']."\n";
|
||||
$aData['json_data'] = json_encode($aOperation);
|
||||
|
||||
echo "--------------------------------------\n";
|
||||
echo "Input:\n";
|
||||
print_r($aOperation);
|
||||
|
||||
$response = DoPostRequest($sUrl, $aData);
|
||||
$aResults = json_decode($response);
|
||||
if ($aResults)
|
||||
{
|
||||
echo "--------------------------------------\n";
|
||||
echo "Reply:\n";
|
||||
print_r($aResults);
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "ERROR rest.php replied:\n";
|
||||
echo $response;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
209
webservices/rest.php
Normal file
209
webservices/rest.php
Normal file
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* Entry point for all the REST services
|
||||
*
|
||||
* --------------------------------------------------
|
||||
* Create an object
|
||||
* --------------------------------------------------
|
||||
* POST itop/webservices/rest.php
|
||||
* {
|
||||
* operation: 'object_create',
|
||||
* comment: 'Synchronization from blah...',
|
||||
* class: 'UserRequest',
|
||||
* results: 'id, friendlyname',
|
||||
* fields:
|
||||
* {
|
||||
* org_id: 'SELECT Organization WHERE name = "Demo"',
|
||||
* caller_id:
|
||||
* {
|
||||
* name: 'monet',
|
||||
* first_name: 'claude',
|
||||
* }
|
||||
* title: 'Houston, got a problem!',
|
||||
* description: 'The fridge is empty'
|
||||
* contacts_list:
|
||||
* [
|
||||
* {
|
||||
* role: 'pizza delivery',
|
||||
* contact_id:
|
||||
* {
|
||||
* finalclass: 'Person',
|
||||
* name: 'monet',
|
||||
* first_name: 'claude'
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
|
||||
require_once(__DIR__.'/../approot.inc.php');
|
||||
require_once(APPROOT.'/application/application.inc.php');
|
||||
require_once(APPROOT.'/application/ajaxwebpage.class.inc.php');
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
|
||||
require_once(APPROOT.'core/restservices.class.inc.php');
|
||||
|
||||
|
||||
/**
|
||||
* Result structure that is specific to the hardcoded verb 'list_operations'
|
||||
*/
|
||||
class RestResultListOperations extends RestResult
|
||||
{
|
||||
public $version;
|
||||
public $operations;
|
||||
|
||||
public function AddOperation($sVerb, $sDescription, $sServiceProviderClass)
|
||||
{
|
||||
$this->operations[] = array(
|
||||
'verb' => $sVerb,
|
||||
'description' => $sDescription,
|
||||
'extension' => $sServiceProviderClass
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Main
|
||||
//
|
||||
$oP = new ajax_page('rest');
|
||||
|
||||
try
|
||||
{
|
||||
utils::UseParamFile();
|
||||
|
||||
$sAuthUser = utils::ReadParam('auth_user', null, false, 'raw_data');
|
||||
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
|
||||
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
|
||||
{
|
||||
UserRights::Login($sAuthUser); // Login & set the user's language
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Invalid login '$sAuthUser'", RestResult::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$sVersion = utils::ReadParam('version', null, false, 'raw_data');
|
||||
if ($sVersion == null)
|
||||
{
|
||||
throw new Exception("Missing parameter 'version' (e.g. '1.0')", RestResult::MISSING_VERSION);
|
||||
}
|
||||
|
||||
$sJsonString = utils::ReadParam('json_data', null, false, 'raw_data');
|
||||
if ($sJsonString == null)
|
||||
{
|
||||
throw new Exception("Missing parameter 'json_data", RestResult::MISSING_JSON);
|
||||
}
|
||||
$aJsonData = @json_decode($sJsonString);
|
||||
if ($aJsonData == null)
|
||||
{
|
||||
throw new Exception("Parameter json_data is not a valid JSON structure", RestResult::INVALID_JSON);
|
||||
}
|
||||
|
||||
|
||||
$aProviders = array();
|
||||
foreach(get_declared_classes() as $sPHPClass)
|
||||
{
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
if ($oRefClass->implementsInterface('iRestServiceProvider'))
|
||||
{
|
||||
$aProviders[] = new $sPHPClass;
|
||||
}
|
||||
}
|
||||
|
||||
$aOpToRestService = array(); // verb => $oRestServiceProvider
|
||||
foreach ($aProviders as $oRestSP)
|
||||
{
|
||||
$aOperations = $oRestSP->ListOperations($sVersion);
|
||||
foreach ($aOperations as $aOpData)
|
||||
{
|
||||
$aOpToRestService[$aOpData['verb']] = array
|
||||
(
|
||||
'service_provider' => $oRestSP,
|
||||
'description' => $aOpData['description'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($aOpToRestService) == 0)
|
||||
{
|
||||
throw new Exception("There is no service available for version '$sVersion'", RestResult::UNSUPPORTED_VERSION);
|
||||
}
|
||||
|
||||
|
||||
$sOperation = RestUtils::GetMandatoryParam($aJsonData, 'operation');
|
||||
if ($sOperation == 'list_operations')
|
||||
{
|
||||
$oResult = new RestResultListOperations();
|
||||
$oResult->message = "Operations: ".count($aOpToRestService);
|
||||
$oResult->version = $sVersion;
|
||||
foreach ($aOpToRestService as $sVerb => $aOpData)
|
||||
{
|
||||
$oResult->AddOperation($sVerb, $aOpData['description'], get_class($aOpData['service_provider']));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!array_key_exists($sOperation, $aOpToRestService))
|
||||
{
|
||||
throw new Exception("Unknown verb '$sOperation' in version '$sVersion'", RestResult::UNKNOWN_OPERATION);
|
||||
}
|
||||
$oRS = $aOpToRestService[$sOperation]['service_provider'];
|
||||
|
||||
$oResult = $oRS->ExecOperation($sVersion, $sOperation, $aJsonData);
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$oResult = new RestResult();
|
||||
if ($e->GetCode() == 0)
|
||||
{
|
||||
$oResult->code = RestResult::INTERNAL_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oResult->code = $e->GetCode();
|
||||
}
|
||||
$oResult->message = "Error: ".$e->GetMessage();
|
||||
}
|
||||
|
||||
// Output the results
|
||||
//
|
||||
$oP->add_header('Access-Control-Allow-Origin: *');
|
||||
|
||||
$sCallback = utils::ReadParam('callback', null);
|
||||
if ($sCallback == null)
|
||||
{
|
||||
$oP->SetContentType('application/json');
|
||||
$oP->add(json_encode($oResult));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->SetContentType('application/javascript');
|
||||
$oP->add($sCallback.'('.json_encode($oResult).')');
|
||||
}
|
||||
$oP->Output();
|
||||
?>
|
||||
Reference in New Issue
Block a user