Exemple #1
0
 def __init__(self, settings=None):
     "Just a thin wrapper for Placeholders; parse options in replace()."
     self.placeholders = Placeholders(self.regex, 'demo')
     if settings:
         self.settings = settings.copy()
     else:
         self.settings = Settings()
         self.settings.set('config:user', '_')
         self.settings.set('config:document', random_slug('demo'))
Exemple #2
0
def test_replace():
    defaults = {'default_variable': 'default setting'}
    settings = Settings(defaults)
    settings.read_settings_block(
        trim("""
        $ variable = setting
        """))
    out = settings.replace(
        trim("""
        This is a $[variable], and a $[default_variable]!
        """))
    assert out == "This is a setting, and a default setting!"
Exemple #3
0
def test_global_settings():
    parts = {
        'index':
        trim("""
            My Document

            $ AUTHOR = Arthur C. Clarke

            ` Part One
            """),
    }
    settings = Settings()
    settings.extract(parts)
    assert settings._ == {'AUTHOR': 'Arthur C. Clarke'}
Exemple #4
0
def test_multiline_authors():
    parts = {
        'index':
        trim("""
            My Document

            $ AUTHOR = Arthur C. Clarke
                     + Isaac Asimov

            ` Part One
            """),
    }
    settings = Settings()
    settings.extract(parts)
    assert settings._ == {'AUTHOR': 'Arthur C. Clarke + Isaac Asimov'}
Exemple #5
0
def test_character_block():
    block = CharacterBlock(trim("""
        @ X
        """))
    assert block.text() == '\n@ X'  # <-- with leading space for a heading
    expected = '<p class="subhead">X</p>'
    assert block.html_only(Html(), Settings()) == expected
Exemple #6
0
def test_process_simple():
    wiki = Wiki(Settings())
    document = {
        'sample':
        trim("""
        Title

        > Quote
        = Calvin, /Institutes/

        @ Headline

        Paragraph

        * Bullets
        * Bullets
        """)
    }
    _ = wiki.process('user-slug', 'doc-slug', document)
    __ = html.fromstring(str(_))
    assert __.xpath("//article/section")
    assert __.xpath("//h1/a[contains(., 'Title')]")
    assert __.xpath("//blockquote[contains(., 'Quote')]")
    assert __.xpath("//p[@class='caption'] and contains(., 'Calvin')")
    assert __.xpath("//p[@class='caption']/em[contains(., 'Institutes')]")
    assert __.xpath("//p[@class='subhead'][contains(., 'Headline')]")
    assert __.xpath("//p[contains(., 'Paragraph')]")
    assert __.xpath("count(//ul/li[contains(., 'Bullets')])") == 2
Exemple #7
0
def test_basic_replacements():
    """
    @todo: There is an occassional thus-far-non-reproducable issue with DEMO
    blocks in the editor; they will sometimes wrap into e.g. ...

    DEMO === + Headline

    ... in cases like the one below. It's caused by the DEMO placeholders not
    matching their blocks, and the DEMO line being taken as the start of a
    paragraph and rewrapped accordingly.
    """
    settings = Settings()
    demo = Demo(settings)
    parts = {
        'test':
        trim("""
        Test

        DEMO ===
        TEST
        ===

        Test

        ~

        DEMO ===
        + Headline

        > Blockquote
        ===

        Test
        """)
    }
    tokenized = demo.insert(parts)
    expected = {
        'test':
        trim("""
        Test

        %sdemo:1%s

        Test

        ~

        %sdemo:2%s

        Test
        """) % (DELIMITER, DELIMITER, DELIMITER, DELIMITER)
    }
    assert expected == tokenized

    decorated = demo.replace(tokenized)
    __ = html.fromstring(decorated['test'])
    assert __.xpath("count(//div[@class='wiki-demo space'])") == 2
    assert __.xpath("//pre[contains(., Test)]")
    assert __.xpath("//article/section")
Exemple #8
0
def test_paragraph():
    block = Paragraph(trim("""
        x
        y
        x
        """))
    assert block.text() == 'x y x'
    assert block.html_only(Html(), Settings()) == '<p>x y x</p>'
