Merge lp:~wgrant/launchpad/rebuild-projectgroup-filebug into lp:launchpad
- rebuild-projectgroup-filebug
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | 15737 |
Proposed branch: | lp:~wgrant/launchpad/rebuild-projectgroup-filebug |
Merge into: | lp:launchpad |
Diff against target: |
917 lines (+100/-469) 11 files modified
lib/lp/bugs/browser/bugtarget.py (+84/-234) lib/lp/bugs/browser/configure.zcml (+2/-1) lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt (+0/-14) lib/lp/bugs/browser/tests/test_bugtarget_filebug.py (+0/-7) lib/lp/bugs/javascript/filebug_dupefinder.js (+4/-37) lib/lp/bugs/javascript/tests/test_filebug_dupefinder.js (+0/-80) lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-guidelines.txt (+4/-4) lib/lp/bugs/stories/guided-filebug/xx-product-guided-filebug.txt (+1/-1) lib/lp/bugs/stories/guided-filebug/xx-project-guided-filebug.txt (+4/-74) lib/lp/bugs/templates/bugtarget-filebug-search.pt (+1/-7) lib/lp/bugs/templates/bugtarget-macros-filebug.pt (+0/-10) |
To merge this branch: | bzr merge lp:~wgrant/launchpad/rebuild-projectgroup-filebug |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
j.c.sackett (community) | Approve | ||
Review via email: mp+117835@code.launchpad.net |
Commit message
Split ProjectGroup:
Description of the change
This branch cleans up some of +filebug's horrors. In particular, it removes the core FileBugViewBase's support for its non-IBugTarget instantiations: IMaloneApplication and IProjectGroup.
The contextless IMaloneApplicat
ProjectGroup:
Preview Diff
1 | === modified file 'lib/lp/bugs/browser/bugtarget.py' |
2 | --- lib/lp/bugs/browser/bugtarget.py 2012-08-01 01:05:08 +0000 |
3 | +++ lib/lp/bugs/browser/bugtarget.py 2012-08-03 00:21:23 +0000 |
4 | @@ -16,7 +16,7 @@ |
5 | "IProductBugConfiguration", |
6 | "OfficialBugTagsManageView", |
7 | "ProductConfigureBugTrackerView", |
8 | - "ProjectFileBugGuidedView", |
9 | + "ProjectGroupFileBugGuidedView", |
10 | "product_to_productbugconfiguration", |
11 | ] |
12 | |
13 | @@ -105,7 +105,6 @@ |
14 | UNRESOLVED_BUGTASK_STATUSES, |
15 | ) |
16 | from lp.bugs.interfaces.bugtracker import IBugTracker |
17 | -from lp.bugs.interfaces.malone import IMaloneApplication |
18 | from lp.bugs.interfaces.securitycontact import IHasSecurityContact |
19 | from lp.bugs.model.bugtask import BugTask |
20 | from lp.bugs.model.structuralsubscription import ( |
21 | @@ -149,7 +148,6 @@ |
22 | from lp.services.webapp.authorization import check_permission |
23 | from lp.services.webapp.batching import BatchNavigator |
24 | from lp.services.webapp.breadcrumb import Breadcrumb |
25 | -from lp.services.webapp.interfaces import ILaunchBag |
26 | from lp.services.webapp.menu import structured |
27 | |
28 | # A simple vocabulary for the subscribe_to_existing_bug form field. |
29 | @@ -253,12 +251,9 @@ |
30 | custom_widget('information_type', LaunchpadRadioWidgetWithDescription) |
31 | |
32 | extra_data_token = None |
33 | - advanced_form = False |
34 | - frontpage_form = False |
35 | - data_parser = None |
36 | |
37 | def __init__(self, context, request): |
38 | - LaunchpadFormView.__init__(self, context, request) |
39 | + super(FileBugViewBase, self).__init__(context, request) |
40 | self.extra_data = FileBugData() |
41 | |
42 | def initialize(self): |
43 | @@ -282,15 +277,12 @@ |
44 | type.name for type in PRIVATE_INFORMATION_TYPES] |
45 | cache.objects['bug_private_by_default'] = ( |
46 | IProduct.providedBy(self.context) and self.context.private_bugs) |
47 | - # Project groups are special. The Next button sends you to |
48 | - # Product:+filebug, so we need none of the usual stuff. |
49 | - if not IProjectGroup.providedBy(self.context): |
50 | - cache.objects['information_type_data'] = [ |
51 | - {'value': term.name, 'description': term.description, |
52 | - 'name': term.title, |
53 | - 'description_css_class': 'choice-description'} |
54 | - for term in |
55 | - self.context.pillar.getAllowedBugInformationTypes()] |
56 | + cache.objects['information_type_data'] = [ |
57 | + {'value': term.name, 'description': term.description, |
58 | + 'name': term.title, |
59 | + 'description_css_class': 'choice-description'} |
60 | + for term in |
61 | + self.context.pillar.getAllowedBugInformationTypes()] |
62 | bugtask_status_data = vocabulary_to_choice_edit_items( |
63 | BugTaskStatus, include_description=True, css_class_prefix='status', |
64 | excluded_items=[ |
65 | @@ -307,8 +299,7 @@ |
66 | excluded_items=[BugTaskImportance.UNKNOWN]) |
67 | cache.objects['bugtask_importance_data'] = bugtask_importance_data |
68 | cache.objects['enable_bugfiling_duplicate_search'] = ( |
69 | - IProjectGroup.providedBy(self.context) |
70 | - or self.context.enable_bugfiling_duplicate_search) |
71 | + self.context.enable_bugfiling_duplicate_search) |
72 | |
73 | super(FileBugViewBase, self).initialize() |
74 | |
75 | @@ -366,21 +357,8 @@ |
76 | if (IDistribution.providedBy(context) or |
77 | IDistributionSourcePackage.providedBy(context)): |
78 | field_names.append('packagename') |
79 | - elif IMaloneApplication.providedBy(context): |
80 | - field_names.append('bugtarget') |
81 | - elif IProjectGroup.providedBy(context): |
82 | - field_names.append('product') |
83 | - elif not IProduct.providedBy(context): |
84 | - raise AssertionError('Unknown context: %r' % context) |
85 | - |
86 | - # If the context is a project group we want to render the optional |
87 | - # fields since they will initially be hidden and later exposed if the |
88 | - # selected project supports them. |
89 | - include_extra_fields = IProjectGroup.providedBy(context) |
90 | - if not include_extra_fields: |
91 | - include_extra_fields = self.is_bug_supervisor |
92 | - |
93 | - if include_extra_fields: |
94 | + |
95 | + if self.is_bug_supervisor: |
96 | field_names.extend( |
97 | ['assignee', 'importance', 'milestone', 'status']) |
98 | |
99 | @@ -405,14 +383,11 @@ |
100 | return IProduct.providedBy(self.context) |
101 | |
102 | def contextIsProject(self): |
103 | - return IProjectGroup.providedBy(self.context) |
104 | + return False |
105 | |
106 | def targetIsUbuntu(self): |
107 | ubuntu = getUtility(ILaunchpadCelebrities).ubuntu |
108 | - return (self.context == ubuntu or |
109 | - (IMaloneApplication.providedBy(self.context) and |
110 | - self.request.form.get('field.bugtarget.distribution') == |
111 | - ubuntu.name)) |
112 | + return self.context == ubuntu |
113 | |
114 | def getPackageNameFieldCSSClass(self): |
115 | """Return the CSS class for the packagename field.""" |
116 | @@ -455,8 +430,6 @@ |
117 | if packagename: |
118 | if IDistribution.providedBy(self.context): |
119 | distribution = self.context |
120 | - elif 'distribution' in data: |
121 | - distribution = data['distribution'] |
122 | else: |
123 | assert IDistributionSourcePackage.providedBy(self.context) |
124 | distribution = self.context.distribution |
125 | @@ -479,17 +452,6 @@ |
126 | self.setFieldError("packagename", |
127 | "Please enter a package name") |
128 | |
129 | - # If we've been called from the frontpage filebug forms we must check |
130 | - # that whatever product or distro is having a bug filed against it |
131 | - # actually uses Malone for its bug tracking. |
132 | - product_or_distro = self.getProductOrDistroFromContext() |
133 | - if (product_or_distro is not None and |
134 | - product_or_distro.bug_tracking_usage != ServiceUsage.LAUNCHPAD): |
135 | - self.setFieldError( |
136 | - 'bugtarget', |
137 | - "%s does not use Launchpad as its bug tracker " % |
138 | - product_or_distro.displayname) |
139 | - |
140 | def setUpWidgets(self): |
141 | """Customize the onKeyPress event of the package name chooser.""" |
142 | super(FileBugViewBase, self).setUpWidgets() |
143 | @@ -502,11 +464,6 @@ |
144 | """Set up the form fields. See `LaunchpadFormView`.""" |
145 | super(FileBugViewBase, self).setUpFields() |
146 | |
147 | - # Project groups are special. The Next button sends you to |
148 | - # Product:+filebug, so we need none of the usual stuff. |
149 | - if IProjectGroup.providedBy(self.context): |
150 | - return |
151 | - |
152 | if self.is_bug_supervisor: |
153 | info_type_vocab = InformationTypeVocabulary( |
154 | types=self.context.pillar.getAllowedBugInformationTypes()) |
155 | @@ -534,14 +491,8 @@ |
156 | |
157 | def contextUsesMalone(self): |
158 | """Does the context use Malone as its official bugtracker?""" |
159 | - if IProjectGroup.providedBy(self.context): |
160 | - products_using_malone = [ |
161 | - product for product in self.context.products |
162 | - if product.bug_tracking_usage == ServiceUsage.LAUNCHPAD] |
163 | - return len(products_using_malone) > 0 |
164 | - else: |
165 | - bug_tracking_usage = self.getMainContext().bug_tracking_usage |
166 | - return bug_tracking_usage == ServiceUsage.LAUNCHPAD |
167 | + bug_tracking_usage = self.getMainContext().bug_tracking_usage |
168 | + return bug_tracking_usage == ServiceUsage.LAUNCHPAD |
169 | |
170 | def shouldSelectPackageName(self): |
171 | """Should the radio button to select a package be selected?""" |
172 | @@ -559,8 +510,6 @@ |
173 | title = data["title"] |
174 | comment = data["comment"].rstrip() |
175 | packagename = data.get("packagename") |
176 | - distribution = data.get( |
177 | - "distribution", getUtility(ILaunchBag).distribution) |
178 | |
179 | information_type = data.get("information_type") |
180 | # If the old UI is enabled, security bugs are always embargoed |
181 | @@ -569,14 +518,6 @@ |
182 | information_type = InformationType.PRIVATESECURITY |
183 | |
184 | context = self.context |
185 | - if distribution is not None: |
186 | - # We're being called from the generic bug filing form, so |
187 | - # manually set the chosen distribution as the context. |
188 | - context = distribution |
189 | - elif IProjectGroup.providedBy(context): |
190 | - context = data['product'] |
191 | - elif IMaloneApplication.providedBy(context): |
192 | - context = data['bugtarget'] |
193 | |
194 | # Ensure that no package information is used, if the user |
195 | # enters a package name but then selects "I don't know". |
196 | @@ -814,22 +755,8 @@ |
197 | return self, () |
198 | |
199 | def getProductOrDistroFromContext(self): |
200 | - """Return the product or distribution relative to the context. |
201 | - |
202 | - For instance, if the context is an IDistroSeries, return the |
203 | - distribution related to it. Will return None if the context is |
204 | - not related to a product or a distro. |
205 | - """ |
206 | - context = self.context |
207 | - if IProduct.providedBy(context) or IDistribution.providedBy(context): |
208 | - return context |
209 | - elif IProductSeries.providedBy(context): |
210 | - return context.product |
211 | - elif (IDistroSeries.providedBy(context) or |
212 | - IDistributionSourcePackage.providedBy(context)): |
213 | - return context.distribution |
214 | - else: |
215 | - return None |
216 | + """Return the product or distribution relative to the context.""" |
217 | + return self.context.pillar |
218 | |
219 | def showOptionalMarker(self, field_name): |
220 | """See `LaunchpadFormView`.""" |
221 | @@ -854,26 +781,11 @@ |
222 | total accuracy, and will return the first 'relevant' bugtask |
223 | it finds even if there are other candidates. Be warned! |
224 | """ |
225 | - context = self.context |
226 | - |
227 | - if IProjectGroup.providedBy(context): |
228 | - contexts = set(context.products) |
229 | - else: |
230 | - contexts = [context] |
231 | - |
232 | for bugtask in bug.bugtasks: |
233 | - if bugtask.target in contexts or bugtask.pillar in contexts: |
234 | + if self.context in (bugtask.target, bugtask.pillar): |
235 | return bugtask |
236 | return None |
237 | |
238 | - @property |
239 | - def bugtarget(self): |
240 | - """The bugtarget we're currently assuming. |
241 | - |
242 | - The same as the context. |
243 | - """ |
244 | - return self.context |
245 | - |
246 | default_bug_reported_acknowledgement = "Thank you for your bug report." |
247 | |
248 | def getAcknowledgementMessage(self, context): |
249 | @@ -947,38 +859,24 @@ |
250 | Returns a list of dicts, with each dict containing values for |
251 | "preamble" and "content". |
252 | """ |
253 | - |
254 | - def target_name(target): |
255 | - # IProjectGroup can be considered the target of a bug during |
256 | - # the bug filing process, but does not extend IBugTarget |
257 | - # and ultimately cannot actually be the target of a |
258 | - # bug. Hence this function to determine a suitable |
259 | - # name/title to display. Hurrumph. |
260 | - if IBugTarget.providedBy(target): |
261 | - return target.bugtargetdisplayname |
262 | - else: |
263 | - return target.displayname |
264 | - |
265 | guidelines = [] |
266 | - bugtarget = self.context |
267 | - if bugtarget is not None: |
268 | - content = bugtarget.bug_reporting_guidelines |
269 | + content = self.context.bug_reporting_guidelines |
270 | + if content is not None and len(content) > 0: |
271 | + guidelines.append({ |
272 | + "source": self.context.bugtargetdisplayname, |
273 | + "content": content, |
274 | + }) |
275 | + # Distribution source packages are shown with both their |
276 | + # own reporting guidelines and those of their |
277 | + # distribution. |
278 | + if IDistributionSourcePackage.providedBy(self.context): |
279 | + distribution = self.context.distribution |
280 | + content = distribution.bug_reporting_guidelines |
281 | if content is not None and len(content) > 0: |
282 | guidelines.append({ |
283 | - "source": target_name(bugtarget), |
284 | - "content": content, |
285 | - }) |
286 | - # Distribution source packages are shown with both their |
287 | - # own reporting guidelines and those of their |
288 | - # distribution. |
289 | - if IDistributionSourcePackage.providedBy(bugtarget): |
290 | - distribution = bugtarget.distribution |
291 | - content = distribution.bug_reporting_guidelines |
292 | - if content is not None and len(content) > 0: |
293 | - guidelines.append({ |
294 | - "source": target_name(distribution), |
295 | - "content": content, |
296 | - }) |
297 | + "source": distribution.bugtargetdisplayname, |
298 | + "content": content, |
299 | + }) |
300 | return guidelines |
301 | |
302 | def getMainContext(self): |
303 | @@ -995,7 +893,7 @@ |
304 | context, self.user) |
305 | |
306 | |
307 | -class FileBugAdvancedView(FileBugViewBase): |
308 | +class FileBugAdvancedView(LaunchpadView): |
309 | """Browser view for filing a bug. |
310 | |
311 | This view exists only to redirect from +filebug-advanced to +filebug. |
312 | @@ -1038,11 +936,6 @@ |
313 | return url |
314 | |
315 | @property |
316 | - def search_context(self): |
317 | - """Return the context used to search for similar bugs.""" |
318 | - return self.context |
319 | - |
320 | - @property |
321 | def search_text(self): |
322 | """Return the search string entered by the user.""" |
323 | return self.request.get('title') |
324 | @@ -1053,19 +946,16 @@ |
325 | title = self.search_text |
326 | if not title: |
327 | return [] |
328 | - search_context = self.search_context |
329 | - if search_context is None: |
330 | - return [] |
331 | - elif IProduct.providedBy(search_context): |
332 | - context_params = {'product': search_context} |
333 | - elif IDistribution.providedBy(search_context): |
334 | - context_params = {'distribution': search_context} |
335 | + elif IProduct.providedBy(self.context): |
336 | + context_params = {'product': self.context} |
337 | + elif IDistribution.providedBy(self.context): |
338 | + context_params = {'distribution': self.context} |
339 | else: |
340 | - assert IDistributionSourcePackage.providedBy(search_context), ( |
341 | - 'Unknown search context: %r' % search_context) |
342 | + assert IDistributionSourcePackage.providedBy(self.context), ( |
343 | + 'Unknown search context: %r' % self.context) |
344 | context_params = { |
345 | - 'distribution': search_context.distribution, |
346 | - 'sourcepackagename': search_context.sourcepackagename} |
347 | + 'distribution': self.context.distribution, |
348 | + 'sourcepackagename': self.context.sourcepackagename} |
349 | |
350 | matching_bugtasks = getUtility(IBugTaskSet).findSimilar( |
351 | self.user, title, **context_params) |
352 | @@ -1118,23 +1008,10 @@ |
353 | |
354 | @property |
355 | def page_title(self): |
356 | - if IMaloneApplication.providedBy(self.context): |
357 | - return 'Report a bug' |
358 | - else: |
359 | - return 'Report a bug about %s' % self.context.title |
360 | - |
361 | - @safe_action |
362 | - @action("Continue", name="projectgroupsearch", |
363 | - validator="validate_search") |
364 | - def projectgroup_search_action(self, action, data): |
365 | - """Search for similar bug reports.""" |
366 | - # Don't give focus to any widget, to ensure that the browser |
367 | - # won't scroll past the "possible duplicates" list. |
368 | - self.initial_focus_widget = None |
369 | - return self._PROJECTGROUP_SEARCH_FOR_DUPES() |
370 | - |
371 | - @safe_action |
372 | - @action("Continue", name="search", validator="validate_search") |
373 | + return 'Report a bug about %s' % self.context.title |
374 | + |
375 | + @safe_action |
376 | + @action("Continue", name="search") |
377 | def search_action(self, action, data): |
378 | """Search for similar bug reports.""" |
379 | # Don't give focus to any widget, to ensure that the browser |
380 | @@ -1143,26 +1020,6 @@ |
381 | return self.showFileBugForm() |
382 | |
383 | @property |
384 | - def search_context(self): |
385 | - """Return the context used to search for similar bugs.""" |
386 | - if IDistributionSourcePackage.providedBy(self.context): |
387 | - return self.context |
388 | - |
389 | - search_context = self.getMainContext() |
390 | - if IProjectGroup.providedBy(search_context): |
391 | - assert self.widgets['product'].hasValidInput(), ( |
392 | - "This method should be called only when we know which" |
393 | - " product the user selected.") |
394 | - search_context = self.widgets['product'].getInputValue() |
395 | - elif IMaloneApplication.providedBy(search_context): |
396 | - if self.widgets['bugtarget'].hasValidInput(): |
397 | - search_context = self.widgets['bugtarget'].getInputValue() |
398 | - else: |
399 | - search_context = None |
400 | - |
401 | - return search_context |
402 | - |
403 | - @property |
404 | def search_text(self): |
405 | """Return the search string entered by the user.""" |
406 | try: |
407 | @@ -1170,18 +1027,6 @@ |
408 | except InputErrors: |
409 | return None |
410 | |
411 | - def validate_search(self, action, data): |
412 | - """Make sure some keywords are provided.""" |
413 | - try: |
414 | - data['title'] = self.widgets['title'].getInputValue() |
415 | - except InputErrors as error: |
416 | - self.setFieldError("title", "A summary is required.") |
417 | - return [error] |
418 | - |
419 | - # Return an empty list of errors to satisfy the validation API, |
420 | - # and say "we've handled the validation and found no errors." |
421 | - return [] |
422 | - |
423 | def validate_no_dupe_found(self, action, data): |
424 | return () |
425 | |
426 | @@ -1195,13 +1040,24 @@ |
427 | return self._FILEBUG_FORM() |
428 | |
429 | |
430 | -class ProjectFileBugGuidedView(FileBugGuidedView): |
431 | +class ProjectGroupFileBugGuidedView(LaunchpadFormView): |
432 | """Guided filebug pages for IProjectGroup.""" |
433 | |
434 | - # Make inheriting the base class' actions work. |
435 | - actions = FileBugGuidedView.actions |
436 | schema = IProjectGroupBugAddForm |
437 | |
438 | + custom_widget('title', TextWidget, displayWidth=40) |
439 | + custom_widget('tags', BugTagsWidget) |
440 | + |
441 | + extra_data_to_process = False |
442 | + |
443 | + @property |
444 | + def field_names(self): |
445 | + return ['product', 'title', 'tags'] |
446 | + |
447 | + @property |
448 | + def page_title(self): |
449 | + return 'Report a bug about %s' % self.context.title |
450 | + |
451 | @cachedproperty |
452 | def products_using_malone(self): |
453 | return [ |
454 | @@ -1215,36 +1071,30 @@ |
455 | else: |
456 | return None |
457 | |
458 | - @property |
459 | - def inline_filebug_form_url(self): |
460 | - """Return the URL to the inline filebug form. |
461 | - |
462 | - If a token was passed to this view, it will be passed through |
463 | - to the inline bug filing form via the returned URL. |
464 | - |
465 | - The URL returned will be the URL of the first of the current |
466 | - ProjectGroup's products, since that's the product that will be |
467 | - selected by default when the view is rendered. |
468 | - """ |
469 | - url = canonical_url( |
470 | - self.default_product, view_name='+filebug-inline-form') |
471 | - if self.extra_data_token is not None: |
472 | - url = urlappend(url, self.extra_data_token) |
473 | - return url |
474 | - |
475 | - @property |
476 | - def duplicate_search_url(self): |
477 | - """Return the URL to the inline duplicate search view. |
478 | - |
479 | - The URL returned will be the URL of the first of the current |
480 | - ProjectGroup's products, since that's the product that will be |
481 | - selected by default when the view is rendered. |
482 | - """ |
483 | - url = canonical_url( |
484 | - self.default_product, view_name='+filebug-show-similar') |
485 | - if self.extra_data_token is not None: |
486 | - url = urlappend(url, self.extra_data_token) |
487 | - return url |
488 | + def contextUsesMalone(self): |
489 | + return self.default_product is not None |
490 | + |
491 | + def contextIsProduct(self): |
492 | + return False |
493 | + |
494 | + def contextIsProject(self): |
495 | + return True |
496 | + |
497 | + def getProductOrDistroFromContext(self): |
498 | + return None |
499 | + |
500 | + @safe_action |
501 | + @action("Continue", name="projectgroupsearch") |
502 | + def projectgroup_search_action(self, action, data): |
503 | + """Redirect to the chosen product's form.""" |
504 | + base = canonical_url(data['product'], view_name='+filebug') |
505 | + query = urllib.urlencode([ |
506 | + ('field.actions.search', 'Continue'), |
507 | + ('field.title', data['title']), |
508 | + ('field.tags', ' '.join(data['tags'])), |
509 | + ]) |
510 | + url = '%s?%s' % (base, query) |
511 | + self.request.response.redirect(url) |
512 | |
513 | |
514 | class BugTargetBugListingView(LaunchpadView): |
515 | |
516 | === modified file 'lib/lp/bugs/browser/configure.zcml' |
517 | --- lib/lp/bugs/browser/configure.zcml 2012-07-31 03:14:11 +0000 |
518 | +++ lib/lp/bugs/browser/configure.zcml 2012-08-03 00:21:23 +0000 |
519 | @@ -422,7 +422,8 @@ |
520 | <browser:page |
521 | name="+filebug" |
522 | for="lp.registry.interfaces.projectgroup.IProjectGroup" |
523 | - class="lp.bugs.browser.bugtarget.ProjectFileBugGuidedView" |
524 | + class="lp.bugs.browser.bugtarget.ProjectGroupFileBugGuidedView" |
525 | + template="../templates/bugtarget-filebug-search.pt" |
526 | permission="launchpad.AnyPerson"/> |
527 | <browser:page |
528 | name="+filebug-advanced" |
529 | |
530 | === modified file 'lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt' |
531 | --- lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt 2012-07-30 23:49:34 +0000 |
532 | +++ lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt 2012-08-03 00:21:23 +0000 |
533 | @@ -79,20 +79,6 @@ |
534 | >>> print filebug_view.duplicate_search_url |
535 | http://launchpad.dev/firefox/+filebug-show-similar |
536 | |
537 | -For project groups, the URLs returned will refer to the default product |
538 | -for those project groups. |
539 | - |
540 | - >>> from lp.registry.interfaces.projectgroup import IProjectGroupSet |
541 | - >>> gnome_project = getUtility(IProjectGroupSet).getByName('gnome') |
542 | - >>> gnome_filebug_view = create_initialized_view( |
543 | - ... gnome_project, '+filebug') |
544 | - |
545 | - >>> print gnome_filebug_view.inline_filebug_form_url |
546 | - http://launchpad.dev/evolution/+filebug-inline-form |
547 | - |
548 | - >>> print gnome_filebug_view.duplicate_search_url |
549 | - http://launchpad.dev/evolution/+filebug-show-similar |
550 | - |
551 | |
552 | Adding extra info to filed bugs |
553 | ------------------------------- |
554 | |
555 | === modified file 'lib/lp/bugs/browser/tests/test_bugtarget_filebug.py' |
556 | --- lib/lp/bugs/browser/tests/test_bugtarget_filebug.py 2012-07-31 03:07:56 +0000 |
557 | +++ lib/lp/bugs/browser/tests/test_bugtarget_filebug.py 2012-08-03 00:21:23 +0000 |
558 | @@ -610,10 +610,3 @@ |
559 | login_person(user) |
560 | view = create_initialized_view(product, '+filebug', principal=user) |
561 | self._assert_cache_values(view, False) |
562 | - |
563 | - def test_project_group(self): |
564 | - project = self.factory.makeProject() |
565 | - user = self.factory.makePerson() |
566 | - login_person(user) |
567 | - view = create_initialized_view(project, '+filebug', principal=user) |
568 | - self._assert_cache_values(view, True) |
569 | |
570 | === modified file 'lib/lp/bugs/javascript/filebug_dupefinder.js' |
571 | --- lib/lp/bugs/javascript/filebug_dupefinder.js 2012-07-23 11:15:20 +0000 |
572 | +++ lib/lp/bugs/javascript/filebug_dupefinder.js 2012-08-03 00:21:23 +0000 |
573 | @@ -314,43 +314,20 @@ |
574 | } |
575 | |
576 | /** |
577 | - * Use the value of the product field to set the relevant urls elements. |
578 | - */ |
579 | -function set_product_urls() |
580 | -{ |
581 | - var product_field = Y.one(Y.DOM.byId('field.product')); |
582 | - if (Y.Lang.isValue(product_field)) { |
583 | - var product = product_field.get('value'); |
584 | - search_url_base = |
585 | - filebug_base_url + product + '/+filebug-show-similar'; |
586 | - var submit_url = [product, '+filebug'].join('/'); |
587 | - var search_form = Y.one('#filebug-search-form'); |
588 | - search_form.setAttribute('action', filebug_base_url+submit_url); |
589 | - } |
590 | -} |
591 | - |
592 | -/** |
593 | * Set up the dupe finder, overriding the default behaviour of the |
594 | * +filebug search form. |
595 | */ |
596 | function set_up_dupe_finder(transaction_id, response, args) { |
597 | // Grab the inline filebug base url and store it. |
598 | filebug_base_url = Y.one('#filebug-base-url').getAttribute('href'); |
599 | - |
600 | - // Set up the product field change listener and related variables. |
601 | - namespace.setup_product_urls(); |
602 | - |
603 | + search_url_base = Y.one('#duplicate-search-url').getAttribute('href'); |
604 | + |
605 | + search_button = Y.one(Y.DOM.byId('field.actions.search')); |
606 | search_field = Y.one(Y.DOM.byId('field.title')); |
607 | |
608 | - var product_field = Y.one(Y.DOM.byId('field.product')); |
609 | - if (Y.Lang.isValue(product_field)) { |
610 | - Y.one( |
611 | - Y.DOM.byId('field.actions.projectgroupsearch')).set( |
612 | - 'value', 'Next'); |
613 | - } else { |
614 | + if (Y.Lang.isValue(search_button)) { |
615 | // Update the label on the search button so that it no longer |
616 | // says "Continue". |
617 | - search_button = Y.one(Y.DOM.byId('field.actions.search')); |
618 | search_button.set('value', 'Next'); |
619 | |
620 | // Change the name and id of the search field so that it doesn't |
621 | @@ -459,16 +436,6 @@ |
622 | }); |
623 | }; |
624 | |
625 | -namespace.setup_product_urls = function() { |
626 | - // Grab the search_url_base value from the page and store it. |
627 | - search_url_base = Y.one('#duplicate-search-url').getAttribute('href'); |
628 | - var product_field = Y.one(Y.DOM.byId('field.product')); |
629 | - if (Y.Lang.isValue(product_field)) { |
630 | - set_product_urls(); |
631 | - product_field.on('change', set_product_urls); |
632 | - } |
633 | -}; |
634 | - |
635 | namespace.setup_dupe_finder = function() { |
636 | var config = {on: {success: set_up_dupe_finder, |
637 | failure: function() {}}}; |
638 | |
639 | === modified file 'lib/lp/bugs/javascript/tests/test_filebug_dupefinder.js' |
640 | --- lib/lp/bugs/javascript/tests/test_filebug_dupefinder.js 2012-06-28 16:54:20 +0000 |
641 | +++ lib/lp/bugs/javascript/tests/test_filebug_dupefinder.js 2012-08-03 00:21:23 +0000 |
642 | @@ -242,86 +242,6 @@ |
643 | Y.Assert.areEqual( |
644 | 'https://bugs.launchpad.dev/foo/+filebug', |
645 | Y.one('#filebug-form').get('action')); |
646 | - }, |
647 | - |
648 | - add_project_selector: function() { |
649 | - var project_selector = Y.Node.create([ |
650 | - '<tr>', |
651 | - ' <td>', |
652 | - ' <label for="field.product">Project:</label>', |
653 | - ' <select size="1" name="field.product" id="field.product">', |
654 | - ' <option value="foo">Foo</option>', |
655 | - ' <option value="bar">Bar</option>', |
656 | - ' </select>', |
657 | - ' </td>', |
658 | - '</tr>' |
659 | - ].join('')); |
660 | - Y.one('#search-field').insert(project_selector, 'before'); |
661 | - module.setup_product_urls(); |
662 | - }, |
663 | - |
664 | - /** |
665 | - * The filebug form url is correctly updated when the project changes. |
666 | - */ |
667 | - test_project_change_filebug_form_action: function() { |
668 | - this.add_project_selector(); |
669 | - var project = Y.one(Y.DOM.byId('field.product')); |
670 | - project.set('value', 'bar'); |
671 | - simulate(project, undefined, 'change'); |
672 | - Y.Assert.areEqual( |
673 | - 'https://bugs.launchpad.dev/bar/+filebug', |
674 | - Y.one('#filebug-search-form').get('action')); |
675 | - }, |
676 | - |
677 | - /** |
678 | - * A user first searches for duplicate bugs and there are none. |
679 | - * They can start typing in some detail. They change the project and |
680 | - * perform a new search. Their input should be retained. |
681 | - */ |
682 | - test_project_change_retains_user_input_after_dups_serach: function() { |
683 | - Y.one(Y.DOM.byId('field.product')).set('value', 'foo'); |
684 | - module.setup_product_urls(); |
685 | - // filebug container should not initially be visible |
686 | - this.assertIsNotVisible(null, '#filebug-form-container'); |
687 | - var search_text = Y.one(Y.DOM.byId('field.search')); |
688 | - search_text.set('value', 'foo'); |
689 | - var search_button = Y.one(Y.DOM.byId('field.actions.search')); |
690 | - this.config.yio.io.responseText = 'No similar bug reports.'; |
691 | - this.config.yio.io.doAfter = function() { |
692 | - var comment_text = Y.one(Y.DOM.byId('field.comment')); |
693 | - comment_text.set('value', 'an error occurred'); |
694 | - |
695 | - this.config.yio.io.responseText = 'Bug filing details'; |
696 | - var project = Y.one(Y.DOM.byId('field.product')); |
697 | - project.set('value', 'bar'); |
698 | - simulate(project, undefined, 'change'); |
699 | - // filebug container should be visible |
700 | - this.assertIsVisible(null, '#filebug-form-container'); |
701 | - |
702 | - // Search button should day 'Check again' because we have |
703 | - // already done a search. |
704 | - var search_button = (Y.one(Y.DOM.byId('field.actions.search'))); |
705 | - Y.Assert.areEqual('Check again', search_button.get('value')); |
706 | - |
707 | - this.config.yio.io.responseText = 'No similar bug reports.'; |
708 | - this.config.yio.io.doAfter = function() { |
709 | - // filebug container should be visible |
710 | - this.assertIsVisible(null, '#filebug-form-container'); |
711 | - // The user input should be retained |
712 | - Y.Assert.areEqual( |
713 | - 'an error occurred', comment_text.get('value')); |
714 | - Y.ArrayAssert.itemsAreEqual( |
715 | - ['https://bugs.launchpad.dev/' + |
716 | - 'foo/+filebug-show-similar?title=foo', |
717 | - 'https://bugs.launchpad.dev/' + |
718 | - 'bar/+filebug-show-similar?title=foo'], |
719 | - this.config.yio.calls); |
720 | - }; |
721 | - simulate(search_button, undefined, 'click'); |
722 | - this.wait(); |
723 | - }; |
724 | - simulate(search_button, undefined, 'click'); |
725 | - this.wait(); |
726 | } |
727 | |
728 | })); |
729 | |
730 | === modified file 'lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-guidelines.txt' |
731 | --- lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-guidelines.txt 2011-04-20 12:59:55 +0000 |
732 | +++ lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-guidelines.txt 2012-08-03 00:21:23 +0000 |
733 | @@ -85,11 +85,11 @@ |
734 | Thank you for filing a bug for https://launchpad.dev/ubuntu |
735 | * |
736 | Mozilla |
737 | - <http://launchpad.dev/mozilla/+filebug> |
738 | - The Mozilla Project bug reporting guidelines: |
739 | - The version of Mozilla you're using. |
740 | + <http://launchpad.dev/firefox/+filebug?field.actions.search=Continue&field.title=It+doesn%27t+work&field.tags=> |
741 | + Mozilla Firefox bug reporting guidelines: |
742 | + The version of Firefox you're using. |
743 | See http://example.com for more details. |
744 | - Thank you for filing a bug for https://launchpad.dev/mozilla |
745 | + Thank you for filing a bug for https://launchpad.dev/firefox |
746 | * |
747 | Firefox |
748 | <http://launchpad.dev/firefox/+filebug> |
749 | |
750 | === modified file 'lib/lp/bugs/stories/guided-filebug/xx-product-guided-filebug.txt' |
751 | --- lib/lp/bugs/stories/guided-filebug/xx-product-guided-filebug.txt 2012-02-23 16:15:43 +0000 |
752 | +++ lib/lp/bugs/stories/guided-filebug/xx-product-guided-filebug.txt 2012-08-03 00:21:23 +0000 |
753 | @@ -24,7 +24,7 @@ |
754 | There is 1 error. |
755 | >>> for message in top_portlet.findAll(attrs={'class': 'message'}): |
756 | ... print message.renderContents() |
757 | - A summary is required. |
758 | + Required input is missing. |
759 | |
760 | The user fills in some keywords, and clicks a button to search existing |
761 | bugs. |
762 | |
763 | === modified file 'lib/lp/bugs/stories/guided-filebug/xx-project-guided-filebug.txt' |
764 | --- lib/lp/bugs/stories/guided-filebug/xx-project-guided-filebug.txt 2012-02-23 16:15:43 +0000 |
765 | +++ lib/lp/bugs/stories/guided-filebug/xx-project-guided-filebug.txt 2012-08-03 00:21:23 +0000 |
766 | @@ -16,14 +16,16 @@ |
767 | >>> user_browser.getControl('Project', index=0).options |
768 | ['evolution'] |
769 | |
770 | -After we selected a product and entered a summary, we get presented with |
771 | -a list of possible duplicates. |
772 | +After we selected a product and entered a summary, we're sent to the |
773 | +product's +filebug page and presented with a list of possible duplicates. |
774 | |
775 | >>> user_browser.getControl('Project', index=0).value = ['evolution'] |
776 | >>> user_browser.getControl('Summary', index=0).value = ( |
777 | ... 'Evolution crashes') |
778 | >>> user_browser.getControl('Continue').click() |
779 | |
780 | + >>> user_browser.url |
781 | + 'http://bugs.launchpad.dev/evolution/+filebug?field.actions.search=Continue&field.title=Evolution+crashes&field.tags=' |
782 | >>> print find_main_content(user_browser.contents).renderContents() |
783 | <... |
784 | No similar bug reports were found... |
785 | @@ -40,78 +42,6 @@ |
786 | 'Bug #...Evolution crashes... : Bugs : Evolution' |
787 | |
788 | |
789 | -Subscribing to a similar bug |
790 | ----------------------------- |
791 | - |
792 | -If our bug is described by one of the suggested similar bugs, we can |
793 | -subscribe to it instead of filing a new bug. This also loosely implies a |
794 | -"me too" vote. |
795 | - |
796 | - >>> user_browser.open("http://bugs.launchpad.dev/gnome/+filebug") |
797 | - >>> user_browser.getControl('Project', index=0).value = ['evolution'] |
798 | - >>> user_browser.getControl('Summary', index=0).value = ( |
799 | - ... 'Evolution crashes') |
800 | - >>> user_browser.getControl('Continue').click() |
801 | - |
802 | -As before, we get a list of similar bugs to choose from, including the |
803 | -bugs we filed recently. |
804 | - |
805 | - >>> from lp.bugs.tests.bug import print_bugs_list |
806 | - >>> print_bugs_list(user_browser.contents, "similar-bugs") |
807 | - #... Evolution crashes |
808 | - New (0 comments) last updated ... |
809 | - |
810 | -This one matches, so we subscribe. |
811 | - |
812 | - >>> user_browser.getControl( |
813 | - ... "Yes, this is the bug I'm trying to report").click() |
814 | - |
815 | - >>> print user_browser.url |
816 | - http://bugs.launchpad.dev/evolution/+bug/... |
817 | - |
818 | -But, of course, we're already subscribed because we created it. |
819 | - |
820 | - >>> for message in get_feedback_messages(user_browser.contents): |
821 | - ... print message |
822 | - This bug is already marked as affecting you. |
823 | - |
824 | - |
825 | -Filing a bug when there are none similar |
826 | ----------------------------------------- |
827 | - |
828 | -When no similar bugs are found the form works the same but appears |
829 | -different in the user agent. |
830 | - |
831 | - >>> user_browser.open("http://launchpad.dev/gnome/+filebug") |
832 | - |
833 | -Submitting some distinctive details... |
834 | - |
835 | - >>> user_browser.getControl('Project', index=0).value = ['evolution'] |
836 | - >>> user_browser.getControl('Summary', index=0).value = ( |
837 | - ... 'Faznambutron dumps core unless clenching') |
838 | - >>> user_browser.getControl('Continue').click() |
839 | - |
840 | -...yields no similar bugs. In fact, the similar bugs table is not even |
841 | -shown. |
842 | - |
843 | - >>> similar_bugs_list = find_tag_by_id( |
844 | - ... user_browser.contents, "similar-bugs") |
845 | - >>> print similar_bugs_list |
846 | - None |
847 | - |
848 | -But, as before, entering a description and submitting the bug takes the |
849 | -user to the bug page. |
850 | - |
851 | - >>> user_browser.getControl('Further information').value = ( |
852 | - ... 'Faznambutron is a plugin designed to ...') |
853 | - >>> user_browser.getControl('Submit Bug Report').click() |
854 | - >>> user_browser.url |
855 | - 'http://bugs.launchpad.dev/evolution/+bug/...' |
856 | - |
857 | - >>> user_browser.title |
858 | - 'Bug #...Faznambutron dumps core... : Bugs : Evolution' |
859 | - |
860 | - |
861 | Empty ProjectGroups |
862 | ------------------- |
863 | |
864 | |
865 | === modified file 'lib/lp/bugs/templates/bugtarget-filebug-search.pt' |
866 | --- lib/lp/bugs/templates/bugtarget-filebug-search.pt 2012-07-07 14:00:30 +0000 |
867 | +++ lib/lp/bugs/templates/bugtarget-filebug-search.pt 2012-08-03 00:21:23 +0000 |
868 | @@ -57,12 +57,6 @@ |
869 | <metal:widget metal:use-macro="context/@@launchpad_form/widget_row" /> |
870 | </tal:product_widget> |
871 | |
872 | - <tal:bugtarget |
873 | - tal:define="widget nocall:view/widgets/bugtarget|nothing" |
874 | - tal:condition="widget"> |
875 | - <metal:widget metal:use-macro="context/@@launchpad_form/widget_row" /> |
876 | - </tal:bugtarget> |
877 | - |
878 | <tal:hidden_tags tal:replace="structure view/widgets/tags/hidden" /> |
879 | |
880 | <tr> |
881 | @@ -137,7 +131,7 @@ |
882 | </tal:filebug-form> |
883 | </div> |
884 | </tal:not_project_group> |
885 | - <p class="hidden"> |
886 | + <p class="hidden" tal:condition="view/inline_filebug_base_url|nothing"> |
887 | <a id="filebug-base-url" |
888 | tal:attributes="href view/inline_filebug_base_url"></a> |
889 | <a id="filebug-form-url" |
890 | |
891 | === modified file 'lib/lp/bugs/templates/bugtarget-macros-filebug.pt' |
892 | --- lib/lp/bugs/templates/bugtarget-macros-filebug.pt 2012-07-31 03:14:11 +0000 |
893 | +++ lib/lp/bugs/templates/bugtarget-macros-filebug.pt 2012-08-03 00:21:23 +0000 |
894 | @@ -4,12 +4,6 @@ |
895 | omit-tag=""> |
896 | <metal:basic_filebug_widgets define-macro="basic_filebug_widgets"> |
897 | |
898 | - <tal:bugtarget |
899 | - tal:define="widget nocall:view/widgets/bugtarget|nothing" |
900 | - tal:condition="widget"> |
901 | - <metal:widget use-macro="context/@@launchpad_form/widget_row" /> |
902 | - </tal:bugtarget> |
903 | - |
904 | <tr tal:condition="view/widgets/packagename|nothing"> |
905 | <th colspan="2" style="text-align: left"><label |
906 | tal:attributes="for view/widgets/packagename/name" |
907 | @@ -154,10 +148,6 @@ |
908 | Change this <span class="sprite edit action-icon">Edit</span> |
909 | </a> |
910 | </div> |
911 | - <span tal:condition="view/frontpage_form"> |
912 | - You can <a href="+filebug">refine and resubmit</a> your bug |
913 | - report. |
914 | - </span> |
915 | <tal:upstream condition="view/contextIsProduct"> |
916 | <tal:defines define="bugtracker product_or_distro/getExternalBugTracker"> |
917 | <h3> |
Looks good, glad to see all this code excised.