Add revision tracking to drafts, remove button, remove from new page #4186
Conversation
@schalkneethling I'd appreciate an initial review. I'm still fighting with some edge cases and need to add tests. @jwhitlock You know the weird rabbit warrens that are the create / edit / translate paths in kuma, can you look and see if I've left an edge case unaccounted for? Create English pages should not get drafts anymore, new translations should, edits should, and translation edits should. If I work on this at all tomorrow it will be tests, so no hurry on the review. I have a few reviews to do myself ;) |
kuma/static/js/wiki.js
Outdated
// Generates a storage key based on pathname | ||
// copied from wiki-edit-draft.js because: race conditions | ||
// does not need to copy logic for dealing with new translations | ||
// |
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.
We should probably start moving to some cross project standards for comments. In this case we probably want to do:
/**
* Generates a storage key based on pathname
* copied from wiki-edit-draft.js because: race conditions
* does not need to copy logic for dealing with new translations
*/
Probably a good idea to follow the JSDoc guidelines http://usejsdoc.org/ - unless MDN already uses another JSDoc standard or tool.
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.
MDN is the wild west of javascript formatting right now (I just copy what is in the file I'm working on at the time). Cross project standards would be awesome. 👍
I will try to get over my distaste for using comments that are not left aligned :P
383f0e7
to
cfbf24d
Compare
Darn, legitimate test failure. That's what I get for editing Python... |
kuma/static/js/wiki-edit-draft.js
Outdated
// some knowledge about the page we're dealing with | ||
var $translateForm = $('#translate-document'); | ||
var $form = $translateForm.length ? $translateForm : $('#wiki-page-edit'); | ||
var isNewTranslation = location.search.indexOf('?tolocale') > -1 ? true : false; |
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.
Will tolocale
always be the first query string parameter?
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.
Not sure, but this should be okay if it's not.
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)'); | ||
var results = regex.exec(location.search); | ||
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' ')); | ||
}; |
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.
JavaScript doesn't have a native method for this. 😢
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.
i no rite?
var draftAutoSaveEnabled = true; | ||
var draftWait = false; | ||
var draftWaitTime = 500; | ||
var $revisionInputs = $('[name=current_rev]'); // can't use ID because there's a duplicate >_< |
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.
We could try to fix this in the form / template logic...
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.
That would be nice. It broke things when I tried and it's an ancient problem so I figured it could be dealt with separately but if you want to pair on this we could try.
kuma/static/js/wiki-edit-draft.js
Outdated
// change translation creation format to match edit format | ||
// /en-US/docs/page$translate?tolocale=fr | ||
// draft/edit/fr/docs/page | ||
var localeCode = location.search.replace('?tolocale=', ''); |
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.
could use the new getUrlParameter
kuma/static/js/wiki-edit-draft.js
Outdated
draftWarning += '<div class="readable-line-length">'; | ||
// long line is long, but breaking it up wrecks it for the localizers | ||
draftWarning += gettext('Compare this date to the latest revision date to ensure you\'re not overwriting later changes.'); | ||
draftWarning += ' <a href="#TODO" class="external-icon">Revision history.</a>'; |
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.
Revision history
should be localized. Is the #TODO
for this PR?
kuma/static/js/wiki-edit-draft.js
Outdated
draftWarning += '<div class="readable-line-length">'; | ||
// long line is long, but breaking it up wrecks it for the localizers | ||
draftWarning += gettext('A newer version of this article has been published since this draft was saved. You can restore the draft to view the content, but you will not be able to submit it for publishing.'); | ||
draftWarning += ' <a href="#TODO" class="external-icon">Revision history.</a> '; |
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.
Revision history
should be localized. Is the #TODO
for this PR?
kuma/static/js/wiki-edit-draft.js
Outdated
// long line is long, but breaking it up wrecks it for the localizers | ||
draftWarning += gettext('A newer version of this article has been published since this draft was saved. You can restore the draft to view the content, but you will not be able to submit it for publishing.'); | ||
draftWarning += ' <a href="#TODO" class="external-icon">Revision history.</a> '; | ||
draftWarning += '<a href="#TODO" class="external-icon">Published version</a>'; |
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.
Published version
should be localized. Is the #TODO
for this PR?
kuma/static/js/wiki-edit-draft.js
Outdated
$draftStatus.append(gettext('Draft') + ' ') | ||
.append($updateAction) | ||
.append(' ') | ||
.append($updateTime); |
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.
I'm not sure localized strings can be combined this way in all locales. It may be better to pass the two values in ('published' and 'discarded') and construct translatable strings for each, like
if (action === 'published') {
$draftStatus.append(interpolate("Draft published at %s", $updateTime))
}
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.
I'm 100% sure they can't be but this is what was there before.
kuma/static/js/wiki-edit-draft.js
Outdated
// disable autosave when saving manually | ||
enableAutoSave(false); | ||
} | ||
*/ |
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.
Remove, if you've gotten consensus to remove manual draft saving.
Ug, thought I was leaving a review. Sorry for the spam... |
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 worked well in practical testing. It was awesome to see all the old local storage keys switch to the new format. It would be nice to throw away the old ones as well, but good work preserving them.
I tried:
- New page - no drafts saved
- Edit page - Drafts auto-saved, recovered, discarded. The revision in the final URLs was removed by JS, as well as the draft.
- Translate page - Drafts auto-saved. There was an empty revision number, and the draft was deleted on save.
- Translation Edit - Drafts auto-saved. Revision number was included in the URL after saving, and was removed by JS, as well as the draft.
I added a lot of comments to the JS code, which I think can mostly be ignored, since this is moving code around and fixing the formatting. All of page editing needs to be re-written, back and front end. But not in this PR.
kuma/static/js/wiki-edit.js
Outdated
// disable form | ||
$('#wiki-page-edit').attr('disabled', true); | ||
|
||
// Clear previous notification messages, if any | ||
$('.notification button.close').click(); | ||
|
||
// give user feedback | ||
var saveNotification = mdn.Notifier.growl('Saving changes…', { duration: 0 , closable: true}); | ||
var saveNotification = mdn.Notifier.growl('Publishing changes…', { duration: 0 , closable: true}); |
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.
wrap in gettext
?
/** | ||
* Generates a storage key based on pathname | ||
* copied from wiki-edit-draft.js because: race conditions | ||
* does not need to copy logic for dealing with new translations |
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.
👍 explaining duplication
kuma/wiki/views/edit.py
Outdated
@@ -268,7 +268,7 @@ def edit(request, document_slug, document_locale, revision_id=None): | |||
# content API, constrain to that section. | |||
params['section'] = section_id | |||
# Parameter for the document saved, so that we can delete the cached draft on load | |||
params['document_saved'] = 'true' | |||
params['rev_saved'] = curr_rev.id |
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.
If this is a document without any revisions, then cur_rev
will be null, and this will raise an exception. I'm not sure how this would be possible, outside of tests. However, there is one in production (https://developer.mozilla.org/de/docs/Glossary/Raster), and several in the sample database (http://localhost:8000/en-US/docs/Project:MDN/Contributing).
I think if curr_rev
is None, you can do whatever you want. This edit view is in need of a re-write, as well as the two tests exercising this invalid code path.
url = doc.get_absolute_url() | ||
params = {} | ||
# Parameter for the document saved, so that we can delete the cached draft on load | ||
params['rev_saved'] = request.POST.get('current_rev', '') |
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.
For the first translation, current_rev
will be empty, and the query string will be rev_saved=
. I think this is what you want, but you'll need to update the translation tests to expect it.
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.
Tests updated 👍
cfbf24d
to
2b7844f
Compare
Should be down to one test failure. |
38a44fe
to
17d3715
Compare
Okay, ready for final reviews now! Still writing tests... |
kuma/wiki/tests/test_views.py
Outdated
args=[edit_doc.slug])) | ||
doc_url = reverse('wiki.document', args=[edit_doc.slug], locale=foreign_locale) | ||
params = {'rev_saved': ''} | ||
doc_url = '%s?%s' % (doc_url, urlencode(xparams)) |
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 should be params
not xparams
, as caught by flake8. It's also code that isn't called during tests, but we agreed to postpone cleanup until after this PR is merged.
17d3715
to
eb20688
Compare
Codecov Report
@@ Coverage Diff @@
## master #4186 +/- ##
==========================================
+ Coverage 87.08% 87.09% +<.01%
==========================================
Files 155 155
Lines 9415 9420 +5
Branches 1263 1263
==========================================
+ Hits 8199 8204 +5
Misses 989 989
Partials 227 227
Continue to review full report at Codecov.
|
@schalkneethling Did you want to take another look at this before it goes live? The code is "final" now I'm just writing tests. |
Looks good to me @stephaniehobson r+ |
eb20688
to
b1ac21d
Compare
I think this is good to go. @jwhitlock I think I have addressed the changes you requested. |
😄 will test it out Tuesday morning |
Tests still failing :( I thought we had that beat. So much for my triumphant weekend. |
kuma/wiki/views/edit.py
Outdated
# if we're not getting raw content. | ||
url = '%s#%s' % (url, section_id) | ||
return redirect(url) | ||
orig_rev = Revision.objects.get(pk=orig_rev_id) |
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 rebase pulled in too much code. Lines 248-307 should be removed, and then the redirect URL code re-indented.
e19a6aa
to
177c1b1
Compare
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.
Looking good. There is a functional test that is broken, a few except
blocks that can be more focused, and a few nits.
I also need to make these tests run better against local dev, but future goals.
url = doc.get_absolute_url() | ||
params = {} | ||
# Parameter for the document saved, so that we can delete the cached draft on load | ||
params['rev_saved'] = request.POST.get('current_rev', '') |
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.
Tests updated 👍
tests/functional/test_article_new.py
Outdated
# wait for throttled draft function to activate (hopefully not) | ||
time.sleep(0.6) | ||
# check save draft not activated | ||
assert not page.is_draft_container_displayed |
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 should be page.editor().is_draft_container_displayed
.
tests/pages/regions/ckeditor.py
Outdated
def is_draft_old_displayed(self): | ||
return self.find_element( | ||
*self._draft_old_locator | ||
).is_displayed() |
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 will raise an exception if the element doesn't exist, which is being tested in test_article_new
. I'd add an import at the top:
from selenium.common.exceptions import NoSuchElementException
and then write it like:
def is_draft_old_displayed(self):
try:
return self.find_element(
*self._draft_old_locator
).is_displayed()
except NoSuchElementException:
return False
tests/pages/regions/ckeditor.py
Outdated
return self.find_element( | ||
*self._draft_restore_locator | ||
).is_displayed() | ||
except: |
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.
except:
is too general, please make this except NoSuchElementException:
tests/pages/regions/ckeditor.py
Outdated
def is_draft_status_displayed(self): | ||
try: | ||
return self.find_element(*self._draft_status_locator).is_displayed() | ||
except: |
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.
except NoSuchElementException:
here as well
tests/pages/article_new.py
Outdated
**self.url_kwargs | ||
locale=self.DEFAULT_LOCALE, | ||
slug=self.DOC_SLUG, | ||
# **self.url_kwargs |
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 comment can be deleted
edit_button = self.find_element(*self._edit_button_locator) | ||
edit_button.click() | ||
if (signedin): | ||
from pages.article_edit import EditPage |
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.
👍 removing this import
|
||
def edit_source(self): | ||
def edit_source(self, content=TEST_TEXT): |
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.
👍 making this an optional parameter instead of a class constant
The goal of this PR is to make the auto save drafting feature reliable. Fix Bug 1264314: Alert user if page content has changed since last draft - revision ID is now saved when draft saved - displays warning message if revision ID is old - displays note if revision ID is old Fix Bug 1298399: Remove ?document_saved - query variable now includes revision ID of draft to delete - query variable now directly consumed by javascript - query variable new removed after draft deleted Fix Bug 1268906: Tool bar buttons now trigger draft saves - changed listener from 'key' to 'change' Fix Bug 1274067: No new drafts created after discarding page - disabled autosave after discard button clicked Fix Bug 1269851: Save draft should not wait for typing break to save - added throttled call to autosave every 500ms - reduced debounce autosave to 500ms, to catch edits made after throttle Fix Bug 1301656: Remove draft feature from new page creation - removed draft widget from new pages - changed data format for new translation pages to retain drafts there Also: - autosave now enabled by default even with saved draft -- removed need to discard saved draft - separated draft functionality into a new file - new utility function to get query strings by variable name - added some analytics instrumentation - standardized draft storage formats for non-English pages -- migrated old draft formats to new storage format
177c1b1
to
b5e34e7
Compare
Made requested changes to tests 🎉 |
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.
🚢 SHIP IT
The goal of this PR is to make the auto save drafting feature reliable.
NOT READY. But this is a big PR and I would appreciate a first pass review while I fight the edge cases.
Still to do:
Fix Bug 1264314: Alert user if page content has changed since last draft
Also: