gitar

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