Added first cut of /shortlog action and simplified the parse_rev_list() method.
[catagits/Gitalist.git] / lib / Gitalist / Controller / Root.pm
CommitLineData
89de6a33 1package Gitalist::Controller::Root;
42fe5d11 2use Moose;
3use namespace::autoclean;
89de6a33 4
42fe5d11 5BEGIN { extends 'Catalyst::Controller' }
89de6a33 6
7#
8# Sets the actions in this controller to be registered with no prefix
9# so they function identically to actions created in MyApp.pm
10#
11__PACKAGE__->config->{namespace} = '';
12
13=head1 NAME
14
15Gitalist::Controller::Root - Root Controller for Gitalist
16
17=head1 DESCRIPTION
18
19[enter your description here]
20
21=head1 METHODS
22
23=cut
24
25=head2 index
26
27=cut
28
89de6a33 29use IO::Capture::Stdout;
d3feefcf 30
1625cd5f 31=head2 run_gitweb
32
33The main shim around C<gitweb.pm>.
34
35=cut
36
e66db0fb 37sub run_gitweb {
86382b95 38 my ( $self, $c ) = @_;
39
e66db0fb 40 # XXX A slippery slope to be sure.
41 if($c->req->param('a')) {
42 my $capture = IO::Capture::Stdout->new();
43 $capture->start();
44 eval {
45 my $action = gitweb::main($c);
46 $action->();
47 };
48 $capture->stop();
49
50 use Data::Dumper;
51 die Dumper($@)
52 if $@;
53
54 my $output = join '', $capture->read;
55 $c->stash->{gitweb_output} = $output;
56 $c->stash->{template} = 'gitweb.tt2';
57 }
86382b95 58}
59
1625cd5f 60=head2 index
61
62Provides the project listing.
63
64=cut
65
86382b95 66sub index :Path :Args(0) {
d5cc37a4 67 my ( $self, $c ) = @_;
86382b95 68
e66db0fb 69 # Leave actions up to gitweb at this point.
70 return $self->run_gitweb($c)
71 if $c->req->param('a');
72
d5cc37a4 73 my $list = $c->model('Git')->list_projects;
74 unless(@$list) {
86382b95 75 die "No projects found";
76 }
77
d5cc37a4 78 $c->stash(
79 searchtext => $c->req->param('searchtext') || '',
80 projects => $list,
81 action => 'index',
82 );
04d1d917 83}
84
1625cd5f 85=head2 blob
86
87The blob action i.e the contents of a file.
88
89=cut
90
295c9703 91sub blob : Local {
92 my ( $self, $c ) = @_;
93
c8870bd3 94 my $h = $c->req->param('h')
95 || $c->model('Git')->hash_by_path($c->req->param('f'))
96 || die "No file or sha1 provided.";
97 my $hb = $c->req->param('hb')
98 || $c->model('Git')->head_hash
99 || die "Couldn't discern the corresponding head.";
100
295c9703 101 $c->stash(
c8870bd3 102 blob => $c->model('Git')->get_object($h)->content,
103 head => $c->model('Git')->get_object($hb),
104 filename => $c->req->param('f') || '',
105 action => 'blob',
295c9703 106 );
7e54e579 107
c8870bd3 108 $c->forward('View::SyntaxHighlight');
295c9703 109}
110
1625cd5f 111=head2 reflog
112
113Expose the local reflog. This may go away.
114
115=cut
116
e516cf80 117sub reflog : Local {
118 my ( $self, $c ) = @_;
119
c5065c66 120 my @log = $c->model('Git')->reflog(
c5065c66 121 '--since=yesterday'
e516cf80 122 );
c5065c66 123
e516cf80 124 $c->stash(
c5065c66 125 log => \@log,
e516cf80 126 action => 'reflog',
127 );
128}
129
1625cd5f 130=head2 commit
131
47495599 132Exposes a given commit.
1625cd5f 133
134=cut
135
1feb3d6b 136sub commit : Local {
d7c9a32f 137 my ( $self, $c ) = @_;
b7aca93a 138
139 $c->stash(
1ef8dc7d 140 commit => $c->model('Git')->get_object($c->req->param('h')),
b7aca93a 141 action => 'commit',
142 );
d7c9a32f 143}
144
47495599 145=head2 shortlog
146
147Expose an abbreviated log of a given sha1.
148
149=cut
150
151sub shortlog : Local {
152 my ( $self, $c ) = @_;
153
154 $c->stash(
155 commit => $c->model('Git')->get_object($c->req->param('h')),
156 action => 'shortlog',
157 );
158}
159
1625cd5f 160=head2 auto
161
162Populate the header and footer. Perhaps not the best location.
163
164=cut
165
04d1d917 166sub auto : Private {
167 my($self, $c) = @_;
168
1ef8dc7d 169 # XXX Temp hack until a decent solution is invented.
170 $c->model('Git')->project($c->req->param('p'))
171 if $c->req->param('p');
172
04d1d917 173 # Yes, this is hideous.
174 $self->header($c);
175 $self->footer($c);
176}
177
c5065c66 178# XXX This could probably be dropped altogether.
04d1d917 179use Gitalist::Util qw(to_utf8);
04d1d917 180# Formally git_header_html
181sub header {
182 my($self, $c) = @_;
183
c5065c66 184 my $title = $c->config->{sitename};
04d1d917 185
186 my $project = $c->req->param('project') || $c->req->param('p');
187 my $action = $c->req->param('action') || $c->req->param('a');
188 my $file_name = $c->req->param('filename') || $c->req->param('f');
c5065c66 189 if(defined $project) {
190 $title .= " - " . to_utf8($project);
191 if (defined $action) {
192 $title .= "/$action";
193 if (defined $file_name) {
194 $title .= " - " . $file_name;
195 if ($action eq "tree" && $file_name !~ m|/$|) {
196 $title .= "/";
197 }
198 }
199 }
200 }
201
202 $c->stash->{version} = $c->config->{version};
203 $c->stash->{git_version} = $c->model('Git')->run_cmd('--version');
204 $c->stash->{title} = $title;
04d1d917 205
206 #$c->stash->{baseurl} = $ENV{PATH_INFO} && uri_escape($base_url);
c5065c66 207 $c->stash->{stylesheet} = $c->config->{stylesheet} || 'gitweb.css';
04d1d917 208
c5065c66 209 $c->stash->{project} = $project;
04d1d917 210 my @links;
c5065c66 211 if($project) {
212 my %href_params = $self->feed_info($c);
213 $href_params{'-title'} ||= 'log';
04d1d917 214
c5065c66 215 foreach my $format qw(RSS Atom) {
216 my $type = lc($format);
04d1d917 217 push @links, {
c5065c66 218 rel => 'alternate',
219 title => "$project - $href_params{'-title'} - $format feed",
220
221 # XXX A bit hacky and could do with using gitweb::href() features
222 href => "?a=$type;p=$project",
223 type => "application/$type+xml"
04d1d917 224 }, {
c5065c66 225 rel => 'alternate',
226
227 # XXX This duplication also feels a bit awkward
228 title => "$project - $href_params{'-title'} - $format feed (no merges)",
229 href => "?a=$type;p=$project;opt=--no-merges",
230 type => "application/$type+xml"
04d1d917 231 };
c5065c66 232 }
233 } else {
04d1d917 234 push @links, {
c5065c66 235 rel => "alternate",
236 title => $c->config->{sitename}." projects list",
237 href => '?a=project_index',
238 type => "text/plain; charset=utf-8"
239 }, {
240 rel => "alternate",
241 title => $c->config->{sitename}." projects feeds",
242 href => '?a=opml',
243 type => "text/plain; charset=utf-8"
244 };
245 }
04d1d917 246
c5065c66 247 $c->stash->{favicon} = $c->config->{favicon};
04d1d917 248
c5065c66 249 # </head><body>
86382b95 250
c5065c66 251 $c->stash(
252 logo_url => $c->config->{logo_url},
253 logo_label => $c->config->{logo_label},
254 logo_img => $c->config->{logo},
255 home_link => $c->config->{home_link},
04d1d917 256 home_link_str => $c->config->{home_link_str},
c5065c66 257 );
04d1d917 258
c5065c66 259 if(defined $project) {
04d1d917 260 $c->stash(
295c9703 261 search_text => ( $c->req->param('s') || $c->req->param('searchtext') ),
262 search_hash => ( $c->req->param('hb') || $c->req->param('hashbase')
c5065c66 263 || $c->req->param('h') || $c->req->param('hash')
264 || 'HEAD' ),
265 );
266 }
04d1d917 267}
268
269# Formally git_footer_html
270sub footer {
271 my($self, $c) = @_;
272
c5065c66 273 my $feed_class = 'rss_logo';
04d1d917 274
275 my @feeds;
276 my $project = $c->req->param('project') || $c->req->param('p');
c5065c66 277 if(defined $project) {
d5cc37a4 278 (my $pstr = $project) =~ s[/?\.git$][];
c5065c66 279 my $descr = $c->model('Git')->project_info($project)->{description};
280 $c->stash->{project_description} = defined $descr
281 ? $descr
282 : '';
04d1d917 283
c5065c66 284 my %href_params = $self->feed_info($c);
285 if (!%href_params) {
286 $feed_class .= ' generic';
287 }
288 $href_params{'-title'} ||= 'log';
04d1d917 289
290 @feeds = [
291 map +{
292 class => $feed_class,
293 title => "$href_params{'-title'} $_ feed",
294 href => "/?p=$project;a=\L$_",
295 name => lc $_,
c5065c66 296 }, qw(RSS Atom)
297 ];
298 } else {
04d1d917 299 @feeds = [
300 map {
301 class => $feed_class,
c5065c66 302 title => '',
303 href => "/?a=$_->[0]",
304 name => $_->[1],
305 }, [opml=>'OPML'],[project_index=>'TXT'],
306 ];
307 }
89de6a33 308}
309
04d1d917 310# XXX This feels wrong here, should probably be refactored.
311# returns hash to be passed to href to generate gitweb URL
312# in -title key it returns description of link
313sub feed_info {
314 my($self, $c) = @_;
315
c5065c66 316 my $format = shift || 'Atom';
317 my %res = (action => lc($format));
04d1d917 318
c5065c66 319 # feed links are possible only for project views
320 return unless $c->req->param('project');
04d1d917 321
c5065c66 322 # some views should link to OPML, or to generic project feed,
323 # or don't have specific feed yet (so they should use generic)
324 return if $c->req->param('action') =~ /^(?:tags|heads|forks|tag|search)$/x;
325
326 my $branch;
04d1d917 327 my $hash = $c->req->param('h') || $c->req->param('hash');
328 my $hash_base = $c->req->param('hb') || $c->req->param('hashbase');
c5065c66 329
330 # branches refs uses 'refs/heads/' prefix (fullname) to differentiate
331 # from tag links; this also makes possible to detect branch links
332 if ((defined $hash_base && $hash_base =~ m!^refs/heads/(.*)$!) ||
333 (defined $hash && $hash =~ m!^refs/heads/(.*)$!)) {
334 $branch = $1;
335 }
336
337 # find log type for feed description (title)
338 my $type = 'log';
04d1d917 339 my $file_name = $c->req->param('f') || $c->req->param('filename');
c5065c66 340 if (defined $file_name) {
341 $type = "history of $file_name";
342 $type .= "/" if $c->req->param('action') eq 'tree';
343 $type .= " on '$branch'" if (defined $branch);
344 } else {
345 $type = "log of $branch" if (defined $branch);
346 }
347
348 $res{-title} = $type;
349 $res{'hash'} = (defined $branch ? "refs/heads/$branch" : undef);
350 $res{'file_name'} = $file_name;
351
352 return %res;
04d1d917 353}
89de6a33 354=head2 end
355
356Attempt to render a view, if needed.
357
358=cut
359
360sub end : ActionClass('RenderView') {}
361
362=head1 AUTHOR
363
364Dan Brook,,,
365
366=head1 LICENSE
367
368This library is free software. You can redistribute it and/or modify
369it under the same terms as Perl itself.
370
371=cut
372
42fe5d11 373__PACKAGE__->meta->make_immutable;