Beispiel #1
0
        def _toc(depth, toc_items):
            items = []

            for toc_item in toc_items:
                if isinstance(toc_item[1], list):
                    section_title, chapters = toc_item

                    items += [{
                        'type': 'section',
                        'level': depth,
                        'title': section_title,
                        'url_title': booktype_slugify(section_title),
                    }]
                    items += _toc(depth + 1, chapters)
                else:
                    chapter_title, chapter_href = toc_item
                    chapter_item = book.get_item_with_href(chapter_href)
                    content = self._get_chapter_content(chapter_item)
                    content = self._fix_horrible_mpdf(content)

                    href_filename, file_extension = os.path.splitext(
                        chapter_href)
                    items.append({
                        'type': 'chapter',
                        'level': depth,
                        'title': chapter_title,
                        'url_title': booktype_slugify(chapter_title),
                        'href': chapter_href,
                        'href_filename': href_filename,
                        'content': content
                    })

            return items
Beispiel #2
0
        def _toc(depth, toc_items):
            items = []

            for toc_item in toc_items:
                if isinstance(toc_item[1], list):
                    section_title, chapters = toc_item

                    items += [{
                        'type': 'section',
                        'level': depth,
                        'title': section_title,
                        'url_title': booktype_slugify(section_title),
                    }]
                    items += _toc(depth + 1, chapters)
                else:
                    chapter_title, chapter_href = toc_item
                    chapter_item = book.get_item_with_href(chapter_href)
                    content = self._get_chapter_content(chapter_item)
                    content = self._fix_horrible_mpdf(content)

                    href_filename, file_extension = os.path.splitext(chapter_href)
                    items.append({
                        'type': 'chapter',
                        'level': depth,
                        'title': chapter_title,
                        'url_title': booktype_slugify(chapter_title),
                        'href': chapter_href,
                        'href_filename': href_filename,
                        'content': content})

            return items
Beispiel #3
0
        def _toc(depth, toc_items, parent=None, toc_setting=None):
            items = []
            sec_count = 1

            for toc_item in toc_items:
                # SECTIONS
                if isinstance(toc_item[1], list):
                    section_title, chapters = toc_item
                    url_title = booktype_slugify(section_title)

                    # let's build a section key and try to get settings for current section
                    section_key = SectionsSettingsPlugin.build_section_key(url_title, sec_count)
                    section_settings = json.loads(settings_dict.get(section_key, '{}'))
                    toc_setting = section_settings.get('toc', {}).get(self.name, '')

                    # jump to next item (continue) if the whole section should be hidden
                    show_in_outputs = section_settings.get('show_in_outputs', {})
                    show_section_in_current_converter = show_in_outputs.get(self.name, True)
                    if not show_section_in_current_converter:
                        continue

                    toc_item = TocItem({
                        'type': 'section',
                        'level': depth,
                        'title': section_title,
                        'url_title': url_title,
                        'show_in_toc': 'hide_section' not in toc_setting
                    })
                    items.append(toc_item)
                    items += _toc(depth + 1, chapters, section_title, toc_setting)
                    sec_count += 1

                # CHAPTERS
                else:
                    chapter_title, chapter_href = toc_item
                    chapter_item = book.get_item_with_href(chapter_href)
                    content = self._get_chapter_content(chapter_item)
                    content = self._fix_horrible_mpdf(content)

                    href_filename, file_extension = os.path.splitext(chapter_href)

                    if not parent:
                        toc_setting = ''

                    toc_item = TocItem({
                        'type': 'chapter',
                        'level': depth,
                        'title': chapter_title,
                        'url_title': booktype_slugify(chapter_title),
                        'href': chapter_href,
                        'href_filename': href_filename,
                        'content': content,
                        'show_in_toc': 'hide_chapters' not in toc_setting
                    })
                    items.append(toc_item)

            return items
Beispiel #4
0
        def _toc(depth, toc_items, parent=None, toc_setting=None):
            items = []
            sec_count = 1

            for toc_item in toc_items:
                if isinstance(toc_item[1], list):
                    section_title, chapters = toc_item
                    url_title = booktype_slugify(section_title)

                    key = 'section_%s_%s' % (url_title, sec_count)
                    section_settings = json.loads(settings.get(key, '{}'))
                    toc_setting = section_settings.get('toc', {}).get(self.name, '')

                    # continue if the whole section should be hidden
                    show_in_outputs = section_settings.get('show_in_outputs', {})
                    if show_in_outputs.get(self.name, 'true') == 'false':
                        continue

                    items += [{
                        'type': 'section',
                        'level': depth,
                        'title': section_title,
                        'url_title': url_title,
                        'show_in_toc': 'hide_section' not in toc_setting
                    }]
                    items += _toc(depth + 1, chapters, section_title, toc_setting)
                    sec_count += 1
                else:
                    chapter_title, chapter_href = toc_item
                    chapter_item = book.get_item_with_href(chapter_href)
                    content = self._get_chapter_content(chapter_item)
                    content = self._fix_horrible_mpdf(content)

                    href_filename, file_extension = os.path.splitext(chapter_href)

                    if not parent:
                        toc_setting = ''

                    items.append({
                        'type': 'chapter',
                        'level': depth,
                        'title': chapter_title,
                        'url_title': booktype_slugify(chapter_title),
                        'href': chapter_href,
                        'href_filename': href_filename,
                        'content': content,
                        'show_in_toc': 'hide_chapters' not in toc_setting
                    })

            return items
Beispiel #5
0
        def _toc(depth, toc_items, parent=None, toc_setting=None):
            items = []
            sec_count = 1

            for toc_item in toc_items:
                if isinstance(toc_item[1], list):
                    section_title, chapters = toc_item
                    url_title = booktype_slugify(section_title)

                    key = 'section_%s_%s' % (url_title, sec_count)
                    section_settings = json.loads(settings.get(key, '{}'))
                    toc_setting = section_settings.get('toc', {}).get(self.name, '')

                    # continue if the whole section should be hidden
                    show_in_outputs = section_settings.get('show_in_outputs', {})
                    if show_in_outputs.get(self.name, 'true') == 'false':
                        continue

                    items += [{
                        'type': 'section',
                        'level': depth,
                        'title': section_title,
                        'url_title': url_title,
                        'show_in_toc': 'hide_section' not in toc_setting
                    }]
                    items += _toc(depth + 1, chapters, section_title, toc_setting)
                    sec_count += 1
                else:
                    chapter_title, chapter_href = toc_item
                    chapter_item = book.get_item_with_href(chapter_href)
                    content = self._get_chapter_content(chapter_item)
                    content = self._fix_horrible_mpdf(content)

                    href_filename, file_extension = os.path.splitext(chapter_href)

                    if not parent:
                        toc_setting = ''

                    items.append({
                        'type': 'chapter',
                        'level': depth,
                        'title': chapter_title,
                        'url_title': booktype_slugify(chapter_title),
                        'href': chapter_href,
                        'href_filename': href_filename,
                        'content': content,
                        'show_in_toc': 'hide_chapters' not in toc_setting
                    })

            return items
Beispiel #6
0
def upload_attachment(request, bookid, version=None):
    try:
        book = models.Book.objects.get(url_title__iexact=bookid)
    except models.Book.DoesNotExist:
        return views.ErrorPage(
            request, "errors/book_does_not_exist.html", {"book_name": bookid})

    user = request.user
    book_security = security.get_security_for_book(user, book)
    can_upload_attachment = book_security.has_perm('edit.upload_attachment')

    if (not user.is_superuser and not can_upload_attachment and book.owner != user):
        raise PermissionDenied

    book_version = book.get_version(version)
    stat = models.BookStatus.objects.filter(book=book)[0]

    with transaction.atomic():
        file_data = request.FILES['files[]']
        att = models.Attachment(
            version=book_version,
            # must remove this reference
            created=datetime.datetime.now(),
            book=book,
            status=stat
        )
        att.save()

        attName, attExt = os.path.splitext(file_data.name)
        att.attachment.save(
            '{}{}'.format(booktype_slugify(attName), attExt),
            file_data,
            save=False
        )
        att.save()

    response_data = {
        "files": [{
            "url": "http://127.0.0.1/",
            "thumbnail_url": "http://127.0.0.1/",
            "name": "boot.png",
            "type": "image/png",
            "size": 172728,
            "delete_url": "",
            "delete_type": "DELETE"
        }]
    }

    # add cliendID and sputnikID to request object
    # this will allow us to use sputnik and addMessageToChannel
    request.clientID = request.POST['clientID']
    request.sputnikID = "%s:%s" % (request.session.session_key, request.clientID)
    send_notification(request, book.id, book_version.get_version(),
                      "notification_new_attachment_uploaded", att.get_name())

    if "application/json" in request.META['HTTP_ACCEPT']:
        return HttpResponse(json.dumps(response_data),
                            content_type="application/json")
    else:
        return HttpResponse(json.dumps(response_data), content_type="text/html")
