image publishing, some tag changes
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Wed, 21 Dec 2011 11:37:50 +0000 (12:37 +0100)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Wed, 21 Dec 2011 11:37:50 +0000 (12:37 +0100)
12 files changed:
apps/catalogue/models/__init__.py
apps/catalogue/models/book.py
apps/catalogue/models/image.py
apps/catalogue/models/listeners.py
apps/catalogue/templates/catalogue/image_detail.html
apps/catalogue/templatetags/book_list.py
apps/catalogue/urls.py
apps/catalogue/views.py
lib/librarian
redakcja/static/js/wiki_img/view_editor_motifs.js
redakcja/static/js/wiki_img/view_editor_objects.js
redakcja/static/js/wiki_img/wikiapi.js

index 82e1c11..08cedd0 100755 (executable)
@@ -5,7 +5,8 @@
 #
 from catalogue.models.chunk import Chunk
 from catalogue.models.image import Image
-from catalogue.models.publish_log import BookPublishRecord, ChunkPublishRecord
+from catalogue.models.publish_log import (BookPublishRecord,
+    ChunkPublishRecord, ImagePublishRecord)
 from catalogue.models.book import Book
 from catalogue.models.listeners import *
 
index 46d11e8..90327b2 100755 (executable)
@@ -9,10 +9,8 @@ from django.template.loader import render_to_string
 from django.utils.translation import ugettext_lazy as _
 from slughifi import slughifi
 
-import apiclient
 from catalogue.helpers import cached_in_field
 from catalogue.models import BookPublishRecord, ChunkPublishRecord
-from catalogue.signals import post_publish
 from catalogue.tasks import refresh_instance, book_content_updated
 from catalogue.xml_tools import compile_text, split_xml
 
@@ -350,6 +348,9 @@ class Book(models.Model):
         """
             Publishes a book on behalf of a (local) user.
         """
+        import apiclient
+        from catalogue.signals import post_publish
+
         self.assert_publishable()
         changes = self.get_current_changes(publishable=True)
         book_xml = self.materialize(changes=changes)
index c5d6bf0..90a6e02 100755 (executable)
@@ -4,6 +4,7 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django.conf import settings
+from django.contrib.sites.models import Site
 from django.db import models
 from django.template.loader import render_to_string
 from django.utils.translation import ugettext_lazy as _
@@ -44,9 +45,46 @@ class Image(dvcs_models.Document):
     def get_absolute_url(self):
         return ("catalogue_image", [self.slug])
 
+    def correct_about(self):
+        return "http://%s%s" % (
+            Site.objects.get_current().domain,
+            self.get_absolute_url()
+        )
+
     # State & cache
     # =============
 
+    def last_published(self):
+        try:
+            return self.publish_log.all()[0].timestamp
+        except IndexError:
+            return None
+
+    def assert_publishable(self):
+        from librarian.picture import WLPicture
+        from librarian import NoDublinCore, ParseError, ValidationError
+
+        class SelfImageStore(object):
+            def path(self_, slug, mime_type):
+                """Returns own file object. Ignores slug ad mime_type."""
+                return open(self.image.path)
+
+        picture_xml = self.publishable().materialize()
+
+        try:
+            picture = WLPicture.from_string(picture_xml.encode('utf-8'),
+                    image_store=SelfImageStore)
+        except ParseError, e:
+            raise AssertionError(_('Invalid XML') + ': ' + str(e))
+        except NoDublinCore:
+            raise AssertionError(_('No Dublin Core found.'))
+        except ValidationError, e:
+            raise AssertionError(_('Invalid Dublin Core') + ': ' + str(e))
+
+        valid_about = self.correct_about()
+        assert (picture.picture_info.about == valid_about,
+                _("rdf:about is not") + " " + valid_about)
+
     def accessible(self, request):
         return self.public or request.user.is_authenticated()
 
@@ -54,7 +92,7 @@ class Image(dvcs_models.Document):
         change = self.publishable()
         if not change:
             return False
-        return change.publish_log.exists()
+        return not change.publish_log.exists()
     new_publishable = cached_in_field('_new_publishable')(is_new_publishable)
 
     def is_published(self):
@@ -93,3 +131,25 @@ class Image(dvcs_models.Document):
         """This should be done offline."""
         self.changed
         self.short_html
