#968: don't import invalid slugs
[wolnelektury.git] / apps / catalogue / models.py
index 1e6bb95..cb1eff8 100644 (file)
@@ -12,6 +12,7 @@ from django.template.defaultfilters import slugify
 from django.utils.safestring import mark_safe
 from django.utils.translation import get_language
 from django.core.urlresolvers import reverse
 from django.utils.safestring import mark_safe
 from django.utils.translation import get_language
 from django.core.urlresolvers import reverse
+from django.db.models.signals import post_save, m2m_changed, pre_delete
 
 from django.conf import settings
 
 
 from django.conf import settings
 
@@ -313,7 +314,7 @@ class Book(models.Model):
             elif type == "html":
                 return self.html_file
             elif type == "epub":
             elif type == "html":
                 return self.html_file
             elif type == "epub":
-                return self.html_file                
+                return self.epub_file
             elif type == "txt":
                 return self.txt_file
             elif type == "pdf":
             elif type == "txt":
                 return self.txt_file
             elif type == "pdf":
@@ -452,8 +453,7 @@ class Book(models.Model):
         epub_file = StringIO()
         try:
             epub.transform(BookImportDocProvider(self), self.slug, output_file=epub_file)
         epub_file = StringIO()
         try:
             epub.transform(BookImportDocProvider(self), self.slug, output_file=epub_file)
-            self.epub_file.save('%s.epub' % self.slug, ContentFile(epub_file.getvalue()), save=False)
-            self.save()
+            self.epub_file.save('%s.epub' % self.slug, ContentFile(epub_file.getvalue()))
             FileRecord(slug=self.slug, type='epub', sha1=sha1(epub_file.getvalue()).hexdigest()).save()
         except NoDublinCore:
             pass
             FileRecord(slug=self.slug, type='epub', sha1=sha1(epub_file.getvalue()).hexdigest()).save()
         except NoDublinCore:
             pass
@@ -467,9 +467,19 @@ class Book(models.Model):
             child_book.save()
             book_descendants += list(child_book.children.all())
 
             child_book.save()
             book_descendants += list(child_book.children.all())
 
+    def build_txt(self):
+        from StringIO import StringIO
+        from django.core.files.base import ContentFile
+        from librarian import text
+
+        out = StringIO()
+        text.transform(open(self.xml_file.path), out)
+        self.txt_file.save('%s.txt' % self.slug, ContentFile(out.getvalue()))
+        self.save()
+
 
     @classmethod
 
     @classmethod
-    def from_xml_file(cls, xml_file, overwrite=False, build_epub=True):
+    def from_xml_file(cls, xml_file, **kwargs):
         # use librarian to parse meta-data
         book_info = dcparser.parse(xml_file)
 
         # use librarian to parse meta-data
         book_info = dcparser.parse(xml_file)
 
@@ -477,12 +487,13 @@ class Book(models.Model):
             xml_file = File(open(xml_file))
 
         try:
             xml_file = File(open(xml_file))
 
         try:
-            return cls.from_text_and_meta(xml_file, book_info, overwrite, build_epub=build_epub)
+            return cls.from_text_and_meta(xml_file, book_info, **kwargs)
         finally:
             xml_file.close()
 
     @classmethod
         finally:
             xml_file.close()
 
     @classmethod
-    def from_text_and_meta(cls, raw_file, book_info, overwrite=False, build_epub=True):
+    def from_text_and_meta(cls, raw_file, book_info, overwrite=False, build_epub=True, build_txt=True):
+        import re
         from tempfile import NamedTemporaryFile
         from slughifi import slughifi
         from markupstring import MarkupString
         from tempfile import NamedTemporaryFile
         from slughifi import slughifi
         from markupstring import MarkupString
@@ -501,6 +512,8 @@ class Book(models.Model):
 
         # Read book metadata
         book_base, book_slug = book_info.url.rsplit('/', 1)
 
         # Read book metadata
         book_base, book_slug = book_info.url.rsplit('/', 1)
+        if re.search(r'[^a-zA-Z0-9-]', book_slug):
+            raise ValueError('Invalid characters in slug')
         book, created = Book.objects.get_or_create(slug=book_slug)
 
         if created:
         book, created = Book.objects.get_or_create(slug=book_slug)
 
         if created:
@@ -591,6 +604,9 @@ class Book(models.Model):
                 new_fragment.save()
                 new_fragment.tags = set(book_tags + themes + [book_tag] + ancestor_tags)
 
                 new_fragment.save()
                 new_fragment.tags = set(book_tags + themes + [book_tag] + ancestor_tags)
 
+        if not settings.NO_BUILD_TXT and build_txt:
+            book.build_txt()
+
         if not settings.NO_BUILD_EPUB and build_epub:
             book.root_ancestor.build_epub()
 
         if not settings.NO_BUILD_EPUB and build_epub:
             book.root_ancestor.build_epub()
 
@@ -720,6 +736,12 @@ class FileRecord(models.Model):
     def __unicode__(self):
         return "%s %s.%s" % (self.sha1,  self.slug, self.type)
 
     def __unicode__(self):
         return "%s %s.%s" % (self.sha1,  self.slug, self.type)
 
+###########
+#
+# SIGNALS
+#
+###########
+
 
 def _tags_updated_handler(sender, affected_tags, **kwargs):
     # reset tag global counter
 
 def _tags_updated_handler(sender, affected_tags, **kwargs):
     # reset tag global counter
@@ -737,3 +759,24 @@ def _tags_updated_handler(sender, affected_tags, **kwargs):
         sender.book.reset_theme_counter()
 tags_updated.connect(_tags_updated_handler)
 
         sender.book.reset_theme_counter()
 tags_updated.connect(_tags_updated_handler)
 
+
+def _m2m_changed_handler(sender, instance, action, reverse, pk_set, **kwargs):
+    """ refresh all the short_html stuff on BookMedia delete """
+    if sender == Book.medias.through and reverse and action == 'pre_clear':
+        for book in instance.book_set.all():
+            book.save()
+m2m_changed.connect(_m2m_changed_handler)
+
+def _pre_delete_handler(sender, instance, **kwargs):
+    """ explicitly clear m2m, so that Books can be refreshed """
+    if sender == BookMedia:
+        instance.book_set.clear()
+pre_delete.connect(_pre_delete_handler)
+
+def _post_save_handler(sender, instance, **kwargs):
+    """ refresh all the short_html stuff on BookMedia update """
+    if sender == BookMedia:
+        for book in instance.book_set.all():
+            book.save()
+post_save.connect(_post_save_handler)
+