From d0928737c06697f6284101bfded61cb8d933ce34 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Tue, 7 Dec 2010 14:03:52 +0100 Subject: [PATCH 1/1] #453: revert document to a past revision --- apps/wiki/forms.py | 33 +++++++++- apps/wiki/models.py | 5 +- .../wiki/templates/wiki/document_details.html | 9 +-- apps/wiki/templates/wiki/revert_dialog.html | 42 ++++++++++++ apps/wiki/urls.py | 3 + apps/wiki/views.py | 35 ++++++++-- redakcja/settings/compress.py | 1 + redakcja/static/css/dialogs.css | 2 +- redakcja/static/js/wiki/dialog_revert.js | 65 +++++++++++++++++++ redakcja/static/js/wiki/dialog_save.js | 1 - redakcja/static/js/wiki/view_history.js | 7 +- redakcja/static/js/wiki/wikiapi.js | 44 +++++++++++++ 12 files changed, 226 insertions(+), 21 deletions(-) create mode 100644 apps/wiki/templates/wiki/revert_dialog.html create mode 100644 redakcja/static/js/wiki/dialog_revert.js diff --git a/apps/wiki/forms.py b/apps/wiki/forms.py index d5c0ed54..74934bd2 100644 --- a/apps/wiki/forms.py +++ b/apps/wiki/forms.py @@ -66,14 +66,12 @@ class DocumentTextSaveForm(forms.Form): """ Form for saving document's text: - * name - document's storage identifier. * parent_revision - revision which the modified text originated from. * comment - user's verbose comment; will be used in commit. * stage_completed - mark this change as end of given stage. """ - id = forms.CharField(widget=forms.HiddenInput) parent_revision = forms.IntegerField(widget=forms.HiddenInput) text = forms.CharField(widget=forms.HiddenInput) @@ -102,3 +100,34 @@ class DocumentTextSaveForm(forms.Form): label=_(u"Completed"), help_text=_(u"If you completed a life cycle stage, select it."), ) + + +class DocumentTextRevertForm(forms.Form): + """ + Form for reverting document's text: + + * revision - revision to revert to. + * comment - user's verbose comment; will be used in commit. + + """ + + revision = forms.IntegerField(widget=forms.HiddenInput) + + author_name = forms.CharField( + required=False, + label=_(u"Author"), + help_text=_(u"Your name"), + ) + + author_email = forms.EmailField( + required=False, + label=_(u"Author's email"), + help_text=_(u"Your email address, so we can show a gravatar :)"), + ) + + comment = forms.CharField( + required=True, + widget=forms.Textarea, + label=_(u"Your comments"), + help_text=_(u"Describe the reason for reverting."), + ) diff --git a/apps/wiki/models.py b/apps/wiki/models.py index ec9ded50..7cb20c65 100644 --- a/apps/wiki/models.py +++ b/apps/wiki/models.py @@ -53,9 +53,8 @@ class DocumentStorage(object): text, rev = self.vstorage.page_text_by_tag(name, tag) return Document(self, name=name, text=text, revision=rev) - def revert(self, name, revision): - text, rev = self.vstorage.revert(name, revision) - return Document(self, name=name, text=text, revision=rev) + def revert(self, name, revision, **commit_args): + self.vstorage.revert(name, revision, **commit_args) def get_or_404(self, *args, **kwargs): try: diff --git a/apps/wiki/templates/wiki/document_details.html b/apps/wiki/templates/wiki/document_details.html index bdb22007..934a8ac6 100644 --- a/apps/wiki/templates/wiki/document_details.html +++ b/apps/wiki/templates/wiki/document_details.html @@ -11,14 +11,14 @@ {% block tabs-menu %} {% include "wiki/tabs/summary_view_item.html" %} {% include "wiki/tabs/wysiwyg_editor_item.html" %} - {% include "wiki/tabs/source_editor_item.html" %} + {% include "wiki/tabs/source_editor_item.html" %} {% include "wiki/tabs/history_view_item.html" %} {% endblock %} {% block tabs-content %} {% include "wiki/tabs/summary_view.html" %} {% include "wiki/tabs/wysiwyg_editor.html" %} - {% include "wiki/tabs/source_editor.html" %} + {% include "wiki/tabs/source_editor.html" %} {% include "wiki/tabs/history_view.html" %} {% endblock %} @@ -40,6 +40,7 @@ {% endblock %} {% block dialogs %} - {% include "wiki/save_dialog.html" %} - {% include "wiki/tag_dialog.html" %} + {% include "wiki/save_dialog.html" %} + {% include "wiki/revert_dialog.html" %} + {% include "wiki/tag_dialog.html" %} {% endblock %} diff --git a/apps/wiki/templates/wiki/revert_dialog.html b/apps/wiki/templates/wiki/revert_dialog.html new file mode 100644 index 00000000..7e5089f0 --- /dev/null +++ b/apps/wiki/templates/wiki/revert_dialog.html @@ -0,0 +1,42 @@ +{% load i18n %} +
+
+