+
+
+    # Publishing
+    # ==========
+
+    def publish(self, user):
+        """Publishes the picture on behalf of a (local) user."""
+        from base64 import b64encode
+        import apiclient
+        from catalogue.signals import post_publish
+
+        self.assert_publishable()
+        change = self.publishable()
+        picture_xml = change.materialize()
+        picture_data = open(self.image.path).read()
+        apiclient.api_call(user, "pictures/", {
+                "picture_xml": picture_xml,
+                "picture_image_data": b64encode(picture_data),
+            })
+        # record the publish
+        log = self.publish_log.create(user=user, change=change)
+        post_publish.send(sender=log)
index 4e76b0d..f98fba4 100755 (executable)
@@ -5,7 +5,8 @@
 #
 from django.contrib.auth.models import User
 from django.db import models
-from catalogue.models import Book, Chunk, Image
+from catalogue.models import (Book, Chunk, Image, BookPublishRecord,
+        ImagePublishRecord)
 from catalogue.signals import post_publish
 from dvcs.signals import post_publishable
 
@@ -39,14 +40,13 @@ models.signals.post_save.connect(user_changed, sender=User)
 
 
 def publish_listener(sender, *args, **kwargs):
-    sender.touch()
-    for c in sender:
-        c.touch()
-post_publish.connect(publish_listener, sender=Book)
-
-def publish_listener(sender, *args, **kwargs):
-    sender.touch()
-post_publish.connect(publish_listener, sender=Image)
+    if isinstance(sender, BookPublishRecord):
+        sender.book.touch()
+        for c in sender.book:
+            c.touch()
+    elif isinstance(sender, ImagePublishRecord):
+        sender.image.touch()
+post_publish.connect(publish_listener)
 
 
 def chunk_publishable_listener(sender, *args, **kwargs):
index fea775b..8049b55 100755 (executable)
 {% if editable %}</form>{% endif %}
 
 
