Merge branch 'master' of git@stigma.nowoczesnapolska.org.pl:platforma
authorzuber <marek@stepniowski.com>
Wed, 2 Sep 2009 10:46:45 +0000 (12:46 +0200)
committerzuber <marek@stepniowski.com>
Wed, 2 Sep 2009 10:46:45 +0000 (12:46 +0200)
Conflicts:
apps/explorer/views.py

apps/explorer/admin.py [new file with mode: 0644]
apps/explorer/forms.py
apps/explorer/views.py
apps/toolbar/admin.py
project/static/css/master.css
project/static/js/editor.js
project/templates/explorer/editor.html
project/templates/explorer/panels/parse_error.html [new file with mode: 0644]

diff --git a/apps/explorer/admin.py b/apps/explorer/admin.py
new file mode 100644 (file)
index 0000000..a03f393
--- /dev/null
@@ -0,0 +1,8 @@
+from django.contrib import admin
+from django.utils.translation import ugettext_lazy as _
+
+from explorer import models
+
+admin.site.register(models.Book)
+admin.site.register(models.PullRequest)
+
index af1534e..d79bc74 100644 (file)
@@ -2,10 +2,45 @@ from django import forms
 
 from lxml import etree
 from librarian import dcparser
-
+import django.utils
 
 from explorer import models
 
+class PersonField(forms.CharField):
+    def clean(self, value):
+        try:
+            return dcparser.Person.from_text( super(PersonField, self).clean(value) )
+        except ValueError, e:
+            raise django.utils.ValidationError(e.message)        
+
+class PersonListField(forms.Field):
+
+    def __init__(self, *args, **kwargs):
+        super(PersonListField, self).__init__(*args, **kwargs)
+    
+    def _get_initial(self):
+        return u'\n'.join( ( unicode(person) for person in self._initial) )
+
+    def _set_initial(self, value):
+        if value is None:
+            self._initial = None
+        elif isinstance(value, list):
+            self._initial = [ e if isinstance(e, dcparser.Person) \
+                else dcparser.Person.from_text(e) for e in value ]
+        elif isinstance(value, basestring):
+            self._initial = [dcparser.Person.from_text(token) for token in value.split('\n') ]
+        else:
+            raise ValueError("Invalid value. Must be a list of dcparser.Person or string")    
+
+    initial = property(_get_initial, _set_initial)
+
+    def clean(self, value):
+        super(PersonListField, self).clean(value)
+        people = value.split('\n')
+        try:
+            return [dcparser.Person.from_text(person) for person in people]
+        except ValueError, e:
+            raise django.utils.ValidationError(e.message)        
 
 class BookForm(forms.Form):
     content = forms.CharField(widget=forms.Textarea)
@@ -23,17 +58,18 @@ class ImageFoldersForm(forms.Form):
 
 
 class DublinCoreForm(forms.Form):
-    wiki_url = forms.URLField(verify_exists=False)
-    author = forms.CharField()
+    about = forms.URLField(verify_exists=False)
+    author = PersonField()
     title = forms.CharField()
     epoch = forms.CharField()
     kind = forms.CharField()
     genre = forms.CharField()
     created_at = forms.DateField()
     released_to_public_domain_at = forms.DateField()
-    translator = forms.CharField(required=False)
-    technical_editor = forms.CharField(required=False)
-    publisher = forms.CharField()
+    editors = PersonListField(widget=forms.Textarea, required=False)
+    translator = PersonField(required=False)
+    technical_editor = PersonField(required=False)
+    publisher = PersonField()
     source_name = forms.CharField(widget=forms.Textarea)
     source_url = forms.URLField(verify_exists=False)
     url = forms.URLField(verify_exists=False)
@@ -41,16 +77,14 @@ class DublinCoreForm(forms.Form):
     license = forms.CharField(required=False)
     license_description = forms.CharField(widget=forms.Textarea, required=False)
     
-    commit_message = forms.CharField(required=False)
+    commit_message = forms.CharField(required=False, widget=forms.HiddenInput)
     
     def __init__(self, *args, **kwargs):
         text = None
-        if 'text' in kwargs:
-            text = kwargs.pop('text')
+        info = kwargs.pop('info', None)
         
         super(DublinCoreForm, self).__init__(*args, **kwargs)
         
-        if text is not None:
-            book_info = dcparser.BookInfo.from_string(text)
-            for name, value in book_info.to_dict().items():
-                self.fields[name].initial = value
\ No newline at end of file
+        if isinstance(info, dcparser.BookInfo):
+            for name, value in info.to_dict().items():
+                self.fields[name].initial = value
index 7121e35..79160f8 100644 (file)
@@ -1,7 +1,6 @@
 import urllib2
 import hg
