1 package Gitalist::Controller::Root;
9 use XML::OPML::SimpleGen;
11 use Gitalist::Utils qw/ age_string /;
13 use namespace::autoclean;
15 BEGIN { extends 'Catalyst::Controller' }
17 __PACKAGE__->config->{namespace} = '';
19 sub root : Chained('/') PathPart('') CaptureArgs(0) {}
22 my($self, $c, $haveh) = @_;
24 my $h = $haveh || $c->req->param('h') || '';
25 my $f = $c->req->param('f');
27 my $m = $c->stash->{Repository};
30 # Either use the provided h(ash) parameter, the f(ile) parameter or just use HEAD.
31 my $hash = ($h =~ /[^a-f0-9]/ ? $m->head_hash($h) : $h)
32 || ($f && $m->hash_by_path($f))
34 # XXX This could definitely use more context.
35 || Carp::croak("Couldn't find a hash for the commit object!");
37 my $obj = $m->get_object($hash)
38 or Carp::croak("Couldn't find a object for '$hash' in '$pd'!");
43 sub index : Chained('base') PathPart('') Args(0) {
44 my ( $self, $c ) = @_;
46 $c->detach($c->req->param('a'))
47 if $c->req->param('a');
49 my $search = $c->req->param('s') || '';
52 search_text => $search,
57 my ( $self, $c ) = @_;
58 my $repository = $c->stash->{Repository};
59 my $h = $c->req->param('h')
60 || $repository->hash_by_path($c->req->param('hb'), $c->req->param('f'))
61 || die "No file or sha1 provided.";
62 my $hb = $c->req->param('hb')
63 || $repository->head_hash
64 || die "Couldn't discern the corresponding head.";
66 my $filename = $c->req->param('f') || '';
68 my $blob = $repository->get_object($h);
69 $blob = $repository->get_object(
70 $repository->hash_by_path($h || $hb, $filename)
71 ) if $blob->type ne 'blob';
73 return $blob, $repository->get_object($hb), $filename;
78 The blob action i.e the contents of a file.
82 sub blob : Chained('base') Args(0) {
83 my ( $self, $c ) = @_;
85 my($blob, $head, $filename) = $self->_blob_objs($c);
87 blob => $blob->content,
89 filename => $filename,
90 # XXX Hack hack hack, see View::SyntaxHighlight
91 language => ($filename =~ /\.p[lm]$/i ? 'Perl' : ''),
94 $c->forward('View::SyntaxHighlight')
95 unless $c->stash->{no_wrapper};
100 The plain text version of blob, where file is rendered as is.
104 sub blob_plain : Chained('base') Args(0) {
107 my($blob) = $self->_blob_objs($c);
108 $c->response->content_type('text/plain; charset=utf-8');
109 $c->response->body($blob->content);
110 $c->response->status(200);
113 =head2 blobdiff_plain
115 The plain text version of blobdiff.
119 sub blobdiff_plain : Chained('base') Args(0) {
122 $c->stash(no_wrapper => 1);
123 $c->response->content_type('text/plain; charset=utf-8');
125 $c->forward('blobdiff');
130 Exposes a given diff of a blob.
134 sub blobdiff : Chained('base') Args(0) {
135 my ( $self, $c ) = @_;
136 my $commit = $self->_get_object($c, $c->req->param('hb'));
137 my $filename = $c->req->param('f')
138 || croak("No file specified!");
139 my($tree, $patch) = $c->stash->{Repository}->diff(
142 parent => $c->req->param('hpb') || undef,
148 filename => $filename,
149 # XXX Hack hack hack, see View::SyntaxHighlight
150 blobs => [$patch->[0]->{diff}],
154 $c->forward('View::SyntaxHighlight')
155 unless $c->stash->{no_wrapper};
158 # For legacy support.
159 sub history : Chained('base') Args(0) {
160 my ( $self, $c ) = @_;
162 my $repository = $c->stash->{Repository};
163 my $file = $repository->get_object(
164 $repository->hash_by_path(
165 $repository->head_hash,
166 $c->stash->{filename}
170 filetype => $file->type,
176 Expose the local reflog. This may go away.
180 sub reflog : Chained('base') Args(0) {
181 my ( $self, $c ) = @_;
182 my @log = $c->stash->{Repository}->reflog(
193 The action for the search form.
197 sub search : Chained('base') Args(0) {
199 my $repository = $c->stash->{Repository};
200 my $commit = $self->_get_object($c);
201 # Lifted from /shortlog.
203 sha1 => $commit->sha1,
204 count => Gitalist->config->{paging}{log},
205 ($c->req->param('f') ? (file => $c->req->param('f')) : ()),
207 type => $c->req->param('type'),
208 text => $c->req->param('text'),
209 regexp => $c->req->param('regexp') || 0,
215 results => [$repository->list_revs(%logargs)],
216 # This could be added - page => $page,
222 Provides some help for the search form.
226 sub search_help : Chained('base') Args(0) {
228 $c->stash(template => 'search_help.tt2');
233 Provides an atom feed for a given repository.
237 sub atom : Chained('base') Args(0) {
240 my $feed = XML::Atom::Feed->new;
242 my $host = lc Sys::Hostname::hostname();
243 $feed->title($host . ' - ' . Gitalist->config->{name});
244 $feed->updated(~~DateTime->now);
246 my $repository = $c->stash->{Repository};
248 sha1 => $repository->head_hash,
249 count => Gitalist->config->{paging}{log} || 25,
250 ($c->req->param('f') ? (file => $c->req->param('f')) : ())
253 my $mk_title = $c->stash->{short_cmt};
254 for my $commit ($repository->list_revs(%logargs)) {
255 my $entry = XML::Atom::Entry->new;
256 $entry->title( $mk_title->($commit->comment) );
257 $entry->id($c->uri_for('commit', {h=>$commit->sha1}));
259 $entry->content($commit->comment);
260 $feed->add_entry($entry);
263 $c->response->body($feed->as_xml);
264 $c->response->content_type('application/atom+xml');
265 $c->response->status(200);
270 Provides an RSS feed for a given repository.
274 sub rss : Chained('base') Args(0) {
277 my $repository = $c->stash->{Repository};
279 my $rss = XML::RSS->new(version => '2.0');
281 title => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name},
282 link => $c->uri_for('summary', {p=>$repository->name}),
284 description => $repository->description,
285 pubDate => DateTime->now,
286 lastBuildDate => DateTime->now,
290 sha1 => $repository->head_hash,
291 count => Gitalist->config->{paging}{log} || 25,
292 ($c->req->param('f') ? (file => $c->req->param('f')) : ())
294 my $mk_title = $c->stash->{short_cmt};
295 for my $commit ($repository->list_revs(%logargs)) {
296 # XXX Needs work ....
298 title => $mk_title->($commit->comment),
299 permaLink => $c->uri_for(commit => {h=>$commit->sha1}),
300 description => $commit->comment,
304 $c->response->body($rss->as_string);
305 $c->response->content_type('application/rss+xml');
306 $c->response->status(200);
309 sub opml : Chained('base') Args(0) {
312 my $opml = XML::OPML::SimpleGen->new();
314 $opml->head(title => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name});
316 my @list = @{ $c->model()->repositories };
317 die 'No repositories found in '. $c->model->repo_dir
320 for my $proj ( @list ) {
321 $opml->insert_outline(
322 text => $proj->name. ' - '. $proj->description,
323 xmlUrl => $c->uri_for(rss => {p => $proj->name}),
327 $c->response->body($opml->as_string);
328 $c->response->content_type('application/rss');
329 $c->response->status(200);
334 A raw patch for a given commit.
338 sub patch : Chained('base') Args(0) {
340 $c->detach('patches', [1]);
345 The patcheset for a given commit ???
349 sub patches : Chained('base') Args(0) {
350 my ($self, $c, $count) = @_;
351 $count ||= Gitalist->config->{patches}{max};
352 my $commit = $self->_get_object($c);
353 my $parent = $c->req->param('hp') || undef;
354 my $patch = $commit->get_patch( $parent, $count );
355 $c->response->body($patch);
356 $c->response->content_type('text/plain');
357 $c->response->status(200);
362 Provides a snapshot of a given commit.
366 sub snapshot : Chained('base') Args(0) {
368 my $format = $c->req->param('sf') || 'tgz';
370 my $sha1 = $c->req->param('h') || $self->_get_object($c)->sha1;
371 my @snap = $c->stash->{Repository}->snapshot(
375 $c->response->status(200);
376 $c->response->headers->header( 'Content-Disposition' =>
377 "attachment; filename=$snap[0]");
378 $c->response->body($snap[1]);
382 sub base : Chained('/root') PathPart('') CaptureArgs(0) {
385 my $git_version = `git --version`;
388 git_version => $git_version,
389 version => $Gitalist::VERSION,
391 # XXX Move these to a plugin!
393 return 'never' unless $_[0];
394 return age_string(time - $_[0]->epoch);
398 my($line) = split /\n/, $cmt;
399 $line =~ s/^(.{70,80}\b).*/$1 \x{2026}/;
402 abridged_description => sub {
403 join(' ', grep { defined } (split / /, shift)[0..10]);
408 sub end : ActionClass('RenderView') {
410 # Give repository views the current HEAD.
411 if ($c->stash->{Repository}) {
412 $c->stash->{HEAD} = $c->stash->{Repository}->head_hash;
416 sub error_404 : Action {
418 $c->response->status(404);
419 $c->response->body('Page not found');
422 __PACKAGE__->meta->make_immutable;
428 Gitalist::Controller::Root - Root controller for the application
432 This controller handles all of the root level paths for the application
438 Root of chained actions
442 Populate the header and footer. Perhaps not the best location.
446 Provides the repository listing.
450 Attempt to render a view, if needed.
460 =head2 repository_index
464 See L<Gitalist> for authors.
468 See L<Gitalist> for the license.