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("

".$this->GetIcon()." \n"); - // Master data sources $bSynchronized = false; $aIcons = array(); @@ -292,16 +290,46 @@ EOF $sTip .= "

".$oDataSource->GetIcon(true, 'style="vertical-align:middle"')." $sLink
"; $sTip .= Dict::S('Core:Synchro:LastSynchro') . '
' . $sLastSynchro . "

"; } - $aIcons[] = ' '; + $sLabel = htmlentities(Dict::S('Tag:Synchronized'), ENT_QUOTES, 'UTF-8'); + $sSynchroTagId = 'synchro_icon-'.$this->GetKey(); + $aIcons[] = "
  $sLabel
"; $sTip = addslashes($sTip); - $oPage->add_ready_script("$('#synchro_icon').qtip( { content: '$sTip', show: 'mouseover', hide: { fixed: true }, style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );"); + $oPage->add_ready_script("$('#$sSynchroTagId').qtip( { content: '$sTip', show: 'mouseover', hide: { fixed: true }, style: { name: 'dark', tip: 'topLeft' }, position: { corner: { target: 'bottomMiddle', tooltip: 'topLeft' }} } );"); } } - - $sIcons = implode(' ', $aIcons); - $oPage->add(MetaModel::GetName(get_class($this)).": ".$this->GetName()."$sIcons

\n"); - $oPage->add("
\n"); - + + if ($this->IsArchived()) + { + $sLabel = htmlentities(Dict::S('Tag:Archived'), ENT_QUOTES, 'UTF-8'); + $sTitle = htmlentities(Dict::S('Tag:Archived+'), ENT_QUOTES, 'UTF-8'); + $aIcons[] = "
  $sLabel
"; + } + + $sObjectIcon = $this->GetIcon(); + $sClassName = MetaModel::GetName(get_class($this)); + $sObjectName = $this->GetName(); + if (count($aIcons) > 0) + { + $sTags = '
'.implode(' ', $aIcons).'
'; + } + else + { + $sTags = ''; + } + + $oPage->add( +<< +
+
$sObjectIcon
+
+

$sClassName: $sObjectName

+ $sTags +
+
+ +EOF + ); } function DisplayBareHistory(WebPage $oPage, $bEditMode = false, $iLimitCount = 0, $iLimitStart = 0) @@ -558,7 +586,6 @@ EOF foreach($aNotificationClasses as $sNotifClass) { - $oPage->p(MetaModel::GetClassIcon($sNotifClass, true).' '.MetaModel::GetName($sNotifClass)); $oBlock = new DisplayBlock($aNotifSearches[$sNotifClass], 'list', false); $oBlock->Display($oPage, 'notifications_'.$sNotifClass, array('menu' => false)); @@ -1933,7 +1960,7 @@ EOF $aEventsList[] ='validate'; $aEventsList[] ='change'; - $oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs); + $oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs, '', $value); $sFieldName = $sFieldPrefix.$sAttCode.$sNameSuffix; $aExtKeyParams = $aArgs; $aExtKeyParams['iFieldSize'] = $oAttDef->GetMaxSize(); @@ -2526,7 +2553,7 @@ EOF { if ($oAttDef->IsExternalKey()) { - $oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs); + $oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs, '', $this->Get($sAttCode)); if ($oAllowedValues->Count() == 1) { $oRemoteObj = $oAllowedValues->Fetch(); diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index 1d4456c78..df57d1869 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -1,5 +1,5 @@ bBreadCrumbEnabled = false; } + $bArchiveMode = utils::IsArchiveMode(); + DBSearch::SetArchiveModeDefault($bArchiveMode); + if ($bArchiveMode) MetaModel::DBSetReadOnly(); + $this->m_sMenu = ""; - $this->m_sMessage = ''; + $this->m_aMessages = array(); $this->SetRootUrl(utils::GetAbsoluteUrlAppRoot()); $this->add_header("Content-type: text/html; charset=utf-8"); $this->add_header("Cache-control: no-cache"); @@ -75,6 +79,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage $this->add_linked_stylesheet("../css/jquery.multiselect.css"); $this->add_linked_stylesheet("../css/magnific-popup.css"); $this->add_linked_stylesheet("../css/c3.min.css"); + $this->add_linked_stylesheet("../css/font-awesome/css/font-awesome.min.css"); $this->add_linked_script('../js/jquery.layout.min.js'); $this->add_linked_script('../js/jquery.ba-bbq.min.js'); @@ -121,6 +126,23 @@ function ShowAboutBox() }); return false; } +function ArchiveMode(bEnable) +{ + var sPrevUrl = StripArchiveArgument(window.location.search); + if (bEnable) + { + window.location.search = sPrevUrl + '&with-archive=1'; + } + else + { + window.location.search = sPrevUrl + '&with-archive=0'; + } +} +function StripArchiveArgument(sUrl) +{ + var res = sUrl.replace(/&with-archive=[01]/g, ''); + return res; +} EOF ); } @@ -732,7 +754,7 @@ EOF if (UserRights::IsAdministrator() && ExecutionKPI::IsEnabled()) { - $sNorthPane .= '
'.ExecutionKPI::GetDescription().'
'; + $sNorthPane .= '
'.ExecutionKPI::GetDescription().'
'; } //$sSouthPane = '

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 = +<< + + + +EOF; + $sAdminMessage = trim(MetaModel::GetConfig()->Get('access_message')); - $sApplicationBanner .= '
'; - $sApplicationBanner .= ''; - $sApplicationBanner .= ' '.$sRestrictions.''; if (strlen($sAdminMessage) > 0) { - $sApplicationBanner .= ' '.$sAdminMessage.''; + $sRestrictions .= ' '.$sAdminMessage; } - $sApplicationBanner .= '
'; + $this->AddApplicationMessage($sRestrictions, $sIcon); } - if(strlen($this->m_sMessage)) + $sApplicationMessages = ''; + foreach ($this->m_aMessages as $aMessage) { - $sApplicationBanner .= '
'.$this->m_sMessage.'
'; + $sHtmlIcon = $aMessage['icon'] ? $aMessage['icon'] : ''; + $sHtmlMessage = $aMessage['message']; + $sTitleAttr = $aMessage['tip'] ? 'title="'.htmlentities($aMessage['tip'], ENT_QUOTES, 'UTF-8').'"' : ''; + $sApplicationMessages .= '
'.$sHtmlIcon.''.$sHtmlMessage.'
'; } - $sApplicationBanner .= $sBannerExtraHtml; + $sApplicationBanner = "
$sApplicationMessages$sBannerExtraHtml
"; if (!empty($sNorthPane)) { @@ -1333,10 +1376,26 @@ EOF } /** - * Set the message to be displayed in the 'admin-banner' section at the top of the page + * Set the message to be displayed in the 'app-banner' section at the top of the page */ - public function SetMessage($sMessage) + public function SetMessage($sHtmlMessage) { - $this->m_sMessage = $sMessage; + $sHtmlIcon = ''; + $this->AddApplicationMessage($sHtmlMessage, $sHtmlIcon); + } + + /** + * Add message to be displayed in the 'app-banner' section at the top of the page + */ + public function AddApplicationMessage($sHtmlMessage, $sHtmlIcon = null, $sTip = null) + { + if (strlen($sHtmlMessage)) + { + $this->m_aMessages[] = array( + 'icon' => $sHtmlIcon, + 'message' => $sHtmlMessage, + 'tip' => $sTip + ); + } } } diff --git a/application/loginwebpage.class.inc.php b/application/loginwebpage.class.inc.php index 195c5c0a5..96d6c8daa 100644 --- a/application/loginwebpage.class.inc.php +++ b/application/loginwebpage.class.inc.php @@ -1,5 +1,5 @@ Get('access_message')); - $sApplicationBanner .= '
'; + $sApplicationBanner .= '
'; $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 - * @copyright Copyright (C) 2010-2014 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ Dict::Add('CS CZ', 'Czech', 'Čeština', array( @@ -47,6 +47,13 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'Core:AttributeBoolean' => 'Boolean', 'Core:AttributeBoolean+' => 'Boolean', + 'Core:AttributeBoolean/Value:null' => '', + 'Core:AttributeBoolean/Value:yes' => 'Yes~~', + 'Core:AttributeBoolean/Value:no' => 'No~~', + + 'Core:AttributeArchiveFlag/Value:yes' => 'Yes~~', + 'Core:AttributeArchiveFlag/Value:yes+' => 'This object is visible only in archive mode~~', + 'Core:AttributeArchiveFlag/Value:no' => 'No~~', 'Core:AttributeString' => 'Řetězec (string)', 'Core:AttributeString+' => 'Alfanumerický řetězec', diff --git a/dictionaries/cs.dictionary.itop.ui.php b/dictionaries/cs.dictionary.itop.ui.php index cbf81041a..3a966ee11 100755 --- a/dictionaries/cs.dictionary.itop.ui.php +++ b/dictionaries/cs.dictionary.itop.ui.php @@ -1,6 +1,6 @@ * @author Daniel Rokos - * @copyright Copyright (C) 2010-2014 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ @@ -1070,6 +1070,8 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'UI:Pagination:All' => 'Vše', 'UI:HierarchyOf_Class' => 'Hierarchie %1$s', 'UI:Preferences' => 'Předvolby', + 'UI:ArchiveModeOn' => 'Activate archive mode~~', + 'UI:ArchiveModeOff' => 'Deactivate archive mode~~', 'UI:FavoriteOrganizations' => 'Oblíbené organizace', 'UI:FavoriteOrganizations+' => 'Zaškrtněte, které organizace chcete vidět v rozbalovacím menu pro rychlý přístup. Mějte na paměti, že toto není bezpečnostní opatření. Objekty všech organizací jsou pořád viditelné a přístupné vybráním "Všechny organizace" z rozbalovacího menu.', 'UI:FavoriteLanguage' => 'Jazyk uživatelského rozhraní', diff --git a/dictionaries/da.dictionary.itop.core.php b/dictionaries/da.dictionary.itop.core.php index b639bd8b7..4d04c6936 100644 --- a/dictionaries/da.dictionary.itop.core.php +++ b/dictionaries/da.dictionary.itop.core.php @@ -1,5 +1,5 @@ * - * @copyright Copyright (C) 2010-2012 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @licence http://opensource.org/licenses/AGPL-3.0 */ @@ -1397,6 +1397,12 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'Core:AttributeDecimal+' => '', 'Core:AttributeBoolean' => 'Boolean', 'Core:AttributeBoolean+' => '', + 'Core:AttributeBoolean/Value:null' => '', + 'Core:AttributeBoolean/Value:yes' => 'Yes~~', + 'Core:AttributeBoolean/Value:no' => 'No~~', + 'Core:AttributeArchiveFlag/Value:yes' => 'Yes~~', + 'Core:AttributeArchiveFlag/Value:yes+' => 'This object is visible only in archive mode~~', + 'Core:AttributeArchiveFlag/Value:no' => 'No~~', 'Core:AttributeString' => 'String', 'Core:AttributeString+' => '', 'Core:AttributeClass' => 'Class', diff --git a/dictionaries/da.dictionary.itop.ui.php b/dictionaries/da.dictionary.itop.ui.php index 360e8df93..9a22a5da1 100644 --- a/dictionaries/da.dictionary.itop.ui.php +++ b/dictionaries/da.dictionary.itop.ui.php @@ -1,5 +1,5 @@ * - * @copyright Copyright (C) 2010-2012 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @licence http://opensource.org/licenses/AGPL-3.0 */ @@ -608,6 +608,11 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array( 'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s detaljer', 'UI:ErrorPageTitle' => 'iTop - Fejl', 'UI:ObjectDoesNotExist' => 'Beklager, dette objekt eksisterer ikke (eller du har ikke tilladelse til at se det).', + 'UI:ObjectArchived' => 'This object has been archived. Please enable the archive mode or contact your administrator.~~', + 'Tag:Archived' => 'Archived~~', + 'Tag:Archived+' => 'Can be accessed only in archive mode~~', + 'Tag:Synchronized' => 'Synchronized~~', + 'ObjectRef:Archived' => 'Archived~~', 'UI:SearchResultsPageTitle' => 'iTop - Søge Resultater', 'UI:SearchResultsTitle' => 'Søge Resultater', 'UI:SearchResultsTitle+' => 'Full-text search results~~', @@ -857,6 +862,10 @@ Ved tilknytningen til en trigger, bliver hver handling tildelt et "rækkefølge" 'UI:Pagination:All' => 'Alle', 'UI:HierarchyOf_Class' => 'Hierarchy af %1$s', 'UI:Preferences' => 'Indstillinger...', + 'UI:ArchiveModeOn' => 'Activate archive mode~~', + 'UI:ArchiveModeOff' => 'Deactivate archive mode~~', + 'UI:ArchiveMode:Banner' => 'Archive mode~~', + 'UI:ArchiveMode:Banner+' => 'Archived objects are visible, and no modification is allowed~~', 'UI:FavoriteOrganizations' => 'Favorit Organisationer', 'UI:FavoriteOrganizations+' => '', 'UI:FavoriteLanguage' => 'Sprog i brugergrænseflade', diff --git a/dictionaries/de.dictionary.itop.core.php b/dictionaries/de.dictionary.itop.core.php index 6057695d7..7f2241d3d 100644 --- a/dictionaries/de.dictionary.itop.core.php +++ b/dictionaries/de.dictionary.itop.core.php @@ -1,5 +1,5 @@ - * @copyright Copyright (C) 2010-2016 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @licence http://opensource.org/licenses/AGPL-3.0 */ @@ -323,6 +323,12 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'Core:AttributeDecimal+' => 'Dezimaler Wert (kann negativ sein)', 'Core:AttributeBoolean' => 'Boolean', 'Core:AttributeBoolean+' => 'Boolscher Wert', + 'Core:AttributeBoolean/Value:null' => '', + 'Core:AttributeBoolean/Value:yes' => 'Yes~~', + 'Core:AttributeBoolean/Value:no' => 'No~~', + 'Core:AttributeArchiveFlag/Value:yes' => 'Yes~~', + 'Core:AttributeArchiveFlag/Value:yes+' => 'This object is visible only in archive mode~~', + 'Core:AttributeArchiveFlag/Value:no' => 'No~~', 'Core:AttributeString' => 'String', 'Core:AttributeString+' => 'Alphanumerischer String', 'Core:AttributeClass' => 'Class', diff --git a/dictionaries/de.dictionary.itop.ui.php b/dictionaries/de.dictionary.itop.ui.php index 58e3598e0..61c2c67df 100644 --- a/dictionaries/de.dictionary.itop.ui.php +++ b/dictionaries/de.dictionary.itop.ui.php @@ -1,5 +1,5 @@ - * @copyright Copyright (C) 2010-2012 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @licence http://opensource.org/licenses/AGPL-3.0 */ @@ -609,6 +609,11 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s Details', 'UI:ErrorPageTitle' => 'iTop - Fehler', 'UI:ObjectDoesNotExist' => 'Leider existiert dieses Objekt nicht oder Sie sind nicht berechtigt es einzusehen.', + 'UI:ObjectArchived' => 'This object has been archived. Please enable the archive mode or contact your administrator.~~', + 'Tag:Archived' => 'Archived~~', + 'Tag:Archived+' => 'Can be accessed only in archive mode~~', + 'Tag:Synchronized' => 'Synchronized~~', + 'ObjectRef:Archived' => 'Archived~~', 'UI:SearchResultsPageTitle' => 'iTop - Suchergebnisse', 'UI:SearchResultsTitle' => 'Suchergebnisse', 'UI:SearchResultsTitle+' => 'Full-text search results~~', @@ -882,6 +887,10 @@ Wenn Aktionen mit Trigger verknüpft sind, bekommt jede Aktion eine Auftragsnumm 'UI:Pagination:All' => 'Alles', 'UI:HierarchyOf_Class' => 'Hierarchie von %1$s', 'UI:Preferences' => 'Einstellungen...', + 'UI:ArchiveModeOn' => 'Activate archive mode~~', + 'UI:ArchiveModeOff' => 'Deactivate archive mode~~', + 'UI:ArchiveMode:Banner' => 'Archive mode~~', + 'UI:ArchiveMode:Banner+' => 'Archived objects are visible, and no modification is allowed~~', 'UI:FavoriteOrganizations' => 'Bevorzugte Organisationen', 'UI:FavoriteOrganizations+' => '', 'UI:FavoriteLanguage' => 'Sprache des Benutzerinterfaces', diff --git a/dictionaries/dictionary.itop.core.php b/dictionaries/dictionary.itop.core.php index 5fad458dd..bae31a442 100644 --- a/dictionaries/dictionary.itop.core.php +++ b/dictionaries/dictionary.itop.core.php @@ -1,5 +1,5 @@ 'Boolean', 'Core:AttributeBoolean+' => 'Boolean', + 'Core:AttributeBoolean/Value:null' => '', + 'Core:AttributeBoolean/Value:yes' => 'Yes', + 'Core:AttributeBoolean/Value:no' => 'No', + + 'Core:AttributeArchiveFlag/Value:yes' => 'Yes', + 'Core:AttributeArchiveFlag/Value:yes+' => 'This object is visible only in archive mode', + 'Core:AttributeArchiveFlag/Value:no' => 'No', 'Core:AttributeString' => 'String', 'Core:AttributeString+' => 'Alphanumeric string', diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php index ee554d55f..fb248c35a 100644 --- a/dictionaries/dictionary.itop.ui.php +++ b/dictionaries/dictionary.itop.ui.php @@ -1,5 +1,5 @@ 'iTop - %1$s - %2$s details', 'UI:ErrorPageTitle' => 'iTop - Error', 'UI:ObjectDoesNotExist' => 'Sorry, this object does not exist (or you are not allowed to view it).', + 'UI:ObjectArchived' => 'This object has been archived. Please enable the archive mode or contact your administrator.', + 'Tag:Archived' => 'Archived', + 'Tag:Archived+' => 'Can be accessed only in archive mode', + 'Tag:Synchronized' => 'Synchronized', + 'ObjectRef:Archived' => 'Archived', 'UI:SearchResultsPageTitle' => 'iTop - Search Results', 'UI:SearchResultsTitle' => 'Search Results', 'UI:SearchResultsTitle+' => 'Full-text search results', @@ -1074,6 +1079,10 @@ When associated with a trigger, each action is given an "order" number, specifyi 'UI:Pagination:All' => 'All', 'UI:HierarchyOf_Class' => 'Hierarchy of %1$s', 'UI:Preferences' => 'Preferences...', + 'UI:ArchiveModeOn' => 'Activate archive mode', + 'UI:ArchiveModeOff' => 'Deactivate archive mode', + 'UI:ArchiveMode:Banner' => 'Archive mode', + 'UI:ArchiveMode:Banner+' => 'Archived objects are visible, and no modification is allowed', 'UI:FavoriteOrganizations' => 'Favorite Organizations', 'UI:FavoriteOrganizations+' => 'Check in the list below the organizations that you want to see in the drop-down menu for a quick access. '. 'Note that this is not a security setting, objects from any organization are still visible and can be accessed by selecting "All Organizations" in the drop-down list.', diff --git a/dictionaries/es_cr.dictionary.itop.core.php b/dictionaries/es_cr.dictionary.itop.core.php index 12537262c..8410c5772 100644 --- a/dictionaries/es_cr.dictionary.itop.core.php +++ b/dictionaries/es_cr.dictionary.itop.core.php @@ -1,5 +1,5 @@ 'Booleano', 'Core:AttributeBoolean+' => 'Booleano', + 'Core:AttributeBoolean/Value:null' => '', + 'Core:AttributeBoolean/Value:yes' => 'Yes~~', + 'Core:AttributeBoolean/Value:no' => 'No~~', + + 'Core:AttributeArchiveFlag/Value:yes' => 'Yes~~', + 'Core:AttributeArchiveFlag/Value:yes+' => 'This object is visible only in archive mode~~', + 'Core:AttributeArchiveFlag/Value:no' => 'No~~', 'Core:AttributeString' => 'Cadena de caracteres', 'Core:AttributeString+' => 'Cadena de caracteres alfanumerico', diff --git a/dictionaries/es_cr.dictionary.itop.ui.php b/dictionaries/es_cr.dictionary.itop.ui.php index a97e10d85..55e298a2a 100644 --- a/dictionaries/es_cr.dictionary.itop.ui.php +++ b/dictionaries/es_cr.dictionary.itop.ui.php @@ -1,5 +1,5 @@ 'iTop - %1$s - Detalles %2$s', 'UI:ErrorPageTitle' => 'iTop - Error', 'UI:ObjectDoesNotExist' => 'Disculpe, este objeto no existe (o no está autorizado para verlo).', + 'UI:ObjectArchived' => 'This object has been archived. Please enable the archive mode or contact your administrator.~~', + 'Tag:Archived' => 'Archived~~', + 'Tag:Archived+' => 'Can be accessed only in archive mode~~', + 'Tag:Synchronized' => 'Synchronized~~', + 'ObjectRef:Archived' => 'Archived~~', 'UI:SearchResultsPageTitle' => 'iTop - Resultados de la Búsqueda', 'UI:SearchResultsTitle' => 'Resultados de la Búsqueda', 'UI:SearchResultsTitle+' => 'Full-text search results~~', @@ -1034,6 +1039,10 @@ Cuando se asocien con un disparador, cada acción recibe un número de "orden", 'UI:Pagination:All' => 'Todos', 'UI:HierarchyOf_Class' => 'Jerarquía de %1$s', 'UI:Preferences' => 'Preferencias', + 'UI:ArchiveModeOn' => 'Activate archive mode~~', + 'UI:ArchiveModeOff' => 'Deactivate archive mode~~', + 'UI:ArchiveMode:Banner' => 'Archive mode~~', + 'UI:ArchiveMode:Banner+' => 'Archived objects are visible, and no modification is allowed~~', 'UI:FavoriteOrganizations' => 'Mi Organización Favorita', 'UI:FavoriteOrganizations+' => 'Verifique en la siguiente lista de Organizaciones, la que necesite ver en los menues para un rápido acceso. '. 'Nota, esto no es una configuración de seguridad, elementos de cualquier Organización son visibles y pueden ser accesados mediante la selección de "Todas las Organizaciones" en la lista del menú.', @@ -1430,5 +1439,4 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array( 'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~', 'UI:Menu:ExportPDF' => 'Export as PDF...~~', 'UI:Menu:PrintableVersion' => 'Versión imprimible', -)); -?> \ No newline at end of file +)); \ No newline at end of file diff --git a/dictionaries/fr.dictionary.itop.core.php b/dictionaries/fr.dictionary.itop.core.php index eb3a36eff..27328e206 100644 --- a/dictionaries/fr.dictionary.itop.core.php +++ b/dictionaries/fr.dictionary.itop.core.php @@ -1,5 +1,5 @@ 'Valeur numérique décimale', 'Core:AttributeBoolean' => 'Booléen', 'Core:AttributeBoolean+' => 'Booléen', + 'Core:AttributeBoolean/Value:null' => '', + 'Core:AttributeBoolean/Value:yes' => 'Oui', + 'Core:AttributeBoolean/Value:no' => 'Non', + 'Core:AttributeArchiveFlag/Value:yes' => 'Oui', + 'Core:AttributeArchiveFlag/Value:yes+' => 'Cet object n\'est visible que dans le mode Archive', + 'Core:AttributeArchiveFlag/Value:no' => 'Non', 'Core:AttributeString' => 'Chaîne de caractères', 'Core:AttributeString+' => 'Chaîne de caractères (limitée à une ligne)', 'Core:AttributeClass' => 'Classe', diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php index ec6a5ff04..7e2061087 100644 --- a/dictionaries/fr.dictionary.itop.ui.php +++ b/dictionaries/fr.dictionary.itop.ui.php @@ -1,5 +1,5 @@ /** - * @copyright Copyright (C) 2010-2012 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ @@ -660,6 +660,11 @@ Dict::Add('FR FR', 'French', 'Français', array( 'UI:DetailsPageTitle' => 'iTop - %2$s - Détails de %1$s', 'UI:ErrorPageTitle' => 'iTop - Erreur', 'UI:ObjectDoesNotExist' => 'Désolé cet objet n\'existe pas (où vous n\'êtes pas autorisé à l\'afficher).', + 'UI:ObjectArchived' => 'Cet objet a été archivé. Veuillez activer le mode Archive, on contactez votre administrateur.', + 'Tag:Archived' => 'Archivé', + 'Tag:Archived+' => 'Accessible seulement dans le mode Archive', + 'Tag:Synchronized' => 'Synchronisé', + 'ObjectRef:Archived' => 'Archivé', 'UI:SearchResultsPageTitle' => 'iTop - Résultats de la recherche', 'UI:SearchResultsTitle' => 'Recherche globale', 'UI:SearchResultsTitle+' => 'Résultat de recherche globale', @@ -938,6 +943,10 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé 'UI:Pagination:All' => 'Tous', 'UI:HierarchyOf_Class' => 'Hiérarchie de type %1$s', 'UI:Preferences' => 'Préférences...', + 'UI:ArchiveModeOn' => 'Activer le mode Archive', + 'UI:ArchiveModeOff' => 'Désactiver le mode Archive', + 'UI:ArchiveMode:Banner' => 'Mode Archive', + 'UI:ArchiveMode:Banner+' => 'Les objets archivés sont visibles, et aucune modification n\'est possible', 'UI:FavoriteOrganizations' => 'Organisations Favorites', 'UI:FavoriteOrganizations+' => 'Cochez dans la liste ci-dessous les organisations que vous voulez voir listées dans le menu principal. '. 'Ceci n\'est pas un réglage de sécurité. Les objets de toutes les organisations sont toujours visibles en choisissant "Toutes les Organisations" dans le menu.', diff --git a/dictionaries/hu.dictionary.itop.core.php b/dictionaries/hu.dictionary.itop.core.php index 68860f5dd..a5a96d865 100755 --- a/dictionaries/hu.dictionary.itop.core.php +++ b/dictionaries/hu.dictionary.itop.core.php @@ -1,5 +1,5 @@ /** - * @copyright Copyright (C) 2010-2016 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ @@ -306,6 +306,12 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'Core:AttributeDecimal+' => '', 'Core:AttributeBoolean' => 'Boolean', 'Core:AttributeBoolean+' => '', + 'Core:AttributeBoolean/Value:null' => '', + 'Core:AttributeBoolean/Value:yes' => 'Yes~~', + 'Core:AttributeBoolean/Value:no' => 'No~~', + 'Core:AttributeArchiveFlag/Value:yes' => 'Yes~~', + 'Core:AttributeArchiveFlag/Value:yes+' => 'This object is visible only in archive mode~~', + 'Core:AttributeArchiveFlag/Value:no' => 'No~~', 'Core:AttributeString' => 'String', 'Core:AttributeString+' => '', 'Core:AttributeClass' => 'Class', @@ -609,5 +615,4 @@ Operators:
'Core:Validator:Mandatory' => 'Please, fill this field~~', 'Core:Validator:MustBeInteger' => 'Must be an integer~~', 'Core:Validator:MustSelectOne' => 'Please, select one~~', -)); -?> \ No newline at end of file +)); \ No newline at end of file diff --git a/dictionaries/hu.dictionary.itop.ui.php b/dictionaries/hu.dictionary.itop.ui.php index 6fdc8d349..bd83ea750 100755 --- a/dictionaries/hu.dictionary.itop.ui.php +++ b/dictionaries/hu.dictionary.itop.ui.php @@ -1,5 +1,5 @@ /** - * @copyright Copyright (C) 2010-2012 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ @@ -539,6 +539,11 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array( 'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s részletek', 'UI:ErrorPageTitle' => 'iTop - Hiba', 'UI:ObjectDoesNotExist' => 'Sajnálom ez az objektum nem létezik (vagy a megtekintése nem engedélyezett a felhasználó számára).', + 'UI:ObjectArchived' => 'This object has been archived. Please enable the archive mode or contact your administrator.~~', + 'Tag:Archived' => 'Archived~~', + 'Tag:Archived+' => 'Can be accessed only in archive mode~~', + 'Tag:Synchronized' => 'Synchronized~~', + 'ObjectRef:Archived' => 'Archived~~', 'UI:SearchResultsPageTitle' => 'iTop - Keresés eredményei', 'UI:SearchResultsTitle' => 'Keresés eredményei', 'UI:SearchResultsTitle+' => 'Full-text search results~~', @@ -906,6 +911,10 @@ Akció kiváltó okhoz rendelésekor kap egy sorszámot , amely meghatározza az 'UI:Pagination:All' => 'All~~', 'UI:HierarchyOf_Class' => 'Hierarchy of %1$s~~', 'UI:Preferences' => 'Preferences...~~', + 'UI:ArchiveModeOn' => 'Activate archive mode~~', + 'UI:ArchiveModeOff' => 'Deactivate archive mode~~', + 'UI:ArchiveMode:Banner' => 'Archive mode~~', + 'UI:ArchiveMode:Banner+' => 'Archived objects are visible, and no modification is allowed~~', 'UI:FavoriteOrganizations' => 'Favorite Organizations~~', 'UI:FavoriteOrganizations+' => 'Check in the list below the organizations that you want to see in the drop-down menu for a quick access. Note that this is not a security setting, objects from any organization are still visible and can be accessed by selecting \"All Organizations\" in the drop-down list.~~', 'UI:FavoriteLanguage' => 'Language of the User Interface~~', @@ -1099,5 +1108,4 @@ Akció kiváltó okhoz rendelésekor kap egy sorszámot , amely meghatározza az 'UI:ConcurrentLockKilled' => 'The lock preventing modifications on the current object has been deleted.~~', 'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~', 'UI:Menu:ExportPDF' => 'Export as PDF...~~', -)); -?> \ No newline at end of file +)); \ No newline at end of file diff --git a/dictionaries/it.dictionary.itop.core.php b/dictionaries/it.dictionary.itop.core.php index fd0f0fe8b..fa58fd5e6 100644 --- a/dictionaries/it.dictionary.itop.core.php +++ b/dictionaries/it.dictionary.itop.core.php @@ -1,5 +1,5 @@ 'Booleano', 'Core:AttributeBoolean+' => 'Booleano', + 'Core:AttributeBoolean/Value:null' => '', + 'Core:AttributeBoolean/Value:yes' => 'Yes~~', + 'Core:AttributeBoolean/Value:no' => 'No~~', + + 'Core:AttributeArchiveFlag/Value:yes' => 'Yes~~', + 'Core:AttributeArchiveFlag/Value:yes+' => 'This object is visible only in archive mode~~', + 'Core:AttributeArchiveFlag/Value:no' => 'No~~', 'Core:AttributeString' => 'Stringa', 'Core:AttributeString+' => 'Stringa alfanumerica', @@ -855,6 +862,4 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array( 'Core:Validator:Mandatory' => 'Please, fill this field~~', 'Core:Validator:MustBeInteger' => 'Must be an integer~~', 'Core:Validator:MustSelectOne' => 'Please, select one~~', -)); - -?> +)); \ No newline at end of file diff --git a/dictionaries/it.dictionary.itop.ui.php b/dictionaries/it.dictionary.itop.ui.php index 608d48e89..622974718 100644 --- a/dictionaries/it.dictionary.itop.ui.php +++ b/dictionaries/it.dictionary.itop.ui.php @@ -1,5 +1,5 @@ 'iTop - %1$s - %2$s dettagli', 'UI:ErrorPageTitle' => 'iTop - Errore', 'UI:ObjectDoesNotExist' => 'Spiacenti, questo oggetto non esiste (o non si è autorizzati per vederlo).', + 'UI:ObjectArchived' => 'This object has been archived. Please enable the archive mode or contact your administrator.~~', + 'Tag:Archived' => 'Archived~~', + 'Tag:Archived+' => 'Can be accessed only in archive mode~~', + 'Tag:Synchronized' => 'Synchronized~~', + 'ObjectRef:Archived' => 'Archived~~', 'UI:SearchResultsPageTitle' => 'iTop - Risultati della ricerca', 'UI:SearchResultsTitle' => 'Risultati della ricerca', 'UI:SearchResultsTitle+' => 'Full-text search results~~', @@ -1031,6 +1036,10 @@ Quando è associata a un trigger, ad ogni azione è assegnato un numero "ordine" 'UI:Pagination:All' => 'All~~', 'UI:HierarchyOf_Class' => 'Hierarchy of %1$s~~', 'UI:Preferences' => 'Preferences...~~', + 'UI:ArchiveModeOn' => 'Activate archive mode~~', + 'UI:ArchiveModeOff' => 'Deactivate archive mode~~', + 'UI:ArchiveMode:Banner' => 'Archive mode~~', + 'UI:ArchiveMode:Banner+' => 'Archived objects are visible, and no modification is allowed~~', 'UI:FavoriteOrganizations' => 'Favorite Organizations~~', 'UI:FavoriteOrganizations+' => 'Check in the list below the organizations that you want to see in the drop-down menu for a quick access. Note that this is not a security setting, objects from any organization are still visible and can be accessed by selecting \"All Organizations\" in the drop-down list.~~', 'UI:FavoriteLanguage' => 'Language of the User Interface~~', @@ -1224,5 +1233,4 @@ Quando è associata a un trigger, ad ogni azione è assegnato un numero "ordine" 'UI:ConcurrentLockKilled' => 'The lock preventing modifications on the current object has been deleted.~~', 'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~', 'UI:Menu:ExportPDF' => 'Export as PDF...~~', -)); -?> +)); \ No newline at end of file diff --git a/dictionaries/ja.dictionary.itop.core.php b/dictionaries/ja.dictionary.itop.core.php index 3a1ac641c..f4d897fc6 100644 --- a/dictionaries/ja.dictionary.itop.core.php +++ b/dictionaries/ja.dictionary.itop.core.php @@ -1,5 +1,5 @@ /** - * @copyright Copyright (C) 2010-2016 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @licence http://opensource.org/licenses/AGPL-3.0 */ @@ -325,6 +325,12 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'Core:AttributeDecimal+' => 'Decimal値 (負数あり)', 'Core:AttributeBoolean' => 'ブール型', 'Core:AttributeBoolean+' => 'Bool値', + 'Core:AttributeBoolean/Value:null' => '', + 'Core:AttributeBoolean/Value:yes' => 'Yes~~', + 'Core:AttributeBoolean/Value:no' => 'No~~', + 'Core:AttributeArchiveFlag/Value:yes' => 'Yes~~', + 'Core:AttributeArchiveFlag/Value:yes+' => 'This object is visible only in archive mode~~', + 'Core:AttributeArchiveFlag/Value:no' => 'No~~', 'Core:AttributeString' => '文字列', 'Core:AttributeString+' => '文字列', 'Core:AttributeClass' => 'クラス', @@ -631,5 +637,4 @@ Operators:
'Core:Validator:Mandatory' => 'Please, fill this field~~', 'Core:Validator:MustBeInteger' => 'Must be an integer~~', 'Core:Validator:MustSelectOne' => 'Please, select one~~', -)); -?> \ No newline at end of file +)); \ No newline at end of file diff --git a/dictionaries/ja.dictionary.itop.ui.php b/dictionaries/ja.dictionary.itop.ui.php index 5797b06a1..cf1129dbb 100644 --- a/dictionaries/ja.dictionary.itop.ui.php +++ b/dictionaries/ja.dictionary.itop.ui.php @@ -1,5 +1,5 @@ /** - * @copyright Copyright (C) 2010-2012 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @licence http://opensource.org/licenses/AGPL-3.0 */ @@ -608,6 +608,11 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'UI:DetailsPageTitle' => 'iTop - %1$s - %2$sの詳細', 'UI:ErrorPageTitle' => 'iTop - エラー', 'UI:ObjectDoesNotExist' => '申し訳ございません。このオブジェクトは既に存在しません。(あるいは参照する権限がありません。)', + 'UI:ObjectArchived' => 'This object has been archived. Please enable the archive mode or contact your administrator.~~', + 'Tag:Archived' => 'Archived~~', + 'Tag:Archived+' => 'Can be accessed only in archive mode~~', + 'Tag:Synchronized' => 'Synchronized~~', + 'ObjectRef:Archived' => 'Archived~~', 'UI:SearchResultsPageTitle' => 'iTop - 検索結果', 'UI:SearchResultsTitle' => '検索結果', 'UI:SearchResultsTitle+' => 'Full-text search results~~', @@ -856,6 +861,10 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'UI:Pagination:All' => '全', 'UI:HierarchyOf_Class' => '%1$s の階層', 'UI:Preferences' => 'プリファレンス...', + 'UI:ArchiveModeOn' => 'Activate archive mode~~', + 'UI:ArchiveModeOff' => 'Deactivate archive mode~~', + 'UI:ArchiveMode:Banner' => 'Archive mode~~', + 'UI:ArchiveMode:Banner+' => 'Archived objects are visible, and no modification is allowed~~', 'UI:FavoriteOrganizations' => 'クイックアクセス組織', 'UI:FavoriteOrganizations+' => '迅速なアクセスのためのドロップダウンメニューに表示したい組織は、以下のリストで確認してください。セキュリティ設定ではないことに注意してください。全ての組織のオブジェクトは、表示可能です。ドロップダウンリストで「すべての組織(All Organizations)」を選択することでアクセスすることができます。', 'UI:FavoriteLanguage' => 'ユーザインターフェースの言語', @@ -1103,5 +1112,4 @@ Dict::Add('JA JP', 'Japanese', '日本語', array( 'UI:ConcurrentLockKilled' => 'The lock preventing modifications on the current object has been deleted.~~', 'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~', 'UI:Menu:ExportPDF' => 'Export as PDF...~~', -)); -?> \ No newline at end of file +)); \ No newline at end of file diff --git a/dictionaries/nl.dictionary.itop.core.php b/dictionaries/nl.dictionary.itop.core.php index 9d3b7531f..84c6d5b4c 100644 --- a/dictionaries/nl.dictionary.itop.core.php +++ b/dictionaries/nl.dictionary.itop.core.php @@ -1,5 +1,5 @@ 'Boolean', 'Core:AttributeBoolean+' => 'Boolean', + 'Core:AttributeBoolean/Value:null' => '', + 'Core:AttributeBoolean/Value:yes' => 'Yes~~', + 'Core:AttributeBoolean/Value:no' => 'No~~', + + 'Core:AttributeArchiveFlag/Value:yes' => 'Yes~~', + 'Core:AttributeArchiveFlag/Value:yes+' => 'This object is visible only in archive mode~~', + 'Core:AttributeArchiveFlag/Value:no' => 'No~~', 'Core:AttributeString' => 'String', 'Core:AttributeString+' => 'Alphanumerieke string', @@ -861,6 +868,4 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array( 'Core:Validator:Mandatory' => 'Please, fill this field~~', 'Core:Validator:MustBeInteger' => 'Must be an integer~~', 'Core:Validator:MustSelectOne' => 'Please, select one~~', -)); - -?> +)); \ No newline at end of file diff --git a/dictionaries/nl.dictionary.itop.ui.php b/dictionaries/nl.dictionary.itop.ui.php index 13e1c9bab..0cfd812a0 100644 --- a/dictionaries/nl.dictionary.itop.ui.php +++ b/dictionaries/nl.dictionary.itop.ui.php @@ -1,5 +1,5 @@ 'iTop - %1$s - %2$s details', 'UI:ErrorPageTitle' => 'iTop - Error', 'UI:ObjectDoesNotExist' => 'Sorry, dit object bestaat niet (of u bent niet gemachtigd het te bekijken).', + 'UI:ObjectArchived' => 'This object has been archived. Please enable the archive mode or contact your administrator.~~', + 'Tag:Archived' => 'Archived~~', + 'Tag:Archived+' => 'Can be accessed only in archive mode~~', + 'Tag:Synchronized' => 'Synchronized~~', + 'ObjectRef:Archived' => 'Archived~~', 'UI:SearchResultsPageTitle' => 'iTop - Zoekresultaten', 'UI:SearchResultsTitle' => 'Zoekresultaten', 'UI:SearchResultsTitle+' => 'Full-text search results~~', @@ -1042,6 +1047,10 @@ Indien gekoppeld aan een Trigger, wordt aan elke actie een "orde" nummer gegeven 'UI:Pagination:All' => 'Alles', 'UI:HierarchyOf_Class' => 'Hierarchie van %1$s', 'UI:Preferences' => 'Voorkeuren...', + 'UI:ArchiveModeOn' => 'Activate archive mode~~', + 'UI:ArchiveModeOff' => 'Deactivate archive mode~~', + 'UI:ArchiveMode:Banner' => 'Archive mode~~', + 'UI:ArchiveMode:Banner+' => 'Archived objects are visible, and no modification is allowed~~', 'UI:FavoriteOrganizations' => 'Favoriete Organizaties', 'UI:FavoriteOrganizations+' => 'Bekijk de organisaties die u wilt zijn in het drop-down menu voor een snelle toegang in de onderstaande lijst. '. 'Merk op dat dit geen security instelling is, objecten van elke organisatie zijn nog steed zichtbaar en toegankelijk door "All Organizations" te selecteren in de drop-down list.', @@ -1292,5 +1301,4 @@ Indien gekoppeld aan een Trigger, wordt aan elke actie een "orde" nummer gegeven 'UI:ConcurrentLockKilled' => 'The lock preventing modifications on the current object has been deleted.~~', 'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~', 'UI:Menu:ExportPDF' => 'Export as PDF...~~', -)); -?> +)); \ No newline at end of file diff --git a/dictionaries/pt_br.dictionary.itop.core.php b/dictionaries/pt_br.dictionary.itop.core.php index 72ab12d49..97613c4a3 100644 --- a/dictionaries/pt_br.dictionary.itop.core.php +++ b/dictionaries/pt_br.dictionary.itop.core.php @@ -1,5 +1,5 @@ 'Boolean', 'Core:AttributeBoolean+' => 'Boolean', + 'Core:AttributeBoolean/Value:null' => '', + 'Core:AttributeBoolean/Value:yes' => 'Yes~~', + 'Core:AttributeBoolean/Value:no' => 'No~~', + + 'Core:AttributeArchiveFlag/Value:yes' => 'Yes~~', + 'Core:AttributeArchiveFlag/Value:yes+' => 'This object is visible only in archive mode~~', + 'Core:AttributeArchiveFlag/Value:no' => 'No~~', 'Core:AttributeString' => 'String', 'Core:AttributeString+' => 'Seqüência alfanumérica', @@ -854,6 +861,4 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'Core:Validator:Mandatory' => 'Please, fill this field~~', 'Core:Validator:MustBeInteger' => 'Must be an integer~~', 'Core:Validator:MustSelectOne' => 'Please, select one~~', -)); - -?> +)); \ No newline at end of file diff --git a/dictionaries/pt_br.dictionary.itop.ui.php b/dictionaries/pt_br.dictionary.itop.ui.php index dbcc67a4b..18eee77fb 100644 --- a/dictionaries/pt_br.dictionary.itop.ui.php +++ b/dictionaries/pt_br.dictionary.itop.ui.php @@ -1,5 +1,5 @@ '%1$s - %2$s detalhes', 'UI:ErrorPageTitle' => 'Erro', 'UI:ObjectDoesNotExist' => 'Desculpe, este objeto não existe (ou você não tem permissão para vê-lo).', + 'UI:ObjectArchived' => 'This object has been archived. Please enable the archive mode or contact your administrator.~~', + 'Tag:Archived' => 'Archived~~', + 'Tag:Archived+' => 'Can be accessed only in archive mode~~', + 'Tag:Synchronized' => 'Synchronized~~', + 'ObjectRef:Archived' => 'Archived~~', 'UI:SearchResultsPageTitle' => 'Resultado da pesquisa', 'UI:SearchResultsTitle' => 'Resultado da pesquisa', 'UI:SearchResultsTitle+' => 'Full-text search results~~', @@ -1033,6 +1038,10 @@ When associated with a trigger, each action is given an "order" number, specifyi 'UI:Pagination:All' => 'Todos', 'UI:HierarchyOf_Class' => 'Hierarquia de %1$s', 'UI:Preferences' => 'Preferências...', + 'UI:ArchiveModeOn' => 'Activate archive mode~~', + 'UI:ArchiveModeOff' => 'Deactivate archive mode~~', + 'UI:ArchiveMode:Banner' => 'Archive mode~~', + 'UI:ArchiveMode:Banner+' => 'Archived objects are visible, and no modification is allowed~~', 'UI:FavoriteOrganizations' => 'Organizações favoritas', 'UI:FavoriteOrganizations+' => 'Confira na lista abaixo as organizações que você deseja ver no menu drop-down para um acesso rápido.'. 'Note-se que esta não é uma configuração de segurança, objetos de qualquer organização ainda são visíveis e podem ser acessadas ao selecionar \"Todos Organizações\" na lista drop-down.', @@ -1283,5 +1292,4 @@ When associated with a trigger, each action is given an "order" number, specifyi 'UI:ConcurrentLockKilled' => 'The lock preventing modifications on the current object has been deleted.~~', 'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~', 'UI:Menu:ExportPDF' => 'Export as PDF...~~', -)); -?> +)); \ No newline at end of file diff --git a/dictionaries/ru.dictionary.itop.core.php b/dictionaries/ru.dictionary.itop.core.php index 1e167cf69..b46ac569c 100644 --- a/dictionaries/ru.dictionary.itop.core.php +++ b/dictionaries/ru.dictionary.itop.core.php @@ -31,6 +31,13 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'Core:AttributeBoolean' => 'Логич.', 'Core:AttributeBoolean+' => 'Boolean', + 'Core:AttributeBoolean/Value:null' => '', + 'Core:AttributeBoolean/Value:yes' => 'Yes~~', + 'Core:AttributeBoolean/Value:no' => 'No~~', + + 'Core:AttributeArchiveFlag/Value:yes' => 'Yes~~', + 'Core:AttributeArchiveFlag/Value:yes+' => 'This object is visible only in archive mode~~', + 'Core:AttributeArchiveFlag/Value:no' => 'No~~', 'Core:AttributeString' => 'Строка', 'Core:AttributeString+' => 'Alphanumeric string', diff --git a/dictionaries/ru.dictionary.itop.ui.php b/dictionaries/ru.dictionary.itop.ui.php index b438a4b04..3b404d8f1 100644 --- a/dictionaries/ru.dictionary.itop.ui.php +++ b/dictionaries/ru.dictionary.itop.ui.php @@ -748,6 +748,11 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s подробности', 'UI:ErrorPageTitle' => 'iTop - Ошибка', 'UI:ObjectDoesNotExist' => 'Извните, этот объект не существует (или вы не можете его видеть).', + 'UI:ObjectArchived' => 'This object has been archived. Please enable the archive mode or contact your administrator.~~', + 'Tag:Archived' => 'Archived~~', + 'Tag:Archived+' => 'Can be accessed only in archive mode~~', + 'Tag:Synchronized' => 'Synchronized~~', + 'ObjectRef:Archived' => 'Archived~~', 'UI:SearchResultsPageTitle' => 'iTop - Результаты поиска', 'UI:SearchResultsTitle' => 'Результаты поиска', 'UI:SearchResultsTitle+' => 'Результаты полнотекстового поиска', @@ -1054,6 +1059,10 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'UI:Pagination:All' => 'Все', 'UI:HierarchyOf_Class' => 'Иерархия по: %1$s~~', 'UI:Preferences' => 'Дополнительно...~~', + 'UI:ArchiveModeOn' => 'Activate archive mode~~', + 'UI:ArchiveModeOff' => 'Deactivate archive mode~~', + 'UI:ArchiveMode:Banner' => 'Archive mode~~', + 'UI:ArchiveMode:Banner+' => 'Archived objects are visible, and no modification is allowed~~', 'UI:FavoriteOrganizations' => 'Избранные организации', 'UI:FavoriteOrganizations+' => 'Check in the list below the organizations that you want to see in the drop-down menu for a quick access. Note that this is not a security setting, objects from any organization are still visible and can be accessed by selecting "All Organizations" in the drop-down list.', 'UI:FavoriteLanguage' => 'Язык пользовательского интерфейса', diff --git a/dictionaries/tr.dictionary.itop.core.php b/dictionaries/tr.dictionary.itop.core.php index cf907feaa..f1eafd79b 100644 --- a/dictionaries/tr.dictionary.itop.core.php +++ b/dictionaries/tr.dictionary.itop.core.php @@ -1,5 +1,5 @@ - * @copyright Copyright (C) 2010-2016 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ @@ -413,6 +413,13 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'Core:AttributeDecimal+' => 'Decimal value (could be negative)~~', 'Core:AttributeBoolean' => 'Boolean~~', 'Core:AttributeBoolean+' => 'Boolean~~', + 'Core:AttributeBoolean/Value:null' => '', + 'Core:AttributeBoolean/Value:yes' => 'Yes~~', + 'Core:AttributeBoolean/Value:no' => 'No~~', + + 'Core:AttributeArchiveFlag/Value:yes' => 'Yes~~', + 'Core:AttributeArchiveFlag/Value:yes+' => 'This object is visible only in archive mode~~', + 'Core:AttributeArchiveFlag/Value:no' => 'No~~', 'Core:AttributeString' => 'String~~', 'Core:AttributeString+' => 'Alphanumeric string~~', 'Core:AttributeClass' => 'Class~~', @@ -781,7 +788,4 @@ Operators:
'Core:Validator:Mandatory' => 'Please, fill this field~~', 'Core:Validator:MustBeInteger' => 'Must be an integer~~', 'Core:Validator:MustSelectOne' => 'Please, select one~~', -)); - - -?> +)); \ No newline at end of file diff --git a/dictionaries/tr.dictionary.itop.ui.php b/dictionaries/tr.dictionary.itop.ui.php index 9ff3537f7..8c73fecd9 100644 --- a/dictionaries/tr.dictionary.itop.ui.php +++ b/dictionaries/tr.dictionary.itop.ui.php @@ -1,5 +1,5 @@ - * @copyright Copyright (C) 2010-2012 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ @@ -645,6 +645,11 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s detayları', 'UI:ErrorPageTitle' => 'iTop - Hata', 'UI:ObjectDoesNotExist' => 'Nesne mevcut değil veya yetkiniz yok.', + 'UI:ObjectArchived' => 'This object has been archived. Please enable the archive mode or contact your administrator.~~', + 'Tag:Archived' => 'Archived~~', + 'Tag:Archived+' => 'Can be accessed only in archive mode~~', + 'Tag:Synchronized' => 'Synchronized~~', + 'ObjectRef:Archived' => 'Archived~~', 'UI:SearchResultsPageTitle' => 'iTop - Arama Sonuçları', 'UI:SearchResultsTitle' => 'Arama Sonuçları', 'UI:SearchResultsTitle+' => 'Full-text search results~~', @@ -1058,6 +1063,10 @@ Tetikleme gerçekleştiriğinde işlemler tanımlanan sıra numarası ile gerçe 'UI:Pagination:All' => 'All~~', 'UI:HierarchyOf_Class' => 'Hierarchy of %1$s~~', 'UI:Preferences' => 'Preferences...~~', + 'UI:ArchiveModeOn' => 'Activate archive mode~~', + 'UI:ArchiveModeOff' => 'Deactivate archive mode~~', + 'UI:ArchiveMode:Banner' => 'Archive mode~~', + 'UI:ArchiveMode:Banner+' => 'Archived objects are visible, and no modification is allowed~~', 'UI:FavoriteOrganizations' => 'Favorite Organizations~~', 'UI:FavoriteOrganizations+' => 'Check in the list below the organizations that you want to see in the drop-down menu for a quick access. Note that this is not a security setting, objects from any organization are still visible and can be accessed by selecting \"All Organizations\" in the drop-down list.~~', 'UI:FavoriteLanguage' => 'Language of the User Interface~~', @@ -1251,8 +1260,4 @@ Tetikleme gerçekleştiriğinde işlemler tanımlanan sıra numarası ile gerçe 'UI:ConcurrentLockKilled' => 'The lock preventing modifications on the current object has been deleted.~~', 'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~', 'UI:Menu:ExportPDF' => 'Export as PDF...~~', -)); - - - -?> +)); \ No newline at end of file diff --git a/dictionaries/zh.dictionary.itop.core.php b/dictionaries/zh.dictionary.itop.core.php index 23262909a..c0b72226b 100644 --- a/dictionaries/zh.dictionary.itop.core.php +++ b/dictionaries/zh.dictionary.itop.core.php @@ -1,5 +1,5 @@ - * @copyright Copyright (C) 2010-2016 Combodo SARL + * @copyright Copyright (C) 2010-2017 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ @@ -412,6 +412,12 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'Core:AttributeDecimal+' => 'Decimal value (could be negative)~~', 'Core:AttributeBoolean' => 'Boolean~~', 'Core:AttributeBoolean+' => 'Boolean~~', + 'Core:AttributeBoolean/Value:null' => '', + 'Core:AttributeBoolean/Value:yes' => 'Yes~~', + 'Core:AttributeBoolean/Value:no' => 'No~~', + 'Core:AttributeArchiveFlag/Value:yes' => 'Yes~~', + 'Core:AttributeArchiveFlag/Value:yes+' => 'This object is visible only in archive mode~~', + 'Core:AttributeArchiveFlag/Value:no' => 'No~~', 'Core:AttributeString' => 'String~~', 'Core:AttributeString+' => 'Alphanumeric string~~', 'Core:AttributeClass' => 'Class~~', @@ -780,7 +786,4 @@ Operators:
'Core:Validator:Mandatory' => 'Please, fill this field~~', 'Core:Validator:MustBeInteger' => 'Must be an integer~~', 'Core:Validator:MustSelectOne' => 'Please, select one~~', -)); - - -?> +)); \ No newline at end of file diff --git a/dictionaries/zh.dictionary.itop.ui.php b/dictionaries/zh.dictionary.itop.ui.php index ce2806695..e321f4a83 100644 --- a/dictionaries/zh.dictionary.itop.ui.php +++ b/dictionaries/zh.dictionary.itop.ui.php @@ -644,6 +644,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s 详细内容', 'UI:ErrorPageTitle' => 'iTop - 错误', 'UI:ObjectDoesNotExist' => '抱歉, 该对象不存在 (或您未被允许浏览该对象).', + 'UI:ObjectArchived' => 'This object has been archived. Please enable the archive mode or contact your administrator.~~', + 'Tag:Archived' => 'Archived~~', + 'Tag:Archived+' => 'Can be accessed only in archive mode~~', + 'Tag:Synchronized' => 'Synchronized~~', + 'ObjectRef:Archived' => 'Archived~~', 'UI:SearchResultsPageTitle' => 'iTop - 搜索结果', 'UI:SearchResultsTitle' => '搜索结果', 'UI:SearchResultsTitle+' => 'Full-text search results~~', @@ -1057,6 +1062,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'UI:Pagination:All' => 'All~~', 'UI:HierarchyOf_Class' => 'Hierarchy of %1$s~~', 'UI:Preferences' => 'Preferences...~~', + 'UI:ArchiveModeOn' => 'Activate archive mode~~', + 'UI:ArchiveModeOff' => 'Deactivate archive mode~~', + 'UI:ArchiveMode:Banner' => 'Archive mode~~', + 'UI:ArchiveMode:Banner+' => 'Archived objects are visible, and no modification is allowed~~', 'UI:FavoriteOrganizations' => 'Favorite Organizations~~', 'UI:FavoriteOrganizations+' => 'Check in the list below the organizations that you want to see in the drop-down menu for a quick access. Note that this is not a security setting, objects from any organization are still visible and can be accessed by selecting \"All Organizations\" in the drop-down list.~~', 'UI:FavoriteLanguage' => 'Language of the User Interface~~', diff --git a/js/breadcrumb.js b/js/breadcrumb.js index a0c5f28dc..d18cfc217 100644 --- a/js/breadcrumb.js +++ b/js/breadcrumb.js @@ -75,7 +75,8 @@ $(function() } else { - sBreadCrumbHtml += ''; + var sSanitizedUrl = StripArchiveArgument(oEntry['url']); + sBreadCrumbHtml += ''; } } } diff --git a/pages/UI.php b/pages/UI.php index a2b86841a..1cc4140b7 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -377,7 +377,25 @@ try $sMessageKey = $sClass.'::'.$id; DisplayMessages($sMessageKey, $oP); $oP->set_title(Dict::S('UI:ErrorPageTitle')); - $oP->P(Dict::S('UI:ObjectDoesNotExist')); + + // Attempt to load the object in archive mode + DBSearch::SetArchiveModeDefault(true); + if (is_numeric($id)) + { + $oObj = MetaModel::GetObject($sClass, $id, false /* MustBeFound */); + } + else + { + $oObj = MetaModel::GetObjectByName($sClass, $id, false /* MustBeFound */); + } + if (is_null($oObj)) + { + $oP->P(Dict::S('UI:ObjectDoesNotExist')); + } + else + { + $oP->P(Dict::S('UI:ObjectArchived')); + } } else { @@ -1745,5 +1763,4 @@ catch(Exception $e) IssueLog::Error($e->getMessage()); } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/portal/index.php b/portal/index.php index cd7b0fc2a..c2b463011 100644 --- a/portal/index.php +++ b/portal/index.php @@ -1310,7 +1310,11 @@ try require_once(APPROOT.'/application/loginwebpage.class.inc.php'); LoginWebPage::DoLogin(false /* bMustBeAdmin */, true /* IsAllowedToPortalUsers */); // Check user rights and prompt if needed - ApplicationContext::SetUrlMakerClass('MyPortalURLMaker'); + ApplicationContext::SetUrlMakerClass('MyPortalURLMaker'); + + $bArchiveMode = utils::IsArchiveMode(); + DBSearch::SetArchiveModeDefault($bArchiveMode); + if ($bArchiveMode) MetaModel::DBSetReadOnly(); $aClasses = explode(',', MetaModel::GetConfig()->Get('portal_tickets')); $sMainClass = trim(reset($aClasses)); diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index 7ea224cec..b58e38bb5 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -725,7 +725,7 @@ EOF $val = $sDefault; } } - return "'".$val."'"; + return "'".str_replace("'", "\\'", $val)."'"; } protected function GetMandatoryPropString($oNode, $sTag) @@ -989,6 +989,11 @@ EOF $aClassParams['indexes'] = var_export($aIndexes, true); } + if ($oArchive = $oProperties->GetOptionalElement('archive')) + { + $bEnabled = $this->GetPropBoolean($oArchive, 'enabled', false); + $aClassParams['archive'] = $bEnabled; + } // Finalize class params declaration //