Exemple #9
0
    def save(self, pregenerate=True, update_doc_slug=None):
        """
        Stores self.parts; compare with self.old to know how to update the
        metadata and cache.
        """
        self.require_slugs()
        self.require_parts()

        # Old and new doc slugs may differ
        old_doc_slug = self.doc_slug
        new_doc_slug = self.doc_slug

        if update_doc_slug is None:
            update_doc_slug = all([
                # fixtures, templates
                old_doc_slug not in self.protected_doc_slugs(),
            ])

        if update_doc_slug:
            if 'index' in self.parts:
                new_text = self.parts['index']
                _, _, title_slug, _ = get_title_data(new_text, 'index')
                if self.doc_slug != title_slug:
                    new_doc_slug = title_slug

        with self.data as _:
            _.userDocument_set(self.user_slug, new_doc_slug, self.parts)
            if old_doc_slug not in self.protected_doc_slugs():
                _.userDocumentLastChanged_set(self.user_slug, old_doc_slug,
                                              new_doc_slug)
            _.userDocumentCache_delete(self.user_slug, old_doc_slug)
            _.userDocumentMetadata_delete(self.user_slug, old_doc_slug)

        self.doc_slug = new_doc_slug

        if pregenerate:

            wiki = Wiki(
                Settings({
                    'config:host': self.host,  # <-- ebooks req. FQDN
                    'config:user': self.user_slug,
                    'config:document': self.doc_slug
                }))
            html = wiki.process(self.user_slug, self.doc_slug, self.parts)
            self.data.userDocumentCache_set(self.user_slug, self.doc_slug,
                                            html)

            metadata = wiki.compile_metadata(self.data.time_zone,
                                             self.user_slug, self.doc_slug)
            self.data.userDocumentMetadata_set(self.user_slug, self.doc_slug,
                                               metadata)

        return self.doc_slug
Exemple #10
0
def test_list_block():
    "Test function block with zero lines"
    text = trim("""
        # Test 1
        # Test 2
        """)
    expect = trim("""
        <ol>
        <li>
        Test 1</li><li>Test 2
        </li>
        </ol>
        """)
    actual = list_block(text, Settings())
    assert expect == actual
Exemple #11
0
def reformat_part(slug, part):
    """
    Normalise the layout of user-entered text. Remove bibliography and Demo
    blocks, process as a Blocklist, then put them back.
    """
    if slug == 'biblio':
        return part
    else:
        content, bibliography = split_bibliography(clean_text(part))
        demo_placeholders = Demo(Settings())
        parts_sans_demo_blocks = demo_placeholders.insert({slug: content})
        blocks = BlockList(parts_sans_demo_blocks[slug])
        out_sans_demo_blocks = blocks.text()
        out_parts = demo_placeholders.replace({slug: out_sans_demo_blocks},
                                              lambda text, slug: text)
        out = out_parts[slug]
        if bibliography:
            out += "\n\n\n_____\n\n" + bibliography
        return out
Exemple #12
0
def show_editor(source: str,
                user_slug: str = '',
                doc_slug: str = '',
                part_slug: str = '',
                is_preview: bool = False,
                can_be_saved: bool = False):
    """
    Common renderer for /playground and /edit/user_slug/doc_slug/part_slug.
    """
    settings = Settings({
        'config:host': domain_name(bottle.request),
        'config:user': user_slug,
        'config:document': doc_slug,
    })
    wiki = Wiki(settings)
    part_slug, title, title_slug, summary = get_title_data(source, part_slug)
    text = reformat_part(part_slug, source)
    if part_slug == '':
        slug = slugify(title)
    elif part_slug != 'index' and is_index_part(text):
        slug = 'index'
    elif part_slug == 'biblio':
        slug = 'biblio'
    else:
        slug = part_slug
    html = wiki.process(user_slug,
                        doc_slug, {
                            slug: copy(text),
                        },
                        fragment=False,
                        preview=True)
    template = views.get_template('editor.html')
    return template.render(page_title="Editing: {:s}".format(title),
                           config=config,
                           user_slug=user_slug,
                           doc_slug=doc_slug,
                           part_slug=part_slug,
                           preview=html,
                           source=escape(text),
                           is_preview=is_preview,
                           can_be_saved=can_be_saved)
