-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix deleting Custom Field value from table when Field not set in object #19742
Conversation
We have been having few PRs trying to fix this issue without success. The reason is because the way checkbox custom field type work. With this modification for example, I think you cannot un-select all checkbox option. |
@joomdonation, this is not specifically for check-boxes, this fix is for any fields getting deleted on a user object (so also media, text, textarea, etc.) |
I am sorry, my comment was not clear. I meant with your fix, custom field checkbox options could not be be unchecked anymore (unchecked all options). To test it, you can:
(That comes from checking several PRs in the past and reading your code, I haven't tested this again myself yet) |
To continue what joomdonation is saying, If you uncheck all values in a checkbox array (or if you have a single checkbox and you uncheck it)
thus it will happen what joomdonation described above, you will not be able to uncheck all checkboxes |
@joomdonation okay, I understand now and see the issue that it introduces for checkboxes. Am I correct in thinking that the cause of this is the way the checkbox fields are implemented: when no checkbox set remove field from table instead of when no checkbox set save field value as empty? |
Actually, it causes by the way checkbox works. See the comment from @ggppdk above #19742 (comment) to understand |
got it :), just changed my PR, for me this works (including checkboxes), does it work for you @joomdonation ? |
You got it, but almost you see if all custom fields in the form are of checkbox type because then no values will be posted for |
As @ggppdk said, it doesn't work |
shoot :( is this an issue only for checkboxes or are there other fields that get erased from the table when empty? |
Checkbox and Checkboxes , see #18207 for the proposed solution but was rejected |
#18207 was never rejected. I voiced a concern about the code/API design and the submitter voiced frustration and requested it closed. |
Okay, finally got it (working that is). Following the logic already in place for the activate, block and unblock save actions. |
ok, but you have changed the PR completely you are now making this PR to fix the issue for a specific component (com_users), but that might have been ok,
|
Probably the proper fix is similar to what #18207 did
and then use: $input = JFactory::getApplication()->input;
$jformData = $input->get('jform', array(), 'array');
$existence = isset($jformData['__existence']['com_fields'])
? $jformData['__existence']['com_fields']
: array();
...
foreach ...
{
...
$presentInForm = key_exists($field->name, existence);
$valuePosted = !is_null($value);
// Always set to true, since canEdit is checked inside setFieldValue method
$canEditFieldValue = true;
if ($presentInForm || $valuePosted) && $canEditFieldValue)
{
// Setting the value for the field and the item
$model->setFieldValue($field->id, $item->id, $value);
}
} |
The Form API shouldn't be aware of what the request (or for that matter whatever input source it has bound to it) does or does not send. This is a level of filtering/validation that should be handled closer to the request (typically a controller or model). Please remember the form field objects in our API are primarily layout renderers, not data handlers/validators. |
Hi @ggppdk the PR has changed, but the issue is still the same. The task is only changed when using the Joomla UserHelp functions to add / remove groups. the task is only temporarily changed and after adding or removing the groups it is changed back to the task of the calling component. |
OK, just looked at the code diff for the PR. There is a major problem here. There is a huge internal coupling somewhere in the system on the request data, specifically the task parameter, and whatever is acting differently based on that parameter is where our root issue lies. This PR is just another bandaid fix over the root issue. Again, it might fix this particular code path and problem, but we aren't addressing the root issue here. And we really need to fix the root issue once and for all, not continue to patch every code path that leads up to that issue with unstable workarounds. |
yes correct, i thought of it too , i also did like adding the request there !! -but it is already being used inside the fields plugin code in 1 or 2 other places we could do it without the request there but in such a case in would involve (?much) more work to also add changes to JForm validation
@Ruud68
|
I think the root issue is |
Nor should it. Because JForm is NOT the request handler. The MVC layer is the request handler, JForm is simply given a data array to be bound to it and can run a set of validation rules based on the form's XML definition. None of this is request aware (and inherently "field presence" aware). Which means that you need to validate the data array before/after binding it to the form to ensure fields are defined with a value, even if null, if you need that particular behavior. JForm isn't stripping null values, it works with a Registry instance and it has no issue whatsoever with doing
This is not the Form API. If it is not JForm, JFormField, JFormRule, or a subclass of those it is not the form API. |
I was also working on another PR (https://issues.joomla.org/tracker/joomla-cms/19473) So when we are able to agree on how to fix this issue (which is a production issue as I am loosing user data on multiple sites with every subscription that expires / reactivated), I can give it a shot when given direction :) |
Here's how I see it.
|
Just a though after looking at this almost all day. This logic is different for checkboxes: when not set they are not in the database (=same as text field), when set the value is in the database (same as text field), but when 'unchecked' (made empty), they are removed from the database whereas the text field is still in the database with an empty value. What if we changed that behavior: when a checkbox is 'unchecked' set the value in the database to '' (empty) instead of removing it? |
That's the part about checking the input data I was mentioning. Because a checkbox field won't be in the request unless checked. This is basically my workflow in a project to ensure checkbox fields are always set (modified for Joomla API use): $data = $this->input->post->get('jform', [], 'array');
foreach ($arrayHoldingCheckboxFieldNames as $checkboxField) {
// If the checkbox is already in the array, we're good, otherwise we need to set it and since checkboxes typically should be a boolean toggle, default to false if not set
$data[$checkboxField] = $data[$checkboxField] ?? false;
} Again, I don't know how this translates into com_fields logic but at the end of the day this particular validation step does NOT belong to the Form API ( |
Maybe @laoneo can tune in (if not already :))? |
I'm here with @mbabker. This pr does not solve the cause of the issue. |
okay, just to stop the 'bleeding' of user data on my (and others?) production sites I have changed this PR to NOT use the task but introduced another variable to switch on. This should not break things. I am willing to work on a definitive solution, but please keep in mind that for me this is a steep learning curve. So although I'm willing, being able depends on the developer community in giving me guidance, direction, padding on the shoulder and kicks under my you know where :) |
I would add somewhere here a check if all fields have a value from the request. At this point you have the form to get the fields and the data from the request. |
At the place suggested by @laoneo i was thinking to so i have added // Test whether the data is valid.
$validData = $model->validate($form, $data);
/**
* Validate field presence array, this is used by some fields together with canEditFieldValue
* to decide if field's values should be emptied or if field values should be maintained
*/
\JArrayHelper::toInteger($presence);
$validData['_presence_'] = $presence; // Pass presence information Plus changes to renderField() method of FormField.php public $addFormPresence = true; Plus changes to the fields system plugin to use $presence_name = 'com_fields_' . str_replace('-', '_', $field->name);
$presentInForm = key_exists($presence_name, $presence);
$valuePosted = !is_null($value);
// NOTE: canEdit field value is checked inside setFieldValue method, so need to check it here
// Even if field presence was tampered with it is OK if user has ACL to edit field value !
if ($presentInForm || $valuePosted)
{
// Setting the value for the field and the item
$model->setFieldValue($field->id, $item->id, $value);
} and seems to be working |
Top: I will be testing and also test against my own PR for the Fields View Level that I am 'cooking' (https://issues.joomla.org/tracker/joomla-cms/19473) here I am running into the issue that when a field is not displayed on the form it gets deleted from the database when the user saves his profile :) |
Here is yet another PR #19752, that tries to resolve the issue by adding field presence in the form |
Okay, while browsing the changes in the joomla-cms project I found #16958 (@joomdonation PR). This could also be the solution for this issue: Just posting here before doing the code changes to avoid a lot of work :) Not sure if this can be classified is a work around or could be seen as real fix as it is also used (and approved) in com_content. |
@laoneo is working on a PR, hopefully with a proper fix. Hope we can get this issue sorted in 3.8.6. Please wait. |
okay, damn. Somehow missed that he was working on a fix (banging my head against the wall for missing that as it would have saved me a lot of work today :S) |
@joomdonation when/where was this mentioned? |
Sorry, I had a quick chat with him on Glip |
Closing this one in favor of #19884 |
Pull Request for Issue # https://issues.joomla.org/tracker/joomla-cms/19735
Summary of Changes
When saving a user object that has NO custom fields attached in a specific context, the saved custom fields for that object will be erased. This will happen e.g. when a the function removeUserFromGroup or function addUserToGroup is called from a component to remove or add a user from a user group.
Testing Instructions
The values should show in the user profile
Expected result
The filed values for this user should not be deleted
Actual result
The field values for this user are deleted (from the #__fiels_values), because when calling the removeUserFromGroup / addUserToGroup the user object doe not have the set fields attached to the object and there for the value for these fields is set to NULL
Documentation Changes Required
none.