-
-
Notifications
You must be signed in to change notification settings - Fork 211
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
Prevent data loss on save (entries and sections) #2659
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I'm assuming the position is that this protection will not be enabled for entries saved from Event's? This is purely a backend only feature to help in scenarios where there are multiple authors?
*/ | ||
public function __construct($table) | ||
{ | ||
assert($table, 'Table cannot be falsy'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't really rely on this for production...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why ? You would prefer to throw ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The behaviour of assert depends on php configuration values. The idea is usually that development code will throw errors, but production instances optimise away the calls.
I'd prefer regular throwing in this scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in d922795. Thanks!
$defaults = array(); | ||
$defaults['creation_date'] = $defaults['modification_date'] = DateTimeObj::get('Y-m-d H:i:s'); | ||
$defaults['creation_date_gmt'] = $defaults['modification_date_gmt'] = DateTimeObj::getGMT('Y-m-d H:i:s'); | ||
$defaults['author_id'] = '1'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noticed below it's 1
, any reason for a string here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it's a copy of this line https://github.com/symphonycms/symphony-2/blob/2.7.x/symphony/lib/toolkit/class.entry.php#L105
Should we change both ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think both should become integers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in d1d7ddf
@nitriques nice touch was looking at this from an extension perspective to provide a From what I can see however (code inspection) there is no way someone can confirm an Secondly if I save and get the error, I'm presuming the post data would be pre-populated as per existing functionality. If so wouldn't saving again have the right time-stamp or is that also in a way maintained through post? Might have missed something as I didn't try it out. |
@brendo Indeed. Right now, the validation is in the publish pages. I did not wanted to break things with this feature. But any entry updated via the frontend will get a new mod_date, so the validation will fail for authors.
Would have to start from scratch. I would hope to have time to do some diffs and try to resolve conflicts, but hey. that's hard.
Exactly. Right now, the save will NEVER work. You have to refresh this page and start from scratch. This is why it's a PR ;) Let's debate. |
I understand the reasoning, but I still try to understand the solution. Does this mean that still two people can open the same entry, but only the first save will be successful? This wouldn't be any better than "the last save wins" (which is what we have now). Also, I think that any "lock" needs the possibilty to "override" the lock. Shouldn't a proper solution prevent access to an entry that is edited currently (by another author)? (Also this would need the possibility to override the lock.) |
Yes.
I do not agree. We had calls from client because of this, and it took the activity tracker to prove to the client that this was happening. From a UX point of view, it's better IMHO. I think that the current error message could be improve, to let the user know more about what happened, like who did the last save and on what time. |
As I said before, I think that a proper solution should in advance prevent access to an entry that is currently edited (by another author). With your solution, authors will ask "Why have you allowed me to edit an entry if I am not allowed to save my work?". There is no good answer to that. One way or another, there should be the possibility to override the "lock", i.e. save nevertheless. |
Agreed re override. That's a must have.
Might also debate if this is something for the core to handle.. at least I
would be happy with this working as you suggested + another flag override
entry which would skip/ignore the check. Could be a checkbox in the UI -
but equally used by other API's to update entries regardless. So if I get
the error modal I don't need to re-start but I can say Ignore the other
dude's changes I want mine.
…On Thu, Apr 27, 2017 at 2:42 PM, michael-e ***@***.***> wrote:
As I said before, I think that a proper solution should in advance prevent
access to an entry that is currently edited (by another author). With your
solution, authors will ask "Why have you allowed me to edit an entry if I
am not allowed to save my work?". There is no good answer to that.
One way or another, there should be the possibility to override the
"lock", i.e. save nevertheless.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#2659 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AA0ef8BbN3Vj8nn3bHEmA9qh2Mv5qpPwks5r0I0zgaJpZM4NEd69>
.
|
That's not always possible. Let's say I open the edit page and go make a coffee. I come back, edit and then press save: only then, do the system know that it's trying to edit based on old records.
Ok this seems to make consensus, I'll do it. Thanks guys! |
"The action requested…" sounds complicated. What about "Saving failed. The entry has been changed by Test on…"? And instead of "Override? Please understand…" I would prefer "Overwrite these changes?". This should be decided by a native speaker. @brendo, ah, no, he's Australian… Hmmm… :-) |
I understand. Thanks. |
bb88937
to
68603b8
Compare
Hahaha. But yeah, I need help with that haha |
I've got override working, and I'm back with the non-existing table problem hahaha |
1914abc is the fix I've meant to not do, by, instead, creating the timestamp validation. But with overriding in place, it still needed to be done. I hope the fix does not suck as much as I think it sucks, but hey, it is the only one I was able to do it. @brendo if you could get your blessing regarding the wording... @michael-e and @jonmifsud suggested
|
@michael-e @jonmifsud @brendo would you mind checking the last commit (fc9adc4). @michael-e I think we need to tell the user at least why Saving "failed" (I prefer aborted since that what's happening) |
@@ -1261,7 +1262,9 @@ public function addTimestampError($errorMessage, $existingObject, $options = arr | |||
)); | |||
$errdiv->appendChild(new XMLElement('label', | |||
Widget::Input('action[ignore-timestamp]', null, 'checkbox')->generate() . ' ' . | |||
__('Override? Please understand that data loss can occur.') | |||
__("I don't care about %s's changes, pretend it never existed.", array( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be "pretend they…" or "pretend these…"?
Anyway it sounds complicated. What about "I don't care about %s's changes. Save my version."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This copy sounds quite rude …
return false; | ||
} else if (isset($_POST['action']['timestamp'])) { | ||
$tv = new TimestampValidator('sections'); | ||
if (!$tv->check($section_id, $_POST['action']['timestamp'])) { | ||
$this->_errors['timestamp'] = __('The action requested was made on a earlier version of the record.'); | ||
$this->_errors['timestamp'] = __('Saving aborted, data integrity check failed.'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"data integrity check failed" isn't very user-friendly, is it? Can we make this more clear?
Mm, I wonder if the in field approach could be refined. At the moment, any 'errors' that occur at a generic level are introduced with a page alert. We have a convention where links can appear as well, and clicking on the link perform actions (usually navigation). Would it be suitable to instead display a page alert along the lines of:
eg. I'm not sure if the This cleans up the interface and doesn't introduce a new field, a pattern that doesn't exist. Items in the main area are always the result of field or extension, never by the core. |
I like this approach.
It's possible translation-wise: the German equivalent would be "Zeige Brendans Eintrag". But you'll run into trouble with names like mine which should be inserted as "View Nils' entry" and not "View Nils's entry". But it's possible to work that out with good copywriting. |
Yeah I'm wondering if other terms could work:
|
|
Yes, debate, ideas! I like Brendon's idea. It was my first idea, but it felt wrong with a check box in it... |
Why does it need a checkbox? Could it be hidden and it's value changed by JS? Or checked by CSS target |
It does not!!! Your solution is completely valid! I just did not thought of it! (somehow, I only saw the checkbox option) |
@brendo There is even a bonus side effect of using the alert: We can display it in index pages as well!!! |
What about:
|
I like @nilshoerrmann wording, it's succinct. My only worry is that "Dismiss changes" is too soft and doesn't clearly explain that you will essentially delete data. What about "Overwrite changes?" or "Replace changes"? |
Both suggestions sound just fine. I had the German word “verwerfen” in mind which translates to “dismiss” or “discard“. So maybe you should just decide, Brendan, because as a native speaker you know best what works in the English language. My main intention was to shorten Nicolas’ message in order to align the wording of both actions with the rest of the interface. |
If it's up to me, I'd vote for |
I meant can I work on finishing this feature since the UI is ok ? :P |
845c71a
to
82d4b3f
Compare
This thing will be an ongoing effort, since it's "complete" (meaning things that are implemented are working), I'll merge soon. |
We add a timestamp check before saving records, either in sections (fields) or entries. When saving a section, if the data changes between the render of the page and the form post, it can lead to data loss if a field was added. The SQL will crash because it assumes the fields did not change. When saving and entry, the SQL was ok, but still, it can lead to data loss. At both places, the fix provided here makes sure we are editing the lastest version of the record. Rebased from 81438f6~1...0c8c765 Rebased from 60e930e~1...1a931a4 Fix SQL error when saving a deleted field This commit makes sure that a request to update a field will not fail if the field as already been deleted by another user (or the same user in a different tab/browser). If the field is found to be non-existant, it is treated as a new field. Fixes symphonycms#2703 pick 1c84b21 Replace the added fieldset with an alert @brendo's suggestion is WAY better then the current implementation, which adds a new UI pattern in the system. Usign links in the alerts makes it possible to offer the overwrite mode, without gluttering the UI. In order to make it possible to submit the form from the alert, a little of pretty simple javascript was needed. Rebased from 6f37eaf~1...c652b18
This is needed to properly display the name of the author that created the conflict. I first thought that the author_id would get updated on save, but it turns our it does not: it's the creation author. So in order to make it backward compatible a modification_author_id is needed. pick dff0d7f
This prevents SQL error in case no author can be found (import script by example). It re-use the default value already established on creation. Fixes symphonycms#2152 pick 64b2b7f
64b2b7f
to
af75b3c
Compare
We add a timestamp check before saving records, either in sections (fields) or entries. When saving a section, if the data changes between the render of the page and the form post, it can lead to data loss if a field was added. The SQL will crash because it assumes the fields did not change. When saving and entry, the SQL was ok, but still, it can lead to data loss. At both places, the fix provided here makes sure we are editing the lastest version of the record. Rebased from 81438f6~1...0c8c765 Rebased from 60e930e~1...1a931a4 Fix SQL error when saving a deleted field This commit makes sure that a request to update a field will not fail if the field as already been deleted by another user (or the same user in a different tab/browser). If the field is found to be non-existant, it is treated as a new field. Fixes #2703 pick 1c84b21 Replace the added fieldset with an alert @brendo's suggestion is WAY better then the current implementation, which adds a new UI pattern in the system. Usign links in the alerts makes it possible to offer the overwrite mode, without gluttering the UI. In order to make it possible to submit the form from the alert, a little of pretty simple javascript was needed. Rebased from 6f37eaf~1...c652b18 Picked from 95fb812
We add a timestamp check before saving records, either in sections (fields) or entries. When saving a section, if the data changes between the render of the page and the form post, it can lead to data loss if a field was added. The SQL will crash because it assumes the fields did not change. When saving and entry, the SQL was ok, but still, it can lead to data loss. At both places, the fix provided here makes sure we are editing the lastest version of the record. Rebased from 81438f6~1...0c8c765 Rebased from 60e930e~1...1a931a4 Fix SQL error when saving a deleted field This commit makes sure that a request to update a field will not fail if the field as already been deleted by another user (or the same user in a different tab/browser). If the field is found to be non-existant, it is treated as a new field. Fixes #2703 pick 1c84b21 Replace the added fieldset with an alert @brendo's suggestion is WAY better then the current implementation, which adds a new UI pattern in the system. Usign links in the alerts makes it possible to offer the overwrite mode, without gluttering the UI. In order to make it possible to submit the form from the alert, a little of pretty simple javascript was needed. Rebased from 6f37eaf~1...c652b18 Picked from 95fb812
next() always returns an entry object or null, never an array. This problem was introduced in 154c161 Re symphonycms#2659 Picked from 119f3fe
next() always returns an entry object or null, never an array. This problem was introduced in 154c161 Re symphonycms#2659 Picked from 119f3fe Picked from d9992e9
We add a timestamp check before saving records.
When saving a section, if the data changes between the render of the
page and the form post, it can lead to data loss if a field was added.
The SQL will crash because it assumes the fields did not change.
When saving and entry, the SQL was ok, but still, it can lead to data
loss.
At both places, the fix provided here makes sure we are editing the
lastest version of the record.
cc @brendo @michael-e