Exemple #13
0
def test_split_author():
    wiki = Wiki(Settings())
    expect = [['Name']]
    actual = wiki.split_author(trim("""
        Name
    """))
    assert expect == actual
    expect = [['Name', 'Email', 'Affiliation']]
    actual = wiki.split_author(
        trim("""
        Name / Email / Affiliation
    """))
    assert expect == actual
    expect = [['Name', 'Email', 'Affiliation'],
              ['Name2', 'Email2', 'Affiliation2']]
    actual = wiki.split_author(
        trim("""
        Name / Email / Affiliation
          + Name2 / Email2 / Affiliation2
    """))
    assert expect == actual
Exemple #14
0
def test_table_block():
    "Simple table, with headers"
    text = trim("""
        | Test 1 | Test 2
        | Test 3 | Test 4
        """)
    expect = trim("""
        <table class="table table-condensed">
        <tbody>
        <tr>
        <td class="text-left">Test 1</td>
        <td class="text-left">Test 2</td>
        </tr>
        <tr>
        <td class="text-left">Test 3</td>
        <td class="text-left">Test 4</td>
        </tr>
        </tbody>
        </table>
        """)
    actual = table_block(text, Settings())
    assert expect == actual
Exemple #15
0
def read_document(user_slug, doc_slug):
    """
    Compile the complete html document.
    """

    header_buttons = [
        home_button(),
        edit_button(user_slug, doc_slug, 'index'),
    ]

    login = get_login()
    if not login:
        header_buttons += [subscribe_button(), rss_button(user_slug)]

    footer_buttons = [biblio_button(user_slug, doc_slug)]
    if has_authority_for_user(user_slug):
        footer_buttons += [upload_button(user_slug, doc_slug)]
    footer_buttons += [download_button(user_slug, doc_slug)]

    settings = Settings({
        'config:host': domain_name(bottle.request),
        'config:user': user_slug,
        'config:document': doc_slug,
    })

    metadata = data.userDocumentMetadata_get(user_slug, doc_slug)

    html = data.userDocumentCache_get(user_slug, doc_slug)
    if not html or not metadata:
        wiki = Wiki(settings)
        doc_parts = require_document(user_slug, doc_slug)
        html = wiki.process(user_slug, doc_slug, doc_parts)
        data.userDocumentCache_set(user_slug, doc_slug, html)
        metadata = wiki.compile_metadata(config['TIME_ZONE'], user_slug,
                                         doc_slug)
        metadata['url'] = '/read/{:s}/{:s}'.format(user_slug, doc_slug)
        data.userDocumentMetadata_set(user_slug, doc_slug, metadata)

    uri = '/read/{:s}/{:s}'.format(user_slug, doc_slug)
    metadata['url'] = abs_url(bottle.request, uri)
    author_uri = '/user/{:s}'.format(user_slug)
    metadata['author_url'] = abs_url(bottle.request, author_uri)
    metadata['home_url'] = abs_url(bottle.request, '/')
    image_uri = '/image/card/{:s}/{:s}.jpg'.format(user_slug, doc_slug)
    metadata['image_url'] = abs_url(bottle.request, image_uri)

    # @todo: function to split on multi authors as well as emails.
    title = metadata.get('title', 'Untitled')
    author = metadata.get('author', 'Anonymous')
    page_title = "{:s} - {:s}".format(title, author)

    template = views.get_template('read.html')
    template.trim_blocks = True
    template.lstrip_blocks = True
    page_html = template.render(config=config,
                                page_title=page_title,
                                metadata=metadata,
                                user_slug=user_slug,
                                doc_slug=doc_slug,
                                web_buttons=web_buttons(user_slug, doc_slug),
                                header_buttons=header_buttons,
                                footer_buttons=footer_buttons,
                                content_html=html)
    return page_html
