diff --git a/pagure/templates/blame.html b/pagure/templates/blame.html new file mode 100644 index 0000000..311366a --- /dev/null +++ b/pagure/templates/blame.html @@ -0,0 +1,226 @@ +{% extends "repo_master.html" %} + +{% block title %}Tree - {{ + repo.namespace + '/' if repo.namespace }}{{ repo.name }}{% endblock %} +{% set tag = "home" %} + + +{% block repo %} +
+
+

+ Blame {{ filename }} +

+
+ +
+
+
+ {% if repo.is_fork %} + + {{ repo.user.user }}/{{ repo.name }} + {% else %} + {{ repo.name }} + {% endif %} + +
+ {% if branchname %} +
+ + +
+ {% endif %} +
+
+ +
+
+
+ +
+ +{% if content %} + {% if output_type in ('file','binary','image','markup', 'blame') %} +
+ {% if content %} + {% if output_type in ('file','binary','image','markup', 'blame') %} +
+ {% if output_type in ('file','markup') and g.repo_admin %} + Edit + {% endif %} + {% if output_type in ('file','markup') %} +
+ + {{ forkbuttonform.csrf_token }} +
+ {% endif %} + {% if output_type == 'markup' %} + Text + {% else %} + Blob + {% endif %} + + Raw +
+ {% endif %} + {% endif %} + + + {% autoescape false %} + {{ content | blame_loc(repo, username, blame) }} + {% endautoescape %} +
+ {% endif %} +{% else %} +No content found in this repository +{% endif %} +
+ + {% if readme %} +
+
+ README{{readme_ext}} +
+
+ {% if safe %} + {{ readme | noJS |safe }} + {% else %} + {{ readme | noJS }} + {% endif %} +
+
+ {% endif %} + +{% endblock %} + +{% block jscripts %} +{{ super() }} + +{% endblock %} diff --git a/pagure/ui/filters.py b/pagure/ui/filters.py index 092b77b..cce6543 100644 --- a/pagure/ui/filters.py +++ b/pagure/ui/filters.py @@ -240,6 +240,64 @@ def format_loc(loc, commit=None, filename=None, tree_id=None, prequest=None, return '\n'.join(output) +@APP.template_filter('blame_loc') +def blame_loc(loc, repo, username, blame): + """ Template filter putting the provided lines of code into a table + """ + if loc is None: + return + + output = [ + '
', + '' + ] + + cnt = 1 + for idx, line in enumerate(loc.split('\n')): + if line == '': + break + output.append( + '' + % ( + { + 'cnt': cnt, + 'cnt_lbl': cnt, + } + ) + ) + + cnt += 1 + if not line: + output.append(line) + continue + if line.startswith('')[1] + diff = blame.for_line(idx + 1) + output.append( + '' % author_to_user( + diff.orig_committer, with_name=False) + ) + output.append( + '' % ( + flask.url_for('view_commit', + repo=repo.name, + username=username, + namespace=repo.namespace, + commitid=diff.final_commit_id + ), + shorted_commit(diff.final_commit_id) + ) + ) + output.append('' % line) + output.append('') + + output.append('
' + '%s%s
%s
') + + return '\n'.join(output) + + @APP.template_filter('wraps') def text_wraps(text, size=10): """ Template filter to wrap text at a specified size @@ -331,7 +389,7 @@ def patch_to_diff(patch): @APP.template_filter('author2user') -def author_to_user(author, size=16, cssclass=None): +def author_to_user(author, size=16, cssclass=None, with_name=True): """ Template filter transforming a pygit2 Author object into a text either with just the username or linking to the user in pagure. """ @@ -340,11 +398,20 @@ def author_to_user(author, size=16, cssclass=None): return output user = pagure.lib.search_user(SESSION, email=author.email) if user: - output = "%s %s" % ( - avatar(user.default_email, size), - flask.url_for('view_user', username=user.username), - ('class="%s"' % cssclass) if cssclass else '', - author.name, + output = "%(avatar)s %(username)s" + if not with_name: + output = "%(avatar)s" + + output = output % ( + { + 'avatar': avatar(user.default_email, size), + 'url': flask.url_for('view_user', username=user.username), + 'cssclass': ('class="%s"' % cssclass) if cssclass else '', + 'username': user.username, + 'name': author.name, + } ) return output @@ -379,6 +446,7 @@ def author_to_user_commits(author, link, size=16, cssclass=None): return output + @APP.template_filter('InsertDiv') def insert_div(content): """ Template filter inserting an opening
and closing
diff --git a/pagure/ui/repo.py b/pagure/ui/repo.py index fe4592c..b2bbe8a 100644 --- a/pagure/ui/repo.py +++ b/pagure/ui/repo.py @@ -679,6 +679,52 @@ def view_raw_file( return (data, 200, headers) +@APP.route('//blame/') +@APP.route('///blame/') +@APP.route( + '/fork///blame/') +@APP.route( + '/fork////blame/') +def view_blame_file(repo, filename, username=None, namespace=None): + """ Displays the blame of a file or a tree for the specified repo. + """ + repo = flask.g.repo + reponame = flask.g.reponame + repo_obj = flask.g.repo_obj + + branchname = flask.request.args.get('identifier', 'master') + + if repo_obj.is_empty: + flask.abort(404, 'Empty repo cannot have a file') + + commit = repo_obj[repo_obj.head.target] + content = __get_file_in_tree( + repo_obj, commit.tree, filename.split('/'), bail_on_tree=True) + if not content: + flask.abort(404, 'File not found') + + if not isinstance(content, pygit2.Blob): + flask.abort(404, 'File not found') + if is_binary_string(content.data): + flask.abort(400, 'Binary files cannot be blamed') + + content = ktc.to_bytes(content.data) + blame = repo_obj.blame(filename) + + return flask.render_template( + 'blame.html', + select='tree', + repo=repo, + origin='view_file', + username=username, + filename=filename, + branchname=branchname, + content=content, + output_type='blame', + blame=blame, + ) + + @APP.route('//c//') @APP.route('//c/') @APP.route('///c//')