Index

joshstock.in / 4deb1aa

Source for serving and static templating/compiling of https://joshstock.in.

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
9512 Dec 2020 00:224deb1aaUpdate lua-gitwebJosh Stockin13940G

Blob @ joshstock.in / lua-gitweb / html.lua

text/plain14562 bytesdownload raw
1-- html.lua
2-- Formatting data with HTML
3
4-- Copyright (c) 2020 Joshua 'joshuas3' Stockin
5-- <https://joshstock.in>
6
7local lyaml = require("lyaml")
8local utils = require("utils")
9local git = require("git_commands")
10local request = require("request")
11local tabulate = require("tabulate")
12
13local create_html_template = function()
14 local t = {}
15 t.title = ""
16 t.meta_tags = {}
17 t.body = ""
18 return t
19end
20
21local tree = function(repo, repo_dir, branch, path)
22 local t = create_html_template()
23
24 if path ~= "" then -- make sure path exists
25 local path_tree = git.list_tree(repo_dir, branch.name, string.sub(path, 1, path:len() - 1))
26 if #path_tree.dirs == 0 then -- no path found
27 return nil
28 end
29 end
30
31 -- Header with repository description and navigation links
32 t.body = t.body..string.format([[<h2><a href="/%s">%s</a> / <a href="/%s/tree/%s">%s</a></h2>]], repo.name, repo.name, repo.name, branch.name, branch.name)
33 t.body = t.body.."<p>"..repo.description.."</p>"
34
35 local navlinks_list = {
36 "<a href=\"/"..repo.name.."/download\">Download</a>",
37 "<a href=\"/"..repo.name.."/refs\">Refs</a>",
38 "<a href=\"/"..repo.name.."/log/"..branch.name.."\">Commit Log</a>",
39 "<b><a href=\"/"..repo.name.."/tree/"..branch.name.."\">Files</a></b>"
40 }
41
42 for _, special in pairs(repo.specialfiles) do
43 local split = string.split(special, " ")
44 table.insert(navlinks_list, string.format([[<a href="/%s/blob/%s/%s">%s</a>]], repo.name, branch.name, split[2], split[1]))
45 end
46
47 t.body = t.body.."<p>"..table.concat(navlinks_list, " | ").."</p>"
48
49 -- Latest Commit table
50 t.body = t.body.."<h3>Latest Commit</h3>"
51
52 local commits_table_data = {}
53 commits_table_data.class = "log"
54 commits_table_data.headers = {
55 {"timestamp", "Time"},
56 {"shorthash", "Hash"},
57 {"subject", "Subject"},
58 {"author", "Author"},
59 {"changed_files", [[<span class="q" title="# of files changed">#</span>]]},
60 {"changed_plus", [[<span class="q" title="Insertions">(+)</span>]]},
61 {"changed_minus", [[<span class="q" title="Deletions">(-)</span>]]},
62 {"gpggood", [[<span class="q" title="GPG signature status
63
64G: Good (valid) signature
65B: Bad signature
66U: Good signature with unknown validity
67X: Good signature that has expired
68Y: Good signature made by an expired key
69R: Good signature made by a revoked key
70E: Signature can't be checked (e.g. missing key)
71N: No signature">GPG?</span>]]}
72 }
73 commits_table_data.rows = {}
74
75 local commits_head = git.log(repo_dir, branch.name, path, 1, 0, true)
76
77 for _, commit in pairs(commits_head) do
78 table.insert(commits_table_data.rows, {
79 utils.iso8601(commit.timestamp),
80 string.format([[<a href="/%s/commit/%s">%s</a>]], repo.name, commit.hash, commit.shorthash),
81 utils.html_sanitize(commit.subject),
82 string.format([[<a href="mailto:%s">%s</a>]], commit.email, utils.html_sanitize(commit.author)),
83 commit.diff.num,
84 commit.diff.plus,
85 commit.diff.minus,
86 commit.gpggood
87 })
88 end
89
90 t.body = t.body..tabulate(commits_table_data)
91
92 -- Tree/files table
93 t.body = t.body.."<h3>Tree"
94 if path == "" then
95 t.body = t.body.."</h3>"
96 else -- build path with hyperlinks for section header
97 local split = string.split(path, "/")
98 table.remove(split, #split)
99 local base = "/"..repo.name.."/tree/"..branch.name
100 t.body = t.body..string.format([[ @ <a href="%s">%s</a>]], base, repo.name)
101 local build = ""
102 for _, part in pairs(split) do
103 build = build.."/"..part
104 t.body = t.body..string.format([[ / <a href="%s%s">%s</a>]], base, build, part)
105 end
106 t.body = t.body.."</h3>"
107 end
108
109 local files_table_data = {}
110 files_table_data.class = "files"
111 files_table_data.headers = {
112 {"object", "Object"},
113 {"subject", "Latest Commit Subject"},
114 {"timestamp", "Time"},
115 {"shorthash", "Hash"}}
116 files_table_data.rows = {}
117
118 local files = git.list_tree(repo_dir, branch.name, path)
119
120 local file_icon = [[<img style="width:1em;height:1em;vertical-align:middle;margin-right:0.5em;" src="https://joshuas3.s3.amazonaws.com/svg/file.svg"/>]]
121 local folder_icon = [[<img style="width:1em;height:1em;vertical-align:middle;margin-right:0.5em;fill:#ffe9a2;" src="https://joshuas3.s3.amazonaws.com/svg/folder.svg"/>]]
122
123 -- .. directory
124 if path ~= "" then
125 local split = string.split(string.sub(path, 1, path:len() - 1), "/")
126 table.remove(split, #split)
127 if #split > 0 then -- deeper than 1 directory
128 table.insert(files_table_data.rows, {
129 string.format([[%s<a href="/%s/tree/%s/%s">..</a>]], folder_icon, repo.name, branch.name, table.concat(split, "/")),
130 "","",""
131 })
132 else -- only one directory deep
133 table.insert(files_table_data.rows, {
134 string.format([[%s<a href="/%s/tree/%s">..</a>]], folder_icon, repo.name, branch.name),
135 "","",""
136 })
137 end
138 end
139
140 -- Regular directories
141 for _, dir in pairs(files.dirs) do
142 local lastedit = git.log(repo_dir, branch.name.." -1", dir, 1, 0, false)[1]
143 local split = string.split(dir, "/")
144 local name = split[#split]
145 table.insert(files_table_data.rows, {
146 string.format([[%s<a href="/%s/tree/%s/%s">%s</a>]], folder_icon, repo.name, branch.name, dir, name),
147 utils.html_sanitize(lastedit.subject),
148 utils.iso8601(lastedit.timestamp),
149 string.format([[<a href="/%s/commit/%s">%s</a>]], repo.name, lastedit.hash, lastedit.shorthash)
150 })
151 end
152
153 -- Regular files
154 for _, file in pairs(files.files) do
155 local lastedit = git.log(repo_dir, branch.name.." -1", file, 1, 0, false)[1]
156 local split = string.split(file, "/")
157 local name = split[#split]
158 table.insert(files_table_data.rows, {
159 string.format([[%s<a href="/%s/blob/%s/%s">%s</a>]], file_icon, repo.name, branch.name, file, name),
160 utils.html_sanitize(lastedit.subject),
161 utils.iso8601(lastedit.timestamp),
162 string.format([[<a href="/%s/commit/%s">%s</a>]], repo.name, lastedit.hash, lastedit.shorthash)
163 })
164 end
165
166 t.body = t.body..tabulate(files_table_data)
167
168 -- Look for and render README if it exists
169 for _, file in pairs(files.files) do
170 local l = file:lower()
171 if l:match("^readme") then
172 t.body = t.body.."<h3>README</h3>"
173 local text = git.show_file(repo_dir, branch.name, path..file)
174 local s = file:len()
175 if string.sub(l, s-2, s) == ".md" then
176 t.body = t.body..[[<div class="markdown">]]..utils.markdown(text).."</div>"
177 else
178 t.body = t.body.."<pre><code>"..text.."</code></pre>"
179 end
180 break
181 end
182 end
183
184 return t
185end
186
187local refs = function(repo, repo_dir, branch)
188 local t = create_html_template()
189
190 -- Header with repository description and navigation links
191 t.body = t.body..string.format([[<h2><a href="/%s">%s</a> / <a href="/%s/refs">refs</a></h2>]], repo.name, repo.name, repo.name)
192 t.body = t.body.."<p>"..repo.description.."</p>"
193
194 local navlinks_list = {
195 "<a href=\"/"..repo.name.."/download\">Download</a>",
196 "<b><a href=\"/"..repo.name.."/refs\">Refs</a></b>",
197 "<a href=\"/"..repo.name.."/log/"..branch.name.."\">Commit Log</a>",
198 "<a href=\"/"..repo.name.."/tree/"..branch.name.."\">Files</a>"
199 }
200
201 for _, special in pairs(repo.specialfiles) do
202 local split = string.split(special, " ")
203 table.insert(navlinks_list, string.format([[<a href="/%s/blob/%s/%s">%s</a>]], repo.name, branch.name, split[2], split[1]))
204 end
205
206 t.body = t.body.."<p>"..table.concat(navlinks_list, " | ").."</p>"
207
208 local all_refs = git.list_refs(repo_dir)
209
210 -- Branches
211 if #all_refs.heads > 0 then
212 t.body = t.body.."<h3>Branches</h3>"
213
214 local branches_table_data = {}
215 branches_table_data.class = "branches"
216 branches_table_data.headers = {
217 {"name", "Name"},
218 {"ref", "Ref"},
219 {"has", "Hash"}
220 }
221 branches_table_data.rows = {}
222
223 for _, b in pairs(all_refs.heads) do
224 table.insert(branches_table_data.rows, {
225 b.name ~= branch.name and b.name or b.name.." <b>(HEAD)</b>",
226 string.format([[<a href="/%s/tree/%s">%s</a>]], repo.name, b.name, b.full),
227 string.format([[<a href="/%s/commit/%s">%s</a>]], repo.name, b.hash, b.shorthash)
228 })
229 end
230
231 t.body = t.body..tabulate(branches_table_data)
232 end
233
234 -- Tags
235 if #all_refs.tags > 0 then
236 t.body = t.body.."<h3>Tags</h3>"
237
238 local tags_table_data = {}
239 tags_table_data.class = "tags"
240 tags_table_data.headers = {
241 {"name", "Name"},
242 {"ref", "Ref"},
243 {"has", "Hash"}
244 }
245 tags_table_data.rows = {}
246 for _, t in pairs(all_refs.tags) do
247 table.insert(tags_table_data.rows, {
248 t.name ~= branch.name and t.name or t.name.." <b>(HEAD)</b>",
249 string.format([[<a href="/%s/tree/%s">%s</a>]], repo.name, t.name, t.full),
250 string.format([[<a href="/%s/commit/%s">%s</a>]], repo.name, t.hash, t.shorthash)
251 })
252 end
253
254 t.body = t.body..tabulate(tags_table_data)
255 end
256
257 return t
258end
259
260local log = function(repo, repo_dir, branch, n, skip)
261 n = n or 40
262 skip = skip or 0
263
264 local t = create_html_template()
265
266 -- Header with repository description and navigation links
267 t.body = t.body..string.format([[<h2><a href="/%s">%s</a> / <a href="/%s/tree/%s">%s</a> / <a href="/%s/log/%s">log</a></h2>]], repo.name, repo.name, repo.name, branch.name, branch.name, repo.name, branch.name)
268 t.body = t.body.."<p>"..repo.description.."</p>"
269
270 local navlinks_list = {
271 "<a href=\"/"..repo.name.."/download\">Download</a>",
272 "<a href=\"/"..repo.name.."/refs\">Refs</a>",
273 "<b><a href=\"/"..repo.name.."/log/"..branch.name.."\">Commit Log</a></b>",
274 "<a href=\"/"..repo.name.."/tree/"..branch.name.."\">Files</a>"
275 }
276
277 for _, special in pairs(repo.specialfiles) do
278 local split = string.split(special, " ")
279 table.insert(navlinks_list, string.format([[<a href="/%s/blob/%s/%s">%s</a>]], repo.name, branch.name, split[2], split[1]))
280 end
281
282 t.body = t.body.."<p>"..table.concat(navlinks_list, " | ").."</p>"
283
284 -- Latest Commit table
285 t.body = t.body.."<h3>Commits</h3>"
286
287 local commits_table_data = {}
288 commits_table_data.class = "log"
289 commits_table_data.headers = {
290 {"timestamp", "Time"},
291 {"shorthash", "Hash"},
292 {"subject", "Subject"},
293 {"author", "Author"},
294 {"changed_files", [[<span class="q" title="# of files changed">#</span>]]},
295 {"changed_plus", [[<span class="q" title="Insertions">(+)</span>]]},
296 {"changed_minus", [[<span class="q" title="Deletions">(-)</span>]]},
297 {"gpggood", [[<span class="q" title="GPG signature status
298
299G: Good (valid) signature
300B: Bad signature
301U: Good signature with unknown validity
302X: Good signature that has expired
303Y: Good signature made by an expired key
304R: Good signature made by a revoked key
305E: Signature can't be checked (e.g. missing key)
306N: No signature">GPG?</span>]]}
307 }
308 commits_table_data.rows = {}
309
310 local commits_head = git.log(repo_dir, branch.name, path, n, skip, true)
311
312 for _, commit in pairs(commits_head) do
313 table.insert(commits_table_data.rows, {
314 utils.iso8601(commit.timestamp),
315 string.format([[<a href="/%s/commit/%s">%s</a>]], repo.name, commit.hash, commit.shorthash),
316 utils.html_sanitize(commit.subject),
317 string.format([[<a href="mailto:%s">%s</a>]], commit.email, utils.html_sanitize(commit.author)),
318 commit.diff.num,
319 commit.diff.plus,
320 commit.diff.minus,
321 commit.gpggood
322 })
323 end
324
325 t.body = t.body..tabulate(commits_table_data)
326
327 return t
328end
329
330local download = function(repo, repo_dir, branch)
331 local t = create_html_template()
332
333 -- Header with repository description and navigation links
334 t.body = t.body..string.format([[<h2><a href="/%s">%s</a> / <a href="/%s/download">download</a></h2>]], repo.name, repo.name, repo.name)
335 t.body = t.body.."<p>"..repo.description.."</p>"
336
337 local navlinks_list = {
338 "<b><a href=\"/"..repo.name.."/download\">Download</a></b>",
339 "<a href=\"/"..repo.name.."/refs\">Refs</a>",
340 "<a href=\"/"..repo.name.."/log/"..branch.name.."\">Commit Log</a>",
341 "<a href=\"/"..repo.name.."/tree/"..branch.name.."\">Files</a>"
342 }
343
344 for _, special in pairs(repo.specialfiles) do
345 local split = string.split(special, " ")
346 table.insert(navlinks_list, string.format([[<a href="/%s/blob/%s/%s">%s</a>]], repo.name, branch.name, split[2], split[1]))
347 end
348
349 t.body = t.body.."<p>"..table.concat(navlinks_list, " | ").."</p>"
350
351 t.body = t.body.."<h3>Download URLs</h3>"
352
353 local urls = {}
354 urls.class = "download-urls"
355 urls.headers = {
356 {"protocol", "Protocol"},
357 {"url", "URL"}
358 }
359 urls.rows = {}
360
361 for _, url in pairs(repo.download) do
362 local split = string.split(url, " ")
363 table.insert(urls.rows, {split[1], string.format([[<a href="%s">%s</a>]], split[2], split[2])})
364 end
365
366 t.body = t.body..tabulate(urls)
367
368 t.body = t.body.."<h3>Websites</h3>"
369
370 local sites = {}
371 sites.class = "websites"
372 sites.headers = {
373 {"name", "Website"},
374 {"url", "URL"}
375 }
376 sites.rows = {}
377
378 for _, site in pairs(repo.urls) do
379 local split = string.split(site, " ")
380 table.insert(sites.rows, {split[1], string.format([[<a href="%s">%s</a>]], split[2], split[2])})
381 end
382
383 t.body = t.body..tabulate(sites)
384
385 return t
386end
387
388
389local _M = {}
390_M.tree = tree
391_M.refs = refs
392_M.log = log
393_M.download = download
394return _M
395