Beispiel #7
0
    def validate(self, attrs):
        attrs['book'] = self.context['view']._book
        attrs['content'] = u'<h1>{}</h1><p><br/></p>'.format(attrs['title'])
        attrs['content_json'] = u'''{
        "entityMap": {},
        "blocks": [
          {
            "key": "bm8jb",
            "text": "",
            "type": "datablock",
            "depth": 0,
            "inlineStyleRanges": [],
            "entityRanges": [],
            "data": {}
          },
          {
            "key": "f29sf",
            "text": "Chapter Title",
            "type": "heading1",
            "depth": 0,
            "inlineStyleRanges": [],
            "entityRanges": [],
            "data": {
              "attributes": {
                "style": {}
              }
            }
          },
          {
            "key": "a4d8p",
            "text": "",
            "type": "unstyled",
            "depth": 0,
            "inlineStyleRanges": [],
            "entityRanges": [],
            "data": {}
          }
        ]
        }'''
        attrs['url_title'] = booktype_slugify(attrs['title'])
        attrs['version'] = attrs['book'].version
        attrs['status'] = BookStatus.objects.filter(book=attrs['book']).order_by("-weight")[0]

        # validate title/url_title
        if not len(attrs['url_title']):
            error_msg = {'title': 'Title is empty or contains wrong characters.'}
            logger.warn('ChapterListCreateSerializer validate: {}'.format(error_msg))
            raise serializers.ValidationError(error_msg)

        # validate title/url_title
        chapter_exists = Chapter.objects.filter(
            book=self.context['view']._book, version=attrs['book'].version, url_title=attrs['url_title']
        ).exists()

        if chapter_exists:
            error_msg = {'title': 'Chapter with this title already exists.'}
            logger.warn('ChapterListCreateSerializer validate: {}'.format(error_msg))
            raise serializers.ValidationError(error_msg)

        return attrs
Beispiel #8
0
def books(request):
    books = []
    for title in request.param:
        books.append(
            BookFactory(title=title, url_title=booktype_slugify(title))
        )
    return books
Beispiel #9
0
def upload_attachment(request, bookid, version=None):
    try:
        book = models.Book.objects.get(url_title__iexact=bookid)
    except models.Book.DoesNotExist:
        return views.ErrorPage(
            request, "errors/book_does_not_exist.html", {"book_name": bookid})

    user = request.user
    book_security = security.get_security_for_book(user, book)
    can_upload_attachment = book_security.has_perm('edit.upload_attachment')

    if (not user.is_superuser and not can_upload_attachment and book.owner != user):
        raise PermissionDenied

    book_version = book.get_version(version)
    stat = models.BookStatus.objects.filter(book=book)[0]

    with transaction.atomic():
        file_data = request.FILES['files[]']
        att = models.Attachment(
            version=book_version,
            # must remove this reference
            created=datetime.datetime.now(),
            book=book,
            status=stat
        )
        att.save()

        attName, attExt = os.path.splitext(file_data.name)
        att.attachment.save(
            '{}{}'.format(booktype_slugify(attName), attExt),
            file_data,
            save=False
        )
        att.save()

    response_data = {
        "files": [{
            "url": "http://127.0.0.1/",
            "thumbnail_url": "http://127.0.0.1/",
            "name": "boot.png",
            "type": "image/png",
            "size": 172728,
            "delete_url": "",
            "delete_type": "DELETE"
        }]
    }

    # add cliendID and sputnikID to request object
    # this will allow us to use sputnik and addMessageToChannel
    request.clientID = request.POST['clientID']
    request.sputnikID = "%s:%s" % (request.session.session_key, request.clientID)
    send_notification(request, book.id, book_version.get_version(),
                      "notification_new_attachment_uploaded", att.get_name())

    if "application/json" in request.META['HTTP_ACCEPT']:
        return HttpResponse(json.dumps(response_data),
                            content_type="application/json")
    else:
        return HttpResponse(json.dumps(response_data), content_type="text/html")
Beispiel #10
0
    def _set_cover(self, book, cover_image):
        """
        Assigns the specified cover.
        """

        validity = self.delegate.is_valid_cover(cover_image)

        if validity is None:
            # not checked by the assigned delegate
            is_valid, reason = is_valid_cover(cover_image)
        elif isinstance(validity, bool):
            is_valid, reason = validity, None
        else:
            is_valid, reason = validity

        if not is_valid:
            if reason:
                self.notifier.warning(
                    _("Not using {} as a cover image -- {}").format(
                        cover_image.file_name, reason))
            else:
                self.notifier.warning(
                    _("Not using {} as a cover image").format(
                        cover_image.file_name))
            return

        cover_file = ContentFile(cover_image.get_content())
        file_name = os.path.basename(cover_image.file_name)

        attName, attExt = os.path.splitext(file_name)
        file_name = '{}{}'.format(booktype_slugify(attName), attExt)

        created = datetime.datetime.now()
        title = ''

        h = hashlib.sha1()
        h.update(cover_image.file_name)
        h.update(title)
        h.update(str(created))

        cover = models.BookCover(
            book=book,
            user=book.owner,
            cid=h.hexdigest(),
            title=title,
            filename=file_name[:250],
            width=0,
            height=0,
            approved=False,
            is_book=False,
            is_ebook=True,
            is_pdf=False,
            created=created
        )
        cover.save()

        cover.attachment.save(file_name, cover_file, save=False)
        cover.save()

        self.notifier.info(_("Using {} as cover image").format(file_name))
Beispiel #11
0
    def _set_cover(self, book, cover_image):
        """
        Assigns the specified cover.
        """

        validity = self.delegate.is_valid_cover(cover_image)

        if validity is None:
            # not checked by the assigned delegate
            is_valid, reason = is_valid_cover(cover_image)
        elif isinstance(validity, bool):
            is_valid, reason = validity, None
        else:
            is_valid, reason = validity

        if not is_valid:
            if reason:
                self.notifier.warning(
                    _("Not using {} as a cover image -- {}").format(
                        cover_image.file_name, reason))
            else:
                self.notifier.warning(
                    _("Not using {} as a cover image").format(
                        cover_image.file_name))
            return

        cover_file = ContentFile(cover_image.get_content())
        file_name = os.path.basename(cover_image.file_name)

        attName, attExt = os.path.splitext(file_name)
        file_name = '{}{}'.format(booktype_slugify(attName), attExt)

        created = datetime.datetime.now()
        title = ''

        h = hashlib.sha1()
        h.update(cover_image.file_name)
        h.update(title)
        h.update(str(created))

        cover = models.BookCover(
            book=book,
            user=book.owner,
            cid=h.hexdigest(),
            title=title,
            filename=file_name[:250],
            width=0,
            height=0,
            approved=False,
            is_book=False,
            is_ebook=True,
            is_pdf=False,
            created=created
        )
        cover.save()

        cover.attachment.save(file_name, cover_file, save=False)
        cover.save()

        self.notifier.info(_("Using {} as cover image").format(file_name))
Beispiel #12
0
    def clean_name(self):
        new_url_name = booktype_slugify(self.cleaned_data['name'])
        group_data_url_name = BookiGroup.objects.filter(url_name=new_url_name).exclude(pk=self.instance.pk)

        if len(group_data_url_name) > 0:
            raise ValidationError(_('Group name is already in use'))

        return self.cleaned_data.get('name', '')
Beispiel #13
0
    def clean_name(self):
        new_url_name = booktype_slugify(self.cleaned_data['name'])
        group_data_url_name = BookiGroup.objects.filter(url_name=new_url_name).exclude(pk=self.instance.pk)

        if len(group_data_url_name) > 0:
            raise ValidationError(_('Group name is already in use'))

        return self.cleaned_data.get('name', '')
Beispiel #14
0
    def _get_section_key(self, title, count):
        """
        Generates a key to get the section settings

        :Args:
          - `String` title
          - `Integer` count to make it unique
        """
        return "section_%s_%s" % (booktype_slugify(title), count)
