Pārlūkot izejas kodu

NEW! Archiving data. Archiving is a soft delete. It can be undone. Enter the archive mode to see all the data including archives (everything is read-only in that mode). Archiving must be enabled per class (data model). Archiving is achieved by the mean of the API DBObject::Archive (or Unarchive).

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@4692 a333f486-631f-4898-b8df-5754b55c2be0
romainq 8 gadi atpakaļ
vecāks
revīzija
34ab6cd77b
54 mainītis faili ar 1402 papildinājumiem un 443 dzēšanām
  1. 7 3
      application/ajaxwebpage.class.inc.php
  2. 39 12
      application/cmdbabstract.class.inc.php
  3. 77 18
      application/itopwebpage.class.inc.php
  4. 4 2
      application/loginwebpage.class.inc.php
  5. 3 3
      application/portalwebpage.class.inc.php
  6. 17 3
      application/utils.inc.php
  7. 194 17
      core/attributedef.class.inc.php
  8. 205 168
      core/cmdbobject.class.inc.php
  9. 139 8
      core/dbobject.class.php
  10. 32 1
      core/dbobjectsearch.class.php
  11. 23 2
      core/dbsearch.class.php
  12. 119 47
      core/metamodel.class.php
  13. 4 6
      core/oql/expression.class.inc.php
  14. 21 2
      core/querybuildercontext.class.inc.php
  15. 1 1
      core/sqlobjectquery.class.inc.php
  16. 26 4
      core/userrights.class.inc.php
  17. 11 3
      core/valuesetdef.class.inc.php
  18. 4 4
      core/xmlbulkexport.class.inc.php
  19. 1 1
      css/css-variables.scss
  20. 90 35
      css/light-grey.css
  21. 71 8
      css/light-grey.scss
  22. 6 1
      datamodels/2.x/itop-portal-base/portal/web/index.php
  23. 9 2
      dictionaries/cs.dictionary.itop.core.php
  24. 4 2
      dictionaries/cs.dictionary.itop.ui.php
  25. 8 2
      dictionaries/da.dictionary.itop.core.php
  26. 11 2
      dictionaries/da.dictionary.itop.ui.php
  27. 8 2
      dictionaries/de.dictionary.itop.core.php
  28. 11 2
      dictionaries/de.dictionary.itop.ui.php
  29. 9 2
      dictionaries/dictionary.itop.core.php
  30. 11 2
      dictionaries/dictionary.itop.ui.php
  31. 9 2
      dictionaries/es_cr.dictionary.itop.core.php
  32. 12 4
      dictionaries/es_cr.dictionary.itop.ui.php
  33. 8 2
      dictionaries/fr.dictionary.itop.core.php
  34. 11 2
      dictionaries/fr.dictionary.itop.ui.php
  35. 9 4
      dictionaries/hu.dictionary.itop.core.php
  36. 12 4
      dictionaries/hu.dictionary.itop.ui.php
  37. 10 5
      dictionaries/it.dictionary.itop.core.php
  38. 12 4
      dictionaries/it.dictionary.itop.ui.php
  39. 9 4
      dictionaries/ja.dictionary.itop.core.php
  40. 12 4
      dictionaries/ja.dictionary.itop.ui.php
  41. 10 5
      dictionaries/nl.dictionary.itop.core.php
  42. 12 4
      dictionaries/nl.dictionary.itop.ui.php
  43. 10 5
      dictionaries/pt_br.dictionary.itop.core.php
  44. 12 4
      dictionaries/pt_br.dictionary.itop.ui.php
  45. 7 0
      dictionaries/ru.dictionary.itop.core.php
  46. 9 0
      dictionaries/ru.dictionary.itop.ui.php
  47. 10 6
      dictionaries/tr.dictionary.itop.core.php
  48. 12 7
      dictionaries/tr.dictionary.itop.ui.php
  49. 9 6
      dictionaries/zh.dictionary.itop.core.php
  50. 9 0
      dictionaries/zh.dictionary.itop.ui.php
  51. 2 1
      js/breadcrumb.js
  52. 20 3
      pages/UI.php
  53. 5 1
      portal/index.php
  54. 6 1
      setup/compiler.class.inc.php

+ 7 - 3
application/ajaxwebpage.class.inc.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
  * Simple web page with no includes, header or fancy formatting, useful to
  * Simple web page with no includes, header or fancy formatting, useful to
  * generate HTML fragments when called by an AJAX method
  * generate HTML fragments when called by an AJAX method
  *
  *
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -53,7 +53,11 @@ class ajax_page extends WebPage implements iTabbedPage
 		$this->sContentType = 'text/html';
 		$this->sContentType = 'text/html';
 		$this->sContentDisposition = 'inline';
 		$this->sContentDisposition = 'inline';
 		$this->m_sMenu = "";
 		$this->m_sMenu = "";
-    }	
+
+		$bArchiveMode = utils::IsArchiveMode();
+		DBSearch::SetArchiveModeDefault($bArchiveMode);
+		if ($bArchiveMode) MetaModel::DBSetReadOnly();
+    }
 
 
 	public function AddTabContainer($sTabContainer, $sPrefix = '')
 	public function AddTabContainer($sTabContainer, $sPrefix = '')
 	{
 	{

+ 39 - 12
application/cmdbabstract.class.inc.php

@@ -198,8 +198,6 @@ EOF
 			$oBlock->Display($oPage, -1);
 			$oBlock->Display($oPage, -1);
 		}
 		}
 
 
-		$oPage->add("<div class=\"page_header\"><h1>".$this->GetIcon()."&nbsp;\n");
-
 		// Master data sources
 		// Master data sources
 		$bSynchronized = false;
 		$bSynchronized = false;
 		$aIcons = array();
 		$aIcons = array();
@@ -292,16 +290,46 @@ EOF
 					$sTip .= "<p style=\"white-space:nowrap\">".$oDataSource->GetIcon(true, 'style="vertical-align:middle"')."&nbsp;$sLink<br/>";
 					$sTip .= "<p style=\"white-space:nowrap\">".$oDataSource->GetIcon(true, 'style="vertical-align:middle"')."&nbsp;$sLink<br/>";
 					$sTip .= Dict::S('Core:Synchro:LastSynchro') . '<br/>' . $sLastSynchro . "</p>";
 					$sTip .= Dict::S('Core:Synchro:LastSynchro') . '<br/>' . $sLastSynchro . "</p>";
 				}
 				}
-				$aIcons[] = '&nbsp;<img style="vertical-align:middle;" id="synchro_icon" src="../images/locked.png"/>';
+				$sLabel = htmlentities(Dict::S('Tag:Synchronized'), ENT_QUOTES, 'UTF-8');
+				$sSynchroTagId = 'synchro_icon-'.$this->GetKey();
+				$aIcons[] = "<div class=\"tag\" id=\"$sSynchroTagId\"><span class=\"object-synchronized fa fa-lock fa-1x\">&nbsp;</span>&nbsp;$sLabel</div>";
 				$sTip = addslashes($sTip);
 				$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)).": <span class=\"hilite\">".$this->GetName()."</span>$sIcons</h1>\n");
-		$oPage->add("</div>\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[] = "<div class=\"tag\" title=\"$sTitle\"><span class=\"object-archived fa fa-archive fa-1x\">&nbsp;</span>&nbsp;$sLabel</div>";
+		}
+
+		$sObjectIcon = $this->GetIcon();
+		$sClassName = MetaModel::GetName(get_class($this));
+		$sObjectName = $this->GetName();
+		if (count($aIcons) > 0)
+		{
+			$sTags = '<div class="tags">'.implode('&nbsp;', $aIcons).'</div>';
+		}
+		else
+		{
+			$sTags = '';
+		}
+
+		$oPage->add(
+<<<EOF
+<div class="page_header">
+   <div class="object-details-header">
+      <div class ="object-icon">$sObjectIcon</div>
+      <div class ="object-infos">
+		  <h1 class="object-name">$sClassName: <span class="hilite">$sObjectName</span></h1>
+		  $sTags
+      </div>
+   </div>
+</div>
+EOF
+		);
 	}
 	}
 
 
 	function DisplayBareHistory(WebPage $oPage, $bEditMode = false, $iLimitCount = 0, $iLimitStart = 0)
 	function DisplayBareHistory(WebPage $oPage, $bEditMode = false, $iLimitCount = 0, $iLimitStart = 0)
@@ -558,7 +586,6 @@ EOF
 				
 				
 				foreach($aNotificationClasses as $sNotifClass)
 				foreach($aNotificationClasses as $sNotifClass)
 				{
 				{
-					
 					$oPage->p(MetaModel::GetClassIcon($sNotifClass, true).'&nbsp;'.MetaModel::GetName($sNotifClass));
 					$oPage->p(MetaModel::GetClassIcon($sNotifClass, true).'&nbsp;'.MetaModel::GetName($sNotifClass));
 					$oBlock = new DisplayBlock($aNotifSearches[$sNotifClass], 'list', false);
 					$oBlock = new DisplayBlock($aNotifSearches[$sNotifClass], 'list', false);
 					$oBlock->Display($oPage, 'notifications_'.$sNotifClass, array('menu' => false));
 					$oBlock->Display($oPage, 'notifications_'.$sNotifClass, array('menu' => false));
@@ -1933,7 +1960,7 @@ EOF
 					$aEventsList[] ='validate';
 					$aEventsList[] ='validate';
 					$aEventsList[] ='change';
 					$aEventsList[] ='change';
 
 
-					$oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs);
+					$oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs, '', $value);
 					$sFieldName = $sFieldPrefix.$sAttCode.$sNameSuffix;
 					$sFieldName = $sFieldPrefix.$sAttCode.$sNameSuffix;
 					$aExtKeyParams = $aArgs;
 					$aExtKeyParams = $aArgs;
 					$aExtKeyParams['iFieldSize'] = $oAttDef->GetMaxSize();
 					$aExtKeyParams['iFieldSize'] = $oAttDef->GetMaxSize();
