Add per line link for previous version of a line in blame.
[catagits/Gitalist.git] / lib / Gitalist / Controller / Root.pm
CommitLineData
89de6a33 1package Gitalist::Controller::Root;
89de6a33 2
c113db92 3use Moose;
cce8c9b6 4use Moose::Autobox;
f796a861 5use Sys::Hostname ();
d17ce39c 6use XML::Atom::Feed;
7use XML::Atom::Entry;
f796a861 8use XML::RSS;
286cea09 9use XML::OPML::SimpleGen;
d17ce39c 10
c113db92 11use Gitalist::Utils qw/ age_string /;
89de6a33 12
c113db92 13use namespace::autoclean;
89de6a33 14
c113db92 15BEGIN { extends 'Catalyst::Controller' }
89de6a33 16
c113db92 17__PACKAGE__->config->{namespace} = '';
89de6a33 18
c113db92 19sub root : Chained('/') PathPart('') CaptureArgs(0) {}
89de6a33 20
832cbc81 21sub _get_object {
b4b4d0fd 22 my($self, $c, $haveh) = @_;
9dc3b9a5 23
c1f608c8 24 my $h = $haveh || $c->req->param('h') || '';
0ee97fec 25 my $f = $c->req->param('f');
8dbe8024 26
87581f05 27 my $m = $c->stash->{Repository};
1aad4e81 28 my $pd = $m->path;
0ee97fec 29
9dc3b9a5 30 # Either use the provided h(ash) parameter, the f(ile) parameter or just use HEAD.
a7cc1ede 31 my $hash = ($h =~ /[^a-f0-9]/ ? $m->head_hash($h) : $h)
32 || ($f && $m->hash_by_path($f))
33 || $m->head_hash
9dc3b9a5 34 # XXX This could definitely use more context.
35 || Carp::croak("Couldn't find a hash for the commit object!");
36
c046a52f 37 my $obj = $m->get_object($hash)
38 or Carp::croak("Couldn't find a object for '$hash' in '$pd'!");
9dc3b9a5 39
c046a52f 40 return $obj;
9dc3b9a5 41}
42
5bb401c6 43sub index : Chained('base') PathPart('') Args(0) {
7bc165b3 44 my ( $self, $c ) = @_;
45
46 $c->detach($c->req->param('a'))
47 if $c->req->param('a');
48
82bc0f05 49 my @list = @{ $c->model()->repositories };
50 die 'No repositories found in '. $c->model->repo_dir
7bc165b3 51 unless @list;
52
53 my $search = $c->req->param('s') || '';
54 if($search) {
55 @list = grep {
56 index($_->name, $search) > -1
57 or ( $_->description !~ /^Unnamed repository/ and index($_->description, $search) > -1 )
58 } @list
59 }
60
61 $c->stash(
62 search_text => $search,
82bc0f05 63 repositories => \@list,
7bc165b3 64 action => 'index',
65 );
04d1d917 66}
67
5bb401c6 68# FIXME - WTF is this for?
82bc0f05 69sub repository_index : Chained('base') Args(0) {
38a4951e 70 my ( $self, $c ) = @_;
71
82bc0f05 72 my @list = @{ $c->model()->repositories };
73 die 'No repositories found in '. $c->model->repo_dir
38a4951e 74 unless @list;
75
76 $c->response->content_type('text/plain');
77 $c->response->body(
78 join "\n", map $_->name, @list
79 );
80 $c->response->status(200);
81}
82bc0f05 82# FIXME - maintain compatibility with previous URI
83sub project_index : Chained('base') Args(0) {
84 my ( $self, $c) = @_;
85 $c->detach('repository_index');
86}
38a4951e 87
790ce598 88=head2 summary
89
90A summary of what's happening in the repo.
91
92=cut
93
5bb401c6 94sub summary : Chained('base') Args(0) {
790ce598 95 my ( $self, $c ) = @_;
82bc0f05 96 my $repository = $c->stash->{Repository};
97 $c->detach('error_404') unless $repository;
832cbc81 98 my $commit = $self->_get_object($c);
82bc0f05 99 my @heads = @{$repository->heads};
06281e11 100 my $maxitems = Gitalist->config->{paging}{summary} || 10;
790ce598 101 $c->stash(
102 commit => $commit,
82bc0f05 103 log_lines => [$repository->list_revs(
4111e151 104 sha1 => $commit->sha1,
06281e11 105 count => $maxitems,
fde5091f 106 )],
82bc0f05 107 refs => $repository->references,
0969b411 108 heads => [ @heads[0 .. ($#heads < $maxitems ? $#heads : $maxitems)] ],
790ce598 109 action => 'summary',
06281e11 110 );
790ce598 111}
112
113=head2 heads
114
115The current list of heads (aka branches) in the repo.
116
117=cut
118
5bb401c6 119sub heads : Chained('base') Args(0) {
790ce598 120 my ( $self, $c ) = @_;
82bc0f05 121 my $repository = $c->stash->{Repository};
790ce598 122 $c->stash(
832cbc81 123 commit => $self->_get_object($c),
82bc0f05 124 heads => $repository->heads,
790ce598 125 action => 'heads',
126 );
127}
128
ea19a20c 129=head2 tags
130
131The current list of tags in the repo.
132
133=cut
134
5bb401c6 135sub tags : Chained('base') Args(0) {
ea19a20c 136 my ( $self, $c ) = @_;
82bc0f05 137 my $repository = $c->stash->{Repository};
ea19a20c 138 $c->stash(
139 commit => $self->_get_object($c),
82bc0f05 140 tags => $repository->tags,
ea19a20c 141 action => 'tags',
142 );
143}
144
5bb401c6 145sub blame : Chained('base') Args(0) {
18a8059a 146 my($self, $c) = @_;
147
82bc0f05 148 my $repository = $c->stash->{Repository};
18a8059a 149 my $h = $c->req->param('h')
82bc0f05 150 || $repository->hash_by_path($c->req->param('hb'), $c->req->param('f'))
18a8059a 151 || die "No file or sha1 provided.";
152 my $hb = $c->req->param('hb')
82bc0f05 153 || $repository->head_hash
18a8059a 154 || die "Couldn't discern the corresponding head.";
155 my $filename = $c->req->param('f') || '';
156
61f14672 157 my $blame = $repository->get_object($hb)->blame($filename, $h);
18a8059a 158 $c->stash(
6bd8b34b 159 blame => $blame,
82bc0f05 160 head => $repository->get_object($hb),
18a8059a 161 filename => $filename,
6bd8b34b 162
163 # XXX Hack hack hack, see View::SyntaxHighlight
164 language => ($filename =~ /\.p[lm]$/i ? 'Perl' : ''),
165 blob => join("\n", map $_->{line}, @$blame),
18a8059a 166 );
6bd8b34b 167
168 $c->forward('View::SyntaxHighlight')
169 unless $c->stash->{no_wrapper};
18a8059a 170}
171
b5f3d3e7 172sub _blob_objs {
295c9703 173 my ( $self, $c ) = @_;
82bc0f05 174 my $repository = $c->stash->{Repository};
c8870bd3 175 my $h = $c->req->param('h')
82bc0f05 176 || $repository->hash_by_path($c->req->param('hb'), $c->req->param('f'))
c8870bd3 177 || die "No file or sha1 provided.";
178 my $hb = $c->req->param('hb')
82bc0f05 179 || $repository->head_hash
c8870bd3 180 || die "Couldn't discern the corresponding head.";
181
f5da8e7a 182 my $filename = $c->req->param('f') || '';
183
82bc0f05 184 my $blob = $repository->get_object($h);
185 $blob = $repository->get_object(
186 $repository->hash_by_path($h || $hb, $filename)
b5f3d3e7 187 ) if $blob->type ne 'blob';
188
82bc0f05 189 return $blob, $repository->get_object($hb), $filename;
b5f3d3e7 190}
191
192=head2 blob
193
194The blob action i.e the contents of a file.
195
196=cut
197
5bb401c6 198sub blob : Chained('base') Args(0) {
b5f3d3e7 199 my ( $self, $c ) = @_;
200
201 my($blob, $head, $filename) = $self->_blob_objs($c);
295c9703 202 $c->stash(
b5f3d3e7 203 blob => $blob->content,
204 head => $head,
f5da8e7a 205 filename => $filename,
206 # XXX Hack hack hack, see View::SyntaxHighlight
6bd8b34b 207 language => ($filename =~ /\.p[lm]$/i ? 'Perl' : ''),
c8870bd3 208 action => 'blob',
295c9703 209 );
7e54e579 210
c8a42dd5 211 $c->forward('View::SyntaxHighlight')
212 unless $c->stash->{no_wrapper};
213}
214
b5f3d3e7 215=head2 blob_plain
216
217The plain text version of blob, where file is rendered as is.
218
219=cut
220
5bb401c6 221sub blob_plain : Chained('base') Args(0) {
c8a42dd5 222 my($self, $c) = @_;
223
b5f3d3e7 224 my($blob) = $self->_blob_objs($c);
c8a42dd5 225 $c->response->content_type('text/plain; charset=utf-8');
b5f3d3e7 226 $c->response->body($blob->content);
227 $c->response->status(200);
c8a42dd5 228}
229
b5f3d3e7 230=head2 blobdiff_plain
231
232The plain text version of blobdiff.
233
234=cut
235
5bb401c6 236sub blobdiff_plain : Chained('base') Args(0) {
c8a42dd5 237 my($self, $c) = @_;
238
239 $c->stash(no_wrapper => 1);
240 $c->response->content_type('text/plain; charset=utf-8');
241
242 $c->forward('blobdiff');
295c9703 243}
244
6cf4366a 245=head2 blobdiff
246
247Exposes a given diff of a blob.
248
249=cut
250
5bb401c6 251sub blobdiff : Chained('base') Args(0) {
6cf4366a 252 my ( $self, $c ) = @_;
832cbc81 253 my $commit = $self->_get_object($c, $c->req->param('hb'));
6cf4366a 254 my $filename = $c->req->param('f')
255 || croak("No file specified!");
87581f05 256 my($tree, $patch) = $c->stash->{Repository}->diff(
ad8884fc 257 commit => $commit,
ad8884fc 258 patch => 1,
c8a42dd5 259 parent => $c->req->param('hpb') || undef,
260 file => $filename,
6cf4366a 261 );
262 $c->stash(
263 commit => $commit,
ad8884fc 264 diff => $patch,
592b68ef 265 filename => $filename,
6cf4366a 266 # XXX Hack hack hack, see View::SyntaxHighlight
ad8884fc 267 blobs => [$patch->[0]->{diff}],
6cf4366a 268 language => 'Diff',
269 action => 'blobdiff',
270 );
271
c8a42dd5 272 $c->forward('View::SyntaxHighlight')
273 unless $c->stash->{no_wrapper};
6cf4366a 274}
275
1625cd5f 276=head2 commit
277
47495599 278Exposes a given commit.
1625cd5f 279
280=cut
281
5bb401c6 282sub commit : Chained('base') Args(0) {
d7c9a32f 283 my ( $self, $c ) = @_;
82bc0f05 284 my $repository = $c->stash->{Repository};
832cbc81 285 my $commit = $self->_get_object($c);
b7aca93a 286 $c->stash(
790ce598 287 commit => $commit,
82bc0f05 288 diff_tree => ($repository->diff(commit => $commit))[0],
289 refs => $repository->references,
790ce598 290 action => 'commit',
b7aca93a 291 );
d7c9a32f 292}
293
2247133f 294=head2 commitdiff
295
296Exposes a given diff of a commit.
297
298=cut
299
5bb401c6 300sub commitdiff : Chained('base') Args(0) {
2247133f 301 my ( $self, $c ) = @_;
832cbc81 302 my $commit = $self->_get_object($c);
87581f05 303 my($tree, $patch) = $c->stash->{Repository}->diff(
ad8884fc 304 commit => $commit,
fc948aee 305 parent => $c->req->param('hp') || undef,
ad8884fc 306 patch => 1,
307 );
2247133f 308 $c->stash(
f5da8e7a 309 commit => $commit,
ad8884fc 310 diff_tree => $tree,
311 diff => $patch,
f5da8e7a 312 # XXX Hack hack hack, see View::SyntaxHighlight
ad8884fc 313 blobs => [map $_->{diff}, @$patch],
f5da8e7a 314 language => 'Diff',
315 action => 'commitdiff',
2247133f 316 );
f5da8e7a 317
c8a42dd5 318 $c->forward('View::SyntaxHighlight')
319 unless $c->stash->{no_wrapper};
320}
321
5bb401c6 322sub commitdiff_plain : Chained('base') Args(0) {
c8a42dd5 323 my($self, $c) = @_;
324
325 $c->stash(no_wrapper => 1);
326 $c->response->content_type('text/plain; charset=utf-8');
327
328 $c->forward('commitdiff');
2247133f 329}
330
47495599 331=head2 shortlog
332
333Expose an abbreviated log of a given sha1.
334
335=cut
336
5bb401c6 337sub shortlog : Chained('base') Args(0) {
47495599 338 my ( $self, $c ) = @_;
592b68ef 339
82bc0f05 340 my $repository = $c->stash->{Repository};
c046a52f 341 my $commit = $self->_get_object($c, $c->req->param('hb'));
592b68ef 342 my $filename = $c->req->param('f') || '';
343
fde5091f 344 my %logargs = (
345 sha1 => $commit->sha1,
f740f8a9 346 count => Gitalist->config->{paging}{log} || 25,
592b68ef 347 ($filename ? (file => $filename) : ())
63e220b9 348 );
fde5091f 349
350 my $page = $c->req->param('pg') || 0;
351 $logargs{skip} = $c->req->param('pg') * $logargs{count}
352 if $c->req->param('pg');
353
47495599 354 $c->stash(
790ce598 355 commit => $commit,
82bc0f05 356 log_lines => [$repository->list_revs(%logargs)],
357 refs => $repository->references,
b4b4d0fd 358 page => $page,
592b68ef 359 filename => $filename,
360 action => 'shortlog',
47495599 361 );
362}
363
790ce598 364=head2 log
365
366Calls shortlog internally. Perhaps that should be reversed ...
367
368=cut
5bb401c6 369
370sub log : Chained('base') Args(0) {
790ce598 371 $_[0]->shortlog($_[1]);
372 $_[1]->stash->{action} = 'log';
373}
374
c8a42dd5 375# For legacy support.
5bb401c6 376sub history : Chained('base') Args(0) {
cce8c9b6 377 my ( $self, $c ) = @_;
378 $self->shortlog($c);
82bc0f05 379 my $repository = $c->stash->{Repository};
380 my $file = $repository->get_object(
381 $repository->hash_by_path(
382 $repository->head_hash,
cce8c9b6 383 $c->stash->{filename}
384 )
385 );
386 $c->stash( action => 'history',
387 filetype => $file->type,
388 );
c8a42dd5 389}
390
b3fa97cd 391=head2 tree
392
393The tree of a given commit.
394
395=cut
396
5bb401c6 397sub tree : Chained('base') Args(0) {
b3fa97cd 398 my ( $self, $c ) = @_;
82bc0f05 399 my $repository = $c->stash->{Repository};
832cbc81 400 my $commit = $self->_get_object($c, $c->req->param('hb'));
c046a52f 401 my $filename = $c->req->param('f') || '';
402 my $tree = $filename
82bc0f05 403 ? $repository->get_object($repository->hash_by_path($commit->sha1, $filename))
404 : $repository->get_object($commit->tree_sha1)
c046a52f 405 ;
b3fa97cd 406 $c->stash(
790ce598 407 commit => $commit,
b4b4d0fd 408 tree => $tree,
82bc0f05 409 tree_list => [$repository->list_tree($tree->sha1)],
832cbc81 410 path => $c->req->param('f') || '',
790ce598 411 action => 'tree',
b3fa97cd 412 );
413}
414
9dc3b9a5 415=head2 reflog
416
417Expose the local reflog. This may go away.
418
419=cut
420
5bb401c6 421sub reflog : Chained('base') Args(0) {
9dc3b9a5 422 my ( $self, $c ) = @_;
87581f05 423 my @log = $c->stash->{Repository}->reflog(
9dc3b9a5 424 '--since=yesterday'
425 );
426
427 $c->stash(
428 log => \@log,
429 action => 'reflog',
430 );
431}
432
ea19a20c 433=head2 search
434
435The action for the search form.
436
437=cut
438
5bb401c6 439sub search : Chained('base') Args(0) {
4df2f62f 440 my($self, $c) = @_;
d8abdf1c 441 $c->stash(current_action => 'GitRepos');
82bc0f05 442 my $repository = $c->stash->{Repository};
832cbc81 443 my $commit = $self->_get_object($c);
4df2f62f 444 # Lifted from /shortlog.
445 my %logargs = (
446 sha1 => $commit->sha1,
447 count => Gitalist->config->{paging}{log},
448 ($c->req->param('f') ? (file => $c->req->param('f')) : ()),
72d72d4d 449 search => {
450 type => $c->req->param('type'),
451 text => $c->req->param('text'),
452 regexp => $c->req->param('regexp') || 0,
453 },
4df2f62f 454 );
455
456 $c->stash(
457 commit => $commit,
82bc0f05 458 results => [$repository->list_revs(%logargs)],
4df2f62f 459 action => 'search',
460 # This could be added - page => $page,
461 );
14664e1c 462}
463
ea19a20c 464=head2 search_help
465
466Provides some help for the search form.
467
468=cut
469
5bb401c6 470sub search_help : Chained('base') Args(0) {
2646511e 471 my ($self, $c) = @_;
472 $c->stash(template => 'search_help.tt2');
6cfcd548 473}
474
ea19a20c 475=head2 atom
476
82bc0f05 477Provides an atom feed for a given repository.
ea19a20c 478
479=cut
480
5bb401c6 481sub atom : Chained('base') Args(0) {
d17ce39c 482 my($self, $c) = @_;
6cfcd548 483
d17ce39c 484 my $feed = XML::Atom::Feed->new;
6cfcd548 485
d17ce39c 486 my $host = lc Sys::Hostname::hostname();
487 $feed->title($host . ' - ' . Gitalist->config->{name});
488 $feed->updated(~~DateTime->now);
489
82bc0f05 490 my $repository = $c->stash->{Repository};
d17ce39c 491 my %logargs = (
82bc0f05 492 sha1 => $repository->head_hash,
d17ce39c 493 count => Gitalist->config->{paging}{log} || 25,
494 ($c->req->param('f') ? (file => $c->req->param('f')) : ())
495 );
496
497 my $mk_title = $c->stash->{short_cmt};
82bc0f05 498 for my $commit ($repository->list_revs(%logargs)) {
d17ce39c 499 my $entry = XML::Atom::Entry->new;
500 $entry->title( $mk_title->($commit->comment) );
501 $entry->id($c->uri_for('commit', {h=>$commit->sha1}));
502 # XXX Needs work ...
503 $entry->content($commit->comment);
504 $feed->add_entry($entry);
505 }
506
f796a861 507 $c->response->body($feed->as_xml);
e75df318 508 $c->response->content_type('application/atom+xml');
f796a861 509 $c->response->status(200);
6cfcd548 510}
511
ea19a20c 512=head2 rss
513
82bc0f05 514Provides an RSS feed for a given repository.
ea19a20c 515
516=cut
517
5bb401c6 518sub rss : Chained('base') Args(0) {
f796a861 519 my ($self, $c) = @_;
520
82bc0f05 521 my $repository = $c->stash->{Repository};
f796a861 522
523 my $rss = XML::RSS->new(version => '2.0');
524 $rss->channel(
525 title => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name},
82bc0f05 526 link => $c->uri_for('summary', {p=>$repository->name}),
f796a861 527 language => 'en',
82bc0f05 528 description => $repository->description,
f796a861 529 pubDate => DateTime->now,
530 lastBuildDate => DateTime->now,
531 );
532
533 my %logargs = (
82bc0f05 534 sha1 => $repository->head_hash,
f796a861 535 count => Gitalist->config->{paging}{log} || 25,
536 ($c->req->param('f') ? (file => $c->req->param('f')) : ())
537 );
538 my $mk_title = $c->stash->{short_cmt};
82bc0f05 539 for my $commit ($repository->list_revs(%logargs)) {
f796a861 540 # XXX Needs work ....
541 $rss->add_item(
542 title => $mk_title->($commit->comment),
543 permaLink => $c->uri_for(commit => {h=>$commit->sha1}),
544 description => $commit->comment,
545 );
546 }
547
548 $c->response->body($rss->as_string);
549 $c->response->content_type('application/rss+xml');
550 $c->response->status(200);
14664e1c 551}
552
5bb401c6 553sub opml : Chained('base') Args(0) {
286cea09 554 my($self, $c) = @_;
555
556 my $opml = XML::OPML::SimpleGen->new();
557
558 $opml->head(title => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name});
559
82bc0f05 560 my @list = @{ $c->model()->repositories };
561 die 'No repositories found in '. $c->model->repo_dir
286cea09 562 unless @list;
563
564 for my $proj ( @list ) {
565 $opml->insert_outline(
566 text => $proj->name. ' - '. $proj->description,
567 xmlUrl => $c->uri_for(rss => {p => $proj->name}),
568 );
569 }
570
571 $c->response->body($opml->as_string);
572 $c->response->content_type('application/rss');
573 $c->response->status(200);
574}
575
ea19a20c 576=head2 patch
577
578A raw patch for a given commit.
579
580=cut
581
5bb401c6 582sub patch : Chained('base') Args(0) {
377bf360 583 my ($self, $c) = @_;
61ba8635 584 $c->detach('patches', [1]);
585}
586
ea19a20c 587=head2 patches
588
589The patcheset for a given commit ???
590
591=cut
592
5bb401c6 593sub patches : Chained('base') Args(0) {
61ba8635 594 my ($self, $c, $count) = @_;
595 $count ||= Gitalist->config->{patches}{max};
377bf360 596 my $commit = $self->_get_object($c);
597 my $parent = $c->req->param('hp') || undef;
f707d264 598 my $patch = $commit->get_patch( $parent, $count );
377bf360 599 $c->response->body($patch);
600 $c->response->content_type('text/plain');
601 $c->response->status(200);
6cfcd548 602}
603
ea19a20c 604=head2 snapshot
605
606Provides a snapshot of a given commit.
607
608=cut
609
5bb401c6 610sub snapshot : Chained('base') Args(0) {
30db8f5b 611 my ($self, $c) = @_;
63afe2db 612 my $format = $c->req->param('sf') || 'tgz';
30db8f5b 613 die unless $format;
2e79039a 614 my $sha1 = $c->req->param('h') || $self->_get_object($c)->sha1;
87581f05 615 my @snap = $c->stash->{Repository}->snapshot(
c0dbe239 616 sha1 => $sha1,
617 format => $format
618 );
30db8f5b 619 $c->response->status(200);
620 $c->response->headers->header( 'Content-Disposition' =>
c0dbe239 621 "attachment; filename=$snap[0]");
622 $c->response->body($snap[1]);
6cfcd548 623}
624
1625cd5f 625
5bb401c6 626sub base : Chained('/root') PathPart('') CaptureArgs(0) {
4621ecf0 627 my($self, $c) = @_;
04d1d917 628
82bc0f05 629 my $repository = $c->req->param('p');
630 if (defined $repository) {
da8f4f82 631 eval {
82bc0f05 632 $c->stash(Repository => $c->model('GitRepos')->get_repository($repository));
da8f4f82 633 };
634 if ($@) {
5232dbdd 635 $c->detach('/error_404');
da8f4f82 636 }
637 }
638
82bc0f05 639 my $a_repository = $c->stash->{Repository} || $c->model()->repositories->[0];
4621ecf0 640 $c->stash(
82bc0f05 641 git_version => $a_repository->run_cmd('--version'),
da8f4f82 642 version => $Gitalist::VERSION,
643
644 # XXX Move these to a plugin!
4621ecf0 645 time_since => sub {
ef11b09e 646 return 'never' unless $_[0];
4621ecf0 647 return age_string(time - $_[0]->epoch);
648 },
649 short_cmt => sub {
650 my $cmt = shift;
651 my($line) = split /\n/, $cmt;
741e110e 652 $line =~ s/^(.{70,80}\b).*/$1 \x{2026}/;
4621ecf0 653 return $line;
654 },
5cd9b9f9 655 abridged_description => sub {
656 join(' ', grep { defined } (split / /, shift)[0..10]);
657 },
4621ecf0 658 );
04d1d917 659}
d9a9b56b 660
fde5091f 661sub end : ActionClass('RenderView') {
1aad4e81 662 my ($self, $c) = @_;
82bc0f05 663 # Give repository views the current HEAD.
87581f05 664 if ($c->stash->{Repository}) {
665 $c->stash->{HEAD} = $c->stash->{Repository}->head_hash;
1aad4e81 666 }
1fd8159c 667}
d9a9b56b 668
c113db92 669sub error_404 : Action {
4111e151 670 my ($self, $c) = @_;
671 $c->response->status(404);
5232dbdd 672 $c->response->body('Page not found');
4111e151 673}
674
775e96e0 675__PACKAGE__->meta->make_immutable;
676
677__END__
678
d137f7d5 679=head1 NAME
680
681Gitalist::Controller::Root - Root controller for the application
682
683=head1 DESCRIPTION
684
685This controller handles all of the root level paths for the application
686
687=head1 METHODS
688
5bb401c6 689=head2 root
690
691Root of chained actions
692
693=head2 base
694
695Populate the header and footer. Perhaps not the best location.
696
697=head2 index
698
82bc0f05 699Provides the repository listing.
5bb401c6 700
701=head2 end
702
703Attempt to render a view, if needed.
d137f7d5 704
705=head2 blame
706
707=head2 commitdiff_plain
708
709=head2 error_404
710
711=head2 history
712
713=head2 opml
714
82bc0f05 715=head2 repository_index
da8f4f82 716
775e96e0 717=head1 AUTHORS
89de6a33 718
775e96e0 719See L<Gitalist> for authors.
89de6a33 720
721=head1 LICENSE
722
775e96e0 723See L<Gitalist> for the license.
89de6a33 724
725=cut