1 package Gitalist::Controller::Root;
9 use XML::OPML::SimpleGen;
11 use Gitalist::Utils qw/ age_string /;
13 use namespace::autoclean;
15 BEGIN { extends 'Gitalist::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;
80 The plain text version of blob, where file is rendered as is.
84 sub blob_plain : Chained('base') Args(0) {
87 my($blob) = $self->_blob_objs($c);
88 $c->response->content_type('text/plain; charset=utf-8');
89 $c->response->body($blob->content);
90 $c->response->status(200);
95 The plain text version of blobdiff.
99 sub blobdiff_plain : Chained('base') Args(0) {
102 $c->stash(no_wrapper => 1);
103 $c->response->content_type('text/plain; charset=utf-8');
105 $c->forward('blobdiff');
110 Exposes a given diff of a blob.
114 sub blobdiff : Chained('base') Args(0) {
115 my ( $self, $c ) = @_;
116 my $commit = $self->_get_object($c, $c->req->param('hb'));
117 my $filename = $c->req->param('f')
118 || croak("No file specified!");
119 my($tree, $patch) = $c->stash->{Repository}->diff(
122 parent => $c->req->param('hpb') || undef,
128 filename => $filename,
129 # XXX Hack hack hack, see View::SyntaxHighlight
130 blobs => [$patch->[0]->{diff}],
134 $c->forward('View::SyntaxHighlight')
135 unless $c->stash->{no_wrapper};
138 # For legacy support.
139 sub history : Chained('base') Args(0) {
140 my ( $self, $c ) = @_;
142 my $repository = $c->stash->{Repository};
143 my $file = $repository->get_object(
144 $repository->hash_by_path(
145 $repository->head_hash,
146 $c->stash->{filename}
150 filetype => $file->type,
156 Expose the local reflog. This may go away.
160 sub reflog : Chained('base') Args(0) {
161 my ( $self, $c ) = @_;
162 my @log = $c->stash->{Repository}->reflog(
173 The action for the search form.
177 sub search : Chained('base') Args(0) {
179 my $repository = $c->stash->{Repository};
180 my $commit = $self->_get_object($c);
181 # Lifted from /shortlog.
183 sha1 => $commit->sha1,
184 count => Gitalist->config->{paging}{log},
185 ($c->req->param('f') ? (file => $c->req->param('f')) : ()),
187 type => $c->req->param('type'),
188 text => $c->req->param('text'),
189 regexp => $c->req->param('regexp') || 0,
195 results => [$repository->list_revs(%logargs)],
196 # This could be added - page => $page,
202 Provides some help for the search form.
206 sub search_help : Chained('base') Args(0) {
208 $c->stash(template => 'search_help.tt2');
213 Provides an atom feed for a given repository.
217 sub atom : Chained('base') Args(0) {
220 my $feed = XML::Atom::Feed->new;
222 my $host = lc Sys::Hostname::hostname();
223 $feed->title($host . ' - ' . Gitalist->config->{name});
224 $feed->updated(~~DateTime->now);
226 my $repository = $c->stash->{Repository};
228 sha1 => $repository->head_hash,
229 count => Gitalist->config->{paging}{log} || 25,
230 ($c->req->param('f') ? (file => $c->req->param('f')) : ())
233 my $mk_title = $c->stash->{short_cmt};
234 for my $commit ($repository->list_revs(%logargs)) {
235 my $entry = XML::Atom::Entry->new;
236 $entry->title( $mk_title->($commit->comment) );
237 $entry->id($c->uri_for('commit', {h=>$commit->sha1}));
239 $entry->content($commit->comment);
240 $feed->add_entry($entry);
243 $c->response->body($feed->as_xml);
244 $c->response->content_type('application/atom+xml');
245 $c->response->status(200);
250 Provides an RSS feed for a given repository.
254 sub rss : Chained('base') Args(0) {
257 my $repository = $c->stash->{Repository};
259 my $rss = XML::RSS->new(version => '2.0');
261 title => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name},
262 link => $c->uri_for('summary', {p=>$repository->name}),
264 description => $repository->description,
265 pubDate => DateTime->now,
266 lastBuildDate => DateTime->now,
270 sha1 => $repository->head_hash,
271 count => Gitalist->config->{paging}{log} || 25,
272 ($c->req->param('f') ? (file => $c->req->param('f')) : ())
274 my $mk_title = $c->stash->{short_cmt};
275 for my $commit ($repository->list_revs(%logargs)) {
276 # XXX Needs work ....
278 title => $mk_title->($commit->comment),
279 permaLink => $c->uri_for(commit => {h=>$commit->sha1}),
280 description => $commit->comment,
284 $c->response->body($rss->as_string);
285 $c->response->content_type('application/rss+xml');
286 $c->response->status(200);
289 sub opml : Chained('base') Args(0) {
292 my $opml = XML::OPML::SimpleGen->new();
294 $opml->head(title => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name});
296 my @list = @{ $c->model()->repositories };
297 die 'No repositories found in '. $c->model->repo_dir
300 for my $proj ( @list ) {
301 $opml->insert_outline(
302 text => $proj->name. ' - '. $proj->description,
303 xmlUrl => $c->uri_for(rss => {p => $proj->name}),
307 $c->response->body($opml->as_string);
308 $c->response->content_type('application/rss');
309 $c->response->status(200);
314 A raw patch for a given commit.
318 sub patch : Chained('base') Args(0) {
320 $c->detach('patches', [1]);
325 The patcheset for a given commit ???
329 sub patches : Chained('base') Args(0) {
330 my ($self, $c, $count) = @_;
331 $count ||= Gitalist->config->{patches}{max};
332 my $commit = $self->_get_object($c);
333 my $parent = $c->req->param('hp') || undef;
334 my $patch = $commit->get_patch( $parent, $count );
335 $c->response->body($patch);
336 $c->response->content_type('text/plain');
337 $c->response->status(200);
342 Provides a snapshot of a given commit.
346 sub snapshot : Chained('base') Args(0) {
348 my $format = $c->req->param('sf') || 'tgz';
350 my $sha1 = $c->req->param('h') || $self->_get_object($c)->sha1;
351 my @snap = $c->stash->{Repository}->snapshot(
355 $c->response->status(200);
356 $c->response->headers->header( 'Content-Disposition' =>
357 "attachment; filename=$snap[0]");
358 $c->response->body($snap[1]);
362 sub base : Chained('/root') PathPart('') CaptureArgs(0) {
365 my $git_version = `git --version`;
368 git_version => $git_version,
369 version => $Gitalist::VERSION,
371 # XXX Move these to a plugin!
373 return 'never' unless $_[0];
374 return age_string(time - $_[0]->epoch);
378 my($line) = split /\n/, $cmt;
379 $line =~ s/^(.{70,80}\b).*/$1 \x{2026}/;
382 abridged_description => sub {
383 join(' ', grep { defined } (split / /, shift)[0..10]);
388 sub end : ActionClass('RenderView') {
390 # Give repository views the current HEAD.
391 if ($c->stash->{Repository}) {
392 $c->stash->{HEAD} = $c->stash->{Repository}->head_hash;
396 sub error_404 : Action {
398 $c->response->status(404);
399 $c->response->body('Page not found');
402 __PACKAGE__->meta->make_immutable;
408 Gitalist::Controller::Root - Root controller for the application
412 This controller handles all of the root level paths for the application
418 Root of chained actions
422 Populate the header and footer. Perhaps not the best location.
426 Provides the repository listing.
430 Attempt to render a view, if needed.
440 =head2 repository_index
444 See L<Gitalist> for authors.
448 See L<Gitalist> for the license.