pdf and epub previews
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Thu, 1 Dec 2011 15:24:17 +0000 (16:24 +0100)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Thu, 1 Dec 2011 15:24:17 +0000 (16:24 +0100)
apps/catalogue/ebook_utils.py [new file with mode: 0644]
apps/catalogue/locale/pl/LC_MESSAGES/django.mo
apps/catalogue/locale/pl/LC_MESSAGES/django.po
apps/catalogue/migrations/0007_auto__add_field_book_dc_slug.py [new file with mode: 0644]
apps/catalogue/models/book.py
apps/catalogue/tasks.py
apps/catalogue/templates/catalogue/book_detail.html
apps/catalogue/urls.py
apps/catalogue/views.py

diff --git a/apps/catalogue/ebook_utils.py b/apps/catalogue/ebook_utils.py
new file mode 100644 (file)
index 0000000..5961b63
--- /dev/null
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+from catalogue.models import Book
+from librarian import DocProvider
+from django.http import HttpResponse
+
+
+class RedakcjaDocProvider(DocProvider):
+    """Used for getting books' children."""
+
+    def by_slug(self, slug):
+        return Book.objects.get(dc_slug=slug).xml_file
+
+
+def serve_file(file_path, name, mime_type):
+    def read_chunks(f, size=8192):
+        chunk = f.read(size)
+        while chunk:
+            yield chunk
+            chunk = f.read(size)
+
+    response = HttpResponse(mimetype=mime_type)
+    response['Content-Disposition'] = 'attachment; filename=%s' % name
+    with open(file_path) as f:
+        for chunk in read_chunks(f):
+            response.write(chunk)
+    return response
index 878689e..e6f7d3b 100644 (file)
Binary files a/apps/catalogue/locale/pl/LC_MESSAGES/django.mo and b/apps/catalogue/locale/pl/LC_MESSAGES/django.mo differ
index 4378dcf..65d9ba3 100644 (file)
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Platforma Redakcyjna\n"
 "Report-Msgid-Bugs-To: \n"
 msgstr ""
 "Project-Id-Version: Platforma Redakcyjna\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-11-30 13:01+0100\n"
-"PO-Revision-Date: 2011-11-30 13:06+0100\n"
+"POT-Creation-Date: 2011-12-01 16:21+0100\n"
+"PO-Revision-Date: 2011-12-01 16:23+0100\n"
 "Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
 "Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org.pl>\n"
 "Language: pl\n"
 "Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
 "Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org.pl>\n"
 "Language: pl\n"
@@ -46,76 +46,76 @@ msgstr "Część z tym slugiem już istnieje"
 msgid "Append to"
 msgstr "Dołącz do"
 
 msgid "Append to"
 msgstr "Dołącz do"
 
-#: views.py:162
+#: views.py:158
 #, python-format
 msgid "Slug already used for %s"
 msgstr "Slug taki sam jak dla pliku %s"
 
 #, python-format
 msgid "Slug already used for %s"
 msgstr "Slug taki sam jak dla pliku %s"
 
-#: views.py:164
+#: views.py:160
 msgid "Slug already used in repository."
 msgstr "Dokument o tym slugu już istnieje w repozytorium."
 
 msgid "Slug already used in repository."
 msgstr "Dokument o tym slugu już istnieje w repozytorium."
 
-#: views.py:170
+#: views.py:166
 msgid "File should be UTF-8 encoded."
 msgstr "Plik powinien mieć kodowanie UTF-8."
 
 msgid "File should be UTF-8 encoded."
 msgstr "Plik powinien mieć kodowanie UTF-8."
 
-#: models/book.py:25
+#: models/book.py:23
 #: models/chunk.py:23
 msgid "title"
 msgstr "tytuł"
 
 #: models/chunk.py:23
 msgid "title"
 msgstr "tytuł"
 
-#: models/book.py:26
+#: models/book.py:24
 #: models/chunk.py:24
 msgid "slug"
 msgstr "slug"
 
 #: models/chunk.py:24
 msgid "slug"
 msgstr "slug"
 
