Blame progit/__init__.py

Pierre-Yves Chibon 2088eb
#-*- coding: utf-8 -*-
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
"""
Pierre-Yves Chibon 8a5345
 (c) 2014-2015 - Copyright Red Hat Inc
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
 Authors:
Pierre-Yves Chibon 2088eb
   Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
"""
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
## These two lines are needed to run on EL6
Pierre-Yves Chibon ee9b42
__requires__ = ['SQLAlchemy >= 0.8', 'jinja2 >= 2.4']
Pierre-Yves Chibon 2088eb
import pkg_resources
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon f006c4
__version__ = '0.0'
Pierre-Yves Chibon 96098d
__api_version__ = '0'
Pierre-Yves Chibon 96098d
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon eef6e0
import datetime
Pierre-Yves Chibon 2088eb
import logging
Pierre-Yves Chibon 2088eb
import os
Pierre-Yves Chibon df4e53
import subprocess
Pierre-Yves Chibon 4118fb
import textwrap
Pierre-Yves Chibon 9f17e1
import urlparse
Pierre-Yves Chibon 2088eb
from logging.handlers import SMTPHandler
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
import arrow
Pierre-Yves Chibon 2088eb
import flask
Pierre-Yves Chibon d8f19f
import pygit2
Pierre-Yves Chibon 2088eb
from flask_fas_openid import FAS
Pierre-Yves Chibon 2088eb
from functools import wraps
Pierre-Yves Chibon 2088eb
from sqlalchemy.exc import SQLAlchemyError
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon d558c6
import progit.lib
Pierre-Yves Chibon 077809
import progit.mail_logging
Pierre-Yves Chibon 89e681
import progit.doc_utils
Pierre-Yves Chibon 01b5fa
import progit.login_forms
Johan Cwiklinski fa06f9
import markdown
Pierre-Yves Chibon 2088eb
Johan Cwiklinski 002118
from pygments import highlight
Johan Cwiklinski 002118
from pygments.lexers.text import DiffLexer
Johan Cwiklinski 002118
from pygments.formatters import HtmlFormatter
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
# Create the application.
Pierre-Yves Chibon 2088eb
APP = flask.Flask(__name__)
Pierre-Yves Chibon 76e062
APP.jinja_env.trim_blocks = True
Pierre-Yves Chibon 76e062
APP.jinja_env.lstrip_blocks = True
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
# set up FAS
Pierre-Yves Chibon 2088eb
APP.config.from_object('progit.default_config')
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
if 'PROGIT_CONFIG' in os.environ:
Pierre-Yves Chibon 2088eb
    APP.config.from_envvar('PROGIT_CONFIG')
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
FAS = FAS(APP)
Pierre-Yves Chibon d558c6
SESSION = progit.lib.create_session(APP.config['DB_URL'])
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
if not APP.debug:
Pierre-Yves Chibon 077809
    APP.logger.addHandler(progit.mail_logging.get_mail_handler(
Pierre-Yves Chibon 077809
        smtp_server=APP.config.get('SMTP_SERVER', '127.0.0.1'),
Pierre-Yves Chibon 077809
        mail_admin=APP.config.get('MAIL_ADMIN', APP.config['EMAIL_ERROR'])
Pierre-Yves Chibon 077809
    ))
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
## Send classic logs into syslog
Pierre-Yves Chibon 2088eb
handler = logging.StreamHandler()
Pierre-Yves Chibon 2088eb
handler.setLevel(APP.config.get('log_level', 'INFO'))
Pierre-Yves Chibon 2088eb
APP.logger.addHandler(handler)
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
LOG = APP.logger
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
def authenticated():
Pierre-Yves Chibon 2088eb
    return hasattr(flask.g, 'fas_user') and flask.g.fas_user
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 9f17e1
def is_safe_url(target):
Pierre-Yves Chibon 9f17e1
    """ Checks that the target url is safe and sending to the current
Pierre-Yves Chibon 9f17e1
    website not some other malicious one.
Pierre-Yves Chibon 9f17e1
    """
Pierre-Yves Chibon 9f17e1
    ref_url = urlparse.urlparse(flask.request.host_url)
Pierre-Yves Chibon 9f17e1
    test_url = urlparse.urlparse(
Pierre-Yves Chibon 9f17e1
        urlparse.urljoin(flask.request.host_url, target))
Pierre-Yves Chibon 9f17e1
    return test_url.scheme in ('http', 'https') and \
Pierre-Yves Chibon 9f17e1
        ref_url.netloc == test_url.netloc