@@ -2526,7 +2553,7 @@ EOF
 					{
 					{
 						if ($oAttDef->IsExternalKey())
 						if ($oAttDef->IsExternalKey())
 						{
 						{
-							$oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs);
+							$oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs, '', $this->Get($sAttCode));
 							if ($oAllowedValues->Count() == 1)
 							if ($oAllowedValues->Count() == 1)
 							{
 							{
 								$oRemoteObj = $oAllowedValues->Fetch();
 								$oRemoteObj = $oAllowedValues->Fetch();

+ 77 - 18
application/itopwebpage.class.inc.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
 /**
 /**
  * Class iTopWebPage
  * Class iTopWebPage
  *
  *
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -34,7 +34,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
 {
 {
 	private $m_sMenu;
 	private $m_sMenu;
 	//	private $m_currentOrganization;
 	//	private $m_currentOrganization;
-	private $m_sMessage;
+	private $m_aMessages;
 	private $m_sInitScript;
 	private $m_sInitScript;
 	protected $m_oTabs;
 	protected $m_oTabs;
 	protected $bBreadCrumbEnabled;
 	protected $bBreadCrumbEnabled;
@@ -63,8 +63,12 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
 			$this->bBreadCrumbEnabled = false;
 			$this->bBreadCrumbEnabled = false;
 		}
 		}
 
 
+		$bArchiveMode = utils::IsArchiveMode();
+		DBSearch::SetArchiveModeDefault($bArchiveMode);
+		if ($bArchiveMode) MetaModel::DBSetReadOnly();
+
 		$this->m_sMenu = "";
 		$this->m_sMenu = "";
-		$this->m_sMessage = '';
+		$this->m_aMessages = array();
 		$this->SetRootUrl(utils::GetAbsoluteUrlAppRoot());
 		$this->SetRootUrl(utils::GetAbsoluteUrlAppRoot());
 		$this->add_header("Content-type: text/html; charset=utf-8");
 		$this->add_header("Content-type: text/html; charset=utf-8");
 		$this->add_header("Cache-control: no-cache");
 		$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/jquery.multiselect.css");
 		$this->add_linked_stylesheet("../css/magnific-popup.css");
 		$this->add_linked_stylesheet("../css/magnific-popup.css");
 		$this->add_linked_stylesheet("../css/c3.min.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.layout.min.js');
 		$this->add_linked_script('../js/jquery.ba-bbq.min.js');
 		$this->add_linked_script('../js/jquery.ba-bbq.min.js');
@@ -121,6 +126,23 @@ function ShowAboutBox()
 	});
 	});
 	return false;
 	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
 EOF
 			);
 			);
 		}
 		}
@@ -732,7 +754,7 @@ EOF
 
 
 		if (UserRights::IsAdministrator() && ExecutionKPI::IsEnabled())
 		if (UserRights::IsAdministrator() && ExecutionKPI::IsEnabled())
 		{
 		{
-			$sNorthPane .= '<div id="admin-banner"><span style="padding:5px;">'.ExecutionKPI::GetDescription().'<span></div>';
+			$sNorthPane .= '<div class="app-message"><span style="padding:5px;">'.ExecutionKPI::GetDescription().'<span></div>';
 		}
 		}
 		
 		
 		//$sSouthPane = '<p>Peak memory Usage: '.sprintf('%.3f MB', memory_get_peak_usage(true) / (1024*1024)).'</p>';
 		//$sSouthPane = '<p>Peak memory Usage: '.sprintf('%.3f MB', memory_get_peak_usage(true) / (1024*1024)).'</p>';
@@ -982,7 +1004,20 @@ EOF
 
 
 			$oPrefs = new URLPopupMenuItem('UI:Preferences', Dict::S('UI:Preferences'), utils::GetAbsoluteUrlAppRoot()."pages/preferences.php?".$oAppContext->GetForLink());
 			$oPrefs = new URLPopupMenuItem('UI:Preferences', Dict::S('UI:Preferences'), utils::GetAbsoluteUrlAppRoot()."pages/preferences.php?".$oAppContext->GetForLink());
 			$aActions[$oPrefs->GetUID()] = $oPrefs->GetMenuItem();
 			$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 = '<span class="fa fa-lock fa-1x"></span>';
+				$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())
 			if (utils::CanLogOff())
 			{
 			{
 				$oLogOff = new URLPopupMenuItem('UI:LogOffMenu', Dict::S('UI:LogOffMenu'), utils::GetAbsoluteUrlAppRoot().'pages/logoff.php?operation=do_logoff');
 				$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');
 				$sRestrictions = Dict::S('UI:AccessRO-Users');
 			}
 			}
 
 
-			$sApplicationBanner = '';
 			if (strlen($sRestrictions) > 0)
 			if (strlen($sRestrictions) > 0)
 			{
 			{
+				$sIcon =
+<<<EOF
+<span class="fa-stack fa-sm">
+  <i class="fa fa-pencil fa-flip-horizontal fa-stack-1x"></i>
+  <i class="fa fa-ban fa-stack-2x text-danger"></i>
+</span>
+EOF;
+
 				$sAdminMessage = trim(MetaModel::GetConfig()->Get('access_message'));
 				$sAdminMessage = trim(MetaModel::GetConfig()->Get('access_message'));
-				$sApplicationBanner .= '<div id="admin-banner">';
-				$sApplicationBanner .= '<img src="../images/locked.png" style="vertical-align:middle;">';
-				$sApplicationBanner .= '&nbsp;<b>'.$sRestrictions.'</b>';
 				if (strlen($sAdminMessage) > 0)
 				if (strlen($sAdminMessage) > 0)
 				{
 				{
-					$sApplicationBanner .= '&nbsp;<b>'.$sAdminMessage.'</b>';
+					$sRestrictions .= '&nbsp;'.$sAdminMessage;
 				}
 				}
-				$sApplicationBanner .= '</div>';
+				$this->AddApplicationMessage($sRestrictions, $sIcon);
 			}
 			}
 
 
-			if(strlen($this->m_sMessage))
+			$sApplicationMessages = '';
+			foreach ($this->m_aMessages as $aMessage)
 			{
 			{
-				$sApplicationBanner .= '<div id="admin-banner"><span style="padding:5px;">'.$this->m_sMessage.'<span></div>';
+				$sHtmlIcon = $aMessage['icon'] ? $aMessage['icon'] : '';
+				$sHtmlMessage = $aMessage['message'];
+				$sTitleAttr = $aMessage['tip'] ? 'title="'.htmlentities($aMessage['tip'], ENT_QUOTES, 'UTF-8').'"' : '';
+				$sApplicationMessages .= '<div class="app-message" '.$sTitleAttr.'><span class="app-message-icon">'.$sHtmlIcon.'</span><span class="app-message-body">'.$sHtmlMessage.'</div></span>';
 			}
 			}
 
 
-			$sApplicationBanner .= $sBannerExtraHtml;
+			$sApplicationBanner = "<div class=\"app-banner ui-helper-clearfix\">$sApplicationMessages$sBannerExtraHtml</div>";
 			
 			
 			if (!empty($sNorthPane))
 			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 = '<span class="fa fa-comment fa-1x"></span>';
+		$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
+			);
+		}
 	}
 	}
 }
 }

+ 4 - 2
application/loginwebpage.class.inc.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
 /**
 /**
  * Class LoginWebPage
  * Class LoginWebPage
  *
  *
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -430,6 +430,8 @@ EOF
 		// Unset all of the session variables.
 		// Unset all of the session variables.
 		unset($_SESSION['auth_user']);
 		unset($_SESSION['auth_user']);
 		unset($_SESSION['login_mode']);
 		unset($_SESSION['login_mode']);
+		unset($_SESSION['archive_mode']);
+		unset($_SESSION['archive_allowed']);
 		UserRights::_ResetSessionCache();
 		UserRights::_ResetSessionCache();
 		// If it's desired to kill the session, also delete the session cookie.
 		// If it's desired to kill the session, also delete the session cookie.
 		// Note: This will destroy the session, and not just the session data!
 		// Note: This will destroy the session, and not just the session data!

+ 3 - 3
application/portalwebpage.class.inc.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2013 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -19,7 +19,7 @@
 /**
 /**
  * Class PortalWebPage
  * Class PortalWebPage
  *
  *
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -387,7 +387,7 @@ EOF
 		{
 		{
 			$sReadOnly = Dict::S('UI:AccessRO-Users');
 			$sReadOnly = Dict::S('UI:AccessRO-Users');
 			$sAdminMessage = trim(MetaModel::GetConfig()->Get('access_message'));
 			$sAdminMessage = trim(MetaModel::GetConfig()->Get('access_message'));
-			$sApplicationBanner .= '<div id="admin-banner">';
+			$sApplicationBanner .= '<div class="app-message">';
 			$sApplicationBanner .= '<img src="../images/locked.png" style="vertical-align:middle;">';
 			$sApplicationBanner .= '<img src="../images/locked.png" style="vertical-align:middle;">';
 			$sApplicationBanner .= '&nbsp;<b>'.$sReadOnly.'</b>';
 			$sApplicationBanner .= '&nbsp;<b>'.$sReadOnly.'</b>';
 			if (strlen($sAdminMessage) > 0)
 			if (strlen($sAdminMessage) > 0)

+ 17 - 3
application/utils.inc.php

@@ -1,7 +1,7 @@
 <?php
 <?php
 use Html2Text\Html2Text;
 use Html2Text\Html2Text;
 use Leafo\ScssPhp\Compiler;
 use Leafo\ScssPhp\Compiler;
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -22,7 +22,7 @@ use Leafo\ScssPhp\Compiler;
 /**
 /**
  * Static class utils
  * Static class utils
  *
  *
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -140,6 +140,20 @@ class utils
 		}
 		}
 	}
 	}
 
 
+	public static function IsArchiveMode()
+	{
+		if (isset($_SESSION['archive_mode']))
+		{
+			$iDefault = $_SESSION['archive_mode'];
+		}
+		else
+		{
+			$iDefault = 0;
+		}
+		$iCurrent = self::ReadParam('with-archive', $iDefault, true);
+		$_SESSION['archive_mode'] = $iCurrent;
+		return ($iCurrent == 1);
+	}
 
 
 	public static function ReadParam($sName, $defaultValue = "", $bAllowCLI = false, $sSanitizationFilter = 'parameter')
 	public static function ReadParam($sName, $defaultValue = "", $bAllowCLI = false, $sSanitizationFilter = 'parameter')
 	{
 	{
@@ -700,7 +714,7 @@ class utils
 		}
 		}
 		return $bResult;
 		return $bResult;
 	}
 	}
-	
+
 	/**
 	/**
 	 * Initializes the CAS client
 	 * Initializes the CAS client
 	 */
 	 */

+ 194 - 17
core/attributedef.class.inc.php

@@ -232,7 +232,8 @@ abstract class AttributeDefinition
 	public function IsExternalKey($iType = EXTKEY_RELATIVE) {return false;} 
 	public function IsExternalKey($iType = EXTKEY_RELATIVE) {return false;} 
 	public function IsHierarchicalKey() {return false;}
 	public function IsHierarchicalKey() {return false;}
 	public function IsExternalField() {return false;} 
 	public function IsExternalField() {return false;} 
-	public function IsWritable() {return false;} 
+	public function IsWritable() {return false;}
+	public function IsMagic() {return $this->GetOptional('magic', false);}
 	public function LoadInObject() {return true;}
 	public function LoadInObject() {return true;}
 	public function LoadFromDB() {return true;}
 	public function LoadFromDB() {return true;}
 	public function AlwaysLoadInTables() {return $this->GetOptional('always_load_in_tables', false);}
 	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 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 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 RequiresIndex() {return false;}
+	public function CopyOnAllTables() {return false;}
 
 
 	public function GetOrderBySQLExpressions($sClassAlias)
 	public function GetOrderBySQLExpressions($sClassAlias)
 	{
 	{
@@ -1416,7 +1418,7 @@ class AttributeDBFieldVoid extends AttributeDefinition
 
 
 	public function IsDirectField() {return true;} 
 	public function IsDirectField() {return true;} 
 	public function IsScalar() {return true;} 
 	public function IsScalar() {return true;} 
-	public function IsWritable() {return true;} 
+	public function IsWritable() {return !$this->IsMagic();}
 	public function GetSQLExpr()
 	public function GetSQLExpr()
 	{
 	{
 		return $this->Get("sql");
 		return $this->Get("sql");
@@ -1799,7 +1801,7 @@ class AttributeBoolean extends AttributeInteger
 
 
 	public function GetEditClass() {return "Integer";}
 	public function GetEditClass() {return "Integer";}
 	protected function GetSQLCol($bFullSpec = false) {return "TINYINT(1)".($bFullSpec ? $this->GetSQLColSpec() : '');}
 	protected function GetSQLCol($bFullSpec = false) {return "TINYINT(1)".($bFullSpec ? $this->GetSQLColSpec() : '');}
-	
+
 	public function MakeRealValue($proposedValue, $oHostObj)
 	public function MakeRealValue($proposedValue, $oHostObj)
 	{
 	{
 		if (is_null($proposedValue)) return null;
 		if (is_null($proposedValue)) return null;
@@ -1814,25 +1816,169 @@ class AttributeBoolean extends AttributeInteger
 		return 0;
 		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)
+	{
+		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($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 = "<span title=\"$sDescription\">".parent::GetAsHtml($sLabel)."</span>";
+		}
+		else
+		{
+			$sRes = $bValue ? 'yes' : 'no';
+		}
+		return $sRes;
 	}
 	}
-	public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
+
+	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)
 	{
 	{
-		return $sValue ? '1' : '0';
+		if (is_null($value))
+		{
+			return '';
+		}
+		else
+		{
+			return $this->GetValueLabel($value);
+		}
 	}
 	}
+
 	/**
 	/**
 	 * Helper to get a value that will be JSON encoded
 	 * 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)
 	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;
 		return false;
 	}
 	}
+	public function IsMagic()
+	{
+		return true;
+	}
 
 
 	public function RequiresIndex()
 	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();
 		$oValSetDef = $this->GetValuesDef();
-		$oSet = $oValSetDef->ToObjectSet($aArgs, $sContains);
+		$oSet = $oValSetDef->ToObjectSet($aArgs, $sContains, $iAdditionalValue);
 		return $oSet;
 		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();
 		$oValSetDef = $this->GetValuesDef();
 		if (array_key_exists('this', $aArgs))
 		if (array_key_exists('this', $aArgs))
@@ -4551,7 +4701,7 @@ class AttributeHierarchicalKey extends AttributeExternalKey
 				$oValSetDef->AddCondition($oFilter);
 				$oValSetDef->AddCondition($oFilter);
 			}
 			}
 		}
 		}
-		$oSet = $oValSetDef->ToObjectSet($aArgs, $sContains);
+		$oSet = $oValSetDef->ToObjectSet($aArgs, $sContains, $iAdditionalValue);
 		return $oSet;
 		return $oSet;
 	}
 	}
 
 
@@ -5265,7 +5415,7 @@ class AttributeStopWatch extends AttributeDefinition
 	
 	
 	public function IsDirectField() {return true;} 
 	public function IsDirectField() {return true;} 
 	public function IsScalar() {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 GetDefaultValue(DBObject $oHostObject = null) {return $this->NewStopWatch();}
 
 
 	public function GetEditValue($value, $oHostObj = null)
 	public function GetEditValue($value, $oHostObj = null)
@@ -6630,6 +6780,10 @@ class AttributeFriendlyName extends AttributeComputedFieldVoid
 	{
 	{
 		return false;
 		return false;
 	}
 	}
+	public function IsMagic()
+	{
+		return true;
+	}
 
 
 	public function IsDirectField()
 	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;
+	}
+}

+ 205 - 168
core/cmdbobject.class.inc.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
 /**
 /**
  * Class cmdbObject
  * Class cmdbObject
  *
  *
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -231,206 +231,219 @@ abstract class CMDBObject extends DBObject
 		$iId = $oMyChangeOp->DBInsertNoReload();
 		$iId = $oMyChangeOp->DBInsertNoReload();
 	}
 	}
 
 
-	protected function RecordAttChanges(array $aValues, array $aOrigValues)
+	/**
+	 * @param $sAttCode
+	 * @param $original Original value
+	 * @param $value Current value
+	 */
+	protected function RecordAttChange($sAttCode, $original, $value)
 	{
 	{
-		parent::RecordAttChanges($aValues, $aOrigValues);
+		$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
+		if ($oAttDef->IsExternalField()) return;
+		if ($oAttDef->IsLinkSet()) return;
+		if ($oAttDef->GetTrackingLevel() == ATTRIBUTE_TRACKING_NONE) return;
 
 
-		// $aValues is an array of $sAttCode => $value
-		//
-		foreach ($aValues as $sAttCode=> $value)
+		if ($oAttDef instanceOf AttributeOneWayPassword)
 		{
 		{
-			$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
-			if ($oAttDef->IsExternalField()) continue;
-			if ($oAttDef->IsLinkSet()) continue;
-			if ($oAttDef->GetTrackingLevel() == ATTRIBUTE_TRACKING_NONE) continue;
+			// 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 (array_key_exists($sAttCode, $aOrigValues))
+			if (is_null($original))
 			{
 			{
-				$original = $aOrigValues[$sAttCode];
+				$original = '';
 			}
 			}
-			else
+			$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 = null;
+				$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 ($oAttDef instanceOf AttributeOneWayPassword)
+			if (is_null($original))
 			{
 			{
-				// 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();
+				$original = new ormDocument();
 			}
 			}
-			elseif ($oAttDef instanceOf AttributeEncryptedString)
+			$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)
 			{
 			{
-				// 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);
+				$item_value = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $value, $this);
+				$item_original = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $original, $this);
 
 
-				if (is_null($original))
+				if ($item_value != $item_original)
 				{
 				{
-					$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();
 				}
 				}
-				$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);
+		}
+		elseif ($oAttDef instanceOf AttributeCaseLog)
+		{
+			$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCaseLog");
+			$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)
+			$oMyChangeOp->Set("lastentry", $value->GetLatestEntryIndex());
+			$iId = $oMyChangeOp->DBInsertNoReload();
+		}
+		elseif ($oAttDef instanceOf AttributeLongText)
+		{
+			// Data blobs
+			if ($oAttDef->GetFormat() == 'html')
 			{
 			{
-				// 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();
-					}
-				}
+				$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
 			}
 			}
-			elseif ($oAttDef instanceOf AttributeCaseLog)
+			else
 			{
 			{
-				$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();
+				$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeLongText");
 			}
 			}
-			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);
+			$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)
+			if (!is_null($original) && ($original instanceof ormCaseLog))
 			{
 			{
-				// 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();
+				$original = $original->GetText();
 			}
 			}
-			elseif ($oAttDef instanceOf AttributeBoolean)
+			$oMyChangeOp->Set("prevdata", $original);
+			$iId = $oMyChangeOp->DBInsertNoReload();
+		}
+		elseif ($oAttDef instanceOf AttributeText)
+		{
+			// Data blobs
+			if ($oAttDef->GetFormat() == 'html')
 			{
 			{
-				$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();
+				$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
 			}
 			}
-			elseif ($oAttDef instanceOf AttributeHierarchicalKey)
+			else
 			{
 			{
-				// 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();
+				$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeText");
 			}
 			}
-			elseif ($oAttDef instanceOf AttributeCustomFields)
+			$oMyChangeOp->Set("objclass", get_class($this));
+			$oMyChangeOp->Set("objkey", $this->GetKey());
+			$oMyChangeOp->Set("attcode", $sAttCode);
+
+			if (!is_null($original) && ($original instanceof ormCaseLog))
 			{
 			{
-				// 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();
+				$original = $original->GetText();
 			}
 			}
-			elseif ($oAttDef instanceOf AttributeURL)
+			$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);
+
+		// $aValues is an array of $sAttCode => $value
+		//
+		foreach ($aValues as $sAttCode=> $value)
+		{
+			if (array_key_exists($sAttCode, $aOrigValues))
 			{
 			{
-				// 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();
+				$original = $aOrigValues[$sAttCode];
 			}
 			}
 			else
 			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();
+				$original = null;
 			}
 			}
+			$this->RecordAttChange($sAttCode, $original, $value);
 		}
 		}
 	}
 	}
 
 
