Blame pagure/docs_server.py

Pierre-Yves Chibon d71d6b
# -*- coding: utf-8 -*-
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
"""
Pierre-Yves Chibon d71d6b
 (c) 2014-2015 - 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 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
Pierre-Yves Chibon d71d6b
import pagure.lib
Shengjing Zhu 348f25
import pagure.lib.mimetype
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 d71d6b
SESSION = pagure.lib.create_session(APP.config['DB_URL'])
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
if not APP.debug:
Pierre-Yves Chibon d71d6b
    APP.logger.addHandler(pagure.mail_logging.get_mail_handler(
Pierre-Yves Chibon d71d6b
        smtp_server=APP.config.get('SMTP_SERVER', '127.0.0.1'),
Pierre-Yves Chibon 48adce
        mail_admin=APP.config.get('MAIL_ADMIN', APP.config['EMAIL_ERROR']),
Pierre-Yves Chibon 48adce
        from_email=APP.config.get('FROM_EMAIL', 'pagure@fedoraproject.org')
Pierre-Yves Chibon d71d6b
    ))
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
# Send classic logs into syslog
Pierre-Yves Chibon 164a57
SHANDLER = logging.StreamHandler()
Pierre-Yves Chibon 164a57
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 7b96f4
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 7b96f4
'''
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 d71d6b
    ''' Retrieve the entry corresponding to the provided filename in a
Pierre-Yves Chibon d71d6b
    given tree.
Pierre-Yves Chibon d71d6b
    '''
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 d80e3b
        if element.name == filename or \
Pierre-Yves Chibon d80e3b
                (not filename and element.name.startswith('index')):
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 d71d6b
                    filepath.append('')
Pierre-Yves Chibon d71d6b
                return __get_tree(
Pierre-Yves Chibon d71d6b
                    repo_obj, repo_obj[element.oid], filepath,
Pierre-Yves Chibon d71d6b
                    index=index + 1, extended=True)
Pierre-Yves Chibon d71d6b
            else:
Pierre-Yves Chibon d71d6b
                return (element, tree, False)
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
    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 d71d6b
            'File %s not found' % ('/'.join(filepath),))
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
def __get_tree_and_content(repo_obj, commit, path):
Pierre-Yves Chibon d71d6b
    ''' 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 d71d6b
        repo_obj, commit.tree, path)
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 d71d6b
        flask.abort(404, '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 6dba78
                    blob_obj.data, ext)
Shengjing Zhu 348f25
                if safe:
Shengjing Zhu 348f25
                    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 d71d6b
@APP.route('/<repo>/')</repo>
Pierre-Yves Chibon 47c176
@APP.route('/<namespace>.<repo>/')</repo></namespace>
Pierre-Yves Chibon d71d6b
@APP.route('/<repo>/<path:filename>')</path:filename></repo>
Pierre-Yves Chibon 47c176
@APP.route('/<namespace>.<repo>/<path:filename>')</path:filename></repo></namespace>
Pierre-Yves Chibon d71d6b
@APP.route('/fork/<username>/<repo>/')</repo></username>
Pierre-Yves Chibon 47c176
@APP.route('/fork/<namespace>.<username>/<repo>/')</repo></username></namespace>
Pierre-Yves Chibon d71d6b
@APP.route('/fork/<username>/<repo>/<path:filename>')</path:filename></repo></username>
Pierre-Yves Chibon 47c176
@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 47c176
    if '.' in repo:
Pierre-Yves Chibon 47c176
        namespace, repo = repo.split('.', 1)
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon b130e5
    repo = pagure.lib.get_authorized_project(
Pierre-Yves Chibon c24ef8
        SESSION, repo, user=username, namespace=namespace)
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
    if not repo:
Pierre-Yves Chibon d71d6b
        flask.abort(404, 'Project not found')
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
    if not repo.settings.get('project_documentation', True):
Pierre-Yves Chibon 481fbf
        flask.abort(404, 'This project has documentation disabled')
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon d71d6b
    reponame = os.path.join(APP.config['DOCS_FOLDER'], repo.path)
Pierre-Yves Chibon d71d6b
    if not os.path.exists(reponame):
Pierre-Yves Chibon d71d6b
        flask.abort(404, '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(
René Genz bf835a
                '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">'
René Genz bf835a
                'Using the doc repository of your project documentation.'
Pierre-Yves Chibon 51cfa4
            )
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 d71d6b
        path = ['']
Pierre-Yves Chibon d71d6b
    else:
Pierre-Yves Chibon d71d6b
        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 d71d6b
                repo_obj, commit, path)
Pierre-Yves Chibon d71d6b
        except pagure.exceptions.FileNotFoundException as err:
Pierre-Yves Chibon d71d6b
            flask.flash(err.message, 'error')
Pierre-Yves Chibon f88a86
        except Exception as err:
Pierre-Yves Chibon 4635b5
            _log.exception(err)
Pierre-Yves Chibon f88a86
            flask.abort(500, 'Unkown error encountered and reported')
Pierre-Yves Chibon d71d6b
Pierre-Yves Chibon 501677
    if not content:
Pierre-Yves Chibon 8c4083
        if not tree or not len(tree):
René Genz bf835a
            flask.abort(404, 'No content found in the repository')
Pierre-Yves Chibon 7b96f4
        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 501677
                    name += '/'
    Pierre-Yves Chibon 9677a4
                html += ''.format(name, name)
    Pierre-Yves Chibon 7b96f4
            html += ''
    Pierre-Yves Chibon 7b96f4
            content = TMPL_HTML.format(content=html)
    Shengjing Zhu 348f25
            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)