Prepare Gitar for internationalization
- Author
- Maarten 'Vngngdn' Vangeneugden
- Date
- Oct. 19, 2017, 6:21 p.m.
- Hash
- 140301439f9da032a0f874bb5ee3a31ce9ce73fe
- Parent
- ecb197b6acbe073d40cb56f2c84cee1aa7b250cc
- Modified files
- templates/gitar/directory.html
- templates/gitar/index.html
- views.py
templates/gitar/directory.html ¶
3 additions and 3 deletions.
View changes Hide changes
1 |
1 |
{% load i18n %} |
2 |
2 |
{% block title %}{{ repository_name }} | Gitar{% endblock title %} |
3 |
3 |
{% block description %} |
4 |
4 |
{{repository_description}} |
5 |
5 |
{% endblock description %} |
6 |
6 |
{% block main %} |
7 |
7 |
{% with mdac=materialDesign_accentColor mdc=materialDesign_color %} {# You'll see why this is handy shortly. #} |
8 |
8 |
<div class="section {{ materialDesign_color }} lighten-2"> |
9 |
9 |
</div> |
10 |
10 |
<div class="section"> |
11 |
11 |
<div class="row"> |
12 |
12 |
<div class="col hide-on-med-and-down l4"> |
13 |
13 |
<!-- Add tertiary information such as branches and licensing here. --> |
14 |
14 |
<h3 class="{{mdc}}-text">{{repository_name}}</h3> |
15 |
15 |
<h5 class="{{mdc}}-text">{% trans "Description" %}</h5> |
16 |
16 |
{{repository_description}} |
17 |
17 |
<h5 class="{{mdc}}-text">Branches</h5> |
18 |
- | {% for bbranch in branches %} |
+ |
18 |
{% for bbranch in branches %} |
19 |
19 |
<a class="{{mdac}}-text text-accent-3" href="{% url 'gitar-repository' repository_name bbranch %}"> |
20 |
20 |
{{bbranch}} |
21 |
21 |
</a><br /> |
22 |
22 |
{% endfor %} |
23 |
23 |
<h5 class="{{mdc}}-text">{% trans "Extra information" %}</h5> |
24 |
24 |
<div class="chip"> |
25 |
25 |
{{repository_language}} |
26 |
26 |
<i class="material-icons {{mdac}}-text text-accent-3">code</i> |
27 |
27 |
</div><br /> |
28 |
28 |
<div class="chip"> |
29 |
29 |
{{repository_license}} |
30 |
30 |
<i class="material-icons {{mdac}}-text text-accent-3">copyright</i> |
31 |
31 |
</div><br /> |
32 |
32 |
</div> |
33 |
33 |
<div class="col s12 m8 l5"> |
34 |
34 |
<!-- Main area with links to files and subdirectories --> |
35 |
35 |
<h3 class="{{mdc}}-text">Files</h3> |
36 |
- | <table class="highlight"> |
+ |
36 |
<table class="highlight"> |
37 |
37 |
{% if subdirectories %} |
38 |
38 |
<thead> |
39 |
39 |
{% for subdirectory in subdirectories %} |
40 |
40 |
<tr> |
41 |
41 |
<th> |
42 |
42 |
<a class="{{mdac}}-text text-accent-4" href="{% url 'gitar-path-explorer' repository_name branch subdirectory.path %}"> |
43 |
43 |
{{subdirectory.name}} |
44 |
44 |
</a> |
45 |
45 |
</th> |
46 |
46 |
</tr> |
47 |
47 |
{% endfor %} |
48 |
48 |
</thead> |
49 |
49 |
{% endif %} |
50 |
50 |
<tbody> |
51 |
51 |
{% for file in files %} |
52 |
52 |
<tr> |
53 |
53 |
<td> |
54 |
54 |
<a class="{{mdac}}-text text-accent-3" href="{% url 'gitar-path-explorer' repository_name branch file.path %}"> |
55 |
55 |
{{file.name}} |
56 |
56 |
</a> |
57 |
57 |
</td> |
58 |
58 |
<td>{{file.commit|truncatechars:6}}</td> |
59 |
59 |
</tr> |
60 |
60 |
{% endfor %} |
61 |
61 |
</tbody> |
62 |
62 |
</table> |
63 |
63 |
</div> |
64 |
64 |
<div class="col hide-on-small-only m4 l3"> |
65 |
65 |
<!-- List of commits on the current branch, chronologically. --> |
66 |
66 |
<h3 class="{{mdc}}-text">Commits</h3> |
67 |
- | {% for commit in commits %} |
+ |
67 |
{% for commit in commits %} |
68 |
68 |
<hr /> |
69 |
69 |
<a |
70 |
70 |
class="{{mdac}}-text text-accent-3 tooltipped" |
71 |
71 |
{# href="{% url 'gitar-commit' repository_name commit.hash %}" #} |
72 |
72 |
data-position="left" |
73 |
73 |
data-delay="50" |
74 |
74 |
data-tooltip="Viewing commits is not implemented yet!"> |
75 |
75 |
{{commit.hash|truncatechars:15}} |
76 |
76 |
</a> |
77 |
77 |
<span class="{{mdc}}-text text-lighten-2"> |
78 |
78 |
{% trans "by" %} {{commit.author}} |
79 |
79 |
</span><br /> |
80 |
80 |
{{commit.description|lower|capfirst}}{% if commit.description|last != "." %}.{% endif %} |
81 |
81 |
{% endfor %} |
82 |
82 |
</div> |
83 |
83 |
</div> |
84 |
84 |
</div> |
85 |
85 |
{% endwith %} |
86 |
86 |
{% endblock main %} |
87 |
87 |
templates/gitar/index.html ¶
12 additions and 9 deletions.
View changes Hide changes
1 |
1 |
{% load i18n %} |
2 |
2 |
|
3 |
3 |
{% block title %}Gitar | Index page{% endblock title %} |
4 |
- | {% block description %} |
+ |
4 |
{% block description %} |
5 |
5 |
Maarten's Gitar app. Still under heavy development, but maintained with <3 |
6 |
- | {% endblock description %} |
+ |
6 |
{% endblock description %} |
7 |
7 |
{% block main %} |
8 |
8 |
{% with mdac=materialDesign_accentColor %} {# You'll see why this is handy shortly. #} |
9 |
9 |
{% with mdc=materialDesign_color %} |
10 |
10 |
<div class="section {{ materialDesign_color }} lighten-2"> |
11 |
11 |
<p class="flow-text container white-text"> |
12 |
12 |
Gitar is a simple web app to easily host Git repositories using the Django framework. |
13 |
- | </p> |
+ |
13 |
</p> |
14 |
14 |
</div> |
15 |
15 |
<div class="container section"> |
16 |
16 |
<h3 class="{{ materialDesign_color }}-text">About Gitar</h3> |
17 |
- | <p> |
+ |
17 |
<p> |
18 |
18 |
{% blocktrans %}Gitar is a hobby project of me, to make it easy for |
19 |
19 |
people to scroll through the code I publish, in a read-only fashion. It |
20 |
20 |
makes use of |
21 |
21 |
<a class="{{ mdac }}-text text-accent-3" href="http://pygments.org/">Pygments</a> |
22 |
22 |
to read the source files, and apply the appropriate syntax coloring. |
23 |
23 |
{% endblocktrans %} |
24 |
24 |
</p> |
25 |
25 |
<p> |
26 |
26 |
{% blocktrans %}Gitar <b>is NOT stable, and under heavy development!</b> |
27 |
- | It's currently able to display code, and traverse a source tree, but is |
28 |
- | limited to the master branch, and does not feature extras like a mailing |
29 |
- | list.<br /> |
30 |
- | Also, if you happen to walk in while I'm working, it's possible you'll |
+ |
27 |
have been pushed to the server, without any manual intervention from me. |
+ |
28 |
Special attention goes to clean URL design, adhering to web standards, |
+ |
29 |
and responsive design across all screen types.{% endblocktrans %} |
+ |
30 |
<p> |
+ |
31 |
{% blocktrans %}Gitar <b>is a project under development!</b> |
+ |
32 |
While it's certainly presentable, there's still a lot of room for improvement.<br /> |
+ |
33 |
Also, if you happen to walk in while I'm working, it's possible you'll |
31 |
34 |
fall through the floor, so be warned =D{% endblocktrans %} |
32 |
35 |
</p> |
33 |
36 |
</div> |
34 |
37 |
<div class="container section"> |
35 |
38 |
<h3 class="{{ materialDesign_color }}-text">Public repositories</h3> |
36 |
- | {% for repository in repositories %} {# ARGH DON'T YOU LOVE THE READABILITY #} |
+ |
39 |
{% for repository in repositories %} {# ARGH DON'T YOU LOVE THE READABILITY #} |
37 |
40 |
<div class="row"> |
38 |
41 |
<div class="col m3 valign-wrapper"> |
39 |
42 |
<a class="valign waves-effect btn white {{ mdac }} accent-3" href="{% url 'gitar-repository' repository_name=repository.name %}"> |
40 |
43 |
{{ repository.name }} |
41 |
44 |
</a> |
42 |
45 |
</div> |
43 |
46 |
<div class="col m7"> |
44 |
47 |
<p> {{ repository.description }} </p> |
45 |
48 |
</div> |
46 |
49 |
<div class="col m2"> |
47 |
50 |
<ul> |
48 |
51 |
<li><i class="material-icons {{ mdac }}-text text-accent-3">code</i> |
49 |
52 |
{{ repository.programmingLanguage }}</li> |
50 |
53 |
<!--<li><i class="material-icons">description</i> |
51 |
54 |
{{ repository.fileCount }}</li>--> |
52 |
55 |
<li><i class="material-icons {{ mdc }}-text">copyright</i> |
53 |
56 |
{{ repository.license }}</li> |
54 |
57 |
</ul> |
55 |
58 |
</div> |
56 |
59 |
</div> |
57 |
60 |
{% endfor %} |
58 |
61 |
</div> |
59 |
62 |
{% endwith %} |
60 |
63 |
{% endwith %} |
61 |
64 |
{% endblock main %} |
62 |
65 |
views.py ¶
3 additions and 2 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.core.urlresolvers import reverse |
20 |
20 |
from .models import * |
21 |
21 |
|
22 |
22 |
from .GitActions import RepoInfo |
23 |
23 |
|
24 |
24 |
from git import Repo # GitPython functionality. |
25 |
25 |
import git |
26 |
26 |
|
27 |
27 |
from .syntax import * |
28 |
28 |
|
29 |
29 |
# First, I list some standard variables that are common for most of the sites of this app. |
30 |
30 |
|
31 |
31 |
def footer_description(): |
32 |
32 |
return "Gitar is a simple web app that allows its users to easily share Git repos in combination with the Django framework." |
33 |
- | |
+ |
33 |
|
34 |
34 |
def footer_links(): |
35 |
35 |
footer_links = [ |
36 |
36 |
#['Source', 'OHGODHELPNOTDONEYET'], |
37 |
37 |
['Personal website', reverse('about-index')], |
38 |
- | ] |
+ |
38 |
[_('Source code'), reverse('gitar-repository', 'gitar')], |
+ |
39 |
] |
39 |
40 |
return footer_links |
40 |
41 |
|
41 |
42 |
def standard_context(): |
42 |
43 |
context = { |
43 |
44 |
'materialDesign_color': "blue-grey", |
44 |
45 |
'materialDesign_accentColor': "green", |
45 |
46 |
'navbar_title': "Gitar", |
46 |
47 |
'navbar_backArrow': False, |
47 |
48 |
'footer_title': "Gitar", |
48 |
49 |
'footer_description': footer_description(), |
49 |
50 |
'footer_links': footer_links(), |
50 |
51 |
} |
51 |
52 |
return context |
52 |
53 |
|
53 |
54 |
# From here, the actual views start. |
54 |
55 |
def index(request): |
55 |
56 |
""" The start page of Gitar. |
56 |
57 |
|
57 |
58 |
The goal of this view, is to collect all the available repositories, |
58 |
59 |
including some additional information, such as programming language, |
59 |
60 |
license, description, ... in order to give a fast overview of the most |
60 |
61 |
prominent information. |
61 |
62 |
""" |
62 |
63 |
|
63 |
64 |
# Collecting the available repositories: |
64 |
65 |
# Template: |
65 |
66 |
template = "gitar/index.html" |
66 |
67 |
# Requesting the repositories: |
67 |
68 |
modelRepos = Repository.objects.all() |
68 |
69 |
# From here, we start collecting info about all the repositories: |
69 |
70 |
class BlankRepository: pass # Blank object in which all data will be collected. |
70 |
71 |
repositories = [] |
71 |
72 |
for modelRepo in modelRepos: |
72 |
73 |
repository = BlankRepository() |
73 |
74 |
# TODO: Find a way to add all of modelRepo's fields without having to |
74 |
75 |
# hardcode them. This is prone to errors and is redundant. |
75 |
76 |
repository.name = str(modelRepo) |
76 |
77 |
repository.programmingLanguage = modelRepo.programmingLanguage |
77 |
78 |
repository.license = modelRepo.license |
78 |
79 |
repository.description = RepoInfo.get_description(modelRepo) |
79 |
80 |
|
80 |
81 |
#gitRepo = Repo.init(modelRepo.directory(), bare=True) # Connects to the Git Repo. |
81 |
82 |
# See tests.py, which assures all repositories exist. Tests are handy. |
82 |
83 |
#repository.description = gitRepo.description |
83 |
84 |
# This is mostly personal taste, but I like to show the amount of files. |
84 |
85 |
#repoTree = gitRepo.heads.master.commit.tree |
85 |
86 |
#repository.fileCount = len(repoTree.blobs) # blobs are files. |
86 |
87 |
repositories.append(repository) |
87 |
88 |
# After that, I extend the standard context with the repositories: |
88 |
89 |
context = standard_context() |
89 |
90 |
context['repositories'] = repositories |
90 |
91 |
# And finally, sending everything back. |
91 |
92 |
return render(request, template, context) |
92 |
93 |
|
93 |
94 |
def commit(request, repository, commit): |
94 |
95 |
pass # TODO |
95 |
96 |
|
96 |
97 |
def repositories(request, repository_name, branch="master"): |
97 |
98 |
# A repo's root is a directory by default, so this will automatically return |
98 |
99 |
# a directory view. But still, this is a bit nicer. |
99 |
100 |
return path_explorer(request, repository_name, branch, "") |
100 |
101 |
|
101 |
102 |
def path_explorer(request, repository_name, branch, path): |
102 |
103 |
""" Checks whether the given path is a file or a directory, and calls the |
103 |
104 |
appropriate view function accordingly. |
104 |
105 |
""" |
105 |
106 |
repository = RepoInfo.get_repository_object(repository_name) |
106 |
107 |
# From the GitPython documentation: |
107 |
108 |
# You can obtain the tree object of a repository, which is the directory of |
108 |
109 |
# that repo. This tree can be accessed as if it were a native Python list, |
109 |
110 |
# where the elements are the subdirectories and files. So, the idea to |
110 |
111 |
# determine whether a file, or a directory was requested, is simple: |
111 |
112 |
# 1. Split the path with "/" as seperator. |
112 |
113 |
# 2. Replace the current tree variable with the one retrieved from the |
113 |
114 |
# subtree element |
114 |
115 |
# 3. Repeat 2. until all parts of the given path are exhausted. |
115 |
116 |
# If we now still have a tree, we're looking at a directory, so display the |
116 |
117 |
# files (and subdirectories) of this directory. |
117 |
118 |
# Else, if we hit a blob, display the file contents. |
118 |
119 |
path_parts = path.split(sep="/") |
119 |
120 |
# FIXME: This is a bug at the URL regex part that I haven't been able to fix |
120 |
121 |
# yet. This serves as a temporary fix: |
121 |
122 |
# If the last part of the path is an empty string (which happens when the |
122 |
123 |
# last symbol was a '/'), remove that part from the list. |
123 |
124 |
# Of course, this is bad monkeypatching, but I suck at regex, so as long as |
124 |
125 |
# I don't find the solution, this'll have to do. |
125 |
126 |
|
126 |
127 |
|
127 |
128 |
print(path_parts) |
128 |
129 |
|
129 |
130 |
if path_parts[len(path_parts)-1] == "": |
130 |
131 |
path_parts.pop() |
131 |
132 |
|
132 |
133 |
if len(path_parts) == 0: |
133 |
134 |
directory = repository.heads[branch].commit.tree |
134 |
135 |
return directory_view(request, repository_name, branch, path, directory) |
135 |
136 |
|
136 |
137 |
assert len(path_parts) != 0 |
137 |
138 |
|
138 |
139 |
# FIXME: If the user gives a "<something>/../<somethingElse>", that should |
139 |
140 |
# become "<something>". Obviously, although I think that's done by default |
140 |
141 |
# already. |
141 |
142 |
directory = repository.heads[branch].commit.tree |
142 |
143 |
for i in range(len(path_parts)): |
143 |
144 |
subdirectories = directory.trees |
144 |
145 |
#if len(subdirectories) == 0: |
145 |
146 |
# This can't happen, as this would imply there is a directory inside |
146 |
147 |
# a file. |
147 |
148 |
# assert False |
148 |
149 |
#else: |
149 |
150 |
for subdirectory in subdirectories: |
150 |
151 |
if subdirectory.name == path_parts[i]: |
151 |
152 |
directory = subdirectory |
152 |
153 |
#break # Useless optimization |
153 |
154 |
# When there are no more directories to traverse, check if the last part of |
154 |
155 |
# the path is either a file, or a directory: |
155 |
156 |
blobs = directory.blobs |
156 |
157 |
print(path_parts) |
157 |
158 |
last_part = path_parts[len(path_parts)-1] |
158 |
159 |
for blob in directory.blobs: |
159 |
160 |
print(blob.name) |
160 |
161 |
if blob.name == last_part: |
161 |
162 |
file_blob = blob |
162 |
163 |
print("Returning file view") |
163 |
164 |
return file_view(request, repository_name, branch, path, file_blob) |
164 |
165 |
else: |
165 |
166 |
print("blob name: " + blob.name) |
166 |
167 |
print("last part: " + last_part) |
167 |
168 |
return directory_view(request, repository_name, branch, path, directory) |
168 |
169 |
|
169 |
170 |
def directory_view(request, repository_name, branch, path, directory): |
170 |
171 |
""" Collects the given directories's files and subdirectories, and renders a |
171 |
172 |
template to display this data. |
172 |
173 |
""" |
173 |
174 |
|
174 |
175 |
# Collecting files in this directory |
175 |
176 |
repository = RepoInfo.get_repository_object(repository_name) |
176 |
177 |
files = [] |
177 |
178 |
for file in directory.blobs: |
178 |
179 |
files.append({ |
179 |
180 |
"name":file.name, |
180 |
181 |
"path":file.path, |
181 |
182 |
"commit":"",#FileInfo.last_commit(repository, file).hexsha[:20], |
182 |
183 |
}) |
183 |
184 |
# Collecting commits for this branch |
184 |
185 |
commits = [] |
185 |
186 |
for commit in repository.iter_commits(branch): |
186 |
187 |
commits.append({ |
187 |
188 |
"hash":commit.hexsha[:20], |
188 |
189 |
"author":commit.author, |
189 |
190 |
"description":commit.summary, |
190 |
191 |
}) |
191 |
192 |
# Collecting subdirectories |
192 |
193 |
subdirectories = [] |
193 |
194 |
for subdirectory in directory.trees: |
194 |
195 |
subdirectories.append({ |
195 |
196 |
"path":subdirectory.path, |
196 |
197 |
"name":subdirectory.name, |
197 |
198 |
}) |
198 |
199 |
# Collecting rendering information: |
199 |
200 |
template = "gitar/directory.html" |
200 |
201 |
context = standard_context() |
201 |
202 |
context["files"] = files |
202 |
203 |
context["subdirectories"] = subdirectories |
203 |
204 |
context["commits"] = commits |
204 |
205 |
context["branch"] = branch |
205 |
206 |
context["repository_name"] = repository_name |
206 |
207 |
context["repository_description"] = repository.description |
207 |
208 |
# Collection repo information |
208 |
209 |
for repo in Repository.objects.all(): |
209 |
210 |
if str(repo) == repository_name: |
210 |
211 |
context["repository_language"] = repo.programmingLanguage |
211 |
212 |
context["repository_license"] = repo.license |
212 |
213 |
break |
213 |
214 |
branches = [] |
214 |
215 |
for bbranch in repository.heads: |
215 |
216 |
branches.append(bbranch.name) |
216 |
217 |
context["branches"] = branches |
217 |
218 |
return render(request, template, context) |
218 |
219 |
|
219 |
220 |
|
220 |
221 |
def file_view(request, repository_name, branch, path, file): |
221 |
222 |
""" Collects the file contents of the given file path, and returns it to the |
222 |
223 |
template, with the file contents already formatted in HTML using Pygments. |
223 |
224 |
""" |
224 |
225 |
|
225 |
226 |
# Turning the file's contents in HTML ready output: |
226 |
227 |
raw_file_data = file.data_stream.read() |
227 |
228 |
html_code = code_to_HTML(raw_file_data, file.name) |
228 |
229 |
# Collecting rendering information: |
229 |
230 |
template = "gitar/file.html" |
230 |
231 |
context = standard_context() |
231 |
232 |
context["content"] = html_code |
232 |
233 |
context["file_name"] = file.name |
233 |
234 |
context["repository_name"] = repository_name |
234 |
235 |
return render(request, template, context) |
235 |
236 |
|
236 |
237 |