-from lxml import etree
-from librarian import html,dcparser
+from librarian import html, parser, dcparser, ParseError, ValidationError
 
 from django.conf import settings
 from django.contrib.auth.decorators import login_required, permission_required
@@ -84,21 +83,28 @@ def file_upload(request, repo):
 @with_repo
 def file_xml(request, repo, path):
     if request.method == 'POST':
+        errors = None
         form = forms.BookForm(request.POST)
         if form.is_valid():
             print 'Saving whole text.', request.user.username
             def save_action():
                 print 'In branch: ' + repo.repo[None].branch()
                 repo._add_file(path, form.cleaned_data['content'])                
-                repo._commit(message=(form.cleaned_data['commit_message'] or 'Lokalny zapis platformy.'), user=request.user.username)
+                repo._commit(message=(form.cleaned_data['commit_message'] or 'Lokalny zapis platformy.'),\
+                     user=request.user.username)
+            try:
+                # wczytaj dokument z ciągu znaków -> weryfikacja
+                document = parser.WLDocument.from_string(form.cleaned_data['content'])
 
-            print repo.in_branch(save_action, models.user_branch(request.user) );
-            result = "ok"
-        else:
-            result = "error"
+                #  save to user's branch
+                repo.in_branch(save_action, models.user_branch(request.user) );
+            except (ParseError, ValidationError), e:
+                errors = [e.message]              
 
-        errors = dict( (field[0], field[1].as_text()) for field in form.errors.iteritems() )
-        return HttpResponse( json.dumps({'result': result, 'errors': errors}) );
+        if not errors:
+            errors = dict( (field[0], field[1].as_text()) for field in form.errors.iteritems() )
+
+        return HttpResponse(json.dumps({'result': errors and 'error' or 'ok', 'errors': errors}));
 
     form = forms.BookForm()
     data = repo.get_file(path, models.user_branch(request.user))
@@ -108,38 +114,49 @@ def file_xml(request, repo, path):
 @ajax_login_required
 @with_repo
 def file_dc(request, path, repo):
+    errors = None
+
     if request.method == 'POST':
         form = forms.DublinCoreForm(request.POST)
         
         if form.is_valid():
             def save_action():
                 file_contents = repo._get_file(path)
-                doc = etree.fromstring(file_contents)
-
-                book_info = dcparser.BookInfo()
-                for name, value in form.cleaned_data.items():
-                    if value is not None and value != '':
-                        setattr(book_info, name, value)
-                rdf = etree.XML(book_info.to_xml())
-
-                old_rdf = doc.getroottree().find('//{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF')
-                old_rdf.getparent().remove(old_rdf)
-                doc.insert(0, rdf)
-                repo._add_file(path, etree.tostring(doc, pretty_print=True, encoding=unicode))
-                repo._commit(message=(form.cleaned_data['commit_message'] or 'Lokalny zapis platformy.'), user=request.user.username)
 
-            repo.in_branch(save_action, models.user_branch(request.user) )
-
-            result = "ok"
-        else:
-            result = "error" 
-
-        errors = dict( (field[0], field[1].as_text()) for field in form.errors.iteritems() )
-        return HttpResponse( json.dumps({'result': result, 'errors': errors}) );
+                # wczytaj dokument z repozytorium
+                document = parser.WLDocument.from_string(file_contents)                    
+                document.book_info.update(form.cleaned_data)
+                
+                print "SAVING DC"
+
+                # zapisz
+                repo._add_file(path, document.serialize())
+                repo._commit( \
+                    message=(form.cleaned_data['commit_message'] or 'Lokalny zapis platformy.'), \
+                    user=request.user.username )
+                
+            try:
+                repo.in_branch(save_action, models.user_branch(request.user) )
+            except (ParseError, ValidationError), e:
+                errors = [e.message]
+
+        if errors is None:
+            errors = ["Pole '%s': %s\n" % (field[0], field[1].as_text()) for field in form.errors.iteritems()]
+
+        return HttpResponse( json.dumps({'result': errors and 'error' or 'ok', 'errors': errors}) );
     
-    fulltext = repo.get_file(path, models.user_branch(request.user))
-    form = forms.DublinCoreForm(text=fulltext)       
-    return HttpResponse( json.dumps({'result': 'ok', 'content': fulltext}) ) 
+    # this is unused currently, but may come in handy 
+    content = []
+    
+    try:
+        fulltext = repo.get_file(path, models.user_branch(request.user))
+        bookinfo = dcparser.BookInfo.from_string(fulltext)
+        content = bookinfo.to_dict()
+    except (ParseError, ValidationError), e:
+        errors = [e.message]
+
+    return HttpResponse( json.dumps({'result': errors and 'error' or 'ok', 
+        'errors': errors, 'content': content }) ) 
 
 # Display the main editor view
 
