Compare commits

...

3 Commits

5 changed files with 138 additions and 136 deletions

View File

@@ -677,33 +677,26 @@ HTML
$sTargetClass = $oLinkingAttDef->GetTargetClass(); $sTargetClass = $oLinkingAttDef->GetTargetClass();
// n:n links => must be allowed to modify the linking class AND read the target class in order to edit the linkedset // n:n links => must be allowed to modify the linking class AND read the target class in order to edit the linkedset
if (!UserRights::IsActionAllowed($sLinkedClass, if (!UserRights::IsActionAllowed($sLinkedClass,
UR_ACTION_MODIFY) || !UserRights::IsActionAllowed($sTargetClass, UR_ACTION_READ)) UR_ACTION_MODIFY) || !UserRights::IsActionAllowed($sTargetClass, UR_ACTION_READ)) {
{
$iFlags |= OPT_ATT_READONLY; $iFlags |= OPT_ATT_READONLY;
} }
// n:n links => must be allowed to read the linking class AND the target class in order to display the linkedset // n:n links => must be allowed to read the linking class AND the target class in order to display the linkedset
if (!UserRights::IsActionAllowed($sLinkedClass, if (!UserRights::IsActionAllowed($sLinkedClass,
UR_ACTION_READ) || !UserRights::IsActionAllowed($sTargetClass, UR_ACTION_READ)) UR_ACTION_READ) || !UserRights::IsActionAllowed($sTargetClass, UR_ACTION_READ)) {
{
$iFlags |= OPT_ATT_HIDDEN; $iFlags |= OPT_ATT_HIDDEN;
} }
} } else {
else
{
// 1:n links => must be allowed to modify the linked class in order to edit the linkedset // 1:n links => must be allowed to modify the linked class in order to edit the linkedset
if (!UserRights::IsActionAllowed($sLinkedClass, UR_ACTION_MODIFY)) if (!UserRights::IsActionAllowed($sLinkedClass, UR_ACTION_MODIFY)) {
{
$iFlags |= OPT_ATT_READONLY; $iFlags |= OPT_ATT_READONLY;
} }
// 1:n links => must be allowed to read the linked class in order to display the linkedset // 1:n links => must be allowed to read the linked class in order to display the linkedset
if (!UserRights::IsActionAllowed($sLinkedClass, UR_ACTION_READ)) if (!UserRights::IsActionAllowed($sLinkedClass, UR_ACTION_READ)) {
{
$iFlags |= OPT_ATT_HIDDEN; $iFlags |= OPT_ATT_HIDDEN;
} }
} }
// Non-readable/hidden linkedset... don't display anything // Non-readable/hidden linkedset... don't display anything
if ($iFlags & OPT_ATT_HIDDEN) if ($iFlags & OPT_ATT_HIDDEN) {
{
continue; continue;
} }
@@ -712,17 +705,13 @@ HTML
$aArgs = array('this' => $this); $aArgs = array('this' => $this);
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE)); $bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE));
if ($bEditMode && (!$bReadOnly)) if ($bEditMode && (!$bReadOnly)) {
{
$sInputId = $this->m_iFormId.'_'.$sAttCode; $sInputId = $this->m_iFormId.'_'.$sAttCode;
if ($oAttDef->IsIndirect()) if ($oAttDef->IsIndirect()) {
{
$oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote()); $oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote());
$sTargetClass = $oLinkingAttDef->GetTargetClass(); $sTargetClass = $oLinkingAttDef->GetTargetClass();
} } else {
else
{
$sTargetClass = $sLinkedClass; $sTargetClass = $sLinkedClass;
} }
@@ -732,7 +721,7 @@ HTML
$sDisplayValue = ''; // not used $sDisplayValue = ''; // not used
$sHTMLValue = "<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode, $sHTMLValue = "<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode,
$oAttDef, $oLinkSet, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>'; $oAttDef, $oOrmLinkSet, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>';
$this->AddToFieldsMap($sAttCode, $sInputId); $this->AddToFieldsMap($sAttCode, $sInputId);
$oPage->add($sHTMLValue); $oPage->add($sHTMLValue);
} }
@@ -2377,8 +2366,7 @@ EOF
case 'LinkedSet': case 'LinkedSet':
$sInputType = self::ENUM_INPUT_TYPE_LINKEDSET; $sInputType = self::ENUM_INPUT_TYPE_LINKEDSET;
if ($oAttDef->IsIndirect()) { if ($oAttDef->IsIndirect()) {
$oWidget = new UILinksWidget($sClass, $sAttCode, $iId, $sNameSuffix, $oWidget = new UILinksWidget($sClass, $sAttCode, $iId, $sNameSuffix, $oAttDef->DuplicatesAllowed());
$oAttDef->DuplicatesAllowed());
} else { } else {
$oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iId, $sNameSuffix); $oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iId, $sNameSuffix);
} }
@@ -4025,18 +4013,14 @@ HTML;
$this->Set($sAttCode, $value); $this->Set($sAttCode, $value);
break; break;
case 'LinkedSet': case 'LinkedSet':
if ($this->IsValueModified($value)) if ($this->IsValueModified($value)) {
{
$oLinkSet = $this->Get($sAttCode); $oLinkSet = $this->Get($sAttCode);
$sLinkedClass = $oAttDef->GetLinkedClass(); $sLinkedClass = $oAttDef->GetLinkedClass();
if (array_key_exists('to_be_created', $value) && (count($value['to_be_created']) > 0)) if (array_key_exists('to_be_created', $value) && (count($value['to_be_created']) > 0)) {
{
// Now handle the links to be created // Now handle the links to be created
foreach ($value['to_be_created'] as $aData) foreach ($value['to_be_created'] as $aData) {
{
$sSubClass = $aData['class']; $sSubClass = $aData['class'];
if (($sLinkedClass == $sSubClass) || (is_subclass_of($sSubClass, $sLinkedClass))) if (($sLinkedClass == $sSubClass) || (is_subclass_of($sSubClass, $sLinkedClass))) {
{
$aObjData = $aData['data']; $aObjData = $aData['data'];
$oLink = MetaModel::NewObject($sSubClass); $oLink = MetaModel::NewObject($sSubClass);
$oLink->UpdateObjectFromArray($aObjData); $oLink->UpdateObjectFromArray($aObjData);
@@ -4248,28 +4232,20 @@ HTML;
case 'LinkedSet': case 'LinkedSet':
/** @var AttributeLinkedSet $oAttDef */ /** @var AttributeLinkedSet $oAttDef */
$aRawToBeCreated = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbc", '{}', $aRawToBeCreated = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbc", '{}', 'raw_data'), true);
'raw_data'), true);
$aToBeCreated = array(); $aToBeCreated = array();
foreach($aRawToBeCreated as $aData) foreach ($aRawToBeCreated as $aData) {
{
$sSubFormPrefix = $aData['formPrefix']; $sSubFormPrefix = $aData['formPrefix'];
$sObjClass = isset($aData['class']) ? $aData['class'] : $oAttDef->GetLinkedClass(); $sObjClass = isset($aData['class']) ? $aData['class'] : $oAttDef->GetLinkedClass();
$aObjData = array(); $aObjData = array();
foreach($aData as $sKey => $value) foreach ($aData as $sKey => $value) {
{ if (preg_match("/^attr_$sSubFormPrefix(.*)$/", $sKey, $aMatches)) {
if (preg_match("/^attr_$sSubFormPrefix(.*)$/", $sKey, $aMatches))
{
$oLinkAttDef = MetaModel::GetAttributeDef($sObjClass, $aMatches[1]); $oLinkAttDef = MetaModel::GetAttributeDef($sObjClass, $aMatches[1]);
// Recursing over n:n link datetime attributes // Recursing over n:n link datetime attributes
// Note: We might need to do it with other attribute types, like Document or redundancy setting. // Note: We might need to do it with other attribute types, like Document or redundancy setting.
if ($oLinkAttDef instanceof AttributeDateTime) if ($oLinkAttDef instanceof AttributeDateTime) {
{ $aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix, $aMatches[1], $sObjClass, $aData);
$aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix, } else {
$aMatches[1], $sObjClass, $aData);
}
else
{
$aObjData[$aMatches[1]] = $value; $aObjData[$aMatches[1]] = $value;
} }
} }
@@ -4277,28 +4253,20 @@ HTML;
$aToBeCreated[] = array('class' => $sObjClass, 'data' => $aObjData); $aToBeCreated[] = array('class' => $sObjClass, 'data' => $aObjData);
} }
$aRawToBeModified = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbm", '{}', $aRawToBeModified = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbm", '{}', 'raw_data'), true);
'raw_data'), true);
$aToBeModified = array(); $aToBeModified = array();
foreach($aRawToBeModified as $iObjKey => $aData) foreach ($aRawToBeModified as $iObjKey => $aData) {
{
$sSubFormPrefix = $aData['formPrefix']; $sSubFormPrefix = $aData['formPrefix'];
$sObjClass = isset($aData['class']) ? $aData['class'] : $oAttDef->GetLinkedClass(); $sObjClass = isset($aData['class']) ? $aData['class'] : $oAttDef->GetLinkedClass();
$aObjData = array(); $aObjData = array();
foreach($aData as $sKey => $value) foreach ($aData as $sKey => $value) {
{ if (preg_match("/^attr_$sSubFormPrefix(.*)$/", $sKey, $aMatches)) {
if (preg_match("/^attr_$sSubFormPrefix(.*)$/", $sKey, $aMatches))
{
$oLinkAttDef = MetaModel::GetAttributeDef($sObjClass, $aMatches[1]); $oLinkAttDef = MetaModel::GetAttributeDef($sObjClass, $aMatches[1]);
// Recursing over n:n link datetime attributes // Recursing over n:n link datetime attributes
// Note: We might need to do it with other attribute types, like Document or redundancy setting. // Note: We might need to do it with other attribute types, like Document or redundancy setting.
if ($oLinkAttDef instanceof AttributeDateTime) if ($oLinkAttDef instanceof AttributeDateTime) {
{ $aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix, $aMatches[1], $sObjClass, $aData);
$aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix, } else {
$aMatches[1], $sObjClass, $aData);
}
else
{
$aObjData[$aMatches[1]] = $value; $aObjData[$aMatches[1]] = $value;
} }
} }
@@ -4307,14 +4275,11 @@ HTML;
} }
$value = array( $value = array(
'to_be_created' => $aToBeCreated, 'to_be_created' => $aToBeCreated,
'to_be_modified' => $aToBeModified, 'to_be_modified' => $aToBeModified,
'to_be_deleted' => json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbd", '[]', 'to_be_deleted' => json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbd", '[]', 'raw_data'), true),
'raw_data'), true), 'to_be_added' => json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tba", '[]', 'raw_data'), true),
'to_be_added' => json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tba", '[]', 'to_be_removed' => json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbr", '[]', 'raw_data'), true),
'raw_data'), true),
'to_be_removed' => json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbr", '[]',
'raw_data'), true),
); );
break; break;

