From e01dee1eda68946acf73e43f2c54b3c730514a21 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Thu, 1 Dec 2011 16:24:17 +0100 Subject: [PATCH] pdf and epub previews --- apps/catalogue/ebook_utils.py | 26 ++++ .../catalogue/locale/pl/LC_MESSAGES/django.mo | Bin 6231 -> 6311 bytes .../catalogue/locale/pl/LC_MESSAGES/django.po | 77 ++++++----- .../0007_auto__add_field_book_dc_slug.py | 127 ++++++++++++++++++ apps/catalogue/models/book.py | 34 ++++- apps/catalogue/tasks.py | 5 + .../templates/catalogue/book_detail.html | 4 +- apps/catalogue/urls.py | 4 +- apps/catalogue/views.py | 62 ++++++++- 9 files changed, 292 insertions(+), 47 deletions(-) create mode 100644 apps/catalogue/ebook_utils.py create mode 100644 apps/catalogue/migrations/0007_auto__add_field_book_dc_slug.py diff --git a/apps/catalogue/ebook_utils.py b/apps/catalogue/ebook_utils.py new file mode 100644 index 00000000..5961b637 --- /dev/null +++ b/apps/catalogue/ebook_utils.py @@ -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 diff --git a/apps/catalogue/locale/pl/LC_MESSAGES/django.mo b/apps/catalogue/locale/pl/LC_MESSAGES/django.mo index 878689e0f63f9c4e4334a7ada9623ba59cf46cb3..e6f7d3b6ebd5af7a990c6f9168495fb083a9a951 100644 GIT binary patch delta 2224 zcmZA2Ye=6}9KiA8G%tUf+tf|h(mCfea~E&ZZK*3$bE}rq%w?->#sa0aK57mHv;y^v9?u_B7>`~7=1q6dG^=bZDL=iDFvbG6fT z$?x+srUKUxaW%0qJ%kBt$>he>ogKm|+=cvwgWRYRhA{_^#QYfMk{^%dPhuhY=VJNm zm_hzsT#E1G$`F#_6p1_v&ft2SNBhpB^(#Y$X(jcA8m=zG0*C7wV#n!p4; zg(WzN+wdK1zRTU#vnqY{X)0!2;|@I~qm@`Vi*hV`%;7k(q>-qwnD|@?WAW zcLuM;-(qLC#ZCHbCcomMITk<$Mpl8v6PNI80 zjlMUFw)ZJIq0eLa*J!=DrL4a*ITtG~p;uqXK^s_(JPNDOjw)il8m-qHZHwidG2e&I zd=S~@a33zmW9SMzinceI$NJmR3l!MkOYy;LX!)CHLnmYTES^sb;S=0K`3$Z4{71C@ zugIft5$!mG-dveHv|c%`!OiHzb|*<#aUZ%QL+EK8LtmW23VZ`y@~_bPbI5rLXVDJN zp?m)qdN>QY)nFCcaUVLt1DIM7wB6(}5|t#Ta3{{94gZb4n8hf}7oZ)KqGzBPsSj#=)qWi5goa`*-!$R zY^XqI)`b28YsDfQM0O#JVku5wJ-!;tzr}>#|IZ{i_~AUdB>!L+uIJ-z!4Y)nr_p*R zqaR^2`7e-H!V-G5UNJh*b?8iM&=u&4A5X;W!Cjd>EbC6X=Vt#PScLpP`5B zG&=Kd&{O;qx`OlZ`JZS93uwJe_Q3%aqlc~>ok%SX+hHpSXE1;^cqBTCHZ&IVkHq{0 zy0lMYYOj%xBJ3vm2v^Uo^xaCrYs+M7W>In%sda=K@!zYA%mJd6a0GV~Uj8Z#5`Gen zbQ{r5xIC#f$A01ubk7ISJm|p?h8q;zk;U@VD z;?LTpE1iU=(G$9n=p^`lQvbJa!rKV1+lgCJsm0sPu!rzly}^e)#2v(b!t2gOQa@Fm zqMb3{fNpLt(Gko3Pa6drW;SHkr{(tax0jtbJbHZi=mRtTId7#UI=U|B7ZN|D=hSbi ztt+c*+EG6_pSLz~IoB{#uq-P*cYs$%4wZG%%*^Wi3u%d1(9_X1xsWKDDKFfb@h?Wt BxnKYQ delta 2168 zcmZA2OKenC9Dwmt`=T!@wTM=bwqR*H4$gG6#jye+U~7xi3az$9s!27Vq9{s&F=J9R zn7A-%#kgqVf`}~eF%c7dEJ#f75reM~Lo~#N2{DjDf(sMW?;8#qPUe4q=bkz5d+yA8 z>Eo%~?{y_pfizCcH;O};)G{ul)>$E3jcv#$tmHzK(2EtgF5VAg75AgD{86muetRr` z8B4f-9cyqe&J7_K_K}%Q!DqM#zd^@7gXMT0`GgBxY*@yqbFdyAumx?`fetW)l{ktE z@DW^wyD@`bp#7c2YQ_)$ka3`LT3v&+convy19YPk?7=!5M(b}yRuG9K*%<6uRY8XoG3w{Dl4JfO+)UA3+c0 z2~6M_EbIt{wrfKBOJEBQ;0oM{_LuvVj59qF3%*A?{uO=p=a5gRVRk;dMqG%UXuBb_ z{=?`>pT}!)FWUYiOyPIv#4n&LujazI{L0pSJVYlyp123yvJ&aZyk3NB2-0wo_hL6#D zN70G?fUfWay7Iqcc^$KJ!t>F3i_msSbYY!ny8+7?KRgyI>_89I3+T!x(L*?eZrwZa z`}ffSK0xaoLI*mE9QTI;e%z6&`GQ$oVHWGg1C=( zkXTN%6Ylj8Ve(hT\n" "Language-Team: Fundacja Nowoczesna Polska \n" "Language: pl\n" @@ -46,76 +46,76 @@ msgstr "Część z tym slugiem już istnieje" 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" -#: views.py:164 +#: views.py:160 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." -#: models/book.py:25 +#: models/book.py:23 #: 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/book.py:27 +#: models/book.py:25 msgid "public" msgstr "publiczna" -#: models/book.py:28 +#: models/book.py:26 msgid "scan gallery name" msgstr "nazwa galerii skanów" -#: models/book.py:31 +#: models/book.py:29 msgid "parent" msgstr "rodzic" -#: models/book.py:32 +#: models/book.py:30 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/book.py:47 +#: models/book.py:46 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." -#: 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." -#: models/book.py:232 +#: models/book.py:234 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." -#: models/book.py:236 +#: models/book.py:238 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" @@ -140,7 +140,7 @@ msgid "time" msgstr "czas" #: models/publish_log.py:19 -#: templates/catalogue/wall.html:17 +#: templates/catalogue/wall.html:18 msgid "user" msgstr "użytkownik" @@ -213,31 +213,39 @@ msgstr "Wersja HTML" 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" -#: templates/catalogue/book_detail.html:78 +#: templates/catalogue/book_detail.html:75 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:" -#: templates/catalogue/book_detail.html:90 +#: templates/catalogue/book_detail.html:87 msgid "Comments" msgstr "Komentarze" -#: templates/catalogue/book_html.html:21 +#: templates/catalogue/book_html.html:13 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." -#: templates/catalogue/book_html.html:23 +#: templates/catalogue/book_html.html:15 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 .xml zostaną zignorowane." #: templates/catalogue/document_upload.html:17 +#: templates/catalogue/upload_pdf.html:13 #: 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:" +#: 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/wall.html:27 +#: templates/catalogue/wall.html:28 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." @@ -493,12 +506,6 @@ msgstr "Komentarz" #~ msgid "Revision marked" #~ msgstr "Wersja oznaczona" -#~ msgid "EPUB version" -#~ msgstr "Wersja EPUB" - -#~ msgid "PDF version" -#~ msgstr "Wersja PDF" - #~ 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 index 00000000..5ae20ea3 --- /dev/null +++ b/apps/catalogue/migrations/0007_auto__add_field_book_dc_slug.py @@ -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'] diff --git a/apps/catalogue/models/book.py b/apps/catalogue/models/book.py index 8c9e6e6a..c3c1369b 100755 --- a/apps/catalogue/models/book.py +++ b/apps/catalogue/models/book.py @@ -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 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 -from catalogue.tasks import refresh_instance +from catalogue.tasks import refresh_instance, book_content_updated 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) + dc_slug = models.CharField(max_length=128, null=True, blank=True, editable=False) 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) + from librarian.dcparser import BookInfo + from librarian import NoDublinCore, ParseError, ValidationError + 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 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): + # 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(), diff --git a/apps/catalogue/tasks.py b/apps/catalogue/tasks.py index 1bb4bc96..547f36b4 100644 --- a/apps/catalogue/tasks.py +++ b/apps/catalogue/tasks.py @@ -32,3 +32,8 @@ def _publishable_error(book, language=None): def publishable_error(book): return _publishable_error.delay(book, translation.get_language()).wait() + + +@task +def book_content_updated(book): + book.refresh_dc_cache() diff --git a/apps/catalogue/templates/catalogue/book_detail.html b/apps/catalogue/templates/catalogue/book_detail.html index b32177d9..bfd4ef5c 100755 --- a/apps/catalogue/templates/catalogue/book_detail.html +++ b/apps/catalogue/templates/catalogue/book_detail.html @@ -54,10 +54,8 @@ {% trans "Full XML" %}
{% trans "HTML version" %}
{% trans "TXT version" %}
- {% comment %} - {% trans "EPUB version" %}
{% trans "PDF version" %}
- {% endcomment %} + {% trans "EPUB version" %}