Beispiel #15
0
    def form_valid(self, form):
        logger.debug('ImporterView::form_valid')

        book_file = form.cleaned_data.get('book_file')
        ext = get_file_extension(book_file.name)

        logger.debug('ImporterView::Importing file extension is "{}".'.format(ext.encode('utf8')))

        default_book_title = self.get_default_title(book_file, ext)
        book_title = form.cleaned_data.get('book_title', default_book_title)

        logger.debug('ImporterView::book_title="{}" default_book_title="{}".'.format(
            book_title.encode('utf8'), default_book_title.encode('utf8')))

        # in case book title in form is empty string
        if len(book_title) == 0:
            book_title = default_book_title

        if not check_book_availability(book_title):
            registered = Book.objects.filter(title__startswith=book_title).count()
            book_title = '%s %s' % (book_title, registered)
            logger.debug('ImporterView::Checking book availability: "{}".'.format(book_title.encode('utf8')))

        book_url = booktype_slugify(book_title)
        book = create_book(self.request.user, book_title, book_url=book_url)
        logger.debug('ImporterView::Book created with url title="{}".'.format(book_url))

        # check if book will be hidden and set to book
        book.hidden = form.cleaned_data.get('hidden')
        book.save()

        notifier = CollectNotifier()
        delegate = Delegate()
        response = {}

        try:
            book_importer = importer_utils.get_importer_module(ext)
        except KeyError:
            logger.error('ImporterView::No importer for this extension')
            response_data = dict(errors=[ugettext('Extension not supported!')])
            return self.render_json_response(response_data)

        try:
            book_importer(
                book_file, book, notifier=notifier, delegate=delegate)
            logger.debug('ImporterView::Book imported.')
            response['url'] = reverse('reader:infopage', args=[book.url_title])
        except Exception as e:
            logger.error('ImporterView::Some kind of error while importing book.')
            logger.exception(e)
            notifier.errors.append(str(e))

        response['infos'] = notifier.infos
        response['warnings'] = notifier.warnings
        response['errors'] = notifier.errors

        return self.render_json_response(response)
Beispiel #16
0
    def build_section_key(title, count):
        """
        Generates a key to get/set the section settings

        :Args:
          - `str` title
          - `int` count to make it unique
        """
        return 'section_%s_%s' % (booktype_slugify(title), count)
Beispiel #17
0
    def build_section_key(title, count):
        """
        Generates a key to get/set the section settings

        :Args:
          - `str` title
          - `int` count to make it unique
        """
        return 'section_%s_%s' % (booktype_slugify(title), count)
Beispiel #18
0
 def _make_url_title(title, i=0):
     url_title = booktype_slugify(title)
     if i > 0:
         url_title += "_" + str(i)
     if url_title not in url_titles:
         url_titles.append(url_title)
         return url_title
     else:
         return _make_url_title(title, i + 1)
Beispiel #19
0
 def _make_url_title(title, i=0):
     url_title = booktype_slugify(title)
     if i > 0:
         url_title += "_" + str(i)
     if url_title not in url_titles:
         url_titles.append(url_title)
         return url_title
     else:
         return _make_url_title(title, i + 1)
Beispiel #20
0
    def _get_section_key(self, title, count):
        """
        Generates a key to get the section settings

        :Args:
          - `String` title
          - `Integer` count to make it unique
        """
        return 'section_%s_%s' % (booktype_slugify(title), count)
Beispiel #21
0
def import_based_on_epub(epub_file, book_dest):
    """
    It will import an epub file into a existent book on the system.
    This will also try to import sections settings and stuff

    Keyword arguments:
        epub_file -- EPUB file to be imported into book_dest
        book_dest -- Destiny book

    TODO: add docstrings of return info
    """

    notifier = CollectNotifier()
    delegate = Delegate()

    epub_importer = EpubImporter()
    epub_importer.notifier = notifier
    epub_importer.delegate = delegate

    result = {}

    try:
        epub_book = epub_importer.import_file(epub_file, book_dest)
    except Exception as e:
        epub_book = None
        logger.error('ImporterView::Some kind of error while importing book.')
        logger.exception(e)
        notifier.errors.append(str(e))

    # let's try to save sections settings
    if epub_book is not None:
        settings_dict = get_sections_settings(epub_book)
        book_dest_version = book_dest.get_version(None)
        sec_count = 1

        for toc_item in book_dest_version.get_toc():
            if toc_item.is_section():
                url_title = booktype_slugify(toc_item.name)
                section_key = SectionsSettingsPlugin.build_section_key(
                    url_title, sec_count)
                section_settings = settings_dict.get(section_key, None)

                if section_settings is not None:
                    toc_item.settings = section_settings
                    toc_item.save()

                    sec_count += 1

    result['infos'] = notifier.infos
    result['warnings'] = notifier.warnings
    result['errors'] = notifier.errors

    return result
Beispiel #22
0
def convert_file_name(file_name):
    name = os.path.basename(file_name)

    if name.rfind('.') != -1:
        _np = name[:name.rfind('.')]
        _ext = name[name.rfind('.'):]
        name = booktype_slugify(_np) + _ext

    name = urllib.unquote(name)
    name = name.replace(' ', '_')

    return name
Beispiel #23
0
def convert_file_name(file_name):
    name = os.path.basename(file_name)

    if name.rfind('.') != -1:
        _np = name[:name.rfind('.')]
        _ext = name[name.rfind('.'):]
        name = booktype_slugify(_np)+_ext

    name = urllib.unquote(name)
    name = name.replace(' ', '_')

    return name
Beispiel #24
0
def makeTitleUnique(requestedTitle):
    """If <requestedTitle> is unused, return that. Otherwise,
    return a title in the form `u'%s - %d' % (requestedTitle, n)`
    where n is the lowest non-clashing positive integer.
    """
    n = 0
    name = requestedTitle
    while True:
        titles = models.Book.objects.filter(title=name).count()
        urls = models.Book.objects.filter(url_title=booktype_slugify(name)).count()
        if not titles and not urls:
            return name
        n += 1
        name = u'%s - %d' % (requestedTitle, n)
Beispiel #25
0
def makeTitleUnique(requestedTitle):
    """If <requestedTitle> is unused, return that. Otherwise,
    return a title in the form `u'%s - %d' % (requestedTitle, n)`
    where n is the lowest non-clashing positive integer.
    """
    n = 0
    name = requestedTitle
    while True:
        titles = models.Book.objects.filter(title=name).count()
        urls = models.Book.objects.filter(url_title=booktype_slugify(name)).count()
        if not titles and not urls:
            return name
        n += 1
        name = u'%s - %d' % (requestedTitle, n)
Beispiel #26
0
    def save_settings(self, request):
        group = self.save(commit=False)
        group.url_name = misc.booktype_slugify(group.name)
        group.created = timezone.now()
        group.save()

        # auto-join owner as team member
        group.members.add(request.user)

        # set group image if exists in post data
        group_image = self.files.get('group_image', None)
        if group_image:
            self.set_group_image(group.pk, group_image)

        return group
Beispiel #27
0
    def save_settings(self, request):
        group = self.save(commit=False)
        group.url_name = misc.booktype_slugify(group.name)
        group.created = timezone.now()
        group.save()

        # auto-join owner as team member
        group.members.add(request.user)

        # set group image if exists in post data
        group_image = self.files.get('group_image', None)
        if group_image:
            self.set_group_image(group.pk, group_image)

        return group
Beispiel #28
0
    def form_valid(self, form):
        group = form.save(commit=False)
        group.owner = self.request.user
        group.url_name = booktype_slugify(group.name)
        group.created = timezone.now()
        group.save()

        # auto-join owner as team member
        group.members.add(self.request.user)

        # set group image if exists in post data
        group_image = form.files.get('group_image', None)
        if group_image:
            form.set_group_image(group.pk, group_image)

        return super(GroupCreateView, self).form_valid(form)
Beispiel #29
0
    def form_valid(self, form):
        group = form.save(commit=False)
        group.owner = self.request.user
        group.url_name = booktype_slugify(group.name)
        group.created = timezone.now()
        group.save()

        # auto-join owner as team member
        group.members.add(self.request.user)

        # set group image if exists in post data
        group_image = form.files.get('group_image', None)
        if group_image:
            form.set_group_image(group.pk, group_image)

        return super(GroupCreateView, self).form_valid(form)
Beispiel #30
0
    def create(self, validated_data):
        n = Book.objects.count()
        book_title = validated_data['title']
        owner = validated_data['owner']
        url_title = '%s-%s' % (n, booktype_slugify(book_title))

        book = create_book(owner, book_title, book_url=url_title)
        book.language = validated_data.get('language', None)
        book.save()

        import_book_url = validated_data.get('import_book_url')
        import_format = validated_data.get('import_book_format')

        if import_book_url:
            book_file = self._get_book_file(import_book_url)
            try:
                book_importer = importer_utils.get_importer_module(
                    import_format)
            except Exception as err:
                error = "Wrong importer format {}".format(err)
                logger.warn('BookCreateSerializer create: {}'.format(error))
                raise serializers.ValidationError(error)

            delegate = Delegate()
            notifier = CollectNotifier()

            try:
                book_importer(book_file,
                              book,
                              notifier=notifier,
                              delegate=delegate)
            except Exception as err:
                error_msg = "Unexpected error while importing the file {}".format(
                    err)
                logger.warn(
                    'BookCreateSerializer create: {}'.format(error_msg))
                raise APIException(error_msg)

            if len(notifier.errors) > 0:
                err = "\n".join(notifier.errors)
                error_msg = "Something went wrong: {}".format(err)
                logger.warn(
                    'BookCreateSerializer create: {}'.format(error_msg))
                raise APIException(error_msg)

        return book
