Blame progit/ui/filters.py

Pierre-Yves Chibon 15e568
# -*- coding: utf-8 -*-
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
"""
Pierre-Yves Chibon 15e568
 (c) 2014 - Copyright Red Hat Inc
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
 Authors:
Pierre-Yves Chibon 15e568
   Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
"""
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
import datetime
Pierre-Yves Chibon 15e568
import textwrap
Pierre-Yves Chibon 15e568
import os
Pierre-Yves Chibon 8774fb
import re
Pierre-Yves Chibon 15e568
from math import ceil
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
import flask
Pierre-Yves Chibon 15e568
import arrow
Pierre-Yves Chibon 15e568
import markdown
Pierre-Yves Chibon 15e568
import pygit2
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
from sqlalchemy.exc import SQLAlchemyError
Pierre-Yves Chibon 15e568
from pygments import highlight
Pierre-Yves Chibon 15e568
from pygments.lexers import guess_lexer
Pierre-Yves Chibon 15e568
from pygments.lexers.text import DiffLexer
Pierre-Yves Chibon 15e568
from pygments.formatters import HtmlFormatter
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
import progit.exceptions
Pierre-Yves Chibon 15e568
import progit.lib
Pierre-Yves Chibon 15e568
import progit.forms
Pierre-Yves Chibon 15e568
from progit import (APP, SESSION, LOG, __get_file_in_tree, cla_required,
Pierre-Yves Chibon 15e568
                    generate_gitolite_acls, generate_gitolite_key,
Pierre-Yves Chibon 15e568
                    generate_authorized_key_file, authenticated)
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
# Jinja filters
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
@APP.template_filter('humanize')
Pierre-Yves Chibon 15e568
def humanize_date(date):
Pierre-Yves Chibon 15e568
    """ Template filter returning the last commit date of the provided repo.
Pierre-Yves Chibon 15e568
    """
Pierre-Yves Chibon 15e568
    return arrow.get(date).humanize()
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
@APP.template_filter('format_ts')
Pierre-Yves Chibon 15e568
def format_ts(string):
Pierre-Yves Chibon 15e568
    """ Template filter transforming a timestamp to a date
Pierre-Yves Chibon 15e568
    """
Pierre-Yves Chibon 15e568
    dt = datetime.datetime.fromtimestamp(int(string))
Pierre-Yves Chibon 15e568
    return dt.strftime('%b %d %Y %H:%M:%S')
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
@APP.template_filter('format_loc')
Pierre-Yves Chibon 15e568
def format_loc(loc, commit=None, filename=None, prequest=None, index=None):
Pierre-Yves Chibon 15e568
    """ Template filter putting the provided lines of code into a table
Pierre-Yves Chibon 15e568
    """
Pierre-Yves Chibon 15e568
    if loc is None:
Pierre-Yves Chibon 15e568
        return
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
    output = [
Pierre-Yves Chibon 15e568
        '
',
Pierre-Yves Chibon 15e568
        ''
Pierre-Yves Chibon 15e568
    ]
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
    comments = {}
Pierre-Yves Chibon 15e568
    if prequest and not isinstance(prequest, flask.wrappers.Request):
Pierre-Yves Chibon 15e568
        for com in prequest.comments:
Pierre-Yves Chibon 15e568
            if commit and com.commit_id == commit:
Pierre-Yves Chibon 15e568
                if com.line in comments:
Pierre-Yves Chibon 15e568
                    comments[com.line].append(com)
Pierre-Yves Chibon 15e568
                else:
Pierre-Yves Chibon 15e568
                    comments[com.line] = [com]
Pierre-Yves Chibon 15e568
    for key in comments:
Pierre-Yves Chibon 15e568
        comments[key] = sorted(
Pierre-Yves Chibon 15e568
            comments[key], key=lambda obj: obj.date_created)
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
    if not index:
Pierre-Yves Chibon 15e568
        index = ''
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
    cnt = 1