@@ -176,24 +193,34 @@ def gallery_panel(request, path):
 @with_repo
 def htmleditor_panel(request, path, repo):
     user_branch = models.user_branch(request.user)
-    return direct_to_template(request, 'explorer/panels/htmleditor.html', extra_context={
-        'fpath': path,
-        'html': html.transform(repo.get_file(path, user_branch), is_file=False),
-    })
+    try:
+        return direct_to_template(request, 'explorer/panels/htmleditor.html', extra_context={
+            'fpath': path,
+            'html': html.transform(repo.get_file(path, user_branch), is_file=False),
+        })
+    except (ParseError, ValidationError), e:
+        return direct_to_template(request, 'explorer/panels/parse_error.html', extra_context={
+            'fpath': path, 'exception_type': type(e).__name__, 'exception': e, 'panel_name': 'Edytor HTML'}) 
 
 @ajax_login_required
 @with_repo
 def dceditor_panel(request, path, repo):
     user_branch = models.user_branch(request.user)
-    text = repo.get_file(path, user_branch)
-    form = forms.DublinCoreForm(text=text)       
-
-    return direct_to_template(request, 'explorer/panels/dceditor.html', extra_context={
-        'fpath': path,
-        'form': form,
-    })
 
+    try:
+        doc_text = repo.get_file(path, user_branch)
+
+        document = parser.WLDocument.from_string(doc_text)
+        form = forms.DublinCoreForm(info=document.book_info)       
+
+        return direct_to_template(request, 'explorer/panels/dceditor.html', extra_context={
+            'fpath': path,
+            'form': form,
+        })
+    except (ParseError, ValidationError), e:
+        return direct_to_template(request, 'explorer/panels/parse_error.html', extra_context={
+            'fpath': path, 'exception_type': type(e).__name__, 'exception': e, 
+            'panel_name': 'Edytor DublinCore'}) 
 
 # =================
 # = Utility views =
index a03f393..2c8cd28 100644 (file)
@@ -1,8 +1,25 @@
 from django.contrib import admin
 from django.utils.translation import ugettext_lazy as _
 
-from explorer import models
+from toolbar import models
 
-admin.site.register(models.Book)
-admin.site.register(models.PullRequest)
+
+class ButtonGroupAdmin(admin.ModelAdmin):
+    list_display = ('name', 'slug', 'position',)
+    search_fields = ('name', 'slug',)
+    prepopulated_fields = {'slug': ('name',)}
+    list_editable = ('position',)
+
+admin.site.register(models.ButtonGroup, ButtonGroupAdmin)
+
+
+class ButtonAdmin(admin.ModelAdmin):
+    list_display = ('label', 'slug', 'tag', 'key', 'position',)
+    list_filter = ('group',)
+    search_fields = ('label', 'slug', 'tag', 'key',)
+    prepopulated_fields = {'slug': ('label',)}
+    filter_horizontal = ('group',)
+    list_editable = ('position',)
+
+admin.site.register(models.Button, ButtonAdmin)
 
index 040549e..7e39942 100644 (file)
@@ -353,12 +353,13 @@ div.isection p {
 #message-box {
     position: fixed;
     top: 2px;
-    left: 40%;   
+    left: 35%;
+    width: 33%;  
 }
 
 .msg-error, .msg-success, .msg-warning {
     overflow: hidden;
-    padding: 0.1em 0.2em;
+    padding: 0.1em 0.5em;
     text-align: center;
     border: 1px solid;
     -moz-border-radius: 8px;
@@ -367,13 +368,19 @@ div.isection p {
     line-height: 11pt;
     display: none;
 
-    width: 200px;
+    width: 100%;
 }
 
+#message-box * p {
+    padding: 0em;
+    margin: 0.1em;
+}
 
 .msg-error {
     background-color: red;
     border-color: red;
+    color: white;
+    font-weight: bold;
 }
 
 .msg-success {
@@ -416,4 +423,4 @@ div.isection p {
      position: relative;
      text-align: center;
      background-color: #CCC;
- }
\ No newline at end of file
+ }
index c12182f..585f3ce 100644 (file)
@@ -121,7 +121,7 @@ Editor.prototype.setupUI = function() {
        $('#panels > *.panel-wrap').each(function() {
                var panelWrap = $(this);
                $.log('wrap: ', panelWrap);
-               panel = new Panel(panelWrap);
+               var panel = new Panel(panelWrap);
                panelWrap.data('ctrl', panel); // attach controllers to wraps
         panel.load($('.panel-toolbar select', panelWrap).val());
         
@@ -130,6 +130,9 @@ Editor.prototype.setupUI = function() {
             panelWrap.data('ctrl').load(url);
             self.savePanelOptions();
         });
+
+        $('.panel-toolbar button.refresh-button', panelWrap).click(
+            function() { panel.refresh(); } );            
     });
 
        $(document).bind('panel:contentChanged', function() { self.onContentChanged.apply(self, arguments) });