Beispiel #31
0
    def _set_sections_settings(self):
        """
        Stores the sections settings inside the book metadata that would be
        used by converter scripts. Using metadata give us the advantage of being
        still generating a valid epub in case these settings are not removed.

        :Args:
          - self (:class:`ExportBook`): current class instance
        """

        from booktype.utils.misc import booktype_slugify

        settings = {}
        count = 1
        for item in self.book_version.get_toc():
            if item.is_section() and item.settings:
                settings['section_%s_%s' % (booktype_slugify(item.name), count)] = item.settings
                count += 1

        self.epub_book.add_metadata(
            None, 'meta', json.dumps(settings), {'property': 'bkterms:sections_settings'})
Beispiel #32
0
    def _set_sections_settings(self):
        """
        Stores the sections settings inside the book metadata that would be
        used by converter scripts. Using metadata give us the advantage of being
        still generating a valid epub in case these settings are not removed.

        :Args:
          - self (:class:`ExportBook`): current class instance
        """

        from booktype.utils.misc import booktype_slugify

        settings = {}
        count = 1
        for item in self.book_version.get_toc():
            if item.is_section() and item.settings:
                settings['section_%s_%s' % (booktype_slugify(item.name), count)] = item.settings
                count += 1

        self.epub_book.add_metadata(
            None, 'meta', json.dumps(settings), {'property': 'bkterms:sections_settings'})
Beispiel #33
0
    def create(self, validated_data):
        n = Book.objects.count()
        book_title = validated_data['title']
        owner = validated_data['owner']
        url_title = '%s-%s' % (n, booktype_slugify(book_title))

        book = create_book(owner, book_title, book_url=url_title)
        book.language = validated_data.get('language', None)
        book.save()

        import_book_url = validated_data.get('import_book_url')
        import_format = validated_data.get('import_book_format')

        if import_book_url:
            book_file = self._get_book_file(import_book_url)
            try:
                book_importer = importer_utils.get_importer_module(import_format)
            except Exception as err:
                error = "Wrong importer format {}".format(err)
                logger.warn('BookCreateSerializer create: {}'.format(error))
                raise serializers.ValidationError(error)

            delegate = Delegate()
            notifier = CollectNotifier()

            try:
                book_importer(book_file, book, notifier=notifier, delegate=delegate)
            except Exception as err:
                error_msg = "Unexpected error while importing the file {}".format(err)
                logger.warn('BookCreateSerializer create: {}'.format(error_msg))
                raise APIException(error_msg)

            if len(notifier.errors) > 0:
                err = "\n".join(notifier.errors)
                error_msg = "Something went wrong: {}".format(err)
                logger.warn('BookCreateSerializer create: {}'.format(error_msg))
                raise APIException(error_msg)

        return book
Beispiel #34
0
    def _import_book(self, epub_book, book):
        titles = {}
        toc = []

        def _parse_toc(elements, parent=None):
            for _elem in elements:
                # used later to get parent of an elem
                unique_id = uuid.uuid4().hex

                if isinstance(_elem, tuple):
                    toc.append((1, _elem[0].title, unique_id, parent))
                    _parse_toc(_elem[1], unique_id)
                elif isinstance(_elem, ebooklib.epub.Link):
                    _urlp = urlparse.urlparse(_elem.href)
                    _name = os.path.normpath(urllib.unquote(_urlp.path))

                    # check in case _name is an empty string
                    if not _name:
                        _name = _elem.title

                    if _name not in titles:
                        titles[_name] = _elem.title
                        toc.append((0, _name, unique_id, parent))

        _parse_toc(epub_book.toc)
        self.notifier.debug("TOC structure: \n{}".format(
            pprint.pformat(toc, indent=4)))

        now = datetime.datetime.utcnow().replace(tzinfo=utc)
        default_status = get_default_book_status()
        stat = models.BookStatus.objects.filter(book=book,
                                                name=default_status)[0]

        # assign cover image if there is one
        cover_image = get_cover_image(epub_book)
        if cover_image:
            self._set_cover(book, cover_image)

        # import all images in the EPUB
        for image in epub_book.get_items_of_type(ebooklib.ITEM_IMAGE):
            if image == cover_image:
                continue

            if not self.delegate.should_import_image(image):
                continue

            name = os.path.normpath(image.file_name)
            att = models.Attachment(book=book,
                                    version=book.version,
                                    status=stat)

            with ContentFile(image.get_content()) as content_file:
                attName, attExt = os.path.splitext(os.path.basename(name))

                att.attachment.save('{}{}'.format(booktype_slugify(attName),
                                                  attExt),
                                    content_file,
                                    save=False)
                att.save()

            self._attachments[name] = att

            self.notifier.debug("Imported image: {} -> {}".format(image, att))

        # URL titles assigned so far
        url_titles = []

        def _make_url_title(title, i=0):
            url_title = booktype_slugify(title)
            if i > 0:
                url_title += "_" + str(i)
            if url_title not in url_titles:
                url_titles.append(url_title)
                return url_title
            else:
                return _make_url_title(title, i + 1)

        # import all document items from the EPUB
        for document in epub_book.get_items_of_type(ebooklib.ITEM_DOCUMENT):
            # Nav and Cover are not imported
            if not document.is_chapter():
                continue

            if not self.delegate.should_import_document(document):
                continue

            name = os.path.normpath(document.file_name)
            title = ''

            # maybe this part has to go to the plugin
            # but you can not get title from <title>
            if name in titles:
                title = titles[name]
            else:
                title = convert_file_name(name)

                if title.rfind('.') != -1:
                    title = title[:title.rfind('.')]

                title = title.replace('.', '')

            url_title = _make_url_title(title)
            content = self._create_content(document, title)

            chapter = models.Chapter(book=book,
                                     version=book.version,
                                     url_title=url_title,
                                     title=title,
                                     status=stat,
                                     content=content,
                                     created=now,
                                     modified=now)
            chapter.save()

            # time to save revisions correctly
            history = logChapterHistory(chapter=chapter,
                                        content=chapter.content,
                                        user=book.owner,
                                        comment='',
                                        revision=chapter.revision)

            if history:
                logBookHistory(book=book,
                               version=book.version,
                               chapter=chapter,
                               chapter_history=history,
                               user=book.owner,
                               kind='chapter_create')

            self._chapters[name] = chapter

            self.notifier.debug("Imported chapter: {} -> {}".format(
                document, chapter))

        # fix links to chapters
        for file_name, chapter in self._chapters.iteritems():
            self._fix_links(chapter, base_path=os.path.dirname(file_name))

        # create TOC objects
        self._make_toc(book, toc)
Beispiel #35
0
    def _fix_links(self, chapter, base_path):
        """
        Fixes internal links so they point to chapter URLs
        """
        try:
            tree = ebooklib.utils.parse_html_string(chapter.content)
        except:
            return

        body = tree.find('body')

        if body is None:
            return

        to_save = False

        for anchor in body.iter('a'):
            href = anchor.get('href')

            if href is None:
                continue

            urlp = urlparse.urlparse(href)
            name = os.path.normpath(
                os.path.join(base_path, urllib.unquote(urlp.path)))

            if name in self._chapters:
                title = self._chapters[name].url_title
                fixed_href = urlparse.urljoin(href, '../{}/'.format(title))

                if urlp.fragment:
                    fixed_href = "{}#{}".format(fixed_href, urlp.fragment)

                anchor.set('href', fixed_href)
                to_save = True

        for image in body.iter('img'):
            src = image.get('src')

            if src is None:
                continue

            urlp = urlparse.urlparse(src)
            name = os.path.normpath(
                os.path.join(base_path, urllib.unquote(urlp.path)))

            if urlp.netloc:
                continue

            if name in self._attachments:
                file_name = os.path.basename(
                    self._attachments[name].attachment.name)
                attName, attExt = os.path.splitext(file_name)

                fixed_src = urllib.quote('static/{}{}'.format(
                    booktype_slugify(attName), attExt))
                image.set('src', fixed_src)
                to_save = True

        if to_save:
            chapter.content = etree.tostring(tree,
                                             pretty_print=True,
                                             encoding='utf-8',
                                             xml_declaration=True)
            chapter.save()