@@ -596,6 +609,30 @@ abstract class CMDBObject extends DBObject
 		}
 		}
 		return $ret;
 		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);
 			$oRetSet->AddObjectExtended($aObjectsByClassAlias);
 		}
 		}
 		return $oRetSet;
 		return $oRetSet;
-	} 
+	}
 }
 }

+ 139 - 8
core/dbobject.class.php

@@ -226,6 +226,18 @@ abstract class DBObject implements iDisplay
 
 
 			$oLinkSearch = new DBObjectSearch($sLinkClass);
 			$oLinkSearch = new DBObjectSearch($sLinkClass);
 			$oLinkSearch->AddCondition_PointingTo($oMyselfSearch, $sExtKeyToMe);
 			$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);
 			$oLinks = new DBObjectSet($oLinkSearch);
 
 
 			$this->m_aCurrValues[$sAttCode] = $oLinks;
 			$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
 			// Ignore it - this attribute is set upon object creation and that's it
 			return false;
 			return false;
 		}
 		}
-		
+
 		$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
 		$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)
 		if ($this->m_bIsInDB && !$this->m_bFullyLoaded && !$this->m_bDirty)
 		{
 		{
 			// First time Set is called... ensure that the object gets fully loaded
 			// First time Set is called... ensure that the object gets fully loaded
@@ -742,7 +761,8 @@ abstract class DBObject implements iDisplay
 			else
 			else
 			{
 			{
 				$sHtmlLabel = htmlentities($this->Get($sAttCode.'_friendlyname'), ENT_QUOTES, 'UTF-8');
 				$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 &lt;)
 	 * @param string $sHtmlLabel Label with HTML entities escaped (< escaped as &lt;)
 	 * @param null $sUrlMakerClass
 	 * @param null $sUrlMakerClass
 	 * @param bool|true $bWithNavigationContext
 	 * @param bool|true $bWithNavigationContext
+	 * @param bool|false $bArchived
 	 * @return string
 	 * @return string
 	 * @throws DictExceptionMissingString
 	 * @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 '<em>'.Dict::S('UI:UndefinedObject').'</em>'; // Objects built in memory have negative IDs
 		if ($sObjKey <= 0) return '<em>'.Dict::S('UI:UndefinedObject').'</em>'; // Objects built in memory have negative IDs
 
 
@@ -847,19 +868,51 @@ abstract class DBObject implements iDisplay
 		}
 		}
 		$sHint = MetaModel::GetName($sObjClass)."::$sObjKey";
 		$sHint = MetaModel::GetName($sObjClass)."::$sObjKey";
 		$sUrl = ApplicationContext::MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass, $bWithNavigationContext);
 		$sUrl = ApplicationContext::MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass, $bWithNavigationContext);
-		if (strlen($sUrl) > 0)
+
+		$bClickable = !$bArchived || utils::IsArchiveMode();
+		if ($bArchived)
 		{
 		{
-			return "<a href=\"$sUrl\" title=\"$sHint\">$sHtmlLabel</a>";
+			$sSpanClass = 'archived';
+			$sFA = 'fa-archive object-archived';
+			$sHint = Dict::S('ObjectRef:Archived');
 		}
 		}
 		else
 		else
 		{
 		{
-			return $sHtmlLabel;
+			$sSpanClass = '';
+			$sFA = '';
+		}
+		if ($sFA == '')
+		{
+			$sIcon = '';
 		}
 		}
+		else
+		{
+			if ($bClickable)
+			{
+				$sIcon = "<span class=\"object-ref-icon fa $sFA fa-1x fa-fw\"></span>";
+			}
+			else
+			{
+				$sIcon = "<span class=\"object-ref-icon-disabled fa $sFA fa-1x fa-fw\"></span>";
+			}
+		}
+
+		if ($bClickable && (strlen($sUrl) > 0))
+		{
+			$sHLink = "<a class=\"object-ref-link\" href=\"$sUrl\">$sIcon$sHtmlLabel</a>";
+		}
+		else
+		{
+			$sHLink = $sIcon.$sHtmlLabel;
+		}
+		$sRet = "<span class=\"object-ref $sSpanClass\" title=\"$sHint\">$sHLink</span>";
+		return $sRet;
 	}
 	}
 
 
 	public function GetHyperlink($sUrlMakerClass = null, $bWithNavigationContext = true)
 	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)
 	public static function ComputeStandardUIPage($sClass)
@@ -1232,7 +1285,7 @@ abstract class DBObject implements iDisplay
 
 
 		$aChanges = $this->ListChanges();
 		$aChanges = $this->ListChanges();
 
 
-		foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
+		foreach($aChanges as $sAttCode => $value)
 		{
 		{
 			$res = $this->CheckValue($sAttCode);
 			$res = $this->CheckValue($sAttCode);
 			if ($res !== true)
 			if ($res !== true)
@@ -3544,5 +3597,83 @@ abstract class DBObject implements iDisplay
 				throw new Exception("Invalid verb");
 				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;
+	}
 }
 }
 
 

+ 32 - 1
core/dbobjectsearch.class.php

@@ -1467,6 +1467,7 @@ class DBObjectSearch extends DBSearch
 			{
 			{
 				$sRawId .= implode(',', $aSelectedClasses); // Unions may alter the list of selected columns
 				$sRawId .= implode(',', $aSelectedClasses); // Unions may alter the list of selected columns
 			}
 			}
+			$sRawId .= $oSearch->GetArchiveMode() ? '--arch' : '';
 			$sOqlId = md5($sRawId);
 			$sOqlId = md5($sRawId);
 		}
 		}
 		else
 		else
@@ -1552,6 +1553,7 @@ class DBObjectSearch extends DBSearch
 			$oSQLQuery->SetSelect($oBuild->m_oQBExpressions->GetSelect());
 			$oSQLQuery->SetSelect($oBuild->m_oQBExpressions->GetSelect());
 		}
 		}
 
 