Pierre-Yves Chibon 15e568
    for line in loc.split('\n'):
Pierre-Yves Chibon 15e568
        if filename and commit:
Pierre-Yves Chibon 15e568
            output.append(
Pierre-Yves Chibon 15e568
                ''
Pierre-Yves Chibon 15e568
                '%(cnt_lbl)s'
Pierre-Yves Chibon 15e568
                '
Pierre-Yves Chibon 15e568
                ' data-filename="%(filename)s" data-commit="%(commit)s">'
Pierre-Yves Chibon 15e568
                '

'

Pierre-Yves Chibon 15e568
                'Add comment'
Pierre-Yves Chibon 15e568
                '

'
Pierre-Yves Chibon 15e568
                '' % (
Pierre-Yves Chibon 15e568
                    {
Pierre-Yves Chibon 15e568
                        'cnt': '%s_%s' % (index, cnt),
Pierre-Yves Chibon 15e568
                        'cnt_lbl': cnt,
Pierre-Yves Chibon 15e568
                        'img': flask.url_for('static', filename='users.png'),
Pierre-Yves Chibon 15e568
                        'filename': filename,
Pierre-Yves Chibon 15e568
                        'commit': commit,
Pierre-Yves Chibon 15e568
                    }
Pierre-Yves Chibon 15e568
                )
Pierre-Yves Chibon 15e568
            )
Pierre-Yves Chibon 15e568
        else:
Pierre-Yves Chibon 15e568
            output.append(
Pierre-Yves Chibon 15e568
                ''
Pierre-Yves Chibon 15e568
                '%(cnt_lbl)s'
Pierre-Yves Chibon 15e568
                % (
Pierre-Yves Chibon 15e568
                    {
Pierre-Yves Chibon 15e568
                        'cnt': '%s_%s' % (index, cnt),
Pierre-Yves Chibon 15e568
                        'cnt_lbl': cnt,
Pierre-Yves Chibon 15e568
                    }
Pierre-Yves Chibon 15e568
                )
Pierre-Yves Chibon 15e568
            )
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
        cnt += 1
Pierre-Yves Chibon 15e568
        if not line:
Pierre-Yves Chibon 15e568
            output.append(line)
Pierre-Yves Chibon 15e568
            continue
Pierre-Yves Chibon 15e568
        if line == '':
Pierre-Yves Chibon 15e568
            continue
