Added history to document view
authorŁukasz Rekucki <lrekucki@gmail.com>
Tue, 9 Mar 2010 20:10:41 +0000 (21:10 +0100)
committerŁukasz Rekucki <lrekucki@gmail.com>
Tue, 9 Mar 2010 20:10:41 +0000 (21:10 +0100)
apps/wiki/models.py
apps/wiki/templates/wiki/document_details.html
apps/wiki/views.py
platforma/static/css/master.css
platforma/static/js/main.js
platforma/urls.py

index b4adba4..ecf7837 100644 (file)
@@ -22,6 +22,9 @@ class DocumentStorage(object):
 
     def all(self):
         return list(self.vstorage.all_pages())
 
     def all(self):
         return list(self.vstorage.all_pages())
+    
+    def history(self, title):
+        return list(self.vstorage.page_history(title))
 
     def _info(self, name):
         return self.vstorage.page_meta(name)
 
     def _info(self, name):
         return self.vstorage.page_meta(name)
@@ -41,6 +44,7 @@ class Document(object):
         except DocumentNotFound:
             return - 1
 
         except DocumentNotFound:
             return - 1
 
+    @property
     def plain_text(self):
         return re.sub(self.META_REGEX, '', self.text, 1)
 
     def plain_text(self):
         return re.sub(self.META_REGEX, '', self.text, 1)
 
index e7a3398..95a7c95 100644 (file)
                 Wersja: <span id="document-revision">{{ document.revision }}</span> <button style="margin-left: 6px" id="save-button">Zapisz</button></div>
             <h1><a href="{% url wiki.views.document_list %}">Platforma</a></h1>
             <ol id="tabs">
                 Wersja: <span id="document-revision">{{ document.revision }}</span> <button style="margin-left: 6px" id="save-button">Zapisz</button></div>
             <h1><a href="{% url wiki.views.document_list %}">Platforma</a></h1>
             <ol id="tabs">
-                <li id="simple-view-tab"><span id="document-name">{{ document.name }}</span></li>
-                <li id="source-view-tab" class="active">Kod źródłowy</li>
+                <li id="simple-view-tab" ui:related="simple-editor"><span id="document-name">{{ document.name }}</span></li>
+                <li id="source-view-tab" ui:related="source-editor">Kod źródłowy</li>
+                               <li id="history-view-tab" ui:related="history-viewer">Historia</li>
             </ol>
             <div style="clear: both"></div>
         </div>
         <div id="splitter">
             <div id="editor">
             </ol>
             <div style="clear: both"></div>
         </div>
         <div id="splitter">
             <div id="editor">
-                <div id="source-editor">
+                <div id="source-editor" class="editor">
                     {% toolbar %}
                     <textarea name="text" id="id_text">{{ document.plain_text }}</textarea>
                     <input type="hidden" name="name" value="{{ document.name }}" />
                     {% toolbar %}
                     <textarea name="text" id="id_text">{{ document.plain_text }}</textarea>
                     <input type="hidden" name="name" value="{{ document.name }}" />
@@ -49,7 +50,7 @@
                     <input type="hidden" name="comment" value="no comment" />
                     <input type="hidden" name="revision" value="{{ document.revision }}" />
                 </div>
                     <input type="hidden" name="comment" value="no comment" />
                     <input type="hidden" name="revision" value="{{ document.revision }}" />
                 </div>
-                <div id="simple-editor" style="display: none">
+                <div id="simple-editor" class="editor" style="display: none">
                     <div class="toolbar">
                         <button id="insert-theme-button">Wstaw motyw</button> <button id="insert-annotation-button">Wstaw przypis</button>
                         <div class="toolbar-end"> </div>
                     <div class="toolbar">
                         <button id="insert-theme-button">Wstaw motyw</button> <button id="insert-annotation-button">Wstaw przypis</button>
                         <div class="toolbar-end"> </div>
                     <div id="html-view" class="htmlview">
                     </div>
                 </div>
                     <div id="html-view" class="htmlview">
                     </div>
                 </div>