+		$aMandatoryTables = null;
 		if (self::$m_bOptimizeQueries)
 		if (self::$m_bOptimizeQueries)
 		{
 		{
 			if ($bGetCount)
 			if ($bGetCount)
@@ -1562,6 +1564,17 @@ class DBObjectSearch extends DBSearch
 			$oBuild->m_oQBExpressions->GetMandatoryTables($aMandatoryTables);
 			$oBuild->m_oQBExpressions->GetMandatoryTables($aMandatoryTables);
 			$oSQLQuery->OptimizeJoins($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;
 		return $oSQLQuery;
 	}
 	}
@@ -1869,7 +1882,7 @@ class DBObjectSearch extends DBSearch
 		$oBuild->m_oQBExpressions->GetUnresolvedFields($sTargetAlias, $aExpectedAtts);
 		$oBuild->m_oQBExpressions->GetUnresolvedFields($sTargetAlias, $aExpectedAtts);
 		
 		
 		$bIsOnQueriedClass = array_key_exists($sTargetAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
 		$bIsOnQueriedClass = array_key_exists($sTargetAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
-		
+
 		self::DbgTrace("Entering: tableclass=$sTableClass, filter=".$this->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
 		self::DbgTrace("Entering: tableclass=$sTableClass, filter=".$this->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
 
 
 		// 1 - SELECT and UPDATE
 		// 1 - SELECT and UPDATE
@@ -2098,6 +2111,24 @@ class DBObjectSearch extends DBSearch
 		$oBuild->m_oQBExpressions->Translate($aTranslation, false);
 		$oBuild->m_oQBExpressions->Translate($aTranslation, false);
 //echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."</pre></p>\n";
 //echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."</pre></p>\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());
 		//MyHelpers::var_dump_html($oSelectBase->RenderSelect());
 		return $oSelectBase;
 		return $oSelectBase;
 	}
 	}

+ 23 - 2
core/dbsearch.class.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2015-2016 Combodo SARL
+// Copyright (C) 2015-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -33,7 +33,7 @@ require_once('dbunionsearch.class.php');
  *    - do not provide a type-hint for function parameters defined in the modules
  *    - do not provide a type-hint for function parameters defined in the modules
  *    - leave the statements DBObjectSearch::FromOQL in the modules, though DBSearch is more relevant 
  *    - leave the statements DBObjectSearch::FromOQL in the modules, though DBSearch is more relevant 
  *
  *
- * @copyright   Copyright (C) 2015-2016 Combodo SARL
+ * @copyright   Copyright (C) 2015-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
  
  
@@ -44,9 +44,11 @@ abstract class DBSearch
 
 
 	protected $m_bNoContextParameters = false;
 	protected $m_bNoContextParameters = false;
 	protected $m_aModifierProperties = array();
 	protected $m_aModifierProperties = array();
+	protected $m_bArchiveMode = false;
 
 
 	public function __construct()
 	public function __construct()
 	{
 	{
+		$this->m_bArchiveMode = self::GetArchiveModeDefault();
 	}
 	}
 
 
 	/**
 	/**
@@ -60,6 +62,25 @@ abstract class DBSearch
 	abstract public function AllowAllData();
 	abstract public function AllowAllData();
 	abstract public function IsAllDataAllowed();
 	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 NoContextParameters() {$this->m_bNoContextParameters = true;}
 	public function HasContextParameters() {return $this->m_bNoContextParameters;}
 	public function HasContextParameters() {return $this->m_bNoContextParameters;}
 
 

+ 119 - 47
core/metamodel.class.php

@@ -319,6 +319,11 @@ abstract class MetaModel
 		self::_check_subclass($sClass);	
 		self::_check_subclass($sClass);	
 		return (self::$m_aClassParams[$sClass]["key_type"] == "autoincrement");
 		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)
 	final static public function GetNameSpec($sClass)
 	{
 	{
 		self::_check_subclass($sClass);
 		self::_check_subclass($sClass);
@@ -559,12 +564,13 @@ abstract class MetaModel
 		self::_check_subclass($sClass);
 		self::_check_subclass($sClass);
 		if (isset(self::$m_aClassParams[$sClass]['indexes']))
 		if (isset(self::$m_aClassParams[$sClass]['indexes']))
 		{
 		{
-			return self::$m_aClassParams[$sClass]['indexes'];
+			$aRet = self::$m_aClassParams[$sClass]['indexes'];
 		}
 		}
 		else
 		else
 		{
 		{
-			return array();
+			$aRet = array();
 		}
 		}
+		return $aRet;
 	}
 	}
 
 
 	final static public function DBGetKey($sClass)
 	final static public function DBGetKey($sClass)
@@ -1639,10 +1645,10 @@ abstract class MetaModel
 		return $oFltDef->GetAllowedValues($aArgs, $sContains);
 		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);
 		$oAttDef = self::GetAttributeDef($sClass, $sAttCode);
-		return $oAttDef->GetAllowedValuesAsObjectSet($aArgs, $sContains);
+		return $oAttDef->GetAllowedValuesAsObjectSet($aArgs, $sContains, $iAdditionalValue);
 	}
 	}
 	//
 	//
 	// Businezz model declaration verbs (should be static)
 	// Businezz model declaration verbs (should be static)
@@ -1672,6 +1678,24 @@ abstract class MetaModel
 		self::$m_aRelationInfos[$sRelCode] = $sRelCode;
 		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...
 	// Must be called once and only once...
 	public static function InitClasses($sTablePrefix)
 	public static function InitClasses($sTablePrefix)
 	{
 	{
@@ -1726,6 +1750,23 @@ abstract class MetaModel
 					if ($oMethod->getDeclaringClass()->name == $sPHPClass)
 					if ($oMethod->getDeclaringClass()->name == $sPHPClass)
 					{
 					{
 						call_user_func(array($sPHPClass, 'Init'));
 						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)
 						foreach (MetaModel::EnumPlugins('iOnClassInitialization') as $sPluginClass => $oClassInit)
 						{
 						{
 							$oClassInit->OnAfterClassInitialization($sPHPClass);
 							$oClassInit->OnAfterClassInitialization($sPHPClass);
@@ -1757,13 +1798,7 @@ abstract class MetaModel
 					"is_null_allowed"=>false,
 					"is_null_allowed"=>false,
 					"depends_on"=>array()
 					"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)
 			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");
 					throw new CoreException("Class $sChildClass, 'finalclass' is a reserved keyword, it cannot be used as a filter code");
 				}
 				}
 				$oCloned = clone $oClassAtt;
 				$oCloned = clone $oClassAtt;
-				$oCloned->SetHostClass($sChildClass);
 				$oCloned->SetFixedValue($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
 			// Create the friendly name attribute
 			$sFriendlyNameAttCode = 'friendlyname'; 
 			$sFriendlyNameAttCode = 'friendlyname'; 
 			$oFriendlyName = new AttributeFriendlyName($sFriendlyNameAttCode, 'id');
 			$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();
 			self::$m_aExtKeyFriends[$sClass] = array();
 			foreach (self::$m_aAttribDefs[$sClass] as $sAttCode => $oAttDef)
 			foreach (self::$m_aAttribDefs[$sClass] as $sAttCode => $oAttDef)
 			{
 			{
@@ -1840,26 +1886,15 @@ abstract class MetaModel
 						$sKeyAttCode = $oAttDef->GetKeyAttCode();
 						$sKeyAttCode = $oAttDef->GetKeyAttCode();
 						$sRemoteAttCode = $oAttDef->GetExtAttCode()."_friendlyname";
 						$sRemoteAttCode = $oAttDef->GetExtAttCode()."_friendlyname";
 						$sFriendlyNameAttCode = $sAttCode.'_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 = 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
 					else
 					{
 					{
 						// Create the friendly name attribute
 						// Create the friendly name attribute
 						$sFriendlyNameAttCode = $sAttCode.'_friendlyname'; 
 						$sFriendlyNameAttCode = $sAttCode.'_friendlyname'; 
 						$oFriendlyName = new AttributeFriendlyName($sFriendlyNameAttCode, $sAttCode);
 						$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))
 						if (self::HasChildrenClasses($sRemoteClass))
 						{
 						{
@@ -1872,13 +1907,7 @@ abstract class MetaModel
 									"is_null_allowed"=>true,
 									"is_null_allowed"=>true,
 									"depends_on"=>array()
 									"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
 							// Add it to the ZLists where the external key is present
 							//foreach(self::$m_aListData[$sClass] as $sListCode => $aAttributes)
 							//foreach(self::$m_aListData[$sClass] as $sListCode => $aAttributes)
@@ -1924,6 +1953,13 @@ abstract class MetaModel
 							self::$m_aExtKeyFriends[$sClass][$sAttCode][$oExtField->GetCode()] = $oExtField;
 							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)
 				if ($oAttDef instanceof AttributeMetaEnum)
 				{
 				{
@@ -2467,6 +2503,19 @@ abstract class MetaModel
 		}
 		}
 		return $aRes;
 		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)
 	public static function HasChildrenClasses($sClass)
 	{
 	{
 		return (count(self::$m_aChildClasses[$sClass]) > 0);
 		return (count(self::$m_aChildClasses[$sClass]) > 0);
@@ -3401,6 +3450,12 @@ abstract class MetaModel
 		return $aDataDump;
 		return $aDataDump;
 	}
 	}
 
 
+	protected static $m_bReadOnlyMode = false;
+	public static function DBSetReadOnly()
+	{
+		self::$m_bReadOnlyMode = true;
+	}
+
 	/*
 	/*
 	* Determines wether the target DB is frozen or not
 	* Determines wether the target DB is frozen or not
 	*/		
 	*/		
@@ -3408,6 +3463,10 @@ abstract class MetaModel
 	{
 	{
 		// Improvement: check the mySQL variable -> Read-only
 		// Improvement: check the mySQL variable -> Read-only
 
 
+		if (self::$m_bReadOnlyMode)
+		{
+			return true;
+		}
 		if (UserRights::IsAdministrator())
 		if (UserRights::IsAdministrator())
 		{
 		{
 			return (!self::DBHasAccess(ACCESS_ADMIN_WRITE));
 			return (!self::DBHasAccess(ACCESS_ADMIN_WRITE));
@@ -3620,8 +3679,11 @@ abstract class MetaModel
 			$aTableInfo['Fields'][$sKeyField]['used'] = true;
 			$aTableInfo['Fields'][$sKeyField]['used'] = true;
 			foreach(self::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef)
 			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)
 				foreach($oAttDef->GetSQLColumns(true) as $sField => $sDBFieldSpec)
 				{
 				{
 					// Keep track of columns used by iTop
 					// Keep track of columns used by iTop
@@ -3725,7 +3787,7 @@ abstract class MetaModel
 					}
 					}
 				}
 				}
 			}
 			}
-			
+
 			// Find out unused columns
 			// Find out unused columns
 			//
 			//
 			foreach($aTableInfo['Fields'] as $sField => $aFieldData)
 			foreach($aTableInfo['Fields'] as $sField => $aFieldData)
@@ -4538,6 +4600,7 @@ abstract class MetaModel
 			$sModifierProperties = json_encode($aModifierProperties);
 			$sModifierProperties = json_encode($aModifierProperties);
 			$sQuerySign .= '_all_'.md5($sModifierProperties);
 			$sQuerySign .= '_all_'.md5($sModifierProperties);
 		}
 		}
