Index

resty-gitweb / c69d6c0

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
904 Jan 2021 11:39c69d6c0Reformat blob preview codeJosh Stockin14514G

Blob @ resty-gitweb / pages / blob.lua

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