View File

@@ -108,15 +108,14 @@ class UILinksWidget
* @throws \CoreUnexpectedValue * @throws \CoreUnexpectedValue
* @throws \Exception * @throws \Exception
*/ */
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false) protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false, $bModified = false)
{ {
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}"; $sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
$aRow = array(); $aRow = array();
$aFieldsMap = array(); $aFieldsMap = array();
$iKey = 0; $iKey = 0;
if (is_object($linkObjOrId) && (!$linkObjOrId->IsNew())) if (is_object($linkObjOrId) && (!$linkObjOrId->IsNew())) {
{
$iKey = $linkObjOrId->GetKey(); $iKey = $linkObjOrId->GetKey();
$iRemoteObjKey = $linkObjOrId->Get($this->m_sExtKeyToRemote); $iRemoteObjKey = $linkObjOrId->Get($this->m_sExtKeyToRemote);
$sPrefix .= "[$iKey]["; $sPrefix .= "[$iKey][";
@@ -125,49 +124,44 @@ class UILinksWidget
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$iKey}"; $aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$iKey}";
$aArgs['this'] = $linkObjOrId; $aArgs['this'] = $linkObjOrId;
if ($bReadOnly) if ($bReadOnly) {
{
$aRow['form::checkbox'] = ""; $aRow['form::checkbox'] = "";
foreach ($this->m_aEditableFields as $sFieldCode) foreach ($this->m_aEditableFields as $sFieldCode) {
{
$sDisplayValue = $linkObjOrId->GetEditValue($sFieldCode); $sDisplayValue = $linkObjOrId->GetEditValue($sFieldCode);
$aRow[$sFieldCode] = $sDisplayValue; $aRow[$sFieldCode] = $sDisplayValue;
} }
} } else {
else
{
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$iKey\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$iKey\">"; $aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$iKey\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$iKey\">";
foreach ($this->m_aEditableFields as $sFieldCode) foreach ($this->m_aEditableFields as $sFieldCode) {
{
$sSafeFieldId = $this->GetFieldId($linkObjOrId->GetKey(), $sFieldCode); $sSafeFieldId = $this->GetFieldId($linkObjOrId->GetKey(), $sFieldCode);
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $linkObjOrId, $oP, $sNameSuffix, $sSafeFieldId); $this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $linkObjOrId, $oP, $sNameSuffix, $sSafeFieldId);
$aFieldsMap[$sFieldCode] = $sSafeFieldId; $aFieldsMap[$sFieldCode] = $sSafeFieldId;
if ($bModified) {
$oP->add_ready_script(
<<<EOF
oWidget{$this->m_iInputId}.AddModified($iUniqueId, {$this->m_iInputId}, $sFieldCode, {$linkObjOrId->Get($sFieldCode)});
EOF
);
}
} }
} }
$sState = $linkObjOrId->GetState(); $sState = $linkObjOrId->GetState();
$sRemoteKeySafeFieldId = $this->GetFieldId($aArgs['this']->GetKey(), $this->m_sExtKeyToRemote);; $sRemoteKeySafeFieldId = $this->GetFieldId($aArgs['this']->GetKey(), $this->m_sExtKeyToRemote);;
} } else {
else
{
// form for creating a new record // form for creating a new record
if (is_object($linkObjOrId)) if (is_object($linkObjOrId)) {
{
// New link existing only in memory // New link existing only in memory
$oNewLinkObj = $linkObjOrId; $oNewLinkObj = $linkObjOrId;
$iRemoteObjKey = $oNewLinkObj->Get($this->m_sExtKeyToRemote); $iRemoteObjKey = $oNewLinkObj->Get($this->m_sExtKeyToRemote);
$oNewLinkObj->Set($this->m_sExtKeyToMe, $oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object also fills the related external fields
$oCurrentObj); // Setting the extkey with the object also fills the related external fields } else {
}
else
{
$iRemoteObjKey = $linkObjOrId; $iRemoteObjKey = $linkObjOrId;
$oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass); $oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass);
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, $iRemoteObjKey); $oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, $iRemoteObjKey);
$oNewLinkObj->Set($this->m_sExtKeyToRemote, $oNewLinkObj->Set($this->m_sExtKeyToRemote, $oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields
$oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields $oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object also fills the related external fields
$oNewLinkObj->Set($this->m_sExtKeyToMe,
$oCurrentObj); // Setting the extkey with the object also fills the related external fields
} }
$sPrefix .= "[-$iUniqueId]["; $sPrefix .= "[-$iUniqueId][";
$sNameSuffix = "]"; // To make a tabular form $sNameSuffix = "]"; // To make a tabular form
@@ -177,8 +171,7 @@ class UILinksWidget
$sInputValue = $iUniqueId > 0 ? "-$iUniqueId" : "$iUniqueId"; $sInputValue = $iUniqueId > 0 ? "-$iUniqueId" : "$iUniqueId";
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"0\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$sInputValue\">"; $aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"0\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$sInputValue\">";
if ($iUniqueId > 0) if ($iUniqueId > 0) {
{
// Rows created with ajax call need OnLinkAdded call. // Rows created with ajax call need OnLinkAdded call.
// //
$oP->add_ready_script( $oP->add_ready_script(
@@ -187,9 +180,7 @@ PrepareWidgets();
oWidget{$this->m_iInputId}.OnLinkAdded($iUniqueId, $iRemoteObjKey); oWidget{$this->m_iInputId}.OnLinkAdded($iUniqueId, $iRemoteObjKey);
EOF EOF
); );
} } else {
else
{
// Rows added before loading the form don't have to call OnLinkAdded. // Rows added before loading the form don't have to call OnLinkAdded.
// Listeners are already present and DOM is not recreated // Listeners are already present and DOM is not recreated
$iPositiveUniqueId = -$iUniqueId; $iPositiveUniqueId = -$iUniqueId;
@@ -378,10 +369,17 @@ JS
$iMaxAddedId = 0; $iMaxAddedId = 0;
$iAddedId = -1; // Unique id for new links $iAddedId = -1; // Unique id for new links
$oBlock->aRemoved = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->m_sAttCode}_tbd", '[]', 'raw_data')); $oBlock->aRemoved = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->m_sAttCode}_tbd", '[]', 'raw_data'));
$oModified = $oValue->GetModified($this->m_sExtKeyToRemote);
while ($oCurrentLink = $oValue->Fetch()) { while ($oCurrentLink = $oValue->Fetch()) {
// We try to retrieve the remote object as usual // We try to retrieve the remote object as usual
if (!in_array($oCurrentLink->GetKey(), $oBlock->aRemoved)) { if (!in_array($oCurrentLink->GetKey(), $oBlock->aRemoved)) {
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */); $bModified = false;
if (array_key_exists($oCurrentLink->GetKey(), $oModified)) {
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oModified[$oCurrentLink->GetKey()], false /* Must not be found */);
$bModified = true;
} else {
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */);
}
// If successful, it means that we can edit its link // If successful, it means that we can edit its link
if ($oLinkedObj !== null) { if ($oLinkedObj !== null) {
$bReadOnly = false; $bReadOnly = false;
@@ -398,11 +396,12 @@ JS
} }
$iMaxAddedId = max($iMaxAddedId, $key); $iMaxAddedId = max($iMaxAddedId, $key);
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly); $aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly, $bModified);
} }
} }
$oBlock->iMaxAddedId = (int)$iMaxAddedId; $oBlock->iMaxAddedId = (int)$iMaxAddedId;
$oDataTable = DataTableUIBlockFactory::MakeForForm("{$this->m_sAttCode}{$this->m_sNameSuffix}", $this->m_aTableConfig, $aForm); $oDataTable = DataTableUIBlockFactory::MakeForForm("{$this->m_sAttCode}{$this->m_sNameSuffix}", $this->m_aTableConfig, $aForm);
$oDataTable->SetOptions(['select_mode' => 'custom', 'disable_hyperlinks' => true]); $oDataTable->SetOptions(['select_mode' => 'custom', 'disable_hyperlinks' => true]);
$oBlock->AddSubBlock($oDataTable); $oBlock->AddSubBlock($oDataTable);

