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