Merge lp:~gmb/launchpad/taste-the-blood-of-ajax-dupefinding into lp:launchpad

Proposed by Graham Binns
Status: Merged
Approved by: Graham Binns
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~gmb/launchpad/taste-the-blood-of-ajax-dupefinding
Merge into: lp:launchpad
Diff against target: 338 lines (+183/-31)
4 files modified
lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js (+81/-14)
lib/lp/bugs/browser/bugtarget.py (+58/-3)
lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt (+30/-2)
lib/lp/bugs/templates/bugtarget-filebug-search.pt (+14/-12)
To merge this branch: bzr merge lp:~gmb/launchpad/taste-the-blood-of-ajax-dupefinding
Reviewer Review Type Date Requested Status
Abel Deuring (community) code Approve
Canonical Launchpad Engineering code, js Pending
Review via email: mp+16006@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) wrote :

This branch enables the AJAX dupefinder for project groups.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js
  lib/lp/bugs/browser/bugtarget.py
  lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt
  lib/lp/bugs/templates/bugtarget-filebug-search.pt

== JSLint notices ==
jslint: No problem found in '/home/graham/canonical/lp-branches/taste-the-blood-of-ajax-dupefinding/lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js'.

jslint: No problem found in '/home/graham/canonical/lp-branches/taste-the-blood-of-ajax-dupefinding/lib/canonical/launchpad/javascript/lp/comment.js'.

jslint: 2 files to lint.

== Pylint notices ==

lib/lp/bugs/browser/bugtarget.py
    501: [C0322, FileBugViewBase.submit_bug_action] Operator not preceded by a space
    failure=handleSubmitBugFailure)
    ^
    def submit_bug_action(self, action, data):
    679: [C0322, FileBugViewBase.this_is_my_bug_action] Operator not preceded by a space
    name="this_is_my_bug", failure=handleSubmitBugFailure)
    ^
    def this_is_my_bug_action(self, action, data):
    1030: [C0322, FileBugGuidedView.continue_action] Operator not preceded by a space
    validator="validate_no_dupe_found")
    ^
    def continue_action(self, action, data):

This last bit of lint is cruft and can be ignored.

Here's a list of changes:

== lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js ==

 - I've added a reload_filebug_form() function and hooked up the
   onchange event of the product field on the project group +filebug
   page to it.
 - I've refactored the existing code a bit to make more use of globals
   where necessary (i.e. for the URLs to the inline filebug form and
   dupe search pages).

== lib/lp/bugs/browser/bugtarget.py ==

 - I've made FileBugViewBase.use_asynchronous_dupefinder return True for
   IProjects.
 - I've added in inline_filebug_base_url property to FileBugViewBase,
   which allows us to grab the base URL for the current request so that
   we're not having to fudge it in Javascript.
 - I've added two new properties to ProjectFileBugGuidedView,
   products_using_malone and default_product. These allow us to get the
   default product for the initial creation of the inline +filebug form.
 - I've added Project-specific versions of inline_filebug_form_url and
   duplicate_search_url to ProjectFileBugGuidedView.

== lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt ==

 - I've added tests for the changes to ProjectFileBugGuidedView and
   FileBugViewBase.

== lib/lp/bugs/templates/bugtarget-filebug-search.pt ==

 - I've added a container for the inline_filebug_base_url on the page.
 - I've moved the containers and links used by the inline dupe search to
   within the <tal:uses malone /> block so that if the Project doesn't
   use Malone we won't hit an OOPS.