Pierre-Yves Chibon 15e568
        if line.startswith('
Pierre-Yves Chibon 15e568
            line = line.split('
')[1]
Pierre-Yves Chibon 15e568
        output.append('
%s
' % line)
Pierre-Yves Chibon 15e568
        output.append('')
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
        if cnt - 1 in comments:
Pierre-Yves Chibon 15e568
            for comment in comments[cnt - 1]:
Pierre-Yves Chibon 15e568
                output.append(
Pierre-Yves Chibon 15e568
                    ''
Pierre-Yves Chibon 15e568
                    ''
Pierre-Yves Chibon 15e568
                    '%(user)s'
Pierre-Yves Chibon 15e568
                    '%(date)s'
Pierre-Yves Chibon 15e568
                    ''
Pierre-Yves Chibon 15e568
                    '%(comment)s'
Pierre-Yves Chibon 15e568
                    ''
Pierre-Yves Chibon 15e568
                    '' % (
Pierre-Yves Chibon 15e568
                        {
Pierre-Yves Chibon 15e568
                            'url': flask.url_for(
Pierre-Yves Chibon 15e568
                                'view_user', username=comment.user.user),
Pierre-Yves Chibon 15e568
                            'user': comment.user.user,
Pierre-Yves Chibon 15e568
                            'date': comment.date_created.strftime(
Pierre-Yves Chibon 15e568
                                '%b %d %Y %H:%M:%S'),
Pierre-Yves Chibon 15e568
                            'comment': comment.comment,
Pierre-Yves Chibon 15e568
                        }
Pierre-Yves Chibon 15e568
                    )
Pierre-Yves Chibon 15e568
                )
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
    output.append('')
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
    return '\n'.join(output)
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
@APP.template_filter('wraps')
Pierre-Yves Chibon 15e568
def text_wraps(text, size=10):
Pierre-Yves Chibon 15e568
    """ Template filter to wrap text at a specified size
Pierre-Yves Chibon 15e568
    """
Pierre-Yves Chibon 15e568
    if text:
Pierre-Yves Chibon 15e568
        parts = textwrap.wrap(text, size)
Pierre-Yves Chibon 15e568
        if len(parts) > 1:
Pierre-Yves Chibon 15e568
            parts = '%s...' % parts[0]
Pierre-Yves Chibon 15e568
        else:
Pierre-Yves Chibon 15e568
            parts = parts[0]
Pierre-Yves Chibon 15e568
        return parts
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
@APP.template_filter('avatar')
Pierre-Yves Chibon 15e568
def avatar(packager, size=64):
Pierre-Yves Chibon 15e568
    """ Template filter sorting the given branches, Fedora first then EPEL,
Pierre-Yves Chibon 15e568
    then whatever is left.
Pierre-Yves Chibon 15e568
    """
Pierre-Yves Chibon 15e568
    output = '' % (
Pierre-Yves Chibon 15e568
        progit.lib.avatar_url(packager, size)
Pierre-Yves Chibon 15e568
    )
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
    return output
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
@APP.template_filter('short')
Pierre-Yves Chibon 15e568
def shorted_commit(cid):
Pierre-Yves Chibon 15e568
    """Gets short version of the commit id"""
Pierre-Yves Chibon 15e568
    return cid[:APP.config['SHORT_LENGTH']]
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon f89a82
@APP.template_filter('crossref')
Pierre-Yves Chibon f89a82
def crossref_filter(text):
Pierre-Yves Chibon f89a82
    """ Template filter adding a link when the provided text references
Pierre-Yves Chibon f89a82
    another issue or pull-request.
Pierre-Yves Chibon f89a82
    """
Pierre-Yves Chibon f89a82
    regex = re.compile('.*\s#(\d+)', re.I)
Pierre-Yves Chibon f89a82
    if text:
Pierre-Yves Chibon f89a82
        if regex.match(text):
Pierre-Yves Chibon f89a82
Pierre-Yves Chibon f89a82
            url = flask.request.url
Pierre-Yves Chibon f89a82
            username = None
Pierre-Yves Chibon f89a82
            if 'fork/' in flask.request.url:
Pierre-Yves Chibon f89a82
                username, project = url.split('fork/')[1].split('/', 2)[:2]
Pierre-Yves Chibon f89a82
            else:
Pierre-Yves Chibon f89a82
                project = url.split(
Pierre-Yves Chibon f89a82
                    flask.request.url_root)[1].split('/', 1)[0]
Pierre-Yves Chibon f89a82
Pierre-Yves Chibon f89a82
            issueid = regex.match(text).group(1)
Pierre-Yves Chibon f89a82
            text = text.replace('#%s' % issueid, '[#%s](%s)' % (
Pierre-Yves Chibon f89a82
                issueid, flask.url_for(
Pierre-Yves Chibon f89a82
                    'view_issue', username=username, repo=project,
Pierre-Yves Chibon f89a82
                    issueid=issueid)
Pierre-Yves Chibon f89a82
                )
Pierre-Yves Chibon f89a82
            )
Pierre-Yves Chibon f89a82
Pierre-Yves Chibon f89a82
        return text
Pierre-Yves Chibon f89a82
    else:
Pierre-Yves Chibon f89a82
        return ''
Pierre-Yves Chibon f89a82
Pierre-Yves Chibon f89a82
Pierre-Yves Chibon 15e568
@APP.template_filter('markdown')
Pierre-Yves Chibon 15e568
def markdown_filter(text):
Pierre-Yves Chibon 15e568
    """ Template filter converting a string into html content using the
Pierre-Yves Chibon 15e568
    markdown library.
Pierre-Yves Chibon 15e568
    """
Pierre-Yves Chibon 15e568
    if text:
Pierre-Yves Chibon 15e568
        # Hack to allow blockquotes to be marked by ~~~
Pierre-Yves Chibon 15e568
        ntext = []
Pierre-Yves Chibon 15e568
        indent = False
Pierre-Yves Chibon 8774fb
        regexes = [
Pierre-Yves Chibon 8774fb
            re.compile('.*\s(http(s)?:\/\/\S+).*'),
Pierre-Yves Chibon 8774fb
            re.compile('^(http(s)?:\/\/\S+).*'),
Pierre-Yves Chibon 8774fb
            re.compile('.*\s(ftp(s)?:\/\/\S+).*'),
Pierre-Yves Chibon 8774fb
            re.compile('^(ftp(s)?:\/\/\S+).*'),
Pierre-Yves Chibon 8774fb
        ]
Pierre-Yves Chibon 15e568
        for line in text.split('\n'):
Pierre-Yves Chibon 8774fb
            # Automatically link URLs
Pierre-Yves Chibon 8774fb
            for regex in regexes:
Pierre-Yves Chibon 8774fb
                if regex.match(line):
Pierre-Yves Chibon 8774fb
                    line = line.replace(
Pierre-Yves Chibon 8774fb
                        regex.match(line).group(1),
Pierre-Yves Chibon 8774fb
                        regex.sub(r'<\1>', line)
Pierre-Yves Chibon 8774fb
                    )
Pierre-Yves Chibon 8774fb
Pierre-Yves Chibon 15e568
            if line.startswith('~~~'):
Pierre-Yves Chibon 15e568
                indent = not indent
Pierre-Yves Chibon 15e568
                continue
Pierre-Yves Chibon 15e568
            if indent:
Pierre-Yves Chibon 15e568
                line = '    %s' % line
Pierre-Yves Chibon 15e568
            ntext.append(line)
Pierre-Yves Chibon 15e568
        return markdown.markdown('\n'.join(ntext))
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
    return ''
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
@APP.template_filter('html_diff')
Pierre-Yves Chibon 15e568
def html_diff(diff):
Pierre-Yves Chibon 15e568
    """Display diff as HTML"""
Pierre-Yves Chibon 15e568
    if diff is None:
Pierre-Yves Chibon 15e568
        return
Pierre-Yves Chibon 15e568
    return highlight(
Pierre-Yves Chibon 15e568
        diff,
Pierre-Yves Chibon 15e568
        DiffLexer(),
Pierre-Yves Chibon 15e568
        HtmlFormatter(
Pierre-Yves Chibon 15e568
            noclasses=True,
Pierre-Yves Chibon 15e568
            style="tango",)
Pierre-Yves Chibon 15e568
    )
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
Pierre-Yves Chibon 15e568
@APP.template_filter('patch_to_diff')
Pierre-Yves Chibon 15e568
def patch_to_diff(patch):
Pierre-Yves Chibon 15e568
    """Render a hunk as a diff"""
Pierre-Yves Chibon 15e568
    content = ""
Pierre-Yves Chibon 15e568
    for hunk in patch.hunks:
Pierre-Yves Chibon 15e568
        content = content + "@@ -%i,%i +%i,%i @@\n" % (
Pierre-Yves Chibon 15e568
            hunk.old_start, hunk.old_lines, hunk.new_start, hunk.new_lines)
Pierre-Yves Chibon 15e568
        for line in hunk.lines:
Pierre-Yves Chibon 15e568
            content = content + ' '.join(line)
Pierre-Yves Chibon 15e568
    return content