Beispiel #36
0
def importBookFromFile(user, zname, createTOC=False, **extraOptions):
    """Create a new book from a bookizip filename"""

    from booki.utils.log import logChapterHistory

    # unzip it
    zf = zipfile.ZipFile(zname)
    # load info.json
    info = json.loads(zf.read('info.json'))
    logWarning("Loaded json file %r" % info)

    metadata = info['metadata']
    manifest = info['manifest']
    TOC =      info['TOC']

    if extraOptions.get('book_title', None):
        bookTitle = extraOptions['book_title']
    else:
        bookTitle = get_metadata(metadata, 'title', ns=DC)[0]

    bookTitle = makeTitleUnique(bookTitle)
    logWarning("Chose unique book title %r" % bookTitle)

    if extraOptions.get('book_url', None):
        bookURL = extraOptions['book_url']
    else:
        bookURL = None

    book = create_book(user, bookTitle, status = "new", bookURL = bookURL)

    if extraOptions.get("hidden"):
        book.hidden = True
        book.save()

    # this is for Table of Contents
    p = re.compile('\ssrc="(.*)"')

    # what if it does not have status "new"
    stat = models.BookStatus.objects.filter(book=book, name="new")[0]

    chapters = getChaptersFromTOC(TOC)
    n = len(chapters) + 1 #is +1 necessary?
    now = datetime.datetime.now()

    for chapterName, chapterFile, is_section in chapters:
        urlName = booktype_slugify(chapterName)

        if is_section: # create section
            if createTOC:
                c = models.BookToc(book = book,
                                   version = book.version,
                                   name = chapterName,
                                   chapter = None,
                                   weight = n,
                                   typeof = 2)
                c.save()
                n -= 1
        else: # create chapter
            # check if i can open this file at all
            content = zf.read(chapterFile)

            #content = p.sub(r' src="../\1"', content)

            chapter = models.Chapter(book = book,
                                     version = book.version,
                                     url_title = urlName,
                                     title = chapterName,
                                     status = stat,
                                     content = content,
                                     created = now,
                                     modified = now)
            chapter.save()

            history = logChapterHistory(chapter = chapter,
                                        content = content,
                                        user = user,
                                        comment = "",
                                        revision = chapter.revision)

            if createTOC:
                c = models.BookToc(book = book,
                                   version = book.version,
                                   name = chapterName,
                                   chapter = chapter,
                                   weight = n,
                                   typeof = 1)
                c.save()
                n -= 1

    stat = models.BookStatus.objects.filter(book=book, name="new")[0]

    from django.core.files import File

    for item in manifest.values():
        if item["mimetype"] != 'text/html':
            attachmentName = item['url']

            if attachmentName.startswith("static/"):
                att = models.Attachment(book = book,
                                        version = book.version,
                                        status = stat)

                s = zf.read(attachmentName)
                f = StringIO(s)
                f2 = File(f)
                f2.size = len(s)
                att.attachment.save(os.path.basename(attachmentName), f2, save=False)
                att.save()
                f.close()

    # metadata
    for namespace in metadata:
        # namespace is something like "http://purl.org/dc/elements/1.1/" or ""
        # in the former case, preepend it to the name, in {}.
        ns = ('{%s}' % namespace if namespace else '')
        for keyword, schemes in metadata[namespace].iteritems():
            for scheme, values in schemes.iteritems():
                #schema, if it is set, describes the value's format.
                #for example, an identifier might be an ISBN.
                sc = ('{%s}' % scheme if scheme else '')
                key = "%s%s%s" % (ns, keyword, sc)
                for v in values:
                    if not v: continue
                    try:
                        info = models.Info(book=book, name=key)
                        if len(v) >= 2500:
                            info.value_text = v
                            info.kind = 2
                        else:
                            info.value_string = v
                            info.kind = 0
                        info.save()
                    except:
                        # For now just ignore any kind of error here.
                        # Considering we don't handle metadata as we
                        # should it is not such a problem.
                        pass

    zf.close()

    return book
Beispiel #37
0
def exportBook(book_version):
    from booki import bookizip
    import time
    starttime = time.time()

    (zfile, zname) = tempfile.mkstemp()

    spine = []
    toc_top = []
    toc_current = toc_top
    waiting_for_url = []

    info = {
        "version": 1,
        "TOC": toc_top,
        "spine": spine,
        "metadata": _format_metadata(book_version.book),
        "manifest": {}
    }

    bzip = bookizip.BookiZip(zname, info=info)
    chapter_n = 1

    for i, chapter in enumerate(
            models.BookToc.objects.filter(
                version=book_version).order_by("-weight")):
        if chapter.chapter:
            # It's a real chapter! With content!
            try:
                content = _fix_content(book_version.book, chapter, chapter_n)
            except:
                continue

            chapter_n += 1
            ID = "ch%03d_%s" % (i, chapter.chapter.url_title.encode('utf-8'))
            filename = ID + '.html'

            toc_current.append({
                "title": chapter.chapter.title,
                "url": filename,
                "type": "chapter",
                "role": "text"
            })

            # If this is the first chapter in a section, lend our url
            # to the section, which has no content and thus no url of
            # its own.  If this section was preceded by an empty
            # section, it will be waiting too, hence "while" rather
            # than "if".
            while waiting_for_url:
                section = waiting_for_url.pop()
                section["url"] = filename

            bzip.add_to_package(ID, filename, content, "text/html")
            spine.append(ID)

        else:
            #A new top level section.
            title = chapter.name.encode("utf-8")
            ID = "s%03d_%s" % (i, booktype_slugify(unicode(title)))

            toc_current = []
            section = {
                "title": title,
                "url": '',
                "type": "booki-section",
                "children": toc_current
            }

            toc_top.append(section)
            waiting_for_url.append(section)

    #Attachments are images (and perhaps more).  They do not know
    #whether they are currently in use, or what chapter they belong
    #to, so we add them all.
    #XXX scan for img links while adding chapters, and only add those.

    for i, attachment in enumerate(
            models.Attachment.objects.filter(version=book_version)):
        try:
            f = open(attachment.attachment.name, "rb")
            blob = f.read()
            f.close()
        except (IOError, OSError), e:
            msg = "couldn't read attachment %s" % e
            logWarning(msg)
            continue

        fn = os.path.basename(attachment.attachment.name.encode("utf-8"))

        ID = "att%03d_%s" % (i, booktype_slugify(unicode(fn)))
        if '.' in fn:
            _, ext = fn.rsplit('.', 1)
            mediatype = bookizip.MEDIATYPES.get(ext.lower(),
                                                bookizip.MEDIATYPES[None])
        else:
            mediatype = bookizip.MEDIATYPES[None]

        bzip.add_to_package(ID, "static/%s" % fn, blob, mediatype)
Beispiel #38
0
    def post(self, request, *args, **kwargs):
        # TODO test it and cover with tests
        book_security = BookSecurity(request.user, self._get_book())
        user = request.user
        can_upload_attachment = book_security.has_perm(
            'edit.upload_attachment')

        if not user.is_superuser and not can_upload_attachment and self._book.owner != user:
            raise PermissionDenied

        stat = BookStatus.objects.filter(book=self._book)[0]

        if 'file' not in request.FILES:
            raise ValidationError({'file': ['"file" is required.']})

        file_data = request.FILES['file']
        attname, attext = os.path.splitext(file_data.name)
        available_extensions = ('jpg', 'png', 'jpeg', 'gif')
        if attext.rsplit('.', 1)[-1].lower() not in available_extensions:
            raise ValidationError({
                'file': [
                    'Not supported extension. Available extensions: {}'.format(
                        ' '.join(available_extensions))
                ]
            })

        with transaction.atomic():
            att = Attachment(
                version=self._book.version,
                # must remove this reference
                created=datetime.datetime.now(),
                book=self._book,
                status=stat)
            att.save()

            att.attachment.save('{}{}'.format(booktype_slugify(attname),
                                              attext),
                                file_data,
                                save=False)
            att.save()

        # notificatoin message
        channel_name = "/booktype/book/{}/{}/".format(
            self._book.id, self._book.version.get_version())
        clnts = sputnik.smembers(
            "sputnik:channel:{}:channel".format(channel_name))

        message = {
            'channel': channel_name,
            'command': 'notification',
            'message': 'notification_new_attachment_uploaded',
            'username': self.request.user.username,
            'message_args': (att.get_name(), )
        }

        for c in clnts:
            if c.strip() != '':
                sputnik.push("ses:%s:messages" % c, json.dumps(message))

        # response
        serializer_instance = self.serializer_class(att)

        return Response(serializer_instance.data,
                        status=status.HTTP_201_CREATED)
Beispiel #39
0
 def clean_url_title(self):
     url_title = self.cleaned_data['url_title']
     if not url_title:
         return misc.booktype_slugify(self.cleaned_data['title'])
     return url_title
Beispiel #40
0
 def _get_section_key(self, title, count):
     return 'section_%s_%s' % (booktype_slugify(title), count)