+		$sQuerySign .= DBSearch::GetArchiveModeDefault() ? '_arch_' : '';
 
 
 		if (!array_key_exists($sQuerySign, self::$aQueryCacheGetObject))
 		if (!array_key_exists($sQuerySign, self::$aQueryCacheGetObject))
 		{
 		{
@@ -4628,6 +4691,15 @@ abstract class MetaModel
 		return self::GetObjectByRow($sClass, $aRow);
 		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)
 	public static function GetObjectByName($sClass, $sName, $bMustBeFound = true)
 	{
 	{
 		self::_check_subclass($sClass);	
 		self::_check_subclass($sClass);	

+ 4 - 6
core/oql/expression.class.inc.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
 /**
 /**
  * General definition of an expression tree (could be OQL, SQL or whatever) 
  * General definition of an expression tree (could be OQL, SQL or whatever) 
  *
  *
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -579,7 +579,7 @@ class FieldExpression extends UnaryExpression
 			$iObjKey = (int)$sValue;
 			$iObjKey = (int)$sValue;
 			if ($iObjKey > 0)
 			if ($iObjKey > 0)
 			{
 			{
-				$oObject = MetaModel::GetObject($sObjClass, $iObjKey);
+				$oObject = MetaModel::GetObjectWithArchive($sObjClass, $iObjKey);
 				$sRes = $oObject->GetHyperlink();
 				$sRes = $oObject->GetHyperlink();
 			}
 			}
 			else
 			else
@@ -1403,6 +1403,4 @@ class QueryBuilderExpressions
 			$this->m_aJoinFields[$index] = $oExpression->RenameParam($sOldName, $sNewName);
 			$this->m_aJoinFields[$index] = $oExpression->RenameParam($sOldName, $sNewName);
 		}
 		}
 	}
 	}
-}
-
-?>
+}

+ 21 - 2
core/querybuildercontext.class.inc.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2015 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -19,7 +19,7 @@
 /**
 /**
  * Associated with the metamodel -> MakeQuery/MakeQuerySingleTable
  * Associated with the metamodel -> MakeQuery/MakeQuerySingleTable
  *
  *
- * @copyright   Copyright (C) 2010-2015 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -30,6 +30,7 @@ class QueryBuilderContext
 	protected $m_aTableAliases;
 	protected $m_aTableAliases;
 	protected $m_aModifierProperties;
 	protected $m_aModifierProperties;
 	protected $m_aSelectedClasses;
 	protected $m_aSelectedClasses;
+	protected $m_aFilteredTables;
 
 
 	public $m_oQBExpressions;
 	public $m_oQBExpressions;
 
 
@@ -40,6 +41,7 @@ class QueryBuilderContext
 
 
 		$this->m_aClassAliases = $oFilter->GetJoinedClasses();
 		$this->m_aClassAliases = $oFilter->GetJoinedClasses();
 		$this->m_aTableAliases = array();
 		$this->m_aTableAliases = array();
+		$this->m_aFilteredTables = array();
 
 
 		$this->m_aModifierProperties = $aModifierProperties;
 		$this->m_aModifierProperties = $aModifierProperties;
 		if (is_null($aSelectedClasses))
 		if (is_null($aSelectedClasses))
@@ -84,4 +86,21 @@ class QueryBuilderContext
 	{
 	{
 		return $this->m_aSelectedClasses[$sAlias];
 		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;
+	}
 }
 }

+ 1 - 1
core/sqlobjectquery.class.inc.php

@@ -166,7 +166,7 @@ class SQLObjectQuery extends SQLQuery
 		}
 		}
 		else
 		else
 		{
 		{
-			$this->m_oConditionExpr->LogAnd($oConditionExpr);
+			$this->m_oConditionExpr = $this->m_oConditionExpr->LogAnd($oConditionExpr);
 		}
 		}
 	}
 	}
 
 

+ 26 - 4
core/userrights.class.inc.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
 /**
 /**
  * User rights management API
  * User rights management API
  *
  *
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -641,6 +641,29 @@ class UserRights
 		}
 		}
 	}
 	}
 
 
+	/**
+	 * Tells whether or not the archive mode is allowed to the current user
+	 * @return boolean
+	 */
+	static function CanBrowseArchive()
+	{
+		if (is_null(self::$m_oUser))
+		{
+			$bRet = false;
+		}
+		elseif (isset($_SESSION['archive_allowed']))
+		{
+			$bRet = $_SESSION['archive_allowed'];
+		}
+		else
+		{
+			// As of now, anybody can swith to the archive mode
+			$bRet = true;
+			$_SESSION['archive_allowed'] = $bRet;
+		}
+		return $bRet;
+	}
+
 	public static function CanChangePassword()
 	public static function CanChangePassword()
 	{
 	{
 		if (MetaModel::DBIsReadOnly())
 		if (MetaModel::DBIsReadOnly())
@@ -1675,5 +1698,4 @@ class CAS_SelfRegister implements iSelfRegister
 }
 }
 
 
 // By default enable the 'CAS_SelfRegister' defined above
 // By default enable the 'CAS_SelfRegister' defined above
-UserRights::SelectSelfRegister('CAS_SelfRegister');
-?>
+UserRights::SelectSelfRegister('CAS_SelfRegister');

+ 11 - 3
core/valuesetdef.class.inc.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2015 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
 /**
 /**
  * Value set definitions (from a fixed list or from a query, etc.)
  * Value set definitions (from a fixed list or from a query, etc.)
  *
  *
- * @copyright   Copyright (C) 2010-2015 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -124,7 +124,7 @@ class ValueSetObjects extends ValueSetDefinition
 		$this->m_aExtraConditions[] = $oFilter;		
 		$this->m_aExtraConditions[] = $oFilter;		
 	}
 	}
 
 
-	public function ToObjectSet($aArgs = array(), $sContains = '')
+	public function ToObjectSet($aArgs = array(), $sContains = '', $iAdditionalValue = null)
 	{
 	{
 		if ($this->m_bAllowAllData)
 		if ($this->m_bAllowAllData)
 		{
 		{
@@ -145,6 +145,14 @@ class ValueSetObjects extends ValueSetDefinition
 				$oFilter->SetModifierProperty($sPluginClass, $sProperty, $value);
 				$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);
 		return new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs);
 	}
 	}

+ 4 - 4
core/xmlbulkexport.class.inc.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2015 Combodo SARL
+// Copyright (C) 2015-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -19,7 +19,7 @@
 /**
 /**
  * Bulk export: XML export
  * Bulk export: XML export
  *
  *
- * @copyright   Copyright (C) 2015 Combodo SARL
+ * @copyright   Copyright (C) 2015-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -118,7 +118,7 @@ class XMLBulkExport extends BulkExport
 					{
 					{
 						continue;
 						continue;
 					}
 					}
-					if (!$oAttDef->IsWritable())
+					if ($oAttDef->IsExternalField())
 					{
 					{
 						continue;
 						continue;
 					}
 					}
@@ -138,7 +138,7 @@ class XMLBulkExport extends BulkExport
 				$aClass2Attributes[$sAlias] = $aAttributes;
 				$aClass2Attributes[$sAlias] = $aAttributes;
 			}
 			}
 		}
 		}
-		
+
 		$iPreviousTimeLimit = ini_get('max_execution_time');
 		$iPreviousTimeLimit = ini_get('max_execution_time');
 		$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
 		$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
 		
 		

+ 1 - 1
css/css-variables.scss

@@ -5,4 +5,4 @@ $complement-light: #d6e8ef;
 $frame-background-color: #F1F1F1;
 $frame-background-color: #F1F1F1;
 $text-color: #000;
 $text-color: #000;
 // Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
 // Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
-$version: "v2.3.0";
+$version: "v2.4.0-alpha";

+ 90 - 35
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 {
 .ui-widget-content td a, p a, p a:visited, td a, td a:visited {
   text-decoration: none;
   text-decoration: none;
   color: #1c94c4;
   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 {
 .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;
   padding-left: 0;
@@ -292,13 +290,13 @@ td a.mailto, td a.mailto:visited {
   text-decoration: none;
   text-decoration: none;
   color: #000;
   color: #000;
   padding-left: 20px;
   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 {
 td a.mailto:hover {
   text-decoration: underline;
   text-decoration: underline;
   color: #e87c1e;
   color: #e87c1e;
   padding-left: 20px;
   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 {
 a.small_action {
   font-family: Tahoma, Verdana, Arial, Helvetica;
   font-family: Tahoma, Verdana, Arial, Helvetica;
@@ -316,10 +314,10 @@ a.small_action {
   padding-left: 5px;
   padding-left: 5px;
   padding-top: 2px;
   padding-top: 2px;
   padding-bottom: 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 {
 .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;
   color: #fff;
   font-weight: bold;
   font-weight: bold;
   padding-top: 2px;
   padding-top: 2px;
@@ -493,7 +491,7 @@ div.actions_menu > ul {
   nowidth: 70px;
   nowidth: 70px;
   padding-left: 5px;
   padding-left: 5px;
   /* Nasty work-around for IE... en attendant mieux */
   /* 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;
   cursor: pointer;
   margin: 0;
   margin: 0;
 }
 }
@@ -505,7 +503,7 @@ div.actions_menu > ul > li {
   height: 17px;
   height: 17px;
   padding-right: 16px;
   padding-right: 16px;
   padding-left: 4px;
   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;
   font-weight: bold;
   color: #fff;
   color: #fff;
   vertical-align: middle;
   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;
   display: block;
   text-indent: -2000px;
   text-indent: -2000px;
   overflow: hidden;
   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 {
 td a.dp-choose-date.dp-disabled, a.dp-choose-date.dp-disabled {
   background-position: 0 -20px;
   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 */
 /* 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 {
 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 */
 /* 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 {
 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 */
 /* 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 {
 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 */
 /* 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 {
 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 {
 table.listResults.truncated {
   border-bottom: 0;
   border-bottom: 0;
@@ -859,7 +857,7 @@ div#logo {
 div#logo div {
 div#logo div {
   height: 88px;
   height: 88px;
   width: 244px;
   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 {
 #left-pane .ui-layout-north {
   overflow: hidden;
   overflow: hidden;
@@ -868,13 +866,56 @@ div#logo div {
   background: #f1f1f1;
   background: #f1f1f1;
   text-align: right;
   text-align: right;
 }
 }
-#admin-banner {
+.app-message {
   float: left;
   float: left;
   margin-top: 2px;
   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 {
 #global-search {
   height: 55px;
   height: 55px;
@@ -908,7 +949,7 @@ div#logo div {
 }
 }
 #global-search-image {
 #global-search-image {
   vertical-align: middle;
   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;
   display: inline-block;
   width: 28px;
   width: 28px;
   height: 30px;
   height: 30px;
@@ -937,7 +978,7 @@ span.ui-icon {
   margin: 0 2px;
   margin: 0 2px;
 }
 }
 .ui-layout-button-pin-down {
 .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;
   width: 16px;
   background-position: -144px -144px;
   background-position: -144px -144px;
 }
 }
@@ -1148,7 +1189,7 @@ img.prev, img.first, img.next, img.last {
 }
 }
 div.actions_button {
 div.actions_button {
   float: right;
   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;
   padding-left: 5px;
   margin-top: 0;
   margin-top: 0;
   margin-right: 10px;
   margin-right: 10px;
@@ -1156,7 +1197,7 @@ div.actions_button {
   vertical-align: middle;
   vertical-align: middle;
 }
 }
 div.actions_button a, .actions_button a:hover, .actions_button a:visited {
 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;
   color: #fff;
   padding-right: 8px;
   padding-right: 8px;
   cursor: pointer;
   cursor: pointer;
@@ -1180,10 +1221,10 @@ select#org_id {
   cursor: not-allowed;
   cursor: not-allowed;
 }
 }
 .dragHover {
 .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 {
 .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;
   padding: 5px;
   margin: 0;
   margin: 0;
   position: relative;
   position: relative;
@@ -1215,7 +1256,7 @@ table.prop_table {
   top: 0;
   top: 0;
   right: 0;
   right: 0;
   z-index: 10;
   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 {
 td.prop_value {
   text-align: left;
   text-align: left;
@@ -1409,17 +1450,17 @@ a.summary, a.summary:hover {
 }
 }
 .message_info {
 .message_info {
   border: 1px solid #993;
   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;
   padding-left: 3em;
 }
 }
 .message_ok {
 .message_ok {
   border: 1px solid #393;
   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;
   padding-left: 3em;
 }
 }
 .message_error {
 .message_error {
   border: 1px solid #933;
   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;
   padding-left: 3em;
 }
 }
 .fg-menu a img {
 .fg-menu a img {
@@ -1547,18 +1588,18 @@ div.explain-printable {
 }
 }
 #hiddeable_chapters .ui-tabs .ui-tabs-nav li.hideable-chapter span {
 #hiddeable_chapters .ui-tabs .ui-tabs-nav li.hideable-chapter span {
   padding-left: 20px;
   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 {
 #hiddeable_chapters .ui-tabs .ui-tabs-nav li.hideable-chapter.strikethrough span {
   text-decoration: line-through;
   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 {
 .printable-version legend {
   padding-left: 26px;
   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 {
 .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 {
 .printable-version fieldset.strikethrough span {
   display: none;
   display: none;
@@ -1577,7 +1618,7 @@ span.refresh-button {
   width: 21px;
   width: 21px;
   height: 18px;
   height: 18px;
   cursor: pointer;
   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 {
 .case-log-history-entry {
   display: block;
   display: block;
@@ -1705,7 +1746,7 @@ span.refresh-button {
 #itop-breadcrumb .breadcrumb-item a::after {
 #itop-breadcrumb .breadcrumb-item a::after {
   content: '';
   content: '';
   position: absolute;
   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;
   background-repeat: no-repeat;
   width: 8px;
   width: 8px;
   height: 16px;
   height: 16px;
@@ -1763,3 +1804,17 @@ span.refresh-button {
   margin-top: 3px;
   margin-top: 3px;
   margin-bottom: 1px;
   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;
+}

+ 71 - 8
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 {
 .ui-widget-content td a, p a, p a:visited, td a, td a:visited {
 	text-decoration:none;
 	text-decoration:none;
 	color: $complement-color;
 	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 {
 .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;
 	padding-left: 0;
@@ -956,13 +954,62 @@ div#logo div {
 	background: $frame-background-color;
 	background: $frame-background-color;
 	text-align: right;
 	text-align: right;
 }
 }
-#admin-banner {
+.app-banner {
+
+}
+.app-message {
 	float: left;
 	float: left;
 	margin-top: 2px;
 	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 {
 #global-search {
 	height: 55px;
 	height: 55px;
@@ -1888,4 +1935,20 @@ span.refresh-button {
 	font-size: smaller;
 	font-size: smaller;
 	margin-top: 3px;
 	margin-top: 3px;
 	margin-bottom: 1px;
 	margin-bottom: 1px;
-}
+}
+.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;
+}

+ 6 - 1
datamodels/2.x/itop-portal-base/portal/web/index.php

@@ -1,6 +1,6 @@
 <?php
 <?php
 
 
-// Copyright (C) 2010-2015 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -86,6 +86,11 @@ $oApp->before(function(Symfony\Component\HttpFoundation\Request $oRequest, Silex
         $oApp->abort(500, Dict::S('Portal:ErrorNoContactForThisUser'));
         $oApp->abort(500, Dict::S('Portal:ErrorNoContactForThisUser'));
     }
     }
 
 
+	// Enable archived data
+	$bArchiveMode = utils::IsArchiveMode();
+	DBSearch::SetArchiveModeDefault($bArchiveMode);
+	if ($bArchiveMode) MetaModel::DBSetReadOnly();
+
     // Enabling datalocalizer if needed
     // Enabling datalocalizer if needed
     if (!defined('DISABLE_DATA_LOCALIZER_PORTAL'))
     if (!defined('DISABLE_DATA_LOCALIZER_PORTAL'))
     {
     {

+ 9 - 2
dictionaries/cs.dictionary.itop.core.php

@@ -1,6 +1,6 @@
 <?php
 <?php
 
 
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -23,7 +23,7 @@
  *
  *
  * @author      Lukáš Dvořák <lukas.dvorak@itopportal.cz>
  * @author      Lukáš Dvořák <lukas.dvorak@itopportal.cz>
  * @author      Daniel Rokos <daniel.rokos@itopportal.cz>
  * @author      Daniel Rokos <daniel.rokos@itopportal.cz>
- * @copyright   Copyright (C) 2010-2014 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 Dict::Add('CS CZ', 'Czech', 'Čeština', array(
 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+' => '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' => 'Řetězec (string)',
     'Core:AttributeString+' => 'Alfanumerický řetězec',
     'Core:AttributeString+' => 'Alfanumerický řetězec',

+ 4 - 2
dictionaries/cs.dictionary.itop.ui.php

@@ -1,6 +1,6 @@
 <?php
 <?php
 
 
-// Copyright (C) 2010-2013 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -23,7 +23,7 @@
  *
  *
  * @author      Lukáš Dvořák <lukas.dvorak@itopportal.cz>
  * @author      Lukáš Dvořák <lukas.dvorak@itopportal.cz>
  * @author      Daniel Rokos <daniel.rokos@itopportal.cz>
  * @author      Daniel Rokos <daniel.rokos@itopportal.cz>
- * @copyright   Copyright (C) 2010-2014 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @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:Pagination:All' => 'Vše',
     'UI:HierarchyOf_Class' => 'Hierarchie %1$s',
     'UI:HierarchyOf_Class' => 'Hierarchie %1$s',
     'UI:Preferences' => 'Předvolby',
     'UI:Preferences' => 'Předvolby',
+	'UI:ArchiveModeOn' => 'Activate archive mode~~',
+	'UI:ArchiveModeOff' => 'Deactivate archive mode~~',
     'UI:FavoriteOrganizations' => 'Oblíbené organizace',
     '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: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í',
     'UI:FavoriteLanguage' => 'Jazyk uživatelského rozhraní',

+ 8 - 2
dictionaries/da.dictionary.itop.core.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -19,7 +19,7 @@
 /**
 /**
  * @author Erik Bøg <erik@boegmoeller.dk>
  * @author Erik Bøg <erik@boegmoeller.dk>
  *
  *
- * @copyright   Copyright (C) 2010-2012 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @licence	http://opensource.org/licenses/AGPL-3.0
  * @licence	http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -1397,6 +1397,12 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
 	'Core:AttributeDecimal+' => '',
 	'Core:AttributeDecimal+' => '',
 	'Core:AttributeBoolean' => 'Boolean',
 	'Core:AttributeBoolean' => 'Boolean',
 	'Core:AttributeBoolean+' => '',
 	'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' => 'String',
 	'Core:AttributeString+' => '',
 	'Core:AttributeString+' => '',
 	'Core:AttributeClass' => 'Class',
 	'Core:AttributeClass' => 'Class',

+ 11 - 2
dictionaries/da.dictionary.itop.ui.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2012 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -19,7 +19,7 @@
 /**
 /**
  * @author Erik Bøg <erik@boegmoeller.dk>
  * @author Erik Bøg <erik@boegmoeller.dk>
  *
  *
- * @copyright   Copyright (C) 2010-2012 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @licence	http://opensource.org/licenses/AGPL-3.0
  * @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:DetailsPageTitle' => 'iTop - %1$s - %2$s detaljer',
 	'UI:ErrorPageTitle' => 'iTop - Fejl',
 	'UI:ErrorPageTitle' => 'iTop - Fejl',
 	'UI:ObjectDoesNotExist' => 'Beklager, dette objekt eksisterer ikke (eller du har ikke tilladelse til at se det).',
 	'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:SearchResultsPageTitle' => 'iTop - Søge Resultater',
 	'UI:SearchResultsTitle' => 'Søge Resultater',
 	'UI:SearchResultsTitle' => 'Søge Resultater',
 	'UI:SearchResultsTitle+' => 'Full-text search results~~',
 	'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:Pagination:All' => 'Alle',
 	'UI:HierarchyOf_Class' => 'Hierarchy af %1$s',
 	'UI:HierarchyOf_Class' => 'Hierarchy af %1$s',
 	'UI:Preferences' => 'Indstillinger...',
 	'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' => 'Favorit Organisationer',
 	'UI:FavoriteOrganizations+' => '',
 	'UI:FavoriteOrganizations+' => '',
 	'UI:FavoriteLanguage' => 'Sprog i brugergrænseflade',
 	'UI:FavoriteLanguage' => 'Sprog i brugergrænseflade',

+ 8 - 2
dictionaries/de.dictionary.itop.core.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -19,7 +19,7 @@
 /**
 /**
  * @author	Stephan Rosenke <stephan.rosenke@itomig.de>
  * @author	Stephan Rosenke <stephan.rosenke@itomig.de>
 
 
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @licence	http://opensource.org/licenses/AGPL-3.0
  * @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:AttributeDecimal+' => 'Dezimaler Wert (kann negativ sein)',
 	'Core:AttributeBoolean' => 'Boolean',
 	'Core:AttributeBoolean' => 'Boolean',
 	'Core:AttributeBoolean+' => 'Boolscher Wert',
 	'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' => 'String',
 	'Core:AttributeString+' => 'Alphanumerischer String',
 	'Core:AttributeString+' => 'Alphanumerischer String',
 	'Core:AttributeClass' => 'Class',
 	'Core:AttributeClass' => 'Class',

+ 11 - 2
dictionaries/de.dictionary.itop.ui.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2012 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -19,7 +19,7 @@
 /**
 /**
  * @author	Stephan Rosenke <stephan.rosenke@itomig.de>
  * @author	Stephan Rosenke <stephan.rosenke@itomig.de>
 
 
- * @copyright   Copyright (C) 2010-2012 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @licence	http://opensource.org/licenses/AGPL-3.0
  * @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:DetailsPageTitle' => 'iTop - %1$s - %2$s Details',
 	'UI:ErrorPageTitle' => 'iTop - Fehler',
 	'UI:ErrorPageTitle' => 'iTop - Fehler',
 	'UI:ObjectDoesNotExist' => 'Leider existiert dieses Objekt nicht oder Sie sind nicht berechtigt es einzusehen.',
 	'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:SearchResultsPageTitle' => 'iTop - Suchergebnisse',
 	'UI:SearchResultsTitle' => 'Suchergebnisse',
 	'UI:SearchResultsTitle' => 'Suchergebnisse',
 	'UI:SearchResultsTitle+' => 'Full-text search results~~',
 	'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:Pagination:All' => 'Alles',
 	'UI:HierarchyOf_Class' => 'Hierarchie von %1$s',
 	'UI:HierarchyOf_Class' => 'Hierarchie von %1$s',
 	'UI:Preferences' => 'Einstellungen...',
 	'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' => 'Bevorzugte Organisationen',
 	'UI:FavoriteOrganizations+' => '',
 	'UI:FavoriteOrganizations+' => '',
 	'UI:FavoriteLanguage' => 'Sprache des Benutzerinterfaces',
 	'UI:FavoriteLanguage' => 'Sprache des Benutzerinterfaces',

+ 9 - 2
dictionaries/dictionary.itop.core.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
 /**
 /**
  * Localized data
  * Localized data
  *
  *
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -45,6 +45,13 @@ Dict::Add('EN US', 'English', 'English', array(
 
 
 	'Core:AttributeBoolean' => 'Boolean',
 	'Core:AttributeBoolean' => 'Boolean',
 	'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' => 'String',
 	'Core:AttributeString+' => 'Alphanumeric string',
 	'Core:AttributeString+' => 'Alphanumeric string',

+ 11 - 2
dictionaries/dictionary.itop.ui.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2013 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
 /**
 /**
  * Localized data
  * Localized data
  *
  *
- * @copyright   Copyright (C) 2010-2012 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -770,6 +770,11 @@ Dict::Add('EN US', 'English', 'English', array(
 	'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s details',
 	'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s details',
 	'UI:ErrorPageTitle' => 'iTop - Error',
 	'UI:ErrorPageTitle' => 'iTop - Error',
 	'UI:ObjectDoesNotExist' => 'Sorry, this object does not exist (or you are not allowed to view it).',
 	'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:SearchResultsPageTitle' => 'iTop - Search Results',
 	'UI:SearchResultsTitle' => 'Search Results',
 	'UI:SearchResultsTitle' => 'Search Results',
 	'UI:SearchResultsTitle+' => 'Full-text 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:Pagination:All' => 'All',
 	'UI:HierarchyOf_Class' => 'Hierarchy of %1$s',
 	'UI:HierarchyOf_Class' => 'Hierarchy of %1$s',
 	'UI:Preferences' => 'Preferences...',
 	'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' => '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. '.
 	'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.',
 								   '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.',

+ 9 - 2
dictionaries/es_cr.dictionary.itop.core.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
 /**
 /**
  * Localized data
  * Localized data
  *
  *
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -45,6 +45,13 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 
 
 	'Core:AttributeBoolean' => 'Booleano',
 	'Core:AttributeBoolean' => 'Booleano',
 	'Core:AttributeBoolean+' => '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',
 	'Core:AttributeString+' => 'Cadena de caracteres alfanumerico',
 	'Core:AttributeString+' => 'Cadena de caracteres alfanumerico',

+ 12 - 4
dictionaries/es_cr.dictionary.itop.ui.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2013 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
 /**
 /**
  * Localized data
  * Localized data
  *
  *
- * @copyright   Copyright (C) 2010-2012 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -760,6 +760,11 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 	'UI:DetailsPageTitle' => 'iTop - %1$s - Detalles %2$s',
 	'UI:DetailsPageTitle' => 'iTop - %1$s - Detalles %2$s',
 	'UI:ErrorPageTitle' => 'iTop - Error',
 	'UI:ErrorPageTitle' => 'iTop - Error',
 	'UI:ObjectDoesNotExist' => 'Disculpe, este objeto no existe (o no está autorizado para verlo).',
 	'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:SearchResultsPageTitle' => 'iTop - Resultados de la Búsqueda',
 	'UI:SearchResultsTitle' => 'Resultados de la Búsqueda',
 	'UI:SearchResultsTitle' => 'Resultados de la Búsqueda',
 	'UI:SearchResultsTitle+' => 'Full-text search results~~',
 	'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:Pagination:All' => 'Todos',
 	'UI:HierarchyOf_Class' => 'Jerarquía de %1$s',
 	'UI:HierarchyOf_Class' => 'Jerarquía de %1$s',
 	'UI:Preferences' => 'Preferencias',
 	'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' => '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. '.
 	'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ú.',
 								   '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:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~',
 	'UI:Menu:ExportPDF' => 'Export as PDF...~~',
 	'UI:Menu:ExportPDF' => 'Export as PDF...~~',
 	'UI:Menu:PrintableVersion' => 'Versión imprimible',
 	'UI:Menu:PrintableVersion' => 'Versión imprimible',
-));
-?>
+));

+ 8 - 2
dictionaries/fr.dictionary.itop.core.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -18,7 +18,7 @@
 
 
 
 
 /**
 /**
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -432,6 +432,12 @@ Dict::Add('FR FR', 'French', 'Français', array(
 	'Core:AttributeDecimal+' => 'Valeur numérique décimale',
 	'Core:AttributeDecimal+' => 'Valeur numérique décimale',
 	'Core:AttributeBoolean' => 'Booléen',
 	'Core:AttributeBoolean' => 'Booléen',
 	'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',
 	'Core:AttributeString+' => 'Chaîne de caractères (limitée à une ligne)',
 	'Core:AttributeString+' => 'Chaîne de caractères (limitée à une ligne)',
 	'Core:AttributeClass' => 'Classe',
 	'Core:AttributeClass' => 'Classe',

+ 11 - 2
dictionaries/fr.dictionary.itop.ui.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2013 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -17,7 +17,7 @@
 //   along with iTop. If not, see <http://www.gnu.org/licenses/>
 //   along with iTop. If not, see <http://www.gnu.org/licenses/>
 
 
 /**
 /**
- * @copyright   Copyright (C) 2010-2012 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @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:DetailsPageTitle' => 'iTop - %2$s - Détails de %1$s',
 	'UI:ErrorPageTitle' => 'iTop - Erreur',
 	'UI:ErrorPageTitle' => 'iTop - Erreur',
 	'UI:ObjectDoesNotExist' => 'Désolé cet objet n\'existe pas (où vous n\'êtes pas autorisé à l\'afficher).',
 	'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:SearchResultsPageTitle' => 'iTop - Résultats de la recherche',
 	'UI:SearchResultsTitle' => 'Recherche globale',
 	'UI:SearchResultsTitle' => 'Recherche globale',
 	'UI:SearchResultsTitle+' => 'Résultat de 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:Pagination:All' => 'Tous',
 	'UI:HierarchyOf_Class' => 'Hiérarchie de type %1$s',
 	'UI:HierarchyOf_Class' => 'Hiérarchie de type %1$s',
 	'UI:Preferences' => 'Préférences...',
 	'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' => 'Organisations Favorites',
 	'UI:FavoriteOrganizations+' => 'Cochez dans la liste ci-dessous les organisations que vous voulez voir listées dans le menu principal. '.
 	'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.',
 								   '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.',

+ 9 - 4
dictionaries/hu.dictionary.itop.core.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -17,7 +17,7 @@
 //   along with iTop. If not, see <http://www.gnu.org/licenses/>
 //   along with iTop. If not, see <http://www.gnu.org/licenses/>
 
 
 /**
 /**
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -306,6 +306,12 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
 	'Core:AttributeDecimal+' => '',
 	'Core:AttributeDecimal+' => '',
 	'Core:AttributeBoolean' => 'Boolean',
 	'Core:AttributeBoolean' => 'Boolean',
 	'Core:AttributeBoolean+' => '',
 	'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' => 'String',
 	'Core:AttributeString+' => '',
 	'Core:AttributeString+' => '',
 	'Core:AttributeClass' => 'Class',
 	'Core:AttributeClass' => 'Class',
@@ -609,5 +615,4 @@ Operators:<br/>
 	'Core:Validator:Mandatory' => 'Please, fill this field~~',
 	'Core:Validator:Mandatory' => 'Please, fill this field~~',
 	'Core:Validator:MustBeInteger' => 'Must be an integer~~',
 	'Core:Validator:MustBeInteger' => 'Must be an integer~~',
 	'Core:Validator:MustSelectOne' => 'Please, select one~~',
 	'Core:Validator:MustSelectOne' => 'Please, select one~~',
-));
-?>
+));

+ 12 - 4
dictionaries/hu.dictionary.itop.ui.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2012 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -17,7 +17,7 @@
 //   along with iTop. If not, see <http://www.gnu.org/licenses/>
 //   along with iTop. If not, see <http://www.gnu.org/licenses/>
 
 
 /**
 /**
- * @copyright   Copyright (C) 2010-2012 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @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:DetailsPageTitle' => 'iTop - %1$s - %2$s részletek',
 	'UI:ErrorPageTitle' => 'iTop - Hiba',
 	'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: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:SearchResultsPageTitle' => 'iTop - Keresés eredményei',
 	'UI:SearchResultsTitle' => 'Keresés eredményei',
 	'UI:SearchResultsTitle' => 'Keresés eredményei',
 	'UI:SearchResultsTitle+' => 'Full-text search results~~',
 	'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:Pagination:All' => 'All~~',
 	'UI:HierarchyOf_Class' => 'Hierarchy of %1$s~~',
 	'UI:HierarchyOf_Class' => 'Hierarchy of %1$s~~',
 	'UI:Preferences' => 'Preferences...~~',
 	'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' => '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: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~~',
 	'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:ConcurrentLockKilled' => 'The lock preventing modifications on the current object has been deleted.~~',
 	'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~',
 	'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~',
 	'UI:Menu:ExportPDF' => 'Export as PDF...~~',
 	'UI:Menu:ExportPDF' => 'Export as PDF...~~',
-));
-?>
+));

+ 10 - 5
dictionaries/it.dictionary.itop.core.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
 /**
 /**
  * Localized data
  * Localized data
  *
  *
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -39,6 +39,13 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
 
 
 	'Core:AttributeBoolean' => 'Booleano',
 	'Core:AttributeBoolean' => 'Booleano',
 	'Core:AttributeBoolean+' => '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',
 	'Core:AttributeString+' => 'Stringa alfanumerica',
 	'Core:AttributeString+' => 'Stringa alfanumerica',
@@ -855,6 +862,4 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
 	'Core:Validator:Mandatory' => 'Please, fill this field~~',
 	'Core:Validator:Mandatory' => 'Please, fill this field~~',
 	'Core:Validator:MustBeInteger' => 'Must be an integer~~',
 	'Core:Validator:MustBeInteger' => 'Must be an integer~~',
 	'Core:Validator:MustSelectOne' => 'Please, select one~~',
 	'Core:Validator:MustSelectOne' => 'Please, select one~~',
-));
-
-?>
+));

+ 12 - 4
dictionaries/it.dictionary.itop.ui.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2012 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
 /**
 /**
  * Localized data
  * Localized data
  *
  *
- * @copyright   Copyright (C) 2010-2012 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -671,6 +671,11 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
 	'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s dettagli',
 	'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s dettagli',
 	'UI:ErrorPageTitle' => 'iTop - Errore',
 	'UI:ErrorPageTitle' => 'iTop - Errore',
 	'UI:ObjectDoesNotExist' => 'Spiacenti, questo oggetto non esiste (o non si è autorizzati per vederlo).',
 	'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:SearchResultsPageTitle' => 'iTop - Risultati della ricerca',
 	'UI:SearchResultsTitle' => 'Risultati della ricerca',
 	'UI:SearchResultsTitle' => 'Risultati della ricerca',
 	'UI:SearchResultsTitle+' => 'Full-text search results~~',
 	'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:Pagination:All' => 'All~~',
 	'UI:HierarchyOf_Class' => 'Hierarchy of %1$s~~',
 	'UI:HierarchyOf_Class' => 'Hierarchy of %1$s~~',
 	'UI:Preferences' => 'Preferences...~~',
 	'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' => '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: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~~',
 	'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:ConcurrentLockKilled' => 'The lock preventing modifications on the current object has been deleted.~~',
 	'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~',
 	'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~',
 	'UI:Menu:ExportPDF' => 'Export as PDF...~~',
 	'UI:Menu:ExportPDF' => 'Export as PDF...~~',
-));
-?>
+));

+ 9 - 4
dictionaries/ja.dictionary.itop.core.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -17,7 +17,7 @@
 //   along with iTop. If not, see <http://www.gnu.org/licenses/>
 //   along with iTop. If not, see <http://www.gnu.org/licenses/>
 
 
 /**
 /**
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @licence	http://opensource.org/licenses/AGPL-3.0
  * @licence	http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -325,6 +325,12 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
 	'Core:AttributeDecimal+' => 'Decimal値 (負数あり)',
 	'Core:AttributeDecimal+' => 'Decimal値 (負数あり)',
 	'Core:AttributeBoolean' => 'ブール型',
 	'Core:AttributeBoolean' => 'ブール型',
 	'Core:AttributeBoolean+' => 'Bool値',
 	'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:AttributeString+' => '文字列',
 	'Core:AttributeString+' => '文字列',
 	'Core:AttributeClass' => 'クラス',
 	'Core:AttributeClass' => 'クラス',
@@ -631,5 +637,4 @@ Operators:<br/>
 	'Core:Validator:Mandatory' => 'Please, fill this field~~',
 	'Core:Validator:Mandatory' => 'Please, fill this field~~',
 	'Core:Validator:MustBeInteger' => 'Must be an integer~~',
 	'Core:Validator:MustBeInteger' => 'Must be an integer~~',
 	'Core:Validator:MustSelectOne' => 'Please, select one~~',
 	'Core:Validator:MustSelectOne' => 'Please, select one~~',
-));
-?>
+));

+ 12 - 4
dictionaries/ja.dictionary.itop.ui.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2012 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -17,7 +17,7 @@
 //   along with iTop. If not, see <http://www.gnu.org/licenses/>
 //   along with iTop. If not, see <http://www.gnu.org/licenses/>
 
 
 /**
 /**
- * @copyright   Copyright (C) 2010-2012 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @licence	http://opensource.org/licenses/AGPL-3.0
  * @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:DetailsPageTitle' => 'iTop - %1$s - %2$sの詳細',
 	'UI:ErrorPageTitle' => 'iTop - エラー',
 	'UI:ErrorPageTitle' => 'iTop - エラー',
 	'UI:ObjectDoesNotExist' => '申し訳ございません。このオブジェクトは既に存在しません。(あるいは参照する権限がありません。)',
 	'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:SearchResultsPageTitle' => 'iTop - 検索結果',
 	'UI:SearchResultsTitle' => '検索結果',
 	'UI:SearchResultsTitle' => '検索結果',
 	'UI:SearchResultsTitle+' => 'Full-text search results~~',
 	'UI:SearchResultsTitle+' => 'Full-text search results~~',
@@ -856,6 +861,10 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
 	'UI:Pagination:All' => '全',
 	'UI:Pagination:All' => '全',
 	'UI:HierarchyOf_Class' => '%1$s の階層',
 	'UI:HierarchyOf_Class' => '%1$s の階層',
 	'UI:Preferences' => 'プリファレンス...',
 	'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' => 'クイックアクセス組織',
 	'UI:FavoriteOrganizations+' => '迅速なアクセスのためのドロップダウンメニューに表示したい組織は、以下のリストで確認してください。セキュリティ設定ではないことに注意してください。全ての組織のオブジェクトは、表示可能です。ドロップダウンリストで「すべての組織(All Organizations)」を選択することでアクセスすることができます。',
 	'UI:FavoriteOrganizations+' => '迅速なアクセスのためのドロップダウンメニューに表示したい組織は、以下のリストで確認してください。セキュリティ設定ではないことに注意してください。全ての組織のオブジェクトは、表示可能です。ドロップダウンリストで「すべての組織(All Organizations)」を選択することでアクセスすることができます。',
 	'UI:FavoriteLanguage' => 'ユーザインターフェースの言語',
 	'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:ConcurrentLockKilled' => 'The lock preventing modifications on the current object has been deleted.~~',
 	'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~',
 	'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~',
 	'UI:Menu:ExportPDF' => 'Export as PDF...~~',
 	'UI:Menu:ExportPDF' => 'Export as PDF...~~',
-));
-?>
+));

+ 10 - 5
dictionaries/nl.dictionary.itop.core.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -23,7 +23,7 @@
  * Linux & Open Source Professionals
  * Linux & Open Source Professionals
  * http://www.linprofs.com
  * http://www.linprofs.com
  * 
  * 
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @licence	http://opensource.org/licenses/AGPL-3.0
  * @licence	http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -50,6 +50,13 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
 
 
 	'Core:AttributeBoolean' => 'Boolean',
 	'Core:AttributeBoolean' => 'Boolean',
 	'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' => 'String',
 	'Core:AttributeString+' => 'Alphanumerieke 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:Mandatory' => 'Please, fill this field~~',
 	'Core:Validator:MustBeInteger' => 'Must be an integer~~',
 	'Core:Validator:MustBeInteger' => 'Must be an integer~~',
 	'Core:Validator:MustSelectOne' => 'Please, select one~~',
 	'Core:Validator:MustSelectOne' => 'Please, select one~~',
-));
-
-?>
+));

+ 12 - 4
dictionaries/nl.dictionary.itop.ui.php

@@ -1,5 +1,5 @@
 <?php 
 <?php 
-// Copyright (C) 2010-2013 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -23,7 +23,7 @@
  * Linux & Open Source Professionals
  * Linux & Open Source Professionals
  * http://www.linprofs.com
  * http://www.linprofs.com
  * 
  * 
- * @copyright   Copyright (C) 2010-2012 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @licence	http://opensource.org/licenses/AGPL-3.0
  * @licence	http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -768,6 +768,11 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
 	'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s details',
 	'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s details',
 	'UI:ErrorPageTitle' => 'iTop - Error',
 	'UI:ErrorPageTitle' => 'iTop - Error',
 	'UI:ObjectDoesNotExist' => 'Sorry, dit object bestaat niet (of u bent niet gemachtigd het te bekijken).',
 	'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:SearchResultsPageTitle' => 'iTop - Zoekresultaten',
 	'UI:SearchResultsTitle' => 'Zoekresultaten',
 	'UI:SearchResultsTitle' => 'Zoekresultaten',
 	'UI:SearchResultsTitle+' => 'Full-text search results~~',
 	'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:Pagination:All' => 'Alles',
 	'UI:HierarchyOf_Class' => 'Hierarchie van %1$s',
 	'UI:HierarchyOf_Class' => 'Hierarchie van %1$s',
 	'UI:Preferences' => 'Voorkeuren...',
 	'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' => 'Favoriete Organizaties',
 	'UI:FavoriteOrganizations+' => 'Bekijk de organisaties die u wilt zijn in het drop-down menu voor een snelle toegang in de onderstaande lijst. '.
 	'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.',
 								   '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:ConcurrentLockKilled' => 'The lock preventing modifications on the current object has been deleted.~~',
 	'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~',
 	'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~',
 	'UI:Menu:ExportPDF' => 'Export as PDF...~~',
 	'UI:Menu:ExportPDF' => 'Export as PDF...~~',
-));
-?>
+));

+ 10 - 5
dictionaries/pt_br.dictionary.itop.core.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
 /**
 /**
  * Localized data
  * Localized data
  *
  *
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -45,6 +45,13 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 
 
 	'Core:AttributeBoolean' => 'Boolean',
 	'Core:AttributeBoolean' => 'Boolean',
 	'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' => 'String',
 	'Core:AttributeString+' => 'Seqüência alfanumérica',
 	'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:Mandatory' => 'Please, fill this field~~',
 	'Core:Validator:MustBeInteger' => 'Must be an integer~~',
 	'Core:Validator:MustBeInteger' => 'Must be an integer~~',
 	'Core:Validator:MustSelectOne' => 'Please, select one~~',
 	'Core:Validator:MustSelectOne' => 'Please, select one~~',
-));
-
-?>
+));

+ 12 - 4
dictionaries/pt_br.dictionary.itop.ui.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2012 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -20,7 +20,7 @@
 /**
 /**
  * Localized data
  * Localized data
  *
  *
- * @copyright   Copyright (C) 2010-2012 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @license     http://opensource.org/licenses/AGPL-3.0
  */
  */
 
 
@@ -760,6 +760,11 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 	'UI:DetailsPageTitle' => '%1$s - %2$s detalhes',
 	'UI:DetailsPageTitle' => '%1$s - %2$s detalhes',
 	'UI:ErrorPageTitle' => 'Erro',
 	'UI:ErrorPageTitle' => 'Erro',
 	'UI:ObjectDoesNotExist' => 'Desculpe, este objeto não existe (ou você não tem permissão para vê-lo).',
 	'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:SearchResultsPageTitle' => 'Resultado da pesquisa',
 	'UI:SearchResultsTitle' => 'Resultado da pesquisa',
 	'UI:SearchResultsTitle' => 'Resultado da pesquisa',
 	'UI:SearchResultsTitle+' => 'Full-text search results~~',
 	'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:Pagination:All' => 'Todos',
 	'UI:HierarchyOf_Class' => 'Hierarquia de %1$s',
 	'UI:HierarchyOf_Class' => 'Hierarquia de %1$s',
 	'UI:Preferences' => 'Preferências...',
 	'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' => '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.'.
 	'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.',
 '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:ConcurrentLockKilled' => 'The lock preventing modifications on the current object has been deleted.~~',
 	'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~',
 	'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~',
 	'UI:Menu:ExportPDF' => 'Export as PDF...~~',
 	'UI:Menu:ExportPDF' => 'Export as PDF...~~',
-));
-?>
+));