Pierre-Yves Chibon 9f17e1
Pierre-Yves Chibon 9f17e1
Pierre-Yves Chibon 2088eb
def is_admin():
Pierre-Yves Chibon 2088eb
    """ Return whether the user is admin for this application or not. """
Pierre-Yves Chibon c491be
    if not authenticated():
Pierre-Yves Chibon 2088eb
        return False
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon f58951
    user = flask.g.fas_user
Pierre-Yves Chibon f58951
Pierre-Yves Chibon c491be
    auth_method = APP.config.get('PROGIT_AUTH', None)
Pierre-Yves Chibon c491be
    if auth_method == 'fas':
Pierre-Yves Chibon c491be
        if not user.cla_done or len(user.groups) < 1:
Pierre-Yves Chibon c491be
            return False
Pierre-Yves Chibon c491be
Pierre-Yves Chibon 2088eb
    admins = APP.config['ADMIN_GROUP']
Pierre-Yves Chibon 2088eb
    if isinstance(admins, basestring):
Pierre-Yves Chibon c491be
        admins = [admins]
Pierre-Yves Chibon c491be
    admins = set(admins)
Pierre-Yves Chibon f58951
    groups = set(flask.g.fas_user.groups)
Pierre-Yves Chibon c491be
Pierre-Yves Chibon f58951
    return not groups.isdisjoint(admins)
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon d5f37b
def is_repo_admin(repo_obj):
Pierre-Yves Chibon d5f37b
    """ Return whether the user is an admin of the provided repo. """
Pierre-Yves Chibon 787f4e
    if not authenticated():
Pierre-Yves Chibon d5f37b
        return False
Pierre-Yves Chibon d5f37b
Pierre-Yves Chibon d5f37b
    user = flask.g.fas_user.username
Pierre-Yves Chibon d5f37b
Pierre-Yves Chibon 7f7a0e
    return user == repo_obj.user.user or (
Pierre-Yves Chibon 7f7a0e
        user in [user.user for user in repo_obj.users])
Pierre-Yves Chibon d5f37b
Pierre-Yves Chibon d5f37b
Pierre-Yves Chibon a2d51a
def generate_gitolite_acls():
Pierre-Yves Chibon a2d51a
    """ Generate the gitolite configuration file for all repos
Pierre-Yves Chibon a2d51a
    """
Pierre-Yves Chibon 7d3f17
    progit.lib.git.write_gitolite_acls(
Pierre-Yves Chibon a4cc93
        SESSION, APP.config['GITOLITE_CONFIG'])
Pierre-Yves Chibon a2d51a
Pierre-Yves Chibon 77a4ae
    gitolite_folder = APP.config.get('GITOLITE_HOME', None)
Pierre-Yves Chibon 77a4ae
    if gitolite_folder:
Pierre-Yves Chibon 1280ca
        cmd = 'GL_RC=%s GL_BINDIR=%s gl-compile-conf' % (
Pierre-Yves Chibon 1280ca
            APP.config.get('GL_RC'), APP.config.get('GL_BINDIR')
Pierre-Yves Chibon 1280ca
        )
Pierre-Yves Chibon 412880
        output = subprocess.Popen(
Pierre-Yves Chibon 412880
            cmd,
Pierre-Yves Chibon 1280ca
            shell=True,
Pierre-Yves Chibon 1280ca
            stdout=subprocess.PIPE,
Pierre-Yves Chibon 1280ca
            stderr=subprocess.PIPE,
Pierre-Yves Chibon 1280ca
            cwd=gitolite_folder
Pierre-Yves Chibon 1280ca
        )
Pierre-Yves Chibon 77a4ae
Pierre-Yves Chibon a2d51a
Pierre-Yves Chibon f1d3a6
def generate_gitolite_key(user, key):
Pierre-Yves Chibon f1d3a6
    """ Generate the gitolite ssh key file for the specified user
Pierre-Yves Chibon f1d3a6
    """
Pierre-Yves Chibon f1d3a6
    gitolite_keydir = APP.config.get('GITOLITE_KEYDIR', None)
Pierre-Yves Chibon f1d3a6
    if gitolite_keydir:
Pierre-Yves Chibon f1d3a6
        keyfile = os.path.join(gitolite_keydir, '%s.pub' % user)
Pierre-Yves Chibon f1d3a6
        with open(keyfile, 'w') as stream:
Pierre-Yves Chibon f1d3a6
            stream.write(key + '\n')
