N°3136 - Add creation and modification of n-n objects in object details (#378)

* Rebase onto develop

* Use exit condition instead of englobing condition

* Add informative modals that can be called from modal toolbox

* Refactor "apply_modify" and "apply_new" into own controller, handle ajax requests with a json response and handle these responses in linkset creation/edition

* Fix merge issues

* Remove inverted condition

* Move linkset create button to a better place, still needs to fix duplicate "New" button caused by a refactor

* Handle "Cancel" button in modals

* Do not display relations when editing an object in a modal

* More elegant way to add "New" button to relations lists

* Factorize vertical highlights in alerts and modal in a single mixin

* Replace button name with dict entry code

* Change route name to snake case

* More elegant way to add "Create in modal" button to relations lists

* Replace triple if with in_array

* Move listener to body

* Rename variable to match boolean rules

* Rename event

* Rename extra param

* Add phpdoc

* Revert changes

* Check indirect linkset rights before allowing creation in modal
This commit is contained in:
Stephen Abello
2023-01-18 13:35:48 +01:00
committed by GitHub
parent cc2881a7b0
commit e1ffa65d8b
21 changed files with 835 additions and 328 deletions

View File

@@ -786,169 +786,8 @@ try
///////////////////////////////////////////////////////////////////////////////////////////
case 'apply_modify': // Applying the modifications to an existing object
$oP->DisableBreadCrumb();
$sClass = utils::ReadPostedParam('class', '', 'class');
$sClassLabel = MetaModel::GetName($sClass);
$id = utils::ReadPostedParam('id', '');
$sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id');
if ( empty($sClass) || empty($id)) // TO DO: check that the class name is valid !
{
IssueLog::Trace('Object not updated (empty class or id)', $sClass, array(
'$operation' => $operation,
'$id' => $id,
'$sTransactionId' => $sTransactionId,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'id'));
}
$bDisplayDetails = true;
$oObj = MetaModel::GetObject($sClass, $id, false);
if ($oObj == null)
{
$bDisplayDetails = false;
$oP->set_title(Dict::S('UI:ErrorPageTitle'));
$oP->P(Dict::S('UI:ObjectDoesNotExist'));
IssueLog::Trace('Object not updated (id not found)', $sClass, array(
'$operation' => $operation,
'$id' => $id,
'$sTransactionId' => $sTransactionId,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
}
elseif (!utils::IsTransactionValid($sTransactionId, false))
{
//TODO: since $bDisplayDetails= true, there will be an redirection, thus, the content generated here is ignored, only the $sMessage and $sSeverity are used afeter the redirection
$sUser = UserRights::GetUser();
IssueLog::Error("UI.php '$operation' : invalid transaction_id ! data: user='$sUser', class='$sClass'");
$oP->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $oObj->GetRawName(), $sClassLabel)); // Set title will take care of the encoding
$oP->p("<strong>".Dict::S('UI:Error:ObjectAlreadyUpdated')."</strong>\n");
$sMessage = Dict::Format('UI:Error:ObjectAlreadyUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName());
$sSeverity = 'error';
IssueLog::Trace('Object not updated (invalid transaction_id)', $sClass, array(
'$operation' => $operation,
'$id' => $id,
'$sTransactionId' => $sTransactionId,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
}
else
{
$aErrors = $oObj->UpdateObjectFromPostedForm();
$sMessage = '';
$sSeverity = 'ok';
if (!$oObj->IsModified() && empty($aErrors))
{
$oP->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $oObj->GetRawName(), $sClassLabel)); // Set title will take care of the encoding
$sMessage = Dict::Format('UI:Class_Object_NotUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName());
$sSeverity = 'info';
IssueLog::Trace('Object not updated (see either $aErrors or IsModified)', $sClass, array(
'$operation' => $operation,
'$id' => $id,
'$sTransactionId' => $sTransactionId,
'$aErrors' => $aErrors,
'IsModified' => $oObj->IsModified(),
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
}
else
{
IssueLog::Trace('Object updated', $sClass, array(
'$operation' => $operation,
'$id' => $id,
'$sTransactionId' => $sTransactionId,
'$aErrors' => $aErrors,
'IsModified' => $oObj->IsModified(),
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
try
{
if (!empty($aErrors))
{
throw new CoreCannotSaveObjectException(array('id' => $oObj->GetKey(), 'class' => $sClass, 'issues' => $aErrors));
}
// Transactions are now handled in DBUpdate
$oObj->DBUpdate();
$sMessage = Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName());
$sSeverity = 'ok';
}
catch (CoreCannotSaveObjectException $e)
{
// Found issues, explain and give the user a second chance
//
$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)
{
// 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';
}
utils::RemoveTransaction($sTransactionId);
}
}
if ($bDisplayDetails)
{
$oObj = MetaModel::GetObject(get_class($oObj), $oObj->GetKey()); //Workaround: reload the object so that the linkedset are displayed properly
$sNextAction = utils::ReadPostedParam('next_action', '');
if (!empty($sNextAction))
{
try
{
ApplyNextAction($oP, $oObj, $sNextAction);
}
catch (ApplicationException $e)
{
$sMessage = $e->getMessage();
$sSeverity = 'info';
ReloadAndDisplay($oP, $oObj, 'update', $sMessage, $sSeverity);
}
}
else
{
// Nothing more to do
$sMessage = isset($sMessage) ? $sMessage : '';
$sSeverity = isset($sSeverity) ? $sSeverity : null;
ReloadAndDisplay($oP, $oObj, 'update', $sMessage, $sSeverity);
}
$bLockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
if ($bLockEnabled)
{
// Release the concurrent lock, if any
$sOwnershipToken = utils::ReadPostedParam('ownership_token', null, 'raw_data');
if ($sOwnershipToken !== null)
{
// We're done, let's release the lock
iTopOwnershipLock::ReleaseLock(get_class($oObj), $oObj->GetKey(), $sOwnershipToken);
}
}
}
$oController = new ObjectController();
$oP = $oController->OperationApplyModify();
break;
///////////////////////////////////////////////////////////////////////////////////////////
@@ -1047,137 +886,10 @@ try
///////////////////////////////////////////////////////////////////////////////////////////
case 'apply_new': // Creation of a new object
$oP->DisableBreadCrumb();
$sClass = utils::ReadPostedParam('class', '', 'class');
$sClassLabel = MetaModel::GetName($sClass);
$sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id');
$aErrors = array();
$aWarnings = array();
if ( empty($sClass) ) // TO DO: check that the class name is valid !
{
IssueLog::Trace('Object not created (empty class)', $sClass, array(
'$operation' => $operation,
'$sTransactionId' => $sTransactionId,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'class'));
}
if (!utils::IsTransactionValid($sTransactionId, false))
{
$sUser = UserRights::GetUser();
IssueLog::Error("UI.php '$operation' : invalid transaction_id ! data: user='$sUser', class='$sClass'");
$oP->p("<strong>".Dict::S('UI:Error:ObjectAlreadyCreated')."</strong>\n");
IssueLog::Trace('Object not created (invalid transaction_id)', $sClass, array(
'$operation' => $operation,
'$sTransactionId' => $sTransactionId,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
}
else
{
/** @var \cmdbAbstractObject $oObj */
$oObj = MetaModel::NewObject($sClass);
if (MetaModel::HasLifecycle($sClass))
{
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
$sTargetState = utils::ReadPostedParam('obj_state', '');
if ($sTargetState != '')
{
$sOrigState = utils::ReadPostedParam('obj_state_orig', '');
if ($sTargetState != $sOrigState)
{
$aWarnings[] = Dict::S('UI:StateChanged');
}
$oObj->Set($sStateAttCode, $sTargetState);
}
}
$aErrors = $oObj->UpdateObjectFromPostedForm();
}
if (isset($oObj) && is_object($oObj))
{
$sClass = get_class($oObj);
$sClassLabel = MetaModel::GetName($sClass);
try
{
if (!empty($aErrors) || !empty($aWarnings))
{
IssueLog::Trace('Object not created (see $aErrors)', $sClass, array(
'$operation' => $operation,
'$sTransactionId' => $sTransactionId,
'$aErrors' => $aErrors,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
throw new CoreCannotSaveObjectException(array('id' => $oObj->GetKey(), 'class' => $sClass, 'issues' => $aErrors));
}
$oObj->DBInsertNoReload();// No need to reload
IssueLog::Trace('Object created', $sClass, array(
'$operation' => $operation,
'$id' => $oObj->GetKey(),
'$sTransactionId' => $sTransactionId,
'$aErrors' => $aErrors,
'$sUser' => UserRights::GetUser(),
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
));
utils::RemoveTransaction($sTransactionId);
$oP->set_title(Dict::S('UI:PageTitle:ObjectCreated'));
QuickCreateHelper::AddClassToHistory($sClass);
// 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();
$sMessage = Dict::Format('UI:Title:Object_Of_Class_Created', $sName, $sClassLabel);
$sNextAction = utils::ReadPostedParam('next_action', '');
if (!empty($sNextAction)) {
$oP->add("<h1>$sMessage</h1>");
try {
ApplyNextAction($oP, $oObj, $sNextAction);
}
catch (ApplicationException $e) {
$sMessage = $e->getMessage();
$sSeverity = 'info';
ReloadAndDisplay($oP, $oObj, 'create', $sMessage, $sSeverity);
}
} else {
// Nothing more to do
ReloadAndDisplay($oP, $oObj, 'create', $sMessage, 'ok');
}
}
catch (CoreCannotSaveObjectException $e) {
// Found issues, explain and give the user a second chance
//
$aIssues = $e->getIssues();
$sObjKey = $oObj->GetKey();
$sClassIcon = MetaModel::GetClassIcon($sClass, false);
$sHeaderTitle = Dict::Format('UI:CreationTitle_Class', $sClassLabel);
$oP->set_title(Dict::Format('UI:CreationPageTitle_Class', $sClassLabel));
if (!empty($aIssues)) {
$oP->AddHeaderMessage($e->getHtmlMessage(), 'message_error');
}
if (!empty($aWarnings)) {
$sWarnings = implode(', ', $aWarnings);
$oP->AddHeaderMessage($sWarnings, 'message_warning');
}
cmdbAbstractObject::DisplayCreationForm($oP, $sClass, $oObj, [], ['transaction_id' => $sTransactionId]);
}
}
break;
$oController = new ObjectController();
$oP = $oController->OperationApplyNew();
break;
///////////////////////////////////////////////////////////////////////////////////////////