419dc25358f4204bb0d9596eced7223440b37fa8
[catagits/Gitalist.git] / lib / Gitalist / Controller / Root.pm
1 package Gitalist::Controller::Root;
2
3 use Moose;
4 use Moose::Autobox;
5 use Sys::Hostname qw/hostname/;
6 use XML::OPML::SimpleGen;
7
8 use Gitalist::Utils qw/ age_string /;
9
10 use namespace::autoclean;
11
12 BEGIN { extends 'Gitalist::Controller' }
13
14 __PACKAGE__->config->{namespace} = '';
15
16 sub root : Chained('/') PathPart('') CaptureArgs(0) {}
17
18 sub _get_object {
19   my($self, $c, $haveh) = @_;
20
21   my $h = $haveh || $c->req->param('h') || '';
22   my $f = $c->req->param('f');
23
24   my $m = $c->stash->{Repository};
25   my $pd = $m->path;
26
27   # Either use the provided h(ash) parameter, the f(ile) parameter or just use HEAD.
28   my $hash = ($h =~ /[^a-f0-9]/ ? $m->head_hash($h) : $h)
29           || ($f && $m->hash_by_path($f))
30           || $m->head_hash
31           # XXX This could definitely use more context.
32           || Carp::croak("Couldn't find a hash for the commit object!");
33
34   my $obj = $m->get_object($hash)
35     or Carp::croak("Couldn't find a object for '$hash' in '$pd'!");
36
37   return $obj;
38 }
39
40 sub index : Chained('base') PathPart('') Args(0) {
41   my ( $self, $c ) = @_;
42
43   $c->detach($c->req->param('a'))
44     if $c->req->param('a');
45
46   my $search = $c->req->param('s') || '';
47
48   $c->stash(
49     search_text => $search,
50   );
51 }
52
53 sub _blob_objs {
54   my ( $self, $c ) = @_;
55   my $repository = $c->stash->{Repository};
56   my $h  = $c->req->param('h')
57        || $repository->hash_by_path($c->req->param('hb'), $c->req->param('f'))
58        || die "No file or sha1 provided.";
59   my $hb = $c->req->param('hb')
60        || $repository->head_hash
61        || die "Couldn't discern the corresponding head.";
62
63   my $filename = $c->req->param('f') || '';
64
65   my $blob = $repository->get_object($h);
66   $blob = $repository->get_object(
67     $repository->hash_by_path($h || $hb, $filename)
68   ) if $blob->type ne 'blob';
69
70   return $blob, $repository->get_object($hb), $filename;
71 }
72
73 =head2 blobdiff_plain
74
75 The plain text version of blobdiff.
76
77 =cut
78
79 sub blobdiff_plain : Chained('base') Args(0) {
80   my($self, $c) = @_;
81
82   $c->stash(no_wrapper => 1);
83   $c->response->content_type('text/plain; charset=utf-8');
84
85   $c->forward('blobdiff');
86 }
87
88 =head2 blobdiff
89
90 Exposes a given diff of a blob.
91
92 =cut
93
94 sub blobdiff : Chained('base') Args(0) {
95   my ( $self, $c ) = @_;
96   my $commit = $self->_get_object($c, $c->req->param('hb'));
97   my $filename = $c->req->param('f')
98               || croak("No file specified!");
99   my($tree, $patch) = $c->stash->{Repository}->diff(
100     commit => $commit,
101     patch  => 1,
102     parent => $c->req->param('hpb') || undef,
103     file   => $filename,
104   );
105   $c->stash(
106     commit    => $commit,
107     diff      => $patch,
108     filename  => $filename,
109     # XXX Hack hack hack, see View::SyntaxHighlight
110     blobs     => [$patch->[0]->{diff}],
111     language  => 'Diff',
112   );
113
114   $c->forward('View::SyntaxHighlight')
115     unless $c->stash->{no_wrapper};
116 }
117
118 =head2 search_help
119
120 Provides some help for the search form.
121
122 =cut
123
124 sub search_help : Chained('base') Args(0) {}
125
126 sub opml : Chained('base') Args(0) {
127   my($self, $c) = @_;
128
129   my $opml = XML::OPML::SimpleGen->new();
130
131   $opml->head(title => lc(hostname()) . ' - ' . Gitalist->config->{name});
132
133   my @list = @{ $c->model()->repositories };
134   die 'No repositories found in '. $c->model->repo_dir
135     unless @list;
136
137   for my $proj ( @list ) {
138     $opml->insert_outline(
139       text   => $proj->name. ' - '. $proj->description,
140       xmlUrl => $c->uri_for(rss => {p => $proj->name}),
141     );
142   }
143
144   $c->response->body($opml->as_string);
145   $c->response->content_type('application/rss');
146   $c->response->status(200);
147 }
148
149 sub base : Chained('/root') PathPart('') CaptureArgs(0) {
150   my($self, $c) = @_;
151
152   my $git_version = `git --version`;
153   chomp($git_version);
154   $c->stash(
155     git_version => $git_version,
156     version     => $Gitalist::VERSION,
157
158     # XXX Move these to a plugin!
159     time_since => sub {
160       return 'never' unless $_[0];
161       return age_string(time - $_[0]->epoch);
162     },
163     short_cmt => sub {
164       my $cmt = shift;
165       my($line) = split /\n/, $cmt;
166       $line =~ s/^(.{70,80}\b).*/$1 \x{2026}/;
167       return $line;
168     },
169     abridged_description => sub {
170         join(' ', grep { defined } (split / /, shift)[0..10]);
171     },
172   );
173 }
174
175 sub end : ActionClass('RenderView') {
176     my ($self, $c) = @_;
177     # Give repository views the current HEAD.
178     if ($c->stash->{Repository}) {
179         $c->stash->{HEAD} = $c->stash->{Repository}->head_hash;
180     }
181 }
182
183 sub error_404 : Action {
184     my ($self, $c) = @_;
185     $c->response->status(404);
186     $c->response->body('Page not found');
187 }
188
189 __PACKAGE__->meta->make_immutable;
190
191 __END__
192
193 =head1 NAME
194
195 Gitalist::Controller::Root - Root controller for the application
196
197 =head1 DESCRIPTION
198
199 This controller handles all of the root level paths for the application
200
201 =head1 METHODS
202
203 =head2 root
204
205 Root of chained actions
206
207 =head2 base
208
209 Populate the header and footer. Perhaps not the best location.
210
211 =head2 index
212
213 Provides the repository listing.
214
215 =head2 end
216
217 Attempt to render a view, if needed.
218
219 =head2 blame
220
221 =head2 error_404
222
223 =head2 history
224
225 =head2 opml
226
227 =head2 repository_index
228
229 =head1 AUTHORS
230
231 See L<Gitalist> for authors.
232
233 =head1 LICENSE
234
235 See L<Gitalist> for the license.
236
237 =cut