-#: models/book.py:27
+#: models/book.py:25
 msgid "public"
 msgstr "publiczna"
 
 msgid "public"
 msgstr "publiczna"
 
-#: models/book.py:28
+#: models/book.py:26
 msgid "scan gallery name"
 msgstr "nazwa galerii skanów"
 
 msgid "scan gallery name"
 msgstr "nazwa galerii skanów"
 
-#: models/book.py:31
+#: models/book.py:29
 msgid "parent"
 msgstr "rodzic"
 
 msgid "parent"
 msgstr "rodzic"
 
-#: models/book.py:32
+#: models/book.py:30
 msgid "parent number"
 msgstr "numeracja rodzica"
 
 msgid "parent number"
 msgstr "numeracja rodzica"
 
-#: models/book.py:46
+#: models/book.py:45
 #: models/chunk.py:21
 #: models/publish_log.py:17
 msgid "book"
 msgstr "książka"
 
 #: models/chunk.py:21
 #: models/publish_log.py:17
 msgid "book"
 msgstr "książka"
 
-#: models/book.py:47
+#: models/book.py:46
 msgid "books"
 msgstr "książki"
 
 msgid "books"
 msgstr "książki"
 
-#: models/book.py:222
+#: models/book.py:221
 msgid "No chunks in the book."
 msgstr "Książka nie ma części."
 
 msgid "No chunks in the book."
 msgstr "Książka nie ma części."
 
-#: models/book.py:226
+#: models/book.py:225
 msgid "Not all chunks have publishable revisions."
 msgstr "Niektóre części nie są gotowe do publikacji."
 
 msgid "Not all chunks have publishable revisions."
 msgstr "Niektóre części nie są gotowe do publikacji."
 
-#: models/book.py:232
+#: models/book.py:234
 msgid "Invalid XML"
 msgstr "Nieprawidłowy XML"
 
 msgid "Invalid XML"
 msgstr "Nieprawidłowy XML"
 
-#: models/book.py:234
+#: models/book.py:236
 msgid "No Dublin Core found."
 msgstr "Brak sekcji Dublin Core."
 
 msgid "No Dublin Core found."
 msgstr "Brak sekcji Dublin Core."
 
-#: models/book.py:236
+#: models/book.py:238
 msgid "Invalid Dublin Core"
 msgstr "Nieprawidłowy Dublin Core"
 
 msgid "Invalid Dublin Core"
 msgstr "Nieprawidłowy Dublin Core"
 
-#: models/book.py:239
+#: models/book.py:241
 msgid "rdf:about is not"
 msgstr "rdf:about jest różny od"
 
 msgid "rdf:about is not"
 msgstr "rdf:about jest różny od"
 
@@ -140,7 +140,7 @@ msgid "time"
 msgstr "czas"
 
 #: models/publish_log.py:19
 msgstr "czas"
 
 #: models/publish_log.py:19
-#: templates/catalogue/wall.html:17
+#: templates/catalogue/wall.html:18
 msgid "user"
 msgstr "użytkownik"
 
 msgid "user"
 msgstr "użytkownik"
 
@@ -213,31 +213,39 @@ msgstr "Wersja HTML"
 msgid "TXT version"
 msgstr "Wersja TXT"
 
 msgid "TXT version"
 msgstr "Wersja TXT"
 
-#: templates/catalogue/book_detail.html:74
+#: templates/catalogue/book_detail.html:57
+msgid "PDF version"
+msgstr "Wersja PDF"
+
+#: templates/catalogue/book_detail.html:58
+msgid "EPUB version"
+msgstr "Wersja EPUB"
+
+#: templates/catalogue/book_detail.html:71
 msgid "Publish"
 msgstr "Opublikuj"
 
 msgid "Publish"
 msgstr "Opublikuj"
 
-#: templates/catalogue/book_detail.html:78
+#: templates/catalogue/book_detail.html:75
 msgid "Log in to publish."
 msgstr "Zaloguj się, aby opublikować."
 
 msgid "Log in to publish."
 msgstr "Zaloguj się, aby opublikować."
 
