mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 18:48:51 +02:00
N°744 Portal: Prevented LinkedSet corruption through simultaneous updates. In the portal are now update incremantally like in the console. This needs to be tested with both 1:n and n:n LinkedSet
SVN:trunk[4857]
This commit is contained in:
@@ -1014,6 +1014,7 @@ class ObjectFormManager extends FormManager
|
||||
{
|
||||
if (MetaModel::IsValidAttCode($sObjectClass, $sAttCode))
|
||||
{
|
||||
/** @var \AttributeDefinition $oAttDef */
|
||||
$oAttDef = MetaModel::GetAttributeDef($sObjectClass, $sAttCode);
|
||||
if ($oAttDef->IsLinkSet())
|
||||
{
|
||||
@@ -1023,59 +1024,50 @@ class ObjectFormManager extends FormManager
|
||||
// Which was an issue when deleting all objects from linkedset
|
||||
$value = json_decode($value, true);
|
||||
|
||||
// Creating set from objects of the form
|
||||
$sTargetClass = $oAttDef->GetLinkedClass();
|
||||
$oValueSet = DBObjectSet::FromScratch($sTargetClass);
|
||||
foreach ($value as $aValue)
|
||||
{
|
||||
$iTargetId = (int) $aValue['id'];
|
||||
// LinkedSet
|
||||
if (!$oAttDef->IsIndirect())
|
||||
{
|
||||
// Note : AllowAllData set to true here instead of checking scope's flag because we are displaying a value that has been set and validated
|
||||
$oLinkedObject = MetaModel::GetObject($sTargetClass, abs($iTargetId), true, true);
|
||||
$oValueSet->AddObject($oLinkedObject);
|
||||
}
|
||||
// LinkedSetIndirect
|
||||
else
|
||||
{
|
||||
// New relation
|
||||
if ($iTargetId < 0)
|
||||
{
|
||||
$oLink = MetaModel::NewObject($sTargetClass);
|
||||
$oLink->Set($oAttDef->GetExtKeyToRemote(), -1 * $iTargetId);
|
||||
$oLink->Set($oAttDef->GetExtKeyToMe(), $this->oObject->GetKey());
|
||||
}
|
||||
// Existing relation
|
||||
else
|
||||
{
|
||||
// Note : AllowAllData set to true here instead of checking scope's flag because we are displaying a value that has been set and validated
|
||||
$oLink = MetaModel::GetObject($sTargetClass, $iTargetId, true, true);
|
||||
}
|
||||
$oValueSet->AddObject($oLink);
|
||||
}
|
||||
}
|
||||
// Comparing set from db to set from form if linkedset is DIRECT in order to identify removed objects
|
||||
if (!$oAttDef->IsIndirect())
|
||||
{
|
||||
// Retrieving remote object's extkey definition in order to nullify it or completely remove the object regarding its mandatory status
|
||||
$oExtKeyToMeAttDef = MetaModel::GetAttributeDef($sTargetClass, $oAttDef->GetExtKeyToMe());
|
||||
if ($oExtKeyToMeAttDef->IsNullAllowed())
|
||||
{
|
||||
// Comparing sets
|
||||
$oDBSet = $this->oObject->Get($sAttCode);
|
||||
$oDBSetComparator = new DBObjectSetComparator($oDBSet, $oValueSet);
|
||||
$aDBSetDifferences = $oDBSetComparator->GetDifferences();
|
||||
// Nullifying remote object's ext key
|
||||
foreach ($aDBSetDifferences['removed'] as $oRemovedLinkedObject)
|
||||
{
|
||||
$oRemovedLinkedObject->Set($oExtKeyToMeAttDef->GetCode(), $oExtKeyToMeAttDef->GetNullValue());
|
||||
$oValueSet->AddObject($oRemovedLinkedObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
/** @var \ormLinkSet $oLinkSet */
|
||||
$oLinkSet = $this->oObject->Get($sAttCode);
|
||||
$sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
|
||||
// Checking links to remove
|
||||
if(isset($value['remove']))
|
||||
{
|
||||
foreach($value['remove'] as $iObjKey => $aObjData)
|
||||
{
|
||||
$oLinkSet->RemoveItem($iObjKey);
|
||||
}
|
||||
}
|
||||
|
||||
// Checking links to add
|
||||
if(isset($value['add']))
|
||||
{
|
||||
foreach($value['add'] as $iObjKey => $aObjdata)
|
||||
{
|
||||
// Creating link when linkset is indirect...
|
||||
if($oAttDef->IsIndirect())
|
||||
{
|
||||
$oLink = MetaModel::NewObject($sLinkedClass);
|
||||
$oLink->Set($oAttDef->GetExtKeyToRemote(), $iObjKey);
|
||||
$oLink->Set($oAttDef->GetExtKeyToMe(), $this->oObject->GetKey());
|
||||
}
|
||||
// ... or adding remote object when linkset id direct
|
||||
else
|
||||
{
|
||||
// Note : AllowAllData set to true here instead of checking scope's flag because we are displaying a value that has been set and validated
|
||||
$oLink = MetaModel::GetObject($sLinkedClass, $iObjKey, false, true);
|
||||
}
|
||||
|
||||
if($oLink !== null)
|
||||
{
|
||||
$oLinkSet->AddItem($oLink);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checking links to modify
|
||||
// TODO: Not implemented yet as we can't change lnk properties in the portal
|
||||
|
||||
// Setting value in the object
|
||||
$this->oObject->Set($sAttCode, $oValueSet);
|
||||
$this->oObject->Set($sAttCode, $oLinkSet);
|
||||
}
|
||||
else if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
|
||||
{
|
||||
|
||||
@@ -57,9 +57,9 @@ class BsLinkedSetFieldRenderer extends FieldRenderer
|
||||
$aItemIds = array();
|
||||
$this->PrepareItems($aItems, $aItemIds);
|
||||
$sItemsAsJson = json_encode($aItems);
|
||||
$sItemIdsAsJson = htmlentities(json_encode($aItemIds), ENT_QUOTES, 'UTF-8');
|
||||
|
||||
if (!$this->oField->GetHidden())
|
||||
$sItemIdsAsJson = htmlentities(json_encode(array('current' => $aItemIds)), ENT_QUOTES, 'UTF-8');
|
||||
|
||||
if (!$this->oField->GetHidden())
|
||||
{
|
||||
// Rendering field
|
||||
$sIsEditable = ($this->oField->GetReadOnly()) ? 'false' : 'true';
|
||||
@@ -331,10 +331,11 @@ EOF
|
||||
// Updating datatables
|
||||
if(oData.items !== undefined)
|
||||
{
|
||||
for(var i in oData.items)
|
||||
for(var i in oData.items)
|
||||
{
|
||||
// Adding target item id information
|
||||
oData.items[i].target_id = oData.items[i].id;
|
||||
|
||||
// Adding item to table only if it's not already there
|
||||
if($('#{$sTableId} tr[role="row"] > td input[data-target-object-id="' + oData.items[i].target_id + '"], #{$sTableId} tr[role="row"] > td input[data-target-object-id="' + (oData.items[i].target_id*-1) + '"]').length === 0)
|
||||
{
|
||||
@@ -342,22 +343,17 @@ EOF
|
||||
oData.items[i].id = -1 * parseInt(oData.items[i].id);
|
||||
oTable_{$this->oField->GetGlobalId()}.row.add(oData.items[i]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
oTable_{$this->oField->GetGlobalId()}.draw();
|
||||
|
||||
// Updating input
|
||||
updateInputValue_{$this->oField->GetGlobalId()}();
|
||||
}
|
||||
}
|
||||
)
|
||||
.done(function(oData){
|
||||
// Updating hidden field
|
||||
var aData = oTable_{$this->oField->GetGlobalId()}.rows().data().toArray();
|
||||
var aObjectIds = [];
|
||||
|
||||
for(var i in aData)
|
||||
{
|
||||
aObjectIds.push({id: aData[i].id});
|
||||
}
|
||||
|
||||
$('#{$this->oField->GetGlobalId()}').val(JSON.stringify(aObjectIds));
|
||||
// Updating items count
|
||||
updateItemCount_{$this->oField->GetGlobalId()}();
|
||||
// Updating global checkbox
|
||||
@@ -371,16 +367,8 @@ EOF
|
||||
// We come from a button
|
||||
else
|
||||
{
|
||||
// Updating hidden field
|
||||
var aData = oTable_{$this->oField->GetGlobalId()}.rows().data().toArray();
|
||||
var aObjectIds = [];
|
||||
|
||||
for(var i in aData)
|
||||
{
|
||||
aObjectIds.push({id: aData[i].id});
|
||||
}
|
||||
|
||||
$('#{$this->oField->GetGlobalId()}').val(JSON.stringify(aObjectIds));
|
||||
// Updating input
|
||||
updateInputValue_{$this->oField->GetGlobalId()}();
|
||||
// Updating items count
|
||||
updateItemCount_{$this->oField->GetGlobalId()}();
|
||||
// Updating global checkbox
|
||||
@@ -429,6 +417,38 @@ EOF
|
||||
{
|
||||
$('#{$sCollapseTogglerId} > .text').text( oTable_{$this->oField->GetGlobalId()}.rows().count() );
|
||||
};
|
||||
// - Field input handler
|
||||
var updateInputValue_{$this->oField->GetGlobalId()} = function()
|
||||
{
|
||||
// Retrieving table rows
|
||||
var aData = oTable_{$this->oField->GetGlobalId()}.rows().data().toArray();
|
||||
|
||||
// Retrieving input values
|
||||
var oValues = JSON.parse($('#{$this->oField->GetGlobalId()}').val());
|
||||
oValues.add = {};
|
||||
oValues.remove = {};
|
||||
|
||||
// Checking removed objects
|
||||
for(var i in oValues.current)
|
||||
{
|
||||
if($('#{$sTableId} tr[role="row"] input[data-object-id="'+i+'"]').length === 0)
|
||||
{
|
||||
oValues.remove[i] = {};
|
||||
}
|
||||
}
|
||||
|
||||
// Checking added objects
|
||||
for(var i in aData)
|
||||
{
|
||||
if(oValues.current[aData[i].id] === undefined)
|
||||
{
|
||||
oValues.add[aData[i].target_id] = {};
|
||||
}
|
||||
}
|
||||
|
||||
// Setting input values
|
||||
$('#{$this->oField->GetGlobalId()}').val(JSON.stringify(oValues));
|
||||
};
|
||||
|
||||
// Handles items remove/add
|
||||
$('#{$sButtonRemoveId}').off('click').on('click', function(){
|
||||
@@ -555,7 +575,7 @@ EOF
|
||||
}
|
||||
|
||||
$aItems[] = $aItemProperties;
|
||||
$aItemIds[] = array('id' => $aItemProperties['id']);
|
||||
$aItemIds[$aItemProperties['id']] = array();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user