summaryrefslogtreecommitdiffstats
path: root/extensions/ProdCompSearch/lib/WebService.pm
blob: bbc355e00448c251ec5b818b1eda73b8e3600043 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.

package Bugzilla::Extension::ProdCompSearch::WebService;

use strict;
use warnings;

use base qw(Bugzilla::WebService);

use Bugzilla::Error;
use Bugzilla::Util qw(detaint_natural trick_taint);

sub prod_comp_search {
    my ($self, $params) = @_;
    my $user = Bugzilla->user;
    my $dbh = Bugzilla->switch_to_shadow_db();

    my $search = $params->{'search'};
    $search || ThrowCodeError('param_required',
        { function => 'Bug.prod_comp_search', param => 'search' });

    my $limit = detaint_natural($params->{'limit'})
                ? $dbh->sql_limit($params->{'limit'})
                : '';

    # We do this in the DB directly as we want it to be fast and
    # not have the overhead of loading full product objects

    # All products which the user has "Entry" access to.
    my $enterable_ids = $dbh->selectcol_arrayref(
           'SELECT products.id FROM products
         LEFT JOIN group_control_map
                   ON group_control_map.product_id = products.id
                      AND group_control_map.entry != 0
                      AND group_id NOT IN (' . $user->groups_as_string . ')
            WHERE group_id IS NULL
                  AND products.isactive = 1');

    if (scalar @$enterable_ids) {
        # And all of these products must have at least one component
        # and one version.
        $enterable_ids = $dbh->selectcol_arrayref(
            'SELECT DISTINCT products.id FROM products
              WHERE ' . $dbh->sql_in('products.id', $enterable_ids) .
              ' AND products.id IN (SELECT DISTINCT components.product_id
                                      FROM components
                                     WHERE components.isactive = 1)
                AND products.id IN (SELECT DISTINCT versions.product_id
                                      FROM versions
                                     WHERE versions.isactive = 1)');
    }

    return { products => [] } if !scalar @$enterable_ids;

    my @list;
    foreach my $word (split(/[\s,]+/, $search)) {
        if ($word ne "") {
            my $sql_word = $dbh->quote($word);
            trick_taint($sql_word);
            # note: CONCAT_WS is MySQL specific
            my $field = "CONCAT_WS(' ', products.name, components.name, components.description)";
            push(@list, $dbh->sql_iposition($sql_word, $field) . " > 0");
        }
    }

    my $products = $dbh->selectall_arrayref("
        SELECT products.name AS product,
               components.name AS component
          FROM products
               INNER JOIN components ON products.id = components.product_id
         WHERE (" . join(" AND ", @list) . ")
               AND products.id IN (" . join(",", @$enterable_ids) . ")
      ORDER BY products.name $limit",
        { Slice => {} });

    # To help mozilla staff file bmo administration bugs into the right
    # component, sort bmo in front of bugzilla.
    if ($user->in_group('mozilla-corporation') || $user->in_group('mozilla-foundation')) {
        $products = [
            sort {
                return 1 if $a->{product} eq 'Bugzilla'
                            && $b->{product} eq 'bugzilla.mozilla.org';
                return -1 if $b->{product} eq 'Bugzilla'
                             && $a->{product} eq 'bugzilla.mozilla.org';
                return lc($a->{product}) cmp lc($b->{product})
                       || lc($a->{component}) cmp lc($b->{component});
            } @$products
        ];
    }

    return { products => $products };
}

1;