-#: templates/catalogue/book_detail.html:81
+#: templates/catalogue/book_detail.html:78
 msgid "This book can't be published yet, because:"
 msgstr "Ta książka nie może jeszcze zostać opublikowana. Powód:"
 
 msgid "This book can't be published yet, because:"
 msgstr "Ta książka nie może jeszcze zostać opublikowana. Powód:"
 
-#: templates/catalogue/book_detail.html:90
+#: templates/catalogue/book_detail.html:87
 msgid "Comments"
 msgstr "Komentarze"
 
 msgid "Comments"
 msgstr "Komentarze"
 
-#: templates/catalogue/book_html.html:21
+#: templates/catalogue/book_html.html:13
 msgid "Table of contents"
 msgstr "Spis treści"
 
 msgid "Table of contents"
 msgstr "Spis treści"
 
-#: templates/catalogue/book_html.html:22
+#: templates/catalogue/book_html.html:14
 msgid "Edit. note"
 msgstr "Nota red."
 
 msgid "Edit. note"
 msgstr "Nota red."
 
-#: templates/catalogue/book_html.html:23
+#: templates/catalogue/book_html.html:15
 msgid "Infobox"
 msgstr "Informacje"
 
 msgid "Infobox"
 msgstr "Informacje"
 
@@ -281,6 +289,7 @@ msgid "Please submit a ZIP with UTF-8 encoded XML files. Files not ending with <
 msgstr "Proszę wskazać archiwum ZIP z plikami XML w kodowaniu UTF-8. Pliki nie kończące się na <code>.xml</code> zostaną zignorowane."
 
 #: templates/catalogue/document_upload.html:17
 msgstr "Proszę wskazać archiwum ZIP z plikami XML w kodowaniu UTF-8. Pliki nie kończące się na <code>.xml</code> zostaną zignorowane."
 
 #: templates/catalogue/document_upload.html:17
+#: templates/catalogue/upload_pdf.html:13
 #: templatetags/catalogue.py:35
 msgid "Upload"
 msgstr "Załaduj"
 #: templatetags/catalogue.py:35
 msgid "Upload"
 msgstr "Załaduj"
@@ -322,16 +331,20 @@ msgstr "Twoje ostatnie edycje"
 msgid "Recent activity for"
 msgstr "Ostatnia aktywność dla:"
 
 msgid "Recent activity for"
 msgstr "Ostatnia aktywność dla:"
 
+#: templates/catalogue/upload_pdf.html:8
+msgid "PDF file upload"
+msgstr ""
+
 #: templates/catalogue/user_list.html:7
 #: templatetags/catalogue.py:31
 msgid "Users"
 msgstr "Użytkownicy"
 
 #: templates/catalogue/user_list.html:7
 #: templatetags/catalogue.py:31
 msgid "Users"
 msgstr "Użytkownicy"
 
-#: templates/catalogue/wall.html:27
+#: templates/catalogue/wall.html:28
 msgid "not logged in"
 msgstr "nie zalogowany"
 
 msgid "not logged in"
 msgstr "nie zalogowany"
 
-#: templates/catalogue/wall.html:32
+#: templates/catalogue/wall.html:33
 msgid "No activity recorded."
 msgstr "Nie zanotowano aktywności."
 
 msgid "No activity recorded."
 msgstr "Nie zanotowano aktywności."
 
@@ -493,12 +506,6 @@ msgstr "Komentarz"
 #~ msgid "Revision marked"
 #~ msgstr "Wersja oznaczona"
 
 #~ msgid "Revision marked"
 #~ msgstr "Wersja oznaczona"
 
-#~ msgid "EPUB version"
-#~ msgstr "Wersja EPUB"
-
-#~ msgid "PDF version"
-#~ msgstr "Wersja PDF"
-
 #~ msgid "Old version"
 #~ msgstr "Stara wersja"
 
 #~ msgid "Old version"
 #~ msgstr "Stara wersja"
 