+                               
+                               <div id="history-viewer" class="editor" style="display: none">
+                                       <div class="toolbar"> </div>
+                                       <div id="history-view">
+                                               <p class="message-box" style="display:none;"></p>
+                                               <table>
+                                                       <thead>                 
+                                                               <th>&nbsp;</th>                                 
+                                                               <th>Numer</th>
+                                                               <th>Opis</th>
+                                                               <th>Użytkownik</th>
+                                                               <th>Data</th>
+                                                       </thead>
+                                                       <tbody id="changes-list">                                                                                                                               
+                                                       </tbody>
+                                               </table>
+                                               
+                                               <p><button type="button" id="make-diff-button">Porównaj</button></p>
+                                               <!-- <p><button type="button" id="more-history-button">Więcej</button></p> -->
+                                               <div id="diff-view">                                                                                                                                            
+                                       </div>                                                                  
+                               </div>                                                  
+                                                       
+                               </div>
             </div>
             <div class="vsplitbar"> </div>
             <div id="sidebar">
             </div>
             <div class="vsplitbar"> </div>
             <div id="sidebar">
index baf30a7..931cf78 100644 (file)
@@ -7,6 +7,17 @@ from django.utils import simplejson as json
 
 from wiki.models import storage, Document, DocumentNotFound
 from wiki.forms import DocumentForm
 
 from wiki.models import storage, Document, DocumentNotFound
 from wiki.forms import DocumentForm
+from datetime import datetime
+
+# import google_diff
+import difflib
+
+
+class DateTimeEncoder(json.JSONEncoder):
+     def default(self, obj):
+         if isinstance(obj, datetime):
+             return datetime.ctime(obj) + " " + (datetime.tzname(obj) or 'GMT')
+         return json.JSONEncoder.default(self, obj)
 
 def document_list(request, template_name = 'wiki/document_list.html'):
     return direct_to_template(request, template_name, extra_context = {
 
 def document_list(request, template_name = 'wiki/document_list.html'):
     return direct_to_template(request, template_name, extra_context = {
@@ -26,7 +37,7 @@ def document_detail(request, name, template_name = 'wiki/document_details.html')
         form = DocumentForm(request.POST, instance = document)
         if form.is_valid():
             document = form.save()
         form = DocumentForm(request.POST, instance = document)
         if form.is_valid():
             document = form.save()
-            return HttpResponse(json.dumps({'text': document.plain_text(), 'meta': document.meta(), 'revision': document.revision()}))
+            return HttpResponse(json.dumps({'text': document.plain_text, 'meta': document.meta(), 'revision': document.revision()}))
         else:
             return HttpResponse(json.dumps({'errors': form.errors}))
     else:
         else:
             return HttpResponse(json.dumps({'errors': form.errors}))
     else:
@@ -49,3 +60,17 @@ def document_gallery(request, directory):
         traceback.print_exc()
 
         raise Http404
         traceback.print_exc()
 
         raise Http404
+    
+def document_diff(request, name, revA, revB):
+    differ = difflib.HtmlDiff(wrapcolumn=60)
+     
+    docA = storage.get(name, int(revA))
+    docB = storage.get(name, int(revB))
+     
+    return HttpResponse(differ.make_table(
+                                docA.plain_text.splitlines(),
+                                docB.plain_text.splitlines() ) )                                           
+    
+    
+def document_history(reuqest, name):
+    return HttpResponse( json.dumps(storage.history(name), cls=DateTimeEncoder), mimetype='application/json')
index 5884d8a..d2768be 100644 (file)
@@ -23,7 +23,7 @@ body {
     cursor: e-resize;
 }
 
     cursor: e-resize;
 }
 
-#source-editor, #simple-editor {
+#source-editor, #simple-editor, #history-viewer {
     position: absolute;
     top: 25px;
     bottom: 0;
     position: absolute;
     top: 25px;
     bottom: 0;
@@ -32,7 +32,7 @@ body {
     overflow: hidden;
 }
 
     overflow: hidden;
 }
 
-#html-view {
+#html-view, #history-view {
     overflow: auto;
     position: absolute;
     top: 27px;
     overflow: auto;
     position: absolute;
     top: 27px;
@@ -279,4 +279,38 @@ p { margin: 0;}
        
        width: 40px;            
 } 
        
        width: 40px;            
 } 