Beispiel #41
0
    def _fix_links(self, chapter, base_path):
        """
        Fixes internal links so they point to chapter URLs
        """
        try:
            tree = ebooklib.utils.parse_html_string(chapter.content)
        except:
            return

        body = tree.find('body')

        if body is None:
            return

        to_save = False

        for anchor in body.iter('a'):
            href = anchor.get('href')

            if href is None:
                continue

            urlp = urlparse.urlparse(href)
            name = os.path.normpath(
                os.path.join(base_path, urllib.unquote(urlp.path)))

            if name in self._chapters:
                title = self._chapters[name].url_title
                fixed_href = urlparse.urljoin(href, '../{}/'.format(title))

                if urlp.fragment:
                    fixed_href = "{}#{}".format(fixed_href, urlp.fragment)

                anchor.set('href', fixed_href)
                to_save = True

        for image in body.iter('img'):
            src = image.get('src')

            if src is None:
                continue

            urlp = urlparse.urlparse(src)
            name = os.path.normpath(
                os.path.join(base_path, urllib.unquote(urlp.path)))

            if urlp.netloc:
                continue

            if name in self._attachments:
                file_name = os.path.basename(
                    self._attachments[name].attachment.name)
                attName, attExt = os.path.splitext(file_name)

                fixed_src = urllib.quote(
                    'static/{}{}'.format(booktype_slugify(attName), attExt))
                image.set('src', fixed_src)
                to_save = True

        if to_save:
            chapter.content = etree.tostring(
                tree, pretty_print=True,
                encoding='utf-8', xml_declaration=True
            )
            chapter.save()
Beispiel #42
0
    def _import_chapters(self, book, chapters):
        now = datetime.datetime.now()
        default_status = get_default_book_status()
        stat = models.BookStatus.objects.filter(book=book,
                                                name=default_status)[0]
        n = 100

        for chapter_title, chapter_content in chapters:
            if len(chapter_title) > 100:
                chapter_title = u'{}...'.format(chapter_title[:100])

            if chapter_title == '':
                chapter_title = _('Title Page') if n == 100 else _('Title')

            chapter_n = 0
            possible_title = chapter_title

            while True:
                does_exists = models.Chapter.objects.filter(
                    book=book,
                    version=book.version,
                    url_title=booktype_slugify(possible_title)).exists()

                if does_exists:
                    chapter_n += 1
                    possible_title = u'{} - {}'.format(chapter_title,
                                                       chapter_n)
                else:
                    break

            if chapter_content[6:-8].strip() == '':
                continue

            _content = self._parse_chapter(chapter_content)
            try:
                chapter_content = unidecode(_content)[6:-8]
            except UnicodeDecodeError:
                chapter_content = _content.decode('utf-8',
                                                  errors='ignore')[6:-8]
            except Exception as err:
                chapter_content = 'Error parsing chapter content'
                logger.exception(
                    "Error while decoding chapter content {0}".format(err))

            chapter = models.Chapter(
                book=book,
                version=book.version,
                url_title=booktype_slugify(possible_title),
                title=possible_title,
                status=stat,
                content=chapter_content,
                created=now,
                modified=now)
            chapter.save()

            toc_item = models.BookToc(book=book,
                                      version=book.version,
                                      name=chapter.title,
                                      chapter=chapter,
                                      weight=n,
                                      typeof=1)
            toc_item.save()
            n -= 1

            self._save_history_records(book, chapter)
Beispiel #43
0
    def _import_chapters(self, book, chapters):
        now = datetime.datetime.now()
        stat = models.BookStatus.objects.filter(book=book, name="new")[0]
        n = 100

        for chapter_title, chapter_content in chapters:
            if len(chapter_title) > 100:
                chapter_title = u'{}...'.format(chapter_title[:100])

            if chapter_title == '':
                if n == 100:
                    chapter_title = _('Title Page')
                else:
                    chapter_title = _('Title')

            chapter_n = 0
            possible_title = chapter_title

            while True:
                does_exists = models.Chapter.objects.filter(
                    book=book, version=book.version,
                    url_title=booktype_slugify(possible_title)
                ).exists()

                if does_exists:
                    chapter_n += 1
                    possible_title = u'{} - {}'.format(
                        chapter_title, chapter_n)
                else:
                    break

            if chapter_content[6:-8].strip() == '':
                continue

            chapter_content = unidecode(self._parse_chapter(chapter_content))

            chapter = models.Chapter(
                book=book,
                version=book.version,
                url_title=booktype_slugify(possible_title),
                title=possible_title,
                status=stat,
                content=chapter_content[6:-8],
                created=now,
                modified=now
            )
            chapter.save()

            toc_item = models.BookToc(
                book=book,
                version=book.version,
                name=chapter.title,
                chapter=chapter,
                weight=n,
                typeof=1
            )
            toc_item.save()
            n -= 1

            # time to save revisions correctly
            history = logChapterHistory(
                chapter=chapter,
                content=chapter.content,
                user=book.owner,
                comment='',
                revision=chapter.revision
            )

            if history:
                logBookHistory(
                    book=book,
                    version=book.version,
                    chapter=chapter,
                    chapter_history=history,
                    user=book.owner,
                    kind='chapter_create'
                )
Beispiel #44
0
    def post(self, request, *args, **kwargs):
        # TODO test it and cover with tests
        book_security = BookSecurity(request.user, self._get_book())
        user = request.user
        can_upload_attachment = book_security.has_perm('edit.upload_attachment')

        if not user.is_superuser and not can_upload_attachment and self._book.owner != user:
            raise PermissionDenied

        stat = BookStatus.objects.filter(book=self._book)[0]

        if 'file' not in request.FILES:
            raise ValidationError({'file': ['"file" is required.']})

        file_data = request.FILES['file']
        attname, attext = os.path.splitext(file_data.name)
        available_extensions = ('jpg', 'png', 'jpeg', 'gif')
        if attext.rsplit('.', 1)[-1].lower() not in available_extensions:
            raise ValidationError({'file': [
                'Not supported extension. Available extensions: {}'.format(
                    ' '.join(available_extensions))
            ]})

        with transaction.atomic():
            att = Attachment(
                version=self._book.version,
                # must remove this reference
                created=datetime.datetime.now(),
                book=self._book,
                status=stat
            )
            att.save()

            att.attachment.save(
                '{}{}'.format(booktype_slugify(attname), attext),
                file_data,
                save=False
            )
            att.save()

        # notificatoin message
        channel_name = "/booktype/book/{}/{}/".format(self._book.id,
                                                      self._book.version.get_version())
        clnts = sputnik.smembers(
            "sputnik:channel:{}:channel".format(channel_name))

        message = {
            'channel': channel_name,
            'command': 'notification',
            'message': 'notification_new_attachment_uploaded',
            'username': self.request.user.username,
            'message_args': (att.get_name(),)
        }

        for c in clnts:
            if c.strip() != '':
                sputnik.push("ses:%s:messages" % c, json.dumps(message))

        # response
        serializer_instance = self.serializer_class(att)

        return Response(serializer_instance.data, status=status.HTTP_201_CREATED)
Beispiel #45
0
    def _import_chapters(self, book, chapters):
        now = datetime.datetime.now()
        stat = models.BookStatus.objects.filter(book=book, name="new")[0]
        n = 100

        for chapter_title, chapter_content in chapters:
            if len(chapter_title) > 100:
                chapter_title = u'{}...'.format(chapter_title[:100])

            if chapter_title == '':
                if n == 100:
                    chapter_title = _('Title Page')
                else:
                    chapter_title = _('Title')

            chapter_n = 0
            possible_title = chapter_title

            while True:
                does_exists = models.Chapter.objects.filter(
                    book=book,
                    version=book.version,
                    url_title=booktype_slugify(possible_title)).exists()

                if does_exists:
                    chapter_n += 1
                    possible_title = u'{} - {}'.format(chapter_title,
                                                       chapter_n)
                else:
                    break

            if chapter_content[6:-8].strip() == '':
                continue

            chapter_content = self._parse_chapter(chapter_content)

            chapter = models.Chapter(
                book=book,
                version=book.version,
                url_title=booktype_slugify(possible_title),
                title=possible_title,
                status=stat,
                content=chapter_content[6:-8],
                created=now,
                modified=now)
            chapter.save()

            toc_item = models.BookToc(book=book,
                                      version=book.version,
                                      name=chapter.title,
                                      chapter=chapter,
                                      weight=n,
                                      typeof=1)
            toc_item.save()
            n -= 1

            # time to save revisions correctly
            history = logChapterHistory(chapter=chapter,
                                        content=chapter.content,
                                        user=book.owner,
                                        comment='',
                                        revision=chapter.revision)

            if history:
                logBookHistory(book=book,
                               version=book.version,
                               chapter=chapter,
                               chapter_history=history,
                               user=book.owner,
                               kind='chapter_create')
