summaryrefslogtreecommitdiffstats
path: root/extensions/ProdCompSearch
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/ProdCompSearch')
-rw-r--r--extensions/ProdCompSearch/lib/WebService.pm25
-rw-r--r--extensions/ProdCompSearch/template/en/default/pages/prodcompsearch.html.tmpl8
-rw-r--r--extensions/ProdCompSearch/template/en/default/prodcompsearch/form.html.tmpl67
-rw-r--r--extensions/ProdCompSearch/web/js/prod_comp_search.js216
-rw-r--r--extensions/ProdCompSearch/web/styles/prod_comp_search.css22
5 files changed, 163 insertions, 175 deletions
diff --git a/extensions/ProdCompSearch/lib/WebService.pm b/extensions/ProdCompSearch/lib/WebService.pm
index b9a03eb27..3bf0e1377 100644
--- a/extensions/ProdCompSearch/lib/WebService.pm
+++ b/extensions/ProdCompSearch/lib/WebService.pm
@@ -15,10 +15,31 @@ use base qw(Bugzilla::WebService);
use Bugzilla::Error;
use Bugzilla::Util qw(detaint_natural trick_taint trim);
+#############
+# Constants #
+#############
+
use constant PUBLIC_METHODS => qw(
prod_comp_search
);
+sub rest_resources {
+ return [
+ qr{^/prod_comp_search/(.*)$}, {
+ GET => {
+ method => 'prod_comp_search',
+ params => sub {
+ return { search => $_[0] }
+ }
+ }
+ }
+ ]
+}
+
+##################
+# Public Methods #
+##################
+
sub prod_comp_search {
my ($self, $params) = @_;
my $user = Bugzilla->user;
@@ -104,6 +125,10 @@ sub prod_comp_search {
return { products => $products };
}
+###################
+# Private Methods #
+###################
+
sub _build_terms {
my ($query, $product, $component) = @_;
my $dbh = Bugzilla->dbh();
diff --git a/extensions/ProdCompSearch/template/en/default/pages/prodcompsearch.html.tmpl b/extensions/ProdCompSearch/template/en/default/pages/prodcompsearch.html.tmpl
index 5b39315b5..6e28d88e5 100644
--- a/extensions/ProdCompSearch/template/en/default/pages/prodcompsearch.html.tmpl
+++ b/extensions/ProdCompSearch/template/en/default/pages/prodcompsearch.html.tmpl
@@ -10,15 +10,15 @@
[% PROCESS global/header.html.tmpl
title = "File a $terms.Bug"
- javascript_urls = [ "js/yui3/yui/yui-min.js",
- "extensions/ProdCompSearch/web/js/prod_comp_search.js" ]
+ 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:"
- script_name = "enter_bug.cgi"
+ input_label = "File a $terms.Bug:"
+ script_name = "enter_bug.cgi"
+ auto_focus = 1
%]
</div>
diff --git a/extensions/ProdCompSearch/template/en/default/prodcompsearch/form.html.tmpl b/extensions/ProdCompSearch/template/en/default/prodcompsearch/form.html.tmpl
index 38f87dc1a..4239a9738 100644
--- a/extensions/ProdCompSearch/template/en/default/prodcompsearch/form.html.tmpl
+++ b/extensions/ProdCompSearch/template/en/default/prodcompsearch/form.html.tmpl
@@ -6,35 +6,48 @@
# defined by the Mozilla Public License, v. 2.0.
#%]
-[% DEFAULT max_results = 100 %]
-<script type="text/javascript">
- [% IF script_name %]
- ProdCompSearch.script_name = '[% script_name FILTER js %]';
- [% END %]
- [% IF format %]
- ProdCompSearch.format = '[% format FILTER js %]';
- [% END %]
- [% IF cloned_bug_id %]
- ProdCompSearch.cloned_bug_id = '[% cloned_bug_id FILTER js %]';
- [% END %]
- [% IF new_tab %]
- ProdCompSearch.new_tab = true;
- [% END %]
- ProdCompSearch.max_results = [% max_results FILTER js %];
-</script>
+[%#
+ # parameters (all are optional, defaults below)
+ # id : id and prefix of elements
+ # script_name : .cgi to redirect to
+ # max_results : maximum results displayed
+ # input_label : input field label
+ # auto_focus : focus the search form on page load
+ # format : format parameter passed to cgi
+ # cloned_bug_id : cloned_bug_id parameter
+ # new_tab : open in a new tab
+ # anchor_component : append #component to url
+ #%]
+
+[%
+ DEFAULT id = "pcs";
+ DEFAULT max_results = 100;
+ DEFAULT script_name = "enter_bug.cgi";
+%]
-<div id="prod_comp_search_form" class="yui3-skin-sam">
- <div id="prod_comp_search_header">
+<div class="pcs-form">
+ <div class="pcs-header">
[% input_label FILTER none %]&nbsp;
- <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>
- <span id="prod_comp_too_many_components" class="bz_default_hidden">
+ <img id="[% id FILTER html %]-throbber"
+ src="extensions/ProdCompSearch/web/images/throbber.gif"
+ style="display:none" width="16" height="11">
+ <span class="pcs-message" id="[% id FILTER html %]-no_components" style="display:none">
+ No components found
+ </span>
+ <span class="pcs-message" id="[% id FILTER html %]-too_many_components" style="display:none">
Result limited to [% max_results FILTER html %] components
- <span id="prod_comp_error" class="bz_default_hidden">
- An error occured</span>
+ </span>
+ <span class="pcs-message" id="[% id FILTER html %]-error" style="display:none">
+ An error occured
+ </span>
</div>
- <input id="prod_comp_search" type="text" size="50"
- placeholder="Search by product and component keywords">
+ <input type="text" class="prod_comp_search" id="[% id FILTER html %]" size="50"
+ placeholder="Search by product and component keywords"
+ data-script_name="[% script_name FILTER html %]"
+ data-format="[% format FILTER html %]"
+ data-cloned_bug_id="[% cloned_bug_id FILTER html %]"
+ data-new_tab="[% new_tab ? "1" : "0" %]"
+ data-anchor_component="[% anchor_component ? "1" : "0" %]"
+ data-max_results="[% max_results FILTER html %]"
+ [% "autofocus" IF auto_focus %]>
</div>
diff --git a/extensions/ProdCompSearch/web/js/prod_comp_search.js b/extensions/ProdCompSearch/web/js/prod_comp_search.js
index f294994e3..ae7353779 100644
--- a/extensions/ProdCompSearch/web/js/prod_comp_search.js
+++ b/extensions/ProdCompSearch/web/js/prod_comp_search.js
@@ -7,138 +7,100 @@
// 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,
- new_tab: null,
- max_results: 100
-};
-
-YUI({
- base: 'js/yui3/',
- combine: false
-}).use("node", "json-stringify", "autocomplete", "escape",
- "datasource-io", "datasource-jsonschema", function(Y) {
- Y.on("domready", function() {
- 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, limit: ProdCompSearch.max_results }
- };
- return Y.JSON.stringify(json_object);
- };
-
- var dataSource = new Y.DataSource.IO({
- source: 'jsonrpc.cgi',
- ioConfig: {
- method: "POST",
- headers: { 'Content-Type': 'application/json' }
- },
- on: {
- error: function(e) {
- if (console.error && e.response.meta.error) {
- console.error(e.response.meta.error.message);
- }
- Y.one("#prod_comp_throbber").addClass('bz_default_hidden');
- Y.one("#prod_comp_error").removeClass('bz_default_hidden');
- }
+$(function() {
+ 'use strict';
+ $('.prod_comp_search').autocomplete({
+ minLength: 3,
+ delay: 500,
+ source: function(request, response) {
+ var el = this.element;
+ var id = '#' + el.prop('id');
+ $(id + '-throbber').show();
+ $(id + '-no_components').hide();
+ $(id + '-too_many_components').hide();
+ $(id + '-error').hide();
+ var url = 'rest/prod_comp_search/' + encodeURIComponent(request.term) +
+ '?limit=' + (el.data('max_results') + 1);
+ if (BUGZILLA.api_token) {
+ url += '&Bugzilla_api_token=' + encodeURIComponent(BUGZILLA.api_token);
}
- });
-
- dataSource.plug(Y.Plugin.DataSourceJSONSchema, {
- schema: {
- resultListLocator : "result.products",
- resultFields : [ "product", "component" ],
- metaFields : { error : 'error' }
- }
- });
-
- 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: ProdCompSearch.max_results,
- 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');
- Y.one("#prod_comp_too_many_components").addClass('bz_default_hidden');
- Y.one("#prod_comp_error").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');
- }
- else if (e.results.length + 1 > ProdCompSearch.max_results) {
- Y.one("#prod_comp_too_many_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);
- }
- if (ProdCompSearch.new_tab) {
- window.open(url, '_blank');
- }
- else {
- window.location.href = url;
- }
+ $.ajax({
+ url: url,
+ contentType: 'application/json'
+ })
+ .done(function(data) {
+ $(id + '-throbber').hide();
+ if (data.error) {
+ $(id + '-error').show();
+ console.log(data.message);
+ return false;
+ }
+ if (data.products.length === 0) {
+ $(id + '-no_components').show();
}
- },
- after: {
- select: function(e) {
- if (ProdCompSearch.new_tab) {
- input.set('value','');
+ else if (data.products.length > el.data('max_results')) {
+ $(id + '-too_many_components').show();
+ }
+ var current_product = "";
+ var prod_comp_array = [];
+ var base_params = [];
+ if (el.data('format')) {
+ base_params.push('format=' + encodeURIComponent(el.data('format')));
+ }
+ if (el.data('cloned_bug_id')) {
+ base_params.push('cloned_bug_id=' + encodeURIComponent(el.data('cloned_bug_id')));
+ }
+ $.each(data.products, function() {
+ var params = base_params.slice();
+ params.push('product=' + encodeURIComponent(this.product));
+ if (this.product != current_product) {
+ prod_comp_array.push({
+ label: this.product,
+ url: el.data('script_name') + '?' + params.join('&')
+ });
+ current_product = this.product;
}
+ params.push('component=' + encodeURIComponent(this.component));
+ var url = el.data('script_name') + '?' + params.join('&');
+ if (el.data('anchor_component')) {
+ url += "#" + encodeURIComponent(this.component);
+ }
+ prod_comp_array.push({
+ label: this.product + ' :: ' + this.component,
+ url: url
+ });
+ });
+ response(prod_comp_array);
+ })
+ .fail(function(xhr, error_text) {
+ if (xhr.responseJSON.error) {
+ error_text = xhr.responseJSON.message;
}
+ $(id + '-throbber').hide();
+ $(id + '-comp_error').show();
+ console.log(error_text);
+ });
+ },
+ focus: function(event, ui) {
+ event.preventDefault();
+ },
+ select: function(event, ui) {
+ event.preventDefault();
+ var el = $(this);
+ el.val(ui.item.label);
+ if (el.data('new_tab')) {
+ window.open(ui.item.url, '_blank');
}
- });
-
- input.on('focus', function (e) {
- if (e.target.value && e.target.value.length > 3) {
- dataSource.load(e.target.value);
+ else {
+ window.location.href = ui.item.url;
}
- });
+ }
+ })
+ .focus(function(event) {
+ var el = $(event.target);
+ if (el.val().length >= el.autocomplete('option', 'minLength')) {
+ el.autocomplete('search');
+ }
});
+ $('.prod_comp_search:focus').select();
});
diff --git a/extensions/ProdCompSearch/web/styles/prod_comp_search.css b/extensions/ProdCompSearch/web/styles/prod_comp_search.css
index ccb5887c4..b898672b4 100644
--- a/extensions/ProdCompSearch/web/styles/prod_comp_search.css
+++ b/extensions/ProdCompSearch/web/styles/prod_comp_search.css
@@ -5,23 +5,11 @@
* 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_search_form .yui3-aclist-content {
- max-height: 500px;
- overflow: auto;
+.pcs-message {
+ color: red;
}
-#prod_comp_no_components,
-#prod_comp_error,
-#prod_comp_too_many_components {
- color: red;
+#prod_comp_search_main {
+ width: 400px;
+ margin: 0 auto;
}