From d85aba8ebc860b05605bde17f1e1d8bfda538701 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Fri, 3 Dec 2010 10:18:28 +0000 Subject: [PATCH] Improved change tracking: user login replaced by the full name if available Added a tab into the CSV import: browse the CSV imports history Finalized the read-only mode (distinguish between users and everybody, admin message displayed on top of the main screen) SVN:trunk[1007] --- application/itopwebpage.class.inc.php | 28 +- application/ui.extkeywidget.class.inc.php | 9 +- core/bulkchange.class.inc.php | 266 ++++++++++++++++++ core/cmdbchange.class.inc.php | 28 ++ core/config.class.inc.php | 29 +- core/metamodel.class.php | 26 +- core/userrights.class.inc.php | 68 ++++- css/light-grey.css | 10 +- dictionaries/de.dictionary.itop.core.php | 3 +- dictionaries/de.dictionary.itop.ui.php | 8 + dictionaries/dictionary.itop.core.php | 1 + dictionaries/dictionary.itop.ui.php | 11 + dictionaries/es_cr.dictionary.itop.ui.php | 11 + dictionaries/fr.dictionary.itop.core.php | 1 + dictionaries/fr.dictionary.itop.ui.php | 11 + dictionaries/pt_br.dictionary.itop.ui.php | 9 + images/locked.png | Bin 0 -> 4233 bytes images/unlocked.png | Bin 0 -> 4116 bytes modules/authent-local/model.authent-local.php | 9 +- pages/UI.php | 54 +--- pages/ajax.render.php | 5 + pages/csvimport.php | 21 +- portal/index.php | 18 +- webservices/import.php | 9 +- 24 files changed, 503 insertions(+), 132 deletions(-) create mode 100644 images/locked.png create mode 100644 images/unlocked.png diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index 3fc98dc15..76ecbc155 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -632,13 +632,32 @@ EOF } $sLogOffMenu .= "\n\n\n"; - if (MetaModel::DBIsReadOnly()) + $sRestrictions = ''; + if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE)) { - $sApplicationMode = Dict::S('UI:ApplicationReadOnly'); + if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE)) + { + $sRestrictions = Dict::S('UI:AccessRO-All'); + } + } + elseif (!MetaModel::DBHasAccess(ACCESS_USER_WRITE)) + { + $sRestrictions = Dict::S('UI:AccessRO-Users'); + } + + if (strlen($sRestrictions) > 0) + { + $sAdminMessage = trim(utils::GetConfig()->Get('access_message')); + $sApplicationBanner = ''; + $sApplicationBanner .= ' '.$sRestrictions.''; + if (strlen($sAdminMessage) > 0) + { + $sApplicationBanner .= ' '.$sAdminMessage.''; + } } else { - $sApplicationMode = ''; + $sApplicationBanner = ''; } //$sLogOffMenu = ""; @@ -667,8 +686,9 @@ EOF echo '
'; echo '
'; + echo '
'.$sApplicationBanner.'
'; echo ' '; //echo '        
'; diff --git a/application/ui.extkeywidget.class.inc.php b/application/ui.extkeywidget.class.inc.php index 10374b656..b4c32690c 100644 --- a/application/ui.extkeywidget.class.inc.php +++ b/application/ui.extkeywidget.class.inc.php @@ -258,14 +258,7 @@ EOF $oObj->UpdateObject($this->sFormPrefix.$this->iId); $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); - if (UserRights::IsImpersonated()) - { - $sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser()); - } - else - { - $sUserString = UserRights::GetUser(); - } + $sUserString = CMDBChange::GetCurrentUserName(); $oMyChange->Set("userinfo", $sUserString); $iChangeId = $oMyChange->DBInsert(); $oObj->DBInsertTracked($oMyChange); diff --git a/core/bulkchange.class.inc.php b/core/bulkchange.class.inc.php index d25e1aac7..ea071dfae 100644 --- a/core/bulkchange.class.inc.php +++ b/core/bulkchange.class.inc.php @@ -789,6 +789,272 @@ class BulkChange return $aResult; } + + /** + * Display the history of bulk imports + */ + static function DisplayImportHistory(WebPage $oPage, $bFromAjax = false, $bShowAll = false) + { + $sAjaxDivId = "CSVImportHistory"; + if (!$bFromAjax) + { + $oPage->add('
'); + } + + $oPage->p(Dict::S('UI:History:BulkImports+')); + + $oBulkChangeSearch = DBObjectSearch::FromOQL("SELECT CMDBChange WHERE userinfo LIKE '%(CSV)'"); + + $iQueryLimit = $bShowAll ? 0 : utils::GetConfig()->GetMaxDisplayLimit() + 1; + $oBulkChanges = new DBObjectSet($oBulkChangeSearch, array('date' => false), array(), $iQueryLimit); + + $oAppContext = new ApplicationContext(); + + $bLimitExceeded = false; + if ($oBulkChanges->Count() > utils::GetConfig()->GetMaxDisplayLimit()) + { + $bLimitExceeded = true; + if (!$bShowAll) + { + $iMaxObjects = utils::GetConfig()->GetMinDisplayLimit(); + $oBulkChanges->SetLimit($iMaxObjects); + } + } + $oBulkChanges->Seek(0); + + $aDetails = array(); + while ($oChange = $oBulkChanges->Fetch()) + { + $sDate = ''.$oChange->Get('date').''; + $sUser = $oChange->GetUserName(); + if (preg_match('/^(.*)\\(CSV\\)$/i', $oChange->Get('userinfo'), $aMatches)) + { + $sUser = $aMatches[1]; + } + else + { + $sUser = $oChange->Get('userinfo'); + } + + $oOpSearch = DBObjectSearch::FromOQL("SELECT CMDBChangeOpCreate WHERE change = :change_id"); + $oOpSet = new DBObjectSet($oOpSearch, array(), array('change_id' => $oChange->GetKey())); + $iCreated = $oOpSet->Count(); + + //while ($oCreated = $oOpSet->Fetch()) + //{ + // $oPage->p("Created ".$oCreated->Get('objclass')."::".$oCreated->Get('objkey')); + //} + + $oOpSearch = DBObjectSearch::FromOQL("SELECT CMDBChangeOpSetAttribute WHERE change = :change_id"); + $oOpSet = new DBObjectSet($oOpSearch, array(), array('change_id' => $oChange->GetKey())); + + $aModified = array(); + $aAttList = array(); + while ($oModified = $oOpSet->Fetch()) + { + $sClass = $oModified->Get('objclass'); + $iKey = $oModified->Get('objkey'); + $sAttCode = $oModified->Get('attcode'); + + $aAttList[$sClass][$sAttCode] = true; + $aModified["$sClass::$iKey"] = true; + } + $iModified = count($aModified); + + // Assumption: there is only one class of objects being loaded + // Then the last class found gives us the class for every object + + $aDetails[] = array('date' => $sDate, 'user' => $sUser, 'class' => $sClass, 'created' => $iCreated, 'modified' => $iModified); + + } + + $aConfig = array( 'date' => array('label' => Dict::S('UI:History:Date'), 'description' => Dict::S('UI:History:Date+')), + 'user' => array('label' => Dict::S('UI:History:User'), 'description' => Dict::S('UI:History:User+')), + 'class' => array('label' => Dict::S('Core:AttributeClass'), 'description' => Dict::S('Core:AttributeClass+')), + 'created' => array('label' => Dict::S('UI:History:StatsCreations'), 'description' => Dict::S('UI:History:StatsCreations+')), + 'modified' => array('label' => Dict::S('UI:History:StatsModifs'), 'description' => Dict::S('UI:History:StatsModifs+')), + ); + + if ($bLimitExceeded) + { + if ($bShowAll) + { + // Collapsible list + $oPage->add('

'.Dict::Format('UI:CountOfResults', $oBulkChanges->Count()).'  '.Dict::S('UI:CollapseList').'

'); + } + else + { + // Truncated list + $iMinDisplayLimit = utils::GetConfig()->GetMinDisplayLimit(); + $sCollapsedLabel = Dict::Format('UI:TruncatedResults', $iMinDisplayLimit, $oBulkChanges->Count()); + $sLinkLabel = Dict::S('UI:DisplayAll'); + $oPage->add('

'.$sCollapsedLabel.'  '.$sLinkLabel.'

'); + + $oPage->add_ready_script( +<<GetForLink(); + $oPage->add_script( +<<table($aConfig, $aDetails); + + if (!$bFromAjax) + { + $oPage->add('
'); + } + } + + /** + * Display the details of an import + */ + static function DisplayImportHistoryDetails(iTopWebPage $oPage, $iChange) + { + if ($iChange == 0) + { + throw new Exception("Missing parameter changeid"); + } + $oChange = MetaModel::GetObject('CMDBChange', $iChange, false); + if (is_null($oChange)) + { + throw new Exception("Unknown change: $iChange"); + } + $oPage->add("

".Dict::Format('UI:History:BulkImportDetails', $oChange->Get('date'), $oChange->GetUserName())."