Beispiel #46
0
    def form_valid(self, form):
        logger.debug('ImporterView::form_valid')

        book_file = self.request.FILES['book_file']
        ext = self.file_extension(book_file.name)

        logger.debug('ImporterView::Importing file extension is "{}".'.format(
            ext.encode('utf8')))

        temp_file = tempfile.NamedTemporaryFile(prefix='importing-',
                                                suffix='%s' % ext,
                                                delete=False)
        temp_file = open(temp_file.name, 'wb+')

        logger.debug('ImporterView::Saving temporary file {}.'.format(
            temp_file.name.encode('utf8')))

        for chunk in book_file.chunks():
            temp_file.write(chunk)
        temp_file.close()
        temp_file = temp_file.name

        default_book_title = self.get_default_title(temp_file, ext)
        book_title = form.cleaned_data.get('book_title', default_book_title)
        logger.debug(
            'ImporterView::book_title="{}"" default_book_title="{}".'.format(
                book_title.encode('utf8'), default_book_title.encode('utf8')))

        # in case book title in form is empty string
        if len(book_title) == 0:
            book_title = default_book_title

        if not check_book_availability(book_title):
            registered = Book.objects.filter(
                title__startswith=book_title).count()
            book_title = '%s %s' % (book_title, registered)
            logger.debug(
                'ImporterView::Checking book availability: "{}".'.format(
                    book_title.encode('utf8')))

        book_url = booktype_slugify(book_title)
        book = create_book(self.request.user, book_title, book_url=book_url)
        logger.debug(
            'ImporterView::Book created with url title="{}".'.format(book_url))

        # check if book will be hidden and set to book
        book_hidden = form.cleaned_data.get('hidden')
        if book_hidden:
            book.hidden = book_hidden
            book.save()
            logger.debug('ImporterView::Setting book hidden.')
        else:
            logger.debug('ImporterView::Setting book visible.')

        notifier = CollectNotifier()
        delegate = Delegate()
        response = {}

        try:
            book_importer = self.get_importer(ext)
        except KeyError:
            logger.error('ImporterView::No importer for this extension')

            response_data = {
                'errors': [_('Extension not supported!')],
            }
            return self.render_json_response(response_data)

        try:
            book_importer(temp_file,
                          book,
                          notifier=notifier,
                          delegate=delegate)
            logger.debug('ImporterView::Book imported.')
            response['url'] = reverse('reader:infopage', args=[book.url_title])
        except Exception as e:
            logger.error(
                'ImporterView::Some kind of error while importing book.')
            logger.exception(e)
            notifier.errors.append(str(e))

        response['infos'] = notifier.infos
        response['warnings'] = notifier.warnings
        response['errors'] = notifier.errors

        return self.render_json_response(response)
Beispiel #47
0
    def _import_book(self, epub_book, book):
        titles = {}
        toc = []

        def _parse_toc(elements, parent=None):
            for _elem in elements:
                # used later to get parent of an elem
                unique_id = uuid.uuid4().hex

                if isinstance(_elem, tuple):
                    toc.append((1, _elem[0].title, unique_id, parent))
                    _parse_toc(_elem[1])
                elif isinstance(_elem, ebooklib.epub.Section):
                    pass
                elif isinstance(_elem, ebooklib.epub.Link):
                    _urlp = urlparse.urlparse(_elem.href)
                    _name = os.path.normpath(urllib.unquote(_urlp.path))

                    # check in case _name is an empty string
                    if not _name:
                        _name = _elem.title

                    if _name not in titles:
                        titles[_name] = _elem.title
                        toc.append((0, _name, unique_id, parent))

        _parse_toc(epub_book.toc)
        self.notifier.debug(
            "TOC structure: \n{}".format(pprint.pformat(toc, indent=4)))

        now = datetime.datetime.utcnow().replace(tzinfo=utc)

        stat = models.BookStatus.objects.filter(book=book, name="new")[0]

        # assign cover image if there is one
        cover_image = get_cover_image(epub_book)
        if cover_image:
            self._set_cover(book, cover_image)

        # import all images in the EPUB
        for image in epub_book.get_items_of_type(ebooklib.ITEM_IMAGE):
            if image == cover_image:
                continue

            if not self.delegate.should_import_image(image):
                continue

            name = os.path.normpath(image.file_name)

            att = models.Attachment(book=book,
                                    version=book.version,
                                    status=stat)

            with ContentFile(image.get_content()) as content_file:
                attName, attExt = os.path.splitext(os.path.basename(name))

                att.attachment.save(
                    '{}{}'.format(booktype_slugify(attName), attExt),
                    content_file,
                    save=False
                )
                att.save()

            self._attachments[name] = att

            self.notifier.debug("Imported image: {} -> {}".format(image, att))

        # URL titles assigned so far
        url_titles = []

        def _make_url_title(title, i=0):
            url_title = booktype_slugify(title)
            if i > 0:
                url_title += "_" + str(i)
            if url_title not in url_titles:
                url_titles.append(url_title)
                return url_title
            else:
                return _make_url_title(title, i + 1)

        # import all document items from the EPUB
        for document in epub_book.get_items_of_type(ebooklib.ITEM_DOCUMENT):
            # Nav and Cover are not imported
            if not document.is_chapter():
                continue

            if not self.delegate.should_import_document(document):
                continue

            name = os.path.normpath(document.file_name)
            title = ''

            # maybe this part has to go to the plugin
            # but you can not get title from <title>
            if name in titles:
                title = titles[name]
            else:
                title = convert_file_name(name)

                if title.rfind('.') != -1:
                    title = title[:title.rfind('.')]

                title = title.replace('.', '')

            url_title = _make_url_title(title)
            content = self._create_content(document, title)

            chapter = models.Chapter(
                book=book,
                version=book.version,
                url_title=url_title,
                title=title,
                status=stat,
                content=content,
                created=now,
                modified=now
            )
            chapter.save()

            # time to save revisions correctly
            history = logChapterHistory(
                chapter=chapter,
                content=chapter.content,
                user=book.owner,
                comment='',
                revision=chapter.revision
            )

            if history:
                logBookHistory(
                    book=book,
                    version=book.version,
                    chapter=chapter,
                    chapter_history=history,
                    user=book.owner,
                    kind='chapter_create'
                )

            self._chapters[name] = chapter

            self.notifier.debug(
                "Imported chapter: {} -> {}".format(document, chapter))

        # fix links to chapters
        for file_name, chapter in self._chapters.iteritems():
            self._fix_links(chapter, base_path=os.path.dirname(file_name))

        # create TOC objects
        self._make_toc(book, toc)
Beispiel #48
0
        def _toc(depth, toc_items, parent=None, toc_setting=None):
            items = []
            sec_count = 1

            for toc_item in toc_items:
                # SECTIONS
                if isinstance(toc_item[1], list):
                    section_title, chapters = toc_item
                    url_title = booktype_slugify(section_title)

                    # let's build a section key and try to get settings for current section
                    section_key = SectionsSettingsPlugin.build_section_key(
                        url_title, sec_count)
                    section_settings = json.loads(
                        settings_dict.get(section_key, '{}'))
                    toc_setting = section_settings.get('toc',
                                                       {}).get(self.name, '')

                    # jump to next item (continue) if the whole section should be hidden
                    show_in_outputs = section_settings.get(
                        'show_in_outputs', {})
                    show_section_in_current_converter = show_in_outputs.get(
                        self.name, True)
                    if not show_section_in_current_converter:
                        continue

                    toc_item = TocItem({
                        'type':
                        'section',
                        'level':
                        depth,
                        'title':
                        section_title,
                        'url_title':
                        url_title,
                        'show_in_toc':
                        'hide_section' not in toc_setting
                    })
                    items.append(toc_item)
                    items += _toc(depth + 1, chapters, section_title,
                                  toc_setting)
                    sec_count += 1

                # CHAPTERS
                else:
                    chapter_title, chapter_href = toc_item
                    chapter_item = book.get_item_with_href(chapter_href)
                    content = self._get_chapter_content(chapter_item)
                    content = self._fix_horrible_mpdf(content)

                    href_filename, file_extension = os.path.splitext(
                        chapter_href)

                    if not parent:
                        toc_setting = ''

                    toc_item = TocItem({
                        'type':
                        'chapter',
                        'level':
                        depth,
                        'title':
                        chapter_title,
                        'url_title':
                        booktype_slugify(chapter_title),
                        'href':
                        chapter_href,
                        'href_filename':
                        href_filename,
                        'content':
                        content,
                        'show_in_toc':
                        'hide_chapters' not in toc_setting
                    })
                    items.append(toc_item)

            return items
Beispiel #49
0
 def clean_url_title(self):
     url_title = self.cleaned_data['url_title']
     if not url_title:
         return misc.booktype_slugify(self.cleaned_data['title'])
     return url_title
