Blame pagure/docs_server.py

Pierre-Yves Chibon d71d6b
# -*- coding: utf-8 -*-
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
"""
Aurélien Bompard 831553
 (c) 2014-2016 - Copyright Red Hat Inc
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
 Authors:
Pierre-Yves Chibon d71d6b
   Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
"""
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon 67d1cc
from __future__ import unicode_literals, absolute_import
Aurélien Bompard dcf6f6
Pierre-Yves Chibon d71d6b
import logging
Pierre-Yves Chibon d71d6b
import os
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
import flask
Pierre-Yves Chibon d71d6b
import pygit2
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon da7798
from binaryornot.helpers import is_binary_string
Pierre-Yves Chibon da7798
Pierre-Yves Chibon b130e5
import pagure.config
Pierre-Yves Chibon d71d6b
import pagure.doc_utils
Pierre-Yves Chibon d71d6b
import pagure.exceptions
Shengjing Zhu 348f25
import pagure.lib.mimetype
Pierre-Yves Chibon cf98be
import pagure.lib.model_base
Pierre-Yves Chibon cf98be
import pagure.lib.query
Pierre-Yves Chibon d71d6b
import pagure.forms
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
# Create the application.
Pierre-Yves Chibon d71d6b
APP = flask.Flask(__name__)
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
# set up FAS
Pierre-Yves Chibon b130e5
APP.config = pagure.config.reload_config()
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon cf98be
SESSION = pagure.lib.model_base.create_session(APP.config["DB_URL"])
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
if not APP.debug:
Pierre-Yves Chibon 9c2953
    APP.logger.addHandler(
Pierre-Yves Chibon 9c2953
        pagure.mail_logging.get_mail_handler(
Pierre-Yves Chibon 9c2953
            smtp_server=APP.config.get("SMTP_SERVER", "127.0.0.1"),
Pierre-Yves Chibon 9c2953
            mail_admin=APP.config.get("MAIL_ADMIN", APP.config["EMAIL_ERROR"]),
Pierre-Yves Chibon 9c2953
            from_email=APP.config.get(
Pierre-Yves Chibon 9c2953
                "FROM_EMAIL", "pagure@fedoraproject.org"
Pierre-Yves Chibon 9c2953
            ),
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
# Send classic logs into syslog
Pierre-Yves Chibon 164a57
SHANDLER = logging.StreamHandler()
Pierre-Yves Chibon 9c2953
SHANDLER.setLevel(APP.config.get("log_level", "INFO"))
Pierre-Yves Chibon 164a57
APP.logger.addHandler(SHANDLER)
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon 4635b5
_log = logging.getLogger(__name__)
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon 9c2953
TMPL_HTML = """
Pierre-Yves Chibon 7b96f4
Pierre-Yves Chibon 7b96f4
Pierre-Yves Chibon 7b96f4
Pierre-Yves Chibon 7b96f4
  <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
Pierre-Yves Chibon 7b96f4
  <style type="text/css"></style>
Pierre-Yves Chibon 7b96f4
    ul {{
Pierre-Yves Chibon 7b96f4
      margin: 0;
Pierre-Yves Chibon 7b96f4
      padding: 0;
Pierre-Yves Chibon 7b96f4
    }}
Pierre-Yves Chibon 7b96f4
  
Pierre-Yves Chibon 7b96f4
Pierre-Yves Chibon 7b96f4
Pierre-Yves Chibon 7b96f4
{content}
Pierre-Yves Chibon 7b96f4
Pierre-Yves Chibon 7b96f4
Pierre-Yves Chibon 9c2953
"""
Pierre-Yves Chibon 7b96f4
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
def __get_tree(repo_obj, tree, filepath, index=0, extended=False):
Pierre-Yves Chibon 9c2953
    """ Retrieve the entry corresponding to the provided filename in a
Pierre-Yves Chibon d71d6b
    given tree.
Pierre-Yves Chibon 9c2953
    """
Pierre-Yves Chibon d71d6b
    filename = filepath[index]
Pierre-Yves Chibon d71d6b
    if isinstance(tree, pygit2.Blob):  # pragma: no cover
Pierre-Yves Chibon d71d6b
        # If we were given a blob, then let's just return it
Pierre-Yves Chibon d71d6b
        return (tree, None, None)
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
    for element in tree:
Pierre-Yves Chibon 9c2953
        if element.name == filename or (
Pierre-Yves Chibon 9c2953
            not filename and element.name.startswith("index")
Pierre-Yves Chibon 9c2953
        ):
Pierre-Yves Chibon d71d6b
            # If we have a folder we must go one level deeper
Pierre-Yves Chibon d71d6b
            if element.filemode == 16384:
Pierre-Yves Chibon d71d6b
                if (index + 1) == len(filepath):
Pierre-Yves Chibon 9c2953
                    filepath.append("")
Pierre-Yves Chibon d71d6b
                return __get_tree(
Pierre-Yves Chibon 9c2953
                    repo_obj,
Pierre-Yves Chibon 9c2953
                    repo_obj[element.oid],
Pierre-Yves Chibon 9c2953
                    filepath,
Pierre-Yves Chibon 9c2953
                    index=index + 1,
Pierre-Yves Chibon 9c2953
                    extended=True,
Pierre-Yves Chibon 9c2953
                )
Pierre-Yves Chibon d71d6b
            else:
Pierre-Yves Chibon d71d6b
                return (element, tree, False)
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon 9c2953
    if filename == "":
Pierre-Yves Chibon d71d6b
        return (None, tree, extended)
Pierre-Yves Chibon d71d6b
    else:
Pierre-Yves Chibon d71d6b
        raise pagure.exceptions.FileNotFoundException(
Pierre-Yves Chibon 9c2953
            "File %s not found" % ("/".join(filepath),)
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
def __get_tree_and_content(repo_obj, commit, path):
Pierre-Yves Chibon 9c2953
    """ Return the tree and the content of the specified file. """
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
    (blob_or_tree, tree_obj, extended) = __get_tree(
Pierre-Yves Chibon 9c2953
        repo_obj, commit.tree, path
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
    if blob_or_tree is None:
Shengjing Zhu 348f25
        return (tree_obj, None, None)
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
    if not repo_obj[blob_or_tree.oid]:
Pierre-Yves Chibon d71d6b
        # Not tested and no idea how to test it, but better safe than sorry
Pierre-Yves Chibon c6cc5c
        flask.abort(404, description="File not found")
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
    if isinstance(blob_or_tree, pygit2.TreeEntry):  # Returned a file
Shengjing Zhu 348f25
        filename = blob_or_tree.name
Shengjing Zhu 348f25
        name, ext = os.path.splitext(filename)
Pierre-Yves Chibon d71d6b
        blob_obj = repo_obj[blob_or_tree.oid]
Pierre-Yves Chibon da7798
        if not is_binary_string(blob_obj.data):
Pierre-Yves Chibon 6dba78
            try:
Pierre-Yves Chibon 6dba78
                content, safe = pagure.doc_utils.convert_readme(
Pierre-Yves Chibon 9c2953
                    blob_obj.data, ext
Pierre-Yves Chibon 9c2953
                )
Shengjing Zhu 348f25
                if safe:
Pierre-Yves Chibon 9c2953
                    filename = name + ".html"
Pierre-Yves Chibon ada323
            except pagure.exceptions.PagureEncodingException:
Pierre-Yves Chibon 6dba78
                content = blob_obj.data
Pierre-Yves Chibon da7798
        else:
Pierre-Yves Chibon da7798
            content = blob_obj.data
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
    tree = sorted(tree_obj, key=lambda x: x.filemode)
Shengjing Zhu 348f25
    return (tree, content, filename)
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon 9c2953
@APP.route("/<repo>/")</repo>
Pierre-Yves Chibon 9c2953
@APP.route("/<namespace>.<repo>/")</repo></namespace>
Pierre-Yves Chibon 9c2953
@APP.route("/<repo>/<path:filename>")</path:filename></repo>
Pierre-Yves Chibon 9c2953
@APP.route("/<namespace>.<repo>/<path:filename>")</path:filename></repo></namespace>
Pierre-Yves Chibon 9c2953
@APP.route("/fork/<username>/<repo>/")</repo></username>
Pierre-Yves Chibon 9c2953
@APP.route("/fork/<namespace>.<username>/<repo>/")</repo></username></namespace>
Pierre-Yves Chibon 9c2953
@APP.route("/fork/<username>/<repo>/<path:filename>")</path:filename></repo></username>
Pierre-Yves Chibon 9c2953
@APP.route("/fork/<namespace>.<username>/<repo>/<path:filename>")</path:filename></repo></username></namespace>
Igor Gnatenko 356b34
def view_docs(repo, username=None, namespace=None, filename=None):
Pierre-Yves Chibon d71d6b
    """ Display the documentation
Pierre-Yves Chibon d71d6b
    """
Pierre-Yves Chibon 9c2953
    if "." in repo:
Pierre-Yves Chibon 9c2953
        namespace, repo = repo.split(".", 1)
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon 930073
    repo = pagure.lib.query.get_authorized_project(
Pierre-Yves Chibon 9c2953
        SESSION, repo, user=username, namespace=namespace
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
    if not repo:
Pierre-Yves Chibon c6cc5c
        flask.abort(404, description="Project not found")
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon 9c2953
    if not repo.settings.get("project_documentation", True):
Pierre-Yves Chibon c6cc5c
        flask.abort(404, description="This project has documentation disabled")
Pierre-Yves Chibon d71d6b
Patrick Uiterwijk 3f97f6
    reponame = repo.repopath("docs")
Pierre-Yves Chibon d71d6b
    if not os.path.exists(reponame):
Pierre-Yves Chibon c6cc5c
        flask.abort(404, description="Documentation not found")
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
    repo_obj = pygit2.Repository(reponame)
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d80e3b
    if not repo_obj.is_empty:
Pierre-Yves Chibon d80e3b
        commit = repo_obj[repo_obj.head.target]
Pierre-Yves Chibon d71d6b
    else:
Pierre-Yves Chibon 51cfa4
        flask.abort(
Pierre-Yves Chibon 51cfa4
            404,
Pierre-Yves Chibon 51cfa4
            flask.Markup(
Pierre-Yves Chibon 9c2953
                "No content found in the repository, you may want to read "
Pierre-Yves Chibon c24ef8
                'the 
Pierre-Yves Chibon c24ef8
                'https://docs.pagure.org/pagure/usage/using_doc.html">'
Pierre-Yves Chibon 9c2953
                "Using the doc repository of your project documentation."
Pierre-Yves Chibon 9c2953
            ),
Pierre-Yves Chibon 51cfa4
        )
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
    content = None
Pierre-Yves Chibon d71d6b
    tree = None
Pierre-Yves Chibon d71d6b
    if not filename:
Pierre-Yves Chibon 9c2953
        path = [""]
Pierre-Yves Chibon d71d6b
    else:
Pierre-Yves Chibon 9c2953
        path = [it for it in filename.split("/") if it]
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
    if commit:
Pierre-Yves Chibon d71d6b
        try:
Shengjing Zhu 348f25
            (tree, content, filename) = __get_tree_and_content(
Pierre-Yves Chibon 9c2953
                repo_obj, commit, path
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon d71d6b
        except pagure.exceptions.FileNotFoundException as err:
Pierre-Yves Chibon 9c2953
            flask.flash("%s" % err, "error")
Pierre-Yves Chibon f88a86
        except Exception as err:
Pierre-Yves Chibon 4635b5
            _log.exception(err)
Pierre-Yves Chibon c6cc5c
            flask.abort(
Pierre-Yves Chibon c6cc5c
                500, description="Unkown error encountered and reported"
Pierre-Yves Chibon c6cc5c
            )
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon 501677
    if not content:
Pierre-Yves Chibon 8c4083
        if not tree or not len(tree):
Pierre-Yves Chibon c6cc5c
            flask.abort(404, description="No content found in the repository")
Pierre-Yves Chibon 9c2953
        html = "
  • "
  • Pierre-Yves Chibon 7b96f4
            for el in tree:
    Pierre-Yves Chibon 501677
                name = el.name
    Pierre-Yves Chibon 501677
                # Append a trailing '/' to the folders
    Pierre-Yves Chibon 501677
                if el.filemode == 16384:
    Pierre-Yves Chibon 9c2953
                    name += "/"
    Pierre-Yves Chibon 9677a4
                html += ''.format(name, name)
    Pierre-Yves Chibon 9c2953
            html += ""
    Pierre-Yves Chibon 7b96f4
            content = TMPL_HTML.format(content=html)
    Pierre-Yves Chibon 9c2953
            mimetype = "text/html"
    Shengjing Zhu 348f25
        else:
    Shengjing Zhu 348f25
            mimetype, _ = pagure.lib.mimetype.guess_type(filename, content)
    Pierre-Yves Chibon 7b96f4
    Pierre-Yves Chibon d80e3b
        return flask.Response(content, mimetype=mimetype)