1 package Gsg::Html;
2 use strict;
3 use warnings;
4 use Log::Log4perl qw(:easy);
5 use lib "/usr/local/lib";
6 use Shellex::Shellex qw(shellex findBin);
7 use Gsg::Gather qw(get_file_tree get_diff_stat);
8 use Gsg::MdParse qw (render_readme);
9 use Exporter qw(import);
10 our @EXPORT_OK = qw(
11 write_file append_file write_root_index clean_web_root
12 write_project_content
13 );
14
15 # These subs might belong in shellex
16 # Add logger for write opts TODO
17 sub write_file($$) {
18
19 my $content = shift;
20 my $path = shift;
21 open(my $fh, ">", $path) or die "Couldnt open $path\n";
22 print $fh "$content";
23 close $fh;
24
25 }
26
27 sub append_file($$) {
28 my $content = shift;
29 my $path = shift;
30 open(my $fh, ">>", $path) or die "Couldnt open $path\n";
31 print $fh "$content";
32 close $fh;
33 }
34
35 sub write_root_index($$$$) {
36
37 my $index = shift;
38 my $project_dirs_ref = shift;
39 my $web_projects_dir_path = shift;
40 my $logger = shift;
41 write_file("", $index);
42 append_file("<html><body><b>Git Projects</b><br><head><META NAME=\"ROBOTS\" CONTENT=\"NOINDEX, NOFOLLOW\"></head>\n",$index);
43 append_file("<small><i>Statically generated web root for browsing this git server</i></small><br>\n",$index);
44 my $date_cmd = findBin("date",$logger);
45 my $current_time = `$date_cmd`;
46 chomp $current_time;
47 append_file("<small><i>Generated at $current_time</i></small><br><hr/>\n",$index);
48
49 my $mkdirCmd = findBin("mkdir",$logger);
50 foreach my $project ( @$project_dirs_ref ) {
51 my $indexPath = $project . "index.html";
52 append_file("<table><div id=\"cotent\"><table id=\"index\"><tbody>\n",$index);
53 append_file("<tr><td><a href=\"projects/$indexPath\">$project</a></td>\n",$index);
54 shellex("$mkdirCmd -p $web_projects_dir_path$project",$logger);
55 }
56 append_file("</tr></tbody></table></div></body></html>\n",$index);
57 $logger->info("Wrote root index at $index");
58
59 }
60
61 sub check_for_html($) {
62
63 # Expects line from gen_line_nums or gen_diff_colors
64 # Will change < and > chars to < or >
65 # This adds tons of overhead, but should work better
66 # than <xmp> and will solve the rendering problems
67 my $line = shift;
68 my $new_line;
69 open my $fh, '>>', \$new_line or die "Can't open variable: $!";
70 foreach my $char ( split("",$line) ) {
71 if ( ! defined $char || $char eq "" ) {
72 print "Empty char\n";
73 next;
74 }
75
76 if ( $char eq "<" ) {
77 print $fh "<";
78 } elsif ( $char eq ">" ) {
79 print $fh ">";
80 } else {
81 print $fh "$char";
82 }
83 }
84
85 close $fh;
86 return $new_line;
87
88 }
89
90
91 sub gen_line_nums($$$) {
92
93 my $raw_file = shift;
94 my $filename = shift;
95 my $logger = shift;
96
97 my $html_file;
98 # Might be a better way to do this? TODO
99 open my $fh, '>>', \$html_file or die "Can't open variable: $!";
100
101 print $fh "<!DOCTYPE html><html><b>$filename</b><hr/><div id=\"content\"><pre id=\"blob\">";
102 my $line_counter = 1;
103 foreach my $line ( split("\n", $raw_file) ) {
104 if ( $line ne "" ) {
105 $line = check_for_html($line);
106 }
107 print $fh "<a href=\"\#l$line_counter\" class=\"line\" id=\"l$line_counter\">$line_counter</a>\t$line<br>";
108 $line_counter++;
109 }
110
111 print $fh "</pre></div><html>";
112 close $fh;
113
114 $logger->info("Generated line numbers for $filename");
115 return $html_file;
116
117 }
118
119 sub gen_raw_html($$$) {
120
121 my $raw_file = shift;
122 my $filename = shift;
123 my $logger = shift;
124
125 my $html_file;
126 # Might be a better way to do this? TODO
127 open my $fh, '>>', \$html_file or die "Can't open variable: $!";
128
129 print $fh "<!DOCTYPE html><html><div id=\"content\"><pre id=\"blob\">";
130 foreach my $line ( split("\n", $raw_file) ) {
131 if ($line ne "") {
132 $line = check_for_html($line);
133 }
134 print $fh "$line<br>";
135 }
136
137 print $fh "</pre></div><html>";
138 close $fh;
139
140 $logger->info("Generated HTML file for $filename");
141 return $html_file;
142
143 }
144
145 sub gen_diff_colors($$$) {
146
147 my $raw_diff = shift;
148 my $id = shift;
149 my $logger = shift;
150
151 my $html_diff;
152 open my $fh, '>>', \$html_diff or die "Can't open variable: $!";
153
154 #print $fh "<!DOCTYPE html><html><div id=\"content\"><pre id=\"blob\">";
155 print $fh "<!DOCTYPE html><html><b>$id</b><hr/><div id=\"content\"><pre>";
156 my $line_counter = 1;
157 foreach my $line ( split("\n", $raw_diff) ) {
158 if ( $line ne "" ) {
159 $line = check_for_html($line);
160 }
161
162 if ( $line =~ m/^\+/ ) {
163 print $fh "<font color=\"green\">$line</font><br>";
164 } elsif ( $line =~ m/^\-/ ) {
165 print $fh "<font color=\"red\">$line</font><br>";
166 } elsif ( $line =~ m/^@@/ ) {
167 print $fh "<font color=\"blue\">$line</font><br>";
168 } elsif ( $line =~ m/^diff/ ) {
169 print $fh "<font color=\"purple\">$line</font><br>";
170 } elsif ( $line =~ m/^commit/ ) {
171 print $fh "<font color=\"purple\"><b>$line</b></font><br>";
172 } else {
173 print $fh "$line<br>";
174 }
175 $line_counter++;
176 }
177
178 print $fh "</pre></div></html>";
179 close $fh;
180
181 $logger->info("Generated colored diff for $id");
182 return $html_diff;
183
184 }
185
186 # Main sub for generating project page
187 # Might make more sense to split into more subs?
188 sub write_project_content($$$$$) {
189
190 my $project_dirs_ref = shift;
191 my $trimmed_project_dirs_ref = shift;
192 my $web_projects_dir = shift;
193 my $clone_path = shift;
194 my $logger = shift;
195
196 # Make these array's easier to work with in a hash
197 # Key is path to actual git dir, val is path to associated web dir
198 my %projects_map;
199 @projects_map{@$project_dirs_ref} = @$trimmed_project_dirs_ref;
200 $logger->info("Assembling data structures of git info");
201
202 # Write files part of project index
203 foreach my $project_path ( keys %projects_map ) {
204 my $spec_web_dir = $web_projects_dir . $projects_map{$project_path};
205 my $project_index = $spec_web_dir . "index.html";
206 write_file("",$project_index);
207 append_file("<html><a href=\"../../index.html\"><b>Return to index</a></b><br>",$project_index);
208 my $uniq_clone_path = "Disabled";
209 if ( $clone_path ne "Disabled" ) {
210 $uniq_clone_path = $clone_path . $projects_map{$project_path};
211 }
212 append_file("<b><pre>Clone:<font color=\"green\"> git clone $uniq_clone_path</font></pre></b><hr/>",$project_index);
213 # Get all project data structures/info
214 my ( $file_tree_ref, $file_content_ref, $commits_ref, $commit_ids_ref ) = get_file_tree($project_path,$logger);
215
216 # Handle README
217 if ( grep /^README.md$/, keys %$file_content_ref ) {
218 $logger->info("$projects_map{$project_path} contains a README");
219 my $readme_html = render_readme(${$file_content_ref}{'README.md'},$logger);
220 append_file("$readme_html",$project_index);
221
222 }
223
224 append_file("<b>Files for $projects_map{$project_path}</b><br>",$project_index);
225 append_file("<hr/>",$project_index);
226 ## Write files ##
227 append_file("<table><div id=\"cotent\"><table id=\"index\"><thead><tr><td><b>File</b></td><td><b>Commit</b></td><td><b>Raw</b></td></tr></thead><tbody>",$project_index);
228 foreach my $filename ( sort keys %$file_content_ref ) {
229 my $browserCompat = $filename . ".html";
230 my $browserCompatRaw = $filename . "_raw" . ".html";
231 # Rewrite dir paths so we can save on disk without producing actual dir structure
232 if ( $filename =~ m/\// ) {
233 my $copy = $filename;
234 $copy =~ s/\//_/g;
235 $browserCompat = $copy . ".html";
236 $browserCompatRaw = $copy . "_raw" . ".html";
237 }
238 append_file("<tr><td><a href=\"$browserCompat\">$filename</a></td><td>${$file_tree_ref}{$filename}</td><td><a href=\"$browserCompatRaw\">raw</a></td>",$project_index);
239 my $html_file = gen_line_nums(${$file_content_ref}{$filename},$filename,$logger);
240 write_file("$html_file",$spec_web_dir . $browserCompat);
241 my $raw_html_file = gen_raw_html(${$file_content_ref}{$filename},$filename,$logger);
242 write_file("$raw_html_file",$spec_web_dir . $browserCompatRaw);
243 }
244
245 append_file("</tr></tbody></table></div></body>",$project_index);
246 append_file("<br>", $project_index);
247
248 append_file("<html><b>Logs for $projects_map{$project_path}</b><br>",$project_index);
249 append_file("<table><div id=\"cotent\"><table id=\"index\"><tbody>",$project_index);
250 append_file("<hr/>",$project_index);
251
252 # iterate over array to keep ordering
253 foreach my $commit_id ( @$commit_ids_ref ) {
254 my $filename = $commit_id . ".html";
255 append_file("<tr><td><a href=\"$filename\">$filename</a></td>",$project_index);
256 my $html_diff = gen_diff_colors(${$commits_ref}{$commit_id},$commit_id,$logger);
257 write_file($html_diff,$spec_web_dir . $filename);
258 }
259 append_file("</tr></tbody></table></div></body>",$project_index);
260 append_file("</html>",$project_index);
261
262 }
263
264 $logger->info("Done writing files");
265
266 }
267
268 # Not used currently, need to do more trimming/etc
269 # TODO
270 # Work around is rm -rf the webroot manually and then just rerun gsg
271 sub clean_web_root($$$) {
272
273 my $web_projects_dir_path = shift;
274 my $git_projects_ref = shift;
275 my $logger = shift;
276 my $lsCmd = findBin("ls",$logger);
277 my $rmCmd = findBin("rm",$logger);
278
279 foreach my $dir ( split("\n", shellex("$lsCmd -d $web_projects_dir_path/*/",$logger)) ) {
280 if ( ! grep( /^$dir$/, @$git_projects_ref ) ) {
281 $logger->info("Found $dir in webroot but not in git root, removing...");
282 my $rmdir = $web_projects_dir_path . $dir;
283 $logger->info("Would remove $rmdir");
284 # Does this need to be safer? TODO
285 #shellex("$rmCmd $rmdir/*",$logger);
286 #shellex("$rmCmd -d $rmdir",$logger);
287 }
288 }
289
290 }
291
292
293 1;