User tests: Successful: Unsuccessful:
When the Joomla timezone is set to none UTC, eg. Chicago, and a calendar form field is created for an article, then the time is moving backwards on every save of the article.
This is an upstream fix, reported by a DPFields user Digital-Peak/DPFields@375eee2.
%Y-%m-%d %H:%M
The date in the calendar custom field should stay as set. On the front end the same date with the same format of the plugin should be shown.
The date is moving backwards. On the front, the date is shown with the database format.
Status | New | ⇒ | Pending |
Category | ⇒ | Front End Plugins |
Labels |
Added:
?
|
Category | Front End Plugins | ⇒ | com_fields Front End Plugins |
I tried both filters and none of them work as expected. They are wrong IMO anyway as they are doing the opposite of what is expected.
At least for core they do what is expected
USER_UTC converts the datetime from the user timezone to UTC (saving) and back (display)
SERVER_UTC converts the datetime from the server timezone to UTC (saving) and back (display)
It is mostly useless when you want to store only a date. Eg a birthday shouldn't be changed based on timezones.
If you want to show the date and time of an event (eg article creation, television broadcast), then you need to adjust the time to the users setting or he will be off by some hours.
It depends on the usecase. That's why I think a parameter may be useful, depending on what the user wants to do here.
IMO, it should be the other side, to assume the value comes in the user timezone and then convert it to UTC and not assume that the time which comes is in UTC and we need to convert it to the user timezone.
IMO, it should be the other side, to assume the value comes in the user timezone and then convert it to UTC
That is what is done during save. The value is taken from the user, assumed it is in his timezone and then converted to UTC and saved as UTC in the database.
When displayed, it is converted back from UTC to the timezone of the user (which may be another timezone depending on user).
Yep, that is during display. Because the data is coming from the database (UTC) and is shown to the user in his timezone.
The save part doesn't happen in the formfield, it happens in JForm: https://github.com/joomla/joomla-cms/blob/staging/libraries/joomla/form/form.php#L1310. That's where it gets converted from user timezone to UTC for saving into the database.
Ok, now it starts to make sense. So why the time shift then as all should be ok, when no filter is set?
Actually, right now, during saving, no filter is applied for calendar custom fields. The value is saved as how it entered, then when it is displays, it is converted using USER_UTC
It seems there is no place for setting filter for calendar custom field at the moment. So during saving, no filter is being applied to the value
However, when the value is displayed again, USER_UTC is applied because no filter is set for calendar custom field https://github.com/joomla/joomla-cms/blob/staging/libraries/joomla/form/fields/calendar.php#L165
But then it is a bug in Joomla as you can't do something on save and then do something different on load.
Yes. Seems to be a bug in JForm field calendar in this case
There is a bug in behavior if the filter isn't set, yes. Not sur how to fix it without breaking anything, because the current default value USER_UTC goes back a long time and is even documented in https://docs.joomla.org/Calendar_form_field_type. Removing that default may have unexpected results.
However in JForm itself, we can't just add the same default because it is used in all fields.
So I think fixing this may have to wait until 4.0 where we can either make the filter a required attrbute or remove the default value for the filter.
How the custom fields values are being saved to database? Is it using the filtered/validated data from form object or using data directly from application input?
I ask the question because if we add the following command
$this->element['filter'] = $this->filter;
After this line
https://github.com/joomla/joomla-cms/blob/staging/libraries/joomla/form/fields/calendar.php#L165
Then the data is properly filtered using correct filter. However, I checked the value stored in jos_fields_values table and see that it is still storing unfiltered data, so maybe a bug in saving custom fields data, too?
Good point, can be the problem that we are not sending the data trough the form validate here https://github.com/joomla/joomla-cms/blob/staging/plugins/system/fields/fields.php#L65 as I was pointing out here #13565 (comment)?
Yes, that's the reason of the problem. I don't know we support custom fields data filtering or not, but if you want to support filter like how it is supported in JForm, then we need to find a way to get filtered/validated data to store instead of getting data directly from input
At the moment, the only way I see is to remove the onsave events in the system plugin and move the logic to the save function in the model. For example in DPCalendar, all the custom fields are available in the params field, so this point here is reached, which is the filtered/validated data.
It would be great for sure to have the input data validated/sanitised. I'm not sure how easy that will be.
Not sure why Joomla doesn't pass the validated/sanitised data while triggering the event at https://github.com/joomla/joomla-cms/blob/staging/libraries/legacy/model/admin.php#L1192 , If it was, then the work would be much simpler
I don't know the custom fields code enough to see a solution :(. How about storing validated data back to input object before calling save method at https://github.com/joomla/joomla-cms/blob/staging/libraries/legacy/controller/form.php#L745
Something like
$this->input->post->set('jform_validated_data', $validData);
Then that validated data can be accessed via input object later?
For example on articles is the problem that the params fieldsets are displayed on the edit form, but they are not sent with the object on the plugin save event because the field is attribs and not params as before. So for me would be the right approach to send also this data with the plugin event, otherwise all the params from other custom fields extensions don't get saved anyway.
Send that data via plugin event would be the correct way. However, do we have a way to code it so that it doesn't affect existing plugins process this event?
I think the other option would be use $app->setUserState like this https://github.com/joomla/joomla-cms/blob/staging/libraries/legacy/controller/form.php#L768
That would be safe option for Joomla 3? We pass data to plugin in the same way in com_users https://github.com/joomla/joomla-cms/blob/staging/components/com_users/controllers/user.php#L100
Category | Front End Plugins com_fields | ⇒ | Libraries Front End Plugins |
I'm adding the params from the request as _params to the table when the params field doesn't exist on the table. Like that the data is filtered and validated, there is no need then to fetch it directly from the request anymore.
It is not possible to add it as $table->params, otherwise we get an error on save that the table doesn't contain a params field in the table.
This allows to set the filter to USER_UTC. Any feedback on my last change?
The changes should be OK. Haven't looked at the code carefully yet, but I have two questions:
In this line of code if (isset($data['params']) && !isset($table->params))
, could you explain the reason of adding !isset($table->params) ?
In this line of code $table->_params = $data['params'];
, is _params safe to use? I mean do we have to worry if existing code use that field before for whatever purpose ?
Seems in custom fields setup, we don't have place to setup the filter for field? If so, I think you will have to set it in the code to set filter for calendar custom field type to use USER_UTC by default?
Should be good now. The only case it would not work is table doesn't use params but use _params . Will test it and mark the test result later today.
Agree. That would be best :)
BTW, I have the feeling that when using filter like this, if the format of the field doesn't contain time parameter, we only store date value and the during the conversion, the date could still be changed. Will try to test and give an example
Please use my last commit as I the date is now also properly printed on the front.
@Bakual this would introduce a dependency in JModelAdmin to com_fields (there was once a discussion about that with com_categories
Title |
|
this would introduce a dependency in JModelAdmin to com_fields
You don't have to change anything in JModelAdmin. As you see in my other PR it works fine when using another property. All JModelAdmin does with "params" is in the getItem method it is converting it to a registry object if it exists, but that has nothing to do with validating a formfield.
On both PR's we have the problem that the data is fetched from the request and not from the item in the plugin event as I'v pointed out here as well #13353 (review).
With my recent changes in this PR, the problem is eliminated with the _params property. In your PR, this must then be _com_fields on JModelAdmin (except when you give it a different name like _bakual).
That's what I meant. We could just name it eg _com_fields instead of _params and don't have the reference to params anymore. They don't have to match any name, it's just the starting underscore which makes a different behavior if I'm right. No need to change anything in JModelAdmin.
It doesn't get attached to the $item because there is no such field, that's the reason why we can't do it with _com_fields, see my change here https://github.com/joomla/joomla-cms/pull/13606/files#diff-39b4bc5cb02bbace2ee4febe8f54583eR1161.
OK. So I tested with two different formatswith and without parameters), two different timezones, the data is saved/displayed properly. So basically, this PR fixed the issue.
For the correct way of passing data from model to the plugin for processing, I will leave it to you two as you have been working on custom fields from beginning.
Ah I missed that you have changed that in JModelAdmin. That doesn't look good.
Also you can forget the changeformat stuff, that's not going to pass. Why do you need that?
To me the proposed code looks like a big hack. There mus be a better way to fix this.
Thanks for testing, please mark it as success here https://issues.joomla.org/tracker/joomla-cms/13606.
That doesn't look good.
Eager to see a proper solution then. Hacks are in JModelAdmin all over.
Also you can forget the changeformat stuff, that's not going to pass. Why do you need that?
Because JDate needs a PHP date format and not this unreadable strftime format.
There mus be a better way to fix this.
Go for it.
For me getting the data unvalidated/filtered is also not an option. So this approach solves it at least in an understandable way.
How about my suggestions about passing data before:
I think the other option would be use $app->setUserState like this https://github.com/joomla/joomla-cms/blob/staging/libraries/legacy/controller/form.php#L768
That would be safe option for Joomla 3 and in Joomla 4, we can pass that data directly to event trigger? We pass data to plugin in the same way in com_users https://github.com/joomla/joomla-cms/blob/staging/components/com_users/controllers/user.php#L100
Use that approach will not only allow this plugin but also other plugins access to validated/sanitized data data, too.
The display part shouldn't rely on the format set in the field. It should use one of the translated formats available in the language strings, see https://github.com/joomla/joomla-cms/blob/staging/language/en-GB/en-GB.ini#L303-L308.
That is how we display a date in each other location. Forms where an exception but with 3.7.0 there will be a solution here as well to use those translated formats.
We could add a parameter field where the user can select the display format. I do something similar in my extension using this code: https://github.com/Bakual/SermonSpeaker/blob/master/com_sermonspeaker/admin/models/fields/dateformat.php#L53-L72 to fetch the available formats.
Another and maybe better approach is to add the new parameters of the new calendar. There is a showtime toggle which enables/disables the time part of the calendar popup. It is also used in the translateFormat feature and would allow us to toggle between two translated formats (with and without time). That would likely solve most of the usecases for the display part and the other can easily do it in an override anyway.
So that would remove the need for the changeFormat stuff.
Agree, an output format parameter would make more sense. What about adding the ones from the language strings and give the admin the ability to define a custom one?
The params issue is still not solved yet.
What about adding the ones from the language strings and give the admin the ability to define a custom one?
We could allow a custom one, but imho it's useless in most cases and will not work for multilingual sites. I would just offer the existing ones and if he needs a different one he can either override the existing language strings or the layout. Both would be easy.
The params issue is still not solved yet.
I started to look into it but was stuck in the basement of a train station with only the laptop screen. So I was quite handycapped
No, datetime is already taken by DPCalendar
The format is not a big deal, just the usual disagreement between @Bakual and me
More importantly is how the custom field data get's all the way along to the onContentBeforeSave event that it gets filtered and validated.
I guess he thinks that we can pass a strftime time format to JDate::format.
See https://github.com/joomla/joomla-cms/compare/staging...Bakual:FieldCalendarPlugin?expand=1 for what I think :-p
This applies the user timezone plus applies a localised date format. The date format needs to be customised to a degree as I wrote above.
Status | Pending | ⇒ | Closed |
Closed_Date | 0000-00-00 00:00:00 | ⇒ | 2017-01-23 09:58:47 |
Closed_By | ⇒ | laoneo |
Imho, better would be to allow the filter to be set in the field. Default would be
USER_UTC
, the other option isSERVER_UTC
and all other values basically disable the feature.I think the issue is because the formfield defaults to USER_UTC if nothing is specified but there is no such default during the save process.