View File

@@ -2095,15 +2095,13 @@ abstract class DBObject implements iDisplay
if ($bHasDuplicates) if ($bHasDuplicates)
{ {
$bIsBlockingRule = $aUniquenessRuleProperties['is_blocking']; $bIsBlockingRule = $aUniquenessRuleProperties['is_blocking'];
if (is_null($bIsBlockingRule)) if (is_null($bIsBlockingRule)) {
{
$bIsBlockingRule = true; $bIsBlockingRule = true;
} }
$sErrorMessage = $this->GetUniquenessRuleMessage($sUniquenessRuleId); $sErrorMessage = $this->GetUniquenessRuleMessage($sUniquenessRuleId, $this);
if ($bIsBlockingRule) if ($bIsBlockingRule) {
{
$this->m_aCheckIssues[] = $sErrorMessage; $this->m_aCheckIssues[] = $sErrorMessage;
continue; continue;
} }
@@ -2114,32 +2112,32 @@ abstract class DBObject implements iDisplay
} }
/** /**
* *
* @internal * @internal
* *
* @param string $sUniquenessRuleId * @param string $sUniquenessRuleId
* @param DBObject $oObj
* *
* @return string dict key : Class:$sClassName/UniquenessRule:$sUniquenessRuleId if none then will use Core:UniquenessDefaultError * @return string dict key : Class:$sClassName/UniquenessRule:$sUniquenessRuleId if none then will use Core:UniquenessDefaultError
* Dictionary keys can contain "$this" placeholders * Dictionary keys can contain "$this" placeholders
* *
* @since 2.6.0 N°659 uniqueness constraint * @since 2.6.0 N°659 uniqueness constraint
*/ */
protected function GetUniquenessRuleMessage($sUniquenessRuleId) protected function GetUniquenessRuleMessage($sUniquenessRuleId, $oObj)
{ {
$sCurrentClass = get_class($this); $sCurrentClass = get_class($oObj);
$sClass = MetaModel::GetRootClassForUniquenessRule($sUniquenessRuleId, $sCurrentClass); $sClass = MetaModel::GetRootClassForUniquenessRule($sUniquenessRuleId, $sCurrentClass);
$sMessageKey = "Class:$sClass/UniquenessRule:$sUniquenessRuleId"; $sMessageKey = "Class:$sClass/UniquenessRule:$sUniquenessRuleId";
$sTemplate = Dict::S($sMessageKey, ''); $sTemplate = Dict::S($sMessageKey, '');
if (empty($sTemplate)) if (empty($sTemplate)) {
{
// we could add also a specific message if user is admin ("dict key is missing") // we could add also a specific message if user is admin ("dict key is missing")
return Dict::Format('Core:UniquenessDefaultError', $sUniquenessRuleId); return Dict::Format('Core:UniquenessDefaultError', $sUniquenessRuleId);
} }
$oString = new TemplateString($sTemplate); $oString = new TemplateString($sTemplate);
return $oString->Render(array('this' => $this)); return $oString->Render(array('this' => $oObj));
} }
/** /**
@@ -2238,20 +2236,41 @@ abstract class DBObject implements iDisplay
$aCurrentRemoteIds = []; $aCurrentRemoteIds = [];
$aDuplicatesFields = []; $aDuplicatesFields = [];
$value->rewind(); $value->rewind();
$oOneLnkFaill = null;
while ($oCurrentLnk = $value->current()) { while ($oCurrentLnk = $value->current()) {
$iExtKeyToRemote = $oCurrentLnk->Get($sExtKeyToRemote); $iExtKeyToRemote = $oCurrentLnk->Get($sExtKeyToRemote);
if (isset($aCurrentRemoteIds[$iExtKeyToRemote])) { if (isset($aCurrentRemoteIds[$iExtKeyToRemote])) {
$aDuplicatesFields[] = $oCurrentLnk->Get($sExtKeyToRemote.'_friendlyname'); $aDuplicatesFields[] = $oCurrentLnk->Get($sExtKeyToRemote.'_friendlyname');
} else { } else {
$aCurrentRemoteIds[$iExtKeyToRemote] = true; $aCurrentRemoteIds[$iExtKeyToRemote] = true;
$oOneLnkFaill = $oCurrentLnk;
} }
$value->next(); $value->next();
} }
if (!empty($aDuplicatesFields)) { if (!empty($aDuplicatesFields)) {
$this->m_aCheckWarnings[] = Dict::Format('Core:AttributeLinkedSetDuplicatesFound', //check uniqueness rule in order to stop saving object if necessary
$oAttDef->GetLabel(), $sCurrentClass = $value->GetClass();
implode(', ', $aDuplicatesFields)); $aUniquenessRules = MetaModel::GetUniquenessRules($sCurrentClass);
foreach ($aUniquenessRules as $sUniquenessRuleId => $aUniquenessRuleProperties) {
$bIsBlockingRule = $aUniquenessRuleProperties['is_blocking'];
if (is_null($bIsBlockingRule)) {
$bIsBlockingRule = true;
}
if ($bIsBlockingRule || $aUniquenessRuleProperties['disabled'] === true) {
$sErrorMessage = $this->GetUniquenessRuleMessage($sUniquenessRuleId, $oOneLnkFaill);
$this->m_aCheckIssues[] = $sErrorMessage;
$this->m_aCheckIssues[] = Dict::Format('Core:AttributeLinkedSetDuplicatesFound',
$oAttDef->GetLabel(),
implode(', ', $aDuplicatesFields));
} else {
$this->m_aCheckWarnings[] = Dict::Format('Core:AttributeLinkedSetDuplicatesFound',
$oAttDef->GetLabel(),
implode(', ', $aDuplicatesFields));
}
}
} }
} }
@@ -3176,8 +3195,7 @@ abstract class DBObject implements iDisplay
// Freeze the changes at this point // Freeze the changes at this point
$this->InitPreviousValuesForUpdatedAttributes(); $this->InitPreviousValuesForUpdatedAttributes();
$aChanges = $this->ListChanges(); $aChanges = $this->ListChanges();
if (count($aChanges) == 0) if (count($aChanges) == 0) {
{
// Attempting to update an unchanged object // Attempting to update an unchanged object
MetaModel::StopReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this); MetaModel::StopReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this);
IssueLog::Debug("CRUD: DBUpdate $sClass::$sKey Aborted (no change)", LogChannels::DM_CRUD); IssueLog::Debug("CRUD: DBUpdate $sClass::$sKey Aborted (no change)", LogChannels::DM_CRUD);
@@ -3281,8 +3299,7 @@ abstract class DBObject implements iDisplay
} }
// Update scalar attributes // Update scalar attributes
if (count($aDBChanges) != 0) if (count($aDBChanges) != 0) {
{
$oFilter = new DBObjectSearch($sClass); $oFilter = new DBObjectSearch($sClass);
$oFilter->AddCondition('id', $this->m_iKey, '='); $oFilter->AddCondition('id', $this->m_iKey, '=');
$oFilter->AllowAllData(); $oFilter->AllowAllData();

View File

@@ -608,16 +608,32 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
$aAdded = $this->aAdded; $aAdded = $this->aAdded;
$aModified = $this->aModified; $aModified = $this->aModified;
$aRemoved = array(); $aRemoved = array();
if (count($this->aRemoved) > 0) if (count($this->aRemoved) > 0) {
{
$oSearch = new DBObjectSearch($this->sClass); $oSearch = new DBObjectSearch($this->sClass);
$oSearch->AddCondition('id', $this->aRemoved, 'IN'); $oSearch->AddCondition('id', $this->aRemoved, 'IN');
$oSet = new DBObjectSet($oSearch); $oSet = new DBObjectSet($oSearch);
$aRemoved = $oSet->ToArray(); $aRemoved = $oSet->ToArray();
} }
return array_merge($aAdded, $aModified, $aRemoved); return array_merge($aAdded, $aModified, $aRemoved);
} }
/**
* Get the list of all modified (added, modified and removed) links
*
* @return array of link objects
* @throws \Exception
*/
public function GetModified($sExtKeyToMe)
{
$aModified = [];
foreach ($this->aModified as $oObj) {
$aModified[$oObj->GetKey()] = $oObj->Get($sExtKeyToMe);
}
return $aModified;
}
/** /**
* @param DBObject $oHostObject * @param DBObject $oHostObject
* *
@@ -661,8 +677,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
{ {
$aCheckLinks[] = $iLinkId; $aCheckLinks[] = $iLinkId;
} }
foreach ($this->aModified as $iLinkId => $oLink) foreach ($this->aModified as $iLinkId => $oLink) {
{
$aCheckLinks[] = $oLink->GetKey(); $aCheckLinks[] = $oLink->GetKey();
} }
@@ -698,8 +713,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
// Write the links according to the existing links // Write the links according to the existing links
// //
foreach ($this->aAdded as $oLink) foreach ($this->aAdded as $oLink) {
{
// Make sure that the objects in the set point to "this" // Make sure that the objects in the set point to "this"
$oLink->Set($sExtKeyToMe, $oHostObject->GetKey()); $oLink->Set($sExtKeyToMe, $oHostObject->GetKey());

View File

@@ -385,12 +385,10 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizH
*/ */
this.OnValueChange = function (iLink, iUniqueId, sAttCode, value, $oSourceObject) { this.OnValueChange = function (iLink, iUniqueId, sAttCode, value, $oSourceObject) {
let sFormPrefix = me.iInputId; let sFormPrefix = me.iInputId;
if (iLink > 0) if (iLink > 0) {
{
// Modifying an existing link // Modifying an existing link
let oModified = me.aModified[iLink]; let oModified = me.aModified[iLink];
if (oModified == undefined) if (oModified == undefined) {
{
// Still not marked as modified // Still not marked as modified
oModified = {}; oModified = {};
oModified['formPrefix'] = sFormPrefix; oModified['formPrefix'] = sFormPrefix;
@@ -398,17 +396,26 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizH
// Weird formatting, aligned with the output of the direct links widget (new links to be created) // Weird formatting, aligned with the output of the direct links widget (new links to be created)
oModified['attr_'+sFormPrefix+sAttCode] = value; oModified['attr_'+sFormPrefix+sAttCode] = value;
me.aModified[iLink] = oModified; me.aModified[iLink] = oModified;
} } else {
else
{
// Modifying a newly added link - the structure should already be up to date // Modifying a newly added link - the structure should already be up to date
if (iUniqueId < 0) if (iUniqueId < 0) {
{
iUniqueId = -iUniqueId; iUniqueId = -iUniqueId;
} }
me.aAdded[iUniqueId]['attr_'+sFormPrefix+sAttCode] = value; me.aAdded[iUniqueId]['attr_'+sFormPrefix+sAttCode] = value;
} }
}; };
this.AddModified = function (iLink, sFormPrefix, sAttCode, value) {
// Modifying an existing link
let oModified = me.aModified[iLink];
if (oModified == undefined) {
// Still not marked as modified
oModified = {};
oModified['formPrefix'] = sFormPrefix;
}
// Weird formatting, aligned with the output of the direct links widget (new links to be created)
oModified['attr_'+sFormPrefix+sAttCode] = value;
me.aModified[iLink] = oModified;
};
this.OnFormSubmit = function () { this.OnFormSubmit = function () {
let oDiv = $('#linkedset_'+me.id); let oDiv = $('#linkedset_'+me.id);