Gave the /commit page a diff-tree.
[catagits/Gitalist.git] / lib / Gitalist / Controller / Root.pm
1 package Gitalist::Controller::Root;
2 use Moose;
3 use namespace::autoclean;
4
5 BEGIN { extends 'Catalyst::Controller' }
6
7 #
8 # Sets the actions in this controller to be registered with no prefix
9 # so they function identically to actions created in MyApp.pm
10 #
11 __PACKAGE__->config->{namespace} = '';
12
13 =head1 NAME
14
15 Gitalist::Controller::Root - Root Controller for Gitalist
16
17 =head1 DESCRIPTION
18
19 [enter your description here]
20
21 =head1 METHODS
22
23 =cut
24
25 =head2 index
26
27 =cut
28
29 use IO::Capture::Stdout;
30
31 =head2 run_gitweb
32
33 The main shim around C<gitweb.pm>.
34
35 =cut
36
37 sub run_gitweb {
38   my ( $self, $c ) = @_;
39
40   # XXX A slippery slope to be sure.
41   if($c->req->param('a')) {
42     my $capture = IO::Capture::Stdout->new();
43     $capture->start();
44     eval {
45       my $action = gitweb::main($c);
46       $action->();
47     };
48     $capture->stop();
49     
50     use Data::Dumper;
51     die Dumper($@)
52       if $@;
53   
54     my $output = join '', $capture->read;
55     $c->stash->{gitweb_output} = $output;
56     $c->stash->{template} = 'gitweb.tt2';
57   }
58 }
59
60 =head2 index
61
62 Provides the project listing.
63
64 =cut
65
66 sub index :Path :Args(0) {
67   my ( $self, $c ) = @_;
68
69   # Leave actions up to gitweb at this point.
70   return $self->run_gitweb($c)
71     if $c->req->param('a');
72
73   my $list = $c->model('Git')->list_projects;
74   unless(@$list) {
75     die "No projects found";
76   }
77
78   $c->stash(
79     searchtext => $c->req->param('searchtext') || '',
80     projects   => $list,
81     action     => 'index',
82   );
83 }
84
85 =head2 blob
86
87 The blob action i.e the contents of a file.
88
89 =cut
90
91 sub blob : Local {
92   my ( $self, $c ) = @_;
93
94   $c->stash(
95     blob   => $c->model('Git')->get_object($c->req->param('h'))->content,
96     action => 'blob',
97   );
98
99   $c->forward('View::Syntax')
100     if $c->req->param('f') and $c->req->param('f') =~ /\.p[lm]$/;
101 }
102
103 =head2 reflog
104
105 Expose the local reflog. This may go away.
106
107 =cut
108
109 sub reflog : Local {
110   my ( $self, $c ) = @_;
111
112   my @log = $c->model('Git')->reflog(
113       '--since=yesterday'
114   );
115
116   $c->stash(
117       log    => \@log,
118       action => 'reflog',
119   );
120 }
121
122 =head2 commit
123
124 Exposes a given commit. Probably too simple currently.
125
126 =cut
127
128 sub commit : Local {
129   my ( $self, $c ) = @_;
130
131   $c->stash(
132       commit => $c->model('Git')->get_object($c->req->param('h')),
133       action => 'commit',
134   );
135 }
136
137 =head2 auto
138
139 Populate the header and footer. Perhaps not the best location.
140
141 =cut
142
143 sub auto : Private {
144     my($self, $c) = @_;
145
146     # XXX Temp hack until a decent solution is invented.
147     $c->model('Git')->project($c->req->param('p'))
148       if $c->req->param('p');
149
150     # Yes, this is hideous.
151     $self->header($c);
152     $self->footer($c);
153 }
154
155 # XXX This could probably be dropped altogether.
156 use Gitalist::Util qw(to_utf8);
157 # Formally git_header_html
158 sub header {
159   my($self, $c) = @_;
160
161   my $title = $c->config->{sitename};
162
163   my $project   = $c->req->param('project')  || $c->req->param('p');
164   my $action    = $c->req->param('action')   || $c->req->param('a');
165   my $file_name = $c->req->param('filename') || $c->req->param('f');
166   if(defined $project) {
167     $title .= " - " . to_utf8($project);
168     if (defined $action) {
169       $title .= "/$action";
170       if (defined $file_name) {
171         $title .= " - " . $file_name;
172         if ($action eq "tree" && $file_name !~ m|/$|) {
173           $title .= "/";
174         }
175       }
176     }
177   }
178
179   $c->stash->{version}     = $c->config->{version};
180   $c->stash->{git_version} = $c->model('Git')->run_cmd('--version');
181   $c->stash->{title}       = $title;
182
183   #$c->stash->{baseurl} = $ENV{PATH_INFO} && uri_escape($base_url);
184   $c->stash->{stylesheet} = $c->config->{stylesheet} || 'gitweb.css';
185
186   $c->stash->{project} = $project;
187   my @links;
188   if($project) {
189     my %href_params = $self->feed_info($c);
190     $href_params{'-title'} ||= 'log';
191
192     foreach my $format qw(RSS Atom) {
193       my $type = lc($format);
194       push @links, {
195         rel   => 'alternate',
196         title => "$project - $href_params{'-title'} - $format feed",
197
198         # XXX A bit hacky and could do with using gitweb::href() features
199         href  => "?a=$type;p=$project",
200         type  => "application/$type+xml"
201         }, {
202         rel   => 'alternate',
203
204         # XXX This duplication also feels a bit awkward
205         title => "$project - $href_params{'-title'} - $format feed (no merges)",
206         href  => "?a=$type;p=$project;opt=--no-merges",
207         type  => "application/$type+xml"
208         };
209     }
210   } else {
211     push @links, {
212       rel => "alternate",
213       title => $c->config->{sitename}." projects list",
214       href => '?a=project_index',
215       type => "text/plain; charset=utf-8"
216       }, {
217       rel => "alternate",
218       title => $c->config->{sitename}." projects feeds",
219       href => '?a=opml',
220       type => "text/plain; charset=utf-8"
221       };
222   }
223
224   $c->stash->{favicon} = $c->config->{favicon};
225
226   # </head><body>
227
228   $c->stash(
229     logo_url      => $c->config->{logo_url},
230     logo_label    => $c->config->{logo_label},
231     logo_img      => $c->config->{logo},
232     home_link     => $c->config->{home_link},
233     home_link_str => $c->config->{home_link_str},
234     );
235
236   if(defined $project) {
237     $c->stash(
238       search_text => ( $c->req->param('s') || $c->req->param('searchtext') ),
239       search_hash => ( $c->req->param('hb') || $c->req->param('hashbase')
240           || $c->req->param('h')  || $c->req->param('hash')
241           || 'HEAD' ),
242       );
243   }
244 }
245
246 # Formally git_footer_html
247 sub footer {
248   my($self, $c) = @_;
249
250   my $feed_class = 'rss_logo';
251
252   my @feeds;
253   my $project = $c->req->param('project')  || $c->req->param('p');
254   if(defined $project) {
255     (my $pstr = $project) =~ s[/?\.git$][];
256     my $descr = $c->model('Git')->project_info($project)->{description};
257     $c->stash->{project_description} = defined $descr
258       ? $descr
259       : '';
260
261     my %href_params = $self->feed_info($c);
262     if (!%href_params) {
263       $feed_class .= ' generic';
264     }
265     $href_params{'-title'} ||= 'log';
266
267     @feeds = [
268       map +{
269         class => $feed_class,
270         title => "$href_params{'-title'} $_ feed",
271         href  => "/?p=$project;a=\L$_",
272         name  => lc $_,
273         }, qw(RSS Atom)
274       ];
275   } else {
276     @feeds = [
277       map {
278         class => $feed_class,
279           title => '',
280           href  => "/?a=$_->[0]",
281           name  => $_->[1],
282         }, [opml=>'OPML'],[project_index=>'TXT'],
283       ];
284   }
285 }
286
287 # XXX This feels wrong here, should probably be refactored.
288 # returns hash to be passed to href to generate gitweb URL
289 # in -title key it returns description of link
290 sub feed_info {
291   my($self, $c) = @_;
292
293   my $format = shift || 'Atom';
294   my %res = (action => lc($format));
295
296   # feed links are possible only for project views
297   return unless $c->req->param('project');
298
299   # some views should link to OPML, or to generic project feed,
300   # or don't have specific feed yet (so they should use generic)
301   return if $c->req->param('action') =~ /^(?:tags|heads|forks|tag|search)$/x;
302
303   my $branch;
304   my $hash = $c->req->param('h')  || $c->req->param('hash');
305   my $hash_base = $c->req->param('hb') || $c->req->param('hashbase');
306
307   # branches refs uses 'refs/heads/' prefix (fullname) to differentiate
308   # from tag links; this also makes possible to detect branch links
309   if ((defined $hash_base && $hash_base =~ m!^refs/heads/(.*)$!) ||
310     (defined $hash      && $hash      =~ m!^refs/heads/(.*)$!)) {
311     $branch = $1;
312   }
313
314   # find log type for feed description (title)
315   my $type = 'log';
316   my $file_name = $c->req->param('f') || $c->req->param('filename');
317   if (defined $file_name) {
318     $type  = "history of $file_name";
319     $type .= "/" if $c->req->param('action') eq 'tree';
320     $type .= " on '$branch'" if (defined $branch);
321   } else {
322     $type = "log of $branch" if (defined $branch);
323   }
324
325   $res{-title} = $type;
326   $res{'hash'} = (defined $branch ? "refs/heads/$branch" : undef);
327   $res{'file_name'} = $file_name;
328
329   return %res;
330 }
331 =head2 end
332
333 Attempt to render a view, if needed.
334
335 =cut
336
337 sub end : ActionClass('RenderView') {}
338
339 =head1 AUTHOR
340
341 Dan Brook,,,
342
343 =head1 LICENSE
344
345 This library is free software. You can redistribute it and/or modify
346 it under the same terms as Perl itself.
347
348 =cut
349
350 __PACKAGE__->meta->make_immutable;