\ No newline at end of file
+/* DIFFS */
+ #history-view table {
+       width: 75%;
+       margin: 1em auto;
+       max-height: 400px;
+       overflow-y: auto;
+ }
+ #diff-view {
+       border: 2px solid black;
+       background: #fafafa;
+       margin: 1em auto;
+       width: 90%;     
+ }
+ td.diff_header {
+       display: none;
+ }
+ td.diff_next {
+       display: none;
+ }
+
+ .diff_sub {
+       background-color: #ef4040;
+ }
+ .diff_chg {
+       background-color: #efef40;
+ }
+ .diff_add {
+       background-color: #40ef40;
+ }
index 3c1a3c0..a92dfe4 100644 (file)
@@ -676,6 +676,78 @@ function html(element) {
     });
 }
 
     });
 }
 
+/*
+ * History
+ */
+
+function refreshHistory(callback){
+       $.blockUI({
+               message: 'Odświeżanie historii...'
+       });
+       
+       $.ajax({
+               url: document.location.href + '/history',
+        dataType: 'json',
+               error: function() {
+                       $('#history-view .message-box').html('Nie udało się odświeżyć historii').show();
+                       $.unblockUI();          
+               },
+               success: function(data) {
+                       $('#history-view .message-box').hide();
+                       var changes_list = $('#changes-list');
+                       changes_list.html('');
+                       
+                       $.each(data, function() {
+                               var val = this[0];
+                               changes_list.append('<tr>'
+                                       +'<td><input type="radio" name="rev_from" value="'+val+'">'
+                                               + '<input type="radio" name="rev_to" value="'+val+'">'
+                                       +'<td>'+ this[0]+'</td>'
+                                       +'<td>'+ this[3]+'</td>'
+                                       +'<td>'+ this[2]+'</td>'
+                                       +'<td>'+ this[1]+'</td></tr>')                  
+                       });                                                     
+                       $.unblockUI();  
+                       callback();
+               }
+       });
+};
+
+function historyDiff(callback) {
+       var changelist = $('#changes-list');
+       var rev_a = $("input[name='rev_from']:checked", changelist);
+       var rev_b = $("input[name='rev_to']:checked", changelist);
+       
+       if (rev_a.length != 1 || rev_b.length != 1) {
+               window.alert("Musisz zaznaczyć dwie wersje do porównania.");
+               return false;
+       }
+       
+       if (rev_a.val() == rev_b.val()) {
+               window.alert("Musisz zaznaczyć dwie różne wersje do porównania.");
+               return false;
+       }
+                       
+       $.blockUI({
+               message: 'Wczytywanie porównania...'
+       });
+       
+       $.ajax({
+               url: document.location.href + '/diff/'+rev_a.val() + '/'+ rev_b.val(),
+        dataType: 'html',
+               error: function() {
+                       $.unblockUI();
+                       window.alert('Nie udało się wykonać porównania :(.')                                        
+               },
+               success: function(data) {
+                       $.unblockUI();                  
+                       var diffview = $('#diff-view');                 
+                       diffview.html(data);
+                       diffview.show();                                                        
+               }
+       });
+}
+       
 
 $(function() {
     gallery('#sidebar', $('#document-meta .gallery').html());
 
 $(function() {
     gallery('#sidebar', $('#document-meta .gallery').html());
@@ -690,7 +762,7 @@ $(function() {
         },
         iframeClass: 'xml-iframe',
         textWrapping: true,
         },
         iframeClass: 'xml-iframe',
         textWrapping: true,
-               lineNumbers: true,
+               /* lineNumbers: true, */
         tabMode: 'spaces',
         indentUnit: 0,
         initCallback: function(editor) {
         tabMode: 'spaces',
         indentUnit: 0,
         initCallback: function(editor) {
@@ -747,29 +819,31 @@ $(function() {
             $('#save-cancel').click(function() {
                 $.unblockUI();
             });
             $('#save-cancel').click(function() {
                 $.unblockUI();
             });
-            
-            function changeTab(callback) {
-                if ($('#simple-view-tab').hasClass('active')) {
-                    return;
-                }
-                $('#simple-view-tab').addClass('active');
-                $('#source-view-tab').removeClass('active');
-                $('#source-editor').hide();
-                $('#simple-editor').show();
+                               
+            var tabs = $('ol#tabs li');
+                       
+                       tabs.click(function(event, callback) {
+                               tabs.removeClass('active');
+                               $('.editor').hide();
+                               $(this).addClass('active');
+                               $('#' + $(this).attr('ui:related')).show();                             
+                               $(this).trigger('wl:tabload', callback);                                                                
+                       });     
+                       
+                       
+            $('#simple-view-tab').bind('wl:tabload', function(event, callback) {
                 transform(editor, callback);
                 transform(editor, callback);
-            }        
-            $('#simple-view-tab').click(function() { changeTab(); });
-            
-            $('#source-view-tab').click(function() { 
-                if ($(this).hasClass('active')) {
-                    return;
-                }
-                $(this).addClass('active');
-                $('#simple-view-tab').removeClass('active');
-                $('#simple-editor').hide();
-                $('#source-editor').show();
-                reverseTransform(editor);    
             });
             });
+                       
+                       $('#source-view-tab').bind('wl:tabload', function(event, callback) {
+                reverseTransform(editor, callback);
+            });                                        
+                       
+                       $('#history-view-tab').bind('wl:tabload', function(event, callback) {
+                               refreshHistory(callback);                                                               
+                       }); 
+                       
+                       $('#make-diff-button').click(historyDiff);
 
             $('#source-editor .toolbar button').click(function(event) {
                 event.preventDefault();
 
             $('#source-editor .toolbar button').click(function(event) {
                 event.preventDefault();
@@ -777,7 +851,7 @@ $(function() {
                 scriptletCenter.scriptlets[$(this).attr('ui:action')](editor, params);
             });                        
 
                 scriptletCenter.scriptlets[$(this).attr('ui:action')](editor, params);
             });                        
 
-            $('.toolbar select').change(function() {
+            $('.toolbar select').change(function(event) {
                 var slug = $(this).val();
 
                 $('.toolbar-buttons-container').hide().filter('[data-group=' + slug + ']').show();
                 var slug = $(this).val();
 
                 $('.toolbar-buttons-container').hide().filter('[data-group=' + slug + ']').show();
@@ -787,7 +861,12 @@ $(function() {
             $('.toolbar-buttons-container').hide();
             $('.toolbar select').change();
 
             $('.toolbar-buttons-container').hide();
             $('.toolbar select').change();
 
-            changeTab(function() { $('#loading-overlay').fadeOut() }, function() { $('#loading-overlay').fadeOut() }, true)
+                       
+                       $('#simple-view-tab').trigger('click', 
+                               function() { 
+                                       $('#loading-overlay').fadeOut();
+                                       return false; 
+                               });            
         }
     });
     
         }
     });
     
index bd296a9..9b4b1a2 100644 (file)
@@ -9,6 +9,8 @@ admin.autodiscover()
 urlpatterns = patterns('',
     url(r'^$', 'wiki.views.document_list'),
     url(r'^gallery/(?P<directory>[^/]+)$', 'wiki.views.document_gallery'),
 urlpatterns = patterns('',
     url(r'^$', 'wiki.views.document_list'),
     url(r'^gallery/(?P<directory>[^/]+)$', 'wiki.views.document_gallery'),
+    url(r'^(?P<name>[^/]+)/history$', 'wiki.views.document_history'),
+    url(r'^(?P<name>[^/]+)/diff/(?P<revA>\d+)/(?P<revB>\d+)$', 'wiki.views.document_diff'),
 
     # Auth
     url(r'^accounts/login/$', 'django_cas.views.login', name = 'login'),
 
     # Auth
     url(r'^accounts/login/$', 'django_cas.views.login', name = 'login'),