Pierre-Yves Chibon f1d3a6
Pierre-Yves Chibon f1d3a6
Pierre-Yves Chibon 078c2b
def generate_authorized_key_file():
Pierre-Yves Chibon 078c2b
    """ Regenerate the `authorized_keys` file used by gitolite.
Pierre-Yves Chibon 078c2b
    """
Pierre-Yves Chibon 078c2b
    gitolite_home = APP.config.get('GITOLITE_HOME', None)
Pierre-Yves Chibon 078c2b
    if gitolite_home:
Pierre-Yves Chibon a1a039
        users = progit.lib.search_user(SESSION)
Pierre-Yves Chibon 078c2b
Pierre-Yves Chibon 078c2b
        authorized_file = os.path.join(
Pierre-Yves Chibon 078c2b
            gitolite_home, '.ssh', 'authorized_keys')
Pierre-Yves Chibon 6310e8
        with open(authorized_file, 'w') as stream:
Pierre-Yves Chibon 078c2b
            stream.write('# gitolite start\n')
Pierre-Yves Chibon 078c2b
            for user in users:
Pierre-Yves Chibon 078c2b
                if not user.public_ssh_key:
Pierre-Yves Chibon 078c2b
                    continue
Pierre-Yves Chibon 078c2b
                row = 'command="/usr/bin/gl-auth-command %s",' \
Pierre-Yves Chibon 078c2b
                      'no-port-forwarding,no-X11-forwarding,'\
Pierre-Yves Chibon 078c2b
                      'no-agent-forwarding,no-pty %s' % (
Pierre-Yves Chibon 05748c
                          user.user, user.public_ssh_key)
Pierre-Yves Chibon 078c2b
                stream.write(row + '\n')
Pierre-Yves Chibon 078c2b
            stream.write('# gitolite end\n')
Pierre-Yves Chibon 078c2b
Pierre-Yves Chibon 078c2b
Pierre-Yves Chibon 2088eb
def cla_required(function):
Pierre-Yves Chibon 2088eb
    """ Flask decorator to retrict access to CLA signed user.
Pierre-Yves Chibon 2088eb
To use this decorator you need to have a function named 'auth_login'.
Pierre-Yves Chibon 2088eb
Without that function the redirect if the user is not logged in will not
Pierre-Yves Chibon 2088eb
work.
Pierre-Yves Chibon 2088eb
"""
Pierre-Yves Chibon aa949a
    auth_method = APP.config.get('PROGIT_AUTH', None)
Pierre-Yves Chibon 2088eb
    @wraps(function)
Pierre-Yves Chibon 2088eb
    def decorated_function(*args, **kwargs):
Pierre-Yves Chibon 2088eb
        """ Decorated function, actually does the work. """
Pierre-Yves Chibon 2088eb
        if not authenticated():
Pierre-Yves Chibon 2088eb
            return flask.redirect(
Pierre-Yves Chibon 2088eb
                flask.url_for('auth_login', next=flask.request.url))
Pierre-Yves Chibon aa949a
        elif auth_method == 'fas' and not flask.g.fas_user.cla_done:
Pierre-Yves Chibon 2088eb
            flask.flash('You must sign the CLA (Contributor License '
Pierre-Yves Chibon 767d71
                        'Agreement to use progit', 'errors')
Pierre-Yves Chibon 2088eb
            return flask.redirect(flask.url_for('.index'))
Pierre-Yves Chibon 2088eb
        return function(*args, **kwargs)
Pierre-Yves Chibon 2088eb
    return decorated_function
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
@APP.context_processor
Pierre-Yves Chibon 2088eb
def inject_variables():
Pierre-Yves Chibon 2088eb
    """ With this decorator we can set some variables to all templates.
Pierre-Yves Chibon 2088eb
    """
Pierre-Yves Chibon 2088eb
    user_admin = is_admin()
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
    return dict(
Pierre-Yves Chibon 2088eb
        version=__version__,
Pierre-Yves Chibon 2088eb
        admin=user_admin,
Pierre-Yves Chibon b9c444
        authenticated=authenticated(),
Pierre-Yves Chibon 2088eb
    )
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 6428d8
Pierre-Yves Chibon 25369f
# pylint: disable=W0613
Pierre-Yves Chibon 25369f
@APP.before_request
Pierre-Yves Chibon 25369f
def set_session():
Pierre-Yves Chibon 25369f
    """ Set the flask session as permanent. """
Pierre-Yves Chibon 25369f
    flask.session.permanent = True
