Index

resty-gitweb / 1bf7bae

A git web interface for Lua/OpenResty (you're on it right now!)

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
1224 Jan 2021 17:05dda0daeNew HTML generatorJosh Stockin12022G

Blob @ resty-gitweb / pages / blob.lua

text/plain6967 bytesdownload raw
1-- resty-gitweb@pages/blob.lua
2-- File blob page builder
3
4-- Copyright (c) 2020 Joshua 'joshuas3' Stockin
5-- <https://git.joshstock.in/resty-gitweb>
6-- This software is licensed under the MIT License.
7
8local puremagic = require("puremagic")
9
10local utils = require("utils/utils")
11local git = require("git/git")
12
13local builder = require("utils/builder")
14local tabulate = require("utils/tabulate")
15local nav = require("utils/nav")
16
17local _M = function(repo, repo_dir, branch, file_path)
18
19 -- Pre checks
20 if file_path ~= "" then -- make sure path exists
21 local path_tree = git.list_tree(repo_dir, branch.name, file_path)
22 if #path_tree.files == 0 then -- no path found
23 error("file "..file_path.." is nonexistent")
24 end
25 else
26 error("file path is empty")
27 end
28
29 local build = builder:new()
30
31 -- Breadcrumb navigation and repository description
32 local breadcrumb_nav = {
33 {string.format("/%s", repo.name), repo.name},
34 {string.format("/%s/tree/%s", repo.name, branch.name), branch.name},
35 }
36 build{
37 build.h2{nav(breadcrumb_nav, " / ")},
38 build.p{repo.description}
39 }
40
41 -- Navigation links
42 local navlinks = {
43 {string.format("/%s/download", repo.name), "Download"},
44 {string.format("/%s/refs", repo.name), "Refs"},
45 {string.format("/%s/log/%s", repo.name, branch.name), "Commit Log"},
46 {string.format("/%s/tree/%s", repo.name, branch.name), "<b>Files</b>"}
47 }
48
49 for _, special in pairs(repo.specialfiles) do -- create nav items for special files
50 local split = string.split(special, " ")
51 table.insert(navlinks, {
52 string.format("/%s/blob/%s/%s", repo.name, branch.name, split[2]),
53 split[1]
54 })
55 end
56
57 build{
58 build.div{class="nav", nav(navlinks)},
59 build.h3{"Latest Commit"}
60 }
61
62 -- Latest Commit table
63 local commit = git.log(repo_dir, branch.name, file_path, 1, 0, true)[1]
64
65 local commits_table_data = {}
66 commits_table_data.class = "log"
67 commits_table_data.headers = {
68 {"count", [[<span class="q" title="Commit number/count">{#}</span>]]},
69 {"timestamp", "Time"},
70 {"shorthash", "Hash"},
71 {"subject", "Subject"},
72 {"author", "Author"},
73 {"changed_files", [[<span class="q" title="# of files changed">#</span>]]},
74 {"changed_plus", [[<span class="q" title="Insertions">(+)</span>]]},
75 {"changed_minus", [[<span class="q" title="Deletions">(-)</span>]]},
76 {"gpggood", [[<span class="q" title="GPG signature status
77
78G: Good (valid) signature
79B: Bad signature
80U: Good signature with unknown validity
81X: Good signature that has expired
82Y: Good signature made by an expired key
83R: Good signature made by a revoked key
84E: Signature can't be checked (e.g. missing key)
85N: No signature">GPG?</span>]]}
86 }
87 commits_table_data.rows = {}
88
89 table.insert(commits_table_data.rows, {
90 git.count(repo_dir, commit.hash),
91 utils.iso8601(commit.timestamp),
92 string.format([[<a href="/%s/commit/%s">%s</a>]], repo.name, commit.hash, commit.shorthash),
93 utils.html_sanitize(commit.subject),
94 string.format([[<a href="mailto:%s">%s</a>]], commit.email, utils.html_sanitize(commit.author)),
95 commit.diff.num,
96 commit.diff.plus,
97 commit.diff.minus,
98 commit.gpggood
99 })
100
101 build{tabulate(commits_table_data)}
102
103 -- Tree breadcrumb
104 local blobheader = build.h3{"Blob @ "}
105
106 local treelinks = {
107 {string.format("/%s/tree/%s", repo.name, branch.name), repo.name}
108 }
109
110 local base_path = treelinks[1][1] -- /repo/tree/branch
111
112 local path_string = ""
113 local path_split = string.split(file_path, "/")
114 local file_name = path_split[#path_split]
115 table.remove(path_split, #path_split)
116
117 for _, part in pairs(path_split) do
118 path_string = path_string.."/"..part
119 table.insert(treelinks, {base_path..path_string, part})
120 end
121 path_string = path_string.."/"..file_name
122 table.insert(treelinks, {
123 string.format("/%s/blob/%s"..path_string, repo.name, branch.name), file_name
124 })
125
126 blobheader{nav(treelinks, " / ")}
127 build{blobheader}
128
129 -- File
130 local success, repo_obj = git.repo.open(repo_dir)
131 local content, is_binary = git.read_blob(repo_obj, branch.name, file_path)
132 local content_size = string.len(content)
133 git.repo.free(repo_obj)
134
135 mimetype = puremagic.via_content(content, file_path)
136
137 local blob = build.div{class="blob"}
138 build{blob}
139
140 blob{
141 string.format(
142 [[<div class="blob header"><span>%s</span><span style="font-weight:normal">%d bytes</span><span style="float: right"><a href="/%s/raw/%s/%s">download raw</a></span></div>]],
143 mimetype,
144 content_size,
145 repo.name,
146 branch.name,
147 file_path
148 )
149 }
150
151 blob{[[<div class="blob table">]]}
152
153 local text_table = {}
154 text_table.headers = {}
155 text_table.rows = {}
156
157 if not is_binary then
158
159 text_table.class = "blob lines"
160
161 if content_size < 100000 then
162 for i, line in pairs(string.split(utils.highlight(content, file_name), "\n")) do
163 if line ~= "" then
164 local ftab = line:gsub("\t", " ")
165 table.insert(text_table.rows, {string.format([[<a id="L%d" href="#L%d">%d</a>]], i, i, i), ftab})
166 else
167 table.insert(text_table.rows, {string.format([[<a id="L%d" href="#L%d">%d</a>]], i, i, i), "\n"}) -- preserve newlines for copying/pasting
168 end
169 end
170 else
171 table.insert(text_table.rows, {1, [[----- can't preview content bigger than 100KB -----]]})
172 end
173
174 else
175
176 text_table.class = "blob binary"
177
178 if content_size < 15000000 then
179
180 if string.sub(mimetype, 1, 6) == "image/" then
181 table.insert(text_table.rows, {string.format([[<img src="/%s/raw/%s/%s">]], repo.name, branch.name, file_path)})
182
183 elseif string.sub(mimetype, 1, 6) == "video/" then
184 table.insert(text_table.rows, {string.format([[<video controls><source src="/%s/raw/%s/%s" type="%s"></audio>]], repo.name, branch.name, file_path, mimetype)})
185
186 elseif string.sub(mimetype, 1, 6) == "audio/" then
187 table.insert(text_table.rows, {string.format([[<audio controls><source src="/%s/raw/%s/%s" type="%s"></audio>]], repo.name, branch.name, file_path, mimetype)})
188
189 else
190 table.insert(text_table.rows, {[[----- can't preview binary content -----]]})
191 end
192
193 else
194 table.insert(text_table.rows, {[[----- can't preview binary content bigger than 15MB -----]]})
195 end
196
197 end
198
199 blob{tabulate(text_table)}
200
201 return build
202end
203
204return _M
205