diff options
Diffstat (limited to 'extensions/ProdCompSearch')
-rw-r--r-- | extensions/ProdCompSearch/Config.pm | 15 | ||||
-rw-r--r-- | extensions/ProdCompSearch/Extension.pm | 21 | ||||
-rw-r--r-- | extensions/ProdCompSearch/lib/WebService.pm | 99 | ||||
-rw-r--r-- | extensions/ProdCompSearch/template/en/default/pages/prodcompsearch.html.tmpl | 23 | ||||
-rw-r--r-- | extensions/ProdCompSearch/template/en/default/prodcompsearch/form.html.tmpl | 19 | ||||
-rw-r--r-- | extensions/ProdCompSearch/web/images/throbber.gif | bin | 0 -> 723 bytes | |||
-rw-r--r-- | extensions/ProdCompSearch/web/js/prod_comp_search.js | 113 | ||||
-rw-r--r-- | extensions/ProdCompSearch/web/styles/prod_comp_search.css | 20 |
8 files changed, 310 insertions, 0 deletions
diff --git a/extensions/ProdCompSearch/Config.pm b/extensions/ProdCompSearch/Config.pm new file mode 100644 index 000000000..c28b6d8f6 --- /dev/null +++ b/extensions/ProdCompSearch/Config.pm @@ -0,0 +1,15 @@ +# 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; +use strict; + +use constant NAME => 'ProdCompSearch'; +use constant REQUIRED_MODULES => []; +use constant OPTIONAL_MODULES => []; + +__PACKAGE__->NAME; diff --git a/extensions/ProdCompSearch/Extension.pm b/extensions/ProdCompSearch/Extension.pm new file mode 100644 index 000000000..a5955fd8b --- /dev/null +++ b/extensions/ProdCompSearch/Extension.pm @@ -0,0 +1,21 @@ +# 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; +use strict; +use base qw(Bugzilla::Extension); + +our $VERSION = '1'; + +sub webservice { + my ($self, $args) = @_; + my $dispatch = $args->{dispatch}; + $dispatch->{PCS} = "Bugzilla::Extension::ProdCompSearch::WebService"; +} + + +__PACKAGE__->NAME; diff --git a/extensions/ProdCompSearch/lib/WebService.pm b/extensions/ProdCompSearch/lib/WebService.pm new file mode 100644 index 000000000..dea2e5e51 --- /dev/null +++ b/extensions/ProdCompSearch/lib/WebService.pm @@ -0,0 +1,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 => 'PCS.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; diff --git a/extensions/ProdCompSearch/template/en/default/pages/prodcompsearch.html.tmpl b/extensions/ProdCompSearch/template/en/default/pages/prodcompsearch.html.tmpl new file mode 100644 index 000000000..533ade888 --- /dev/null +++ b/extensions/ProdCompSearch/template/en/default/pages/prodcompsearch.html.tmpl @@ -0,0 +1,23 @@ +[%# 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. + #%] + +[% PROCESS global/variables.none.tmpl %] + +[% PROCESS global/header.html.tmpl + title = "File a $terms.Bug" + javascript_urls = [ "extensions/ProdCompSearch/web/js/prod_comp_search.js" ] + style_urls = [ "extensions/ProdCompSearch/web/styles/prod_comp_search.css" ] +%] + +<div id="prod_comp_search_main"> + [% PROCESS prodcompsearch/form.html.tmpl + query_header = "File a $terms.Bug:" + %] +</div> + +[% PROCESS global/footer.html.tmpl %] diff --git a/extensions/ProdCompSearch/template/en/default/prodcompsearch/form.html.tmpl b/extensions/ProdCompSearch/template/en/default/prodcompsearch/form.html.tmpl new file mode 100644 index 000000000..9d75f66f1 --- /dev/null +++ b/extensions/ProdCompSearch/template/en/default/prodcompsearch/form.html.tmpl @@ -0,0 +1,19 @@ +[%# 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. + #%] + +<div id="prod_comp_search_form" class="yui3-skin-sam"> + <div id="prod_comp_search_header"> + [% input_label FILTER none %] + <img id="prod_comp_throbber" src="extensions/ProdCompSearch/web/images/throbber.gif" + class="bz_default_hidden" width="16" height="11"> + <span id="prod_comp_no_components" class="bz_default_hidden"> + No components found</span> + </div> + <input id="prod_comp_search" type="text" size="50" + placeholder="Search by product and component keywords"> +</div> diff --git a/extensions/ProdCompSearch/web/images/throbber.gif b/extensions/ProdCompSearch/web/images/throbber.gif Binary files differnew file mode 100644 index 000000000..bc4fa6561 --- /dev/null +++ b/extensions/ProdCompSearch/web/images/throbber.gif diff --git a/extensions/ProdCompSearch/web/js/prod_comp_search.js b/extensions/ProdCompSearch/web/js/prod_comp_search.js new file mode 100644 index 000000000..bc0f41a2b --- /dev/null +++ b/extensions/ProdCompSearch/web/js/prod_comp_search.js @@ -0,0 +1,113 @@ +/* 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. */ + +// Product and component search to file a new bug + +var ProdCompSearch = { + script_name: 'enter_bug.cgi', + script_choices: ['enter_bug.cgi', 'describecomponents.cgi'], + format: null, + cloned_bug_id: null +}; + +YUI({ + base: 'js/yui3/', + combine: false +}).use("node", "json-stringify", "autocomplete", "escape", + "datasource-io", "datasource-jsonschema", function (Y) { + var counter = 0, + dataSource = null, + autoComplete = null; + + var resultListFormat = function(query, results) { + return Y.Array.map(results, function(result) { + var data = result.raw; + result.text = data.product + ' :: ' + data.component; + return Y.Escape.html(result.text); + }); + }; + + var requestTemplate = function(query) { + counter = counter + 1; + var json_object = { + version: "1.1", + method : "PCS.prod_comp_search", + id : counter, + params : { search: query } + }; + return Y.JSON.stringify(json_object); + }; + + var dataSource = new Y.DataSource.IO({ + source: 'jsonrpc.cgi', + ioConfig: { + method: "POST", + headers: { 'Content-Type': 'application/json' } + } + }); + + dataSource.plug(Y.Plugin.DataSourceJSONSchema, { + schema: { + resultListLocator : "result.products", + resultFields : [ "product", "component" ] + } + }); + + var input = Y.one('#prod_comp_search'); + + input.plug(Y.Plugin.AutoComplete, { + activateFirstItem: false, + enableCache: true, + source: dataSource, + minQueryLength: 3, + queryDelay: 0.05, + resultFormatter: resultListFormat, + suppressInputUpdate: true, + maxResults: 25, + scrollIntoView: true, + requestTemplate: requestTemplate, + on: { + query: function(e) { + Y.one("#prod_comp_throbber").removeClass('bz_default_hidden'); + Y.one("#prod_comp_no_components").addClass('bz_default_hidden'); + }, + results: function(e) { + Y.one("#prod_comp_throbber").addClass('bz_default_hidden'); + input.ac.set('activateFirstItem', e.results.length == 1); + if (e.results.length == 0) { + Y.one("#prod_comp_no_components").removeClass('bz_default_hidden'); + } + }, + select: function(e) { + // Only redirect if the script_name is a valid choice + if (Y.Array.indexOf(ProdCompSearch.script_choices, ProdCompSearch.script_name) == -1) + return; + + var data = e.result.raw; + var url = ProdCompSearch.script_name + + "?product=" + encodeURIComponent(data.product) + + "&component=" + encodeURIComponent(data.component); + if (ProdCompSearch.script_name == 'enter_bug.cgi') { + if (ProdCompSearch.format) + url += "&format=" + encodeURIComponent(ProdCompSearch.format); + if (ProdCompSearch.cloned_bug_id) + url += "&cloned_bug_id=" + encodeURIComponent(ProdCompSearch.cloned_bug_id); + } + if (ProdCompSearch.script_name == 'describecomponents.cgi') { + url += "#" + encodeURIComponent(data.component); + } + window.location.href = url; + } + } + }); + + input.on('focus', function (e) { + if (e.target.value && e.target.value.length > 3) { + dataSource.load(e.target.value); + } + }); +}); diff --git a/extensions/ProdCompSearch/web/styles/prod_comp_search.css b/extensions/ProdCompSearch/web/styles/prod_comp_search.css new file mode 100644 index 000000000..71829023c --- /dev/null +++ b/extensions/ProdCompSearch/web/styles/prod_comp_search.css @@ -0,0 +1,20 @@ +/* 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. */ + +#prod_comp_search_main { + width: 400px; + margin-right: auto; + margin-left: auto; +} + +#prod_comp_search_form .yui3-aclist-input { + width: 360px; +} + +#prod_comp_no_components { + color: red; +} |