diff --git a/apps/catalogue/migrations/0007_auto__add_field_book_dc_slug.py b/apps/catalogue/migrations/0007_auto__add_field_book_dc_slug.py
new file mode 100644 (file)
index 0000000..5ae20ea
--- /dev/null
@@ -0,0 +1,127 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding field 'Book.dc_slug'
+        db.add_column('catalogue_book', 'dc_slug', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True), keep_default=False)
+
+
+    def backwards(self, orm):
+        
+        # Deleting field 'Book.dc_slug'
+        db.delete_column('catalogue_book', 'dc_slug')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
+            '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'dc_slug': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['catalogue.Book']"}),
+            'parent_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'catalogue.bookpublishrecord': {
+            'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'catalogue.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'catalogue.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+        },
+        'catalogue.chunkpublishrecord': {
+            'Meta': {'object_name': 'ChunkPublishRecord'},
+            'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+            'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'catalogue.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        }
+    }
+
+    complete_apps = ['catalogue']
index 8c9e6e6..c3c1369 100755 (executable)
@@ -8,14 +8,12 @@ from django.db import models, transaction
 from django.template.loader import render_to_string
 from django.utils.translation import ugettext_lazy as _
 from slughifi import slughifi
 from django.template.loader import render_to_string
 from django.utils.translation import ugettext_lazy as _
 from slughifi import slughifi
-from librarian import NoDublinCore, ParseError, ValidationError
-from librarian.dcparser import BookInfo
 
 import apiclient
 from catalogue.helpers import cached_in_field
 from catalogue.models import BookPublishRecord, ChunkPublishRecord
 from catalogue.signals import post_publish
 
 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
+from catalogue.tasks import refresh_instance, book_content_updated
 from catalogue.xml_tools import compile_text, split_xml
 
 
 from catalogue.xml_tools import compile_text, split_xml
 
 
@@ -36,6 +34,7 @@ class Book(models.Model):
     _single = models.NullBooleanField(editable=False, db_index=True)
     _new_publishable = models.NullBooleanField(editable=False)
     _published = models.NullBooleanField(editable=False)
     _single = models.NullBooleanField(editable=False, db_index=True)
     _new_publishable = models.NullBooleanField(editable=False)
     _published = models.NullBooleanField(editable=False)
+    dc_slug = models.CharField(max_length=128, null=True, blank=True, editable=False)
 
     class NoTextError(BaseException):
         pass
 
     class NoTextError(BaseException):
         pass
@@ -226,6 +225,9 @@ class Book(models.Model):
             raise AssertionError(_('Not all chunks have publishable revisions.'))
         book_xml = self.materialize(changes=changes)
 
             raise AssertionError(_('Not all chunks have publishable revisions.'))
         book_xml = self.materialize(changes=changes)
 
+        from librarian.dcparser import BookInfo
+        from librarian import NoDublinCore, ParseError, ValidationError
+
         try:
             bi = BookInfo.from_string(book_xml.encode('utf-8'))
         except ParseError, e:
         try:
             bi = BookInfo.from_string(book_xml.encode('utf-8'))
         except ParseError, e:
@@ -272,7 +274,33 @@ class Book(models.Model):
     def short_html(self):
         return render_to_string('catalogue/book_list/book.html', {'book': self})
 
     def short_html(self):
         return render_to_string('catalogue/book_list/book.html', {'book': self})
 
+    def book_info(self, publishable=True):
+        try:
+            book_xml = self.materialize(publishable=publishable)
+        except self.NoTextError:
+            pass
+        else:
+            from librarian.dcparser import BookInfo
+            from librarian import NoDublinCore, ParseError, ValidationError
+            try:
+                return BookInfo.from_string(book_xml.encode('utf-8'))
+            except (self.NoTextError, ParseError, NoDublinCore, ValidationError):
+                return None
+
+    def refresh_dc_cache(self):
+        update = {
+            'dc_slug': None,
+        }
+
+        info = self.book_info()
+        if info is not None:
+            update['dc_slug'] = info.slug
+        Book.objects.filter(pk=self.pk).update(**update)
+
     def touch(self):
     def touch(self):