Pierre-Yves Chibon 25369f
Pierre-Yves Chibon 25369f
Pierre-Yves Chibon e108db
@APP.template_filter('hasattr')
Pierre-Yves Chibon e108db
def jinja_hasattr(obj, string):
Pierre-Yves Chibon e108db
    """ Template filter checking if the provided object at the provided
Pierre-Yves Chibon e108db
    string as attribute
Pierre-Yves Chibon e108db
    """
Pierre-Yves Chibon e108db
    return hasattr(obj, string)
Pierre-Yves Chibon e108db
Pierre-Yves Chibon e108db
Pierre-Yves Chibon 2088eb
@APP.template_filter('lastcommit_date')
Pierre-Yves Chibon 2088eb
def lastcommit_date_filter(repo):
Pierre-Yves Chibon 2088eb
    """ Template filter returning the last commit date of the provided repo.
Pierre-Yves Chibon 2088eb
    """
Pierre-Yves Chibon 671d31
    if not repo.is_empty:
Pierre-Yves Chibon 671d31
        commit = repo[repo.head.target]
Pierre-Yves Chibon 671d31
        return arrow.get(commit.commit_time).humanize()
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 6e710b
@APP.template_filter('humanize')
Pierre-Yves Chibon 6e710b
def humanize_date(date):
Pierre-Yves Chibon 6e710b
    """ Template filter returning the last commit date of the provided repo.
Pierre-Yves Chibon 6e710b
    """
Pierre-Yves Chibon 6e710b
    return arrow.get(date).humanize()
Pierre-Yves Chibon 6e710b
Pierre-Yves Chibon 6e710b
Pierre-Yves Chibon c560ab
@APP.template_filter('rst2html')
Pierre-Yves Chibon c560ab
def rst2html(rst_string):
Pierre-Yves Chibon c560ab
    """ Template filter transforming rst text into html
Pierre-Yves Chibon c560ab
    """
Pierre-Yves Chibon cbdf87
    if rst_string:
Pierre-Yves Chibon cbdf87
        return progit.doc_utils.convert_doc(unicode(rst_string))
Pierre-Yves Chibon c560ab
Pierre-Yves Chibon c560ab
Pierre-Yves Chibon eef6e0
@APP.template_filter('format_ts')
Pierre-Yves Chibon eef6e0
def format_ts(string):
Pierre-Yves Chibon eef6e0
    """ Template filter transforming a timestamp to a date
Pierre-Yves Chibon eef6e0
    """
Pierre-Yves Chibon eef6e0
    dt = datetime.datetime.fromtimestamp(int(string))
Pierre-Yves Chibon eef6e0
    return dt.strftime('%b %d %Y %H:%M:%S')
Pierre-Yves Chibon eef6e0
Pierre-Yves Chibon eef6e0
Pierre-Yves Chibon 8a11eb
@APP.template_filter('format_loc')
Pierre-Yves Chibon 6f8e9d
def format_loc(loc, commit=None, filename=None, prequest=None, index=None):
Pierre-Yves Chibon 8a11eb
    """ Template filter putting the provided lines of code into a table
Pierre-Yves Chibon 8a11eb
    """
Pierre-Yves Chibon 126396
    if loc is None:
Pierre-Yves Chibon 126396
        return
Pierre-Yves Chibon 126396
Pierre-Yves Chibon 8a11eb
    output = [
Pierre-Yves Chibon f7c6d4
        '
',
Pierre-Yves Chibon 8a11eb
        ''
Pierre-Yves Chibon 8a11eb
    ]
Pierre-Yves Chibon 12e0df
Pierre-Yves Chibon 12e0df
    comments = {}
Pierre-Yves Chibon 3d1811
    if prequest and not isinstance(prequest, flask.wrappers.Request):
Pierre-Yves Chibon 12e0df
        for com in prequest.comments:
Pierre-Yves Chibon 6f8e9d
            if commit and com.commit_id == commit:
Pierre-Yves Chibon 61123c
                if com.line in comments:
Pierre-Yves Chibon 61123c
                    comments[com.line].append(com)
Pierre-Yves Chibon 61123c
                else:
Pierre-Yves Chibon 61123c
                    comments[com.line] = [com]
Pierre-Yves Chibon 61123c
    for key in comments:
Pierre-Yves Chibon 6f8e9d
        comments[key] = sorted(
Pierre-Yves Chibon 6f8e9d
            comments[key], key=lambda obj: obj.date_created)
Pierre-Yves Chibon 12e0df
Pierre-Yves Chibon fe7ea3
    if not index:
Pierre-Yves Chibon fe7ea3
        index = ''
Pierre-Yves Chibon fe7ea3
Pierre-Yves Chibon 8a11eb
    cnt = 1
