Get blame view working in the new url scheme
[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
7bc165b3 49 my $search = $c->req->param('s') || '';
7bc165b3 50
51 $c->stash(
52 search_text => $search,
7bc165b3 53 );
04d1d917 54}
55
b5f3d3e7 56sub _blob_objs {
295c9703 57 my ( $self, $c ) = @_;
82bc0f05 58 my $repository = $c->stash->{Repository};
c8870bd3 59 my $h = $c->req->param('h')
82bc0f05 60 || $repository->hash_by_path($c->req->param('hb'), $c->req->param('f'))
c8870bd3 61 || die "No file or sha1 provided.";
62 my $hb = $c->req->param('hb')
82bc0f05 63 || $repository->head_hash
c8870bd3 64 || die "Couldn't discern the corresponding head.";
65
f5da8e7a 66 my $filename = $c->req->param('f') || '';
67
82bc0f05 68 my $blob = $repository->get_object($h);
69 $blob = $repository->get_object(
70 $repository->hash_by_path($h || $hb, $filename)
b5f3d3e7 71 ) if $blob->type ne 'blob';
72
82bc0f05 73 return $blob, $repository->get_object($hb), $filename;
b5f3d3e7 74}
75
76=head2 blob
77
78The blob action i.e the contents of a file.
79
80=cut
81
5bb401c6 82sub blob : Chained('base') Args(0) {
b5f3d3e7 83 my ( $self, $c ) = @_;
84
85 my($blob, $head, $filename) = $self->_blob_objs($c);
295c9703 86 $c->stash(
b5f3d3e7 87 blob => $blob->content,
88 head => $head,
f5da8e7a 89 filename => $filename,
90 # XXX Hack hack hack, see View::SyntaxHighlight
6bd8b34b 91 language => ($filename =~ /\.p[lm]$/i ? 'Perl' : ''),
295c9703 92 );
7e54e579 93
c8a42dd5 94 $c->forward('View::SyntaxHighlight')
95 unless $c->stash->{no_wrapper};
96}
97
b5f3d3e7 98=head2 blob_plain
99
100The plain text version of blob, where file is rendered as is.
101
102=cut
103
5bb401c6 104sub blob_plain : Chained('base') Args(0) {
c8a42dd5 105 my($self, $c) = @_;
106
b5f3d3e7 107 my($blob) = $self->_blob_objs($c);
c8a42dd5 108 $c->response->content_type('text/plain; charset=utf-8');
b5f3d3e7 109 $c->response->body($blob->content);
110 $c->response->status(200);
c8a42dd5 111}
112
b5f3d3e7 113=head2 blobdiff_plain
114
115The plain text version of blobdiff.
116
117=cut
118
5bb401c6 119sub blobdiff_plain : Chained('base') Args(0) {
c8a42dd5 120 my($self, $c) = @_;
121
122 $c->stash(no_wrapper => 1);
123 $c->response->content_type('text/plain; charset=utf-8');
124
125 $c->forward('blobdiff');
295c9703 126}
127
6cf4366a 128=head2 blobdiff
129
130Exposes a given diff of a blob.
131
132=cut
133
5bb401c6 134sub blobdiff : Chained('base') Args(0) {
6cf4366a 135 my ( $self, $c ) = @_;
832cbc81 136 my $commit = $self->_get_object($c, $c->req->param('hb'));
6cf4366a 137 my $filename = $c->req->param('f')
138 || croak("No file specified!");
87581f05 139 my($tree, $patch) = $c->stash->{Repository}->diff(
ad8884fc 140 commit => $commit,
ad8884fc 141 patch => 1,
c8a42dd5 142 parent => $c->req->param('hpb') || undef,
143 file => $filename,
6cf4366a 144 );
145 $c->stash(
146 commit => $commit,
ad8884fc 147 diff => $patch,
592b68ef 148 filename => $filename,
6cf4366a 149 # XXX Hack hack hack, see View::SyntaxHighlight
ad8884fc 150 blobs => [$patch->[0]->{diff}],
6cf4366a 151 language => 'Diff',
6cf4366a 152 );
153
c8a42dd5 154 $c->forward('View::SyntaxHighlight')
155 unless $c->stash->{no_wrapper};
6cf4366a 156}
157
c8a42dd5 158# For legacy support.
5bb401c6 159sub history : Chained('base') Args(0) {
cce8c9b6 160 my ( $self, $c ) = @_;
161 $self->shortlog($c);
82bc0f05 162 my $repository = $c->stash->{Repository};
163 my $file = $repository->get_object(
164 $repository->hash_by_path(
165 $repository->head_hash,
cce8c9b6 166 $c->stash->{filename}
167 )
168 );
066e9aa4 169 $c->stash(
cce8c9b6 170 filetype => $file->type,
171 );
c8a42dd5 172}
173
9dc3b9a5 174=head2 reflog
175
176Expose the local reflog. This may go away.
177
178=cut
179
5bb401c6 180sub reflog : Chained('base') Args(0) {
9dc3b9a5 181 my ( $self, $c ) = @_;
87581f05 182 my @log = $c->stash->{Repository}->reflog(
9dc3b9a5 183 '--since=yesterday'
184 );
185
186 $c->stash(
187 log => \@log,
9dc3b9a5 188 );
189}
190
ea19a20c 191=head2 search
192
193The action for the search form.
194
195=cut
196
5bb401c6 197sub search : Chained('base') Args(0) {
4df2f62f 198 my($self, $c) = @_;
82bc0f05 199 my $repository = $c->stash->{Repository};
832cbc81 200 my $commit = $self->_get_object($c);
4df2f62f 201 # Lifted from /shortlog.
202 my %logargs = (
203 sha1 => $commit->sha1,
204 count => Gitalist->config->{paging}{log},
205 ($c->req->param('f') ? (file => $c->req->param('f')) : ()),
72d72d4d 206 search => {
207 type => $c->req->param('type'),
208 text => $c->req->param('text'),
209 regexp => $c->req->param('regexp') || 0,
210 },
4df2f62f 211 );
212
213 $c->stash(
214 commit => $commit,
82bc0f05 215 results => [$repository->list_revs(%logargs)],
4df2f62f 216 # This could be added - page => $page,
217 );
14664e1c 218}
219
ea19a20c 220=head2 search_help
221
222Provides some help for the search form.
223
224=cut
225
5bb401c6 226sub search_help : Chained('base') Args(0) {
2646511e 227 my ($self, $c) = @_;
228 $c->stash(template => 'search_help.tt2');
6cfcd548 229}
230
ea19a20c 231=head2 atom
232
82bc0f05 233Provides an atom feed for a given repository.
ea19a20c 234
235=cut
236
5bb401c6 237sub atom : Chained('base') Args(0) {
d17ce39c 238 my($self, $c) = @_;
6cfcd548 239
d17ce39c 240 my $feed = XML::Atom::Feed->new;
6cfcd548 241
d17ce39c 242 my $host = lc Sys::Hostname::hostname();
243 $feed->title($host . ' - ' . Gitalist->config->{name});
244 $feed->updated(~~DateTime->now);
245
82bc0f05 246 my $repository = $c->stash->{Repository};
d17ce39c 247 my %logargs = (
82bc0f05 248 sha1 => $repository->head_hash,
d17ce39c 249 count => Gitalist->config->{paging}{log} || 25,
250 ($c->req->param('f') ? (file => $c->req->param('f')) : ())
251 );
252
253 my $mk_title = $c->stash->{short_cmt};
82bc0f05 254 for my $commit ($repository->list_revs(%logargs)) {
d17ce39c 255 my $entry = XML::Atom::Entry->new;
256 $entry->title( $mk_title->($commit->comment) );
257 $entry->id($c->uri_for('commit', {h=>$commit->sha1}));
258 # XXX Needs work ...
259 $entry->content($commit->comment);
260 $feed->add_entry($entry);
261 }
262
f796a861 263 $c->response->body($feed->as_xml);
e75df318 264 $c->response->content_type('application/atom+xml');
f796a861 265 $c->response->status(200);
6cfcd548 266}
267
ea19a20c 268=head2 rss
269
82bc0f05 270Provides an RSS feed for a given repository.
ea19a20c 271
272=cut
273
5bb401c6 274sub rss : Chained('base') Args(0) {
f796a861 275 my ($self, $c) = @_;
276
82bc0f05 277 my $repository = $c->stash->{Repository};
f796a861 278
279 my $rss = XML::RSS->new(version => '2.0');
280 $rss->channel(
281 title => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name},
82bc0f05 282 link => $c->uri_for('summary', {p=>$repository->name}),
f796a861 283 language => 'en',
82bc0f05 284 description => $repository->description,
f796a861 285 pubDate => DateTime->now,
286 lastBuildDate => DateTime->now,
287 );
288
289 my %logargs = (
82bc0f05 290 sha1 => $repository->head_hash,
f796a861 291 count => Gitalist->config->{paging}{log} || 25,
292 ($c->req->param('f') ? (file => $c->req->param('f')) : ())
293 );
294 my $mk_title = $c->stash->{short_cmt};
82bc0f05 295 for my $commit ($repository->list_revs(%logargs)) {
f796a861 296 # XXX Needs work ....
297 $rss->add_item(
298 title => $mk_title->($commit->comment),
299 permaLink => $c->uri_for(commit => {h=>$commit->sha1}),
300 description => $commit->comment,
301 );
302 }
303
304 $c->response->body($rss->as_string);
305 $c->response->content_type('application/rss+xml');
306 $c->response->status(200);
14664e1c 307}
308
5bb401c6 309sub opml : Chained('base') Args(0) {
286cea09 310 my($self, $c) = @_;
311
312 my $opml = XML::OPML::SimpleGen->new();
313
314 $opml->head(title => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name});
315
82bc0f05 316 my @list = @{ $c->model()->repositories };
317 die 'No repositories found in '. $c->model->repo_dir
286cea09 318 unless @list;
319
320 for my $proj ( @list ) {
321 $opml->insert_outline(
322 text => $proj->name. ' - '. $proj->description,
323 xmlUrl => $c->uri_for(rss => {p => $proj->name}),
324 );
325 }
326
327 $c->response->body($opml->as_string);
328 $c->response->content_type('application/rss');
329 $c->response->status(200);
330}
331
ea19a20c 332=head2 patch
333
334A raw patch for a given commit.
335
336=cut
337
5bb401c6 338sub patch : Chained('base') Args(0) {
377bf360 339 my ($self, $c) = @_;
61ba8635 340 $c->detach('patches', [1]);
341}
342
ea19a20c 343=head2 patches
344
345The patcheset for a given commit ???
346
347=cut
348
5bb401c6 349sub patches : Chained('base') Args(0) {
61ba8635 350 my ($self, $c, $count) = @_;
351 $count ||= Gitalist->config->{patches}{max};
377bf360 352 my $commit = $self->_get_object($c);
353 my $parent = $c->req->param('hp') || undef;
f707d264 354 my $patch = $commit->get_patch( $parent, $count );
377bf360 355 $c->response->body($patch);
356 $c->response->content_type('text/plain');
357 $c->response->status(200);
6cfcd548 358}
359
ea19a20c 360=head2 snapshot
361
362Provides a snapshot of a given commit.
363
364=cut
365
5bb401c6 366sub snapshot : Chained('base') Args(0) {
30db8f5b 367 my ($self, $c) = @_;
63afe2db 368 my $format = $c->req->param('sf') || 'tgz';
30db8f5b 369 die unless $format;
2e79039a 370 my $sha1 = $c->req->param('h') || $self->_get_object($c)->sha1;
87581f05 371 my @snap = $c->stash->{Repository}->snapshot(
c0dbe239 372 sha1 => $sha1,
373 format => $format
374 );
30db8f5b 375 $c->response->status(200);
376 $c->response->headers->header( 'Content-Disposition' =>
c0dbe239 377 "attachment; filename=$snap[0]");
378 $c->response->body($snap[1]);
6cfcd548 379}
380
1625cd5f 381
5bb401c6 382sub base : Chained('/root') PathPart('') CaptureArgs(0) {
4621ecf0 383 my($self, $c) = @_;
04d1d917 384
066e9aa4 385 my $git_version = `git --version`;
386 chomp($git_version);
4621ecf0 387 $c->stash(
066e9aa4 388 git_version => $git_version,
da8f4f82 389 version => $Gitalist::VERSION,
390
391 # XXX Move these to a plugin!
4621ecf0 392 time_since => sub {
ef11b09e 393 return 'never' unless $_[0];
4621ecf0 394 return age_string(time - $_[0]->epoch);
395 },
396 short_cmt => sub {
397 my $cmt = shift;
398 my($line) = split /\n/, $cmt;
741e110e 399 $line =~ s/^(.{70,80}\b).*/$1 \x{2026}/;
4621ecf0 400 return $line;
401 },
5cd9b9f9 402 abridged_description => sub {
403 join(' ', grep { defined } (split / /, shift)[0..10]);
404 },
4621ecf0 405 );
04d1d917 406}
d9a9b56b 407
fde5091f 408sub end : ActionClass('RenderView') {
1aad4e81 409 my ($self, $c) = @_;
82bc0f05 410 # Give repository views the current HEAD.
87581f05 411 if ($c->stash->{Repository}) {
412 $c->stash->{HEAD} = $c->stash->{Repository}->head_hash;
1aad4e81 413 }
1fd8159c 414}
d9a9b56b 415
c113db92 416sub error_404 : Action {
4111e151 417 my ($self, $c) = @_;
418 $c->response->status(404);
5232dbdd 419 $c->response->body('Page not found');
4111e151 420}
421
775e96e0 422__PACKAGE__->meta->make_immutable;
423
424__END__
425
d137f7d5 426=head1 NAME
427
428Gitalist::Controller::Root - Root controller for the application
429
430=head1 DESCRIPTION
431
432This controller handles all of the root level paths for the application
433
434=head1 METHODS
435
5bb401c6 436=head2 root
437
438Root of chained actions
439
440=head2 base
441
442Populate the header and footer. Perhaps not the best location.
443
444=head2 index
445
82bc0f05 446Provides the repository listing.
5bb401c6 447
448=head2 end
449
450Attempt to render a view, if needed.
d137f7d5 451
452=head2 blame
453
d137f7d5 454=head2 error_404
455
456=head2 history
457
458=head2 opml
459
82bc0f05 460=head2 repository_index
da8f4f82 461
775e96e0 462=head1 AUTHORS
89de6a33 463
775e96e0 464See L<Gitalist> for authors.
89de6a33 465
466=head1 LICENSE
467
775e96e0 468See L<Gitalist> for the license.
89de6a33 469
470=cut