Revision history for this message
Abel Deuring (adeuring) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js'
2--- lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js 2009-12-10 13:45:40 +0000
3+++ lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js 2009-12-11 11:21:16 +0000
4@@ -33,6 +33,18 @@
5 * The search button on the +filebug form
6 */
7 var search_button;
8+/**
9+ * The base URL for similar bug searches.
10+ */
11+var search_url_base;
12+/**
13+ * The base URL for all inline +filebug work.
14+ */
15+var filebug_base_url;
16+/**
17+ * The URL of the inline +filebug form.
18+ */
19+var filebug_form_url;
20 /*
21 * The boilerplate elements for the do-you-want-to-subscribe
22 * FormOverlay.
23@@ -176,15 +188,14 @@
24 }
25
26 var search_term = encodeURI(search_field.get('value'));
27- var search_url_base = Y.one(
28- '#duplicate-search-url').getAttribute('href');
29 var search_url = search_url_base + '?title=' + search_term;
30
31- // Hide the button, show the spinner and clear the contents of the
32- // possible duplicates div.
33+ // Hide the button and +filebug form, show the spinner and clear the
34+ // contents of the possible duplicates div.
35 search_button.addClass(UNSEEN);
36 Y.one('#spinner').removeClass(UNSEEN);
37 Y.one('#possible-duplicates').set(INNER_HTML, '');
38+ Y.one('#bug_reporting_form').setStyle(DISPLAY, NONE);
39
40 config = {on: {success: on_success,
41 failure: show_failure_message}};
42@@ -263,14 +274,50 @@
43 return subscribe_form_overlay;
44 }
45
46-
47-/**
48- * Set up the dupe finder, overriding the default behaviour of the
49- * +filebug search form.
50- */
51-function set_up_dupe_finder(transaction_id, response, args) {
52+/**
53+ * Reload the +filebug-inline form for the correct product or package.
54+ */
55+function reload_filebug_form() {
56+ var config = {
57+ on: {
58+ success: function(transaction_id, response, args) {
59+ // Hide the filebug form container.
60+ var filebug_form_container = Y.one(
61+ '#filebug-form-container');
62+ filebug_form_container.setStyle(DISPLAY, NONE);
63+ Y.log(filebug_form_container);
64+
65+ // Clear the contents of the search results.
66+ Y.one('#possible-duplicates').set(INNER_HTML, '');
67+
68+ // Set up the +filebug form.
69+ set_up_filebug_form(response.responseText);
70+
71+ // Change the label on the search button to its default.
72+ search_button.set('value', 'Next');
73+ }
74+ }
75+ };
76+
77+ var product_field = Y.one(Y.DOM.byId('field.product'));
78+ if (Y.Lang.isValue(product_field)) {
79+ var product = product_field.get('value');
80+ filebug_form_url =
81+ filebug_base_url + product + '/+filebug-inline-form';
82+ search_url_base =
83+ filebug_base_url + product + '/+filebug-show-similar';
84+ }
85+
86+ // Reload the filebug form.
87+ Y.io(filebug_form_url, config);
88+}
89+
90+/**
91+ * Set up the filebug form.
92+ */
93+function set_up_filebug_form(form_contents) {
94 var filebug_form_container = Y.one('#filebug-form-container');
95- filebug_form_container.set(INNER_HTML, response.responseText);
96+ filebug_form_container.set(INNER_HTML, form_contents);
97
98 // Activate the extra options collapsible section on the bug
99 // reporting form.
100@@ -278,8 +325,21 @@
101 if (Y.Lang.isValue(bug_reporting_form)) {
102 activateCollapsibles();
103 }
104-
105- search_button = Y.one(Y.DOM.byId('field.actions.search'));
106+}
107+
108+/**
109+ * Set up the dupe finder, overriding the default behaviour of the
110+ * +filebug search form.
111+ */
112+function set_up_dupe_finder(transaction_id, response, args) {
113+ // Grab the inline filebug base url and store it.
114+ filebug_base_url = Y.one('#filebug-base-url').getAttribute('href');
115+
116+ // Load the +filebug form into its container.
117+ set_up_filebug_form(response.responseText);
118+
119+ // Grab the search_url_base value from the page and store it.
120+ search_url_base = Y.one('#duplicate-search-url').getAttribute('href');
121
122 // Change the name and id of the search field so that it doesn't
123 // confuse the view when we submit a bug report.
124@@ -287,8 +347,15 @@
125 search_field.set('name', 'field.search');
126 search_field.set('id', 'field.search');
127
128+ // If there's a product field, hook it up to the
129+ // reload_filebug_form() function.
130+ Y.on('change', function(e) {
131+ reload_filebug_form();
132+ }, Y.DOM.byId('field.product'));
133+
134 // Update the label on the search button so that it no longer
135 // says "Continue".
136+ search_button = Y.one(Y.DOM.byId('field.actions.search'));
137 search_button.set('value', 'Next');
138
139 // Set up the handler for the search form.
140@@ -408,7 +475,7 @@
141 var filebug_form_url_element = Y.one(
142 '#filebug-form-url');
143 if (Y.Lang.isValue(filebug_form_url_element)) {
144- var filebug_form_url = filebug_form_url_element.getAttribute(
145+ filebug_form_url = filebug_form_url_element.getAttribute(
146 'href');
147 Y.io(filebug_form_url, config);
148 }
149
150=== modified file 'lib/lp/bugs/browser/bugtarget.py'
151--- lib/lp/bugs/browser/bugtarget.py 2009-12-09 17:22:12 +0000
152+++ lib/lp/bugs/browser/bugtarget.py 2009-12-11 11:21:16 +0000
153@@ -363,7 +363,9 @@
154 @property
155 def use_asynchronous_dupefinder(self):
156 """Return True if the asynchronous dupe finder can be used."""
157- return IProduct.providedBy(self.context)
158+ return (
159+ IProduct.providedBy(self.context) or
160+ IProject.providedBy(self.context))
161
162 def getPackageNameFieldCSSClass(self):
163 """Return the CSS class for the packagename field."""
164@@ -706,8 +708,17 @@
165 raise NotImplementedError
166
167 @property
168+ def inline_filebug_base_url(self):
169+ """Return the base URL for the current request.
170+
171+ This allows us to build URLs in Javascript without guessing at
172+ domains.
173+ """
174+ return self.request.getRootURL(None)
175+
176+ @property
177 def inline_filebug_form_url(self):
178- """The URL to the inline filebug form.
179+ """Return the URL to the inline filebug form.
180
181 If a token was passed to this view, it will be be passed through
182 to the inline bug filing form via the returned URL.
183@@ -719,7 +730,7 @@
184
185 @property
186 def duplicate_search_url(self):
187- """The URL to the inline duplicate search view."""
188+ """Return the URL to the inline duplicate search view."""
189 url = canonical_url(self.context, view_name='+filebug-show-similar')
190 if self.extra_data_token is not None:
191 url = urlappend(url, self.extra_data_token)
192@@ -1032,6 +1043,50 @@
193 actions = FileBugGuidedView.actions
194 schema = IProjectBugAddForm
195
196+ @cachedproperty
197+ def products_using_malone(self):
198+ return [
199+ product for product in self.context.products
200+ if product.official_malone]
201+
202+ @property
203+ def default_product(self):
204+ if len(self.products_using_malone) > 0:
205+ return self.products_using_malone[0]
206+ else:
207+ return None
208+
209+ @property
210+ def inline_filebug_form_url(self):
211+ """Return the URL to the inline filebug form.
212+
213+ If a token was passed to this view, it will be be passed through
214+ to the inline bug filing form via the returned URL.
215+
216+ The URL returned will be the URL of the first of the current
217+ Project's products, since that's the product that will be
218+ selected by default when the view is rendered.
219+ """
220+ url = canonical_url(
221+ self.default_product, view_name='+filebug-inline-form')
222+ if self.extra_data_token is not None:
223+ url = urlappend(url, self.extra_data_token)
224+ return url
225+
226+ @property
227+ def duplicate_search_url(self):
228+ """Return the URL to the inline duplicate search view.
229+
230+ The URL returned will be the URL of the first of the current
231+ Project's products, since that's the product that will be
232+ selected by default when the view is rendered.
233+ """
234+ url = canonical_url(
235+ self.default_product, view_name='+filebug-show-similar')
236+ if self.extra_data_token is not None:
237+ url = urlappend(url, self.extra_data_token)
238+ return url
239+
240 def _getSelectedProduct(self):
241 """Return the product that's selected."""
242 assert self.widgets['product'].hasValidInput(), (
243
244=== modified file 'lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt'
245--- lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt 2009-12-08 15:08:26 +0000
246+++ lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt 2009-12-11 11:21:16 +0000
247@@ -45,6 +45,20 @@
248
249 == URLs to additional FileBug elements ==
250
251+FileBugViewBase's inline_filebug_base_url returns the base URL for all
252+inline +filebug work.
253+
254+ >>> from lp.registry.interfaces.product import IProductSet
255+ >>> firefox = getUtility(IProductSet).getByName('firefox')
256+ >>> filebug_view = create_initialized_view(
257+ ... firefox, '+filebug')
258+
259+This property returns the root URL of the current request, so in the
260+case of this test will return 127.0.0.1.
261+
262+ >>> print filebug_view.inline_filebug_base_url
263+ http://127.0.0.1/
264+
265 FileBugViewBase provides properties that return the URLs of further
266 useful parts of the +filebug process.
267
268@@ -52,14 +66,28 @@
269 filebug form so that it may be loaded asynchronously.
270
271 >>> print filebug_view.inline_filebug_form_url
272- http://launchpad.dev/ubuntu/+source/mozilla-firefox/+filebug-inline-form
273+ http://launchpad.dev/firefox/+filebug-inline-form
274
275 Similarly, the duplicate_search_url property returns the base URL for
276 the duplicate search view, which can be used to load the list of
277 possible duplicates for a bug asynchronously.
278
279 >>> print filebug_view.duplicate_search_url
280- http://launchpad.dev/ubuntu/+source/mozilla-firefox/+filebug-show-similar
281+ http://launchpad.dev/firefox/+filebug-show-similar
282+
283+For project groups, the URLs returned will refer to the default product
284+for those project groups.
285+
286+ >>> from lp.registry.interfaces.project import IProjectSet
287+ >>> gnome_project = getUtility(IProjectSet).getByName('gnome')
288+ >>> gnome_filebug_view = create_initialized_view(
289+ ... gnome_project, '+filebug')
290+
291+ >>> print gnome_filebug_view.inline_filebug_form_url
292+ http://launchpad.dev/evolution/+filebug-inline-form
293+
294+ >>> print gnome_filebug_view.duplicate_search_url
295+ http://launchpad.dev/evolution/+filebug-show-similar
296
297
298 == Adding extra info to filed bugs ==
299
300=== modified file 'lib/lp/bugs/templates/bugtarget-filebug-search.pt'
301--- lib/lp/bugs/templates/bugtarget-filebug-search.pt 2009-12-09 17:22:12 +0000
302+++ lib/lp/bugs/templates/bugtarget-filebug-search.pt 2009-12-11 11:21:16 +0000
303@@ -110,21 +110,23 @@
304 </div>
305 </div>
306 </div>
307+
308+ <div id="possible-duplicates" style="text-align: left;">
309+ </div>
310+ <div id="filebug-form-container" style="display: none;">
311+ </div>
312+
313+ <p style="display: none" tal:condition="view/use_asynchronous_dupefinder">
314+ <a id="filebug-base-url"
315+ tal:attributes="href view/inline_filebug_base_url"></a>
316+ <a id="filebug-form-url"
317+ tal:attributes="href view/inline_filebug_form_url"></a>
318+ <a id="duplicate-search-url"
319+ tal:attributes="href view/duplicate_search_url"></a>
320+ </p>
321 </tal:uses-malone>
322 </div>
323
324- <div id="possible-duplicates" style="text-align: left;">
325- </div>
326- <div id="filebug-form-container" style="display: none;">
327- </div>
328-
329- <p style="display: none" tal:condition="view/use_asynchronous_dupefinder">
330- <a id="filebug-form-url"
331- tal:attributes="href view/inline_filebug_form_url"></a>
332- <a id="duplicate-search-url"
333- tal:attributes="href view/duplicate_search_url"></a>
334- </p>
335-
336 </div>
337
338 </html>