Pierre-Yves Chibon 8a11eb
    for line in loc.split('\n'):
Pierre-Yves Chibon 6f8e9d
        if filename and commit:
Pierre-Yves Chibon 03b8f4
            output.append(
Pierre-Yves Chibon 03b8f4
                ''
Pierre-Yves Chibon fe7ea3
                '%(cnt_lbl)s'
Pierre-Yves Chibon 6f8e9d
                '
Pierre-Yves Chibon 6f8e9d
                ' data-filename="%(filename)s" data-commit="%(commit)s">'
Pierre-Yves Chibon 03b8f4
                '

'

Pierre-Yves Chibon 03b8f4
                'Add comment'
Pierre-Yves Chibon 03b8f4
                '

'
Pierre-Yves Chibon 03b8f4
                '' % (
Pierre-Yves Chibon 03b8f4
                    {
Pierre-Yves Chibon fe7ea3
                        'cnt': '%s_%s' % (index, cnt),
Pierre-Yves Chibon fe7ea3
                        'cnt_lbl': cnt,
Pierre-Yves Chibon 03b8f4
                        'img': flask.url_for('static', filename='users.png'),
Pierre-Yves Chibon 6f8e9d
                        'filename': filename,
Pierre-Yves Chibon 6f8e9d
                        'commit': commit,
Pierre-Yves Chibon 03b8f4
                    }
Pierre-Yves Chibon 03b8f4
                )
Pierre-Yves Chibon 03b8f4
            )
Pierre-Yves Chibon 03b8f4
        else:
Pierre-Yves Chibon 03b8f4
            output.append(
Pierre-Yves Chibon 03b8f4
                ''
Pierre-Yves Chibon 23b035
                '%(cnt_lbl)s'
Pierre-Yves Chibon fe7ea3
                % (
Pierre-Yves Chibon fe7ea3
                    {
Pierre-Yves Chibon fe7ea3
                        'cnt': '%s_%s' % (index, cnt),
Pierre-Yves Chibon fe7ea3
                        'cnt_lbl': cnt,
Pierre-Yves Chibon fe7ea3
                    }
Pierre-Yves Chibon fe7ea3
                )
Pierre-Yves Chibon 03b8f4
            )
Pierre-Yves Chibon 03b8f4
Pierre-Yves Chibon 8a11eb
        cnt += 1
Pierre-Yves Chibon 8a11eb
        if not line:
Pierre-Yves Chibon 8a11eb
            output.append(line)
Pierre-Yves Chibon 8a11eb
            continue
Pierre-Yves Chibon 8a11eb
        if line == '':
Pierre-Yves Chibon 8a11eb
            continue
