Example #1
0
    def _save_history_records(self, book, chapter, kind='chapter_create'):
        """
        Save history records for chapter and book. It saves a revision of the chapter


        :Args:
          - book: `booki.edit.models.Book` Django model instance
          - chapter: `booki.edit.models.Chapter` Django model instance
          - kind: `str` the chapter history type. See booki.editor.models.HISTORY_CHOICES

        """

        rev = chapter.revision

        if kind != 'chapter_create':
            rev = chapter.revision + 1

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

        if history:
            logBookHistory(book=book,
                           version=book.version,
                           chapter=chapter,
                           chapter_history=history,
                           user=book.owner,
                           kind=kind)
Example #2
0
    def test_log_chapter_history(self):
        chapter_content = 'Test of log chapter history'
        history = logChapterHistory(chapter=self.chapter,
                                    user=self.user,
                                    revision=1,
                                    content=chapter_content)

        # check returned object
        self.assertTrue(
            isinstance(history, ChapterHistory),
            "Returned object should be instance of ChapterHistory")

        # checl some values also
        self.assertEqual(history.chapter, self.chapter)
        self.assertEqual(history.content, chapter_content)
        self.assertEqual(history.revision, 1)

        # test with some bad values
        none_history = logChapterHistory(chapter=self.chapter, user='')

        # check returned values for bad params
        self.assertEqual(none_history, None,
                         "It should return None in case of bad parameters")
Example #3
0
    def post(self, request, *args, **kwargs):
        book = self.get_object()
        self.object = book
        self.get_context_data(**kwargs)
        book_security = security.get_security_for_book(request.user, book)

        if not book_security.has_perm('edit.history_revert'):
            raise PermissionDenied

        revision = get_object_or_404(
            models.ChapterHistory,
            revision=request.POST["revert"],
            chapter=self.chapter,
            chapter__version=book.version.id
        )

        history = logChapterHistory(
            chapter=self.chapter,
            content=revision.content,
            user=request.user,
            comment=_("Reverted to revision %s.") % revision.revision,
            revision=self.chapter.revision + 1
        )

        if history:
            logBookHistory(
                book=book,
                version=book.version.id,
                chapter=self.chapter,
                chapter_history=history,
                user=request.user,
                args={},
                kind='chapter_save'
            )

        self.chapter.revision += 1
        self.chapter.content = revision.content
        try:
            self.chapter.save()
            messages.success(request, _('Chapter revision successfully reverted.'))
        except:
            messages.warning(request, _('Chapter revision could not be reverted.'))

        url = "{0}#history/{1}".format(
            reverse('edit:editor', args=[book.url_title]),
            self.chapter.url_title
        )
        return HttpResponseRedirect(url)
Example #4
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
Example #5
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)
Example #6
0
    def create(self, validated_data):
        chapter = super(ChapterListCreateSerializer, self).create(validated_data)

        # create toc
        book = self.context['view']._book
        book_version = self.context['view']._book.version
        weight = len(book_version.get_toc()) + 1

        for itm in BookToc.objects.filter(version=book_version, book=book).order_by("-weight"):
            itm.weight = weight
            itm.save()
            weight -= 1

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

        # create chapter history
        history = logChapterHistory(
            chapter=chapter,
            content=chapter.content,
            user=self.context['request'].user,
            comment="created via api",
            revision=chapter.revision
        )

        # create book history
        if history:
            logBookHistory(
                book=book,
                version=book_version,
                chapter=chapter,
                chapter_history=history,
                user=self.context['request'].user,
                kind='chapter_create'
            )

        # TODO
        # this is just playground
        # we must create separate tool to push messages through the sputnik channel from API endpoints
        # without having clientID in request

        # message_info
        channel_name = "/chat/{}/".format(book.id)
        clnts = sputnik.smembers("sputnik:channel:{}:channel".format(channel_name))
        message = {
            'channel': channel_name,
            "command": "message_info",
            "from": self.context['request'].user.username,
            "email": self.context['request'].user.email,
            "message_id": "user_new_chapter",
            "message_args": [self.context['request'].user.username, chapter.title]
        }

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

        # chapter_create
        channel_name = "/booktype/book/{}/{}/".format(book.id, book_version.get_version())
        clnts = sputnik.smembers("sputnik:channel:{}:channel".format(channel_name))
        message = {
            'channel': channel_name,
            "command": "chapter_create",
            "chapter": (chapter.id, chapter.title, chapter.url_title, 1, chapter.status.id,
                        chapter.lock_type, chapter.lock_username, "root", toc_item.id, "normal", None)
        }

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

        # notificatoin message
        message = {
            'channel': channel_name,
            'command': 'notification',
            'message': 'notification_chapter_was_created',
            'username': self.context['request'].user.username,
            'message_args': (chapter.title,)
        }

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

        return chapter
Example #7
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 == '':
                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

            _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

            # 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')
Example #8
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')