Exemple #16
0
def user_page(user_slug):
    """
    Show <user_slug>/fixtures/author + user documents.
    """
    header_buttons = [login_or_logout_button()]
    login = get_login()
    if login and login['username'] == user_slug:
        header_buttons += [
            new_article_button(user_slug),
        ]

    header_buttons += [
        edit_button(user_slug, 'fixtures', 'author'),
    ]

    if not login:
        header_buttons += [subscribe_button(), rss_button(user_slug)]

    footer_buttons = []
    if config['ARTICLE_WIKI_CREDIT'] == 'YES':
        footer_buttons += [source_button()]
    footer_buttons += [help_button()]
    if has_authority_for_user(user_slug):
        footer_buttons += [export_archive_button(user_slug)]

    slugs = data.userDocumentSet_list(user_slug)
    changes_list = data.userDocumentLastChanged_list(user_slug)

    if not has_authority_for_user(user_slug):
        # Show only those that have been published
        changes_list, __ = split_published(changes_list)

    article_slugs = [_ for _ in slugs if _ not in ['fixtures', 'templates']]
    article_keys = [
        data.userDocumentMetadata_key(user_slug, _) for _ in article_slugs
    ]
    article_list = sorted(data.get_hashes(article_keys),
                          key=lambda _: _.get('title', ''))

    published_articles, unpublished_articles = split_published(article_list)
    if not has_authority_for_user(user_slug):
        unpublished_articles = []

    settings = Settings({
        'config:host': domain_name(bottle.request),
        'config:user': user_slug,
        'config:document': 'fixtures',
    })
    wiki = Wiki(settings)
    document = data.userDocument_get(user_slug, 'fixtures')
    if not document:
        msg = "User '{:s}' not found."
        bottle.abort(HTTP_NOT_FOUND, msg.format(user_slug))
    if 'author' in document:
        text = document['author']
    else:
        text = trim("""
            Author Page

            (Author information to be added here...)
            """)

    blocks = BlockList(clean_text(text))
    page_title, page_summary = blocks.pop_titles()
    content_html = wiki.process(None,
                                None, {'index': blocks.text()},
                                fragment=True,
                                preview=True)
    inline = Inline()

    return views.get_template('user.html').render(
        config=config,
        user=user_slug,
        page_title="{:s} - {:s}".format(page_title, page_summary),
        title_html=inline.process(page_title),
        summary_html=inline.process(page_summary),
        header_buttons=header_buttons,
        footer_buttons=footer_buttons,
        changes_list=changes_list,
        published_articles=published_articles,
        unpublished_articles=unpublished_articles,
        content_html=content_html,
        pluralize=pluralize  # <-- hack function injection
    )
Exemple #17
0
def test_counters():
    settings = Settings()
    out = settings.replace(trim("""
        $[a++], $[a++], $[a++]
        """))
    assert out == "1, 2, 3"
Exemple #18
0
def test_function_block():
    block = FunctionBlock(Text, [], '-', 'x')
    assert block.text() == 'TEXT ---\nx\n---'
    assert block.html_only(Html(), Settings()) == '<pre>x</pre>'
Exemple #19
0
def test_divider():
    block = Divider(trim("""
        - - -
        """))
    assert block.text() == '- - -'
    assert block.html_only(Html(), Settings()) == '<hr class="div-solid" />'
