1 package WWW::Bugzilla::Search;
3 $WWW::Bugzilla::Search::VERSION = '0.2';
8 use Carp qw(croak carp);
10 use vars qw($VERSION @ISA @EXPORT);
15 WWW::Bugzilla::Search - Handles searching bugzilla bugs via WWW::Mechanize.
19 use WWW::Bugzilla::Search;
22 my $search = WWW::Bugzilla::Search->new(
23 server => 'bugs.example.com',
24 email => 'user@example.com',
25 password => 'my_passwd',
28 $search->summary('This is my summary');
29 my @bugs = $search->search();
33 WWW::Bugzilla::Search provides an API to search for bugs in a Bugzilla database. Any resulting bugs will be returned as instances of WWW::Bugzilla bugs.
37 =head2 Multiple choice search criteria
39 The following fields are multiple choice fields:
41 classification, component, op_sys, priority, product, resolution, bug_severity, bug_status, target_milestone, version, hardware, rep_platform
43 Available options can be retrieved via:
47 To choose a given value, use:
49 $search->field('value');
51 =head2 Text search criteria
53 The following fields are avaiilable for text searching:
55 assigned_to, reporter, summary
57 To searc using a given field, use:
59 $search->field('value');
65 Searches Bugzilla with the defined criteria. Returns a list of bugs that match the criteria. Each bug is a seperate instance of WWW::Bugzilla
69 Resets all search criteria.
80 # my $name = $AUTOLOAD;
82 # warn Dumper($self->{'_fields'});
83 # if ($self->{'_fields'}{$name}) {
84 # $name =~ s/^WWW::Bugzilla::Search:://;
85 # my $out =$self->_field_values($name);
86 # # warn Dumper($out);
90 # return $self->{$name} = shift;
92 # if (defined($self->{$name})) {
93 # return $self->{$name};
104 my $class = ref($that) || $that;
115 foreach my $field (qw(mech protocol server email password)) {
116 *{ $class . '::' . $field } = sub { my ($self, $value) = @_; if (defined $value) { $self->{$field} = $value; } return $self->{$field} }
120 foreach my $field (qw(classification component op_sys priority product resolution bug_severity bug_status target_milestone version hardware rep_platform)) {
121 *{ $class . '::' . $field } = sub {
122 my ($self, $value) = @_;
123 if (defined $value) {
124 $self->{'search_keys'}{$field} = $value;
127 return $self->_field_values($field);
132 # search fields that are used as accessors
133 foreach my $field (qw(assigned_to reporter summary)) {
134 *{ $class . '::' . $field } = sub { my ($self, $value) = @_; if (defined $value) { $self->{'search_keys'}{$field} = $value; } return $self->{'search_keys'}{$field} }
141 while (my ($k, $v) = each %conf) {
146 $self->protocol('http') if !$self->protocol();
148 $self->{'mech'} = WWW::Mechanize->new();
155 my ($self, $name) = @_;
156 my $url = $self->protocol().'://'.$self->server().'/query.cgi?format=advanced';
157 my $mech = $self->{'mech'};
158 if ($mech->{'uri'} ne $url) {
161 $mech->form_name('queryform');
165 my $form = $mech->current_form();
166 foreach my $field ($form->inputs()) {
167 if ($field->name && $field->name eq $name) {
168 push (@values, grep { defined $_ }$field->possible_values());
172 return grep { defined $_ } @values;
174 warn "no values for $name";
181 $self->{'search_keys'} = {};
186 my $mech = $self->{'mech'};
187 my $login_page = $self->protocol().'://'.$self->server().'/query.cgi?format=advanced';
188 $mech->get( $login_page );
189 $mech->form_name('queryform');
191 foreach my $key (keys %{ $self->{'search_keys'} }) {
192 my $value = $self->{'search_keys'}{$key};
193 if ($key eq 'summary') {
194 $mech->field('short_desc', $value, 1);
195 } elsif ($key eq 'assigned_to') {
196 $mech->field('email1', $value);
197 $mech->field('emailtype1', 'regexp') if (ref($value) eq 'Regexp');
198 } elsif ($key eq 'reporter') {
199 $mech->field('email2', $value);
200 $mech->field('emailtype2', 'regexp') if (ref($value) eq 'Regexp');
201 $mech->tick('emailreporter2', 1);
202 map($mech->untick($_, 1), qw(emailqa_contact2 emailassigned_to2 emailcc2));
204 if ($self->_field_values($key)) {
205 # ghetto hack. Grr, Mechanize is making each of the form elements a seperate entry.
207 foreach my $input ($mech->current_form()->inputs()) {
208 next if (defined $input->name && $input->name ne $key);
209 foreach my $val ($input->possible_values) {
210 next if !defined $val;
211 if ($value eq $val) {
212 $input->value($value);
213 # $mech->field($key, $value, $i);
219 $mech->field($key, $self->{'search_keys'}{$key});
226 foreach my $link ($mech->links()) {
227 if ($link->url() =~ /^show_bug\.cgi\?id=(\d+)$/) {
228 push (@bugs, WWW::Bugzilla->new( 'server' => $self->{'server'}, 'email' => $self->{'email'}, 'password' => $self->{'password'}, 'bug_number' => $1, 'use_ssl' => ($self->protocol() eq 'https') ? 1 : 0));
234 # based on the current page, set the current form to the first form with a specified field
235 sub _get_form_by_field {
236 my ($self, $field) = @_;
237 croak("invalid field") if !$field;
239 my $mech = $self->{'mech'};
241 foreach my $form ($mech->forms()) {
242 if ($form->find_input($field)) {
243 $mech->form_number($i);
253 my $mech = $self->{'mech'};
254 my $login_page = $self->protocol().'://'.$self->server().'/query.cgi?GoAheadAndLogIn=1';
255 $mech->get( $login_page );
257 # bail unless OK or Redirect happens
258 croak("Cannot open page $login_page") unless ( ($mech->status == '200') or ($mech->status == '404') );
259 croak("Login form is missing") if !$self->_get_form_by_field('Bugzilla_login');
260 $mech->field('Bugzilla_login', $self->email());
261 $mech->field('Bugzilla_password', $self->password());
262 $mech->submit_form();
263 $mech->get( $login_page );
264 croak("Login failed") if $self->_get_form_by_field('Bugzilla_login');
267 =head1 BUGS, IMPROVEMENTS
269 There may well be bugs in this module. Using it as I have, I just have not run
270 into any. In addition, this module does not support ALL of Bugzilla's
271 features. I will consider any patches or improvements, just send me an email
272 at the address listed below.
277 Brian Caswell (bmc@shmoo.com)
279 Portions taken from WWW::Bugzilla, originally written by:
280 Matthew C. Vella (the_mcv@yahoo.com)
284 WWW::Bugzilla::Search - Module providing API to search Bugzilla bugs.
285 Copyright (C) 2006 Brian Caswell (bmc@shmoo.com)
287 Portions Copyright (C) 2003 Matthew C. Vella (the_mcv@yahoo.com)
289 This module is free software; you can redistribute it and/or modify it
290 under the terms of either:
292 a) the GNU General Public License as published by the Free Software
293 Foundation; either version 1, or (at your option) any later version,
296 b) the "Artistic License" which comes with this module.
298 This program is distributed in the hope that it will be useful,
299 but WITHOUT ANY WARRANTY; without even the implied warranty of
300 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See either
301 the GNU General Public License or the Artistic License for more details.
303 You should have received a copy of the Artistic License with this
304 module, in the file ARTISTIC. If not, I'll be glad to provide one.
306 You should have received a copy of the GNU General Public License
307 along with this program; if not, write to the Free Software
308 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307