Beispiel #50
0
def importBookFromFile(user, zname, createTOC=False, **extraOptions):
    """Create a new book from a bookizip filename"""

    from booki.utils.log import logChapterHistory

    # unzip it
    zf = zipfile.ZipFile(zname)
    # load info.json
    info = json.loads(zf.read('info.json'))
    logWarning("Loaded json file %r" % info)

    metadata = info['metadata']
    manifest = info['manifest']
    TOC = info['TOC']

    if extraOptions.get('book_title', None):
        bookTitle = extraOptions['book_title']
    else:
        bookTitle = get_metadata(metadata, 'title', ns=DC)[0]

    bookTitle = makeTitleUnique(bookTitle)
    logWarning("Chose unique book title %r" % bookTitle)

    if extraOptions.get('book_url', None):
        bookURL = extraOptions['book_url']
    else:
        bookURL = None

    book = create_book(user, bookTitle, status="new", bookURL=bookURL)

    if extraOptions.get("hidden"):
        book.hidden = True
        book.save()

    # this is for Table of Contents
    p = re.compile('\ssrc="(.*)"')

    # what if it does not have status "new"
    stat = models.BookStatus.objects.filter(book=book, name="new")[0]

    chapters = getChaptersFromTOC(TOC)
    n = len(chapters) + 1  #is +1 necessary?
    now = datetime.datetime.now()

    for chapterName, chapterFile, is_section in chapters:
        urlName = booktype_slugify(chapterName)

        if is_section:  # create section
            if createTOC:
                c = models.BookToc(book=book,
                                   version=book.version,
                                   name=chapterName,
                                   chapter=None,
                                   weight=n,
                                   typeof=2)
                c.save()
                n -= 1
        else:  # create chapter
            # check if i can open this file at all
            content = zf.read(chapterFile)

            #content = p.sub(r' src="../\1"', content)

            chapter = models.Chapter(book=book,
                                     version=book.version,
                                     url_title=urlName,
                                     title=chapterName,
                                     status=stat,
                                     content=content,
                                     created=now,
                                     modified=now)
            chapter.save()

            history = logChapterHistory(chapter=chapter,
                                        content=content,
                                        user=user,
                                        comment="",
                                        revision=chapter.revision)

            if createTOC:
                c = models.BookToc(book=book,
                                   version=book.version,
                                   name=chapterName,
                                   chapter=chapter,
                                   weight=n,
                                   typeof=1)
                c.save()
                n -= 1

    stat = models.BookStatus.objects.filter(book=book, name="new")[0]

    from django.core.files import File

    for item in manifest.values():
        if item["mimetype"] != 'text/html':
            attachmentName = item['url']

            if attachmentName.startswith("static/"):
                att = models.Attachment(book=book,
                                        version=book.version,
                                        status=stat)

                s = zf.read(attachmentName)
                f = StringIO(s)
                f2 = File(f)
                f2.size = len(s)
                att.attachment.save(os.path.basename(attachmentName),
                                    f2,
                                    save=False)
                att.save()
                f.close()

    # metadata
    for namespace in metadata:
        # namespace is something like "http://purl.org/dc/elements/1.1/" or ""
        # in the former case, preepend it to the name, in {}.
        ns = ('{%s}' % namespace if namespace else '')
        for keyword, schemes in metadata[namespace].iteritems():
            for scheme, values in schemes.iteritems():
                #schema, if it is set, describes the value's format.
                #for example, an identifier might be an ISBN.
                sc = ('{%s}' % scheme if scheme else '')
                key = "%s%s%s" % (ns, keyword, sc)
                for v in values:
                    if not v: continue
                    try:
                        info = models.Info(book=book, name=key)
                        if len(v) >= 2500:
                            info.value_text = v
                            info.kind = 2
                        else:
                            info.value_string = v
                            info.kind = 0
                        info.save()
                    except:
                        # For now just ignore any kind of error here.
                        # Considering we don't handle metadata as we
                        # should it is not such a problem.
                        pass

    zf.close()

    return book
Beispiel #51
0
    def form_valid(self, form):
        logger.debug('ImporterView::form_valid')

        book_file = self.request.FILES['book_file']
        ext = self.file_extension(book_file.name)

        logger.debug('ImporterView::Importing file extension is "{}".'.format(ext.encode('utf8')))

        temp_file = tempfile.NamedTemporaryFile(
            prefix='importing-', suffix='%s' % ext, delete=False)
        temp_file = open(temp_file.name, 'wb+')

        logger.debug('ImporterView::Saving temporary file {}.'.format(temp_file.name.encode('utf8')))

        for chunk in book_file.chunks():
            temp_file.write(chunk)
        temp_file.close()
        temp_file = temp_file.name

        default_book_title = self.get_default_title(temp_file, ext)
        book_title = form.cleaned_data.get('book_title', default_book_title)
        logger.debug('ImporterView::book_title="{}"" default_book_title="{}".'.format(book_title.encode('utf8'), default_book_title.encode('utf8')))

        # in case book title in form is empty string
        if len(book_title) == 0:
            book_title = default_book_title

        if not check_book_availability(book_title):
            registered = Book.objects.filter(
                title__startswith=book_title).count()
            book_title = '%s %s' % (book_title, registered)
            logger.debug('ImporterView::Checking book availability: "{}".'.format(book_title.encode('utf8')))

        book_url = booktype_slugify(book_title)
        book = create_book(
            self.request.user, book_title, book_url=book_url)
        logger.debug('ImporterView::Book created with url title="{}".'.format(book_url))

        # check if book will be hidden and set to book
        book_hidden = form.cleaned_data.get('hidden')
        if book_hidden:
            book.hidden = book_hidden
            book.save()
            logger.debug('ImporterView::Setting book hidden.')
        else:
            logger.debug('ImporterView::Setting book visible.')

        notifier = CollectNotifier()
        delegate = Delegate()
        response = {}

        try:
            book_importer = self.get_importer(ext)
        except KeyError:
            logger.error('ImporterView::No importer for this extension')

            response_data = {
                'errors': [_('Extension not supported!')],
            }
            return self.render_json_response(response_data)

        try:
            book_importer(
                temp_file, book,
                notifier=notifier,
                delegate=delegate
            )
            logger.debug('ImporterView::Book imported.')
            response['url'] = reverse('reader:infopage', args=[book.url_title])
        except Exception as e:
            logger.error('ImporterView::Some kind of error while importing book.')
            logger.exception(e)
            notifier.errors.append(str(e))

        response['infos'] = notifier.infos
        response['warnings'] = notifier.warnings
        response['errors'] = notifier.errors

        return self.render_json_response(response)
Beispiel #52
0
def books(request):
    books = []
    for title in request.param:
        books.append(
            BookFactory(title=title, url_title=booktype_slugify(title)))
    return books
Beispiel #53
0
def exportBook(book_version):
    from booki import bookizip
    import time
    starttime = time.time()

    (zfile, zname) = tempfile.mkstemp()

    spine = []
    toc_top = []
    toc_current = toc_top
    waiting_for_url = []

    info = {
        "version": 1,
        "TOC": toc_top,
        "spine": spine,
        "metadata": _format_metadata(book_version.book),
        "manifest": {}
        }

    bzip = bookizip.BookiZip(zname, info=info)
    chapter_n = 1

    for i, chapter in enumerate(models.BookToc.objects.filter(version=book_version).order_by("-weight")):
        if chapter.chapter:
            # It's a real chapter! With content!
            try:
                content = _fix_content(book_version.book, chapter, chapter_n)
            except:
                continue

            chapter_n += 1
            ID = "ch%03d_%s" % (i, chapter.chapter.url_title.encode('utf-8'))
            filename = ID + '.html'

            toc_current.append({"title": chapter.chapter.title,
                                "url": filename,
                                "type": "chapter",
                                "role": "text"
                                })

            # If this is the first chapter in a section, lend our url
            # to the section, which has no content and thus no url of
            # its own.  If this section was preceded by an empty
            # section, it will be waiting too, hence "while" rather
            # than "if".
            while waiting_for_url:
                section = waiting_for_url.pop()
                section["url"] = filename

            bzip.add_to_package(ID, filename, content, "text/html")
            spine.append(ID)

        else:
            #A new top level section.
            title = chapter.name.encode("utf-8")
            ID = "s%03d_%s" % (i, booktype_slugify(unicode(title)))

            toc_current = []
            section = {"title": title,
                       "url": '',
                       "type": "booki-section",
                       "children": toc_current
                       }

            toc_top.append(section)
            waiting_for_url.append(section)


    #Attachments are images (and perhaps more).  They do not know
    #whether they are currently in use, or what chapter they belong
    #to, so we add them all.
    #XXX scan for img links while adding chapters, and only add those.

    for i, attachment in enumerate(models.Attachment.objects.filter(version=book_version)):
        try:
            f = open(attachment.attachment.name, "rb")
            blob = f.read()
            f.close()
        except (IOError, OSError), e:
            msg = "couldn't read attachment %s" % e
            logWarning(msg)
            continue

        fn = os.path.basename(attachment.attachment.name.encode("utf-8"))

        ID = "att%03d_%s" % (i, booktype_slugify(unicode(fn)))
        if '.' in fn:
            _, ext = fn.rsplit('.', 1)
            mediatype = bookizip.MEDIATYPES.get(ext.lower(), bookizip.MEDIATYPES[None])
        else:
            mediatype = bookizip.MEDIATYPES[None]

        bzip.add_to_package(ID,
                            "static/%s" % fn,
                            blob,
                            mediatype)