Pierre-Yves Chibon 8a11eb
        if line.startswith('
Pierre-Yves Chibon 8a11eb
            line = line.split('
')[1]
Pierre-Yves Chibon b7dd86
        output.append('
%s
' % line)
Pierre-Yves Chibon 8a11eb
        output.append('')
Pierre-Yves Chibon 12e0df
Pierre-Yves Chibon 12e0df
        if cnt - 1 in comments:
Pierre-Yves Chibon 692b92
            for comment in comments[cnt -1]:
Pierre-Yves Chibon 692b92
                output.append(
Pierre-Yves Chibon 692b92
                    ''
Pierre-Yves Chibon 692b92
                    ''
Pierre-Yves Chibon 692b92
                    '%(user)s%(date)s'
Pierre-Yves Chibon 692b92
                    ''
Pierre-Yves Chibon 692b92
                    '%(comment)s'
Pierre-Yves Chibon 692b92
                    '' % (
Pierre-Yves Chibon 692b92
                        {
Pierre-Yves Chibon 692b92
                            'url': flask.url_for(
Pierre-Yves Chibon 692b92
                                'view_user', username=comment.user.user),
Pierre-Yves Chibon 692b92
                            'user': comment.user.user,
Pierre-Yves Chibon 692b92
                            'date': comment.date_created.strftime(
Pierre-Yves Chibon 692b92
                                '%b %d %Y %H:%M:%S'),
Pierre-Yves Chibon 692b92
                            'comment': comment.comment,
Pierre-Yves Chibon 692b92
                        }
Pierre-Yves Chibon 692b92
                    )
Pierre-Yves Chibon 12e0df
                )
Pierre-Yves Chibon 12e0df
Pierre-Yves Chibon 8a11eb
    output.append('')
Pierre-Yves Chibon 8a11eb
Pierre-Yves Chibon 8a11eb
    return '\n'.join(output)
Pierre-Yves Chibon 8a11eb
Pierre-Yves Chibon 8a11eb
Pierre-Yves Chibon 4118fb
@APP.template_filter('wraps')
Pierre-Yves Chibon 4118fb
def text_wraps(text, size=10):
Pierre-Yves Chibon 4118fb
    """ Template filter to wrap text at a specified size
Pierre-Yves Chibon 4118fb
    """
Pierre-Yves Chibon 4118fb
    if text:
Pierre-Yves Chibon 4118fb
        parts = textwrap.wrap(text, size)
Pierre-Yves Chibon 4118fb
        if len(parts) > 1:
Pierre-Yves Chibon 4118fb
            parts = '%s...' % parts[0]
Pierre-Yves Chibon 4118fb
        else:
Pierre-Yves Chibon 4118fb
            parts = parts[0]
Pierre-Yves Chibon 4118fb
        return parts
Pierre-Yves Chibon 4118fb
Pierre-Yves Chibon 4118fb
Pierre-Yves Chibon 8d50a9
@APP.template_filter('avatar')
Pierre-Yves Chibon 8d50a9
def avatar(packager, size=64):
Pierre-Yves Chibon 8d50a9
    """ Template filter sorting the given branches, Fedora first then EPEL,
Pierre-Yves Chibon 8d50a9
    then whatever is left.
Pierre-Yves Chibon 8d50a9
    """
Pierre-Yves Chibon 8d50a9
    output = '' % (
Pierre-Yves Chibon 8d50a9
        progit.lib.avatar_url(packager, size)
Pierre-Yves Chibon 8d50a9
    )
Pierre-Yves Chibon 8d50a9
Pierre-Yves Chibon 8d50a9
    return output
Pierre-Yves Chibon 8d50a9
Pierre-Yves Chibon 8d50a9
Johan Cwiklinski b930cf
@APP.template_filter('short')
Johan Cwiklinski b930cf
def shorted_commit(cid):
Johan Cwiklinski b930cf
    """Gets short version of the commit id"""
Johan Cwiklinski b930cf
    return cid[:APP.config['SHORT_LENGTH']]
Johan Cwiklinski b930cf
Johan Cwiklinski b930cf
Johan Cwiklinski fa06f9
@APP.template_filter('markdown')
Johan Cwiklinski fa06f9
def markdown_filter(text):
Johan Cwiklinski fa06f9
    """ Template filter converting a string into html content using the
Johan Cwiklinski fa06f9
    markdown library.
Johan Cwiklinski fa06f9
    """
Johan Cwiklinski fa06f9
    if text:
Pierre-Yves Chibon cbb77d
        # Hack to allow blockquotes to be marked by ~~~
Pierre-Yves Chibon cbb77d
        ntext = []
Pierre-Yves Chibon cbb77d
        indent = False
Pierre-Yves Chibon cbb77d
        for line in text.split('\n'):
Pierre-Yves Chibon cbb77d
            if line.startswith('~~~'):
Pierre-Yves Chibon cbb77d
                indent = not indent
Pierre-Yves Chibon cbb77d
                continue
Pierre-Yves Chibon cbb77d
            if indent:
Pierre-Yves Chibon cbb77d
                line = '    %s' % line
Pierre-Yves Chibon cbb77d
            ntext.append(line)
Pierre-Yves Chibon cbb77d
        return markdown.markdown('\n'.join(ntext))
Johan Cwiklinski fa06f9
Johan Cwiklinski fa06f9
    return ''
Johan Cwiklinski fa06f9
Johan Cwiklinski fa06f9
Johan Cwiklinski 002118
@APP.template_filter('html_diff')
Johan Cwiklinski 002118
def html_diff(diff):
Johan Cwiklinski 002118
    """Display diff as HTML"""
Pierre-Yves Chibon 126396
    if diff is None:
Pierre-Yves Chibon 126396
        return
Johan Cwiklinski 002118
    return highlight(
Johan Cwiklinski 002118
        diff,
Johan Cwiklinski 002118
        DiffLexer(),
Johan Cwiklinski 002118
        HtmlFormatter(
Johan Cwiklinski 002118
            noclasses=True,
Johan Cwiklinski 002118
            style="tango",)
Johan Cwiklinski 002118
    )
Johan Cwiklinski 002118
Johan Cwiklinski 002118
Johan Cwiklinski 002118
@APP.template_filter('patch_to_diff')
Johan Cwiklinski 002118
def patch_to_diff(patch):
Johan Cwiklinski 002118
    """Render a hunk as a diff"""
Johan Cwiklinski 002118
    content = ""
Johan Cwiklinski 002118
    for hunk in patch.hunks:
Johan Cwiklinski 002118
        content = content + "@@ -%i,%i +%i,%i @@\n" % (hunk.old_start,
Johan Cwiklinski 002118
            hunk.old_lines, hunk.new_start, hunk.new_lines)
Johan Cwiklinski 002118
        for line in hunk.lines:
Johan Cwiklinski 002118
            content = content + ' '.join(line)
Johan Cwiklinski 002118
    return content
Johan Cwiklinski 002118
Johan Cwiklinski 002118
Pierre-Yves Chibon e6a6e2
@FAS.postlogin
Pierre-Yves Chibon e6a6e2
def set_user(return_url):
Pierre-Yves Chibon e6a6e2
    ''' After login method. '''
Pierre-Yves Chibon e6a6e2
    try:
Pierre-Yves Chibon e6a6e2
        progit.lib.set_up_user(
Pierre-Yves Chibon e6a6e2
            session=SESSION,
Pierre-Yves Chibon e6a6e2
            username=flask.g.fas_user.username,
Pierre-Yves Chibon e6a6e2
            fullname=flask.g.fas_user.fullname,
Pierre-Yves Chibon e6a6e2
            user_email=flask.g.fas_user.email,
Pierre-Yves Chibon e6a6e2
        )
Pierre-Yves Chibon e6a6e2
        SESSION.commit()
Pierre-Yves Chibon e6a6e2
    except SQLAlchemyError, err:
Pierre-Yves Chibon c3beca
        SESSION.rollback()
Pierre-Yves Chibon 01f0bb
        LOG.debug(err)
Pierre-Yves Chibon 01f0bb
        LOG.exception(err)
Pierre-Yves Chibon e6a6e2
        flask.flash(
Pierre-Yves Chibon e6a6e2
            'Could not set up you as a user properly, please contact '
Pierre-Yves Chibon e6a6e2
            'an admin', 'error')
Pierre-Yves Chibon e6a6e2
    return flask.redirect(return_url)
Pierre-Yves Chibon e6a6e2
Pierre-Yves Chibon e6a6e2
Johan Cwiklinski 2eb061
@APP.errorhandler(404)
Johan Cwiklinski 2eb061
def not_found(error):
Johan Cwiklinski 2eb061
    """404 Not Found page"""
Johan Cwiklinski 2eb061
    return flask.render_template('not_found.html'), 404
Johan Cwiklinski 2eb061
Johan Cwiklinski 2eb061
Johan Cwiklinski 2eb061
@APP.errorhandler(500)
Johan Cwiklinski 2eb061
def fatal_error(error):
Johan Cwiklinski 2eb061
    """500 Fatal Error page"""
Johan Cwiklinski 2eb061
    return flask.render_template('fatal_error.html'), 500
Johan Cwiklinski 2eb061
Johan Cwiklinski 2eb061
Johan Cwiklinski 2eb061
@APP.errorhandler(401)
Johan Cwiklinski 2eb061
def unauthorized(error):
Johan Cwiklinski 2eb061
    """401 Unauthorized page"""
Johan Cwiklinski 2eb061
    return flask.render_template('unauthorized.html'), 401
Johan Cwiklinski 2eb061
Johan Cwiklinski 2eb061
Pierre-Yves Chibon 2088eb
@APP.route('/login/', methods=('GET', 'POST'))
Pierre-Yves Chibon 2088eb
def auth_login():
Pierre-Yves Chibon 2088eb
    """ Method to log into the application using FAS OpenID. """
Pierre-Yves Chibon 2088eb
    return_point = flask.url_for('index')
Pierre-Yves Chibon 2088eb
    if 'next' in flask.request.args:
Pierre-Yves Chibon 9f17e1
        if is_safe_url(flask.request.args['next']):
Pierre-Yves Chibon 9f17e1
            return_point = flask.request.args['next']
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
    if authenticated():
Pierre-Yves Chibon 2088eb
        return flask.redirect(return_point)
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 85023f
    admins = APP.config['ADMIN_GROUP']
Pierre-Yves Chibon 85023f
    if isinstance(admins, basestring):
Pierre-Yves Chibon 85023f
        admins = set([admins])
Pierre-Yves Chibon 85023f
    else:  # pragma: no cover
Pierre-Yves Chibon 85023f
        admins = set(admins)
Pierre-Yves Chibon 85023f
Pierre-Yves Chibon 01b5fa
    if APP.config.get('PROGIT_AUTH', None) == 'fas':
Pierre-Yves Chibon 01b5fa
        return FAS.login(return_url=return_point, groups=admins)
Pierre-Yves Chibon 01b5fa
    elif APP.config.get('PROGIT_AUTH', None) == 'local':
Pierre-Yves Chibon 01b5fa
        form = progit.login_forms.LoginForm()
Pierre-Yves Chibon 01b5fa
        return flask.render_template(
Pierre-Yves Chibon 01b5fa
            'login/login.html',
Pierre-Yves Chibon 01b5fa
            next_url=return_point,
Pierre-Yves Chibon 01b5fa
            form=form,
Pierre-Yves Chibon 01b5fa
        )
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 2088eb
@APP.route('/logout/')
Pierre-Yves Chibon 2088eb
def auth_logout():
Pierre-Yves Chibon 2088eb
    """ Method to log out from the application. """
Pierre-Yves Chibon a3f381
    return_point = flask.url_for('index')
Pierre-Yves Chibon a3f381
    if 'next' in flask.request.args:
Pierre-Yves Chibon a3f381
        if is_safe_url(flask.request.args['next']):
Pierre-Yves Chibon a3f381
            return_point = flask.request.args['next']
Pierre-Yves Chibon a3f381
Pierre-Yves Chibon 2088eb
    if not authenticated():
Pierre-Yves Chibon a3f381
        return flask.redirect(return_point)
Pierre-Yves Chibon a3f381
Pierre-Yves Chibon 09b4ef
    if APP.config.get('PROGIT_AUTH', None) == 'fas':
Pierre-Yves Chibon 09b4ef
        if hasattr(flask.g, 'fas_user') and flask.g.fas_user is not None:
Pierre-Yves Chibon 09b4ef
            FAS.logout()
Pierre-Yves Chibon 09b4ef
            flask.flash("You are no longer logged-in")
Pierre-Yves Chibon 09b4ef
    elif APP.config.get('PROGIT_AUTH', None) == 'local':
Pierre-Yves Chibon 09b4ef
        login.logout()
Pierre-Yves Chibon a3f381
    return flask.redirect(return_point)
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon d8f19f
Pierre-Yves Chibon d8f19f
def __get_file_in_tree(repo_obj, tree, filepath):
Pierre-Yves Chibon d8f19f
    ''' Retrieve the entry corresponding to the provided filename in a
Pierre-Yves Chibon d8f19f
    given tree.
Pierre-Yves Chibon d8f19f
    '''
Pierre-Yves Chibon d8f19f
    filename = filepath[0]
Pierre-Yves Chibon d8f19f
    if isinstance(tree, pygit2.Blob):
Pierre-Yves Chibon d8f19f
        return
Pierre-Yves Chibon d8f19f
    for el in tree:
Pierre-Yves Chibon d8f19f
        if el.name == filename:
Pierre-Yves Chibon d8f19f
            if len(filepath) == 1:
Pierre-Yves Chibon d8f19f
                return repo_obj[el.oid]
Pierre-Yves Chibon d8f19f
            else:
Pierre-Yves Chibon d8f19f
                return __get_file_in_tree(
Pierre-Yves Chibon d8f19f
                    repo_obj, repo_obj[el.oid], filepath[1:])
Pierre-Yves Chibon d8f19f
Pierre-Yves Chibon 2088eb
Pierre-Yves Chibon 754cef
## Import the application
Pierre-Yves Chibon 9a7c26
import progit.ui.app
Pierre-Yves Chibon 9a7c26
import progit.ui.admin
Pierre-Yves Chibon 9a7c26
import progit.ui.docs
Pierre-Yves Chibon 9a7c26
import progit.ui.fork
Pierre-Yves Chibon 9a7c26
import progit.ui.issues
Pierre-Yves Chibon 9a7c26
import progit.ui.plugins
Pierre-Yves Chibon 9a7c26
import progit.ui.repo
Pierre-Yves Chibon 754cef
Pierre-Yves Chibon 96098d
import progit.api
Pierre-Yves Chibon 96098d
APP.register_blueprint(progit.api.API)
Pierre-Yves Chibon 96098d
Pierre-Yves Chibon 754cef
Pierre-Yves Chibon 754cef
# Only import the login controller if the app is set up for local login
Pierre-Yves Chibon 754cef
if APP.config.get('PROGIT_AUTH', None) == 'local':
Pierre-Yves Chibon 754cef
    import progit.ui.login as login
Pierre-Yves Chibon 754cef
    APP.before_request(login._check_session_cookie)
Pierre-Yves Chibon 754cef
    APP.after_request(login._send_session_cookie)