#
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 *
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
"""
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)
# 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 _
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()
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):
"""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)
#
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
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):
{% 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 %}
+ —
+ {% 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>
('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):
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({
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'),
'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"),
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
#
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={
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())
-Subproject commit 28878296bacad453735a520f350ba5a971f8ffc8
+Subproject commit 5b407667ca47cf4d9752821fd49e5611737146d2
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});
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});
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]);