value)
*
* @param array $aRow
* @param string $sClassAlias
* @param array $aAttToLoad
* @param array $aExtendedDataSpec
*
* @throws \CoreException
*/
public function __construct($aRow = null, $sClassAlias = '', $aAttToLoad = null, $aExtendedDataSpec = null)
{
parent::__construct($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec);
$this->bAllowWrite = false;
$this->bAllowDelete = false;
}
/**
* Return the allowed object modes
*
* @see static::ENUM_OBJECT_MODE_XXX
*
* @return string[]
* @since 3.0.0
*/
public static function EnumObjectModes(): array
{
return [
static::ENUM_OBJECT_MODE_VIEW,
static::ENUM_OBJECT_MODE_EDIT,
static::ENUM_OBJECT_MODE_CREATE,
static::ENUM_OBJECT_MODE_STIMULUS,
];
}
/**
* returns what will be the next ID for the forms
*/
public static function GetNextFormId()
{
return 1 + self::$iGlobalFormId;
}
public static function GetUIPage()
{
return 'UI.php';
}
/**
* @param \WebPage $oPage
* @param \DBObject $oObj
* @param array $aParams
*
* @throws \Exception
*/
public static function ReloadAndDisplay($oPage, $oObj, $aParams)
{
$oAppContext = new ApplicationContext();
// Reload the page to let the "calling" page execute its 'onunload' method.
// Note 1: The redirection MUST NOT be made via an HTTP "header" since onunload is only called when the actual content of the DOM
// is replaced by some other content. So the "bouncing" page must provide some content (in our case a script making the redirection).
// Note 2: make sure that the URL below is different from the one of the "Modify" button, otherwise the button will have no effect. This is why we add "&a=1" at the end !!!
// Note 3: we use the toggle of a flag in the sessionStorage object to prevent an infinite loop of reloads in case the object is actually locked by another window
$sSessionStorageKey = get_class($oObj).'_'.$oObj->GetKey();
$sParams = '';
foreach($aParams as $sName => $value)
{
$sParams .= $sName.'='.urlencode($value).'&'; // Always add a trailing &
}
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/'.$oObj->GetUIPage().'?'.$sParams.'class='.get_class($oObj).'&id='.$oObj->getKey().'&'.$oAppContext->GetForLink().'&a=1';
$oPage->add_script(
<< Use the search form above to search for objects to be added. current value for $sAttCode : $currValue ".Dict::Format('UI:BulkModify_Count_DistinctValues', $iCount)."
",
$sDescription);
$sTip .= "
';
$sComments = $sSynchroIcon;
}
// Attribute is read-only
$sHTMLValue = "".$this->GetAsHTML($sAttCode).'';
} else {
$sValue = $this->Get($sAttCode);
$sDisplayValue = $this->GetEditValue($sAttCode);
$aArgs = array('this' => $this, 'formPrefix' => $sPrefix);
$sHTMLValue = "".self::GetFormElementForField(
$oPage, $sClass, $sAttCode, $oAttDef, $sValue,
$sDisplayValue, $sInputId, '', $iFlags, $aArgs,
true, $sInputType
).'';
}
$aFieldsMap[$sAttCode] = $sInputId;
// Attribute description
$sDescription = $oAttDef->GetDescription();
$sDescriptionForHTMLTag = utils::HtmlEntities($sDescription);
$sDescriptionHTMLTag = (empty($sDescriptionForHTMLTag) || $sDescription === $oAttDef->GetLabel()) ? '' : 'class="ibo-has-description" data-tooltip-content="'.$sDescriptionForHTMLTag.'"';
$val = array(
'label' => ''.$oAttDef->GetLabel().'',
'value' => $sHTMLValue,
'input_id' => $sInputId,
'input_type' => $sInputType,
'comments' => $sComments,
'infos' => $sInfos,
);
}
}
else
{
// Attribute description
$sDescription = $oAttDef->GetDescription();
$sDescriptionForHTMLTag = utils::HtmlEntities($sDescription);
$sDescriptionHTMLTag = (empty($sDescriptionForHTMLTag) || $sDescription === $oAttDef->GetLabel()) ? '' : 'class="ibo-has-description" data-tooltip-content="'.$sDescriptionForHTMLTag.'"';
$val = array(
'label' => ''.$oAttDef->GetLabel().'',
'value' => "".$this->GetAsHTML($sAttCode)."",
'comments' => $sComments,
'infos' => $sInfos,
);
$aFieldsMap[$sAttCode] = $sInputId;
}
}
else
{
$val = null; // Skip this field
}
}
else
{
// !bEditMode
$val = $this->GetFieldAsHtml($sClass, $sAttCode, $sStateAttCode);
}
if ($val != null)
{
// Add extra data for markup generation
// - Attribute code and AttributeDef. class
$val['attcode'] = $sAttCode;
$val['atttype'] = $sAttDefClass;
$val['attlabel'] = $sAttLabel;
$val['attflags'] = ($bEditMode) ? $this->GetFormAttributeFlags($sAttCode) : OPT_ATT_READONLY;
// - How the field should be rendered
$val['layout'] =
(in_array($oAttDef->GetEditClass(), static::GetAttEditClassesToRenderAsLargeField()))
? Field::ENUM_FIELD_LAYOUT_LARGE
: Field::ENUM_FIELD_LAYOUT_SMALL;
// - For simple fields, we get the raw (stored) value as well
$bExcludeRawValue = false;
foreach (static::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as $sAttDefClassToExclude) {
if (is_a($sAttDefClass, $sAttDefClassToExclude, true)) {
$bExcludeRawValue = true;
break;
}
}
$val['value_raw'] = ($bExcludeRawValue === false) ? $this->Get($sAttCode) : '';
// The field is visible, add it to the current column
$oField = FieldUIBlockFactory::MakeFromParams($val);
if ($sFieldsetName[0] != '_') {
$oFieldSet->AddSubBlock($oField);
} else {
$oColumn->AddSubBlock($oField);
}
}
}
}
}
}
return $aFieldsMap;
}
/**
* @param \WebPage $oPage
* @param bool $bEditMode Note that this parameter is no longer used in ths method, $sMode is used instead, but we cannot remove it as it part of the base interface (iDisplay)...
* @param string $sMode Mode in which the object will be displayed (see static::ENUM_OBJECT_MODE_XXX)
*
* @throws \ApplicationException
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DictExceptionMissingString
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
public function DisplayDetails(WebPage $oPage, $bEditMode = false, $sMode = self::ENUM_OBJECT_MODE_VIEW)
{
$sClass = get_class($this);
$iKey = $this->GetKey();
// Object's details
$oObjectDetails = ObjectFactory::MakeDetails($this);
// Note: DisplayBareHeader is called before adding $oObjectDetails to the page, so it can inject HTML before it through $oPage.
/** @var \iTopWebPage $oPage */
$aHeadersBlocks = $this->DisplayBareHeader($oPage, $bEditMode, $sMode);
if (false === empty($aHeadersBlocks['subtitle'])) {
$oObjectDetails->AddSubTitleBlocks($aHeadersBlocks['subtitle']);
}
if (false === empty($aHeadersBlocks['toolbar'])) {
$oObjectDetails->AddToolbarBlocks($aHeadersBlocks['toolbar']);
}
$oPage->AddUiBlock($oObjectDetails);
$oPage->AddTabContainer(OBJECT_PROPERTIES_TAB, '', $oObjectDetails);
$oPage->SetCurrentTabContainer(OBJECT_PROPERTIES_TAB);
$oPage->SetCurrentTab('UI:PropertiesTab');
$this->DisplayBareProperties($oPage, $bEditMode);
$this->DisplayBareRelations($oPage, $bEditMode);
// TODO 3.0.0: What to do with this?
//$this->DisplayBareHistory($oPage, $bEditMode);
// Note: Adding the JS snippet which enables the image upload should have been done directly by the ActivityPanel which would have kept the independance principle
// of the UIBlock. For now we keep it this way in order to move on and trace this known limitation in N°3736.
/** @var ActivityPanel $oActivityPanel */
$oActivityPanel = $oPage->GetContentLayout()->GetSubBlock(ActivityPanel::BLOCK_CODE);
// Note: Testing if block exists is necessary as during the 'release_lock_and_details' operation we don't have an activity panel
if (!is_null($oActivityPanel) && $oActivityPanel->HasTransactionId()) {
$iTransactionId = $oActivityPanel->GetTransactionId();
$sTempId = utils::GetUploadTempId($iTransactionId);
$oPage->add_ready_script(InlineImage::EnableCKEditorImageUpload($this, $sTempId));
}
}
/**
* @param \WebPage $oPage
*
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \Exception
*/
public function DisplayPreview(WebPage $oPage)
{
$aDetails = array();
$sClass = get_class($this);
$aList = MetaModel::GetZListItems($sClass, 'preview');
foreach($aList as $sAttCode)
{
$aDetails[] = array(
'label' => MetaModel::GetLabel($sClass, $sAttCode),
'value' => $this->GetAsHTML($sAttCode),
);
}
$oPage->details($aDetails);
}
/**
* @param \WebPage $oPage
* @param \CMDBObjectSet $oSet
* @param array $aExtraParams
*
* @throws \ApplicationException
* @throws \CoreException
*/
public static function DisplaySet(WebPage $oPage, CMDBObjectSet $oSet, $aExtraParams = array())
{
$oPage->AddUiBlock(self::GetDisplaySetBlock($oPage, $oSet, $aExtraParams));
}
/**
* Simplified version of GetDisplaySet() with less "decoration" around the table (and no paging)
* that fits better into a printed document (like a PDF or a printable view)
*
* @param WebPage $oPage
* @param DBObjectSet $oSet
* @param array $aExtraParams
*
* @return string The HTML representation of the table
* @throws \CoreException
*/
public static function GetDisplaySetForPrinting(WebPage $oPage, DBObjectSet $oSet, $aExtraParams = array())
{
$sTableId = isset($aExtraParams['table_id']) ? $aExtraParams['table_id'] : null;
$bViewLink = true;
$sSelectMode = 'none';
$iListId = $sTableId;
$sClassAlias = $oSet->GetClassAlias();
$sClassName = $oSet->GetClass();
$sZListName = 'list';
$aClassAliases = array($sClassAlias => $sClassName);
$aList = cmdbAbstractObject::FlattenZList(MetaModel::GetZListItems($sClassName, $sZListName));
$oDataTable = new PrintableDataTable($iListId, $oSet, $aClassAliases, $sTableId);
$oSettings = DataTableSettings::GetDataModelSettings($aClassAliases, $bViewLink, array($sClassAlias => $aList));
$oSettings->iDefaultPageSize = 0;
$oSettings->aSortOrder = MetaModel::GetOrderByDefault($sClassName);
return $oDataTable->Display($oPage, $oSettings, false /* $bDisplayMenu */, $sSelectMode, $bViewLink,
$aExtraParams);
}
/**
* Get the HTML fragment corresponding to the display of a table representing a set of objects
*
* @param WebPage $oPage The page object is used for out-of-band information (mostly scripts) output
* @param \DBObjectSet $oSet The set of objects to display
* @param array $aExtraParams key used :
*
*
*
* @return String The HTML fragment representing the table of objects. Warning : no JS added to handled
* pagination or table sorting !
*
* @see DisplayBlock to get a similar table but with the JS for pagination & sorting
*
* @deprecated 3.0.0 use GetDisplaySetBlock
*/
public static function GetDisplaySet(WebPage $oPage, DBObjectSet $oSet, $aExtraParams = array())
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use GetDisplaySetBlock');
$oPage->AddUiBlock(static::GetDisplaySetBlock($oPage, $oSet, $aExtraParams));
return "";
}
public static function GetDisplaySetBlock(WebPage $oPage, DBObjectSet $oSet, $aExtraParams = array())
{
if ($oPage->IsPrintableVersion() || $oPage->is_pdf()) {
return self::GetDisplaySetForPrinting($oPage, $oSet, $aExtraParams);
}
if (empty($aExtraParams['currentId'])) {
$iListId = utils::GetUniqueId(); // Works only if not in an Ajax page !!
} else {
$iListId = $aExtraParams['currentId'];
}
return DataTableUIBlockFactory::MakeForResult($oPage, $iListId, $oSet, $aExtraParams);
}
public static function GetDataTableFromDBObjectSet(DBObjectSet $oSet, $aParams = array())
{
$aFields = null;
if (isset($aParams['fields']) && (strlen($aParams['fields']) > 0)) {
$aFields = explode(',', $aParams['fields']);
}
$bFieldsAdvanced = false;
if (isset($aParams['fields_advanced'])) {
$bFieldsAdvanced = (bool)$aParams['fields_advanced'];
}
$bLocalize = true;
if (isset($aParams['localize_values'])) {
$bLocalize = (bool)$aParams['localize_values'];
}
$aList = array();
$aClasses = $oSet->GetFilter()->GetSelectedClasses();
$aAuthorizedClasses = array();
foreach ($aClasses as $sAlias => $sClassName) {
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO) {
$aAuthorizedClasses[$sAlias] = $sClassName;
}
}
$aHeader = array();
foreach ($aAuthorizedClasses as $sAlias => $sClassName) {
$aList[$sAlias] = array();
foreach (MetaModel::GetZListItems($sClassName, 'list') as $sAttCode) {
$oAttDef = Metamodel::GetAttributeDef($sClassName, $sAttCode);
if (is_null($aFields) || (count($aFields) == 0)) {
// Standard list of attributes (no link sets)
if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField())) {
$sAttCodeEx = $oAttDef->IsExternalField() ? $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode() : $sAttCode;
$aList[$sAlias][$sAttCodeEx] = $oAttDef;
if ($bFieldsAdvanced && $oAttDef->IsExternalKey(EXTKEY_RELATIVE)) {
$sRemoteClass = $oAttDef->GetTargetClass();
foreach (MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode) {
$aList[$sAlias][$sAttCode.'->'.$sRemoteAttCode] = MetaModel::GetAttributeDef($sRemoteClass,
$sRemoteAttCode);
}
}
}
} else {
// User defined list of attributes
if (in_array($sAttCode, $aFields) || in_array($sAlias.'.'.$sAttCode, $aFields)) {
$aList[$sAlias][$sAttCode] = $oAttDef;
}
}
}
// Replace external key by the corresponding friendly name (if not already in the list)
foreach ($aList[$sAlias] as $sAttCode => $oAttDef) {
if ($oAttDef->IsExternalKey()) {
unset($aList[$sAlias][$sAttCode]);
$sFriendlyNameAttCode = $sAttCode.'_friendlyname';
if (!array_key_exists($sFriendlyNameAttCode,
$aList[$sAlias]) && MetaModel::IsValidAttCode($sClassName, $sFriendlyNameAttCode)) {
$oFriendlyNameAtt = MetaModel::GetAttributeDef($sClassName, $sFriendlyNameAttCode);
$aList[$sAlias][$sFriendlyNameAttCode] = $oFriendlyNameAtt;
}
}
}
foreach ($aList[$sAlias] as $sAttCodeEx => $oAttDef) {
$sColLabel = $bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx) : $sAttCodeEx;
$oFinalAttDef = $oAttDef->GetFinalAttDef();
if (get_class($oFinalAttDef) == 'AttributeDateTime') {
$aHeader[$oAttDef->GetCode().'/D'] = ['label' => $sColLabel.' ('.Dict::S('UI:SplitDateTime-Date').')'];
$aHeader[$oAttDef->GetCode().'/T'] = ['label' => $sColLabel.' ('.Dict::S('UI:SplitDateTime-Time').')'];
} else {
$aHeader[$oAttDef->GetCode()] = ['label' => $sColLabel];
}
}
}
$oSet->Seek(0);
$aRows = [];
while ($aObjects = $oSet->FetchAssoc()) {
$aRow = [];
foreach ($aAuthorizedClasses as $sAlias => $sClassName) {
$oObj = $aObjects[$sAlias];
foreach ($aList[$sAlias] as $sAttCodeEx => $oAttDef) {
if (is_null($oObj)) {
$aRow[$oAttDef->GetCode()] = '';
} else {
$oFinalAttDef = $oAttDef->GetFinalAttDef();
if (get_class($oFinalAttDef) == 'AttributeDateTime') {
$sDate = $oObj->Get($sAttCodeEx);
if ($sDate === null) {
$aRow[$oAttDef->GetCode().'/D'] = '';
$aRow[$oAttDef->GetCode().'/T'] = '';
} else {
$iDate = AttributeDateTime::GetAsUnixSeconds($sDate);
$aRow[$oAttDef->GetCode().'/D'] = date('Y-m-d', $iDate); // Format kept as-is for 100% backward compatibility of the exports
$aRow[$oAttDef->GetCode().'/T'] = date('H:i:s', $iDate); // Format kept as-is for 100% backward compatibility of the exports
}
} else {
if ($oAttDef instanceof AttributeCaseLog) {
$rawValue = $oObj->Get($sAttCodeEx);
$outputValue = str_replace("\n", "
", htmlentities($rawValue->__toString(), ENT_QUOTES, 'UTF-8'));
// Trick for Excel: treat the content as text even if it begins with an equal sign
$aRow[$oAttDef->GetCode()] = $outputValue;
} else {
$rawValue = $oObj->Get($sAttCodeEx);
// Due to custom formatting rules, empty friendlynames may be rendered as non-empty strings
// let's fix this and make sure we render an empty string if the key == 0
if ($oAttDef instanceof AttributeExternalField && $oAttDef->IsFriendlyName()) {
$sKeyAttCode = $oAttDef->GetKeyAttCode();
if ($oObj->Get($sKeyAttCode) == 0) {
$rawValue = '';
}
}
if ($bLocalize) {
$outputValue = htmlentities($oFinalAttDef->GetEditValue($rawValue), ENT_QUOTES, 'UTF-8');
} else {
$outputValue = htmlentities($rawValue, ENT_QUOTES, 'UTF-8');
}
$aRow[$oAttDef->GetCode()] = $outputValue;
}
}
}
}
}
$aRows[] = $aRow;
}
$oTable = new StaticTable();
$oTable->SetColumns($aHeader);
$oTable->SetData($aRows);
return $oTable;
//DataTableUIBlockFactory::MakeForStaticData('', $aHeader, $aRows);
}
/**
* @param \WebPage $oPage
* @param \CMDBObjectSet $oSet
* @param array $aExtraParams key used :
*
*
*
* @return string
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @deprecated since 3.0.0
*/
public static function GetDisplayExtendedSet(WebPage $oPage, CMDBObjectSet $oSet, $aExtraParams = array())
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
if (empty($aExtraParams['currentId'])) {
$iListId = utils::GetUniqueId(); // Works only if not in an Ajax page !!
} else {
$iListId = $aExtraParams['currentId'];
}
$aList = array();
// Initialize and check the parameters
$bViewLink = isset($aExtraParams['view_link']) ? $aExtraParams['view_link'] : true;
$bDisplayMenu = isset($aExtraParams['menu']) ? $aExtraParams['menu'] == true : true;
// Check if there is a list of aliases to limit the display to...
$aDisplayAliases = isset($aExtraParams['display_aliases']) ? explode(',',
$aExtraParams['display_aliases']) : array();
$sZListName = isset($aExtraParams['zlist']) ? ($aExtraParams['zlist']) : 'list';
$aExtraFieldsRaw = isset($aExtraParams['extra_fields']) ? explode(',',
trim($aExtraParams['extra_fields'])) : array();
$aExtraFields = array();
$sAttCode = '';
foreach($aExtraFieldsRaw as $sFieldName)
{
// Ignore attributes not of the main queried class
if (preg_match('/^(.*)\.(.*)$/', $sFieldName, $aMatches))
{
$sClassAlias = $aMatches[1];
$sAttCode = $aMatches[2];
if (array_key_exists($sClassAlias, $oSet->GetSelectedClasses()))
{
$aExtraFields[$sClassAlias][] = $sAttCode;
}
}
else
{
$aExtraFields['*'] = $sAttCode;
}
}
$aClasses = $oSet->GetFilter()->GetSelectedClasses();
$aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if ((UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO) &&
((count($aDisplayAliases) == 0) || (in_array($sAlias, $aDisplayAliases))))
{
$aAuthorizedClasses[$sAlias] = $sClassName;
}
}
foreach($aAuthorizedClasses as $sAlias => $sClassName)
{
if (array_key_exists($sAlias, $aExtraFields))
{
$aList[$sAlias] = $aExtraFields[$sAlias];
}
else
{
$aList[$sAlias] = array();
}
if ($sZListName !== false)
{
$aDefaultList = self::FlattenZList(MetaModel::GetZListItems($sClassName, $sZListName));
$aList[$sAlias] = array_merge($aDefaultList, $aList[$sAlias]);
}
// Filter the list to removed linked set since we are not able to display them here
foreach ($aList[$sAlias] as $index => $sAttCode)
{
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode);
if ($oAttDef instanceof AttributeLinkedSet)
{
// Removed from the display list
unset($aList[$sAlias][$index]);
}
}
if (empty($aList[$sAlias]))
{
unset($aList[$sAlias], $aAuthorizedClasses[$sAlias]);
}
}
$sSelectMode = 'none';
$oDataTable = new DataTable($iListId, $oSet, $aAuthorizedClasses);
$oSettings = DataTableSettings::GetDataModelSettings($aAuthorizedClasses, $bViewLink, $aList);
$bDisplayLimit = isset($aExtraParams['display_limit']) ? $aExtraParams['display_limit'] : true;
if ($bDisplayLimit)
{
$iDefaultPageSize = appUserPreferences::GetPref('default_page_size',
MetaModel::GetConfig()->GetMinDisplayLimit());
$oSettings->iDefaultPageSize = $iDefaultPageSize;
}
$oSettings->aSortOrder = MetaModel::GetOrderByDefault($sClassName);
return $oDataTable->Display($oPage, $oSettings, $bDisplayMenu, $sSelectMode, $bViewLink, $aExtraParams);
}
/**
* @param \WebPage $oPage
* @param \CMDBObjectSet $oSet
* @param array $aParams
* @param string $sCharset
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public static function DisplaySetAsCSV(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array(), $sCharset = 'UTF-8')
{
$oPage->add(self::GetSetAsCSV($oSet, $aParams, $sCharset));
}
/**
* @param \DBObjectSet $oSet
* @param array $aParams
* @param string $sCharset
*
* @return string
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \Exception
*/
public static function GetSetAsCSV(DBObjectSet $oSet, $aParams = array(), $sCharset = 'UTF-8')
{
$sSeparator = isset($aParams['separator']) ? $aParams['separator'] : ','; // default separator is comma
$sTextQualifier = isset($aParams['text_qualifier']) ? $aParams['text_qualifier'] : '"'; // default text qualifier is double quote
$aFields = null;
if (isset($aParams['fields']) && (strlen($aParams['fields']) > 0))
{
$aFields = explode(',', $aParams['fields']);
}
$bFieldsAdvanced = false;
if (isset($aParams['fields_advanced']))
{
$bFieldsAdvanced = (bool)$aParams['fields_advanced'];
}
$bLocalize = true;
if (isset($aParams['localize_values']))
{
$bLocalize = (bool)$aParams['localize_values'];
}
$aList = array();
$aClasses = $oSet->GetFilter()->GetSelectedClasses();
$aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO)
{
$aAuthorizedClasses[$sAlias] = $sClassName;
}
}
$aHeader = array();
foreach($aAuthorizedClasses as $sAlias => $sClassName)
{
$aList[$sAlias] = array();
foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef)
{
if (is_null($aFields) || (count($aFields) == 0))
{
// Standard list of attributes (no link sets)
if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField()))
{
$sAttCodeEx = $oAttDef->IsExternalField() ? $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode() : $sAttCode;
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
{
if ($bFieldsAdvanced)
{
$aList[$sAlias][$sAttCodeEx] = $oAttDef;
if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE))
{
$sRemoteClass = $oAttDef->GetTargetClass();
foreach(MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode)
{
$aList[$sAlias][$sAttCode.'->'.$sRemoteAttCode] = MetaModel::GetAttributeDef($sRemoteClass,
$sRemoteAttCode);
}
}
}
}
else
{
// Any other attribute
$aList[$sAlias][$sAttCodeEx] = $oAttDef;
}
}
}
else
{
// User defined list of attributes
if (in_array($sAttCode, $aFields) || in_array($sAlias.'.'.$sAttCode, $aFields))
{
$aList[$sAlias][$sAttCode] = $oAttDef;
}
}
}
if ($bFieldsAdvanced)
{
$aHeader[] = 'id';
}
foreach($aList[$sAlias] as $sAttCodeEx => $oAttDef)
{
$aHeader[] = $bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx,
isset($aParams['showMandatoryFields'])) : $sAttCodeEx;
}
}
$sHtml = implode($sSeparator, $aHeader)."\n";
$oSet->Seek(0);
while ($aObjects = $oSet->FetchAssoc())
{
$aRow = array();
foreach($aAuthorizedClasses as $sAlias => $sClassName)
{
$oObj = $aObjects[$sAlias];
if ($bFieldsAdvanced)
{
if (is_null($oObj))
{
$aRow[] = '';
}
else
{
$aRow[] = $oObj->GetKey();
}
}
foreach($aList[$sAlias] as $sAttCodeEx => $oAttDef)
{
if (is_null($oObj))
{
$aRow[] = '';
}
else
{
$value = $oObj->Get($sAttCodeEx);
$sCSVValue = $oAttDef->GetAsCSV($value, $sSeparator, $sTextQualifier, $oObj, $bLocalize);
$aRow[] = iconv('UTF-8', $sCharset.'//IGNORE//TRANSLIT', $sCSVValue);
}
}
}
$sHtml .= implode($sSeparator, $aRow)."\n";
}
return $sHtml;
}
/**
* @param \WebPage $oPage
* @param \CMDBObjectSet $oSet
* @param array $aParams
*
* @throws \Exception
*/
public static function DisplaySetAsHTMLSpreadsheet(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
{
$oPage->add(self::GetSetAsHTMLSpreadsheet($oSet, $aParams));
}
/**
* Spreadsheet output: designed for end users doing some reporting
* Then the ids are excluded and replaced by the corresponding friendlyname
*
* @param \DBObjectSet $oSet
* @param array $aParams
*
* @return string
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \Exception
*/
public static function GetSetAsHTMLSpreadsheet(DBObjectSet $oSet, $aParams = array())
{
$aFields = null;
if (isset($aParams['fields']) && (strlen($aParams['fields']) > 0))
{
$aFields = explode(',', $aParams['fields']);
}
$bFieldsAdvanced = false;
if (isset($aParams['fields_advanced']))
{
$bFieldsAdvanced = (bool)$aParams['fields_advanced'];
}
$bLocalize = true;
if (isset($aParams['localize_values']))
{
$bLocalize = (bool)$aParams['localize_values'];
}
$aList = array();
$aClasses = $oSet->GetFilter()->GetSelectedClasses();
$aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO)
{
$aAuthorizedClasses[$sAlias] = $sClassName;
}
}
$aHeader = array();
foreach($aAuthorizedClasses as $sAlias => $sClassName)
{
$aList[$sAlias] = array();
foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef)
{
if (is_null($aFields) || (count($aFields) == 0))
{
// Standard list of attributes (no link sets)
if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField()))
{
$sAttCodeEx = $oAttDef->IsExternalField() ? $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode() : $sAttCode;
$aList[$sAlias][$sAttCodeEx] = $oAttDef;
if ($bFieldsAdvanced && $oAttDef->IsExternalKey(EXTKEY_RELATIVE))
{
$sRemoteClass = $oAttDef->GetTargetClass();
foreach(MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode)
{
$aList[$sAlias][$sAttCode.'->'.$sRemoteAttCode] = MetaModel::GetAttributeDef($sRemoteClass,
$sRemoteAttCode);
}
}
}
}
else
{
// User defined list of attributes
if (in_array($sAttCode, $aFields) || in_array($sAlias.'.'.$sAttCode, $aFields))
{
$aList[$sAlias][$sAttCode] = $oAttDef;
}
}
}
// Replace external key by the corresponding friendly name (if not already in the list)
foreach($aList[$sAlias] as $sAttCode => $oAttDef)
{
if ($oAttDef->IsExternalKey())
{
unset($aList[$sAlias][$sAttCode]);
$sFriendlyNameAttCode = $sAttCode.'_friendlyname';
if (!array_key_exists($sFriendlyNameAttCode,
$aList[$sAlias]) && MetaModel::IsValidAttCode($sClassName, $sFriendlyNameAttCode))
{
$oFriendlyNameAtt = MetaModel::GetAttributeDef($sClassName, $sFriendlyNameAttCode);
$aList[$sAlias][$sFriendlyNameAttCode] = $oFriendlyNameAtt;
}
}
}
foreach($aList[$sAlias] as $sAttCodeEx => $oAttDef)
{
$sColLabel = $bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx) : $sAttCodeEx;
$oFinalAttDef = $oAttDef->GetFinalAttDef();
if (get_class($oFinalAttDef) == 'AttributeDateTime')
{
$aHeader[] = $sColLabel.' ('.Dict::S('UI:SplitDateTime-Date').')';
$aHeader[] = $sColLabel.' ('.Dict::S('UI:SplitDateTime-Time').')';
}
else
{
$aHeader[] = $sColLabel;
}
}
}
$sHtml = "\n";
$sHtml .= "
\n";
return $sHtml;
}
/**
* @param \WebPage $oPage
* @param \CMDBObjectSet $oSet
* @param array $aParams
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public static function DisplaySetAsXML(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
{
$bLocalize = true;
if (isset($aParams['localize_values']))
{
$bLocalize = (bool)$aParams['localize_values'];
}
$aClasses = $oSet->GetFilter()->GetSelectedClasses();
$aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO)
{
$aAuthorizedClasses[$sAlias] = $sClassName;
}
}
$aList = array();
$aList[$sAlias] = MetaModel::GetZListItems($sClassName, 'details');
$oPage->add("\n";
$sHtml .= " \n";
$oSet->Seek(0);
while ($aObjects = $oSet->FetchAssoc())
{
$aRow = array();
foreach($aAuthorizedClasses as $sAlias => $sClassName)
{
$oObj = $aObjects[$sAlias];
foreach($aList[$sAlias] as $sAttCodeEx => $oAttDef)
{
if (is_null($oObj))
{
$aRow[] = '".implode(" ", $aHeader)." \n";
$sHtml .= "';
}
else
{
$oFinalAttDef = $oAttDef->GetFinalAttDef();
if (get_class($oFinalAttDef) == 'AttributeDateTime')
{
$sDate = $oObj->Get($sAttCodeEx);
if ($sDate === null)
{
$aRow[] = ' ';
$aRow[] = ' ';
}
else
{
$iDate = AttributeDateTime::GetAsUnixSeconds($sDate);
$aRow[] = ' '.date('Y-m-d',
$iDate).' '; // Format kept as-is for 100% backward compatibility of the exports
$aRow[] = ''.date('H:i:s',
$iDate).' '; // Format kept as-is for 100% backward compatibility of the exports
}
}
else
{
if ($oAttDef instanceof AttributeCaseLog)
{
$rawValue = $oObj->Get($sAttCodeEx);
$outputValue = str_replace("\n", "
",
htmlentities($rawValue->__toString(), ENT_QUOTES, 'UTF-8'));
// Trick for Excel: treat the content as text even if it begins with an equal sign
$aRow[] = ''.$outputValue.' ';
}
else
{
$rawValue = $oObj->Get($sAttCodeEx);
// Due to custom formatting rules, empty friendlynames may be rendered as non-empty strings
// let's fix this and make sure we render an empty string if the key == 0
if ($oAttDef instanceof AttributeExternalField && $oAttDef->IsFriendlyName())
{
$sKeyAttCode = $oAttDef->GetKeyAttCode();
if ($oObj->Get($sKeyAttCode) == 0)
{
$rawValue = '';
}
}
if ($bLocalize)
{
$outputValue = htmlentities($oFinalAttDef->GetEditValue($rawValue), ENT_QUOTES,
'UTF-8');
}
else
{
$outputValue = htmlentities($rawValue, ENT_QUOTES, 'UTF-8');
}
$aRow[] = ''.$outputValue.' ';
}
}
}
}
}
$sHtml .= implode("\n", $aRow);
$sHtml .= "\n";
}
$sHtml .= "
{$sValidationSpan}{$sReloadSpan}
HTML;
if ($sFileName == '') {
$oPage->add_ready_script("$('#remove_attr_{$iId}').addClass('ibo-is-hidden');");
}
break;
case 'Image':
$sInputType = self::ENUM_INPUT_TYPE_IMAGE;
$aEventsList[] = 'validate';
$aEventsList[] = 'change';
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/edit_image.js');
$oDocument = $value; // Value is an ormDocument objectm
$sDefaultUrl = $oAttDef->Get('default_image');
if (is_object($oDocument) && !$oDocument->IsEmpty()) {
$sUrl = 'data:'.$oDocument->GetMimeType().';base64,'.base64_encode($oDocument->GetData());
} else {
$sUrl = null;
}
$sHTMLValue = "
');
$oAppContext = new ApplicationContext();
$oForm->AddHtml($oAppContext->GetForForm());
$oCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'), 'cancel', 'cancel');
$oCancelButton->SetOnClickJsCode("BackToDetails('{$sClass}', '{$this->GetKey()}', '', '{$sOwnershipToken}');");
$oForm->AddSubBlock($oCancelButton);
$oSubmitButton = ButtonUIBlockFactory::MakeForPrimaryAction($sActionLabel, 'submit', 'submit', true);
$oForm->AddSubBlock($oSubmitButton);
$oPage->add(<<
');
$oForm->AddHtml($oPage->GetDetails($aDetails));
$oForm->AddHtml(' ".htmlentities(MyHelpers::beautifulstr($data, 1000, true), ENT_QUOTES,
'UTF-8')."
\n");
}
break;
case 'application':
switch ($oDoc->GetMimeType())
{
case 'application/pdf':
$oPage->add("\n");
break;
default:
$oPage->add(Dict::S('UI:Document:NoPreview'));
}
break;
case 'image':
$oPage->add("\n");
break;
default:
$oPage->add(Dict::S('UI:Document:NoPreview'));
}
return '';
}
// $m_highlightComparison[previous][new] => next value
protected static $m_highlightComparison = array(
HILIGHT_CLASS_CRITICAL => array(
HILIGHT_CLASS_CRITICAL => HILIGHT_CLASS_CRITICAL,
HILIGHT_CLASS_WARNING => HILIGHT_CLASS_CRITICAL,
HILIGHT_CLASS_OK => HILIGHT_CLASS_CRITICAL,
HILIGHT_CLASS_NONE => HILIGHT_CLASS_CRITICAL,
),
HILIGHT_CLASS_WARNING => array(
HILIGHT_CLASS_CRITICAL => HILIGHT_CLASS_CRITICAL,
HILIGHT_CLASS_WARNING => HILIGHT_CLASS_WARNING,
HILIGHT_CLASS_OK => HILIGHT_CLASS_WARNING,
HILIGHT_CLASS_NONE => HILIGHT_CLASS_WARNING,
),
HILIGHT_CLASS_OK => array(
HILIGHT_CLASS_CRITICAL => HILIGHT_CLASS_CRITICAL,
HILIGHT_CLASS_WARNING => HILIGHT_CLASS_WARNING,
HILIGHT_CLASS_OK => HILIGHT_CLASS_OK,
HILIGHT_CLASS_NONE => HILIGHT_CLASS_OK,
),
HILIGHT_CLASS_NONE => array(
HILIGHT_CLASS_CRITICAL => HILIGHT_CLASS_CRITICAL,
HILIGHT_CLASS_WARNING => HILIGHT_CLASS_WARNING,
HILIGHT_CLASS_OK => HILIGHT_CLASS_OK,
HILIGHT_CLASS_NONE => HILIGHT_CLASS_NONE,
),
);
/**
* This function returns a 'hilight' CSS class, used to hilight a given row in a table
* There are currently (i.e defined in the CSS) 4 possible values HILIGHT_CLASS_CRITICAL,
* HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE
* To Be overridden by derived classes
*
* @param void
*
* @return String The desired higlight class for the object/row
*/
public function GetHilightClass()
{
// Possible return values are:
// HILIGHT_CLASS_CRITICAL, HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE
$current = parent::GetHilightClass(); // Default computation
// Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information
/** @var \iApplicationUIExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
{
$new = $oExtensionInstance->GetHilightClass($this);
@$current = self::$m_highlightComparison[$current][$new];
}
return $current;
}
/**
* Re-order the fields based on their inter-dependencies
*
* @params hash @aFields field_code => array_of_depencies
*
* @param $aFields
* @return array Ordered array of fields or throws an exception
* @throws \Exception
*/
public static function OrderDependentFields($aFields)
{
$aResult = array();
$iCount = 0;
do
{
$bSet = false;
$iCount++;
foreach($aFields as $sFieldCode => $aDeps)
{
foreach($aDeps as $key => $sDependency)
{
if (in_array($sDependency, $aResult))
{
// Dependency is resolved, remove it
unset($aFields[$sFieldCode][$key]);
}
else
{
if (!array_key_exists($sDependency, $aFields))
{
// The current fields depends on a field not present in the form
// let's ignore it (since it cannot change)
unset($aFields[$sFieldCode][$key]);
}
}
}
if (count($aFields[$sFieldCode]) == 0)
{
// No more pending depencies for this field, add it to the list
$aResult[] = $sFieldCode;
unset($aFields[$sFieldCode]);
$bSet = true;
}
}
} while ($bSet && (count($aFields) > 0));
if (count($aFields) > 0)
{
$sMessage = "Error: Circular dependencies between the fields!
".print_r($aFields, true)."
";
throw(new Exception($sMessage));
}
return $aResult;
}
/**
* Get the list of actions to be displayed as 'shortcuts' (i.e buttons) instead of inside the Actions popup menu
*
* @api
* @overwritable-hook
*
* @param $sFinalClass string The actual class of the objects for which to display the menu
*
* @return array the list of menu codes (i.e dictionary entries) that can be displayed as shortcuts next to the
* actions menu
*/
public static function GetShortcutActions($sFinalClass)
{
$sShortcutActions = MetaModel::GetConfig()->Get('shortcut_actions');
$aShortcutActions = explode(',', $sShortcutActions);
return $aShortcutActions;
}
/**
* Maps the given context parameter name to the appropriate filter/search code for this class
*
* @param string $sContextParam Name of the context parameter, i.e. 'org_id'
*
* @return string Filter code, i.e. 'customer_id'
*/
public static function MapContextParam($sContextParam)
{
if ($sContextParam == 'menu')
{
return null;
}
else
{
return $sContextParam;
}
}
/**
* Updates the object from a flat array of values
*
* @param $aAttList array $aAttList array of attcode
* @param $aErrors array Returns information about slave attributes
* @param $aAttFlags array Attribute codes => Flags to use instead of those from the MetaModel
*
* @return array of attcodes that can be used for writing on the current object
* @throws \CoreException
*/
public function GetWriteableAttList($aAttList, &$aErrors, $aAttFlags = array())
{
if (!is_array($aAttList))
{
$aAttList = $this->FlattenZList(MetaModel::GetZListItems(get_class($this), 'details'));
// Special case to process the case log, if any...
// WARNING: if you change this also check the functions DisplayModifyForm and DisplayCaseLog
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
{
if (array_key_exists($sAttCode, $aAttFlags))
{
$iFlags = $aAttFlags[$sAttCode];
}
elseif ($this->IsNew())
{
$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
}
else
{
$aVoid = array();
$iFlags = $this->GetAttributeFlags($sAttCode, $aVoid);
}
if ($oAttDef instanceof AttributeCaseLog)
{
if (!($iFlags & (OPT_ATT_HIDDEN | OPT_ATT_SLAVE | OPT_ATT_READONLY)))
{
// The case log is editable, append it to the list of fields to retrieve
$aAttList[] = $sAttCode;
}
}
}
}
$aWriteableAttList = array();
foreach($aAttList as $sAttCode)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
if (array_key_exists($sAttCode, $aAttFlags))
{
$iFlags = $aAttFlags[$sAttCode];
}
elseif ($this->IsNew())
{
$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
}
else
{
$aVoid = array();
$iFlags = $this->GetAttributeFlags($sAttCode, $aVoid);
}
if ($oAttDef->IsWritable())
{
if ($iFlags & (OPT_ATT_HIDDEN | OPT_ATT_READONLY))
{
// Non-visible, or read-only attribute, do nothing
}
elseif ($iFlags & OPT_ATT_SLAVE)
{
$aErrors[$sAttCode] = Dict::Format('UI:AttemptingToSetASlaveAttribute_Name', $oAttDef->GetLabel());
}
else
{
$aWriteableAttList[$sAttCode] = $oAttDef;
}
}
}
return $aWriteableAttList;
}
/**
* Compute the attribute flags depending on the object state
*/
public function GetFormAttributeFlags($sAttCode)
{
if ($this->IsNew())
{
$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
}
else
{
$iFlags = $this->GetAttributeFlags($sAttCode);
}
if (($iFlags & OPT_ATT_MANDATORY) && $this->IsNew())
{
$iFlags = $iFlags & ~OPT_ATT_READONLY; // Mandatory fields cannot be read-only when creating an object
}
return $iFlags;
}
/**
* Updates the object from a flat array of values
*
* @param $aValues array of attcode => scalar or array (N-N links)
*
* @return void
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
*/
public function UpdateObjectFromArray($aValues)
{
foreach($aValues as $sAttCode => $value)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
switch ($oAttDef->GetEditClass())
{
case 'Document':
case 'Image':
// There should be an uploaded file with the named attr_
", $sDescription);
$sTip .= "
' : '';
$sFieldAsHtml = self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs);
$sHTMLValue = <<
' : '';
$sFieldAsHtml = self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs);
$sHTMLValue = <<
";
$index = 0;
foreach ($aMultiValues as $sCurrValue => $aVal) {
$sDisplayValue = empty($aVal['display']) ? ''.Dict::S('Enum:Undefined').'' : str_replace(array(
"\n",
"\r",
), " ", $aVal['display']);
$sTip .= "
'.($bResult ? '' : implode('
', $aErrors)).'
', ); if ($bResult && (!$bPreview)) { $oObj->DBUpdate(); } } set_time_limit(intval($iPreviousTimeLimit)); $oTable = DataTableUIBlockFactory::MakeForForm('BulkModify', $aHeaders, $aRows); $oTable->AddOption("bFullscreen", true); $oPanel = PanelUIBlockFactory::MakeForClass($sClass, ''); $oPanel->AddCSSClass('ibo-datatable-panel'); $oPanel->AddSubBlock($oTable); if ($bPreview) { $sFormAction = utils::GetAbsoluteUrlAppRoot().'pages/UI.php'; // No parameter in the URL, the only parameter will be the ones passed through the form // Form to submit: $oForm = FormUIBlockFactory::MakeStandard('')->SetAction($sFormAction); $oP->AddSubBlock($oForm); $oForm->AddSubBlock($oPanel); $oPanel->SetTitleBlock($oTitle); $oAppContext = new ApplicationContext(); $oP->add($oAppContext->GetForForm()); foreach ($aContextData as $sKey => $value) { $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden($sKey, $value)); } $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', $sCustomOperation)); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $sClass)); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('preview_mode', 0)); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::GetNewTransactionId())); $oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null); $oToolbarButtons->AddCSSClass('ibo-toolbar--button'); $oForm->AddSubBlock($oToolbarButtons); $oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'))->SetOnClickJsCode("window.location.href='$sCancelUrl'")); $oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('UI:Button:ModifyAll'), '', '', true)); 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) { $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden($sKey.'['.$vKey.']', $vValue)); } } else { $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden($sKey, $value)); } } } } else { $oP->AddUiBlock($oTitle); $oP->AddUiBlock($oPanel); $oP->AddSubBlock(ButtonUIBlockFactory::MakeForSecondaryAction(Dict::S('UI:Button:Done')))->SetOnClickJsCode("window.location.href='$sCancelUrl'")->AddCSSClass('mt-5'); } } /** * Perform all the needed checks to delete one (or more) objects * * @param \WebPage $oP * @param $sClass * @param \DBObject[] $aObjects * @param $bPreview * @param $sCustomOperation * @param array $aContextData * * @throws \CoreException * @throws \DictExceptionMissingString * @throws \Exception */ 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->DBDelete($oDeletionPlan); } } if ($bPreview) { if (count($aObjects) == 1) { $oObj = $aObjects[0]; $sTitle = Dict::Format('UI:Delete:ConfirmDeletionOf_Name', $oObj->GetName()); } else { $sTitle = Dict::Format('UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass)); } $oP->AddUiBlock(TitleUIBlockFactory::MakeForPage($sTitle)); // 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->AddSubBlock(DataTableUIBlockFactory::MakeForForm(preg_replace('/[^a-zA-Z0-9_-]/', '', uniqid('form_', true)), $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')); } $oForm = FormUIBlockFactory::MakeStandard(''); $oP->AddSubBlock($oForm); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::ReadParam('transaction_id', '', false, 'transaction_id'))); $oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null); $oToolbarButtons->AddCSSClass('ibo-toolbar--button'); $oForm->AddSubBlock($oToolbarButtons); $oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Back'))->SetOnClickJsCode("window.history.back();")); $oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForDestructiveAction(Dict::S('UI:Button:Delete'), null, null, true)->SetIsDisabled(true)); $oAppContext = new ApplicationContext(); $oForm->AddSubBlock($oAppContext->GetForFormBlock()); } else { if (count($aObjects) == 1) { $oObj = $aObjects[0]; $sSubtitle = Dict::Format('UI:Delect:Confirm_Object', $oObj->GetHyperLink()); } else { $sSubtitle = Dict::Format('UI:Delect:Confirm_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass)); } $oP->AddUiBlock(TitleUIBlockFactory::MakeStandard(new Html($sSubtitle))); foreach ($aObjects as $oObj) { $aKeys[] = $oObj->GetKey(); } $oFilter = new DBObjectSearch($sClass); $oFilter->AddCondition('id', $aKeys, 'IN'); $oSet = new CMDBobjectSet($oFilter); $oDisplaySet = \Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory::MakeStandard("0"); $oP->AddSubBlock($oDisplaySet); $oDisplaySet->AddSubBlock(CMDBAbstractObject::GetDisplaySetBlock($oP, $oSet, array('display_limit' => false, 'menu' => false))); $oForm = FormUIBlockFactory::MakeStandard(''); $oP->AddSubBlock($oForm); foreach ($aContextData as $sKey => $value) { $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden($sKey, $value)); } $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::GetNewTransactionId())); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', $sCustomOperation)); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('filter', $oFilter->Serialize())); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $sClass)); foreach ($aObjects as $oObj) { $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('selectObject[]', $oObj->GetKey())); } $oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null); $oToolbarButtons->AddCSSClass('ibo-toolbar--button'); $oForm->AddSubBlock($oToolbarButtons); $oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Back'))->SetOnClickJsCode("window.history.back();")); $oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForDestructiveAction(Dict::S('UI:Button:Delete'), null, null, true)); $oAppContext = new ApplicationContext(); $oForm->AddSubBlock($oAppContext->GetForFormBlock()); } } else // if ($bPreview)... { // Execute the deletion // if (count($aObjects) == 1) { $oObj = $aObjects[0]; $sTitle = Dict::Format('UI:Title:DeletionOf_Object', $oObj->GetName()); } else { $sTitle = Dict::Format('UI:Title:BulkDeletionOf_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass)); } $oP->AddUiBlock(TitleUIBlockFactory::MakeForPage($sTitle)); // 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]; $sSubtitle = Dict::Format('UI:Delete:CleaningUpRefencesTo_Object', $oObj->GetName()); } else { $sSubtitle = Dict::Format('UI:Delete:CleaningUpRefencesTo_Several_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass)); } $oP->AddUiBlock(TitleUIBlockFactory::MakeForPage($sSubtitle)); $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+')); $oResultsPanel = PanelUIBlockFactory::MakeForInformation(''); $oP->AddUiBlock($oResultsPanel); $oResultsPanel->AddSubBlock( DataTableUIBlockFactory::MakeForStaticData('', $aDisplayConfig, $aDisplayData) ); } } } /** * Find redundancy settings that can be viewed and modified in a tab * Settings are distributed to the corresponding link set attribute so as to be shown in the relevant tab * * @throws \Exception */ protected function FindVisibleRedundancySettings() { $aRet = array(); foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef) { if ($oAttDef instanceof AttributeRedundancySettings) { if ($oAttDef->IsVisible()) { $aQueryInfo = $oAttDef->GetRelationQueryData(); if (isset($aQueryInfo['sAttribute'])) { $oUpperAttDef = MetaModel::GetAttributeDef($aQueryInfo['sFromClass'], $aQueryInfo['sAttribute']); $oHostAttDef = $oUpperAttDef->GetMirrorLinkAttribute(); if ($oHostAttDef) { $sHostAttCode = $oHostAttDef->GetCode(); $aRet[$sHostAttCode][] = $oAttDef; } } } } } return $aRet; } /** * Generates the javascript code handle the "watchdog" associated with the concurrent access locking mechanism * * @param Webpage $oPage * @param string $sOwnershipToken */ protected function GetOwnershipJSHandler($oPage, $sOwnershipToken) { $iInterval = max(MIN_WATCHDOG_INTERVAL, MetaModel::GetConfig()->Get('concurrent_lock_expiration_delay')) * 1000 / 2; // Minimum interval for the watchdog is MIN_WATCHDOG_INTERVAL $sJSClass = json_encode(get_class($this)); $iKey = (int)$this->GetKey(); $sJSToken = json_encode($sOwnershipToken); $sJSTitle = json_encode(Dict::S('UI:DisconnectedDlgTitle')); $sJSOk = json_encode(Dict::S('UI:Button:Ok')); $oPage->add_ready_script( <<