+
+<div class='section'>
+    <h2>{% trans "Editor" %}</h2>
+
+    <p><a href="{% url wiki_img_editor object.slug %}">{% trans "Proceed to the editor." %}</a></p>
+</div>
+
+
+
+<div class='section'>
+
+
+<h2>{% trans "Publication" %}</h2>
+
+<p>{% trans "Last published" %}: 
+    {% if object.last_published %}
+        {{ object.last_published }}
+    {% else %}
+        &mdash;
+    {% endif %}
+</p>
+
+{% if publishable %}
+    {% if user.is_authenticated %}
+        <!--
+        Angel photos:
+        Angels in Ely Cathedral (http://www.flickr.com/photos/21804434@N02/4483220595/) /
+        mira66 (http://www.flickr.com/photos/21804434@N02/) /
+        CC BY 2.0 (http://creativecommons.org/licenses/by/2.0/)
+        -->
+        <form method="POST" action="{% url catalogue_publish_image object.slug %}">{% csrf_token %}
+            <!--img src="{{ STATIC_URL }}img/angel-left.png" style="vertical-align: middle" /-->
+            <button id="publish-button" type="submit">
+                <span>{% trans "Publish" %}</span></button>
+            <!--img src="{{ STATIC_URL }}img/angel-right.png" style="vertical-align: middle" /-->
+            </form>
+    {% else %}
+        <a href="{% url login %}">{% trans "Log in to publish." %}</a>
+    {% endif %}
+{% else %}
+    <p>{% trans "This book can't be published yet, because:" %}</p>
+    <ul><li>{{ publishable_error }}</li></ul>
+{% endif %}
+
+</div>
+
+
 <div class='section'>
     <h2>{% trans "Comments" %}</h2>
 
index 5e18b7e..9344053 100755 (executable)
@@ -148,8 +148,8 @@ _image_states = [
         ('unpublished', _('unpublished'), Q(_published=False)),
         ('empty', _('empty'), Q(head=None)),
     ]
-_image_states_options = [s[:2] for s in _states]
-_image_states_dict = dict([(s[0], s[2]) for s in _states])
+_image_states_options = [s[:2] for s in _image_states]
+_image_states_dict = dict([(s[0], s[2]) for s in _image_states])
 
 def image_list_filter(request, **kwargs):
 
@@ -181,7 +181,7 @@ def image_list(context, user=None):
     else:
         filters = {}
         new_context = {"users": User.objects.annotate(
-                count=Count('chunk')).filter(count__gt=0).order_by(
+                count=Count('image')).filter(count__gt=0).order_by(
                 '-count', 'last_name', 'first_name')}
 
     new_context.update({
index 621eb12..31df1c1 100644 (file)
@@ -8,6 +8,8 @@ urlpatterns = patterns('catalogue.views',
 
     url(r'^images/$', 'image_list', name='catalogue_image_list'),
     url(r'^image/(?P<slug>[^/]+)/$', 'image', name="catalogue_image"),
+    url(r'^image/(?P<slug>[^/]+)/publish$', 'publish_image',
+            name="catalogue_publish_image"),
 
     url(r'^catalogue/$', 'document_list', name='catalogue_document_list'),
     url(r'^user/$', 'my', name='catalogue_user'),
@@ -26,7 +28,6 @@ urlpatterns = patterns('catalogue.views',
         'create_missing', name='catalogue_create_missing'),
 
     url(r'^book/(?P<slug>[^/]+)/publish$', 'publish', name="catalogue_publish"),
-    #url(r'^(?P<name>[^/]+)/publish/(?P<version>\d+)$', 'publish', name="catalogue_publish"),
 
     url(r'^book/(?P<slug>[^/]+)/$', 'book', name="catalogue_book"),
     url(r'^book/(?P<slug>[^/]+)/xml$', 'book_xml', name="catalogue_book_xml"),
index 52008c9..bc54c3e 100644 (file)
@@ -23,8 +23,8 @@ from apiclient import NotAuthorizedError
 from catalogue import forms
 from catalogue import helpers
 from catalogue.helpers import active_tab
-from catalogue.models import (Book, Chunk, BookPublishRecord, 
-        ChunkPublishRecord, Image)
+from catalogue.models import (Book, Chunk, Image, BookPublishRecord, 
+        ChunkPublishRecord, ImagePublishRecord)
 from catalogue.tasks import publishable_error
 
 #
@@ -358,8 +358,7 @@ def image(request, slug):
         form = forms.ReadonlyImageForm(instance=image)
         editable = False
 
-    #publish_error = publishable_error(book)
-    publish_error = 'Publishing not implemented yet.'
+    publish_error = publishable_error(image)
     publishable = publish_error is None
 
     return direct_to_template(request, "catalogue/image_detail.html", extra_context={
@@ -481,3 +480,20 @@ def publish(request, slug):
         return http.HttpResponse(e)
     else:
         return http.HttpResponseRedirect(book.get_absolute_url())
+
+
+@require_POST
+@login_required
+def publish_image(request, slug):
+    image = get_object_or_404(Image, slug=slug)
+    if not image.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    try:
+        image.publish(request.user)
+    except NotAuthorizedError:
+        return http.HttpResponseRedirect(reverse('apiclient_oauth'))
+    except BaseException, e:
+        return http.HttpResponse(e)
+    else:
+        return http.HttpResponseRedirect(image.get_absolute_url())
index 2887829..5b40766 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 28878296bacad453735a520f350ba5a971f8ffc8
+Subproject commit 5b407667ca47cf4d9752821fd49e5611737146d2
index 7ce9665..3b6f45d 100644 (file)
         var self = this;
         this.$objects_list.children().remove();
 
-        $.each(this.doc.getImageItems('motyw'), function(i, e) {
+        $.each(this.doc.getImageItems('theme'), function(i, e) {
             self._push.apply(self, e);
         });
 
             args.unshift($(e).text());
             motifs.push(args);
         })
-        self.doc.setImageItems('motyw', motifs);
+        self.doc.setImageItems('theme', motifs);
 
         this.ias.setOptions({disable: true, hide: true});
 
index 9e6a3cb..97d4ba2 100644 (file)
         var self = this;
         this.$objects_list.children().remove();
 
-        $.each(this.doc.getImageItems('obiekt'), function(i, e) {
+        $.each(this.doc.getImageItems('object'), function(i, e) {
             self._push.apply(self, e);
         });
 
             args.unshift($(e).text());
             objects.push(args);
         })
-        self.doc.setImageItems('obiekt', objects);
+        self.doc.setImageItems('object', objects);
 
         this.ias.setOptions({disable: true, hide: true});
 
index 33af176..219f53f 100644 (file)
@@ -76,7 +76,7 @@
                                if (self.text === null || self.commit !== data.commit) {
                                        self.text = data.text;
                                        if (self.text === '') {
-                                           self.text = '<obraz></obraz>';
+                                           self.text = '<picture></picture>';
                                        }
                                        self.revision = data.revision;
                     self.commit = data.commit;
             $e.find('div').each(function(i, div) {
                 var $div = $(div);
                 switch ($div.attr('type')) {
-                    case 'area':
+                    case 'rect':
                         a.push([
                             value,
                             $div.attr('x1'),
             $sem.attr(tag, e[0]);
             $div = $(doc.createElement("div"));
             if (e[1]) {
-                $div.attr('type', 'area');
+                $div.attr('type', 'rect');
                 $div.attr('x1', e[1]);
                 $div.attr('y1', e[2]);
                 $div.attr('x2', e[3]);