+ 7 - 0
dictionaries/ru.dictionary.itop.core.php

@@ -31,6 +31,13 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
 
 
 	'Core:AttributeBoolean' => 'Логич.',
 	'Core:AttributeBoolean' => 'Логич.',
 	'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' => 'Строка',
 	'Core:AttributeString' => 'Строка',
 	'Core:AttributeString+' => 'Alphanumeric string',
 	'Core:AttributeString+' => 'Alphanumeric string',

+ 9 - 0
dictionaries/ru.dictionary.itop.ui.php

@@ -748,6 +748,11 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
 	'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s подробности',
 	'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s подробности',
 	'UI:ErrorPageTitle' => 'iTop - Ошибка',
 	'UI:ErrorPageTitle' => 'iTop - Ошибка',
 	'UI:ObjectDoesNotExist' => 'Извните, этот объект не существует (или вы не можете его видеть).',
 	'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:SearchResultsPageTitle' => 'iTop - Результаты поиска',
 	'UI:SearchResultsTitle' => 'Результаты поиска',
 	'UI:SearchResultsTitle' => 'Результаты поиска',
 	'UI:SearchResultsTitle+' => 'Результаты полнотекстового поиска',
 	'UI:SearchResultsTitle+' => 'Результаты полнотекстового поиска',
