Source code for railgun.website.manual

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# @file: railgun/website/manual.py
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# This file is released under BSD 2-clause license.

"""This module formats markdown manuals into html pages.

The manual pages support translations.  They are written in markdown and
stored under ``railgun/website/manual``.
Each directory is one single manual page, where each locale holds the
translated contents of this manual page in "``[lang].md``".
"""

import os

from flask import render_template
from markdown import markdown
from jinja2 import FileSystemLoader, Environment

from railgun.common.url import reform_path, UrlMatcher
from .i18n import get_best_locale_name
from .context import app
from .utility import format_size


class ManualPage(object):

    # The environment shared by all manual page instances
    template_env = Environment(
        loader=FileSystemLoader(
            os.path.join(os.path.dirname(__file__), 'manual')
        )
    )
    template_env.filters['sizeformat'] = format_size

    # Main body of the ManualPage
    def __init__(self, name):
        self.name = name
        self.root = os.path.join(os.path.dirname(__file__), 'manual/%s' % name)
        if os.path.isdir(self.root):
            files = os.listdir(self.root)
            self.locales = [f[:-3] for f in files if f.endswith('.md')]
        else:
            self.locales = []
        self._title = {l: self._make_title(l) for l in self.locales}
        self._formatted = {
            l: self._format_webpage(self._make_markdown(l))
            for l in self.locales
        }

    def _make_title(self, locale):
        """Load the title of this manual page."""
        with open(os.path.join(self.root, '%s.md' % locale), 'rb') as f:
            for l in f:
                if l.startswith('#'):
                    return l[1:].strip().decode('utf-8')

    def _make_template_kwargs(self):
        """Make the dictionary passed to template render."""
        return dict(
            max_upload=app.config['MAX_SUBMISSION_SIZE'],
            max_archive_file=app.config['MAX_SUBMISSION_FILE_COUNT'],
            max_pending=app.config['MAX_USER_PENDING_PER_HW'],
        )

    # To expose static resources in homework desc directory, we need to
    # convert all "hw://<path>" urls to hwstatic view urls.
    def _translate_url(self, u):
        # Get rid of 'hw://' and leading '/'
        u = reform_path(u[5:])
        if u.startswith('/'):
            u = u[1:]
        # translate to hwstatic file
        filename = '%s/%s' % (self.name, u)
        # NOTE: here I hardcoded the url for hwstatic!
        ret = '/static/img/%s' % filename
        # we also need to prepend website base url
        return app.config['WEBSITE_BASEURL'] + ret

    def _make_markdown(self, locale, webpage=True):
        """Render manual page in given `locale` into markdown source."""
        path = '%s/%s.md' % (self.name, locale)
        tpl = ManualPage.template_env.get_template(path)
        kwargs = self._make_template_kwargs()
        mkd = tpl.render(webpage=webpage, **kwargs)
        return UrlMatcher(['rc']).replace(mkd.strip(), self._translate_url)

    def _format_webpage(self, mkd):
        """Format markdown source into html."""
        return markdown(
            text=mkd,
            output_format='xhtml1',
            extensions=[
                'extra',
                'tables',
                'codehilite',
                'nl2br',
                'toc',
                'fenced_code',
            ]
        )

    def get(self):
        """Get the most suitable manual (title, content) for current locale."""
        best_locale = get_best_locale_name(self.locales)
        return self._title[best_locale], self._formatted[best_locale]

    def markdown(self):
        """Make pure markdown source targeted for general usage."""
        best_locale = get_best_locale_name(self.locales)
        return self._make_markdown(locale=best_locale, webpage=False)


[docs]def translated_page(name): """Render the requested manual page to visitor. The best locale for current user will be detected automatically. :template: manual.html :param name: The manual page name. :type name: :class:`str` """ title, content = manual_pages[name].get() return render_template('manual.html', title=title, content=content)
[docs]def translated_page_source(name): """Render the source of requested manual page to visitor. The best locale for current user will be detected automatically. :param name: The manual page name. :type name: :class:`str` """ return manual_pages[name].markdown(), 200, \ {'Content-Type': 'text/x-markdown; charset=utf-8'} #: Cache all the manual page instances in memory, so that the markdown #: source code will only be formatted once.
manual_pages = { k: ManualPage(k) for k in os.listdir(os.path.join(os.path.dirname(__file__), 'manual')) }