@@ -222,20 +225,21 @@ Editor.prototype.saveToBranch = function(msg)
        }
 
        saveInfo = changed_panel.data('ctrl').saveInfo();
-        var postData = ''
-        if(saveInfo.postData instanceof Object)
-            postData = $.param(saveInfo.postData);
-        else
-            postData = saveInfo.postData;
+    var postData = ''
+    
+    if(saveInfo.postData instanceof Object)
+        postData = $.param(saveInfo.postData);
+    else
+        postData = saveInfo.postData;
 
-        postData += '&' + $.param({'commit_message': msg})
+    postData += '&' + $.param({'commit_message': msg})
 
        $.ajax({
                url: saveInfo.url,
                dataType: 'json',
                success: function(data, textStatus) {
                        if (data.result != 'ok')
-                               $.log('save errors: ', data.errors)
+                               self.showPopup('save-error', data.errors[0]);
                        else {
                                self.refreshPanels(changed_panel);
                 $('#toolbar-button-save').attr('disabled', 'disabled');
@@ -309,30 +313,37 @@ Editor.prototype.sendPullRequest = function () {
        }); */
 }
 
-Editor.prototype.showPopup = function(name) 
+Editor.prototype.showPopup = function(name, text
 {
     var self = this;
-    self.popupQueue.push(name)
+    self.popupQueue.push( [name, text] )
 
     if( self.popupQueue.length > 1) 
         return;
 
-    $('#message-box > #' + name).fadeIn();
+    var box = $('#message-box > #' + name);
+    $('*.data', box).html(text);
+    box.fadeIn();
  
-   self._nextPopup = function() {
+    self._nextPopup = function() {
         var elem = self.popupQueue.pop()
         if(elem) {
-            elem = $('#message-box > #' + elem);
-            elem.fadeOut(300, function() {
-                if( self.popupQueue.length > 0) 
-                    $('#message-box > #' + self.popupQueue[0]).fadeIn();
-                    setTimeout(self._nextPopup, 1700);
-            });
+            var box = $('#message-box > #' + elem[0]);
 
+            box.fadeOut(300, function() {
+                $('*.data', box).html();
+    
+                if( self.popupQueue.length > 0) {
+                    box = $('#message-box > #' + self.popupQueue[0][0]);
+                    $('*.data', box).html(self.popupQueue[0][1]);
+                    box.fadeIn();
+                    setTimeout(self._nextPopup, 5000);
+                }
+            });
         }
     }
 
-    setTimeout(self._nextPopup, 2000);
+    setTimeout(self._nextPopup, 5000);
 }
 
 
index 75e62c9..68c8c7f 100644 (file)
@@ -19,9 +19,9 @@
 {% endblock %}
 
 {% block message-box %}
-    <div class="msg-success" id="save-successful">Zapisano :)</div>
-    <div class="msg-error" id="save-error">Błąd przy zapisie. <span class="errors" /></div>
-    <div class="msg-warning" id="not-implemented">Tej funkcji jeszcze nie ma :(</div>
+    <div class="msg-success" id="save-successful"><p>Zapisano :)</p><p class="data" /></div>
+    <div class="msg-error" id="save-error"><p>Błąd przy zapisie.</p> <p class="data" /></div>
+    <div class="msg-warning" id="not-implemented"><p>Tej funkcji jeszcze nie ma :(</p><p class="data" /></div>
 {% endblock %}
 
 {% block maincontent %}
@@ -36,6 +36,7 @@
                         <option value="{% url gallery_panel hash %}" name="gallery">Galeria skanów</option>
                         <option value="{% url dceditor_panel hash %}" name="dceditor">Edytor DublinCore</option>
                     </select>
+                    <button type="button" class="refresh-button">Odśwież</button>
                     <strong class="change-notification" style="display: none">Widok nieaktualny!</strong>
                </div>
                <div id="panel-{{n}}-content" class="panel-content"></div>
diff --git a/project/templates/explorer/panels/parse_error.html b/project/templates/explorer/panels/parse_error.html
new file mode 100644 (file)
index 0000000..ac28fcb
--- /dev/null
@@ -0,0 +1,14 @@
+<h2>Podczas otwierania panelu "{{ panel_name }}" wystąpił przetwarzania pliku źródłowego:</h2>
+<p><b>{{ exception_type }}</b></p>
+<p>{{ exception.message }}</p>
+
+<script type="text/javascript" charset="utf-8">
+panel_hooks = {
+    refresh: function() {
+        return true; // error page is static
+    },
+    dirty: function() {
+        return true; // and it doesn't get dirty 
+    }
+};
+</script>