|
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)
|