diff --git a/application/ajaxwebpage.class.inc.php b/application/ajaxwebpage.class.inc.php index cc3517730..15bd06a28 100644 --- a/application/ajaxwebpage.class.inc.php +++ b/application/ajaxwebpage.class.inc.php @@ -1,5 +1,5 @@ sContentType = 'text/html'; $this->sContentDisposition = 'inline'; $this->m_sMenu = ""; - } + + $bArchiveMode = utils::IsArchiveMode(); + DBSearch::SetArchiveModeDefault($bArchiveMode); + if ($bArchiveMode) MetaModel::DBSetReadOnly(); + } public function AddTabContainer($sTabContainer, $sPrefix = '') { diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 15404aaab..9892bad2c 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -198,8 +198,6 @@ EOF $oBlock->Display($oPage, -1); } - $oPage->add("
".$oDataSource->GetIcon(true, 'style="vertical-align:middle"')." $sLink
";
$sTip .= Dict::S('Core:Synchro:LastSynchro') . '
' . $sLastSynchro . "
';
+ $sLabel = htmlentities(Dict::S('Tag:Synchronized'), ENT_QUOTES, 'UTF-8');
+ $sSynchroTagId = 'synchro_icon-'.$this->GetKey();
+ $aIcons[] = "Peak memory Usage: '.sprintf('%.3f MB', memory_get_peak_usage(true) / (1024*1024)).'
'; @@ -982,7 +1004,20 @@ EOF $oPrefs = new URLPopupMenuItem('UI:Preferences', Dict::S('UI:Preferences'), utils::GetAbsoluteUrlAppRoot()."pages/preferences.php?".$oAppContext->GetForLink()); $aActions[$oPrefs->GetUID()] = $oPrefs->GetMenuItem(); - + + if (utils::IsArchiveMode()) + { + $oExitArchive = new JSPopupMenuItem('UI:ArchiveModeOff', Dict::S('UI:ArchiveModeOff'), 'return ArchiveMode(false);'); + $aActions[$oExitArchive->GetUID()] = $oExitArchive->GetMenuItem(); + + $sIcon = ''; + $this->AddApplicationMessage(Dict::S('UI:ArchiveMode:Banner'), $sIcon, Dict::S('UI:ArchiveMode:Banner+')); + } + elseif (UserRights::CanBrowseArchive()) + { + $oBrowseArchive = new JSPopupMenuItem('UI:ArchiveModeOn', Dict::S('UI:ArchiveModeOn'), 'return ArchiveMode(true);'); + $aActions[$oBrowseArchive->GetUID()] = $oBrowseArchive->GetMenuItem(); + } if (utils::CanLogOff()) { $oLogOff = new URLPopupMenuItem('UI:LogOffMenu', Dict::S('UI:LogOffMenu'), utils::GetAbsoluteUrlAppRoot().'pages/logoff.php?operation=do_logoff'); @@ -1014,26 +1049,34 @@ EOF $sRestrictions = Dict::S('UI:AccessRO-Users'); } - $sApplicationBanner = ''; if (strlen($sRestrictions) > 0) { + $sIcon = +<<
';
- $sApplicationBanner .= ' '.$sRestrictions.'';
if (strlen($sAdminMessage) > 0)
{
- $sApplicationBanner .= ' '.$sAdminMessage.'';
+ $sRestrictions .= ' '.$sAdminMessage;
}
- $sApplicationBanner .= '
';
$sApplicationBanner .= ' '.$sReadOnly.'';
if (strlen($sAdminMessage) > 0)
diff --git a/application/utils.inc.php b/application/utils.inc.php
index e4a8357e1..7d873d428 100644
--- a/application/utils.inc.php
+++ b/application/utils.inc.php
@@ -1,7 +1,7 @@
GetOptional('magic', false);}
public function LoadInObject() {return true;}
public function LoadFromDB() {return true;}
public function AlwaysLoadInTables() {return $this->GetOptional('always_load_in_tables', false);}
@@ -399,6 +400,7 @@ abstract class AttributeDefinition
public function GetSQLColumns($bFullSpec = false) {return array();} // returns column/spec pairs (1 in most of the cases), for STRUCTURING (DB creation)
public function GetSQLValues($value) {return array();} // returns column/value pairs (1 in most of the cases), for WRITING (Insert, Update)
public function RequiresIndex() {return false;}
+ public function CopyOnAllTables() {return false;}
public function GetOrderBySQLExpressions($sClassAlias)
{
@@ -1416,7 +1418,7 @@ class AttributeDBFieldVoid extends AttributeDefinition
public function IsDirectField() {return true;}
public function IsScalar() {return true;}
- public function IsWritable() {return true;}
+ public function IsWritable() {return !$this->IsMagic();}
public function GetSQLExpr()
{
return $this->Get("sql");
@@ -1799,7 +1801,7 @@ class AttributeBoolean extends AttributeInteger
public function GetEditClass() {return "Integer";}
protected function GetSQLCol($bFullSpec = false) {return "TINYINT(1)".($bFullSpec ? $this->GetSQLColSpec() : '');}
-
+
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue)) return null;
@@ -1814,25 +1816,169 @@ class AttributeBoolean extends AttributeInteger
return 0;
}
- public function GetAsXML($sValue, $oHostObject = null, $bLocalize = true)
+ public function GetValueLabel($bValue)
{
- return $sValue ? '1' : '0';
+ if (is_null($bValue))
+ {
+ $sLabel = Dict::S('Core:'.get_class($this).'/Value:null');
+ }
+ else
+ {
+ $sValue = $bValue ? 'yes' : 'no';
+ $sDefault = Dict::S('Core:'.get_class($this).'/Value:'.$sValue);
+ $sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sValue, $sDefault, true /*user lang*/);
+ }
+ return $sLabel;
}
- public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
+
+ public function GetValueDescription($bValue)
{
- return $sValue ? '1' : '0';
+ if (is_null($bValue))
+ {
+ $sDescription = Dict::S('Core:'.get_class($this).'/Value:null+');
+ }
+ else
+ {
+ $sValue = $bValue ? 'yes' : 'no';
+ $sDefault = Dict::S('Core:'.get_class($this).'/Value:'.$sValue.'+');
+ $sDescription = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sValue.'+', $sDefault, true /*user lang*/);
+ }
+ return $sDescription;
}
- public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
+
+ public function GetAsHTML($bValue, $oHostObject = null, $bLocalize = true)
{
- return $sValue ? '1' : '0';
+ if (is_null($bValue))
+ {
+ $sRes = '';
+ }
+ elseif ($bLocalize)
+ {
+ $sLabel = $this->GetValueLabel($bValue);
+ $sDescription = $this->GetValueDescription($bValue);
+ // later, we could imagine a detailed description in the title
+ $sRes = "".parent::GetAsHtml($sLabel)."";
+ }
+ else
+ {
+ $sRes = $bValue ? 'yes' : 'no';
+ }
+ return $sRes;
}
+
+ public function GetAsXML($bValue, $oHostObject = null, $bLocalize = true)
+ {
+ if (is_null($bValue))
+ {
+ $sFinalValue = '';
+ }
+ elseif ($bLocalize)
+ {
+ $sFinalValue = $this->GetValueLabel($bValue);
+ }
+ else
+ {
+ $sFinalValue = $bValue ? 'yes' : 'no';
+ }
+ $sRes = parent::GetAsXML($sFinalValue, $oHostObject, $bLocalize);
+ return $sRes;
+ }
+
+ public function GetAsCSV($bValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
+ {
+ if (is_null($bValue))
+ {
+ $sFinalValue = '';
+ }
+ elseif ($bLocalize)
+ {
+ $sFinalValue = $this->GetValueLabel($bValue);
+ }
+ else
+ {
+ $sFinalValue = $bValue ? 'yes' : 'no';
+ }
+ $sRes = parent::GetAsCSV($sFinalValue, $sSeparator, $sTextQualifier, $oHostObject, $bLocalize);
+ return $sRes;
+ }
+
+ static public function GetFormFieldClass()
+ {
+ return '\\Combodo\\iTop\\Form\\Field\\SelectField';
+ }
+
+ public function MakeFormField(DBObject $oObject, $oFormField = null)
+ {
+ if ($oFormField === null)
+ {
+ $sFormFieldClass = static::GetFormFieldClass();
+ $oFormField = new $sFormFieldClass($this->GetCode());
+ }
+
+ $oFormField->SetChoices(array('yes' => $this->GetValueLabel(true), 'no' => $this->GetValueLabel(false)));
+ parent::MakeFormField($oObject, $oFormField);
+
+ return $oFormField;
+ }
+
+ public function GetEditValue($value, $oHostObj = null)
+ {
+ if (is_null($value))
+ {
+ return '';
+ }
+ else
+ {
+ return $this->GetValueLabel($value);
+ }
+ }
+
/**
* Helper to get a value that will be JSON encoded
- * The operation is the opposite to FromJSONToValue
- */
+ * The operation is the opposite to FromJSONToValue
+ */
public function GetForJSON($value)
{
- return $value ? '1' : '0';
+ return (bool)$value;
+ }
+
+ public function MakeValueFromString($sProposedValue, $bLocalizedValue = false, $sSepItem = null, $sSepAttribute = null, $sSepValue = null, $sAttributeQualifier = null)
+ {
+ $sInput = strtolower(trim($sProposedValue));
+ if ($bLocalizedValue)
+ {
+ switch ($sInput)
+ {
+ case '1': // backward compatibility
+ case $this->GetValueLabel(true):
+ $value = true;
+ break;
+ case '0': // backward compatibility
+ case 'no':
+ case $this->GetValueLabel(false):
+ $value = false;
+ break;
+ default:
+ $value = null;
+ }
+ }
+ else
+ {
+ switch ($sInput)
+ {
+ case '1': // backward compatibility
+ case 'yes':
+ $value = true;
+ break;
+ case '0': // backward compatibility
+ case 'no':
+ $value = false;
+ break;
+ default:
+ $value = null;
+ }
+ }
+ return $value;
}
}
@@ -2088,6 +2234,10 @@ class AttributeFinalClass extends AttributeString
{
return false;
}
+ public function IsMagic()
+ {
+ return true;
+ }
public function RequiresIndex()
{
@@ -4312,10 +4462,10 @@ class AttributeExternalKey extends AttributeDBFieldVoid
}
}
- public function GetAllowedValuesAsObjectSet($aArgs = array(), $sContains = '')
+ public function GetAllowedValuesAsObjectSet($aArgs = array(), $sContains = '', $iAdditionalValue = null)
{
$oValSetDef = $this->GetValuesDef();
- $oSet = $oValSetDef->ToObjectSet($aArgs, $sContains);
+ $oSet = $oValSetDef->ToObjectSet($aArgs, $sContains, $iAdditionalValue);
return $oSet;
}
@@ -4535,7 +4685,7 @@ class AttributeHierarchicalKey extends AttributeExternalKey
}
}
- public function GetAllowedValuesAsObjectSet($aArgs = array(), $sContains = '')
+ public function GetAllowedValuesAsObjectSet($aArgs = array(), $sContains = '', $iAdditionalValue = null)
{
$oValSetDef = $this->GetValuesDef();
if (array_key_exists('this', $aArgs))
@@ -4551,7 +4701,7 @@ class AttributeHierarchicalKey extends AttributeExternalKey
$oValSetDef->AddCondition($oFilter);
}
}
- $oSet = $oValSetDef->ToObjectSet($aArgs, $sContains);
+ $oSet = $oValSetDef->ToObjectSet($aArgs, $sContains, $iAdditionalValue);
return $oSet;
}
@@ -5265,7 +5415,7 @@ class AttributeStopWatch extends AttributeDefinition
public function IsDirectField() {return true;}
public function IsScalar() {return true;}
- public function IsWritable() {return false;}
+ public function IsWritable() {return true;}
public function GetDefaultValue(DBObject $oHostObject = null) {return $this->NewStopWatch();}
public function GetEditValue($value, $oHostObj = null)
@@ -6630,6 +6780,10 @@ class AttributeFriendlyName extends AttributeComputedFieldVoid
{
return false;
}
+ public function IsMagic()
+ {
+ return true;
+ }
public function IsDirectField()
{
@@ -7440,3 +7594,26 @@ class AttributeCustomFields extends AttributeDefinition
}
}
+class AttributeArchiveFlag extends AttributeBoolean
+{
+ public function __construct($sCode)
+ {
+ parent::__construct($sCode, array("allowed_values" => null, "sql" => $sCode, "default_value" => false, "is_null_allowed" => false, "depends_on" => array()));
+ }
+ public function RequiresIndex()
+ {
+ return true;
+ }
+ public function CopyOnAllTables()
+ {
+ return true;
+ }
+ public function IsWritable()
+ {
+ return false;
+ }
+ public function IsMagic()
+ {
+ return true;
+ }
+}
diff --git a/core/cmdbobject.class.inc.php b/core/cmdbobject.class.inc.php
index 0fcdf4f0c..6c42472aa 100644
--- a/core/cmdbobject.class.inc.php
+++ b/core/cmdbobject.class.inc.php
@@ -1,5 +1,5 @@
DBInsertNoReload();
}
+ /**
+ * @param $sAttCode
+ * @param $original Original value
+ * @param $value Current value
+ */
+ protected function RecordAttChange($sAttCode, $original, $value)
+ {
+ $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
+ if ($oAttDef->IsExternalField()) return;
+ if ($oAttDef->IsLinkSet()) return;
+ if ($oAttDef->GetTrackingLevel() == ATTRIBUTE_TRACKING_NONE) return;
+
+ if ($oAttDef instanceOf AttributeOneWayPassword)
+ {
+ // One Way encrypted passwords' history is stored -one way- encrypted
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeOneWayPassword");
+ $oMyChangeOp->Set("objclass", get_class($this));
+ $oMyChangeOp->Set("objkey", $this->GetKey());
+ $oMyChangeOp->Set("attcode", $sAttCode);
+
+ if (is_null($original))
+ {
+ $original = '';
+ }
+ $oMyChangeOp->Set("prev_pwd", $original);
+ $iId = $oMyChangeOp->DBInsertNoReload();
+ }
+ elseif ($oAttDef instanceOf AttributeEncryptedString)
+ {
+ // Encrypted string history is stored encrypted
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeEncrypted");
+ $oMyChangeOp->Set("objclass", get_class($this));
+ $oMyChangeOp->Set("objkey", $this->GetKey());
+ $oMyChangeOp->Set("attcode", $sAttCode);
+
+ if (is_null($original))
+ {
+ $original = '';
+ }
+ $oMyChangeOp->Set("prevstring", $original);
+ $iId = $oMyChangeOp->DBInsertNoReload();
+ }
+ elseif ($oAttDef instanceOf AttributeBlob)
+ {
+ // Data blobs
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeBlob");
+ $oMyChangeOp->Set("objclass", get_class($this));
+ $oMyChangeOp->Set("objkey", $this->GetKey());
+ $oMyChangeOp->Set("attcode", $sAttCode);
+
+ if (is_null($original))
+ {
+ $original = new ormDocument();
+ }
+ $oMyChangeOp->Set("prevdata", $original);
+ $iId = $oMyChangeOp->DBInsertNoReload();
+ }
+ elseif ($oAttDef instanceOf AttributeStopWatch)
+ {
+ // Stop watches - record changes for sub items only (they are visible, the rest is not visible)
+ //
+ foreach ($oAttDef->ListSubItems() as $sSubItemAttCode => $oSubItemAttDef)
+ {
+ $item_value = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $value, $this);
+ $item_original = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $original, $this);
+
+ if ($item_value != $item_original)
+ {
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
+ $oMyChangeOp->Set("objclass", get_class($this));
+ $oMyChangeOp->Set("objkey", $this->GetKey());
+ $oMyChangeOp->Set("attcode", $sSubItemAttCode);
+
+ $oMyChangeOp->Set("oldvalue", $item_original);
+ $oMyChangeOp->Set("newvalue", $item_value);
+ $iId = $oMyChangeOp->DBInsertNoReload();
+ }
+ }
+ }
+ elseif ($oAttDef instanceOf AttributeCaseLog)
+ {
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCaseLog");
+ $oMyChangeOp->Set("objclass", get_class($this));
+ $oMyChangeOp->Set("objkey", $this->GetKey());
+ $oMyChangeOp->Set("attcode", $sAttCode);
+
+ $oMyChangeOp->Set("lastentry", $value->GetLatestEntryIndex());
+ $iId = $oMyChangeOp->DBInsertNoReload();
+ }
+ elseif ($oAttDef instanceOf AttributeLongText)
+ {
+ // Data blobs
+ if ($oAttDef->GetFormat() == 'html')
+ {
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
+ }
+ else
+ {
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeLongText");
+ }
+ $oMyChangeOp->Set("objclass", get_class($this));
+ $oMyChangeOp->Set("objkey", $this->GetKey());
+ $oMyChangeOp->Set("attcode", $sAttCode);
+
+ if (!is_null($original) && ($original instanceof ormCaseLog))
+ {
+ $original = $original->GetText();
+ }
+ $oMyChangeOp->Set("prevdata", $original);
+ $iId = $oMyChangeOp->DBInsertNoReload();
+ }
+ elseif ($oAttDef instanceOf AttributeText)
+ {
+ // Data blobs
+ if ($oAttDef->GetFormat() == 'html')
+ {
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
+ }
+ else
+ {
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeText");
+ }
+ $oMyChangeOp->Set("objclass", get_class($this));
+ $oMyChangeOp->Set("objkey", $this->GetKey());
+ $oMyChangeOp->Set("attcode", $sAttCode);
+
+ if (!is_null($original) && ($original instanceof ormCaseLog))
+ {
+ $original = $original->GetText();
+ }
+ $oMyChangeOp->Set("prevdata", $original);
+ $iId = $oMyChangeOp->DBInsertNoReload();
+ }
+ elseif ($oAttDef instanceOf AttributeBoolean)
+ {
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
+ $oMyChangeOp->Set("objclass", get_class($this));
+ $oMyChangeOp->Set("objkey", $this->GetKey());
+ $oMyChangeOp->Set("attcode", $sAttCode);
+ $oMyChangeOp->Set("oldvalue", $original ? 1 : 0);
+ $oMyChangeOp->Set("newvalue", $value ? 1 : 0);
+ $iId = $oMyChangeOp->DBInsertNoReload();
+ }
+ elseif ($oAttDef instanceOf AttributeHierarchicalKey)
+ {
+ // Hierarchical keys
+ //
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
+ $oMyChangeOp->Set("objclass", get_class($this));
+ $oMyChangeOp->Set("objkey", $this->GetKey());
+ $oMyChangeOp->Set("attcode", $sAttCode);
+ $oMyChangeOp->Set("oldvalue", $original);
+ $oMyChangeOp->Set("newvalue", $value[$sAttCode]);
+ $iId = $oMyChangeOp->DBInsertNoReload();
+ }
+ elseif ($oAttDef instanceOf AttributeCustomFields)
+ {
+ // Custom fields
+ //
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCustomFields");
+ $oMyChangeOp->Set("objclass", get_class($this));
+ $oMyChangeOp->Set("objkey", $this->GetKey());
+ $oMyChangeOp->Set("attcode", $sAttCode);
+ $oMyChangeOp->Set("prevdata", json_encode($original->GetValues()));
+ $iId = $oMyChangeOp->DBInsertNoReload();
+ }
+ elseif ($oAttDef instanceOf AttributeURL)
+ {
+ // URLs
+ //
+ $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeURL");
+ $oMyChangeOp->Set("objclass", get_class($this));
+ $oMyChangeOp->Set("objkey", $this->GetKey());
+ $oMyChangeOp->Set("attcode", $sAttCode);
+ $oMyChangeOp->Set("oldvalue", $original);
+ $oMyChangeOp->Set("newvalue", $value);
+ $iId = $oMyChangeOp->DBInsertNoReload();
+ }
+ else
+ {
+ // Scalars
+ //
+ $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);
+ $iId = $oMyChangeOp->DBInsertNoReload();
+ }
+ }
+
+ /**
+ * @param array $aValues
+ * @param array $aOrigValues
+ */
protected function RecordAttChanges(array $aValues, array $aOrigValues)
{
parent::RecordAttChanges($aValues, $aOrigValues);
@@ -239,11 +435,6 @@ abstract class CMDBObject extends DBObject
//
foreach ($aValues as $sAttCode=> $value)
{
- $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
- if ($oAttDef->IsExternalField()) continue;
- if ($oAttDef->IsLinkSet()) continue;
- if ($oAttDef->GetTrackingLevel() == ATTRIBUTE_TRACKING_NONE) continue;
-
if (array_key_exists($sAttCode, $aOrigValues))
{
$original = $aOrigValues[$sAttCode];
@@ -252,185 +443,7 @@ abstract class CMDBObject extends DBObject
{
$original = null;
}
-
- if ($oAttDef instanceOf AttributeOneWayPassword)
- {
- // One Way encrypted passwords' history is stored -one way- encrypted
- $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeOneWayPassword");
- $oMyChangeOp->Set("objclass", get_class($this));
- $oMyChangeOp->Set("objkey", $this->GetKey());
- $oMyChangeOp->Set("attcode", $sAttCode);
-
- if (is_null($original))
- {
- $original = '';
- }
- $oMyChangeOp->Set("prev_pwd", $original);
- $iId = $oMyChangeOp->DBInsertNoReload();
- }
- elseif ($oAttDef instanceOf AttributeEncryptedString)
- {
- // Encrypted string history is stored encrypted
- $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeEncrypted");
- $oMyChangeOp->Set("objclass", get_class($this));
- $oMyChangeOp->Set("objkey", $this->GetKey());
- $oMyChangeOp->Set("attcode", $sAttCode);
-
- if (is_null($original))
- {
- $original = '';
- }
- $oMyChangeOp->Set("prevstring", $original);
- $iId = $oMyChangeOp->DBInsertNoReload();
- }
- elseif ($oAttDef instanceOf AttributeBlob)
- {
- // Data blobs
- $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeBlob");
- $oMyChangeOp->Set("objclass", get_class($this));
- $oMyChangeOp->Set("objkey", $this->GetKey());
- $oMyChangeOp->Set("attcode", $sAttCode);
-
- if (is_null($original))
- {
- $original = new ormDocument();
- }
- $oMyChangeOp->Set("prevdata", $original);
- $iId = $oMyChangeOp->DBInsertNoReload();
- }
- elseif ($oAttDef instanceOf AttributeStopWatch)
- {
- // Stop watches - record changes for sub items only (they are visible, the rest is not visible)
- //
- foreach ($oAttDef->ListSubItems() as $sSubItemAttCode => $oSubItemAttDef)
- {
- $item_value = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $value, $this);
- $item_original = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $original, $this);
-
- if ($item_value != $item_original)
- {
- $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
- $oMyChangeOp->Set("objclass", get_class($this));
- $oMyChangeOp->Set("objkey", $this->GetKey());
- $oMyChangeOp->Set("attcode", $sSubItemAttCode);
-
- $oMyChangeOp->Set("oldvalue", $item_original);
- $oMyChangeOp->Set("newvalue", $item_value);
- $iId = $oMyChangeOp->DBInsertNoReload();
- }
- }
- }
- elseif ($oAttDef instanceOf AttributeCaseLog)
- {
- $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCaseLog");
- $oMyChangeOp->Set("objclass", get_class($this));
- $oMyChangeOp->Set("objkey", $this->GetKey());
- $oMyChangeOp->Set("attcode", $sAttCode);
-
- $oMyChangeOp->Set("lastentry", $value->GetLatestEntryIndex());
- $iId = $oMyChangeOp->DBInsertNoReload();
- }
- elseif ($oAttDef instanceOf AttributeLongText)
- {
- // Data blobs
- if ($oAttDef->GetFormat() == 'html')
- {
- $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
- }
- else
- {
- $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeLongText");
- }
- $oMyChangeOp->Set("objclass", get_class($this));
- $oMyChangeOp->Set("objkey", $this->GetKey());
- $oMyChangeOp->Set("attcode", $sAttCode);
-
- if (!is_null($original) && ($original instanceof ormCaseLog))
- {
- $original = $original->GetText();
- }
- $oMyChangeOp->Set("prevdata", $original);
- $iId = $oMyChangeOp->DBInsertNoReload();
- }
- elseif ($oAttDef instanceOf AttributeText)
- {
- // Data blobs
- if ($oAttDef->GetFormat() == 'html')
- {
- $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
- }
- else
- {
- $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeText");
- }
- $oMyChangeOp->Set("objclass", get_class($this));
- $oMyChangeOp->Set("objkey", $this->GetKey());
- $oMyChangeOp->Set("attcode", $sAttCode);
-
- if (!is_null($original) && ($original instanceof ormCaseLog))
- {
- $original = $original->GetText();
- }
- $oMyChangeOp->Set("prevdata", $original);
- $iId = $oMyChangeOp->DBInsertNoReload();
- }
- elseif ($oAttDef instanceOf AttributeBoolean)
- {
- $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
- $oMyChangeOp->Set("objclass", get_class($this));
- $oMyChangeOp->Set("objkey", $this->GetKey());
- $oMyChangeOp->Set("attcode", $sAttCode);
- $oMyChangeOp->Set("oldvalue", $original ? 1 : 0);
- $oMyChangeOp->Set("newvalue", $value ? 1 : 0);
- $iId = $oMyChangeOp->DBInsertNoReload();
- }
- elseif ($oAttDef instanceOf AttributeHierarchicalKey)
- {
- // Hierarchical keys
- //
- $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
- $oMyChangeOp->Set("objclass", get_class($this));
- $oMyChangeOp->Set("objkey", $this->GetKey());
- $oMyChangeOp->Set("attcode", $sAttCode);
- $oMyChangeOp->Set("oldvalue", $original);
- $oMyChangeOp->Set("newvalue", $value[$sAttCode]);
- $iId = $oMyChangeOp->DBInsertNoReload();
- }
- elseif ($oAttDef instanceOf AttributeCustomFields)
- {
- // Custom fields
- //
- $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCustomFields");
- $oMyChangeOp->Set("objclass", get_class($this));
- $oMyChangeOp->Set("objkey", $this->GetKey());
- $oMyChangeOp->Set("attcode", $sAttCode);
- $oMyChangeOp->Set("prevdata", json_encode($original->GetValues()));
- $iId = $oMyChangeOp->DBInsertNoReload();
- }
- elseif ($oAttDef instanceOf AttributeURL)
- {
- // URLs
- //
- $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeURL");
- $oMyChangeOp->Set("objclass", get_class($this));
- $oMyChangeOp->Set("objkey", $this->GetKey());
- $oMyChangeOp->Set("attcode", $sAttCode);
- $oMyChangeOp->Set("oldvalue", $original);
- $oMyChangeOp->Set("newvalue", $value);
- $iId = $oMyChangeOp->DBInsertNoReload();
- }
- else
- {
- // Scalars
- //
- $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);
- $iId = $oMyChangeOp->DBInsertNoReload();
- }
+ $this->RecordAttChange($sAttCode, $original, $value);
}
}
@@ -596,6 +609,30 @@ abstract class CMDBObject extends DBObject
}
return $ret;
}
+
+ public function DBArchive()
+ {
+ // Note: do the job anyway, so as to repair any DB discrepancy
+ $bOriginal = $this->Get('archive_flag');
+ parent::DBArchive();
+
+ if (!$bOriginal)
+ {
+ $this->RecordAttChange('archive_flag', false, true);
+ }
+ }
+
+ public function DBUnarchive()
+ {
+ // Note: do the job anyway, so as to repair any DB discrepancy
+ $bOriginal = $this->Get('archive_flag');
+ parent::DBUnarchive();
+
+ if ($bOriginal)
+ {
+ $this->RecordAttChange('archive_flag', true, false);
+ }
+ }
}
@@ -649,5 +686,5 @@ class CMDBObjectSet extends DBObjectSet
$oRetSet->AddObjectExtended($aObjectsByClassAlias);
}
return $oRetSet;
- }
+ }
}
diff --git a/core/dbobject.class.php b/core/dbobject.class.php
index c9dc7b6f5..2c70cf8be 100644
--- a/core/dbobject.class.php
+++ b/core/dbobject.class.php
@@ -226,6 +226,18 @@ abstract class DBObject implements iDisplay
$oLinkSearch = new DBObjectSearch($sLinkClass);
$oLinkSearch->AddCondition_PointingTo($oMyselfSearch, $sExtKeyToMe);
+ if ($oAttDef->IsIndirect())
+ {
+ // Join the remote class so that the archive flag will be taken into account
+ $sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
+ $oExtKeyToRemote = MetaModel::GetAttributeDef($sLinkClass, $sExtKeyToRemote);
+ $sRemoteClass = $oExtKeyToRemote->GetTargetClass();
+ if (MetaModel::IsArchivable($sRemoteClass))
+ {
+ $oRemoteSearch = new DBObjectSearch($sRemoteClass);
+ $oLinkSearch->AddCondition_PointingTo($oRemoteSearch, $oAttDef->GetExtKeyToRemote());
+ }
+ }
$oLinks = new DBObjectSet($oLinkSearch);
$this->m_aCurrValues[$sAttCode] = $oLinks;
@@ -360,8 +372,15 @@ abstract class DBObject implements iDisplay
// Ignore it - this attribute is set upon object creation and that's it
return false;
}
-
+
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
+
+ if (!$oAttDef->IsWritable())
+ {
+ $sClass = get_class($this);
+ throw new Exception("Attempting to set the value on the read-only attribute $sClass::$sAttCode");
+ }
+
if ($this->m_bIsInDB && !$this->m_bFullyLoaded && !$this->m_bDirty)
{
// First time Set is called... ensure that the object gets fully loaded
@@ -742,7 +761,8 @@ abstract class DBObject implements iDisplay
else
{
$sHtmlLabel = htmlentities($this->Get($sAttCode.'_friendlyname'), ENT_QUOTES, 'UTF-8');
- return $this->MakeHyperLink($sTargetClass, $iTargetKey, $sHtmlLabel);
+ $bArchived = $this->IsArchived($sAttCode);
+ return $this->MakeHyperLink($sTargetClass, $iTargetKey, $sHtmlLabel, null, true, $bArchived);
}
}
@@ -821,10 +841,11 @@ abstract class DBObject implements iDisplay
* @param string $sHtmlLabel Label with HTML entities escaped (< escaped as <)
* @param null $sUrlMakerClass
* @param bool|true $bWithNavigationContext
+ * @param bool|false $bArchived
* @return string
* @throws DictExceptionMissingString
*/
- public static function MakeHyperLink($sObjClass, $sObjKey, $sHtmlLabel = '', $sUrlMakerClass = null, $bWithNavigationContext = true)
+ public static function MakeHyperLink($sObjClass, $sObjKey, $sHtmlLabel = '', $sUrlMakerClass = null, $bWithNavigationContext = true, $bArchived = false)
{
if ($sObjKey <= 0) return ''.Dict::S('UI:UndefinedObject').''; // Objects built in memory have negative IDs
@@ -847,19 +868,51 @@ abstract class DBObject implements iDisplay
}
$sHint = MetaModel::GetName($sObjClass)."::$sObjKey";
$sUrl = ApplicationContext::MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass, $bWithNavigationContext);
- if (strlen($sUrl) > 0)
+
+ $bClickable = !$bArchived || utils::IsArchiveMode();
+ if ($bArchived)
{
- return "$sHtmlLabel";
+ $sSpanClass = 'archived';
+ $sFA = 'fa-archive object-archived';
+ $sHint = Dict::S('ObjectRef:Archived');
}
else
{
- return $sHtmlLabel;
+ $sSpanClass = '';
+ $sFA = '';
}
+ if ($sFA == '')
+ {
+ $sIcon = '';
+ }
+ else
+ {
+ if ($bClickable)
+ {
+ $sIcon = "";
+ }
+ else
+ {
+ $sIcon = "";
+ }
+ }
+
+ if ($bClickable && (strlen($sUrl) > 0))
+ {
+ $sHLink = "$sIcon$sHtmlLabel";
+ }
+ else
+ {
+ $sHLink = $sIcon.$sHtmlLabel;
+ }
+ $sRet = "$sHLink";
+ return $sRet;
}
public function GetHyperlink($sUrlMakerClass = null, $bWithNavigationContext = true)
{
- return self::MakeHyperLink(get_class($this), $this->GetKey(), $this->GetName(), $sUrlMakerClass, $bWithNavigationContext);
+ $bArchived = $this->IsArchived();
+ return self::MakeHyperLink(get_class($this), $this->GetKey(), $this->GetName(), $sUrlMakerClass, $bWithNavigationContext, $bArchived);
}
public static function ComputeStandardUIPage($sClass)
@@ -1232,7 +1285,7 @@ abstract class DBObject implements iDisplay
$aChanges = $this->ListChanges();
- foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
+ foreach($aChanges as $sAttCode => $value)
{
$res = $this->CheckValue($sAttCode);
if ($res !== true)
@@ -3544,5 +3597,83 @@ abstract class DBObject implements iDisplay
throw new Exception("Invalid verb");
}
}
+
+ public function IsArchived($sKeyAttCode = null)
+ {
+ $bRet = false;
+ $sFlagAttCode = is_null($sKeyAttCode) ? 'archive_flag' : $sKeyAttCode.'_archive_flag';
+ if (MetaModel::IsValidAttCode(get_class($this), $sFlagAttCode) && $this->Get($sFlagAttCode))
+ {
+ $bRet = true;
+ }
+ return $bRet;
+ }
+
+ /**
+ * @param $bArchive
+ * @throws Exception
+ */
+ protected function DBWriteArchiveFlag($bArchive)
+ {
+ if (!MetaModel::IsArchivable(get_class($this)))
+ {
+ throw new Exception(get_class($this).' is not an archivable class');
+ }
+
+ $iFlag = $bArchive ? 1 : 0;
+ $sDate = $bArchive ? '"'.date(AttributeDate::GetSQLFormat()).'"' : 'null';
+
+ $sClass = get_class($this);
+ $sArchiveRoot = MetaModel::GetAttributeOrigin($sClass, 'archive_flag');
+ $sRootTable = MetaModel::DBGetTable($sArchiveRoot);
+ $sRootKey = MetaModel::DBGetKey($sArchiveRoot);
+ $aJoins = array("`$sRootTable`");
+ $aUpdates = array();
+ foreach (MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL) as $sParentClass)
+ {
+ if (!MetaModel::IsValidAttCode($sParentClass, 'archive_flag')) continue;
+
+ $sTable = MetaModel::DBGetTable($sParentClass);
+ $aUpdates[] = "`$sTable`.`archive_flag` = $iFlag";
+ if ($sParentClass == $sArchiveRoot)
+ {
+ if (!$bArchive || $this->Get('archive_date') == '')
+ {
+ // Erase or set the date (do not change it)
+ $aUpdates[] = "`$sTable`.`archive_date` = $sDate";
+ }
+ }
+ else
+ {
+ $sKey = MetaModel::DBGetKey($sParentClass);
+ $aJoins[] = "`$sTable` ON `$sTable`.`$sKey` = `$sRootTable`.`$sRootKey`";
+ }
+ }
+ $sJoins = implode(' INNER JOIN ', $aJoins);
+ $sValues = implode(', ', $aUpdates);
+ $sUpdateQuery = "UPDATE $sJoins SET $sValues WHERE `$sRootTable`.`$sRootKey` = ".$this->GetKey();
+ CMDBSource::Query($sUpdateQuery);
+ }
+
+ /**
+ * Can be called to repair the database (tables consistency)
+ * The archive_date will be preserved
+ * @throws Exception
+ */
+ public function DBArchive()
+ {
+ $this->DBWriteArchiveFlag(true);
+ $this->m_aCurrValues['archive_flag'] = true;
+ $this->m_aOrigValues['archive_flag'] = true;
+ }
+
+ public function DBUnarchive()
+ {
+ $this->DBWriteArchiveFlag(false);
+ $this->m_aCurrValues['archive_flag'] = false;
+ $this->m_aOrigValues['archive_flag'] = false;
+ $this->m_aCurrValues['archive_date'] = null;
+ $this->m_aOrigValues['archive_date'] = null;
+ }
}
diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php
index adf662445..16edcd763 100644
--- a/core/dbobjectsearch.class.php
+++ b/core/dbobjectsearch.class.php
@@ -1467,6 +1467,7 @@ class DBObjectSearch extends DBSearch
{
$sRawId .= implode(',', $aSelectedClasses); // Unions may alter the list of selected columns
}
+ $sRawId .= $oSearch->GetArchiveMode() ? '--arch' : '';
$sOqlId = md5($sRawId);
}
else
@@ -1552,6 +1553,7 @@ class DBObjectSearch extends DBSearch
$oSQLQuery->SetSelect($oBuild->m_oQBExpressions->GetSelect());
}
+ $aMandatoryTables = null;
if (self::$m_bOptimizeQueries)
{
if ($bGetCount)
@@ -1562,6 +1564,17 @@ class DBObjectSearch extends DBSearch
$oBuild->m_oQBExpressions->GetMandatoryTables($aMandatoryTables);
$oSQLQuery->OptimizeJoins($aMandatoryTables);
}
+ // Filter tables as late as possible: do not interfere with the optimization process
+ foreach ($oBuild->GetFilteredTables() as $sTableAlias => $aConditions)
+ {
+ if ($aMandatoryTables && array_key_exists($sTableAlias, $aMandatoryTables))
+ {
+ foreach ($aConditions as $oCondition)
+ {
+ $oSQLQuery->AddCondition($oCondition);
+ }
+ }
+ }
return $oSQLQuery;
}
@@ -1869,7 +1882,7 @@ class DBObjectSearch extends DBSearch
$oBuild->m_oQBExpressions->GetUnresolvedFields($sTargetAlias, $aExpectedAtts);
$bIsOnQueriedClass = array_key_exists($sTargetAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
-
+
self::DbgTrace("Entering: tableclass=$sTableClass, filter=".$this->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
// 1 - SELECT and UPDATE
@@ -2098,6 +2111,24 @@ class DBObjectSearch extends DBSearch
$oBuild->m_oQBExpressions->Translate($aTranslation, false);
//echo "oQBExpr ".__LINE__.":
\n".print_r($oBuild->m_oQBExpressions, true)."\n"; + // Filter out archived records + // + if (MetaModel::IsArchivable($sTableClass)) + { + if (!$oBuild->GetRootFilter()->GetArchiveMode()) + { + $bIsOnJoinedClass = array_key_exists($sTargetAlias, $oBuild->GetRootFilter()->GetJoinedClasses()); + //$bIsOnJoinedClass = true; + if ($bIsOnJoinedClass) + { + if (MetaModel::IsParentClass($sTableClass, $sTargetClass)) + { + $oNotArchived = new BinaryExpression(new FieldExpressionResolved('archive_flag', $sTableAlias), '=', new ScalarExpression(0)); + $oBuild->AddFilteredTable($sTableAlias, $oNotArchived); + } + } + } + } //MyHelpers::var_dump_html($oSelectBase->RenderSelect()); return $oSelectBase; } diff --git a/core/dbsearch.class.php b/core/dbsearch.class.php index 0e3969126..7c78b1ba3 100644 --- a/core/dbsearch.class.php +++ b/core/dbsearch.class.php @@ -1,5 +1,5 @@ m_bArchiveMode = self::GetArchiveModeDefault(); } /** @@ -60,6 +62,25 @@ abstract class DBSearch abstract public function AllowAllData(); abstract public function IsAllDataAllowed(); + static $bArchiveModeDefault = false; + static public function SetArchiveModeDefault($bEnable) + { + self::$bArchiveModeDefault = $bEnable; + } + static public function GetArchiveModeDefault() + { + return self::$bArchiveModeDefault; + } + + public function SetArchiveMode($bEnable) + { + $this->m_bArchiveMode = $bEnable; + } + public function GetArchiveMode() + { + return $this->m_bArchiveMode; + } + public function NoContextParameters() {$this->m_bNoContextParameters = true;} public function HasContextParameters() {return $this->m_bNoContextParameters;} diff --git a/core/metamodel.class.php b/core/metamodel.class.php index e21fa7c34..c24d753b9 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -319,6 +319,11 @@ abstract class MetaModel self::_check_subclass($sClass); return (self::$m_aClassParams[$sClass]["key_type"] == "autoincrement"); } + final static public function IsArchivable($sClass) + { + self::_check_subclass($sClass); + return self::$m_aClassParams[$sClass]["archive"]; + } final static public function GetNameSpec($sClass) { self::_check_subclass($sClass); @@ -559,12 +564,13 @@ abstract class MetaModel self::_check_subclass($sClass); if (isset(self::$m_aClassParams[$sClass]['indexes'])) { - return self::$m_aClassParams[$sClass]['indexes']; + $aRet = self::$m_aClassParams[$sClass]['indexes']; } else { - return array(); + $aRet = array(); } + return $aRet; } final static public function DBGetKey($sClass) @@ -1639,10 +1645,10 @@ abstract class MetaModel return $oFltDef->GetAllowedValues($aArgs, $sContains); } - public static function GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs = array(), $sContains = '') + public static function GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs = array(), $sContains = '', $iAdditionalValue = null) { $oAttDef = self::GetAttributeDef($sClass, $sAttCode); - return $oAttDef->GetAllowedValuesAsObjectSet($aArgs, $sContains); + return $oAttDef->GetAllowedValuesAsObjectSet($aArgs, $sContains, $iAdditionalValue); } // // Businezz model declaration verbs (should be static) @@ -1672,6 +1678,24 @@ abstract class MetaModel self::$m_aRelationInfos[$sRelCode] = $sRelCode; } + /* + * Helper to correctly add a magic attribute (called from InitClasses) + */ + private static function AddMagicAttribute(AttributeDefinition $oAttribute, $sTargetClass, $sOriginClass = null) + { + $sCode = $oAttribute->GetCode(); + if (is_null($sOriginClass)) + { + $sOriginClass = $sTargetClass; + } + $oAttribute->SetHostClass($sTargetClass); + self::$m_aAttribDefs[$sTargetClass][$sCode] = $oAttribute; + self::$m_aAttribOrigins[$sTargetClass][$sCode] = $sOriginClass; + + $oFlt = new FilterFromAttribute($oAttribute); + self::$m_aFilterDefs[$sTargetClass][$sCode] = $oFlt; + self::$m_aFilterOrigins[$sTargetClass][$sCode] = $sOriginClass; + } // Must be called once and only once... public static function InitClasses($sTablePrefix) { @@ -1726,6 +1750,23 @@ abstract class MetaModel if ($oMethod->getDeclaringClass()->name == $sPHPClass) { call_user_func(array($sPHPClass, 'Init')); + + // Inherit archive flag + $bParentArchivable = isset(self::$m_aClassParams[$sParent]['archive']) ? self::$m_aClassParams[$sParent]['archive'] : false; + $bArchivable = isset(self::$m_aClassParams[$sPHPClass]['archive']) ? self::$m_aClassParams[$sPHPClass]['archive'] : null; + if ($bParentArchivable && ($bArchivable === false)) + { + throw new Exception("$sPHPClass must be archivable (consistency throughout the whole class tree is a must)"); + } + $bReallyArchivable = $bParentArchivable || $bArchivable; + self::$m_aClassParams[$sPHPClass]['archive'] = $bReallyArchivable; + $bArchiveRoot = $bReallyArchivable && !$bParentArchivable; + self::$m_aClassParams[$sPHPClass]['archive_root'] = $bArchiveRoot; + if ($bReallyArchivable) + { + self::$m_aClassParams[$sPHPClass]['archive_root_class'] = $bArchiveRoot ? $sPHPClass : self::$m_aClassParams[$sParent]['archive_root_class']; + } + foreach (MetaModel::EnumPlugins('iOnClassInitialization') as $sPluginClass => $oClassInit) { $oClassInit->OnAfterClassInitialization($sPHPClass); @@ -1757,13 +1798,7 @@ abstract class MetaModel "is_null_allowed"=>false, "depends_on"=>array() )); - $oClassAtt->SetHostClass($sRootClass); - self::$m_aAttribDefs[$sRootClass]['finalclass'] = $oClassAtt; - self::$m_aAttribOrigins[$sRootClass]['finalclass'] = $sRootClass; - - $oClassFlt = new FilterFromAttribute($oClassAtt); - self::$m_aFilterDefs[$sRootClass]['finalclass'] = $oClassFlt; - self::$m_aFilterOrigins[$sRootClass]['finalclass'] = $sRootClass; + self::AddMagicAttribute($oClassAtt, $sRootClass); foreach(self::EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_EXCLUDETOP) as $sChildClass) { @@ -1776,14 +1811,8 @@ abstract class MetaModel throw new CoreException("Class $sChildClass, 'finalclass' is a reserved keyword, it cannot be used as a filter code"); } $oCloned = clone $oClassAtt; - $oCloned->SetHostClass($sChildClass); $oCloned->SetFixedValue($sChildClass); - self::$m_aAttribDefs[$sChildClass]['finalclass'] = $oCloned; - self::$m_aAttribOrigins[$sChildClass]['finalclass'] = $sRootClass; - - $oClassFlt = new FilterFromAttribute($oClassAtt); - self::$m_aFilterDefs[$sChildClass]['finalclass'] = $oClassFlt; - self::$m_aFilterOrigins[$sChildClass]['finalclass'] = self::GetRootClass($sChildClass); + self::AddMagicAttribute($oCloned, $sChildClass, $sRootClass); } } @@ -1795,13 +1824,30 @@ abstract class MetaModel // Create the friendly name attribute $sFriendlyNameAttCode = 'friendlyname'; $oFriendlyName = new AttributeFriendlyName($sFriendlyNameAttCode, 'id'); - $oFriendlyName->SetHostClass($sClass); - self::$m_aAttribDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyName; - self::$m_aAttribOrigins[$sClass][$sFriendlyNameAttCode] = $sClass; - $oFriendlyNameFlt = new FilterFromAttribute($oFriendlyName); - self::$m_aFilterDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyNameFlt; - self::$m_aFilterOrigins[$sClass][$sFriendlyNameAttCode] = $sClass; + self::AddMagicAttribute($oFriendlyName, $sClass); + if (self::$m_aClassParams[$sClass]["archive_root"]) + { + // Create archive attributes on top the archivable hierarchy + $oArchiveFlag = new AttributeArchiveFlag('archive_flag'); + self::AddMagicAttribute($oArchiveFlag, $sClass); + + $oArchiveDate = new AttributeDate('archive_date', array('magic' => true, "allowed_values"=>null, "sql"=>'archive_date', "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())); + self::AddMagicAttribute($oArchiveDate, $sClass); + } + elseif (self::$m_aClassParams[$sClass]["archive"]) + { + $sArchiveRoot = self::$m_aClassParams[$sClass]['archive_root_class']; + // Inherit archive attributes + $oArchiveFlag = clone self::$m_aAttribDefs[$sArchiveRoot]['archive_flag']; + $oArchiveFlag->SetHostClass($sArchiveRoot); + self::$m_aAttribDefs[$sClass]['archive_flag'] = $oArchiveFlag; + self::$m_aAttribOrigins[$sClass]['archive_flag'] = $sArchiveRoot; + $oArchiveDate = clone self::$m_aAttribDefs[$sArchiveRoot]['archive_date']; + $oArchiveDate->SetHostClass($sArchiveRoot); + self::$m_aAttribDefs[$sClass]['archive_date'] = $oArchiveDate; + self::$m_aAttribOrigins[$sClass]['archive_date'] = $sArchiveRoot; + } self::$m_aExtKeyFriends[$sClass] = array(); foreach (self::$m_aAttribDefs[$sClass] as $sAttCode => $oAttDef) { @@ -1840,26 +1886,15 @@ abstract class MetaModel $sKeyAttCode = $oAttDef->GetKeyAttCode(); $sRemoteAttCode = $oAttDef->GetExtAttCode()."_friendlyname"; $sFriendlyNameAttCode = $sAttCode.'_friendlyname'; - // propagate "is_null_allowed" ? $oFriendlyName = new AttributeExternalField($sFriendlyNameAttCode, array("allowed_values"=>null, "extkey_attcode"=>$sKeyAttCode, "target_attcode"=>$sRemoteAttCode, "depends_on"=>array())); - $oFriendlyName->SetHostClass($sClass); - self::$m_aAttribDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyName; - self::$m_aAttribOrigins[$sClass][$sFriendlyNameAttCode] = self::$m_aAttribOrigins[$sClass][$sKeyAttCode]; - $oFriendlyNameFlt = new FilterFromAttribute($oFriendlyName); - self::$m_aFilterDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyNameFlt; - self::$m_aFilterOrigins[$sClass][$sFriendlyNameAttCode] = self::$m_aFilterOrigins[$sClass][$sKeyAttCode]; + self::AddMagicAttribute($oFriendlyName, $sClass, self::$m_aAttribOrigins[$sClass][$sKeyAttCode]); } else { // Create the friendly name attribute $sFriendlyNameAttCode = $sAttCode.'_friendlyname'; $oFriendlyName = new AttributeFriendlyName($sFriendlyNameAttCode, $sAttCode); - $oFriendlyName->SetHostClass($sClass); - self::$m_aAttribDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyName; - self::$m_aAttribOrigins[$sClass][$sFriendlyNameAttCode] = self::$m_aAttribOrigins[$sClass][$sAttCode]; - $oFriendlyNameFlt = new FilterFromAttribute($oFriendlyName); - self::$m_aFilterDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyNameFlt; - self::$m_aFilterOrigins[$sClass][$sFriendlyNameAttCode] = self::$m_aFilterOrigins[$sClass][$sAttCode]; + self::AddMagicAttribute($oFriendlyName, $sClass, self::$m_aAttribOrigins[$sClass][$sAttCode]); if (self::HasChildrenClasses($sRemoteClass)) { @@ -1872,13 +1907,7 @@ abstract class MetaModel "is_null_allowed"=>true, "depends_on"=>array() )); - $oClassRecall->SetHostClass($sClass); - self::$m_aAttribDefs[$sClass][$sClassRecallAttCode] = $oClassRecall; - self::$m_aAttribOrigins[$sClass][$sClassRecallAttCode] = self::$m_aAttribOrigins[$sClass][$sAttCode]; - - $oClassFlt = new FilterFromAttribute($oClassRecall); - self::$m_aFilterDefs[$sClass][$sClassRecallAttCode] = $oClassFlt; - self::$m_aFilterOrigins[$sClass][$sClassRecallAttCode] = self::$m_aFilterOrigins[$sClass][$sAttCode]; + self::AddMagicAttribute($oClassRecall, $sClass, self::$m_aAttribOrigins[$sClass][$sAttCode]); // Add it to the ZLists where the external key is present //foreach(self::$m_aListData[$sClass] as $sListCode => $aAttributes) @@ -1924,6 +1953,13 @@ abstract class MetaModel self::$m_aExtKeyFriends[$sClass][$sAttCode][$oExtField->GetCode()] = $oExtField; } } + + if (self::IsArchivable($sRemoteClass)) + { + $sArchiveRemote = $sAttCode.'_archive_flag'; + $oArchiveRemote = new AttributeExternalField($sArchiveRemote, array("allowed_values"=>null, "extkey_attcode"=>$sAttCode, "target_attcode"=>'archive_flag', "depends_on"=>array())); + self::AddMagicAttribute($oArchiveRemote, $sClass, self::$m_aAttribOrigins[$sClass][$sAttCode]); + } } if ($oAttDef instanceof AttributeMetaEnum) { @@ -2467,6 +2503,19 @@ abstract class MetaModel } return $aRes; } + public static function EnumArchivableClasses() + { + $aRes = array(); + foreach (self::GetClasses() as $sClass) + { + if (self::IsArchivable($sClass)) + { + $aRes[] = $sClass; + } + } + return $aRes; + } + public static function HasChildrenClasses($sClass) { return (count(self::$m_aChildClasses[$sClass]) > 0); @@ -3401,6 +3450,12 @@ abstract class MetaModel return $aDataDump; } + protected static $m_bReadOnlyMode = false; + public static function DBSetReadOnly() + { + self::$m_bReadOnlyMode = true; + } + /* * Determines wether the target DB is frozen or not */ @@ -3408,6 +3463,10 @@ abstract class MetaModel { // Improvement: check the mySQL variable -> Read-only + if (self::$m_bReadOnlyMode) + { + return true; + } if (UserRights::IsAdministrator()) { return (!self::DBHasAccess(ACCESS_ADMIN_WRITE)); @@ -3620,8 +3679,11 @@ abstract class MetaModel $aTableInfo['Fields'][$sKeyField]['used'] = true; foreach(self::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef) { - // Skip this attribute if not originaly defined in this class - if (self::$m_aAttribOrigins[$sClass][$sAttCode] != $sClass) continue; + if (!$oAttDef->CopyOnAllTables()) + { + // Skip this attribute if not originaly defined in this class + if (self::$m_aAttribOrigins[$sClass][$sAttCode] != $sClass) continue; + } foreach($oAttDef->GetSQLColumns(true) as $sField => $sDBFieldSpec) { // Keep track of columns used by iTop @@ -3725,7 +3787,7 @@ abstract class MetaModel } } } - + // Find out unused columns // foreach($aTableInfo['Fields'] as $sField => $aFieldData) @@ -4538,6 +4600,7 @@ abstract class MetaModel $sModifierProperties = json_encode($aModifierProperties); $sQuerySign .= '_all_'.md5($sModifierProperties); } + $sQuerySign .= DBSearch::GetArchiveModeDefault() ? '_arch_' : ''; if (!array_key_exists($sQuerySign, self::$aQueryCacheGetObject)) { @@ -4628,6 +4691,15 @@ abstract class MetaModel return self::GetObjectByRow($sClass, $aRow); } + public static function GetObjectWithArchive($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null) + { + $bPreviousMode = DBSearch::GetArchiveModeDefault(); + DBSearch::SetArchiveModeDefault(true); + $oObject = static::GetObject($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties); + DBSearch::SetArchiveModeDefault($bPreviousMode); + return $oObject; + } + public static function GetObjectByName($sClass, $sName, $bMustBeFound = true) { self::_check_subclass($sClass); diff --git a/core/oql/expression.class.inc.php b/core/oql/expression.class.inc.php index 7768ec428..b9615181a 100644 --- a/core/oql/expression.class.inc.php +++ b/core/oql/expression.class.inc.php @@ -1,5 +1,5 @@ 0) { - $oObject = MetaModel::GetObject($sObjClass, $iObjKey); + $oObject = MetaModel::GetObjectWithArchive($sObjClass, $iObjKey); $sRes = $oObject->GetHyperlink(); } else @@ -1403,6 +1403,4 @@ class QueryBuilderExpressions $this->m_aJoinFields[$index] = $oExpression->RenameParam($sOldName, $sNewName); } } -} - -?> +} \ No newline at end of file diff --git a/core/querybuildercontext.class.inc.php b/core/querybuildercontext.class.inc.php index fd46b6af2..71f456502 100644 --- a/core/querybuildercontext.class.inc.php +++ b/core/querybuildercontext.class.inc.php @@ -1,5 +1,5 @@ MakeQuery/MakeQuerySingleTable * - * @copyright Copyright (C) 2010-2015 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ @@ -30,6 +30,7 @@ class QueryBuilderContext protected $m_aTableAliases; protected $m_aModifierProperties; protected $m_aSelectedClasses; + protected $m_aFilteredTables; public $m_oQBExpressions; @@ -40,6 +41,7 @@ class QueryBuilderContext $this->m_aClassAliases = $oFilter->GetJoinedClasses(); $this->m_aTableAliases = array(); + $this->m_aFilteredTables = array(); $this->m_aModifierProperties = $aModifierProperties; if (is_null($aSelectedClasses)) @@ -84,4 +86,21 @@ class QueryBuilderContext { return $this->m_aSelectedClasses[$sAlias]; } + + public function AddFilteredTable($sTableAlias, $oCondition) + { + if (array_key_exists($sTableAlias, $this->m_aFilteredTables)) + { + $this->m_aFilteredTables[$sTableAlias][] = $oCondition; + } + else + { + $this->m_aFilteredTables[$sTableAlias] = array($oCondition); + } + } + + public function GetFilteredTables() + { + return $this->m_aFilteredTables; + } } diff --git a/core/sqlobjectquery.class.inc.php b/core/sqlobjectquery.class.inc.php index 6cad9b3f2..66de0ca3d 100644 --- a/core/sqlobjectquery.class.inc.php +++ b/core/sqlobjectquery.class.inc.php @@ -166,7 +166,7 @@ class SQLObjectQuery extends SQLQuery } else { - $this->m_oConditionExpr->LogAnd($oConditionExpr); + $this->m_oConditionExpr = $this->m_oConditionExpr->LogAnd($oConditionExpr); } } diff --git a/core/userrights.class.inc.php b/core/userrights.class.inc.php index 704d75fb7..fba6c6a63 100644 --- a/core/userrights.class.inc.php +++ b/core/userrights.class.inc.php @@ -1,5 +1,5 @@ +UserRights::SelectSelfRegister('CAS_SelfRegister'); \ No newline at end of file diff --git a/core/valuesetdef.class.inc.php b/core/valuesetdef.class.inc.php index 5e1facc68..294a2832b 100644 --- a/core/valuesetdef.class.inc.php +++ b/core/valuesetdef.class.inc.php @@ -1,5 +1,5 @@ m_aExtraConditions[] = $oFilter; } - public function ToObjectSet($aArgs = array(), $sContains = '') + public function ToObjectSet($aArgs = array(), $sContains = '', $iAdditionalValue = null) { if ($this->m_bAllowAllData) { @@ -145,6 +145,14 @@ class ValueSetObjects extends ValueSetDefinition $oFilter->SetModifierProperty($sPluginClass, $sProperty, $value); } } + if ($iAdditionalValue > 0) + { + $oSearchAdditionalValue = new DBObjectSearch($oFilter->GetClass()); + $oSearchAdditionalValue->AddCondition('id', $iAdditionalValue); + $oSearchAdditionalValue->AllowAllData(); + $oSearchAdditionalValue->SetArchiveMode(true); + $oFilter = new DBUnionSearch(array($oFilter, $oSearchAdditionalValue)); + } return new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs); } diff --git a/core/xmlbulkexport.class.inc.php b/core/xmlbulkexport.class.inc.php index 353872295..afa9f9f10 100644 --- a/core/xmlbulkexport.class.inc.php +++ b/core/xmlbulkexport.class.inc.php @@ -1,5 +1,5 @@ IsWritable()) + if ($oAttDef->IsExternalField()) { continue; } @@ -138,7 +138,7 @@ class XMLBulkExport extends BulkExport $aClass2Attributes[$sAlias] = $aAttributes; } } - + $iPreviousTimeLimit = ini_get('max_execution_time'); $iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop'); diff --git a/css/css-variables.scss b/css/css-variables.scss index 2dbe2d379..66b0dd054 100644 --- a/css/css-variables.scss +++ b/css/css-variables.scss @@ -5,4 +5,4 @@ $complement-light: #d6e8ef; $frame-background-color: #F1F1F1; $text-color: #000; // Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0 -$version: "v2.3.0"; \ No newline at end of file +$version: "v2.4.0-alpha"; \ No newline at end of file diff --git a/css/light-grey.css b/css/light-grey.css index 69b383718..66c8bc83a 100644 --- a/css/light-grey.css +++ b/css/light-grey.css @@ -253,8 +253,6 @@ legend.transparent { .ui-widget-content td a, p a, p a:visited, td a, td a:visited { text-decoration: none; color: #1c94c4; - padding-left: 14px; - background: url(../images/mini-arrow-orange.gif) no-repeat left; } .ui-widget-content td a.cke_button, .ui-widget-content td a.cke_toolbox_collapser, .ui-widget-content td a.cke_combo_button, cke_dialog a { padding-left: 0; @@ -292,13 +290,13 @@ td a.mailto, td a.mailto:visited { text-decoration: none; color: #000; padding-left: 20px; - background: url(../images/mail.png?v=v2.3.0) no-repeat left; + background: url(../images/mail.png?v=v2.4.0-alpha) no-repeat left; } td a.mailto:hover { text-decoration: underline; color: #e87c1e; padding-left: 20px; - background: url(../images/mail.png?v=v2.3.0) no-repeat left; + background: url(../images/mail.png?v=v2.4.0-alpha) no-repeat left; } a.small_action { font-family: Tahoma, Verdana, Arial, Helvetica; @@ -316,10 +314,10 @@ a.small_action { padding-left: 5px; padding-top: 2px; padding-bottom: 2px; - background: #e87c1e url(../images/actions_left.png?v=v2.3.0) no-repeat left; + background: #e87c1e url(../images/actions_left.png?v=v2.4.0-alpha) no-repeat left; } .actions_details span { - background: url(../images/actions_right.png?v=v2.3.0) no-repeat right; + background: url(../images/actions_right.png?v=v2.4.0-alpha) no-repeat right; color: #fff; font-weight: bold; padding-top: 2px; @@ -493,7 +491,7 @@ div.actions_menu > ul { nowidth: 70px; padding-left: 5px; /* Nasty work-around for IE... en attendant mieux */ - background: #e87c1e url(../images/actions_left.png?v=v2.3.0) no-repeat top left; + background: #e87c1e url(../images/actions_left.png?v=v2.4.0-alpha) no-repeat top left; cursor: pointer; margin: 0; } @@ -505,7 +503,7 @@ div.actions_menu > ul > li { height: 17px; padding-right: 16px; padding-left: 4px; - background: url(../images/actions_right.png?v=v2.3.0) no-repeat top right transparent; + background: url(../images/actions_right.png?v=v2.4.0-alpha) no-repeat top right transparent; font-weight: bold; color: #fff; vertical-align: middle; @@ -648,7 +646,7 @@ td a.dp-choose-date, a.dp-choose-date, td a.dp-choose-date:hover, a.dp-choose-da display: block; text-indent: -2000px; overflow: hidden; - background: url(../images/calendar.png?v=v2.3.0) no-repeat; + background: url(../images/calendar.png?v=v2.4.0-alpha) no-repeat; } td a.dp-choose-date.dp-disabled, a.dp-choose-date.dp-disabled { background-position: 0 -20px; @@ -739,19 +737,19 @@ div.HRDrawer { } /* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */ table.listResults tr.odd td.truncated, table.listResults tr td.truncated, .wizContainer table.listResults tr.odd td.truncated, .wizContainer table.listResults tr td.truncated { - background: url(../images/truncated.png?v=v2.3.0) bottom repeat-x; + background: url(../images/truncated.png?v=v2.4.0-alpha) bottom repeat-x; } /* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */ table.listResults tr.even td.truncated, .wizContainer table.listResults tr.even td.truncated { - background: #f9f9f1 url(../images/truncated.png?v=v2.3.0) bottom repeat-x; + background: #f9f9f1 url(../images/truncated.png?v=v2.4.0-alpha) bottom repeat-x; } /* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */ table.listResults tr.even td.hover.truncated, .wizContainer table.listResults tr.even td.hover.truncated { - background: #fdf5d0 url(../images/truncated.png?v=v2.3.0) bottom repeat-x; + background: #fdf5d0 url(../images/truncated.png?v=v2.4.0-alpha) bottom repeat-x; } /* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */ table.listResults tr.odd td.hover.truncated, table.listResults tr td.hover.truncated, .wizContainer table.listResults tr.odd td.hover.truncated, .wizContainer table.listResults tr td.hover.truncated { - background: #fdf5d0 url(../images/truncated.png?v=v2.3.0) bottom repeat-x; + background: #fdf5d0 url(../images/truncated.png?v=v2.4.0-alpha) bottom repeat-x; } table.listResults.truncated { border-bottom: 0; @@ -859,7 +857,7 @@ div#logo { div#logo div { height: 88px; width: 244px; - background: url(../images/itop-logo-2.png?v=v2.3.0) left no-repeat; + background: url(../images/itop-logo-2.png?v=v2.4.0-alpha) left no-repeat; } #left-pane .ui-layout-north { overflow: hidden; @@ -868,13 +866,56 @@ div#logo div { background: #f1f1f1; text-align: right; } -#admin-banner { +.app-message { float: left; margin-top: 2px; - padding: 8px; - border: 1px solid #c33; - background-color: #fee; - -moz-border-radius: 0.5em; + margin-right: 4px; + padding: 6px 9px; + background-color: #e87c1e; + color: white; + border-radius: 6px; + text-align: left; +} +.app-message-icon { + margin-right: 5px; +} +.fa-sm { + font-size: 0.66em; +} +.object-details-header { + margin-top: 7px; + margin-bottom: 7px; +} +.object-icon { + display: table-cell; + vertical-align: middle; + margin-left: 10px; + margin-right: 10px; +} +.object-infos { + display: table-cell; + vertical-align: middle; +} +.object-name { + margin-top: 0px; + margin-bottom: 0px; +} +.tags { + margin-top: 5px; +} +.tag { + font-size: 10px; + font-weight: initial; + display: inline-block; + color: white; + background-color: #555; + padding: 3px 6px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.text-danger { + color: red; } #global-search { height: 55px; @@ -908,7 +949,7 @@ div#logo div { } #global-search-image { vertical-align: middle; - background: url(../images/search.png?v=v2.3.0) center center no-repeat; + background: url(../images/search.png?v=v2.4.0-alpha) center center no-repeat; display: inline-block; width: 28px; height: 30px; @@ -937,7 +978,7 @@ span.ui-icon { margin: 0 2px; } .ui-layout-button-pin-down { - background: url(../images/splitter-bkg.png?v=v2.3.0) transparent; + background: url(../images/splitter-bkg.png?v=v2.4.0-alpha) transparent; width: 16px; background-position: -144px -144px; } @@ -1148,7 +1189,7 @@ img.prev, img.first, img.next, img.last { } div.actions_button { float: right; - background: #e87c1e url("../images/actions_left.png?v=v2.3.0") no-repeat scroll left top; + background: #e87c1e url("../images/actions_left.png?v=v2.4.0-alpha") no-repeat scroll left top; padding-left: 5px; margin-top: 0; margin-right: 10px; @@ -1156,7 +1197,7 @@ div.actions_button { vertical-align: middle; } div.actions_button a, .actions_button a:hover, .actions_button a:visited { - background: #e87c1e url(../images/actions_bkg.png?v=v2.3.0) no-repeat scroll right top; + background: #e87c1e url(../images/actions_bkg.png?v=v2.4.0-alpha) no-repeat scroll right top; color: #fff; padding-right: 8px; cursor: pointer; @@ -1180,10 +1221,10 @@ select#org_id { cursor: not-allowed; } .dragHover { - background: url(./ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=v2.3.0); + background: url(./ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=v2.4.0-alpha); } .edit_mode .dashlet { - background: url(./ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=v2.3.0); + background: url(./ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=v2.4.0-alpha); padding: 5px; margin: 0; position: relative; @@ -1215,7 +1256,7 @@ table.prop_table { top: 0; right: 0; z-index: 10; - background: transparent url(../images/delete.png?v=v2.3.0) no-repeat center; + background: transparent url(../images/delete.png?v=v2.4.0-alpha) no-repeat center; } td.prop_value { text-align: left; @@ -1409,17 +1450,17 @@ a.summary, a.summary:hover { } .message_info { border: 1px solid #993; - background: url(../images/info-mini.png?v=v2.3.0) 1em 1em no-repeat #ffc; + background: url(../images/info-mini.png?v=v2.4.0-alpha) 1em 1em no-repeat #ffc; padding-left: 3em; } .message_ok { border: 1px solid #393; - background: url(../images/ok.png?v=v2.3.0) 1em 1em no-repeat #cfc; + background: url(../images/ok.png?v=v2.4.0-alpha) 1em 1em no-repeat #cfc; padding-left: 3em; } .message_error { border: 1px solid #933; - background: url(../images/error.png?v=v2.3.0) 1em 1em no-repeat #fcc; + background: url(../images/error.png?v=v2.4.0-alpha) 1em 1em no-repeat #fcc; padding-left: 3em; } .fg-menu a img { @@ -1547,18 +1588,18 @@ div.explain-printable { } #hiddeable_chapters .ui-tabs .ui-tabs-nav li.hideable-chapter span { padding-left: 20px; - background: url(../images/eye-open-555.png?v=v2.3.0) 2px center no-repeat; + background: url(../images/eye-open-555.png?v=v2.4.0-alpha) 2px center no-repeat; } #hiddeable_chapters .ui-tabs .ui-tabs-nav li.hideable-chapter.strikethrough span { text-decoration: line-through; - background: url(../images/eye-closed-555.png?v=v2.3.0) 2px center no-repeat; + background: url(../images/eye-closed-555.png?v=v2.4.0-alpha) 2px center no-repeat; } .printable-version legend { padding-left: 26px; - background: #1c94c4 url(../images/eye-open-fff.png?v=v2.3.0) 8px center no-repeat; + background: #1c94c4 url(../images/eye-open-fff.png?v=v2.4.0-alpha) 8px center no-repeat; } .printable-version .strikethrough legend { - background: #1c94c4 url(../images/eye-closed-fff.png?v=v2.3.0) 8px center no-repeat; + background: #1c94c4 url(../images/eye-closed-fff.png?v=v2.4.0-alpha) 8px center no-repeat; } .printable-version fieldset.strikethrough span { display: none; @@ -1577,7 +1618,7 @@ span.refresh-button { width: 21px; height: 18px; cursor: pointer; - background: transparent url(../images/refresh-fff.png?v=v2.3.0) left center no-repeat; + background: transparent url(../images/refresh-fff.png?v=v2.4.0-alpha) left center no-repeat; } .case-log-history-entry { display: block; @@ -1705,7 +1746,7 @@ span.refresh-button { #itop-breadcrumb .breadcrumb-item a::after { content: ''; position: absolute; - background-image: url(../images/breadcrumb-separator.png?v=v2.3.0); + background-image: url(../images/breadcrumb-separator.png?v=v2.4.0-alpha); background-repeat: no-repeat; width: 8px; height: 16px; @@ -1763,3 +1804,17 @@ span.refresh-button { margin-top: 3px; margin-bottom: 1px; } +.object-ref-icon.fa { + color: #e87c1e; + font-size: smaller; + vertical-align: 1px; + margin-right: 1px; +} +.object-ref-icon-disabled.fa { + color: #555; + font-size: smaller; + margin-right: 1px; +} +.object-ref-link { + background: none; +} diff --git a/css/light-grey.scss b/css/light-grey.scss index d7dc9eaf9..8feabc2be 100644 --- a/css/light-grey.scss +++ b/css/light-grey.scss @@ -302,8 +302,6 @@ legend.transparent { .ui-widget-content td a, p a, p a:visited, td a, td a:visited { text-decoration:none; color: $complement-color; - padding-left:14px; - background: url(../images/mini-arrow-orange.gif) no-repeat left; } .ui-widget-content td a.cke_button, .ui-widget-content td a.cke_toolbox_collapser, .ui-widget-content td a.cke_combo_button, cke_dialog a { padding-left: 0; @@ -956,13 +954,62 @@ div#logo div { background: $frame-background-color; text-align: right; } -#admin-banner { +.app-banner { + +} +.app-message { float: left; margin-top: 2px; - padding: 8px; - border: 1px solid #c33; - background-color: #fee; - -moz-border-radius: 0.5em; + margin-right: 4px; + padding: 6px 9px; + background-color: $highlight-color; + color: white; + border-radius: 6px; + text-align: left; +} +.app-message-icon { + margin-right: 5px; +} +.app-message-body { +} +.fa-sm { + font-size: 0.66em; +} +.object-details-header { + margin-top: 7px; + margin-bottom: 7px; + +} +.object-icon { + display: table-cell; + vertical-align: middle; + margin-left: 10px; + margin-right: 10px; +} +.object-infos { + display: table-cell; + vertical-align: middle; +} +.object-name { + margin-top: 0px; + margin-bottom: 0px; +} +.tags { + margin-top: 5px; +} +.tag { + font-size: 10px; + font-weight: initial; + display: inline-block; + color:white; + background-color:#555; + padding: 3px 6px; + -webkit-border-radius:4px; + -moz-border-radius:4px; + border-radius:4px; +} +.text-danger { + color: red; } #global-search { height: 55px; @@ -1888,4 +1935,20 @@ span.refresh-button { font-size: smaller; margin-top: 3px; margin-bottom: 1px; -} \ No newline at end of file +} +.object-ref.archived { +} +.object-ref-icon.fa { + color: $highlight-color; + font-size: smaller; + vertical-align: 1px; + margin-right: 1px; +} +.object-ref-icon-disabled.fa { + color: $grey-color; + font-size: smaller; + margin-right: 1px; +} +.object-ref-link { + background: none; +} diff --git a/datamodels/2.x/itop-portal-base/portal/web/index.php b/datamodels/2.x/itop-portal-base/portal/web/index.php index 14d2a5a75..729b985bc 100644 --- a/datamodels/2.x/itop-portal-base/portal/web/index.php +++ b/datamodels/2.x/itop-portal-base/portal/web/index.php @@ -1,6 +1,6 @@ before(function(Symfony\Component\HttpFoundation\Request $oRequest, Silex $oApp->abort(500, Dict::S('Portal:ErrorNoContactForThisUser')); } + // Enable archived data + $bArchiveMode = utils::IsArchiveMode(); + DBSearch::SetArchiveModeDefault($bArchiveMode); + if ($bArchiveMode) MetaModel::DBSetReadOnly(); + // Enabling datalocalizer if needed if (!defined('DISABLE_DATA_LOCALIZER_PORTAL')) { diff --git a/dictionaries/cs.dictionary.itop.core.php b/dictionaries/cs.dictionary.itop.core.php index 64585b432..4c2d0872d 100755 --- a/dictionaries/cs.dictionary.itop.core.php +++ b/dictionaries/cs.dictionary.itop.core.php @@ -1,6 +1,6 @@ * @author Daniel Rokos