#453: revert document to a past revision
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Tue, 7 Dec 2010 13:03:52 +0000 (14:03 +0100)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Tue, 7 Dec 2010 13:03:52 +0000 (14:03 +0100)
12 files changed:
apps/wiki/forms.py
apps/wiki/models.py
apps/wiki/templates/wiki/document_details.html
apps/wiki/templates/wiki/revert_dialog.html [new file with mode: 0644]
apps/wiki/urls.py
apps/wiki/views.py
redakcja/settings/compress.py
redakcja/static/css/dialogs.css
redakcja/static/js/wiki/dialog_revert.js [new file with mode: 0644]
redakcja/static/js/wiki/dialog_save.js
redakcja/static/js/wiki/view_history.js
redakcja/static/js/wiki/wikiapi.js

index d5c0ed5..74934bd 100644 (file)
@@ -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."),
+    )
index ec9ded5..7cb20c6 100644 (file)
@@ -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:
index bdb2200..934a8ac 100644 (file)
 {% 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 (file)
index 0000000..7e5089f
--- /dev/null
@@ -0,0 +1,42 @@
+{% load i18n %}
+<div id="revert_dialog" class="dialog" data-ui-jsclass="RevertDialog">
+       <form method="POST" action="">
+       <p>{{ forms.text_revert.comment.label }}</p>
+       <p class="help_text">
+               {{ forms.text_revert.comment.help_text}}
+               <span data-ui-error-for="{{ forms.text_revert.comment.name }}"> </span>
+       </p>
+       {{forms.text_revert.comment }}
+
+
+
+       {% if request.user.is_anonymous %}
+    <table style='margin:0 4%;'>
+    <tr>
+        <td>{{ forms.text_revert.author_name.label }}:</td>
+        <td>{{ forms.text_revert.author_name }}
+        <span class="help_text">{{ forms.text_revert.author_name.help_text }}</span>
+        <span data-ui-error-for="{{ forms.text_revert.author_name.name }}"> </span></td>
+    </tr>
+    <tr>
+        <td>{{ forms.text_revert.author_email.label }}:</td>
+        <td>{{ forms.text_revert.author_email }}
+        <span class="help_text">{{ forms.text_revert.author_email.help_text }}</span>
+        <span data-ui-error-for="{{ forms.text_revert.author_email.name }}"> </span></td>
+    </tr>
+    </table>
+       {% endif %}
+
+
+       {% for f in forms.text_revert.hidden_fields %}
+               {{ f }}
+       {% endfor %}
+
+       <p data-ui-error-for="__all__"> </p>
+
+       <p class="action_area">
+               <button type="submit" class"ok" data-ui-action="revert">{% trans "Revert" %}</button>
+               <button type="button" class="cancel" data-ui-action="cancel">{% trans "Cancel" %}</button>
+       </p>
+       </form>
+</div>
index a84330a..2b4a65a 100644 (file)
@@ -38,6 +38,9 @@ urlpatterns = patterns('wiki.views',
     url(r'^(?P<name>[^/]+)/text$',
         'text', name="wiki_text"),
 
+    url(r'^(?P<name>[^/]+)/revert$',
+        'revert', name='wiki_revert'),
+
     url(r'^(?P<name>[^/]+)/publish$', 'publish', name="wiki_publish"),
     url(r'^(?P<name>[^/]+)/publish/(?P<version>\d+)$', 'publish', name="wiki_publish"),
 
index 7d60341..918eb91 100644 (file)
@@ -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):
index 2ce6ac9..8bade8d 100644 (file)
@@ -43,6 +43,7 @@ COMPRESS_JS = {
 
                 # dialogs
                 'js/wiki/dialog_save.js',
+                'js/wiki/dialog_revert.js',
                 'js/wiki/dialog_addtag.js',
 
                 # views
index 871d723..1032b13 100644 (file)
@@ -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 (file)
index 0000000..4d550f9
--- /dev/null
@@ -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);
index aa9258d..903c0e1 100644 (file)
@@ -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);
index d35a8da..94a369b 100644 (file)
@@ -16,7 +16,7 @@
                        });
 
                $('#doc-revert-button').click(function() {
-                   self.revertDocumentToVersion();
+                   self.revertDialog();
                });
 
                        $('#open-preview-button').click(function(event) {
         });
     };
 
-    HistoryPerspective.prototype.revertDocumentToVersion = function(){
+    HistoryPerspective.prototype.revertDialog = function(){
+        var self = this;
         var selected = $('#changes-list .entry.selected');
 
         if (selected.length != 1) {
         }
 
         var version = parseInt($("*[data-stub-value='version']", selected[0]).text());
-        this.doc.revertToVersion({'revision': version});
+        $.wiki.showDialog('#revert_dialog', {revision: version});
     };
 
     $.wiki.HistoryPerspective = HistoryPerspective;
index 97d1886..8da5929 100644 (file)
                        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";
         });
        }; /* 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;