{{ forms.text_revert.comment.label }}

+

+ {{ forms.text_revert.comment.help_text}} + +

+ {{forms.text_revert.comment }} + + + + {% if request.user.is_anonymous %} + + + + + + + + + +
{{ forms.text_revert.author_name.label }}:{{ forms.text_revert.author_name }} + {{ forms.text_revert.author_name.help_text }} +
{{ forms.text_revert.author_email.label }}:{{ forms.text_revert.author_email }} + {{ forms.text_revert.author_email.help_text }} +
+ {% endif %} + + + {% for f in forms.text_revert.hidden_fields %} + {{ f }} + {% endfor %} + +

+ +

+ + +

+
+
diff --git a/apps/wiki/urls.py b/apps/wiki/urls.py index a84330a4..2b4a65ae 100644 --- a/apps/wiki/urls.py +++ b/apps/wiki/urls.py @@ -38,6 +38,9 @@ urlpatterns = patterns('wiki.views', url(r'^(?P[^/]+)/text$', 'text', name="wiki_text"), + url(r'^(?P[^/]+)/revert$', + 'revert', name='wiki_revert'), + url(r'^(?P[^/]+)/publish$', 'publish', name="wiki_publish"), url(r'^(?P[^/]+)/publish/(?P\d+)$', 'publish', name="wiki_publish"), diff --git a/apps/wiki/views.py b/apps/wiki/views.py index 7d60341c..918eb91d 100644 --- a/apps/wiki/views.py +++ b/apps/wiki/views.py @@ -13,7 +13,7 @@ from wiki.helpers import (JSONResponse, JSONFormInvalid, JSONServerError, from django import http from wiki.models import getstorage, DocumentNotFound, normalize_name, split_name, join_name, Theme -from wiki.forms import DocumentTextSaveForm, DocumentTagForm, DocumentCreateForm, DocumentsUploadForm +from wiki.forms import DocumentTextSaveForm, DocumentTextRevertForm, DocumentTagForm, DocumentCreateForm, DocumentsUploadForm from datetime import datetime from django.utils.encoding import smart_unicode from django.utils.translation import ugettext_lazy as _ @@ -84,6 +84,7 @@ def editor(request, name, template_name='wiki/document_details.html'): 'document_meta': document.meta, 'forms': { "text_save": DocumentTextSaveForm(prefix="textsave"), + "text_revert": DocumentTextRevertForm(prefix="textrevert"), "add_tag": DocumentTagForm(prefix="addtag"), }, 'REDMINE_URL': settings.REDMINE_URL, @@ -260,18 +261,38 @@ def text(request, name): @require_POST def revert(request, name): storage = getstorage() - revision = request.POST['target_revision'] + form = DocumentTextRevertForm(request.POST, prefix="textrevert") + if form.is_valid(): + print 'valid' + revision = form.cleaned_data['revision'] + + comment = form.cleaned_data['comment'] + comment += "\n#revert to %s" % revision + + if request.user.is_authenticated(): + author_name = request.user + author_email = request.user.email + else: + author_name = form.cleaned_data['author_name'] + author_email = form.cleaned_data['author_email'] + author = "%s <%s>" % (author_name, author_email) + + before = storage.get(name).revision + logger.info("Reverting %s to %s", name, revision) + storage.revert(name, revision, comment=comment, author=author) + logger.info("Fetching %s", name) + document = storage.get(name) - try: - document = storage.revert(name, revision) return JSONResponse({ - 'text': document.plain_text if revision != document.revision else None, + 'text': document.plain_text if before != document.revision else None, 'meta': document.meta(), 'revision': document.revision, }) - except DocumentNotFound: - raise http.Http404 + else: + print 'invalid' + return JSONFormInvalid(form) + @never_cache def gallery(request, directory): diff --git a/redakcja/settings/compress.py b/redakcja/settings/compress.py index 2ce6ac91..8bade8d0 100644 --- a/redakcja/settings/compress.py +++ b/redakcja/settings/compress.py @@ -43,6 +43,7 @@ COMPRESS_JS = { # dialogs 'js/wiki/dialog_save.js', + 'js/wiki/dialog_revert.js', 'js/wiki/dialog_addtag.js', # views diff --git a/redakcja/static/css/dialogs.css b/redakcja/static/css/dialogs.css index 871d7236..1032b138 100644 --- a/redakcja/static/css/dialogs.css +++ b/redakcja/static/css/dialogs.css @@ -26,7 +26,7 @@ font-weight: bold; } -#save_dialog textarea { +#save_dialog textarea, #revert_dialog textarea { width: 90%; margin: 0.2em 4%; } \ No newline at end of file diff --git a/redakcja/static/js/wiki/dialog_revert.js b/redakcja/static/js/wiki/dialog_revert.js new file mode 100644 index 00000000..4d550f9d --- /dev/null +++ b/redakcja/static/js/wiki/dialog_revert.js @@ -0,0 +1,65 @@ +/* + * Dialog for reverting document on the server + * + */ +(function($) { + + function RevertDialog(element, options) { + this.ctx = $.wiki.exitContext(); + this.clearForm(); + + /* fill out hidden fields */ + this.$form = $('form', element); + + $("input[name='textrevert-revision']", this.$form).val(options.revision); + + $.wiki.cls.GenericDialog.call(this, element); + }; + + RevertDialog.prototype = new $.wiki.cls.GenericDialog(); + + RevertDialog.prototype.cancelAction = function() { + $.wiki.enterContext(this.ctx); + this.hide(); + }; + + RevertDialog.prototype.revertAction = function() { + var self = this; + + self.$elem.block({ + message: "Przywracanie...", + fadeIn: 0, + }); + $.wiki.blocking = self.$elem; + + try { + + CurrentDocument.revertToVersion({ + form: self.$form, + success: function(e, msg) { + self.$elem.block({ + message: msg, + timeout: 2000, + fadeOut: 0, + onUnblock: function() { + self.hide(); + $.wiki.enterContext(self.ctx); + } + }); + }, + 'failure': function(e, info) { + console.log("Failure", info); + self.reportErrors(info); + self.$elem.unblock(); + } + }); + + } catch(e) { + console.log('Exception:', e) + self.$elem.unblock(); + } + }; /* end of revert dialog */ + + /* make it global */ + $.wiki.cls.RevertDialog = RevertDialog; +})(jQuery); diff --git a/redakcja/static/js/wiki/dialog_save.js b/redakcja/static/js/wiki/dialog_save.js index aa9258d5..903c0e1c 100644 --- a/redakcja/static/js/wiki/dialog_save.js +++ b/redakcja/static/js/wiki/dialog_save.js @@ -11,7 +11,6 @@ /* fill out hidden fields */ this.$form = $('form', element); - $("input[name='textsave-id']", this.$form).val(CurrentDocument.id); $("input[name='textsave-parent_revision']", this.$form).val(CurrentDocument.revision); $.wiki.cls.GenericDialog.call(this, element); diff --git a/redakcja/static/js/wiki/view_history.js b/redakcja/static/js/wiki/view_history.js index d35a8da0..94a369b6 100644 --- a/redakcja/static/js/wiki/view_history.js +++ b/redakcja/static/js/wiki/view_history.js @@ -16,7 +16,7 @@ }); $('#doc-revert-button').click(function() { - self.revertDocumentToVersion(); + self.revertDialog(); }); $('#open-preview-button').click(function(event) { @@ -172,7 +172,8 @@ }); }; - HistoryPerspective.prototype.revertDocumentToVersion = function(){ + HistoryPerspective.prototype.revertDialog = function(){ + var self = this; var selected = $('#changes-list .entry.selected'); if (selected.length != 1) { @@ -181,7 +182,7 @@ } var version = parseInt($("*[data-stub-value='version']", selected[0]).text()); - this.doc.revertToVersion({'revision': version}); + $.wiki.showDialog('#revert_dialog', {revision: version}); }; $.wiki.HistoryPerspective = HistoryPerspective; diff --git a/redakcja/static/js/wiki/wikiapi.js b/redakcja/static/js/wiki/wikiapi.js index 97d18862..8da5929a 100644 --- a/redakcja/static/js/wiki/wikiapi.js +++ b/redakcja/static/js/wiki/wikiapi.js @@ -26,6 +26,11 @@ return base_path + path; } + if (vname == "ajax_document_revert") { + return base_path + "/" + arguments[1] + "/revert"; + } + + if (vname == "ajax_document_history") { return base_path + "/" + arguments[1] + "/history"; @@ -283,6 +288,45 @@ }); }; /* end of save() */ + WikiDocument.prototype.revertToVersion = function(params) { + var self = this; + params = $.extend({}, noops, params); + + if (params.revision >= this.revision) { + params.failure(self, 'Proszę wybrać rewizję starszą niż aktualna.'); + return; + } + + // Serialize form to dictionary + var data = {}; + $.each(params['form'].serializeArray(), function() { + data[this.name] = this.value; + }); + + $.ajax({ + url: reverse("ajax_document_revert", self.id), + type: "POST", + dataType: "json", + data: data, + success: function(data) { + if (data.text) { + self.text = data.text; + self.revision = data.revision; + self.gallery = data.gallery; + self.triggerDocumentChanged(); + + params.success(self, "Udało się przywrócić wersję :)"); + } + else { + params.failure(self, "Przywracana wersja identyczna z aktualną. Anulowano przywracanie."); + } + }, + error: function(xhr) { + params.failure(self, "Nie udało się przywrócić wersji - błąd serwera."); + } + }); + }; + WikiDocument.prototype.publish = function(params) { params = $.extend({}, noops, params); var self = this; -- 2.20.1