diff --git a/application/applicationextension.inc.php b/application/applicationextension.inc.php new file mode 100644 index 000000000..68378a894 --- /dev/null +++ b/application/applicationextension.inc.php @@ -0,0 +1,49 @@ + + * @author Romain Quetiez + * @author Denis Flaven + * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL + */ + +interface iApplicationUIExtension +{ + public function OnDisplayProperties($oObject, WebPage $oPage, $bEditMode = false); + public function OnDisplayRelations($oObject, WebPage $oPage, $bEditMode = false); + public function OnFormSubmit($oObject, $sFormPrefix = ''); + + public function EnumUsedAttributes($oObject); // Not yet implemented + public function GetIcon($oObject); // Not yet implemented + public function GetHilightClass($oObject); + + public function EnumAllowedActions(DBObjectSet $oSet); +} + +interface iApplicationObjectExtension +{ + public function OnCheckToWrite($oObject); + public function OnCheckToDelete($oObject); + public function OnDBUpdate($oObject, $oChange = null); + public function OnDBInsert($oObject, $oChange = null); + public function OnDBDelete($oObject, $oChange = null); +} + +?> diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 4689c1c21..8e3d06ded 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -32,6 +32,7 @@ define('HILIGHT_CLASS_OK', 'green'); define('HILIGHT_CLASS_NONE', ''); require_once(APPROOT.'/core/cmdbobject.class.inc.php'); +require_once(APPROOT.'/application/applicationextension.inc.php'); require_once(APPROOT.'/application/utils.inc.php'); require_once(APPROOT.'/application/applicationcontext.class.inc.php'); require_once(APPROOT.'/application/ui.linkswidget.class.inc.php'); @@ -75,7 +76,7 @@ interface iDisplay abstract class cmdbAbstractObject extends CMDBObject implements iDisplay { protected $m_iFormId; // The ID of the form used to edit the object (when in edition mode !) - + public static function GetUIPage() { return '../pages/UI.php'; @@ -163,6 +164,11 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay function DisplayBareProperties(WebPage $oPage, $bEditMode = false) { $oPage->add($this->GetBareProperties($oPage, $bEditMode)); + + foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) + { + $oExtensionInstance->OnDisplayProperties($this, $oPage, $bEditMode); + } } function DisplayBareRelations(WebPage $oPage, $bEditMode = false) @@ -306,6 +312,11 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay $oBlock->Display($oPage, 'notifications', array()); } } + + foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) + { + $oExtensionInstance->OnDisplayRelations($this, $oPage, $bEditMode); + } } function GetBareProperties(WebPage $oPage, $bEditMode = false) @@ -1772,6 +1783,34 @@ EOF } } + // $m_highlightComparison[previous][new] => next value + protected static $m_highlightComparison = array( + HILIGHT_CLASS_CRITICAL => array( + HILIGHT_CLASS_CRITICAL => HILIGHT_CLASS_CRITICAL, + HILIGHT_CLASS_WARNING => HILIGHT_CLASS_CRITICAL, + HILIGHT_CLASS_OK => HILIGHT_CLASS_CRITICAL, + HILIGHT_CLASS_NONE => HILIGHT_CLASS_CRITICAL, + ), + HILIGHT_CLASS_WARNING => array( + HILIGHT_CLASS_CRITICAL => HILIGHT_CLASS_CRITICAL, + HILIGHT_CLASS_WARNING => HILIGHT_CLASS_WARNING, + HILIGHT_CLASS_OK => HILIGHT_CLASS_WARNING, + HILIGHT_CLASS_NONE => HILIGHT_CLASS_WARNING, + ), + HILIGHT_CLASS_OK => array( + HILIGHT_CLASS_CRITICAL => HILIGHT_CLASS_CRITICAL, + HILIGHT_CLASS_WARNING => HILIGHT_CLASS_WARNING, + HILIGHT_CLASS_OK => HILIGHT_CLASS_OK, + HILIGHT_CLASS_NONE => HILIGHT_CLASS_OK, + ), + HILIGHT_CLASS_NONE => array( + HILIGHT_CLASS_CRITICAL => HILIGHT_CLASS_CRITICAL, + HILIGHT_CLASS_WARNING => HILIGHT_CLASS_WARNING, + HILIGHT_CLASS_OK => HILIGHT_CLASS_OK, + HILIGHT_CLASS_NONE => HILIGHT_CLASS_NONE, + ), + ); + /** * This function returns a 'hilight' CSS class, used to hilight a given row in a table * There are currently (i.e defined in the CSS) 4 possible values HILIGHT_CLASS_CRITICAL, @@ -1784,7 +1823,15 @@ EOF { // Possible return values are: // HILIGHT_CLASS_CRITICAL, HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE - return HILIGHT_CLASS_NONE; // Not hilighted by default + $current = HILIGHT_CLASS_NONE; // Not hilighted by default + + // Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information + foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) + { + $new = $oExtensionInstance->GetHilightClass($this); + @$current = self::$m_highlightComparison[$current][$new]; + } + return $current; } /** @@ -1959,5 +2006,91 @@ EOF } } } + + protected function DBInsertTracked_Internal($bDoNotReload = false) + { + $res = parent::DBInsertTracked_Internal($bDoNotReload); + + // Invoke extensions after insertion (the object must exist, have an id, etc.) + foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) + { + $oExtensionInstance->OnDBInsert($this, self::$m_oCurrChange); + } + + return $res; + } + + protected function DBCloneTracked_Internal($newKey = null) + { + $oNewObj = parent::DBCloneTracked_Internal($newKey); + + // Invoke extensions after insertion (the object must exist, have an id, etc.) + foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) + { + $oExtensionInstance->OnDBInsert($oNewObj, self::$m_oCurrChange); + } + return $oNewObj; + } + + protected function DBUpdateTracked_Internal() + { + $res = parent::DBUpdateTracked_Internal(); + + // Invoke extensions after the update (could be before) + foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) + { + $oExtensionInstance->OnDBUpdate($this, self::$m_oCurrChange); + } + return $res; + } + + protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues) + { + // Todo - invoke the extension + return parent::BulkUpdateTracked_Internal($oFilter, $aValues); + } + + protected function DBDeleteTracked_Internal() + { + // Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information + foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) + { + $oExtensionInstance->OnDBDelete($this, self::$m_oCurrChange); + } + + return parent::DBDeleteTracked_Internal(); + } + + protected static function BulkDeleteTracked_Internal(DBObjectSearch $oFilter) + { + // Todo - invoke the extension + return parent::BulkDeleteTracked_Internal($oFilter); + } + + public function DoCheckToWrite() + { + parent::DoCheckToWrite(); + foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) + { + $aNewIssues = $oExtensionInstance->OnCheckToWrite($this); + if (count($aNewIssues) > 0) + { + $this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues); + } + } + } + + protected function DoCheckToDelete() + { + parent::DoCheckToDelete(); + foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) + { + $aNewIssues = $oExtensionInstance->OnCheckToDelete($this); + if (count($aNewIssues) > 0) + { + $this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues); + } + } + } } ?> diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 31080aa53..6cd987d7c 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -1059,6 +1059,15 @@ class MenuBlock extends DisplayBlock if ($bIsModifyAllowed) { $aActions[] = array ('label' => Dict::S('UI:Menu:Add'), 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&addObjects=true&$sContext"); } if ($bIsModifyAllowed) { $aActions[] = array ('label' => Dict::S('UI:Menu:Manage'), 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&sContext"); } } + $this->AddMenuSeparator($aActions); + foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) + { + $oSet->Rewind(); + foreach($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $sUrl) + { + $aActions[] = array ('label' => $sLabel, 'url' => $sUrl); + } + } break; default: @@ -1089,6 +1098,15 @@ class MenuBlock extends DisplayBlock $aActions[] = array ('label' => Dict::S('UI:Menu:EMail'), 'url' => "mailto:?subject=".$oSet->GetFilter()->__DescribeHTML()."&body=".urlencode("$sUrl?operation=search&filter=$sFilter&$sContext")); $aActions[] = array ('label' => Dict::S('UI:Menu:CSVExport'), 'url' => "../pages/$sUIPage?operation=search&filter=$sFilter&format=csv&$sContext"); } + $this->AddMenuSeparator($aActions); + foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) + { + $oSet->Rewind(); + foreach($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $sUrl) + { + $aActions[] = array ('label' => $sLabel, 'url' => $sUrl); + } + } } $sHtml .= "
    \n
  • ".Dict::S('UI:Menu:Actions')."\n
      \n"; foreach ($aActions as $aAction) diff --git a/core/dbobject.class.php b/core/dbobject.class.php index 19d977c37..138a29b94 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -46,6 +46,8 @@ abstract class DBObject private $m_bCheckStatus = null; // Means: the object has been verified and is consistent with integrity rules // if null, then the check has to be performed again to know the status protected $m_aCheckIssues = null; + protected $m_aDeleteIssues = null; + protected $m_aAsArgs = null; // The current object as a standard argument (cache) private $m_bFullyLoaded = false; // Compound objects can be partially loaded @@ -786,11 +788,14 @@ abstract class DBObject // check if it is allowed to delete the existing object from the database // a displayable error is returned - public function CheckToDelete() + protected function DoCheckToDelete() { - return true; + $this->m_aDeleteIssues = array(); // Ok } + // final public function CheckToDelete() - THE EQUIVALENT OF CheckToWrite IS NOT AVAILABLE + // Todo - split the "DeleteObject()" function (UI.php) and move the generic part in cmdbAbstractObject, etc. + protected function ListChangedValues(array $aProposal) { $aDelta = array(); @@ -1346,15 +1351,24 @@ abstract class DBObject */ public function GetDeletionScheme(&$aDeletedObjs, &$aResetedObjs, $aVisited = array()) { - if (array_key_exists(get_class($this), $aVisited)) + $sClass = get_class($this); + $iThisId = $this->GetKey(); + + if (array_key_exists($sClass, $aVisited)) { - if (in_array($this->GetKey(), $aVisited[get_class($this)])) + if (in_array($iThisId, $aVisited[$sClass])) { return; } } - $aVisited[get_class($this)] = $this->GetKey(); + $aVisited[$sClass] = $iThisId; + $aDeletedObjs[$sClass][$iThisId]['to_delete'] = $this; + $aDeletedObjs[$sClass][$iThisId]['auto_delete'] = true; + // Check the node itself + $this->DoCheckToDelete(); + $aDeletedObjs[$sClass][$iThisId]['issues'] = $this->m_aDeleteIssues; + $aDependentObjects = $this->GetReferencingObjects(); foreach ($aDependentObjects as $sRemoteClass => $aPotentialDeletes) { @@ -1397,12 +1411,15 @@ abstract class DBObject { // First time we find the given object in the list // (and most likely case is that no other occurence will be found) - $aDeletedObjs[$sRemoteClass][$iId]['to_delete'] = $oDependentObj; - $aDeletedObjs[$sRemoteClass][$iId]['auto_delete'] = ($iDeletePropagationOption == DEL_AUTO); - // Recursively inspect this object if ($iDeletePropagationOption == DEL_AUTO) { + // Recursively inspect this object $oDependentObj->GetDeletionScheme($aDeletedObjs, $aResetedObjs, $aVisited); + } + else + { + $aDeletedObjs[$sRemoteClass][$iId]['to_delete'] = $oDependentObj; + $aDeletedObjs[$sRemoteClass][$iId]['auto_delete'] = false; } } } diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 2a65b3560..9ebab4ad0 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -3444,6 +3444,30 @@ abstract class MetaModel public static function Startup($sConfigFile, $bModelOnly = false) { self::LoadConfig($sConfigFile); + + $aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension'); + foreach($aInterfaces as $sInterface) + { + self::$m_aExtensionClasses[$sInterface] = array(); + } + + foreach(get_declared_classes() as $sPHPClass) + { + $oRefClass = new ReflectionClass($sPHPClass); + $oExtensionInstance = null; + foreach($aInterfaces as $sInterface) + { + if ($oRefClass->implementsInterface($sInterface)) + { + if (is_null($oExtensionInstance)) + { + $oExtensionInstance = new $sPHPClass; + } + self::$m_aExtensionClasses[$sInterface][] = $oExtensionInstance; + } + } + } + if ($bModelOnly) return; CMDBSource::SelectDB(self::$m_sDBName); @@ -3570,6 +3594,8 @@ abstract class MetaModel return self::$m_oConfig; } + protected static $m_aExtensionClasses = array(); + protected static $m_aPlugins = array(); public static function RegisterPlugin($sType, $sName, $aInitCallSpec = array()) { @@ -3960,6 +3986,20 @@ abstract class MetaModel return str_replace($aSearches, $aReplacements, $aInput); } + /** + * Returns an array of classes implementing the given interface + */ + public static function EnumPlugins($sInterface) + { + if (array_key_exists($sInterface, self::$m_aExtensionClasses)) + { + return self::$m_aExtensionClasses[$sInterface]; + } + else + { + return array(); + } + } } // class MetaModel diff --git a/dictionaries/de.dictionary.itop.ui.php b/dictionaries/de.dictionary.itop.ui.php index 784f62402..d416f0ed6 100644 --- a/dictionaries/de.dictionary.itop.ui.php +++ b/dictionaries/de.dictionary.itop.ui.php @@ -653,6 +653,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'UI:Title:BulkDeletionOf_Count_ObjectsOf_Class' => 'Massenlöschung von %1$d Objekten der %2$s', 'UI:Delete:NotAllowedToDelete' => 'Sie sind nicht berechtigt, dieses Objekt zu löschen.', 'UI:Delete:NotAllowedToUpdate_Fields' => 'Sie sind nicht berechtigt, die folgenden Felder zu aktualisieren: %1$s', + 'UI:Error:CannotDeleteBecause' => 'This object could not be deleted because: %1$s', 'UI:Error:NotEnoughRightsToDelete' => 'Dieses Objekt konnte nicht gelöscht werden, da der derzeitige Benutzer nicht die notwendigen Rechte dazu besitzt.', 'UI:Error:CannotDeleteBecauseOfDepencies' => 'Dieses Objekt konnte nicht gelöscht werden, da zuerst dazu einige manuelle Operationen durchgeführt werden müssen.', 'UI:Archive_User_OnBehalfOf_User' => '%1$s im Auftrag von %2$s', @@ -664,8 +665,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'UI:Delete:_Name_Class_Deleted' => '%1$s - %2$s gelöscht.', 'UI:Delete:ConfirmDeletionOf_Name' => 'Löschung von %1$s', 'UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class' => 'Löschung von %1$d Objekten der Klasse %2$s', - 'UI:Delete:ShouldBeDeletedAtomaticallyButNotAllowed' => 'Sollte automatisch gelöscht werden, aber Sie sind nicht berechtigt, dies zu tun', - 'UI:Delete:MustBeDeletedManuallyButNotAllowed' => 'Muss automatisch gelöscht werden, aber Sie sind nicht berechtigt, dieses Objekt zu löschen. Bitte kontaktieren Sie Ihren Anwendungs-Administrator', +// 'UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible' => 'Sollte automatisch gelöscht werden, aber Sie sind nicht berechtigt, dies zu tun', +// 'UI:Delete:MustBeDeletedManuallyButNotPossible' => 'Muss automatisch gelöscht werden, aber Sie sind nicht berechtigt, dieses Objekt zu löschen. Bitte kontaktieren Sie Ihren Anwendungs-Administrator', 'UI:Delete:WillBeDeletedAutomatically' => 'Wird automatisch gelöscht', 'UI:Delete:MustBeDeletedManually' => 'Muss manuell gelöscht werden', 'UI:Delete:CannotUpdateBecause_Issue' => 'Sollte automatisch aktualisiert werden, aber: %1$s', diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php index d57801380..dddc5dfc0 100644 --- a/dictionaries/dictionary.itop.ui.php +++ b/dictionaries/dictionary.itop.ui.php @@ -623,8 +623,11 @@ Dict::Add('EN US', 'English', 'English', array( 'UI:Delete:NotAllowedToDelete' => 'You are not allowed to delete this object', 'UI:Delete:NotAllowedToUpdate_Fields' => 'You are not allowed to update the following field(s): %1$s', 'UI:Error:NotEnoughRightsToDelete' => 'This object could not be deleted because the current user do not have sufficient rights', + 'UI:Error:CannotDeleteBecause' => 'This object could not be deleted because: %1$s', 'UI:Error:CannotDeleteBecauseOfDepencies' => 'This object could not be deleted because some manual operations must be performed prior to that', + 'UI:Error:CannotDeleteBecauseManualOpNeeded' => 'This object could not be deleted because some manual operations must be performed prior to that', 'UI:Archive_User_OnBehalfOf_User' => '%1$s on behalf of %2$s', + 'UI:Delete:Deleted' => 'deleted', 'UI:Delete:AutomaticallyDeleted' => 'automatically deleted', 'UI:Delete:AutomaticResetOf_Fields' => 'automatic reset of field(s): %1$s', 'UI:Delete:CleaningUpRefencesTo_Object' => 'Cleaning up all references to %1$s...', @@ -633,8 +636,9 @@ Dict::Add('EN US', 'English', 'English', array( 'UI:Delete:_Name_Class_Deleted' => '%1$s - %2$s deleted.', 'UI:Delete:ConfirmDeletionOf_Name' => 'Deletion of %1$s', 'UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class' => 'Deletion of %1$d objects of class %2$s', - 'UI:Delete:ShouldBeDeletedAtomaticallyButNotAllowed' => 'Should be automaticaly deleted, but you are not allowed to do so', - 'UI:Delete:MustBeDeletedManuallyButNotAllowed' => 'Must be deleted manually - but you are not allowed to delete this object, please contact your application admin', + 'UI:Delete:CannotDeleteBecause' => 'Could not be deleted: %1$s', + 'UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible' => 'Should be automaticaly deleted, but this is not feasible: %1$s', + 'UI:Delete:MustBeDeletedManuallyButNotPossible' => 'Must be deleted manually, but this is not feasible: %1$s', 'UI:Delete:WillBeDeletedAutomatically' => 'Will be automaticaly deleted', 'UI:Delete:MustBeDeletedManually' => 'Must be deleted manually', 'UI:Delete:CannotUpdateBecause_Issue' => 'Should be automatically updated, but: %1$s', diff --git a/dictionaries/es_cr.dictionary.itop.ui.php b/dictionaries/es_cr.dictionary.itop.ui.php index a873a82ef..beb527d17 100644 --- a/dictionaries/es_cr.dictionary.itop.ui.php +++ b/dictionaries/es_cr.dictionary.itop.ui.php @@ -628,8 +628,8 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array( 'UI:Delete:_Name_Class_Deleted' => '%1$s - %2$s borrado.', 'UI:Delete:ConfirmDeletionOf_Name' => 'Borrado de %1$s', 'UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class' => 'Borrado de %1$d objetos de al clase %2$s', - 'UI:Delete:ShouldBeDeletedAtomaticallyButNotAllowed' => 'Beberia ser eliminado automaticamente, pero usted no esta autorizado para hacerlo', - 'UI:Delete:MustBeDeletedManuallyButNotAllowed' => 'Debe ser borrado manualmente - pero usted no esta autorizado para borrar este objeto, por favor contacte al administrador de la aplicacion', +// 'UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible' => 'Beberia ser eliminado automaticamente, pero usted no esta autorizado para hacerlo', +// 'UI:Delete:MustBeDeletedManuallyButNotPossible' => 'Debe ser borrado manualmente - pero usted no esta autorizado para borrar este objeto, por favor contacte al administrador de la aplicacion', 'UI:Delete:WillBeDeletedAutomatically' => 'Sera borrado automaticamente', 'UI:Delete:MustBeDeletedManually' => 'Debe ser borrado manualmente', 'UI:Delete:CannotUpdateBecause_Issue' => 'Debe ser actualizado automaticamente, pero: %1$s', diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php index 9cd88c190..ee5908233 100644 --- a/dictionaries/fr.dictionary.itop.ui.php +++ b/dictionaries/fr.dictionary.itop.ui.php @@ -626,9 +626,11 @@ Dict::Add('FR FR', 'French', 'Français', array( 'UI:Title:BulkDeletionOf_Count_ObjectsOf_Class' => 'Suppression massive de %1$d objets de type %2$s', 'UI:Delete:NotAllowedToDelete' => 'Vous n\'êtes pas autorisé à supprimer cet objet', 'UI:Delete:NotAllowedToUpdate_Fields' => 'Vous n\'êtes pas autorisé à mettre à jour les champs suivants : %1$s', + 'UI:Error:CannotDeleteBecause' => 'This object could not be deleted because: %1$s', 'UI:Error:NotEnoughRightsToDelete' => 'Cet objet ne peut pas être supprimé car l\'utilisateur courant n\'a pas les droits nécessaires.', 'UI:Error:CannotDeleteBecauseOfDepencies' => 'Cet objet ne peut pas être supprimé, des opérations manuelles sont nécessaire avant sa suppression.', 'UI:Archive_User_OnBehalfOf_User' => '%1$s pour %2$s', + 'UI:Delete:Deleted' => 'supprimé', 'UI:Delete:AutomaticallyDeleted' => 'supprimé automatiquement', 'UI:Delete:AutomaticResetOf_Fields' => 'mise à jour automatique des champ(s): %1$s', 'UI:Delete:CleaningUpRefencesTo_Object' => 'Suppression de toutes les références vers %1$s...', @@ -637,8 +639,9 @@ Dict::Add('FR FR', 'French', 'Français', array( 'UI:Delete:_Name_Class_Deleted' => ' %2$s %1$s supprimé.', 'UI:Delete:ConfirmDeletionOf_Name' => 'Suppression de %1$s', 'UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class' => 'Suppression de %1$d objets de type %2$s', - 'UI:Delete:ShouldBeDeletedAtomaticallyButNotAllowed' => 'Devrait être supprimé automatiquement, mais vous n\'avez pas le droit d\'effectuer cette opération.', - 'UI:Delete:MustBeDeletedManuallyButNotAllowed' => 'Doit être supprimé manuellement - mais vous n\'avez pas le droit de supprimer cet objet, contactez votre administrateur pour régler ce problème', + 'UI:Delete:CannotDeleteBecause' => 'Ne peux pas être supprimé: %1$s', + 'UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible' => 'Devrait être supprimé automatiquement, mais cela n\'est pas possible: %1$s', + 'UI:Delete:MustBeDeletedManuallyButNotPossible' => 'Doit être supprimé manuellement, mais cela n\'est pas possible: %1$s', 'UI:Delete:WillBeDeletedAutomatically' => 'Sera supprimé automatiquement', 'UI:Delete:MustBeDeletedManually' => 'Doit être supprimé manuellement', 'UI:Delete:CannotUpdateBecause_Issue' => 'Devrait être mis à jour automatiquement, mais: %1$s', diff --git a/dictionaries/pt_br.dictionary.itop.ui.php b/dictionaries/pt_br.dictionary.itop.ui.php index 0262db4f6..7a04ec4e2 100644 --- a/dictionaries/pt_br.dictionary.itop.ui.php +++ b/dictionaries/pt_br.dictionary.itop.ui.php @@ -623,6 +623,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'UI:Title:BulkDeletionOf_Count_ObjectsOf_Class' => 'Bulk deletion of %1$d objects of class %2$s', 'UI:Delete:NotAllowedToDelete' => 'Permissão negado para eliminar este objeto', 'UI:Delete:NotAllowedToUpdate_Fields' => 'Permissão negado para atualizar o(s) seguinte(s) campo(s): %1$s', + 'UI:Error:CannotDeleteBecause' => 'This object could not be deleted because: %1$s', 'UI:Error:NotEnoughRightsToDelete' => 'Este objeto não pode ser apagado pelo usuário não ter direitos administrativos', 'UI:Error:CannotDeleteBecauseOfDepencies' => 'This object could not be deleted because some manual operations must be performed prior to that', 'UI:Archive_User_OnBehalfOf_User' => '%1$s em nome de %2$s', @@ -634,8 +635,8 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array( 'UI:Delete:_Name_Class_Deleted' => '%1$s - %2$s eliminado.', 'UI:Delete:ConfirmDeletionOf_Name' => 'Eliminação de %1$s', 'UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class' => 'Eliminação do objeto %1$d da classe %2$s', - 'UI:Delete:ShouldBeDeletedAtomaticallyButNotAllowed' => 'Should be automaticaly deleted, but you are not allowed to do so', - 'UI:Delete:MustBeDeletedManuallyButNotAllowed' => 'Must be deleted manually - but you are not allowed to delete this object, please contact your application admin', +// 'UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible' => 'Should be automaticaly deleted, but this is not feasible: %1$s', +// 'UI:Delete:MustBeDeletedManuallyButNotPossible' => 'Must be deleted manually, but this is not feasible: %1$s', 'UI:Delete:WillBeDeletedAutomatically' => 'Será automaticamente excluído', 'UI:Delete:MustBeDeletedManually' => 'Deve ser excluído manualmente', 'UI:Delete:CannotUpdateBecause_Issue' => 'Devem ser atualizados automaticamente, mas: %1$s', diff --git a/dictionaries/ru.dictionary.itop.ui.php b/dictionaries/ru.dictionary.itop.ui.php index 17d33e677..03c65f5e1 100644 --- a/dictionaries/ru.dictionary.itop.ui.php +++ b/dictionaries/ru.dictionary.itop.ui.php @@ -609,6 +609,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'UI:Title:BulkDeletionOf_Count_ObjectsOf_Class' => 'Пакетное удаление %1$d объектов класса %2$s', 'UI:Delete:NotAllowedToDelete' => 'Вы не можете удалить этот объект', 'UI:Delete:NotAllowedToUpdate_Fields' => 'Вы не можете обновить следующее(ие) поле(я): %1$s', + 'UI:Error:CannotDeleteBecause' => 'This object could not be deleted because: %1$s', 'UI:Error:NotEnoughRightsToDelete' => 'Этот объект не может быть удален, потому что текущий пользователь не имеет достаточных прав', 'UI:Error:CannotDeleteBecauseOfDepencies' => 'Этот объект не может быть удален, потому что некоторые ручные операции должны быть выполнены до этого', 'UI:Archive_User_OnBehalfOf_User' => '%1$s от имени %2$s', @@ -620,8 +621,8 @@ Dict::Add('RU RU', 'Russian', 'Русский', array( 'UI:Delete:_Name_Class_Deleted' => '%1$s - %2$s удалено.', 'UI:Delete:ConfirmDeletionOf_Name' => 'Удаление %1$s', 'UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class' => 'Удаление %1$d объектов класса %2$s', - 'UI:Delete:ShouldBeDeletedAtomaticallyButNotAllowed' => 'Должно быть автоматичски удалено, но вы не можете это сделать', - 'UI:Delete:MustBeDeletedManuallyButNotAllowed' => 'Необходимо удалить вручную - но вы не можете удалить этот объект, свяжитесь с администратором вашего приложения', +// 'UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible' => 'Должно быть автоматичски удалено, но вы не можете это сделать', +// 'UI:Delete:MustBeDeletedManuallyButNotPossible' => 'Необходимо удалить вручную - но вы не можете удалить этот объект, свяжитесь с администратором вашего приложения', 'UI:Delete:WillBeDeletedAutomatically' => 'Будет удалено автоматически', 'UI:Delete:MustBeDeletedManually' => 'Необходимо удалить вручную', 'UI:Delete:CannotUpdateBecause_Issue' => 'Должно быть автоматически обновлено, но: %1$s', diff --git a/dictionaries/tr.dictionary.itop.ui.php b/dictionaries/tr.dictionary.itop.ui.php index ca7eaef84..7130ad157 100644 --- a/dictionaries/tr.dictionary.itop.ui.php +++ b/dictionaries/tr.dictionary.itop.ui.php @@ -608,6 +608,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'UI:Title:BulkDeletionOf_Count_ObjectsOf_Class' => '%2$s sınıfına ait çoklu %1$d nesne silimi', 'UI:Delete:NotAllowedToDelete' => 'Bu nesneyi silmek için yetkiniz yok', 'UI:Delete:NotAllowedToUpdate_Fields' => '%1$s alanlarını güncellemek için yetkiniz yok', + 'UI:Error:CannotDeleteBecause' => 'This object could not be deleted because: %1$s', 'UI:Error:NotEnoughRightsToDelete' => 'Nesne yetersiz yetki nedeniyle silinemedi', 'UI:Error:CannotDeleteBecauseOfDepencies' => 'Bu nesneyi silmek için öncelikli dışarıdan yapılması gereken işlemler var', 'UI:Archive_User_OnBehalfOf_User' => '%1$s on behalf of %2$s', @@ -619,8 +620,8 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array( 'UI:Delete:_Name_Class_Deleted' => '%1$s - %2$s silindi.', 'UI:Delete:ConfirmDeletionOf_Name' => '%1$s\'in silimi', 'UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class' => '%2$s sınıfına ait %1$d nesnelerinin silimi ', - 'UI:Delete:ShouldBeDeletedAtomaticallyButNotAllowed' => 'Otomatik silimesini mi istiyorsunuz, ancak buna yetkiniz yok', - 'UI:Delete:MustBeDeletedManuallyButNotAllowed' => 'Manuel silinmeli - ancak bu nesneyi silmeye yetkiniz yok, lütfen sistem yöneticisiyle irtibata geçiniz.', +// 'UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible' => 'Otomatik silimesini mi istiyorsunuz, ancak buna yetkiniz yok', +// 'UI:Delete:MustBeDeletedManuallyButNotPossible' => 'Manuel silinmeli - ancak bu nesneyi silmeye yetkiniz yok, lütfen sistem yöneticisiyle irtibata geçiniz.', 'UI:Delete:WillBeDeletedAutomatically' => 'Otomatik olarak silinecek', 'UI:Delete:MustBeDeletedManually' => 'Manuel silinmeli', 'UI:Delete:CannotUpdateBecause_Issue' => 'Otomatik güncellenmeli, ancak: %1$s', diff --git a/dictionaries/zh.dictionary.itop.ui.php b/dictionaries/zh.dictionary.itop.ui.php index f4387d8a2..af18e8682 100644 --- a/dictionaries/zh.dictionary.itop.ui.php +++ b/dictionaries/zh.dictionary.itop.ui.php @@ -607,6 +607,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'UI:Title:BulkDeletionOf_Count_ObjectsOf_Class' => '批量删除 %1$d 个 %2$s 类的对象', 'UI:Delete:NotAllowedToDelete' => '您未被允许删除该对象', 'UI:Delete:NotAllowedToUpdate_Fields' => '您未被允许更新下述栏目: %1$s', + 'UI:Error:CannotDeleteBecause' => 'This object could not be deleted because: %1$s', 'UI:Error:NotEnoughRightsToDelete' => '该对象不能被删除, 因为当前用户没有足够权限', 'UI:Error:CannotDeleteBecauseOfDepencies' => '该对象不能被删除, 因为一些手工操作必须事先完成', 'UI:Archive_User_OnBehalfOf_User' => '%1$s on behalf of %2$s', @@ -618,8 +619,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array( 'UI:Delete:_Name_Class_Deleted' => '%1$s - %2$s 删除了.', 'UI:Delete:ConfirmDeletionOf_Name' => '删除 %1$s', 'UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class' => '删除 %2$s 类的 %1$d 个对象', - 'UI:Delete:ShouldBeDeletedAtomaticallyButNotAllowed' => '应该自动删除, 但您未被允许这样做', - 'UI:Delete:MustBeDeletedManuallyButNotAllowed' => '必须手工删除 - 但您未被允许删除该对象, 请联系您的应用程序系统管理员', +// 'UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible' => '应该自动删除, 但您未被允许这样做', +// 'UI:Delete:MustBeDeletedManuallyButNotPossible' => '必须手工删除 - 但您未被允许删除该对象, 请联系您的应用程序系统管理员', 'UI:Delete:WillBeDeletedAutomatically' => '将被自动删除', 'UI:Delete:MustBeDeletedManually' => '必须手工删除', 'UI:Delete:CannotUpdateBecause_Issue' => '应该被自动更新, 但是: %1$s', diff --git a/pages/UI.php b/pages/UI.php index afba7cdd0..4088aba1c 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -28,12 +28,22 @@ */ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed) { - $bFoundManual = false; $bFoundStopper = false; + $bFoundManuelOp = false; // A manual operation is needed + $bFoundManualDel = false; // Manual deletion (or change an ext key) is needed + $bFoundSecurityIssue = false; // Some operation not allowed to the end-user + $iTotalDelete = 0; // count of object that must be deleted $iTotalReset = 0; // count of object for which an ext key will be reset (to 0) $aTotalDeletedObjs = array(); $aTotalResetedObjs = array(); + + $aRequestedByUser = array(); + foreach($aObjects as $oObj) + { + $aRequestedByUser[] = $oObj->GetKey(); + } + foreach($aObjects as $oObj) { // Evaluate the impact on the DB integrity @@ -50,26 +60,36 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed) foreach ($aDeletes as $iId => $aData) { $oToDelete = $aData['to_delete']; + $aTotalDeletedObjs[$sRemoteClass][$iId]['to_delete'] = $oToDelete; + if (in_array($iId, $aRequestedByUser)) + { + $aTotalDeletedObjs[$sRemoteClass][$iId]['requested_by_user'] = true; + } $bDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, DBObjectSet::FromObject($oToDelete)); $aTotalDeletedObjs[$sRemoteClass][$iId]['auto_delete'] = $aData['auto_delete']; if (!$bDeleteAllowed) { $aTotalDeletedObjs[$sRemoteClass][$iId]['issue'] = Dict::S('UI:Delete:NotAllowedToDelete'); $bFoundStopper = true; + $bFoundSecurityIssue = true; } - else + elseif (isset($aData['issues']) && (count($aData['issues']) > 0)) { - $aTotalDeletedObjs[$sRemoteClass][$iId]['to_delete'] = $oToDelete; + $sIssueDesc = implode(', ', $aData['issues']); + $aTotalDeletedObjs[$sRemoteClass][$iId]['issue'] = $sIssueDesc; + $bFoundStopper = true; + $bFoundManuelOp = true; } $bAutoDel = $aData['auto_delete']; if (!$bAutoDel) { - $bFoundManual = true; + $bFoundStopper = true; + $bFoundManualDel = true; } } } - + foreach ($aResetedObjs as $sRemoteClass => $aToReset) { $iTotalReset += count($aToReset); @@ -84,6 +104,7 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed) if (!$bUpdateAllowed) { $bFoundStopper = true; + $bFoundSecurityIssue = true; $aForbiddenKeys[] = $aRemoteAttDef->GetLabel(); } $aExtKeyLabels[] = $aRemoteAttDef->GetLabel(); @@ -117,11 +138,15 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed) $oP->add("

      ".Dict::Format('UI:Title:BulkDeletionOf_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass))."

      \n"); } // Security - do not allow the user to force a forbidden delete by the mean of page arguments... - if ($bFoundStopper) + if ($bFoundSecurityIssue) { throw new CoreException(Dict::S('UI:Error:NotEnoughRightsToDelete')); } - if ($bFoundManual) + if ($bFoundManuelOp) + { + throw new CoreException(Dict::S('UI:Error:CannotDeleteBecauseManualOpNeeded')); + } + if ($bFoundManualDel) { throw new CoreException(Dict::S('UI:Error:CannotDeleteBecauseOfDepencies')); } @@ -143,10 +168,18 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed) { $oToDelete = $aData['to_delete']; + if (isset($aData['requested_by_user'])) + { + $sMessage = Dict::S('UI:Delete:Deleted'); + } + else + { + $sMessage = Dict::S('UI:Delete:AutomaticallyDeleted'); + } $aDisplayData[] = array( 'class' => MetaModel::GetName(get_class($oToDelete)), 'object' => $oToDelete->GetHyperLink(), - 'consequence' => Dict::S('UI:Delete:AutomaticallyDeleted'), + 'consequence' => $sMessage, ); $oToDelete->DBDeleteTracked($oMyChange); @@ -194,11 +227,11 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed) $oP->table($aDisplayConfig, $aDisplayData); } + // Final report foreach($aObjects as $oObj) { $sName = $oObj->GetName(); $sClassLabel = MetaModel::GetName(get_class($oObj)); - $oObj->DBDeleteTracked($oMyChange); $oP->add("

      ".Dict::Format('UI:Delete:_Name_Class_Deleted', $sName, $sClassLabel)."

      \n"); } } @@ -226,18 +259,32 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed) { if ($bAutoDel) { - $sConsequence = Dict::S('UI:Delete:ShouldBeDeletedAtomaticallyButNotAllowed'); + if (isset($aData['requested_by_user'])) + { + $sConsequence = Dict::Format('UI:Delete:CannotDeleteBecause', $aData['issue']); + } + else + { + $sConsequence = Dict::Format('UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible', $aData['issue']); + } } else { - $sConsequence = Dict::S('UI:Delete:MustBeDeletedManuallyButNotAllowed'); + $sConsequence = Dict::Format('UI:Delete:MustBeDeletedManuallyButNotPossible', $aData['issue']); } } else { if ($bAutoDel) { - $sConsequence = Dict::S('UI:Delete:WillBeDeletedAutomatically'); + if (isset($aData['requested_by_user'])) + { + $sConsequence = ''; // not applicable + } + else + { + $sConsequence = Dict::S('UI:Delete:WillBeDeletedAutomatically'); + } } else { @@ -272,19 +319,23 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed) } } - if ($iTotalTargets > 0) + $iInducedDeletions = $iTotalTargets - count($aObjects); + if ($iInducedDeletions > 0) { if (count($aObjects) == 1) { $oObj = $aObjects[0]; - $oP->p(Dict::Format('UI:Delete:Count_Objects/LinksReferencing_Object', $iTotalTargets, $oObj->GetName())); + $oP->p(Dict::Format('UI:Delete:Count_Objects/LinksReferencing_Object', $iInducedDeletions, $oObj->GetName())); } else { - $oP->p(Dict::Format('UI:Delete:Count_Objects/LinksReferencingTheObjects', $iTotalTargets)); + $oP->p(Dict::Format('UI:Delete:Count_Objects/LinksReferencingTheObjects', $iInducedDeletions)); } $oP->p(Dict::S('UI:Delete:ReferencesMustBeDeletedToEnsureIntegrity')); + } + if (($iInducedDeletions > 0) || $bFoundStopper) + { $aDisplayConfig = array(); $aDisplayConfig['class'] = array('label' => 'Class', 'description' => ''); $aDisplayConfig['object'] = array('label' => 'Object', 'description' => ''); @@ -292,13 +343,17 @@ function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bDeleteConfirmed) $oP->table($aDisplayConfig, $aDisplayData); } - if ($iTotalTargets > 0 && ($bFoundManual || $bFoundStopper)) + if ($bFoundStopper) { - if ($bFoundStopper) + if ($bFoundSecurityIssue) { $oP->p(Dict::S('UI:Delete:SorryDeletionNotAllowed')); } - elseif ($bFoundManual) + elseif ($bFoundManualDel) + { + $oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations')); + } + else // $bFoundManualOp { $oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations')); }