{% if user.is_authenticated %} diff --git a/apps/catalogue/urls.py b/apps/catalogue/urls.py index f7d37616..ab9b5704 100644 --- a/apps/catalogue/urls.py +++ b/apps/catalogue/urls.py @@ -29,8 +29,8 @@ urlpatterns = patterns('catalogue.views', url(r'^book/(?P[^/]+)/xml$', 'book_xml', name="catalogue_book_xml"), url(r'^book/(?P[^/]+)/txt$', 'book_txt', name="catalogue_book_txt"), url(r'^book/(?P[^/]+)/html$', 'book_html', name="catalogue_book_html"), - #url(r'^book/(?P[^/]+)/epub$', 'book_epub', name="catalogue_book_epub"), - #url(r'^book/(?P[^/]+)/pdf$', 'book_pdf', name="catalogue_book_pdf"), + url(r'^book/(?P[^/]+)/epub$', 'book_epub', name="catalogue_book_epub"), + url(r'^book/(?P[^/]+)/pdf$', 'book_pdf', name="catalogue_book_pdf"), url(r'^chunk_add/(?P[^/]+)/(?P[^/]+)/$', 'chunk_add', name="catalogue_chunk_add"), url(r'^chunk_edit/(?P[^/]+)/(?P[^/]+)/$', diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py index c7189d47..87236855 100644 --- a/apps/catalogue/views.py +++ b/apps/catalogue/views.py @@ -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.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 @@ -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 -import librarian.html -import librarian.text - 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? + + import librarian.text 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? + + 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 + +@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: -- 2.20.1