Plugin API - alpha version

SVN:trunk[1124]
This commit is contained in:
Romain Quetiez
2011-03-11 11:04:50 +00:00
parent bca713a964
commit 0b32f511b8
14 changed files with 368 additions and 44 deletions

View File

@@ -0,0 +1,49 @@
<?php
// Copyright (C) 2010 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class iPlugin
* Management of application plugin
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @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);
}
?>

View File

@@ -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);
}
}
}
}
?>

View File

@@ -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 .= "<div class=\"itop_popup\"><ul>\n<li>".Dict::S('UI:Menu:Actions')."\n<ul>\n";
foreach ($aActions as $aAction)

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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&aacute;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&aacute; automaticamente exclu&iacute;do',
'UI:Delete:MustBeDeletedManually' => 'Deve ser exclu&iacute;do manualmente',
'UI:Delete:CannotUpdateBecause_Issue' => 'Devem ser atualizados automaticamente, mas: %1$s',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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("<h1>".Dict::Format('UI:Title:BulkDeletionOf_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass))."</h1>\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("<h1>".Dict::Format('UI:Delete:_Name_Class_Deleted', $sName, $sClassLabel)."</h1>\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'));
}