Exemple #20
0
def write_epub(user_slug, doc_slug, file_path):

    # Get all the data
    config = load_env_config()
    data = Data(config)

    user = data.user_get(user_slug)  # or None
    if not user:
        raise RuntimeError("User not found: %s", user_slug)

    document = data.userDocument_get(user_slug, doc_slug)  # or Noen
    if not document:
        raise RuntimeError("Document not found: %s" % doc_slug)

    # -------------------------
    # 0. Create book
    # 1. Create cover
    # 2. Create title page
    # 3. Create chapter (which basically is the book)
    #    ... This upgrades to multiple chapters when compiling books.

    # Pre-processing...

    settings = Settings({
        'config:user': user_slug,
        'config:document': doc_slug,
    })
    wiki = Wiki(settings)
    xhtml = wiki.process(user_slug, doc_slug, document)
    metadata = wiki.compile_metadata(config['TIME_ZONE'], user_slug, doc_slug)
    metadata['url'] = '/read/{:s}/{:s}'.format(user_slug, doc_slug),

    title = metadata.get('title', 'Untitled')
    summary = metadata.get('summary', '')
    author = metadata.get('author', 'Anonymous')
    date = metadata.get('date', '')

    # -------------------------
    # 0. CREATE BOOK

    book = epub.EpubBook()

    # set metadata
    book.set_identifier(user_slug + '+' + doc_slug)
    book.set_title(title)
    book.set_language('en')
    book.add_author(author)

    # define CSS style
    with open('static/epub.css') as f:
        style = f.read()
    global_css = epub.EpubItem(uid="style_nav",
                               file_name="style/nav.css",
                               media_type="text/css",
                               content=style)
    book.add_item(global_css)

    # -------------------------
    # 1. Create Cover

    tmp_cover_file = "/tmp/%s-%s-cover.png" % (user_slug, doc_slug)
    image = make_background((1600, 2200), (160, 184, 160))
    cover = make_cover(image, [title, summary, author, date],
                       [COLOR_TEXT, COLOR_SHADOW])
    cover.save(tmp_cover_file, "JPEG")
    chapter_file_name = doc_slug + '.xhtml'

    assert os.path.exists(tmp_cover_file)
    cover_image = open(tmp_cover_file, 'rb').read()
    book.set_cover("image.jpg", cover_image)

    # -------------------------
    # 2. Create Title Page

    date_string = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    title_xhtml = """
    <html>
    <body>
        <div>Generated by <i>Article Wiki</i>:</div>
        <div>%s</div>
        <div>&nbsp;</div>
        <div>Permanent URL:</div>
        <div>http://chapman.wiki/read/%s/%s</div>
    </body>
    </html>
    """ % (date_string, user_slug, doc_slug)

    c1 = epub.EpubHtml(title="About this book",
                       file_name="title.xhtml",
                       lang='en')
    c1.content = title_xhtml
    c1.add_item(global_css)
    book.add_item(c1)

    # -------------------------
    # 3. Create Chapter

    c2 = epub.EpubHtml(title=title, file_name=chapter_file_name, lang='en')
    c2.content = xhtml
    c2.add_item(global_css)
    book.add_item(c2)

    # Define Table Of Contents
    book.toc = (
        epub.Link(chapter_file_name, title, doc_slug),
        # (epub.Section(user_slug), (c2))
    )

    # add default NCX and Nav file
    book.add_item(epub.EpubNcx())
    book.add_item(epub.EpubNav())

    # basic spine
    book.spine = ['nav', c1, c2]

    # write to the file
    epub.write_epub(file_path, book, {})
Exemple #21
0
class Demo(object):
    """
    Process DEMO blocks and their arguments.

    Located here because of interdependency with Wiki().
    """
    regex = r"DEMO\s+(\([^)]+\)\s)?\s*([%s])\2\2\s*\n.+?\n\2\2\2" % \
        re.escape(Config.delimiters)

    def __init__(self, settings=None):
        "Just a thin wrapper for Placeholders; parse options in replace()."
        self.placeholders = Placeholders(self.regex, 'demo')
        if settings:
            self.settings = settings.copy()
        else:
            self.settings = Settings()
            self.settings.set('config:user', '_')
            self.settings.set('config:document', random_slug('demo'))

    def insert(self, parts):
        "Add placeholders."
        return self.placeholders.insert(parts)

    def decorate(self, pattern, part_slug):
        """
        When we process a new demo block it needs to be assigned a unique
        id_prefix as its config:document name. Micro chance of a collision;
        maybe replace with a singleton to track random IDs in use.
        """
        self.settings.set('config:document', random_slug('demo'))

        wiki = Wiki(self.settings)
        options = match_demo_options(pattern)
        fragment = ('index' not in options)
        part_slug = 'index' if 'index' in options else random_slug('demo-')
        lines = pattern.splitlines()
        source = "\n".join(lines[1:-1])
        output = wiki.process(None, None, {part_slug: source}, fragment)

        env = Environment(autoescape=True)
        if 'wide' in options:
            tpl = env.from_string(
                trim("""
                <div class="wiki-demo-wide space">
                    <table>
                        <tbody>
                            <tr>
                                <td>
                                    <pre>{{ source|safe }}</pre>
                                </td>
                            </tr>
                            <tr>
                                <td class="wiki-demo-output">
                                    {{ output|safe }}
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            """))
        else:
            tpl = env.from_string(
                trim("""
                <div class="wiki-demo space">
                    <table>
                        <tbody>
                            <tr>
                                <td width="50%">
                                    <pre>{{ source|safe }}</pre>
                                </td>
                                <td>&nbsp;</td>
                                <td width="48%" class="wiki-demo-output">
                                    {{ output|safe }}
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            """))

        return tpl.render(source=escape(source), output=output)

    def replace(self, html_parts, decorator=None):
        "Generate demo blocks."
        if decorator:
            return self.placeholders.replace(decorator, html_parts)
        else:
            return self.placeholders.replace(self.decorate, html_parts)