Various additions to the Gitar app
Firstly, the new language files for Flemish and Belgian French have been added.
Secondly, a lot of additions regarding commit viewing have also been added, a grand new feature of Gitar. The code has been fixed in places and made more robust against future changes.
- Author
- Maarten Vangeneugden
- Date
- March 31, 2022, 4:55 p.m.
- Hash
- 80a82aca6095f7ab4934365b8cb7edebe6480bab
- Parent
- 5c86ea235ea472b411e8e45835e088e8f3fe30f5
- Modified files
- GitActions/CommitInfo.py
- locale/fr-be/LC_MESSAGES/django.po
- locale/nl-be/LC_MESSAGES/django.po
- syntax.py
- templates/gitar/commit.djhtml
- templates/gitar/directory.djhtml
- templates/gitar/file.djhtml
- templates/gitar/locale/fr-be/LC_MESSAGES/django.po
- templates/gitar/locale/nl-be/LC_MESSAGES/django.po
- views.py
GitActions/CommitInfo.py ¶
110 additions and 7 deletions.
View changes Hide changes
1 |
1 |
# formatted file in a commit. |
2 |
2 |
|
3 |
3 |
# The function returns a list |
4 |
4 |
|
5 |
5 |
# If one line is removed, and one line is added, and both have the same line |
6 |
6 |
# number, then that should be regarded as a changed line, not a removed+added line. |
7 |
7 |
# This holds for each non-broken list of removed+added lines. |
8 |
8 |
|
9 |
9 |
def process_file(source_before, source_after, deletions, additions): |
10 |
- | rows = [] |
+ |
10 |
from ..syntax import * |
+ |
11 |
|
+ |
12 |
""" It's a lot of annoying glue code to transform a modified file object into |
+ |
13 |
what we actually want: The data that needs to be passed to the Django template. |
+ |
14 |
This convenience function accepts the modified file object as given by |
+ |
15 |
Pydriller's Commit class, and returns a list of dicts, each dict representing |
+ |
16 |
the information needed to properly present a file diff in the template. |
+ |
17 |
""" |
+ |
18 |
def prepare_and_process(modified_file): |
+ |
19 |
#print(modified_file.source_code) |
+ |
20 |
result = dict() |
+ |
21 |
result["object"] = modified_file # Could come in handy |
+ |
22 |
result["change_type"] = modified_file.change_type |
+ |
23 |
result["html_code_after"] = code_to_HTML( |
+ |
24 |
modified_file.source_code, |
+ |
25 |
modified_file.filename) |
+ |
26 |
result["html_code_before"] = code_to_HTML( |
+ |
27 |
modified_file.source_code_before, |
+ |
28 |
modified_file.filename) |
+ |
29 |
|
+ |
30 |
if modified_file.change_type == ModificationType.ADD: |
+ |
31 |
result["path"] = modified_file.new_path |
+ |
32 |
elif modified_file.change_type == ModificationType.DELETE: |
+ |
33 |
result["path"] = modified_file.old_path |
+ |
34 |
elif modified_file.change_type == ModificationType.RENAME: |
+ |
35 |
result["path"] = modified_file.old_path +" → "+ modified_file.new_path |
+ |
36 |
elif modified_file.change_type == ModificationType.MODIFY: |
+ |
37 |
result["path"] = modified_file.old_path |
+ |
38 |
|
+ |
39 |
added = dict() |
+ |
40 |
deleted = dict() |
+ |
41 |
for number, line in modified_file.diff_parsed["added"]: |
+ |
42 |
added[number] = line |
+ |
43 |
for number, line in modified_file.diff_parsed["deleted"]: |
+ |
44 |
if result["path"] == "Challenge 4/Controller.java": |
+ |
45 |
print("PING") |
+ |
46 |
deleted[number] = line |
+ |
47 |
if result["path"] == "Challenge 4/Controller.java": |
+ |
48 |
print("NU CONTROLLER.jAVA") |
+ |
49 |
#print(modified_file.diff_parsed) |
+ |
50 |
print(deleted) |
+ |
51 |
result["rows"] = process_file( |
+ |
52 |
result["html_code_before"], |
+ |
53 |
result["html_code_after"], |
+ |
54 |
added, |
+ |
55 |
deleted) |
+ |
56 |
|
+ |
57 |
# These three lists are going to be empty if the methods couldn't be detected |
+ |
58 |
deleted, transferred, new = process_methods(modified_file) |
+ |
59 |
if len(deleted) == len(transferred) == len(new) == 0: |
+ |
60 |
result["methods_available"] = False |
+ |
61 |
else: |
+ |
62 |
result["methods_available"] = True |
+ |
63 |
result["deleted_methods"] = deleted |
+ |
64 |
result["transferred_methods"] = transferred |
+ |
65 |
result["new_methods"] = new |
+ |
66 |
return result |
+ |
67 |
|
+ |
68 |
""" Sometimes, the methods in a file can be detected, which adds some nice |
+ |
69 |
information """ |
+ |
70 |
def process_methods(modified_file): |
+ |
71 |
before_methods = modified_file.methods_before |
+ |
72 |
after_methods = modified_file.methods |
+ |
73 |
deleted_methods = [] |
+ |
74 |
transferred_methods = [] |
+ |
75 |
new_methods = [] |
+ |
76 |
|
+ |
77 |
# Finding all transferred and deleted methods |
+ |
78 |
for before_method in before_methods: |
+ |
79 |
transferred = False |
+ |
80 |
for after_method in after_methods: |
+ |
81 |
if before_method.name == after_method.name: # Match found |
+ |
82 |
transferred_methods.append((before_method, after_method)) |
+ |
83 |
transferred = True |
+ |
84 |
break |
+ |
85 |
if not transferred: |
+ |
86 |
deleted_methods.append(before_method) |
+ |
87 |
# Finding all new methods |
+ |
88 |
for after_method in after_methods: |
+ |
89 |
transferred = False |
+ |
90 |
for _, am in transferred_methods: |
+ |
91 |
if after_method == am: |
+ |
92 |
transferred = True |
+ |
93 |
break |
+ |
94 |
if not transferred: |
+ |
95 |
new_methods.append(after_method) |
+ |
96 |
|
+ |
97 |
return deleted_methods, transferred_methods, new_methods |
+ |
98 |
|
+ |
99 |
|
+ |
100 |
def process_file(source_before, source_after, additions, deletions): |
+ |
101 |
rows = [] |
11 |
102 |
before_count = 0 |
12 |
- | after_count = 0 |
13 |
- | while True: |
+ |
103 |
# NOTE: Deletions komen wel degelijk goed binnen |
+ |
104 |
print("deletions:") |
+ |
105 |
print(deletions) |
+ |
106 |
before_count = 1 |
+ |
107 |
after_count = 1 |
+ |
108 |
while True: |
14 |
109 |
row = dict() |
15 |
110 |
# We check the deletion lines first; if a line was "changed", the |
+ |
111 |
#print(deletions) |
+ |
112 |
#print(additions) |
+ |
113 |
# We check the deletion lines first; if a line was "changed", the |
16 |
114 |
# 'deleted' line needs to be listed first, and after that, the 'added' |
17 |
115 |
# line |
18 |
116 |
if before_count in deletions: |
+ |
117 |
if before_count in deletions: |
19 |
118 |
row["type"] = "deletion" |
20 |
119 |
row["old_num"] = before_count |
21 |
120 |
row["line"] = deletions[before_count] |
22 |
- | before_count += 1 |
+ |
121 |
# Because source_before[] has been processed by Pygments, and thus |
+ |
122 |
# has the syntax highlighting in it. deletions[] is just plain text. |
+ |
123 |
row["line"] = source_before[before_count] |
+ |
124 |
before_count += 1 |
23 |
125 |
elif after_count in additions: |
24 |
126 |
row["type"] = "addition" |
25 |
127 |
row["new_num"] = after_count |
26 |
128 |
row["line"] = additions[after_count] |
27 |
- | after_count += 1 |
+ |
129 |
row["line"] = source_after[after_count] |
+ |
130 |
after_count += 1 |
28 |
131 |
else: # No change to this particular line |
29 |
132 |
row["old_num"] = before_count |
30 |
133 |
row["new_num"] = after_count |
31 |
134 |
if len(source_before) < before_count: |
32 |
- | row["line"] = source_before[before_count] |
+ |
135 |
row["line"] = source_before[before_count] |
33 |
136 |
elif len(source_after) < after_count: |
34 |
- | row["line"] = source_after[after_count] |
+ |
137 |
row["line"] = source_after[after_count] |
35 |
138 |
else: # No lines left |
36 |
139 |
break |
37 |
140 |
# If not end of both files, increment both counters anyway |
38 |
141 |
before_count += 1 |
39 |
142 |
after_count += 1 |
40 |
143 |
# Adding the new row to the list of rows |
41 |
144 |
rows.append(row) |
42 |
145 |
return rows |
43 |
146 |
|
44 |
147 |
|
45 |
148 |
|
46 |
149 |
|
47 |
150 |
|
48 |
151 |
|
49 |
152 |
|
50 |
153 |
|
51 |
154 |
locale/fr-be/LC_MESSAGES/django.po ¶
32 additions and 0 deletions.
View changes Hide changes
+ |
1 |
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER |
+ |
2 |
# This file is distributed under the same license as the PACKAGE package. |
+ |
3 |
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. |
+ |
4 |
# |
+ |
5 |
#, fuzzy |
+ |
6 |
msgid "" |
+ |
7 |
msgstr "" |
+ |
8 |
"Project-Id-Version: PACKAGE VERSION\n" |
+ |
9 |
"Report-Msgid-Bugs-To: \n" |
+ |
10 |
"POT-Creation-Date: 2021-11-24 14:00+0100\n" |
+ |
11 |
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
+ |
12 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
+ |
13 |
"Language-Team: LANGUAGE <LL@li.org>\n" |
+ |
14 |
"Language: \n" |
+ |
15 |
"MIME-Version: 1.0\n" |
+ |
16 |
"Content-Type: text/plain; charset=UTF-8\n" |
+ |
17 |
"Content-Transfer-Encoding: 8bit\n" |
+ |
18 |
|
+ |
19 |
#: gitar/views.py:35 |
+ |
20 |
msgid "" |
+ |
21 |
"Gitar is a simple web app that allows its users to easily share Git repos in " |
+ |
22 |
"combination with the Django framework." |
+ |
23 |
msgstr "" |
+ |
24 |
|
+ |
25 |
#: gitar/views.py:40 |
+ |
26 |
msgid "Home page" |
+ |
27 |
msgstr "" |
+ |
28 |
|
+ |
29 |
#: gitar/views.py:41 |
+ |
30 |
msgid "Source code" |
+ |
31 |
msgstr "" |
+ |
32 |
locale/nl-be/LC_MESSAGES/django.po ¶
32 additions and 0 deletions.
View changes Hide changes
+ |
1 |
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER |
+ |
2 |
# This file is distributed under the same license as the PACKAGE package. |
+ |
3 |
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. |
+ |
4 |
# |
+ |
5 |
#, fuzzy |
+ |
6 |
msgid "" |
+ |
7 |
msgstr "" |
+ |
8 |
"Project-Id-Version: PACKAGE VERSION\n" |
+ |
9 |
"Report-Msgid-Bugs-To: \n" |
+ |
10 |
"POT-Creation-Date: 2021-11-24 14:00+0100\n" |
+ |
11 |
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
+ |
12 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
+ |
13 |
"Language-Team: LANGUAGE <LL@li.org>\n" |
+ |
14 |
"Language: \n" |
+ |
15 |
"MIME-Version: 1.0\n" |
+ |
16 |
"Content-Type: text/plain; charset=UTF-8\n" |
+ |
17 |
"Content-Transfer-Encoding: 8bit\n" |
+ |
18 |
|
+ |
19 |
#: gitar/views.py:35 |
+ |
20 |
msgid "" |
+ |
21 |
"Gitar is a simple web app that allows its users to easily share Git repos in " |
+ |
22 |
"combination with the Django framework." |
+ |
23 |
msgstr "" |
+ |
24 |
|
+ |
25 |
#: gitar/views.py:40 |
+ |
26 |
msgid "Home page" |
+ |
27 |
msgstr "" |
+ |
28 |
|
+ |
29 |
#: gitar/views.py:41 |
+ |
30 |
msgid "Source code" |
+ |
31 |
msgstr "" |
+ |
32 |
syntax.py ¶
6 additions and 1 deletion.
View changes Hide changes
1 |
1 |
|
2 |
2 |
In essence, this means that in this module, a file's contents are being parsed |
3 |
3 |
to Pygments, which will then return the appropriate HTML output, which can then |
4 |
4 |
be directly parsed in a Django template. |
5 |
5 |
""" |
6 |
6 |
|
7 |
7 |
from pygments import highlight |
8 |
8 |
from pygments.lexers import get_lexer_by_name |
9 |
9 |
from pygments.formatters import HtmlFormatter |
10 |
10 |
from pygments.lexers import guess_lexer_for_filename |
11 |
11 |
|
12 |
12 |
def code_to_HTML(code, file_name): |
13 |
13 |
""" Turns the given list of code strings in HTML, ready for output. |
14 |
14 |
|
15 |
15 |
Please note that the lexer that will be used for the syntax coloring, is |
16 |
16 |
determined by the given file name, so assert that the given code comes from |
17 |
17 |
the given file. |
18 |
18 |
Keyword arguments: |
19 |
19 |
code -- A non-empty list of code strings. |
20 |
20 |
file_name -- The name of the file from where the given code originates. |
21 |
21 |
""" |
22 |
22 |
# stripall removes whitespace in front, but that also removes file |
23 |
23 |
# indentation. |
24 |
24 |
try: |
+ |
25 |
return [] |
+ |
26 |
try: |
25 |
27 |
lexer = guess_lexer_for_filename(file_name, code, stripall=False) |
26 |
28 |
# linenos (line-n°'s) adds line numbering to the front of the output. I'm |
27 |
29 |
# doing that myself, so False. |
28 |
30 |
# cssclass sets the enclosing <div>'s class to the given CSS class name. |
29 |
31 |
formatter = HtmlFormatter(linenos=False, cssclass="source") |
30 |
32 |
#result = highlight(code, lexer, formatter) |
31 |
33 |
unicode_data = decode_to_unicode(highlight(code, lexer, formatter)) |
32 |
34 |
return unicode_data |
33 |
35 |
#except pygments.ClassNotFound as exception: |
34 |
36 |
except: |
35 |
37 |
# This happens with files lacking a file extension, and MarkDown files. |
36 |
38 |
# In that case, no lexer should be used, but Simple parse the same code. |
37 |
39 |
return no_syntax(code) |
38 |
40 |
|
39 |
41 |
def no_syntax(data): |
40 |
42 |
""" Decodes a given bytearray to a list of unicode strings.""" |
41 |
43 |
decoded_data = data.decode("utf-8") |
42 |
- | formatted_data = [] |
+ |
44 |
decoded_data = data |
+ |
45 |
else: |
+ |
46 |
decoded_data = data.decode("utf-8") |
+ |
47 |
formatted_data = [] |
43 |
48 |
line = "" |
44 |
49 |
for character in decoded_data: |
45 |
50 |
if character != "\n": |
46 |
51 |
line = line + character |
47 |
52 |
else: |
48 |
53 |
formatted_data.append(line) |
49 |
54 |
line = "" |
50 |
55 |
return formatted_data |
51 |
56 |
|
52 |
57 |
|
53 |
58 |
def decode_to_unicode(data): |
54 |
59 |
""" Decodes a given bytearray to a list of unicode strings.""" |
55 |
60 |
#decoded_data = data.decode("utf-8") |
56 |
61 |
decoded_data = data |
57 |
62 |
formatted_data = [] |
58 |
63 |
line = "" |
59 |
64 |
for character in decoded_data: |
60 |
65 |
if character != "\n": |
61 |
66 |
line = line + character |
62 |
67 |
else: |
63 |
68 |
formatted_data.append(line) |
64 |
69 |
line = "" |
65 |
70 |
return formatted_data |
66 |
71 |
templates/gitar/commit.djhtml ¶
137 additions and 60 deletions.
View changes Hide changes
1 |
1 |
{% load i18n %} |
2 |
2 |
{% load humanize %} |
3 |
3 |
{% load static %} |
4 |
4 |
|
5 |
5 |
{% block title %}{{ repository_name }} | Gitar{% endblock title %} |
6 |
6 |
|
7 |
7 |
{% block stylesheets %} |
8 |
8 |
{{ block.super }} |
9 |
9 |
<link href="{% static "website/gitar.css" %}" rel="stylesheet" media="screen, projection" /> |
10 |
- | {% endblock stylesheets %} |
+ |
10 |
<link rel="stylesheet" type="text/css" href="/static/gitar/css/file.css" /> |
+ |
11 |
<style> |
+ |
12 |
table.highlight { |
+ |
13 |
border-collapse: collapse; |
+ |
14 |
} |
+ |
15 |
table.highlight tr { |
+ |
16 |
line-height: 1em; |
+ |
17 |
} |
+ |
18 |
|
+ |
19 |
tr.deletion { |
+ |
20 |
background-color: rgba(255, 0, 0, .1); |
+ |
21 |
} |
+ |
22 |
tr.addition { |
+ |
23 |
background-color: rgba(0, 255, 0, .1); |
+ |
24 |
} |
+ |
25 |
|
+ |
26 |
section { |
+ |
27 |
overflow-x: scroll; |
+ |
28 |
} |
+ |
29 |
|
+ |
30 |
.symbol { |
+ |
31 |
text-align: right; |
+ |
32 |
/*font-weight: bolder;*/ |
+ |
33 |
/* These vendor prefixes are still necessary, yeah...*/ |
+ |
34 |
} |
+ |
35 |
.line-number { |
+ |
36 |
text-align: right; |
+ |
37 |
width: 3em; |
+ |
38 |
-moz-user-select: none; |
+ |
39 |
-webkit-user-select: none; |
+ |
40 |
-ms-user-select: none; |
+ |
41 |
user-select: none; |
+ |
42 |
} |
+ |
43 |
.line-number a { |
+ |
44 |
color: grey; |
+ |
45 |
} |
+ |
46 |
|
+ |
47 |
details span.opened { |
+ |
48 |
display: none; |
+ |
49 |
} |
+ |
50 |
details[open] span.opened { |
+ |
51 |
display: inline; |
+ |
52 |
} |
+ |
53 |
details[open] span.closed { |
+ |
54 |
display: none; |
+ |
55 |
} |
+ |
56 |
</style> |
+ |
57 |
{% endblock stylesheets %} |
11 |
58 |
|
12 |
59 |
{% block description %} |
13 |
60 |
{{ repository_name }} {{ commit.hash|truncatechars:10 }}: {{ commit.msg }} |
14 |
61 |
{% endblock description %} |
15 |
62 |
|
16 |
63 |
{% block header %} |
17 |
64 |
<header> |
18 |
65 |
<h1>{{ repository_name }}</h1> |
19 |
66 |
<label for="nav-drawer-toggle">≡</label> |
20 |
67 |
</header> |
21 |
68 |
{% endblock header %} |
22 |
69 |
|
23 |
70 |
{% block main %} |
24 |
71 |
<aside> |
25 |
- | <dl> |
26 |
- | <dt>{% translate "Author" %}:</dt> |
27 |
- | <dd>{{ commit.author }}</dd> |
28 |
- | </dl> |
29 |
- | </aside> |
30 |
- | |
31 |
72 |
<section class="emphasis"> |
32 |
73 |
<h4>{{ commit.msg }}</h4> |
33 |
- | <p>{% translate "Commited by" %} {{ commit.author }} </p> |
34 |
- | </section> |
+ |
74 |
<p>{{ other_lines|safe }}</p> |
+ |
75 |
<dl> |
+ |
76 |
<dt>{% translate "Author" %}</dt> |
+ |
77 |
<dd>{{ commit.author.name }}</dd> |
+ |
78 |
|
+ |
79 |
<dt>{% translate "Date" %}</dt> |
+ |
80 |
<dd>{{ commit.committer_date|date:"DATETIME_FORMAT" }}</dd> |
+ |
81 |
|
+ |
82 |
<dt>{% translate "Hash" %}</dt> |
+ |
83 |
<dd><samp>{{ commit.hash }}</samp></dd> |
+ |
84 |
|
+ |
85 |
{% if commit.parents|length_is:"1" %} |
+ |
86 |
<dt>{% translate "Parent" %}</dt> |
+ |
87 |
{% else %} |
+ |
88 |
<dt>{% translate "Parents" %}</dt> |
+ |
89 |
{% endif %} |
+ |
90 |
{% for parent in commit.parents %} |
+ |
91 |
<dd><samp><a href="{% url "gitar-commit" repository_name parent %}"> |
+ |
92 |
{{ parent }} |
+ |
93 |
</a></samp</dd> |
+ |
94 |
{% endfor %} |
+ |
95 |
|
+ |
96 |
{% if modified_files|length_is:"1" %} |
+ |
97 |
<dt>{% translate "Modified file" %}</dt> |
+ |
98 |
{% else %} |
+ |
99 |
<dt>{% translate "Modified files" %}</dt> |
+ |
100 |
{% endif %} |
+ |
101 |
{% for mod_file in modified_files %} |
+ |
102 |
<dd><a href="#{{ mod_file.path }}">{{ mod_file.path }}</a></dd> |
+ |
103 |
{% endfor %} |
+ |
104 |
{#<dt>{% translate "Child" %}</dt>#} |
+ |
105 |
{# <dd>{{ commit.author_date|date:"DATETIME_FORMAT" }}</dd>#} |
+ |
106 |
{# Gonna leave the email out for now, don't like email scrapers #} |
+ |
107 |
{# <dt>{% translate "E-mail" %}</dt> #} |
+ |
108 |
{# <dl>{{ commit.author.email }}</dl> #} |
+ |
109 |
</dl> |
+ |
110 |
</section> |
35 |
111 |
|
36 |
112 |
{% for mod_file in commit.modified_files %} |
37 |
- | <section style="font-family: 'Fira Code', monospace;"> |
38 |
- | <h3>{{ mod_file.filename }}</h3> |
39 |
- | <table class="highlight"> |
40 |
- | {% for line in file_html_code. %} |
41 |
- | <tr> |
42 |
- | <td id="{{ forloop.counter }}" class="line-number" style=":hover { background-color: #00E676;}"> |
43 |
- | <a style=":hover { background-color: #00e676;}" href="#{{ forloop.counter }}"> |
44 |
- | <pre>{{ forloop.counter }}</pre></a></td> |
45 |
- | <td><pre>{{ line|safe }}</pre></td> |
46 |
- | </tr> |
47 |
- | {% endfor %} |
48 |
- | </table> |
+ |
113 |
<section> |
+ |
114 |
<h3 id="{{ mod_file.path }}">{{ mod_file.path }} <a href="#{{ mod_file.path }}">¶</a></h3> |
+ |
115 |
<p> |
+ |
116 |
{{ mod_file.object.added_lines }} |
+ |
117 |
{% blocktranslate count counter=mod_file.object.added_lines %} |
+ |
118 |
addition |
+ |
119 |
{% plural %} |
+ |
120 |
additions |
+ |
121 |
{% endblocktranslate %} |
+ |
122 |
{% translate "and" %} |
+ |
123 |
{{ mod_file.object.deleted_lines }} |
+ |
124 |
{% blocktranslate count counter=mod_file.object.deleted_lines %} |
+ |
125 |
deletion. |
+ |
126 |
{% plural %} |
+ |
127 |
deletions. |
+ |
128 |
{% endblocktranslate %} |
+ |
129 |
</p> |
+ |
130 |
{% if mod_file.methods_available %} |
+ |
131 |
|
+ |
132 |
{% endif %} |
+ |
133 |
<details> |
+ |
134 |
<summary><span class="closed">{% translate "View changes" %}</span><span class="opened">{% translate "Hide changes" %}</span> |
+ |
135 |
</summary> |
+ |
136 |
|
+ |
137 |
<table class="highlight" style="font-family: 'Fira Code', monospace;"> |
+ |
138 |
{% for row in mod_file.rows %} |
+ |
139 |
{% if row.type == "deletion" %} |
+ |
140 |
<tr class="deletion"> |
+ |
141 |
<td id="{{ mod_file.path }}-A{{ row.old_num }}" class="line-number"> |
+ |
142 |
<a href="#{{ mod_file.path }}-A{{ row.old_num }}" class="accent-on-hover-only"><pre>{{ row.old_num }}</pre> |
+ |
143 |
</a></td> |
+ |
144 |
<td class="line-number symbol">-</td> |
+ |
145 |
{% elif row.type == "addition" %} |
+ |
146 |
<tr class="addition"> |
+ |
147 |
<td class="line-number symbol">+</td> |
+ |
148 |
<td id="{{ mod_file.path }}-B{{ row.new_num }}" class="line-number"> |
+ |
149 |
<a href="#{{ mod_file.path }}-B{{ row.new_num }}" class="accent-on-hover-only"><pre>{{ row.new_num }}</pre> |
+ |
150 |
</a></td> |
+ |
151 |
{% else %} |
+ |
152 |
<tr class="nochange"> |
+ |
153 |
<td id="{{ mod_file.path }}-A{{ row.old_num }}" class="line-number"> |
+ |
154 |
<a href="#{{ mod_file.path }}-A{{ row.old_num }}" class="accent-on-hover-only"><pre>{{ row.old_num }}</pre> |
+ |
155 |
<td id="{{ mod_file.path }}-B{{ row.new_num }}" class="line-number"> |
+ |
156 |
<a href="#{{ mod_file.path }}-B{{ row.new_num }}" class="accent-on-hover-only"><pre>{{ row.new_num }}</pre> |
+ |
157 |
</a></td> |
+ |
158 |
{% endif %} |
+ |
159 |
<td style="padding-left: 1em;"><pre>{{ row.line|safe }}</pre></td> |
+ |
160 |
</tr> |
+ |
161 |
{% endfor %} |
+ |
162 |
</table> |
49 |
163 |
</section> |
50 |
- | <section> |
51 |
- | {# Determining whether we're dealing with an addition, modification or deletion #} |
52 |
- | {# {% if mod_file.change_type == "Added" %} #} |
53 |
- | |
54 |
- | <p> |
55 |
- | Voor {{ mod_file.filename }} |
56 |
- | {{ mod_file.source_code_before }} |
57 |
- | Na: |
58 |
- | {{ mod_file.source_code }} |
59 |
- | </p> |
60 |
- | |
61 |
- | <table> |
62 |
- | {% for row in rows %} |
63 |
- | {% if row.type == "deletion" %} |
64 |
- | <tr class="deletion"> |
65 |
- | <td id="A{{ row.old_num }}"> |
66 |
- | <a href="A{{ row.old_num }}">{{ row.old_num }} |
67 |
- | </a></td> |
68 |
- | <td></td> |
69 |
- | {% elif row.type == "addition" %} |
70 |
- | <tr class="addition"> |
71 |
- | <td></td> |
72 |
- | <td id="B{{ row.new_num }}"> |
73 |
- | <a href="B{{ row.new_num }}">{{ row.new_num }} |
74 |
- | </a></td> |
75 |
- | {% else %} |
76 |
- | <tr class="nochange"> |
77 |
- | <td id="A{{ row.old_num }}"> |
78 |
- | <a href="A{{ row.old_num }}">{{ row.old_num }} |
79 |
- | <td id="B{{ row.new_num }}"> |
80 |
- | <a href="B{{ row.new_num }}">{{ row.new_num }} |
81 |
- | </a></td> |
82 |
- | {% endif %} |
83 |
- | <td><pre>{{ row.line|safe }}</pre></td> |
84 |
- | </tr> |
85 |
- | {% endfor %} |
86 |
- | </table> |
87 |
- | </section> |
88 |
- | {% endfor %} |
+ |
164 |
</section> |
+ |
165 |
{% endfor %} |
89 |
166 |
|
90 |
167 |
{% endblock main %} |
91 |
168 |
templates/gitar/directory.djhtml ¶
0 additions and 1 deletion.
View changes Hide changes
1 |
1 |
{% load i18n %} |
2 |
2 |
{% load humanize %} |
3 |
3 |
{% load static %} |
4 |
4 |
|
5 |
5 |
{% block title %}{{ repository_name }} | Gitar{% endblock title %} |
6 |
6 |
|
7 |
7 |
{% block stylesheets %} |
8 |
8 |
{{ block.super }} |
9 |
9 |
<link href="{% static "website/gitar.css" %}" rel="stylesheet" media="screen, projection" /> |
10 |
- | {% endblock stylesheets %} |
11 |
10 |
|
12 |
11 |
{% block description %} |
13 |
12 |
{{repository_description}} |
14 |
13 |
{% endblock description %} |
15 |
14 |
|
16 |
15 |
{% block header %} |
17 |
16 |
<header> |
18 |
17 |
<h1>{{ repository_name }}</h1> |
19 |
18 |
<label for="nav-drawer-toggle">≡</label> |
20 |
19 |
</header> |
21 |
20 |
{% endblock header %} |
22 |
21 |
|
23 |
22 |
{% block main %} |
24 |
23 |
<section> |
25 |
24 |
|
26 |
25 |
<!--Tree contents--> |
27 |
26 |
<table> |
28 |
27 |
<thead> |
29 |
28 |
<tr> |
30 |
29 |
<th scope="col">{% translate "Name" %}</th> |
31 |
30 |
<th scope="col">{% translate "Latest commit" %}</th> |
32 |
31 |
<th scope="col">{% translate "Latest update" %}</th> |
33 |
32 |
</tr> |
34 |
33 |
</thead> |
35 |
34 |
<tbody> |
36 |
35 |
{% if subdirectories %} |
37 |
36 |
{% for subdirectory in subdirectories %} |
38 |
37 |
{% endfor %} |
39 |
38 |
{% endif %} |
40 |
39 |
|
41 |
40 |
{% for file in files %} |
42 |
41 |
<tr> |
43 |
42 |
<td><a href="{% url 'gitar-path-explorer' repository_name branch file.path %}"> |
44 |
43 |
{{ file.name }}</a></td> |
45 |
44 |
<td><a href="{% url 'gitar-commit' repository_name file.commit.hash %}"> |
46 |
45 |
{{ file.commit.msg }}</a></td> |
47 |
46 |
<td>{% if file.older_than_one_month %} |
48 |
47 |
{{ file.commit.committer_date|date }} |
49 |
48 |
{% else %} |
50 |
49 |
{{ file.commit.committer_date|naturaltime }} |
51 |
50 |
{% endif %} |
52 |
51 |
</td> |
53 |
52 |
</tr> |
54 |
53 |
{% endfor %} |
55 |
54 |
</tbody> |
56 |
55 |
</table> |
57 |
56 |
|
58 |
57 |
<div class="row"> |
59 |
58 |
<div class="col hide-on-med-and-down l4"> |
60 |
59 |
<!-- Add tertiary information such as branches and licensing here. --> |
61 |
60 |
<h3 class="{{mdc}}-text">{{repository_name}}</h3> |
62 |
61 |
<h5 class="{{mdc}}-text">{% trans "Description" %}</h5> |
63 |
62 |
{{repository_description}} |
64 |
63 |
<h5 class="{{mdc}}-text">{% trans "Branches" %}</h5> |
65 |
64 |
{% for bbranch in branches %} |
66 |
65 |
<a class="{{mdac}}-text text-accent-3" href="{% url 'gitar-repository' repository_name bbranch %}"> |
67 |
66 |
{{bbranch}} |
68 |
67 |
</a><br /> |
69 |
68 |
{% endfor %} |
70 |
69 |
<h5 class="{{mdc}}-text">{% trans "Extra information" %}</h5> |
71 |
70 |
<div class="chip"> |
72 |
71 |
{{repository_language}} |
73 |
72 |
<i class="material-icons {{mdac}}-text text-accent-3">code</i> |
74 |
73 |
</div><br /> |
75 |
74 |
<div class="chip"> |
76 |
75 |
{{repository_license}} |
77 |
76 |
<i class="material-icons {{mdac}}-text text-accent-3">copyright</i> |
78 |
77 |
</div><br /> |
79 |
78 |
</div> |
80 |
79 |
<div class="col s12 m8 l5"> |
81 |
80 |
<!-- Main area with links to files and subdirectories --> |
82 |
81 |
<h3 class="{{mdc}}-text">{% trans "Files" %}</h3> |
83 |
82 |
<table class="highlight"> |
84 |
83 |
{% if subdirectories %} |
85 |
84 |
<thead> |
86 |
85 |
{% for subdirectory in subdirectories %} |
87 |
86 |
<tr> |
88 |
87 |
<th> |
89 |
88 |
<a class="{{mdac}}-text text-accent-4" href="{% url 'gitar-path-explorer' repository_name branch subdirectory.path %}"> |
90 |
89 |
{{subdirectory.name}} |
91 |
90 |
</a> |
92 |
91 |
</th> |
93 |
92 |
</tr> |
94 |
93 |
{% endfor %} |
95 |
94 |
</thead> |
96 |
95 |
{% endif %} |
97 |
96 |
<tbody> |
98 |
97 |
{% for file in files %} |
99 |
98 |
<tr> |
100 |
99 |
<td> |
101 |
100 |
<a class="{{mdac}}-text text-accent-3" href="{% url 'gitar-path-explorer' repository_name branch file.path %}"> |
102 |
101 |
{{file.name}} |
103 |
102 |
</a> |
104 |
103 |
</td> |
105 |
104 |
<td>{{file.commit|truncatechars:6}}</td> |
106 |
105 |
</tr> |
107 |
106 |
{% endfor %} |
108 |
107 |
</tbody> |
109 |
108 |
</table> |
110 |
109 |
</div> |
111 |
110 |
<div class="col hide-on-small-only m4 l3"> |
112 |
111 |
<!-- List of commits on the current branch, chronologically. --> |
113 |
112 |
<h3 class="{{mdc}}-text">{% trans "Commits" %}</h3> |
114 |
113 |
{% for commit in commits %} |
115 |
114 |
<hr /> |
116 |
115 |
<a |
117 |
116 |
class="{{mdac}}-text text-accent-3 tooltipped" |
118 |
117 |
{# href="{% url 'gitar-commit' repository_name commit.hash %}" #} |
119 |
118 |
data-position="left" |
120 |
119 |
data-delay="50" |
121 |
120 |
data-tooltip="Viewing commits is not implemented yet!"> |
122 |
121 |
{{commit.hash|truncatechars:15}} |
123 |
122 |
</a> |
124 |
123 |
<span class="{{mdc}}-text text-lighten-2"> |
125 |
124 |
{% trans "by" %} {{commit.author}} |
126 |
125 |
</span><br /> |
127 |
126 |
{{commit.description|lower|capfirst}}{% if commit.description|last != "." %}.{% endif %} |
128 |
127 |
{% endfor %} |
129 |
128 |
</div> |
130 |
129 |
</div> |
131 |
130 |
</section> |
132 |
131 |
{% endblock main %} |
133 |
132 |
templates/gitar/file.djhtml ¶
0 additions and 1 deletion.
View changes Hide changes
1 |
1 |
{% load i18n %} |
2 |
2 |
{% load static %} |
3 |
3 |
|
4 |
4 |
{% block title %}{{repository_name}}/{{file_name}} | Gitar{% endblock title %} |
5 |
5 |
|
6 |
6 |
{% block stylesheets %} |
7 |
7 |
{{ block.super }} |
8 |
8 |
<link href="{% static "website/gitar.css" %}" rel="stylesheet" media="screen, projection" /> |
9 |
- | <link rel="stylesheet" type="text/css" href="/static/website/syntax.css" /> |
10 |
9 |
<link rel="stylesheet" type="text/css" href="/static/gitar/css/file.css" /> |
11 |
10 |
<style> |
12 |
11 |
table.highlight tr { |
13 |
12 |
line-height: 15px; |
14 |
13 |
} |
15 |
14 |
|
16 |
15 |
/* These vendor prefixes are still necessary, yeah...*/ |
17 |
16 |
.line-number { |
18 |
17 |
-moz-user-select: none; |
19 |
18 |
-webkit-user-select: none; |
20 |
19 |
-ms-user-select: none; |
21 |
20 |
user-select: none; |
22 |
21 |
} |
23 |
22 |
</style> |
24 |
23 |
{% endblock stylesheets %} |
25 |
24 |
|
26 |
25 |
{% block description %} |
27 |
26 |
Content of {{file_name}} in {{repository_name}} |
28 |
27 |
{% endblock description %} |
29 |
28 |
|
30 |
29 |
{% block header %} |
31 |
30 |
<header> |
32 |
31 |
<h1>{{ repository_name }}</h1> |
33 |
32 |
<label for="nav-drawer-toggle">≡</label> |
34 |
33 |
</header> |
35 |
34 |
{% endblock header %} |
36 |
35 |
|
37 |
36 |
{% comment %} |
38 |
37 |
{% block stylesheets %} |
39 |
38 |
<link href="/static/website/materialize/css/google-icons.css" rel="stylesheet" /> |
40 |
39 |
<link href="/static/website/materialize/css/materialize.css" rel="stylesheet" media="screen, projection" /> |
41 |
40 |
<!--<link rel="stylesheet href="https://cdn.rawgit.com/tonsky/FiraCode/1.204/distr/fira_code.css">--> |
42 |
41 |
<!-- TODO: Download Fira Code stylesheet and serve via the static folder! |
43 |
42 |
This is necessary BEFORE activating the Fira Code ligature font, because |
44 |
43 |
using a CDN I do not controll brings in tonnes of JS code from Google, |
45 |
44 |
Amazon, and whatnot. --> |
46 |
45 |
<!--<style> |
47 |
46 |
td { |
48 |
47 |
padding: 0 0 0 0; |
49 |
48 |
} |
50 |
49 |
pre { |
51 |
50 |
margin-top: 10px; |
52 |
51 |
margin-bottom: 10px; |
53 |
52 |
} |
54 |
53 |
table { |
55 |
54 |
line-height: 0px; |
56 |
55 |
padding-top: 0px; |
57 |
56 |
padding-bottom: 0px; |
58 |
57 |
border-spacing: 0 0; |
59 |
58 |
margin-top: 0px; |
60 |
59 |
margin-bottom: 0px; |
61 |
60 |
} |
62 |
61 |
</style>--> |
63 |
62 |
{# For the syntax coloring of Gitar. TODO for later. #} |
64 |
63 |
<link rel="stylesheet" type="text/css" href="/static/website/syntax.css" /> |
65 |
64 |
<link rel="stylesheet" type="text/css" href="/static/gitar/css/file.css" /> |
66 |
65 |
{% endblock stylesheets %} |
67 |
66 |
{% endcomment %} |
68 |
67 |
|
69 |
68 |
<!-- NOTE: Currently I'm using an ordered list to get numbers, but now I can't |
70 |
69 |
link to the different lines. So until I figure out a way to make tables behave |
71 |
70 |
correctly, this ought to be the temporary solution--> |
72 |
71 |
{% block main %} |
73 |
72 |
<section style="font-family: 'Fira Code', monospace;"> |
74 |
73 |
<h3>{{file_name}}</h3> |
75 |
74 |
<table class="highlight"> |
76 |
75 |
{% for line in content %} |
77 |
76 |
<tr> |
78 |
77 |
<td id="{{ forloop.counter }}" class="line-number" style=":hover { background-color: #00E676;}"> |
79 |
78 |
<a style=":hover { background-color: #00e676;}" href="#{{ forloop.counter }}"> |
80 |
79 |
<pre>{{ forloop.counter }}</pre></a></td> |
81 |
80 |
<td><pre>{{ line|safe }}</pre></td> |
82 |
81 |
</tr> |
83 |
82 |
{% endfor %} |
84 |
83 |
</table> |
85 |
84 |
</section> |
86 |
85 |
{% endblock main %} |
87 |
86 |
templates/gitar/locale/fr-be/LC_MESSAGES/django.po ¶
97 additions and 0 deletions.
View changes Hide changes
+ |
1 |
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER |
+ |
2 |
# This file is distributed under the same license as the PACKAGE package. |
+ |
3 |
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. |
+ |
4 |
# |
+ |
5 |
#, fuzzy |
+ |
6 |
msgid "" |
+ |
7 |
msgstr "" |
+ |
8 |
"Project-Id-Version: PACKAGE VERSION\n" |
+ |
9 |
"Report-Msgid-Bugs-To: \n" |
+ |
10 |
"POT-Creation-Date: 2021-11-24 14:00+0100\n" |
+ |
11 |
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
+ |
12 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
+ |
13 |
"Language-Team: LANGUAGE <LL@li.org>\n" |
+ |
14 |
"Language: \n" |
+ |
15 |
"MIME-Version: 1.0\n" |
+ |
16 |
"Content-Type: text/plain; charset=UTF-8\n" |
+ |
17 |
"Content-Transfer-Encoding: 8bit\n" |
+ |
18 |
|
+ |
19 |
#: gitar/templates/gitar/directory.djhtml:29 |
+ |
20 |
msgid "Description" |
+ |
21 |
msgstr "" |
+ |
22 |
|
+ |
23 |
#: gitar/templates/gitar/directory.djhtml:31 |
+ |
24 |
msgid "Branches" |
+ |
25 |
msgstr "" |
+ |
26 |
|
+ |
27 |
#: gitar/templates/gitar/directory.djhtml:37 |
+ |
28 |
msgid "Extra information" |
+ |
29 |
msgstr "" |
+ |
30 |
|
+ |
31 |
#: gitar/templates/gitar/directory.djhtml:49 |
+ |
32 |
msgid "Files" |
+ |
33 |
msgstr "" |
+ |
34 |
|
+ |
35 |
#: gitar/templates/gitar/directory.djhtml:80 |
+ |
36 |
msgid "Commits" |
+ |
37 |
msgstr "" |
+ |
38 |
|
+ |
39 |
#: gitar/templates/gitar/directory.djhtml:92 |
+ |
40 |
msgid "by" |
+ |
41 |
msgstr "" |
+ |
42 |
|
+ |
43 |
#: gitar/templates/gitar/index.djhtml:5 |
+ |
44 |
msgid "Gitar | Index page" |
+ |
45 |
msgstr "" |
+ |
46 |
|
+ |
47 |
#: gitar/templates/gitar/index.djhtml:8 |
+ |
48 |
msgid "My personal answer to GitHub." |
+ |
49 |
msgstr "" |
+ |
50 |
|
+ |
51 |
#: gitar/templates/gitar/index.djhtml:29 |
+ |
52 |
msgid "Front page" |
+ |
53 |
msgstr "" |
+ |
54 |
|
+ |
55 |
#: gitar/templates/gitar/index.djhtml:39 |
+ |
56 |
msgid "About Gitar" |
+ |
57 |
msgstr "" |
+ |
58 |
|
+ |
59 |
#: gitar/templates/gitar/index.djhtml:41 |
+ |
60 |
msgid "" |
+ |
61 |
"\n" |
+ |
62 |
"\t\tGitar is a simple web app to easily host Git repositories using the " |
+ |
63 |
"Django framework.\n" |
+ |
64 |
" It's a hobby project of me, to make it easy for\n" |
+ |
65 |
" people to scroll through the code I publish, in a read-only fashion. " |
+ |
66 |
"It\n" |
+ |
67 |
" makes use of\n" |
+ |
68 |
" <a href=\"https://pygments.org/\" target=\"_blank\">Pygments</a>\n" |
+ |
69 |
" to read the source files, and apply the appropriate syntax " |
+ |
70 |
"coloring.\n" |
+ |
71 |
" " |
+ |
72 |
msgstr "" |
+ |
73 |
|
+ |
74 |
#: gitar/templates/gitar/index.djhtml:51 |
+ |
75 |
msgid "" |
+ |
76 |
"All repositories are automatically updated when changes\n" |
+ |
77 |
" have been pushed to the server, without any manual intervention from " |
+ |
78 |
"me.\n" |
+ |
79 |
" Special attention goes to clean URL design, adhering to web " |
+ |
80 |
"standards,\n" |
+ |
81 |
" and responsive design across all screen types." |
+ |
82 |
msgstr "" |
+ |
83 |
|
+ |
84 |
#: gitar/templates/gitar/index.djhtml:57 |
+ |
85 |
msgid "" |
+ |
86 |
"Gitar <b>is a project under development!</b>\n" |
+ |
87 |
" While it's certainly presentable, there's still a lot of room for " |
+ |
88 |
"improvement.<br />\n" |
+ |
89 |
" Also, if you happen to walk in while I'm working, it's possible " |
+ |
90 |
"you'll\n" |
+ |
91 |
" fall through the floor, so be warned =D" |
+ |
92 |
msgstr "" |
+ |
93 |
|
+ |
94 |
#: gitar/templates/gitar/index.djhtml:64 |
+ |
95 |
msgid "Public repositories" |
+ |
96 |
msgstr "" |
+ |
97 |
templates/gitar/locale/nl-be/LC_MESSAGES/django.po ¶
97 additions and 0 deletions.
View changes Hide changes
+ |
1 |
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER |
+ |
2 |
# This file is distributed under the same license as the PACKAGE package. |
+ |
3 |
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. |
+ |
4 |
# |
+ |
5 |
#, fuzzy |
+ |
6 |
msgid "" |
+ |
7 |
msgstr "" |
+ |
8 |
"Project-Id-Version: PACKAGE VERSION\n" |
+ |
9 |
"Report-Msgid-Bugs-To: \n" |
+ |
10 |
"POT-Creation-Date: 2021-11-24 14:00+0100\n" |
+ |
11 |
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
+ |
12 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
+ |
13 |
"Language-Team: LANGUAGE <LL@li.org>\n" |
+ |
14 |
"Language: \n" |
+ |
15 |
"MIME-Version: 1.0\n" |
+ |
16 |
"Content-Type: text/plain; charset=UTF-8\n" |
+ |
17 |
"Content-Transfer-Encoding: 8bit\n" |
+ |
18 |
|
+ |
19 |
#: gitar/templates/gitar/directory.djhtml:29 |
+ |
20 |
msgid "Description" |
+ |
21 |
msgstr "" |
+ |
22 |
|
+ |
23 |
#: gitar/templates/gitar/directory.djhtml:31 |
+ |
24 |
msgid "Branches" |
+ |
25 |
msgstr "" |
+ |
26 |
|
+ |
27 |
#: gitar/templates/gitar/directory.djhtml:37 |
+ |
28 |
msgid "Extra information" |
+ |
29 |
msgstr "" |
+ |
30 |
|
+ |
31 |
#: gitar/templates/gitar/directory.djhtml:49 |
+ |
32 |
msgid "Files" |
+ |
33 |
msgstr "" |
+ |
34 |
|
+ |
35 |
#: gitar/templates/gitar/directory.djhtml:80 |
+ |
36 |
msgid "Commits" |
+ |
37 |
msgstr "" |
+ |
38 |
|
+ |
39 |
#: gitar/templates/gitar/directory.djhtml:92 |
+ |
40 |
msgid "by" |
+ |
41 |
msgstr "" |
+ |
42 |
|
+ |
43 |
#: gitar/templates/gitar/index.djhtml:5 |
+ |
44 |
msgid "Gitar | Index page" |
+ |
45 |
msgstr "" |
+ |
46 |
|
+ |
47 |
#: gitar/templates/gitar/index.djhtml:8 |
+ |
48 |
msgid "My personal answer to GitHub." |
+ |
49 |
msgstr "" |
+ |
50 |
|
+ |
51 |
#: gitar/templates/gitar/index.djhtml:29 |
+ |
52 |
msgid "Front page" |
+ |
53 |
msgstr "" |
+ |
54 |
|
+ |
55 |
#: gitar/templates/gitar/index.djhtml:39 |
+ |
56 |
msgid "About Gitar" |
+ |
57 |
msgstr "" |
+ |
58 |
|
+ |
59 |
#: gitar/templates/gitar/index.djhtml:41 |
+ |
60 |
msgid "" |
+ |
61 |
"\n" |
+ |
62 |
"\t\tGitar is a simple web app to easily host Git repositories using the " |
+ |
63 |
"Django framework.\n" |
+ |
64 |
" It's a hobby project of me, to make it easy for\n" |
+ |
65 |
" people to scroll through the code I publish, in a read-only fashion. " |
+ |
66 |
"It\n" |
+ |
67 |
" makes use of\n" |
+ |
68 |
" <a href=\"https://pygments.org/\" target=\"_blank\">Pygments</a>\n" |
+ |
69 |
" to read the source files, and apply the appropriate syntax " |
+ |
70 |
"coloring.\n" |
+ |
71 |
" " |
+ |
72 |
msgstr "" |
+ |
73 |
|
+ |
74 |
#: gitar/templates/gitar/index.djhtml:51 |
+ |
75 |
msgid "" |
+ |
76 |
"All repositories are automatically updated when changes\n" |
+ |
77 |
" have been pushed to the server, without any manual intervention from " |
+ |
78 |
"me.\n" |
+ |
79 |
" Special attention goes to clean URL design, adhering to web " |
+ |
80 |
"standards,\n" |
+ |
81 |
" and responsive design across all screen types." |
+ |
82 |
msgstr "" |
+ |
83 |
|
+ |
84 |
#: gitar/templates/gitar/index.djhtml:57 |
+ |
85 |
msgid "" |
+ |
86 |
"Gitar <b>is a project under development!</b>\n" |
+ |
87 |
" While it's certainly presentable, there's still a lot of room for " |
+ |
88 |
"improvement.<br />\n" |
+ |
89 |
" Also, if you happen to walk in while I'm working, it's possible " |
+ |
90 |
"you'll\n" |
+ |
91 |
" fall through the floor, so be warned =D" |
+ |
92 |
msgstr "" |
+ |
93 |
|
+ |
94 |
#: gitar/templates/gitar/index.djhtml:64 |
+ |
95 |
msgid "Public repositories" |
+ |
96 |
msgstr "" |
+ |
97 |
views.py ¶
43 additions and 16 deletions.
View changes Hide changes
1 |
1 |
Copyright © 2016 Maarten "Vngngdn" Vangeneugden |
2 |
2 |
|
3 |
3 |
This program is free software: you can redistribute it and/or modify |
4 |
4 |
it under the terms of the GNU Affero General Public License as |
5 |
5 |
published by the Free Software Foundation, either version 3 of the |
6 |
6 |
License, or (at your option) any later version. |
7 |
7 |
|
8 |
8 |
This program is distributed in the hope that it will be useful, |
9 |
9 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 |
10 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 |
11 |
GNU Affero General Public License for more details. |
12 |
12 |
|
13 |
13 |
You should have received a copy of the GNU Affero General Public License |
14 |
14 |
along with this program. If not, see https://www.gnu.org/licenses/agpl.html. |
15 |
15 |
""" |
16 |
16 |
|
17 |
17 |
from django.shortcuts import get_object_or_404, render # This allows to render the template with the view here. It's pretty cool and important. |
18 |
18 |
from django.http import HttpResponseRedirect, HttpResponse |
19 |
19 |
from django.urls import reverse |
20 |
20 |
from .models import * |
21 |
21 |
|
22 |
22 |
from .GitActions import RepoInfo, FileInfo |
23 |
- | from django.utils.translation import ugettext as _ |
+ |
23 |
from django.utils.translation import ugettext as _ |
24 |
24 |
|
25 |
25 |
from django.utils.translation import ugettext as _ |
26 |
26 |
from git import Repo # GitPython functionality. |
27 |
27 |
import git |
28 |
28 |
import pydriller |
29 |
29 |
|
30 |
30 |
import datetime |
31 |
31 |
|
32 |
32 |
from .syntax import * |
33 |
33 |
|
34 |
34 |
# First, I list some standard variables that are common for most of the sites of this app. |
+ |
35 |
# First, I list some standard variables that are common for most of the sites of this app. |
35 |
36 |
|
36 |
37 |
def footer_description(): |
37 |
38 |
return _("Gitar is a simple web app that allows its users to easily share Git repos in combination with the Django framework.") |
38 |
39 |
|
39 |
40 |
def footer_links(): |
40 |
41 |
footer_links = [ |
41 |
42 |
[_('Home page'), reverse('about-index')], |
42 |
43 |
#[_('Source code'), reverse('gitar-repository', kwargs={'repository_name':'gitar'})], |
43 |
44 |
] |
44 |
45 |
return footer_links |
45 |
46 |
|
46 |
47 |
def standard_context(): |
47 |
48 |
context = { |
48 |
49 |
'navbar_title': "Gitar", |
49 |
50 |
'navbar_backArrow': False, |
50 |
51 |
'footer_title': "Gitar", |
51 |
52 |
'footer_description': footer_description(), |
52 |
53 |
'footer_links': footer_links(), |
53 |
54 |
'stylesheet_name': "gitar", |
54 |
55 |
} |
55 |
56 |
return context |
56 |
57 |
|
57 |
58 |
def download_tar(request): |
58 |
59 |
pass |
59 |
60 |
def download_git(request): |
60 |
61 |
pass |
61 |
62 |
def commit(request, repository_name, commit_hash): |
62 |
63 |
template = "gitar/commit.djhtml" |
63 |
64 |
|
64 |
65 |
repository_model = RepoInfo.get_repository_model(repository_name) |
65 |
66 |
commit_repo = pydriller.Repository(repository_model.directory_path, single=commit_hash) |
66 |
67 |
context = standard_context() |
67 |
68 |
file_html_code = dict() |
68 |
- | for c in commit_repo.traverse_commits(): |
+ |
69 |
context["modified_files"] = list() |
+ |
70 |
# NOTE: Although this looks like a for loop, it's not: It's a generator that |
+ |
71 |
# will only produce a single commit. But since it's a generator, I can't |
+ |
72 |
# extract the first element with indexing. So this will have to do. |
+ |
73 |
for c in commit_repo.traverse_commits(): |
69 |
74 |
context["commit"] = c |
70 |
75 |
for modified_file in c.modified_files: |
+ |
76 |
# case: Only summary line, no \n: |
+ |
77 |
if '\n' not in c.msg: |
+ |
78 |
context["first_line"] = c.msg |
+ |
79 |
# case: Summary line split by two \n: |
+ |
80 |
elif '\n' not in c.msg.split('\n\n', maxsplit=1)[0]: |
+ |
81 |
parts = c.msg.split('\n\n', maxsplit=1) |
+ |
82 |
context["first_line"] = parts[0] |
+ |
83 |
context["other_lines"] = parts[1] |
+ |
84 |
# case: Badly formatted message |
+ |
85 |
else: |
+ |
86 |
parts = c.msg.split('\n', maxsplit=1) |
+ |
87 |
context["first_line"] = parts[0] |
+ |
88 |
context["other_lines"] = parts[1] |
+ |
89 |
# Final formatting: |
+ |
90 |
if "other_lines" in context: |
+ |
91 |
context["other_lines"] = context["other_lines"].replace('\n\n','<br>').replace('\n', ' ') |
+ |
92 |
# Processing the modified files |
+ |
93 |
for modified_file in c.modified_files: |
71 |
94 |
html_code_before = None |
72 |
- | html_code = None |
73 |
- | if modified_file.source_code_before is not None: |
74 |
- | html_code_before = code_to_HTML( |
75 |
- | modified_file.source_code_before, |
76 |
- | modified_file.filename) |
77 |
- | if modified_file.source_code is not None: |
78 |
- | html_code = code_to_HTML( |
79 |
- | modified_file.source_code, |
80 |
- | modified_file.filename) |
81 |
- | # XXX: This function WILL OVERWRITE the presented code of a file if |
+ |
95 |
processed = CommitInfo.prepare_and_process(modified_file) |
+ |
96 |
#print(processed) |
+ |
97 |
context["modified_files"].append(processed) |
+ |
98 |
#html_code_before = None |
+ |
99 |
#html_code_after = None |
+ |
100 |
#if modified_file.source_code_before is not None: |
+ |
101 |
#html_code_before = code_to_HTML( |
+ |
102 |
#modified_file.source_code_before, |
+ |
103 |
#modified_file.filename) |
+ |
104 |
#if modified_file.source_code is not None: |
+ |
105 |
#html_code_after = code_to_HTML( |
+ |
106 |
#modified_file.source_code, |
+ |
107 |
#modified_file.filename) |
+ |
108 |
# XXX: This function WILL OVERWRITE the presented code of a file if |
82 |
109 |
# this particular commit includes multiple files with the same name, |
83 |
110 |
# but a different path. I'll update this in the future... |
84 |
111 |
file_html_code[modified_file.filename] = (html_code_before, html_code) |
85 |
- | context["file_html_code"] = file_html_code |
86 |
- | |
+ |
112 |
#context["file_html_code"] = file_html_code |
+ |
113 |
|
87 |
114 |
#context["subdirectories"] = subdirectories |
88 |
115 |
#context["commits"] = commits |
89 |
116 |
#context["branch"] = branch |
90 |
117 |
context["repository_name"] = repository_name |
91 |
118 |
# Adding the html code for the files |
92 |
119 |
context["file_html_code"] |
93 |
- | #context["repository_description"] = repository.description |
+ |
120 |
#context["repository_description"] = repository.description |
94 |
121 |
html_code = code_to_HTML(raw_file_data, file.name) |
95 |
- | |
+ |
122 |
|
96 |
123 |
return render(request, template, context) |
97 |
124 |
|
98 |
125 |
def file_commit(request): |
99 |
126 |
pass |
100 |
127 |
|
101 |
128 |
# From here, the actual views start. |
102 |
129 |
def index(request): |
103 |
130 |
""" The start page of Gitar. |
104 |
131 |
|
105 |
132 |
The goal of this view, is to collect all the available repositories, |
106 |
133 |
including some additional information, such as programming language, |
107 |
134 |
license, description, ... in order to give a fast overview of the most |
108 |
135 |
prominent information. |
109 |
136 |
""" |
110 |
137 |
|
111 |
138 |
# Collecting the available repositories: |
112 |
139 |
# Template: |
113 |
140 |
template = "gitar/index.djhtml" |
114 |
141 |
# Requesting the repositories: |
115 |
142 |
modelRepos = Repository.objects.all() |
116 |
143 |
# From here, we start collecting info about all the repositories: |
117 |
144 |
class BlankRepository: pass # Blank object in which all data will be collected. |
118 |
145 |
repositories = [] |
119 |
146 |
for modelRepo in modelRepos: |
120 |
147 |
repository = BlankRepository() |
121 |
148 |
# TODO: Find a way to add all of modelRepo's fields without having to |
122 |
149 |
# hardcode them. This is prone to errors and is redundant. |
123 |
150 |
repository.name = str(modelRepo) |
124 |
151 |
repository.programmingLanguage = modelRepo.programmingLanguage |
125 |
152 |
repository.license = modelRepo.license |
126 |
153 |
repository.description = RepoInfo.get_description(modelRepo) |
127 |
154 |
|
128 |
155 |
#gitRepo = Repo.init(modelRepo.directory(), bare=True) # Connects to the Git Repo. |
129 |
156 |
# See tests.py, which assures all repositories exist. Tests are handy. |
130 |
157 |
#repository.description = gitRepo.description |
131 |
158 |
# This is mostly personal taste, but I like to show the amount of files. |
132 |
159 |
#repoTree = gitRepo.heads.master.commit.tree |
133 |
160 |
#repository.fileCount = len(repoTree.blobs) # blobs are files. |
134 |
161 |
repositories.append(repository) |
135 |
162 |
# After that, I extend the standard context with the repositories: |
136 |
163 |
context = standard_context() |
137 |
164 |
context['repositories'] = repositories |
138 |
165 |
# And finally, sending everything back. |
139 |
166 |
return render(request, template, context) |
140 |
167 |
|
141 |
168 |
def repositories(request, repository_name, branch="master"): |
142 |
169 |
# A repo's root is a directory by default, so this will automatically return |
143 |
170 |
# a directory view. But still, this is a bit nicer. |
144 |
171 |
return path_explorer(request, repository_name, branch, "") |
145 |
172 |
|
146 |
173 |
def path_explorer(request, repository_name, branch, path=""): |
147 |
174 |
""" Checks whether the given path is a file or a directory, and calls the |
148 |
175 |
appropriate view function accordingly. |
149 |
176 |
""" |
150 |
177 |
repository = RepoInfo.get_repository_object(repository_name) |
151 |
178 |
# From the GitPython documentation: |
152 |
179 |
# You can obtain the tree object of a repository, which is the directory of |
153 |
180 |
# that repo. This tree can be accessed as if it were a native Python list, |
154 |
181 |
# where the elements are the subdirectories and files. So, the idea to |
155 |
182 |
# determine whether a file, or a directory was requested, is simple: |
156 |
183 |
# 1. Split the path with "/" as seperator. |
157 |
184 |
# 2. Replace the current tree variable with the one retrieved from the |
158 |
185 |
# subtree element |
159 |
186 |
# 3. Repeat 2. until all parts of the given path are exhausted. |
160 |
187 |
# If we now still have a tree, we're looking at a directory, so display the |
161 |
188 |
# files (and subdirectories) of this directory. |
162 |
189 |
# Else, if we hit a blob, display the file contents. |
163 |
190 |
path_parts = path.split(sep="/") |
164 |
191 |
# FIXME: This is a bug at the URL regex part that I haven't been able to fix |
165 |
192 |
# yet. This serves as a temporary fix: |
166 |
193 |
# If the last part of the path is an empty string (which happens when the |
167 |
194 |
# last symbol was a '/'), remove that part from the list. |
168 |
195 |
# Of course, this is bad monkeypatching, but I suck at regex, so as long as |
169 |
196 |
# I don't find the solution, this'll have to do. |
170 |
197 |
|
171 |
198 |
|
172 |
199 |
#print(path_parts) |
173 |
200 |
|
174 |
201 |
if path_parts[len(path_parts)-1] == "": |
175 |
202 |
path_parts.pop() |
176 |
203 |
|
177 |
204 |
if len(path_parts) == 0: |
178 |
205 |
directory = repository.heads[branch].commit.tree |
179 |
206 |
return directory_view(request, repository_name, branch, path, directory) |
180 |
207 |
|
181 |
208 |
assert len(path_parts) != 0 |
182 |
209 |
|
183 |
210 |
# FIXME: If the user gives a "<something>/../<somethingElse>", that should |
184 |
211 |
# become "<something>". Obviously, although I think that's done by default |
185 |
212 |
# already. |
186 |
213 |
directory = repository.heads[branch].commit.tree |
187 |
214 |
for i in range(len(path_parts)): |
188 |
215 |
subdirectories = directory.trees |
189 |
216 |
#if len(subdirectories) == 0: |
190 |
217 |
# This can't happen, as this would imply there is a directory inside |
191 |
218 |
# a file. |
192 |
219 |
# assert False |
193 |
220 |
#else: |
194 |
221 |
for subdirectory in subdirectories: |
195 |
222 |
if subdirectory.name == path_parts[i]: |
196 |
223 |
directory = subdirectory |
197 |
224 |
#break # Useless optimization |
198 |
225 |
# When there are no more directories to traverse, check if the last part of |
199 |
226 |
# the path is either a file, or a directory: |
200 |
227 |
blobs = directory.blobs |
201 |
228 |
#print(path_parts) |
202 |
229 |
last_part = path_parts[len(path_parts)-1] |
203 |
230 |
for blob in directory.blobs: |
204 |
231 |
#print(blob.name) |
205 |
232 |
if blob.name == last_part: |
206 |
233 |
file_blob = blob |
207 |
234 |
#print("Returning file view") |
208 |
235 |
return file_view(request, repository_name, branch, path, file_blob) |
209 |
236 |
else: |
210 |
237 |
pass |
211 |
238 |
#print("blob name: " + blob.name) |
212 |
239 |
#print("last part: " + last_part) |
213 |
240 |
return directory_view(request, repository_name, branch, path, directory) |
214 |
241 |
|
215 |
242 |
def directory_view(request, repository_name, branch, path, directory): |
216 |
243 |
""" Collects the given directories's files and subdirectories, and renders a |
217 |
244 |
template to display this data. |
218 |
245 |
""" |
219 |
246 |
|
220 |
247 |
# Collecting files in this directory |
221 |
248 |
repository = RepoInfo.get_repository_object(repository_name) |
222 |
249 |
files = [] |
223 |
250 |
for file in directory.blobs: |
224 |
251 |
latest_commit_object = FileInfo.last_commit(repository_name, branch, file.path) |
225 |
252 |
older_than_one_month = (datetime.datetime.now(datetime.timezone.utc) - latest_commit_object.committer_date).days > 30 |
226 |
253 |
print(latest_commit_object) |
227 |
254 |
files.append({ |
228 |
255 |
"name":file.name, |
229 |
256 |
"path":file.path, |
230 |
257 |
#"commit":"",#FileInfo.last_commit(repository, file).hexsha[:20], |
231 |
258 |
"commit": latest_commit_object, |
232 |
259 |
"older_than_one_month": older_than_one_month, |
233 |
260 |
}) |
234 |
261 |
#print(FileInfo.last_commit(repository_name, branch, file)) |
235 |
262 |
# Collecting commits for this branch |
236 |
263 |
commits = [] |
237 |
264 |
for commit in repository.iter_commits(branch): |
238 |
265 |
commits.append({ |
239 |
266 |
"hash":commit.hexsha[:20], |
240 |
267 |
"author":commit.author, |
241 |
268 |
"description":commit.summary, |
242 |
269 |
}) |
243 |
270 |
# Collecting subdirectories |
244 |
271 |
subdirectories = [] |
245 |
272 |
for subdirectory in directory.trees: |
246 |
273 |
subdirectories.append({ |
247 |
274 |
"path":subdirectory.path, |
248 |
275 |
"name":subdirectory.name, |
249 |
276 |
}) |
250 |
277 |
# Collecting rendering information: |
251 |
278 |
template = "gitar/directory.djhtml" |
252 |
279 |
context = standard_context() |
253 |
280 |
context["files"] = files |
254 |
281 |
context["subdirectories"] = subdirectories |
255 |
282 |
context["commits"] = commits |
256 |
283 |
context["branch"] = branch |
257 |
284 |
context["repository_name"] = repository_name |
258 |
285 |
context["repository_description"] = repository.description |
259 |
286 |
# Collection repo information |
260 |
287 |
for repo in Repository.objects.all(): |
261 |
288 |
if str(repo) == repository_name: |
262 |
289 |
context["repository_language"] = repo.programmingLanguage |
263 |
290 |
context["repository_license"] = repo.license |
264 |
291 |
break |
265 |
292 |
branches = [] |
266 |
293 |
for bbranch in repository.heads: |
267 |
294 |
branches.append(bbranch.name) |
268 |
295 |
context["branches"] = branches |
269 |
296 |
return render(request, template, context) |
270 |
297 |
|
271 |
298 |
|
272 |
299 |
def file_view(request, repository_name, branch, path, file): |
273 |
300 |
""" Collects the file contents of the given file path, and returns it to the |
274 |
301 |
template, with the file contents already formatted in HTML using Pygments. |
275 |
302 |
""" |
276 |
303 |
|
277 |
304 |
# Turning the file's contents in HTML ready output: |
278 |
305 |
raw_file_data = file.data_stream.read() |
279 |
306 |
html_code = code_to_HTML(raw_file_data, file.name) |
280 |
307 |
# Collecting rendering information: |
281 |
308 |
template = "gitar/file.djhtml" |
282 |
309 |
context = standard_context() |
283 |
310 |
context["content"] = html_code |
284 |
311 |
context["file_name"] = file.name |
285 |
312 |
context["repository_name"] = repository_name |
286 |
313 |
return render(request, template, context) |
287 |
314 |
|
288 |
315 |