@@ -1054,6 +1059,10 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
 	'UI:Pagination:All' => 'Все',
 	'UI:Pagination:All' => 'Все',
 	'UI:HierarchyOf_Class' => 'Иерархия по: %1$s~~',
 	'UI:HierarchyOf_Class' => 'Иерархия по: %1$s~~',
 	'UI:Preferences' => 'Дополнительно...~~',
 	'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' => 'Избранные организации',
 	'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: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' => 'Язык пользовательского интерфейса',
 	'UI:FavoriteLanguage' => 'Язык пользовательского интерфейса',

+ 10 - 6
dictionaries/tr.dictionary.itop.core.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -21,7 +21,7 @@
  * Localized data
  * Localized data
  *
  *
  * @author      Izzet Sirin <izzet.sirin@htr.com.tr>
  * @author      Izzet Sirin <izzet.sirin@htr.com.tr>
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @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:AttributeDecimal+' => 'Decimal value (could be negative)~~',
 	'Core:AttributeBoolean' => 'Boolean~~',
 	'Core:AttributeBoolean' => 'Boolean~~',
 	'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' => 'String~~',
 	'Core:AttributeString+' => 'Alphanumeric string~~',
 	'Core:AttributeString+' => 'Alphanumeric string~~',
 	'Core:AttributeClass' => 'Class~~',
 	'Core:AttributeClass' => 'Class~~',