+        # this should only really be done when text or publishable status changes
+        book_content_updated.delay(self)
+
         update = {
             "_new_publishable": self.is_new_publishable(),
             "_published": self.is_published(),
         update = {
             "_new_publishable": self.is_new_publishable(),
             "_published": self.is_published(),
index 1bb4bc9..547f36b 100644 (file)
@@ -32,3 +32,8 @@ def _publishable_error(book, language=None):
 def publishable_error(book):
     return _publishable_error.delay(book, 
         translation.get_language()).wait()
 def publishable_error(book):
     return _publishable_error.delay(book, 
         translation.get_language()).wait()
+
+
+@task
+def book_content_updated(book):
+    book.refresh_dc_cache()
index b32177d..bfd4ef5 100755 (executable)
     <a href="{% url catalogue_book_xml book.slug %}">{% trans "Full XML" %}</a><br/>
     <a target="_blank" href="{% url catalogue_book_html book.slug %}">{% trans "HTML version" %}</a><br/>
     <a href="{% url catalogue_book_txt book.slug %}">{% trans "TXT version" %}</a><br/>
     <a href="{% url catalogue_book_xml book.slug %}">{% trans "Full XML" %}</a><br/>
     <a target="_blank" href="{% url catalogue_book_html book.slug %}">{% trans "HTML version" %}</a><br/>
     <a href="{% url catalogue_book_txt book.slug %}">{% trans "TXT version" %}</a><br/>
-    {% comment %}
-    <a href="{% url catalogue_book_epub book.slug %}">{% trans "EPUB version" %}</a><br/>
     <a href="{% url catalogue_book_pdf book.slug %}">{% trans "PDF version" %}</a><br/>
     <a href="{% url catalogue_book_pdf book.slug %}">{% trans "PDF version" %}</a><br/>
-    {% endcomment %}
+    <a href="{% url catalogue_book_epub book.slug %}">{% trans "EPUB version" %}</a><br/>
     </p>
 
     {% if user.is_authenticated %}
     </p>
 
     {% if user.is_authenticated %}
index f7d3761..ab9b570 100644 (file)
@@ -29,8 +29,8 @@ urlpatterns = patterns('catalogue.views',
     url(r'^book/(?P<slug>[^/]+)/xml$', 'book_xml', name="catalogue_book_xml"),
     url(r'^book/(?P<slug>[^/]+)/txt$', 'book_txt', name="catalogue_book_txt"),
     url(r'^book/(?P<slug>[^/]+)/html$', 'book_html', name="catalogue_book_html"),
     url(r'^book/(?P<slug>[^/]+)/xml$', 'book_xml', name="catalogue_book_xml"),
     url(r'^book/(?P<slug>[^/]+)/txt$', 'book_txt', name="catalogue_book_txt"),
     url(r'^book/(?P<slug>[^/]+)/html$', 'book_html', name="catalogue_book_html"),
-    #url(r'^book/(?P<slug>[^/]+)/epub$', 'book_epub', name="catalogue_book_epub"),
-    #url(r'^book/(?P<slug>[^/]+)/pdf$', 'book_pdf', name="catalogue_book_pdf"),
+    url(r'^book/(?P<slug>[^/]+)/epub$', 'book_epub', name="catalogue_book_epub"),
+    url(r'^book/(?P<slug>[^/]+)/pdf$', 'book_pdf', name="catalogue_book_pdf"),
     url(r'^chunk_add/(?P<slug>[^/]+)/(?P<chunk>[^/]+)/$',
         'chunk_add', name="catalogue_chunk_add"),
     url(r'^chunk_edit/(?P<slug>[^/]+)/(?P<chunk>[^/]+)/$',
     url(r'^chunk_add/(?P<slug>[^/]+)/(?P<chunk>[^/]+)/$',
         'chunk_add', name="catalogue_chunk_add"),
     url(r'^chunk_edit/(?P<slug>[^/]+)/(?P<chunk>[^/]+)/$',
index c7189d4..8723685 100644 (file)
@@ -11,7 +11,7 @@ from django.contrib.auth.decorators import login_required, permission_required
 from django.core.urlresolvers import reverse
 from django.db.models import Count, Q
 from django import http
 from django.core.urlresolvers import reverse
 from django.db.models import Count, Q
 from django import http
-from django.http import Http404, HttpResponseForbidden
+from django.http import Http404, HttpResponse, HttpResponseForbidden
 from django.shortcuts import get_object_or_404, render
 from django.utils.encoding import iri_to_uri
 from django.utils.http import urlquote_plus
 from django.shortcuts import get_object_or_404, render
 from django.utils.encoding import iri_to_uri
 from django.utils.http import urlquote_plus
@@ -19,9 +19,6 @@ from django.utils.translation import ugettext_lazy as _
 from django.views.decorators.http import require_POST
 from django.views.generic.simple import direct_to_template
 
 from django.views.decorators.http import require_POST
 from django.views.generic.simple import direct_to_template
 
-import librarian.html
-import librarian.text
-
 from apiclient import NotAuthorizedError
 from catalogue import forms
 from catalogue import helpers
 from apiclient import NotAuthorizedError
 from catalogue import forms
 from catalogue import helpers
@@ -216,6 +213,8 @@ def book_txt(request, slug):
     xml = book.materialize()
     output = StringIO()
     # errors?
     xml = book.materialize()
     output = StringIO()
     # errors?
+
+    import librarian.text
     librarian.text.transform(StringIO(xml), output)
     text = output.getvalue()
     response = http.HttpResponse(text, content_type='text/plain', mimetype='text/plain')
     librarian.text.transform(StringIO(xml), output)
     text = output.getvalue()
     response = http.HttpResponse(text, content_type='text/plain', mimetype='text/plain')
@@ -231,12 +230,67 @@ def book_html(request, slug):
     xml = book.materialize()
     output = StringIO()
     # errors?
     xml = book.materialize()
     output = StringIO()
     # errors?
+
+    import librarian.html
     librarian.html.transform(StringIO(xml), output, parse_dublincore=False,
                              flags=['full-page'])
     html = output.getvalue()
     response = http.HttpResponse(html, content_type='text/html', mimetype='text/html')
     return response
 
     librarian.html.transform(StringIO(xml), output, parse_dublincore=False,
                              flags=['full-page'])
     html = output.getvalue()
     response = http.HttpResponse(html, content_type='text/html', mimetype='text/html')
     return response
 
+
+@never_cache
+def book_pdf(request, slug):
+    book = get_object_or_404(Book, slug=slug)
+    if not book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    from tempfile import NamedTemporaryFile
+    from os import unlink
+    from librarian import pdf
+    from catalogue.ebook_utils import RedakcjaDocProvider, serve_file
+
+    xml = book.materialize()
+    xml_file = NamedTemporaryFile()
+    xml_file.write(xml.encode('utf-8'))
+    xml_file.flush()
+
+    try:
+        pdf_file = NamedTemporaryFile(delete=False)
+        pdf.transform(RedakcjaDocProvider(),
+                  file_path=xml_file.name,
+                  output_file=pdf_file,
+                  )
+        return serve_file(pdf_file.name, book.slug + '.pdf', 'application/pdf')
+    finally:
+        unlink(pdf_file.name)
+
+
+@never_cache
+def book_epub(request, slug):
+    book = get_object_or_404(Book, slug=slug)
+    if not book.accessible(request):
+        return HttpResponseForbidden("Not authorized.")
+
+    from StringIO import StringIO
+    from tempfile import NamedTemporaryFile
+    from librarian import epub
+    from catalogue.ebook_utils import RedakcjaDocProvider
+
+    xml = book.materialize()
+    xml_file = NamedTemporaryFile()
+    xml_file.write(xml.encode('utf-8'))
+    xml_file.flush()
+
+    epub_file = StringIO()
+    epub.transform(RedakcjaDocProvider(), file_path=xml_file.name,
+            output_file=epub_file)
+    response = HttpResponse(mimetype='application/epub+zip')
+    response['Content-Disposition'] = 'attachment; filename=%s' % book.slug + '.epub'
+    response.write(epub_file.getvalue())
+    return response
+
+
 @never_cache
 def revision(request, slug, chunk=None):
     try:
 @never_cache
 def revision(request, slug, chunk=None):
     try: