source: server/trunk/web/lib/Sophie/Controller/Rpms.pm @ 265

Last change on this file since 265 was 241, checked in by nanardon, 13 years ago
  • compat URL rpms/query
File size: 16.4 KB
Line 
1package Sophie::Controller::Rpms;
2use Moose;
3use namespace::autoclean;
4use Encode::Guess;
5use Encode;
6use POSIX;
7
8BEGIN {extends 'Catalyst::Controller'; }
9
10=head1 NAME
11
12Sophie::Controller::Rpms - Catalyst Controller
13
14=head1 DESCRIPTION
15
16Catalyst Controller.
17
18=head1 METHODS
19
20=cut
21
22
23=head2 index
24
25=cut
26
27sub index :Path :Args(0) {
28    my ( $self, $c ) = @_;
29
30    $c->response->redirect('/');
31}
32
33=head2 rpms.queryformat( PKGID, FORMAT )
34
35Perform an C<rpm -q --qf> on the package having C<PKGID>.
36
37=cut
38
39sub queryformat : XMLRPCLocal {
40    my ( $self, $c, $pkgid, $qf ) = @_;
41    $c->stash->{xmlrpc} = $c->model('base')->resultset('Rpms')->search(
42        { pkgid => $pkgid },
43        { 
44            select => [ qq{rpmqueryformat("header", ?)} ],
45            as => [ 'qf' ],
46            bind => [ $qf ],
47        }
48    )->next->get_column('qf');
49}
50
51=head2 rpms.tag( PKGID, TAG )
52
53Return the list of C<TAG> values for package C<PKGID>
54
55=cut
56
57sub tag : XMLRPCLocal {
58    my ( $self, $c, $pkgid, $tag ) = @_;
59    $c->stash->{xmlrpc} = [ map { $_->get_column('tag') } $c->model('Base')->resultset('Rpms')->search(
60        { pkgid => $pkgid },
61        { 
62            select => [ qq{rpmquery("header", rpmtag(?))} ],
63            as => [ 'tag' ],
64            bind => [ $tag ], 
65        }
66    )->all ]
67}
68
69=head2 rpms.basicinfo( PKGID )
70
71Return a struct about basic informations about rpm having pkgid C<PKGID>.
72
73Example of information return:
74
75    {
76          'arch' => 'x86_64',
77          'version' => '0.0.3',
78          'src' => '1',
79          'issrc' => '1',
80          'name' => 'ecap-samples',
81          'release' => '1mdv2010.2',
82          'description' => 'The sample contains three basic adapters.',
83          'pkgid' => 'aa17ce95dd816e0817da78d7af54abdb',
84          'summary' => 'Simple ecap samples',
85          'filename' => 'ecap-samples-0.0.3-1mdv2010.2.src.rpm',
86          'evr' => '0.0.3-1mdv2010.2'
87    };
88
89=head2 Url: /rpms/<PKGID>/basicinfo?json
90
91Return a struct about basic informations about rpm having pkgid C<PKGID>.
92
93Example of information return:
94
95    {
96          'arch' => 'x86_64',
97          'version' => '0.0.3',
98          'src' => '1',
99          'issrc' => '1',
100          'name' => 'ecap-samples',
101          'release' => '1mdv2010.2',
102          'description' => 'The sample contains three basic adapters.',
103          'pkgid' => 'aa17ce95dd816e0817da78d7af54abdb',
104          'summary' => 'Simple ecap samples',
105          'filename' => 'ecap-samples-0.0.3-1mdv2010.2.src.rpm',
106          'evr' => '0.0.3-1mdv2010.2'
107    };
108
109NB: This url works only in JSON format.
110
111=cut
112
113sub basicinfo :XMLRPCLocal :Chained('rpms_') :PathPart('basicinfo') :Args(0) {
114    my ($self, $c, $pkgid) = @_;
115    $pkgid ||= $c->stash->{pkgid};
116
117    my $rpm = $c->model('base::Rpms')->find(
118        { pkgid => $pkgid },
119    );
120    $rpm or return;
121    my %info = $rpm->get_columns;
122    $info{src} = $info{issrc} ? 1 : 0;
123    foreach (qw(version release arch)) {
124        if (my $r = $c->model('base')->resultset('Rpms')->search(
125            { pkgid => $pkgid },
126            { 
127                select => [ qq{rpmquery("header", ?)} ],
128                as => [ 'qf' ],
129                bind => [ $_ ],
130            }
131            )->next) { 
132            $info{$_} = $r->get_column('qf');
133        }
134    }
135    $info{filename} = $c->forward('queryformat',
136        [ $pkgid,
137            '%{NAME}-%{VERSION}-%{RELEASE}.%|SOURCERPM?{%{ARCH}}:{src}|.rpm'
138        ]);
139
140    return $c->stash->{xmlrpc} = \%info;
141}
142
143=head2 rpms.info( PKGID )
144
145Like rpms.basicinfo return a struct containing single information about the request rpm.
146
147=head2 Url: /rpms/<PKGID>/info?json
148
149Like rpms/<PKGID>basicinfo return a struct containing single information about the request rpm.
150
151NB: This url works only in JSON format.
152
153=cut
154
155sub info : XMLRPCLocal :Chained('rpms_') :PathPart('info') :Args(0) {
156    my ($self, $c, $pkgid) = @_;
157    $pkgid ||= $c->stash->{pkgid};
158
159    my $info = $c->forward('basicinfo', [ $pkgid ]);
160    foreach (qw(name epoch url group size packager
161                url sourcerpm license buildhost
162                distribution)) {
163        if (my $r = $c->model('base')->resultset('Rpms')->search(
164            { pkgid => $pkgid },
165            { 
166                select => [ qq{rpmquery("header", ?)} ],
167                as => [ 'qf' ],
168                bind => [ $_ ],
169            }
170            )->next) { 
171            $info->{$_} = $r->get_column('qf');
172        }
173    }
174
175    return $c->stash->{xmlrpc} = $info;
176}
177
178=head2 rpms.dependency(PKGID, DEPTYPE)
179
180Return a list of C<DEPTYPE> dependencies for package C<PKGID> where C<DEPTYPE>
181is one of:
182
183=over 4
184
185=item C<P> for Provides
186
187=item C<R> for Requires
188
189=item C<C> for Conflicts
190
191=item C<O> for Obsoletes
192
193=item C<E> for Enhanced
194
195=item C<S> for Suggests
196
197=back
198
199=cut
200
201sub xmlrpc_dependency : XMLRPCPath('dependency') {
202    my ($self, $c, @args) = @_;
203    $c->forward('dependency', [ @args ]);
204}
205
206=head2 Url: /rpms/<PKGID>/dependency/<DEPTYPE>?json
207
208Return a list of C<DEPTYPE> dependencies for package C<PKGID> where C<DEPTYPE>
209is one of:
210
211=over 4
212
213=item C<P> for Provides
214
215=item C<R> for Requires
216
217=item C<C> for Conflicts
218
219=item C<O> for Obsoletes
220
221=item C<E> for Enhanced
222
223=item C<S> for Suggests
224
225=back
226
227=cut
228
229sub dependency :XMLRPC :Chained('rpms_') :PathPart('dependency') :Args(1) {
230    my ($self, $c, $pkgid, $deptype) = @_;
231    if (!$deptype) {
232        $deptype = $pkgid;
233        $pkgid = $c->stash->{pkgid};
234    }
235
236    $c->stash->{xmlrpc} = [ 
237        map { 
238            { 
239                name => $_->get_column('depname'),
240                flags => $_->get_column('flags'),
241                evr => $_->get_column('evr'),
242                sense => $_->get_column('sense'),
243            }
244        } 
245        $c->model('Base')->resultset('Deps')->search(
246            { 
247                pkgid => $pkgid,
248                deptype => $deptype,
249            },
250            { 
251                order_by => [ 'count' ],
252                select => [ 'rpmsenseflag("flags")', qw(depname flags evr) ],
253                as => [ qw'sense depname flags evr' ],
254
255            },
256        )->all ];
257}
258
259sub sources : XMLRPCLocal {
260    my ( $self, $c, $pkgid ) = @_;
261
262    my $sourcerpm = $c->forward('queryformat', [ $pkgid, '%{SOURCERPM}' ]);
263    my $nosourcerpm = $sourcerpm;
264    $nosourcerpm =~ s/\.src.rpm$/\.nosrc.rpm/;
265
266    $c->stash->{xmlrpc} = [ $c->model('Base::Rpms')->search(
267        {
268            pkgid => { 
269                IN => $c->model('Base::RpmFile')->search(
270                    { filename => [ $sourcerpm, $nosourcerpm ] }
271                )->get_column('pkgid')->as_query
272            },
273        }
274    )->get_column('pkgid')->all ];
275}
276
277sub binaries : XMLRPCLocal {
278    my ( $self, $c, $pkgid ) = @_;
279
280    my $sourcerpm = $c->forward('queryformat', [ $pkgid,
281            '%{NAME}-%{VERSION}-%{RELEASE}.src.rpm' ]);
282    my $nosourcerpm = $sourcerpm;
283    $nosourcerpm =~ s/\.src.rpm$/\.nosrc.rpm/;
284
285    my $tagrs = $c->model('Base')->resultset('Tags')
286        ->search({ tagname => 'sourcerpm', value => [ $sourcerpm, $nosourcerpm ] })
287        ->get_column('pkgid');
288    $c->stash->{xmlrpc} = [ $c->model('Base::Rpms')->search(
289        {
290            -and => [
291                { issrc => 0 },
292                { pkgid =>
293                    { IN => $tagrs->as_query, },
294                },
295            ]
296        },
297        {
298            order_by => [ qw(arch name), 'evr using >>' ],
299        },
300    )->get_column('pkgid')->all ];
301
302}
303
304
305=head2 rpms.maintainers( PKGID )
306
307Return the maintainers for this package.
308
309The list of maintainers is limited to distribution where the package is located.
310
311If the package is a binary the C<SOURCERPM> tag is used to find the source rpm
312name.
313
314=cut
315
316sub maintainers : XMLRPCLocal {
317    my ($self, $c, $pkgid) = @_;
318
319    my $binfo = $c->forward('/rpms/basicinfo', [ $pkgid ]);
320    my $rpmname;
321    if ($binfo->{issrc}) {
322        $rpmname = $binfo->{name};
323    } else {
324        my $sourcerpm = $c->forward('queryformat', [ $pkgid, '%{SOURCERPM}' ]);
325        $sourcerpm =~ /^(.*)-([^-]+)-([^-]+)\.[^\.]+.rpm$/;
326        $rpmname = $1;
327    }
328    my %dist;
329    foreach (@{ $c->forward('/rpms/location', [ $pkgid ]) }) {
330        $dist{$_->{distribution}} = 1;
331    }
332
333    $c->forward('/maintainers/byrpm', [ $rpmname, [ keys %dist ] ]);
334}
335
336sub rpms_ :PathPrefix :Chained :CaptureArgs(1) {
337    my ( $self, $c, $pkgid ) = @_;
338    $c->stash->{pkgid} = $pkgid if($pkgid);
339    {
340        my $match = $c->stash->{pkgid};
341    }
342    if (!$c->model('Base::Rpms')->find({ pkgid => $c->stash->{pkgid} })) {
343        $c->go('/404/index');
344    }
345    my $info = $c->stash->{rpms}{info} =
346        $c->forward('info', [ $c->stash->{pkgid} ]);
347
348    $c->stash->{metatitle} = sprintf("%s-%s %s",
349        $info->{name},
350        $info->{evr},
351        $info->{issrc} ? 'src' : $info->{arch},
352    );
353    push(@{ $c->stash->{keywords} }, $info->{name}, $info->{evr},
354        $info->{issrc} ? 'src' : $info->{arch},);
355    $c->stash->{metarevisit} = 30;
356
357    # for later usage, keep history of visited rpms
358    $c->session->{visited_rpms}{$c->stash->{pkgid}} = time;
359    if (keys %{ $c->session->{visited_rpms} } > 20) {
360        my @visited = sort
361        { $c->session->{visited_rpms}{$b} <=> $c->session->{visited_rpms}{$a} }
362        keys %{ $c->session->{visited_rpms} };
363        splice(@visited, 0, 20);
364        delete $c->session->{visited_rpms}{$_} foreach (@visited);
365    }
366
367    $c->stash->{rpms}{location} =
368        $c->forward('location', [ $c->stash->{pkgid} ]);
369}
370
371sub rpms : Private {
372    my ( $self, $c, $pkgid, $subpart, @args) = @_;
373    # Because $c->forward don't take into account Chained sub
374    $c->forward('rpms_', [ $pkgid ]);
375    for ($subpart || '') {
376        /^deps$/       and $c->go('deps',        [ $pkgid, @args ]);
377        /^files$/      and $c->go('files',       [ $pkgid, @args ]);
378        /^changelog$/  and $c->go('changelog',   [ $pkgid, @args ]);
379        /^location$/   and $c->go('location',    [ $pkgid, @args ]);
380        /^basicinfo$/  and $c->go('basicinfo',   [ $pkgid, @args ]);
381        /^info$/       and $c->go('info',        [ $pkgid, @args ]);
382        /^analyse$/    and $c->go('analyse',     [ $pkgid, @args ]);
383        /^dependency$/ and $c->go('dependency',  [ $pkgid, @args ]);
384        /^query$/      and $c->go('query',       [ $pkgid, @args ]);
385        /./            and $c->go('/404/index'); # other subpart dont exists
386    }
387    $c->stash->{rpmurl} = $c->req->path;
388
389    return $c->stash->{xmlrpc} = $c->stash->{rpms};
390}
391
392sub rpms__ : Chained('/rpms/rpms_') :PathPart('') :Args(0) :XMLRPCLocal {
393    my ( $self, $c ) = @_;
394
395    $c->go('rpms', [ $c->stash->{pkgid} ]);
396}
397
398
399sub deps :Chained('rpms_') :PathPart('deps') :Args(0) :XMLRPCLocal {
400    my ( $self, $c, $pkgid ) = @_;
401    $c->stash->{rpmurl} = ($c->req->path =~ m:(.*)/[^/]+:)[0];
402    $pkgid ||= $c->stash->{pkgid};
403
404    my %deps;
405    foreach (
406        $c->model('Base')->resultset('Deps')->search(
407            { 
408                pkgid => $pkgid,
409            },
410            { 
411                order_by => [ 'count' ],
412                select => [ 'rpmsenseflag("flags")',
413                    qw(depname flags evr deptype) ],
414                as => [ qw'sense depname flags evr deptype' ],
415
416            },
417        )->all) {
418        push( @{ $deps{$_->get_column('deptype')} },
419            {
420                name => $_->get_column('depname'),
421                flags => $_->get_column('flags'),
422                evr => $_->get_column('evr'),
423                sense => $_->get_column('sense'),
424            }
425        );
426    }
427    $c->stash->{xmlrpc} = \%deps;
428}
429
430sub files :Chained('rpms_') :PathPart('files') :Args(0) :XMLRPCLocal {
431    my ( $self, $c, $pkgid, $number ) = @_;
432    $c->stash->{rpmurl} = ($c->req->path =~ m:(.*)/[^/]+:)[0];
433    $pkgid ||= $c->stash->{pkgid};
434
435    if ($number) { # This come from a forward
436        $c->go('files_contents', [ $number ]);
437    }
438
439    my @col = qw(dirname basename md5 size count);
440    $c->stash->{xmlrpc} = [ map {
441        {
442            filename => $_->get_column('dirname') . $_->get_column('basename'),
443            dirname => $_->get_column('dirname'),
444            basename => $_->get_column('basename'),
445            md5 => $_->get_column('md5'),
446            perm => $_->get_column('perm'),
447            size => $_->get_column('size'),
448            user => $_->get_column('user'),
449            group => $_->get_column('group'),
450            has_content => $_->get_column('has_content'),
451            count => $_->get_column('count'),
452        }
453    } $c->model('Base')->resultset('Files')->search(
454            { 
455                pkgid => $pkgid,
456            },
457            { 
458                'select' => [ 'contents is NOT NULL as has_content', 'rpmfilesmode(mode) as perm', @col, '"group"',
459                    '"user"' ],
460                as => [ qw(has_content perm), @col, 'group', 'user' ],
461                order_by => [ 'dirname', 'basename' ],
462
463            },
464        )->all ];
465}
466
467sub files_contents :Chained('rpms_') :PathPart('files') :Args(1) {
468    my ( $self, $c, $number ) = @_;
469    $c->stash->{rpmurl} = ($c->req->path =~ m:(.*)/[^/]+/[^/]+:)[0];
470    my $pkgid = $c->stash->{pkgid};
471
472    $c->stash->{xmlrpc} = $c->model('Base::Files')->search(
473        {
474            pkgid => $pkgid,
475            count => $number,
476        },
477        {
478            select => ['contents'],
479        }
480    )->get_column('contents')->first;
481}
482
483sub changelog :Chained('rpms_') :PathPart('changelog') :Args(0) :XMLRPCLocal {
484    my ( $self, $c, $pkgid ) = @_;
485    $pkgid ||= $c->stash->{pkgid};
486    $c->stash->{rpmurl} = ($c->req->path =~ m:(.*)/[^/]+:)[0];
487
488    my @ch;
489    foreach ($c->model('Base')->resultset('RpmsChangelog')->search({},
490            { 
491                bind => [ $pkgid ],
492                order_by => [ 'time::int desc' ],
493            },
494        )->all) {
495        my $chentry;
496        my $enc = guess_encoding($_->get_column('text'), qw/latin1/);
497        $chentry->{text} = $enc && ref $enc
498            ? encode('utf8', $_->get_column('text'))
499            : $_->get_column('text');
500        $enc = guess_encoding($_->get_column('name'), qw/latin1/);
501        $chentry->{name} = $enc && ref $enc
502            ? encode('utf8', $_->get_column('name'))
503            : $_->get_column('name');
504        $chentry->{time} = $_->get_column('time');
505        $chentry->{date} = POSIX::strftime('%a %b %e %Y', gmtime($_->get_column('time')));
506        push(@ch, $chentry);
507    }
508
509    $c->stash->{xmlrpc} = \@ch;
510}
511
512=head2 rpms.location( PKGID )
513
514Return all distribution where the package having C<PKGID> can be found.
515
516=cut
517
518sub location :Chained('rpms_') :PathPart('location') :Args(0) {
519    my ( $self, $c, $pkgid ) = @_;
520    $pkgid ||= $c->stash->{pkgid};
521    $c->stash->{rpmurl} = ($c->req->path =~ m:(.*)/[^/]+:)[0];
522
523    $c->stash->{xmlrpc} = [
524        map {
525        {
526            distribution => $_->get_column('name'),
527            dist => $_->get_column('shortname'),
528            release => $_->get_column('version'),
529            arch => $_->get_column('arch'), 
530            media => $_->get_column('label'),
531            media_group => $_->get_column('group_label'),
532        }
533        }
534        $c->forward('/distrib/distrib_rs', [ {} ])
535         ->search_related('MediasPaths')
536                 ->search_related('Paths')
537        ->search_related('Rpmfiles',
538            { pkgid => $pkgid },
539            {
540                select => [ qw(shortname name version arch label group_label) ],
541                order_by => [ qw(name version arch label) ],
542            }
543        )->all ]
544}
545
546sub analyse :Chained('rpms_') :PathPart('analyse') :Args(0) :XMLRPC {
547    my ( $self, $c, $pkgid, $dist ) = @_;
548    $pkgid ||= $c->stash->{pkgid};
549    $c->stash->{rpmurl} = ($c->req->path =~ m:(.*)/[^/]+:)[0];
550    $dist->{distribution} ||= $c->req->param('distribution');
551    $dist->{release} ||= $c->req->param('release');
552    $dist->{arch} ||= $c->req->param('arch');
553
554    if ($c->req->param('analyse')) {
555
556        my @deplist = map {
557            [ $_->{name}, $_->{sense}, $_->{evr} ]
558        } @{ $c->forward('dependency', [ $pkgid, 'R' ]) };
559
560        $c->stash->{xmlrpc} = $c->forward(
561            '/analysis/solver/find_requirements',
562            [ $dist,
563                'P', \@deplist, [] ]
564        );
565    } else {
566        $c->stash->{xmlrpc} = '';
567    }
568}
569
570# compat URL:
571sub query :Chained('rpms_') :PathPart('analyse') :Args(0) :XMLRPC {
572    my ( $self, $c, $pkgid, $dist ) = @_;
573    $pkgid ||= $c->stash->{pkgid};
574    $c->stash->{rpmurl} = ($c->req->path =~ m:(.*)/[^/]+:)[0];
575    $c->res->redirect($c->uri_for('/', $c->stash->{rpmurl}, 'analyse'));
576}
577
578=head1 AUTHOR
579
580Olivier Thauvin
581
582=head1 LICENSE
583
584This library is free software. You can redistribute it and/or modify
585it under the same terms as Perl itself.
586
587=cut
588
589__PACKAGE__->meta->make_immutable;
590
5911;
Note: See TracBrowser for help on using the repository browser.