\n"); + + // Assumption : change made one single class of objects + $aObjects = array(); + $aAttributes = array(); // array of attcode => occurences + + $oOpSearch = DBObjectSearch::FromOQL("SELECT CMDBChangeOp WHERE change = :change_id"); + $oOpSet = new DBObjectSet($oOpSearch, array(), array('change_id' => $iChange)); + while ($oOperation = $oOpSet->Fetch()) + { + $sClass = $oOperation->Get('objclass'); + $iKey = $oOperation->Get('objkey'); + $iObjId = "$sClass::$iKey"; + if (!isset($aObjects[$iObjId])) + { + $aObjects[$iObjId] = array(); + $aObjects[$iObjId]['__class__'] = $sClass; + $aObjects[$iObjId]['__id__'] = $iKey; + } + if (get_class($oOperation) == 'CMDBChangeOpCreate') + { + $aObjects[$iObjId]['__created__'] = true; + } + elseif (is_subclass_of($oOperation, 'CMDBChangeOpSetAttribute')) + { + $sAttCode = $oOperation->Get('attcode'); + + if (get_class($oOperation) == 'CMDBChangeOpSetAttributeScalar') + { + $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + if ($oAttDef->IsExternalKey()) + { + $oOldTarget = MetaModel::GetObject($oAttDef->GetTargetClass(), $oOperation->Get('oldvalue')); + $oNewTarget = MetaModel::GetObject($oAttDef->GetTargetClass(), $oOperation->Get('newvalue')); + $sOldValue = $oOldTarget->GetHyperlink(); + $sNewValue = $oNewTarget->GetHyperlink(); + } + else + { + $sOldValue = $oOperation->GetAsHTML('oldvalue'); + $sNewValue = $oOperation->GetAsHTML('newvalue'); + } + $aObjects[$iObjId][$sAttCode] = $sOldValue.' -> '.$sNewValue; + } + else + { + $aObjects[$iObjId][$sAttCode] = 'n/a'; + } + + if (isset($aAttributes[$sAttCode])) + { + $aAttributes[$sAttCode]++; + } + else + { + $aAttributes[$sAttCode] = 1; + } + } + } + + $aDetails = array(); + foreach($aObjects as $iUId => $aObjData) + { + $aRow = array(); + $oObject = MetaModel::GetObject($aObjData['__class__'], $aObjData['__id__'], false); + if (is_null($oObject)) + { + $aRow['object'] = $aObjData['__class__'].'::'.$aObjData['__id__'].' (deleted)'; + } + else + { + $aRow['object'] = $oObject->GetHyperlink(); + } + if (isset($aObjData['__created__'])) + { + $aRow['operation'] = Dict::S('Change:ObjectCreated'); + } + else + { + $aRow['operation'] = Dict::S('Change:ObjectModified'); + } + foreach ($aAttributes as $sAttCode => $iOccurences) + { + if (isset($aObjData[$sAttCode])) + { + $aRow[$sAttCode] = $aObjData[$sAttCode]; + } + elseif (!is_null($oObject)) + { + // This is the current vaslue: $oObject->GetAsHtml($sAttCode) + // whereas we are displaying the value that was set at the time + // the object was created + // This requires addtional coding...let's do that later + $aRow[$sAttCode] = ''; + } + else + { + $aRow[$sAttCode] = ''; + } + } + $aDetails[] = $aRow; + } + + $aConfig = array(); + $aConfig['object'] = array('label' => MetaModel::GetName($sClass), 'description' => MetaModel::GetClassDescription($sClass)); + $aConfig['operation'] = array('label' => Dict::S('UI:History:Changes'), 'description' => Dict::S('UI:History:Changes+')); + foreach ($aAttributes as $sAttCode => $iOccurences) + { + $aConfig[$sAttCode] = array('label' => MetaModel::GetLabel($sClass, $sAttCode), 'description' => MetaModel::GetDescription($sClass, $sAttCode)); + } + $oPage->table($aConfig, $aDetails); + } } diff --git a/core/cmdbchange.class.inc.php b/core/cmdbchange.class.inc.php index 4f7a0c80c..db2a9f84b 100644 --- a/core/cmdbchange.class.inc.php +++ b/core/cmdbchange.class.inc.php @@ -49,6 +49,34 @@ class CMDBChange extends DBObject MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); } + + // Helper to keep track of the author of a given change, + // taking into account a variety of cases (contact attached or not, impersonation) + static public function GetCurrentUserName() + { + if (UserRights::IsImpersonated()) + { + $sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUserFriendlyName(), UserRights::GetUserFriendlyName()); + } + else + { + $sUserString = UserRights::GetUserFriendlyName(); + } + return $sUserString; + } + + public function GetUserName() + { + if (preg_match('/^(.*)\\(CSV\\)$/i', $this->Get('userinfo'), $aMatches)) + { + $sUser = $aMatches[1]; + } + else + { + $sUser = $this->Get('userinfo'); + } + return $sUser; + } } ?> diff --git a/core/config.class.inc.php b/core/config.class.inc.php index 51ef33192..87de78e8d 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -18,6 +18,11 @@ define('ITOP_VERSION', '$ITOP_VERSION$'); define('ITOP_REVISION', '$WCREV$'); define('ITOP_BUILD_DATE', '$WCNOW$'); +define('ACCESS_USER_WRITE', 1); +define('ACCESS_ADMIN_WRITE', 2); +define('ACCESS_FULL', ACCESS_USER_WRITE | ACCESS_ADMIN_WRITE); +define('ACCESS_READONLY', 0); + /** * Configuration read/write * @@ -143,14 +148,6 @@ class Config 'source_of_value' => '', 'show_in_conf_sample' => false, ), - 'database_read_only' => array( - 'type' => 'bool', - 'description' => 'Freeze the data for administration purposes - administrators can still do anything... in appearance!', - 'default' => false, - 'value' => '', - 'source_of_value' => '', - 'show_in_conf_sample' => false, - ), // Levels that trigger a confirmation in the CSV import/synchro wizard 'csv_import_min_object_confirmation' => array( 'type' => 'integer', @@ -184,6 +181,22 @@ class Config 'source_of_value' => '', 'show_in_conf_sample' => false, ), + 'access_mode' => array( + 'type' => 'integer', + 'description' => 'Combination of flags (ACCESS_USER_WRITE | ACCESS_ADMIN_WRITE, or ACCESS_FULL)', + 'default' => ACCESS_FULL, + 'value' => ACCESS_FULL, + 'source_of_value' => '', + 'show_in_conf_sample' => true, + ), + 'access_message' => array( + 'type' => 'string', + 'description' => 'Message displayed to the users when there is any access restriction', + 'default' => 'iTop is temporarily frozen, please wait... (the admin team)', + 'value' => '', + 'source_of_value' => '', + 'show_in_conf_sample' => true, + ), ); public function IsProperty($sPropCode) diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 687774695..40de91821 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -2547,28 +2547,26 @@ abstract class MetaModel /* * Determines wether the target DB is frozen or not - * 1 - consider the DB property 'status' - * 2 - check the setting 'database_read_only' */ public static function DBIsReadOnly() { - $sStatus = DBProperty::GetProperty('status', null); - if (!is_null($sStatus)) + // Improvement: check the mySQL variable -> Read-only + + if (UserRights::IsAdministrator()) { - switch (strtolower(trim($sStatus))) - { - case 'fullaccess': - $ret = false; - break; - default: - $ret = true; - } + return (!self::DBHasAccess(ACCESS_ADMIN_WRITE)); } else { - $ret = self::$m_oConfig->Get('database_read_only'); + return (!self::DBHasAccess(ACCESS_USER_WRITE)); } - return $ret; + } + + public static function DBHasAccess($iRequested = ACCESS_FULL) + { + $iMode = self::$m_oConfig->Get('access_mode'); + if (($iMode & $iRequested) == 0) return false; + return true; } protected static function MakeDictEntry($sKey, $sValueFromOldSystem, $sDefaultValue, &$bNotInDico) diff --git a/core/userrights.class.inc.php b/core/userrights.class.inc.php index bd3bb81e2..7ef26d117 100644 --- a/core/userrights.class.inc.php +++ b/core/userrights.class.inc.php @@ -110,6 +110,35 @@ abstract class User extends cmdbAbstractObject abstract public function CanChangePassword(); abstract public function ChangePassword($sOldPassword, $sNewPassword); + /* + * Compute a name in best effort mode + */ + public function GetFriendlyName() + { + if (!MetaModel::IsValidAttCode(get_class($this), 'contactid')) + { + return $this->Get('login'); + } + if ($this->Get('contactid') != 0) + { + $sFirstName = $this->Get('first_name'); + $sLastName = $this->Get('last_name'); + $sEmail = $this->Get('email'); + if (strlen($sFirstName) > 0) + { + return "$sFirstName $sLastName"; + } + elseif (strlen($sEmail) > 0) + { + return "$sLastName <$sEmail>"; + } + else + { + return $sLastName; + } + } + } + /* * Overload the standard behavior */ @@ -490,6 +519,24 @@ class UserRights return $oUser->Get('contactid'); } + // Render the user name in best effort mode + public static function GetUserFriendlyName($sName = '') + { + if (empty($sName)) + { + $oUser = self::$m_oUser; + } + else + { + $oUser = FindUser($sName); + } + if (is_null($oUser)) + { + return ''; + } + return $oUser->GetFriendlyName(); + } + public static function IsImpersonated() { if (is_null(self::$m_oRealUser)) @@ -517,6 +564,15 @@ class UserRights return self::$m_oRealUser->GetKey(); } + public static function GetRealUserFriendlyName() + { + if (is_null(self::$m_oRealUser)) + { + return ''; + } + return self::$m_oRealUser->GetFriendlyName(); + } + protected static function CheckLogin() { if (!self::IsLoggedIn()) @@ -551,8 +607,6 @@ class UserRights // When initializing, we need to let everything pass trough if (!self::CheckLogin()) return true; - if (self::IsAdministrator($oUser)) return true; - if (MetaModel::DBIsReadOnly()) { if ($iActionCode == UR_ACTION_MODIFY) return false; @@ -561,6 +615,8 @@ class UserRights if ($iActionCode == UR_ACTION_BULK_DELETE) return false; } + if (self::IsAdministrator($oUser)) return true; + if (MetaModel::HasCategory($sClass, 'bizmodel')) { // #@# Temporary????? @@ -589,8 +645,6 @@ class UserRights // When initializing, we need to let everything pass trough if (!self::CheckLogin()) return true; - if (self::IsAdministrator($oUser)) return true; - if (MetaModel::DBIsReadOnly()) { if ($iActionCode == UR_ACTION_MODIFY) return false; @@ -599,6 +653,8 @@ class UserRights if ($iActionCode == UR_ACTION_BULK_DELETE) return false; } + if (self::IsAdministrator($oUser)) return true; + if (MetaModel::HasCategory($sClass, 'bizmodel')) { if (is_null($oUser)) @@ -619,8 +675,6 @@ class UserRights // When initializing, we need to let everything pass trough if (!self::CheckLogin()) return true; - if (self::IsAdministrator($oUser)) return true; - if (MetaModel::DBIsReadOnly()) { if ($iActionCode == UR_ACTION_MODIFY) return false; @@ -629,6 +683,8 @@ class UserRights if ($iActionCode == UR_ACTION_BULK_DELETE) return false; } + if (self::IsAdministrator($oUser)) return true; + // this module is forbidden for non admins if (MetaModel::HasCategory($sClass, 'addon/userrights')) return false; diff --git a/css/light-grey.css b/css/light-grey.css index df86f5135..0cdd24416 100644 --- a/css/light-grey.css +++ b/css/light-grey.css @@ -807,6 +807,14 @@ div#logo div { background: url(../images/banner-bkg.png) repeat-x scroll 0 0 transparent; text-align: right; } +#admin-banner { + float: left; + margin-top: 2px; + padding: 8px; + border: 1px solid #c33; + background-color: #fee; + -moz-border-radius: 0.5em; +} #global-search { height: 55px; float: right; @@ -908,4 +916,4 @@ span.form_validation { text-align:center; width: 95%; -moz-border-radius: 0.5em; -} \ No newline at end of file +} diff --git a/dictionaries/de.dictionary.itop.core.php b/dictionaries/de.dictionary.itop.core.php index 70d7f591c..b55bf3bf5 100644 --- a/dictionaries/de.dictionary.itop.core.php +++ b/dictionaries/de.dictionary.itop.core.php @@ -203,7 +203,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( // Used by CMDBChangeOp... & derived classes Dict::Add('DE DE', 'German', 'Deutsch', array( 'Change:ObjectCreated' => 'Objekt erstellt', - 'Change:ObjectDeleted' => 'Objekt erstellt', + 'Change:ObjectDeleted' => 'Object deleted', + 'Change:ObjectModified' => 'Object modified', 'Change:AttName_SetTo_NewValue_PreviousValue_OldValue' => '%1$s geändert zu %2$s (vorheriger Wert: %3$s)', 'Change:Text_AppendedTo_AttName' => '%1$s zugefügt an %2$s', 'Change:AttName_Changed_PreviousValue_OldValue' => '%1$s modifiziert, vorheriger Wert: %2$s', diff --git a/dictionaries/de.dictionary.itop.ui.php b/dictionaries/de.dictionary.itop.ui.php index 45d82e464..784f62402 100644 --- a/dictionaries/de.dictionary.itop.ui.php +++ b/dictionaries/de.dictionary.itop.ui.php @@ -439,6 +439,12 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'UI:History:User+' => 'Benutzer, der die Änderung durchführte', 'UI:History:Changes' => 'Änderungen', 'UI:History:Changes+' => 'Änderungen, die am Objekt durchgeführt wurden', + 'UI:History:StatsCreations' => 'Created', + 'UI:History:StatsCreations+' => 'Count of objects created', + 'UI:History:StatsModifs' => 'Modified', + 'UI:History:StatsModifs+' => 'Count of objects modified', + 'UI:History:StatsDeletes' => 'Deleted', + 'UI:History:StatsDeletes+' => 'Count of objects deleted', 'UI:Loading' => 'Laden...', 'UI:Menu:Actions' => 'Aktionen', 'UI:Menu:New' => 'Neu...', @@ -481,6 +487,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'UI:LogOff:ThankYou' => 'Vielen Dank dafür, dass Sie iTop benutzen!', 'UI:LogOff:ClickHereToLoginAgain' => 'Klicken Sie hier, um sich wieder anzumelden...', 'UI:ChangePwdMenu' => 'Passwort ändern...', + 'UI:AccessRO-All' => 'iTop is read-only', + 'UI:AccessRO-Users' => 'iTop is read-only for end-users', 'UI:Login:RetypePwdDoesNotMatch' => 'Neues Passwort und das wiederholte Passwort entsprechen nicht überein!', 'UI:Button:Login' => 'iTop betreten', 'UI:Login:Error:AccessRestricted' => 'Der iTop-Zugang ist gesperrt. Bitte kontaktieren Sie einen iTop-Administrator.', diff --git a/dictionaries/dictionary.itop.core.php b/dictionaries/dictionary.itop.core.php index 5c1047a2f..67b9eb290 100644 --- a/dictionaries/dictionary.itop.core.php +++ b/dictionaries/dictionary.itop.core.php @@ -203,6 +203,7 @@ Dict::Add('EN US', 'English', 'English', array( Dict::Add('EN US', 'English', 'English', array( 'Change:ObjectCreated' => 'Object created', 'Change:ObjectDeleted' => 'Object deleted', + 'Change:ObjectModified' => 'Object modified', 'Change:AttName_SetTo_NewValue_PreviousValue_OldValue' => '%1$s set to %2$s (previous value: %3$s)', 'Change:Text_AppendedTo_AttName' => '%1$s appended to %2$s', 'Change:AttName_Changed_PreviousValue_OldValue' => '%1$s modified, previous value: %2$s', diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php index db93edd04..294582d00 100644 --- a/dictionaries/dictionary.itop.ui.php +++ b/dictionaries/dictionary.itop.ui.php @@ -399,12 +399,21 @@ Dict::Add('EN US', 'English', 'English', array( 'UI:History:LastModified_On_By' => 'Last modified on %1$s by %2$s.', 'UI:HistoryTab' => 'History', 'UI:NotificationsTab' => 'Notifications', + 'UI:History:BulkImports' => 'History', + 'UI:History:BulkImports+' => 'List of CSV imports (last first)', + 'UI:History:BulkImportDetails' => 'Changes resulting from the CSV import performed on %1$s (by %2$s)', 'UI:History:Date' => 'Date', 'UI:History:Date+' => 'Date of the change', 'UI:History:User' => 'User', 'UI:History:User+' => 'User who made the change', 'UI:History:Changes' => 'Changes', 'UI:History:Changes+' => 'Changes made to the object', + 'UI:History:StatsCreations' => 'Created', + 'UI:History:StatsCreations+' => 'Count of objects created', + 'UI:History:StatsModifs' => 'Modified', + 'UI:History:StatsModifs+' => 'Count of objects modified', + 'UI:History:StatsDeletes' => 'Deleted', + 'UI:History:StatsDeletes+' => 'Count of objects deleted', 'UI:Loading' => 'Loading...', 'UI:Menu:Actions' => 'Actions', 'UI:Menu:New' => 'New...', @@ -447,6 +456,8 @@ Dict::Add('EN US', 'English', 'English', array( 'UI:LogOff:ThankYou' => 'Thank you for using iTop', 'UI:LogOff:ClickHereToLoginAgain' => 'Click here to login again...', 'UI:ChangePwdMenu' => 'Change Password...', + 'UI:AccessRO-All' => 'iTop is read-only', + 'UI:AccessRO-Users' => 'iTop is read-only for end-users', 'UI:Login:RetypePwdDoesNotMatch' => 'New password and retyped new password do not match !', 'UI:Button:Login' => 'Enter iTop', 'UI:Login:Error:AccessRestricted' => 'iTop access is restricted. Please, contact an iTop administrator.', diff --git a/dictionaries/es_cr.dictionary.itop.ui.php b/dictionaries/es_cr.dictionary.itop.ui.php index 22531fe3b..9c04ad709 100644 --- a/dictionaries/es_cr.dictionary.itop.ui.php +++ b/dictionaries/es_cr.dictionary.itop.ui.php @@ -406,12 +406,21 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array( 'UI:NoObject_Class_ToDisplay' => 'No %1$s to display', 'UI:History:LastModified_On_By' => 'Last modified on %1$s by %2$s.', 'UI:HistoryTab' => 'History', + 'UI:History:BulkImports' => 'History', + 'UI:History:BulkImports+' => 'List of CSV imports (last first)', + 'UI:History:BulkImportDetails' => 'Changes resulting from the CSV import performed on %1$s (by %2$s)', 'UI:History:Date' => 'Date', 'UI:History:Date+' => 'Date of the change', 'UI:History:User' => 'User', 'UI:History:User+' => 'User who made the change', 'UI:History:Changes' => 'Changes', 'UI:History:Changes+' => 'Changes made to the object', + 'UI:History:StatsCreations' => 'Created', + 'UI:History:StatsCreations+' => 'Count of objects created', + 'UI:History:StatsModifs' => 'Modified', + 'UI:History:StatsModifs+' => 'Count of objects modified', + 'UI:History:StatsDeletes' => 'Deleted', + 'UI:History:StatsDeletes+' => 'Count of objects deleted', 'UI:Loading' => 'Loading...', 'UI:Menu:Actions' => 'Actions', 'UI:Menu:New' => 'New...', @@ -451,6 +460,8 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array( 'UI:Login:IncorrectOldPassword' => 'Error: the old password is incorrect', 'UI:LogOffMenu' => 'Log off', 'UI:ChangePwdMenu' => 'Change Password...', + 'UI:AccessRO-All' => 'iTop is read-only', + 'UI:AccessRO-Users' => 'iTop is read-only for end-users', 'UI:Login:RetypePwdDoesNotMatch' => 'New password and retyped new password do not match !', 'UI:Button:Login' => 'Enter iTop', 'UI:Login:Error:AccessRestricted' => 'iTop access is restricted. Please, contact an iTop administrator.', diff --git a/dictionaries/fr.dictionary.itop.core.php b/dictionaries/fr.dictionary.itop.core.php index 575a66aa1..b5d0e6434 100644 --- a/dictionaries/fr.dictionary.itop.core.php +++ b/dictionaries/fr.dictionary.itop.core.php @@ -204,6 +204,7 @@ Dict::Add('FR FR', 'French', 'Français', array( Dict::Add('FR FR', 'French', 'Français', array( 'Change:ObjectCreated' => 'Elément créé', 'Change:ObjectDeleted' => 'Elément effacé', + 'Change:ObjectModified' => 'Elément modifié', 'Change:AttName_SetTo_NewValue_PreviousValue_OldValue' => '%1$s modifié en %2$s (ancienne valeur: %3$s)', 'Change:Text_AppendedTo_AttName' => '%1$s ajouté à %2$s', 'Change:AttName_Changed_PreviousValue_OldValue' => '%1$s modifié, ancienne valeur: %2$s', diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php index e189be551..80ddb3f8b 100644 --- a/dictionaries/fr.dictionary.itop.ui.php +++ b/dictionaries/fr.dictionary.itop.ui.php @@ -400,12 +400,21 @@ Dict::Add('FR FR', 'French', 'Français', array( 'UI:History:LastModified_On_By' => 'Dernière modification par %2$s le %1$s.', 'UI:HistoryTab' => 'Historique', 'UI:NotificationsTab' => 'Notifications', + 'UI:History:BulkImports' => 'Historique', + 'UI:History:BulkImports+' => 'Liste des imports CSV (le dernier est en haut de la liste)', + 'UI:History:BulkImportDetails' => 'Changements résultant de l\'import CSV du %1$s (auteur: %2$s)', 'UI:History:Date' => 'Date', 'UI:History:Date+' => 'Date de modification', 'UI:History:User' => 'Utilisateur', 'UI:History:User+' => 'Utilisateur qui a fait la modification', 'UI:History:Changes' => 'Changements', 'UI:History:Changes+' => 'Changements sur cet objet', + 'UI:History:StatsCreations' => 'Créés', + 'UI:History:StatsCreations+' => 'Nombre d\'objets créés', + 'UI:History:StatsModifs' => 'Modifiés', + 'UI:History:StatsModifs+' => 'Nombre d\'objets modifiés', + 'UI:History:StatsDeletes' => 'Effacés', + 'UI:History:StatsDeletes+' => 'Nombre d\'objets effacés', 'UI:Loading' => 'Chargement...', 'UI:Menu:Actions' => 'Actions', 'UI:Menu:New' => 'Créer...', @@ -448,6 +457,8 @@ Dict::Add('FR FR', 'French', 'Français', array( 'UI:LogOff:ThankYou' => 'Merci d\'avoir utilisé iTop', 'UI:LogOff:ClickHereToLoginAgain' => 'Cliquez ici pour vous reconnecter...', 'UI:ChangePwdMenu' => 'Changer de mot de passe...', + 'UI:AccessRO-All' => 'iTop est en lecture seule', + 'UI:AccessRO-Users' => 'iTop est en lecture seule pour les utilisateurs finaux', 'UI:Login:RetypePwdDoesNotMatch' => 'Les deux saisies du nouveau mot de passe ne sont pas identiques !', 'UI:Button:Login' => 'Entrer dans iTop', 'UI:Login:Error:AccessRestricted' => 'L\'accès à iTop est soumis à autorisation. Merci de contacter votre administrateur iTop.', diff --git a/dictionaries/pt_br.dictionary.itop.ui.php b/dictionaries/pt_br.dictionary.itop.ui.php index 3ad8ccd73..0262db4f6 100644 --- a/dictionaries/pt_br.dictionary.itop.ui.php +++ b/dictionaries/pt_br.dictionary.itop.ui.php @@ -408,12 +408,19 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'UI:NoObject_Class_ToDisplay' => 'Nenhum %1$s para mostrar', 'UI:History:LastModified_On_By' => 'Ultima modificacao de %1$s por %2$s.', 'UI:HistoryTab' => 'Historico', + 'UI:History:BulkImports' => 'History', + 'UI:History:BulkImports+' => 'List of CSV imports (last first)', + 'UI:History:BulkImportDetails' => 'Changes resulting from the CSV import performed on %1$s (by %2$s)', 'UI:History:Date' => 'Data', 'UI:History:Date+' => 'Data da alteração', 'UI:History:User' => 'Usuario', 'UI:History:User+' => 'Usuario que fez alteração', 'UI:History:Changes' => 'Alterações', 'UI:History:Changes+' => 'Alterações feita no objeto', + 'UI:History:StatsModifs' => 'Modified', + 'UI:History:StatsModifs+' => 'Count of objects modified', + 'UI:History:StatsDeletes' => 'Deleted', + 'UI:History:StatsDeletes+' => 'Count of objects deleted', 'UI:Loading' => 'Carregando...', 'UI:Menu:Actions' => 'Ações', 'UI:Menu:New' => 'Novo...', @@ -453,6 +460,8 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'UI:Login:IncorrectOldPassword' => 'Erro: senha incorreta', 'UI:LogOffMenu' => 'Sair', 'UI:ChangePwdMenu' => 'Alterar senha...', + 'UI:AccessRO-All' => 'iTop is read-only', + 'UI:AccessRO-Users' => 'iTop is read-only for end-users', 'UI:Login:RetypePwdDoesNotMatch' => 'A nova senha nao confere!', 'UI:Button:Login' => 'Enter iTop', 'UI:Login:Error:AccessRestricted' => 'iTop accesso restrito. Por favor, contate o suporte.', diff --git a/images/locked.png b/images/locked.png new file mode 100644 index 0000000000000000000000000000000000000000..b00530a3258f4e728b050f7ec969ba5bbcc79c02 GIT binary patch literal 4233 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 literal 0 HcmV?d00001 diff --git a/images/unlocked.png b/images/unlocked.png new file mode 100644 index 0000000000000000000000000000000000000000..69a09fd824970f9ab148a599a17a2f9839075bf5 GIT binary patch literal 4116 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'password', $sNewPassword); $oChange = MetaModel::NewObject("CMDBChange"); $oChange->Set("date", time()); - if (UserRights::IsImpersonated()) - { - $sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser()); - } - else - { - $sUserString = UserRights::GetUser(); - } + $sUserString = CMDBChange::GetCurrentUserName(); $oChange->Set("userinfo", $sUserString); $oChange->DBInsert(); $this->DBUpdateTracked($oChange, true); diff --git a/pages/UI.php b/pages/UI.php index 5ef83c13a..e6a42faf7 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -130,14 +130,7 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed) // $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); - if (UserRights::IsImpersonated()) - { - $sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser()); - } - else - { - $sUserString = UserRights::GetUser(); - } + $sUserString = CMDBChange::GetCurrentUserName(); $oMyChange->Set("userinfo", $sUserString); $oMyChange->DBInsert(); @@ -918,14 +911,7 @@ try $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); - if (UserRights::IsImpersonated()) - { - $sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser()); - } - else - { - $sUserString = UserRights::GetUser(); - } + $sUserString = CMDBChange::GetCurrentUserName(); $oMyChange->Set("userinfo", $sUserString); $iChangeId = $oMyChange->DBInsert(); $oObj->DBUpdateTracked($oMyChange); @@ -1055,14 +1041,7 @@ try { $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); - if (UserRights::IsImpersonated()) - { - $sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser()); - } - else - { - $sUserString = UserRights::GetUser(); - } + $sUserString = CMDBChange::GetCurrentUserName(); $oMyChange->Set("userinfo", $sUserString); $iChangeId = $oMyChange->DBInsert(); $oObj->DBInsertTracked($oMyChange); @@ -1111,14 +1090,7 @@ try $sClassLabel = MetaModel::GetName($sClass); $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); - if (UserRights::IsImpersonated()) - { - $sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser()); - } - else - { - $sUserString = UserRights::GetUser(); - } + $sUserString = CMDBChange::GetCurrentUserName(); $oMyChange->Set("userinfo", $sUserString); $iChangeId = $oMyChange->DBInsert(); $oObj->DBInsertTracked($oMyChange); @@ -1276,14 +1248,7 @@ EOF { $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); - if (UserRights::IsImpersonated()) - { - $sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser()); - } - else - { - $sUserString = UserRights::GetUser(); - } + $sUserString = CMDBChange::GetCurrentUserName(); $oMyChange->Set("userinfo", $sUserString); $iChangeId = $oMyChange->DBInsert(); $oObj->DBUpdateTracked($oMyChange); @@ -1328,14 +1293,7 @@ EOF $sLinkingAttCode = utils::ReadPostedParam('linking_attcode', ''); $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); - if (UserRights::IsImpersonated()) - { - $sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser()); - } - else - { - $sUserString = UserRights::GetUser(); - } + $sUserString = CMDBChange::GetCurrentUserName(); $oMyChange->Set("userinfo", $sUserString); $iChangeId = $oMyChange->DBInsert(); diff --git a/pages/ajax.render.php b/pages/ajax.render.php index c51c3c662..41579f8c4 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -206,6 +206,11 @@ try } break; + case 'displayCSVHistory': + $bShowAll = (utils::ReadParam('showall', 'false') == 'true'); + BulkChange::DisplayImportHistory($oPage, true, $bShowAll); + break; + case 'details': $key = utils::ReadParam('id', 0); $oFilter = new DBObjectSearch($sClass); diff --git a/pages/csvimport.php b/pages/csvimport.php index 6b9aef28d..984488987 100644 --- a/pages/csvimport.php +++ b/pages/csvimport.php @@ -35,7 +35,6 @@ try require_once(APPROOT.'/application/loginwebpage.class.inc.php'); LoginWebPage::DoLogin(); // Check user rights and prompt if needed - $oAppContext = new ApplicationContext(); $iStep = utils::ReadParam('step', 1); $oPage = new iTopWebPage(Dict::S('UI:Title:BulkImport')); @@ -344,14 +343,7 @@ try // We're doing it for real, let's create a change $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); - if (UserRights::IsImpersonated()) - { - $sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser()); - } - else - { - $sUserString = UserRights::GetUser(); - } + $sUserString = CMDBChange::GetCurrentUserName(); $sUserString .= ' (CSV)'; $oMyChange->Set("userinfo", $sUserString); $iChangeId = $oMyChange->DBInsert(); @@ -1246,7 +1238,7 @@ EOF ); $oPage->add_ready_script('DoPreview();'); } - + /** * Prompt for the data to be loaded (either via a file or a copy/paste) * @param WebPage $oPage The current web page @@ -1412,10 +1404,19 @@ $('#select_template_class').change( function() { }); EOF ); + + $oPage->SetCurrentTabContainer('tabs1'); + $oPage->SetCurrentTab(Dict::S('UI:History:BulkImports')); + BulkChange::DisplayImportHistory($oPage); } switch($iStep) { + case 10: + $iChange = (int)utils::ReadParam('changeid', 0); + BulkChange::DisplayImportHistoryDetails($oPage, $iChange); + break; + case 5: LoadData($oPage); break; diff --git a/portal/index.php b/portal/index.php index 31f10776c..f61b2c255 100644 --- a/portal/index.php +++ b/portal/index.php @@ -326,14 +326,7 @@ function DoCreateRequest($oP, $oUserOrg) { $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); - if (UserRights::IsImpersonated()) - { - $sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser()); - } - else - { - $sUserString = UserRights::GetUser(); - } + $sUserString = CMDBChange::GetCurrentUserName(); $oMyChange->Set("userinfo", $sUserString); $iChangeId = $oMyChange->DBInsert(); $oRequest->DBInsertTracked($oMyChange); @@ -622,14 +615,7 @@ function DoCloseRequest($oP, UserRequest $oRequest) { $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); - if (UserRights::IsImpersonated()) - { - $sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser()); - } - else - { - $sUserString = UserRights::GetUser(); - } + $sUserString = CMDBChange::GetCurrentUserName(); $oMyChange->Set("userinfo", $sUserString); $iChangeId = $oMyChange->DBInsert(); $oRequest->DBUpdateTracked($oMyChange); diff --git a/webservices/import.php b/webservices/import.php index 4e04bc7aa..dee36f50b 100644 --- a/webservices/import.php +++ b/webservices/import.php @@ -483,14 +483,7 @@ try { $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); - if (UserRights::IsImpersonated()) - { - $sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUser(), UserRights::GetUser()); - } - else - { - $sUserString = UserRights::GetUser(); - } + $sUserString = CMDBChange::GetCurrentUserName(); if (strlen($sComment) > 0) { $sMoreInfo = 'Web Service (CSV) - '.$sComment;