mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
N°659 Console & Portal now we have same object save error messages and sessionmessages
* create new CoreCannotSaveException * throw this exception in DBInsertNoReload/DBUpdate if CheckToWrite fails * console : change UI.php code to catch this exception instead of calling CheckToWrite itself (was called twice :(( ) * portal : specific catch for the new exception * portal : get and displays session messages
This commit is contained in:
@@ -1581,38 +1581,14 @@ EOD
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sTitle
|
||||
* @param string[] $aIssues
|
||||
*
|
||||
* @see AddHeaderMessage
|
||||
*/
|
||||
public function AddHeaderMessageForErrors($sTitle, $aIssues)
|
||||
{
|
||||
$sContent = "<span><strong>$sTitle</strong></span>";
|
||||
|
||||
if (count($aIssues) == 1)
|
||||
{
|
||||
$sContent .= " <span>{$aIssues[0]}</span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sContent .= '\n<ul>';
|
||||
foreach ($aIssues as $sError)
|
||||
{
|
||||
$sContent .= "\n<li>$sError";
|
||||
}
|
||||
$sContent .= '</ul>';
|
||||
}
|
||||
|
||||
$this->AddHeaderMessage($sContent, 'message_error');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds in the page a container with the header_message CSS class
|
||||
*
|
||||
* @param string $sContent
|
||||
* @param string $sCssClasses CSS classes to add to the container
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public function AddHeaderMessage($sContent, $sCssClasses = 'message_info')
|
||||
{
|
||||
|
||||
@@ -107,6 +107,81 @@ class CoreException extends Exception
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class CoreCannotSaveObjectException
|
||||
*
|
||||
* Specialized exception to raise if {@link DBObject::CheckToWrite()} fails, which allow easy data retrieval
|
||||
*
|
||||
* @see \DBObject::DBInsertNoReload()
|
||||
* @see \DBObject::DBUpdate()
|
||||
*
|
||||
* @since 2.6 N°659 uniqueness constraint
|
||||
*/
|
||||
class CoreCannotSaveObjectException extends CoreException
|
||||
{
|
||||
/** @var string[] */
|
||||
private $aIssues;
|
||||
/** @var int */
|
||||
private $iObjectId;
|
||||
/** @var string */
|
||||
private $sObjectClass;
|
||||
|
||||
/**
|
||||
* CoreCannotSaveObjectException constructor.
|
||||
*
|
||||
* @param array $aContextData containing at least those keys : issues, id, class
|
||||
*/
|
||||
public function __construct($aContextData)
|
||||
{
|
||||
$this->aIssues = $aContextData['issues'];
|
||||
$this->iObjectId = $aContextData['id'];
|
||||
$this->sObjectClass = $aContextData['class'];
|
||||
|
||||
$sIssues = implode(', ', $this->aIssues);
|
||||
parent::__construct($sIssues, $aContextData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHtmlMessage()
|
||||
{
|
||||
$sTitle = Dict::S('UI:Error:SaveFailed');
|
||||
$sContent = "<span><strong>{$sTitle}</strong></span>";
|
||||
|
||||
if (count($this->aIssues) == 1)
|
||||
{
|
||||
$sContent .= " <span>{$this->aIssues[0]}</span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sContent .= '\n<ul>';
|
||||
foreach ($this->aIssues as $sError)
|
||||
{
|
||||
$sContent .= "\n<li>$sError";
|
||||
}
|
||||
$sContent .= '</ul>';
|
||||
}
|
||||
|
||||
return $sContent;
|
||||
}
|
||||
|
||||
public function getIssues()
|
||||
{
|
||||
return $this->aIssues;
|
||||
}
|
||||
|
||||
public function getObjectId()
|
||||
{
|
||||
return $this->iObjectId;
|
||||
}
|
||||
|
||||
public function getObjectClass()
|
||||
{
|
||||
return $this->sObjectClass;
|
||||
}
|
||||
}
|
||||
|
||||
class CoreWarning extends CoreException
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1905,8 +1905,18 @@ abstract class DBObject implements iDisplay
|
||||
return $this->m_iKey;
|
||||
}
|
||||
|
||||
// Insert of record for the new object into the database
|
||||
// Returns the key of the newly created object
|
||||
/**
|
||||
* Insert of record for the new object into the database
|
||||
*
|
||||
* @return int key of the newly created object
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreCannotSaveObjectException if {@link CheckToWrite()} returns issues
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \CoreWarning
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function DBInsertNoReload()
|
||||
{
|
||||
if ($this->m_bIsInDB)
|
||||
@@ -1940,8 +1950,7 @@ abstract class DBObject implements iDisplay
|
||||
list($bRes, $aIssues) = $this->CheckToWrite();
|
||||
if (!$bRes)
|
||||
{
|
||||
$sIssues = implode(', ', $aIssues);
|
||||
throw new CoreException("Object not following integrity rules", array('issues' => $sIssues, 'class' => get_class($this), 'id' => $this->GetKey()));
|
||||
throw new CoreCannotSaveObjectException(array('issues' => $aIssues, 'class' => get_class($this), 'id' => $this->GetKey()));
|
||||
}
|
||||
|
||||
// Stop watches
|
||||
@@ -2152,7 +2161,13 @@ abstract class DBObject implements iDisplay
|
||||
$this->m_iKey = self::GetNextTempId(get_class($this));
|
||||
}
|
||||
|
||||
// Update a record
|
||||
/**
|
||||
* Update an object in DB
|
||||
*
|
||||
* @return int object key
|
||||
* @throws \CoreException
|
||||
* @throws \CoreCannotSaveObjectException if {@link CheckToWrite()} returns issues
|
||||
*/
|
||||
public function DBUpdate()
|
||||
{
|
||||
if (!$this->m_bIsInDB)
|
||||
@@ -2205,8 +2220,7 @@ abstract class DBObject implements iDisplay
|
||||
list($bRes, $aIssues) = $this->CheckToWrite();
|
||||
if (!$bRes)
|
||||
{
|
||||
$sIssues = implode(', ', $aIssues);
|
||||
throw new CoreException("Object not following integrity rules", array('issues' => $sIssues, 'class' => get_class($this), 'id' => $this->GetKey()));
|
||||
throw new CoreCannotSaveObjectException(array('issues' => $aIssues, 'class' => get_class($this), 'id' => $this->GetKey()));
|
||||
}
|
||||
|
||||
// Save the original values (will be reset to the new values when the object get written to the DB)
|
||||
@@ -2326,13 +2340,19 @@ abstract class DBObject implements iDisplay
|
||||
$this->RecordAttChanges($aChanges, $aOriginalValues);
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
catch (CoreCannotSaveObjectException $e)
|
||||
{
|
||||
unset($aUpdateReentrance[$sKey]);
|
||||
throw $e;
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
unset($aUpdateReentrance[$sKey]);
|
||||
}
|
||||
|
||||
unset($aUpdateReentrance[$sKey]);
|
||||
return $this->m_iKey;
|
||||
}
|
||||
|
||||
@@ -2342,7 +2362,13 @@ abstract class DBObject implements iDisplay
|
||||
return $this->DBUpdate();
|
||||
}
|
||||
|
||||
// Make the current changes persistent - clever wrapper for Insert or Update
|
||||
/**
|
||||
* Make the current changes persistent - clever wrapper for Insert or Update
|
||||
*
|
||||
* @return int
|
||||
* @throws \CoreCannotSaveObjectException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function DBWrite()
|
||||
{
|
||||
if ($this->m_bIsInDB)
|
||||
|
||||
@@ -19,29 +19,30 @@
|
||||
|
||||
namespace Combodo\iTop\Portal\Form;
|
||||
|
||||
use Exception;
|
||||
use Silex\Application;
|
||||
use utils;
|
||||
use Dict;
|
||||
use IssueLog;
|
||||
use UserRights;
|
||||
use MetaModel;
|
||||
use CMDBSource;
|
||||
use DBObject;
|
||||
use DBObjectSet;
|
||||
use DBSearch;
|
||||
use DBObjectSearch;
|
||||
use InlineImage;
|
||||
use ormTagSet;
|
||||
use AttachmentPlugIn;
|
||||
use AttributeDateTime;
|
||||
use AttributeTagSet;
|
||||
use AttachmentPlugIn;
|
||||
use Combodo\iTop\Form\FormManager;
|
||||
use Combodo\iTop\Form\Form;
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Form\Field\Field;
|
||||
use Combodo\iTop\Form\Field\FileUploadField;
|
||||
use Combodo\iTop\Form\Field\LabelField;
|
||||
use Combodo\iTop\Form\Form;
|
||||
use Combodo\iTop\Form\FormManager;
|
||||
use Combodo\iTop\Portal\Helper\ApplicationHelper;
|
||||
use CoreCannotSaveObjectException;
|
||||
use DBObject;
|
||||
use DBObjectSearch;
|
||||
use DBObjectSet;
|
||||
use DBSearch;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use InlineImage;
|
||||
use IssueLog;
|
||||
use MetaModel;
|
||||
use ormTagSet;
|
||||
use Silex\Application;
|
||||
use UserRights;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Description of objectformmanager
|
||||
@@ -976,7 +977,14 @@ class ObjectFormManager extends FormManager
|
||||
// Writing object to DB
|
||||
$bActivateTriggers = (!$this->oObject->IsNew() && $this->oObject->IsModified());
|
||||
$bWasModified = $this->oObject->IsModified();
|
||||
$this->oObject->DBWrite();
|
||||
try
|
||||
{
|
||||
$this->oObject->DBWrite();
|
||||
}
|
||||
catch (CoreCannotSaveObjectException $e)
|
||||
{
|
||||
throw new Exception($e->getHtmlMessage());
|
||||
}
|
||||
// Finalizing images link to object, otherwise it will be cleaned by the GC
|
||||
InlineImage::FinalizeInlineImages($this->oObject);
|
||||
// Finalizing attachments link to object
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
namespace Combodo\iTop\Portal\Helper;
|
||||
|
||||
use ApplicationContext;
|
||||
use cmdbAbstractObject;
|
||||
use Combodo\iTop\Portal\Brick\AbstractBrick;
|
||||
use Combodo\iTop\Portal\Brick\PortalBrick;
|
||||
use DBObjectSearch;
|
||||
@@ -30,12 +31,10 @@ use Exception;
|
||||
use iPortalUIExtension;
|
||||
use IssueLog;
|
||||
use MetaModel;
|
||||
use cmdbAbstractObject;
|
||||
use ModuleDesign;
|
||||
use Silex\Application;
|
||||
use Symfony\Component\Debug\ErrorHandler;
|
||||
use Symfony\Component\Debug\ExceptionHandler;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Twig_Environment;
|
||||
use Twig_SimpleFilter;
|
||||
@@ -1343,6 +1342,45 @@ class ApplicationHelper
|
||||
return $aUIExtensions;
|
||||
}
|
||||
|
||||
public static function LoadSessionMessages(Application $oApp)
|
||||
{
|
||||
$aAllMessages = array();
|
||||
if ((array_key_exists('obj_messages', $_SESSION)) && (!empty($_SESSION['obj_messages'])))
|
||||
{
|
||||
foreach ($_SESSION['obj_messages'] as $sMessageKey => $aMessageObjectData)
|
||||
{
|
||||
$aObjectMessages = array();
|
||||
$aRanks = array();
|
||||
foreach ($aMessageObjectData as $sMessageId => $aMessageData)
|
||||
{
|
||||
$sMsgClass = 'alert alert-';
|
||||
switch ($aMessageData['severity'])
|
||||
{
|
||||
case 'info':
|
||||
$sMsgClass .= 'info';
|
||||
break;
|
||||
case 'error':
|
||||
$sMsgClass .= 'danger';
|
||||
break;
|
||||
case 'ok':
|
||||
default:
|
||||
$sMsgClass .= 'success';
|
||||
break;
|
||||
}
|
||||
$aObjectMessages[] = array('cssClass' => $sMsgClass, 'message' => $aMessageData['message']);
|
||||
$aRanks[] = $aMessageData['rank'];
|
||||
}
|
||||
unset($_SESSION['obj_messages'][$sMessageKey]);
|
||||
array_multisort($aRanks, $aObjectMessages);
|
||||
foreach ($aObjectMessages as $aObjectMessage)
|
||||
{
|
||||
$aAllMessages[] = $aObjectMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
$oApp['combodo.current_user.session_messages'] = $aAllMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the form data for the $sClass.
|
||||
* Form will look like the "Properties" tab of a $sClass object in the console.
|
||||
|
||||
@@ -313,7 +313,17 @@
|
||||
<div class="container-fluid" id="main-wrapper">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-9 col-md-10 col-sm-offset-3 col-md-offset-2">
|
||||
|
||||
|
||||
{% if app['combodo.current_user.session_messages']|length > 0 %}
|
||||
<section class="row" id="session-messages">
|
||||
<div class="col-xs-12">
|
||||
{% for sessionMessage in app['combodo.current_user.session_messages'] %}
|
||||
<div class="{{ sessionMessage['cssClass'] }}">{{ sessionMessage['message'] }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<section class="row" id="main-header">
|
||||
{% block pMainHeader %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -45,8 +45,8 @@ require_once __DIR__ . '/../src/helpers/lifecyclevalidatorhelper.class.inc.php';
|
||||
require_once __DIR__ . '/../src/helpers/securityhelper.class.inc.php';
|
||||
require_once __DIR__ . '/../src/helpers/applicationhelper.class.inc.php';
|
||||
|
||||
use Silex\Application;
|
||||
use Combodo\iTop\Portal\Helper\ApplicationHelper;
|
||||
use Silex\Application;
|
||||
|
||||
// Stacking context tag so it knows we are in the portal
|
||||
$oContex = new ContextTag('GUI:Portal');
|
||||
@@ -141,6 +141,7 @@ $oApp->before(function(Symfony\Component\HttpFoundation\Request $oRequest, Silex
|
||||
// Loading portal configuration from the module design
|
||||
$oKPI = new ExecutionKPI();
|
||||
ApplicationHelper::LoadPortalConfiguration($oApp);
|
||||
ApplicationHelper::LoadSessionMessages($oApp);
|
||||
$oKPI->ComputeAndReport('Parsing portal configuration');
|
||||
// Loading current user
|
||||
ApplicationHelper::LoadCurrentUser($oApp);
|
||||
|
||||
74
pages/UI.php
74
pages/UI.php
@@ -920,40 +920,40 @@ EOF
|
||||
}
|
||||
else
|
||||
{
|
||||
list($bRes, $aIssues) = $oObj->CheckToWrite(); // called also in DBUpdate()
|
||||
if ($bRes)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
CMDBSource::Query('START TRANSACTION');
|
||||
$oObj->DBUpdate();
|
||||
CMDBSource::Query('COMMIT');
|
||||
$sMessage = Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName());
|
||||
$sSeverity = 'ok';
|
||||
}
|
||||
catch(DeleteException $e)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
// Say two things: 1) Don't be afraid nothing was modified
|
||||
$sMessage = Dict::Format('UI:Class_Object_NotUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName());
|
||||
$sSeverity = 'info';
|
||||
cmdbAbstractObject::SetSessionMessage(get_class($oObj), $oObj->GetKey(), 'UI:Class_Object_NotUpdated', $sMessage, $sSeverity, 0, true /* must not exist */);
|
||||
// 2) Ok, there was some trouble indeed
|
||||
$sMessage = $e->getMessage();
|
||||
$sSeverity = 'error';
|
||||
$bDisplayDetails = true;
|
||||
}
|
||||
utils::RemoveTransaction($sTransactionId);
|
||||
|
||||
CMDBSource::Query('START TRANSACTION');
|
||||
$oObj->DBUpdate();
|
||||
CMDBSource::Query('COMMIT');
|
||||
$sMessage = Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName());
|
||||
$sSeverity = 'ok';
|
||||
}
|
||||
else
|
||||
catch (CoreCannotSaveObjectException $e)
|
||||
{
|
||||
$bDisplayDetails = false;
|
||||
// Found issues, explain and give the user a second chance
|
||||
//
|
||||
$oP->AddHeaderMessageForErrors(Dict::S('UI:Error:SaveFailed'), $aIssues);
|
||||
$oObj->DisplayModifyForm($oP, array('wizard_container' => true)); // wizard_container: display the wizard border and the title
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
$bDisplayDetails = false;
|
||||
$aIssues = $e->getIssues();
|
||||
$oP->AddHeaderMessage($e->getHtmlMessage(), 'message_error');
|
||||
$oObj->DisplayModifyForm($oP,
|
||||
array('wizard_container' => true)); // wizard_container: display the wizard border and the title
|
||||
}
|
||||
catch (DeleteException $e)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
// Say two things:
|
||||
// - 1) Don't be afraid nothing was modified
|
||||
$sMessage = Dict::Format('UI:Class_Object_NotUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName());
|
||||
$sSeverity = 'info';
|
||||
cmdbAbstractObject::SetSessionMessage(get_class($oObj), $oObj->GetKey(), 'UI:Class_Object_NotUpdated', $sMessage,
|
||||
$sSeverity, 0, true /* must not exist */);
|
||||
// - 2) Ok, there was some trouble indeed
|
||||
$sMessage = $e->getMessage();
|
||||
$sSeverity = 'error';
|
||||
$bDisplayDetails = true;
|
||||
}
|
||||
utils::RemoveTransaction($sTransactionId);
|
||||
}
|
||||
}
|
||||
if ($bDisplayDetails)
|
||||
@@ -1101,18 +1101,18 @@ EOF
|
||||
$sClass = get_class($oObj);
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
|
||||
list($bRes, $aIssues) = $oObj->CheckToWrite(); // called also in DBInsertNoReload()
|
||||
if ($bRes)
|
||||
try
|
||||
{
|
||||
$oObj->DBInsertNoReload(); // No need to reload
|
||||
$oObj->DBInsertNoReload();// No need to reload
|
||||
|
||||
utils::RemoveTransaction($sTransactionId);
|
||||
$oP->set_title(Dict::S('UI:PageTitle:ObjectCreated'));
|
||||
|
||||
|
||||
// Compute the name, by reloading the object, even if it disappeared from the silo
|
||||
$oObj = MetaModel::GetObject($sClass, $oObj->GetKey(), true /* Must be found */, true /* Allow All Data*/);
|
||||
$sName = $oObj->GetName();
|
||||
$sName = $oObj->GetName();
|
||||
$sMessage = Dict::Format('UI:Title:Object_Of_Class_Created', $sName, $sClassLabel);
|
||||
|
||||
|
||||
$sNextAction = utils::ReadPostedParam('next_action', '');
|
||||
if (!empty($sNextAction))
|
||||
{
|
||||
@@ -1125,14 +1125,16 @@ EOF
|
||||
ReloadAndDisplay($oP, $oObj, 'create', $sMessage, 'ok');
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (CoreCannotSaveObjectException $e)
|
||||
{
|
||||
// Found issues, explain and give the user a second chance
|
||||
//
|
||||
$aIssues = $e->getIssues();
|
||||
|
||||
$oP->set_title(Dict::Format('UI:CreationPageTitle_Class', $sClassLabel));
|
||||
$oP->add("<h1>".MetaModel::GetClassIcon($sClass)." ".Dict::Format('UI:CreationTitle_Class', $sClassLabel)."</h1>\n");
|
||||
$oP->add("<div class=\"wizContainer\">\n");
|
||||
$oP->AddHeaderMessageForErrors(Dict::S('UI:Error:SaveFailed'), $aIssues);
|
||||
$oP->AddHeaderMessage($e->getHtmlMessage(), 'message_error');
|
||||
cmdbAbstractObject::DisplayCreationForm($oP, $sClass, $oObj);
|
||||
$oP->add("</div>\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user