@@ -781,7 +788,4 @@ Operators:<br/>
 	'Core:Validator:Mandatory' => 'Please, fill this field~~',
 	'Core:Validator:Mandatory' => 'Please, fill this field~~',
 	'Core:Validator:MustBeInteger' => 'Must be an integer~~',
 	'Core:Validator:MustBeInteger' => 'Must be an integer~~',
 	'Core:Validator:MustSelectOne' => 'Please, select one~~',
 	'Core:Validator:MustSelectOne' => 'Please, select one~~',
-));
-
-
-?>
+));

+ 12 - 7
dictionaries/tr.dictionary.itop.ui.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2012 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -21,7 +21,7 @@
  * Localized data
  * Localized data
  *
  *
  * @author      Izzet Sirin <izzet.sirin@htr.com.tr>
  * @author      Izzet Sirin <izzet.sirin@htr.com.tr>
- * @copyright   Copyright (C) 2010-2012 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @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:DetailsPageTitle' => 'iTop - %1$s - %2$s detayları',
 	'UI:ErrorPageTitle' => 'iTop - Hata',
 	'UI:ErrorPageTitle' => 'iTop - Hata',
 	'UI:ObjectDoesNotExist' => 'Nesne mevcut değil veya yetkiniz yok.',
 	'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:SearchResultsPageTitle' => 'iTop - Arama Sonuçları',
 	'UI:SearchResultsTitle' => 'Arama Sonuçları',
 	'UI:SearchResultsTitle' => 'Arama Sonuçları',
 	'UI:SearchResultsTitle+' => 'Full-text search results~~',
 	'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:Pagination:All' => 'All~~',
 	'UI:HierarchyOf_Class' => 'Hierarchy of %1$s~~',
 	'UI:HierarchyOf_Class' => 'Hierarchy of %1$s~~',
 	'UI:Preferences' => 'Preferences...~~',
 	'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' => '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: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~~',
 	'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:ConcurrentLockKilled' => 'The lock preventing modifications on the current object has been deleted.~~',
 	'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~',
 	'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~',
 	'UI:Menu:ExportPDF' => 'Export as PDF...~~',
 	'UI:Menu:ExportPDF' => 'Export as PDF...~~',
-));
-
-
-
-?>
+));

+ 9 - 6
dictionaries/zh.dictionary.itop.core.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-// Copyright (C) 2010-2016 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //
 //   This file is part of iTop.
 //   This file is part of iTop.
 //
 //
@@ -21,7 +21,7 @@
  * Localized data
  * Localized data
  *
  *
  * @author      Robert Deng <denglx@gmail.com>
  * @author      Robert Deng <denglx@gmail.com>
- * @copyright   Copyright (C) 2010-2016 Combodo SARL
+ * @copyright   Copyright (C) 2010-2017 Combodo SARL
  * @license     http://opensource.org/licenses/AGPL-3.0
  * @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:AttributeDecimal+' => 'Decimal value (could be negative)~~',
 	'Core:AttributeBoolean' => 'Boolean~~',
 	'Core:AttributeBoolean' => 'Boolean~~',
 	'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' => 'String~~',
 	'Core:AttributeString+' => 'Alphanumeric string~~',
 	'Core:AttributeString+' => 'Alphanumeric string~~',
 	'Core:AttributeClass' => 'Class~~',
 	'Core:AttributeClass' => 'Class~~',
@@ -780,7 +786,4 @@ Operators:<br/>
 	'Core:Validator:Mandatory' => 'Please, fill this field~~',
 	'Core:Validator:Mandatory' => 'Please, fill this field~~',
 	'Core:Validator:MustBeInteger' => 'Must be an integer~~',
 	'Core:Validator:MustBeInteger' => 'Must be an integer~~',
 	'Core:Validator:MustSelectOne' => 'Please, select one~~',
 	'Core:Validator:MustSelectOne' => 'Please, select one~~',
-));
-
-
-?>
+));

+ 9 - 0
dictionaries/zh.dictionary.itop.ui.php

@@ -644,6 +644,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
 	'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s 详细内容',
 	'UI:DetailsPageTitle' => 'iTop - %1$s - %2$s 详细内容',
 	'UI:ErrorPageTitle' => 'iTop - 错误',
 	'UI:ErrorPageTitle' => 'iTop - 错误',
 	'UI:ObjectDoesNotExist' => '抱歉, 该对象不存在 (或您未被允许浏览该对象).',
 	'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:SearchResultsPageTitle' => 'iTop - 搜索结果',
 	'UI:SearchResultsTitle' => '搜索结果',
 	'UI:SearchResultsTitle' => '搜索结果',
 	'UI:SearchResultsTitle+' => 'Full-text search results~~',
 	'UI:SearchResultsTitle+' => 'Full-text search results~~',
@@ -1057,6 +1062,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
 	'UI:Pagination:All' => 'All~~',
 	'UI:Pagination:All' => 'All~~',
 	'UI:HierarchyOf_Class' => 'Hierarchy of %1$s~~',
 	'UI:HierarchyOf_Class' => 'Hierarchy of %1$s~~',
 	'UI:Preferences' => 'Preferences...~~',
 	'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' => '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: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~~',
 	'UI:FavoriteLanguage' => 'Language of the User Interface~~',

+ 2 - 1
js/breadcrumb.js

@@ -75,7 +75,8 @@ $(function()
 						}
 						}
 						else
 						else
 						{
 						{
-							sBreadCrumbHtml += '<div class="breadcrumb-item"><a class="breadcrumb-link" breadcrumb-entry="'+iEntry+'" href="'+oEntry['url']+'" title="'+sTitle+'">'+sIconSpec+'<span class="truncate">'+oEntry['label']+'</span></a></div>';
+							var sSanitizedUrl = StripArchiveArgument(oEntry['url']);
+							sBreadCrumbHtml += '<div class="breadcrumb-item"><a class="breadcrumb-link" breadcrumb-entry="'+iEntry+'" href="'+sSanitizedUrl+'" title="'+sTitle+'">'+sIconSpec+'<span class="truncate">'+oEntry['label']+'</span></a></div>';
 						}
 						}
 					}
 					}
 				}
 				}

+ 20 - 3
pages/UI.php

@@ -377,7 +377,25 @@ try
 				$sMessageKey = $sClass.'::'.$id;
 				$sMessageKey = $sClass.'::'.$id;
 				DisplayMessages($sMessageKey, $oP);
 				DisplayMessages($sMessageKey, $oP);
 				$oP->set_title(Dict::S('UI:ErrorPageTitle'));
 				$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
 			else
 			{
 			{
@@ -1745,5 +1763,4 @@ catch(Exception $e)
 
 
 		IssueLog::Error($e->getMessage());
 		IssueLog::Error($e->getMessage());
 	}
 	}
-}
-?>
+}

+ 5 - 1
portal/index.php

@@ -1310,7 +1310,11 @@ try
 	require_once(APPROOT.'/application/loginwebpage.class.inc.php');
 	require_once(APPROOT.'/application/loginwebpage.class.inc.php');
 	LoginWebPage::DoLogin(false /* bMustBeAdmin */, true /* IsAllowedToPortalUsers */); // Check user rights and prompt if needed
 	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'));
 	$aClasses = explode(',', MetaModel::GetConfig()->Get('portal_tickets'));
 	$sMainClass = trim(reset($aClasses));
 	$sMainClass = trim(reset($aClasses));

+ 6 - 1
setup/compiler.class.inc.php

@@ -725,7 +725,7 @@ EOF
 				$val = $sDefault;
 				$val = $sDefault;
 			}
 			}
 		}
 		}
-		return "'".$val."'";
+		return "'".str_replace("'", "\\'", $val)."'";
 	}
 	}
 	
 	
 	protected function GetMandatoryPropString($oNode, $sTag)
 	protected function GetMandatoryPropString($oNode, $sTag)
@@ -989,6 +989,11 @@ EOF
 			$aClassParams['indexes'] = var_export($aIndexes, true);
 			$aClassParams['indexes'] = var_export($aIndexes, true);
 		}
 		}
 
 
+		if ($oArchive = $oProperties->GetOptionalElement('archive'))
+		{
+			$bEnabled = $this->GetPropBoolean($oArchive, 'enabled', false);
+			$aClassParams['archive'] = $bEnabled;
+		}
 
 
 		// Finalize class params declaration
 		// Finalize class params declaration
 		//
 		//