Blob Blame Raw
# -*- coding: utf-8 -*-

"""
 (c) 2014 - Copyright Red Hat Inc

 Authors:
   Pierre-Yves Chibon <pingou@pingoured.fr>

"""

import datetime
import textwrap
import os
import re
from math import ceil

import flask
import arrow
import markdown
import pygit2

from sqlalchemy.exc import SQLAlchemyError
from pygments import highlight
from pygments.lexers import guess_lexer
from pygments.lexers.text import DiffLexer
from pygments.formatters import HtmlFormatter

import progit.exceptions
import progit.lib
import progit.forms
from progit import (APP, SESSION, LOG, __get_file_in_tree, cla_required,
                    generate_gitolite_acls, generate_gitolite_key,
                    generate_authorized_key_file, authenticated)


# Jinja filters


@APP.template_filter('humanize')
def humanize_date(date):
    """ Template filter returning the last commit date of the provided repo.
    """
    return arrow.get(date).humanize()


@APP.template_filter('format_ts')
def format_ts(string):
    """ Template filter transforming a timestamp to a date
    """
    dt = datetime.datetime.fromtimestamp(int(string))
    return dt.strftime('%b %d %Y %H:%M:%S')


@APP.template_filter('format_loc')
def format_loc(loc, commit=None, filename=None, prequest=None, index=None):
    """ Template filter putting the provided lines of code into a table
    """
    if loc is None:
        return

    output = [
        '<div class="highlight">',
        '<table class="code_table">'
    ]

    comments = {}
    if prequest and not isinstance(prequest, flask.wrappers.Request):
        for com in prequest.comments:
            if commit and com.commit_id == commit:
                if com.line in comments:
                    comments[com.line].append(com)
                else:
                    comments[com.line] = [com]
    for key in comments:
        comments[key] = sorted(
            comments[key], key=lambda obj: obj.date_created)

    if not index:
        index = ''

    cnt = 1
    for line in loc.split('\n'):
        if filename and commit:
            output.append(
                '<tr><td class="cell1">'
                '<a id="%(cnt)s" href="#%(cnt)s">%(cnt_lbl)s</a></td>'
                '<td class="prc" data-row="%(cnt_lbl)s"'
                ' data-filename="%(filename)s" data-commit="%(commit)s">'
                '<p>'
                '<img src="%(img)s" alt="Add comment" title="Add comment"/>'
                '</p>'
                '</td>' % (
                    {
                        'cnt': '%s_%s' % (index, cnt),
                        'cnt_lbl': cnt,
                        'img': flask.url_for('static', filename='users.png'),
                        'filename': filename,
                        'commit': commit,
                    }
                )
            )
        else:
            output.append(
                '<tr><td class="cell1">'
                '<a id="%(cnt)s" href="#%(cnt)s">%(cnt_lbl)s</a></td>'
                % (
                    {
                        'cnt': '%s_%s' % (index, cnt),
                        'cnt_lbl': cnt,
                    }
                )
            )

        cnt += 1
        if not line:
            output.append(line)
            continue
        if line == '</pre></div>':
            continue
        if line.startswith('<div'):
            line = line.split('<pre style="line-height: 125%">')[1]
        output.append('<td class="cell2"><pre>%s</pre></td>' % line)
        output.append('</tr>')

        if cnt - 1 in comments:
            for comment in comments[cnt - 1]:
                output.append(
                    '<tr><td></td>'
                    '<td colspan="2"><table style="width:100%%"><tr>'
                    '<td><a href="%(url)s">%(user)s</a></td>'
                    '<td class="right">%(date)s</td>'
                    '</tr>'
                    '<tr><td colspan="2" class="pr_comment">%(comment)s'
                    '</td></tr>'
                    '</table></td></tr>' % (
                        {
                            'url': flask.url_for(
                                'view_user', username=comment.user.user),
                            'user': comment.user.user,
                            'date': comment.date_created.strftime(
                                '%b %d %Y %H:%M:%S'),
                            'comment': comment.comment,
                        }
                    )
                )

    output.append('</table></div>')

    return '\n'.join(output)


@APP.template_filter('wraps')
def text_wraps(text, size=10):
    """ Template filter to wrap text at a specified size
    """
    if text:
        parts = textwrap.wrap(text, size)
        if len(parts) > 1:
            parts = '%s...' % parts[0]
        else:
            parts = parts[0]
        return parts


@APP.template_filter('avatar')
def avatar(packager, size=64):
    """ Template filter sorting the given branches, Fedora first then EPEL,
    then whatever is left.
    """
    output = '<img class="avatar circle" src="%s"/>' % (
        progit.lib.avatar_url(packager, size)
    )

    return output


@APP.template_filter('short')
def shorted_commit(cid):
    """Gets short version of the commit id"""
    return cid[:APP.config['SHORT_LENGTH']]


@APP.template_filter('crossref')
def crossref_filter(text):
    """ Template filter adding a link when the provided text references
    another issue or pull-request.
    """
    regex = re.compile('.*\s#(\d+)', re.I)
    if text:
        if regex.match(text):

            url = flask.request.url
            username = None
            if 'fork/' in flask.request.url:
                username, project = url.split('fork/')[1].split('/', 2)[:2]
            else:
                project = url.split(
                    flask.request.url_root)[1].split('/', 1)[0]

            issueid = regex.match(text).group(1)
            text = text.replace('#%s' % issueid, '[#%s](%s)' % (
                issueid, flask.url_for(
                    'view_issue', username=username, repo=project,
                    issueid=issueid)
                )
            )

        return text
    else:
        return ''


@APP.template_filter('markdown')
def markdown_filter(text):
    """ Template filter converting a string into html content using the
    markdown library.
    """
    if text:
        # Hack to allow blockquotes to be marked by ~~~
        ntext = []
        indent = False
        regexes = [
            re.compile('.*\s(http(s)?:\/\/\S+).*'),
            re.compile('^(http(s)?:\/\/\S+).*'),
            re.compile('.*\s(ftp(s)?:\/\/\S+).*'),
            re.compile('^(ftp(s)?:\/\/\S+).*'),
        ]
        for line in text.split('\n'):
            # Automatically link URLs
            for regex in regexes:
                if regex.match(line):
                    line = line.replace(
                        regex.match(line).group(1),
                        regex.sub(r'<\1>', line)
                    )

            if line.startswith('~~~'):
                indent = not indent
                continue
            if indent:
                line = '    %s' % line
            ntext.append(line)
        return markdown.markdown('\n'.join(ntext))

    return ''


@APP.template_filter('html_diff')
def html_diff(diff):
    """Display diff as HTML"""
    if diff is None:
        return
    return highlight(
        diff,
        DiffLexer(),
        HtmlFormatter(
            noclasses=True,
            style="tango",)
    )


@APP.template_filter('patch_to_diff')
def patch_to_diff(patch):
    """Render a hunk as a diff"""
    content = ""
    for hunk in patch.hunks:
        content = content + "@@ -%i,%i +%i,%i @@\n" % (
            hunk.old_start, hunk.old_lines, hunk.new_start, hunk.new_lines)
        for line in hunk.lines:
            content = content + ' '.join(line)
    return content